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;

/**
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;
};

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


+ 1
- 0
dgl/ImageBase.hpp View File

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

enum ImageFormat {
kImageFormatNull,
kImageFormatBGR,
kImageFormatBGRA,
kImageFormatRGB,


+ 3
- 0
dgl/StandaloneWindow.hpp View File

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


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

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


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

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


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

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


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

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


+ 9
- 9
tests/Demo.cpp View File

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


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

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



Loading…
Cancel
Save