LibreOffice Module drawinglayer (master) 1
d2dpixelprocessor2d.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <sal/config.h>
21
22// win-specific
23#include <prewin.h>
24#include <d2d1.h>
25#include <d2d1_1.h>
26#include <postwin.h>
27
30#include <sal/log.hxx>
31#include <vcl/outdev.hxx>
57#include <vcl/svapp.hxx>
58
59using namespace com::sun::star;
60
61namespace
62{
63class ID2D1GlobalFactoryProvider
64{
65 sal::systools::COMReference<ID2D1Factory> mpD2DFactory;
66
67public:
68 ID2D1GlobalFactoryProvider()
69 : mpD2DFactory(nullptr)
70 {
71 const HRESULT hr(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
72 __uuidof(ID2D1Factory), nullptr,
73 reinterpret_cast<void**>(&mpD2DFactory)));
74
75 if (!SUCCEEDED(hr))
76 mpD2DFactory.clear();
77 }
78
79 sal::systools::COMReference<ID2D1Factory>& getID2D1Factory() { return mpD2DFactory; }
80};
81
82ID2D1GlobalFactoryProvider aID2D1GlobalFactoryProvider;
83
84class ID2D1GlobalRenderTargetProvider
85{
86 sal::systools::COMReference<ID2D1DCRenderTarget> mpID2D1DCRenderTarget;
87
88public:
89 ID2D1GlobalRenderTargetProvider()
90 : mpID2D1DCRenderTarget()
91 {
92 }
93
94 sal::systools::COMReference<ID2D1DCRenderTarget>& getID2D1DCRenderTarget()
95 {
96 if (!mpID2D1DCRenderTarget && aID2D1GlobalFactoryProvider.getID2D1Factory())
97 {
98 const D2D1_RENDER_TARGET_PROPERTIES aRTProps(D2D1::RenderTargetProperties(
99 D2D1_RENDER_TARGET_TYPE_DEFAULT,
100 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
101 D2D1_ALPHA_MODE_IGNORE), //D2D1_ALPHA_MODE_PREMULTIPLIED),
102 0, 0, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT));
103
104 const HRESULT hr(aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateDCRenderTarget(
105 &aRTProps, &mpID2D1DCRenderTarget));
106
107 // interestingly this ID2D1DCRenderTarget already works and can hold
108 // created ID2D1Bitmap(s) in RenderTarget-specific form, *without*
109 // any call to "BindDC", thus *without* the need of a real HDC - nice :-)
110 // When that would be needed, Application::GetDefaultDevice() would need
111 // to have a HDC that is valid during LO's lifetime.
112
113 if (!SUCCEEDED(hr))
114 mpID2D1DCRenderTarget.clear();
115 }
116
117 return mpID2D1DCRenderTarget;
118 }
119};
120
121ID2D1GlobalRenderTargetProvider aID2D1GlobalRenderTargetProvider;
122
123class SystemDependentData_ID2D1PathGeometry : public basegfx::SystemDependentData
124{
125private:
126 sal::systools::COMReference<ID2D1PathGeometry> mpID2D1PathGeometry;
127
128public:
129 SystemDependentData_ID2D1PathGeometry(
130 sal::systools::COMReference<ID2D1PathGeometry>& rID2D1PathGeometry)
131 : basegfx::SystemDependentData(Application::GetSystemDependentDataManager())
132 , mpID2D1PathGeometry(rID2D1PathGeometry)
133 {
134 }
135
136 const sal::systools::COMReference<ID2D1PathGeometry>& getID2D1PathGeometry() const
137 {
138 return mpID2D1PathGeometry;
139 }
140 virtual sal_Int64 estimateUsageInBytes() const override;
141};
142
143sal_Int64 SystemDependentData_ID2D1PathGeometry::estimateUsageInBytes() const
144{
145 sal_Int64 aRetval(0);
146
147 if (getID2D1PathGeometry())
148 {
149 UINT32 nCount(0);
150 const HRESULT hr(getID2D1PathGeometry()->GetSegmentCount(&nCount));
151
152 if (SUCCEEDED(hr))
153 {
154 // without completely receiving and tracing the GeometrySink
155 // do a rough estimation - each segment is 2D, so has two doubles.
156 // Some are beziers, so add some guessed buffer for two additional
157 // control points
158 aRetval = static_cast<sal_Int64>(nCount) * (6 * sizeof(double));
159 }
160 }
161
162 return aRetval;
163}
164
165basegfx::B2DPoint impPixelSnap(const basegfx::B2DPolygon& rPolygon,
166 const drawinglayer::geometry::ViewInformation2D& rViewInformation,
167 sal_uInt32 nIndex)
168{
169 const sal_uInt32 nCount(rPolygon.count());
170
171 // get the data
172 const basegfx::B2ITuple aPrevTuple(
174 * rPolygon.getB2DPoint((nIndex + nCount - 1) % nCount)));
175 const basegfx::B2DPoint aCurrPoint(rViewInformation.getObjectToViewTransformation()
176 * rPolygon.getB2DPoint(nIndex));
177 const basegfx::B2ITuple aCurrTuple(basegfx::fround(aCurrPoint));
178 const basegfx::B2ITuple aNextTuple(
180 * rPolygon.getB2DPoint((nIndex + 1) % nCount)));
181
182 // get the states
183 const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
184 const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
185 const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
186 const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
187 const bool bSnapX(bPrevVertical || bNextVertical);
188 const bool bSnapY(bPrevHorizontal || bNextHorizontal);
189
190 if (bSnapX || bSnapY)
191 {
192 basegfx::B2DPoint aSnappedPoint(bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(),
193 bSnapY ? aCurrTuple.getY() : aCurrPoint.getY());
194
195 aSnappedPoint *= rViewInformation.getInverseObjectToViewTransformation();
196
197 return aSnappedPoint;
198 }
199
200 return rPolygon.getB2DPoint(nIndex);
201}
202
203void addB2DPolygonToPathGeometry(sal::systools::COMReference<ID2D1GeometrySink>& rSink,
204 const basegfx::B2DPolygon& rPolygon,
205 const drawinglayer::geometry::ViewInformation2D* pViewInformation)
206{
207 const sal_uInt32 nPointCount(rPolygon.count());
208 const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nPointCount : nPointCount - 1);
210
211 for (sal_uInt32 a(0); a < nEdgeCount; a++)
212 {
213 rPolygon.getBezierSegment(a, aEdge);
214
215 const basegfx::B2DPoint aEndPoint(
216 nullptr == pViewInformation
217 ? aEdge.getEndPoint()
218 : impPixelSnap(rPolygon, *pViewInformation, (a + 1) % nPointCount));
219
220 if (aEdge.isBezier())
221 {
222 rSink->AddBezier(
223 D2D1::BezierSegment(D2D1::Point2F(aEdge.getControlPointA().getX(),
224 aEdge.getControlPointA().getY()), //C1
225 D2D1::Point2F(aEdge.getControlPointB().getX(),
226 aEdge.getControlPointB().getY()), //c2
227 D2D1::Point2F(aEndPoint.getX(), aEndPoint.getY()))); //end
228 }
229 else
230 {
231 rSink->AddLine(D2D1::Point2F(aEndPoint.getX(), aEndPoint.getY()));
232 }
233 }
234}
235
236std::shared_ptr<SystemDependentData_ID2D1PathGeometry>
237getOrCreatePathGeometry(const basegfx::B2DPolygon& rPolygon,
238 const drawinglayer::geometry::ViewInformation2D& rViewInformation)
239{
240 // try to access buffered data
241 std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1PathGeometry(
242 rPolygon.getSystemDependentData<SystemDependentData_ID2D1PathGeometry>());
243
244 if (pSystemDependentData_ID2D1PathGeometry)
245 {
246 if (rViewInformation.getPixelSnapHairline())
247 {
248 // do not buffer when PixelSnap is active
249 pSystemDependentData_ID2D1PathGeometry.reset();
250 }
251 else
252 {
253 // use and return buffered data
254 return pSystemDependentData_ID2D1PathGeometry;
255 }
256 }
257
258 sal::systools::COMReference<ID2D1PathGeometry> pID2D1PathGeometry;
259 HRESULT hr(
260 aID2D1GlobalFactoryProvider.getID2D1Factory()->CreatePathGeometry(&pID2D1PathGeometry));
261 const sal_uInt32 nPointCount(rPolygon.count());
262
263 if (SUCCEEDED(hr) && nPointCount)
264 {
265 sal::systools::COMReference<ID2D1GeometrySink> pSink;
266 hr = pID2D1PathGeometry->Open(&pSink);
267
268 if (SUCCEEDED(hr) && pSink)
269 {
270 const basegfx::B2DPoint aStart(rViewInformation.getPixelSnapHairline()
271 ? rPolygon.getB2DPoint(0)
272 : impPixelSnap(rPolygon, rViewInformation, 0));
273
274 pSink->BeginFigure(D2D1::Point2F(aStart.getX(), aStart.getY()),
275 D2D1_FIGURE_BEGIN_HOLLOW);
276 addB2DPolygonToPathGeometry(pSink, rPolygon, &rViewInformation);
277 pSink->EndFigure(rPolygon.isClosed() ? D2D1_FIGURE_END_CLOSED : D2D1_FIGURE_END_OPEN);
278 pSink->Close();
279 }
280 }
281
282 // add to buffering mechanism
283 if (pID2D1PathGeometry)
284 {
285 if (rViewInformation.getPixelSnapHairline() || nPointCount <= 4)
286 {
287 // do not buffer when PixelSnap is active or small polygon
288 return std::make_shared<SystemDependentData_ID2D1PathGeometry>(pID2D1PathGeometry);
289 }
290 else
291 {
292 return rPolygon.addOrReplaceSystemDependentData<SystemDependentData_ID2D1PathGeometry>(
293 pID2D1PathGeometry);
294 }
295 }
296
297 return std::shared_ptr<SystemDependentData_ID2D1PathGeometry>();
298}
299
300std::shared_ptr<SystemDependentData_ID2D1PathGeometry>
301getOrCreateFillGeometry(const basegfx::B2DPolyPolygon& rPolyPolygon)
302{
303 // try to access buffered data
304 std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1PathGeometry(
305 rPolyPolygon.getSystemDependentData<SystemDependentData_ID2D1PathGeometry>());
306
307 if (pSystemDependentData_ID2D1PathGeometry)
308 {
309 // use and return buffered data
310 return pSystemDependentData_ID2D1PathGeometry;
311 }
312
313 sal::systools::COMReference<ID2D1PathGeometry> pID2D1PathGeometry;
314 HRESULT hr(
315 aID2D1GlobalFactoryProvider.getID2D1Factory()->CreatePathGeometry(&pID2D1PathGeometry));
316 const sal_uInt32 nCount(rPolyPolygon.count());
317
318 if (SUCCEEDED(hr) && nCount)
319 {
320 sal::systools::COMReference<ID2D1GeometrySink> pSink;
321 hr = pID2D1PathGeometry->Open(&pSink);
322
323 if (SUCCEEDED(hr) && pSink)
324 {
325 for (sal_uInt32 a(0); a < nCount; a++)
326 {
327 const basegfx::B2DPolygon& rPolygon(rPolyPolygon.getB2DPolygon(a));
328 const sal_uInt32 nPointCount(rPolygon.count());
329
330 if (nPointCount)
331 {
332 const basegfx::B2DPoint aStart(rPolygon.getB2DPoint(0));
333
334 pSink->BeginFigure(D2D1::Point2F(aStart.getX(), aStart.getY()),
335 D2D1_FIGURE_BEGIN_FILLED);
336 addB2DPolygonToPathGeometry(pSink, rPolygon, nullptr);
337 pSink->EndFigure(D2D1_FIGURE_END_CLOSED);
338 }
339 }
340
341 pSink->Close();
342 }
343 }
344
345 // add to buffering mechanism
346 if (pID2D1PathGeometry)
347 {
348 return rPolyPolygon.addOrReplaceSystemDependentData<SystemDependentData_ID2D1PathGeometry>(
349 pID2D1PathGeometry);
350 }
351
352 return std::shared_ptr<SystemDependentData_ID2D1PathGeometry>();
353}
354
355class SystemDependentData_ID2D1Bitmap : public basegfx::SystemDependentData
356{
357private:
358 sal::systools::COMReference<ID2D1Bitmap> mpD2DBitmap;
359 const std::shared_ptr<SalBitmap> maAssociatedAlpha;
360
361public:
362 SystemDependentData_ID2D1Bitmap(sal::systools::COMReference<ID2D1Bitmap>& rD2DBitmap,
363 const std::shared_ptr<SalBitmap>& rAssociatedAlpha)
364 : basegfx::SystemDependentData(Application::GetSystemDependentDataManager())
365 , mpD2DBitmap(rD2DBitmap)
366 , maAssociatedAlpha(rAssociatedAlpha)
367 {
368 }
369
370 const sal::systools::COMReference<ID2D1Bitmap>& getID2D1Bitmap() const { return mpD2DBitmap; }
371 const std::shared_ptr<SalBitmap>& getAssociatedAlpha() const { return maAssociatedAlpha; }
372
373 virtual sal_Int64 estimateUsageInBytes() const override;
374};
375
376sal_Int64 SystemDependentData_ID2D1Bitmap::estimateUsageInBytes() const
377{
378 sal_Int64 aRetval(0);
379
380 if (getID2D1Bitmap())
381 {
382 // use factor 4 for RGBA_8 as estimation
383 const D2D1_SIZE_U aSizePixel(getID2D1Bitmap()->GetPixelSize());
384 aRetval = static_cast<sal_Int64>(aSizePixel.width)
385 * static_cast<sal_Int64>(aSizePixel.height) * 4;
386 }
387
388 return aRetval;
389}
390
391sal::systools::COMReference<ID2D1Bitmap> createB2DBitmap(const BitmapEx& rBitmapEx)
392{
393 const Size& rSizePixel(rBitmapEx.GetSizePixel());
394 const bool bAlpha(rBitmapEx.IsAlpha());
395 const sal_uInt32 nPixelCount(rSizePixel.Width() * rSizePixel.Height());
396 std::unique_ptr<sal_uInt32[]> aData(new sal_uInt32[nPixelCount]);
397 sal_uInt32* pTarget = aData.get();
398
399 if (bAlpha)
400 {
401 Bitmap aSrcAlpha(rBitmapEx.GetAlphaMask().GetBitmap());
402 Bitmap::ScopedReadAccess pReadAccess(const_cast<Bitmap&>(rBitmapEx.GetBitmap()));
403 Bitmap::ScopedReadAccess pAlphaReadAccess(aSrcAlpha.AcquireReadAccess(), aSrcAlpha);
404 const tools::Long nHeight(pReadAccess->Height());
405 const tools::Long nWidth(pReadAccess->Width());
406
407 for (tools::Long y = 0; y < nHeight; ++y)
408 {
409 for (tools::Long x = 0; x < nWidth; ++x)
410 {
411 const BitmapColor aColor(pReadAccess->GetColor(y, x));
412 const BitmapColor aAlpha(pAlphaReadAccess->GetColor(y, x));
413 const sal_uInt16 nAlpha(aAlpha.GetRed());
414
415 *pTarget++ = sal_uInt32(BitmapColor(
416 ColorAlpha, sal_uInt8((sal_uInt16(aColor.GetRed()) * nAlpha) >> 8),
417 sal_uInt8((sal_uInt16(aColor.GetGreen()) * nAlpha) >> 8),
418 sal_uInt8((sal_uInt16(aColor.GetBlue()) * nAlpha) >> 8), aAlpha.GetRed()));
419 }
420 }
421 }
422 else
423 {
424 Bitmap::ScopedReadAccess pReadAccess(const_cast<Bitmap&>(rBitmapEx.GetBitmap()));
425 const tools::Long nHeight(pReadAccess->Height());
426 const tools::Long nWidth(pReadAccess->Width());
427
428 for (tools::Long y = 0; y < nHeight; ++y)
429 {
430 for (tools::Long x = 0; x < nWidth; ++x)
431 {
432 const BitmapColor aColor(pReadAccess->GetColor(y, x));
433 *pTarget++ = sal_uInt32(aColor);
434 }
435 }
436 }
437
438 // use GlobalRenderTarget to allow usage combined with
439 // the Direct2D CreateSharedBitmap-mechanism. This is needed
440 // since ID2D1Bitmap is a ID2D1RenderTarget-dependent resource
441 // and thus - in principle - would have to be re-created for
442 // *each* new ID2D1RenderTarget, that means for *each* new
443 // target HDC, resp. OutputDevice
444 sal::systools::COMReference<ID2D1Bitmap> pID2D1Bitmap;
445
446 if (aID2D1GlobalRenderTargetProvider.getID2D1DCRenderTarget())
447 {
448 const HRESULT hr(aID2D1GlobalRenderTargetProvider.getID2D1DCRenderTarget()->CreateBitmap(
449 D2D1::SizeU(rSizePixel.Width(), rSizePixel.Height()), &aData[0],
450 rSizePixel.Width() * sizeof(sal_uInt32),
451 D2D1::BitmapProperties(
452 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, // DXGI_FORMAT
453 bAlpha ? D2D1_ALPHA_MODE_PREMULTIPLIED
454 : D2D1_ALPHA_MODE_IGNORE)), // D2D1_ALPHA_MODE
455 &pID2D1Bitmap));
456
457 if (!SUCCEEDED(hr))
458 pID2D1Bitmap.clear();
459 }
460
461 return pID2D1Bitmap;
462}
463
464sal::systools::COMReference<ID2D1Bitmap>
465getOrCreateB2DBitmap(sal::systools::COMReference<ID2D1RenderTarget>& rRT, const BitmapEx& rBitmapEx)
466{
469 std::shared_ptr<SystemDependentData_ID2D1Bitmap> pSystemDependentData_ID2D1Bitmap;
470
471 if (nullptr != pHolder)
472 {
473 // try to access SystemDependentDataHolder and buffered data
474 pSystemDependentData_ID2D1Bitmap
475 = std::static_pointer_cast<SystemDependentData_ID2D1Bitmap>(
476 pHolder->getSystemDependentData(
477 typeid(SystemDependentData_ID2D1Bitmap).hash_code()));
478
479 // check data validity for associated Alpha
480 if (pSystemDependentData_ID2D1Bitmap
481 && pSystemDependentData_ID2D1Bitmap->getAssociatedAlpha()
482 != rBitmapEx.GetAlphaMask().GetBitmap().ImplGetSalBitmap())
483 {
484 // AssociatedAlpha did change, data invalid
485 pSystemDependentData_ID2D1Bitmap.reset();
486 }
487 }
488
489 if (!pSystemDependentData_ID2D1Bitmap)
490 {
491 // have to create newly
492 sal::systools::COMReference<ID2D1Bitmap> pID2D1Bitmap(createB2DBitmap(rBitmapEx));
493
494 if (pID2D1Bitmap)
495 {
496 // creation worked, create SystemDependentData_ID2D1Bitmap
497 pSystemDependentData_ID2D1Bitmap = std::make_shared<SystemDependentData_ID2D1Bitmap>(
498 pID2D1Bitmap, rBitmapEx.GetAlphaMask().GetBitmap().ImplGetSalBitmap());
499
500 // only add if feasible
501 if (nullptr != pHolder
502 && pSystemDependentData_ID2D1Bitmap->calculateCombinedHoldCyclesInSeconds() > 0)
503 {
504 basegfx::SystemDependentData_SharedPtr r2(pSystemDependentData_ID2D1Bitmap);
505 const_cast<basegfx::SystemDependentDataHolder*>(pHolder)
506 ->addOrReplaceSystemDependentData(r2);
507 }
508 }
509 }
510
511 sal::systools::COMReference<ID2D1Bitmap> pWrappedD2DBitmap;
512
513 if (pSystemDependentData_ID2D1Bitmap)
514 {
515 // embed to CreateSharedBitmap, that makes it usable on
516 // the specified RenderTarget
517 const HRESULT hr(rRT->CreateSharedBitmap(
518 __uuidof(ID2D1Bitmap),
519 static_cast<void*>(pSystemDependentData_ID2D1Bitmap->getID2D1Bitmap()), nullptr,
520 &pWrappedD2DBitmap));
521
522 if (!SUCCEEDED(hr))
523 pWrappedD2DBitmap.clear();
524 }
525
526 return pWrappedD2DBitmap;
527}
528
529// This is a simple local derivation of D2DPixelProcessor2D to be used
530// when sub-content needs to be rendered to pixels. Hand over the adapted
531// ViewInformation2D, a pixel size and the parent RenderTarget. It will
532// locally create and use a ID2D1BitmapRenderTarget to render the stuff
533// (you need to call process() with the primitives to be painted of
534// course). Then use the local helper getID2D1Bitmap() to access the
535// ID2D1Bitmap which was the target of that operation.
536class D2DBitmapPixelProcessor2D final : public drawinglayer::processor2d::D2DPixelProcessor2D
537{
538 // the local ID2D1BitmapRenderTarget
539 sal::systools::COMReference<ID2D1BitmapRenderTarget> mpBitmapRenderTarget;
540
541public:
542 // helper class to create another instance of D2DPixelProcessor2D for
543 // creating helper-ID2D1Bitmap's for a given ID2D1RenderTarget
544 D2DBitmapPixelProcessor2D(const drawinglayer::geometry::ViewInformation2D& rViewInformation,
545 sal_uInt32 nWidth, sal_uInt32 nHeight,
546 const sal::systools::COMReference<ID2D1RenderTarget>& rParent)
547 : drawinglayer::processor2d::D2DPixelProcessor2D(rViewInformation)
548 , mpBitmapRenderTarget()
549 {
550 if (0 == nWidth || 0 == nHeight)
551 {
552 // no width/height, done
554 }
555
556 if (!hasError())
557 {
558 // Allocate compatible RGBA render target
559 const D2D1_SIZE_U aRenderTargetSizePixel(D2D1::SizeU(nWidth, nHeight));
560 const HRESULT hr(rParent->CreateCompatibleRenderTarget(
561 nullptr, &aRenderTargetSizePixel, nullptr,
562 D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE, &mpBitmapRenderTarget));
563
564 if (!SUCCEEDED(hr) || nullptr == mpBitmapRenderTarget)
565 {
566 // did not work, done
568 }
569 else
570 {
571 sal::systools::COMReference<ID2D1RenderTarget> pRT;
572 mpBitmapRenderTarget->QueryInterface(__uuidof(ID2D1RenderTarget),
573 reinterpret_cast<void**>(&pRT));
574 setRenderTarget(pRT);
575 }
576 }
577
578 if (hasRenderTarget())
579 {
580 // set Viewort if none was given. We have a fixed pixel target, s we know the
581 // exact Viewport to work on
582 if (getViewInformation2D().getViewport().isEmpty())
583 {
585 basegfx::B2DRange aViewport(0.0, 0.0, nWidth, nHeight);
586 basegfx::B2DHomMatrix aInvViewTransform(aViewInformation.getViewTransformation());
587
588 aInvViewTransform.invert();
589 aViewport.transform(aInvViewTransform);
590 aViewInformation.setViewport(aViewport);
591 updateViewInformation(aViewInformation);
592 }
593
594 // clear as render preparation
595 getRenderTarget()->BeginDraw();
596 getRenderTarget()->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f));
597 getRenderTarget()->EndDraw();
598 }
599 }
600
601 sal::systools::COMReference<ID2D1Bitmap> getID2D1Bitmap() const
602 {
603 sal::systools::COMReference<ID2D1Bitmap> pResult;
604
605 // access the resulting bitmap if exists
606 if (mpBitmapRenderTarget)
607 {
608 mpBitmapRenderTarget->GetBitmap(&pResult);
609 }
610
611 return pResult;
612 }
613};
614
615bool createBitmapSubContent(sal::systools::COMReference<ID2D1Bitmap>& rResult,
616 basegfx::B2DRange& rDiscreteVisibleRange,
618 const drawinglayer::geometry::ViewInformation2D& rViewInformation2D,
619 const sal::systools::COMReference<ID2D1RenderTarget>& rRenderTarget)
620{
621 if (rContent.empty() || !rRenderTarget)
622 {
623 // no content or no render target, done
624 return false;
625 }
626
628 rDiscreteVisibleRange, rContent.getB2DRange(rViewInformation2D), rViewInformation2D);
629
630 if (rDiscreteVisibleRange.isEmpty())
631 {
632 // not visible, done
633 return false;
634 }
635
636 // Use a temporary second instance of a D2DBitmapPixelProcessor2D with adapted
637 // ViewInformation2D, it will create the needed ID2D1BitmapRenderTarget
638 // locally and Clear() it.
639 drawinglayer::geometry::ViewInformation2D aAdaptedViewInformation2D(rViewInformation2D);
640 const double fTargetWidth(ceil(rDiscreteVisibleRange.getWidth()));
641 const double fTargetHeight(ceil(rDiscreteVisibleRange.getHeight()));
642
643 {
644 // create adapted ViewTransform, needs to be offset in discrete coordinates,
645 // so multiply from left
646 basegfx::B2DHomMatrix aAdapted(
648 -rDiscreteVisibleRange.getMinY())
649 * rViewInformation2D.getViewTransformation());
650 aAdaptedViewInformation2D.setViewTransformation(aAdapted);
651
652 // reset Viewport (world coordinates), so the helper renderer will create it's
653 // own based on it's given internal discrete size
654 aAdaptedViewInformation2D.setViewport(basegfx::B2DRange());
655 }
656
657 D2DBitmapPixelProcessor2D aSubContentRenderer(aAdaptedViewInformation2D, fTargetWidth,
658 fTargetHeight, rRenderTarget);
659
660 if (!aSubContentRenderer.valid())
661 {
662 // did not work, done
663 return false;
664 }
665
666 // render sub-content recursively
667 aSubContentRenderer.process(rContent);
668
669 // grab Bitmap & prepare results from RGBA content rendering
670 rResult = aSubContentRenderer.getID2D1Bitmap();
671 return true;
672}
673}
674
676{
678 : BaseProcessor2D(rViewInformation)
679 , maBColorModifierStack()
680 , mpRT()
681 , mnRecursionCounter(0)
682 , mnErrorCounter(0)
683{
684}
685
687 HDC aHdc)
688 : BaseProcessor2D(rViewInformation)
689 , maBColorModifierStack()
690 , mpRT()
691 , mnRecursionCounter(0)
692 , mnErrorCounter(0)
693{
694 sal::systools::COMReference<ID2D1DCRenderTarget> pDCRT;
695 tools::Long aOutWidth(0), aOutHeight(0);
696
697 if (aHdc)
698 {
699 aOutWidth = GetDeviceCaps(aHdc, HORZRES);
700 aOutHeight = GetDeviceCaps(aHdc, VERTRES);
701 }
702
703 if (aOutWidth > 0 && aOutHeight > 0 && aID2D1GlobalFactoryProvider.getID2D1Factory())
704 {
705 const D2D1_RENDER_TARGET_PROPERTIES aRTProps(D2D1::RenderTargetProperties(
706 D2D1_RENDER_TARGET_TYPE_DEFAULT,
707 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
708 D2D1_ALPHA_MODE_IGNORE), //D2D1_ALPHA_MODE_PREMULTIPLIED),
709 0, 0, D2D1_RENDER_TARGET_USAGE_NONE, D2D1_FEATURE_LEVEL_DEFAULT));
710
711 const HRESULT hr(
712 aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateDCRenderTarget(&aRTProps, &pDCRT));
713
714 if (!SUCCEEDED(hr))
715 pDCRT.clear();
716 }
717
718 if (pDCRT)
719 {
720 const RECT rc(
721 { 0, 0, o3tl::narrowing<LONG>(aOutWidth), o3tl::narrowing<LONG>(aOutHeight) });
722 const HRESULT hr(pDCRT->BindDC(aHdc, &rc));
723
724 if (!SUCCEEDED(hr))
725 pDCRT.clear();
726 }
727
728 if (pDCRT)
729 {
730 if (rViewInformation.getUseAntiAliasing())
731 {
732 D2D1_ANTIALIAS_MODE eAAMode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
733 pDCRT->SetAntialiasMode(eAAMode);
734 }
735 else
736 {
737 D2D1_ANTIALIAS_MODE eAAMode = D2D1_ANTIALIAS_MODE_ALIASED;
738 pDCRT->SetAntialiasMode(eAAMode);
739 }
740
741 // since ID2D1DCRenderTarget depends on the transformation
742 // set at hdc, be careful and reset it to identity
743 XFORM aXForm;
744 aXForm.eM11 = 1.0;
745 aXForm.eM12 = 0.0;
746 aXForm.eM21 = 0.0;
747 aXForm.eM22 = 1.0;
748 aXForm.eDx = 0.0;
749 aXForm.eDy = 0.0;
750 SetWorldTransform(aHdc, &aXForm);
751 }
752
753 if (pDCRT)
754 {
755 sal::systools::COMReference<ID2D1RenderTarget> pRT;
756 pDCRT->QueryInterface(__uuidof(ID2D1RenderTarget), reinterpret_cast<void**>(&pRT));
757 setRenderTarget(pRT);
758 }
759 else
760 {
762 }
763}
764
766 const primitive2d::PolygonHairlinePrimitive2D& rPolygonHairlinePrimitive2D)
767{
768 const basegfx::B2DPolygon& rPolygon(rPolygonHairlinePrimitive2D.getB2DPolygon());
769
770 if (!rPolygon.count())
771 {
772 // no geometry, done
773 return;
774 }
775
776 bool bDone(false);
777 std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1PathGeometry(
778 getOrCreatePathGeometry(rPolygon, getViewInformation2D()));
779
780 if (pSystemDependentData_ID2D1PathGeometry)
781 {
782 sal::systools::COMReference<ID2D1TransformedGeometry> pTransformedGeometry;
783 const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
784 const basegfx::B2DHomMatrix& rObjectToView(
785 getViewInformation2D().getObjectToViewTransformation());
786 HRESULT hr(aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateTransformedGeometry(
787 pSystemDependentData_ID2D1PathGeometry->getID2D1PathGeometry(),
788 D2D1::Matrix3x2F(rObjectToView.a(), rObjectToView.b(), rObjectToView.c(),
789 rObjectToView.d(), rObjectToView.e() + fAAOffset,
790 rObjectToView.f() + fAAOffset),
791 &pTransformedGeometry));
792
793 if (SUCCEEDED(hr) && pTransformedGeometry)
794 {
795 const basegfx::BColor aHairlineColor(
796 maBColorModifierStack.getModifiedColor(rPolygonHairlinePrimitive2D.getBColor()));
797 const D2D1::ColorF aD2DColor(aHairlineColor.getRed(), aHairlineColor.getGreen(),
798 aHairlineColor.getBlue());
799 sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush;
800 hr = getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush);
801
802 if (SUCCEEDED(hr) && pColorBrush)
803 {
804 getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
805 // TODO: Unfortunately Direct2D paint of one pixel wide lines does not
806 // correctly and completely blend 100% over the background. Experimenting
807 // shows that a value around/slightly below 2.0 is needed which hints that
808 // alpha blending the half-shifted lines (see fAAOffset above) is involved.
809 // To get correct blending I try to use just wider hairlines for now. This
810 // may need to be improved - or balanced (trying sqrt(2) now...)
811 getRenderTarget()->DrawGeometry(pTransformedGeometry, pColorBrush, 1.44f);
812 bDone = true;
813 }
814 }
815 }
816
817 if (!bDone)
819}
820
822 const basegfx::B2DHomMatrix& rTansformation, const basegfx::B2DPolyPolygon& rPolyPolygon,
823 const basegfx::BColor& rColor)
824{
825 std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1PathGeometry(
826 getOrCreateFillGeometry(rPolyPolygon));
827
828 if (pSystemDependentData_ID2D1PathGeometry)
829 {
830 sal::systools::COMReference<ID2D1TransformedGeometry> pTransformedGeometry;
831 const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
832 basegfx::B2DHomMatrix aTansformation(getViewInformation2D().getObjectToViewTransformation()
833 * rTansformation);
834 HRESULT hr(aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateTransformedGeometry(
835 pSystemDependentData_ID2D1PathGeometry->getID2D1PathGeometry(),
836 D2D1::Matrix3x2F(aTansformation.a(), aTansformation.b(), aTansformation.c(),
837 aTansformation.d(), aTansformation.e() + fAAOffset,
838 aTansformation.f() + fAAOffset),
839 &pTransformedGeometry));
840
841 if (SUCCEEDED(hr) && pTransformedGeometry)
842 {
844 const D2D1::ColorF aD2DColor(aFillColor.getRed(), aFillColor.getGreen(),
845 aFillColor.getBlue());
846
847 sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush;
848 hr = getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush);
849
850 if (SUCCEEDED(hr) && pColorBrush)
851 {
852 getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
853 getRenderTarget()->FillGeometry(pTransformedGeometry, pColorBrush);
854 return true;
855 }
856 }
857 }
858
859 return false;
860}
861
863 const primitive2d::PolyPolygonColorPrimitive2D& rPolyPolygonColorPrimitive2D)
864{
865 const basegfx::B2DPolyPolygon& rPolyPolygon(rPolyPolygonColorPrimitive2D.getB2DPolyPolygon());
866 const sal_uInt32 nCount(rPolyPolygon.count());
867
868 if (!nCount)
869 {
870 // no geometry, done
871 return;
872 }
873
874 const bool bDone(drawPolyPolygonColorTransformed(basegfx::B2DHomMatrix(), rPolyPolygon,
875 rPolyPolygonColorPrimitive2D.getBColor()));
876
877 if (!bDone)
879}
880
882 const primitive2d::BitmapPrimitive2D& rBitmapCandidate)
883{
884 // check if graphic content is inside discrete local ViewPort
885 if (!getViewInformation2D().getDiscreteViewport().isEmpty())
886 {
887 // calculate logic object range, remember: the helper below will
888 // transform using getObjectToViewTransformation, so the bitmap-local
889 // transform would be missing
891 aDiscreteVisibleRange.transform(rBitmapCandidate.getTransform());
892
893 // calculate visible range
894 calculateDiscreteVisibleRange(aDiscreteVisibleRange, aDiscreteVisibleRange,
896
897 if (aDiscreteVisibleRange.isEmpty())
898 {
899 // not visible, done
900 return;
901 }
902 }
903
904 BitmapEx aBitmapEx(rBitmapCandidate.getBitmap());
905
906 if (aBitmapEx.IsEmpty() || aBitmapEx.GetSizePixel().IsEmpty())
907 {
908 // no pixel data, done
909 return;
910 }
911
913 {
914 // need to apply ColorModifier to Bitmap data
915 aBitmapEx = aBitmapEx.ModifyBitmapEx(maBColorModifierStack);
916
917 if (aBitmapEx.IsEmpty())
918 {
919 // color gets completely replaced, get it (any input works)
920 const basegfx::BColor aModifiedColor(
922
923 // use unit geometry as fallback object geometry. Do *not*
924 // transform, the below used method will use the already
925 // correctly initialized local ViewInformation
927
930 aModifiedColor));
931
932 // draw as Polygon, done
934 return;
935 }
936 }
937
938 bool bDone(false);
939 sal::systools::COMReference<ID2D1Bitmap> pD2DBitmap(
940 getOrCreateB2DBitmap(getRenderTarget(), aBitmapEx));
941
942 if (pD2DBitmap)
943 {
944 const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
945 const basegfx::B2DHomMatrix aLocalTransform(
946 getViewInformation2D().getObjectToViewTransformation()
947 * rBitmapCandidate.getTransform());
948 getRenderTarget()->SetTransform(D2D1::Matrix3x2F(
949 aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(), aLocalTransform.d(),
950 aLocalTransform.e() + fAAOffset, aLocalTransform.f() + fAAOffset));
951
952 // destinationRectangle is part of transformation above, so use UnitRange
953 getRenderTarget()->DrawBitmap(pD2DBitmap, D2D1::RectF(0.0, 0.0, 1.0, 1.0));
954 bDone = true;
955 }
956
957 if (!bDone)
959}
960
961sal::systools::COMReference<ID2D1Bitmap> D2DPixelProcessor2D::implCreateAlpha_Direct(
962 const primitive2d::TransparencePrimitive2D& rTransCandidate)
963{
964 // Try if we can use ID2D1DeviceContext/d2d1_1 by querying for interface.
965 // Only then can we use ID2D1Effect/CLSID_D2D1LuminanceToAlpha and it makes
966 // sense to try to do it this way in this implementation
967 sal::systools::COMReference<ID2D1DeviceContext> pID2D1DeviceContext;
968 getRenderTarget()->QueryInterface(__uuidof(ID2D1DeviceContext),
969 reinterpret_cast<void**>(&pID2D1DeviceContext));
970 sal::systools::COMReference<ID2D1Bitmap> pRetval;
971
972 if (!pID2D1DeviceContext)
973 {
974 // no, done - tell caller to use fallback by returning empty - we have
975 // not the preconditions for this
976 return pRetval;
977 }
978
979 // Release early
980 pID2D1DeviceContext.clear();
981 basegfx::B2DRange aDiscreteVisibleRange;
982
983 if (!createBitmapSubContent(pRetval, aDiscreteVisibleRange, rTransCandidate.getTransparence(),
985 || !pRetval)
986 {
987 // return of false means no display needed, return
988 return pRetval;
989 }
990
991 // Now we need a target to render this to, using the ID2D1Effect tooling.
992 // We can directly apply the effect to an alpha-only 8bit target here,
993 // so create one (no RGBA needed for this).
994 // We need another render target: I tried to render pInBetweenResult
995 // to pContent again, but that does not work due to the bitmap
996 // fetched being probably only an internal reference to the
997 // ID2D1BitmapRenderTarget, thus it would draw onto itself -> chaos
998 sal::systools::COMReference<ID2D1BitmapRenderTarget> pContent;
999 const D2D1_PIXEL_FORMAT aAlphaFormat(
1000 D2D1::PixelFormat(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_STRAIGHT));
1001 const D2D1_SIZE_U aRenderTargetSizePixel(D2D1::SizeU(ceil(aDiscreteVisibleRange.getWidth()),
1002 ceil(aDiscreteVisibleRange.getHeight())));
1003 const HRESULT hr(getRenderTarget()->CreateCompatibleRenderTarget(
1004 nullptr, &aRenderTargetSizePixel, &aAlphaFormat, D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE,
1005 &pContent));
1006
1007 if (SUCCEEDED(hr) && pContent)
1008 {
1009 // try to access ID2D1DeviceContext of that target, we need that *now*
1010 pContent->QueryInterface(__uuidof(ID2D1DeviceContext),
1011 reinterpret_cast<void**>(&pID2D1DeviceContext));
1012
1013 if (pID2D1DeviceContext)
1014 {
1015 // create the effect
1016 sal::systools::COMReference<ID2D1Effect> pLuminanceToAlpha;
1017 pID2D1DeviceContext->CreateEffect(CLSID_D2D1LuminanceToAlpha, &pLuminanceToAlpha);
1018
1019 if (pLuminanceToAlpha)
1020 {
1021 // chain effect stuff together & paint it
1022 pLuminanceToAlpha->SetInput(0, pRetval);
1023
1024 pID2D1DeviceContext->BeginDraw();
1025 pID2D1DeviceContext->Clear(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.0f));
1026 pID2D1DeviceContext->DrawImage(pLuminanceToAlpha);
1027 pID2D1DeviceContext->EndDraw();
1028
1029 // grab result
1030 pContent->GetBitmap(&pRetval);
1031 }
1032 }
1033 }
1034
1035 return pRetval;
1036}
1037
1038sal::systools::COMReference<ID2D1Bitmap> D2DPixelProcessor2D::implCreateAlpha_B2DBitmap(
1039 const primitive2d::TransparencePrimitive2D& rTransCandidate,
1040 const basegfx::B2DRange& rVisibleRange, D2D1_MATRIX_3X2_F& rMaskScale)
1041{
1042 // Use this fallback that will also use a pixel processor indirectly,
1043 // but allows to get the AlphaMask as vcl Bitmap using existing tooling
1044 const sal_uInt32 nDiscreteClippedWidth(ceil(rVisibleRange.getWidth()));
1045 const sal_uInt32 nDiscreteClippedHeight(ceil(rVisibleRange.getHeight()));
1046 const sal_uInt32 nMaximumQuadraticPixels(250000);
1047
1048 // Embed content graphics to TransformPrimitive2D
1049 const basegfx::B2DHomMatrix aAlphaEmbedding(
1051 -rVisibleRange.getMinY())
1052 * getViewInformation2D().getObjectToViewTransformation());
1054 aAlphaEmbedding,
1056 drawinglayer::primitive2d::Primitive2DContainer xEmbedSeq{ xAlphaEmbedRef };
1057
1058 // use empty ViewInformation to have neutral transformation
1059 const geometry::ViewInformation2D aEmptyViewInformation2D;
1060
1061 // use new mode to create AlphaChannel (not just AlphaMask) for transparency channel
1063 std::move(xEmbedSeq), aEmptyViewInformation2D, nDiscreteClippedWidth,
1064 nDiscreteClippedHeight, nMaximumQuadraticPixels, true));
1065 sal::systools::COMReference<ID2D1Bitmap> pRetval;
1066
1067 if (aAlpha.IsEmpty())
1068 {
1069 // if we have no content we are done
1070 return pRetval;
1071 }
1072
1073 // use alpha data to create the ID2D1Bitmap
1074 const Size& rSizePixel(aAlpha.GetSizePixel());
1075 const sal_uInt32 nPixelCount(rSizePixel.Width() * rSizePixel.Height());
1076 std::unique_ptr<sal_uInt8[]> aData(new sal_uInt8[nPixelCount]);
1077 sal_uInt8* pTarget = aData.get();
1078 Bitmap aSrcAlpha(aAlpha.GetBitmap());
1079 Bitmap::ScopedReadAccess pReadAccess(aSrcAlpha.AcquireReadAccess(), aSrcAlpha);
1080 const tools::Long nHeight(pReadAccess->Height());
1081 const tools::Long nWidth(pReadAccess->Width());
1082
1083 for (tools::Long y = 0; y < nHeight; ++y)
1084 {
1085 for (tools::Long x = 0; x < nWidth; ++x)
1086 {
1087 const BitmapColor aColor(pReadAccess->GetColor(y, x));
1088 *pTarget++ = aColor.GetLuminance();
1089 }
1090 }
1091
1092 const D2D1_BITMAP_PROPERTIES aBmProps(D2D1::BitmapProperties(
1093 D2D1::PixelFormat(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED)));
1094 const HRESULT hr(getRenderTarget()->CreateBitmap(
1095 D2D1::SizeU(rSizePixel.Width(), rSizePixel.Height()), &aData[0],
1096 rSizePixel.Width() * sizeof(sal_uInt8), &aBmProps, &pRetval));
1097
1098 if (!SUCCEEDED(hr) || !pRetval)
1099 {
1100 // did not work, done
1101 return pRetval;
1102 }
1103
1104 // create needed adapted transformation for alpha brush.
1105 // We may have to take a corrective scaling into account when the
1106 // MaximumQuadraticPixel limit was used/triggered
1107 const Size& rBitmapExSizePixel(aAlpha.GetSizePixel());
1108
1109 if (static_cast<sal_uInt32>(rBitmapExSizePixel.Width()) != nDiscreteClippedWidth
1110 || static_cast<sal_uInt32>(rBitmapExSizePixel.Height()) != nDiscreteClippedHeight)
1111 {
1112 // scale in X and Y should be the same (see fReduceFactor in createAlphaMask),
1113 // so adapt numerically to a single scale value, they are integer rounded values
1114 const double fScaleX(static_cast<double>(rBitmapExSizePixel.Width())
1115 / static_cast<double>(nDiscreteClippedWidth));
1116 const double fScaleY(static_cast<double>(rBitmapExSizePixel.Height())
1117 / static_cast<double>(nDiscreteClippedHeight));
1118
1119 const double fScale(1.0 / ((fScaleX + fScaleY) * 0.5));
1120 rMaskScale = D2D1::Matrix3x2F::Scale(fScale, fScale);
1121 }
1122
1123 return pRetval;
1124}
1125
1127 const primitive2d::TransparencePrimitive2D& rTransCandidate)
1128{
1129 if (rTransCandidate.getChildren().empty())
1130 {
1131 // no content, done
1132 return;
1133 }
1134
1135 if (rTransCandidate.getTransparence().empty())
1136 {
1137 // no mask (so nothing visible), done
1138 return;
1139 }
1140
1141 // calculate visible range, create only for that range
1142 basegfx::B2DRange aDiscreteVisibleRange;
1143 calculateDiscreteVisibleRange(aDiscreteVisibleRange,
1144 rTransCandidate.getChildren().getB2DRange(getViewInformation2D()),
1146
1147 if (aDiscreteVisibleRange.isEmpty())
1148 {
1149 // not visible, done
1150 return;
1151 }
1152
1153 // try to create directly, this needs the current mpRT to be a ID2D1DeviceContext/d2d1_1
1154 // what is not guaranteed but usually works for more modern windows (after 7)
1155 sal::systools::COMReference<ID2D1Bitmap> pAlphaBitmap(implCreateAlpha_Direct(rTransCandidate));
1156 D2D1_MATRIX_3X2_F aMaskScale(D2D1::Matrix3x2F::Identity());
1157
1158 if (!pAlphaBitmap)
1159 {
1160 // did not work, use more expensive fallback to existing tooling
1161 pAlphaBitmap
1162 = implCreateAlpha_B2DBitmap(rTransCandidate, aDiscreteVisibleRange, aMaskScale);
1163 }
1164
1165 if (!pAlphaBitmap)
1166 {
1167 // could not create alpha channel, error
1168 increaseError();
1169 return;
1170 }
1171
1172 sal::systools::COMReference<ID2D1Layer> pLayer;
1173 HRESULT hr(getRenderTarget()->CreateLayer(nullptr, &pLayer));
1174 bool bDone(false);
1175
1176 if (SUCCEEDED(hr) && pLayer)
1177 {
1178 sal::systools::COMReference<ID2D1BitmapBrush> pBitmapBrush;
1179 hr = getRenderTarget()->CreateBitmapBrush(pAlphaBitmap, &pBitmapBrush);
1180
1181 if (SUCCEEDED(hr) && pBitmapBrush)
1182 {
1183 // apply MaskScale to Brush, maybe used if implCreateAlpha_B2DBitmap was needed
1184 pBitmapBrush->SetTransform(aMaskScale);
1185
1186 // need to set transform offset for Layer initialization, we work
1187 // in discrete device coordinates
1188 getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Translation(
1189 floor(aDiscreteVisibleRange.getMinX()), floor(aDiscreteVisibleRange.getMinY())));
1190
1191 getRenderTarget()->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr,
1192 D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
1193 D2D1::Matrix3x2F::Identity(), 1.0,
1194 pBitmapBrush),
1195 pLayer);
1196
1197 // ... but need to reset to paint content unchanged
1198 getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
1199
1200 // draw content recursively
1201 process(rTransCandidate.getChildren());
1202
1203 getRenderTarget()->PopLayer();
1204 bDone = true;
1205 }
1206 }
1207
1208 if (!bDone)
1209 increaseError();
1210}
1211
1213 const primitive2d::UnifiedTransparencePrimitive2D& rTransCandidate)
1214{
1215 if (rTransCandidate.getChildren().empty())
1216 {
1217 // no content, done
1218 return;
1219 }
1220
1221 if (0.0 == rTransCandidate.getTransparence())
1222 {
1223 // not transparent at all, use content
1224 process(rTransCandidate.getChildren());
1225 return;
1226 }
1227
1228 if (rTransCandidate.getTransparence() < 0.0 || rTransCandidate.getTransparence() > 1.0)
1229 {
1230 // invalid transparence, done
1231 return;
1232 }
1233
1234 // calculate visible range
1235 basegfx::B2DRange aDiscreteVisibleRange;
1236 calculateDiscreteVisibleRange(aDiscreteVisibleRange,
1237 rTransCandidate.getChildren().getB2DRange(getViewInformation2D()),
1239
1240 if (aDiscreteVisibleRange.isEmpty())
1241 {
1242 // not visible, done
1243 return;
1244 }
1245
1246 bool bDone(false);
1247 sal::systools::COMReference<ID2D1Layer> pLayer;
1248 const HRESULT hr(getRenderTarget()->CreateLayer(nullptr, &pLayer));
1249
1250 if (SUCCEEDED(hr) && pLayer)
1251 {
1252 // need to set correct transform for Layer initialization, we work
1253 // in discrete device coordinates
1254 getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
1255 getRenderTarget()->PushLayer(
1256 D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
1257 D2D1::IdentityMatrix(),
1258 1.0 - rTransCandidate.getTransparence()), // opacity
1259 pLayer);
1260 process(rTransCandidate.getChildren());
1261 getRenderTarget()->PopLayer();
1262 bDone = true;
1263 }
1264
1265 if (!bDone)
1266 increaseError();
1267}
1268
1270 const primitive2d::MaskPrimitive2D& rMaskCandidate)
1271{
1272 if (rMaskCandidate.getChildren().empty())
1273 {
1274 // no content, done
1275 return;
1276 }
1277
1278 basegfx::B2DPolyPolygon aMask(rMaskCandidate.getMask());
1279
1280 if (!aMask.count())
1281 {
1282 // no mask (so nothing inside), done
1283 return;
1284 }
1285
1286 // calculate visible range
1287 basegfx::B2DRange aDiscreteVisibleRange;
1288 calculateDiscreteVisibleRange(aDiscreteVisibleRange, aMask.getB2DRange(),
1290
1291 if (aDiscreteVisibleRange.isEmpty())
1292 {
1293 // not visible, done
1294 return;
1295 }
1296
1297 bool bDone(false);
1298 std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1MaskGeometry(
1299 getOrCreateFillGeometry(rMaskCandidate.getMask()));
1300
1301 if (pSystemDependentData_ID2D1MaskGeometry)
1302 {
1303 sal::systools::COMReference<ID2D1TransformedGeometry> pTransformedMaskGeometry;
1304 const basegfx::B2DHomMatrix& rObjectToView(
1305 getViewInformation2D().getObjectToViewTransformation());
1306 HRESULT hr(aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateTransformedGeometry(
1307 pSystemDependentData_ID2D1MaskGeometry->getID2D1PathGeometry(),
1308 D2D1::Matrix3x2F(rObjectToView.a(), rObjectToView.b(), rObjectToView.c(),
1309 rObjectToView.d(), rObjectToView.e(), rObjectToView.f()),
1310 &pTransformedMaskGeometry));
1311
1312 if (SUCCEEDED(hr) && pTransformedMaskGeometry)
1313 {
1314 sal::systools::COMReference<ID2D1Layer> pLayer;
1315 hr = getRenderTarget()->CreateLayer(nullptr, &pLayer);
1316
1317 if (SUCCEEDED(hr) && pLayer)
1318 {
1319 // need to set correct transform for Layer initialization, we work
1320 // in discrete device coordinates
1321 getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
1322 getRenderTarget()->PushLayer(
1323 D2D1::LayerParameters(D2D1::InfiniteRect(), pTransformedMaskGeometry), pLayer);
1324 process(rMaskCandidate.getChildren());
1325 getRenderTarget()->PopLayer();
1326 bDone = true;
1327 }
1328 }
1329 }
1330
1331 if (!bDone)
1332 increaseError();
1333}
1334
1336 const primitive2d::PointArrayPrimitive2D& rPointArrayCandidate)
1337{
1338 const std::vector<basegfx::B2DPoint>& rPositions(rPointArrayCandidate.getPositions());
1339
1340 if (rPositions.empty())
1341 {
1342 // no geometry, done
1343 return;
1344 }
1345
1346 const basegfx::BColor aPointColor(
1347 maBColorModifierStack.getModifiedColor(rPointArrayCandidate.getRGBColor()));
1348 sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush;
1349 D2D1::ColorF aD2DColor(aPointColor.getRed(), aPointColor.getGreen(), aPointColor.getBlue());
1350 const HRESULT hr(getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush));
1351 bool bDone(false);
1352
1353 if (SUCCEEDED(hr) && pColorBrush)
1354 {
1355 getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
1356
1357 // To really paint a single pixel I found nothing better than
1358 // switch off AA and draw a pixel-aligned rectangle
1359 const D2D1_ANTIALIAS_MODE aOldAAMode(getRenderTarget()->GetAntialiasMode());
1360 getRenderTarget()->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
1361
1362 for (auto const& pos : rPositions)
1363 {
1364 const basegfx::B2DPoint aDiscretePos(
1365 getViewInformation2D().getObjectToViewTransformation() * pos);
1366 const double fX(ceil(aDiscretePos.getX()));
1367 const double fY(ceil(aDiscretePos.getY()));
1368 const D2D1_RECT_F rect = { FLOAT(fX), FLOAT(fY), FLOAT(fX), FLOAT(fY) };
1369
1370 getRenderTarget()->DrawRectangle(&rect, pColorBrush);
1371 }
1372
1373 getRenderTarget()->SetAntialiasMode(aOldAAMode);
1374 bDone = true;
1375 }
1376
1377 if (!bDone)
1378 increaseError();
1379}
1380
1382 const primitive2d::MarkerArrayPrimitive2D& rMarkerArrayCandidate)
1383{
1384 const std::vector<basegfx::B2DPoint>& rPositions(rMarkerArrayCandidate.getPositions());
1385
1386 if (rPositions.empty())
1387 {
1388 // no geometry, done
1389 return;
1390 }
1391
1392 const BitmapEx& rMarker(rMarkerArrayCandidate.getMarker());
1393
1394 if (rMarker.IsEmpty())
1395 {
1396 // no marker defined, done
1397 return;
1398 }
1399
1400 sal::systools::COMReference<ID2D1Bitmap> pD2DBitmap(
1401 getOrCreateB2DBitmap(getRenderTarget(), rMarker));
1402 bool bDone(false);
1403
1404 if (pD2DBitmap)
1405 {
1406 getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
1407 const Size& rSizePixel(rMarker.GetSizePixel());
1408 const tools::Long nMiX((rSizePixel.Width() / 2) + 1);
1409 const tools::Long nMiY((rSizePixel.Height() / 2) + 1);
1410 const tools::Long nPlX(rSizePixel.Width() - nMiX);
1411 const tools::Long nPlY(rSizePixel.Height() - nMiY);
1412
1413 // draw with non-AA to show unhampered, clear, non-scaled marker
1414 const D2D1_ANTIALIAS_MODE aOldAAMode(getRenderTarget()->GetAntialiasMode());
1415 getRenderTarget()->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
1416
1417 for (auto const& pos : rPositions)
1418 {
1419 const basegfx::B2DPoint aDiscretePos(
1420 getViewInformation2D().getObjectToViewTransformation() * pos);
1421 const double fX(ceil(aDiscretePos.getX()));
1422 const double fY(ceil(aDiscretePos.getY()));
1423 const D2D1_RECT_F rect
1424 = { FLOAT(fX - nMiX), FLOAT(fY - nMiY), FLOAT(fX + nPlX), FLOAT(fY + nPlY) };
1425
1426 getRenderTarget()->DrawBitmap(pD2DBitmap, &rect);
1427 }
1428
1429 getRenderTarget()->SetAntialiasMode(aOldAAMode);
1430 bDone = true;
1431 }
1432
1433 if (!bDone)
1434 increaseError();
1435}
1436
1438 const primitive2d::BackgroundColorPrimitive2D& rBackgroundColorCandidate)
1439{
1440 // check for allowed range [0.0 .. 1.0[
1441 if (rBackgroundColorCandidate.getTransparency() < 0.0
1442 || rBackgroundColorCandidate.getTransparency() >= 1.0)
1443 return;
1444
1445 const D2D1::ColorF aD2DColor(rBackgroundColorCandidate.getBColor().getRed(),
1446 rBackgroundColorCandidate.getBColor().getGreen(),
1447 rBackgroundColorCandidate.getBColor().getBlue(),
1448 1.0 - rBackgroundColorCandidate.getTransparency());
1449
1450 getRenderTarget()->Clear(aD2DColor);
1451}
1452
1454 const primitive2d::ModifiedColorPrimitive2D& rModifiedCandidate)
1455{
1456 if (!rModifiedCandidate.getChildren().empty())
1457 {
1458 maBColorModifierStack.push(rModifiedCandidate.getColorModifier());
1459 process(rModifiedCandidate.getChildren());
1461 }
1462}
1463
1465 const primitive2d::TransformPrimitive2D& rTransformCandidate)
1466{
1467 // remember current transformation and ViewInformation
1468 const geometry::ViewInformation2D aLastViewInformation2D(getViewInformation2D());
1469
1470 // create new transformations for local ViewInformation2D
1472 aViewInformation2D.setObjectTransformation(getViewInformation2D().getObjectTransformation()
1473 * rTransformCandidate.getTransformation());
1474 updateViewInformation(aViewInformation2D);
1475
1476 // process content
1477 process(rTransformCandidate.getChildren());
1478
1479 // restore transformations
1480 updateViewInformation(aLastViewInformation2D);
1481}
1482
1484 const primitive2d::PolygonStrokePrimitive2D& rPolygonStrokeCandidate)
1485{
1486 const basegfx::B2DPolygon& rPolygon(rPolygonStrokeCandidate.getB2DPolygon());
1487 const attribute::LineAttribute& rLineAttribute(rPolygonStrokeCandidate.getLineAttribute());
1488
1489 if (!rPolygon.count() || rLineAttribute.getWidth() < 0.0)
1490 {
1491 // no geometry, done
1492 return;
1493 }
1494
1495 // get some values early that might be used for decisions
1496 const bool bHairline(0.0 == rLineAttribute.getWidth());
1497 const basegfx::B2DHomMatrix& rObjectToView(
1498 getViewInformation2D().getObjectToViewTransformation());
1499 const double fDiscreteLineWidth(
1500 bHairline
1501 ? 1.0
1502 : (rObjectToView * basegfx::B2DVector(rLineAttribute.getWidth(), 0.0)).getLength());
1503
1504 // Here for every combination which the system-specific implementation is not
1505 // capable of visualizing, use the (for decomposable Primitives always possible)
1506 // fallback to the decomposition.
1507 if (basegfx::B2DLineJoin::NONE == rLineAttribute.getLineJoin() && fDiscreteLineWidth > 1.5)
1508 {
1509 // basegfx::B2DLineJoin::NONE is special for our office, no other GraphicSystem
1510 // knows that (so far), so fallback to decomposition. This is only needed if
1511 // LineJoin will be used, so also check for discrete LineWidth before falling back
1512 process(rPolygonStrokeCandidate);
1513 return;
1514 }
1515
1516 // This is a method every system-specific implementation of a decomposable Primitive
1517 // can use to allow simple optical control of paint implementation:
1518 // Create a copy, e.g. change color to 'red' as here and paint before the system
1519 // paints it using the decomposition. That way you can - if active - directly
1520 // optically compare if the system-specific solution is geometrically identical to
1521 // the decomposition (which defines our interpretation that we need to visualize).
1522 // Look below in the impl for bRenderDecomposeForCompareInRed to see that in that case
1523 // we create a half-transparent paint to better support visual control
1524 static bool bRenderDecomposeForCompareInRed(false);
1525
1526 if (bRenderDecomposeForCompareInRed)
1527 {
1528 const attribute::LineAttribute aRed(
1529 basegfx::BColor(1.0, 0.0, 0.0), rLineAttribute.getWidth(), rLineAttribute.getLineJoin(),
1530 rLineAttribute.getLineCap(), rLineAttribute.getMiterMinimumAngle());
1533 rPolygonStrokeCandidate.getB2DPolygon(), aRed,
1534 rPolygonStrokeCandidate.getStrokeAttribute()));
1535 process(*aCopy);
1536 }
1537
1538 bool bDone(false);
1539 std::shared_ptr<SystemDependentData_ID2D1PathGeometry> pSystemDependentData_ID2D1PathGeometry(
1540 getOrCreatePathGeometry(rPolygon, getViewInformation2D()));
1541
1542 if (pSystemDependentData_ID2D1PathGeometry)
1543 {
1544 sal::systools::COMReference<ID2D1TransformedGeometry> pTransformedGeometry;
1545 const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
1546 HRESULT hr(aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateTransformedGeometry(
1547 pSystemDependentData_ID2D1PathGeometry->getID2D1PathGeometry(),
1548 D2D1::Matrix3x2F(rObjectToView.a(), rObjectToView.b(), rObjectToView.c(),
1549 rObjectToView.d(), rObjectToView.e() + fAAOffset,
1550 rObjectToView.f() + fAAOffset),
1551 &pTransformedGeometry));
1552
1553 if (SUCCEEDED(hr) && pTransformedGeometry)
1554 {
1555 const basegfx::BColor aLineColor(
1557 D2D1::ColorF aD2DColor(aLineColor.getRed(), aLineColor.getGreen(),
1558 aLineColor.getBlue());
1559
1560 if (bRenderDecomposeForCompareInRed)
1561 {
1562 aD2DColor.a = 0.5;
1563 }
1564
1565 sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush;
1566 hr = getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush);
1567
1568 if (SUCCEEDED(hr) && pColorBrush)
1569 {
1570 sal::systools::COMReference<ID2D1StrokeStyle> pStrokeStyle;
1571 D2D1_CAP_STYLE aCapStyle(D2D1_CAP_STYLE_FLAT);
1572 D2D1_LINE_JOIN aLineJoin(D2D1_LINE_JOIN_MITER);
1573 const attribute::StrokeAttribute& rStrokeAttribute(
1574 rPolygonStrokeCandidate.getStrokeAttribute());
1575 const bool bDashUsed(!rStrokeAttribute.isDefault()
1576 && !rStrokeAttribute.getDotDashArray().empty()
1577 && 0.0 < rStrokeAttribute.getFullDotDashLen());
1578 D2D1_DASH_STYLE aDashStyle(bDashUsed ? D2D1_DASH_STYLE_CUSTOM
1579 : D2D1_DASH_STYLE_SOLID);
1580 std::vector<float> dashes;
1581 float miterLimit(1.0);
1582
1583 switch (rLineAttribute.getLineCap())
1584 {
1585 case css::drawing::LineCap_ROUND:
1586 aCapStyle = D2D1_CAP_STYLE_ROUND;
1587 break;
1588 case css::drawing::LineCap_SQUARE:
1589 aCapStyle = D2D1_CAP_STYLE_SQUARE;
1590 break;
1591 default:
1592 break;
1593 }
1594
1595 switch (rLineAttribute.getLineJoin())
1596 {
1598 break;
1600 aLineJoin = D2D1_LINE_JOIN_BEVEL;
1601 break;
1603 {
1604 // for basegfx::B2DLineJoin::Miter there are two problems:
1605 // (1) MS uses D2D1_LINE_JOIN_MITER which handles the cut-off when MiterLimit is hit not by
1606 // fallback to Bevel, but by cutting miter geometry at the defined distance. That is
1607 // nice, but not what we need or is the standard for other graphic systems. Luckily there
1608 // is also D2D1_LINE_JOIN_MITER_OR_BEVEL and (after some search) the page
1609 // https://learn.microsoft.com/en-us/windows/win32/api/d2d1/ne-d2d1-d2d1_line_join
1610 // which gives some explanation, so that is what we need to use here.
1611 // (2) Instead of using an angle in radians (15 deg default) MS uses
1612 // "miterLimit is relative to 1/2 LineWidth", so a length. After some experimenting
1613 // it shows that the (better understandable) angle has to be converted to the length
1614 // that a miter prolongation would have at that angle, so use some trigonometry.
1615 // Unfortunately there is also some'precision' problem (probably), so I had to
1616 // experimentally come to a correction value around 0.9925. Since that seems to
1617 // be no obvious numerical value involved somehow (and as long as I find no other
1618 // explanation) I will have to use that.
1619 // NOTE: To find that correction value I usd that handy bRenderDecomposeForCompareInRed
1620 // and changes in debugger - as work tipp
1621 // With both done I can use Direct2D for Miter completely - what is good for speed.
1622 aLineJoin = D2D1_LINE_JOIN_MITER_OR_BEVEL;
1623
1624 // snap absolute value of angle in radians to [0.0 .. PI]
1625 double fVal(::basegfx::snapToZeroRange(
1626 fabs(rLineAttribute.getMiterMinimumAngle()), M_PI));
1627
1628 // cut at 0.0 and PI since sin would be zero ('endless' miter)
1629 const double fSmallValue(M_PI * 0.0000001);
1630 fVal = std::max(fSmallValue, fVal);
1631 fVal = std::min(M_PI - fSmallValue, fVal);
1632
1633 // get relative length
1634 fVal = 1.0 / sin(fVal);
1635
1636 // use for miterLimit, we need factor 2.0 (relative to double LineWidth)
1637 // and the correction mentioned in (2) above
1638 const double fCorrector(2.0 * 0.9925);
1639
1640 miterLimit = fVal * fCorrector;
1641 break;
1642 }
1644 aLineJoin = D2D1_LINE_JOIN_ROUND;
1645 break;
1646 default:
1647 break;
1648 }
1649
1650 if (bDashUsed)
1651 {
1652 // dashes need to be discrete and relative to LineWidth
1653 for (auto& value : rStrokeAttribute.getDotDashArray())
1654 {
1655 dashes.push_back(
1656 (rObjectToView * basegfx::B2DVector(value, 0.0)).getLength()
1657 / fDiscreteLineWidth);
1658 }
1659 }
1660
1661 hr = aID2D1GlobalFactoryProvider.getID2D1Factory()->CreateStrokeStyle(
1662 D2D1::StrokeStyleProperties(aCapStyle, // startCap
1663 aCapStyle, // endCap
1664 aCapStyle, // dashCap
1665 aLineJoin, // lineJoin
1666 miterLimit, // miterLimit
1667 aDashStyle, // dashStyle
1668 0.0f), // dashOffset
1669 bDashUsed ? dashes.data() : nullptr, bDashUsed ? dashes.size() : 0,
1670 &pStrokeStyle);
1671
1672 if (SUCCEEDED(hr) && pStrokeStyle)
1673 {
1674 getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
1675 getRenderTarget()->DrawGeometry(
1676 pTransformedGeometry, pColorBrush,
1677 // TODO: Hairline LineWidth, see comment at processPolygonHairlinePrimitive2D
1678 bHairline ? 1.44 : fDiscreteLineWidth, pStrokeStyle);
1679 bDone = true;
1680 }
1681 }
1682 }
1683 }
1684
1685 if (!bDone)
1686 {
1687 // fallback to decomposition
1688 process(rPolygonStrokeCandidate);
1689 }
1690}
1691
1693 const primitive2d::LineRectanglePrimitive2D& rLineRectanglePrimitive2D)
1694{
1695 if (rLineRectanglePrimitive2D.getB2DRange().isEmpty())
1696 {
1697 // no geometry, done
1698 return;
1699 }
1700
1701 const basegfx::BColor aHairlineColor(
1702 maBColorModifierStack.getModifiedColor(rLineRectanglePrimitive2D.getBColor()));
1703 const D2D1::ColorF aD2DColor(aHairlineColor.getRed(), aHairlineColor.getGreen(),
1704 aHairlineColor.getBlue());
1705 sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush;
1706 const HRESULT hr(getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush));
1707 bool bDone(false);
1708
1709 if (SUCCEEDED(hr) && pColorBrush)
1710 {
1711 const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
1712 const basegfx::B2DHomMatrix aLocalTransform(
1713 getViewInformation2D().getObjectToViewTransformation());
1714 getRenderTarget()->SetTransform(D2D1::Matrix3x2F(
1715 aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(), aLocalTransform.d(),
1716 aLocalTransform.e() + fAAOffset, aLocalTransform.f() + fAAOffset));
1717 const basegfx::B2DRange& rRange(rLineRectanglePrimitive2D.getB2DRange());
1718 const D2D1_RECT_F rect = { FLOAT(rRange.getMinX()), FLOAT(rRange.getMinY()),
1719 FLOAT(rRange.getMaxX()), FLOAT(rRange.getMaxY()) };
1720 const double fDiscreteLineWidth(
1721 (getViewInformation2D().getInverseObjectToViewTransformation()
1722 * basegfx::B2DVector(1.44, 0.0))
1723 .getLength());
1724
1725 getRenderTarget()->DrawRectangle(&rect, pColorBrush, fDiscreteLineWidth);
1726 bDone = true;
1727 }
1728
1729 if (!bDone)
1730 increaseError();
1731}
1732
1734 const primitive2d::FilledRectanglePrimitive2D& rFilledRectanglePrimitive2D)
1735{
1736 if (rFilledRectanglePrimitive2D.getB2DRange().isEmpty())
1737 {
1738 // no geometry, done
1739 return;
1740 }
1741
1742 const basegfx::BColor aFillColor(
1743 maBColorModifierStack.getModifiedColor(rFilledRectanglePrimitive2D.getBColor()));
1744 const D2D1::ColorF aD2DColor(aFillColor.getRed(), aFillColor.getGreen(), aFillColor.getBlue());
1745 sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush;
1746 const HRESULT hr(getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush));
1747 bool bDone(false);
1748
1749 if (SUCCEEDED(hr) && pColorBrush)
1750 {
1751 const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
1752 const basegfx::B2DHomMatrix aLocalTransform(
1753 getViewInformation2D().getObjectToViewTransformation());
1754 getRenderTarget()->SetTransform(D2D1::Matrix3x2F(
1755 aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(), aLocalTransform.d(),
1756 aLocalTransform.e() + fAAOffset, aLocalTransform.f() + fAAOffset));
1757 const basegfx::B2DRange& rRange(rFilledRectanglePrimitive2D.getB2DRange());
1758 const D2D1_RECT_F rect = { FLOAT(rRange.getMinX()), FLOAT(rRange.getMinY()),
1759 FLOAT(rRange.getMaxX()), FLOAT(rRange.getMaxY()) };
1760
1761 getRenderTarget()->FillRectangle(&rect, pColorBrush);
1762 bDone = true;
1763 }
1764
1765 if (!bDone)
1766 increaseError();
1767}
1768
1770 const primitive2d::SingleLinePrimitive2D& rSingleLinePrimitive2D)
1771{
1772 const basegfx::BColor aLineColor(
1773 maBColorModifierStack.getModifiedColor(rSingleLinePrimitive2D.getBColor()));
1774 const D2D1::ColorF aD2DColor(aLineColor.getRed(), aLineColor.getGreen(), aLineColor.getBlue());
1775 sal::systools::COMReference<ID2D1SolidColorBrush> pColorBrush;
1776 const HRESULT hr(getRenderTarget()->CreateSolidColorBrush(aD2DColor, &pColorBrush));
1777 bool bDone(false);
1778
1779 if (SUCCEEDED(hr) && pColorBrush)
1780 {
1781 const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
1782 basegfx::B2DHomMatrix aLocalTransform(
1783 getViewInformation2D().getObjectToViewTransformation());
1784 const basegfx::B2DPoint aStart(aLocalTransform * rSingleLinePrimitive2D.getStart());
1785 const basegfx::B2DPoint aEnd(aLocalTransform * rSingleLinePrimitive2D.getEnd());
1786
1787 getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
1788 const D2D1_POINT_2F aD2D1Start
1789 = { FLOAT(aStart.getX() + fAAOffset), FLOAT(aStart.getY() + fAAOffset) };
1790 const D2D1_POINT_2F aD2D1End
1791 = { FLOAT(aEnd.getX() + fAAOffset), FLOAT(aEnd.getY() + fAAOffset) };
1792
1793 getRenderTarget()->DrawLine(aD2D1Start, aD2D1End, pColorBrush, 1.44f);
1794 bDone = true;
1795 }
1796
1797 if (!bDone)
1798 increaseError();
1799}
1800
1802 const primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D)
1803{
1804 BitmapEx aPreparedBitmap;
1805 basegfx::B2DRange aFillUnitRange(rFillGraphicPrimitive2D.getFillGraphic().getGraphicRange());
1806 static double fBigDiscreteArea(300.0 * 300.0);
1807
1808 // use tooling to do various checks and prepare tiled rendering, see
1809 // description of method, parameters and return value there
1810 if (!prepareBitmapForDirectRender(rFillGraphicPrimitive2D, getViewInformation2D(),
1811 aPreparedBitmap, aFillUnitRange, fBigDiscreteArea))
1812 {
1813 // no output needed, done
1814 return;
1815 }
1816
1817 if (aPreparedBitmap.IsEmpty())
1818 {
1819 // output needed and Bitmap data empty, so no bitmap data based
1820 // tiled rendering is suggested. Use fallback for paint (decomposition)
1821 process(rFillGraphicPrimitive2D);
1822 return;
1823 }
1824
1825 // render tiled using the prepared Bitmap data
1827 {
1828 // need to apply ColorModifier to Bitmap data
1829 aPreparedBitmap = aPreparedBitmap.ModifyBitmapEx(maBColorModifierStack);
1830
1831 if (aPreparedBitmap.IsEmpty())
1832 {
1833 // color gets completely replaced, get it (any input works)
1834 const basegfx::BColor aModifiedColor(
1836
1837 // use unit geometry as fallback object geometry. Do *not*
1838 // transform, the below used method will use the already
1839 // correctly initialized local ViewInformation
1841
1842 // what we still need to apply is the object transform from the
1843 // local primitive, that is not part of DisplayInfo yet
1844 aPolygon.transform(rFillGraphicPrimitive2D.getTransformation());
1845
1848 aModifiedColor));
1849
1850 // draw as colored Polygon, done
1852 return;
1853 }
1854 }
1855
1856 bool bDone(false);
1857 sal::systools::COMReference<ID2D1Bitmap> pD2DBitmap(
1858 getOrCreateB2DBitmap(getRenderTarget(), aPreparedBitmap));
1859
1860 if (pD2DBitmap)
1861 {
1862 sal::systools::COMReference<ID2D1BitmapBrush> pBitmapBrush;
1863 const HRESULT hr(getRenderTarget()->CreateBitmapBrush(pD2DBitmap, &pBitmapBrush));
1864
1865 if (SUCCEEDED(hr) && pBitmapBrush)
1866 {
1867 // set extended to repeat/wrap AKA tiling
1868 pBitmapBrush->SetExtendModeX(D2D1_EXTEND_MODE_WRAP);
1869 pBitmapBrush->SetExtendModeY(D2D1_EXTEND_MODE_WRAP);
1870
1871 // set interpolation mode
1872 // NOTE: This uses D2D1_BITMAP_INTERPOLATION_MODE, but there seem to be
1873 // advanced modes when using D2D1_INTERPOLATION_MODE, but that needs
1874 // D2D1_BITMAP_BRUSH_PROPERTIES1 and ID2D1BitmapBrush1
1875 sal::systools::COMReference<ID2D1BitmapBrush1> pBrush1;
1876 pBitmapBrush->QueryInterface(__uuidof(ID2D1BitmapBrush1),
1877 reinterpret_cast<void**>(&pBrush1));
1878
1879 if (pBrush1)
1880 {
1881 pBrush1->SetInterpolationMode1(D2D1_INTERPOLATION_MODE_MULTI_SAMPLE_LINEAR);
1882 }
1883 else
1884 {
1885 pBitmapBrush->SetInterpolationMode(D2D1_BITMAP_INTERPOLATION_MODE_LINEAR);
1886 }
1887
1888 // set BitmapBrush transformation relative to it's PixelSize and
1889 // the used FillUnitRange. Since we use unit coordinates here this
1890 // is pretty simple
1891 const D2D1_SIZE_U aBMSPixel(pD2DBitmap->GetPixelSize());
1892 const double fScaleX((aFillUnitRange.getMaxX() - aFillUnitRange.getMinX())
1893 / aBMSPixel.width);
1894 const double fScaleY((aFillUnitRange.getMaxY() - aFillUnitRange.getMinY())
1895 / aBMSPixel.height);
1896 const D2D1_MATRIX_3X2_F aBTrans(D2D1::Matrix3x2F(
1897 fScaleX, 0.0, 0.0, fScaleY, aFillUnitRange.getMinX(), aFillUnitRange.getMinY()));
1898 pBitmapBrush->SetTransform(&aBTrans);
1899
1900 // set transform to ObjectToWorld to be able to paint in unit coordinates, so
1901 // evtl. shear/rotate in that transform is used and does not influence the
1902 // orthogonal and unit-oriented brush handling
1903 const double fAAOffset(getViewInformation2D().getUseAntiAliasing() ? 0.5 : 0.0);
1904 const basegfx::B2DHomMatrix aLocalTransform(
1905 getViewInformation2D().getObjectToViewTransformation()
1906 * rFillGraphicPrimitive2D.getTransformation());
1907 getRenderTarget()->SetTransform(D2D1::Matrix3x2F(
1908 aLocalTransform.a(), aLocalTransform.b(), aLocalTransform.c(), aLocalTransform.d(),
1909 aLocalTransform.e() + fAAOffset, aLocalTransform.f() + fAAOffset));
1910
1911 // use unit rectangle, transformation is already set to include ObjectToWorld
1912 const D2D1_RECT_F rect = { FLOAT(0.0), FLOAT(0.0), FLOAT(1.0), FLOAT(1.0) };
1913
1914 // draw as unit rectangle as brush filled rectangle
1915 getRenderTarget()->FillRectangle(&rect, pBitmapBrush);
1916 bDone = true;
1917 }
1918 }
1919
1920 if (!bDone)
1921 increaseError();
1922}
1923
1925 const primitive2d::FillGradientPrimitive2D& rFillGradientPrimitive2D)
1926{
1927 // draw all-covering initial BG polygon 1st
1931 basegfx::utils::createPolygonFromRect(rFillGradientPrimitive2D.getOutputRange())),
1932 rFillGradientPrimitive2D.getOuterColor()));
1933
1934 if (bDone)
1935 {
1936 const basegfx::B2DPolyPolygon aForm(rFillGradientPrimitive2D.getUnitPolygon());
1937
1938 // paint solid fill steps by providing callback as lambda
1939 auto aCallback([&aForm, &bDone, this](const basegfx::B2DHomMatrix& rMatrix,
1940 const basegfx::BColor& rColor) {
1941 if (bDone)
1942 {
1943 bDone = drawPolyPolygonColorTransformed(rMatrix, aForm, rColor);
1944 }
1945 });
1946
1947 // call value generator to trigger callbacks
1948 rFillGradientPrimitive2D.generateMatricesAndColors(aCallback);
1949 }
1950
1951 if (!bDone)
1952 increaseError();
1953}
1954
1956 const primitive2d::InvertPrimitive2D& rInvertPrimitive2D)
1957{
1958 if (rInvertPrimitive2D.getChildren().empty())
1959 {
1960 // no content, done
1961 return;
1962 }
1963
1964 // Try if we can use ID2D1DeviceContext/d2d1_1 by querying for interface.
1965 // Only with ID2D1DeviceContext we can use ::DrawImage which supports
1966 // D2D1_COMPOSITE_MODE_XOR
1967 sal::systools::COMReference<ID2D1DeviceContext> pID2D1DeviceContext;
1968 getRenderTarget()->QueryInterface(__uuidof(ID2D1DeviceContext),
1969 reinterpret_cast<void**>(&pID2D1DeviceContext));
1970
1971 if (!pID2D1DeviceContext)
1972 {
1973 // TODO: We have *no* ID2D1DeviceContext and cannot use D2D1_COMPOSITE_MODE_XOR,
1974 // so there is currently no (simple?) way to solve this, there is no 'Invert' method.
1975 // It may be possible to convert to a WICBitmap (gets read access) and do the invert
1976 // there, but that needs experimenting and is probably not performant - but doable.
1977 increaseError();
1978 return;
1979 }
1980
1981 sal::systools::COMReference<ID2D1Bitmap> pInBetweenResult;
1982 basegfx::B2DRange aDiscreteVisibleRange;
1983
1984 // create in-between result in discrete coordinates, clipped against visible
1985 // part of ViewInformation (if available)
1986 if (!createBitmapSubContent(pInBetweenResult, aDiscreteVisibleRange,
1987 rInvertPrimitive2D.getChildren(), getViewInformation2D(),
1988 getRenderTarget()))
1989 {
1990 // return of false means no display needed, return
1991 return;
1992 }
1993
1994 bool bDone(false);
1995
1996 if (pInBetweenResult)
1997 {
1998 getRenderTarget()->SetTransform(D2D1::Matrix3x2F::Identity());
1999 const D2D1_POINT_2F aTopLeft = { FLOAT(floor(aDiscreteVisibleRange.getMinX())),
2000 FLOAT(floor(aDiscreteVisibleRange.getMinY())) };
2001
2002 pID2D1DeviceContext->DrawImage(pInBetweenResult, aTopLeft, D2D1_INTERPOLATION_MODE_LINEAR,
2003 D2D1_COMPOSITE_MODE_XOR);
2004 bDone = true;
2005 }
2006
2007 if (!bDone)
2008 increaseError();
2009}
2010
2012{
2013 if (0 == mnRecursionCounter)
2014 getRenderTarget()->BeginDraw();
2016
2017 switch (rCandidate.getPrimitive2DID())
2018 {
2019 // Geometry that *has* to be processed
2020 //
2021 // These Primitives have *no* decompose implementation, so these are the basic ones
2022 // Just four to go to make a processor work completely (but not optimized)
2023 // NOTE: This *could* theoretically be reduced to one and all could implement
2024 // a decompose to pixel data, but that seemed not to make sense to me when
2025 // I designed this. Thus these four are the lowest-level best representation
2026 // from my POV
2028 {
2030 static_cast<const primitive2d::BitmapPrimitive2D&>(rCandidate));
2031 break;
2032 }
2034 {
2036 static_cast<const primitive2d::PointArrayPrimitive2D&>(rCandidate));
2037 break;
2038 }
2040 {
2042 static_cast<const primitive2d::PolygonHairlinePrimitive2D&>(rCandidate));
2043 break;
2044 }
2046 {
2048 static_cast<const primitive2d::PolyPolygonColorPrimitive2D&>(rCandidate));
2049 break;
2050 }
2051
2052 // Embedding/groups that *have* to be processed
2053 //
2054 // These represent qualifiers for freely defined content, e.g. making
2055 // any combination of primitives freely transformed or transparent
2056 // NOTE: PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D and
2057 // PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D are pretty much default-
2058 // implementations that can and are re-used in all processors.
2059 // So - with these and PRIMITIVE2D_ID_INVERTPRIMITIVE2D marked to
2060 // be removed in the future - just three really to be implemented
2061 // locally specifically
2063 {
2065 static_cast<const primitive2d::TransparencePrimitive2D&>(rCandidate));
2066 break;
2067 }
2069 {
2070 // We urgently should get rid of XOR paint, modern graphic systems
2071 // allow no read access to the pixel targets, but that's naturally
2072 // a precondition for XOR. While we can do that for the office's
2073 // visualization, we can in principle *not* fully avoid getting
2074 // stuff that needs/defines XOR paint, e.g. EMF/WMF imports, so
2075 // we *have* to support it (for now - sigh)...
2077 static_cast<const primitive2d::InvertPrimitive2D&>(rCandidate));
2078 break;
2079 }
2081 {
2083 static_cast<const primitive2d::MaskPrimitive2D&>(rCandidate));
2084 break;
2085 }
2087 {
2089 static_cast<const primitive2d::ModifiedColorPrimitive2D&>(rCandidate));
2090 break;
2091 }
2093 {
2095 static_cast<const primitive2d::TransformPrimitive2D&>(rCandidate));
2096 break;
2097 }
2098
2099 // Geometry that *may* be processed due to being able to do it better
2100 // then using the decomposition.
2101 // NOTE: In these implementations you could always call what the default
2102 // case below does - call process(rCandidate) to use the decomposition.
2103 // So these impls should only do something if they can do it better/
2104 // faster that the decomposition. So some of them check if they could
2105 // - and if not - use exactly that.
2107 {
2108 // transparence with a fixed alpha for all content, can be done
2109 // significally faster
2111 static_cast<const primitive2d::UnifiedTransparencePrimitive2D&>(rCandidate));
2112 break;
2113 }
2115 {
2116 // can be done simpler and without AA better locally
2118 static_cast<const primitive2d::MarkerArrayPrimitive2D&>(rCandidate));
2119 break;
2120 }
2122 {
2123 // reset to a color, can be done more effectively locally, would
2124 // else decompose to a polygon fill
2126 static_cast<const primitive2d::BackgroundColorPrimitive2D&>(rCandidate));
2127 break;
2128 }
2130 {
2131 // fat and stroked lines - much better doable locally, would decompose
2132 // to the full line geometry creation (tessellation)
2134 static_cast<const primitive2d::PolygonStrokePrimitive2D&>(rCandidate));
2135 break;
2136 }
2138 {
2139 // simple primitive to support future fast callbacks from OutputDevice
2140 // (see 'Example POC' in Gerrit), decomposes to polygon primitive
2142 static_cast<const primitive2d::LineRectanglePrimitive2D&>(rCandidate));
2143 break;
2144 }
2146 {
2147 // simple primitive to support future fast callbacks from OutputDevice
2148 // (see 'Example POC' in Gerrit), decomposes to filled polygon primitive
2150 static_cast<const primitive2d::FilledRectanglePrimitive2D&>(rCandidate));
2151 break;
2152 }
2154 {
2155 // simple primitive to support future fast callbacks from OutputDevice
2156 // (see 'Example POC' in Gerrit), decomposes to polygon primitive
2158 static_cast<const primitive2d::SingleLinePrimitive2D&>(rCandidate));
2159 break;
2160 }
2162 {
2164 static_cast<const primitive2d::FillGraphicPrimitive2D&>(rCandidate));
2165 break;
2166 }
2168 {
2170 static_cast<const primitive2d::FillGradientPrimitive2D&>(rCandidate));
2171 break;
2172 }
2173
2174 // continue with decompose as fallback
2175 default:
2176 {
2177 SAL_INFO("drawinglayer", "default case for " << drawinglayer::primitive2d::idToString(
2178 rCandidate.getPrimitive2DID()));
2179 // process recursively
2180 process(rCandidate);
2181 break;
2182 }
2183 }
2184
2186 if (0 == mnRecursionCounter)
2187 getRenderTarget()->EndDraw();
2188}
2189} // end of namespace
2190
2191/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Bitmap const & GetBitmap() const
bool IsEmpty() const
const AlphaMask & GetAlphaMask() const
bool IsAlpha() const
bool IsEmpty() const
Bitmap GetBitmap(Color aTransparentReplaceColor) const
BitmapEx ModifyBitmapEx(const basegfx::BColorModifierStack &rBColorModifierStack) const
const Size & GetSizePixel() const
const std::shared_ptr< SalBitmap > & ImplGetSalBitmap() const
Size GetSizePixel() const
BitmapReadAccess * AcquireReadAccess()
const basegfx::SystemDependentDataHolder * accessSystemDependentDataHolder() const
bool IsEmpty() const
constexpr tools::Long Height() const
constexpr tools::Long Width() const
const B2DPoint & getControlPointB() const
const B2DPoint & getEndPoint() const
const B2DPoint & getControlPointA() const
double c() const
double e() const
double a() const
double f() const
double d() const
double b() const
B2DPolygon const & getB2DPolygon(sal_uInt32 nIndex) const
B2DRange getB2DRange() const
std::shared_ptr< T > addOrReplaceSystemDependentData(Args &&... args) const
std::shared_ptr< T > getSystemDependentData() const
sal_uInt32 count() const
std::shared_ptr< T > getSystemDependentData() const
bool isClosed() const
basegfx::B2DPoint const & getB2DPoint(sal_uInt32 nIndex) const
void transform(const basegfx::B2DHomMatrix &rMatrix)
void getBezierSegment(sal_uInt32 nIndex, B2DCubicBezier &rTarget) const
std::shared_ptr< T > addOrReplaceSystemDependentData(Args &&... args) const
sal_uInt32 count() const
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
static BASEGFX_DLLPUBLIC const B2DRange & getUnitB2DRange()
void push(const BColorModifierSharedPtr &rNew)
::basegfx::BColor getModifiedColor(const ::basegfx::BColor &rSource) const
sal_uInt32 count() const
double getBlue() const
double getRed() const
double getGreen() const
TYPE getMaxX() const
TYPE getWidth() const
TYPE getMinX() const
TYPE getMinY() const
TYPE getMaxY() const
bool isEmpty() const
TYPE getHeight() const
virtual sal_Int64 estimateUsageInBytes() const
TYPE getX() const
TYPE getY() const
const basegfx::B2DRange & getGraphicRange() const
basegfx::B2DLineJoin getLineJoin() const
css::drawing::LineCap getLineCap() const
const basegfx::BColor & getColor() const
const ::std::vector< double > & getDotDashArray() const
const basegfx::B2DHomMatrix & getInverseObjectToViewTransformation() const
void setObjectTransformation(const basegfx::B2DHomMatrix &rNew)
bool getPixelSnapHairline() const
Determine if to use PixelSnapHairline on target pixel device, PropertyName is 'PixelSnapHairline'.
bool getUseAntiAliasing() const
Determine if to use AntiAliasing on target pixel device, PropertyName is 'UseAntiAliasing'.
const basegfx::B2DHomMatrix & getViewTransformation() const
const basegfx::B2DHomMatrix & getObjectToViewTransformation() const
On-demand prepared Object to View transformation and its inverse for convenience.
const basegfx::BColor & getBColor() const
data read access
virtual sal_uInt32 getPrimitive2DID() const =0
provide unique ID for fast identifying of known primitive implementations in renderers.
const basegfx::B2DHomMatrix & getTransform() const
const BitmapEx & getBitmap() const
data read access
basegfx::B2DPolygon getUnitPolygon() const
helpers that support e.g. direct paint/geometry creation
const basegfx::B2DRange & getOutputRange() const
data read access
void generateMatricesAndColors(std::function< void(const basegfx::B2DHomMatrix &rMatrix, const basegfx::BColor &rColor)> aCallback) const
const attribute::FillGraphicAttribute & getFillGraphic() const
const basegfx::B2DHomMatrix & getTransformation() const
data read access
const basegfx::B2DRange & getB2DRange() const
data read access
const Primitive2DContainer & getChildren() const
data read access
const basegfx::B2DRange & getB2DRange() const
data read access
const std::vector< basegfx::B2DPoint > & getPositions() const
data read access
const basegfx::B2DPolyPolygon & getMask() const
data read access
const basegfx::BColorModifierSharedPtr & getColorModifier() const
data read access
const std::vector< basegfx::B2DPoint > & getPositions() const
data read access
const basegfx::B2DPolyPolygon & getB2DPolyPolygon() const
data read access
const basegfx::B2DPolygon & getB2DPolygon() const
data read access
const attribute::StrokeAttribute & getStrokeAttribute() const
const basegfx::B2DPolygon & getB2DPolygon() const
data read access
const attribute::LineAttribute & getLineAttribute() const
basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &aViewInformation) const
const basegfx::B2DPoint & getStart() const
data read access
const basegfx::B2DHomMatrix & getTransformation() const
data read access
const Primitive2DContainer & getTransparence() const
data read access
void process(const primitive2d::BasePrimitive2D &rCandidate)
void updateViewInformation(const geometry::ViewInformation2D &rViewInformation2D)
const geometry::ViewInformation2D & getViewInformation2D() const
data read access
void processFillGradientPrimitive2D(const primitive2d::FillGradientPrimitive2D &rFillGradientPrimitive2D)
void processBitmapPrimitive2D(const primitive2d::BitmapPrimitive2D &rBitmapCandidate)
void processPolyPolygonColorPrimitive2D(const primitive2d::PolyPolygonColorPrimitive2D &rPolyPolygonColorPrimitive2D)
void processUnifiedTransparencePrimitive2D(const primitive2d::UnifiedTransparencePrimitive2D &rTransCandidate)
void setRenderTarget(const sal::systools::COMReference< ID2D1RenderTarget > &rNewRT)
void processPolygonHairlinePrimitive2D(const primitive2d::PolygonHairlinePrimitive2D &rPolygonHairlinePrimitive2D)
sal::systools::COMReference< ID2D1RenderTarget > & getRenderTarget()
sal::systools::COMReference< ID2D1Bitmap > implCreateAlpha_B2DBitmap(const primitive2d::TransparencePrimitive2D &rTransCandidate, const basegfx::B2DRange &rVisibleRange, D2D1_MATRIX_3X2_F &rMaskScale)
virtual void processBasePrimitive2D(const primitive2d::BasePrimitive2D &rCandidate) override
void processMarkerArrayPrimitive2D(const primitive2d::MarkerArrayPrimitive2D &rMarkerArrayCandidate)
void processLineRectanglePrimitive2D(const primitive2d::LineRectanglePrimitive2D &rLineRectanglePrimitive2D)
void processModifiedColorPrimitive2D(const primitive2d::ModifiedColorPrimitive2D &rModifiedCandidate)
void processPolygonStrokePrimitive2D(const primitive2d::PolygonStrokePrimitive2D &rPolygonStrokeCandidate)
void processTransformPrimitive2D(const primitive2d::TransformPrimitive2D &rTransformCandidate)
void processPointArrayPrimitive2D(const primitive2d::PointArrayPrimitive2D &rPointArrayCandidate)
void processMaskPrimitive2DPixel(const primitive2d::MaskPrimitive2D &rMaskCandidate)
void processInvertPrimitive2D(const primitive2d::InvertPrimitive2D &rInvertPrimitive2D)
bool drawPolyPolygonColorTransformed(const basegfx::B2DHomMatrix &rTansformation, const basegfx::B2DPolyPolygon &rPolyPolygon, const basegfx::BColor &rColor)
sal::systools::COMReference< ID2D1Bitmap > implCreateAlpha_Direct(const primitive2d::TransparencePrimitive2D &rTransCandidate)
void processBackgroundColorPrimitive2D(const primitive2d::BackgroundColorPrimitive2D &rBackgroundColorCandidate)
void processFillGraphicPrimitive2D(const primitive2d::FillGraphicPrimitive2D &rFillGraphicPrimitive2D)
void processSingleLinePrimitive2D(const primitive2d::SingleLinePrimitive2D &rSingleLinePrimitive2D)
void processTransparencePrimitive2D(const primitive2d::TransparencePrimitive2D &rTransCandidate)
void processFilledRectanglePrimitive2D(const primitive2d::FilledRectanglePrimitive2D &rFilledRectanglePrimitive2D)
D2DPixelProcessor2D(const geometry::ViewInformation2D &rViewInformation)
Any value
int nCount
#define PRIMITIVE2D_ID_FILLGRADIENTPRIMITIVE2D
#define PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D
#define PRIMITIVE2D_ID_LINERECTANGLEPRIMITIVE2D
#define PRIMITIVE2D_ID_UNIFIEDTRANSPARENCEPRIMITIVE2D
#define PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D
#define PRIMITIVE2D_ID_POLYPOLYGONCOLORPRIMITIVE2D
#define PRIMITIVE2D_ID_INVERTPRIMITIVE2D
#define PRIMITIVE2D_ID_POLYGONHAIRLINEPRIMITIVE2D
#define PRIMITIVE2D_ID_TRANSPARENCEPRIMITIVE2D
#define PRIMITIVE2D_ID_MODIFIEDCOLORPRIMITIVE2D
#define PRIMITIVE2D_ID_FILLEDRECTANGLEPRIMITIVE2D
#define PRIMITIVE2D_ID_SINGLELINEPRIMITIVE2D
#define PRIMITIVE2D_ID_POINTARRAYPRIMITIVE2D
#define PRIMITIVE2D_ID_MASKPRIMITIVE2D
#define PRIMITIVE2D_ID_POLYGONSTROKEPRIMITIVE2D
#define PRIMITIVE2D_ID_BITMAPPRIMITIVE2D
#define PRIMITIVE2D_ID_BACKGROUNDCOLORPRIMITIVE2D
#define PRIMITIVE2D_ID_MARKERARRAYPRIMITIVE2D
float y
float x
uno_Any a
#define SAL_INFO(area, stream)
constexpr OUStringLiteral aData
double getLength(const B2DPolygon &rCandidate)
B2DPolygon createPolygonFromRect(const B2DRectangle &rRect, double fRadiusX, double fRadiusY)
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
B2DPolygon const & createUnitPolygon()
std::shared_ptr< SystemDependentData > SystemDependentData_SharedPtr
B2IRange fround(const B2DRange &rRange)
OUString idToString(sal_uInt32 nId)
bool prepareBitmapForDirectRender(const drawinglayer::primitive2d::FillGraphicPrimitive2D &rFillGraphicPrimitive2D, const drawinglayer::geometry::ViewInformation2D &rViewInformation2D, BitmapEx &rTarget, basegfx::B2DRange &rFillUnitRange, double fBigDiscreteArea)
helper to process FillGraphicPrimitive2D:
void calculateDiscreteVisibleRange(basegfx::B2DRange &rDiscreteVisibleRange, const basegfx::B2DRange &rContentRange, const drawinglayer::geometry::ViewInformation2D &rViewInformation2D)
helper to calculate a discrete visible range based on a given logic range and a current ViewInformati...
AlphaMask createAlphaMask(drawinglayer::primitive2d::Primitive2DContainer &&rSeq, const geometry::ViewInformation2D &rViewInformation2D, sal_uInt32 nDiscreteWidth, sal_uInt32 nDiscreteHeight, sal_uInt32 nMaxSquarePixels, bool bUseLuminance)
Definition: converters.cxx:139
long Long
return hr
unsigned char sal_uInt8
size_t pos