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