DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

Cairo.cpp (24998B)


      1 /*
      2  * DISTRHO Plugin Framework (DPF)
      3  * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com>
      4  * Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru>
      5  *
      6  * Permission to use, copy, modify, and/or distribute this software for any purpose with
      7  * or without fee is hereby granted, provided that the above copyright notice and this
      8  * permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
     11  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
     12  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
     13  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
     14  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
     15  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     16  */
     17 
     18 #ifdef _MSC_VER
     19 // instantiated template classes whose methods are defined elsewhere
     20 # pragma warning(disable:4661)
     21 #endif
     22 
     23 #include "../Cairo.hpp"
     24 #include "../Color.hpp"
     25 #include "../ImageBaseWidgets.hpp"
     26 
     27 #include "SubWidgetPrivateData.hpp"
     28 #include "TopLevelWidgetPrivateData.hpp"
     29 #include "WidgetPrivateData.hpp"
     30 #include "WindowPrivateData.hpp"
     31 
     32 // templated classes
     33 #include "ImageBaseWidgets.cpp"
     34 
     35 START_NAMESPACE_DGL
     36 
     37 // -----------------------------------------------------------------------
     38 
     39 static void notImplemented(const char* const name)
     40 {
     41     d_stderr2("cairo function not implemented: %s", name);
     42 }
     43 
     44 // -----------------------------------------------------------------------
     45 // Color
     46 
     47 void Color::setFor(const GraphicsContext& context, const bool includeAlpha)
     48 {
     49     cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
     50 
     51     if (includeAlpha)
     52         cairo_set_source_rgba(handle, red, green, blue, alpha);
     53     else
     54         cairo_set_source_rgb(handle, red, green, blue);
     55 }
     56 
     57 // -----------------------------------------------------------------------
     58 // Line
     59 
     60 template<typename T>
     61 void Line<T>::draw(const GraphicsContext& context, const T width)
     62 {
     63     DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,);
     64     DISTRHO_SAFE_ASSERT_RETURN(width != 0,);
     65 
     66     cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
     67 
     68     cairo_set_line_width(handle, width);
     69     cairo_move_to(handle, posStart.getX(), posStart.getY());
     70     cairo_line_to(handle, posEnd.getX(), posEnd.getY());
     71     cairo_stroke(handle);
     72 }
     73 
     74 template<typename T>
     75 void Line<T>::draw()
     76 {
     77     notImplemented("Line::draw");
     78 }
     79 
     80 template class Line<double>;
     81 template class Line<float>;
     82 template class Line<int>;
     83 template class Line<uint>;
     84 template class Line<short>;
     85 template class Line<ushort>;
     86 
     87 // -----------------------------------------------------------------------
     88 // Circle
     89 
     90 template<typename T>
     91 static void drawCircle(cairo_t* const handle,
     92                        const Point<T>& pos,
     93                        const uint numSegments,
     94                        const float size,
     95                        const float sin,
     96                        const float cos,
     97                        const bool outline)
     98 {
     99     DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,);
    100 
    101     const T origx = pos.getX();
    102     const T origy = pos.getY();
    103     double t, x = size, y = 0.0;
    104 
    105     // TODO use arc
    106     /*
    107     cairo_arc(handle, origx, origy, size, sin, cos);
    108     */
    109 
    110     cairo_move_to(handle, x + origx, y + origy);
    111 
    112     for (uint i=1; i<numSegments; ++i)
    113     {
    114         cairo_line_to(handle, x + origx, y + origy);
    115 
    116         t = x;
    117         x = cos * x - sin * y;
    118         y = sin * t + cos * y;
    119     }
    120 
    121     cairo_line_to(handle, x + origx, y + origy);
    122 
    123     if (outline)
    124         cairo_stroke(handle);
    125     else
    126         cairo_fill(handle);
    127 }
    128 
    129 template<typename T>
    130 void Circle<T>::draw(const GraphicsContext& context)
    131 {
    132     cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
    133 
    134     drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, false);
    135 }
    136 
    137 template<typename T>
    138 void Circle<T>::drawOutline(const GraphicsContext& context, const T lineWidth)
    139 {
    140     DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);
    141 
    142     cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
    143 
    144     cairo_set_line_width(handle, lineWidth);
    145     drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, true);
    146 }
    147 
    148 template<typename T>
    149 void Circle<T>::draw()
    150 {
    151     notImplemented("Circle::draw");
    152 }
    153 
    154 template<typename T>
    155 void Circle<T>::drawOutline()
    156 {
    157     notImplemented("Circle::drawOutline");
    158 }
    159 
    160 template class Circle<double>;
    161 template class Circle<float>;
    162 template class Circle<int>;
    163 template class Circle<uint>;
    164 template class Circle<short>;
    165 template class Circle<ushort>;
    166 
    167 // -----------------------------------------------------------------------
    168 // Triangle
    169 
    170 template<typename T>
    171 static void drawTriangle(cairo_t* const handle,
    172                          const Point<T>& pos1,
    173                          const Point<T>& pos2,
    174                          const Point<T>& pos3,
    175                          const bool outline)
    176 {
    177     DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,);
    178 
    179     cairo_move_to(handle, pos1.getX(), pos1.getY());
    180     cairo_line_to(handle, pos2.getX(), pos2.getY());
    181     cairo_line_to(handle, pos3.getX(), pos3.getY());
    182     cairo_line_to(handle, pos1.getX(), pos1.getY());
    183 
    184     if (outline)
    185         cairo_stroke(handle);
    186     else
    187         cairo_fill(handle);
    188 }
    189 
    190 template<typename T>
    191 void Triangle<T>::draw(const GraphicsContext& context)
    192 {
    193     cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
    194 
    195     drawTriangle<T>(handle, pos1, pos2, pos3, false);
    196 }
    197 
    198 template<typename T>
    199 void Triangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth)
    200 {
    201     DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);
    202 
    203     cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
    204 
    205     cairo_set_line_width(handle, lineWidth);
    206     drawTriangle<T>(handle, pos1, pos2, pos3, true);
    207 }
    208 
    209 template<typename T>
    210 void Triangle<T>::draw()
    211 {
    212     notImplemented("Triangle::draw");
    213 }
    214 
    215 template<typename T>
    216 void Triangle<T>::drawOutline()
    217 {
    218     notImplemented("Triangle::drawOutline");
    219 }
    220 
    221 template class Triangle<double>;
    222 template class Triangle<float>;
    223 template class Triangle<int>;
    224 template class Triangle<uint>;
    225 template class Triangle<short>;
    226 template class Triangle<ushort>;
    227 
    228 // -----------------------------------------------------------------------
    229 // Rectangle
    230 
    231 template<typename T>
    232 static void drawRectangle(cairo_t* const handle, const Rectangle<T>& rect, const bool outline)
    233 {
    234     cairo_rectangle(handle, rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
    235 
    236     if (outline)
    237         cairo_stroke(handle);
    238     else
    239         cairo_fill(handle);
    240 }
    241 
    242 template<typename T>
    243 void Rectangle<T>::draw(const GraphicsContext& context)
    244 {
    245     DISTRHO_SAFE_ASSERT_RETURN(isValid(),);
    246 
    247     cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
    248 
    249     drawRectangle(handle, *this, false);
    250 }
    251 
    252 template<typename T>
    253 void Rectangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth)
    254 {
    255     DISTRHO_SAFE_ASSERT_RETURN(isValid(),);
    256     DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);
    257 
    258     cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
    259 
    260     cairo_set_line_width(handle, lineWidth);
    261     drawRectangle(handle, *this, true);
    262 }
    263 
    264 template<typename T>
    265 void Rectangle<T>::draw()
    266 {
    267     notImplemented("Rectangle::draw");
    268 }
    269 
    270 template<typename T>
    271 void Rectangle<T>::drawOutline()
    272 {
    273     notImplemented("Rectangle::drawOutline");
    274 }
    275 
    276 template class Rectangle<double>;
    277 template class Rectangle<float>;
    278 template class Rectangle<int>;
    279 template class Rectangle<uint>;
    280 template class Rectangle<short>;
    281 template class Rectangle<ushort>;
    282 
    283 // -----------------------------------------------------------------------
    284 // CairoImage
    285 
    286 static cairo_format_t asCairoImageFormat(const ImageFormat format) noexcept
    287 {
    288     switch (format)
    289     {
    290     case kImageFormatNull:
    291         break;
    292     case kImageFormatGrayscale:
    293     case kImageFormatBGR:
    294     case kImageFormatRGB:
    295         return CAIRO_FORMAT_RGB24;
    296     case kImageFormatBGRA:
    297     case kImageFormatRGBA:
    298         return CAIRO_FORMAT_ARGB32;
    299     }
    300 
    301     return CAIRO_FORMAT_INVALID;
    302 }
    303 
    304 /*
    305 static ImageFormat asCairoImageFormat(const cairo_format_t format) noexcept
    306 {
    307     switch (format)
    308     {
    309     case CAIRO_FORMAT_INVALID:
    310         break;
    311     case CAIRO_FORMAT_ARGB32:
    312         break;
    313     case CAIRO_FORMAT_RGB24:
    314         break;
    315     case CAIRO_FORMAT_A8:
    316         break;
    317     case CAIRO_FORMAT_A1:
    318         break;
    319     case CAIRO_FORMAT_RGB16_565:
    320         break;
    321     case CAIRO_FORMAT_RGB30:
    322         break;
    323     }
    324 
    325     return kImageFormatNull;
    326 }
    327 */
    328 
    329 CairoImage::CairoImage()
    330     : ImageBase(),
    331       surface(nullptr),
    332       surfacedata(nullptr),
    333       datarefcount(nullptr) {}
    334 
    335 CairoImage::CairoImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt)
    336     : ImageBase(rdata, w, h, fmt),
    337       surface(nullptr),
    338       surfacedata(nullptr),
    339       datarefcount(nullptr)
    340 {
    341     loadFromMemory(rdata, w, h, fmt);
    342 }
    343 
    344 CairoImage::CairoImage(const char* const rdata, const Size<uint>& s, const ImageFormat fmt)
    345     : ImageBase(rdata, s, fmt),
    346       surface(nullptr),
    347       surfacedata(nullptr),
    348       datarefcount(nullptr)
    349 {
    350     loadFromMemory(rdata, s, fmt);
    351 }
    352 
    353 CairoImage::CairoImage(const CairoImage& image)
    354     : ImageBase(image.rawData, image.size, image.format),
    355       surface(cairo_surface_reference(image.surface)),
    356       surfacedata(image.surfacedata),
    357       datarefcount(image.datarefcount)
    358 {
    359     if (datarefcount != nullptr)
    360         ++(*datarefcount);
    361 }
    362 
    363 CairoImage::~CairoImage()
    364 {
    365     cairo_surface_destroy(surface);
    366 
    367     if (datarefcount != nullptr && --(*datarefcount) == 0)
    368     {
    369         std::free(surfacedata);
    370         std::free(datarefcount);
    371     }
    372 }
    373 
    374 void CairoImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept
    375 {
    376     const cairo_format_t cairoformat = asCairoImageFormat(fmt);
    377     DISTRHO_SAFE_ASSERT_RETURN(cairoformat != CAIRO_FORMAT_INVALID,);
    378 
    379     const int width  = static_cast<int>(s.getWidth());
    380     const int height = static_cast<int>(s.getHeight());
    381     const int stride = cairo_format_stride_for_width(cairoformat, width);
    382 
    383     uchar* const newdata = static_cast<uchar*>(std::malloc(static_cast<size_t>(width * height * stride * 4)));
    384     DISTRHO_SAFE_ASSERT_RETURN(newdata != nullptr,);
    385 
    386     cairo_surface_t* const newsurface = cairo_image_surface_create_for_data(newdata, cairoformat, width, height, stride);
    387     DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,);
    388     DISTRHO_SAFE_ASSERT_RETURN(static_cast<int>(s.getWidth()) == cairo_image_surface_get_width(newsurface),);
    389     DISTRHO_SAFE_ASSERT_RETURN(static_cast<int>(s.getHeight()) == cairo_image_surface_get_height(newsurface),);
    390 
    391     cairo_surface_destroy(surface);
    392 
    393     if (datarefcount != nullptr && --(*datarefcount) == 0)
    394         std::free(surfacedata);
    395     else
    396         datarefcount = static_cast<int*>(std::malloc(sizeof(int)));
    397 
    398     surface = newsurface;
    399     surfacedata = newdata;
    400     *datarefcount = 1;
    401 
    402     const uchar* const urdata = reinterpret_cast<const uchar*>(rdata);
    403 
    404     switch (fmt)
    405     {
    406     case kImageFormatNull:
    407         break;
    408     case kImageFormatGrayscale:
    409         // Grayscale to CAIRO_FORMAT_RGB24
    410         for (int h = 0; h < height; ++h)
    411         {
    412             for (int w = 0; w < width; ++w)
    413             {
    414                 newdata[h*width*4+w*4+0] = urdata[h*width+w];
    415                 newdata[h*width*4+w*4+1] = urdata[h*width+w];
    416                 newdata[h*width*4+w*4+2] = urdata[h*width+w];
    417                 newdata[h*width*4+w*4+3] = 0;
    418             }
    419         }
    420         break;
    421     case kImageFormatBGR:
    422         // BGR8 to CAIRO_FORMAT_RGB24
    423         for (int h = 0; h < height; ++h)
    424         {
    425             for (int w = 0; w < width; ++w)
    426             {
    427                 newdata[h*width*4+w*4+0] = urdata[h*width*3+w*3+0];
    428                 newdata[h*width*4+w*4+1] = urdata[h*width*3+w*3+1];
    429                 newdata[h*width*4+w*4+2] = urdata[h*width*3+w*3+2];
    430                 newdata[h*width*4+w*4+3] = 0;
    431             }
    432         }
    433         break;
    434     case kImageFormatBGRA:
    435         // BGRA8 to CAIRO_FORMAT_ARGB32
    436         for (int h = 0; h < height; ++h)
    437         {
    438             for (int w = 0; w < width; ++w)
    439             {
    440                 const uchar a = urdata[h*width*4+w*4+3];
    441                 newdata[h*width*4+w*4+0] = static_cast<uchar>((urdata[h*width*4+w*4+0] * a) >> 8);
    442                 newdata[h*width*4+w*4+1] = static_cast<uchar>((urdata[h*width*4+w*4+1] * a) >> 8);
    443                 newdata[h*width*4+w*4+2] = static_cast<uchar>((urdata[h*width*4+w*4+2] * a) >> 8);
    444                 newdata[h*width*4+w*4+3] = a;
    445             }
    446         }
    447         break;
    448     case kImageFormatRGB:
    449         // RGB8 to CAIRO_FORMAT_RGB24
    450         for (int h = 0; h < height; ++h)
    451         {
    452             for (int w = 0; w < width; ++w)
    453             {
    454                 newdata[h*width*4+w*4+0] = urdata[h*width*3+w*3+2];
    455                 newdata[h*width*4+w*4+1] = urdata[h*width*3+w*3+1];
    456                 newdata[h*width*4+w*4+2] = urdata[h*width*3+w*3+0];
    457                 newdata[h*width*4+w*4+3] = 0;
    458             }
    459         }
    460         break;
    461     case kImageFormatRGBA:
    462         // RGBA8 to CAIRO_FORMAT_ARGB32
    463         for (int h = 0; h < height; ++h)
    464         {
    465             for (int w = 0; w < width; ++w)
    466             {
    467                 const uchar a = urdata[h*width*4+w*4+3];
    468                 newdata[h*width*4+w*4+0] = static_cast<uchar>((urdata[h*width*4+w*4+2] * a) >> 8);
    469                 newdata[h*width*4+w*4+1] = static_cast<uchar>((urdata[h*width*4+w*4+1] * a) >> 8);
    470                 newdata[h*width*4+w*4+2] = static_cast<uchar>((urdata[h*width*4+w*4+0] * a) >> 8);
    471                 newdata[h*width*4+w*4+3] = a;
    472             }
    473         }
    474         break;
    475     }
    476 
    477     ImageBase::loadFromMemory(rdata, s, fmt);
    478 }
    479 
    480 // const GraphicsContext& context
    481 void CairoImage::loadFromPNG(const char* const pngData, const uint pngSize) noexcept
    482 {
    483     struct PngReaderData
    484     {
    485         const char* dataPtr;
    486         uint sizeLeft;
    487 
    488         static cairo_status_t read(void* const closure, uchar* const data, const uint length) noexcept
    489         {
    490             PngReaderData& readerData = *reinterpret_cast<PngReaderData*>(closure);
    491 
    492             if (readerData.sizeLeft < length)
    493                 return CAIRO_STATUS_READ_ERROR;
    494 
    495             std::memcpy(data, readerData.dataPtr, length);
    496             readerData.dataPtr += length;
    497             readerData.sizeLeft -= length;
    498             return CAIRO_STATUS_SUCCESS;
    499         }
    500     };
    501 
    502     PngReaderData readerData;
    503     readerData.dataPtr = pngData;
    504     readerData.sizeLeft = pngSize;
    505 
    506     cairo_surface_t* const newsurface = cairo_image_surface_create_from_png_stream(PngReaderData::read, &readerData);
    507     DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,);
    508 
    509     const int newwidth = cairo_image_surface_get_width(newsurface);
    510     const int newheight = cairo_image_surface_get_height(newsurface);
    511     DISTRHO_SAFE_ASSERT_INT_RETURN(newwidth > 0, newwidth,);
    512     DISTRHO_SAFE_ASSERT_INT_RETURN(newheight > 0, newheight,);
    513 
    514     cairo_surface_destroy(surface);
    515 
    516     if (datarefcount != nullptr && --(*datarefcount) == 0)
    517         std::free(surfacedata);
    518     else
    519         datarefcount = (int*)malloc(sizeof(*datarefcount));
    520 
    521     surface = newsurface;
    522     surfacedata = nullptr; // cairo_image_surface_get_data(newsurface);
    523     *datarefcount = 1;
    524 
    525     rawData = nullptr;
    526     format = kImageFormatNull; // asCairoImageFormat(cairo_image_surface_get_format(newsurface));
    527     size = Size<uint>(static_cast<uint>(newwidth), static_cast<uint>(newheight));
    528 }
    529 
    530 void CairoImage::drawAt(const GraphicsContext& context, const Point<int>& pos)
    531 {
    532     DISTRHO_SAFE_ASSERT_RETURN(surface != nullptr,);
    533 
    534     cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
    535 
    536     cairo_set_source_surface(handle, surface, pos.getX(), pos.getY());
    537     cairo_paint(handle);
    538 }
    539 
    540 CairoImage& CairoImage::operator=(const CairoImage& image) noexcept
    541 {
    542     cairo_surface_t* newsurface = cairo_surface_reference(image.surface);
    543     cairo_surface_destroy(surface);
    544 
    545     if (datarefcount != nullptr && --(*datarefcount) == 0)
    546     {
    547         std::free(surfacedata);
    548         std::free(datarefcount);
    549     }
    550 
    551     surface = newsurface;
    552     rawData = image.rawData;
    553     size    = image.size;
    554     format  = image.format;
    555     surfacedata = image.surfacedata;
    556     datarefcount = image.datarefcount;
    557 
    558     if (datarefcount != nullptr)
    559         ++(*datarefcount);
    560 
    561     return *this;
    562 }
    563 
    564 // -----------------------------------------------------------------------
    565 // CairoSubWidget
    566 
    567 template <>
    568 CairoBaseWidget<SubWidget>::CairoBaseWidget(Widget* const parent)
    569     : SubWidget(parent) {}
    570 
    571 template class CairoBaseWidget<SubWidget>;
    572 
    573 // -----------------------------------------------------------------------
    574 // CairoTopLevelWidget
    575 
    576 template <>
    577 CairoBaseWidget<TopLevelWidget>::CairoBaseWidget(Window& windowToMapTo)
    578     : TopLevelWidget(windowToMapTo) {}
    579 
    580 template class CairoBaseWidget<TopLevelWidget>;
    581 
    582 // -----------------------------------------------------------------------
    583 // CairoStandaloneWindow
    584 
    585 template <>
    586 CairoBaseWidget<StandaloneWindow>::CairoBaseWidget(Application& app)
    587     : StandaloneWindow(app) {}
    588 
    589 template <>
    590 CairoBaseWidget<StandaloneWindow>::CairoBaseWidget(Application& app, Window& parentWindow)
    591     : StandaloneWindow(app, parentWindow) {}
    592 
    593 template class CairoBaseWidget<StandaloneWindow>;
    594 
    595 // -----------------------------------------------------------------------
    596 // ImageBaseAboutWindow
    597 
    598 #if 0
    599 template <>
    600 void ImageBaseAboutWindow<CairoImage>::onDisplay()
    601 {
    602     img.draw(getGraphicsContext());
    603 }
    604 #endif
    605 
    606 template class ImageBaseAboutWindow<CairoImage>;
    607 
    608 // -----------------------------------------------------------------------
    609 // ImageBaseButton
    610 
    611 template class ImageBaseButton<CairoImage>;
    612 
    613 // -----------------------------------------------------------------------
    614 // ImageBaseKnob
    615 
    616 template <>
    617 void ImageBaseKnob<CairoImage>::PrivateData::init()
    618 {
    619     alwaysRepaint = true;
    620     cairoSurface = nullptr;
    621 }
    622 
    623 template <>
    624 void ImageBaseKnob<CairoImage>::PrivateData::cleanup()
    625 {
    626     cairo_surface_destroy((cairo_surface_t*)cairoSurface);
    627     cairoSurface = nullptr;
    628 }
    629 
    630 /**
    631    Get the pixel size in bytes.
    632    @return pixel size, or 0 if the format is unknown, or pixels are not aligned to bytes.
    633 */
    634 static int getBytesPerPixel(const cairo_format_t format) noexcept
    635 {
    636     switch (format)
    637     {
    638     case CAIRO_FORMAT_ARGB32:
    639     case CAIRO_FORMAT_RGB24:
    640         return 4;
    641     case CAIRO_FORMAT_A8:
    642         return 1;
    643     default:
    644         DISTRHO_SAFE_ASSERT(false);
    645         return 0;
    646     }
    647 }
    648 
    649 static cairo_surface_t* getRegion(cairo_surface_t* origsurface, int x, int y, int width, int height) noexcept
    650 {
    651     const cairo_format_t format = cairo_image_surface_get_format(origsurface);
    652     DISTRHO_SAFE_ASSERT_RETURN(format != CAIRO_FORMAT_INVALID, nullptr);
    653 
    654     const int bpp = getBytesPerPixel(format);
    655     DISTRHO_SAFE_ASSERT_RETURN(bpp != 0, nullptr);
    656 
    657     const int fullWidth   = cairo_image_surface_get_width(origsurface);
    658     const int fullHeight  = cairo_image_surface_get_height(origsurface);
    659     const int stride      = cairo_image_surface_get_stride(origsurface);
    660     uchar* const fullData = cairo_image_surface_get_data(origsurface);
    661 
    662     x = (x < fullWidth) ? x : fullWidth;
    663     y = (y < fullHeight) ? y : fullHeight;
    664     width = (x + width < fullWidth) ? width : (fullWidth - x);
    665     height = (x + height < fullHeight) ? height : (fullHeight - x);
    666 
    667     uchar* const data = fullData + (x * bpp + y * stride);
    668     return cairo_image_surface_create_for_data(data, format, width, height, stride);
    669 }
    670 
    671 template <>
    672 void ImageBaseKnob<CairoImage>::onDisplay()
    673 {
    674     const GraphicsContext& context(getGraphicsContext());
    675     cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
    676     const double normValue = getNormalizedValue();
    677 
    678     cairo_surface_t* surface = (cairo_surface_t*)pData->cairoSurface;
    679 
    680     if (! pData->isReady)
    681     {
    682         const int layerW = static_cast<int>(pData->imgLayerWidth);
    683         const int layerH = static_cast<int>(pData->imgLayerHeight);
    684         int layerNum = 0;
    685 
    686         if (pData->rotationAngle == 0)
    687             layerNum = static_cast<int>(normValue * static_cast<double>(pData->imgLayerCount - 1) + 0.5);
    688 
    689         const int layerX = pData->isImgVertical ? 0 : layerNum * layerW;
    690         const int layerY = !pData->isImgVertical ? 0 : layerNum * layerH;
    691 
    692         cairo_surface_t* newsurface;
    693 
    694         if (pData->rotationAngle == 0)
    695         {
    696             newsurface = getRegion(pData->image.getSurface(), layerX, layerY, layerW, layerH);
    697         }
    698         else
    699         {
    700             newsurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, layerW, layerH);
    701             cairo_t* const cr = cairo_create(newsurface);
    702             cairo_translate(cr, 0.5 * layerW, 0.5 * layerH);
    703             cairo_rotate(cr, normValue * pData->rotationAngle * (M_PI / 180));
    704             cairo_set_source_surface(cr, pData->image.getSurface(), -0.5 * layerW, -0.5 * layerH);
    705             cairo_paint(cr);
    706             cairo_destroy(cr);
    707         }
    708 
    709         DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,);
    710 
    711         cairo_surface_destroy(surface);
    712         pData->cairoSurface = surface = newsurface;
    713         pData->isReady = true;
    714     }
    715 
    716     if (surface != nullptr)
    717     {
    718         cairo_set_source_surface(handle, surface, 0, 0);
    719         cairo_paint(handle);
    720     }
    721 }
    722 
    723 template class ImageBaseKnob<CairoImage>;
    724 
    725 // -----------------------------------------------------------------------
    726 // ImageBaseSlider
    727 
    728 template class ImageBaseSlider<CairoImage>;
    729 
    730 // -----------------------------------------------------------------------
    731 // ImageBaseSwitch
    732 
    733 template class ImageBaseSwitch<CairoImage>;
    734 
    735 // -----------------------------------------------------------------------
    736 
    737 void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor)
    738 {
    739     cairo_t* const handle = static_cast<const CairoGraphicsContext&>(self->getGraphicsContext()).handle;
    740 
    741     bool needsResetClip = false;
    742 
    743     cairo_matrix_t matrix;
    744     cairo_get_matrix(handle, &matrix);
    745 
    746     if (needsViewportScaling)
    747     {
    748         // limit viewport to widget bounds
    749         // NOTE only used for nanovg for now, which is not relevant here
    750     }
    751     else if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size<uint>(width, height)))
    752     {
    753         // full viewport size
    754         cairo_translate(handle, 0, 0);
    755         cairo_scale(handle, autoScaleFactor, autoScaleFactor);
    756     }
    757     else
    758     {
    759         // set viewport pos
    760         cairo_translate(handle, absolutePos.getX() * autoScaleFactor, absolutePos.getY() * autoScaleFactor);
    761 
    762         // then cut the outer bounds
    763         cairo_rectangle(handle,
    764                         0,
    765                         0,
    766                         std::round(self->getWidth() * autoScaleFactor),
    767                         std::round(self->getHeight() * autoScaleFactor));
    768 
    769         cairo_clip(handle);
    770         needsResetClip = true;
    771 
    772         // set viewport scaling
    773         cairo_scale(handle, autoScaleFactor, autoScaleFactor);
    774     }
    775 
    776     // display widget
    777     self->onDisplay();
    778 
    779     if (needsResetClip)
    780         cairo_reset_clip(handle);
    781 
    782     cairo_set_matrix(handle, &matrix);
    783 
    784     selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
    785 }
    786 
    787 // -----------------------------------------------------------------------
    788 
    789 void TopLevelWidget::PrivateData::display()
    790 {
    791     if (! selfw->pData->visible)
    792         return;
    793 
    794     cairo_t* const handle = static_cast<const CairoGraphicsContext&>(self->getGraphicsContext()).handle;
    795 
    796     const Size<uint> size(window.getSize());
    797     const uint width  = size.getWidth();
    798     const uint height = size.getHeight();
    799 
    800     const double autoScaleFactor = window.pData->autoScaleFactor;
    801 
    802     cairo_matrix_t matrix;
    803     cairo_get_matrix(handle, &matrix);
    804 
    805     // full viewport size
    806     cairo_translate(handle, 0, 0);
    807 
    808     if (window.pData->autoScaling)
    809         cairo_scale(handle, autoScaleFactor, autoScaleFactor);
    810     else
    811         cairo_scale(handle, 1.0, 1.0);
    812 
    813     // main widget drawing
    814     self->onDisplay();
    815 
    816     cairo_set_matrix(handle, &matrix);
    817 
    818     // now draw subwidgets if there are any
    819     selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
    820 }
    821 
    822 // -----------------------------------------------------------------------
    823 
    824 void Window::PrivateData::renderToPicture(const char*, const GraphicsContext&, uint, uint)
    825 {
    826     notImplemented("Window::PrivateData::renderToPicture");
    827 }
    828 
    829 // -----------------------------------------------------------------------
    830 
    831 const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
    832 {
    833     GraphicsContext& context((GraphicsContext&)graphicsContext);
    834     ((CairoGraphicsContext&)context).handle = (cairo_t*)puglGetContext(view);
    835     return context;
    836 }
    837 
    838 // -----------------------------------------------------------------------
    839 
    840 END_NAMESPACE_DGL