3 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #define DEBUG_KP_PIXMAP_FX 0
32 #include <kpPixmapFX.h>
36 #include <qapplication.h>
38 #include <qdatetime.h>
41 #include <qpainterpath.h>
48 #include <kconfiggroup.h>
52 #include <kmessagebox.h>
54 #include <kpAbstractSelection.h>
61 QImage
kpPixmapFX::convertToQImage (const QPixmap
&pixmap
)
66 const QImage ret
= pixmap
.toImage ();
67 Q_ASSERT (!ret
.isNull ());
72 // Returns true if <image> contains translucency (rather than just transparency)
73 // QPixmap::hasAlphaChannel() is a blind method that doesn't actually check
75 static bool imageHasAlphaChannel (const QImage
&image
)
77 if (image
.depth () < 32)
80 for (int y
= 0; y
< image
.height (); y
++)
82 for (int x
= 0; x
< image
.width (); x
++)
84 const QRgb rgb
= image
.pixel (x
, y
);
86 if (qAlpha (rgb
) > 0 && qAlpha (rgb
) < 255)
94 static int imageNumColorsUpTo (const QImage
&image
, int max
)
96 // OPT: Use hash table, not binary tree.
97 QMap
<QRgb
, bool> rgbMap
;
99 if (image
.depth () <= 8)
101 for (int i
= 0; i
< image
.numColors () && (int) rgbMap
.size () < max
; i
++)
103 rgbMap
.insert (image
.color (i
), true);
108 for (int y
= 0; y
< image
.height () && (int) rgbMap
.size () < max
; y
++)
110 for (int x
= 0; x
< image
.width () && (int) rgbMap
.size () < max
; x
++)
112 rgbMap
.insert (image
.pixel (x
, y
), true);
117 return rgbMap
.size ();
120 static void convertToPixmapWarnAboutLoss (const QImage
&image
,
121 const kpPixmapFX::WarnAboutLossInfo
&wali
)
123 if (!wali
.isValid ())
127 const QString colorDepthTranslucencyDontAskAgain
=
128 wali
.m_dontAskAgainPrefix
+ QLatin1String ("_ColorDepthTranslucency");
129 const QString colorDepthDontAskAgain
=
130 wali
.m_dontAskAgainPrefix
+ QLatin1String ("_ColorDepth");
131 const QString translucencyDontAskAgain
=
132 wali
.m_dontAskAgainPrefix
+ QLatin1String ("_Translucency");
134 #if DEBUG_KP_PIXMAP_FX && 1
139 bool hasAlphaChannel
=
140 (KMessageBox::shouldBeShownContinue (translucencyDontAskAgain
) &&
141 imageHasAlphaChannel (image
));
143 #if DEBUG_KP_PIXMAP_FX && 1
144 kDebug () << "\twarnAboutLoss - check hasAlphaChannel took "
145 << timer
.restart () << "msec" << endl
;
148 bool moreColorsThanDisplay
=
149 (KMessageBox::shouldBeShownContinue (colorDepthDontAskAgain
) &&
150 image
.depth () > QPixmap::defaultDepth() &&
151 QPixmap::defaultDepth () < 24); // 32 indicates alpha channel
153 int screenDepthNeeded
= 0;
155 if (moreColorsThanDisplay
)
156 screenDepthNeeded
= qMin (24, image
.depth ());
158 #if DEBUG_KP_PIXMAP_FX && 1
159 kDebug () << "\ttranslucencyShouldBeShown="
160 << KMessageBox::shouldBeShownContinue (translucencyDontAskAgain
)
162 << "\thasAlphaChannel=" << hasAlphaChannel
164 << "\tcolorDepthShownBeShown="
165 << KMessageBox::shouldBeShownContinue (colorDepthDontAskAgain
)
167 << "\timage.depth()=" << image
.depth ()
169 << "\tscreenDepth=" << QPixmap::defaultDepth ()
171 << "\tmoreColorsThanDisplay=" << moreColorsThanDisplay
173 << "\tneedDepth=" << screenDepthNeeded
178 QApplication::setOverrideCursor (Qt::ArrowCursor
);
180 // TODO: We really need something like KMessageBox::warning() as the
181 // messages are about data loss.
183 if (moreColorsThanDisplay
&& hasAlphaChannel
)
185 KMessageBox::information (wali
.m_parent
,
186 wali
.m_moreColorsThanDisplayAndHasAlphaChannelMessage
187 .subs (screenDepthNeeded
).toString (),
188 i18n ("Loss of Color and Translucency Information"),
189 colorDepthTranslucencyDontAskAgain
);
191 if (!KMessageBox::shouldBeShownContinue (colorDepthTranslucencyDontAskAgain
))
193 KMessageBox::saveDontShowAgainContinue (colorDepthDontAskAgain
);
194 KMessageBox::saveDontShowAgainContinue (translucencyDontAskAgain
);
197 else if (moreColorsThanDisplay
)
199 KMessageBox::information (wali
.m_parent
,
200 wali
.m_moreColorsThanDisplayMessage
201 .subs (screenDepthNeeded
).toString (),
202 i18n ("Loss of Color Information"),
203 colorDepthDontAskAgain
);
205 else if (hasAlphaChannel
)
207 KMessageBox::information (wali
.m_parent
,
208 wali
.m_hasAlphaChannelMessage
,
209 i18n ("Loss of Translucency Information"),
210 translucencyDontAskAgain
);
213 QApplication::restoreOverrideCursor ();
217 QPixmap
kpPixmapFX::convertToPixmap (const QImage
&image
, bool pretty
,
218 const WarnAboutLossInfo
&wali
)
220 #if DEBUG_KP_PIXMAP_FX && 1
221 kDebug () << "kpPixmapFX::convertToPixmap(image,pretty=" << pretty
222 << ",warnAboutLossInfo.isValid=" << wali
.isValid ()
236 destPixmap
= QPixmap::fromImage(image
,
237 Qt::ColorOnly
/*always display depth*/ |
238 Qt::ThresholdDither
/*no dither*/ |
239 Qt::ThresholdAlphaDither
/*no dither alpha*/|
244 destPixmap
= QPixmap::fromImage (image
,
245 Qt::ColorOnly
/*always display depth*/ |
246 Qt::DiffuseDither
/*hi quality dither*/ |
247 Qt::ThresholdAlphaDither
/*no dither alpha*/ |
248 Qt::PreferDither
/*(dither even if <256 colours)*/);
251 #if DEBUG_KP_PIXMAP_FX && 1
252 kDebug () << "\tconversion took " << timer
.elapsed () << "msec";
255 kpPixmapFX::ensureNoAlphaChannel (&destPixmap
);
259 convertToPixmapWarnAboutLoss (image
, wali
);
265 // TODO: don't dup convertToPixmap() code
267 QPixmap
kpPixmapFX::convertToPixmapAsLosslessAsPossible (const QImage
&image
,
268 const WarnAboutLossInfo
&wali
)
270 #if DEBUG_KP_PIXMAP_FX && 1
271 kDebug () << "kpPixmapFX::convertToPixmapAsLosslessAsPossible(image depth="
273 << ",warnAboutLossInfo.isValid=" << wali
.isValid ()
274 << ") screenDepth=" << QPixmap::defaultDepth ()
275 << " imageNumColorsUpTo257=" << imageNumColorsUpTo (image
, 257)
285 const int screenDepth
= (QPixmap::defaultDepth () >= 24 ?
287 QPixmap::defaultDepth ());
290 Qt::ImageConversionFlags ditherFlags
= 0;
292 if (image
.depth () <= screenDepth
)
294 #if DEBUG_KP_PIXMAP_FX && 1
295 kDebug () << "\timage depth <= screen depth - don't dither"
296 << " (AvoidDither | ThresholdDither)" << endl
;
299 ditherFlags
= (Qt::AvoidDither
| Qt::ThresholdDither
);
301 // PRE: image.depth() > screenDepth
302 // ASSERT: screenDepth < 32
303 else if (screenDepth
<= 8)
305 const int screenNumColors
= (1 << screenDepth
);
307 #if DEBUG_KP_PIXMAP_FX && 1
308 kDebug () << "\tscreen depth <= 8; imageNumColorsUpTo"
309 << (screenNumColors
+ 1)
310 << "=" << imageNumColorsUpTo (image
, screenNumColors
+ 1)
314 if (imageNumColorsUpTo (image
, screenNumColors
+ 1) <= screenNumColors
)
316 #if DEBUG_KP_PIXMAP_FX && 1
317 kDebug () << "\t\tcolors fit on screen - don't dither"
318 << " (AvoidDither | ThresholdDither)" << endl
;
320 ditherFlags
= (Qt::AvoidDither
| Qt::ThresholdDither
);
324 #if DEBUG_KP_PIXMAP_FX && 1
325 kDebug () << "\t\tcolors don't fit on screen - dither"
326 << " (PreferDither | DiffuseDither)" << endl
;
328 ditherFlags
= (Qt::PreferDither
| Qt::DiffuseDither
);
331 // PRE: image.depth() > screenDepth &&
333 // ASSERT: screenDepth < 32
336 #if DEBUG_KP_PIXMAP_FX && 1
337 kDebug () << "\tscreen depth > 8 - read config";
340 int configDitherIfNumColorsGreaterThan
= 323;
342 KConfigGroup
cfg (KGlobal::config (), kpSettingsGroupGeneral
);
343 if (cfg
.hasKey (kpSettingDitherOnOpen
))
345 configDitherIfNumColorsGreaterThan
= cfg
.readEntry (kpSettingDitherOnOpen
, 0);
349 cfg
.writeEntry (kpSettingDitherOnOpen
, configDitherIfNumColorsGreaterThan
);
353 #if DEBUG_KP_PIXMAP_FX && 1
354 kDebug () << "\t\tcfg=" << configDitherIfNumColorsGreaterThan
355 << " image=" << imageNumColorsUpTo (image
, configDitherIfNumColorsGreaterThan
+ 1)
359 if (imageNumColorsUpTo (image
, configDitherIfNumColorsGreaterThan
+ 1) >
360 configDitherIfNumColorsGreaterThan
)
362 #if DEBUG_KP_PIXMAP_FX && 1
363 kDebug () << "\t\t\talways dither (PreferDither | DiffuseDither)"
366 ditherFlags
= (Qt::PreferDither
| Qt::DiffuseDither
);
370 #if DEBUG_KP_PIXMAP_FX && 1
371 kDebug () << "\t\t\tdon't dither (AvoidDither | ThresholdDither)"
374 ditherFlags
= (Qt::AvoidDither
| Qt::ThresholdDither
);
379 destPixmap
= QPixmap::fromImage (image
,
380 Qt::ColorOnly
/*always display depth*/ |
381 Qt::ThresholdAlphaDither
/*no dither alpha*/ |
384 #if DEBUG_KP_PIXMAP_FX && 1
385 kDebug () << "\tconversion took " << timer
.elapsed () << "msec";
388 kpPixmapFX::ensureNoAlphaChannel (&destPixmap
);
392 convertToPixmapWarnAboutLoss (image
, wali
);
400 QPixmap
kpPixmapFX::pixmapWithDefinedTransparentPixels (const QPixmap
&pixmap
,
401 const QColor
&transparentColor
)
403 KP_PFX_CHECK_NO_ALPHA_CHANNEL (pixmap
);
406 if (!kpPixmapFX::hasMask (pixmap
))
408 // No transparent pixels to change RGB of.
412 QPixmap
retPixmap (pixmap
.width (), pixmap
.height ());
413 retPixmap
.fill (transparentColor
);
415 QPainter
p (&retPixmap
);
416 p
.drawPixmap (QPoint (0, 0), pixmap
);
419 retPixmap
.setMask (pixmap
.mask ());
421 KP_PFX_CHECK_NO_ALPHA_CHANNEL (retPixmap
);