@@ -72,14 +72,34 @@ public: | |||||
*/ | */ | ||||
~CairoImage() override; | ~CairoImage() override; | ||||
/** | |||||
Load image data from memory. | |||||
@note @a rawData must remain valid for the lifetime of this Image. | |||||
*/ | |||||
void loadFromMemory(const char* rawData, | |||||
const Size<uint>& size, | |||||
ImageFormat format = kImageFormatBGRA) noexcept override; | |||||
/** | /** | ||||
Draw this image at position @a pos using the graphics context @a context. | Draw this image at position @a pos using the graphics context @a context. | ||||
*/ | */ | ||||
void drawAt(const GraphicsContext& context, const Point<int>& pos) override; | void drawAt(const GraphicsContext& context, const Point<int>& pos) override; | ||||
/** | |||||
TODO document this. | |||||
*/ | |||||
CairoImage& operator=(const CairoImage& image) noexcept; | |||||
// FIXME this should not be needed | // FIXME this should not be needed | ||||
inline void loadFromMemory(const char* rawData, uint w, uint h, ImageFormat format) | |||||
{ loadFromMemory(rawData, Size<uint>(w, h), format); }; | |||||
inline void drawAt(const GraphicsContext& context, int x, int y) | inline void drawAt(const GraphicsContext& context, int x, int y) | ||||
{ drawAt(context, Point<int>(x, y)); }; | { drawAt(context, Point<int>(x, y)); }; | ||||
private: | |||||
cairo_surface_t* surface; | |||||
uchar* surfacedata; | |||||
int* datarefcount; | |||||
}; | }; | ||||
// -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
@@ -24,6 +24,7 @@ START_NAMESPACE_DGL | |||||
// -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
enum ImageFormat { | enum ImageFormat { | ||||
kImageFormatNull, | |||||
kImageFormatBGR, | kImageFormatBGR, | ||||
kImageFormatBGRA, | kImageFormatBGRA, | ||||
kImageFormatRGB, | kImageFormatRGB, | ||||
@@ -61,6 +61,9 @@ public: | |||||
{ return Window::addIdleCallback(callback, timerFrequencyInMs); } | { return Window::addIdleCallback(callback, timerFrequencyInMs); } | ||||
bool removeIdleCallback(IdleCallback* callback) { return Window::removeIdleCallback(callback); } | bool removeIdleCallback(IdleCallback* callback) { return Window::removeIdleCallback(callback); } | ||||
const GraphicsContext& getGraphicsContext() const noexcept { return Window::getGraphicsContext(); } | const GraphicsContext& getGraphicsContext() const noexcept { return Window::getGraphicsContext(); } | ||||
void setGeometryConstraints(uint minimumWidth, uint minimumHeight, | |||||
bool keepAspectRatio = false, bool automaticallyScale = false) | |||||
{ Window::setGeometryConstraints(minimumWidth, minimumHeight, keepAspectRatio, automaticallyScale); } | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StandaloneWindow) | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(StandaloneWindow) | ||||
}; | }; | ||||
@@ -92,20 +92,28 @@ static void drawCircle(cairo_t* const handle, | |||||
const T origy = pos.getY(); | const T origy = pos.getY(); | ||||
double t, x = size, y = 0.0; | double t, x = size, y = 0.0; | ||||
// TODO use arc | |||||
/* | /* | ||||
glBegin(outline ? GL_LINE_LOOP : GL_POLYGON); | |||||
cairo_arc(handle, origx, origy, size, sin, cos); | |||||
*/ | |||||
cairo_move_to(handle, x + origx, y + origy); | |||||
for (uint i=0; i<numSegments; ++i) | |||||
for (uint i=1; i<numSegments; ++i) | |||||
{ | { | ||||
glVertex2d(x + origx, y + origy); | |||||
cairo_line_to(handle, x + origx, y + origy); | |||||
t = x; | t = x; | ||||
x = cos * x - sin * y; | x = cos * x - sin * y; | ||||
y = sin * t + cos * y; | y = sin * t + cos * y; | ||||
} | } | ||||
glEnd(); | |||||
*/ | |||||
cairo_line_to(handle, x + origx, y + origy); | |||||
if (outline) | |||||
cairo_stroke(handle); | |||||
else | |||||
cairo_fill(handle); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
@@ -158,7 +166,15 @@ static void drawTriangle(cairo_t* const handle, | |||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,); | DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,); | ||||
// TODO | |||||
cairo_move_to(handle, pos1.getX(), pos1.getY()); | |||||
cairo_line_to(handle, pos2.getX(), pos2.getY()); | |||||
cairo_line_to(handle, pos3.getX(), pos3.getY()); | |||||
cairo_line_to(handle, pos1.getX(), pos1.getY()); | |||||
if (outline) | |||||
cairo_stroke(handle); | |||||
else | |||||
cairo_fill(handle); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
@@ -206,7 +222,12 @@ template class Triangle<ushort>; | |||||
template<typename T> | template<typename T> | ||||
static void drawRectangle(cairo_t* const handle, const Rectangle<T>& rect, const bool outline) | static void drawRectangle(cairo_t* const handle, const Rectangle<T>& rect, const bool outline) | ||||
{ | { | ||||
// TODO | |||||
cairo_rectangle(handle, rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); | |||||
if (outline) | |||||
cairo_stroke(handle); | |||||
else | |||||
cairo_fill(handle); | |||||
} | } | ||||
template<typename T> | template<typename T> | ||||
@@ -253,24 +274,152 @@ template class Rectangle<ushort>; | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// CairoImage | // CairoImage | ||||
static cairo_format_t asCairoImageFormat(const ImageFormat format) | |||||
{ | |||||
switch (format) | |||||
{ | |||||
case kImageFormatNull: | |||||
break; | |||||
case kImageFormatBGR: | |||||
case kImageFormatRGB: | |||||
return CAIRO_FORMAT_RGB24; | |||||
case kImageFormatBGRA: | |||||
case kImageFormatRGBA: | |||||
return CAIRO_FORMAT_ARGB32; | |||||
} | |||||
return CAIRO_FORMAT_INVALID; | |||||
} | |||||
CairoImage::CairoImage() | CairoImage::CairoImage() | ||||
: ImageBase() {} | |||||
: ImageBase(), | |||||
surface(nullptr), | |||||
surfacedata(nullptr), | |||||
datarefcount(nullptr) {} | |||||
CairoImage::CairoImage(const char* const rawData, const uint width, const uint height, const ImageFormat format) | CairoImage::CairoImage(const char* const rawData, const uint width, const uint height, const ImageFormat format) | ||||
: ImageBase(rawData, width, height, format) {} | |||||
: ImageBase(rawData, width, height, format), | |||||
surface(nullptr), | |||||
surfacedata(nullptr), | |||||
datarefcount(nullptr) | |||||
{ | |||||
loadFromMemory(rawData, width, height, format); | |||||
} | |||||
CairoImage::CairoImage(const char* const rawData, const Size<uint>& size, const ImageFormat format) | CairoImage::CairoImage(const char* const rawData, const Size<uint>& size, const ImageFormat format) | ||||
: ImageBase(rawData, size, format) {} | |||||
: ImageBase(rawData, size, format), | |||||
surface(nullptr), | |||||
surfacedata(nullptr), | |||||
datarefcount(nullptr) | |||||
{ | |||||
loadFromMemory(rawData, size, format); | |||||
} | |||||
CairoImage::CairoImage(const CairoImage& image) | CairoImage::CairoImage(const CairoImage& image) | ||||
: ImageBase(image.rawData, image.size, image.format) {} | |||||
: ImageBase(image.rawData, image.size, image.format), | |||||
surface(cairo_surface_reference(image.surface)), | |||||
surfacedata(image.surfacedata), | |||||
datarefcount(image.datarefcount) | |||||
{ | |||||
if (datarefcount != nullptr) | |||||
++(*datarefcount); | |||||
} | |||||
CairoImage::~CairoImage() | CairoImage::~CairoImage() | ||||
{ | { | ||||
cairo_surface_destroy(surface); | |||||
if (datarefcount != nullptr && --(*datarefcount) == 0) | |||||
{ | |||||
std::free(surfacedata); | |||||
std::free(datarefcount); | |||||
} | |||||
} | } | ||||
void CairoImage::drawAt(const GraphicsContext&, const Point<int>&) | |||||
void CairoImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept | |||||
{ | { | ||||
const cairo_format_t cairoformat = asCairoImageFormat(fmt); | |||||
const uint width = s.getWidth(); | |||||
const uint height = s.getHeight(); | |||||
const int stride = cairo_format_stride_for_width(cairoformat, width); | |||||
uchar* const newdata = (uchar*)std::malloc(width * height * stride * 4); | |||||
DISTRHO_SAFE_ASSERT_RETURN(newdata != nullptr,); | |||||
cairo_surface_t* const newsurface = cairo_image_surface_create_for_data(newdata, cairoformat, width, height, stride); | |||||
DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,); | |||||
DISTRHO_SAFE_ASSERT_RETURN(s.getWidth() == cairo_image_surface_get_width(newsurface),); | |||||
DISTRHO_SAFE_ASSERT_RETURN(s.getHeight() == cairo_image_surface_get_height(newsurface),); | |||||
cairo_surface_destroy(surface); | |||||
if (datarefcount != nullptr && --(*datarefcount) == 0) | |||||
std::free(surfacedata); | |||||
else | |||||
datarefcount = (int*)malloc(sizeof(*datarefcount)); | |||||
surface = newsurface; | |||||
surfacedata = newdata; | |||||
*datarefcount = 1; | |||||
switch (fmt) | |||||
{ | |||||
case kImageFormatNull: | |||||
break; | |||||
case kImageFormatBGR: | |||||
// BGR8 to CAIRO_FORMAT_RGB24 | |||||
for (uint h = 0; h < height; ++h) | |||||
{ | |||||
for (uint w = 0; w < width; ++w) | |||||
{ | |||||
newdata[h*width*4+w*4+0] = rdata[h*width*3+w*3+0]; | |||||
newdata[h*width*4+w*4+1] = rdata[h*width*3+w*3+1]; | |||||
newdata[h*width*4+w*4+2] = rdata[h*width*3+w*3+2]; | |||||
newdata[h*width*4+w*4+3] = 0; | |||||
} | |||||
} | |||||
break; | |||||
case kImageFormatBGRA: | |||||
// RGB8 to CAIRO_FORMAT_ARGB32 | |||||
// TODO | |||||
break; | |||||
case kImageFormatRGB: | |||||
// RGB8 to CAIRO_FORMAT_RGB24 | |||||
// TODO | |||||
break; | |||||
case kImageFormatRGBA: | |||||
// RGBA8 to CAIRO_FORMAT_ARGB32 | |||||
// TODO | |||||
break; | |||||
} | |||||
ImageBase::loadFromMemory(rdata, s, fmt); | |||||
} | |||||
void CairoImage::drawAt(const GraphicsContext& context, const Point<int>& pos) | |||||
{ | |||||
if (surface == nullptr) | |||||
return; | |||||
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||||
cairo_set_source_surface(handle, surface, pos.getX(), pos.getY()); | |||||
cairo_paint(handle); | |||||
} | |||||
CairoImage& CairoImage::operator=(const CairoImage& image) noexcept | |||||
{ | |||||
cairo_surface_t* newsurface = cairo_surface_reference(image.surface); | |||||
cairo_surface_destroy(surface); | |||||
surface = newsurface; | |||||
rawData = image.rawData; | |||||
size = image.size; | |||||
format = image.format; | |||||
surfacedata = image.surfacedata; | |||||
datarefcount = image.datarefcount; | |||||
if (datarefcount != nullptr) | |||||
++(*datarefcount); | |||||
return *this; | |||||
} | } | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -318,21 +467,47 @@ template class ImageBaseAboutWindow<CairoImage>; | |||||
void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) | void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) | ||||
{ | { | ||||
/* | |||||
if ((skipDisplay && ! renderingSubWidget) || size.isInvalid() || ! visible) | |||||
return; | |||||
*/ | |||||
cairo_t* const handle = static_cast<const CairoGraphicsContext&>(self->getGraphicsContext()).handle; | |||||
bool needsResetClip = false; | |||||
cairo_t* cr = static_cast<const CairoGraphicsContext&>(self->getGraphicsContext()).handle; | |||||
cairo_matrix_t matrix; | cairo_matrix_t matrix; | ||||
cairo_get_matrix(cr, &matrix); | |||||
cairo_translate(cr, absolutePos.getX(), absolutePos.getY()); | |||||
// TODO: autoScaling and cropping | |||||
cairo_get_matrix(handle, &matrix); | |||||
if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size<uint>(width, height))) | |||||
{ | |||||
// full viewport size | |||||
cairo_translate(handle, 0, 0); | |||||
} | |||||
else if (needsViewportScaling) | |||||
{ | |||||
// limit viewport to widget bounds | |||||
// NOTE only used for nanovg for now, which is not relevant here | |||||
cairo_translate(handle, 0, 0); | |||||
} | |||||
else | |||||
{ | |||||
// set viewport pos | |||||
cairo_translate(handle, absolutePos.getX(), absolutePos.getY()); | |||||
// then cut the outer bounds | |||||
cairo_rectangle(handle, | |||||
0, | |||||
0, | |||||
std::round(self->getWidth() * autoScaleFactor), | |||||
std::round(self->getHeight() * autoScaleFactor)); | |||||
cairo_clip(handle); | |||||
needsResetClip = true; | |||||
} | |||||
// display widget | // display widget | ||||
self->onDisplay(); | self->onDisplay(); | ||||
cairo_set_matrix(cr, &matrix); | |||||
if (needsResetClip) | |||||
cairo_reset_clip(handle); | |||||
cairo_set_matrix(handle, &matrix); | |||||
selfw->pData->displaySubWidgets(width, height, autoScaleFactor); | selfw->pData->displaySubWidgets(width, height, autoScaleFactor); | ||||
} | } | ||||
@@ -347,6 +522,7 @@ void TopLevelWidget::PrivateData::display() | |||||
const double autoScaleFactor = window.pData->autoScaleFactor; | const double autoScaleFactor = window.pData->autoScaleFactor; | ||||
// FIXME anything needed here? | |||||
#if 0 | #if 0 | ||||
// full viewport size | // full viewport size | ||||
if (window.pData->autoScaling) | if (window.pData->autoScaling) | ||||
@@ -24,7 +24,7 @@ START_NAMESPACE_DGL | |||||
ImageBase::ImageBase() | ImageBase::ImageBase() | ||||
: rawData(nullptr), | : rawData(nullptr), | ||||
size(0, 0), | size(0, 0), | ||||
format(kImageFormatBGRA) {} | |||||
format(kImageFormatNull) {} | |||||
ImageBase::ImageBase(const char* const rdata, const uint width, const uint height, const ImageFormat fmt) | ImageBase::ImageBase(const char* const rdata, const uint width, const uint height, const ImageFormat fmt) | ||||
: rawData(rdata), | : rawData(rdata), | ||||
@@ -264,11 +264,14 @@ template class Rectangle<short>; | |||||
template class Rectangle<ushort>; | template class Rectangle<ushort>; | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// OpenGLImage | |||||
static GLenum asOpenGLImageFormat(const ImageFormat format) | static GLenum asOpenGLImageFormat(const ImageFormat format) | ||||
{ | { | ||||
switch (format) | switch (format) | ||||
{ | { | ||||
case kImageFormatNull: | |||||
break; | |||||
case kImageFormatBGR: | case kImageFormatBGR: | ||||
return GL_BGR; | return GL_BGR; | ||||
case kImageFormatBGRA: | case kImageFormatBGRA: | ||||
@@ -279,7 +282,7 @@ static GLenum asOpenGLImageFormat(const ImageFormat format) | |||||
return GL_RGBA; | return GL_RGBA; | ||||
} | } | ||||
return GL_BGRA; | |||||
return 0x0; | |||||
} | } | ||||
static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId) | static void setupOpenGLImage(const OpenGLImage& image, GLuint textureId) | ||||
@@ -353,8 +356,8 @@ OpenGLImage::~OpenGLImage() | |||||
void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept | void OpenGLImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept | ||||
{ | { | ||||
ImageBase::loadFromMemory(rdata, s, fmt); | |||||
setupCalled = false; | setupCalled = false; | ||||
ImageBase::loadFromMemory(rdata, s, fmt); | |||||
} | } | ||||
void OpenGLImage::drawAt(const GraphicsContext&, const Point<int>& pos) | void OpenGLImage::drawAt(const GraphicsContext&, const Point<int>& pos) | ||||
@@ -456,7 +459,7 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
// only set viewport pos | |||||
// set viewport pos | |||||
glViewport(absolutePos.getX() * autoScaleFactor, | glViewport(absolutePos.getX() * autoScaleFactor, | ||||
-std::round((height * autoScaleFactor - height) + (absolutePos.getY() * autoScaleFactor)), | -std::round((height * autoScaleFactor - height) + (absolutePos.getY() * autoScaleFactor)), | ||||
std::round(width * autoScaleFactor), | std::round(width * autoScaleFactor), | ||||
@@ -476,10 +479,7 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const | |||||
self->onDisplay(); | self->onDisplay(); | ||||
if (needsDisableScissor) | if (needsDisableScissor) | ||||
{ | |||||
glDisable(GL_SCISSOR_TEST); | glDisable(GL_SCISSOR_TEST); | ||||
needsDisableScissor = false; | |||||
} | |||||
selfw->pData->displaySubWidgets(width, height, autoScaleFactor); | selfw->pData->displaySubWidgets(width, height, autoScaleFactor); | ||||
} | } | ||||
@@ -26,6 +26,7 @@ SubWidget::PrivateData::PrivateData(SubWidget* const s, Widget* const pw) | |||||
selfw((Widget*)s), | selfw((Widget*)s), | ||||
parentWidget(pw), | parentWidget(pw), | ||||
absolutePos(), | absolutePos(), | ||||
needsFullViewportForDrawing(false), | |||||
needsViewportScaling(false) | needsViewportScaling(false) | ||||
{ | { | ||||
parentWidget->pData->subWidgets.push_back(self); | parentWidget->pData->subWidgets.push_back(self); | ||||
@@ -67,17 +67,19 @@ public: | |||||
curPage(0), | curPage(0), | ||||
curHover(-1) | curHover(-1) | ||||
{ | { | ||||
#ifdef DGL_OPENGL | |||||
// for text | |||||
nvg.loadSharedResources(); | |||||
#endif | |||||
using namespace DemoArtwork; | using namespace DemoArtwork; | ||||
img1.loadFromMemory(ico1Data, ico1Width, ico1Height, kImageFormatBGR); | img1.loadFromMemory(ico1Data, ico1Width, ico1Height, kImageFormatBGR); | ||||
img2.loadFromMemory(ico2Data, ico2Width, ico2Height, kImageFormatBGR); | img2.loadFromMemory(ico2Data, ico2Width, ico2Height, kImageFormatBGR); | ||||
img3.loadFromMemory(ico3Data, ico3Width, ico2Height, kImageFormatBGR); | img3.loadFromMemory(ico3Data, ico3Width, ico2Height, kImageFormatBGR); | ||||
img4.loadFromMemory(ico4Data, ico4Width, ico4Height, kImageFormatBGR); | img4.loadFromMemory(ico4Data, ico4Width, ico4Height, kImageFormatBGR); | ||||
#ifdef DGL_OPENGL | |||||
img5.loadFromMemory(ico5Data, ico5Width, ico5Height, kImageFormatBGR); | img5.loadFromMemory(ico5Data, ico5Width, ico5Height, kImageFormatBGR); | ||||
// for text | |||||
nvg.loadSharedResources(); | |||||
#endif | |||||
} | } | ||||
protected: | protected: | ||||
@@ -120,9 +122,10 @@ protected: | |||||
img2.drawAt(context, pad, pad + 3 + iconSize); | img2.drawAt(context, pad, pad + 3 + iconSize); | ||||
img3.drawAt(context, pad, pad + 6 + iconSize*2); | img3.drawAt(context, pad, pad + 6 + iconSize*2); | ||||
img4.drawAt(context, pad, pad + 9 + iconSize*3); | img4.drawAt(context, pad, pad + 9 + iconSize*3); | ||||
img5.drawAt(context, pad, pad + 12 + iconSize*4); | |||||
#ifdef DGL_OPENGL | #ifdef DGL_OPENGL | ||||
img5.drawAt(context, pad, pad + 12 + iconSize*4); | |||||
// draw some text | // draw some text | ||||
nvg.beginFrame(this); | nvg.beginFrame(this); | ||||
@@ -267,10 +270,6 @@ public: | |||||
#endif | #endif | ||||
wLeft.setAbsolutePos(2, 2); | wLeft.setAbsolutePos(2, 2); | ||||
setResizable(true); | |||||
setSize(600, 500); | |||||
setTitle("DGL Demo"); | |||||
curPageChanged(0); | curPageChanged(0); | ||||
} | } | ||||
@@ -350,6 +349,7 @@ template <class ExampleWidgetStandaloneWindow> | |||||
void createAndShowExampleWidgetStandaloneWindow(Application& app) | void createAndShowExampleWidgetStandaloneWindow(Application& app) | ||||
{ | { | ||||
ExampleWidgetStandaloneWindow swin(app); | ExampleWidgetStandaloneWindow swin(app); | ||||
swin.setResizable(true); | |||||
swin.setSize(600, 500); | swin.setSize(600, 500); | ||||
swin.setTitle(ExampleWidgetStandaloneWindow::kExampleWidgetName); | swin.setTitle(ExampleWidgetStandaloneWindow::kExampleWidgetName); | ||||
swin.show(); | swin.show(); | ||||
@@ -89,7 +89,7 @@ protected: | |||||
else | else | ||||
Color(0.3f, 0.5f, 0.8f).setFor(context); | Color(0.3f, 0.5f, 0.8f).setFor(context); | ||||
r.draw(); | |||||
r.draw(context); | |||||
// 2nd | // 2nd | ||||
r.setY(3 + height/3); | r.setY(3 + height/3); | ||||
@@ -99,7 +99,7 @@ protected: | |||||
else | else | ||||
Color(0.3f, 0.5f, 0.8f).setFor(context); | Color(0.3f, 0.5f, 0.8f).setFor(context); | ||||
r.draw(); | |||||
r.draw(context); | |||||
// 3rd | // 3rd | ||||
r.setY(3 + height*2/3); | r.setY(3 + height*2/3); | ||||
@@ -109,7 +109,7 @@ protected: | |||||
else | else | ||||
Color(0.3f, 0.5f, 0.8f).setFor(context); | Color(0.3f, 0.5f, 0.8f).setFor(context); | ||||
r.draw(); | |||||
r.draw(context); | |||||
} | } | ||||
} | } | ||||