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