@@ -0,0 +1,236 @@ | |||||
/* | |||||
* DISTRHO Plugin Framework (DPF) | |||||
* Copyright (C) 2012-2014 Filipe Coelho <falktx@falktx.com> | |||||
* | |||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
* or without fee is hereby granted, provided that the above copyright notice and this | |||||
* permission notice appear in all copies. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
// ------------------------------------------------------ | |||||
// DGL Stuff | |||||
#include "StandaloneWindow.hpp" | |||||
// ------------------------------------------------------ | |||||
// NanoVG Stuff | |||||
#include "NanoVG.hpp" | |||||
#include "widgets/NanoPerfWidget.hpp" | |||||
// ------------------------------------------------------ | |||||
// use namespace | |||||
using namespace DGL; | |||||
// ------------------------------------------------------ | |||||
// NanoVG Example Widget | |||||
int blowup = 0; | |||||
int premult = 0; | |||||
int mx = 0; | |||||
int my = 0; | |||||
class NanoExampleWidget : public NanoWidget, | |||||
public IdleCallback | |||||
{ | |||||
public: | |||||
NanoExampleWidget(Window& parent) | |||||
: NanoWidget(parent) | |||||
{ | |||||
parent.addIdleCallback(this); | |||||
for (int i = 0; i < 12; ++i) | |||||
{ | |||||
char file[128]; | |||||
std::snprintf(file, 128, "./nanovg_res/images/image%d.jpg", i+1); | |||||
fImages[i] = createImage(file); | |||||
if (! fImages[i].isValid()) | |||||
{ | |||||
d_stdout("Could not load %s.", file); | |||||
return; | |||||
} | |||||
} | |||||
fFontIcons = createFont("icons", "./nanovg_res/entypo.ttf"); | |||||
fFontNormal = createFont("sans", "./nanovg_res/Roboto-Regular.ttf"); | |||||
fFontBold = createFont("sans-bold", "./nanovg_res/Roboto-Bold.ttf"); | |||||
} | |||||
protected: | |||||
void idleCallback() override | |||||
{ | |||||
repaint(); | |||||
} | |||||
void onNanoDisplay() override | |||||
{ | |||||
const int width = getWidth(); | |||||
const int height = getHeight(); | |||||
const double t = gTime.getTime(); | |||||
if (premult) | |||||
glClearColor(0, 0, 0, 0); | |||||
else | |||||
glClearColor(0.3f, 0.3f, 0.32f, 1.0f); | |||||
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); | |||||
drawEyes(width - 250, 50, 150, 100, mx, my, t); | |||||
} | |||||
bool onKeyboard(const KeyboardEvent& ev) override | |||||
{ | |||||
if (! ev.press) | |||||
return false; | |||||
switch (ev.key) | |||||
{ | |||||
case CHAR_ESCAPE: | |||||
getParentApp().quit(); | |||||
break; | |||||
case ' ': | |||||
blowup = !blowup; | |||||
break; | |||||
case 'p': | |||||
case 'P': | |||||
premult = !premult; | |||||
break; | |||||
} | |||||
return true; | |||||
} | |||||
bool onMotion(const MotionEvent& ev) override | |||||
{ | |||||
mx = ev.pos.getX(); | |||||
my = ev.pos.getY(); | |||||
return false; | |||||
} | |||||
private: | |||||
FontId fFontNormal, fFontBold, fFontIcons; | |||||
NanoImage fImages[12]; | |||||
void drawEyes(float x, float y, float w, float h, float mx, float my, float t) | |||||
{ | |||||
Paint gloss, bg; | |||||
float ex = w *0.23f; | |||||
float ey = h * 0.5f; | |||||
float lx = x + ex; | |||||
float ly = y + ey; | |||||
float rx = x + w - ex; | |||||
float ry = y + ey; | |||||
float dx,dy,d; | |||||
float br = (ex < ey ? ex : ey) * 0.5f; | |||||
float blink = 1 - std::pow(std::sin(t*0.5f),200)*0.8f; | |||||
bg = linearGradient(x,y+h*0.5f,x+w*0.1f,y+h, RGBA(0,0,0,32), RGBA(0,0,0,16)); | |||||
beginPath(); | |||||
ellipse(lx+3.0f,ly+16.0f, ex,ey); | |||||
ellipse(rx+3.0f,ry+16.0f, ex,ey); | |||||
fillPaint(bg); | |||||
fill(); | |||||
bg = linearGradient(x,y+h*0.25f,x+w*0.1f,y+h, RGBA(220,220,220,255), RGBA(128,128,128,255)); | |||||
beginPath(); | |||||
ellipse(lx,ly, ex,ey); | |||||
ellipse(rx,ry, ex,ey); | |||||
fillPaint(bg); | |||||
fill(); | |||||
dx = (mx - rx) / (ex * 10); | |||||
dy = (my - ry) / (ey * 10); | |||||
d = std::sqrt(dx*dx+dy*dy); | |||||
if (d > 1.0f) { | |||||
dx /= d; dy /= d; | |||||
} | |||||
dx *= ex*0.4f; | |||||
dy *= ey*0.5f; | |||||
beginPath(); | |||||
ellipse(lx+dx,ly+dy+ey*0.25f*(1-blink), br,br*blink); | |||||
fillColor(RGBA(32,32,32,255)); | |||||
fill(); | |||||
dx = (mx - rx) / (ex * 10); | |||||
dy = (my - ry) / (ey * 10); | |||||
d = std::sqrt(dx*dx+dy*dy); | |||||
if (d > 1.0f) { | |||||
dx /= d; dy /= d; | |||||
} | |||||
dx *= ex*0.4f; | |||||
dy *= ey*0.5f; | |||||
beginPath(); | |||||
ellipse(rx+dx,ry+dy+ey*0.25f*(1-blink), br,br*blink); | |||||
fillColor(RGBA(32,32,32,255)); | |||||
fill(); | |||||
gloss = radialGradient(lx-ex*0.25f,ly-ey*0.5f, ex*0.1f,ex*0.75f, RGBA(255,255,255,128), RGBA(255,255,255,0)); | |||||
beginPath(); | |||||
ellipse(lx,ly, ex,ey); | |||||
fillPaint(gloss); | |||||
fill(); | |||||
gloss = radialGradient(rx-ex*0.25f,ry-ey*0.5f, ex*0.1f,ex*0.75f, RGBA(255,255,255,128), RGBA(255,255,255,0)); | |||||
beginPath(); | |||||
ellipse(rx,ry, ex,ey); | |||||
fillPaint(gloss); | |||||
fill(); | |||||
} | |||||
}; | |||||
// ----------------------------------------------------------------------- | |||||
// We need a custom window for multiple widgets | |||||
class NanoExampleWindow : public Window | |||||
{ | |||||
public: | |||||
NanoExampleWindow(App& app) | |||||
: Window(app), | |||||
fDemo(*this), | |||||
fPerf(*this, NanoPerfWidget::RENDER_FPS, "Frame Time") | |||||
{ | |||||
fPerf.setAbsolutePos(5, 5); | |||||
setSize(1000, 600); | |||||
setTitle("NanoVG"); | |||||
} | |||||
protected: | |||||
void onReshape(int width, int height) | |||||
{ | |||||
fDemo.setSize(width, height); | |||||
Window::onReshape(width, height); | |||||
} | |||||
private: | |||||
NanoExampleWidget fDemo; | |||||
NanoPerfWidget fPerf; | |||||
}; | |||||
// ------------------------------------------------------ | |||||
// main entry point | |||||
int main() | |||||
{ | |||||
App app; | |||||
NanoExampleWindow win(app); | |||||
win.show(); | |||||
app.exec(); | |||||
return 0; | |||||
} | |||||
// ------------------------------------------------------ |
@@ -0,0 +1,249 @@ | |||||
/* | |||||
* DISTRHO Plugin Framework (DPF) | |||||
* Copyright (C) 2012-2014 Filipe Coelho <falktx@falktx.com> | |||||
* | |||||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||||
* or without fee is hereby granted, provided that the above copyright notice and this | |||||
* permission notice appear in all copies. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||||
*/ | |||||
#ifndef NANO_PERF_WIDGET_HPP_INCLUDED | |||||
#define NANO_PERF_WIDGET_HPP_INCLUDED | |||||
// ------------------------------------------------------ | |||||
// DGL Stuff | |||||
#include "NanoVG.hpp" | |||||
#include "../../dpf/distrho/extra/d_string.hpp" | |||||
// ------------------------------------------------------ | |||||
// use namespace | |||||
using DGL::IdleCallback; | |||||
using DGL::NanoVG; | |||||
using DGL::NanoWidget; | |||||
using DGL::Widget; | |||||
using DGL::Window; | |||||
// ------------------------------------------------------ | |||||
// get time | |||||
#include <sys/time.h> | |||||
#include <time.h> | |||||
#ifdef DISTRHO_OS_WINDOWS | |||||
#else | |||||
struct TimePOSIX { | |||||
bool monotonic; | |||||
double resolution; | |||||
uint64_t base; | |||||
TimePOSIX() | |||||
: monotonic(false), | |||||
resolution(1e-6), | |||||
base(0) | |||||
{ | |||||
#if defined(CLOCK_MONOTONIC) | |||||
struct timespec ts; | |||||
if (::clock_gettime(CLOCK_MONOTONIC, &ts) == 0) | |||||
{ | |||||
monotonic = true; | |||||
resolution = 1e-9; | |||||
} | |||||
#endif | |||||
base = getRawTime(); | |||||
} | |||||
uint64_t getRawTime() | |||||
{ | |||||
#if defined(CLOCK_MONOTONIC) | |||||
if (monotonic) | |||||
{ | |||||
struct timespec ts; | |||||
::clock_gettime(CLOCK_MONOTONIC, &ts); | |||||
return (uint64_t) ts.tv_sec * (uint64_t) 1000000000 + (uint64_t) ts.tv_nsec; | |||||
} | |||||
else | |||||
#endif | |||||
{ | |||||
struct timeval tv; | |||||
::gettimeofday(&tv, NULL); | |||||
return (uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) tv.tv_usec; | |||||
} | |||||
} | |||||
double getTime() | |||||
{ | |||||
return (double)(getRawTime() - base) * resolution; | |||||
} | |||||
}; | |||||
static TimePOSIX gTime; | |||||
#endif | |||||
// ------------------------------------------------------ | |||||
// our widget | |||||
class NanoPerfWidget : public NanoWidget, | |||||
public IdleCallback | |||||
{ | |||||
public: | |||||
static const int kHistoryCount = 100; | |||||
enum RenderStyle { | |||||
RENDER_FPS, | |||||
RENDER_MS, | |||||
}; | |||||
NanoPerfWidget(Window& parent, RenderStyle style, const char* name) | |||||
: NanoWidget(parent), | |||||
fHead(0), | |||||
fStyle(style), | |||||
fName(name) | |||||
{ | |||||
parent.addIdleCallback(this); | |||||
setSize(200, 35); | |||||
std::memset(fValues, 0, sizeof(float)*kHistoryCount); | |||||
createFont("sans", "./nanovg_res/Roboto-Regular.ttf"); | |||||
prevt = gTime.getTime(); | |||||
} | |||||
protected: | |||||
void idleCallback() override | |||||
{ | |||||
repaint(); | |||||
} | |||||
void onNanoDisplay() override | |||||
{ | |||||
double t, dt; | |||||
t = gTime.getTime(); | |||||
dt = t - prevt; | |||||
prevt = t; | |||||
update(dt); | |||||
const int w = 200; //getWidth(); | |||||
const int h = 35; //getHeight(); | |||||
int i; | |||||
float avg; | |||||
char str[64]; | |||||
avg = getAverage(); | |||||
beginPath(); | |||||
rect(0, 0, w, h); | |||||
fillColor(RGBA(0,0,0,128)); | |||||
fill(); | |||||
beginPath(); | |||||
moveTo(0, h); | |||||
if (fStyle == RENDER_FPS) | |||||
{ | |||||
for (i = 0; i < kHistoryCount; ++i) | |||||
{ | |||||
float v = 1.0f / (0.00001f + fValues[(fHead+i) % kHistoryCount]); | |||||
float vx, vy; | |||||
if (v > 80.0f) v = 80.0f; | |||||
vx = ((float)i/(kHistoryCount-1)) * w; | |||||
vy = h - ((v / 80.0f) * h); | |||||
lineTo(vx, vy); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
for (i = 0; i < kHistoryCount; ++i) | |||||
{ | |||||
float v = fValues[(fHead+i) % kHistoryCount] * 1000.0f; | |||||
float vx, vy; | |||||
if (v > 20.0f) v = 20.0f; | |||||
vx = ((float)i/(kHistoryCount-1)) * w; | |||||
vy = h - ((v / 20.0f) * h); | |||||
lineTo(vx, vy); | |||||
} | |||||
} | |||||
lineTo(w, h); | |||||
fillColor(RGBA(255,192,0,128)); | |||||
fill(); | |||||
fontFace("sans"); | |||||
if (fName.isNotEmpty()) | |||||
{ | |||||
fontSize(14.0f); | |||||
textAlign(ALIGN_LEFT|ALIGN_TOP); | |||||
fillColor(RGBA(240,240,240,192)); | |||||
text(3, 1, fName, nullptr); | |||||
} | |||||
if (fStyle == RENDER_FPS) | |||||
{ | |||||
fontSize(18.0f); | |||||
textAlign(ALIGN_RIGHT|ALIGN_TOP); | |||||
fillColor(RGBA(240,240,240,255)); | |||||
std::sprintf(str, "%.2f FPS", 1.0f / avg); | |||||
text(w-3, 1, str, nullptr); | |||||
fontSize(15.0f); | |||||
textAlign(ALIGN_RIGHT|ALIGN_BOTTOM); | |||||
fillColor(RGBA(240,240,240,160)); | |||||
std::sprintf(str, "%.2f ms", avg * 1000.0f); | |||||
text(w-3, h-1, str, nullptr); | |||||
} | |||||
else | |||||
{ | |||||
fontSize(18.0f); | |||||
textAlign(ALIGN_RIGHT|ALIGN_TOP); | |||||
fillColor(RGBA(240,240,240,255)); | |||||
std::sprintf(str, "%.2f ms", avg * 1000.0f); | |||||
text(w-3, 1, str, nullptr); | |||||
} | |||||
} | |||||
private: | |||||
int fHead; | |||||
float fValues[kHistoryCount]; | |||||
const int fStyle; | |||||
const d_string fName; | |||||
double prevt; | |||||
void update(float frameTime) noexcept | |||||
{ | |||||
fHead = (fHead+1) % kHistoryCount; | |||||
fValues[fHead] = frameTime; | |||||
} | |||||
float getAverage() const noexcept | |||||
{ | |||||
int i; | |||||
float avg = 0; | |||||
for (i = 0; i < kHistoryCount; ++i) | |||||
avg += fValues[i]; | |||||
return avg / (float)kHistoryCount; | |||||
} | |||||
}; | |||||
// ------------------------------------------------------ | |||||
#endif // NANO_PERF_WIDGET_HPP_INCLUDED |