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.

851 lines
30KB

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