Bump version to 24.04.3.4
[LibreOffice.git] / drawinglayer / source / processor2d / SDPRProcessor2dTools.cxx
blob3d738cd09c990bf4686957814fdb0df5d61e34e6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
27 #ifdef DBG_UTIL
28 #include <tools/stream.hxx>
29 #include <vcl/filter/PngImageWriter.hxx>
30 #endif
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);
52 if (bOffsetXIsUsed)
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())));
60 if (0 != a && w != a)
62 const tools::Long h(rSize.Height());
63 const tools::Long b(w - a);
64 BitmapEx aTarget(Size(w, h * 2), rTarget.getPixelFormat());
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);
81 setOffsetXYCreatedBitmap(
82 const_cast<drawinglayer::primitive2d::FillGraphicPrimitive2D&>(
83 rFillGraphicPrimitive2D),
84 aTarget);
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());
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);
124 setOffsetXYCreatedBitmap(
125 const_cast<drawinglayer::primitive2d::FillGraphicPrimitive2D&>(
126 rFillGraphicPrimitive2D),
127 aTarget);
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
152 return false;
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
166 return true;
169 if (rFillUnitRange.isEmpty())
171 // no fill range definition, no fill, done
172 return false;
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
188 return false;
192 if (GraphicType::Bitmap == rGraphic.GetType() && rGraphic.IsAnimated())
194 // Need to prepare specialized AnimatedGraphicPrimitive2D,
195 // cannot handle here. Signal that paint is needed
196 return true;
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();
205 else
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()));
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
231 return true;
233 else
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
250 return false;
253 // react if OffsetX/OffsetY of the FillGraphicAttribute is used
254 takeCareOfOffsetXY(rFillGraphicPrimitive2D, rTarget, rFillUnitRange);
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)
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);
271 #endif
273 // signal to render it
274 return true;
277 void calculateDiscreteVisibleRange(
278 basegfx::B2DRange& rDiscreteVisibleRange, const basegfx::B2DRange& rContentRange,
279 const drawinglayer::geometry::ViewInformation2D& rViewInformation2D)
281 if (rContentRange.isEmpty())
283 // no content, done
284 rDiscreteVisibleRange.reset();
285 return;
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: */