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.

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