Audio plugin host https://kx.studio/carla
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.

780 lines
26KB

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