DISTRHO Plugin Framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

840 lines
24KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2023 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. #ifdef _MSC_VER
  18. // instantiated template classes whose methods are defined elsewhere
  19. # pragma warning(disable:4661)
  20. #endif
  21. #include "../Cairo.hpp"
  22. #include "../Color.hpp"
  23. #include "../ImageBaseWidgets.hpp"
  24. #include "SubWidgetPrivateData.hpp"
  25. #include "TopLevelWidgetPrivateData.hpp"
  26. #include "WidgetPrivateData.hpp"
  27. #include "WindowPrivateData.hpp"
  28. // templated classes
  29. #include "ImageBaseWidgets.cpp"
  30. START_NAMESPACE_DGL
  31. // -----------------------------------------------------------------------
  32. static void notImplemented(const char* const name)
  33. {
  34. d_stderr2("cairo function not implemented: %s", name);
  35. }
  36. // -----------------------------------------------------------------------
  37. // Color
  38. void Color::setFor(const GraphicsContext& context, const bool includeAlpha)
  39. {
  40. cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
  41. if (includeAlpha)
  42. cairo_set_source_rgba(handle, red, green, blue, alpha);
  43. else
  44. cairo_set_source_rgb(handle, red, green, blue);
  45. }
  46. // -----------------------------------------------------------------------
  47. // Line
  48. template<typename T>
  49. void Line<T>::draw(const GraphicsContext& context, const T width)
  50. {
  51. DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,);
  52. DISTRHO_SAFE_ASSERT_RETURN(width != 0,);
  53. cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
  54. cairo_set_line_width(handle, width);
  55. cairo_move_to(handle, posStart.getX(), posStart.getY());
  56. cairo_line_to(handle, posEnd.getX(), posEnd.getY());
  57. cairo_stroke(handle);
  58. }
  59. template<typename T>
  60. void Line<T>::draw()
  61. {
  62. notImplemented("Line::draw");
  63. }
  64. template class Line<double>;
  65. template class Line<float>;
  66. template class Line<int>;
  67. template class Line<uint>;
  68. template class Line<short>;
  69. template class Line<ushort>;
  70. // -----------------------------------------------------------------------
  71. // Circle
  72. template<typename T>
  73. static void drawCircle(cairo_t* const handle,
  74. const Point<T>& pos,
  75. const uint numSegments,
  76. const float size,
  77. const float sin,
  78. const float cos,
  79. const bool outline)
  80. {
  81. DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,);
  82. const T origx = pos.getX();
  83. const T origy = pos.getY();
  84. double t, x = size, y = 0.0;
  85. // TODO use arc
  86. /*
  87. cairo_arc(handle, origx, origy, size, sin, cos);
  88. */
  89. cairo_move_to(handle, x + origx, y + origy);
  90. for (uint i=1; i<numSegments; ++i)
  91. {
  92. cairo_line_to(handle, x + origx, y + origy);
  93. t = x;
  94. x = cos * x - sin * y;
  95. y = sin * t + cos * y;
  96. }
  97. cairo_line_to(handle, x + origx, y + origy);
  98. if (outline)
  99. cairo_stroke(handle);
  100. else
  101. cairo_fill(handle);
  102. }
  103. template<typename T>
  104. void Circle<T>::draw(const GraphicsContext& context)
  105. {
  106. cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
  107. drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, false);
  108. }
  109. template<typename T>
  110. void Circle<T>::drawOutline(const GraphicsContext& context, const T lineWidth)
  111. {
  112. DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);
  113. cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
  114. cairo_set_line_width(handle, lineWidth);
  115. drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, true);
  116. }
  117. template<typename T>
  118. void Circle<T>::draw()
  119. {
  120. notImplemented("Circle::draw");
  121. }
  122. template<typename T>
  123. void Circle<T>::drawOutline()
  124. {
  125. notImplemented("Circle::drawOutline");
  126. }
  127. template class Circle<double>;
  128. template class Circle<float>;
  129. template class Circle<int>;
  130. template class Circle<uint>;
  131. template class Circle<short>;
  132. template class Circle<ushort>;
  133. // -----------------------------------------------------------------------
  134. // Triangle
  135. template<typename T>
  136. static void drawTriangle(cairo_t* const handle,
  137. const Point<T>& pos1,
  138. const Point<T>& pos2,
  139. const Point<T>& pos3,
  140. const bool outline)
  141. {
  142. DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,);
  143. cairo_move_to(handle, pos1.getX(), pos1.getY());
  144. cairo_line_to(handle, pos2.getX(), pos2.getY());
  145. cairo_line_to(handle, pos3.getX(), pos3.getY());
  146. cairo_line_to(handle, pos1.getX(), pos1.getY());
  147. if (outline)
  148. cairo_stroke(handle);
  149. else
  150. cairo_fill(handle);
  151. }
  152. template<typename T>
  153. void Triangle<T>::draw(const GraphicsContext& context)
  154. {
  155. cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
  156. drawTriangle<T>(handle, pos1, pos2, pos3, false);
  157. }
  158. template<typename T>
  159. void Triangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth)
  160. {
  161. DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);
  162. cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
  163. cairo_set_line_width(handle, lineWidth);
  164. drawTriangle<T>(handle, pos1, pos2, pos3, true);
  165. }
  166. template<typename T>
  167. void Triangle<T>::draw()
  168. {
  169. notImplemented("Triangle::draw");
  170. }
  171. template<typename T>
  172. void Triangle<T>::drawOutline()
  173. {
  174. notImplemented("Triangle::drawOutline");
  175. }
  176. template class Triangle<double>;
  177. template class Triangle<float>;
  178. template class Triangle<int>;
  179. template class Triangle<uint>;
  180. template class Triangle<short>;
  181. template class Triangle<ushort>;
  182. // -----------------------------------------------------------------------
  183. // Rectangle
  184. template<typename T>
  185. static void drawRectangle(cairo_t* const handle, const Rectangle<T>& rect, const bool outline)
  186. {
  187. cairo_rectangle(handle, rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
  188. if (outline)
  189. cairo_stroke(handle);
  190. else
  191. cairo_fill(handle);
  192. }
  193. template<typename T>
  194. void Rectangle<T>::draw(const GraphicsContext& context)
  195. {
  196. DISTRHO_SAFE_ASSERT_RETURN(isValid(),);
  197. cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
  198. drawRectangle(handle, *this, false);
  199. }
  200. template<typename T>
  201. void Rectangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth)
  202. {
  203. DISTRHO_SAFE_ASSERT_RETURN(isValid(),);
  204. DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,);
  205. cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
  206. cairo_set_line_width(handle, lineWidth);
  207. drawRectangle(handle, *this, true);
  208. }
  209. template<typename T>
  210. void Rectangle<T>::draw()
  211. {
  212. notImplemented("Rectangle::draw");
  213. }
  214. template<typename T>
  215. void Rectangle<T>::drawOutline()
  216. {
  217. notImplemented("Rectangle::drawOutline");
  218. }
  219. template class Rectangle<double>;
  220. template class Rectangle<float>;
  221. template class Rectangle<int>;
  222. template class Rectangle<uint>;
  223. template class Rectangle<short>;
  224. template class Rectangle<ushort>;
  225. // -----------------------------------------------------------------------
  226. // CairoImage
  227. static cairo_format_t asCairoImageFormat(const ImageFormat format) noexcept
  228. {
  229. switch (format)
  230. {
  231. case kImageFormatNull:
  232. break;
  233. case kImageFormatGrayscale:
  234. case kImageFormatBGR:
  235. case kImageFormatRGB:
  236. return CAIRO_FORMAT_RGB24;
  237. case kImageFormatBGRA:
  238. case kImageFormatRGBA:
  239. return CAIRO_FORMAT_ARGB32;
  240. }
  241. return CAIRO_FORMAT_INVALID;
  242. }
  243. /*
  244. static ImageFormat asCairoImageFormat(const cairo_format_t format) noexcept
  245. {
  246. switch (format)
  247. {
  248. case CAIRO_FORMAT_INVALID:
  249. break;
  250. case CAIRO_FORMAT_ARGB32:
  251. break;
  252. case CAIRO_FORMAT_RGB24:
  253. break;
  254. case CAIRO_FORMAT_A8:
  255. break;
  256. case CAIRO_FORMAT_A1:
  257. break;
  258. case CAIRO_FORMAT_RGB16_565:
  259. break;
  260. case CAIRO_FORMAT_RGB30:
  261. break;
  262. }
  263. return kImageFormatNull;
  264. }
  265. */
  266. CairoImage::CairoImage()
  267. : ImageBase(),
  268. surface(nullptr),
  269. surfacedata(nullptr),
  270. datarefcount(nullptr) {}
  271. CairoImage::CairoImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt)
  272. : ImageBase(rdata, w, h, fmt),
  273. surface(nullptr),
  274. surfacedata(nullptr),
  275. datarefcount(nullptr)
  276. {
  277. loadFromMemory(rdata, w, h, fmt);
  278. }
  279. CairoImage::CairoImage(const char* const rdata, const Size<uint>& s, const ImageFormat fmt)
  280. : ImageBase(rdata, s, fmt),
  281. surface(nullptr),
  282. surfacedata(nullptr),
  283. datarefcount(nullptr)
  284. {
  285. loadFromMemory(rdata, s, fmt);
  286. }
  287. CairoImage::CairoImage(const CairoImage& image)
  288. : ImageBase(image.rawData, image.size, image.format),
  289. surface(cairo_surface_reference(image.surface)),
  290. surfacedata(image.surfacedata),
  291. datarefcount(image.datarefcount)
  292. {
  293. if (datarefcount != nullptr)
  294. ++(*datarefcount);
  295. }
  296. CairoImage::~CairoImage()
  297. {
  298. cairo_surface_destroy(surface);
  299. if (datarefcount != nullptr && --(*datarefcount) == 0)
  300. {
  301. std::free(surfacedata);
  302. std::free(datarefcount);
  303. }
  304. }
  305. void CairoImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept
  306. {
  307. const cairo_format_t cairoformat = asCairoImageFormat(fmt);
  308. DISTRHO_SAFE_ASSERT_RETURN(cairoformat != CAIRO_FORMAT_INVALID,);
  309. const int width = static_cast<int>(s.getWidth());
  310. const int height = static_cast<int>(s.getHeight());
  311. const int stride = cairo_format_stride_for_width(cairoformat, width);
  312. uchar* const newdata = static_cast<uchar*>(std::malloc(static_cast<size_t>(width * height * stride * 4)));
  313. DISTRHO_SAFE_ASSERT_RETURN(newdata != nullptr,);
  314. cairo_surface_t* const newsurface = cairo_image_surface_create_for_data(newdata, cairoformat, width, height, stride);
  315. DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,);
  316. DISTRHO_SAFE_ASSERT_RETURN(static_cast<int>(s.getWidth()) == cairo_image_surface_get_width(newsurface),);
  317. DISTRHO_SAFE_ASSERT_RETURN(static_cast<int>(s.getHeight()) == cairo_image_surface_get_height(newsurface),);
  318. cairo_surface_destroy(surface);
  319. if (datarefcount != nullptr && --(*datarefcount) == 0)
  320. std::free(surfacedata);
  321. else
  322. datarefcount = static_cast<int*>(std::malloc(sizeof(int)));
  323. surface = newsurface;
  324. surfacedata = newdata;
  325. *datarefcount = 1;
  326. const uchar* const urdata = reinterpret_cast<const uchar*>(rdata);
  327. switch (fmt)
  328. {
  329. case kImageFormatNull:
  330. break;
  331. case kImageFormatGrayscale:
  332. // Grayscale to CAIRO_FORMAT_RGB24
  333. for (int h = 0; h < height; ++h)
  334. {
  335. for (int w = 0; w < width; ++w)
  336. {
  337. newdata[h*width*4+w*4+0] = urdata[h*width+w];
  338. newdata[h*width*4+w*4+1] = urdata[h*width+w];
  339. newdata[h*width*4+w*4+2] = urdata[h*width+w];
  340. newdata[h*width*4+w*4+3] = 0;
  341. }
  342. }
  343. break;
  344. case kImageFormatBGR:
  345. // BGR8 to CAIRO_FORMAT_RGB24
  346. for (int h = 0; h < height; ++h)
  347. {
  348. for (int w = 0; w < width; ++w)
  349. {
  350. newdata[h*width*4+w*4+0] = urdata[h*width*3+w*3+0];
  351. newdata[h*width*4+w*4+1] = urdata[h*width*3+w*3+1];
  352. newdata[h*width*4+w*4+2] = urdata[h*width*3+w*3+2];
  353. newdata[h*width*4+w*4+3] = 0;
  354. }
  355. }
  356. break;
  357. case kImageFormatBGRA:
  358. // BGRA8 to CAIRO_FORMAT_ARGB32
  359. for (int h = 0; h < height; ++h)
  360. {
  361. for (int w = 0; w < width; ++w)
  362. {
  363. const uchar a = urdata[h*width*4+w*4+3];
  364. newdata[h*width*4+w*4+0] = (urdata[h*width*4+w*4+0] * a) >> 8;
  365. newdata[h*width*4+w*4+1] = (urdata[h*width*4+w*4+1] * a) >> 8;
  366. newdata[h*width*4+w*4+2] = (urdata[h*width*4+w*4+2] * a) >> 8;
  367. newdata[h*width*4+w*4+3] = a;
  368. }
  369. }
  370. break;
  371. case kImageFormatRGB:
  372. // RGB8 to CAIRO_FORMAT_RGB24
  373. for (int h = 0; h < height; ++h)
  374. {
  375. for (int w = 0; w < width; ++w)
  376. {
  377. newdata[h*width*4+w*4+0] = urdata[h*width*3+w*3+2];
  378. newdata[h*width*4+w*4+1] = urdata[h*width*3+w*3+1];
  379. newdata[h*width*4+w*4+2] = urdata[h*width*3+w*3+0];
  380. newdata[h*width*4+w*4+3] = 0;
  381. }
  382. }
  383. break;
  384. case kImageFormatRGBA:
  385. // RGBA8 to CAIRO_FORMAT_ARGB32
  386. for (int h = 0; h < height; ++h)
  387. {
  388. for (int w = 0; w < width; ++w)
  389. {
  390. const uchar a = urdata[h*width*4+w*4+3];
  391. newdata[h*width*4+w*4+0] = (urdata[h*width*4+w*4+2] * a) >> 8;
  392. newdata[h*width*4+w*4+1] = (urdata[h*width*4+w*4+1] * a) >> 8;
  393. newdata[h*width*4+w*4+2] = (urdata[h*width*4+w*4+0] * a) >> 8;
  394. newdata[h*width*4+w*4+3] = a;
  395. }
  396. }
  397. break;
  398. }
  399. ImageBase::loadFromMemory(rdata, s, fmt);
  400. }
  401. // const GraphicsContext& context
  402. void CairoImage::loadFromPNG(const char* const pngData, const uint pngSize) noexcept
  403. {
  404. struct PngReaderData
  405. {
  406. const char* dataPtr;
  407. uint sizeLeft;
  408. static cairo_status_t read(void* const closure, uchar* const data, const uint length) noexcept
  409. {
  410. PngReaderData& readerData = *reinterpret_cast<PngReaderData*>(closure);
  411. if (readerData.sizeLeft < length)
  412. return CAIRO_STATUS_READ_ERROR;
  413. std::memcpy(data, readerData.dataPtr, length);
  414. readerData.dataPtr += length;
  415. readerData.sizeLeft -= length;
  416. return CAIRO_STATUS_SUCCESS;
  417. }
  418. };
  419. PngReaderData readerData;
  420. readerData.dataPtr = pngData;
  421. readerData.sizeLeft = pngSize;
  422. cairo_surface_t* const newsurface = cairo_image_surface_create_from_png_stream(PngReaderData::read, &readerData);
  423. DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,);
  424. const int newwidth = cairo_image_surface_get_width(newsurface);
  425. const int newheight = cairo_image_surface_get_height(newsurface);
  426. DISTRHO_SAFE_ASSERT_INT_RETURN(newwidth > 0, newwidth,);
  427. DISTRHO_SAFE_ASSERT_INT_RETURN(newheight > 0, newheight,);
  428. cairo_surface_destroy(surface);
  429. if (datarefcount != nullptr && --(*datarefcount) == 0)
  430. std::free(surfacedata);
  431. else
  432. datarefcount = (int*)malloc(sizeof(*datarefcount));
  433. surface = newsurface;
  434. surfacedata = nullptr; // cairo_image_surface_get_data(newsurface);
  435. *datarefcount = 1;
  436. rawData = nullptr;
  437. format = kImageFormatNull; // asCairoImageFormat(cairo_image_surface_get_format(newsurface));
  438. size = Size<uint>(static_cast<uint>(newwidth), static_cast<uint>(newheight));
  439. }
  440. void CairoImage::drawAt(const GraphicsContext& context, const Point<int>& pos)
  441. {
  442. DISTRHO_SAFE_ASSERT_RETURN(surface != nullptr,);
  443. cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
  444. cairo_set_source_surface(handle, surface, pos.getX(), pos.getY());
  445. cairo_paint(handle);
  446. }
  447. CairoImage& CairoImage::operator=(const CairoImage& image) noexcept
  448. {
  449. cairo_surface_t* newsurface = cairo_surface_reference(image.surface);
  450. cairo_surface_destroy(surface);
  451. if (datarefcount != nullptr && --(*datarefcount) == 0)
  452. {
  453. std::free(surfacedata);
  454. std::free(datarefcount);
  455. }
  456. surface = newsurface;
  457. rawData = image.rawData;
  458. size = image.size;
  459. format = image.format;
  460. surfacedata = image.surfacedata;
  461. datarefcount = image.datarefcount;
  462. if (datarefcount != nullptr)
  463. ++(*datarefcount);
  464. return *this;
  465. }
  466. // -----------------------------------------------------------------------
  467. // CairoSubWidget
  468. template <>
  469. CairoBaseWidget<SubWidget>::CairoBaseWidget(Widget* const parent)
  470. : SubWidget(parent) {}
  471. template class CairoBaseWidget<SubWidget>;
  472. // -----------------------------------------------------------------------
  473. // CairoTopLevelWidget
  474. template <>
  475. CairoBaseWidget<TopLevelWidget>::CairoBaseWidget(Window& windowToMapTo)
  476. : TopLevelWidget(windowToMapTo) {}
  477. template class CairoBaseWidget<TopLevelWidget>;
  478. // -----------------------------------------------------------------------
  479. // CairoStandaloneWindow
  480. template <>
  481. CairoBaseWidget<StandaloneWindow>::CairoBaseWidget(Application& app)
  482. : StandaloneWindow(app) {}
  483. template <>
  484. CairoBaseWidget<StandaloneWindow>::CairoBaseWidget(Application& app, Window& parentWindow)
  485. : StandaloneWindow(app, parentWindow) {}
  486. template class CairoBaseWidget<StandaloneWindow>;
  487. // -----------------------------------------------------------------------
  488. // ImageBaseAboutWindow
  489. #if 0
  490. template <>
  491. void ImageBaseAboutWindow<CairoImage>::onDisplay()
  492. {
  493. img.draw(getGraphicsContext());
  494. }
  495. #endif
  496. template class ImageBaseAboutWindow<CairoImage>;
  497. // -----------------------------------------------------------------------
  498. // ImageBaseButton
  499. template class ImageBaseButton<CairoImage>;
  500. // -----------------------------------------------------------------------
  501. // ImageBaseKnob
  502. template <>
  503. void ImageBaseKnob<CairoImage>::PrivateData::init()
  504. {
  505. alwaysRepaint = true;
  506. cairoSurface = nullptr;
  507. }
  508. template <>
  509. void ImageBaseKnob<CairoImage>::PrivateData::cleanup()
  510. {
  511. cairo_surface_destroy((cairo_surface_t*)cairoSurface);
  512. cairoSurface = nullptr;
  513. }
  514. /**
  515. Get the pixel size in bytes.
  516. @return pixel size, or 0 if the format is unknown, or pixels are not aligned to bytes.
  517. */
  518. static int getBytesPerPixel(const cairo_format_t format) noexcept
  519. {
  520. switch (format)
  521. {
  522. case CAIRO_FORMAT_ARGB32:
  523. case CAIRO_FORMAT_RGB24:
  524. return 4;
  525. case CAIRO_FORMAT_A8:
  526. return 1;
  527. default:
  528. DISTRHO_SAFE_ASSERT(false);
  529. return 0;
  530. }
  531. }
  532. static cairo_surface_t* getRegion(cairo_surface_t* origsurface, int x, int y, int width, int height) noexcept
  533. {
  534. const cairo_format_t format = cairo_image_surface_get_format(origsurface);
  535. DISTRHO_SAFE_ASSERT_RETURN(format != CAIRO_FORMAT_INVALID, nullptr);
  536. const int bpp = getBytesPerPixel(format);
  537. DISTRHO_SAFE_ASSERT_RETURN(bpp != 0, nullptr);
  538. const int fullWidth = cairo_image_surface_get_width(origsurface);
  539. const int fullHeight = cairo_image_surface_get_height(origsurface);
  540. const int stride = cairo_image_surface_get_stride(origsurface);
  541. uchar* const fullData = cairo_image_surface_get_data(origsurface);
  542. x = (x < fullWidth) ? x : fullWidth;
  543. y = (y < fullHeight) ? y : fullHeight;
  544. width = (x + width < fullWidth) ? width : (fullWidth - x);
  545. height = (x + height < fullHeight) ? height : (fullHeight - x);
  546. uchar* const data = fullData + (x * bpp + y * stride);
  547. return cairo_image_surface_create_for_data(data, format, width, height, stride);
  548. }
  549. template <>
  550. void ImageBaseKnob<CairoImage>::onDisplay()
  551. {
  552. const GraphicsContext& context(getGraphicsContext());
  553. cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
  554. const double normValue = getNormalizedValue();
  555. cairo_surface_t* surface = (cairo_surface_t*)pData->cairoSurface;
  556. if (! pData->isReady)
  557. {
  558. const int layerW = static_cast<int>(pData->imgLayerWidth);
  559. const int layerH = static_cast<int>(pData->imgLayerHeight);
  560. int layerNum = 0;
  561. if (pData->rotationAngle == 0)
  562. layerNum = static_cast<int>(normValue * static_cast<double>(pData->imgLayerCount - 1) + 0.5);
  563. const int layerX = pData->isImgVertical ? 0 : layerNum * layerW;
  564. const int layerY = !pData->isImgVertical ? 0 : layerNum * layerH;
  565. cairo_surface_t* newsurface;
  566. if (pData->rotationAngle == 0)
  567. {
  568. newsurface = getRegion(pData->image.getSurface(), layerX, layerY, layerW, layerH);
  569. }
  570. else
  571. {
  572. newsurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, layerW, layerH);
  573. cairo_t* const cr = cairo_create(newsurface);
  574. cairo_translate(cr, 0.5 * layerW, 0.5 * layerH);
  575. cairo_rotate(cr, normValue * pData->rotationAngle * (M_PI / 180));
  576. cairo_set_source_surface(cr, pData->image.getSurface(), -0.5 * layerW, -0.5 * layerH);
  577. cairo_paint(cr);
  578. cairo_destroy(cr);
  579. }
  580. DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,);
  581. cairo_surface_destroy(surface);
  582. pData->cairoSurface = surface = newsurface;
  583. pData->isReady = true;
  584. }
  585. if (surface != nullptr)
  586. {
  587. cairo_set_source_surface(handle, surface, 0, 0);
  588. cairo_paint(handle);
  589. }
  590. }
  591. template class ImageBaseKnob<CairoImage>;
  592. // -----------------------------------------------------------------------
  593. // ImageBaseSlider
  594. template class ImageBaseSlider<CairoImage>;
  595. // -----------------------------------------------------------------------
  596. // ImageBaseSwitch
  597. template class ImageBaseSwitch<CairoImage>;
  598. // -----------------------------------------------------------------------
  599. void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor)
  600. {
  601. cairo_t* const handle = static_cast<const CairoGraphicsContext&>(self->getGraphicsContext()).handle;
  602. bool needsResetClip = false;
  603. cairo_matrix_t matrix;
  604. cairo_get_matrix(handle, &matrix);
  605. if (needsViewportScaling)
  606. {
  607. // limit viewport to widget bounds
  608. // NOTE only used for nanovg for now, which is not relevant here
  609. }
  610. else if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size<uint>(width, height)))
  611. {
  612. // full viewport size
  613. cairo_translate(handle, 0, 0);
  614. cairo_scale(handle, autoScaleFactor, autoScaleFactor);
  615. }
  616. else
  617. {
  618. // set viewport pos
  619. cairo_translate(handle, absolutePos.getX() * autoScaleFactor, absolutePos.getY() * autoScaleFactor);
  620. // then cut the outer bounds
  621. cairo_rectangle(handle,
  622. 0,
  623. 0,
  624. std::round(self->getWidth() * autoScaleFactor),
  625. std::round(self->getHeight() * autoScaleFactor));
  626. cairo_clip(handle);
  627. needsResetClip = true;
  628. // set viewport scaling
  629. cairo_scale(handle, autoScaleFactor, autoScaleFactor);
  630. }
  631. // display widget
  632. self->onDisplay();
  633. if (needsResetClip)
  634. cairo_reset_clip(handle);
  635. cairo_set_matrix(handle, &matrix);
  636. selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
  637. }
  638. // -----------------------------------------------------------------------
  639. void TopLevelWidget::PrivateData::display()
  640. {
  641. if (! selfw->pData->visible)
  642. return;
  643. cairo_t* const handle = static_cast<const CairoGraphicsContext&>(self->getGraphicsContext()).handle;
  644. const Size<uint> size(window.getSize());
  645. const uint width = size.getWidth();
  646. const uint height = size.getHeight();
  647. const double autoScaleFactor = window.pData->autoScaleFactor;
  648. cairo_matrix_t matrix;
  649. cairo_get_matrix(handle, &matrix);
  650. cairo_translate(handle, 0, 0);
  651. // full viewport size
  652. if (window.pData->autoScaling)
  653. cairo_scale(handle, autoScaleFactor, autoScaleFactor);
  654. else
  655. cairo_scale(handle, 1.0, 1.0);
  656. // main widget drawing
  657. self->onDisplay();
  658. cairo_set_matrix(handle, &matrix);
  659. // now draw subwidgets if there are any
  660. selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
  661. }
  662. // -----------------------------------------------------------------------
  663. void Window::PrivateData::renderToPicture(const char*, const GraphicsContext&, uint, uint)
  664. {
  665. notImplemented("Window::PrivateData::renderToPicture");
  666. }
  667. // -----------------------------------------------------------------------
  668. const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
  669. {
  670. GraphicsContext& context((GraphicsContext&)graphicsContext);
  671. ((CairoGraphicsContext&)context).handle = (cairo_t*)puglGetContext(view);
  672. return context;
  673. }
  674. // -----------------------------------------------------------------------
  675. END_NAMESPACE_DGL