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