Browse Source

Implement core cairo support (shapes and images)

pull/272/head
falkTX 4 years ago
parent
commit
6e141323c8
9 changed files with 241 additions and 40 deletions
  1. +20
    -0
      dgl/Cairo.hpp
  2. +1
    -0
      dgl/ImageBase.hpp
  3. +3
    -0
      dgl/StandaloneWindow.hpp
  4. +197
    -21
      dgl/src/Cairo.cpp
  5. +1
    -1
      dgl/src/ImageBase.cpp
  6. +6
    -6
      dgl/src/OpenGL.cpp
  7. +1
    -0
      dgl/src/SubWidgetPrivateData.cpp
  8. +9
    -9
      tests/Demo.cpp
  9. +3
    -3
      tests/widgets/ExampleRectanglesWidget.hpp

+ 20
- 0
dgl/Cairo.hpp View File

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


// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------


+ 1
- 0
dgl/ImageBase.hpp View File

@@ -24,6 +24,7 @@ START_NAMESPACE_DGL
// -------------------------------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------------------------------


enum ImageFormat { enum ImageFormat {
kImageFormatNull,
kImageFormatBGR, kImageFormatBGR,
kImageFormatBGRA, kImageFormatBGRA,
kImageFormatRGB, kImageFormatRGB,


+ 3
- 0
dgl/StandaloneWindow.hpp View File

@@ -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)
}; };


+ 197
- 21
dgl/src/Cairo.cpp View File

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


+ 1
- 1
dgl/src/ImageBase.cpp View File

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


+ 6
- 6
dgl/src/OpenGL.cpp View File

@@ -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);
} }


+ 1
- 0
dgl/src/SubWidgetPrivateData.cpp View File

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


+ 9
- 9
tests/Demo.cpp View File

@@ -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();


+ 3
- 3
tests/widgets/ExampleRectanglesWidget.hpp View File

@@ -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);
} }
} }




Loading…
Cancel
Save