File indexing completed on 2024-05-19 04:22:56

0001 
0002 /*
0003    Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
0004    Copyright (c) 2011 Martin Koller <kollix@aon.at>
0005    All rights reserved.
0006 
0007    Redistribution and use in source and binary forms, with or without
0008    modification, are permitted provided that the following conditions
0009    are met:
0010 
0011    1. Redistributions of source code must retain the above copyright
0012       notice, this list of conditions and the following disclaimer.
0013    2. Redistributions in binary form must reproduce the above copyright
0014       notice, this list of conditions and the following disclaimer in the
0015       documentation and/or other materials provided with the distribution.
0016 
0017    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0018    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0019    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0020    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0021    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0022    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0023    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0024    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0025    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0026    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0027 */
0028 
0029 
0030 #define DEBUG_KP_EFFECT_REDUCE_COLORS 0
0031 
0032 
0033 #include "imagelib/effects/kpEffectReduceColors.h"
0034 
0035 #include "kpLogCategories.h"
0036 
0037 //---------------------------------------------------------------------
0038 
0039 static QImage::Format DepthToFormat (int depth)
0040 {
0041     // These values are QImage's supported depths.
0042     switch (depth)
0043     {
0044     case 1:
0045         // (can be MSB instead, I suppose)
0046         return QImage::Format_MonoLSB;
0047 
0048     case 8:
0049         return QImage::Format_Indexed8;
0050 
0051     case 16:
0052         return QImage::Format_ARGB4444_Premultiplied;
0053 
0054     case 24:
0055         return QImage::Format_ARGB6666_Premultiplied;
0056 
0057     case 32:
0058         return QImage::Format_ARGB32_Premultiplied;
0059 
0060     default:
0061         Q_ASSERT (!"unknown depth");
0062         return QImage::Format_Invalid;
0063     }
0064 }
0065 
0066 //---------------------------------------------------------------------
0067 
0068 // public static
0069 QImage kpEffectReduceColors::convertImageDepth (const QImage &image, int depth, bool dither)
0070 {
0071 #if DEBUG_KP_EFFECT_REDUCE_COLORS
0072     qCDebug(kpLogImagelib) << "kpeffectreducecolors.cpp:ConvertImageDepth() changing image (w=" << image.width ()
0073                << ",h=" << image.height ()
0074                << ") depth from " << image.depth ()
0075                 << " to " << depth
0076                 << " (dither=" << dither << ")"
0077                 << endl;
0078 #endif
0079 
0080     if (image.isNull ()) {
0081         return image;
0082     }
0083 
0084     if (depth == image.depth ()) {
0085         return image;
0086     }
0087 
0088 
0089 #if DEBUG_KP_EFFECT_REDUCE_COLORS && 0
0090     for (int y = 0; y < image.height (); y++)
0091     {
0092         for (int x = 0; x < image.width (); x++)
0093         {
0094             fprintf (stderr, " %08X", image.pixel (x, y));
0095         }
0096         fprintf (stderr, "\n");
0097     }
0098 #endif
0099 
0100 
0101     // Hack around Qt's braindead QImage::convertToFormat(QImage::Format_MonoLSB, ...)
0102     // (with dithering off) which produces pathetic results with an image that
0103     // only has 2 colors - sometimes it just gives a completely black
0104     // result (try yellow and white as input).  Instead, we simply preserve
0105     // the 2 colours.
0106     //
0107     // One use case is resaving a "color monochrome" image (<= 2 colors but
0108     // not necessarily black & white).
0109     if (depth == 1 && !dither)
0110     {
0111     #if DEBUG_KP_EFFECT_REDUCE_COLORS
0112         qCDebug(kpLogImagelib) << "\tinvoking convert-to-depth 1 hack";
0113     #endif
0114         QRgb color0 = 0, color1 = 0;
0115         bool color0Valid = false, color1Valid = false;
0116 
0117         bool moreThan2Colors = false;
0118 
0119         QImage monoImage (image.width (), image.height (), QImage::Format_MonoLSB);
0120         monoImage.setColorCount (2);
0121     #if DEBUG_KP_EFFECT_REDUCE_COLORS
0122         qCDebug(kpLogImagelib) << "\t\tinitialising output image w=" << monoImage.width ()
0123                    << ",h=" << monoImage.height ()
0124                    << ",d=" << monoImage.depth ();
0125     #endif
0126         for (int y = 0; y < image.height (); y++)
0127         {
0128             for (int x = 0; x < image.width (); x++)
0129             {
0130                 // (this can be transparent)
0131                 QRgb imagePixel = image.pixel (x, y);
0132 
0133                 if (color0Valid && imagePixel == color0) {
0134                     monoImage.setPixel (x, y, 0);
0135                 }
0136                 else if (color1Valid && imagePixel == color1) {
0137                     monoImage.setPixel (x, y, 1);
0138                 }
0139                 else if (!color0Valid) {
0140                     color0 = imagePixel;
0141                     color0Valid = true;
0142                     monoImage.setPixel (x, y, 0);
0143                 #if DEBUG_KP_EFFECT_REDUCE_COLORS
0144                     qCDebug(kpLogImagelib) << "\t\t\tcolor0=" << (int *) color0
0145                                << " at x=" << x << ",y=" << y;
0146                 #endif
0147                 }
0148                 else if (!color1Valid)
0149                 {
0150                     color1 = imagePixel;
0151                     color1Valid = true;
0152                     monoImage.setPixel (x, y, 1);
0153                 #if DEBUG_KP_EFFECT_REDUCE_COLORS
0154                     qCDebug(kpLogImagelib) << "\t\t\tcolor1=" << (int *) color1
0155                                << " at x=" << x << ",y=" << y;
0156                 #endif
0157                 }
0158                 else
0159                 {
0160                 #if DEBUG_KP_EFFECT_REDUCE_COLORS
0161                     qCDebug(kpLogImagelib) << "\t\t\timagePixel=" << (int *) imagePixel
0162                                << " at x=" << x << ",y=" << y
0163                                << " moreThan2Colors - abort hack";
0164                 #endif
0165                     moreThan2Colors = true;
0166 
0167                     // Dijkstra, this is clearer than double break'ing or
0168                     // a check in both loops
0169                     goto exit_loop;
0170                 }
0171             }
0172         }
0173     exit_loop:
0174 
0175         if (!moreThan2Colors)
0176         {
0177             monoImage.setColor (0, color0Valid ? color0 : 0xFFFFFF);
0178             monoImage.setColor (1, color1Valid ? color1 : 0x000000);
0179             return monoImage;
0180         }
0181     }
0182 
0183     QImage retImage = image.convertToFormat (::DepthToFormat (depth),
0184         Qt::AutoColor |
0185         (dither ? Qt::DiffuseDither : Qt::ThresholdDither) |
0186         Qt::ThresholdAlphaDither |
0187         (dither ? Qt::PreferDither : Qt::AvoidDither));
0188 #if DEBUG_KP_EFFECT_REDUCE_COLORS
0189     qCDebug(kpLogImagelib) << "\tformat: before=" << image.format ()
0190               << "after=" << retImage.format ();
0191 #endif
0192 
0193 #if DEBUG_KP_EFFECT_REDUCE_COLORS && 0
0194     qCDebug(kpLogImagelib) << "After colour reduction:";
0195     for (int y = 0; y < image.height (); y++)
0196     {
0197         for (int x = 0; x < image.width (); x++)
0198         {
0199             fprintf (stderr, " %08X", image.pixel (x, y));
0200         }
0201         fprintf (stderr, "\n");
0202     }
0203 #endif
0204 
0205     return retImage;
0206 }
0207 
0208 //---------------------------------------------------------------------
0209 
0210 // public static
0211 void kpEffectReduceColors::applyEffect (QImage *destPtr, int depth, bool dither)
0212 {
0213     if (!destPtr) {
0214         return;
0215     }
0216 
0217     // You can't "reduce" to 32-bit since it's the highest depth.
0218     if (depth != 1 && depth != 8) {
0219         return;
0220     }
0221 
0222     *destPtr = convertImageDepth(*destPtr, depth, dither);
0223 
0224     // internally we always use QImage::Format_ARGB32_Premultiplied and
0225     // this effect is just an "effect" in that it changes the image (the look) somehow
0226     // When one wants a different depth on the file, then he needs to save the image
0227     // in that depth
0228     destPtr->convertTo(QImage::Format_ARGB32_Premultiplied);
0229 }
0230 
0231 //---------------------------------------------------------------------
0232 
0233 QImage kpEffectReduceColors::applyEffect (const QImage &pm, int depth, bool dither)
0234 {
0235     QImage ret = pm;
0236     applyEffect (&ret, depth, dither);
0237     return ret;
0238 }
0239 
0240 //---------------------------------------------------------------------