/src/libreoffice/vcl/source/gdi/gradient.cxx
Line | Count | Source |
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 <tools/gen.hxx> |
21 | | |
22 | | #include <vcl/gradient.hxx> |
23 | | #include <vcl/metaact.hxx> |
24 | | #include <vcl/rendercontext/State.hxx> |
25 | | #include <cmath> |
26 | | |
27 | | class Gradient::Impl |
28 | | { |
29 | | public: |
30 | | css::awt::GradientStyle meStyle; |
31 | | Color maStartColor; |
32 | | Color maEndColor; |
33 | | Degree10 mnAngle; |
34 | | sal_uInt16 mnBorder; |
35 | | sal_uInt16 mnOfsX; |
36 | | sal_uInt16 mnOfsY; |
37 | | sal_uInt16 mnIntensityStart; |
38 | | sal_uInt16 mnIntensityEnd; |
39 | | sal_uInt16 mnStepCount; |
40 | | |
41 | | Impl() |
42 | 25.5k | : meStyle (css::awt::GradientStyle_LINEAR) |
43 | 25.5k | , maStartColor(COL_BLACK) |
44 | 25.5k | , maEndColor(COL_WHITE) |
45 | 25.5k | , mnAngle(0) |
46 | 25.5k | , mnBorder(0) |
47 | 25.5k | , mnOfsX(50) |
48 | 25.5k | , mnOfsY(50) |
49 | 25.5k | , mnIntensityStart(100) |
50 | 25.5k | , mnIntensityEnd(100) |
51 | 25.5k | , mnStepCount(0) |
52 | 25.5k | { |
53 | 25.5k | } |
54 | | |
55 | | Impl(const Impl& rImplGradient) |
56 | 6.03k | : meStyle (rImplGradient.meStyle) |
57 | 6.03k | , maStartColor(rImplGradient.maStartColor) |
58 | 6.03k | , maEndColor(rImplGradient.maEndColor) |
59 | 6.03k | , mnAngle(rImplGradient.mnAngle) |
60 | 6.03k | , mnBorder(rImplGradient.mnBorder) |
61 | 6.03k | , mnOfsX(rImplGradient.mnOfsX) |
62 | 6.03k | , mnOfsY(rImplGradient.mnOfsY) |
63 | 6.03k | , mnIntensityStart(rImplGradient.mnIntensityStart) |
64 | 6.03k | , mnIntensityEnd(rImplGradient.mnIntensityEnd) |
65 | 6.03k | , mnStepCount(rImplGradient.mnStepCount) |
66 | 6.03k | { |
67 | 6.03k | } |
68 | | |
69 | | bool operator==(const Impl& rImpl_Gradient) const |
70 | 0 | { |
71 | 0 | return (meStyle == rImpl_Gradient.meStyle) |
72 | 0 | && (mnAngle == rImpl_Gradient.mnAngle) |
73 | 0 | && (mnBorder == rImpl_Gradient.mnBorder) |
74 | 0 | && (mnOfsX == rImpl_Gradient.mnOfsX) |
75 | 0 | && (mnOfsY == rImpl_Gradient.mnOfsY) |
76 | 0 | && (mnStepCount == rImpl_Gradient.mnStepCount) |
77 | 0 | && (mnIntensityStart == rImpl_Gradient.mnIntensityStart) |
78 | 0 | && (mnIntensityEnd == rImpl_Gradient.mnIntensityEnd) |
79 | 0 | && (maStartColor == rImpl_Gradient.maStartColor) |
80 | 0 | && (maEndColor == rImpl_Gradient.maEndColor); |
81 | 0 | } |
82 | | }; |
83 | | |
84 | 25.5k | Gradient::Gradient() = default; |
85 | | |
86 | 44.3k | Gradient::Gradient( const Gradient& ) = default; |
87 | | |
88 | 24.9k | Gradient::Gradient( Gradient&& ) = default; |
89 | | |
90 | | Gradient::Gradient( css::awt::GradientStyle eStyle, |
91 | | const Color& rStartColor, const Color& rEndColor ) |
92 | 38 | { |
93 | 38 | mpImplGradient->meStyle = eStyle; |
94 | 38 | mpImplGradient->maStartColor = rStartColor; |
95 | 38 | mpImplGradient->maEndColor = rEndColor; |
96 | 38 | } |
97 | | |
98 | 94.8k | Gradient::~Gradient() = default; |
99 | | |
100 | | |
101 | | css::awt::GradientStyle Gradient::GetStyle() const |
102 | 768k | { |
103 | 768k | return mpImplGradient->meStyle; |
104 | 768k | } |
105 | | |
106 | | void Gradient::SetStyle( css::awt::GradientStyle eStyle ) |
107 | 25.5k | { |
108 | 25.5k | mpImplGradient->meStyle = eStyle; |
109 | 25.5k | } |
110 | | |
111 | | const Color& Gradient::GetStartColor() const |
112 | 8.88k | { |
113 | 8.88k | return mpImplGradient->maStartColor; |
114 | 8.88k | } |
115 | | |
116 | | void Gradient::SetStartColor( const Color& rColor ) |
117 | 25.5k | { |
118 | 25.5k | mpImplGradient->maStartColor = rColor; |
119 | 25.5k | } |
120 | | |
121 | | const Color& Gradient::GetEndColor() const |
122 | 8.84k | { |
123 | 8.84k | return mpImplGradient->maEndColor; |
124 | 8.84k | } |
125 | | |
126 | | void Gradient::SetEndColor( const Color& rColor ) |
127 | 25.5k | { |
128 | 25.5k | mpImplGradient->maEndColor = rColor; |
129 | 25.5k | } |
130 | | |
131 | | Degree10 Gradient::GetAngle() const |
132 | 21.0k | { |
133 | 21.0k | return mpImplGradient->mnAngle; |
134 | 21.0k | } |
135 | | |
136 | | void Gradient::SetAngle( Degree10 nAngle ) |
137 | 30.7k | { |
138 | 30.7k | mpImplGradient->mnAngle = nAngle; |
139 | 30.7k | } |
140 | | |
141 | | sal_uInt16 Gradient::GetBorder() const |
142 | 12.8k | { |
143 | 12.8k | return mpImplGradient->mnBorder; |
144 | 12.8k | } |
145 | | |
146 | | void Gradient::SetBorder( sal_uInt16 nBorder ) |
147 | 25.5k | { |
148 | 25.5k | mpImplGradient->mnBorder = nBorder; |
149 | 25.5k | } |
150 | | |
151 | | sal_uInt16 Gradient::GetOfsX() const |
152 | 2.53k | { |
153 | 2.53k | return mpImplGradient->mnOfsX; |
154 | 2.53k | } |
155 | | |
156 | | void Gradient::SetOfsX( sal_uInt16 nOfsX ) |
157 | 25.5k | { |
158 | 25.5k | mpImplGradient->mnOfsX = nOfsX; |
159 | 25.5k | } |
160 | | |
161 | | sal_uInt16 Gradient::GetOfsY() const |
162 | 2.53k | { |
163 | 2.53k | return mpImplGradient->mnOfsY; |
164 | 2.53k | } |
165 | | |
166 | | void Gradient::SetOfsY( sal_uInt16 nOfsY ) |
167 | 25.5k | { |
168 | 25.5k | mpImplGradient->mnOfsY = nOfsY; |
169 | 25.5k | } |
170 | | |
171 | | sal_uInt16 Gradient::GetStartIntensity() const |
172 | 21.0k | { |
173 | 21.0k | return mpImplGradient->mnIntensityStart; |
174 | 21.0k | } |
175 | | |
176 | | void Gradient::SetStartIntensity( sal_uInt16 nIntens ) |
177 | 25.5k | { |
178 | 25.5k | mpImplGradient->mnIntensityStart = nIntens; |
179 | 25.5k | } |
180 | | |
181 | | sal_uInt16 Gradient::GetEndIntensity() const |
182 | 21.0k | { |
183 | 21.0k | return mpImplGradient->mnIntensityEnd; |
184 | 21.0k | } |
185 | | |
186 | | void Gradient::SetEndIntensity( sal_uInt16 nIntens ) |
187 | 25.5k | { |
188 | 25.5k | mpImplGradient->mnIntensityEnd = nIntens; |
189 | 25.5k | } |
190 | | |
191 | | sal_uInt16 Gradient::GetSteps() const |
192 | 14.6k | { |
193 | 14.6k | return mpImplGradient->mnStepCount; |
194 | 14.6k | } |
195 | | |
196 | | void Gradient::SetSteps( sal_uInt16 nSteps ) |
197 | 26.3k | { |
198 | 26.3k | mpImplGradient->mnStepCount = nSteps; |
199 | 26.3k | } |
200 | | |
201 | | void Gradient::GetBoundRect( const tools::Rectangle& rRect, tools::Rectangle& rBoundRect, Point& rCenter ) const |
202 | 8.69k | { |
203 | 8.69k | tools::Rectangle aRect( rRect ); |
204 | 8.69k | Degree10 nAngle = GetAngle() % 3600_deg10; |
205 | | |
206 | 8.69k | if( GetStyle() == css::awt::GradientStyle_LINEAR || GetStyle() == css::awt::GradientStyle_AXIAL ) |
207 | 6.15k | { |
208 | 6.15k | const double fAngle = toRadians(nAngle); |
209 | 6.15k | const double fWidth = aRect.GetWidth(); |
210 | 6.15k | const double fHeight = aRect.GetHeight(); |
211 | 6.15k | double fDX = fWidth * fabs( cos( fAngle ) ) + |
212 | 6.15k | fHeight * fabs( sin( fAngle ) ); |
213 | 6.15k | double fDY = fHeight * fabs( cos( fAngle ) ) + |
214 | 6.15k | fWidth * fabs( sin( fAngle ) ); |
215 | 6.15k | fDX = (fDX - fWidth) * 0.5 + 0.5; |
216 | 6.15k | fDY = (fDY - fHeight) * 0.5 + 0.5; |
217 | 6.15k | aRect.AdjustLeft( -static_cast<tools::Long>(fDX) ); |
218 | 6.15k | aRect.AdjustRight(static_cast<tools::Long>(fDX) ); |
219 | 6.15k | aRect.AdjustTop( -static_cast<tools::Long>(fDY) ); |
220 | 6.15k | aRect.AdjustBottom(static_cast<tools::Long>(fDY) ); |
221 | | |
222 | 6.15k | rBoundRect = aRect; |
223 | 6.15k | rCenter = rRect.Center(); |
224 | 6.15k | } |
225 | 2.53k | else |
226 | 2.53k | { |
227 | 2.53k | if( GetStyle() == css::awt::GradientStyle_SQUARE || GetStyle() == css::awt::GradientStyle_RECT ) |
228 | 11 | { |
229 | 11 | const double fAngle = toRadians(nAngle); |
230 | 11 | const double fWidth = aRect.GetWidth(); |
231 | 11 | const double fHeight = aRect.GetHeight(); |
232 | 11 | double fDX = fWidth * fabs( cos( fAngle ) ) + fHeight * fabs( sin( fAngle ) ); |
233 | 11 | double fDY = fHeight * fabs( cos( fAngle ) ) + fWidth * fabs( sin( fAngle ) ); |
234 | | |
235 | 11 | fDX = ( fDX - fWidth ) * 0.5 + 0.5; |
236 | 11 | fDY = ( fDY - fHeight ) * 0.5 + 0.5; |
237 | | |
238 | 11 | aRect.AdjustLeft( -static_cast<tools::Long>(fDX) ); |
239 | 11 | aRect.AdjustRight(static_cast<tools::Long>(fDX) ); |
240 | 11 | aRect.AdjustTop( -static_cast<tools::Long>(fDY) ); |
241 | 11 | aRect.AdjustBottom(static_cast<tools::Long>(fDY) ); |
242 | 11 | } |
243 | | |
244 | 2.53k | Size aSize( aRect.GetSize() ); |
245 | | |
246 | 2.53k | if( GetStyle() == css::awt::GradientStyle_RADIAL ) |
247 | 1.65k | { |
248 | | // Calculation of radii for circle |
249 | 1.65k | aSize.setWidth( static_cast<tools::Long>(0.5 + std::hypot(aSize.Width(), aSize.Height())) ); |
250 | 1.65k | aSize.setHeight( aSize.Width() ); |
251 | 1.65k | } |
252 | 879 | else if( GetStyle() == css::awt::GradientStyle_ELLIPTICAL ) |
253 | 10 | { |
254 | | // Calculation of radii for ellipse |
255 | 10 | aSize.setWidth( static_cast<tools::Long>( 0.5 + static_cast<double>(aSize.Width()) * M_SQRT2 ) ); |
256 | 10 | aSize.setHeight( static_cast<tools::Long>( 0.5 + static_cast<double>(aSize.Height()) * M_SQRT2) ); |
257 | 10 | } |
258 | | |
259 | | // Calculate new centers |
260 | 2.53k | tools::Long nZWidth = aRect.GetWidth() * static_cast<tools::Long>(GetOfsX()) / 100; |
261 | 2.53k | tools::Long nZHeight = aRect.GetHeight() * static_cast<tools::Long>(GetOfsY()) / 100; |
262 | 2.53k | tools::Long nBorderX = static_cast<tools::Long>(GetBorder()) * aSize.Width() / 100; |
263 | 2.53k | tools::Long nBorderY = static_cast<tools::Long>(GetBorder()) * aSize.Height() / 100; |
264 | 2.53k | rCenter = Point( aRect.Left() + nZWidth, aRect.Top() + nZHeight ); |
265 | | |
266 | | // Respect borders |
267 | 2.53k | aSize.AdjustWidth( -nBorderX ); |
268 | 2.53k | aSize.AdjustHeight( -nBorderY ); |
269 | | |
270 | | // Recalculate output rectangle |
271 | 2.53k | aRect.SetLeft( rCenter.X() - ( aSize.Width() >> 1 ) ); |
272 | 2.53k | aRect.SetTop( rCenter.Y() - ( aSize.Height() >> 1 ) ); |
273 | | |
274 | 2.53k | aRect.SetSize( aSize ); |
275 | 2.53k | rBoundRect = aRect; |
276 | 2.53k | } |
277 | 8.69k | } |
278 | | |
279 | | void Gradient::MakeGrayscale() |
280 | 24 | { |
281 | 24 | Color aStartCol(GetStartColor()); |
282 | 24 | Color aEndCol(GetEndColor()); |
283 | 24 | sal_uInt8 cStartLum = aStartCol.GetLuminance(); |
284 | 24 | sal_uInt8 cEndLum = aEndCol.GetLuminance(); |
285 | | |
286 | 24 | aStartCol = Color(cStartLum, cStartLum, cStartLum); |
287 | 24 | aEndCol = Color(cEndLum, cEndLum, cEndLum); |
288 | | |
289 | 24 | SetStartColor(aStartCol); |
290 | 24 | SetEndColor(aEndCol); |
291 | 24 | } |
292 | | |
293 | 0 | Gradient& Gradient::operator=( const Gradient& ) = default; |
294 | | |
295 | 0 | Gradient& Gradient::operator=( Gradient&& ) = default; |
296 | | |
297 | | bool Gradient::operator==( const Gradient& rGradient ) const |
298 | 0 | { |
299 | 0 | return mpImplGradient == rGradient.mpImplGradient; |
300 | 0 | } |
301 | | |
302 | | const sal_uInt32 GRADIENT_DEFAULT_STEPCOUNT = 0; |
303 | | |
304 | | void Gradient::AddGradientActions(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile) |
305 | 0 | { |
306 | 0 | tools::Rectangle aRect(rRect); |
307 | 0 | aRect.Normalize(); |
308 | | |
309 | | // do nothing if the rectangle is empty |
310 | 0 | if (aRect.IsEmpty()) |
311 | 0 | return; |
312 | | |
313 | 0 | rMetaFile.AddAction(new MetaPushAction(vcl::PushFlags::ALL)); |
314 | 0 | rMetaFile.AddAction(new MetaISectRectClipRegionAction( aRect)); |
315 | 0 | rMetaFile.AddAction(new MetaLineColorAction(Color(), false)); |
316 | | |
317 | | // because we draw with no border line, we have to expand gradient |
318 | | // rect to avoid missing lines on the right and bottom edge |
319 | 0 | aRect.AdjustLeft( -1 ); |
320 | 0 | aRect.AdjustTop( -1 ); |
321 | 0 | aRect.AdjustRight( 1 ); |
322 | 0 | aRect.AdjustBottom( 1 ); |
323 | | |
324 | | // calculate step count if necessary |
325 | 0 | if (!GetSteps()) |
326 | 0 | SetSteps(GRADIENT_DEFAULT_STEPCOUNT); |
327 | |
|
328 | 0 | if (GetStyle() == css::awt::GradientStyle_LINEAR || GetStyle() == css::awt::GradientStyle_AXIAL) |
329 | 0 | DrawLinearGradientToMetafile(aRect, rMetaFile); |
330 | 0 | else |
331 | 0 | DrawComplexGradientToMetafile(aRect, rMetaFile); |
332 | |
|
333 | 0 | rMetaFile.AddAction(new MetaPopAction()); |
334 | 0 | } |
335 | | |
336 | | tools::Long Gradient::GetMetafileSteps(tools::Rectangle const& rRect) const |
337 | 0 | { |
338 | | // calculate step count |
339 | 0 | tools::Long nStepCount = GetSteps(); |
340 | |
|
341 | 0 | if (nStepCount) |
342 | 0 | return nStepCount; |
343 | | |
344 | 0 | if (GetStyle() == css::awt::GradientStyle_LINEAR || GetStyle() == css::awt::GradientStyle_AXIAL) |
345 | 0 | return rRect.GetHeight(); |
346 | 0 | else |
347 | 0 | return std::min(rRect.GetWidth(), rRect.GetHeight()); |
348 | 0 | } |
349 | | |
350 | | |
351 | | static sal_uInt8 GetGradientColorValue(tools::Long nValue) |
352 | 0 | { |
353 | 0 | if ( nValue < 0 ) |
354 | 0 | return 0; |
355 | 0 | else if ( nValue > 0xFF ) |
356 | 0 | return 0xFF; |
357 | 0 | else |
358 | 0 | return static_cast<sal_uInt8>(nValue); |
359 | 0 | } |
360 | | |
361 | | void Gradient::DrawLinearGradientToMetafile(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile) const |
362 | 0 | { |
363 | | // get BoundRect of rotated rectangle |
364 | 0 | tools::Rectangle aRect; |
365 | 0 | Point aCenter; |
366 | 0 | Degree10 nAngle = GetAngle() % 3600_deg10; |
367 | |
|
368 | 0 | GetBoundRect(rRect, aRect, aCenter); |
369 | |
|
370 | 0 | bool bLinear = (GetStyle() == css::awt::GradientStyle_LINEAR); |
371 | 0 | double fBorder = GetBorder() * aRect.GetHeight() / 100.0; |
372 | 0 | if ( !bLinear ) |
373 | 0 | { |
374 | 0 | fBorder /= 2.0; |
375 | 0 | } |
376 | 0 | tools::Rectangle aMirrorRect = aRect; // used in style axial |
377 | 0 | aMirrorRect.SetTop( ( aRect.Top() + aRect.Bottom() ) / 2 ); |
378 | 0 | if ( !bLinear ) |
379 | 0 | { |
380 | 0 | aRect.SetBottom( aMirrorRect.Top() ); |
381 | 0 | } |
382 | | |
383 | | // colour-intensities of start- and finish; change if needed |
384 | 0 | tools::Long nFactor; |
385 | 0 | Color aStartCol = GetStartColor(); |
386 | 0 | Color aEndCol = GetEndColor(); |
387 | 0 | tools::Long nStartRed = aStartCol.GetRed(); |
388 | 0 | tools::Long nStartGreen = aStartCol.GetGreen(); |
389 | 0 | tools::Long nStartBlue = aStartCol.GetBlue(); |
390 | 0 | tools::Long nEndRed = aEndCol.GetRed(); |
391 | 0 | tools::Long nEndGreen = aEndCol.GetGreen(); |
392 | 0 | tools::Long nEndBlue = aEndCol.GetBlue(); |
393 | 0 | nFactor = GetStartIntensity(); |
394 | 0 | nStartRed = (nStartRed * nFactor) / 100; |
395 | 0 | nStartGreen = (nStartGreen * nFactor) / 100; |
396 | 0 | nStartBlue = (nStartBlue * nFactor) / 100; |
397 | 0 | nFactor = GetEndIntensity(); |
398 | 0 | nEndRed = (nEndRed * nFactor) / 100; |
399 | 0 | nEndGreen = (nEndGreen * nFactor) / 100; |
400 | 0 | nEndBlue = (nEndBlue * nFactor) / 100; |
401 | | |
402 | | // gradient style axial has exchanged start and end colors |
403 | 0 | if ( !bLinear) |
404 | 0 | { |
405 | 0 | std::swap( nStartRed, nEndRed ); |
406 | 0 | std::swap( nStartGreen, nEndGreen ); |
407 | 0 | std::swap( nStartBlue, nEndBlue ); |
408 | 0 | } |
409 | |
|
410 | 0 | sal_uInt8 nRed; |
411 | 0 | sal_uInt8 nGreen; |
412 | 0 | sal_uInt8 nBlue; |
413 | | |
414 | | // Create border |
415 | 0 | tools::Rectangle aBorderRect = aRect; |
416 | 0 | tools::Polygon aPoly( 4 ); |
417 | 0 | if (fBorder > 0.0) |
418 | 0 | { |
419 | 0 | nRed = static_cast<sal_uInt8>(nStartRed); |
420 | 0 | nGreen = static_cast<sal_uInt8>(nStartGreen); |
421 | 0 | nBlue = static_cast<sal_uInt8>(nStartBlue); |
422 | |
|
423 | 0 | rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); |
424 | |
|
425 | 0 | aBorderRect.SetBottom( static_cast<tools::Long>( aBorderRect.Top() + fBorder ) ); |
426 | 0 | aRect.SetTop( aBorderRect.Bottom() ); |
427 | 0 | aPoly[0] = aBorderRect.TopLeft(); |
428 | 0 | aPoly[1] = aBorderRect.TopRight(); |
429 | 0 | aPoly[2] = aBorderRect.BottomRight(); |
430 | 0 | aPoly[3] = aBorderRect.BottomLeft(); |
431 | 0 | aPoly.Rotate( aCenter, nAngle ); |
432 | |
|
433 | 0 | rMetaFile.AddAction( new MetaPolygonAction( aPoly ) ); |
434 | |
|
435 | 0 | if ( !bLinear) |
436 | 0 | { |
437 | 0 | aBorderRect = aMirrorRect; |
438 | 0 | aBorderRect.SetTop( static_cast<tools::Long>( aBorderRect.Bottom() - fBorder ) ); |
439 | 0 | aMirrorRect.SetBottom( aBorderRect.Top() ); |
440 | 0 | aPoly[0] = aBorderRect.TopLeft(); |
441 | 0 | aPoly[1] = aBorderRect.TopRight(); |
442 | 0 | aPoly[2] = aBorderRect.BottomRight(); |
443 | 0 | aPoly[3] = aBorderRect.BottomLeft(); |
444 | 0 | aPoly.Rotate( aCenter, nAngle ); |
445 | |
|
446 | 0 | rMetaFile.AddAction( new MetaPolygonAction( aPoly ) ); |
447 | 0 | } |
448 | 0 | } |
449 | |
|
450 | 0 | tools::Long nStepCount = GetMetafileSteps(aRect); |
451 | | |
452 | | // minimal three steps and maximal as max color steps |
453 | 0 | tools::Long nAbsRedSteps = std::abs( nEndRed - nStartRed ); |
454 | 0 | tools::Long nAbsGreenSteps = std::abs( nEndGreen - nStartGreen ); |
455 | 0 | tools::Long nAbsBlueSteps = std::abs( nEndBlue - nStartBlue ); |
456 | 0 | tools::Long nMaxColorSteps = std::max( nAbsRedSteps , nAbsGreenSteps ); |
457 | 0 | nMaxColorSteps = std::max( nMaxColorSteps, nAbsBlueSteps ); |
458 | 0 | tools::Long nSteps = std::min( nStepCount, nMaxColorSteps ); |
459 | 0 | if ( nSteps < 3) |
460 | 0 | { |
461 | 0 | nSteps = 3; |
462 | 0 | } |
463 | |
|
464 | 0 | double fScanInc = static_cast<double>(aRect.GetHeight()) / static_cast<double>(nSteps); |
465 | 0 | double fGradientLine = static_cast<double>(aRect.Top()); |
466 | 0 | double fMirrorGradientLine = static_cast<double>(aMirrorRect.Bottom()); |
467 | |
|
468 | 0 | const double fStepsMinus1 = static_cast<double>(nSteps) - 1.0; |
469 | 0 | if ( !bLinear) |
470 | 0 | { |
471 | 0 | nSteps -= 1; // draw middle polygons as one polygon after loop to avoid gap |
472 | 0 | } |
473 | 0 | for ( tools::Long i = 0; i < nSteps; i++ ) |
474 | 0 | { |
475 | | // linear interpolation of color |
476 | 0 | double fAlpha = static_cast<double>(i) / fStepsMinus1; |
477 | 0 | double fTempColor = static_cast<double>(nStartRed) * (1.0-fAlpha) + static_cast<double>(nEndRed) * fAlpha; |
478 | 0 | nRed = GetGradientColorValue(static_cast<tools::Long>(fTempColor)); |
479 | 0 | fTempColor = static_cast<double>(nStartGreen) * (1.0-fAlpha) + static_cast<double>(nEndGreen) * fAlpha; |
480 | 0 | nGreen = GetGradientColorValue(static_cast<tools::Long>(fTempColor)); |
481 | 0 | fTempColor = static_cast<double>(nStartBlue) * (1.0-fAlpha) + static_cast<double>(nEndBlue) * fAlpha; |
482 | 0 | nBlue = GetGradientColorValue(static_cast<tools::Long>(fTempColor)); |
483 | |
|
484 | 0 | rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); |
485 | | |
486 | | // Polygon for this color step |
487 | 0 | aRect.SetTop( static_cast<tools::Long>( fGradientLine + static_cast<double>(i) * fScanInc ) ); |
488 | 0 | aRect.SetBottom( static_cast<tools::Long>( fGradientLine + ( static_cast<double>(i) + 1.0 ) * fScanInc ) ); |
489 | 0 | aPoly[0] = aRect.TopLeft(); |
490 | 0 | aPoly[1] = aRect.TopRight(); |
491 | 0 | aPoly[2] = aRect.BottomRight(); |
492 | 0 | aPoly[3] = aRect.BottomLeft(); |
493 | 0 | aPoly.Rotate( aCenter, nAngle ); |
494 | |
|
495 | 0 | rMetaFile.AddAction( new MetaPolygonAction( aPoly ) ); |
496 | |
|
497 | 0 | if ( !bLinear ) |
498 | 0 | { |
499 | 0 | aMirrorRect.SetBottom( static_cast<tools::Long>( fMirrorGradientLine - static_cast<double>(i) * fScanInc ) ); |
500 | 0 | aMirrorRect.SetTop( static_cast<tools::Long>( fMirrorGradientLine - (static_cast<double>(i) + 1.0)* fScanInc ) ); |
501 | 0 | aPoly[0] = aMirrorRect.TopLeft(); |
502 | 0 | aPoly[1] = aMirrorRect.TopRight(); |
503 | 0 | aPoly[2] = aMirrorRect.BottomRight(); |
504 | 0 | aPoly[3] = aMirrorRect.BottomLeft(); |
505 | 0 | aPoly.Rotate( aCenter, nAngle ); |
506 | |
|
507 | 0 | rMetaFile.AddAction( new MetaPolygonAction( aPoly ) ); |
508 | 0 | } |
509 | 0 | } |
510 | 0 | if ( bLinear) |
511 | 0 | return; |
512 | | |
513 | | // draw middle polygon with end color |
514 | 0 | nRed = GetGradientColorValue(nEndRed); |
515 | 0 | nGreen = GetGradientColorValue(nEndGreen); |
516 | 0 | nBlue = GetGradientColorValue(nEndBlue); |
517 | |
|
518 | 0 | rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); |
519 | |
|
520 | 0 | aRect.SetTop( static_cast<tools::Long>( fGradientLine + static_cast<double>(nSteps) * fScanInc ) ); |
521 | 0 | aRect.SetBottom( static_cast<tools::Long>( fMirrorGradientLine - static_cast<double>(nSteps) * fScanInc ) ); |
522 | 0 | aPoly[0] = aRect.TopLeft(); |
523 | 0 | aPoly[1] = aRect.TopRight(); |
524 | 0 | aPoly[2] = aRect.BottomRight(); |
525 | 0 | aPoly[3] = aRect.BottomLeft(); |
526 | 0 | aPoly.Rotate( aCenter, nAngle ); |
527 | |
|
528 | 0 | rMetaFile.AddAction( new MetaPolygonAction( std::move(aPoly) ) ); |
529 | |
|
530 | 0 | } |
531 | | |
532 | | void Gradient::DrawComplexGradientToMetafile(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile) const |
533 | 0 | { |
534 | | // Determine if we output via Polygon or PolyPolygon |
535 | | // For all rasteroperations other than Overpaint always use PolyPolygon, |
536 | | // as we will get wrong results if we output multiple times on top of each other. |
537 | | // Also for printers always use PolyPolygon, as not all printers |
538 | | // can print polygons on top of each other. |
539 | |
|
540 | 0 | tools::Rectangle aRect; |
541 | 0 | Point aCenter; |
542 | 0 | GetBoundRect(rRect, aRect, aCenter); |
543 | |
|
544 | 0 | std::optional<tools::PolyPolygon> xPolyPoly; |
545 | 0 | xPolyPoly = tools::PolyPolygon( 2 ); |
546 | | |
547 | | // last parameter - true if complex gradient, false if linear |
548 | 0 | tools::Long nStepCount = GetMetafileSteps(rRect); |
549 | | |
550 | | // at least three steps and at most the number of colour differences |
551 | 0 | tools::Long nSteps = std::max(nStepCount, tools::Long(2)); |
552 | |
|
553 | 0 | Color aStartCol(GetStartColor()); |
554 | 0 | Color aEndCol(GetEndColor()); |
555 | |
|
556 | 0 | tools::Long nStartRed = (static_cast<tools::Long>(aStartCol.GetRed()) * GetStartIntensity()) / 100; |
557 | 0 | tools::Long nStartGreen = (static_cast<tools::Long>(aStartCol.GetGreen()) * GetStartIntensity()) / 100; |
558 | 0 | tools::Long nStartBlue = (static_cast<tools::Long>(aStartCol.GetBlue()) * GetStartIntensity()) / 100; |
559 | |
|
560 | 0 | tools::Long nEndRed = (static_cast<tools::Long>(aEndCol.GetRed()) * GetEndIntensity()) / 100; |
561 | 0 | tools::Long nEndGreen = (static_cast<tools::Long>(aEndCol.GetGreen()) * GetEndIntensity()) / 100; |
562 | 0 | tools::Long nEndBlue = (static_cast<tools::Long>(aEndCol.GetBlue()) * GetEndIntensity()) / 100; |
563 | |
|
564 | 0 | tools::Long nRedSteps = nEndRed - nStartRed; |
565 | 0 | tools::Long nGreenSteps = nEndGreen - nStartGreen; |
566 | 0 | tools::Long nBlueSteps = nEndBlue - nStartBlue; |
567 | |
|
568 | 0 | tools::Long nCalcSteps = std::abs(nRedSteps); |
569 | 0 | tools::Long nTempSteps = std::abs(nGreenSteps); |
570 | |
|
571 | 0 | if (nTempSteps > nCalcSteps) |
572 | 0 | nCalcSteps = nTempSteps; |
573 | |
|
574 | 0 | nTempSteps = std::abs( nBlueSteps ); |
575 | |
|
576 | 0 | if (nTempSteps > nCalcSteps) |
577 | 0 | nCalcSteps = nTempSteps; |
578 | |
|
579 | 0 | if (nCalcSteps < nSteps) |
580 | 0 | nSteps = nCalcSteps; |
581 | |
|
582 | 0 | if ( !nSteps ) |
583 | 0 | nSteps = 1; |
584 | | |
585 | | // determine output limits and stepsizes for all directions |
586 | 0 | tools::Polygon aPoly; |
587 | 0 | double fScanLeft = aRect.Left(); |
588 | 0 | double fScanTop = aRect.Top(); |
589 | 0 | double fScanRight = aRect.Right(); |
590 | 0 | double fScanBottom = aRect.Bottom(); |
591 | 0 | double fScanIncX = static_cast<double>(aRect.GetWidth()) / static_cast<double>(nSteps) * 0.5; |
592 | 0 | double fScanIncY = static_cast<double>(aRect.GetHeight()) / static_cast<double>(nSteps) * 0.5; |
593 | | |
594 | | // all gradients are rendered as nested rectangles which shrink |
595 | | // equally in each dimension - except for 'square' gradients |
596 | | // which shrink to a central vertex but are not per-se square. |
597 | 0 | if (GetStyle() != css::awt::GradientStyle_SQUARE) |
598 | 0 | { |
599 | 0 | fScanIncY = std::min( fScanIncY, fScanIncX ); |
600 | 0 | fScanIncX = fScanIncY; |
601 | 0 | } |
602 | 0 | sal_uInt8 nRed = static_cast<sal_uInt8>(nStartRed), nGreen = static_cast<sal_uInt8>(nStartGreen), nBlue = static_cast<sal_uInt8>(nStartBlue); |
603 | 0 | bool bPaintLastPolygon( false ); // #107349# Paint last polygon only if loop has generated any output |
604 | |
|
605 | 0 | rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); |
606 | |
|
607 | 0 | aPoly = tools::Polygon(rRect); |
608 | 0 | xPolyPoly->Insert( aPoly ); |
609 | 0 | xPolyPoly->Insert( aPoly ); |
610 | | |
611 | | // loop to output Polygon/PolyPolygon sequentially |
612 | 0 | for( tools::Long i = 1; i < nSteps; i++ ) |
613 | 0 | { |
614 | | // calculate new Polygon |
615 | 0 | fScanLeft += fScanIncX; |
616 | 0 | aRect.SetLeft( static_cast<tools::Long>( fScanLeft ) ); |
617 | 0 | fScanTop += fScanIncY; |
618 | 0 | aRect.SetTop( static_cast<tools::Long>( fScanTop ) ); |
619 | 0 | fScanRight -= fScanIncX; |
620 | 0 | aRect.SetRight( static_cast<tools::Long>( fScanRight ) ); |
621 | 0 | fScanBottom -= fScanIncY; |
622 | 0 | aRect.SetBottom( static_cast<tools::Long>( fScanBottom ) ); |
623 | |
|
624 | 0 | if( ( aRect.GetWidth() < 2 ) || ( aRect.GetHeight() < 2 ) ) |
625 | 0 | break; |
626 | | |
627 | 0 | if (GetStyle() == css::awt::GradientStyle_RADIAL || GetStyle() == css::awt::GradientStyle_ELLIPTICAL) |
628 | 0 | aPoly = tools::Polygon( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 ); |
629 | 0 | else |
630 | 0 | aPoly = tools::Polygon( aRect ); |
631 | |
|
632 | 0 | aPoly.Rotate(aCenter, GetAngle() % 3600_deg10); |
633 | | |
634 | | // adapt colour accordingly |
635 | 0 | const tools::Long nStepIndex = ( xPolyPoly ? i : ( i + 1 ) ); |
636 | 0 | nRed = GetGradientColorValue( nStartRed + ( ( nRedSteps * nStepIndex ) / nSteps ) ); |
637 | 0 | nGreen = GetGradientColorValue( nStartGreen + ( ( nGreenSteps * nStepIndex ) / nSteps ) ); |
638 | 0 | nBlue = GetGradientColorValue( nStartBlue + ( ( nBlueSteps * nStepIndex ) / nSteps ) ); |
639 | |
|
640 | 0 | bPaintLastPolygon = true; // #107349# Paint last polygon only if loop has generated any output |
641 | |
|
642 | 0 | xPolyPoly->Replace( xPolyPoly->GetObject( 1 ), 0 ); |
643 | 0 | xPolyPoly->Replace( aPoly, 1 ); |
644 | |
|
645 | 0 | rMetaFile.AddAction( new MetaPolyPolygonAction( *xPolyPoly ) ); |
646 | | |
647 | | // #107349# Set fill color _after_ geometry painting: |
648 | | // xPolyPoly's geometry is the band from last iteration's |
649 | | // aPoly to current iteration's aPoly. The window outdev |
650 | | // path (see else below), on the other hand, paints the |
651 | | // full aPoly. Thus, here, we're painting the band before |
652 | | // the one painted in the window outdev path below. To get |
653 | | // matching colors, have to delay color setting here. |
654 | 0 | rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); |
655 | 0 | } |
656 | |
|
657 | 0 | const tools::Polygon& rPoly = xPolyPoly->GetObject( 1 ); |
658 | |
|
659 | 0 | if( rPoly.GetBoundRect().IsEmpty() ) |
660 | 0 | return; |
661 | | |
662 | | // #107349# Paint last polygon with end color only if loop |
663 | | // has generated output. Otherwise, the current |
664 | | // (i.e. start) color is taken, to generate _any_ output. |
665 | 0 | if( bPaintLastPolygon ) |
666 | 0 | { |
667 | 0 | nRed = GetGradientColorValue( nEndRed ); |
668 | 0 | nGreen = GetGradientColorValue( nEndGreen ); |
669 | 0 | nBlue = GetGradientColorValue( nEndBlue ); |
670 | 0 | } |
671 | |
|
672 | 0 | rMetaFile.AddAction( new MetaFillColorAction( Color( nRed, nGreen, nBlue ), true ) ); |
673 | 0 | rMetaFile.AddAction( new MetaPolygonAction( rPoly ) ); |
674 | 0 | } |
675 | | |
676 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |