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.

858 lines
30KB

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