there is no moc file generated for this class
[kdegraphics.git] / kolourpaint / pixmapfx / kpPixmapFX_FormatConversion.cpp
blobee0582b794451862757df1fbf695f2edb07bcdcd
2 /*
3 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
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>
34 #include <math.h>
36 #include <qapplication.h>
37 #include <qbitmap.h>
38 #include <qdatetime.h>
39 #include <qimage.h>
40 #include <qpainter.h>
41 #include <qpainterpath.h>
42 #include <qpixmap.h>
43 #include <qpoint.h>
44 #include <qpolygon.h>
45 #include <qrect.h>
47 #include <kconfig.h>
48 #include <kconfiggroup.h>
49 #include <kdebug.h>
50 #include <kglobal.h>
51 #include <klocale.h>
52 #include <kmessagebox.h>
54 #include <kpAbstractSelection.h>
55 #include <kpColor.h>
56 #include <kpDefs.h>
57 #include <kpTool.h>
60 // public static
61 QImage kpPixmapFX::convertToQImage (const QPixmap &pixmap)
63 if (pixmap.isNull ())
64 return QImage ();
66 const QImage ret = pixmap.toImage ();
67 Q_ASSERT (!ret.isNull ());
68 return ret;
72 // Returns true if <image> contains translucency (rather than just transparency)
73 // QPixmap::hasAlphaChannel() is a blind method that doesn't actually check
74 // each pixel.
75 static bool imageHasAlphaChannel (const QImage &image)
77 if (image.depth () < 32)
78 return false;
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)
87 return true;
91 return false;
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);
106 else
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 ())
124 return;
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
135 QTime timer;
136 timer.start ();
137 #endif
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;
146 #endif
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)
161 << endl
162 << "\thasAlphaChannel=" << hasAlphaChannel
163 << endl
164 << "\tcolorDepthShownBeShown="
165 << KMessageBox::shouldBeShownContinue (colorDepthDontAskAgain)
166 << endl
167 << "\timage.depth()=" << image.depth ()
168 << endl
169 << "\tscreenDepth=" << QPixmap::defaultDepth ()
170 << endl
171 << "\tmoreColorsThanDisplay=" << moreColorsThanDisplay
172 << endl
173 << "\tneedDepth=" << screenDepthNeeded
174 << endl;
175 #endif
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 ();
216 // public static
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 ()
223 << ")" << endl;
224 QTime timer;
225 timer.start ();
226 #endif
228 if (image.isNull ())
229 return QPixmap ();
232 QPixmap destPixmap;
234 if (!pretty)
236 destPixmap = QPixmap::fromImage(image,
237 Qt::ColorOnly/*always display depth*/ |
238 Qt::ThresholdDither/*no dither*/ |
239 Qt::ThresholdAlphaDither/*no dither alpha*/|
240 Qt::AvoidDither);
242 else
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";
253 #endif
255 kpPixmapFX::ensureNoAlphaChannel (&destPixmap);
258 if (wali.isValid ())
259 convertToPixmapWarnAboutLoss (image, wali);
262 return destPixmap;
265 // TODO: don't dup convertToPixmap() code
266 // public static
267 QPixmap kpPixmapFX::convertToPixmapAsLosslessAsPossible (const QImage &image,
268 const WarnAboutLossInfo &wali)
270 #if DEBUG_KP_PIXMAP_FX && 1
271 kDebug () << "kpPixmapFX::convertToPixmapAsLosslessAsPossible(image depth="
272 << image.depth ()
273 << ",warnAboutLossInfo.isValid=" << wali.isValid ()
274 << ") screenDepth=" << QPixmap::defaultDepth ()
275 << " imageNumColorsUpTo257=" << imageNumColorsUpTo (image, 257)
276 << endl;
277 QTime timer;
278 timer.start ();
279 #endif
281 if (image.isNull ())
282 return QPixmap ();
285 const int screenDepth = (QPixmap::defaultDepth () >= 24 ?
286 32 :
287 QPixmap::defaultDepth ());
289 QPixmap destPixmap;
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;
297 #endif
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)
311 << endl;
312 #endif
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;
319 #endif
320 ditherFlags = (Qt::AvoidDither | Qt::ThresholdDither);
322 else
324 #if DEBUG_KP_PIXMAP_FX && 1
325 kDebug () << "\t\tcolors don't fit on screen - dither"
326 << " (PreferDither | DiffuseDither)" << endl;
327 #endif
328 ditherFlags = (Qt::PreferDither | Qt::DiffuseDither);
331 // PRE: image.depth() > screenDepth &&
332 // screenDepth > 8
333 // ASSERT: screenDepth < 32
334 else
336 #if DEBUG_KP_PIXMAP_FX && 1
337 kDebug () << "\tscreen depth > 8 - read config";
338 #endif
340 int configDitherIfNumColorsGreaterThan = 323;
342 KConfigGroup cfg (KGlobal::config (), kpSettingsGroupGeneral);
343 if (cfg.hasKey (kpSettingDitherOnOpen))
345 configDitherIfNumColorsGreaterThan = cfg.readEntry (kpSettingDitherOnOpen, 0);
347 else
349 cfg.writeEntry (kpSettingDitherOnOpen, configDitherIfNumColorsGreaterThan);
350 cfg.sync ();
353 #if DEBUG_KP_PIXMAP_FX && 1
354 kDebug () << "\t\tcfg=" << configDitherIfNumColorsGreaterThan
355 << " image=" << imageNumColorsUpTo (image, configDitherIfNumColorsGreaterThan + 1)
356 << endl;
357 #endif
359 if (imageNumColorsUpTo (image, configDitherIfNumColorsGreaterThan + 1) >
360 configDitherIfNumColorsGreaterThan)
362 #if DEBUG_KP_PIXMAP_FX && 1
363 kDebug () << "\t\t\talways dither (PreferDither | DiffuseDither)"
364 << endl;
365 #endif
366 ditherFlags = (Qt::PreferDither | Qt::DiffuseDither);
368 else
370 #if DEBUG_KP_PIXMAP_FX && 1
371 kDebug () << "\t\t\tdon't dither (AvoidDither | ThresholdDither)"
372 << endl;
373 #endif
374 ditherFlags = (Qt::AvoidDither | Qt::ThresholdDither);
379 destPixmap = QPixmap::fromImage (image,
380 Qt::ColorOnly/*always display depth*/ |
381 Qt::ThresholdAlphaDither/*no dither alpha*/ |
382 ditherFlags);
384 #if DEBUG_KP_PIXMAP_FX && 1
385 kDebug () << "\tconversion took " << timer.elapsed () << "msec";
386 #endif
388 kpPixmapFX::ensureNoAlphaChannel (&destPixmap);
391 if (wali.isValid ())
392 convertToPixmapWarnAboutLoss (image, wali);
395 return destPixmap;
399 // public static
400 QPixmap kpPixmapFX::pixmapWithDefinedTransparentPixels (const QPixmap &pixmap,
401 const QColor &transparentColor)
403 KP_PFX_CHECK_NO_ALPHA_CHANNEL (pixmap);
405 // Already opaque?
406 if (!kpPixmapFX::hasMask (pixmap))
408 // No transparent pixels to change RGB of.
409 return pixmap;
412 QPixmap retPixmap (pixmap.width (), pixmap.height ());
413 retPixmap.fill (transparentColor);
415 QPainter p (&retPixmap);
416 p.drawPixmap (QPoint (0, 0), pixmap);
417 p.end ();
419 retPixmap.setMask (pixmap.mask ());
421 KP_PFX_CHECK_NO_ALPHA_CHANNEL (retPixmap);
422 return retPixmap;