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.

781 lines
26KB

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