The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

826 lines
28KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. template <typename Type>
  20. D2D1_RECT_F rectangleToRectF (const Rectangle<Type>& r)
  21. {
  22. return D2D1::RectF ((float) r.getX(), (float) r.getY(), (float) r.getRight(), (float) r.getBottom());
  23. }
  24. static D2D1_COLOR_F colourToD2D (Colour c)
  25. {
  26. return D2D1::ColorF::ColorF (c.getFloatRed(), c.getFloatGreen(), c.getFloatBlue(), c.getFloatAlpha());
  27. }
  28. static void pathToGeometrySink (const Path& path, ID2D1GeometrySink* sink, const AffineTransform& transform)
  29. {
  30. Path::Iterator it (path);
  31. while (it.next())
  32. {
  33. switch (it.elementType)
  34. {
  35. case Path::Iterator::cubicTo:
  36. {
  37. D2D1_BEZIER_SEGMENT seg;
  38. transform.transformPoint (it.x1, it.y1);
  39. seg.point1 = D2D1::Point2F (it.x1, it.y1);
  40. transform.transformPoint (it.x2, it.y2);
  41. seg.point2 = D2D1::Point2F (it.x2, it.y2);
  42. transform.transformPoint (it.x3, it.y3);
  43. seg.point3 = D2D1::Point2F (it.x3, it.y3);
  44. sink->AddBezier (seg);
  45. break;
  46. }
  47. case Path::Iterator::lineTo:
  48. {
  49. transform.transformPoint (it.x1, it.y1);
  50. sink->AddLine (D2D1::Point2F (it.x1, it.y1));
  51. break;
  52. }
  53. case Path::Iterator::quadraticTo:
  54. {
  55. D2D1_QUADRATIC_BEZIER_SEGMENT seg;
  56. transform.transformPoint (it.x1, it.y1);
  57. seg.point1 = D2D1::Point2F (it.x1, it.y1);
  58. transform.transformPoint (it.x2, it.y2);
  59. seg.point2 = D2D1::Point2F (it.x2, it.y2);
  60. sink->AddQuadraticBezier (seg);
  61. break;
  62. }
  63. case Path::Iterator::closePath:
  64. {
  65. sink->EndFigure (D2D1_FIGURE_END_CLOSED);
  66. break;
  67. }
  68. case Path::Iterator::startNewSubPath:
  69. {
  70. transform.transformPoint (it.x1, it.y1);
  71. sink->BeginFigure (D2D1::Point2F (it.x1, it.y1), D2D1_FIGURE_BEGIN_FILLED);
  72. break;
  73. }
  74. }
  75. }
  76. }
  77. static D2D1::Matrix3x2F transformToMatrix (const AffineTransform& transform)
  78. {
  79. D2D1::Matrix3x2F matrix;
  80. matrix._11 = transform.mat00;
  81. matrix._12 = transform.mat10;
  82. matrix._21 = transform.mat01;
  83. matrix._22 = transform.mat11;
  84. matrix._31 = transform.mat02;
  85. matrix._32 = transform.mat12;
  86. return matrix;
  87. }
  88. static D2D1_POINT_2F pointTransformed (int x, int y, const AffineTransform& transform)
  89. {
  90. transform.transformPoint (x, y);
  91. return D2D1::Point2F ((FLOAT) x, (FLOAT) y);
  92. }
  93. static void rectToGeometrySink (const Rectangle<int>& rect, ID2D1GeometrySink* sink, const AffineTransform& transform)
  94. {
  95. sink->BeginFigure (pointTransformed (rect.getX(), rect.getY(), transform), D2D1_FIGURE_BEGIN_FILLED);
  96. sink->AddLine (pointTransformed (rect.getRight(), rect.getY(), transform));
  97. sink->AddLine (pointTransformed (rect.getRight(), rect.getBottom(), transform));
  98. sink->AddLine (pointTransformed (rect.getX(), rect.getBottom(), transform));
  99. sink->EndFigure (D2D1_FIGURE_END_CLOSED);
  100. }
  101. //==============================================================================
  102. struct Direct2DLowLevelGraphicsContext::Pimpl
  103. {
  104. ID2D1PathGeometry* rectListToPathGeometry (const RectangleList<int>& clipRegion)
  105. {
  106. ID2D1PathGeometry* p = nullptr;
  107. factories->d2dFactory->CreatePathGeometry (&p);
  108. ComSmartPtr<ID2D1GeometrySink> sink;
  109. HRESULT hr = p->Open (sink.resetAndGetPointerAddress()); // xxx handle error
  110. sink->SetFillMode (D2D1_FILL_MODE_WINDING);
  111. for (int i = clipRegion.getNumRectangles(); --i >= 0;)
  112. rectToGeometrySink (clipRegion.getRectangle(i), sink, AffineTransform());
  113. hr = sink->Close();
  114. return p;
  115. }
  116. ID2D1PathGeometry* pathToPathGeometry (const Path& path, const AffineTransform& transform)
  117. {
  118. ID2D1PathGeometry* p = nullptr;
  119. factories->d2dFactory->CreatePathGeometry (&p);
  120. ComSmartPtr<ID2D1GeometrySink> sink;
  121. HRESULT hr = p->Open (sink.resetAndGetPointerAddress());
  122. sink->SetFillMode (D2D1_FILL_MODE_WINDING); // xxx need to check Path::isUsingNonZeroWinding()
  123. pathToGeometrySink (path, sink, transform);
  124. hr = sink->Close();
  125. return p;
  126. }
  127. SharedResourcePointer<Direct2DFactories> factories;
  128. ComSmartPtr<ID2D1HwndRenderTarget> renderingTarget;
  129. ComSmartPtr<ID2D1SolidColorBrush> colourBrush;
  130. };
  131. //==============================================================================
  132. struct Direct2DLowLevelGraphicsContext::SavedState
  133. {
  134. public:
  135. SavedState (Direct2DLowLevelGraphicsContext& owner_)
  136. : owner (owner_)
  137. {
  138. if (owner.currentState != nullptr)
  139. {
  140. // xxx seems like a very slow way to create one of these, and this is a performance
  141. // bottleneck.. Can the same internal objects be shared by multiple state objects, maybe using copy-on-write?
  142. setFill (owner.currentState->fillType);
  143. currentBrush = owner.currentState->currentBrush;
  144. clipRect = owner.currentState->clipRect;
  145. transform = owner.currentState->transform;
  146. font = owner.currentState->font;
  147. currentFontFace = owner.currentState->currentFontFace;
  148. }
  149. else
  150. {
  151. const D2D1_SIZE_U size (owner.pimpl->renderingTarget->GetPixelSize());
  152. clipRect.setSize (size.width, size.height);
  153. setFill (FillType (Colours::black));
  154. }
  155. }
  156. ~SavedState()
  157. {
  158. clearClip();
  159. clearFont();
  160. clearFill();
  161. clearPathClip();
  162. clearImageClip();
  163. complexClipLayer = nullptr;
  164. bitmapMaskLayer = nullptr;
  165. }
  166. void clearClip()
  167. {
  168. popClips();
  169. shouldClipRect = false;
  170. }
  171. void clipToRectangle (const Rectangle<int>& r)
  172. {
  173. clearClip();
  174. clipRect = r.toFloat().transformed (transform).getSmallestIntegerContainer();
  175. shouldClipRect = true;
  176. pushClips();
  177. }
  178. void clearPathClip()
  179. {
  180. popClips();
  181. if (shouldClipComplex)
  182. {
  183. complexClipGeometry = nullptr;
  184. shouldClipComplex = false;
  185. }
  186. }
  187. void Direct2DLowLevelGraphicsContext::SavedState::clipToPath (ID2D1Geometry* geometry)
  188. {
  189. clearPathClip();
  190. if (complexClipLayer == nullptr)
  191. owner.pimpl->renderingTarget->CreateLayer (complexClipLayer.resetAndGetPointerAddress());
  192. complexClipGeometry = geometry;
  193. shouldClipComplex = true;
  194. pushClips();
  195. }
  196. void clearRectListClip()
  197. {
  198. popClips();
  199. if (shouldClipRectList)
  200. {
  201. rectListGeometry = nullptr;
  202. shouldClipRectList = false;
  203. }
  204. }
  205. void clipToRectList (ID2D1Geometry* geometry)
  206. {
  207. clearRectListClip();
  208. if (rectListLayer == nullptr)
  209. owner.pimpl->renderingTarget->CreateLayer (rectListLayer.resetAndGetPointerAddress());
  210. rectListGeometry = geometry;
  211. shouldClipRectList = true;
  212. pushClips();
  213. }
  214. void clearImageClip()
  215. {
  216. popClips();
  217. if (shouldClipBitmap)
  218. {
  219. maskBitmap = nullptr;
  220. bitmapMaskBrush = nullptr;
  221. shouldClipBitmap = false;
  222. }
  223. }
  224. void clipToImage (const Image& clipImage, const AffineTransform& clipTransform)
  225. {
  226. clearImageClip();
  227. if (bitmapMaskLayer == nullptr)
  228. owner.pimpl->renderingTarget->CreateLayer (bitmapMaskLayer.resetAndGetPointerAddress());
  229. D2D1_BRUSH_PROPERTIES brushProps;
  230. brushProps.opacity = 1;
  231. brushProps.transform = transformToMatrix (clipTransform);
  232. D2D1_BITMAP_BRUSH_PROPERTIES bmProps = D2D1::BitmapBrushProperties (D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_WRAP);
  233. D2D1_SIZE_U size;
  234. size.width = clipImage.getWidth();
  235. size.height = clipImage.getHeight();
  236. D2D1_BITMAP_PROPERTIES bp = D2D1::BitmapProperties();
  237. maskImage = clipImage.convertedToFormat (Image::ARGB);
  238. Image::BitmapData bd (maskImage, Image::BitmapData::readOnly); // xxx should be maskImage?
  239. bp.pixelFormat = owner.pimpl->renderingTarget->GetPixelFormat();
  240. bp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
  241. HRESULT hr = owner.pimpl->renderingTarget->CreateBitmap (size, bd.data, bd.lineStride, bp, maskBitmap.resetAndGetPointerAddress());
  242. hr = owner.pimpl->renderingTarget->CreateBitmapBrush (maskBitmap, bmProps, brushProps, bitmapMaskBrush.resetAndGetPointerAddress());
  243. imageMaskLayerParams = D2D1::LayerParameters();
  244. imageMaskLayerParams.opacityBrush = bitmapMaskBrush;
  245. shouldClipBitmap = true;
  246. pushClips();
  247. }
  248. void popClips()
  249. {
  250. if (clipsBitmap)
  251. {
  252. owner.pimpl->renderingTarget->PopLayer();
  253. clipsBitmap = false;
  254. }
  255. if (clipsComplex)
  256. {
  257. owner.pimpl->renderingTarget->PopLayer();
  258. clipsComplex = false;
  259. }
  260. if (clipsRectList)
  261. {
  262. owner.pimpl->renderingTarget->PopLayer();
  263. clipsRectList = false;
  264. }
  265. if (clipsRect)
  266. {
  267. owner.pimpl->renderingTarget->PopAxisAlignedClip();
  268. clipsRect = false;
  269. }
  270. }
  271. void pushClips()
  272. {
  273. if (shouldClipRect && !clipsRect)
  274. {
  275. owner.pimpl->renderingTarget->PushAxisAlignedClip (rectangleToRectF (clipRect), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
  276. clipsRect = true;
  277. }
  278. if (shouldClipRectList && !clipsRectList)
  279. {
  280. D2D1_LAYER_PARAMETERS layerParams = D2D1::LayerParameters();
  281. rectListGeometry->GetBounds (D2D1::IdentityMatrix(), &layerParams.contentBounds);
  282. layerParams.geometricMask = rectListGeometry;
  283. owner.pimpl->renderingTarget->PushLayer (layerParams, rectListLayer);
  284. clipsRectList = true;
  285. }
  286. if (shouldClipComplex && !clipsComplex)
  287. {
  288. D2D1_LAYER_PARAMETERS layerParams = D2D1::LayerParameters();
  289. complexClipGeometry->GetBounds (D2D1::IdentityMatrix(), &layerParams.contentBounds);
  290. layerParams.geometricMask = complexClipGeometry;
  291. owner.pimpl->renderingTarget->PushLayer (layerParams, complexClipLayer);
  292. clipsComplex = true;
  293. }
  294. if (shouldClipBitmap && !clipsBitmap)
  295. {
  296. owner.pimpl->renderingTarget->PushLayer (imageMaskLayerParams, bitmapMaskLayer);
  297. clipsBitmap = true;
  298. }
  299. }
  300. void setFill (const FillType& newFillType)
  301. {
  302. if (fillType != newFillType)
  303. {
  304. fillType = newFillType;
  305. clearFill();
  306. }
  307. }
  308. void clearFont()
  309. {
  310. currentFontFace = localFontFace = nullptr;
  311. }
  312. void setFont (const Font& newFont)
  313. {
  314. if (font != newFont)
  315. {
  316. font = newFont;
  317. clearFont();
  318. }
  319. }
  320. void createFont()
  321. {
  322. if (currentFontFace == nullptr)
  323. {
  324. WindowsDirectWriteTypeface* typeface = dynamic_cast<WindowsDirectWriteTypeface*> (font.getTypeface());
  325. currentFontFace = typeface->getIDWriteFontFace();
  326. fontHeightToEmSizeFactor = typeface->getUnitsToHeightScaleFactor();
  327. }
  328. }
  329. void setOpacity (float newOpacity)
  330. {
  331. fillType.setOpacity (newOpacity);
  332. if (currentBrush != nullptr)
  333. currentBrush->SetOpacity (newOpacity);
  334. }
  335. void clearFill()
  336. {
  337. gradientStops = nullptr;
  338. linearGradient = nullptr;
  339. radialGradient = nullptr;
  340. bitmap = nullptr;
  341. bitmapBrush = nullptr;
  342. currentBrush = nullptr;
  343. }
  344. void createBrush()
  345. {
  346. if (currentBrush == nullptr)
  347. {
  348. if (fillType.isColour())
  349. {
  350. D2D1_COLOR_F colour = colourToD2D (fillType.colour);
  351. owner.pimpl->colourBrush->SetColor (colour);
  352. currentBrush = owner.pimpl->colourBrush;
  353. }
  354. else if (fillType.isTiledImage())
  355. {
  356. D2D1_BRUSH_PROPERTIES brushProps;
  357. brushProps.opacity = fillType.getOpacity();
  358. brushProps.transform = transformToMatrix (fillType.transform);
  359. D2D1_BITMAP_BRUSH_PROPERTIES bmProps = D2D1::BitmapBrushProperties (D2D1_EXTEND_MODE_WRAP, D2D1_EXTEND_MODE_WRAP);
  360. image = fillType.image;
  361. D2D1_SIZE_U size;
  362. size.width = image.getWidth();
  363. size.height = image.getHeight();
  364. D2D1_BITMAP_PROPERTIES bp = D2D1::BitmapProperties();
  365. this->image = image.convertedToFormat (Image::ARGB);
  366. Image::BitmapData bd (this->image, Image::BitmapData::readOnly);
  367. bp.pixelFormat = owner.pimpl->renderingTarget->GetPixelFormat();
  368. bp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
  369. HRESULT hr = owner.pimpl->renderingTarget->CreateBitmap (size, bd.data, bd.lineStride, bp, bitmap.resetAndGetPointerAddress());
  370. hr = owner.pimpl->renderingTarget->CreateBitmapBrush (bitmap, bmProps, brushProps, bitmapBrush.resetAndGetPointerAddress());
  371. currentBrush = bitmapBrush;
  372. }
  373. else if (fillType.isGradient())
  374. {
  375. gradientStops = nullptr;
  376. D2D1_BRUSH_PROPERTIES brushProps;
  377. brushProps.opacity = fillType.getOpacity();
  378. brushProps.transform = transformToMatrix (fillType.transform.followedBy (transform));
  379. const int numColors = fillType.gradient->getNumColours();
  380. HeapBlock<D2D1_GRADIENT_STOP> stops (numColors);
  381. for (int i = fillType.gradient->getNumColours(); --i >= 0;)
  382. {
  383. stops[i].color = colourToD2D (fillType.gradient->getColour (i));
  384. stops[i].position = (FLOAT) fillType.gradient->getColourPosition (i);
  385. }
  386. owner.pimpl->renderingTarget->CreateGradientStopCollection (stops.getData(), numColors, gradientStops.resetAndGetPointerAddress());
  387. if (fillType.gradient->isRadial)
  388. {
  389. radialGradient = nullptr;
  390. const Point<float> p1 = fillType.gradient->point1;
  391. const Point<float> p2 = fillType.gradient->point2;
  392. float r = p1.getDistanceFrom(p2);
  393. D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES props =
  394. D2D1::RadialGradientBrushProperties(D2D1::Point2F(p1.x, p1.y),
  395. D2D1::Point2F(0, 0),
  396. r, r);
  397. owner.pimpl->renderingTarget->CreateRadialGradientBrush(props, brushProps, gradientStops, radialGradient.resetAndGetPointerAddress());
  398. currentBrush = radialGradient;
  399. }
  400. else
  401. {
  402. linearGradient = 0;
  403. const Point<float> p1 = fillType.gradient->point1;
  404. const Point<float> p2 = fillType.gradient->point2;
  405. D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES props =
  406. D2D1::LinearGradientBrushProperties(D2D1::Point2F(p1.x, p1.y),
  407. D2D1::Point2F(p2.x, p2.y));
  408. owner.pimpl->renderingTarget->CreateLinearGradientBrush (props, brushProps, gradientStops, linearGradient.resetAndGetPointerAddress());
  409. currentBrush = linearGradient;
  410. }
  411. }
  412. }
  413. }
  414. Direct2DLowLevelGraphicsContext& owner;
  415. AffineTransform transform;
  416. Font font;
  417. float fontHeightToEmSizeFactor = 1.0;
  418. IDWriteFontFace* currentFontFace = nullptr;
  419. ComSmartPtr<IDWriteFontFace> localFontFace;
  420. Rectangle<int> clipRect;
  421. bool clipsRect = false, shouldClipRect = false;
  422. Image image;
  423. ComSmartPtr<ID2D1Bitmap> bitmap; // xxx needs a better name - what is this for??
  424. bool clipsBitmap = false, shouldClipBitmap = false;
  425. ComSmartPtr<ID2D1Geometry> complexClipGeometry;
  426. D2D1_LAYER_PARAMETERS complexClipLayerParams;
  427. ComSmartPtr<ID2D1Layer> complexClipLayer;
  428. bool clipsComplex = false, shouldClipComplex = false;
  429. ComSmartPtr<ID2D1Geometry> rectListGeometry;
  430. D2D1_LAYER_PARAMETERS rectListLayerParams;
  431. ComSmartPtr<ID2D1Layer> rectListLayer;
  432. bool clipsRectList = false, shouldClipRectList = false;
  433. Image maskImage;
  434. D2D1_LAYER_PARAMETERS imageMaskLayerParams;
  435. ComSmartPtr<ID2D1Layer> bitmapMaskLayer;
  436. ComSmartPtr<ID2D1Bitmap> maskBitmap;
  437. ComSmartPtr<ID2D1BitmapBrush> bitmapMaskBrush;
  438. ID2D1Brush* currentBrush = nullptr;
  439. ComSmartPtr<ID2D1BitmapBrush> bitmapBrush;
  440. ComSmartPtr<ID2D1LinearGradientBrush> linearGradient;
  441. ComSmartPtr<ID2D1RadialGradientBrush> radialGradient;
  442. ComSmartPtr<ID2D1GradientStopCollection> gradientStops;
  443. FillType fillType;
  444. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SavedState)
  445. };
  446. //==============================================================================
  447. Direct2DLowLevelGraphicsContext::Direct2DLowLevelGraphicsContext (HWND hwnd_)
  448. : hwnd (hwnd_),
  449. currentState (nullptr),
  450. pimpl (new Pimpl())
  451. {
  452. RECT windowRect;
  453. GetClientRect (hwnd, &windowRect);
  454. D2D1_SIZE_U size = { (UINT32) (windowRect.right - windowRect.left), (UINT32) (windowRect.bottom - windowRect.top) };
  455. bounds.setSize (size.width, size.height);
  456. D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties();
  457. D2D1_HWND_RENDER_TARGET_PROPERTIES propsHwnd = D2D1::HwndRenderTargetProperties (hwnd, size);
  458. if (pimpl->factories->d2dFactory != nullptr)
  459. {
  460. HRESULT hr = pimpl->factories->d2dFactory->CreateHwndRenderTarget (props, propsHwnd, pimpl->renderingTarget.resetAndGetPointerAddress());
  461. jassert (SUCCEEDED (hr)); ignoreUnused (hr);
  462. hr = pimpl->renderingTarget->CreateSolidColorBrush (D2D1::ColorF::ColorF (0.0f, 0.0f, 0.0f, 1.0f), pimpl->colourBrush.resetAndGetPointerAddress());
  463. }
  464. }
  465. Direct2DLowLevelGraphicsContext::~Direct2DLowLevelGraphicsContext()
  466. {
  467. states.clear();
  468. }
  469. void Direct2DLowLevelGraphicsContext::resized()
  470. {
  471. RECT windowRect;
  472. GetClientRect (hwnd, &windowRect);
  473. D2D1_SIZE_U size = { (UINT32) (windowRect.right - windowRect.left), (UINT32) (windowRect.bottom - windowRect.top) };
  474. pimpl->renderingTarget->Resize (size);
  475. bounds.setSize (size.width, size.height);
  476. }
  477. void Direct2DLowLevelGraphicsContext::clear()
  478. {
  479. pimpl->renderingTarget->Clear (D2D1::ColorF (D2D1::ColorF::White, 0.0f)); // xxx why white and not black?
  480. }
  481. void Direct2DLowLevelGraphicsContext::start()
  482. {
  483. pimpl->renderingTarget->BeginDraw();
  484. saveState();
  485. }
  486. void Direct2DLowLevelGraphicsContext::end()
  487. {
  488. states.clear();
  489. currentState = 0;
  490. pimpl->renderingTarget->EndDraw();
  491. pimpl->renderingTarget->CheckWindowState();
  492. }
  493. void Direct2DLowLevelGraphicsContext::setOrigin (Point<int> o)
  494. {
  495. addTransform (AffineTransform::translation ((float) o.x, (float) o.y));
  496. }
  497. void Direct2DLowLevelGraphicsContext::addTransform (const AffineTransform& transform)
  498. {
  499. currentState->transform = transform.followedBy (currentState->transform);
  500. }
  501. float Direct2DLowLevelGraphicsContext::getPhysicalPixelScaleFactor()
  502. {
  503. return currentState->transform.getScaleFactor();
  504. }
  505. bool Direct2DLowLevelGraphicsContext::clipToRectangle (const Rectangle<int>& r)
  506. {
  507. currentState->clipToRectangle (r);
  508. return ! isClipEmpty();
  509. }
  510. bool Direct2DLowLevelGraphicsContext::clipToRectangleList (const RectangleList<int>& clipRegion)
  511. {
  512. currentState->clipToRectList (pimpl->rectListToPathGeometry (clipRegion));
  513. return ! isClipEmpty();
  514. }
  515. void Direct2DLowLevelGraphicsContext::excludeClipRectangle (const Rectangle<int>&)
  516. {
  517. //xxx
  518. }
  519. void Direct2DLowLevelGraphicsContext::clipToPath (const Path& path, const AffineTransform& transform)
  520. {
  521. currentState->clipToPath (pimpl->pathToPathGeometry (path, transform));
  522. }
  523. void Direct2DLowLevelGraphicsContext::clipToImageAlpha (const Image& sourceImage, const AffineTransform& transform)
  524. {
  525. currentState->clipToImage (sourceImage, transform);
  526. }
  527. bool Direct2DLowLevelGraphicsContext::clipRegionIntersects (const Rectangle<int>& r)
  528. {
  529. return currentState->clipRect.intersects (r.toFloat().transformed (currentState->transform).getSmallestIntegerContainer());
  530. }
  531. Rectangle<int> Direct2DLowLevelGraphicsContext::getClipBounds() const
  532. {
  533. // xxx could this take into account complex clip regions?
  534. return currentState->clipRect.toFloat().transformed (currentState->transform.inverted()).getSmallestIntegerContainer();
  535. }
  536. bool Direct2DLowLevelGraphicsContext::isClipEmpty() const
  537. {
  538. return currentState->clipRect.isEmpty();
  539. }
  540. void Direct2DLowLevelGraphicsContext::saveState()
  541. {
  542. states.add (new SavedState (*this));
  543. currentState = states.getLast();
  544. }
  545. void Direct2DLowLevelGraphicsContext::restoreState()
  546. {
  547. jassert (states.size() > 1); //you should never pop the last state!
  548. states.removeLast (1);
  549. currentState = states.getLast();
  550. }
  551. void Direct2DLowLevelGraphicsContext::beginTransparencyLayer (float /*opacity*/)
  552. {
  553. jassertfalse; //xxx todo
  554. }
  555. void Direct2DLowLevelGraphicsContext::endTransparencyLayer()
  556. {
  557. jassertfalse; //xxx todo
  558. }
  559. void Direct2DLowLevelGraphicsContext::setFill (const FillType& fillType)
  560. {
  561. currentState->setFill (fillType);
  562. }
  563. void Direct2DLowLevelGraphicsContext::setOpacity (float newOpacity)
  564. {
  565. currentState->setOpacity (newOpacity);
  566. }
  567. void Direct2DLowLevelGraphicsContext::setInterpolationQuality (Graphics::ResamplingQuality /*quality*/)
  568. {
  569. }
  570. void Direct2DLowLevelGraphicsContext::fillRect (const Rectangle<int>& r, bool /*replaceExistingContents*/)
  571. {
  572. fillRect (r.toFloat());
  573. }
  574. void Direct2DLowLevelGraphicsContext::fillRect (const Rectangle<float>& r)
  575. {
  576. pimpl->renderingTarget->SetTransform (transformToMatrix (currentState->transform));
  577. currentState->createBrush();
  578. pimpl->renderingTarget->FillRectangle (rectangleToRectF (r), currentState->currentBrush);
  579. pimpl->renderingTarget->SetTransform (D2D1::IdentityMatrix());
  580. }
  581. void Direct2DLowLevelGraphicsContext::fillRectList (const RectangleList<float>& list)
  582. {
  583. for (auto& r : list)
  584. fillRect (r);
  585. }
  586. void Direct2DLowLevelGraphicsContext::fillPath (const Path& p, const AffineTransform& transform)
  587. {
  588. currentState->createBrush();
  589. ComSmartPtr<ID2D1Geometry> geometry (pimpl->pathToPathGeometry (p, transform.followedBy (currentState->transform)));
  590. if (pimpl->renderingTarget != nullptr)
  591. pimpl->renderingTarget->FillGeometry (geometry, currentState->currentBrush);
  592. }
  593. void Direct2DLowLevelGraphicsContext::drawImage (const Image& image, const AffineTransform& transform)
  594. {
  595. pimpl->renderingTarget->SetTransform (transformToMatrix (transform.followedBy (currentState->transform)));
  596. D2D1_SIZE_U size;
  597. size.width = image.getWidth();
  598. size.height = image.getHeight();
  599. D2D1_BITMAP_PROPERTIES bp = D2D1::BitmapProperties();
  600. Image img (image.convertedToFormat (Image::ARGB));
  601. Image::BitmapData bd (img, Image::BitmapData::readOnly);
  602. bp.pixelFormat = pimpl->renderingTarget->GetPixelFormat();
  603. bp.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
  604. {
  605. ComSmartPtr<ID2D1Bitmap> tempBitmap;
  606. pimpl->renderingTarget->CreateBitmap (size, bd.data, bd.lineStride, bp, tempBitmap.resetAndGetPointerAddress());
  607. if (tempBitmap != nullptr)
  608. pimpl->renderingTarget->DrawBitmap (tempBitmap);
  609. }
  610. pimpl->renderingTarget->SetTransform (D2D1::IdentityMatrix());
  611. }
  612. void Direct2DLowLevelGraphicsContext::drawLine (const Line <float>& line)
  613. {
  614. // xxx doesn't seem to be correctly aligned, may need nudging by 0.5 to match the software renderer's behaviour
  615. pimpl->renderingTarget->SetTransform (transformToMatrix (currentState->transform));
  616. currentState->createBrush();
  617. pimpl->renderingTarget->DrawLine (D2D1::Point2F (line.getStartX(), line.getStartY()),
  618. D2D1::Point2F (line.getEndX(), line.getEndY()),
  619. currentState->currentBrush);
  620. pimpl->renderingTarget->SetTransform (D2D1::IdentityMatrix());
  621. }
  622. void Direct2DLowLevelGraphicsContext::setFont (const Font& newFont)
  623. {
  624. currentState->setFont (newFont);
  625. }
  626. const Font& Direct2DLowLevelGraphicsContext::getFont()
  627. {
  628. return currentState->font;
  629. }
  630. void Direct2DLowLevelGraphicsContext::drawGlyph (int glyphNumber, const AffineTransform& transform)
  631. {
  632. currentState->createBrush();
  633. currentState->createFont();
  634. float hScale = currentState->font.getHorizontalScale();
  635. pimpl->renderingTarget->SetTransform (transformToMatrix (AffineTransform::scale (hScale, 1.0f)
  636. .followedBy (transform)
  637. .followedBy (currentState->transform)));
  638. const UINT16 glyphIndices = (UINT16) glyphNumber;
  639. const FLOAT glyphAdvances = 0;
  640. DWRITE_GLYPH_OFFSET offset;
  641. offset.advanceOffset = 0;
  642. offset.ascenderOffset = 0;
  643. DWRITE_GLYPH_RUN glyphRun;
  644. glyphRun.fontFace = currentState->currentFontFace;
  645. glyphRun.fontEmSize = (FLOAT) (currentState->font.getHeight() * currentState->fontHeightToEmSizeFactor);
  646. glyphRun.glyphCount = 1;
  647. glyphRun.glyphIndices = &glyphIndices;
  648. glyphRun.glyphAdvances = &glyphAdvances;
  649. glyphRun.glyphOffsets = &offset;
  650. glyphRun.isSideways = FALSE;
  651. glyphRun.bidiLevel = 0;
  652. pimpl->renderingTarget->DrawGlyphRun (D2D1::Point2F (0, 0), &glyphRun, currentState->currentBrush);
  653. pimpl->renderingTarget->SetTransform (D2D1::IdentityMatrix());
  654. }
  655. bool Direct2DLowLevelGraphicsContext::drawTextLayout (const AttributedString& text, const Rectangle<float>& area)
  656. {
  657. pimpl->renderingTarget->SetTransform (transformToMatrix (currentState->transform));
  658. DirectWriteTypeLayout::drawToD2DContext (text, area,
  659. *(pimpl->renderingTarget),
  660. *(pimpl->factories->directWriteFactory),
  661. *(pimpl->factories->systemFonts));
  662. pimpl->renderingTarget->SetTransform (D2D1::IdentityMatrix());
  663. return true;
  664. }