@@ -72,18 +72,34 @@ public: | |||
~CairoImage() override; | |||
/** | |||
Load image data from memory. | |||
Load raw 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; | |||
/** | |||
Load PNG image from memory. | |||
Image size is read from PNG contents. | |||
@note @a pngData must remain valid for the lifetime of this Image. | |||
*/ | |||
void loadFromPNG(const char* pngData, uint dataSize) noexcept; | |||
/** | |||
Draw this image at position @a pos using the graphics context @a context. | |||
*/ | |||
void drawAt(const GraphicsContext& context, const Point<int>& pos) override; | |||
/** | |||
Get the cairo surface currently associated with this image. | |||
FIXME might be removed | |||
*/ | |||
inline cairo_surface_t* getSurface() const noexcept | |||
{ | |||
return surface; | |||
} | |||
/** | |||
TODO document this. | |||
*/ | |||
@@ -1,6 +1,7 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru> | |||
* | |||
* 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 | |||
@@ -275,7 +276,7 @@ template class Rectangle<ushort>; | |||
// ----------------------------------------------------------------------- | |||
// CairoImage | |||
static cairo_format_t asCairoImageFormat(const ImageFormat format) | |||
static cairo_format_t asCairoImageFormat(const ImageFormat format) noexcept | |||
{ | |||
switch (format) | |||
{ | |||
@@ -294,6 +295,31 @@ static cairo_format_t asCairoImageFormat(const ImageFormat format) | |||
return CAIRO_FORMAT_INVALID; | |||
} | |||
/* | |||
static ImageFormat asCairoImageFormat(const cairo_format_t format) noexcept | |||
{ | |||
switch (format) | |||
{ | |||
case CAIRO_FORMAT_INVALID: | |||
break; | |||
case CAIRO_FORMAT_ARGB32: | |||
break; | |||
case CAIRO_FORMAT_RGB24: | |||
break; | |||
case CAIRO_FORMAT_A8: | |||
break; | |||
case CAIRO_FORMAT_A1: | |||
break; | |||
case CAIRO_FORMAT_RGB16_565: | |||
break; | |||
case CAIRO_FORMAT_RGB30: | |||
break; | |||
} | |||
return kImageFormatNull; | |||
} | |||
*/ | |||
CairoImage::CairoImage() | |||
: ImageBase(), | |||
surface(nullptr), | |||
@@ -403,6 +429,51 @@ void CairoImage::loadFromMemory(const char* const rdata, const Size<uint>& s, co | |||
ImageBase::loadFromMemory(rdata, s, fmt); | |||
} | |||
// const GraphicsContext& context | |||
void CairoImage::loadFromPNG(const char* const pngData, const uint pngSize) noexcept | |||
{ | |||
struct PngReaderData | |||
{ | |||
const char* dataPtr; | |||
uint sizeLeft; | |||
static cairo_status_t read(void* const closure, uchar* const data, const uint length) noexcept | |||
{ | |||
PngReaderData& readerData = *reinterpret_cast<PngReaderData*>(closure); | |||
if (readerData.sizeLeft < length) | |||
return CAIRO_STATUS_READ_ERROR; | |||
std::memcpy(data, readerData.dataPtr, length); | |||
readerData.dataPtr += length; | |||
readerData.sizeLeft -= length; | |||
return CAIRO_STATUS_SUCCESS; | |||
} | |||
}; | |||
PngReaderData readerData; | |||
readerData.dataPtr = pngData; | |||
readerData.sizeLeft = pngSize; | |||
cairo_surface_t* const newsurface = cairo_image_surface_create_from_png_stream(PngReaderData::read, &readerData); | |||
DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,); | |||
cairo_surface_destroy(surface); | |||
if (datarefcount != nullptr && --(*datarefcount) == 0) | |||
std::free(surfacedata); | |||
else | |||
datarefcount = (int*)malloc(sizeof(*datarefcount)); | |||
surface = newsurface; | |||
surfacedata = nullptr; // cairo_image_surface_get_data(newsurface); | |||
*datarefcount = 1; | |||
rawData = nullptr; | |||
format = kImageFormatNull; // asCairoImageFormat(cairo_image_surface_get_format(newsurface)); | |||
size = Size<uint>(cairo_image_surface_get_width(newsurface), cairo_image_surface_get_height(newsurface)); | |||
} | |||
void CairoImage::drawAt(const GraphicsContext& context, const Point<int>& pos) | |||
{ | |||
if (surface == nullptr) | |||
@@ -418,14 +489,23 @@ CairoImage& CairoImage::operator=(const CairoImage& image) noexcept | |||
{ | |||
cairo_surface_t* newsurface = cairo_surface_reference(image.surface); | |||
cairo_surface_destroy(surface); | |||
if (datarefcount != nullptr && --(*datarefcount) == 0) | |||
{ | |||
std::free(surfacedata); | |||
std::free(datarefcount); | |||
} | |||
surface = newsurface; | |||
rawData = image.rawData; | |||
size = image.size; | |||
format = image.format; | |||
surfacedata = image.surfacedata; | |||
datarefcount = image.datarefcount; | |||
if (datarefcount != nullptr) | |||
++(*datarefcount); | |||
return *this; | |||
} | |||
@@ -484,19 +564,114 @@ template class ImageBaseButton<CairoImage>; | |||
template <> | |||
void ImageBaseKnob<CairoImage>::PrivateData::init() | |||
{ | |||
notImplemented("ImageBaseKnob::PrivateData::init"); | |||
// new (&cairoDisplayImage) CairoImage(); | |||
cairoSurface = nullptr; | |||
} | |||
template <> | |||
void ImageBaseKnob<CairoImage>::PrivateData::cleanup() | |||
{ | |||
notImplemented("ImageBaseKnob::PrivateData::cleanup"); | |||
// cairoDisplayImage.~CairoImage(); | |||
cairo_surface_destroy((cairo_surface_t*)cairoSurface); | |||
cairoSurface = nullptr; | |||
} | |||
/** | |||
Get the pixel size in bytes. | |||
@return pixel size, or 0 if the format is unknown, or pixels are not aligned to bytes. | |||
*/ | |||
static uint getBytesPerPixel(cairo_format_t format) noexcept | |||
{ | |||
switch (format) | |||
{ | |||
case CAIRO_FORMAT_ARGB32: | |||
case CAIRO_FORMAT_RGB24: | |||
case CAIRO_FORMAT_RGB30: | |||
return 4; | |||
case CAIRO_FORMAT_RGB16_565: | |||
return 2; | |||
case CAIRO_FORMAT_A8: | |||
return 1; | |||
case CAIRO_FORMAT_A1: | |||
return 0; | |||
default: | |||
DISTRHO_SAFE_ASSERT(false); | |||
return 0; | |||
} | |||
} | |||
static cairo_surface_t* getRegion(cairo_surface_t* origsurface, uint x, uint y, uint width, uint height) noexcept | |||
{ | |||
const cairo_format_t format = cairo_image_surface_get_format(origsurface); | |||
const uint bpp = getBytesPerPixel(format); | |||
if (bpp == 0) | |||
return nullptr; | |||
const uint fullWidth = cairo_image_surface_get_width(origsurface); | |||
const uint fullHeight = cairo_image_surface_get_height(origsurface); | |||
const uint stride = cairo_image_surface_get_stride(origsurface); | |||
uchar* const fullData = cairo_image_surface_get_data(origsurface); | |||
x = (x < fullWidth) ? x : fullWidth; | |||
y = (y < fullHeight) ? y : fullHeight; | |||
width = (x + width < fullWidth) ? width : (fullWidth - x); | |||
height = (x + height < fullHeight) ? height : (fullHeight - x); | |||
uchar* const data = fullData + x * bpp + y * stride; | |||
return cairo_image_surface_create_for_data(data, format, width, height, stride); | |||
} | |||
template <> | |||
void ImageBaseKnob<CairoImage>::onDisplay() | |||
{ | |||
notImplemented("ImageBaseKnob::onDisplay"); | |||
const GraphicsContext& context(getGraphicsContext()); | |||
cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |||
const float normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum) | |||
/ (pData->maximum - pData->minimum); | |||
cairo_surface_t* surface = (cairo_surface_t*)pData->cairoSurface; | |||
if (! pData->isReady) | |||
{ | |||
const uint layerW = pData->imgLayerWidth; | |||
const uint layerH = pData->imgLayerHeight; | |||
uint layerNum = 0; | |||
if (pData->rotationAngle == 0) | |||
layerNum = uint(normValue * float(pData->imgLayerCount-1)); | |||
const uint layerX = pData->isImgVertical ? 0 : layerNum * layerW; | |||
const uint layerY = !pData->isImgVertical ? 0 : layerNum * layerH; | |||
cairo_surface_t* const newsurface = getRegion(pData->image.getSurface(), layerX, layerY, layerW, layerH); | |||
DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,); | |||
if (pData->rotationAngle != 0) | |||
{ | |||
// TODO | |||
/* | |||
CairoImage rotated(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, layerW, layerH), false); | |||
cairo_t* cr = cairo_create(rotated.getSurface()); | |||
cairo_translate(cr, 0.5 * layerW, 0.5 * layerH); | |||
cairo_rotate(cr, normValue * angle * (float)(M_PI / 180)); | |||
cairo_set_source_surface(cr, displayImage.getSurface(), -0.5f * layerW, -0.5f * layerH); | |||
cairo_paint(cr); | |||
cairo_destroy(cr); | |||
pData->cairoDisplayImage = rotated; | |||
*/ | |||
} | |||
cairo_surface_destroy(surface); | |||
pData->cairoSurface = surface = newsurface; | |||
pData->isReady = true; | |||
} | |||
if (surface != nullptr) | |||
{ | |||
cairo_set_source_surface(handle, surface, 0, 0); | |||
cairo_paint(handle); | |||
} | |||
} | |||
template class ImageBaseKnob<CairoImage>; | |||
@@ -145,7 +145,12 @@ struct ImageBaseKnob<ImageType>::PrivateData { | |||
uint imgLayerHeight; | |||
uint imgLayerCount; | |||
bool isReady; | |||
/*GL*/uint textureId; | |||
union { | |||
uint glTextureId; | |||
// ImageType cairoDisplayImage; | |||
void* cairoSurface; | |||
}; | |||
explicit PrivateData(const ImageType& img, const Orientation o); | |||
explicit PrivateData(PrivateData* const other); | |||
@@ -208,8 +208,7 @@ ImageBaseKnob<ImageType>::PrivateData::PrivateData(const ImageType& img, const O | |||
imgLayerWidth(isImgVertical ? img.getWidth() : img.getHeight()), | |||
imgLayerHeight(imgLayerWidth), | |||
imgLayerCount(isImgVertical ? img.getHeight()/imgLayerHeight : img.getWidth()/imgLayerWidth), | |||
isReady(false), | |||
textureId(0) | |||
isReady(false) | |||
{ | |||
init(); | |||
} | |||
@@ -235,8 +234,7 @@ ImageBaseKnob<ImageType>::PrivateData::PrivateData(PrivateData* const other) | |||
imgLayerWidth(other->imgLayerWidth), | |||
imgLayerHeight(other->imgLayerHeight), | |||
imgLayerCount(other->imgLayerCount), | |||
isReady(false), | |||
textureId(0) | |||
isReady(false) | |||
{ | |||
init(); | |||
} | |||
@@ -460,27 +460,29 @@ template class ImageBaseButton<OpenGLImage>; | |||
template <> | |||
void ImageBaseKnob<OpenGLImage>::PrivateData::init() | |||
{ | |||
glGenTextures(1, &textureId); | |||
glTextureId = 0; | |||
glGenTextures(1, &glTextureId); | |||
} | |||
template <> | |||
void ImageBaseKnob<OpenGLImage>::PrivateData::cleanup() | |||
{ | |||
if (textureId != 0) | |||
{ | |||
glDeleteTextures(1, &textureId); | |||
textureId = 0; | |||
} | |||
if (glTextureId == 0) | |||
return; | |||
glDeleteTextures(1, &glTextureId); | |||
glTextureId = 0; | |||
} | |||
template <> | |||
void ImageBaseKnob<OpenGLImage>::onDisplay() | |||
{ | |||
const GraphicsContext& context(getGraphicsContext()); | |||
const float normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum) / (pData->maximum - pData->minimum); | |||
const float normValue = ((pData->usingLog ? pData->invlogscale(pData->value) : pData->value) - pData->minimum) | |||
/ (pData->maximum - pData->minimum); | |||
glEnable(GL_TEXTURE_2D); | |||
glBindTexture(GL_TEXTURE_2D, pData->textureId); | |||
glBindTexture(GL_TEXTURE_2D, pData->glTextureId); | |||
if (! pData->isReady) | |||
{ | |||
@@ -505,6 +507,7 @@ void ImageBaseKnob<OpenGLImage>::onDisplay() | |||
const uint& v1(pData->isImgVertical ? pData->imgLayerWidth : pData->imgLayerHeight); | |||
const uint& v2(pData->isImgVertical ? pData->imgLayerHeight : pData->imgLayerWidth); | |||
// TODO kImageFormatGreyscale | |||
const uint layerDataSize = v1 * v2 * ((pData->image.getFormat() == kImageFormatBGRA || | |||
pData->image.getFormat() == kImageFormatRGBA) ? 4 : 3); | |||
/* */ imageDataOffset = layerDataSize * uint(normValue * float(pData->imgLayerCount-1)); | |||
@@ -0,0 +1,19 @@ | |||
/* (Auto-generated binary data file). */ | |||
#ifndef BINARY_ARTWORK_HPP | |||
#define BINARY_ARTWORK_HPP | |||
namespace Artwork | |||
{ | |||
extern const char* buttonOffData; | |||
const unsigned int buttonOffDataSize = 970; | |||
extern const char* buttonOnData; | |||
const unsigned int buttonOnDataSize = 983; | |||
extern const char* knobData; | |||
const unsigned int knobDataSize = 52344; | |||
} | |||
#endif // BINARY_ARTWORK_HPP | |||
@@ -1,6 +1,7 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru> | |||
* | |||
* 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 | |||
@@ -1,6 +1,7 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru> | |||
* | |||
* 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 | |||
@@ -15,6 +16,8 @@ | |||
*/ | |||
#include "DistrhoUI.hpp" | |||
#include "Artwork.hpp" | |||
#include "DemoWidgetBanner.hpp" | |||
#include "DemoWidgetClickable.hpp" | |||
@@ -35,17 +38,29 @@ public: | |||
fWidgetBanner = widgetBanner; | |||
widgetBanner->setSize(180, 80); | |||
widgetBanner->setAbsolutePos(10, 10); | |||
} | |||
~CairoExampleUI() | |||
{ | |||
CairoImage knobSkin; | |||
knobSkin.loadFromPNG(Artwork::knobData, Artwork::knobDataSize); | |||
CairoImageKnob* knob = new CairoImageKnob(this, knobSkin); | |||
fKnob = knob; | |||
knob->setSize(80, 80); | |||
knob->setAbsolutePos(10, 100); | |||
CairoImage buttonOn, buttonOff; | |||
buttonOn.loadFromPNG(Artwork::buttonOnData, Artwork::buttonOnDataSize); | |||
buttonOff.loadFromPNG(Artwork::buttonOffData, Artwork::buttonOffDataSize); | |||
CairoImageButton* button = new CairoImageButton(this, buttonOff, buttonOn); | |||
fButton = button; | |||
button->setSize(60, 35); | |||
button->setAbsolutePos(100, 160); | |||
} | |||
protected: | |||
void onCairoDisplay(const CairoGraphicsContext& context) | |||
{ | |||
cairo_t* cr = context.handle; | |||
cairo_t* const cr = context.handle; | |||
cairo_set_source_rgb(cr, 1.0, 0.8, 0.5); | |||
cairo_paint(cr); | |||
} | |||
@@ -60,6 +75,8 @@ protected: | |||
private: | |||
ScopedPointer<DemoWidgetClickable> fWidgetClickable; | |||
ScopedPointer<DemoWidgetBanner> fWidgetBanner; | |||
ScopedPointer<CairoImageKnob> fKnob; | |||
ScopedPointer<CairoImageButton> fButton; | |||
}; | |||
UI* createUI() | |||
@@ -1,6 +1,7 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru> | |||
* | |||
* 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 | |||
@@ -1,6 +1,7 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru> | |||
* | |||
* 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 | |||
@@ -1,6 +1,7 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru> | |||
* | |||
* 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 | |||
@@ -1,6 +1,7 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru> | |||
* | |||
* 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 | |||
@@ -16,6 +16,7 @@ FILES_DSP = \ | |||
CairoExamplePlugin.cpp | |||
FILES_UI = \ | |||
Artwork.cpp \ | |||
DemoWidgetBanner.cpp \ | |||
DemoWidgetClickable.cpp \ | |||
CairoExampleUI.cpp | |||