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.

juce_win32_Direct2DGraphicsContext.cpp 26KB

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