commit 6e141323c833cbd4aa024c20a8c65f2ff207324b
parent ba4b903cbf5c5424716236333914666725d96b6f
Author: falkTX <falktx@falktx.com>
Date: Mon, 17 May 2021 04:52:10 +0100
Implement core cairo support (shapes and images)
Diffstat:
9 files changed, 241 insertions(+), 40 deletions(-)
diff --git a/dgl/Cairo.hpp b/dgl/Cairo.hpp
@@ -73,13 +73,33 @@ public:
~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.
*/
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
+ 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)
{ drawAt(context, Point<int>(x, y)); };
+
+private:
+ cairo_surface_t* surface;
+ uchar* surfacedata;
+ int* datarefcount;
};
// --------------------------------------------------------------------------------------------------------------------
diff --git a/dgl/ImageBase.hpp b/dgl/ImageBase.hpp
@@ -24,6 +24,7 @@ START_NAMESPACE_DGL
// --------------------------------------------------------------------------------------------------------------------
enum ImageFormat {
+ kImageFormatNull,
kImageFormatBGR,
kImageFormatBGRA,
kImageFormatRGB,
diff --git a/dgl/StandaloneWindow.hpp b/dgl/StandaloneWindow.hpp
@@ -61,6 +61,9 @@ public:
{ return Window::addIdleCallback(callback, timerFrequencyInMs); }
bool removeIdleCallback(IdleCallback* callback) { return Window::removeIdleCallback(callback); }
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)
};
diff --git a/dgl/src/Cairo.cpp b/dgl/src/Cairo.cpp
@@ -92,20 +92,28 @@ static void drawCircle(cairo_t* const handle,
const T origy = pos.getY();
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;
x = cos * x - sin * 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>
@@ -158,7 +166,15 @@ static void drawTriangle(cairo_t* const handle,
{
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>
@@ -206,7 +222,12 @@ template class Triangle<ushort>;
template<typename T>
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>
@@ -253,24 +274,152 @@ template class Rectangle<ushort>;
// -----------------------------------------------------------------------
// 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()
- : ImageBase() {}
+ : ImageBase(),
+ surface(nullptr),
+ surfacedata(nullptr),
+ datarefcount(nullptr) {}
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)
- : ImageBase(rawData, size, format) {}
+ : ImageBase(rawData, size, format),
+ surface(nullptr),
+ surfacedata(nullptr),
+ datarefcount(nullptr)
+{
+ loadFromMemory(rawData, size, format);
+}
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()
{
+ 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)
{
- /*
- 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_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
self->onDisplay();
- cairo_set_matrix(cr, &matrix);
+ if (needsResetClip)
+ cairo_reset_clip(handle);
+
+ cairo_set_matrix(handle, &matrix);
selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}
@@ -347,6 +522,7 @@ void TopLevelWidget::PrivateData::display()
const double autoScaleFactor = window.pData->autoScaleFactor;
+ // FIXME anything needed here?
#if 0
// full viewport size
if (window.pData->autoScaling)
diff --git a/dgl/src/ImageBase.cpp b/dgl/src/ImageBase.cpp
@@ -24,7 +24,7 @@ START_NAMESPACE_DGL
ImageBase::ImageBase()
: rawData(nullptr),
size(0, 0),
- format(kImageFormatBGRA) {}
+ format(kImageFormatNull) {}
ImageBase::ImageBase(const char* const rdata, const uint width, const uint height, const ImageFormat fmt)
: rawData(rdata),
diff --git a/dgl/src/OpenGL.cpp b/dgl/src/OpenGL.cpp
@@ -264,11 +264,14 @@ template class Rectangle<short>;
template class Rectangle<ushort>;
// -----------------------------------------------------------------------
+// OpenGLImage
static GLenum asOpenGLImageFormat(const ImageFormat format)
{
switch (format)
{
+ case kImageFormatNull:
+ break;
case kImageFormatBGR:
return GL_BGR;
case kImageFormatBGRA:
@@ -279,7 +282,7 @@ static GLenum asOpenGLImageFormat(const ImageFormat format)
return GL_RGBA;
}
- return GL_BGRA;
+ return 0x0;
}
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
{
- ImageBase::loadFromMemory(rdata, s, fmt);
setupCalled = false;
+ ImageBase::loadFromMemory(rdata, s, fmt);
}
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
{
- // only set viewport pos
+ // set viewport pos
glViewport(absolutePos.getX() * autoScaleFactor,
-std::round((height * autoScaleFactor - height) + (absolutePos.getY() * autoScaleFactor)),
std::round(width * autoScaleFactor),
@@ -476,10 +479,7 @@ void SubWidget::PrivateData::display(const uint width, const uint height, const
self->onDisplay();
if (needsDisableScissor)
- {
glDisable(GL_SCISSOR_TEST);
- needsDisableScissor = false;
- }
selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
}
diff --git a/dgl/src/SubWidgetPrivateData.cpp b/dgl/src/SubWidgetPrivateData.cpp
@@ -26,6 +26,7 @@ SubWidget::PrivateData::PrivateData(SubWidget* const s, Widget* const pw)
selfw((Widget*)s),
parentWidget(pw),
absolutePos(),
+ needsFullViewportForDrawing(false),
needsViewportScaling(false)
{
parentWidget->pData->subWidgets.push_back(self);
diff --git a/tests/Demo.cpp b/tests/Demo.cpp
@@ -67,17 +67,19 @@ public:
curPage(0),
curHover(-1)
{
-#ifdef DGL_OPENGL
- // for text
- nvg.loadSharedResources();
-#endif
using namespace DemoArtwork;
img1.loadFromMemory(ico1Data, ico1Width, ico1Height, kImageFormatBGR);
img2.loadFromMemory(ico2Data, ico2Width, ico2Height, kImageFormatBGR);
img3.loadFromMemory(ico3Data, ico3Width, ico2Height, kImageFormatBGR);
img4.loadFromMemory(ico4Data, ico4Width, ico4Height, kImageFormatBGR);
+
+#ifdef DGL_OPENGL
img5.loadFromMemory(ico5Data, ico5Width, ico5Height, kImageFormatBGR);
+
+ // for text
+ nvg.loadSharedResources();
+#endif
}
protected:
@@ -120,9 +122,10 @@ protected:
img2.drawAt(context, pad, pad + 3 + iconSize);
img3.drawAt(context, pad, pad + 6 + iconSize*2);
img4.drawAt(context, pad, pad + 9 + iconSize*3);
- img5.drawAt(context, pad, pad + 12 + iconSize*4);
#ifdef DGL_OPENGL
+ img5.drawAt(context, pad, pad + 12 + iconSize*4);
+
// draw some text
nvg.beginFrame(this);
@@ -267,10 +270,6 @@ public:
#endif
wLeft.setAbsolutePos(2, 2);
- setResizable(true);
- setSize(600, 500);
- setTitle("DGL Demo");
-
curPageChanged(0);
}
@@ -350,6 +349,7 @@ template <class ExampleWidgetStandaloneWindow>
void createAndShowExampleWidgetStandaloneWindow(Application& app)
{
ExampleWidgetStandaloneWindow swin(app);
+ swin.setResizable(true);
swin.setSize(600, 500);
swin.setTitle(ExampleWidgetStandaloneWindow::kExampleWidgetName);
swin.show();
diff --git a/tests/widgets/ExampleRectanglesWidget.hpp b/tests/widgets/ExampleRectanglesWidget.hpp
@@ -89,7 +89,7 @@ protected:
else
Color(0.3f, 0.5f, 0.8f).setFor(context);
- r.draw();
+ r.draw(context);
// 2nd
r.setY(3 + height/3);
@@ -99,7 +99,7 @@ protected:
else
Color(0.3f, 0.5f, 0.8f).setFor(context);
- r.draw();
+ r.draw(context);
// 3rd
r.setY(3 + height*2/3);
@@ -109,7 +109,7 @@ protected:
else
Color(0.3f, 0.5f, 0.8f).setFor(context);
- r.draw();
+ r.draw(context);
}
}