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.

841 lines
29KB

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