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.

828 lines
23KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2021 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. return CAIRO_FORMAT_A8;
  235. case kImageFormatBGR:
  236. case kImageFormatRGB:
  237. return CAIRO_FORMAT_RGB24;
  238. case kImageFormatBGRA:
  239. case kImageFormatRGBA:
  240. return CAIRO_FORMAT_ARGB32;
  241. }
  242. return CAIRO_FORMAT_INVALID;
  243. }
  244. /*
  245. static ImageFormat asCairoImageFormat(const cairo_format_t format) noexcept
  246. {
  247. switch (format)
  248. {
  249. case CAIRO_FORMAT_INVALID:
  250. break;
  251. case CAIRO_FORMAT_ARGB32:
  252. break;
  253. case CAIRO_FORMAT_RGB24:
  254. break;
  255. case CAIRO_FORMAT_A8:
  256. break;
  257. case CAIRO_FORMAT_A1:
  258. break;
  259. case CAIRO_FORMAT_RGB16_565:
  260. break;
  261. case CAIRO_FORMAT_RGB30:
  262. break;
  263. }
  264. return kImageFormatNull;
  265. }
  266. */
  267. CairoImage::CairoImage()
  268. : ImageBase(),
  269. surface(nullptr),
  270. surfacedata(nullptr),
  271. datarefcount(nullptr) {}
  272. CairoImage::CairoImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt)
  273. : ImageBase(rdata, w, h, fmt),
  274. surface(nullptr),
  275. surfacedata(nullptr),
  276. datarefcount(nullptr)
  277. {
  278. loadFromMemory(rdata, w, h, fmt);
  279. }
  280. CairoImage::CairoImage(const char* const rdata, const Size<uint>& s, const ImageFormat fmt)
  281. : ImageBase(rdata, s, fmt),
  282. surface(nullptr),
  283. surfacedata(nullptr),
  284. datarefcount(nullptr)
  285. {
  286. loadFromMemory(rdata, s, fmt);
  287. }
  288. CairoImage::CairoImage(const CairoImage& image)
  289. : ImageBase(image.rawData, image.size, image.format),
  290. surface(cairo_surface_reference(image.surface)),
  291. surfacedata(image.surfacedata),
  292. datarefcount(image.datarefcount)
  293. {
  294. if (datarefcount != nullptr)
  295. ++(*datarefcount);
  296. }
  297. CairoImage::~CairoImage()
  298. {
  299. cairo_surface_destroy(surface);
  300. if (datarefcount != nullptr && --(*datarefcount) == 0)
  301. {
  302. std::free(surfacedata);
  303. std::free(datarefcount);
  304. }
  305. }
  306. void CairoImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept
  307. {
  308. const cairo_format_t cairoformat = asCairoImageFormat(fmt);
  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 = (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(s.getWidth() == cairo_image_surface_get_width(newsurface),);
  317. DISTRHO_SAFE_ASSERT_RETURN(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 = (int*)malloc(sizeof(*datarefcount));
  323. surface = newsurface;
  324. surfacedata = newdata;
  325. *datarefcount = 1;
  326. switch (fmt)
  327. {
  328. case kImageFormatNull:
  329. break;
  330. case kImageFormatGrayscale:
  331. // Grayscale to A8
  332. // TODO
  333. break;
  334. case kImageFormatBGR:
  335. // BGR8 to CAIRO_FORMAT_RGB24
  336. for (int h = 0; h < height; ++h)
  337. {
  338. for (int w = 0; w < width; ++w)
  339. {
  340. newdata[h*width*4+w*4+0] = static_cast<uchar>(rdata[h*width*3+w*3+0]);
  341. newdata[h*width*4+w*4+1] = static_cast<uchar>(rdata[h*width*3+w*3+1]);
  342. newdata[h*width*4+w*4+2] = static_cast<uchar>(rdata[h*width*3+w*3+2]);
  343. newdata[h*width*4+w*4+3] = 0;
  344. }
  345. }
  346. break;
  347. case kImageFormatBGRA:
  348. // BGRA8 to CAIRO_FORMAT_ARGB32
  349. // FIXME something is wrong here...
  350. for (int h = 0, t; h < height; ++h)
  351. {
  352. for (int w = 0; w < width; ++w)
  353. {
  354. if ((t = rdata[h*width*4+w*4+3]) != 0)
  355. {
  356. newdata[h*width*4+w*4+0] = static_cast<uchar>(rdata[h*width*4+w*4+0]);
  357. newdata[h*width*4+w*4+1] = static_cast<uchar>(rdata[h*width*4+w*4+1]);
  358. newdata[h*width*4+w*4+2] = static_cast<uchar>(rdata[h*width*4+w*4+2]);
  359. newdata[h*width*4+w*4+3] = static_cast<uchar>(t);
  360. }
  361. else
  362. {
  363. // make all pixels zero, cairo does not render full transparency otherwise
  364. memset(&newdata[h*width*4+w*4], 0, 4);
  365. }
  366. }
  367. }
  368. break;
  369. case kImageFormatRGB:
  370. // RGB8 to CAIRO_FORMAT_RGB24
  371. // TODO
  372. break;
  373. case kImageFormatRGBA:
  374. // RGBA8 to CAIRO_FORMAT_ARGB32
  375. // TODO
  376. break;
  377. }
  378. ImageBase::loadFromMemory(rdata, s, fmt);
  379. }
  380. // const GraphicsContext& context
  381. void CairoImage::loadFromPNG(const char* const pngData, const uint pngSize) noexcept
  382. {
  383. struct PngReaderData
  384. {
  385. const char* dataPtr;
  386. uint sizeLeft;
  387. static cairo_status_t read(void* const closure, uchar* const data, const uint length) noexcept
  388. {
  389. PngReaderData& readerData = *reinterpret_cast<PngReaderData*>(closure);
  390. if (readerData.sizeLeft < length)
  391. return CAIRO_STATUS_READ_ERROR;
  392. std::memcpy(data, readerData.dataPtr, length);
  393. readerData.dataPtr += length;
  394. readerData.sizeLeft -= length;
  395. return CAIRO_STATUS_SUCCESS;
  396. }
  397. };
  398. PngReaderData readerData;
  399. readerData.dataPtr = pngData;
  400. readerData.sizeLeft = pngSize;
  401. cairo_surface_t* const newsurface = cairo_image_surface_create_from_png_stream(PngReaderData::read, &readerData);
  402. DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,);
  403. const int newwidth = cairo_image_surface_get_width(newsurface);
  404. const int newheight = cairo_image_surface_get_height(newsurface);
  405. DISTRHO_SAFE_ASSERT_INT_RETURN(newwidth > 0, newwidth,);
  406. DISTRHO_SAFE_ASSERT_INT_RETURN(newheight > 0, newheight,);
  407. cairo_surface_destroy(surface);
  408. if (datarefcount != nullptr && --(*datarefcount) == 0)
  409. std::free(surfacedata);
  410. else
  411. datarefcount = (int*)malloc(sizeof(*datarefcount));
  412. surface = newsurface;
  413. surfacedata = nullptr; // cairo_image_surface_get_data(newsurface);
  414. *datarefcount = 1;
  415. rawData = nullptr;
  416. format = kImageFormatNull; // asCairoImageFormat(cairo_image_surface_get_format(newsurface));
  417. size = Size<uint>(static_cast<uint>(newwidth), static_cast<uint>(newheight));
  418. }
  419. void CairoImage::drawAt(const GraphicsContext& context, const Point<int>& pos)
  420. {
  421. if (surface == nullptr)
  422. return;
  423. cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
  424. cairo_set_source_surface(handle, surface, pos.getX(), pos.getY());
  425. cairo_paint(handle);
  426. }
  427. CairoImage& CairoImage::operator=(const CairoImage& image) noexcept
  428. {
  429. cairo_surface_t* newsurface = cairo_surface_reference(image.surface);
  430. cairo_surface_destroy(surface);
  431. if (datarefcount != nullptr && --(*datarefcount) == 0)
  432. {
  433. std::free(surfacedata);
  434. std::free(datarefcount);
  435. }
  436. surface = newsurface;
  437. rawData = image.rawData;
  438. size = image.size;
  439. format = image.format;
  440. surfacedata = image.surfacedata;
  441. datarefcount = image.datarefcount;
  442. if (datarefcount != nullptr)
  443. ++(*datarefcount);
  444. return *this;
  445. }
  446. // -----------------------------------------------------------------------
  447. // CairoSubWidget
  448. template <>
  449. CairoBaseWidget<SubWidget>::CairoBaseWidget(Widget* const parent)
  450. : SubWidget(parent) {}
  451. template class CairoBaseWidget<SubWidget>;
  452. // -----------------------------------------------------------------------
  453. // CairoTopLevelWidget
  454. template <>
  455. CairoBaseWidget<TopLevelWidget>::CairoBaseWidget(Window& windowToMapTo)
  456. : TopLevelWidget(windowToMapTo) {}
  457. template class CairoBaseWidget<TopLevelWidget>;
  458. // -----------------------------------------------------------------------
  459. // CairoStandaloneWindow
  460. template <>
  461. CairoBaseWidget<StandaloneWindow>::CairoBaseWidget(Application& app)
  462. : StandaloneWindow(app) {}
  463. template <>
  464. CairoBaseWidget<StandaloneWindow>::CairoBaseWidget(Application& app, Window& parentWindow)
  465. : StandaloneWindow(app, parentWindow) {}
  466. template class CairoBaseWidget<StandaloneWindow>;
  467. // -----------------------------------------------------------------------
  468. // ImageBaseAboutWindow
  469. #if 0
  470. template <>
  471. void ImageBaseAboutWindow<CairoImage>::onDisplay()
  472. {
  473. img.draw(getGraphicsContext());
  474. }
  475. #endif
  476. template class ImageBaseAboutWindow<CairoImage>;
  477. // -----------------------------------------------------------------------
  478. // ImageBaseButton
  479. template class ImageBaseButton<CairoImage>;
  480. // -----------------------------------------------------------------------
  481. // ImageBaseKnob
  482. template <>
  483. void ImageBaseKnob<CairoImage>::PrivateData::init()
  484. {
  485. alwaysRepaint = true;
  486. cairoSurface = nullptr;
  487. }
  488. template <>
  489. void ImageBaseKnob<CairoImage>::PrivateData::cleanup()
  490. {
  491. cairo_surface_destroy((cairo_surface_t*)cairoSurface);
  492. cairoSurface = nullptr;
  493. }
  494. /**
  495. Get the pixel size in bytes.
  496. @return pixel size, or 0 if the format is unknown, or pixels are not aligned to bytes.
  497. */
  498. static int getBytesPerPixel(const cairo_format_t format) noexcept
  499. {
  500. switch (format)
  501. {
  502. case CAIRO_FORMAT_ARGB32:
  503. case CAIRO_FORMAT_RGB24:
  504. case CAIRO_FORMAT_RGB30:
  505. return 4;
  506. case CAIRO_FORMAT_RGB16_565:
  507. return 2;
  508. case CAIRO_FORMAT_A8:
  509. return 1;
  510. case CAIRO_FORMAT_A1:
  511. return 0;
  512. default:
  513. DISTRHO_SAFE_ASSERT(false);
  514. return 0;
  515. }
  516. }
  517. static cairo_surface_t* getRegion(cairo_surface_t* origsurface, int x, int y, int width, int height) noexcept
  518. {
  519. const cairo_format_t format = cairo_image_surface_get_format(origsurface);
  520. const int bpp = getBytesPerPixel(format);
  521. if (bpp == 0)
  522. return nullptr;
  523. const int fullWidth = cairo_image_surface_get_width(origsurface);
  524. const int fullHeight = cairo_image_surface_get_height(origsurface);
  525. const int stride = cairo_image_surface_get_stride(origsurface);
  526. uchar* const fullData = cairo_image_surface_get_data(origsurface);
  527. x = (x < fullWidth) ? x : fullWidth;
  528. y = (y < fullHeight) ? y : fullHeight;
  529. width = (x + width < fullWidth) ? width : (fullWidth - x);
  530. height = (x + height < fullHeight) ? height : (fullHeight - x);
  531. uchar* const data = fullData + (x * bpp + y * stride);
  532. return cairo_image_surface_create_for_data(data, format, width, height, stride);
  533. }
  534. template <>
  535. void ImageBaseKnob<CairoImage>::onDisplay()
  536. {
  537. const GraphicsContext& context(getGraphicsContext());
  538. cairo_t* const handle = ((const CairoGraphicsContext&)context).handle;
  539. const double normValue = getNormalizedValue();
  540. cairo_surface_t* surface = (cairo_surface_t*)pData->cairoSurface;
  541. if (! pData->isReady)
  542. {
  543. const int layerW = static_cast<int>(pData->imgLayerWidth);
  544. const int layerH = static_cast<int>(pData->imgLayerHeight);
  545. int layerNum = 0;
  546. if (pData->rotationAngle == 0)
  547. layerNum = static_cast<int>(normValue * static_cast<double>(pData->imgLayerCount - 1) + 0.5);
  548. const int layerX = pData->isImgVertical ? 0 : layerNum * layerW;
  549. const int layerY = !pData->isImgVertical ? 0 : layerNum * layerH;
  550. cairo_surface_t* newsurface;
  551. if (pData->rotationAngle == 0)
  552. {
  553. newsurface = getRegion(pData->image.getSurface(), layerX, layerY, layerW, layerH);
  554. }
  555. else
  556. {
  557. newsurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, layerW, layerH);
  558. cairo_t* const cr = cairo_create(newsurface);
  559. cairo_translate(cr, 0.5 * layerW, 0.5 * layerH);
  560. cairo_rotate(cr, normValue * pData->rotationAngle * (M_PI / 180));
  561. cairo_set_source_surface(cr, pData->image.getSurface(), -0.5 * layerW, -0.5 * layerH);
  562. cairo_paint(cr);
  563. cairo_destroy(cr);
  564. }
  565. DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,);
  566. cairo_surface_destroy(surface);
  567. pData->cairoSurface = surface = newsurface;
  568. pData->isReady = true;
  569. }
  570. if (surface != nullptr)
  571. {
  572. cairo_set_source_surface(handle, surface, 0, 0);
  573. cairo_paint(handle);
  574. }
  575. }
  576. template class ImageBaseKnob<CairoImage>;
  577. // -----------------------------------------------------------------------
  578. // ImageBaseSlider
  579. template class ImageBaseSlider<CairoImage>;
  580. // -----------------------------------------------------------------------
  581. // ImageBaseSwitch
  582. template class ImageBaseSwitch<CairoImage>;
  583. // -----------------------------------------------------------------------
  584. void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor)
  585. {
  586. cairo_t* const handle = static_cast<const CairoGraphicsContext&>(self->getGraphicsContext()).handle;
  587. bool needsResetClip = false;
  588. cairo_matrix_t matrix;
  589. cairo_get_matrix(handle, &matrix);
  590. if (needsViewportScaling)
  591. {
  592. // limit viewport to widget bounds
  593. // NOTE only used for nanovg for now, which is not relevant here
  594. }
  595. else if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size<uint>(width, height)))
  596. {
  597. // full viewport size
  598. cairo_translate(handle, 0, 0);
  599. cairo_scale(handle, autoScaleFactor, autoScaleFactor);
  600. }
  601. else
  602. {
  603. // set viewport pos
  604. cairo_translate(handle, absolutePos.getX() * autoScaleFactor, absolutePos.getY() * autoScaleFactor);
  605. // then cut the outer bounds
  606. cairo_rectangle(handle,
  607. 0,
  608. 0,
  609. std::round(self->getWidth() * autoScaleFactor),
  610. std::round(self->getHeight() * autoScaleFactor));
  611. cairo_clip(handle);
  612. needsResetClip = true;
  613. // set viewport scaling
  614. cairo_scale(handle, autoScaleFactor, autoScaleFactor);
  615. }
  616. // display widget
  617. self->onDisplay();
  618. if (needsResetClip)
  619. cairo_reset_clip(handle);
  620. cairo_set_matrix(handle, &matrix);
  621. selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
  622. }
  623. // -----------------------------------------------------------------------
  624. void TopLevelWidget::PrivateData::display()
  625. {
  626. if (! selfw->pData->visible)
  627. return;
  628. cairo_t* const handle = static_cast<const CairoGraphicsContext&>(self->getGraphicsContext()).handle;
  629. const Size<uint> size(window.getSize());
  630. const uint width = size.getWidth();
  631. const uint height = size.getHeight();
  632. const double autoScaleFactor = window.pData->autoScaleFactor;
  633. cairo_matrix_t matrix;
  634. cairo_get_matrix(handle, &matrix);
  635. // full viewport size
  636. if (window.pData->autoScaling)
  637. {
  638. cairo_translate(handle, 0, 0);
  639. cairo_scale(handle, autoScaleFactor, autoScaleFactor);
  640. }
  641. else
  642. {
  643. cairo_translate(handle, 0, 0);
  644. cairo_scale(handle, 1.0, 1.0);
  645. }
  646. // main widget drawing
  647. self->onDisplay();
  648. cairo_set_matrix(handle, &matrix);
  649. // now draw subwidgets if there are any
  650. selfw->pData->displaySubWidgets(width, height, autoScaleFactor);
  651. }
  652. // -----------------------------------------------------------------------
  653. void Window::PrivateData::renderToPicture(const char*, const GraphicsContext&, uint, uint)
  654. {
  655. notImplemented("Window::PrivateData::renderToPicture");
  656. }
  657. // -----------------------------------------------------------------------
  658. const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept
  659. {
  660. GraphicsContext& context((GraphicsContext&)graphicsContext);
  661. ((CairoGraphicsContext&)context).handle = (cairo_t*)puglGetContext(view);
  662. return context;
  663. }
  664. // -----------------------------------------------------------------------
  665. END_NAMESPACE_DGL