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.

904 lines
31KB

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