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 template <typename Type>
rectangleToRectF(const Rectangle<Type> & r)30 D2D1_RECT_F rectangleToRectF (const Rectangle<Type>& r)
31 {
32 return { (float) r.getX(), (float) r.getY(), (float) r.getRight(), (float) r.getBottom() };
33 }
34
colourToD2D(Colour c)35 static D2D1_COLOR_F colourToD2D (Colour c)
36 {
37 return { c.getFloatRed(), c.getFloatGreen(), c.getFloatBlue(), c.getFloatAlpha() };
38 }
39
pathToGeometrySink(const Path & path,ID2D1GeometrySink * sink,const AffineTransform & transform)40 static void pathToGeometrySink (const Path& path, ID2D1GeometrySink* sink, const AffineTransform& transform)
41 {
42 Path::Iterator it (path);
43
44 while (it.next())
45 {
46 switch (it.elementType)
47 {
48 case Path::Iterator::cubicTo:
49 {
50 transform.transformPoint (it.x1, it.y1);
51 transform.transformPoint (it.x2, it.y2);
52 transform.transformPoint (it.x3, it.y3);
53
54 sink->AddBezier ({ { it.x1, it.y1 }, { it.x2, it.y2 }, { it.x3, it.y3 } });
55 break;
56 }
57
58 case Path::Iterator::lineTo:
59 {
60 transform.transformPoint (it.x1, it.y1);
61 sink->AddLine ({ it.x1, it.y1 });
62 break;
63 }
64
65 case Path::Iterator::quadraticTo:
66 {
67 transform.transformPoint (it.x1, it.y1);
68 transform.transformPoint (it.x2, it.y2);
69 sink->AddQuadraticBezier ({ { it.x1, it.y1 }, { it.x2, it.y2 } });
70 break;
71 }
72
73 case Path::Iterator::closePath:
74 {
75 sink->EndFigure (D2D1_FIGURE_END_CLOSED);
76 break;
77 }
78
79 case Path::Iterator::startNewSubPath:
80 {
81 transform.transformPoint (it.x1, it.y1);
82 sink->BeginFigure ({ it.x1, it.y1 }, D2D1_FIGURE_BEGIN_FILLED);
83 break;
84 }
85 }
86 }
87 }
88
transformToMatrix(const AffineTransform & transform)89 static D2D1::Matrix3x2F transformToMatrix (const AffineTransform& transform)
90 {
91 return { transform.mat00, transform.mat10, transform.mat01, transform.mat11, transform.mat02, transform.mat12 };
92 }
93
pointTransformed(int x,int y,const AffineTransform & transform)94 static D2D1_POINT_2F pointTransformed (int x, int y, const AffineTransform& transform)
95 {
96 transform.transformPoint (x, y);
97 return { (FLOAT) x, (FLOAT) y };
98 }
99
rectToGeometrySink(const Rectangle<int> & rect,ID2D1GeometrySink * sink,const AffineTransform & transform)100 static void rectToGeometrySink (const Rectangle<int>& rect, ID2D1GeometrySink* sink, const AffineTransform& transform)
101 {
102 sink->BeginFigure (pointTransformed (rect.getX(), rect.getY(), transform), D2D1_FIGURE_BEGIN_FILLED);
103 sink->AddLine (pointTransformed (rect.getRight(), rect.getY(), transform));
104 sink->AddLine (pointTransformed (rect.getRight(), rect.getBottom(), transform));
105 sink->AddLine (pointTransformed (rect.getX(), rect.getBottom(), transform));
106 sink->EndFigure (D2D1_FIGURE_END_CLOSED);
107 }
108
109 //==============================================================================
110 struct Direct2DLowLevelGraphicsContext::Pimpl
111 {
rectListToPathGeometryjuce::Direct2DLowLevelGraphicsContext::Pimpl112 ID2D1PathGeometry* rectListToPathGeometry (const RectangleList<int>& clipRegion)
113 {
114 ID2D1PathGeometry* p = nullptr;
115 factories->d2dFactory->CreatePathGeometry (&p);
116
117 ComSmartPtr<ID2D1GeometrySink> sink;
118 auto hr = p->Open (sink.resetAndGetPointerAddress()); // xxx handle error
119 sink->SetFillMode (D2D1_FILL_MODE_WINDING);
120
121 for (int i = clipRegion.getNumRectangles(); --i >= 0;)
122 rectToGeometrySink (clipRegion.getRectangle(i), sink, AffineTransform());
123
124 hr = sink->Close();
125 return p;
126 }
127
pathToPathGeometryjuce::Direct2DLowLevelGraphicsContext::Pimpl128 ID2D1PathGeometry* pathToPathGeometry (const Path& path, const AffineTransform& transform)
129 {
130 ID2D1PathGeometry* p = nullptr;
131 factories->d2dFactory->CreatePathGeometry (&p);
132
133 ComSmartPtr<ID2D1GeometrySink> sink;
134 auto hr = p->Open (sink.resetAndGetPointerAddress());
135 sink->SetFillMode (D2D1_FILL_MODE_WINDING); // xxx need to check Path::isUsingNonZeroWinding()
136
137 pathToGeometrySink (path, sink, transform);
138
139 hr = sink->Close();
140 return p;
141 }
142
143 SharedResourcePointer<Direct2DFactories> factories;
144
145 ComSmartPtr<ID2D1HwndRenderTarget> renderingTarget;
146 ComSmartPtr<ID2D1SolidColorBrush> colourBrush;
147 };
148
149 //==============================================================================
150 struct Direct2DLowLevelGraphicsContext::SavedState
151 {
152 public:
SavedStatejuce::Direct2DLowLevelGraphicsContext::SavedState153 SavedState (Direct2DLowLevelGraphicsContext& owner_)
154 : owner (owner_)
155 {
156 if (owner.currentState != nullptr)
157 {
158 // xxx seems like a very slow way to create one of these, and this is a performance
159 // bottleneck.. Can the same internal objects be shared by multiple state objects, maybe using copy-on-write?
160 setFill (owner.currentState->fillType);
161 currentBrush = owner.currentState->currentBrush;
162 clipRect = owner.currentState->clipRect;
163 transform = owner.currentState->transform;
164
165 font = owner.currentState->font;
166 currentFontFace = owner.currentState->currentFontFace;
167 }
168 else
169 {
170 const auto size = owner.pimpl->renderingTarget->GetPixelSize();
171 clipRect.setSize (size.width, size.height);
172 setFill (FillType (Colours::black));
173 }
174 }
175
~SavedStatejuce::Direct2DLowLevelGraphicsContext::SavedState176 ~SavedState()
177 {
178 clearClip();
179 clearFont();
180 clearFill();
181 clearPathClip();
182 clearImageClip();
183 complexClipLayer = nullptr;
184 bitmapMaskLayer = nullptr;
185 }
186
clearClipjuce::Direct2DLowLevelGraphicsContext::SavedState187 void clearClip()
188 {
189 popClips();
190 shouldClipRect = false;
191 }
192
clipToRectanglejuce::Direct2DLowLevelGraphicsContext::SavedState193 void clipToRectangle (const Rectangle<int>& r)
194 {
195 clearClip();
196 clipRect = r.toFloat().transformedBy (transform).getSmallestIntegerContainer();
197 shouldClipRect = true;
198 pushClips();
199 }
200
clearPathClipjuce::Direct2DLowLevelGraphicsContext::SavedState201 void clearPathClip()
202 {
203 popClips();
204
205 if (shouldClipComplex)
206 {
207 complexClipGeometry = nullptr;
208 shouldClipComplex = false;
209 }
210 }
211
clipToPath(ID2D1Geometry * geometry)212 void Direct2DLowLevelGraphicsContext::SavedState::clipToPath (ID2D1Geometry* geometry)
213 {
214 clearPathClip();
215
216 if (complexClipLayer == nullptr)
217 owner.pimpl->renderingTarget->CreateLayer (complexClipLayer.resetAndGetPointerAddress());
218
219 complexClipGeometry = geometry;
220 shouldClipComplex = true;
221 pushClips();
222 }
223
clearRectListClipjuce::Direct2DLowLevelGraphicsContext::SavedState224 void clearRectListClip()
225 {
226 popClips();
227
228 if (shouldClipRectList)
229 {
230 rectListGeometry = nullptr;
231 shouldClipRectList = false;
232 }
233 }
234
clipToRectListjuce::Direct2DLowLevelGraphicsContext::SavedState235 void clipToRectList (ID2D1Geometry* geometry)
236 {
237 clearRectListClip();
238
239 if (rectListLayer == nullptr)
240 owner.pimpl->renderingTarget->CreateLayer (rectListLayer.resetAndGetPointerAddress());
241
242 rectListGeometry = geometry;
243 shouldClipRectList = true;
244 pushClips();
245 }
246
clearImageClipjuce::Direct2DLowLevelGraphicsContext::SavedState247 void clearImageClip()
248 {
249 popClips();
250
251 if (shouldClipBitmap)
252 {
253 maskBitmap = nullptr;
254 bitmapMaskBrush = nullptr;
255 shouldClipBitmap = false;
256 }
257 }
258
clipToImagejuce::Direct2DLowLevelGraphicsContext::SavedState259 void clipToImage (const Image& clipImage, const AffineTransform& clipTransform)
260 {
261 clearImageClip();
262
263 if (bitmapMaskLayer == nullptr)
264 owner.pimpl->renderingTarget->CreateLayer (bitmapMaskLayer.resetAndGetPointerAddress());
265
266 D2D1_BRUSH_PROPERTIES brushProps = { 1, transformToMatrix (clipTransform) };
267 auto bmProps = D2D1::BitmapBrushProperties (D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_WRAP);
268 D2D1_SIZE_U size = { (UINT32) clipImage.getWidth(), (UINT32) clipImage.getHeight() };
269 auto bp = D2D1::BitmapProperties();
270
271 maskImage = clipImage.convertedToFormat (Image::ARGB);
272 Image::BitmapData bd (maskImage, Image::BitmapData::readOnly); // xxx should be maskImage?
273 bp.pixelFormat = owner.pimpl->renderingTarget->GetPixelFormat();
274 bp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
275
276 auto hr = owner.pimpl->renderingTarget->CreateBitmap (size, bd.data, bd.lineStride, bp, maskBitmap.resetAndGetPointerAddress());
277 hr = owner.pimpl->renderingTarget->CreateBitmapBrush (maskBitmap, bmProps, brushProps, bitmapMaskBrush.resetAndGetPointerAddress());
278
279 imageMaskLayerParams = D2D1::LayerParameters();
280 imageMaskLayerParams.opacityBrush = bitmapMaskBrush;
281
282 shouldClipBitmap = true;
283 pushClips();
284 }
285
popClipsjuce::Direct2DLowLevelGraphicsContext::SavedState286 void popClips()
287 {
288 if (clipsBitmap)
289 {
290 owner.pimpl->renderingTarget->PopLayer();
291 clipsBitmap = false;
292 }
293
294 if (clipsComplex)
295 {
296 owner.pimpl->renderingTarget->PopLayer();
297 clipsComplex = false;
298 }
299
300 if (clipsRectList)
301 {
302 owner.pimpl->renderingTarget->PopLayer();
303 clipsRectList = false;
304 }
305
306 if (clipsRect)
307 {
308 owner.pimpl->renderingTarget->PopAxisAlignedClip();
309 clipsRect = false;
310 }
311 }
312
pushClipsjuce::Direct2DLowLevelGraphicsContext::SavedState313 void pushClips()
314 {
315 if (shouldClipRect && !clipsRect)
316 {
317 owner.pimpl->renderingTarget->PushAxisAlignedClip (rectangleToRectF (clipRect), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
318 clipsRect = true;
319 }
320
321 if (shouldClipRectList && !clipsRectList)
322 {
323 auto layerParams = D2D1::LayerParameters();
324 rectListGeometry->GetBounds (D2D1::IdentityMatrix(), &layerParams.contentBounds);
325 layerParams.geometricMask = rectListGeometry;
326 owner.pimpl->renderingTarget->PushLayer (layerParams, rectListLayer);
327 clipsRectList = true;
328 }
329
330 if (shouldClipComplex && !clipsComplex)
331 {
332 auto layerParams = D2D1::LayerParameters();
333 complexClipGeometry->GetBounds (D2D1::IdentityMatrix(), &layerParams.contentBounds);
334 layerParams.geometricMask = complexClipGeometry;
335 owner.pimpl->renderingTarget->PushLayer (layerParams, complexClipLayer);
336 clipsComplex = true;
337 }
338
339 if (shouldClipBitmap && !clipsBitmap)
340 {
341 owner.pimpl->renderingTarget->PushLayer (imageMaskLayerParams, bitmapMaskLayer);
342 clipsBitmap = true;
343 }
344 }
345
setFilljuce::Direct2DLowLevelGraphicsContext::SavedState346 void setFill (const FillType& newFillType)
347 {
348 if (fillType != newFillType)
349 {
350 fillType = newFillType;
351 clearFill();
352 }
353 }
354
clearFontjuce::Direct2DLowLevelGraphicsContext::SavedState355 void clearFont()
356 {
357 currentFontFace = localFontFace = nullptr;
358 }
359
setFontjuce::Direct2DLowLevelGraphicsContext::SavedState360 void setFont (const Font& newFont)
361 {
362 if (font != newFont)
363 {
364 font = newFont;
365 clearFont();
366 }
367 }
368
createFontjuce::Direct2DLowLevelGraphicsContext::SavedState369 void createFont()
370 {
371 if (currentFontFace == nullptr)
372 {
373 auto* typeface = dynamic_cast<WindowsDirectWriteTypeface*> (font.getTypeface());
374 currentFontFace = typeface->getIDWriteFontFace();
375 fontHeightToEmSizeFactor = typeface->getUnitsToHeightScaleFactor();
376 }
377 }
378
setOpacityjuce::Direct2DLowLevelGraphicsContext::SavedState379 void setOpacity (float newOpacity)
380 {
381 fillType.setOpacity (newOpacity);
382
383 if (currentBrush != nullptr)
384 currentBrush->SetOpacity (newOpacity);
385 }
386
clearFilljuce::Direct2DLowLevelGraphicsContext::SavedState387 void clearFill()
388 {
389 gradientStops = nullptr;
390 linearGradient = nullptr;
391 radialGradient = nullptr;
392 bitmap = nullptr;
393 bitmapBrush = nullptr;
394 currentBrush = nullptr;
395 }
396
createBrushjuce::Direct2DLowLevelGraphicsContext::SavedState397 void createBrush()
398 {
399 if (currentBrush == nullptr)
400 {
401 if (fillType.isColour())
402 {
403 auto colour = colourToD2D (fillType.colour);
404 owner.pimpl->colourBrush->SetColor (colour);
405 currentBrush = owner.pimpl->colourBrush;
406 }
407 else if (fillType.isTiledImage())
408 {
409 D2D1_BRUSH_PROPERTIES brushProps = { fillType.getOpacity(), transformToMatrix (fillType.transform) };
410 auto bmProps = D2D1::BitmapBrushProperties (D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_WRAP);
411
412 image = fillType.image;
413
414 D2D1_SIZE_U size = { (UINT32) image.getWidth(), (UINT32) image.getHeight() };
415 auto bp = D2D1::BitmapProperties();
416
417 this->image = image.convertedToFormat (Image::ARGB);
418 Image::BitmapData bd (this->image, Image::BitmapData::readOnly);
419 bp.pixelFormat = owner.pimpl->renderingTarget->GetPixelFormat();
420 bp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
421
422 auto hr = owner.pimpl->renderingTarget->CreateBitmap (size, bd.data, bd.lineStride, bp, bitmap.resetAndGetPointerAddress());
423 hr = owner.pimpl->renderingTarget->CreateBitmapBrush (bitmap, bmProps, brushProps, bitmapBrush.resetAndGetPointerAddress());
424
425 currentBrush = bitmapBrush;
426 }
427 else if (fillType.isGradient())
428 {
429 gradientStops = nullptr;
430
431 D2D1_BRUSH_PROPERTIES brushProps = { fillType.getOpacity(), transformToMatrix (fillType.transform.followedBy (transform)) };
432
433 const int numColors = fillType.gradient->getNumColours();
434
435 HeapBlock<D2D1_GRADIENT_STOP> stops (numColors);
436
437 for (int i = fillType.gradient->getNumColours(); --i >= 0;)
438 {
439 stops[i].color = colourToD2D (fillType.gradient->getColour (i));
440 stops[i].position = (FLOAT) fillType.gradient->getColourPosition (i);
441 }
442
443 owner.pimpl->renderingTarget->CreateGradientStopCollection (stops.getData(), numColors, gradientStops.resetAndGetPointerAddress());
444
445 if (fillType.gradient->isRadial)
446 {
447 radialGradient = nullptr;
448
449 const auto p1 = fillType.gradient->point1;
450 const auto p2 = fillType.gradient->point2;
451 const auto r = p1.getDistanceFrom(p2);
452 const auto props = D2D1::RadialGradientBrushProperties ({ p1.x, p1.y }, {}, r, r);
453
454 owner.pimpl->renderingTarget->CreateRadialGradientBrush (props, brushProps, gradientStops, radialGradient.resetAndGetPointerAddress());
455 currentBrush = radialGradient;
456 }
457 else
458 {
459 linearGradient = 0;
460
461 const auto p1 = fillType.gradient->point1;
462 const auto p2 = fillType.gradient->point2;
463 const auto props = D2D1::LinearGradientBrushProperties ({ p1.x, p1.y }, { p2.x, p2.y });
464
465 owner.pimpl->renderingTarget->CreateLinearGradientBrush (props, brushProps, gradientStops, linearGradient.resetAndGetPointerAddress());
466
467 currentBrush = linearGradient;
468 }
469 }
470 }
471 }
472
473 Direct2DLowLevelGraphicsContext& owner;
474
475 AffineTransform transform;
476
477 Font font;
478 float fontHeightToEmSizeFactor = 1.0f;
479
480 IDWriteFontFace* currentFontFace = nullptr;
481 ComSmartPtr<IDWriteFontFace> localFontFace;
482
483 Rectangle<int> clipRect;
484 bool clipsRect = false, shouldClipRect = false;
485
486 Image image;
487 ComSmartPtr<ID2D1Bitmap> bitmap; // xxx needs a better name - what is this for??
488 bool clipsBitmap = false, shouldClipBitmap = false;
489
490 ComSmartPtr<ID2D1Geometry> complexClipGeometry;
491 D2D1_LAYER_PARAMETERS complexClipLayerParams;
492 ComSmartPtr<ID2D1Layer> complexClipLayer;
493 bool clipsComplex = false, shouldClipComplex = false;
494
495 ComSmartPtr<ID2D1Geometry> rectListGeometry;
496 D2D1_LAYER_PARAMETERS rectListLayerParams;
497 ComSmartPtr<ID2D1Layer> rectListLayer;
498 bool clipsRectList = false, shouldClipRectList = false;
499
500 Image maskImage;
501 D2D1_LAYER_PARAMETERS imageMaskLayerParams;
502 ComSmartPtr<ID2D1Layer> bitmapMaskLayer;
503 ComSmartPtr<ID2D1Bitmap> maskBitmap;
504 ComSmartPtr<ID2D1BitmapBrush> bitmapMaskBrush;
505
506 ID2D1Brush* currentBrush = nullptr;
507 ComSmartPtr<ID2D1BitmapBrush> bitmapBrush;
508 ComSmartPtr<ID2D1LinearGradientBrush> linearGradient;
509 ComSmartPtr<ID2D1RadialGradientBrush> radialGradient;
510 ComSmartPtr<ID2D1GradientStopCollection> gradientStops;
511
512 FillType fillType;
513
514 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SavedState)
515 };
516
517 //==============================================================================
Direct2DLowLevelGraphicsContext(HWND hwnd_)518 Direct2DLowLevelGraphicsContext::Direct2DLowLevelGraphicsContext (HWND hwnd_)
519 : hwnd (hwnd_),
520 currentState (nullptr),
521 pimpl (new Pimpl())
522 {
523 RECT windowRect;
524 GetClientRect (hwnd, &windowRect);
525 D2D1_SIZE_U size = { (UINT32) (windowRect.right - windowRect.left), (UINT32) (windowRect.bottom - windowRect.top) };
526 bounds.setSize (size.width, size.height);
527
528 if (pimpl->factories->d2dFactory != nullptr)
529 {
530 auto hr = pimpl->factories->d2dFactory->CreateHwndRenderTarget ({}, { hwnd, size }, pimpl->renderingTarget.resetAndGetPointerAddress());
531 jassert (SUCCEEDED (hr)); ignoreUnused (hr);
532 hr = pimpl->renderingTarget->CreateSolidColorBrush (D2D1::ColorF::ColorF (0.0f, 0.0f, 0.0f, 1.0f), pimpl->colourBrush.resetAndGetPointerAddress());
533 }
534 }
535
~Direct2DLowLevelGraphicsContext()536 Direct2DLowLevelGraphicsContext::~Direct2DLowLevelGraphicsContext()
537 {
538 states.clear();
539 }
540
resized()541 void Direct2DLowLevelGraphicsContext::resized()
542 {
543 RECT windowRect;
544 GetClientRect (hwnd, &windowRect);
545 D2D1_SIZE_U size = { (UINT32) (windowRect.right - windowRect.left), (UINT32) (windowRect.bottom - windowRect.top) };
546
547 pimpl->renderingTarget->Resize (size);
548 bounds.setSize (size.width, size.height);
549 }
550
clear()551 void Direct2DLowLevelGraphicsContext::clear()
552 {
553 pimpl->renderingTarget->Clear (D2D1::ColorF (D2D1::ColorF::White, 0.0f)); // xxx why white and not black?
554 }
555
start()556 void Direct2DLowLevelGraphicsContext::start()
557 {
558 pimpl->renderingTarget->BeginDraw();
559 saveState();
560 }
561
end()562 void Direct2DLowLevelGraphicsContext::end()
563 {
564 states.clear();
565 currentState = nullptr;
566 pimpl->renderingTarget->EndDraw();
567 pimpl->renderingTarget->CheckWindowState();
568 }
569
setOrigin(Point<int> o)570 void Direct2DLowLevelGraphicsContext::setOrigin (Point<int> o)
571 {
572 addTransform (AffineTransform::translation ((float) o.x, (float) o.y));
573 }
574
addTransform(const AffineTransform & transform)575 void Direct2DLowLevelGraphicsContext::addTransform (const AffineTransform& transform)
576 {
577 currentState->transform = transform.followedBy (currentState->transform);
578 }
579
getPhysicalPixelScaleFactor()580 float Direct2DLowLevelGraphicsContext::getPhysicalPixelScaleFactor()
581 {
582 return std::sqrt (std::abs (currentState->transform.getDeterminant()));
583 }
584
clipToRectangle(const Rectangle<int> & r)585 bool Direct2DLowLevelGraphicsContext::clipToRectangle (const Rectangle<int>& r)
586 {
587 currentState->clipToRectangle (r);
588 return ! isClipEmpty();
589 }
590
clipToRectangleList(const RectangleList<int> & clipRegion)591 bool Direct2DLowLevelGraphicsContext::clipToRectangleList (const RectangleList<int>& clipRegion)
592 {
593 currentState->clipToRectList (pimpl->rectListToPathGeometry (clipRegion));
594 return ! isClipEmpty();
595 }
596
excludeClipRectangle(const Rectangle<int> &)597 void Direct2DLowLevelGraphicsContext::excludeClipRectangle (const Rectangle<int>&)
598 {
599 //xxx
600 }
601
clipToPath(const Path & path,const AffineTransform & transform)602 void Direct2DLowLevelGraphicsContext::clipToPath (const Path& path, const AffineTransform& transform)
603 {
604 currentState->clipToPath (pimpl->pathToPathGeometry (path, transform));
605 }
606
clipToImageAlpha(const Image & sourceImage,const AffineTransform & transform)607 void Direct2DLowLevelGraphicsContext::clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform)
608 {
609 currentState->clipToImage (sourceImage, transform);
610 }
611
clipRegionIntersects(const Rectangle<int> & r)612 bool Direct2DLowLevelGraphicsContext::clipRegionIntersects (const Rectangle<int>& r)
613 {
614 return currentState->clipRect.intersects (r.toFloat().transformedBy (currentState->transform).getSmallestIntegerContainer());
615 }
616
getClipBounds() const617 Rectangle<int> Direct2DLowLevelGraphicsContext::getClipBounds() const
618 {
619 // xxx could this take into account complex clip regions?
620 return currentState->clipRect.toFloat().transformedBy (currentState->transform.inverted()).getSmallestIntegerContainer();
621 }
622
isClipEmpty() const623 bool Direct2DLowLevelGraphicsContext::isClipEmpty() const
624 {
625 return currentState->clipRect.isEmpty();
626 }
627
saveState()628 void Direct2DLowLevelGraphicsContext::saveState()
629 {
630 states.add (new SavedState (*this));
631 currentState = states.getLast();
632 }
633
restoreState()634 void Direct2DLowLevelGraphicsContext::restoreState()
635 {
636 jassert (states.size() > 1); //you should never pop the last state!
637 states.removeLast (1);
638 currentState = states.getLast();
639 }
640
beginTransparencyLayer(float)641 void Direct2DLowLevelGraphicsContext::beginTransparencyLayer (float /*opacity*/)
642 {
643 jassertfalse; //xxx todo
644 }
645
endTransparencyLayer()646 void Direct2DLowLevelGraphicsContext::endTransparencyLayer()
647 {
648 jassertfalse; //xxx todo
649 }
650
setFill(const FillType & fillType)651 void Direct2DLowLevelGraphicsContext::setFill (const FillType& fillType)
652 {
653 currentState->setFill (fillType);
654 }
655
setOpacity(float newOpacity)656 void Direct2DLowLevelGraphicsContext::setOpacity (float newOpacity)
657 {
658 currentState->setOpacity (newOpacity);
659 }
660
setInterpolationQuality(Graphics::ResamplingQuality)661 void Direct2DLowLevelGraphicsContext::setInterpolationQuality (Graphics::ResamplingQuality /*quality*/)
662 {
663 }
664
fillRect(const Rectangle<int> & r,bool)665 void Direct2DLowLevelGraphicsContext::fillRect (const Rectangle<int>& r, bool /*replaceExistingContents*/)
666 {
667 fillRect (r.toFloat());
668 }
669
fillRect(const Rectangle<float> & r)670 void Direct2DLowLevelGraphicsContext::fillRect (const Rectangle<float>& r)
671 {
672 pimpl->renderingTarget->SetTransform (transformToMatrix (currentState->transform));
673 currentState->createBrush();
674 pimpl->renderingTarget->FillRectangle (rectangleToRectF (r), currentState->currentBrush);
675 pimpl->renderingTarget->SetTransform (D2D1::IdentityMatrix());
676 }
677
fillRectList(const RectangleList<float> & list)678 void Direct2DLowLevelGraphicsContext::fillRectList (const RectangleList<float>& list)
679 {
680 for (auto& r : list)
681 fillRect (r);
682 }
683
fillPath(const Path & p,const AffineTransform & transform)684 void Direct2DLowLevelGraphicsContext::fillPath (const Path& p, const AffineTransform& transform)
685 {
686 currentState->createBrush();
687 ComSmartPtr<ID2D1Geometry> geometry (pimpl->pathToPathGeometry (p, transform.followedBy (currentState->transform)));
688
689 if (pimpl->renderingTarget != nullptr)
690 pimpl->renderingTarget->FillGeometry (geometry, currentState->currentBrush);
691 }
692
drawImage(const Image & image,const AffineTransform & transform)693 void Direct2DLowLevelGraphicsContext::drawImage (const Image& image, const AffineTransform& transform)
694 {
695 pimpl->renderingTarget->SetTransform (transformToMatrix (transform.followedBy (currentState->transform)));
696
697 D2D1_SIZE_U size = { (UINT32) image.getWidth(), (UINT32) image.getHeight() };
698 auto bp = D2D1::BitmapProperties();
699
700 Image img (image.convertedToFormat (Image::ARGB));
701 Image::BitmapData bd (img, Image::BitmapData::readOnly);
702 bp.pixelFormat = pimpl->renderingTarget->GetPixelFormat();
703 bp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
704
705 {
706 ComSmartPtr<ID2D1Bitmap> tempBitmap;
707 pimpl->renderingTarget->CreateBitmap (size, bd.data, bd.lineStride, bp, tempBitmap.resetAndGetPointerAddress());
708 if (tempBitmap != nullptr)
709 pimpl->renderingTarget->DrawBitmap (tempBitmap);
710 }
711
712 pimpl->renderingTarget->SetTransform (D2D1::IdentityMatrix());
713 }
714
drawLine(const Line<float> & line)715 void Direct2DLowLevelGraphicsContext::drawLine (const Line<float>& line)
716 {
717 // xxx doesn't seem to be correctly aligned, may need nudging by 0.5 to match the software renderer's behaviour
718 pimpl->renderingTarget->SetTransform (transformToMatrix (currentState->transform));
719 currentState->createBrush();
720
721 pimpl->renderingTarget->DrawLine (D2D1::Point2F (line.getStartX(), line.getStartY()),
722 D2D1::Point2F (line.getEndX(), line.getEndY()),
723 currentState->currentBrush);
724 pimpl->renderingTarget->SetTransform (D2D1::IdentityMatrix());
725 }
726
setFont(const Font & newFont)727 void Direct2DLowLevelGraphicsContext::setFont (const Font& newFont)
728 {
729 currentState->setFont (newFont);
730 }
731
getFont()732 const Font& Direct2DLowLevelGraphicsContext::getFont()
733 {
734 return currentState->font;
735 }
736
drawGlyph(int glyphNumber,const AffineTransform & transform)737 void Direct2DLowLevelGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& transform)
738 {
739 currentState->createBrush();
740 currentState->createFont();
741
742 auto hScale = currentState->font.getHorizontalScale();
743
744 pimpl->renderingTarget->SetTransform (transformToMatrix (AffineTransform::scale (hScale, 1.0f)
745 .followedBy (transform)
746 .followedBy (currentState->transform)));
747
748 const auto glyphIndices = (UINT16) glyphNumber;
749 const auto glyphAdvances = 0.0f;
750 DWRITE_GLYPH_OFFSET offset = { 0.0f, 0.0f };
751
752 DWRITE_GLYPH_RUN glyphRun;
753 glyphRun.fontFace = currentState->currentFontFace;
754 glyphRun.fontEmSize = (FLOAT) (currentState->font.getHeight() * currentState->fontHeightToEmSizeFactor);
755 glyphRun.glyphCount = 1;
756 glyphRun.glyphIndices = &glyphIndices;
757 glyphRun.glyphAdvances = &glyphAdvances;
758 glyphRun.glyphOffsets = &offset;
759 glyphRun.isSideways = FALSE;
760 glyphRun.bidiLevel = 0;
761
762 pimpl->renderingTarget->DrawGlyphRun ({}, &glyphRun, currentState->currentBrush);
763 pimpl->renderingTarget->SetTransform (D2D1::IdentityMatrix());
764 }
765
drawTextLayout(const AttributedString & text,const Rectangle<float> & area)766 bool Direct2DLowLevelGraphicsContext::drawTextLayout (const AttributedString& text, const Rectangle<float>& area)
767 {
768 pimpl->renderingTarget->SetTransform (transformToMatrix (currentState->transform));
769
770 DirectWriteTypeLayout::drawToD2DContext (text, area,
771 *(pimpl->renderingTarget),
772 *(pimpl->factories->directWriteFactory),
773 *(pimpl->factories->systemFonts));
774
775 pimpl->renderingTarget->SetTransform (D2D1::IdentityMatrix());
776 return true;
777 }
778
779 } // namespace juce
780