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.

909 lines
32KB

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