1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <drawinglayer/processor2d/SDPRProcessor2dTools.hxx>
21 #include <drawinglayer/primitive2d/fillgraphicprimitive2d.hxx>
22 #include <drawinglayer/geometry/viewinformation2d.hxx>
23 #include <vcl/bitmapex.hxx>
24 #include <vcl/graph.hxx>
25 #include <basegfx/range/b2drange.hxx>
28 #include <tools/stream.hxx>
29 #include <vcl/filter/PngImageWriter.hxx>
32 namespace drawinglayer::processor2d
34 void setOffsetXYCreatedBitmap(
35 drawinglayer::primitive2d::FillGraphicPrimitive2D
& rFillGraphicPrimitive2D
,
36 const BitmapEx
& rBitmap
)
38 rFillGraphicPrimitive2D
.impSetOffsetXYCreatedBitmap(rBitmap
);
41 void takeCareOfOffsetXY(
42 const drawinglayer::primitive2d::FillGraphicPrimitive2D
& rFillGraphicPrimitive2D
,
43 BitmapEx
& rTarget
, basegfx::B2DRange
& rFillUnitRange
)
45 const attribute::FillGraphicAttribute
& rFillGraphicAttribute(
46 rFillGraphicPrimitive2D
.getFillGraphic());
47 const bool bOffsetXIsUsed(rFillGraphicAttribute
.getOffsetX() > 0.0
48 && rFillGraphicAttribute
.getOffsetX() < 1.0);
49 const bool bOffsetYIsUsed(rFillGraphicAttribute
.getOffsetY() > 0.0
50 && rFillGraphicAttribute
.getOffsetY() < 1.0);
54 if (rFillGraphicPrimitive2D
.getOffsetXYCreatedBitmap().IsEmpty())
56 const Size
& rSize(rTarget
.GetSizePixel());
57 const tools::Long
w(rSize
.Width());
58 const tools::Long
a(basegfx::fround(w
* (1.0 - rFillGraphicAttribute
.getOffsetX())));
62 const tools::Long
h(rSize
.Height());
63 const tools::Long
b(w
- a
);
64 BitmapEx
aTarget(Size(w
, h
* 2), rTarget
.getPixelFormat());
67 Size(rTarget
.GetPrefSize().Width(), rTarget
.GetPrefSize().Height() * 2));
68 const tools::Rectangle
aSrcDst(Point(), rSize
);
69 aTarget
.CopyPixel(aSrcDst
, // Dst
72 const Size
aSizeA(b
, h
);
73 aTarget
.CopyPixel(tools::Rectangle(Point(0, h
), aSizeA
), // Dst
74 tools::Rectangle(Point(a
, 0), aSizeA
), // Src
76 const Size
aSizeB(a
, h
);
77 aTarget
.CopyPixel(tools::Rectangle(Point(b
, h
), aSizeB
), // Dst
78 tools::Rectangle(Point(), aSizeB
), // Src
81 setOffsetXYCreatedBitmap(
82 const_cast<drawinglayer::primitive2d::FillGraphicPrimitive2D
&>(
83 rFillGraphicPrimitive2D
),
88 if (!rFillGraphicPrimitive2D
.getOffsetXYCreatedBitmap().IsEmpty())
90 rTarget
= rFillGraphicPrimitive2D
.getOffsetXYCreatedBitmap();
91 rFillUnitRange
.expand(basegfx::B2DPoint(
92 rFillUnitRange
.getMinX(), rFillUnitRange
.getMaxY() + rFillUnitRange
.getHeight()));
95 else if (bOffsetYIsUsed
)
97 if (rFillGraphicPrimitive2D
.getOffsetXYCreatedBitmap().IsEmpty())
99 const Size
& rSize(rTarget
.GetSizePixel());
100 const tools::Long
h(rSize
.Height());
101 const tools::Long
a(basegfx::fround(h
* (1.0 - rFillGraphicAttribute
.getOffsetY())));
103 if (0 != a
&& h
!= a
)
105 const tools::Long
w(rSize
.Width());
106 const tools::Long
b(h
- a
);
107 BitmapEx
aTarget(Size(w
* 2, h
), rTarget
.getPixelFormat());
110 Size(rTarget
.GetPrefSize().Width() * 2, rTarget
.GetPrefSize().Height()));
111 const tools::Rectangle
aSrcDst(Point(), rSize
);
112 aTarget
.CopyPixel(aSrcDst
, // Dst
115 const Size
aSizeA(w
, b
);
116 aTarget
.CopyPixel(tools::Rectangle(Point(w
, 0), aSizeA
), // Dst
117 tools::Rectangle(Point(0, a
), aSizeA
), // Src
119 const Size
aSizeB(w
, a
);
120 aTarget
.CopyPixel(tools::Rectangle(Point(w
, b
), aSizeB
), // Dst
121 tools::Rectangle(Point(), aSizeB
), // Src
124 setOffsetXYCreatedBitmap(
125 const_cast<drawinglayer::primitive2d::FillGraphicPrimitive2D
&>(
126 rFillGraphicPrimitive2D
),
131 if (!rFillGraphicPrimitive2D
.getOffsetXYCreatedBitmap().IsEmpty())
133 rTarget
= rFillGraphicPrimitive2D
.getOffsetXYCreatedBitmap();
134 rFillUnitRange
.expand(basegfx::B2DPoint(
135 rFillUnitRange
.getMaxX() + rFillUnitRange
.getWidth(), rFillUnitRange
.getMinY()));
140 bool prepareBitmapForDirectRender(
141 const drawinglayer::primitive2d::FillGraphicPrimitive2D
& rFillGraphicPrimitive2D
,
142 const drawinglayer::geometry::ViewInformation2D
& rViewInformation2D
, BitmapEx
& rTarget
,
143 basegfx::B2DRange
& rFillUnitRange
, double fBigDiscreteArea
)
145 const attribute::FillGraphicAttribute
& rFillGraphicAttribute(
146 rFillGraphicPrimitive2D
.getFillGraphic());
147 const Graphic
& rGraphic(rFillGraphicAttribute
.getGraphic());
149 if (rFillGraphicAttribute
.isDefault() || rGraphic
.IsNone())
151 // default attributes or GraphicType::NONE, so no fill .-> done
155 if (!rFillGraphicAttribute
.getTiling())
157 // If no tiling used, the Graphic will need to be painted just once. This
158 // is perfectly done using the decomposition, so use it.
159 // What we want to do here is to optimize tiled paint, for two reasons:
160 // (a) speed: draw one tile, repeat -> obvious
161 // (b) correctness: not so obvious, but since in AAed paint the same edge
162 // of touching polygons both AAed do *not* sum up, but get blended by
163 // multiplication (0.5 * 0.5 -> 0.25) the connection will stay visible,
164 // not only with filled polygons, but also with bitmaps
165 // Signal that paint is needed
169 if (rFillUnitRange
.isEmpty())
171 // no fill range definition, no fill, done
175 const basegfx::B2DHomMatrix
aLocalTransform(rViewInformation2D
.getObjectToViewTransformation()
176 * rFillGraphicPrimitive2D
.getTransformation());
177 const basegfx::B2DRange
& rDiscreteViewPort(rViewInformation2D
.getDiscreteViewport());
179 if (!rDiscreteViewPort
.isEmpty())
181 // calculate discrete covered pixel area
182 basegfx::B2DRange
aDiscreteRange(basegfx::B2DRange::getUnitB2DRange());
183 aDiscreteRange
.transform(aLocalTransform
);
185 if (!rDiscreteViewPort
.overlaps(rDiscreteViewPort
))
187 // we have a Viewport and visible range of geometry is outside -> not visible, done
192 if (GraphicType::Bitmap
== rGraphic
.GetType() && rGraphic
.IsAnimated())
194 // Need to prepare specialized AnimatedGraphicPrimitive2D,
195 // cannot handle here. Signal that paint is needed
199 if (GraphicType::Bitmap
== rGraphic
.GetType() && !rGraphic
.getVectorGraphicData())
201 // bitmap graphic, always handle locally, so get bitmap data independent
202 // if it'sie or it's discrete display size
203 rTarget
= rGraphic
.GetBitmapEx();
207 // Vector Graphic Data fill, including metafile:
208 // We can know about discrete pixel size here, calculate and use it.
209 // To do so, using Vectors is sufficient to get the lengths. It is
210 // not necessary to transform the whole target coordinate system.
211 const basegfx::B2DVector
aDiscreteXAxis(
213 * basegfx::B2DVector(rFillUnitRange
.getMaxX() - rFillUnitRange
.getMinX(), 0.0));
214 const basegfx::B2DVector
aDiscreteYAxis(
216 * basegfx::B2DVector(0.0, rFillUnitRange
.getMaxY() - rFillUnitRange
.getMinY()));
218 // get and ensure minimal size
219 const double fDiscreteWidth(std::max(1.0, aDiscreteXAxis
.getLength()));
220 const double fDiscreteHeight(std::max(1.0, aDiscreteYAxis
.getLength()));
222 // compare with a big visualization size in discrete pixels
223 const double fTargetDiscreteArea(fDiscreteWidth
* fDiscreteHeight
);
225 if (fTargetDiscreteArea
> fBigDiscreteArea
)
227 // When the vector data is visualized big it is better to not handle here
228 // but use decomposition fallback which then will visualize the vector data
229 // directly -> better quality, acceptable number of tile repeat(s)
230 // signal that paint is needed
235 // If visualized small, the amount of repeated fills gets expensive, so
236 // in that case use a Bitmap and the Brush technique below.
237 // The Bitmap may be created here exactly for the needed target size
238 // (using local D2DBitmapPixelProcessor2D and the vector data),
239 // but since we have a HW renderer and re-use of system-dependent data
240 // at BitmapEx is possible, just get the default fallback Bitmap from the
241 // vector data to continue. Trust the existing converters for now to
242 // do something with good quality.
243 rTarget
= rGraphic
.GetBitmapEx();
247 if (rTarget
.IsEmpty() || rTarget
.GetSizePixel().IsEmpty())
249 // no pixel data, done
253 // react if OffsetX/OffsetY of the FillGraphicAttribute is used
254 takeCareOfOffsetXY(rFillGraphicPrimitive2D
, rTarget
, rFillUnitRange
);
257 // allow to check bitmap data, e.g. control OffsetX/OffsetY stuff
258 static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
259 if (bDoSaveForVisualControl
)
261 static const OUString
sDumpPath(
262 OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
263 if (!sDumpPath
.isEmpty())
265 SvFileStream
aNew(sDumpPath
+ "test_getreplacement.png",
266 StreamMode::WRITE
| StreamMode::TRUNC
);
267 vcl::PngImageWriter
aPNGWriter(aNew
);
268 aPNGWriter
.write(rTarget
);
273 // signal to render it
277 void calculateDiscreteVisibleRange(
278 basegfx::B2DRange
& rDiscreteVisibleRange
, const basegfx::B2DRange
& rContentRange
,
279 const drawinglayer::geometry::ViewInformation2D
& rViewInformation2D
)
281 if (rContentRange
.isEmpty())
284 rDiscreteVisibleRange
.reset();
288 basegfx::B2DRange
aDiscreteRange(rContentRange
);
289 aDiscreteRange
.transform(rViewInformation2D
.getObjectToViewTransformation());
290 const basegfx::B2DRange
& rDiscreteViewPort(rViewInformation2D
.getDiscreteViewport());
291 rDiscreteVisibleRange
= aDiscreteRange
;
293 if (!rDiscreteViewPort
.isEmpty())
295 rDiscreteVisibleRange
.intersect(rDiscreteViewPort
);
298 } // end of namespace
300 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */