Mercurial > hg > pub > prymula > com
comparison DPF-Prymula-audioplugins/dpf/dgl/src/Cairo.cpp @ 3:84e66ea83026
DPF-Prymula-audioplugins-0.231015-2
author | prymula <prymula76@outlook.com> |
---|---|
date | Mon, 16 Oct 2023 21:53:34 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
2:cf2cb71d31dd | 3:84e66ea83026 |
---|---|
1 /* | |
2 * DISTRHO Plugin Framework (DPF) | |
3 * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com> | |
4 * Copyright (C) 2019-2021 Jean Pierre Cimalando <jp-dev@inbox.ru> | |
5 * | |
6 * Permission to use, copy, modify, and/or distribute this software for any purpose with | |
7 * or without fee is hereby granted, provided that the above copyright notice and this | |
8 * permission notice appear in all copies. | |
9 * | |
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |
11 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |
12 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |
13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |
14 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |
15 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
16 */ | |
17 | |
18 #ifdef _MSC_VER | |
19 // instantiated template classes whose methods are defined elsewhere | |
20 # pragma warning(disable:4661) | |
21 #endif | |
22 | |
23 #include "../Cairo.hpp" | |
24 #include "../Color.hpp" | |
25 #include "../ImageBaseWidgets.hpp" | |
26 | |
27 #include "SubWidgetPrivateData.hpp" | |
28 #include "TopLevelWidgetPrivateData.hpp" | |
29 #include "WidgetPrivateData.hpp" | |
30 #include "WindowPrivateData.hpp" | |
31 | |
32 // templated classes | |
33 #include "ImageBaseWidgets.cpp" | |
34 | |
35 START_NAMESPACE_DGL | |
36 | |
37 // ----------------------------------------------------------------------- | |
38 | |
39 static void notImplemented(const char* const name) | |
40 { | |
41 d_stderr2("cairo function not implemented: %s", name); | |
42 } | |
43 | |
44 // ----------------------------------------------------------------------- | |
45 // Color | |
46 | |
47 void Color::setFor(const GraphicsContext& context, const bool includeAlpha) | |
48 { | |
49 cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |
50 | |
51 if (includeAlpha) | |
52 cairo_set_source_rgba(handle, red, green, blue, alpha); | |
53 else | |
54 cairo_set_source_rgb(handle, red, green, blue); | |
55 } | |
56 | |
57 // ----------------------------------------------------------------------- | |
58 // Line | |
59 | |
60 template<typename T> | |
61 void Line<T>::draw(const GraphicsContext& context, const T width) | |
62 { | |
63 DISTRHO_SAFE_ASSERT_RETURN(posStart != posEnd,); | |
64 DISTRHO_SAFE_ASSERT_RETURN(width != 0,); | |
65 | |
66 cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |
67 | |
68 cairo_set_line_width(handle, width); | |
69 cairo_move_to(handle, posStart.getX(), posStart.getY()); | |
70 cairo_line_to(handle, posEnd.getX(), posEnd.getY()); | |
71 cairo_stroke(handle); | |
72 } | |
73 | |
74 template<typename T> | |
75 void Line<T>::draw() | |
76 { | |
77 notImplemented("Line::draw"); | |
78 } | |
79 | |
80 template class Line<double>; | |
81 template class Line<float>; | |
82 template class Line<int>; | |
83 template class Line<uint>; | |
84 template class Line<short>; | |
85 template class Line<ushort>; | |
86 | |
87 // ----------------------------------------------------------------------- | |
88 // Circle | |
89 | |
90 template<typename T> | |
91 static void drawCircle(cairo_t* const handle, | |
92 const Point<T>& pos, | |
93 const uint numSegments, | |
94 const float size, | |
95 const float sin, | |
96 const float cos, | |
97 const bool outline) | |
98 { | |
99 DISTRHO_SAFE_ASSERT_RETURN(numSegments >= 3 && size > 0.0f,); | |
100 | |
101 const T origx = pos.getX(); | |
102 const T origy = pos.getY(); | |
103 double t, x = size, y = 0.0; | |
104 | |
105 // TODO use arc | |
106 /* | |
107 cairo_arc(handle, origx, origy, size, sin, cos); | |
108 */ | |
109 | |
110 cairo_move_to(handle, x + origx, y + origy); | |
111 | |
112 for (uint i=1; i<numSegments; ++i) | |
113 { | |
114 cairo_line_to(handle, x + origx, y + origy); | |
115 | |
116 t = x; | |
117 x = cos * x - sin * y; | |
118 y = sin * t + cos * y; | |
119 } | |
120 | |
121 cairo_line_to(handle, x + origx, y + origy); | |
122 | |
123 if (outline) | |
124 cairo_stroke(handle); | |
125 else | |
126 cairo_fill(handle); | |
127 } | |
128 | |
129 template<typename T> | |
130 void Circle<T>::draw(const GraphicsContext& context) | |
131 { | |
132 cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |
133 | |
134 drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, false); | |
135 } | |
136 | |
137 template<typename T> | |
138 void Circle<T>::drawOutline(const GraphicsContext& context, const T lineWidth) | |
139 { | |
140 DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |
141 | |
142 cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |
143 | |
144 cairo_set_line_width(handle, lineWidth); | |
145 drawCircle<T>(handle, fPos, fNumSegments, fSize, fSin, fCos, true); | |
146 } | |
147 | |
148 template<typename T> | |
149 void Circle<T>::draw() | |
150 { | |
151 notImplemented("Circle::draw"); | |
152 } | |
153 | |
154 template<typename T> | |
155 void Circle<T>::drawOutline() | |
156 { | |
157 notImplemented("Circle::drawOutline"); | |
158 } | |
159 | |
160 template class Circle<double>; | |
161 template class Circle<float>; | |
162 template class Circle<int>; | |
163 template class Circle<uint>; | |
164 template class Circle<short>; | |
165 template class Circle<ushort>; | |
166 | |
167 // ----------------------------------------------------------------------- | |
168 // Triangle | |
169 | |
170 template<typename T> | |
171 static void drawTriangle(cairo_t* const handle, | |
172 const Point<T>& pos1, | |
173 const Point<T>& pos2, | |
174 const Point<T>& pos3, | |
175 const bool outline) | |
176 { | |
177 DISTRHO_SAFE_ASSERT_RETURN(pos1 != pos2 && pos1 != pos3,); | |
178 | |
179 cairo_move_to(handle, pos1.getX(), pos1.getY()); | |
180 cairo_line_to(handle, pos2.getX(), pos2.getY()); | |
181 cairo_line_to(handle, pos3.getX(), pos3.getY()); | |
182 cairo_line_to(handle, pos1.getX(), pos1.getY()); | |
183 | |
184 if (outline) | |
185 cairo_stroke(handle); | |
186 else | |
187 cairo_fill(handle); | |
188 } | |
189 | |
190 template<typename T> | |
191 void Triangle<T>::draw(const GraphicsContext& context) | |
192 { | |
193 cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |
194 | |
195 drawTriangle<T>(handle, pos1, pos2, pos3, false); | |
196 } | |
197 | |
198 template<typename T> | |
199 void Triangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth) | |
200 { | |
201 DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |
202 | |
203 cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |
204 | |
205 cairo_set_line_width(handle, lineWidth); | |
206 drawTriangle<T>(handle, pos1, pos2, pos3, true); | |
207 } | |
208 | |
209 template<typename T> | |
210 void Triangle<T>::draw() | |
211 { | |
212 notImplemented("Triangle::draw"); | |
213 } | |
214 | |
215 template<typename T> | |
216 void Triangle<T>::drawOutline() | |
217 { | |
218 notImplemented("Triangle::drawOutline"); | |
219 } | |
220 | |
221 template class Triangle<double>; | |
222 template class Triangle<float>; | |
223 template class Triangle<int>; | |
224 template class Triangle<uint>; | |
225 template class Triangle<short>; | |
226 template class Triangle<ushort>; | |
227 | |
228 // ----------------------------------------------------------------------- | |
229 // Rectangle | |
230 | |
231 template<typename T> | |
232 static void drawRectangle(cairo_t* const handle, const Rectangle<T>& rect, const bool outline) | |
233 { | |
234 cairo_rectangle(handle, rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); | |
235 | |
236 if (outline) | |
237 cairo_stroke(handle); | |
238 else | |
239 cairo_fill(handle); | |
240 } | |
241 | |
242 template<typename T> | |
243 void Rectangle<T>::draw(const GraphicsContext& context) | |
244 { | |
245 DISTRHO_SAFE_ASSERT_RETURN(isValid(),); | |
246 | |
247 cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |
248 | |
249 drawRectangle(handle, *this, false); | |
250 } | |
251 | |
252 template<typename T> | |
253 void Rectangle<T>::drawOutline(const GraphicsContext& context, const T lineWidth) | |
254 { | |
255 DISTRHO_SAFE_ASSERT_RETURN(isValid(),); | |
256 DISTRHO_SAFE_ASSERT_RETURN(lineWidth != 0,); | |
257 | |
258 cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |
259 | |
260 cairo_set_line_width(handle, lineWidth); | |
261 drawRectangle(handle, *this, true); | |
262 } | |
263 | |
264 template<typename T> | |
265 void Rectangle<T>::draw() | |
266 { | |
267 notImplemented("Rectangle::draw"); | |
268 } | |
269 | |
270 template<typename T> | |
271 void Rectangle<T>::drawOutline() | |
272 { | |
273 notImplemented("Rectangle::drawOutline"); | |
274 } | |
275 | |
276 template class Rectangle<double>; | |
277 template class Rectangle<float>; | |
278 template class Rectangle<int>; | |
279 template class Rectangle<uint>; | |
280 template class Rectangle<short>; | |
281 template class Rectangle<ushort>; | |
282 | |
283 // ----------------------------------------------------------------------- | |
284 // CairoImage | |
285 | |
286 static cairo_format_t asCairoImageFormat(const ImageFormat format) noexcept | |
287 { | |
288 switch (format) | |
289 { | |
290 case kImageFormatNull: | |
291 break; | |
292 case kImageFormatGrayscale: | |
293 return CAIRO_FORMAT_A8; | |
294 case kImageFormatBGR: | |
295 case kImageFormatRGB: | |
296 return CAIRO_FORMAT_RGB24; | |
297 case kImageFormatBGRA: | |
298 case kImageFormatRGBA: | |
299 return CAIRO_FORMAT_ARGB32; | |
300 } | |
301 | |
302 return CAIRO_FORMAT_INVALID; | |
303 } | |
304 | |
305 /* | |
306 static ImageFormat asCairoImageFormat(const cairo_format_t format) noexcept | |
307 { | |
308 switch (format) | |
309 { | |
310 case CAIRO_FORMAT_INVALID: | |
311 break; | |
312 case CAIRO_FORMAT_ARGB32: | |
313 break; | |
314 case CAIRO_FORMAT_RGB24: | |
315 break; | |
316 case CAIRO_FORMAT_A8: | |
317 break; | |
318 case CAIRO_FORMAT_A1: | |
319 break; | |
320 case CAIRO_FORMAT_RGB16_565: | |
321 break; | |
322 case CAIRO_FORMAT_RGB30: | |
323 break; | |
324 } | |
325 | |
326 return kImageFormatNull; | |
327 } | |
328 */ | |
329 | |
330 CairoImage::CairoImage() | |
331 : ImageBase(), | |
332 surface(nullptr), | |
333 surfacedata(nullptr), | |
334 datarefcount(nullptr) {} | |
335 | |
336 CairoImage::CairoImage(const char* const rdata, const uint w, const uint h, const ImageFormat fmt) | |
337 : ImageBase(rdata, w, h, fmt), | |
338 surface(nullptr), | |
339 surfacedata(nullptr), | |
340 datarefcount(nullptr) | |
341 { | |
342 loadFromMemory(rdata, w, h, fmt); | |
343 } | |
344 | |
345 CairoImage::CairoImage(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) | |
346 : ImageBase(rdata, s, fmt), | |
347 surface(nullptr), | |
348 surfacedata(nullptr), | |
349 datarefcount(nullptr) | |
350 { | |
351 loadFromMemory(rdata, s, fmt); | |
352 } | |
353 | |
354 CairoImage::CairoImage(const CairoImage& image) | |
355 : ImageBase(image.rawData, image.size, image.format), | |
356 surface(cairo_surface_reference(image.surface)), | |
357 surfacedata(image.surfacedata), | |
358 datarefcount(image.datarefcount) | |
359 { | |
360 if (datarefcount != nullptr) | |
361 ++(*datarefcount); | |
362 } | |
363 | |
364 CairoImage::~CairoImage() | |
365 { | |
366 cairo_surface_destroy(surface); | |
367 | |
368 if (datarefcount != nullptr && --(*datarefcount) == 0) | |
369 { | |
370 std::free(surfacedata); | |
371 std::free(datarefcount); | |
372 } | |
373 } | |
374 | |
375 void CairoImage::loadFromMemory(const char* const rdata, const Size<uint>& s, const ImageFormat fmt) noexcept | |
376 { | |
377 const cairo_format_t cairoformat = asCairoImageFormat(fmt); | |
378 const int width = static_cast<int>(s.getWidth()); | |
379 const int height = static_cast<int>(s.getHeight()); | |
380 const int stride = cairo_format_stride_for_width(cairoformat, width); | |
381 | |
382 uchar* const newdata = (uchar*)std::malloc(static_cast<size_t>(width * height * stride * 4)); | |
383 DISTRHO_SAFE_ASSERT_RETURN(newdata != nullptr,); | |
384 | |
385 cairo_surface_t* const newsurface = cairo_image_surface_create_for_data(newdata, cairoformat, width, height, stride); | |
386 DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,); | |
387 DISTRHO_SAFE_ASSERT_RETURN(static_cast<int>(s.getWidth()) == cairo_image_surface_get_width(newsurface),); | |
388 DISTRHO_SAFE_ASSERT_RETURN(static_cast<int>(s.getHeight()) == cairo_image_surface_get_height(newsurface),); | |
389 | |
390 cairo_surface_destroy(surface); | |
391 | |
392 if (datarefcount != nullptr && --(*datarefcount) == 0) | |
393 std::free(surfacedata); | |
394 else | |
395 datarefcount = (int*)malloc(sizeof(*datarefcount)); | |
396 | |
397 surface = newsurface; | |
398 surfacedata = newdata; | |
399 *datarefcount = 1; | |
400 | |
401 switch (fmt) | |
402 { | |
403 case kImageFormatNull: | |
404 break; | |
405 case kImageFormatGrayscale: | |
406 // Grayscale to A8 | |
407 // TODO | |
408 break; | |
409 case kImageFormatBGR: | |
410 // BGR8 to CAIRO_FORMAT_RGB24 | |
411 for (int h = 0; h < height; ++h) | |
412 { | |
413 for (int w = 0; w < width; ++w) | |
414 { | |
415 newdata[h*width*4+w*4+0] = static_cast<uchar>(rdata[h*width*3+w*3+0]); | |
416 newdata[h*width*4+w*4+1] = static_cast<uchar>(rdata[h*width*3+w*3+1]); | |
417 newdata[h*width*4+w*4+2] = static_cast<uchar>(rdata[h*width*3+w*3+2]); | |
418 newdata[h*width*4+w*4+3] = 0; | |
419 } | |
420 } | |
421 break; | |
422 case kImageFormatBGRA: | |
423 // BGRA8 to CAIRO_FORMAT_ARGB32 | |
424 // FIXME something is wrong here... | |
425 for (int h = 0, t; h < height; ++h) | |
426 { | |
427 for (int w = 0; w < width; ++w) | |
428 { | |
429 if ((t = rdata[h*width*4+w*4+3]) != 0) | |
430 { | |
431 newdata[h*width*4+w*4+0] = static_cast<uchar>(rdata[h*width*4+w*4+0]); | |
432 newdata[h*width*4+w*4+1] = static_cast<uchar>(rdata[h*width*4+w*4+1]); | |
433 newdata[h*width*4+w*4+2] = static_cast<uchar>(rdata[h*width*4+w*4+2]); | |
434 newdata[h*width*4+w*4+3] = static_cast<uchar>(t); | |
435 } | |
436 else | |
437 { | |
438 // make all pixels zero, cairo does not render full transparency otherwise | |
439 memset(&newdata[h*width*4+w*4], 0, 4); | |
440 } | |
441 } | |
442 } | |
443 break; | |
444 case kImageFormatRGB: | |
445 // RGB8 to CAIRO_FORMAT_RGB24 | |
446 // TODO | |
447 break; | |
448 case kImageFormatRGBA: | |
449 // RGBA8 to CAIRO_FORMAT_ARGB32 | |
450 // TODO | |
451 break; | |
452 } | |
453 | |
454 ImageBase::loadFromMemory(rdata, s, fmt); | |
455 } | |
456 | |
457 // const GraphicsContext& context | |
458 void CairoImage::loadFromPNG(const char* const pngData, const uint pngSize) noexcept | |
459 { | |
460 struct PngReaderData | |
461 { | |
462 const char* dataPtr; | |
463 uint sizeLeft; | |
464 | |
465 static cairo_status_t read(void* const closure, uchar* const data, const uint length) noexcept | |
466 { | |
467 PngReaderData& readerData = *reinterpret_cast<PngReaderData*>(closure); | |
468 | |
469 if (readerData.sizeLeft < length) | |
470 return CAIRO_STATUS_READ_ERROR; | |
471 | |
472 std::memcpy(data, readerData.dataPtr, length); | |
473 readerData.dataPtr += length; | |
474 readerData.sizeLeft -= length; | |
475 return CAIRO_STATUS_SUCCESS; | |
476 } | |
477 }; | |
478 | |
479 PngReaderData readerData; | |
480 readerData.dataPtr = pngData; | |
481 readerData.sizeLeft = pngSize; | |
482 | |
483 cairo_surface_t* const newsurface = cairo_image_surface_create_from_png_stream(PngReaderData::read, &readerData); | |
484 DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,); | |
485 | |
486 const int newwidth = cairo_image_surface_get_width(newsurface); | |
487 const int newheight = cairo_image_surface_get_height(newsurface); | |
488 DISTRHO_SAFE_ASSERT_INT_RETURN(newwidth > 0, newwidth,); | |
489 DISTRHO_SAFE_ASSERT_INT_RETURN(newheight > 0, newheight,); | |
490 | |
491 cairo_surface_destroy(surface); | |
492 | |
493 if (datarefcount != nullptr && --(*datarefcount) == 0) | |
494 std::free(surfacedata); | |
495 else | |
496 datarefcount = (int*)malloc(sizeof(*datarefcount)); | |
497 | |
498 surface = newsurface; | |
499 surfacedata = nullptr; // cairo_image_surface_get_data(newsurface); | |
500 *datarefcount = 1; | |
501 | |
502 rawData = nullptr; | |
503 format = kImageFormatNull; // asCairoImageFormat(cairo_image_surface_get_format(newsurface)); | |
504 size = Size<uint>(static_cast<uint>(newwidth), static_cast<uint>(newheight)); | |
505 } | |
506 | |
507 void CairoImage::drawAt(const GraphicsContext& context, const Point<int>& pos) | |
508 { | |
509 if (surface == nullptr) | |
510 return; | |
511 | |
512 cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |
513 | |
514 cairo_set_source_surface(handle, surface, pos.getX(), pos.getY()); | |
515 cairo_paint(handle); | |
516 } | |
517 | |
518 CairoImage& CairoImage::operator=(const CairoImage& image) noexcept | |
519 { | |
520 cairo_surface_t* newsurface = cairo_surface_reference(image.surface); | |
521 cairo_surface_destroy(surface); | |
522 | |
523 if (datarefcount != nullptr && --(*datarefcount) == 0) | |
524 { | |
525 std::free(surfacedata); | |
526 std::free(datarefcount); | |
527 } | |
528 | |
529 surface = newsurface; | |
530 rawData = image.rawData; | |
531 size = image.size; | |
532 format = image.format; | |
533 surfacedata = image.surfacedata; | |
534 datarefcount = image.datarefcount; | |
535 | |
536 if (datarefcount != nullptr) | |
537 ++(*datarefcount); | |
538 | |
539 return *this; | |
540 } | |
541 | |
542 // ----------------------------------------------------------------------- | |
543 // CairoSubWidget | |
544 | |
545 template <> | |
546 CairoBaseWidget<SubWidget>::CairoBaseWidget(Widget* const parent) | |
547 : SubWidget(parent) {} | |
548 | |
549 template class CairoBaseWidget<SubWidget>; | |
550 | |
551 // ----------------------------------------------------------------------- | |
552 // CairoTopLevelWidget | |
553 | |
554 template <> | |
555 CairoBaseWidget<TopLevelWidget>::CairoBaseWidget(Window& windowToMapTo) | |
556 : TopLevelWidget(windowToMapTo) {} | |
557 | |
558 template class CairoBaseWidget<TopLevelWidget>; | |
559 | |
560 // ----------------------------------------------------------------------- | |
561 // CairoStandaloneWindow | |
562 | |
563 template <> | |
564 CairoBaseWidget<StandaloneWindow>::CairoBaseWidget(Application& app) | |
565 : StandaloneWindow(app) {} | |
566 | |
567 template <> | |
568 CairoBaseWidget<StandaloneWindow>::CairoBaseWidget(Application& app, Window& parentWindow) | |
569 : StandaloneWindow(app, parentWindow) {} | |
570 | |
571 template class CairoBaseWidget<StandaloneWindow>; | |
572 | |
573 // ----------------------------------------------------------------------- | |
574 // ImageBaseAboutWindow | |
575 | |
576 #if 0 | |
577 template <> | |
578 void ImageBaseAboutWindow<CairoImage>::onDisplay() | |
579 { | |
580 img.draw(getGraphicsContext()); | |
581 } | |
582 #endif | |
583 | |
584 template class ImageBaseAboutWindow<CairoImage>; | |
585 | |
586 // ----------------------------------------------------------------------- | |
587 // ImageBaseButton | |
588 | |
589 template class ImageBaseButton<CairoImage>; | |
590 | |
591 // ----------------------------------------------------------------------- | |
592 // ImageBaseKnob | |
593 | |
594 template <> | |
595 void ImageBaseKnob<CairoImage>::PrivateData::init() | |
596 { | |
597 alwaysRepaint = true; | |
598 cairoSurface = nullptr; | |
599 } | |
600 | |
601 template <> | |
602 void ImageBaseKnob<CairoImage>::PrivateData::cleanup() | |
603 { | |
604 cairo_surface_destroy((cairo_surface_t*)cairoSurface); | |
605 cairoSurface = nullptr; | |
606 } | |
607 | |
608 /** | |
609 Get the pixel size in bytes. | |
610 @return pixel size, or 0 if the format is unknown, or pixels are not aligned to bytes. | |
611 */ | |
612 static int getBytesPerPixel(const cairo_format_t format) noexcept | |
613 { | |
614 switch (format) | |
615 { | |
616 case CAIRO_FORMAT_ARGB32: | |
617 case CAIRO_FORMAT_RGB24: | |
618 case CAIRO_FORMAT_RGB30: | |
619 return 4; | |
620 case CAIRO_FORMAT_RGB16_565: | |
621 return 2; | |
622 case CAIRO_FORMAT_A8: | |
623 return 1; | |
624 case CAIRO_FORMAT_A1: | |
625 return 0; | |
626 default: | |
627 DISTRHO_SAFE_ASSERT(false); | |
628 return 0; | |
629 } | |
630 } | |
631 | |
632 static cairo_surface_t* getRegion(cairo_surface_t* origsurface, int x, int y, int width, int height) noexcept | |
633 { | |
634 const cairo_format_t format = cairo_image_surface_get_format(origsurface); | |
635 const int bpp = getBytesPerPixel(format); | |
636 | |
637 if (bpp == 0) | |
638 return nullptr; | |
639 | |
640 const int fullWidth = cairo_image_surface_get_width(origsurface); | |
641 const int fullHeight = cairo_image_surface_get_height(origsurface); | |
642 const int stride = cairo_image_surface_get_stride(origsurface); | |
643 uchar* const fullData = cairo_image_surface_get_data(origsurface); | |
644 | |
645 x = (x < fullWidth) ? x : fullWidth; | |
646 y = (y < fullHeight) ? y : fullHeight; | |
647 width = (x + width < fullWidth) ? width : (fullWidth - x); | |
648 height = (x + height < fullHeight) ? height : (fullHeight - x); | |
649 | |
650 uchar* const data = fullData + (x * bpp + y * stride); | |
651 return cairo_image_surface_create_for_data(data, format, width, height, stride); | |
652 } | |
653 | |
654 template <> | |
655 void ImageBaseKnob<CairoImage>::onDisplay() | |
656 { | |
657 const GraphicsContext& context(getGraphicsContext()); | |
658 cairo_t* const handle = ((const CairoGraphicsContext&)context).handle; | |
659 const double normValue = getNormalizedValue(); | |
660 | |
661 cairo_surface_t* surface = (cairo_surface_t*)pData->cairoSurface; | |
662 | |
663 if (! pData->isReady) | |
664 { | |
665 const int layerW = static_cast<int>(pData->imgLayerWidth); | |
666 const int layerH = static_cast<int>(pData->imgLayerHeight); | |
667 int layerNum = 0; | |
668 | |
669 if (pData->rotationAngle == 0) | |
670 layerNum = static_cast<int>(normValue * static_cast<double>(pData->imgLayerCount - 1) + 0.5); | |
671 | |
672 const int layerX = pData->isImgVertical ? 0 : layerNum * layerW; | |
673 const int layerY = !pData->isImgVertical ? 0 : layerNum * layerH; | |
674 | |
675 cairo_surface_t* newsurface; | |
676 | |
677 if (pData->rotationAngle == 0) | |
678 { | |
679 newsurface = getRegion(pData->image.getSurface(), layerX, layerY, layerW, layerH); | |
680 } | |
681 else | |
682 { | |
683 newsurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, layerW, layerH); | |
684 cairo_t* const cr = cairo_create(newsurface); | |
685 cairo_translate(cr, 0.5 * layerW, 0.5 * layerH); | |
686 cairo_rotate(cr, normValue * pData->rotationAngle * (M_PI / 180)); | |
687 cairo_set_source_surface(cr, pData->image.getSurface(), -0.5 * layerW, -0.5 * layerH); | |
688 cairo_paint(cr); | |
689 cairo_destroy(cr); | |
690 } | |
691 | |
692 DISTRHO_SAFE_ASSERT_RETURN(newsurface != nullptr,); | |
693 | |
694 cairo_surface_destroy(surface); | |
695 pData->cairoSurface = surface = newsurface; | |
696 pData->isReady = true; | |
697 } | |
698 | |
699 if (surface != nullptr) | |
700 { | |
701 cairo_set_source_surface(handle, surface, 0, 0); | |
702 cairo_paint(handle); | |
703 } | |
704 } | |
705 | |
706 template class ImageBaseKnob<CairoImage>; | |
707 | |
708 // ----------------------------------------------------------------------- | |
709 // ImageBaseSlider | |
710 | |
711 template class ImageBaseSlider<CairoImage>; | |
712 | |
713 // ----------------------------------------------------------------------- | |
714 // ImageBaseSwitch | |
715 | |
716 template class ImageBaseSwitch<CairoImage>; | |
717 | |
718 // ----------------------------------------------------------------------- | |
719 | |
720 void SubWidget::PrivateData::display(const uint width, const uint height, const double autoScaleFactor) | |
721 { | |
722 cairo_t* const handle = static_cast<const CairoGraphicsContext&>(self->getGraphicsContext()).handle; | |
723 | |
724 bool needsResetClip = false; | |
725 | |
726 cairo_matrix_t matrix; | |
727 cairo_get_matrix(handle, &matrix); | |
728 | |
729 if (needsViewportScaling) | |
730 { | |
731 // limit viewport to widget bounds | |
732 // NOTE only used for nanovg for now, which is not relevant here | |
733 } | |
734 else if (needsFullViewportForDrawing || (absolutePos.isZero() && self->getSize() == Size<uint>(width, height))) | |
735 { | |
736 // full viewport size | |
737 cairo_translate(handle, 0, 0); | |
738 cairo_scale(handle, autoScaleFactor, autoScaleFactor); | |
739 } | |
740 else | |
741 { | |
742 // set viewport pos | |
743 cairo_translate(handle, absolutePos.getX() * autoScaleFactor, absolutePos.getY() * autoScaleFactor); | |
744 | |
745 // then cut the outer bounds | |
746 cairo_rectangle(handle, | |
747 0, | |
748 0, | |
749 std::round(self->getWidth() * autoScaleFactor), | |
750 std::round(self->getHeight() * autoScaleFactor)); | |
751 | |
752 cairo_clip(handle); | |
753 needsResetClip = true; | |
754 | |
755 // set viewport scaling | |
756 cairo_scale(handle, autoScaleFactor, autoScaleFactor); | |
757 } | |
758 | |
759 // display widget | |
760 self->onDisplay(); | |
761 | |
762 if (needsResetClip) | |
763 cairo_reset_clip(handle); | |
764 | |
765 cairo_set_matrix(handle, &matrix); | |
766 | |
767 selfw->pData->displaySubWidgets(width, height, autoScaleFactor); | |
768 } | |
769 | |
770 // ----------------------------------------------------------------------- | |
771 | |
772 void TopLevelWidget::PrivateData::display() | |
773 { | |
774 if (! selfw->pData->visible) | |
775 return; | |
776 | |
777 cairo_t* const handle = static_cast<const CairoGraphicsContext&>(self->getGraphicsContext()).handle; | |
778 | |
779 const Size<uint> size(window.getSize()); | |
780 const uint width = size.getWidth(); | |
781 const uint height = size.getHeight(); | |
782 | |
783 const double autoScaleFactor = window.pData->autoScaleFactor; | |
784 | |
785 cairo_matrix_t matrix; | |
786 cairo_get_matrix(handle, &matrix); | |
787 | |
788 // full viewport size | |
789 if (window.pData->autoScaling) | |
790 { | |
791 cairo_translate(handle, 0, 0); | |
792 cairo_scale(handle, autoScaleFactor, autoScaleFactor); | |
793 } | |
794 else | |
795 { | |
796 cairo_translate(handle, 0, 0); | |
797 cairo_scale(handle, 1.0, 1.0); | |
798 } | |
799 | |
800 // main widget drawing | |
801 self->onDisplay(); | |
802 | |
803 cairo_set_matrix(handle, &matrix); | |
804 | |
805 // now draw subwidgets if there are any | |
806 selfw->pData->displaySubWidgets(width, height, autoScaleFactor); | |
807 } | |
808 | |
809 // ----------------------------------------------------------------------- | |
810 | |
811 void Window::PrivateData::renderToPicture(const char*, const GraphicsContext&, uint, uint) | |
812 { | |
813 notImplemented("Window::PrivateData::renderToPicture"); | |
814 } | |
815 | |
816 // ----------------------------------------------------------------------- | |
817 | |
818 const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | |
819 { | |
820 GraphicsContext& context((GraphicsContext&)graphicsContext); | |
821 ((CairoGraphicsContext&)context).handle = (cairo_t*)puglGetContext(view); | |
822 return context; | |
823 } | |
824 | |
825 // ----------------------------------------------------------------------- | |
826 | |
827 END_NAMESPACE_DGL |