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.

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