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