LibreOffice Module drawinglayer (master) 1
SDPRProcessor2dTools.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
23#include <vcl/bitmapex.hxx>
24#include <vcl/graph.hxx>
26
27#ifdef DBG_UTIL
28#include <tools/stream.hxx>
30#endif
31
33{
36 const BitmapEx& rBitmap)
37{
38 rFillGraphicPrimitive2D.impSetOffsetXYCreatedBitmap(rBitmap);
39}
40
42 const drawinglayer::primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D,
43 BitmapEx& rTarget, basegfx::B2DRange& rFillUnitRange)
44{
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);
51
52 if (bOffsetXIsUsed)
53 {
54 if (rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap().IsEmpty())
55 {
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())));
59
60 if (0 != a && w != a)
61 {
62 const tools::Long h(rSize.Height());
63 const tools::Long b(w - a);
64 BitmapEx aTarget(Size(w, h * 2), rTarget.getPixelFormat());
65
66 aTarget.SetPrefSize(
67 Size(rTarget.GetPrefSize().Width(), rTarget.GetPrefSize().Height() * 2));
68 const tools::Rectangle aSrcDst(Point(), rSize);
69 aTarget.CopyPixel(aSrcDst, // Dst
70 aSrcDst, // Src
71 &rTarget);
72 const Size aSizeA(b, h);
73 aTarget.CopyPixel(tools::Rectangle(Point(0, h), aSizeA), // Dst
74 tools::Rectangle(Point(a, 0), aSizeA), // Src
75 &rTarget);
76 const Size aSizeB(a, h);
77 aTarget.CopyPixel(tools::Rectangle(Point(b, h), aSizeB), // Dst
78 tools::Rectangle(Point(), aSizeB), // Src
79 &rTarget);
80
83 rFillGraphicPrimitive2D),
84 aTarget);
85 }
86 }
87
88 if (!rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap().IsEmpty())
89 {
90 rTarget = rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap();
91 rFillUnitRange.expand(basegfx::B2DPoint(
92 rFillUnitRange.getMinX(), rFillUnitRange.getMaxY() + rFillUnitRange.getHeight()));
93 }
94 }
95 else if (bOffsetYIsUsed)
96 {
97 if (rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap().IsEmpty())
98 {
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())));
102
103 if (0 != a && h != a)
104 {
105 const tools::Long w(rSize.Width());
106 const tools::Long b(h - a);
107 BitmapEx aTarget(Size(w * 2, h), rTarget.getPixelFormat());
108
109 aTarget.SetPrefSize(
110 Size(rTarget.GetPrefSize().Width() * 2, rTarget.GetPrefSize().Height()));
111 const tools::Rectangle aSrcDst(Point(), rSize);
112 aTarget.CopyPixel(aSrcDst, // Dst
113 aSrcDst, // Src
114 &rTarget);
115 const Size aSizeA(w, b);
116 aTarget.CopyPixel(tools::Rectangle(Point(w, 0), aSizeA), // Dst
117 tools::Rectangle(Point(0, a), aSizeA), // Src
118 &rTarget);
119 const Size aSizeB(w, a);
120 aTarget.CopyPixel(tools::Rectangle(Point(w, b), aSizeB), // Dst
121 tools::Rectangle(Point(), aSizeB), // Src
122 &rTarget);
123
126 rFillGraphicPrimitive2D),
127 aTarget);
128 }
129 }
130
131 if (!rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap().IsEmpty())
132 {
133 rTarget = rFillGraphicPrimitive2D.getOffsetXYCreatedBitmap();
134 rFillUnitRange.expand(basegfx::B2DPoint(
135 rFillUnitRange.getMaxX() + rFillUnitRange.getWidth(), rFillUnitRange.getMinY()));
136 }
137 }
138}
139
141 const drawinglayer::primitive2d::FillGraphicPrimitive2D& rFillGraphicPrimitive2D,
142 const drawinglayer::geometry::ViewInformation2D& rViewInformation2D, BitmapEx& rTarget,
143 basegfx::B2DRange& rFillUnitRange, double fBigDiscreteArea)
144{
145 const attribute::FillGraphicAttribute& rFillGraphicAttribute(
146 rFillGraphicPrimitive2D.getFillGraphic());
147 const Graphic& rGraphic(rFillGraphicAttribute.getGraphic());
148
149 if (rFillGraphicAttribute.isDefault() || rGraphic.IsNone())
150 {
151 // default attributes or GraphicType::NONE, so no fill .-> done
152 return false;
153 }
154
155 if (!rFillGraphicAttribute.getTiling())
156 {
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
166 return true;
167 }
168
169 if (rFillUnitRange.isEmpty())
170 {
171 // no fill range definition, no fill, done
172 return false;
173 }
174
175 const basegfx::B2DHomMatrix aLocalTransform(rViewInformation2D.getObjectToViewTransformation()
176 * rFillGraphicPrimitive2D.getTransformation());
177 const basegfx::B2DRange& rDiscreteViewPort(rViewInformation2D.getDiscreteViewport());
178
179 if (!rDiscreteViewPort.isEmpty())
180 {
181 // calculate discrete covered pixel area
183 aDiscreteRange.transform(aLocalTransform);
184
185 if (!rDiscreteViewPort.overlaps(rDiscreteViewPort))
186 {
187 // we have a Viewport and visible range of geometry is outside -> not visible, done
188 return false;
189 }
190 }
191
192 if (GraphicType::Bitmap == rGraphic.GetType() && rGraphic.IsAnimated())
193 {
194 // Need to prepare specialized AnimatedGraphicPrimitive2D,
195 // cannot handle here. Signal that paint is needed
196 return true;
197 }
198
199 if (GraphicType::Bitmap == rGraphic.GetType() && !rGraphic.getVectorGraphicData())
200 {
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();
204 }
205 else
206 {
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(
212 aLocalTransform
213 * basegfx::B2DVector(rFillUnitRange.getMaxX() - rFillUnitRange.getMinX(), 0.0));
214 const basegfx::B2DVector aDiscreteYAxis(
215 aLocalTransform
216 * basegfx::B2DVector(0.0, rFillUnitRange.getMaxY() - rFillUnitRange.getMinY()));
217
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()));
221
222 // compare with a big visualization size in discrete pixels
223 const double fTargetDiscreteArea(fDiscreteWidth * fDiscreteHeight);
224
225 if (fTargetDiscreteArea > fBigDiscreteArea)
226 {
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
231 return true;
232 }
233 else
234 {
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();
244 }
245 }
246
247 if (rTarget.IsEmpty() || rTarget.GetSizePixel().IsEmpty())
248 {
249 // no pixel data, done
250 return false;
251 }
252
253 // react if OffsetX/OffsetY of the FillGraphicAttribute is used
254 takeCareOfOffsetXY(rFillGraphicPrimitive2D, rTarget, rFillUnitRange);
255
256#ifdef DBG_UTIL
257 // allow to check bitmap data, e.g. control OffsetX/OffsetY stuff
258 static bool bDoSaveForVisualControl(false); // loplugin:constvars:ignore
259 if (bDoSaveForVisualControl)
260 {
261 static const OUString sDumpPath(
262 OUString::createFromAscii(std::getenv("VCL_DUMP_BMP_PATH")));
263 if (!sDumpPath.isEmpty())
264 {
265 SvFileStream aNew(sDumpPath + "test_getreplacement.png",
266 StreamMode::WRITE | StreamMode::TRUNC);
267 vcl::PngImageWriter aPNGWriter(aNew);
268 aPNGWriter.write(rTarget);
269 }
270 }
271#endif
272
273 // signal to render it
274 return true;
275}
276
278 basegfx::B2DRange& rDiscreteVisibleRange, const basegfx::B2DRange& rContentRange,
279 const drawinglayer::geometry::ViewInformation2D& rViewInformation2D)
280{
281 if (rContentRange.isEmpty())
282 {
283 // no content, done
284 rDiscreteVisibleRange.reset();
285 return;
286 }
287
288 basegfx::B2DRange aDiscreteRange(rContentRange);
289 aDiscreteRange.transform(rViewInformation2D.getObjectToViewTransformation());
290 const basegfx::B2DRange& rDiscreteViewPort(rViewInformation2D.getDiscreteViewport());
291 rDiscreteVisibleRange = aDiscreteRange;
292
293 if (!rDiscreteViewPort.isEmpty())
294 {
295 rDiscreteVisibleRange.intersect(rDiscreteViewPort);
296 }
297}
298} // end of namespace
299
300/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void SetPrefSize(const Size &rPrefSize)
bool CopyPixel(const tools::Rectangle &rRectDst, const tools::Rectangle &rRectSrc, const BitmapEx *pBmpExSrc)
bool IsEmpty() const
GraphicType GetType() const
bool IsAnimated() const
bool IsNone() const
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
const std::shared_ptr< VectorGraphicData > & getVectorGraphicData() const
constexpr tools::Long Height() const
constexpr tools::Long Width() const
BASEGFX_DLLPUBLIC void transform(const B2DHomMatrix &rMatrix)
static BASEGFX_DLLPUBLIC const B2DRange & getUnitB2DRange()
double getLength() const
TYPE getMaxX() const
TYPE getWidth() const
TYPE getMinX() const
TYPE getMinY() const
void expand(const Tuple2D< TYPE > &rTuple)
TYPE getMaxY() const
void intersect(const Range2D &rRange)
bool isEmpty() const
TYPE getHeight() const
bool overlaps(const Range2D &rRange) const
const basegfx::B2DRange & getDiscreteViewport() const
On-demand prepared Viewport in discrete units for convenience Empty viewport means everything is visi...
const basegfx::B2DHomMatrix & getObjectToViewTransformation() const
On-demand prepared Object to View transformation and its inverse for convenience.
const attribute::FillGraphicAttribute & getFillGraphic() const
const basegfx::B2DHomMatrix & getTransformation() const
data read access
bool write(const BitmapEx &rBitmap)
FilterGroup & rTarget
uno_Any a
B2IRange fround(const B2DRange &rRange)
bool prepareBitmapForDirectRender(const drawinglayer::primitive2d::FillGraphicPrimitive2D &rFillGraphicPrimitive2D, const drawinglayer::geometry::ViewInformation2D &rViewInformation2D, BitmapEx &rTarget, basegfx::B2DRange &rFillUnitRange, double fBigDiscreteArea)
helper to process FillGraphicPrimitive2D:
void takeCareOfOffsetXY(const drawinglayer::primitive2d::FillGraphicPrimitive2D &rFillGraphicPrimitive2D, BitmapEx &rTarget, basegfx::B2DRange &rFillUnitRange)
helper to react/process if OffsetX/OffsetY of the FillGraphicAttribute is used.
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...
void setOffsetXYCreatedBitmap(drawinglayer::primitive2d::FillGraphicPrimitive2D &rFillGraphicPrimitive2D, const BitmapEx &rBitmap)
long Long
sal_Int32 h
sal_Int32 w