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