1 // This file is part of VSTGUI. It is subject to the license terms
2 // in the LICENSE file found in the top-level directory of this
3 // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
4
5 #include "d2ddrawcontext.h"
6
7 #if WINDOWS
8
9 #include "../win32support.h"
10 #include "../../../cgradient.h"
11 #include "d2dbitmap.h"
12 #include "d2dgraphicspath.h"
13 #include "d2dfont.h"
14 #include <cassert>
15
16 namespace VSTGUI {
17
18 static D2D1_RENDER_TARGET_TYPE gRenderTargetType = D2D1_RENDER_TARGET_TYPE_SOFTWARE;
19
20 //-----------------------------------------------------------------------------
useD2DHardwareRenderer(bool state)21 void useD2DHardwareRenderer (bool state)
22 {
23 if (state)
24 gRenderTargetType = D2D1_RENDER_TARGET_TYPE_HARDWARE;
25 else
26 gRenderTargetType = D2D1_RENDER_TARGET_TYPE_SOFTWARE;
27 }
28
29 //-----------------------------------------------------------------------------
D2DApplyClip(D2DDrawContext * drawContext,bool halfPointOffset)30 D2DDrawContext::D2DApplyClip::D2DApplyClip (D2DDrawContext* drawContext, bool halfPointOffset)
31 : drawContext (drawContext)
32 {
33 CGraphicsTransform transform = drawContext->getCurrentTransform ();
34 auto scale = drawContext->getScaleFactor ();
35 transform.scale (scale, scale);
36 if (halfPointOffset)
37 {
38 CPoint offset (0.5, 0.5);
39 transform.translate (offset);
40 }
41 if (transform.m12 != 0. || transform.m21 != 0.)
42 { // we have a rotated matrix, we need to use a layer
43 layerIsUsed = true;
44 if (drawContext->currentClip.isEmpty () == false)
45 drawContext->getRenderTarget ()->PopAxisAlignedClip ();
46
47 ID2D1RectangleGeometry* geometry;
48 if (FAILED (getD2DFactory ()->CreateRectangleGeometry (makeD2DRect (drawContext->getCurrentState ().clipRect), &geometry)))
49 return;
50 auto d2dMatrix = convert (transform);
51 drawContext->getRenderTarget ()->PushLayer (
52 D2D1::LayerParameters (D2D1::InfiniteRect (), geometry,
53 D2D1_ANTIALIAS_MODE_ALIASED),
54 nullptr);
55 drawContext->getRenderTarget ()->SetTransform (d2dMatrix);
56 geometry->Release ();
57 applyClip = drawContext->getCurrentState ().clipRect;
58 drawContext->currentClip = {};
59 }
60 else
61 {
62 if (drawContext->currentClip != drawContext->getCurrentState ().clipRect)
63 {
64 CRect clip = drawContext->getCurrentState ().clipRect;
65 if (drawContext->currentClip.isEmpty () == false)
66 drawContext->getRenderTarget ()->PopAxisAlignedClip ();
67 if (clip.isEmpty () == false)
68 drawContext->getRenderTarget ()->PushAxisAlignedClip (makeD2DRect (clip), D2D1_ANTIALIAS_MODE_ALIASED);
69 drawContext->currentClip = applyClip = clip;
70 }
71 else
72 {
73 applyClip = drawContext->currentClip;
74 }
75 drawContext->getRenderTarget ()->SetTransform (convert (transform));
76 }
77 }
78
79 //-----------------------------------------------------------------------------
~D2DApplyClip()80 D2DDrawContext::D2DApplyClip::~D2DApplyClip ()
81 {
82 if (layerIsUsed)
83 {
84 drawContext->getRenderTarget ()->PopLayer ();
85 }
86 auto scale = drawContext->getScaleFactor ();
87 CGraphicsTransform transform;
88 transform.scale (scale, scale);
89 drawContext->getRenderTarget ()->SetTransform (convert (transform));
90 }
91
92 //-----------------------------------------------------------------------------
D2DDrawContext(HWND window,const CRect & drawSurface)93 D2DDrawContext::D2DDrawContext (HWND window, const CRect& drawSurface)
94 : COffscreenContext (drawSurface)
95 , window (window)
96 , renderTarget (nullptr)
97 , fillBrush (nullptr)
98 , strokeBrush (nullptr)
99 , fontBrush (nullptr)
100 , strokeStyle (nullptr)
101 {
102 createRenderTarget ();
103 }
104
105 //-----------------------------------------------------------------------------
D2DDrawContext(D2DBitmap * inBitmap)106 D2DDrawContext::D2DDrawContext (D2DBitmap* inBitmap)
107 : COffscreenContext (new CBitmap (inBitmap))
108 , window (nullptr)
109 , renderTarget (nullptr)
110 , fillBrush (nullptr)
111 , strokeBrush (nullptr)
112 , fontBrush (nullptr)
113 , strokeStyle (nullptr)
114 , scaleFactor (inBitmap->getScaleFactor ())
115 {
116 createRenderTarget ();
117 bitmap->forget ();
118 }
119
120 //-----------------------------------------------------------------------------
~D2DDrawContext()121 D2DDrawContext::~D2DDrawContext ()
122 {
123 releaseRenderTarget ();
124 }
125
126 //-----------------------------------------------------------------------------
createRenderTarget()127 void D2DDrawContext::createRenderTarget ()
128 {
129 if (window)
130 {
131 RECT rc;
132 GetClientRect (window, &rc);
133
134 D2D1_SIZE_U size = D2D1::SizeU (static_cast<UINT32> (rc.right - rc.left), static_cast<UINT32> (rc.bottom - rc.top));
135 ID2D1HwndRenderTarget* hwndRenderTarget = nullptr;
136 D2D1_PIXEL_FORMAT pixelFormat = D2D1::PixelFormat (DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED);
137 HRESULT hr = getD2DFactory ()->CreateHwndRenderTarget (D2D1::RenderTargetProperties (gRenderTargetType, pixelFormat), D2D1::HwndRenderTargetProperties (window, size, D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS), &hwndRenderTarget);
138 if (SUCCEEDED (hr))
139 {
140 renderTarget = hwndRenderTarget;
141 renderTarget->SetDpi (96, 96);
142 }
143 }
144 else if (bitmap)
145 {
146 D2DBitmap* d2dBitmap = dynamic_cast<D2DBitmap*> (bitmap->getPlatformBitmap ().get ());
147 if (d2dBitmap)
148 {
149 D2D1_RENDER_TARGET_TYPE targetType = D2D1_RENDER_TARGET_TYPE_SOFTWARE;
150 D2D1_PIXEL_FORMAT pixelFormat = D2D1::PixelFormat (DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED);
151 getD2DFactory ()->CreateWicBitmapRenderTarget (d2dBitmap->getBitmap (), D2D1::RenderTargetProperties (targetType, pixelFormat), &renderTarget);
152 }
153 }
154 vstgui_assert (renderTarget);
155 init ();
156 }
157
158 //-----------------------------------------------------------------------------
releaseRenderTarget()159 void D2DDrawContext::releaseRenderTarget ()
160 {
161 if (fillBrush)
162 {
163 fillBrush->Release ();
164 fillBrush = nullptr;
165 }
166 if (strokeBrush)
167 {
168 strokeBrush->Release ();
169 strokeBrush = nullptr;
170 }
171 if (fontBrush)
172 {
173 fontBrush->Release ();
174 fontBrush = nullptr;
175 }
176 if (strokeStyle)
177 {
178 strokeStyle->Release ();
179 strokeStyle = nullptr;
180 }
181 if (renderTarget)
182 {
183 D2DBitmapCache::instance ()->removeRenderTarget (renderTarget);
184 renderTarget->Release ();
185 renderTarget = nullptr;
186 }
187 }
188
189 //-----------------------------------------------------------------------------
beginDraw()190 void D2DDrawContext::beginDraw ()
191 {
192 if (renderTarget)
193 {
194 auto scale = getScaleFactor ();
195 CGraphicsTransform transform;
196 transform.scale (scale, scale);
197 renderTarget->BeginDraw ();
198 renderTarget->SetTransform (convert (transform));
199 }
200 }
201
202 //-----------------------------------------------------------------------------
endDraw()203 void D2DDrawContext::endDraw ()
204 {
205 if (renderTarget)
206 {
207 if (currentClip.isEmpty () == false)
208 {
209 getRenderTarget ()->PopAxisAlignedClip ();
210 currentClip = CRect ();
211 }
212 renderTarget->Flush ();
213 HRESULT result = renderTarget->EndDraw ();
214 if (result == (HRESULT)D2DERR_RECREATE_TARGET)
215 {
216 releaseRenderTarget ();
217 createRenderTarget ();
218 }
219 else
220 {
221 vstgui_assert (result == S_OK);
222 }
223 if (bitmap)
224 {
225 D2DBitmap* d2dBitmap = dynamic_cast<D2DBitmap*> (bitmap->getPlatformBitmap ().get ());
226 D2DBitmapCache::instance ()->removeBitmap (d2dBitmap);
227 }
228 }
229 }
230
231 //-----------------------------------------------------------------------------
init()232 void D2DDrawContext::init ()
233 {
234 COffscreenContext::init ();
235 }
236
237 //-----------------------------------------------------------------------------
createGraphicsPath()238 CGraphicsPath* D2DDrawContext::createGraphicsPath ()
239 {
240 return new D2DGraphicsPath ();
241 }
242
243 //-----------------------------------------------------------------------------
createTextPath(const CFontRef font,UTF8StringPtr text)244 CGraphicsPath* D2DDrawContext::createTextPath (const CFontRef font, UTF8StringPtr text)
245 {
246 auto ctFont = font->getPlatformFont ().cast<const D2DFont> ();
247 return ctFont ? new D2DGraphicsPath (ctFont, text) : nullptr;
248 }
249
250 //-----------------------------------------------------------------------------
drawGraphicsPath(CGraphicsPath * _path,PathDrawMode mode,CGraphicsTransform * t)251 void D2DDrawContext::drawGraphicsPath (CGraphicsPath* _path, PathDrawMode mode, CGraphicsTransform* t)
252 {
253 if (renderTarget == nullptr)
254 return;
255 D2DApplyClip ac (this);
256 if (ac.isEmpty ())
257 return;
258
259 D2DGraphicsPath* d2dPath = dynamic_cast<D2DGraphicsPath*> (_path);
260 if (d2dPath == nullptr)
261 return;
262
263 ID2D1Geometry* path = d2dPath->createPath (mode == kPathFilledEvenOdd ? D2D1_FILL_MODE_ALTERNATE : D2D1_FILL_MODE_WINDING, nullptr, t);
264 if (path)
265 {
266 if (mode == kPathFilled || mode == kPathFilledEvenOdd)
267 getRenderTarget ()->FillGeometry (path, getFillBrush ());
268 else if (mode == kPathStroked)
269 getRenderTarget ()->DrawGeometry (path, getStrokeBrush (), (FLOAT)getLineWidth (), getStrokeStyle ());
270 path->Release ();
271 }
272 }
273
274 //-----------------------------------------------------------------------------
createGradientStopCollection(const CGradient & d2dGradient) const275 ID2D1GradientStopCollection* D2DDrawContext::createGradientStopCollection (const CGradient& d2dGradient) const
276 {
277 ID2D1GradientStopCollection* collection = nullptr;
278 D2D1_GRADIENT_STOP* gradientStops = new D2D1_GRADIENT_STOP [d2dGradient.getColorStops ().size ()];
279 uint32_t index = 0;
280 for (CGradient::ColorStopMap::const_iterator it = d2dGradient.getColorStops ().begin (); it != d2dGradient.getColorStops ().end (); ++it, ++index)
281 {
282 gradientStops[index].position = (FLOAT)it->first;
283 gradientStops[index].color = D2D1::ColorF (it->second.red/255.f, it->second.green/255.f, it->second.blue/255.f, it->second.alpha/255.f * getCurrentState ().globalAlpha);
284 }
285 getRenderTarget ()->CreateGradientStopCollection (gradientStops, static_cast<UINT32> (d2dGradient.getColorStops ().size ()), &collection);
286 delete [] gradientStops;
287 return collection;
288 }
289
290 //-----------------------------------------------------------------------------
fillLinearGradient(CGraphicsPath * _path,const CGradient & gradient,const CPoint & startPoint,const CPoint & endPoint,bool evenOdd,CGraphicsTransform * t)291 void D2DDrawContext::fillLinearGradient (CGraphicsPath* _path, const CGradient& gradient, const CPoint& startPoint, const CPoint& endPoint, bool evenOdd, CGraphicsTransform* t)
292 {
293 if (renderTarget == nullptr)
294 return;
295
296 D2DApplyClip ac (this, true);
297 if (ac.isEmpty ())
298 return;
299
300 D2DGraphicsPath* d2dPath = dynamic_cast<D2DGraphicsPath*> (_path);
301 if (d2dPath == nullptr)
302 return;
303
304 ID2D1Geometry* path = d2dPath->createPath (evenOdd ? D2D1_FILL_MODE_ALTERNATE : D2D1_FILL_MODE_WINDING, nullptr, t);
305 if (path)
306 {
307
308 ID2D1GradientStopCollection* collection = createGradientStopCollection (gradient);
309 if (collection)
310 {
311 ID2D1LinearGradientBrush* brush = nullptr;
312 D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES properties;
313 properties.startPoint = makeD2DPoint (startPoint);
314 properties.endPoint = makeD2DPoint (endPoint);
315 if (SUCCEEDED (getRenderTarget ()->CreateLinearGradientBrush (properties, collection, &brush)))
316 {
317 getRenderTarget ()->FillGeometry (path, brush);
318 brush->Release ();
319 }
320 collection->Release ();
321 }
322 path->Release ();
323 }
324 }
325
326 //-----------------------------------------------------------------------------
fillRadialGradient(CGraphicsPath * _path,const CGradient & gradient,const CPoint & center,CCoord radius,const CPoint & originOffset,bool evenOdd,CGraphicsTransform * t)327 void D2DDrawContext::fillRadialGradient (CGraphicsPath* _path, const CGradient& gradient, const CPoint& center, CCoord radius, const CPoint& originOffset, bool evenOdd, CGraphicsTransform* t)
328 {
329 if (renderTarget == nullptr)
330 return;
331
332 D2DApplyClip ac (this, true);
333 if (ac.isEmpty ())
334 return;
335
336 D2DGraphicsPath* d2dPath = dynamic_cast<D2DGraphicsPath*> (_path);
337 if (d2dPath == nullptr)
338 return;
339
340 ID2D1Geometry* path = d2dPath->createPath (evenOdd ? D2D1_FILL_MODE_ALTERNATE : D2D1_FILL_MODE_WINDING);
341 if (path)
342 {
343 ID2D1Geometry* geometry = nullptr;
344 if (t)
345 {
346 ID2D1TransformedGeometry* tg = nullptr;
347 getD2DFactory ()->CreateTransformedGeometry (path, convert (*t), &tg);
348 geometry = tg;
349 }
350 else
351 {
352 geometry = path;
353 geometry->AddRef ();
354 }
355 ID2D1GradientStopCollection* collection = createGradientStopCollection (gradient);
356 if (collection)
357 {
358 // brush properties
359 ID2D1RadialGradientBrush* brush = nullptr;
360 D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES properties;
361 properties.center = makeD2DPoint (center);
362 properties.gradientOriginOffset = makeD2DPoint (originOffset);
363 properties.radiusX = (FLOAT)radius;
364 properties.radiusY = (FLOAT)radius;
365
366 if (SUCCEEDED (getRenderTarget ()->CreateRadialGradientBrush (properties, collection, &brush)))
367 {
368 getRenderTarget ()->FillGeometry (geometry, brush);
369 brush->Release ();
370 }
371 collection->Release ();
372 }
373 geometry->Release ();
374 path->Release ();
375 }
376 }
377
378 //-----------------------------------------------------------------------------
clearRect(const CRect & rect)379 void D2DDrawContext::clearRect (const CRect& rect)
380 {
381 if (renderTarget)
382 {
383 CRect oldClip = getCurrentState ().clipRect;
384 setClipRect (rect);
385 D2DApplyClip ac (this);
386 renderTarget->Clear (D2D1::ColorF (1.f, 1.f, 1.f, 0.f));
387 setClipRect (oldClip);
388 }
389 }
390
391 //-----------------------------------------------------------------------------
drawBitmap(CBitmap * bitmap,const CRect & dest,const CPoint & offset,float alpha)392 void D2DDrawContext::drawBitmap (CBitmap* bitmap, const CRect& dest, const CPoint& offset, float alpha)
393 {
394 if (renderTarget == nullptr)
395 return;
396 ConcatClip concatClip (*this, dest);
397 D2DApplyClip ac (this);
398 if (ac.isEmpty ())
399 return;
400
401 double transformedScaleFactor = getScaleFactor ();
402 CGraphicsTransform t = getCurrentTransform ();
403 if (t.m11 == t.m22 && t.m12 == 0 && t.m21 == 0)
404 transformedScaleFactor *= t.m11;
405 IPlatformBitmap* platformBitmap = bitmap->getBestPlatformBitmapForScaleFactor (transformedScaleFactor);
406 D2DBitmap* d2dBitmap = platformBitmap ? dynamic_cast<D2DBitmap*> (platformBitmap) : nullptr;
407 if (d2dBitmap)
408 {
409 if (d2dBitmap->getSource ())
410 {
411 ID2D1Bitmap* d2d1Bitmap = D2DBitmapCache::instance ()->getBitmap (d2dBitmap, renderTarget);
412 if (d2d1Bitmap)
413 {
414 double bitmapScaleFactor = platformBitmap->getScaleFactor ();
415 CGraphicsTransform bitmapTransform;
416 bitmapTransform.scale (1./bitmapScaleFactor, 1./bitmapScaleFactor);
417 Transform transform (*this, bitmapTransform);
418
419 CRect d (dest);
420 d.setWidth (bitmap->getWidth ());
421 d.setHeight (bitmap->getHeight ());
422 d.offset (-offset.x, -offset.y);
423 d.makeIntegral ();
424 CRect source;
425 source.setWidth (d2d1Bitmap->GetSize ().width);
426 source.setHeight (d2d1Bitmap->GetSize ().height);
427
428 D2D1_BITMAP_INTERPOLATION_MODE mode;
429 switch (getCurrentState ().bitmapQuality)
430 {
431 case BitmapInterpolationQuality::kLow:
432 mode = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
433 break;
434
435 case BitmapInterpolationQuality::kMedium:
436 case BitmapInterpolationQuality::kHigh:
437 default:
438 mode = D2D1_BITMAP_INTERPOLATION_MODE_LINEAR;
439 break;
440 }
441
442 D2D1_RECT_F sourceRect = makeD2DRect (source);
443 renderTarget->DrawBitmap (d2d1Bitmap, makeD2DRect (d), alpha * getCurrentState ().globalAlpha, mode, &sourceRect);
444 }
445 }
446 }
447 }
448
449 //-----------------------------------------------------------------------------
drawLineInternal(CPoint start,CPoint end)450 void D2DDrawContext::drawLineInternal (CPoint start, CPoint end)
451 {
452 renderTarget->DrawLine (makeD2DPoint (start), makeD2DPoint (end), strokeBrush, (FLOAT)getCurrentState ().frameWidth, strokeStyle);
453 }
454
455 //-----------------------------------------------------------------------------
needsHalfPointOffset() const456 bool D2DDrawContext::needsHalfPointOffset () const
457 {
458 return static_cast<int32_t> (getCurrentState ().frameWidth) % 2 != 0;
459 }
460
461 //-----------------------------------------------------------------------------
drawLine(const LinePair & line)462 void D2DDrawContext::drawLine (const LinePair& line)
463 {
464 if (renderTarget == nullptr)
465 return;
466 D2DApplyClip ac (this);
467 if (ac.isEmpty ())
468 return;
469
470 CPoint start (line.first);
471 CPoint end (line.second);
472 if (getDrawMode ().integralMode ())
473 {
474 pixelAllign (start);
475 pixelAllign (end);
476 }
477 if (needsHalfPointOffset ())
478 {
479 start.offset (0.5, 0.5);
480 end.offset (0.5, 0.5);
481 }
482 drawLineInternal (start, end);
483 }
484
485 //-----------------------------------------------------------------------------
drawLines(const LineList & lines)486 void D2DDrawContext::drawLines (const LineList& lines)
487 {
488 if (lines.size () == 0 || renderTarget == nullptr)
489 return;
490 D2DApplyClip ac (this);
491 if (ac.isEmpty ())
492 return;
493
494 bool needsOffset = needsHalfPointOffset ();
495 bool integralMode = getDrawMode ().integralMode ();
496 for (const auto& line : lines)
497 {
498 CPoint start (line.first);
499 CPoint end (line.second);
500 if (integralMode)
501 {
502 pixelAllign (start);
503 pixelAllign (end);
504 }
505 if (needsOffset)
506 {
507 start.offset (0.5, 0.5);
508 end.offset (0.5, 0.5);
509 }
510 drawLineInternal (start, end);
511 }
512 }
513
514 //-----------------------------------------------------------------------------
drawPolygon(const PointList & polygonPointList,const CDrawStyle drawStyle)515 void D2DDrawContext::drawPolygon (const PointList& polygonPointList, const CDrawStyle drawStyle)
516 {
517 if (renderTarget == nullptr || polygonPointList.size () == 0)
518 return;
519 D2DApplyClip ac (this);
520 if (ac.isEmpty ())
521 return;
522
523 D2DGraphicsPath path;
524 path.beginSubpath (polygonPointList[0]);
525 for (uint32_t i = 1; i < polygonPointList.size (); ++i)
526 {
527 path.addLine (polygonPointList[i]);
528 }
529 if (drawStyle == kDrawFilled || drawStyle == kDrawFilledAndStroked)
530 {
531 drawGraphicsPath (&path, kPathFilled);
532 }
533 if (drawStyle == kDrawStroked || drawStyle == kDrawFilledAndStroked)
534 {
535 drawGraphicsPath (&path, kPathStroked);
536 }
537 }
538
539 //-----------------------------------------------------------------------------
drawRect(const CRect & _rect,const CDrawStyle drawStyle)540 void D2DDrawContext::drawRect (const CRect &_rect, const CDrawStyle drawStyle)
541 {
542 if (renderTarget == nullptr)
543 return;
544 D2DApplyClip ac (this);
545 if (ac.isEmpty ())
546 return;
547 CRect rect (_rect);
548 if (drawStyle != kDrawFilled)
549 {
550 rect.right -= 1.;
551 rect.bottom -= 1.;
552 }
553 if (drawStyle == kDrawFilled || drawStyle == kDrawFilledAndStroked)
554 {
555 renderTarget->FillRectangle (makeD2DRect (rect), fillBrush);
556 }
557 if (drawStyle == kDrawStroked || drawStyle == kDrawFilledAndStroked)
558 {
559 if (needsHalfPointOffset ())
560 {
561 rect.offset (0.5, 0.5);
562 }
563 renderTarget->DrawRectangle (makeD2DRect (rect), strokeBrush, (FLOAT)getCurrentState ().frameWidth, strokeStyle);
564 }
565 }
566
567 //-----------------------------------------------------------------------------
drawArc(const CRect & _rect,const float _startAngle,const float _endAngle,const CDrawStyle drawStyle)568 void D2DDrawContext::drawArc (const CRect& _rect, const float _startAngle, const float _endAngle, const CDrawStyle drawStyle)
569 {
570 if (auto path = owned (createGraphicsPath ()))
571 {
572 CRect rect (_rect);
573 if (getDrawMode ().integralMode ())
574 pixelAllign (rect);
575 path->addArc (rect, _startAngle, _endAngle, true);
576 if (drawStyle == kDrawFilled || drawStyle == kDrawFilledAndStroked)
577 drawGraphicsPath (path, kPathFilled);
578 if (drawStyle == kDrawStroked || drawStyle == kDrawFilledAndStroked)
579 drawGraphicsPath (path, kPathStroked);
580 }
581 }
582
583 //-----------------------------------------------------------------------------
drawEllipse(const CRect & _rect,const CDrawStyle drawStyle)584 void D2DDrawContext::drawEllipse (const CRect &_rect, const CDrawStyle drawStyle)
585 {
586 if (renderTarget == nullptr)
587 return;
588 D2DApplyClip ac (this);
589 if (ac.isEmpty ())
590 return;
591 CRect rect (_rect);
592 if (getDrawMode ().integralMode ())
593 pixelAllign (rect);
594 if (drawStyle == kDrawStroked)
595 rect.inset (0.5, 0.5);
596 CPoint center (rect.getTopLeft ());
597 center.offset (rect.getWidth () / 2., rect.getHeight () / 2.);
598 D2D1_ELLIPSE ellipse;
599 ellipse.point = makeD2DPoint (center);
600 ellipse.radiusX = (FLOAT)(rect.getWidth () / 2.);
601 ellipse.radiusY = (FLOAT)(rect.getHeight () / 2.);
602 if (drawStyle == kDrawFilled || drawStyle == kDrawFilledAndStroked)
603 {
604 renderTarget->FillEllipse (ellipse, fillBrush);
605 }
606 if (drawStyle == kDrawStroked || drawStyle == kDrawFilledAndStroked)
607 {
608 renderTarget->DrawEllipse (ellipse, strokeBrush, (FLOAT)getCurrentState ().frameWidth, strokeStyle);
609 }
610 }
611
612 //-----------------------------------------------------------------------------
drawPoint(const CPoint & point,const CColor & color)613 void D2DDrawContext::drawPoint (const CPoint &point, const CColor& color)
614 {
615 saveGlobalState ();
616 setLineWidth (1);
617 setFrameColor (color);
618 CPoint point2 (point);
619 point2.x++;
620 COffscreenContext::drawLine (point, point2);
621 restoreGlobalState ();
622 }
623
624 //-----------------------------------------------------------------------------
setLineStyle(const CLineStyle & style)625 void D2DDrawContext::setLineStyle (const CLineStyle& style)
626 {
627 if (strokeStyle && getCurrentState ().lineStyle == style)
628 return;
629 setLineStyleInternal (style);
630 COffscreenContext::setLineStyle (style);
631 }
632
633 //-----------------------------------------------------------------------------
setLineStyleInternal(const CLineStyle & style)634 void D2DDrawContext::setLineStyleInternal (const CLineStyle& style)
635 {
636 if (strokeStyle)
637 {
638 strokeStyle->Release ();
639 strokeStyle = nullptr;
640 }
641 D2D1_STROKE_STYLE_PROPERTIES properties;
642 switch (style.getLineCap ())
643 {
644 case CLineStyle::kLineCapButt: properties.startCap = properties.endCap = properties.dashCap = D2D1_CAP_STYLE_FLAT; break;
645 case CLineStyle::kLineCapRound: properties.startCap = properties.endCap = properties.dashCap = D2D1_CAP_STYLE_ROUND; break;
646 case CLineStyle::kLineCapSquare: properties.startCap = properties.endCap = properties.dashCap = D2D1_CAP_STYLE_SQUARE; break;
647 }
648 switch (style.getLineJoin ())
649 {
650 case CLineStyle::kLineJoinMiter: properties.lineJoin = D2D1_LINE_JOIN_MITER; break;
651 case CLineStyle::kLineJoinRound: properties.lineJoin = D2D1_LINE_JOIN_ROUND; break;
652 case CLineStyle::kLineJoinBevel: properties.lineJoin = D2D1_LINE_JOIN_BEVEL; break;
653 }
654 properties.dashOffset = (FLOAT)style.getDashPhase ();
655 properties.miterLimit = 10.f;
656 if (style.getDashCount ())
657 {
658 properties.dashStyle = D2D1_DASH_STYLE_CUSTOM;
659 FLOAT* lengths = new FLOAT[style.getDashCount ()];
660 for (uint32_t i = 0; i < style.getDashCount (); i++)
661 lengths[i] = (FLOAT)style.getDashLengths ()[i];
662 getD2DFactory ()->CreateStrokeStyle (properties, lengths, style.getDashCount (), &strokeStyle);
663 delete [] lengths;
664 }
665 else
666 {
667 properties.dashStyle = D2D1_DASH_STYLE_SOLID;
668 getD2DFactory ()->CreateStrokeStyle (properties, nullptr, 0, &strokeStyle);
669 }
670 }
671
672 //-----------------------------------------------------------------------------
setLineWidth(CCoord width)673 void D2DDrawContext::setLineWidth (CCoord width)
674 {
675 if (getCurrentState ().frameWidth == width)
676 return;
677 COffscreenContext::setLineWidth (width);
678 }
679
680 //-----------------------------------------------------------------------------
setDrawMode(CDrawMode mode)681 void D2DDrawContext::setDrawMode (CDrawMode mode)
682 {
683 if (getCurrentState ().drawMode == mode && getCurrentState ().drawMode.integralMode () == mode.integralMode ())
684 return;
685 setDrawModeInternal (mode);
686 COffscreenContext::setDrawMode (mode);
687 }
688
689 //-----------------------------------------------------------------------------
setDrawModeInternal(CDrawMode mode)690 void D2DDrawContext::setDrawModeInternal (CDrawMode mode)
691 {
692 if (renderTarget)
693 {
694 if (mode == kAntiAliasing)
695 renderTarget->SetAntialiasMode (D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
696 else
697 renderTarget->SetAntialiasMode (D2D1_ANTIALIAS_MODE_ALIASED);
698 }
699 }
700
701 //-----------------------------------------------------------------------------
setClipRect(const CRect & clip)702 void D2DDrawContext::setClipRect (const CRect &clip)
703 {
704 COffscreenContext::setClipRect (clip);
705 }
706
707 //-----------------------------------------------------------------------------
resetClipRect()708 void D2DDrawContext::resetClipRect ()
709 {
710 COffscreenContext::resetClipRect ();
711 }
712
713 //-----------------------------------------------------------------------------
setFillColor(const CColor & color)714 void D2DDrawContext::setFillColor (const CColor& color)
715 {
716 if (getCurrentState ().fillColor == color)
717 return;
718 setFillColorInternal (color);
719 COffscreenContext::setFillColor (color);
720 }
721
722 //-----------------------------------------------------------------------------
setFrameColor(const CColor & color)723 void D2DDrawContext::setFrameColor (const CColor& color)
724 {
725 if (getCurrentState ().frameColor == color)
726 return;
727 setFrameColorInternal (color);
728 COffscreenContext::setFrameColor (color);
729 }
730
731 //-----------------------------------------------------------------------------
setFontColor(const CColor & color)732 void D2DDrawContext::setFontColor (const CColor& color)
733 {
734 if (getCurrentState ().fontColor == color)
735 return;
736 setFontColorInternal (color);
737 COffscreenContext::setFontColor (color);
738 }
739
740 //-----------------------------------------------------------------------------
setFillColorInternal(const CColor & color)741 void D2DDrawContext::setFillColorInternal (const CColor& color)
742 {
743 if (fillBrush)
744 {
745 fillBrush->Release ();
746 fillBrush = nullptr;
747 }
748 if (renderTarget)
749 {
750 D2D1_COLOR_F d2Color = {color.red/255.f, color.green/255.f, color.blue/255.f, (color.alpha/255.f) * getCurrentState ().globalAlpha};
751 renderTarget->CreateSolidColorBrush (d2Color, &fillBrush);
752 }
753 }
754
755 //-----------------------------------------------------------------------------
setFrameColorInternal(const CColor & color)756 void D2DDrawContext::setFrameColorInternal (const CColor& color)
757 {
758 if (strokeBrush)
759 {
760 strokeBrush->Release ();
761 strokeBrush = nullptr;
762 }
763 if (renderTarget)
764 {
765 D2D1_COLOR_F d2Color = {color.red/255.f, color.green/255.f, color.blue/255.f, (color.alpha/255.f) * getCurrentState ().globalAlpha};
766 renderTarget->CreateSolidColorBrush (d2Color, &strokeBrush);
767 }
768 }
769
770 //-----------------------------------------------------------------------------
setFontColorInternal(const CColor & color)771 void D2DDrawContext::setFontColorInternal (const CColor& color)
772 {
773 if (fontBrush)
774 {
775 fontBrush->Release ();
776 fontBrush = nullptr;
777 }
778 if (renderTarget)
779 {
780 D2D1_COLOR_F d2Color = {color.red/255.f, color.green/255.f, color.blue/255.f, (color.alpha/255.f) * getCurrentState ().globalAlpha};
781 renderTarget->CreateSolidColorBrush (d2Color, &fontBrush);
782 }
783 }
784
785 //-----------------------------------------------------------------------------
setGlobalAlpha(float newAlpha)786 void D2DDrawContext::setGlobalAlpha (float newAlpha)
787 {
788 if (getCurrentState ().globalAlpha == newAlpha)
789 return;
790 COffscreenContext::setGlobalAlpha (newAlpha);
791 setFrameColorInternal (getCurrentState ().frameColor);
792 setFillColorInternal (getCurrentState ().fillColor);
793 setFontColorInternal (getCurrentState ().fontColor);
794 }
795
796 //-----------------------------------------------------------------------------
saveGlobalState()797 void D2DDrawContext::saveGlobalState ()
798 {
799 COffscreenContext::saveGlobalState ();
800 }
801
802 //-----------------------------------------------------------------------------
restoreGlobalState()803 void D2DDrawContext::restoreGlobalState ()
804 {
805 CColor prevFillColor = getCurrentState ().fillColor;
806 CColor prevFrameColor = getCurrentState ().frameColor;
807 CColor prevFontColor = getCurrentState ().fontColor;
808 CLineStyle prevLineStye = getCurrentState ().lineStyle;
809 CDrawMode prevDrawMode = getCurrentState ().drawMode;
810 float prevAlpha = getCurrentState ().globalAlpha;
811 COffscreenContext::restoreGlobalState ();
812 if (prevAlpha != getCurrentState ().globalAlpha)
813 {
814 float prevAlpha = getCurrentState ().globalAlpha;
815 getCurrentState ().globalAlpha = -1.f;
816 setGlobalAlpha (prevAlpha);
817 }
818 else
819 {
820 if (prevFillColor != getCurrentState ().fillColor)
821 {
822 setFillColorInternal (getCurrentState ().fillColor);
823 }
824 if (prevFrameColor != getCurrentState ().frameColor)
825 {
826 setFrameColorInternal (getCurrentState ().frameColor);
827 }
828 if (prevFontColor != getCurrentState ().fontColor)
829 {
830 setFontColorInternal (getCurrentState ().fontColor);
831 }
832 }
833 if (prevLineStye != getCurrentState ().lineStyle)
834 {
835 setLineStyleInternal (getCurrentState ().lineStyle);
836 }
837 if (prevDrawMode != getCurrentState ().drawMode)
838 {
839 setDrawModeInternal (getCurrentState ().drawMode);
840 }
841 }
842
843 } // namespace
844
845 #endif // WINDOWS
846