@@ -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 |