Audio plugin host https://kx.studio/carla
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.

831 lines
28KB

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