JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_RenderingHelpers.h
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce::RenderingHelpers
27{
28
29JUCE_BEGIN_IGNORE_WARNINGS_MSVC (4127)
30
31//==============================================================================
37{
38public:
39 TranslationOrTransform() = default;
40 TranslationOrTransform (Point<int> origin) noexcept : offset (origin) {}
41
43
44 AffineTransform getTransform() const noexcept
45 {
46 return isOnlyTranslated ? AffineTransform::translation (offset)
47 : complexTransform;
48 }
49
50 AffineTransform getTransformWith (const AffineTransform& userTransform) const noexcept
51 {
52 return isOnlyTranslated ? userTransform.translated (offset)
53 : userTransform.followedBy (complexTransform);
54 }
55
56 bool isIdentity() const noexcept
57 {
58 return isOnlyTranslated && offset.isOrigin();
59 }
60
61 void setOrigin (Point<int> delta) noexcept
62 {
63 if (isOnlyTranslated)
64 offset += delta;
65 else
66 complexTransform = AffineTransform::translation (delta)
67 .followedBy (complexTransform);
68 }
69
70 void addTransform (const AffineTransform& t) noexcept
71 {
72 if (isOnlyTranslated && t.isOnlyTranslation())
73 {
74 auto tx = (int) (t.getTranslationX() * 256.0f);
75 auto ty = (int) (t.getTranslationY() * 256.0f);
76
77 if (((tx | ty) & 0xf8) == 0)
78 {
79 offset += Point<int> (tx >> 8, ty >> 8);
80 return;
81 }
82 }
83
84 complexTransform = getTransformWith (t);
85 isOnlyTranslated = false;
86 isRotated = (! approximatelyEqual (complexTransform.mat01, 0.0f)
87 || ! approximatelyEqual (complexTransform.mat10, 0.0f)
88 || complexTransform.mat00 < 0
89 || complexTransform.mat11 < 0);
90 }
91
92 float getPhysicalPixelScaleFactor() const noexcept
93 {
94 return isOnlyTranslated ? 1.0f : std::sqrt (std::abs (complexTransform.getDeterminant()));
95 }
96
97 void moveOriginInDeviceSpace (Point<int> delta) noexcept
98 {
99 if (isOnlyTranslated)
100 offset += delta;
101 else
102 complexTransform = complexTransform.translated (delta);
103 }
104
105 Rectangle<int> translated (Rectangle<int> r) const noexcept
106 {
107 jassert (isOnlyTranslated);
108 return r + offset;
109 }
110
111 Rectangle<float> translated (Rectangle<float> r) const noexcept
112 {
113 jassert (isOnlyTranslated);
114 return r + offset.toFloat();
115 }
116
117 template <typename RectangleOrPoint>
118 RectangleOrPoint transformed (RectangleOrPoint r) const noexcept
119 {
120 jassert (! isOnlyTranslated);
121 return r.transformedBy (complexTransform);
122 }
123
124 template <typename Type>
125 Rectangle<Type> deviceSpaceToUserSpace (Rectangle<Type> r) const noexcept
126 {
127 return isOnlyTranslated ? r - offset
128 : r.transformedBy (complexTransform.inverted());
129 }
130
131 AffineTransform complexTransform;
132 Point<int> offset;
133 bool isOnlyTranslated = true, isRotated = false;
134};
135
136//==============================================================================
141template <class CachedGlyphType, class RenderTargetType>
143{
144public:
145 GlyphCache()
146 {
147 reset();
148 }
149
150 ~GlyphCache() override
151 {
152 getSingletonPointer() = nullptr;
153 }
154
155 static GlyphCache& getInstance()
156 {
157 auto& g = getSingletonPointer();
158
159 if (g == nullptr)
160 g = new GlyphCache();
161
162 return *g;
163 }
164
165 //==============================================================================
166 void reset()
167 {
168 const ScopedLock sl (lock);
169 glyphs.clear();
170 addNewGlyphSlots (120);
171 hits = 0;
172 misses = 0;
173 }
174
175 void drawGlyph (RenderTargetType& target, const Font& font, const int glyphNumber, Point<float> pos)
176 {
177 if (auto glyph = findOrCreateGlyph (font, glyphNumber))
178 {
179 glyph->lastAccessCount = ++accessCounter;
180 glyph->draw (target, pos);
181 }
182 }
183
184 ReferenceCountedObjectPtr<CachedGlyphType> findOrCreateGlyph (const Font& font, int glyphNumber)
185 {
186 const ScopedLock sl (lock);
187
188 if (auto g = findExistingGlyph (font, glyphNumber))
189 {
190 ++hits;
191 return g;
192 }
193
194 ++misses;
195 auto g = getGlyphForReuse();
196 jassert (g != nullptr);
197 g->generate (font, glyphNumber);
198 return g;
199 }
200
201private:
203 Atomic<int> accessCounter, hits, misses;
204 CriticalSection lock;
205
206 ReferenceCountedObjectPtr<CachedGlyphType> findExistingGlyph (const Font& font, int glyphNumber) const noexcept
207 {
208 for (auto g : glyphs)
209 if (g->glyph == glyphNumber && g->font == font)
210 return *g;
211
212 return {};
213 }
214
216 {
217 if (hits.get() + misses.get() > glyphs.size() * 16)
218 {
219 if (misses.get() * 2 > hits.get())
220 addNewGlyphSlots (32);
221
222 hits = 0;
223 misses = 0;
224 }
225
226 if (auto g = findLeastRecentlyUsedGlyph())
227 return *g;
228
229 addNewGlyphSlots (32);
230 return glyphs.getLast();
231 }
232
233 void addNewGlyphSlots (int num)
234 {
235 glyphs.ensureStorageAllocated (glyphs.size() + num);
236
237 while (--num >= 0)
238 glyphs.add (new CachedGlyphType());
239 }
240
241 CachedGlyphType* findLeastRecentlyUsedGlyph() const noexcept
242 {
243 CachedGlyphType* oldest = nullptr;
245
246 for (auto* g : glyphs)
247 {
248 if (g->lastAccessCount <= oldestCounter
249 && g->getReferenceCount() == 1)
250 {
251 oldestCounter = g->lastAccessCount;
252 oldest = g;
253 }
254 }
255
256 return oldest;
257 }
258
259 static GlyphCache*& getSingletonPointer() noexcept
260 {
261 static GlyphCache* g = nullptr;
262 return g;
263 }
264
266};
267
268//==============================================================================
273template <class RendererType>
275{
276public:
277 CachedGlyphEdgeTable() = default;
278
279 void draw (RendererType& state, Point<float> pos) const
280 {
281 if (snapToIntegerCoordinate)
282 pos.x = std::floor (pos.x + 0.5f);
283
284 if (edgeTable != nullptr)
285 state.fillEdgeTable (*edgeTable, pos.x, roundToInt (pos.y));
286 }
287
288 void generate (const Font& newFont, int glyphNumber)
289 {
290 font = newFont;
291 auto typeface = newFont.getTypefacePtr();
292 snapToIntegerCoordinate = typeface->isHinted();
293 glyph = glyphNumber;
294
295 auto fontHeight = font.getHeight();
296 edgeTable.reset (typeface->getEdgeTableForGlyph (glyphNumber,
297 AffineTransform::scale (fontHeight * font.getHorizontalScale(),
298 fontHeight), fontHeight));
299 }
300
301 Font font;
303 int glyph = 0, lastAccessCount = 0;
304 bool snapToIntegerCoordinate = false;
305
307};
308
309//==============================================================================
316{
318 : left (roundToInt (256.0f * area.getX())),
319 top (roundToInt (256.0f * area.getY())),
320 right (roundToInt (256.0f * area.getRight())),
321 bottom (roundToInt (256.0f * area.getBottom()))
322 {
323 if ((top >> 8) == (bottom >> 8))
324 {
325 topAlpha = bottom - top;
326 bottomAlpha = 0;
327 totalTop = top >> 8;
328 totalBottom = bottom = top = totalTop + 1;
329 }
330 else
331 {
332 if ((top & 255) == 0)
333 {
334 topAlpha = 0;
335 top = totalTop = (top >> 8);
336 }
337 else
338 {
339 topAlpha = 255 - (top & 255);
340 totalTop = (top >> 8);
341 top = totalTop + 1;
342 }
343
344 bottomAlpha = bottom & 255;
345 bottom >>= 8;
346 totalBottom = bottom + (bottomAlpha != 0 ? 1 : 0);
347 }
348
349 if ((left >> 8) == (right >> 8))
350 {
351 leftAlpha = right - left;
352 rightAlpha = 0;
353 totalLeft = (left >> 8);
354 totalRight = right = left = totalLeft + 1;
355 }
356 else
357 {
358 if ((left & 255) == 0)
359 {
360 leftAlpha = 0;
361 left = totalLeft = (left >> 8);
362 }
363 else
364 {
365 leftAlpha = 255 - (left & 255);
366 totalLeft = (left >> 8);
367 left = totalLeft + 1;
368 }
369
370 rightAlpha = right & 255;
371 right >>= 8;
372 totalRight = right + (rightAlpha != 0 ? 1 : 0);
373 }
374 }
375
376 template <class Callback>
377 void iterate (Callback& callback) const
378 {
379 if (topAlpha != 0) callback (totalLeft, totalTop, totalRight - totalLeft, 1, topAlpha);
380 if (bottomAlpha != 0) callback (totalLeft, bottom, totalRight - totalLeft, 1, bottomAlpha);
381 if (leftAlpha != 0) callback (totalLeft, totalTop, 1, totalBottom - totalTop, leftAlpha);
382 if (rightAlpha != 0) callback (right, totalTop, 1, totalBottom - totalTop, rightAlpha);
383
384 callback (left, top, right - left, bottom - top, 255);
385 }
386
387 inline bool isOnePixelWide() const noexcept { return right - left == 1 && leftAlpha + rightAlpha == 0; }
388
389 inline int getTopLeftCornerAlpha() const noexcept { return (topAlpha * leftAlpha) >> 8; }
390 inline int getTopRightCornerAlpha() const noexcept { return (topAlpha * rightAlpha) >> 8; }
391 inline int getBottomLeftCornerAlpha() const noexcept { return (bottomAlpha * leftAlpha) >> 8; }
392 inline int getBottomRightCornerAlpha() const noexcept { return (bottomAlpha * rightAlpha) >> 8; }
393
394 //==============================================================================
395 int left, top, right, bottom; // bounds of the solid central area, excluding anti-aliased edges
396 int totalTop, totalLeft, totalBottom, totalRight; // bounds of the total area, including edges
397 int topAlpha, leftAlpha, bottomAlpha, rightAlpha; // alpha of each anti-aliased edge
398};
399
400//==============================================================================
402namespace GradientPixelIterators
403{
405 struct Linear
406 {
407 Linear (const ColourGradient& gradient, const AffineTransform& transform,
408 const PixelARGB* colours, int numColours)
409 : lookupTable (colours),
410 numEntries (numColours)
411 {
412 jassert (numColours >= 0);
413 auto p1 = gradient.point1;
414 auto p2 = gradient.point2;
415
416 if (! transform.isIdentity())
417 {
418 auto p3 = Line<float> (p2, p1).getPointAlongLine (0.0f, 100.0f);
419
420 p1.applyTransform (transform);
421 p2.applyTransform (transform);
422 p3.applyTransform (transform);
423
424 p2 = Line<float> (p2, p3).findNearestPointTo (p1);
425 }
426
427 vertical = std::abs (p1.x - p2.x) < 0.001f;
428 horizontal = std::abs (p1.y - p2.y) < 0.001f;
429
430 if (vertical)
431 {
432 scale = roundToInt ((double) ((int64_t) numEntries << (int) numScaleBits) / (double) (p2.y - p1.y));
433 start = roundToInt (p1.y * (float) scale);
434 }
435 else if (horizontal)
436 {
437 scale = roundToInt ((double) ((int64_t) numEntries << (int) numScaleBits) / (double) (p2.x - p1.x));
438 start = roundToInt (p1.x * (float) scale);
439 }
440 else
441 {
442 grad = (p2.getY() - p1.y) / (double) (p1.x - p2.x);
443 yTerm = p1.getY() - p1.x / grad;
444 scale = roundToInt ((double) ((int64_t) numEntries << (int) numScaleBits) / (yTerm * grad - (p2.y * grad - p2.x)));
445 grad *= scale;
446 }
447 }
448
449 forcedinline void setY (int y) noexcept
450 {
451 if (vertical)
452 linePix = lookupTable[jlimit (0, numEntries, (y * scale - start) >> (int) numScaleBits)];
453 else if (! horizontal)
454 start = roundToInt ((y - yTerm) * grad);
455 }
456
457 inline PixelARGB getPixel (int x) const noexcept
458 {
459 return vertical ? linePix
460 : lookupTable[jlimit (0, numEntries, (x * scale - start) >> (int) numScaleBits)];
461 }
462
463 const PixelARGB* const lookupTable;
464 const int numEntries;
465 PixelARGB linePix;
466 int start, scale;
467 double grad, yTerm;
468 bool vertical, horizontal;
469 enum { numScaleBits = 12 };
470
472 };
473
474 //==============================================================================
476 struct Radial
477 {
478 Radial (const ColourGradient& gradient, const AffineTransform&,
479 const PixelARGB* colours, int numColours)
480 : lookupTable (colours),
481 numEntries (numColours),
482 gx1 (gradient.point1.x),
483 gy1 (gradient.point1.y)
484 {
485 jassert (numColours >= 0);
486 auto diff = gradient.point1 - gradient.point2;
487 maxDist = diff.x * diff.x + diff.y * diff.y;
488 invScale = numEntries / std::sqrt (maxDist);
489 jassert (roundToInt (std::sqrt (maxDist) * invScale) <= numEntries);
490 }
491
492 forcedinline void setY (int y) noexcept
493 {
494 dy = y - gy1;
495 dy *= dy;
496 }
497
498 inline PixelARGB getPixel (int px) const noexcept
499 {
500 auto x = px - gx1;
501 x *= x;
502 x += dy;
503
504 return lookupTable[x >= maxDist ? numEntries : roundToInt (std::sqrt (x) * invScale)];
505 }
506
507 const PixelARGB* const lookupTable;
508 const int numEntries;
509 const double gx1, gy1;
510 double maxDist, invScale, dy;
511
513 };
514
515 //==============================================================================
517 struct TransformedRadial : public Radial
518 {
519 TransformedRadial (const ColourGradient& gradient, const AffineTransform& transform,
520 const PixelARGB* colours, int numColours)
521 : Radial (gradient, transform, colours, numColours),
522 inverseTransform (transform.inverted())
523 {
524 tM10 = inverseTransform.mat10;
525 tM00 = inverseTransform.mat00;
526 }
527
528 forcedinline void setY (int y) noexcept
529 {
530 auto floatY = (float) y;
531 lineYM01 = inverseTransform.mat01 * floatY + inverseTransform.mat02 - gx1;
532 lineYM11 = inverseTransform.mat11 * floatY + inverseTransform.mat12 - gy1;
533 }
534
535 inline PixelARGB getPixel (int px) const noexcept
536 {
537 double x = px;
538 auto y = tM10 * x + lineYM11;
539 x = tM00 * x + lineYM01;
540 x *= x;
541 x += y * y;
542
543 if (x >= maxDist)
544 return lookupTable[numEntries];
545
546 return lookupTable[jmin (numEntries, roundToInt (std::sqrt (x) * invScale))];
547 }
548
549 private:
550 double tM10, tM00, lineYM01, lineYM11;
551 const AffineTransform inverseTransform;
552
554 };
555}
556
557#define JUCE_PERFORM_PIXEL_OP_LOOP(op) \
558{ \
559 const int destStride = destData.pixelStride; \
560 do { dest->op; dest = addBytesToPointer (dest, destStride); } while (--width > 0); \
561}
562
563//==============================================================================
565namespace EdgeTableFillers
566{
568 template <class PixelType, bool replaceExisting = false>
569 struct SolidColour
570 {
571 SolidColour (const Image::BitmapData& image, PixelARGB colour)
572 : destData (image), sourceColour (colour)
573 {
574 if (sizeof (PixelType) == 3 && (size_t) destData.pixelStride == sizeof (PixelType))
575 areRGBComponentsEqual = sourceColour.getRed() == sourceColour.getGreen()
576 && sourceColour.getGreen() == sourceColour.getBlue();
577 else
578 areRGBComponentsEqual = false;
579 }
580
581 forcedinline void setEdgeTableYPos (int y) noexcept
582 {
583 linePixels = (PixelType*) destData.getLinePointer (y);
584 }
585
586 forcedinline void handleEdgeTablePixel (int x, int alphaLevel) const noexcept
587 {
588 if (replaceExisting)
589 getPixel (x)->set (sourceColour);
590 else
591 getPixel (x)->blend (sourceColour, (uint32) alphaLevel);
592 }
593
594 forcedinline void handleEdgeTablePixelFull (int x) const noexcept
595 {
596 if (replaceExisting)
597 getPixel (x)->set (sourceColour);
598 else
599 getPixel (x)->blend (sourceColour);
600 }
601
602 forcedinline void handleEdgeTableLine (int x, int width, int alphaLevel) const noexcept
603 {
604 auto p = sourceColour;
605 p.multiplyAlpha (alphaLevel);
606
607 auto* dest = getPixel (x);
608
609 if (replaceExisting || p.getAlpha() >= 0xff)
610 replaceLine (dest, p, width);
611 else
612 blendLine (dest, p, width);
613 }
614
615 forcedinline void handleEdgeTableLineFull (int x, int width) const noexcept
616 {
617 auto* dest = getPixel (x);
618
619 if (replaceExisting || sourceColour.getAlpha() >= 0xff)
620 replaceLine (dest, sourceColour, width);
621 else
622 blendLine (dest, sourceColour, width);
623 }
624
625 void handleEdgeTableRectangle (int x, int y, int width, int height, int alphaLevel) noexcept
626 {
627 auto p = sourceColour;
628 p.multiplyAlpha (alphaLevel);
629
630 setEdgeTableYPos (y);
631 auto* dest = getPixel (x);
632
633 if (replaceExisting || p.getAlpha() >= 0xff)
634 {
635 while (--height >= 0)
636 {
637 replaceLine (dest, p, width);
638 dest = addBytesToPointer (dest, destData.lineStride);
639 }
640 }
641 else
642 {
643 while (--height >= 0)
644 {
645 blendLine (dest, p, width);
646 dest = addBytesToPointer (dest, destData.lineStride);
647 }
648 }
649 }
650
651 void handleEdgeTableRectangleFull (int x, int y, int width, int height) noexcept
652 {
653 handleEdgeTableRectangle (x, y, width, height, 255);
654 }
655
656 private:
657 const Image::BitmapData& destData;
658 PixelType* linePixels;
659 PixelARGB sourceColour;
660 bool areRGBComponentsEqual;
661
662 forcedinline PixelType* getPixel (int x) const noexcept
663 {
664 return addBytesToPointer (linePixels, x * destData.pixelStride);
665 }
666
667 inline void blendLine (PixelType* dest, PixelARGB colour, int width) const noexcept
668 {
669 JUCE_PERFORM_PIXEL_OP_LOOP (blend (colour))
670 }
671
672 forcedinline void replaceLine (PixelRGB* dest, PixelARGB colour, int width) const noexcept
673 {
674 if ((size_t) destData.pixelStride == sizeof (*dest) && areRGBComponentsEqual)
675 memset ((void*) dest, colour.getRed(), (size_t) width * 3); // if all the component values are the same, we can cheat..
676 else
677 JUCE_PERFORM_PIXEL_OP_LOOP (set (colour));
678 }
679
680 forcedinline void replaceLine (PixelAlpha* dest, const PixelARGB colour, int width) const noexcept
681 {
682 if ((size_t) destData.pixelStride == sizeof (*dest))
683 memset ((void*) dest, colour.getAlpha(), (size_t) width);
684 else
685 JUCE_PERFORM_PIXEL_OP_LOOP (setAlpha (colour.getAlpha()))
686 }
687
688 forcedinline void replaceLine (PixelARGB* dest, const PixelARGB colour, int width) const noexcept
689 {
690 JUCE_PERFORM_PIXEL_OP_LOOP (set (colour))
691 }
692
693 JUCE_DECLARE_NON_COPYABLE (SolidColour)
694 };
695
696 //==============================================================================
698 template <class PixelType, class GradientType>
699 struct Gradient : public GradientType
700 {
701 Gradient (const Image::BitmapData& dest, const ColourGradient& gradient, const AffineTransform& transform,
702 const PixelARGB* colours, int numColours)
703 : GradientType (gradient, transform, colours, numColours - 1),
704 destData (dest)
705 {
706 }
707
708 forcedinline void setEdgeTableYPos (int y) noexcept
709 {
710 linePixels = (PixelType*) destData.getLinePointer (y);
711 GradientType::setY (y);
712 }
713
714 forcedinline void handleEdgeTablePixel (int x, int alphaLevel) const noexcept
715 {
716 getPixel (x)->blend (GradientType::getPixel (x), (uint32) alphaLevel);
717 }
718
719 forcedinline void handleEdgeTablePixelFull (int x) const noexcept
720 {
721 getPixel (x)->blend (GradientType::getPixel (x));
722 }
723
724 void handleEdgeTableLine (int x, int width, int alphaLevel) const noexcept
725 {
726 auto* dest = getPixel (x);
727
728 if (alphaLevel < 0xff)
729 JUCE_PERFORM_PIXEL_OP_LOOP (blend (GradientType::getPixel (x++), (uint32) alphaLevel))
730 else
731 JUCE_PERFORM_PIXEL_OP_LOOP (blend (GradientType::getPixel (x++)))
732 }
733
734 void handleEdgeTableLineFull (int x, int width) const noexcept
735 {
736 auto* dest = getPixel (x);
737 JUCE_PERFORM_PIXEL_OP_LOOP (blend (GradientType::getPixel (x++)))
738 }
739
740 void handleEdgeTableRectangle (int x, int y, int width, int height, int alphaLevel) noexcept
741 {
742 while (--height >= 0)
743 {
744 setEdgeTableYPos (y++);
745 handleEdgeTableLine (x, width, alphaLevel);
746 }
747 }
748
749 void handleEdgeTableRectangleFull (int x, int y, int width, int height) noexcept
750 {
751 while (--height >= 0)
752 {
753 setEdgeTableYPos (y++);
754 handleEdgeTableLineFull (x, width);
755 }
756 }
757
758 private:
759 const Image::BitmapData& destData;
760 PixelType* linePixels;
761
762 forcedinline PixelType* getPixel (int x) const noexcept
763 {
764 return addBytesToPointer (linePixels, x * destData.pixelStride);
765 }
766
768 };
769
770 //==============================================================================
772 template <class DestPixelType, class SrcPixelType, bool repeatPattern>
773 struct ImageFill
774 {
775 ImageFill (const Image::BitmapData& dest, const Image::BitmapData& src, int alpha, int x, int y)
776 : destData (dest),
777 srcData (src),
778 extraAlpha (alpha + 1),
779 xOffset (repeatPattern ? negativeAwareModulo (x, src.width) - src.width : x),
780 yOffset (repeatPattern ? negativeAwareModulo (y, src.height) - src.height : y)
781 {
782 }
783
784 forcedinline void setEdgeTableYPos (int y) noexcept
785 {
786 linePixels = (DestPixelType*) destData.getLinePointer (y);
787 y -= yOffset;
788
789 if (repeatPattern)
790 {
791 jassert (y >= 0);
792 y %= srcData.height;
793 }
794
795 sourceLineStart = (SrcPixelType*) srcData.getLinePointer (y);
796 }
797
798 forcedinline void handleEdgeTablePixel (int x, int alphaLevel) const noexcept
799 {
800 alphaLevel = (alphaLevel * extraAlpha) >> 8;
801
802 getDestPixel (x)->blend (*getSrcPixel (repeatPattern ? ((x - xOffset) % srcData.width) : (x - xOffset)), (uint32) alphaLevel);
803 }
804
805 forcedinline void handleEdgeTablePixelFull (int x) const noexcept
806 {
807 getDestPixel (x)->blend (*getSrcPixel (repeatPattern ? ((x - xOffset) % srcData.width) : (x - xOffset)), (uint32) extraAlpha);
808 }
809
810 void handleEdgeTableLine (int x, int width, int alphaLevel) const noexcept
811 {
812 auto* dest = getDestPixel (x);
813 alphaLevel = (alphaLevel * extraAlpha) >> 8;
814 x -= xOffset;
815
816 if (repeatPattern)
817 {
818 if (alphaLevel < 0xfe)
819 JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width), (uint32) alphaLevel))
820 else
821 JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width)))
822 }
823 else
824 {
825 jassert (x >= 0 && x + width <= srcData.width);
826
827 if (alphaLevel < 0xfe)
828 JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++), (uint32) alphaLevel))
829 else
830 copyRow (dest, getSrcPixel (x), width);
831 }
832 }
833
834 void handleEdgeTableLineFull (int x, int width) const noexcept
835 {
836 auto* dest = getDestPixel (x);
837 x -= xOffset;
838
839 if (repeatPattern)
840 {
841 if (extraAlpha < 0xfe)
842 JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width), (uint32) extraAlpha))
843 else
844 JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++ % srcData.width)))
845 }
846 else
847 {
848 jassert (x >= 0 && x + width <= srcData.width);
849
850 if (extraAlpha < 0xfe)
851 JUCE_PERFORM_PIXEL_OP_LOOP (blend (*getSrcPixel (x++), (uint32) extraAlpha))
852 else
853 copyRow (dest, getSrcPixel (x), width);
854 }
855 }
856
857 void handleEdgeTableRectangle (int x, int y, int width, int height, int alphaLevel) noexcept
858 {
859 while (--height >= 0)
860 {
861 setEdgeTableYPos (y++);
862 handleEdgeTableLine (x, width, alphaLevel);
863 }
864 }
865
866 void handleEdgeTableRectangleFull (int x, int y, int width, int height) noexcept
867 {
868 while (--height >= 0)
869 {
870 setEdgeTableYPos (y++);
871 handleEdgeTableLineFull (x, width);
872 }
873 }
874
875 void clipEdgeTableLine (EdgeTable& et, int x, int y, int width)
876 {
877 jassert (x - xOffset >= 0 && x + width - xOffset <= srcData.width);
878 auto* s = (SrcPixelType*) srcData.getLinePointer (y - yOffset);
879 auto* mask = (uint8*) (s + x - xOffset);
880
881 if (sizeof (SrcPixelType) == sizeof (PixelARGB))
882 mask += PixelARGB::indexA;
883
884 et.clipLineToMask (x, y, mask, sizeof (SrcPixelType), width);
885 }
886
887 private:
888 const Image::BitmapData& destData;
889 const Image::BitmapData& srcData;
890 const int extraAlpha, xOffset, yOffset;
891 DestPixelType* linePixels;
892 SrcPixelType* sourceLineStart;
893
894 forcedinline DestPixelType* getDestPixel (int x) const noexcept
895 {
896 return addBytesToPointer (linePixels, x * destData.pixelStride);
897 }
898
899 forcedinline SrcPixelType const* getSrcPixel (int x) const noexcept
900 {
901 return addBytesToPointer (sourceLineStart, x * srcData.pixelStride);
902 }
903
904 forcedinline void copyRow (DestPixelType* dest, SrcPixelType const* src, int width) const noexcept
905 {
906 auto destStride = destData.pixelStride;
907 auto srcStride = srcData.pixelStride;
908
909 if (destStride == srcStride
910 && srcData.pixelFormat == Image::RGB
911 && destData.pixelFormat == Image::RGB)
912 {
913 memcpy ((void*) dest, src, (size_t) (width * srcStride));
914 }
915 else
916 {
917 do
918 {
919 dest->blend (*src);
920 dest = addBytesToPointer (dest, destStride);
921 src = addBytesToPointer (src, srcStride);
922 } while (--width > 0);
923 }
924 }
925
926 JUCE_DECLARE_NON_COPYABLE (ImageFill)
927 };
928
929 //==============================================================================
931 template <class DestPixelType, class SrcPixelType, bool repeatPattern>
932 struct TransformedImageFill
933 {
934 TransformedImageFill (const Image::BitmapData& dest, const Image::BitmapData& src,
935 const AffineTransform& transform, int alpha, Graphics::ResamplingQuality q)
936 : interpolator (transform,
937 q != Graphics::lowResamplingQuality ? 0.5f : 0.0f,
938 q != Graphics::lowResamplingQuality ? -128 : 0),
939 destData (dest),
940 srcData (src),
941 extraAlpha (alpha + 1),
942 quality (q),
943 maxX (src.width - 1),
944 maxY (src.height - 1)
945 {
946 scratchBuffer.malloc (scratchSize);
947 }
948
949 forcedinline void setEdgeTableYPos (int newY) noexcept
950 {
951 currentY = newY;
952 linePixels = (DestPixelType*) destData.getLinePointer (newY);
953 }
954
955 forcedinline void handleEdgeTablePixel (int x, int alphaLevel) noexcept
956 {
957 SrcPixelType p;
958 generate (&p, x, 1);
959
960 getDestPixel (x)->blend (p, (uint32) (alphaLevel * extraAlpha) >> 8);
961 }
962
963 forcedinline void handleEdgeTablePixelFull (int x) noexcept
964 {
965 SrcPixelType p;
966 generate (&p, x, 1);
967
968 getDestPixel (x)->blend (p, (uint32) extraAlpha);
969 }
970
971 void handleEdgeTableLine (int x, int width, int alphaLevel) noexcept
972 {
973 if (width > (int) scratchSize)
974 {
975 scratchSize = (size_t) width;
976 scratchBuffer.malloc (scratchSize);
977 }
978
979 SrcPixelType* span = scratchBuffer;
980 generate (span, x, width);
981
982 auto* dest = getDestPixel (x);
983 alphaLevel *= extraAlpha;
984 alphaLevel >>= 8;
985
986 if (alphaLevel < 0xfe)
987 JUCE_PERFORM_PIXEL_OP_LOOP (blend (*span++, (uint32) alphaLevel))
988 else
989 JUCE_PERFORM_PIXEL_OP_LOOP (blend (*span++))
990 }
991
992 forcedinline void handleEdgeTableLineFull (int x, int width) noexcept
993 {
994 handleEdgeTableLine (x, width, 255);
995 }
996
997 void handleEdgeTableRectangle (int x, int y, int width, int height, int alphaLevel) noexcept
998 {
999 while (--height >= 0)
1000 {
1001 setEdgeTableYPos (y++);
1002 handleEdgeTableLine (x, width, alphaLevel);
1003 }
1004 }
1005
1006 void handleEdgeTableRectangleFull (int x, int y, int width, int height) noexcept
1007 {
1008 while (--height >= 0)
1009 {
1010 setEdgeTableYPos (y++);
1011 handleEdgeTableLineFull (x, width);
1012 }
1013 }
1014
1015 void clipEdgeTableLine (EdgeTable& et, int x, int y, int width)
1016 {
1017 if (width > (int) scratchSize)
1018 {
1019 scratchSize = (size_t) width;
1020 scratchBuffer.malloc (scratchSize);
1021 }
1022
1023 currentY = y;
1024 generate (scratchBuffer.get(), x, width);
1025
1026 et.clipLineToMask (x, y,
1027 reinterpret_cast<uint8*> (scratchBuffer.get()) + SrcPixelType::indexA,
1028 sizeof (SrcPixelType), width);
1029 }
1030
1031 private:
1032 forcedinline DestPixelType* getDestPixel (int x) const noexcept
1033 {
1034 return addBytesToPointer (linePixels, x * destData.pixelStride);
1035 }
1036
1037 //==============================================================================
1038 template <class PixelType>
1039 void generate (PixelType* dest, int x, int numPixels) noexcept
1040 {
1041 this->interpolator.setStartOfLine ((float) x, (float) currentY, numPixels);
1042
1043 do
1044 {
1045 int hiResX, hiResY;
1046 this->interpolator.next (hiResX, hiResY);
1047
1048 int loResX = hiResX >> 8;
1049 int loResY = hiResY >> 8;
1050
1051 if (repeatPattern)
1052 {
1053 loResX = negativeAwareModulo (loResX, srcData.width);
1054 loResY = negativeAwareModulo (loResY, srcData.height);
1055 }
1056
1057 if (quality != Graphics::lowResamplingQuality)
1058 {
1059 if (isPositiveAndBelow (loResX, maxX))
1060 {
1061 if (isPositiveAndBelow (loResY, maxY))
1062 {
1063 // In the centre of the image..
1064 render4PixelAverage (dest, this->srcData.getPixelPointer (loResX, loResY),
1065 hiResX & 255, hiResY & 255);
1066 ++dest;
1067 continue;
1068 }
1069
1070 if (! repeatPattern)
1071 {
1072 // At a top or bottom edge..
1073 if (loResY < 0)
1074 render2PixelAverageX (dest, this->srcData.getPixelPointer (loResX, 0), hiResX & 255);
1075 else
1076 render2PixelAverageX (dest, this->srcData.getPixelPointer (loResX, maxY), hiResX & 255);
1077
1078 ++dest;
1079 continue;
1080 }
1081 }
1082 else
1083 {
1084 if (isPositiveAndBelow (loResY, maxY) && ! repeatPattern)
1085 {
1086 // At a left or right hand edge..
1087 if (loResX < 0)
1088 render2PixelAverageY (dest, this->srcData.getPixelPointer (0, loResY), hiResY & 255);
1089 else
1090 render2PixelAverageY (dest, this->srcData.getPixelPointer (maxX, loResY), hiResY & 255);
1091
1092 ++dest;
1093 continue;
1094 }
1095 }
1096 }
1097
1098 if (! repeatPattern)
1099 {
1100 if (loResX < 0) loResX = 0;
1101 if (loResY < 0) loResY = 0;
1102 if (loResX > maxX) loResX = maxX;
1103 if (loResY > maxY) loResY = maxY;
1104 }
1105
1106 dest->set (*(const PixelType*) this->srcData.getPixelPointer (loResX, loResY));
1107 ++dest;
1108
1109 } while (--numPixels > 0);
1110 }
1111
1112 //==============================================================================
1113 void render4PixelAverage (PixelARGB* dest, const uint8* src, int subPixelX, int subPixelY) noexcept
1114 {
1115 uint32 c[4] = { 256 * 128, 256 * 128, 256 * 128, 256 * 128 };
1116
1117 auto weight = (uint32) ((256 - subPixelX) * (256 - subPixelY));
1118 c[0] += weight * src[0];
1119 c[1] += weight * src[1];
1120 c[2] += weight * src[2];
1121 c[3] += weight * src[3];
1122
1123 src += this->srcData.pixelStride;
1124
1125 weight = (uint32) (subPixelX * (256 - subPixelY));
1126 c[0] += weight * src[0];
1127 c[1] += weight * src[1];
1128 c[2] += weight * src[2];
1129 c[3] += weight * src[3];
1130
1131 src += this->srcData.lineStride;
1132
1134 c[0] += weight * src[0];
1135 c[1] += weight * src[1];
1136 c[2] += weight * src[2];
1137 c[3] += weight * src[3];
1138
1139 src -= this->srcData.pixelStride;
1140
1141 weight = (uint32) ((256 - subPixelX) * subPixelY);
1142 c[0] += weight * src[0];
1143 c[1] += weight * src[1];
1144 c[2] += weight * src[2];
1145 c[3] += weight * src[3];
1146
1147 dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 16),
1148 (uint8) (c[PixelARGB::indexR] >> 16),
1149 (uint8) (c[PixelARGB::indexG] >> 16),
1150 (uint8) (c[PixelARGB::indexB] >> 16));
1151 }
1152
1153 void render2PixelAverageX (PixelARGB* dest, const uint8* src, uint32 subPixelX) noexcept
1154 {
1155 uint32 c[4] = { 128, 128, 128, 128 };
1156
1157 uint32 weight = 256 - subPixelX;
1158 c[0] += weight * src[0];
1159 c[1] += weight * src[1];
1160 c[2] += weight * src[2];
1161 c[3] += weight * src[3];
1162
1163 src += this->srcData.pixelStride;
1164
1165 weight = subPixelX;
1166 c[0] += weight * src[0];
1167 c[1] += weight * src[1];
1168 c[2] += weight * src[2];
1169 c[3] += weight * src[3];
1170
1171 dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 8),
1172 (uint8) (c[PixelARGB::indexR] >> 8),
1173 (uint8) (c[PixelARGB::indexG] >> 8),
1174 (uint8) (c[PixelARGB::indexB] >> 8));
1175 }
1176
1177 void render2PixelAverageY (PixelARGB* dest, const uint8* src, uint32 subPixelY) noexcept
1178 {
1179 uint32 c[4] = { 128, 128, 128, 128 };
1180
1181 uint32 weight = 256 - subPixelY;
1182 c[0] += weight * src[0];
1183 c[1] += weight * src[1];
1184 c[2] += weight * src[2];
1185 c[3] += weight * src[3];
1186
1187 src += this->srcData.lineStride;
1188
1189 weight = subPixelY;
1190 c[0] += weight * src[0];
1191 c[1] += weight * src[1];
1192 c[2] += weight * src[2];
1193 c[3] += weight * src[3];
1194
1195 dest->setARGB ((uint8) (c[PixelARGB::indexA] >> 8),
1196 (uint8) (c[PixelARGB::indexR] >> 8),
1197 (uint8) (c[PixelARGB::indexG] >> 8),
1198 (uint8) (c[PixelARGB::indexB] >> 8));
1199 }
1200
1201 //==============================================================================
1202 void render4PixelAverage (PixelRGB* dest, const uint8* src, uint32 subPixelX, uint32 subPixelY) noexcept
1203 {
1204 uint32 c[3] = { 256 * 128, 256 * 128, 256 * 128 };
1205
1206 uint32 weight = (256 - subPixelX) * (256 - subPixelY);
1207 c[0] += weight * src[0];
1208 c[1] += weight * src[1];
1209 c[2] += weight * src[2];
1210
1211 src += this->srcData.pixelStride;
1212
1213 weight = subPixelX * (256 - subPixelY);
1214 c[0] += weight * src[0];
1215 c[1] += weight * src[1];
1216 c[2] += weight * src[2];
1217
1218 src += this->srcData.lineStride;
1219
1221 c[0] += weight * src[0];
1222 c[1] += weight * src[1];
1223 c[2] += weight * src[2];
1224
1225 src -= this->srcData.pixelStride;
1226
1227 weight = (256 - subPixelX) * subPixelY;
1228 c[0] += weight * src[0];
1229 c[1] += weight * src[1];
1230 c[2] += weight * src[2];
1231
1232 dest->setARGB ((uint8) 255,
1233 (uint8) (c[PixelRGB::indexR] >> 16),
1234 (uint8) (c[PixelRGB::indexG] >> 16),
1235 (uint8) (c[PixelRGB::indexB] >> 16));
1236 }
1237
1238 void render2PixelAverageX (PixelRGB* dest, const uint8* src, uint32 subPixelX) noexcept
1239 {
1240 uint32 c[3] = { 128, 128, 128 };
1241
1242 const uint32 weight = 256 - subPixelX;
1243 c[0] += weight * src[0];
1244 c[1] += weight * src[1];
1245 c[2] += weight * src[2];
1246
1247 src += this->srcData.pixelStride;
1248
1249 c[0] += subPixelX * src[0];
1250 c[1] += subPixelX * src[1];
1251 c[2] += subPixelX * src[2];
1252
1253 dest->setARGB ((uint8) 255,
1254 (uint8) (c[PixelRGB::indexR] >> 8),
1255 (uint8) (c[PixelRGB::indexG] >> 8),
1256 (uint8) (c[PixelRGB::indexB] >> 8));
1257 }
1258
1259 void render2PixelAverageY (PixelRGB* dest, const uint8* src, uint32 subPixelY) noexcept
1260 {
1261 uint32 c[3] = { 128, 128, 128 };
1262
1263 const uint32 weight = 256 - subPixelY;
1264 c[0] += weight * src[0];
1265 c[1] += weight * src[1];
1266 c[2] += weight * src[2];
1267
1268 src += this->srcData.lineStride;
1269
1270 c[0] += subPixelY * src[0];
1271 c[1] += subPixelY * src[1];
1272 c[2] += subPixelY * src[2];
1273
1274 dest->setARGB ((uint8) 255,
1275 (uint8) (c[PixelRGB::indexR] >> 8),
1276 (uint8) (c[PixelRGB::indexG] >> 8),
1277 (uint8) (c[PixelRGB::indexB] >> 8));
1278 }
1279
1280 //==============================================================================
1281 void render4PixelAverage (PixelAlpha* dest, const uint8* src, uint32 subPixelX, uint32 subPixelY) noexcept
1282 {
1283 uint32 c = 256 * 128;
1284 c += src[0] * ((256 - subPixelX) * (256 - subPixelY));
1285 src += this->srcData.pixelStride;
1286 c += src[0] * (subPixelX * (256 - subPixelY));
1287 src += this->srcData.lineStride;
1288 c += src[0] * (subPixelX * subPixelY);
1289 src -= this->srcData.pixelStride;
1290
1291 c += src[0] * ((256 - subPixelX) * subPixelY);
1292
1293 *((uint8*) dest) = (uint8) (c >> 16);
1294 }
1295
1296 void render2PixelAverageX (PixelAlpha* dest, const uint8* src, uint32 subPixelX) noexcept
1297 {
1298 uint32 c = 128;
1299 c += src[0] * (256 - subPixelX);
1300 src += this->srcData.pixelStride;
1301 c += src[0] * subPixelX;
1302 *((uint8*) dest) = (uint8) (c >> 8);
1303 }
1304
1305 void render2PixelAverageY (PixelAlpha* dest, const uint8* src, uint32 subPixelY) noexcept
1306 {
1307 uint32 c = 128;
1308 c += src[0] * (256 - subPixelY);
1309 src += this->srcData.lineStride;
1310 c += src[0] * subPixelY;
1311 *((uint8*) dest) = (uint8) (c >> 8);
1312 }
1313
1314 //==============================================================================
1315 struct TransformedImageSpanInterpolator
1316 {
1317 TransformedImageSpanInterpolator (const AffineTransform& transform, float offsetFloat, int offsetInt) noexcept
1318 : inverseTransform (transform.inverted()),
1319 pixelOffset (offsetFloat), pixelOffsetInt (offsetInt)
1320 {}
1321
1322 void setStartOfLine (float sx, float sy, int numPixels) noexcept
1323 {
1324 jassert (numPixels > 0);
1325
1326 sx += pixelOffset;
1327 sy += pixelOffset;
1328 auto x1 = sx, y1 = sy;
1329 sx += (float) numPixels;
1330 inverseTransform.transformPoints (x1, y1, sx, sy);
1331
1332 xBresenham.set ((int) (x1 * 256.0f), (int) (sx * 256.0f), numPixels, pixelOffsetInt);
1333 yBresenham.set ((int) (y1 * 256.0f), (int) (sy * 256.0f), numPixels, pixelOffsetInt);
1334 }
1335
1336 void next (int& px, int& py) noexcept
1337 {
1338 px = xBresenham.n; xBresenham.stepToNext();
1339 py = yBresenham.n; yBresenham.stepToNext();
1340 }
1341
1342 private:
1343 struct BresenhamInterpolator
1344 {
1345 BresenhamInterpolator() = default;
1346
1347 void set (int n1, int n2, int steps, int offsetInt) noexcept
1348 {
1349 numSteps = steps;
1350 step = (n2 - n1) / numSteps;
1351 remainder = modulo = (n2 - n1) % numSteps;
1352 n = n1 + offsetInt;
1353
1354 if (modulo <= 0)
1355 {
1356 modulo += numSteps;
1357 remainder += numSteps;
1358 --step;
1359 }
1360
1361 modulo -= numSteps;
1362 }
1363
1364 forcedinline void stepToNext() noexcept
1365 {
1366 modulo += remainder;
1367 n += step;
1368
1369 if (modulo > 0)
1370 {
1371 modulo -= numSteps;
1372 ++n;
1373 }
1374 }
1375
1376 int n;
1377
1378 private:
1379 int numSteps, step, modulo, remainder;
1380 };
1381
1382 const AffineTransform inverseTransform;
1383 BresenhamInterpolator xBresenham, yBresenham;
1384 const float pixelOffset;
1385 const int pixelOffsetInt;
1386
1387 JUCE_DECLARE_NON_COPYABLE (TransformedImageSpanInterpolator)
1388 };
1389
1390 //==============================================================================
1391 TransformedImageSpanInterpolator interpolator;
1392 const Image::BitmapData& destData;
1393 const Image::BitmapData& srcData;
1394 const int extraAlpha;
1395 const Graphics::ResamplingQuality quality;
1396 const int maxX, maxY;
1397 int currentY;
1398 DestPixelType* linePixels;
1399 HeapBlock<SrcPixelType> scratchBuffer;
1400 size_t scratchSize = 2048;
1401
1402 JUCE_DECLARE_NON_COPYABLE (TransformedImageFill)
1403 };
1404
1405
1406 //==============================================================================
1407 template <class Iterator>
1408 void renderImageTransformed (Iterator& iter, const Image::BitmapData& destData, const Image::BitmapData& srcData,
1409 int alpha, const AffineTransform& transform, Graphics::ResamplingQuality quality, bool tiledFill)
1410 {
1411 switch (destData.pixelFormat)
1412 {
1413 case Image::ARGB:
1414 switch (srcData.pixelFormat)
1415 {
1416 case Image::ARGB:
1417 if (tiledFill) { TransformedImageFill<PixelARGB, PixelARGB, true> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1418 else { TransformedImageFill<PixelARGB, PixelARGB, false> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1419 break;
1420 case Image::RGB:
1421 if (tiledFill) { TransformedImageFill<PixelARGB, PixelRGB, true> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1422 else { TransformedImageFill<PixelARGB, PixelRGB, false> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1423 break;
1425 case Image::UnknownFormat:
1426 default:
1427 if (tiledFill) { TransformedImageFill<PixelARGB, PixelAlpha, true> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1428 else { TransformedImageFill<PixelARGB, PixelAlpha, false> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1429 break;
1430 }
1431 break;
1432
1433 case Image::RGB:
1434 {
1435 switch (srcData.pixelFormat)
1436 {
1437 case Image::ARGB:
1438 if (tiledFill) { TransformedImageFill<PixelRGB, PixelARGB, true> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1439 else { TransformedImageFill<PixelRGB, PixelARGB, false> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1440 break;
1441 case Image::RGB:
1442 if (tiledFill) { TransformedImageFill<PixelRGB, PixelRGB, true> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1443 else { TransformedImageFill<PixelRGB, PixelRGB, false> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1444 break;
1446 case Image::UnknownFormat:
1447 default:
1448 if (tiledFill) { TransformedImageFill<PixelRGB, PixelAlpha, true> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1449 else { TransformedImageFill<PixelRGB, PixelAlpha, false> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1450 break;
1451 }
1452 break;
1453 }
1454
1456 case Image::UnknownFormat:
1457 default:
1458 switch (srcData.pixelFormat)
1459 {
1460 case Image::ARGB:
1461 if (tiledFill) { TransformedImageFill<PixelAlpha, PixelARGB, true> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1462 else { TransformedImageFill<PixelAlpha, PixelARGB, false> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1463 break;
1464 case Image::RGB:
1465 if (tiledFill) { TransformedImageFill<PixelAlpha, PixelRGB, true> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1466 else { TransformedImageFill<PixelAlpha, PixelRGB, false> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1467 break;
1469 case Image::UnknownFormat:
1470 default:
1471 if (tiledFill) { TransformedImageFill<PixelAlpha, PixelAlpha, true> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1472 else { TransformedImageFill<PixelAlpha, PixelAlpha, false> r (destData, srcData, transform, alpha, quality); iter.iterate (r); }
1473 break;
1474 }
1475 break;
1476 }
1477 }
1478
1479 template <class Iterator>
1480 void renderImageUntransformed (Iterator& iter, const Image::BitmapData& destData, const Image::BitmapData& srcData, int alpha, int x, int y, bool tiledFill)
1481 {
1482 switch (destData.pixelFormat)
1483 {
1484 case Image::ARGB:
1485 switch (srcData.pixelFormat)
1486 {
1487 case Image::ARGB:
1488 if (tiledFill) { ImageFill<PixelARGB, PixelARGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1489 else { ImageFill<PixelARGB, PixelARGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1490 break;
1491 case Image::RGB:
1492 if (tiledFill) { ImageFill<PixelARGB, PixelRGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1493 else { ImageFill<PixelARGB, PixelRGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1494 break;
1496 case Image::UnknownFormat:
1497 default:
1498 if (tiledFill) { ImageFill<PixelARGB, PixelAlpha, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1499 else { ImageFill<PixelARGB, PixelAlpha, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1500 break;
1501 }
1502 break;
1503
1504 case Image::RGB:
1505 switch (srcData.pixelFormat)
1506 {
1507 case Image::ARGB:
1508 if (tiledFill) { ImageFill<PixelRGB, PixelARGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1509 else { ImageFill<PixelRGB, PixelARGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1510 break;
1511 case Image::RGB:
1512 if (tiledFill) { ImageFill<PixelRGB, PixelRGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1513 else { ImageFill<PixelRGB, PixelRGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1514 break;
1516 case Image::UnknownFormat:
1517 default:
1518 if (tiledFill) { ImageFill<PixelRGB, PixelAlpha, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1519 else { ImageFill<PixelRGB, PixelAlpha, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1520 break;
1521 }
1522 break;
1523
1525 case Image::UnknownFormat:
1526 default:
1527 switch (srcData.pixelFormat)
1528 {
1529 case Image::ARGB:
1530 if (tiledFill) { ImageFill<PixelAlpha, PixelARGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1531 else { ImageFill<PixelAlpha, PixelARGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1532 break;
1533 case Image::RGB:
1534 if (tiledFill) { ImageFill<PixelAlpha, PixelRGB, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1535 else { ImageFill<PixelAlpha, PixelRGB, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1536 break;
1538 case Image::UnknownFormat:
1539 default:
1540 if (tiledFill) { ImageFill<PixelAlpha, PixelAlpha, true> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1541 else { ImageFill<PixelAlpha, PixelAlpha, false> r (destData, srcData, alpha, x, y); iter.iterate (r); }
1542 break;
1543 }
1544 break;
1545 }
1546 }
1547
1548 template <class Iterator, class DestPixelType>
1549 void renderSolidFill (Iterator& iter, const Image::BitmapData& destData, PixelARGB fillColour, bool replaceContents, DestPixelType*)
1550 {
1551 if (replaceContents)
1552 {
1553 EdgeTableFillers::SolidColour<DestPixelType, true> r (destData, fillColour);
1554 iter.iterate (r);
1555 }
1556 else
1557 {
1558 EdgeTableFillers::SolidColour<DestPixelType, false> r (destData, fillColour);
1559 iter.iterate (r);
1560 }
1561 }
1562
1563 template <class Iterator, class DestPixelType>
1564 void renderGradient (Iterator& iter, const Image::BitmapData& destData, const ColourGradient& g, const AffineTransform& transform,
1565 const PixelARGB* lookupTable, int numLookupEntries, bool isIdentity, DestPixelType*)
1566 {
1567 if (g.isRadial)
1568 {
1569 if (isIdentity)
1570 {
1571 EdgeTableFillers::Gradient<DestPixelType, GradientPixelIterators::Radial> renderer (destData, g, transform, lookupTable, numLookupEntries);
1572 iter.iterate (renderer);
1573 }
1574 else
1575 {
1576 EdgeTableFillers::Gradient<DestPixelType, GradientPixelIterators::TransformedRadial> renderer (destData, g, transform, lookupTable, numLookupEntries);
1577 iter.iterate (renderer);
1578 }
1579 }
1580 else
1581 {
1582 EdgeTableFillers::Gradient<DestPixelType, GradientPixelIterators::Linear> renderer (destData, g, transform, lookupTable, numLookupEntries);
1583 iter.iterate (renderer);
1584 }
1585 }
1586}
1587
1588//==============================================================================
1589template <class SavedStateType>
1591{
1593 {
1594 Base() = default;
1595 ~Base() override = default;
1596
1598
1599 virtual Ptr clone() const = 0;
1600 virtual Ptr applyClipTo (const Ptr& target) const = 0;
1601
1602 virtual Ptr clipToRectangle (Rectangle<int>) = 0;
1603 virtual Ptr clipToRectangleList (const RectangleList<int>&) = 0;
1604 virtual Ptr excludeClipRectangle (Rectangle<int>) = 0;
1605 virtual Ptr clipToPath (const Path&, const AffineTransform&) = 0;
1606 virtual Ptr clipToEdgeTable (const EdgeTable&) = 0;
1607 virtual Ptr clipToImageAlpha (const Image&, const AffineTransform&, Graphics::ResamplingQuality) = 0;
1608 virtual void translate (Point<int> delta) = 0;
1609
1610 virtual bool clipRegionIntersects (Rectangle<int>) const = 0;
1611 virtual Rectangle<int> getClipBounds() const = 0;
1612
1613 virtual void fillRectWithColour (SavedStateType&, Rectangle<int>, PixelARGB colour, bool replaceContents) const = 0;
1614 virtual void fillRectWithColour (SavedStateType&, Rectangle<float>, PixelARGB colour) const = 0;
1615 virtual void fillAllWithColour (SavedStateType&, PixelARGB colour, bool replaceContents) const = 0;
1616 virtual void fillAllWithGradient (SavedStateType&, ColourGradient&, const AffineTransform&, bool isIdentity) const = 0;
1617 virtual void renderImageTransformed (SavedStateType&, const Image&, int alpha, const AffineTransform&, Graphics::ResamplingQuality, bool tiledFill) const = 0;
1618 virtual void renderImageUntransformed (SavedStateType&, const Image&, int alpha, int x, int y, bool tiledFill) const = 0;
1619 };
1620
1621 //==============================================================================
1622 struct EdgeTableRegion : public Base
1623 {
1624 EdgeTableRegion (const EdgeTable& e) : edgeTable (e) {}
1625 EdgeTableRegion (Rectangle<int> r) : edgeTable (r) {}
1626 EdgeTableRegion (Rectangle<float> r) : edgeTable (r) {}
1627 EdgeTableRegion (const RectangleList<int>& r) : edgeTable (r) {}
1628 EdgeTableRegion (const RectangleList<float>& r) : edgeTable (r) {}
1629 EdgeTableRegion (Rectangle<int> bounds, const Path& p, const AffineTransform& t) : edgeTable (bounds, p, t) {}
1630
1631 EdgeTableRegion (const EdgeTableRegion& other) : Base(), edgeTable (other.edgeTable) {}
1632 EdgeTableRegion& operator= (const EdgeTableRegion&) = delete;
1633
1634 using Ptr = typename Base::Ptr;
1635
1636 Ptr clone() const override { return *new EdgeTableRegion (*this); }
1637 Ptr applyClipTo (const Ptr& target) const override { return target->clipToEdgeTable (edgeTable); }
1638
1639 Ptr clipToRectangle (Rectangle<int> r) override
1640 {
1641 edgeTable.clipToRectangle (r);
1642 return edgeTable.isEmpty() ? Ptr() : Ptr (*this);
1643 }
1644
1645 Ptr clipToRectangleList (const RectangleList<int>& r) override
1646 {
1647 RectangleList<int> inverse (edgeTable.getMaximumBounds());
1648
1649 if (inverse.subtract (r))
1650 for (auto& i : inverse)
1651 edgeTable.excludeRectangle (i);
1652
1653 return edgeTable.isEmpty() ? Ptr() : Ptr (*this);
1654 }
1655
1656 Ptr excludeClipRectangle (Rectangle<int> r) override
1657 {
1658 edgeTable.excludeRectangle (r);
1659 return edgeTable.isEmpty() ? Ptr() : Ptr (*this);
1660 }
1661
1662 Ptr clipToPath (const Path& p, const AffineTransform& transform) override
1663 {
1664 EdgeTable et (edgeTable.getMaximumBounds(), p, transform);
1665 edgeTable.clipToEdgeTable (et);
1666 return edgeTable.isEmpty() ? Ptr() : Ptr (*this);
1667 }
1668
1669 Ptr clipToEdgeTable (const EdgeTable& et) override
1670 {
1671 edgeTable.clipToEdgeTable (et);
1672 return edgeTable.isEmpty() ? Ptr() : Ptr (*this);
1673 }
1674
1675 Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, Graphics::ResamplingQuality quality) override
1676 {
1677 const Image::BitmapData srcData (image, Image::BitmapData::readOnly);
1678
1679 if (transform.isOnlyTranslation())
1680 {
1681 // If our translation doesn't involve any distortion, just use a simple blit..
1682 auto tx = (int) (transform.getTranslationX() * 256.0f);
1683 auto ty = (int) (transform.getTranslationY() * 256.0f);
1684
1685 if (quality == Graphics::lowResamplingQuality || ((tx | ty) & 224) == 0)
1686 {
1687 auto imageX = ((tx + 128) >> 8);
1688 auto imageY = ((ty + 128) >> 8);
1689
1690 if (image.getFormat() == Image::ARGB)
1691 straightClipImage (srcData, imageX, imageY, (PixelARGB*) nullptr);
1692 else
1693 straightClipImage (srcData, imageX, imageY, (PixelAlpha*) nullptr);
1694
1695 return edgeTable.isEmpty() ? Ptr() : Ptr (*this);
1696 }
1697 }
1698
1699 if (transform.isSingularity())
1700 return Ptr();
1701
1702 {
1703 Path p;
1704 p.addRectangle (0, 0, (float) srcData.width, (float) srcData.height);
1705 EdgeTable et2 (edgeTable.getMaximumBounds(), p, transform);
1706 edgeTable.clipToEdgeTable (et2);
1707 }
1708
1709 if (! edgeTable.isEmpty())
1710 {
1711 if (image.getFormat() == Image::ARGB)
1712 transformedClipImage (srcData, transform, quality, (PixelARGB*) nullptr);
1713 else
1714 transformedClipImage (srcData, transform, quality, (PixelAlpha*) nullptr);
1715 }
1716
1717 return edgeTable.isEmpty() ? Ptr() : Ptr (*this);
1718 }
1719
1720 void translate (Point<int> delta) override
1721 {
1722 edgeTable.translate ((float) delta.x, delta.y);
1723 }
1724
1725 bool clipRegionIntersects (Rectangle<int> r) const override
1726 {
1727 return edgeTable.getMaximumBounds().intersects (r);
1728 }
1729
1730 Rectangle<int> getClipBounds() const override
1731 {
1732 return edgeTable.getMaximumBounds();
1733 }
1734
1735 void fillRectWithColour (SavedStateType& state, Rectangle<int> area, PixelARGB colour, bool replaceContents) const override
1736 {
1737 auto totalClip = edgeTable.getMaximumBounds();
1738 auto clipped = totalClip.getIntersection (area);
1739
1740 if (! clipped.isEmpty())
1741 {
1743 et.edgeTable.clipToEdgeTable (edgeTable);
1744 state.fillWithSolidColour (et.edgeTable, colour, replaceContents);
1745 }
1746 }
1747
1748 void fillRectWithColour (SavedStateType& state, Rectangle<float> area, PixelARGB colour) const override
1749 {
1750 auto totalClip = edgeTable.getMaximumBounds().toFloat();
1751 auto clipped = totalClip.getIntersection (area);
1752
1753 if (! clipped.isEmpty())
1754 {
1756 et.edgeTable.clipToEdgeTable (edgeTable);
1757 state.fillWithSolidColour (et.edgeTable, colour, false);
1758 }
1759 }
1760
1761 void fillAllWithColour (SavedStateType& state, PixelARGB colour, bool replaceContents) const override
1762 {
1763 state.fillWithSolidColour (edgeTable, colour, replaceContents);
1764 }
1765
1766 void fillAllWithGradient (SavedStateType& state, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const override
1767 {
1768 state.fillWithGradient (edgeTable, gradient, transform, isIdentity);
1769 }
1770
1771 void renderImageTransformed (SavedStateType& state, const Image& src, int alpha, const AffineTransform& transform, Graphics::ResamplingQuality quality, bool tiledFill) const override
1772 {
1773 state.renderImageTransformed (edgeTable, src, alpha, transform, quality, tiledFill);
1774 }
1775
1776 void renderImageUntransformed (SavedStateType& state, const Image& src, int alpha, int x, int y, bool tiledFill) const override
1777 {
1778 state.renderImageUntransformed (edgeTable, src, alpha, x, y, tiledFill);
1779 }
1780
1781 EdgeTable edgeTable;
1782
1783 private:
1784 template <class SrcPixelType>
1785 void transformedClipImage (const Image::BitmapData& srcData, const AffineTransform& transform, Graphics::ResamplingQuality quality, const SrcPixelType*)
1786 {
1787 EdgeTableFillers::TransformedImageFill<SrcPixelType, SrcPixelType, false> renderer (srcData, srcData, transform, 255, quality);
1788
1789 for (int y = 0; y < edgeTable.getMaximumBounds().getHeight(); ++y)
1790 renderer.clipEdgeTableLine (edgeTable, edgeTable.getMaximumBounds().getX(), y + edgeTable.getMaximumBounds().getY(),
1791 edgeTable.getMaximumBounds().getWidth());
1792 }
1793
1794 template <class SrcPixelType>
1795 void straightClipImage (const Image::BitmapData& srcData, int imageX, int imageY, const SrcPixelType*)
1796 {
1797 Rectangle<int> r (imageX, imageY, srcData.width, srcData.height);
1798 edgeTable.clipToRectangle (r);
1799
1800 EdgeTableFillers::ImageFill<SrcPixelType, SrcPixelType, false> renderer (srcData, srcData, 255, imageX, imageY);
1801
1802 for (int y = 0; y < r.getHeight(); ++y)
1803 renderer.clipEdgeTableLine (edgeTable, r.getX(), y + r.getY(), r.getWidth());
1804 }
1805 };
1806
1807 //==============================================================================
1809 {
1810 public:
1811 RectangleListRegion (Rectangle<int> r) : clip (r) {}
1812 RectangleListRegion (const RectangleList<int>& r) : clip (r) {}
1813 RectangleListRegion (const RectangleListRegion& other) : Base(), clip (other.clip) {}
1814
1815 using Ptr = typename Base::Ptr;
1816
1817 Ptr clone() const override { return *new RectangleListRegion (*this); }
1818 Ptr applyClipTo (const Ptr& target) const override { return target->clipToRectangleList (clip); }
1819
1820 Ptr clipToRectangle (Rectangle<int> r) override
1821 {
1822 clip.clipTo (r);
1823 return clip.isEmpty() ? Ptr() : Ptr (*this);
1824 }
1825
1826 Ptr clipToRectangleList (const RectangleList<int>& r) override
1827 {
1828 clip.clipTo (r);
1829 return clip.isEmpty() ? Ptr() : Ptr (*this);
1830 }
1831
1832 Ptr excludeClipRectangle (Rectangle<int> r) override
1833 {
1834 clip.subtract (r);
1835 return clip.isEmpty() ? Ptr() : Ptr (*this);
1836 }
1837
1838 Ptr clipToPath (const Path& p, const AffineTransform& transform) override { return toEdgeTable()->clipToPath (p, transform); }
1839 Ptr clipToEdgeTable (const EdgeTable& et) override { return toEdgeTable()->clipToEdgeTable (et); }
1840
1841 Ptr clipToImageAlpha (const Image& image, const AffineTransform& transform, Graphics::ResamplingQuality quality) override
1842 {
1843 return toEdgeTable()->clipToImageAlpha (image, transform, quality);
1844 }
1845
1846 void translate (Point<int> delta) override { clip.offsetAll (delta); }
1847 bool clipRegionIntersects (Rectangle<int> r) const override { return clip.intersects (r); }
1848 Rectangle<int> getClipBounds() const override { return clip.getBounds(); }
1849
1850 void fillRectWithColour (SavedStateType& state, Rectangle<int> area, PixelARGB colour, bool replaceContents) const override
1851 {
1852 SubRectangleIterator iter (clip, area);
1853 state.fillWithSolidColour (iter, colour, replaceContents);
1854 }
1855
1856 void fillRectWithColour (SavedStateType& state, Rectangle<float> area, PixelARGB colour) const override
1857 {
1858 SubRectangleIteratorFloat iter (clip, area);
1859 state.fillWithSolidColour (iter, colour, false);
1860 }
1861
1862 void fillAllWithColour (SavedStateType& state, PixelARGB colour, bool replaceContents) const override
1863 {
1864 state.fillWithSolidColour (*this, colour, replaceContents);
1865 }
1866
1867 void fillAllWithGradient (SavedStateType& state, ColourGradient& gradient, const AffineTransform& transform, bool isIdentity) const override
1868 {
1869 state.fillWithGradient (*this, gradient, transform, isIdentity);
1870 }
1871
1872 void renderImageTransformed (SavedStateType& state, const Image& src, int alpha, const AffineTransform& transform, Graphics::ResamplingQuality quality, bool tiledFill) const override
1873 {
1874 state.renderImageTransformed (*this, src, alpha, transform, quality, tiledFill);
1875 }
1876
1877 void renderImageUntransformed (SavedStateType& state, const Image& src, int alpha, int x, int y, bool tiledFill) const override
1878 {
1879 state.renderImageUntransformed (*this, src, alpha, x, y, tiledFill);
1880 }
1881
1882 RectangleList<int> clip;
1883
1884 //==============================================================================
1885 template <class Renderer>
1886 void iterate (Renderer& r) const noexcept
1887 {
1888 for (auto& i : clip)
1889 {
1890 auto x = i.getX();
1891 auto w = i.getWidth();
1892 jassert (w > 0);
1893 auto bottom = i.getBottom();
1894
1895 for (int y = i.getY(); y < bottom; ++y)
1896 {
1897 r.setEdgeTableYPos (y);
1898 r.handleEdgeTableLineFull (x, w);
1899 }
1900 }
1901 }
1902
1903 private:
1904 //==============================================================================
1905 class SubRectangleIterator
1906 {
1907 public:
1908 SubRectangleIterator (const RectangleList<int>& clipList, Rectangle<int> clipBounds)
1909 : clip (clipList), area (clipBounds)
1910 {}
1911
1912 template <class Renderer>
1913 void iterate (Renderer& r) const noexcept
1914 {
1915 for (auto& i : clip)
1916 {
1917 auto rect = i.getIntersection (area);
1918
1919 if (! rect.isEmpty())
1920 r.handleEdgeTableRectangleFull (rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
1921 }
1922 }
1923
1924 private:
1925 const RectangleList<int>& clip;
1926 const Rectangle<int> area;
1927
1928 JUCE_DECLARE_NON_COPYABLE (SubRectangleIterator)
1929 };
1930
1931 //==============================================================================
1932 class SubRectangleIteratorFloat
1933 {
1934 public:
1935 SubRectangleIteratorFloat (const RectangleList<int>& clipList, Rectangle<float> clipBounds) noexcept
1936 : clip (clipList), area (clipBounds)
1937 {
1938 }
1939
1940 template <class Renderer>
1941 void iterate (Renderer& r) const noexcept
1942 {
1944
1945 for (auto& i : clip)
1946 {
1947 auto clipLeft = i.getX();
1948 auto clipRight = i.getRight();
1949 auto clipTop = i.getY();
1950 auto clipBottom = i.getBottom();
1951
1952 if (f.totalBottom > clipTop && f.totalTop < clipBottom
1953 && f.totalRight > clipLeft && f.totalLeft < clipRight)
1954 {
1955 if (f.isOnePixelWide())
1956 {
1957 if (f.topAlpha != 0 && f.totalTop >= clipTop)
1958 {
1959 r.setEdgeTableYPos (f.totalTop);
1960 r.handleEdgeTablePixel (f.left, f.topAlpha);
1961 }
1962
1963 auto y1 = jmax (clipTop, f.top);
1964 auto y2 = jmin (f.bottom, clipBottom);
1965 auto h = y2 - y1;
1966
1967 if (h > 0)
1968 r.handleEdgeTableRectangleFull (f.left, y1, 1, h);
1969
1970 if (f.bottomAlpha != 0 && f.bottom < clipBottom)
1971 {
1972 r.setEdgeTableYPos (f.bottom);
1973 r.handleEdgeTablePixel (f.left, f.bottomAlpha);
1974 }
1975 }
1976 else
1977 {
1978 auto clippedLeft = jmax (f.left, clipLeft);
1979 auto clippedWidth = jmin (f.right, clipRight) - clippedLeft;
1980 bool doLeftAlpha = f.leftAlpha != 0 && f.totalLeft >= clipLeft;
1981 bool doRightAlpha = f.rightAlpha != 0 && f.right < clipRight;
1982
1983 if (f.topAlpha != 0 && f.totalTop >= clipTop)
1984 {
1985 r.setEdgeTableYPos (f.totalTop);
1986
1987 if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.getTopLeftCornerAlpha());
1988 if (clippedWidth > 0) r.handleEdgeTableLine (clippedLeft, clippedWidth, f.topAlpha);
1989 if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.getTopRightCornerAlpha());
1990 }
1991
1992 auto y1 = jmax (clipTop, f.top);
1993 auto y2 = jmin (f.bottom, clipBottom);
1994 auto h = y2 - y1;
1995
1996 if (h > 0)
1997 {
1998 if (h == 1)
1999 {
2000 r.setEdgeTableYPos (y1);
2001
2002 if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.leftAlpha);
2003 if (clippedWidth > 0) r.handleEdgeTableLineFull (clippedLeft, clippedWidth);
2004 if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.rightAlpha);
2005 }
2006 else
2007 {
2008 if (doLeftAlpha) r.handleEdgeTableRectangle (f.totalLeft, y1, 1, h, f.leftAlpha);
2009 if (clippedWidth > 0) r.handleEdgeTableRectangleFull (clippedLeft, y1, clippedWidth, h);
2010 if (doRightAlpha) r.handleEdgeTableRectangle (f.right, y1, 1, h, f.rightAlpha);
2011 }
2012 }
2013
2014 if (f.bottomAlpha != 0 && f.bottom < clipBottom)
2015 {
2016 r.setEdgeTableYPos (f.bottom);
2017
2018 if (doLeftAlpha) r.handleEdgeTablePixel (f.totalLeft, f.getBottomLeftCornerAlpha());
2019 if (clippedWidth > 0) r.handleEdgeTableLine (clippedLeft, clippedWidth, f.bottomAlpha);
2020 if (doRightAlpha) r.handleEdgeTablePixel (f.right, f.getBottomRightCornerAlpha());
2021 }
2022 }
2023 }
2024 }
2025 }
2026
2027 private:
2028 const RectangleList<int>& clip;
2029 Rectangle<float> area;
2030
2031 JUCE_DECLARE_NON_COPYABLE (SubRectangleIteratorFloat)
2032 };
2033
2034 Ptr toEdgeTable() const { return *new EdgeTableRegion (clip); }
2035
2036 RectangleListRegion& operator= (const RectangleListRegion&) = delete;
2037 };
2038};
2039
2040//==============================================================================
2041template <class SavedStateType>
2043{
2044public:
2045 using BaseRegionType = typename ClipRegions<SavedStateType>::Base;
2046 using EdgeTableRegionType = typename ClipRegions<SavedStateType>::EdgeTableRegion;
2047 using RectangleListRegionType = typename ClipRegions<SavedStateType>::RectangleListRegion;
2048
2050 : clip (new RectangleListRegionType (initialClip)),
2051 interpolationQuality (Graphics::mediumResamplingQuality), transparencyLayerAlpha (1.0f)
2052 {
2053 }
2054
2056 : clip (new RectangleListRegionType (clipList)), transform (origin),
2057 interpolationQuality (Graphics::mediumResamplingQuality), transparencyLayerAlpha (1.0f)
2058 {
2059 }
2060
2062 : clip (other.clip), transform (other.transform), fillType (other.fillType),
2063 interpolationQuality (other.interpolationQuality),
2064 transparencyLayerAlpha (other.transparencyLayerAlpha)
2065 {
2066 }
2067
2068 SavedStateType& getThis() noexcept { return *static_cast<SavedStateType*> (this); }
2069
2070 bool clipToRectangle (Rectangle<int> r)
2071 {
2072 if (clip != nullptr)
2073 {
2074 if (transform.isOnlyTranslated)
2075 {
2076 cloneClipIfMultiplyReferenced();
2077 clip = clip->clipToRectangle (transform.translated (r));
2078 }
2079 else if (! transform.isRotated)
2080 {
2081 cloneClipIfMultiplyReferenced();
2082 clip = clip->clipToRectangle (transform.transformed (r));
2083 }
2084 else
2085 {
2086 Path p;
2087 p.addRectangle (r);
2088 clipToPath (p, {});
2089 }
2090 }
2091
2092 return clip != nullptr;
2093 }
2094
2095 bool clipToRectangleList (const RectangleList<int>& r)
2096 {
2097 if (clip != nullptr)
2098 {
2099 if (transform.isOnlyTranslated)
2100 {
2101 cloneClipIfMultiplyReferenced();
2102
2103 if (transform.isIdentity())
2104 {
2105 clip = clip->clipToRectangleList (r);
2106 }
2107 else
2108 {
2110 offsetList.offsetAll (transform.offset);
2111 clip = clip->clipToRectangleList (offsetList);
2112 }
2113 }
2114 else if (! transform.isRotated)
2115 {
2116 cloneClipIfMultiplyReferenced();
2118
2119 for (auto& i : r)
2120 scaledList.add (transform.transformed (i));
2121
2122 clip = clip->clipToRectangleList (scaledList);
2123 }
2124 else
2125 {
2126 clipToPath (r.toPath(), {});
2127 }
2128 }
2129
2130 return clip != nullptr;
2131 }
2132
2133 static Rectangle<int> getLargestIntegerWithin (Rectangle<float> r)
2134 {
2135 auto x1 = (int) std::ceil (r.getX());
2136 auto y1 = (int) std::ceil (r.getY());
2137 auto x2 = (int) std::floor (r.getRight());
2138 auto y2 = (int) std::floor (r.getBottom());
2139
2140 return { x1, y1, x2 - x1, y2 - y1 };
2141 }
2142
2143 bool excludeClipRectangle (Rectangle<int> r)
2144 {
2145 if (clip != nullptr)
2146 {
2147 cloneClipIfMultiplyReferenced();
2148
2149 if (transform.isOnlyTranslated)
2150 {
2151 clip = clip->excludeClipRectangle (getLargestIntegerWithin (transform.translated (r.toFloat())));
2152 }
2153 else if (! transform.isRotated)
2154 {
2155 clip = clip->excludeClipRectangle (getLargestIntegerWithin (transform.transformed (r.toFloat())));
2156 }
2157 else
2158 {
2159 Path p;
2160 p.addRectangle (r.toFloat());
2161 p.applyTransform (transform.complexTransform);
2162 p.addRectangle (clip->getClipBounds().toFloat());
2163 p.setUsingNonZeroWinding (false);
2164 clip = clip->clipToPath (p, {});
2165 }
2166 }
2167
2168 return clip != nullptr;
2169 }
2170
2171 void clipToPath (const Path& p, const AffineTransform& t)
2172 {
2173 if (clip != nullptr)
2174 {
2175 cloneClipIfMultiplyReferenced();
2176 clip = clip->clipToPath (p, transform.getTransformWith (t));
2177 }
2178 }
2179
2180 void clipToImageAlpha (const Image& sourceImage, const AffineTransform& t)
2181 {
2182 if (clip != nullptr)
2183 {
2184 if (sourceImage.hasAlphaChannel())
2185 {
2186 cloneClipIfMultiplyReferenced();
2187 clip = clip->clipToImageAlpha (sourceImage, transform.getTransformWith (t), interpolationQuality);
2188 }
2189 else
2190 {
2191 Path p;
2192 p.addRectangle (sourceImage.getBounds());
2193 clipToPath (p, t);
2194 }
2195 }
2196 }
2197
2198 bool clipRegionIntersects (Rectangle<int> r) const
2199 {
2200 if (clip != nullptr)
2201 {
2202 if (transform.isOnlyTranslated)
2203 return clip->clipRegionIntersects (transform.translated (r));
2204
2205 return getClipBounds().intersects (r);
2206 }
2207
2208 return false;
2209 }
2210
2211 Rectangle<int> getClipBounds() const
2212 {
2213 return clip != nullptr ? transform.deviceSpaceToUserSpace (clip->getClipBounds())
2214 : Rectangle<int>();
2215 }
2216
2217 void setFillType (const FillType& newFill)
2218 {
2219 fillType = newFill;
2220 }
2221
2222 void fillTargetRect (Rectangle<int> r, bool replaceContents)
2223 {
2224 if (fillType.isColour())
2225 {
2226 clip->fillRectWithColour (getThis(), r, fillType.colour.getPixelARGB(), replaceContents);
2227 }
2228 else
2229 {
2230 auto clipped = clip->getClipBounds().getIntersection (r);
2231
2232 if (! clipped.isEmpty())
2233 fillShape (*new RectangleListRegionType (clipped), false);
2234 }
2235 }
2236
2237 void fillTargetRect (Rectangle<float> r)
2238 {
2239 if (fillType.isColour())
2240 {
2241 clip->fillRectWithColour (getThis(), r, fillType.colour.getPixelARGB());
2242 }
2243 else
2244 {
2245 auto clipped = clip->getClipBounds().toFloat().getIntersection (r);
2246
2247 if (! clipped.isEmpty())
2248 fillShape (*new EdgeTableRegionType (clipped), false);
2249 }
2250 }
2251
2252 template <typename CoordType>
2253 void fillRectAsPath (Rectangle<CoordType> r)
2254 {
2255 Path p;
2256 p.addRectangle (r);
2257 fillPath (p, {});
2258 }
2259
2260 void fillRect (Rectangle<int> r, bool replaceContents)
2261 {
2262 if (clip != nullptr)
2263 {
2264 if (transform.isOnlyTranslated)
2265 {
2266 fillTargetRect (transform.translated (r), replaceContents);
2267 }
2268 else if (! transform.isRotated)
2269 {
2270 fillTargetRect (transform.transformed (r), replaceContents);
2271 }
2272 else
2273 {
2274 jassert (! replaceContents); // not implemented..
2275 fillRectAsPath (r);
2276 }
2277 }
2278 }
2279
2280 void fillRect (Rectangle<float> r)
2281 {
2282 if (clip != nullptr)
2283 {
2284 if (transform.isOnlyTranslated)
2285 fillTargetRect (transform.translated (r));
2286 else if (! transform.isRotated)
2287 fillTargetRect (transform.transformed (r));
2288 else
2289 fillRectAsPath (r);
2290 }
2291 }
2292
2293 void fillRectList (const RectangleList<float>& list)
2294 {
2295 if (clip != nullptr)
2296 {
2297 if (list.getNumRectangles() == 1)
2298 return fillRect (*list.begin());
2299
2300 if (transform.isIdentity())
2301 {
2302 fillShape (*new EdgeTableRegionType (list), false);
2303 }
2304 else if (! transform.isRotated)
2305 {
2306 RectangleList<float> transformed (list);
2307
2308 if (transform.isOnlyTranslated)
2309 transformed.offsetAll (transform.offset.toFloat());
2310 else
2311 transformed.transformAll (transform.getTransform());
2312
2313 fillShape (*new EdgeTableRegionType (transformed), false);
2314 }
2315 else
2316 {
2317 fillPath (list.toPath(), {});
2318 }
2319 }
2320 }
2321
2322 void fillPath (const Path& path, const AffineTransform& t)
2323 {
2324 if (clip != nullptr)
2325 {
2326 auto trans = transform.getTransformWith (t);
2327 auto clipRect = clip->getClipBounds();
2328
2330 fillShape (*new EdgeTableRegionType (clipRect, path, trans), false);
2331 }
2332 }
2333
2334 void fillEdgeTable (const EdgeTable& edgeTable, float x, int y)
2335 {
2336 if (clip != nullptr)
2337 {
2338 auto* edgeTableClip = new EdgeTableRegionType (edgeTable);
2339 edgeTableClip->edgeTable.translate (x, y);
2340
2341 if (fillType.isColour())
2342 {
2343 auto brightness = fillType.colour.getBrightness() - 0.5f;
2344
2345 if (brightness > 0.0f)
2346 edgeTableClip->edgeTable.multiplyLevels (1.0f + 1.6f * brightness);
2347 }
2348
2349 fillShape (*edgeTableClip, false);
2350 }
2351 }
2352
2353 void drawLine (Line<float> line)
2354 {
2355 Path p;
2356 p.addLineSegment (line, 1.0f);
2357 fillPath (p, {});
2358 }
2359
2360 void drawImage (const Image& sourceImage, const AffineTransform& trans)
2361 {
2362 if (clip != nullptr && ! fillType.colour.isTransparent())
2363 renderImage (sourceImage, trans, {});
2364 }
2365
2366 static bool isOnlyTranslationAllowingError (const AffineTransform& t, float tolerance) noexcept
2367 {
2368 return std::abs (t.mat01) < tolerance
2369 && std::abs (t.mat10) < tolerance
2370 && std::abs (t.mat00 - 1.0f) < tolerance
2371 && std::abs (t.mat11 - 1.0f) < tolerance;
2372 }
2373
2374 void renderImage (const Image& sourceImage, const AffineTransform& trans, const BaseRegionType* tiledFillClipRegion)
2375 {
2376 auto t = transform.getTransformWith (trans);
2377 auto alpha = fillType.colour.getAlpha();
2378
2379 if (isOnlyTranslationAllowingError (t, 0.002f))
2380 {
2381 // If our translation doesn't involve any distortion, just use a simple blit..
2382 auto tx = (int) (t.getTranslationX() * 256.0f);
2383 auto ty = (int) (t.getTranslationY() * 256.0f);
2384
2385 if (interpolationQuality == Graphics::lowResamplingQuality || ((tx | ty) & 224) == 0)
2386 {
2387 tx = ((tx + 128) >> 8);
2388 ty = ((ty + 128) >> 8);
2389
2390 if (tiledFillClipRegion != nullptr)
2391 {
2392 tiledFillClipRegion->renderImageUntransformed (getThis(), sourceImage, alpha, tx, ty, true);
2393 }
2394 else
2395 {
2396 Rectangle<int> area (tx, ty, sourceImage.getWidth(), sourceImage.getHeight());
2397 area = area.getIntersection (getThis().getMaximumBounds());
2398
2399 if (! area.isEmpty())
2400 if (auto c = clip->applyClipTo (*new EdgeTableRegionType (area)))
2401 c->renderImageUntransformed (getThis(), sourceImage, alpha, tx, ty, false);
2402 }
2403
2404 return;
2405 }
2406 }
2407
2408 if (! t.isSingularity())
2409 {
2410 if (tiledFillClipRegion != nullptr)
2411 {
2412 tiledFillClipRegion->renderImageTransformed (getThis(), sourceImage, alpha,
2413 t, interpolationQuality, true);
2414 }
2415 else
2416 {
2417 Path p;
2418 p.addRectangle (sourceImage.getBounds());
2419
2420 if (auto c = clip->clone()->clipToPath (p, t))
2421 c->renderImageTransformed (getThis(), sourceImage, alpha,
2422 t, interpolationQuality, false);
2423 }
2424 }
2425 }
2426
2427 void fillShape (typename BaseRegionType::Ptr shapeToFill, bool replaceContents)
2428 {
2429 jassert (clip != nullptr);
2430 shapeToFill = clip->applyClipTo (shapeToFill);
2431
2432 if (shapeToFill != nullptr)
2433 {
2434 if (fillType.isGradient())
2435 {
2436 jassert (! replaceContents); // that option is just for solid colours
2437
2438 auto g2 = *(fillType.gradient);
2439 g2.multiplyOpacity (fillType.getOpacity());
2440 auto t = transform.getTransformWith (fillType.transform).translated (-0.5f, -0.5f);
2441
2442 bool isIdentity = t.isOnlyTranslation();
2443
2444 if (isIdentity)
2445 {
2446 // If our translation doesn't involve any distortion, we can speed it up..
2447 g2.point1.applyTransform (t);
2448 g2.point2.applyTransform (t);
2449 t = {};
2450 }
2451
2452 shapeToFill->fillAllWithGradient (getThis(), g2, t, isIdentity);
2453 }
2454 else if (fillType.isTiledImage())
2455 {
2456 renderImage (fillType.image, fillType.transform, shapeToFill.get());
2457 }
2458 else
2459 {
2460 shapeToFill->fillAllWithColour (getThis(), fillType.colour.getPixelARGB(), replaceContents);
2461 }
2462 }
2463 }
2464
2465 void cloneClipIfMultiplyReferenced()
2466 {
2467 if (clip->getReferenceCount() > 1)
2468 clip = clip->clone();
2469 }
2470
2471 typename BaseRegionType::Ptr clip;
2473 FillType fillType;
2474 Graphics::ResamplingQuality interpolationQuality;
2475 float transparencyLayerAlpha;
2476};
2477
2478//==============================================================================
2479class SoftwareRendererSavedState : public SavedStateBase<SoftwareRendererSavedState>
2480{
2482
2483public:
2485 : BaseClass (clipBounds), image (im)
2486 {
2487 }
2488
2490 : BaseClass (clipList, origin), image (im)
2491 {
2492 }
2493
2495
2496 SoftwareRendererSavedState* beginTransparencyLayer (float opacity)
2497 {
2498 auto* s = new SoftwareRendererSavedState (*this);
2499
2500 if (clip != nullptr)
2501 {
2502 auto layerBounds = clip->getClipBounds();
2503
2504 s->image = Image (Image::ARGB, layerBounds.getWidth(), layerBounds.getHeight(), true);
2505 s->transparencyLayerAlpha = opacity;
2506 s->transform.moveOriginInDeviceSpace (-layerBounds.getPosition());
2507 s->cloneClipIfMultiplyReferenced();
2508 s->clip->translate (-layerBounds.getPosition());
2509 }
2510
2511 return s;
2512 }
2513
2514 void endTransparencyLayer (SoftwareRendererSavedState& finishedLayerState)
2515 {
2516 if (clip != nullptr)
2517 {
2518 auto layerBounds = clip->getClipBounds();
2519
2520 auto g = image.createLowLevelContext();
2521 g->setOpacity (finishedLayerState.transparencyLayerAlpha);
2522 g->drawImage (finishedLayerState.image, AffineTransform::translation (layerBounds.getPosition()));
2523 }
2524 }
2525
2527
2528 static void clearGlyphCache()
2529 {
2530 GlyphCacheType::getInstance().reset();
2531 }
2532
2533 //==============================================================================
2534 void drawGlyph (int glyphNumber, const AffineTransform& trans)
2535 {
2536 if (clip != nullptr)
2537 {
2538 if (trans.isOnlyTranslation() && ! transform.isRotated)
2539 {
2540 auto& cache = GlyphCacheType::getInstance();
2541 Point<float> pos (trans.getTranslationX(), trans.getTranslationY());
2542
2543 if (transform.isOnlyTranslated)
2544 {
2545 cache.drawGlyph (*this, font, glyphNumber, pos + transform.offset.toFloat());
2546 }
2547 else
2548 {
2549 pos = transform.transformed (pos);
2550
2551 Font f (font);
2552 f.setHeight (font.getHeight() * transform.complexTransform.mat11);
2553
2554 auto xScale = transform.complexTransform.mat00 / transform.complexTransform.mat11;
2555
2556 if (std::abs (xScale - 1.0f) > 0.01f)
2558
2559 cache.drawGlyph (*this, f, glyphNumber, pos);
2560 }
2561 }
2562 else
2563 {
2564 auto fontHeight = font.getHeight();
2565
2566 auto t = transform.getTransformWith (AffineTransform::scale (fontHeight * font.getHorizontalScale(), fontHeight)
2567 .followedBy (trans));
2568
2570
2571 if (et != nullptr)
2572 fillShape (*new EdgeTableRegionType (*et), false);
2573 }
2574 }
2575 }
2576
2577 Rectangle<int> getMaximumBounds() const { return image.getBounds(); }
2578
2579 //==============================================================================
2580 template <typename IteratorType>
2581 void renderImageTransformed (IteratorType& iter, const Image& src, int alpha, const AffineTransform& trans, Graphics::ResamplingQuality quality, bool tiledFill) const
2582 {
2583 Image::BitmapData destData (image, Image::BitmapData::readWrite);
2584 const Image::BitmapData srcData (src, Image::BitmapData::readOnly);
2585 EdgeTableFillers::renderImageTransformed (iter, destData, srcData, alpha, trans, quality, tiledFill);
2586 }
2587
2588 template <typename IteratorType>
2589 void renderImageUntransformed (IteratorType& iter, const Image& src, int alpha, int x, int y, bool tiledFill) const
2590 {
2591 Image::BitmapData destData (image, Image::BitmapData::readWrite);
2592 const Image::BitmapData srcData (src, Image::BitmapData::readOnly);
2593 EdgeTableFillers::renderImageUntransformed (iter, destData, srcData, alpha, x, y, tiledFill);
2594 }
2595
2596 template <typename IteratorType>
2597 void fillWithSolidColour (IteratorType& iter, PixelARGB colour, bool replaceContents) const
2598 {
2599 Image::BitmapData destData (image, Image::BitmapData::readWrite);
2600
2601 switch (destData.pixelFormat)
2602 {
2603 case Image::ARGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelARGB*) nullptr); break;
2604 case Image::RGB: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelRGB*) nullptr); break;
2606 case Image::UnknownFormat:
2607 default: EdgeTableFillers::renderSolidFill (iter, destData, colour, replaceContents, (PixelAlpha*) nullptr); break;
2608 }
2609 }
2610
2611 template <typename IteratorType>
2612 void fillWithGradient (IteratorType& iter, ColourGradient& gradient, const AffineTransform& trans, bool isIdentity) const
2613 {
2614 HeapBlock<PixelARGB> lookupTable;
2615 auto numLookupEntries = gradient.createLookupTable (trans, lookupTable);
2617
2618 Image::BitmapData destData (image, Image::BitmapData::readWrite);
2619
2620 switch (destData.pixelFormat)
2621 {
2622 case Image::ARGB: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelARGB*) nullptr); break;
2623 case Image::RGB: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelRGB*) nullptr); break;
2625 case Image::UnknownFormat:
2626 default: EdgeTableFillers::renderGradient (iter, destData, gradient, trans, lookupTable, numLookupEntries, isIdentity, (PixelAlpha*) nullptr); break;
2627 }
2628 }
2629
2630 //==============================================================================
2631 Image image;
2632 Font font;
2633
2634private:
2635 SoftwareRendererSavedState& operator= (const SoftwareRendererSavedState&) = delete;
2636};
2637
2638//==============================================================================
2639template <class StateObjectType>
2641{
2642public:
2644 : currentState (initialState)
2645 {}
2646
2647 SavedStateStack() = default;
2648
2649 void initialise (StateObjectType* state)
2650 {
2651 currentState.reset (state);
2652 }
2653
2654 inline StateObjectType* operator->() const noexcept { return currentState.get(); }
2655 inline StateObjectType& operator*() const noexcept { return *currentState; }
2656
2657 void save()
2658 {
2659 stack.add (new StateObjectType (*currentState));
2660 }
2661
2662 void restore()
2663 {
2664 if (auto* top = stack.getLast())
2665 {
2666 currentState.reset (top);
2667 stack.removeLast (1, false);
2668 }
2669 else
2670 {
2671 jassertfalse; // trying to pop with an empty stack!
2672 }
2673 }
2674
2675 void beginTransparencyLayer (float opacity)
2676 {
2677 save();
2678 currentState.reset (currentState->beginTransparencyLayer (opacity));
2679 }
2680
2681 void endTransparencyLayer()
2682 {
2684 restore();
2685 currentState->endTransparencyLayer (*finishedTransparencyLayer);
2686 }
2687
2688private:
2691
2693};
2694
2695//==============================================================================
2696template <class SavedStateType>
2698{
2699public:
2700 bool isVectorDevice() const override { return false; }
2701 void setOrigin (Point<int> o) override { stack->transform.setOrigin (o); }
2702 void addTransform (const AffineTransform& t) override { stack->transform.addTransform (t); }
2703 float getPhysicalPixelScaleFactor() override { return stack->transform.getPhysicalPixelScaleFactor(); }
2704 Rectangle<int> getClipBounds() const override { return stack->getClipBounds(); }
2705 bool isClipEmpty() const override { return stack->clip == nullptr; }
2706 bool clipRegionIntersects (const Rectangle<int>& r) override { return stack->clipRegionIntersects (r); }
2707 bool clipToRectangle (const Rectangle<int>& r) override { return stack->clipToRectangle (r); }
2708 bool clipToRectangleList (const RectangleList<int>& r) override { return stack->clipToRectangleList (r); }
2709 void excludeClipRectangle (const Rectangle<int>& r) override { stack->excludeClipRectangle (r); }
2710 void clipToPath (const Path& path, const AffineTransform& t) override { stack->clipToPath (path, t); }
2711 void clipToImageAlpha (const Image& im, const AffineTransform& t) override { stack->clipToImageAlpha (im, t); }
2712 void saveState() override { stack.save(); }
2713 void restoreState() override { stack.restore(); }
2714 void beginTransparencyLayer (float opacity) override { stack.beginTransparencyLayer (opacity); }
2715 void endTransparencyLayer() override { stack.endTransparencyLayer(); }
2716 void setFill (const FillType& fillType) override { stack->setFillType (fillType); }
2717 void setOpacity (float newOpacity) override { stack->fillType.setOpacity (newOpacity); }
2718 void setInterpolationQuality (Graphics::ResamplingQuality quality) override { stack->interpolationQuality = quality; }
2719 void fillRect (const Rectangle<int>& r, bool replace) override { stack->fillRect (r, replace); }
2720 void fillRect (const Rectangle<float>& r) override { stack->fillRect (r); }
2721 void fillRectList (const RectangleList<float>& list) override { stack->fillRectList (list); }
2722 void fillPath (const Path& path, const AffineTransform& t) override { stack->fillPath (path, t); }
2723 void drawImage (const Image& im, const AffineTransform& t) override { stack->drawImage (im, t); }
2724 void drawGlyph (int glyphNumber, const AffineTransform& t) override { stack->drawGlyph (glyphNumber, t); }
2725 void drawLine (const Line<float>& line) override { stack->drawLine (line); }
2726 void setFont (const Font& newFont) override { stack->font = newFont; }
2727 const Font& getFont() override { return stack->font; }
2728
2729protected:
2730 StackBasedLowLevelGraphicsContext (SavedStateType* initialState) : stack (initialState) {}
2731 StackBasedLowLevelGraphicsContext() = default;
2732
2733 RenderingHelpers::SavedStateStack<SavedStateType> stack;
2734};
2735
2736JUCE_END_IGNORE_WARNINGS_MSVC
2737
2738} // namespace juce::RenderingHelpers
T ceil(T... args)
Represents a 2D affine-transformation matrix.
bool isSingularity() const noexcept
Returns true if this transform maps to a singularity - i.e.
static AffineTransform scale(float factorX, float factorY) noexcept
Returns a new transform which is a re-scale about the origin.
AffineTransform followedBy(const AffineTransform &other) const noexcept
Returns the result of concatenating another transformation after this one.
bool isOnlyTranslation() const noexcept
Returns true if the transform only translates, and doesn't scale or rotate the points.
float getTranslationY() const noexcept
If this transform is only a translation, this returns the X offset.
static AffineTransform translation(float deltaX, float deltaY) noexcept
Returns a new transform which is a translation.
float getTranslationX() const noexcept
If this transform is only a translation, this returns the X offset.
AffineTransform translated(float deltaX, float deltaY) const noexcept
Returns a new transform which is the same as this one followed by a translation.
Describes the layout and colours that should be used to paint a colour gradient.
int createLookupTable(const AffineTransform &transform, HeapBlock< PixelARGB > &resultLookupTable) const
Creates a set of interpolated premultiplied ARGB values.
bool isRadial
If true, the gradient should be filled circularly, centred around point1, with point2 defining a poin...
float getBrightness() const noexcept
Returns the colour's brightness component.
PixelARGB getPixelARGB() const noexcept
Returns a premultiplied ARGB pixel object that represents this colour.
uint8 getAlpha() const noexcept
Returns the colour's alpha (opacity).
bool isTransparent() const noexcept
Returns true if this colour is completely transparent.
Classes derived from this will be automatically deleted when the application exits.
A table of horizontal scan-line segments - used for rasterising Paths.
Represents a colour or fill pattern to use for rendering paths.
bool isGradient() const noexcept
Returns true if this is a gradient fill.
Image image
The image that should be used for tiling.
AffineTransform transform
The transform that should be applied to the image or gradient that's being drawn.
float getOpacity() const noexcept
Returns the current opacity to be applied to the colour, gradient, or image.
bool isTiledImage() const noexcept
Returns true if this is a tiled image pattern fill.
Colour colour
The solid colour being used.
bool isColour() const noexcept
Returns true if this is a solid colour fill, and not a gradient or image.
std::unique_ptr< ColourGradient > gradient
Returns the gradient that should be used for filling.
Represents a particular font, including its size, style, etc.
Definition juce_Font.h:42
Typeface::Ptr getTypefacePtr() const
Returns the typeface used by this font.
float getHeight() const noexcept
Returns the total height of this font, in pixels.
void setHorizontalScale(float scaleFactor)
Changes the font's horizontal scale factor.
void setHeight(float newHeight)
Changes the font's height.
float getHorizontalScale() const noexcept
Returns the font's horizontal scale.
Automatically locks and unlocks a mutex object.
ResamplingQuality
Types of rendering quality that can be specified when drawing images.
@ lowResamplingQuality
Just uses a nearest-neighbour algorithm for resampling.
@ mediumResamplingQuality
Uses bilinear interpolation for upsampling and area-averaging for downsampling.
Very simple container class to hold a pointer to some data on the heap.
Retrieves a section of an image as raw pixel data, so it can be read or written to.
Definition juce_Image.h:310
int pixelStride
The number of bytes between each pixel.
Definition juce_Image.h:355
uint8 * getPixelPointer(int x, int y) const noexcept
Returns a pointer to a pixel in the image.
Definition juce_Image.h:334
int lineStride
The number of bytes between each line.
Definition juce_Image.h:354
PixelFormat pixelFormat
The format of the data.
Definition juce_Image.h:353
Holds a fixed-size bitmap.
Definition juce_Image.h:58
int getWidth() const noexcept
Returns the image's width (in pixels).
std::unique_ptr< LowLevelGraphicsContext > createLowLevelContext() const
Creates a context suitable for drawing onto this image.
bool hasAlphaChannel() const noexcept
True if the image contains an alpha-channel.
int getHeight() const noexcept
Returns the image's height (in pixels).
Rectangle< int > getBounds() const noexcept
Returns a rectangle with the same size as this image.
@ SingleChannel
< each pixel is a 1-byte alpha channel value.
Definition juce_Image.h:68
@ ARGB
< each pixel is a 4-byte ARGB premultiplied colour value.
Definition juce_Image.h:67
@ RGB
< each pixel is a 3-byte packed RGB colour value.
Definition juce_Image.h:66
Represents a line.
Definition juce_Line.h:47
Interface class for graphics context objects, used internally by the Graphics class.
An array designed for holding objects.
void removeLast(int howManyToRemove=1, bool deleteObjects=true)
Removes the last n objects from the array.
ObjectClass * add(ObjectClass *newObject)
Appends a new object to the end of the array.
ObjectClass * getLast() const noexcept
Returns a pointer to the last object in the array.
A path is a sequence of lines and curves that may either form a closed shape or be open-ended.
Definition juce_Path.h:65
Rectangle< float > getBoundsTransformed(const AffineTransform &transform) const noexcept
Returns the smallest rectangle that contains all points within the path after it's been transformed w...
void addRectangle(float x, float y, float width, float height)
Adds a rectangle to the path.
void applyTransform(const AffineTransform &transform) noexcept
Applies a 2D transform to all the vertices in the path.
void addLineSegment(Line< float > line, float lineThickness)
Adds a line with a specified thickness.
void setUsingNonZeroWinding(bool isNonZeroWinding) noexcept
Changes the winding-rule to be used when filling the path.
Represents a 32-bit INTERNAL pixel with premultiplied alpha, and can perform compositing operations w...
Represents an 8-bit single-channel pixel, and can perform compositing operations on it.
Represents a 24-bit RGB pixel, and can perform compositing operations on it.
A pair of (x, y) coordinates.
Definition juce_Point.h:42
constexpr Point translated(ValueType deltaX, ValueType deltaY) const noexcept
Returns a point with a given offset from this one.
Definition juce_Point.h:97
constexpr Point< float > toFloat() const noexcept
Casts this point to a Point<float> object.
Definition juce_Point.h:239
void applyTransform(const AffineTransform &transform) noexcept
Uses a transform to change the point's coordinates.
Definition juce_Point.h:225
ValueType y
The point's Y coordinate.
Definition juce_Point.h:252
ValueType x
The point's X coordinate.
Definition juce_Point.h:251
Maintains a set of rectangles as a complex region.
RectangleType getBounds() const noexcept
Returns the smallest rectangle that can enclose the whole of this region.
Path toPath() const
Creates a Path object to represent this region.
void offsetAll(Point< ValueType > offset) noexcept
Adds an x and y value to all the coordinates.
bool intersects(const RectangleList &other) const noexcept
Checks whether this region intersects any part of another one.
bool clipTo(RectangleType rect)
Removes any areas of the region that lie outside a given rectangle.
void add(RectangleType rect)
Merges a new rectangle into the list.
void subtract(const RectangleType rect)
Removes a rectangular region from the list.
bool isEmpty() const noexcept
Returns true if the region is empty.
void transformAll(const AffineTransform &transform) noexcept
Applies a transform to all the rectangles.
Manages a rectangle and allows geometric operations to be performed on it.
ValueType getRight() const noexcept
Returns the x coordinate of the rectangle's right-hand-side.
bool intersects(Rectangle other) const noexcept
Returns true if any part of another rectangle overlaps this one.
Rectangle< float > toFloat() const noexcept
Casts this rectangle to a Rectangle<float>.
ValueType getX() const noexcept
Returns the x coordinate of the rectangle's left-hand-side.
Rectangle< int > getSmallestIntegerContainer() const noexcept
Returns the smallest integer-aligned rectangle that completely contains this one.
Rectangle getIntersection(Rectangle other) const noexcept
Returns the region that is the overlap between this and another rectangle.
ValueType getBottom() const noexcept
Returns the y coordinate of the rectangle's bottom edge.
ValueType getWidth() const noexcept
Returns the width of the rectangle.
ValueType getY() const noexcept
Returns the y coordinate of the rectangle's top edge.
bool isEmpty() const noexcept
Returns true if the rectangle's width or height are zero or less.
Rectangle transformedBy(const AffineTransform &transform) const noexcept
Returns the smallest rectangle that can contain the shape created by applying a transform to this rec...
ValueType getHeight() const noexcept
Returns the height of the rectangle.
Holds a list of objects derived from ReferenceCountedObject, or which implement basic reference-count...
int size() const noexcept
Returns the current number of objects in the array.
void ensureStorageAllocated(const int minNumElements)
Increases the array's internal storage to hold a minimum number of elements.
void clear()
Removes all objects from the array.
ObjectClassPtr getLast() const noexcept
Returns a pointer to the last object in the array.
ObjectClass * add(ObjectClass *newObject)
Appends a new object to the end of the array.
A smart-pointer class which points to a reference-counted object.
A base class which provides methods for reference-counting.
Holds a cache of recently-used glyph objects of some type.
void setOrigin(Point< int > o) override
Moves the origin to a new position.
bool isVectorDevice() const override
Returns true if this device is vector-based, e.g.
Holds either a simple integer translation, or an affine transform.
virtual EdgeTable * getEdgeTableForGlyph(int glyphNumber, const AffineTransform &transform, float fontHeight)
Returns a new EdgeTable that contains the path for the given glyph, with the specified transform appl...
T floor(T... args)
T get(T... args)
#define jassert(expression)
Platform-independent assertion macro.
#define JUCE_DECLARE_NON_COPYABLE(className)
This is a shorthand macro for deleting a class's copy constructor and copy assignment operator.
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for ...
#define forcedinline
A platform-independent way of forcing an inline function.
#define jassertfalse
This will always cause an assertion failure.
typedef int
typedef float
T max(T... args)
memcpy
memset
constexpr bool approximatelyEqual(Type a, Type b, Tolerance< Type > tolerance=Tolerance< Type >{} .withAbsolute(std::numeric_limits< Type >::min()) .withRelative(std::numeric_limits< Type >::epsilon()))
Returns true if the two floating-point numbers are approximately equal.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Constrains a value to keep it within a given range.
Type * addBytesToPointer(Type *basePointer, IntegerType bytes) noexcept
A handy function which adds a number of bytes to any type of pointer and returns the result.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Returns true if a value is at least zero, and also below a specified upper limit.
unsigned int uint32
A platform-independent 32-bit unsigned integer type.
IntegerType negativeAwareModulo(IntegerType dividend, const IntegerType divisor) noexcept
Performs a modulo operation, but can cope with the dividend being negative.
unsigned char uint8
A platform-independent 8-bit unsigned integer type.
int roundToInt(const FloatType value) noexcept
Fast floating-point-to-integer conversion.
T release(T... args)
remainder
T reset(T... args)
T sqrt(T... args)
typedef int64_t
A simple wrapper around std::atomic.
Definition juce_Atomic.h:42
Type get() const noexcept
Atomically reads and returns the current value.
Definition juce_Atomic.h:64
Calculates the alpha values and positions for rendering the edges of a non-pixel-aligned rectangle.
Iterates the colour of pixels in a linear gradient.
Iterates the colour of pixels in a circular radial gradient.
Iterates the colour of pixels in a skewed radial gradient.
typedef size_t
y1