Marble

BlendingAlgorithms.cpp
1 // SPDX-FileCopyrightText: 2010 Jens-Michael Hoffmann <[email protected]>
2 //
3 // SPDX-License-Identifier: LGPL-2.1-or-later
4 
5 #include "BlendingAlgorithms.h"
6 
7 #include "TextureTile.h"
8 
9 #include <cmath>
10 
11 #include <QImage>
12 #include <QPainter>
13 
14 namespace Marble
15 {
16 
17 void OverpaintBlending::blend( QImage * const bottom, TextureTile const * const top ) const
18 {
19  Q_ASSERT( bottom );
20  Q_ASSERT( top );
21  Q_ASSERT( top->image() );
22  Q_ASSERT( bottom->size() == top->image()->size() );
23  Q_ASSERT( bottom->format() == QImage::Format_ARGB32_Premultiplied );
24 
25  QPainter painter( bottom );
26 
27  painter.drawImage( 0, 0, *top->image() );
28 }
29 
30 void GrayscaleBlending::blend( QImage * const bottom, TextureTile const * const top ) const
31 {
32  Q_ASSERT( bottom );
33  Q_ASSERT( top );
34  Q_ASSERT( top->image() );
35  Q_ASSERT( bottom->size() == top->image()->size() );
36  Q_ASSERT( bottom->format() == QImage::Format_ARGB32_Premultiplied );
37  QImage const topImagePremult = top->image()->convertToFormat( QImage::Format_ARGB32_Premultiplied );
38 
39  // Draw a grayscale version of the bottom image
40  int const width = bottom->width();
41  int const height = bottom->height();
42 
43  for ( int y = 0; y < height; ++y ) {
44  for ( int x = 0; x < width; ++x ) {
45  QRgb const topPixel = topImagePremult.pixel( x, y );
46  int const gray = qGray( topPixel );
47  QRgb const grayPixel = qRgb( gray, gray, gray );
48  bottom->setPixel( x, y,grayPixel );
49  }
50  }
51 
52 }
53 
54 void InvertColorBlending::blend( QImage * const bottom, TextureTile const * const top ) const
55 {
56  Q_ASSERT( bottom );
57  Q_ASSERT( top );
58  Q_ASSERT( top->image() );
59  Q_ASSERT( bottom->size() == top->image()->size() );
60  Q_ASSERT( bottom->format() == QImage::Format_ARGB32_Premultiplied );
61  QImage const topImagePremult = top->image()->convertToFormat( QImage::Format_ARGB32_Premultiplied );
62 
63  // Draw an inverted version of the bottom image
64  int const width = bottom->width();
65  int const height = bottom->height();
66 
67  for ( int y = 0; y < height; ++y ) {
68  for ( int x = 0; x < width; ++x ) {
69  QRgb const topPixel = topImagePremult.pixel( x, y );
70  QRgb const invertedPixel = qRgb( 255 - qRed(topPixel), 255 - qGreen(topPixel), 255 - qBlue(topPixel) );
71  bottom->setPixel( x, y, invertedPixel );
72  }
73  }
74 }
75 
76 void InvertHueBlending::blend( QImage * const bottom, TextureTile const * const top ) const
77 {
78  Q_ASSERT( bottom );
79  Q_ASSERT( top );
80  Q_ASSERT( top->image() );
81  Q_ASSERT( bottom->size() == top->image()->size() );
82  Q_ASSERT( bottom->format() == QImage::Format_ARGB32_Premultiplied );
83  QImage const topImagePremult = top->image()->convertToFormat( QImage::Format_ARGB32_Premultiplied );
84 
85  // Draw an inverted version of the bottom image
86  int const width = bottom->width();
87  int const height = bottom->height();
88 
89  for ( int y = 0; y < height; ++y ) {
90  for ( int x = 0; x < width; ++x ) {
91  QRgb const topPixel = topImagePremult.pixel( x, y );
92  QColor invertedColor(255 - qRed(topPixel), 255 - qGreen(topPixel), 255 - qBlue(topPixel));
93  int hue = invertedColor.hslHue();
94  int saturation = invertedColor.hslSaturation();
95  int lightness = invertedColor.lightness();
96  QColor naturalInvertedColor = QColor::fromHsl((hue + 195) % 255 , saturation, lightness);
97  QRgb naturalInvertedPixel = naturalInvertedColor.rgb();
98  bottom->setPixel( x, y, naturalInvertedPixel );
99  }
100  }
101 }
102 
103 // pre-conditions:
104 // - bottom and top image have the same size
105 // - bottom image format is ARGB32_Premultiplied
106 void IndependentChannelBlending::blend( QImage * const bottom,
107  TextureTile const * const top ) const
108 {
109  QImage const * const topImage = top->image();
110  Q_ASSERT( topImage );
111  Q_ASSERT( bottom->size() == topImage->size() );
112  Q_ASSERT( bottom->format() == QImage::Format_ARGB32_Premultiplied );
113 
114  int const width = bottom->width();
115  int const height = bottom->height();
116  QImage const topImagePremult = topImage->convertToFormat( QImage::Format_ARGB32_Premultiplied );
117  for ( int y = 0; y < height; ++y ) {
118  for ( int x = 0; x < width; ++x ) {
119  QRgb const bottomPixel = bottom->pixel( x, y );
120  QRgb const topPixel = topImagePremult.pixel( x, y );
121  qreal const resultRed = blendChannel( qRed( bottomPixel ) / 255.0,
122  qRed( topPixel ) / 255.0 );
123  qreal const resultGreen = blendChannel( qGreen( bottomPixel ) / 255.0,
124  qGreen( topPixel ) / 255.0 );
125  qreal const resultBlue = blendChannel( qBlue( bottomPixel ) / 255.0,
126  qBlue( topPixel ) / 255.0 );
127  bottom->setPixel( x, y, qRgb( resultRed * 255.0,
128  resultGreen * 255.0,
129  resultBlue * 255.0 ));
130  }
131  }
132 }
133 
134 
135 // Neutral blendings
136 
137 qreal AllanonBlending::blendChannel( qreal const bottomColorIntensity,
138  qreal const topColorIntensity ) const
139 {
140  return ( bottomColorIntensity + topColorIntensity ) / 2.0;
141 }
142 
143 qreal ArcusTangentBlending::blendChannel( qreal const bottomColorIntensity,
144  qreal const topColorIntensity ) const
145 {
146  return 2.0 * atan( topColorIntensity / bottomColorIntensity ) / M_PI;
147 }
148 
149 qreal GeometricMeanBlending::blendChannel( qreal const bottomColorIntensity,
150  qreal const topColorIntensity ) const
151 {
152  return sqrt( bottomColorIntensity * topColorIntensity );
153 }
154 
155 qreal LinearLightBlending::blendChannel( qreal const bottomColorIntensity,
156  qreal const topColorIntensity ) const
157 {
158  return qMin( qreal( 1.0 ),
159  qMax( qreal( 0.0 ), qreal( bottomColorIntensity + 2.0 * topColorIntensity - 1.0 )));
160 }
161 
162 qreal OverlayBlending::blendChannel( qreal const bottomColorIntensity,
163  qreal const topColorIntensity ) const
164 {
165  if ( bottomColorIntensity < 0.5 )
166  return 2.0 * bottomColorIntensity * topColorIntensity;
167  else
168  return 1.0 - 2.0 * ( 1.0 - bottomColorIntensity ) * ( 1.0 - topColorIntensity );
169 }
170 
171 qreal ParallelBlending::blendChannel( qreal const bottomColorIntensity,
172  qreal const topColorIntensity ) const
173 {
174  Q_UNUSED(bottomColorIntensity);
175  Q_UNUSED(topColorIntensity);
176  // FIXME: return qMin( qMax( 2.0 / ( 1.0 / bottomColorIntensity + 1.0 / topColorIntensity )), 0.0, 1.0 );
177  return 0.0;
178 }
179 
180 qreal TextureBlending::blendChannel( qreal const bottomColorIntensity,
181  qreal const topColorIntensity ) const
182 {
183  Q_UNUSED(bottomColorIntensity);
184  Q_UNUSED(topColorIntensity);
185  // FIXME: return qMax( qMin( topColorIntensity + bottomColorIntensity ) - 0.5 ), 1.0 ), 0.0 );
186  return 0.0;
187 }
188 
189 
190 // Darkening blendings
191 
192 qreal ColorBurnBlending::blendChannel( qreal const bottomColorIntensity,
193  qreal const topColorIntensity ) const
194 {
195  Q_UNUSED(bottomColorIntensity);
196  Q_UNUSED(topColorIntensity);
197  // FIXME: check if this formula makes sense
198  return qMin( qreal( 1.0 ),
199  qMax( qreal( 0.0 ), qreal( 1.0 - ( 1.0 - bottomColorIntensity ) / topColorIntensity )));
200 }
201 
202 qreal DarkBlending::blendChannel( qreal const bottomColorIntensity,
203  qreal const topColorIntensity ) const
204 {
205  return ( bottomColorIntensity + 1.0 - topColorIntensity ) * topColorIntensity;
206 }
207 
208 qreal DarkenBlending::blendChannel( qreal const bottomColorIntensity,
209  qreal const topColorIntensity ) const
210 {
211  // FIXME: is this really ok? not vice versa?
212  return bottomColorIntensity > topColorIntensity ? topColorIntensity : bottomColorIntensity;
213 }
214 
215 qreal DivideBlending::blendChannel( qreal const bottomColorIntensity,
216  qreal const topColorIntensity ) const
217 {
218  return log1p( bottomColorIntensity / ( 1.0 - topColorIntensity ) / 8.0) / log(2.0);
219 }
220 
221 qreal GammaDarkBlending::blendChannel( qreal const bottomColorIntensity,
222  qreal const topColorIntensity ) const
223 {
224  return pow( bottomColorIntensity, 1.0 / topColorIntensity );
225 }
226 
227 qreal LinearBurnBlending::blendChannel( qreal const bottomColorIntensity,
228  qreal const topColorIntensity ) const
229 {
230  return qMax( qreal(0.0), bottomColorIntensity + topColorIntensity - qreal( 1.0 ) );
231 }
232 
233 qreal MultiplyBlending::blendChannel( qreal const bottomColorIntensity,
234  qreal const topColorIntensity ) const
235 {
236  return bottomColorIntensity * topColorIntensity;
237 }
238 
239 qreal SubtractiveBlending::blendChannel( qreal const bottomColorIntensity,
240  qreal const topColorIntensity ) const
241 {
242  return qMax( bottomColorIntensity - topColorIntensity, qreal(0.0) );
243 }
244 
245 
246 // Lightening blendings
247 
248 qreal AdditiveBlending::blendChannel( qreal const bottomColorIntensity,
249  qreal const topColorIntensity ) const
250 {
251  return qMin( topColorIntensity + bottomColorIntensity, qreal(1.0) );
252 }
253 
254 qreal ColorDodgeBlending::blendChannel( qreal const bottomColorIntensity,
255  qreal const topColorIntensity ) const
256 {
257  return qMin( qreal( 1.0 ),
258  qMax( qreal( 0.0 ), qreal( bottomColorIntensity / ( 1.0 - topColorIntensity ))));
259 }
260 
261 qreal GammaLightBlending::blendChannel( qreal const bottomColorIntensity,
262  qreal const topColorIntensity ) const
263 {
264  return pow( bottomColorIntensity, topColorIntensity );
265 }
266 
267 qreal HardLightBlending::blendChannel( qreal const bottomColorIntensity,
268  qreal const topColorIntensity ) const
269 {
270  return topColorIntensity < 0.5
271  ? 2.0 * bottomColorIntensity * topColorIntensity
272  : 1.0 - 2.0 * ( 1.0 - bottomColorIntensity ) * ( 1.0 - topColorIntensity );
273 }
274 
275 qreal LightBlending::blendChannel( qreal const bottomColorIntensity,
276  qreal const topColorIntensity ) const
277 {
278  return bottomColorIntensity * ( 1.0 - topColorIntensity ) + pow( topColorIntensity, 2 );
279 }
280 
281 qreal LightenBlending::blendChannel( qreal const bottomColorIntensity,
282  qreal const topColorIntensity ) const
283 {
284  // is this ok?
285  return bottomColorIntensity < topColorIntensity ? topColorIntensity : bottomColorIntensity;
286 }
287 
288 qreal PinLightBlending::blendChannel( qreal const bottomColorIntensity,
289  qreal const topColorIntensity ) const
290 {
291  return qMax( qreal(0.0), qMax( qreal(2.0 + topColorIntensity - 1.0),
292  qMin( bottomColorIntensity, qreal(2.0 * topColorIntensity ))));
293 }
294 
295 qreal ScreenBlending::blendChannel( qreal const bottomColorIntensity,
296  qreal const topColorIntensity ) const
297 {
298  return 1.0 - ( 1.0 - bottomColorIntensity ) * ( 1.0 - topColorIntensity );
299 }
300 
301 qreal SoftLightBlending::blendChannel( qreal const bottomColorIntensity,
302  qreal const topColorIntensity ) const
303 {
304  return pow( bottomColorIntensity, pow( 2.0, ( 2.0 * ( 0.5 - topColorIntensity ))));
305 }
306 
307 qreal VividLightBlending::blendChannel( qreal const bottomColorIntensity,
308  qreal const topColorIntensity ) const
309 {
310  return topColorIntensity < 0.5
311  ? qMin( qreal( 1.0 ),
312  qMax( qreal( 0.0 ), qreal( 1.0 - ( 1.0 - bottomColorIntensity ) / ( 2.0 * topColorIntensity ))))
313  : qMin( qreal( 1.0 ),
314  qMax( qreal( 0.0 ), qreal( bottomColorIntensity / ( 2.0 * ( 1.0 - topColorIntensity )))));
315 }
316 
317 
318 // Inverter blendings
319 
320 qreal AdditiveSubtractiveBlending::blendChannel( qreal const bottomColorIntensity,
321  qreal const topColorIntensity ) const
322 {
323  Q_UNUSED(bottomColorIntensity);
324  Q_UNUSED(topColorIntensity);
325  // FIXME:
326  // return qMin( 1.0, qMax( 0.0, abs( bottomColorIntensity * bottomColorIntensity
327  // - topColorIntensity * topColorIntensity )));
328  return 0.0;
329 }
330 
331 qreal BleachBlending::blendChannel( qreal const bottomColorIntensity,
332  qreal const topColorIntensity ) const
333 {
334  // FIXME: "why this is the same formula as Screen Blending? Please correct.)"
335  return 1.0 - ( 1.0 - bottomColorIntensity ) * ( 1.0 - topColorIntensity );
336 }
337 
338 qreal DifferenceBlending::blendChannel( qreal const bottomColorIntensity,
339  qreal const topColorIntensity ) const
340 {
341  return qMax( qMin( qreal( 1.0 ), qreal( bottomColorIntensity - topColorIntensity + 0.5 )),
342  qreal( 0.0 ));
343 }
344 
345 qreal EquivalenceBlending::blendChannel( qreal const bottomColorIntensity,
346  qreal const topColorIntensity ) const
347 {
348  return 1.0 - qAbs( bottomColorIntensity - topColorIntensity );
349 }
350 
351 qreal HalfDifferenceBlending::blendChannel( qreal const bottomColorIntensity,
352  qreal const topColorIntensity ) const
353 {
354  return bottomColorIntensity + topColorIntensity
355  - 2.0 * ( bottomColorIntensity * topColorIntensity );
356 }
357 
358 
359 // Special purpose blendings
360 
361 void CloudsBlending::blend( QImage * const bottom, TextureTile const * const top ) const
362 {
363  QImage const * const topImage = top->image();
364  Q_ASSERT( topImage );
365  Q_ASSERT( bottom->size() == topImage->size() );
366  int const width = bottom->width();
367  int const height = bottom->height();
368  for ( int y = 0; y < height; ++y ) {
369  for ( int x = 0; x < width; ++x ) {
370  qreal const c = qRed( topImage->pixel( x, y )) / 255.0;
371  QRgb const bottomPixel = bottom->pixel( x, y );
372  int const bottomRed = qRed( bottomPixel );
373  int const bottomGreen = qGreen( bottomPixel );
374  int const bottomBlue = qBlue( bottomPixel );
375  bottom->setPixel( x, y, qRgb(( int )( bottomRed + ( 255 - bottomRed ) * c ),
376  ( int )( bottomGreen + ( 255 - bottomGreen ) * c ),
377  ( int )( bottomBlue + ( 255 - bottomBlue ) * c )));
378  }
379  }
380 }
381 
382 
383 }
Format_ARGB32_Premultiplied
int height() const const
KGUIADDONS_EXPORT qreal hue(const QColor &)
QRgb rgb() const const
QImage::Format format() const const
void setPixel(int x, int y, uint index_or_rgb)
QRgb pixel(int x, int y) const const
QImage convertToFormat(QImage::Format format, Qt::ImageConversionFlags flags) const &const
QColor fromHsl(int h, int s, int l, int a)
Binds a QML item to a specific geodetic location in screen coordinates.
QSize size() const const
int width() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Thu Sep 21 2023 04:12:25 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.