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.

839 lines
29KB

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