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 namespace
30 {
31     template <typename Type>
coordsToRectangle(Type x,Type y,Type w,Type h)32     Rectangle<Type> coordsToRectangle (Type x, Type y, Type w, Type h) noexcept
33     {
34        #if JUCE_DEBUG
35         const int maxVal = 0x3fffffff;
36 
37         jassert ((int) x >= -maxVal && (int) x <= maxVal
38               && (int) y >= -maxVal && (int) y <= maxVal
39               && (int) w >= 0 && (int) w <= maxVal
40               && (int) h >= 0 && (int) h <= maxVal);
41        #endif
42 
43         return { x, y, w, h };
44     }
45 }
46 
47 //==============================================================================
LowLevelGraphicsContext()48 LowLevelGraphicsContext::LowLevelGraphicsContext() {}
~LowLevelGraphicsContext()49 LowLevelGraphicsContext::~LowLevelGraphicsContext() {}
50 
51 //==============================================================================
Graphics(const Image & imageToDrawOnto)52 Graphics::Graphics (const Image& imageToDrawOnto)
53     : contextHolder (imageToDrawOnto.createLowLevelContext()),
54       context (*contextHolder)
55 {
56     jassert (imageToDrawOnto.isValid()); // Can't draw into a null image!
57 }
58 
Graphics(LowLevelGraphicsContext & internalContext)59 Graphics::Graphics (LowLevelGraphicsContext& internalContext) noexcept
60     : context (internalContext)
61 {
62 }
63 
~Graphics()64 Graphics::~Graphics()
65 {
66 }
67 
68 //==============================================================================
resetToDefaultState()69 void Graphics::resetToDefaultState()
70 {
71     saveStateIfPending();
72     context.setFill (FillType());
73     context.setFont (Font());
74     context.setInterpolationQuality (Graphics::mediumResamplingQuality);
75 }
76 
isVectorDevice() const77 bool Graphics::isVectorDevice() const
78 {
79     return context.isVectorDevice();
80 }
81 
reduceClipRegion(Rectangle<int> area)82 bool Graphics::reduceClipRegion (Rectangle<int> area)
83 {
84     saveStateIfPending();
85     return context.clipToRectangle (area);
86 }
87 
reduceClipRegion(int x,int y,int w,int h)88 bool Graphics::reduceClipRegion (int x, int y, int w, int h)
89 {
90     return reduceClipRegion (coordsToRectangle (x, y, w, h));
91 }
92 
reduceClipRegion(const RectangleList<int> & clipRegion)93 bool Graphics::reduceClipRegion (const RectangleList<int>& clipRegion)
94 {
95     saveStateIfPending();
96     return context.clipToRectangleList (clipRegion);
97 }
98 
reduceClipRegion(const Path & path,const AffineTransform & transform)99 bool Graphics::reduceClipRegion (const Path& path, const AffineTransform& transform)
100 {
101     saveStateIfPending();
102     context.clipToPath (path, transform);
103     return ! context.isClipEmpty();
104 }
105 
reduceClipRegion(const Image & image,const AffineTransform & transform)106 bool Graphics::reduceClipRegion (const Image& image, const AffineTransform& transform)
107 {
108     saveStateIfPending();
109     context.clipToImageAlpha (image, transform);
110     return ! context.isClipEmpty();
111 }
112 
excludeClipRegion(Rectangle<int> rectangleToExclude)113 void Graphics::excludeClipRegion (Rectangle<int> rectangleToExclude)
114 {
115     saveStateIfPending();
116     context.excludeClipRectangle (rectangleToExclude);
117 }
118 
isClipEmpty() const119 bool Graphics::isClipEmpty() const
120 {
121     return context.isClipEmpty();
122 }
123 
getClipBounds() const124 Rectangle<int> Graphics::getClipBounds() const
125 {
126     return context.getClipBounds();
127 }
128 
saveState()129 void Graphics::saveState()
130 {
131     saveStateIfPending();
132     saveStatePending = true;
133 }
134 
restoreState()135 void Graphics::restoreState()
136 {
137     if (saveStatePending)
138         saveStatePending = false;
139     else
140         context.restoreState();
141 }
142 
saveStateIfPending()143 void Graphics::saveStateIfPending()
144 {
145     if (saveStatePending)
146     {
147         saveStatePending = false;
148         context.saveState();
149     }
150 }
151 
setOrigin(Point<int> newOrigin)152 void Graphics::setOrigin (Point<int> newOrigin)
153 {
154     saveStateIfPending();
155     context.setOrigin (newOrigin);
156 }
157 
setOrigin(int x,int y)158 void Graphics::setOrigin (int x, int y)
159 {
160     setOrigin ({ x, y });
161 }
162 
addTransform(const AffineTransform & transform)163 void Graphics::addTransform (const AffineTransform& transform)
164 {
165     saveStateIfPending();
166     context.addTransform (transform);
167 }
168 
clipRegionIntersects(Rectangle<int> area) const169 bool Graphics::clipRegionIntersects (Rectangle<int> area) const
170 {
171     return context.clipRegionIntersects (area);
172 }
173 
beginTransparencyLayer(float layerOpacity)174 void Graphics::beginTransparencyLayer (float layerOpacity)
175 {
176     saveStateIfPending();
177     context.beginTransparencyLayer (layerOpacity);
178 }
179 
endTransparencyLayer()180 void Graphics::endTransparencyLayer()
181 {
182     context.endTransparencyLayer();
183 }
184 
185 //==============================================================================
setColour(Colour newColour)186 void Graphics::setColour (Colour newColour)
187 {
188     saveStateIfPending();
189     context.setFill (newColour);
190 }
191 
setOpacity(float newOpacity)192 void Graphics::setOpacity (float newOpacity)
193 {
194     saveStateIfPending();
195     context.setOpacity (newOpacity);
196 }
197 
setGradientFill(const ColourGradient & gradient)198 void Graphics::setGradientFill (const ColourGradient& gradient)
199 {
200     setFillType (gradient);
201 }
202 
setGradientFill(ColourGradient && gradient)203 void Graphics::setGradientFill (ColourGradient&& gradient)
204 {
205     setFillType (std::move (gradient));
206 }
207 
setTiledImageFill(const Image & imageToUse,const int anchorX,const int anchorY,const float opacity)208 void Graphics::setTiledImageFill (const Image& imageToUse, const int anchorX, const int anchorY, const float opacity)
209 {
210     saveStateIfPending();
211     context.setFill (FillType (imageToUse, AffineTransform::translation ((float) anchorX, (float) anchorY)));
212     context.setOpacity (opacity);
213 }
214 
setFillType(const FillType & newFill)215 void Graphics::setFillType (const FillType& newFill)
216 {
217     saveStateIfPending();
218     context.setFill (newFill);
219 }
220 
221 //==============================================================================
setFont(const Font & newFont)222 void Graphics::setFont (const Font& newFont)
223 {
224     saveStateIfPending();
225     context.setFont (newFont);
226 }
227 
setFont(const float newFontHeight)228 void Graphics::setFont (const float newFontHeight)
229 {
230     setFont (context.getFont().withHeight (newFontHeight));
231 }
232 
getCurrentFont() const233 Font Graphics::getCurrentFont() const
234 {
235     return context.getFont();
236 }
237 
238 //==============================================================================
drawSingleLineText(const String & text,const int startX,const int baselineY,Justification justification) const239 void Graphics::drawSingleLineText (const String& text, const int startX, const int baselineY,
240                                    Justification justification) const
241 {
242     if (text.isNotEmpty())
243     {
244         // Don't pass any vertical placement flags to this method - they'll be ignored.
245         jassert (justification.getOnlyVerticalFlags() == 0);
246 
247         auto flags = justification.getOnlyHorizontalFlags();
248 
249         if (flags == Justification::right && startX < context.getClipBounds().getX())
250             return;
251 
252         if (flags == Justification::left && startX > context.getClipBounds().getRight())
253             return;
254 
255         GlyphArrangement arr;
256         arr.addLineOfText (context.getFont(), text, (float) startX, (float) baselineY);
257 
258         if (flags != Justification::left)
259         {
260             auto w = arr.getBoundingBox (0, -1, true).getWidth();
261 
262             if ((flags & (Justification::horizontallyCentred | Justification::horizontallyJustified)) != 0)
263                 w /= 2.0f;
264 
265             arr.draw (*this, AffineTransform::translation (-w, 0));
266         }
267         else
268         {
269             arr.draw (*this);
270         }
271     }
272 }
273 
drawMultiLineText(const String & text,const int startX,const int baselineY,const int maximumLineWidth,Justification justification,const float leading) const274 void Graphics::drawMultiLineText (const String& text, const int startX,
275                                   const int baselineY, const int maximumLineWidth,
276                                   Justification justification, const float leading) const
277 {
278     if (text.isNotEmpty()
279          && startX < context.getClipBounds().getRight())
280     {
281         GlyphArrangement arr;
282         arr.addJustifiedText (context.getFont(), text,
283                               (float) startX, (float) baselineY, (float) maximumLineWidth,
284                               justification, leading);
285         arr.draw (*this);
286     }
287 }
288 
drawText(const String & text,Rectangle<float> area,Justification justificationType,bool useEllipsesIfTooBig) const289 void Graphics::drawText (const String& text, Rectangle<float> area,
290                          Justification justificationType, bool useEllipsesIfTooBig) const
291 {
292     if (text.isNotEmpty() && context.clipRegionIntersects (area.getSmallestIntegerContainer()))
293     {
294         GlyphArrangement arr;
295         arr.addCurtailedLineOfText (context.getFont(), text, 0.0f, 0.0f,
296                                     area.getWidth(), useEllipsesIfTooBig);
297 
298         arr.justifyGlyphs (0, arr.getNumGlyphs(),
299                            area.getX(), area.getY(), area.getWidth(), area.getHeight(),
300                            justificationType);
301         arr.draw (*this);
302     }
303 }
304 
drawText(const String & text,Rectangle<int> area,Justification justificationType,bool useEllipsesIfTooBig) const305 void Graphics::drawText (const String& text, Rectangle<int> area,
306                          Justification justificationType, bool useEllipsesIfTooBig) const
307 {
308     drawText (text, area.toFloat(), justificationType, useEllipsesIfTooBig);
309 }
310 
drawText(const String & text,int x,int y,int width,int height,Justification justificationType,const bool useEllipsesIfTooBig) const311 void Graphics::drawText (const String& text, int x, int y, int width, int height,
312                          Justification justificationType, const bool useEllipsesIfTooBig) const
313 {
314     drawText (text, coordsToRectangle (x, y, width, height), justificationType, useEllipsesIfTooBig);
315 }
316 
drawFittedText(const String & text,Rectangle<int> area,Justification justification,const int maximumNumberOfLines,const float minimumHorizontalScale) const317 void Graphics::drawFittedText (const String& text, Rectangle<int> area,
318                                Justification justification,
319                                const int maximumNumberOfLines,
320                                const float minimumHorizontalScale) const
321 {
322     if (text.isNotEmpty() && (! area.isEmpty()) && context.clipRegionIntersects (area))
323     {
324         GlyphArrangement arr;
325         arr.addFittedText (context.getFont(), text,
326                            (float) area.getX(), (float) area.getY(),
327                            (float) area.getWidth(), (float) area.getHeight(),
328                            justification,
329                            maximumNumberOfLines,
330                            minimumHorizontalScale);
331 
332         arr.draw (*this);
333     }
334 }
335 
drawFittedText(const String & text,int x,int y,int width,int height,Justification justification,const int maximumNumberOfLines,const float minimumHorizontalScale) const336 void Graphics::drawFittedText (const String& text, int x, int y, int width, int height,
337                                Justification justification,
338                                const int maximumNumberOfLines,
339                                const float minimumHorizontalScale) const
340 {
341     drawFittedText (text, coordsToRectangle (x, y, width, height),
342                     justification, maximumNumberOfLines, minimumHorizontalScale);
343 }
344 
345 //==============================================================================
fillRect(Rectangle<int> r) const346 void Graphics::fillRect (Rectangle<int> r) const
347 {
348     context.fillRect (r, false);
349 }
350 
fillRect(Rectangle<float> r) const351 void Graphics::fillRect (Rectangle<float> r) const
352 {
353     context.fillRect (r);
354 }
355 
fillRect(int x,int y,int width,int height) const356 void Graphics::fillRect (int x, int y, int width, int height) const
357 {
358     context.fillRect (coordsToRectangle (x, y, width, height), false);
359 }
360 
fillRect(float x,float y,float width,float height) const361 void Graphics::fillRect (float x, float y, float width, float height) const
362 {
363     fillRect (coordsToRectangle (x, y, width, height));
364 }
365 
fillRectList(const RectangleList<float> & rectangles) const366 void Graphics::fillRectList (const RectangleList<float>& rectangles) const
367 {
368     context.fillRectList (rectangles);
369 }
370 
fillRectList(const RectangleList<int> & rects) const371 void Graphics::fillRectList (const RectangleList<int>& rects) const
372 {
373     for (auto& r : rects)
374         context.fillRect (r, false);
375 }
376 
fillAll() const377 void Graphics::fillAll() const
378 {
379     fillRect (context.getClipBounds());
380 }
381 
fillAll(Colour colourToUse) const382 void Graphics::fillAll (Colour colourToUse) const
383 {
384     if (! colourToUse.isTransparent())
385     {
386         auto clip = context.getClipBounds();
387 
388         context.saveState();
389         context.setFill (colourToUse);
390         context.fillRect (clip, false);
391         context.restoreState();
392     }
393 }
394 
395 
396 //==============================================================================
fillPath(const Path & path) const397 void Graphics::fillPath (const Path& path) const
398 {
399     if (! (context.isClipEmpty() || path.isEmpty()))
400         context.fillPath (path, AffineTransform());
401 }
402 
fillPath(const Path & path,const AffineTransform & transform) const403 void Graphics::fillPath (const Path& path, const AffineTransform& transform) const
404 {
405     if (! (context.isClipEmpty() || path.isEmpty()))
406         context.fillPath (path, transform);
407 }
408 
strokePath(const Path & path,const PathStrokeType & strokeType,const AffineTransform & transform) const409 void Graphics::strokePath (const Path& path,
410                            const PathStrokeType& strokeType,
411                            const AffineTransform& transform) const
412 {
413     Path stroke;
414     strokeType.createStrokedPath (stroke, path, transform, context.getPhysicalPixelScaleFactor());
415     fillPath (stroke);
416 }
417 
418 //==============================================================================
drawRect(float x,float y,float width,float height,float lineThickness) const419 void Graphics::drawRect (float x, float y, float width, float height, float lineThickness) const
420 {
421     drawRect (coordsToRectangle (x, y, width, height), lineThickness);
422 }
423 
drawRect(int x,int y,int width,int height,int lineThickness) const424 void Graphics::drawRect (int x, int y, int width, int height, int lineThickness) const
425 {
426     drawRect (coordsToRectangle (x, y, width, height), lineThickness);
427 }
428 
drawRect(Rectangle<int> r,int lineThickness) const429 void Graphics::drawRect (Rectangle<int> r, int lineThickness) const
430 {
431     drawRect (r.toFloat(), (float) lineThickness);
432 }
433 
drawRect(Rectangle<float> r,const float lineThickness) const434 void Graphics::drawRect (Rectangle<float> r, const float lineThickness) const
435 {
436     jassert (r.getWidth() >= 0.0f && r.getHeight() >= 0.0f);
437 
438     RectangleList<float> rects;
439     rects.addWithoutMerging (r.removeFromTop    (lineThickness));
440     rects.addWithoutMerging (r.removeFromBottom (lineThickness));
441     rects.addWithoutMerging (r.removeFromLeft   (lineThickness));
442     rects.addWithoutMerging (r.removeFromRight  (lineThickness));
443     context.fillRectList (rects);
444 }
445 
446 //==============================================================================
fillEllipse(Rectangle<float> area) const447 void Graphics::fillEllipse (Rectangle<float> area) const
448 {
449     Path p;
450     p.addEllipse (area);
451     fillPath (p);
452 }
453 
fillEllipse(float x,float y,float w,float h) const454 void Graphics::fillEllipse (float x, float y, float w, float h) const
455 {
456     fillEllipse (coordsToRectangle (x, y, w, h));
457 }
458 
drawEllipse(float x,float y,float width,float height,float lineThickness) const459 void Graphics::drawEllipse (float x, float y, float width, float height, float lineThickness) const
460 {
461     drawEllipse (coordsToRectangle (x, y, width, height), lineThickness);
462 }
463 
drawEllipse(Rectangle<float> area,float lineThickness) const464 void Graphics::drawEllipse (Rectangle<float> area, float lineThickness) const
465 {
466     Path p;
467 
468     if (area.getWidth() == area.getHeight())
469     {
470         // For a circle, we can avoid having to generate a stroke
471         p.addEllipse (area.expanded (lineThickness * 0.5f));
472         p.addEllipse (area.reduced  (lineThickness * 0.5f));
473         p.setUsingNonZeroWinding (false);
474         fillPath (p);
475     }
476     else
477     {
478         p.addEllipse (area);
479         strokePath (p, PathStrokeType (lineThickness));
480     }
481 }
482 
fillRoundedRectangle(float x,float y,float width,float height,float cornerSize) const483 void Graphics::fillRoundedRectangle (float x, float y, float width, float height, float cornerSize) const
484 {
485     fillRoundedRectangle (coordsToRectangle (x, y, width, height), cornerSize);
486 }
487 
fillRoundedRectangle(Rectangle<float> r,const float cornerSize) const488 void Graphics::fillRoundedRectangle (Rectangle<float> r, const float cornerSize) const
489 {
490     Path p;
491     p.addRoundedRectangle (r, cornerSize);
492     fillPath (p);
493 }
494 
drawRoundedRectangle(float x,float y,float width,float height,float cornerSize,float lineThickness) const495 void Graphics::drawRoundedRectangle (float x, float y, float width, float height,
496                                      float cornerSize, float lineThickness) const
497 {
498     drawRoundedRectangle (coordsToRectangle (x, y, width, height), cornerSize, lineThickness);
499 }
500 
drawRoundedRectangle(Rectangle<float> r,float cornerSize,float lineThickness) const501 void Graphics::drawRoundedRectangle (Rectangle<float> r, float cornerSize, float lineThickness) const
502 {
503     Path p;
504     p.addRoundedRectangle (r, cornerSize);
505     strokePath (p, PathStrokeType (lineThickness));
506 }
507 
drawArrow(Line<float> line,float lineThickness,float arrowheadWidth,float arrowheadLength) const508 void Graphics::drawArrow (Line<float> line, float lineThickness, float arrowheadWidth, float arrowheadLength) const
509 {
510     Path p;
511     p.addArrow (line, lineThickness, arrowheadWidth, arrowheadLength);
512     fillPath (p);
513 }
514 
fillCheckerBoard(Rectangle<float> area,float checkWidth,float checkHeight,Colour colour1,Colour colour2) const515 void Graphics::fillCheckerBoard (Rectangle<float> area, float checkWidth, float checkHeight,
516                                  Colour colour1, Colour colour2) const
517 {
518     jassert (checkWidth > 0 && checkHeight > 0); // can't be zero or less!
519 
520     if (checkWidth > 0 && checkHeight > 0)
521     {
522         context.saveState();
523 
524         if (colour1 == colour2)
525         {
526             context.setFill (colour1);
527             context.fillRect (area);
528         }
529         else
530         {
531             auto clipped = context.getClipBounds().getIntersection (area.getSmallestIntegerContainer());
532 
533             if (! clipped.isEmpty())
534             {
535                 const int checkNumX = (int) (((float) clipped.getX() - area.getX()) / checkWidth);
536                 const int checkNumY = (int) (((float) clipped.getY() - area.getY()) / checkHeight);
537                 const float startX = area.getX() + (float) checkNumX * checkWidth;
538                 const float startY = area.getY() + (float) checkNumY * checkHeight;
539                 const float right  = (float) clipped.getRight();
540                 const float bottom = (float) clipped.getBottom();
541 
542                 for (int i = 0; i < 2; ++i)
543                 {
544                     int cy = i;
545                     RectangleList<float> checks;
546 
547                     for (float y = startY; y < bottom; y += checkHeight)
548                         for (float x = startX + (cy++ & 1) * checkWidth; x < right; x += checkWidth * 2.0f)
549                             checks.addWithoutMerging ({ x, y, checkWidth, checkHeight });
550 
551                     checks.clipTo (area);
552                     context.setFill (i == ((checkNumX ^ checkNumY) & 1) ? colour1 : colour2);
553                     context.fillRectList (checks);
554                 }
555             }
556         }
557 
558         context.restoreState();
559     }
560 }
561 
562 //==============================================================================
drawVerticalLine(const int x,float top,float bottom) const563 void Graphics::drawVerticalLine (const int x, float top, float bottom) const
564 {
565     if (top < bottom)
566         context.fillRect (Rectangle<float> ((float) x, top, 1.0f, bottom - top));
567 }
568 
drawHorizontalLine(const int y,float left,float right) const569 void Graphics::drawHorizontalLine (const int y, float left, float right) const
570 {
571     if (left < right)
572         context.fillRect (Rectangle<float> (left, (float) y, right - left, 1.0f));
573 }
574 
drawLine(Line<float> line) const575 void Graphics::drawLine (Line<float> line) const
576 {
577     context.drawLine (line);
578 }
579 
drawLine(float x1,float y1,float x2,float y2) const580 void Graphics::drawLine (float x1, float y1, float x2, float y2) const
581 {
582     context.drawLine (Line<float> (x1, y1, x2, y2));
583 }
584 
drawLine(float x1,float y1,float x2,float y2,float lineThickness) const585 void Graphics::drawLine (float x1, float y1, float x2, float y2, float lineThickness) const
586 {
587     drawLine (Line<float> (x1, y1, x2, y2), lineThickness);
588 }
589 
drawLine(Line<float> line,const float lineThickness) const590 void Graphics::drawLine (Line<float> line, const float lineThickness) const
591 {
592     Path p;
593     p.addLineSegment (line, lineThickness);
594     fillPath (p);
595 }
596 
drawDashedLine(Line<float> line,const float * dashLengths,int numDashLengths,float lineThickness,int n) const597 void Graphics::drawDashedLine (Line<float> line, const float* dashLengths,
598                                int numDashLengths, float lineThickness, int n) const
599 {
600     jassert (n >= 0 && n < numDashLengths); // your start index must be valid!
601 
602     const Point<double> delta ((line.getEnd() - line.getStart()).toDouble());
603     const double totalLen = delta.getDistanceFromOrigin();
604 
605     if (totalLen >= 0.1)
606     {
607         const double onePixAlpha = 1.0 / totalLen;
608 
609         for (double alpha = 0.0; alpha < 1.0;)
610         {
611             jassert (dashLengths[n] > 0); // can't have zero-length dashes!
612 
613             const double lastAlpha = alpha;
614             alpha += dashLengths [n] * onePixAlpha;
615             n = (n + 1) % numDashLengths;
616 
617             if ((n & 1) != 0)
618             {
619                 const Line<float> segment (line.getStart() + (delta * lastAlpha).toFloat(),
620                                            line.getStart() + (delta * jmin (1.0, alpha)).toFloat());
621 
622                 if (lineThickness != 1.0f)
623                     drawLine (segment, lineThickness);
624                 else
625                     context.drawLine (segment);
626             }
627         }
628     }
629 }
630 
631 //==============================================================================
setImageResamplingQuality(const Graphics::ResamplingQuality newQuality)632 void Graphics::setImageResamplingQuality (const Graphics::ResamplingQuality newQuality)
633 {
634     saveStateIfPending();
635     context.setInterpolationQuality (newQuality);
636 }
637 
638 //==============================================================================
drawImageAt(const Image & imageToDraw,int x,int y,bool fillAlphaChannel) const639 void Graphics::drawImageAt (const Image& imageToDraw, int x, int y, bool fillAlphaChannel) const
640 {
641     drawImageTransformed (imageToDraw,
642                           AffineTransform::translation ((float) x, (float) y),
643                           fillAlphaChannel);
644 }
645 
drawImage(const Image & imageToDraw,Rectangle<float> targetArea,RectanglePlacement placementWithinTarget,bool fillAlphaChannelWithCurrentBrush) const646 void Graphics::drawImage (const Image& imageToDraw, Rectangle<float> targetArea,
647                           RectanglePlacement placementWithinTarget, bool fillAlphaChannelWithCurrentBrush) const
648 {
649     if (imageToDraw.isValid())
650         drawImageTransformed (imageToDraw,
651                               placementWithinTarget.getTransformToFit (imageToDraw.getBounds().toFloat(), targetArea),
652                               fillAlphaChannelWithCurrentBrush);
653 }
654 
drawImageWithin(const Image & imageToDraw,int dx,int dy,int dw,int dh,RectanglePlacement placementWithinTarget,bool fillAlphaChannelWithCurrentBrush) const655 void Graphics::drawImageWithin (const Image& imageToDraw, int dx, int dy, int dw, int dh,
656                                 RectanglePlacement placementWithinTarget, bool fillAlphaChannelWithCurrentBrush) const
657 {
658     drawImage (imageToDraw, coordsToRectangle (dx, dy, dw, dh).toFloat(),
659                placementWithinTarget, fillAlphaChannelWithCurrentBrush);
660 }
661 
drawImage(const Image & imageToDraw,int dx,int dy,int dw,int dh,int sx,int sy,int sw,int sh,const bool fillAlphaChannelWithCurrentBrush) const662 void Graphics::drawImage (const Image& imageToDraw,
663                           int dx, int dy, int dw, int dh,
664                           int sx, int sy, int sw, int sh,
665                           const bool fillAlphaChannelWithCurrentBrush) const
666 {
667     if (imageToDraw.isValid() && context.clipRegionIntersects (coordsToRectangle (dx, dy, dw, dh)))
668         drawImageTransformed (imageToDraw.getClippedImage (coordsToRectangle (sx, sy, sw, sh)),
669                               AffineTransform::scale ((float) dw / (float) sw, (float) dh / (float) sh)
670                                               .translated ((float) dx, (float) dy),
671                               fillAlphaChannelWithCurrentBrush);
672 }
673 
drawImageTransformed(const Image & imageToDraw,const AffineTransform & transform,const bool fillAlphaChannelWithCurrentBrush) const674 void Graphics::drawImageTransformed (const Image& imageToDraw,
675                                      const AffineTransform& transform,
676                                      const bool fillAlphaChannelWithCurrentBrush) const
677 {
678     if (imageToDraw.isValid() && ! context.isClipEmpty())
679     {
680         if (fillAlphaChannelWithCurrentBrush)
681         {
682             context.saveState();
683             context.clipToImageAlpha (imageToDraw, transform);
684             fillAll();
685             context.restoreState();
686         }
687         else
688         {
689             context.drawImage (imageToDraw, transform);
690         }
691     }
692 }
693 
694 //==============================================================================
ScopedSaveState(Graphics & g)695 Graphics::ScopedSaveState::ScopedSaveState (Graphics& g)  : context (g)
696 {
697     context.saveState();
698 }
699 
~ScopedSaveState()700 Graphics::ScopedSaveState::~ScopedSaveState()
701 {
702     context.restoreState();
703 }
704 
705 } // namespace juce
706