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.

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