kpixmap.cpp
00001 /* 00002 * This file is part of the KDE libraries 00003 * Copyright (C) 1998 Mark Donohoe <donohoe@kde.org> 00004 * Stephan Kulow <coolo@kde.org> 00005 * 00006 * $Id$ 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Library General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2 of the License, or (at your option) any later version. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Library General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Library General Public License 00019 * along with this library; see the file COPYING.LIB. If not, write to 00020 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00021 * Boston, MA 02110-1301, USA. 00022 */ 00023 00024 #include <tqpixmap.h> 00025 #include <tqpainter.h> 00026 #include <tqimage.h> 00027 #include <tqbitmap.h> 00028 #include <tqcolor.h> 00029 00030 #include <stdlib.h> 00031 #include "kpixmap.h" 00032 00033 // Fast diffuse dither to 3x3x3 color cube 00034 // Based on Qt's image conversion functions 00035 static bool kdither_32_to_8( const TQImage *src, TQImage *dst ) 00036 { 00037 // TQRgb *p; 00038 uchar *b; 00039 int y; 00040 00041 if ( !dst->create(src->width(), src->height(), 8, 256) ) { 00042 tqWarning("KPixmap: destination image not valid\n"); 00043 return false; 00044 } 00045 00046 dst->setNumColors( 256 ); 00047 00048 #define MAX_R 2 00049 #define MAX_G 2 00050 #define MAX_B 2 00051 #define INDEXOF(r,g,b) (((r)*(MAX_G+1)+(g))*(MAX_B+1)+(b)) 00052 00053 int rc, gc, bc; 00054 00055 for ( rc=0; rc<=MAX_R; rc++ ) // build 2x2x2 color cube 00056 for ( gc=0; gc<=MAX_G; gc++ ) 00057 for ( bc=0; bc<=MAX_B; bc++ ) { 00058 dst->setColor( INDEXOF(rc,gc,bc), 00059 tqRgb( rc*255/MAX_R, gc*255/MAX_G, bc*255/MAX_B ) ); 00060 } 00061 00062 int sw = src->width(); 00063 int* line1[3]; 00064 int* line2[3]; 00065 int* pv[3]; 00066 00067 line1[0] = new int[src->width()]; 00068 line2[0] = new int[src->width()]; 00069 line1[1] = new int[src->width()]; 00070 line2[1] = new int[src->width()]; 00071 line1[2] = new int[src->width()]; 00072 line2[2] = new int[src->width()]; 00073 pv[0] = new int[sw]; 00074 pv[1] = new int[sw]; 00075 pv[2] = new int[sw]; 00076 00077 for ( y=0; y < src->height(); y++ ) { 00078 // p = (QRgb *)src->scanLine(y); 00079 b = dst->scanLine(y); 00080 int endian = (TQImage::systemBitOrder() == TQImage::BigEndian); 00081 int x; 00082 uchar* q = const_cast<TQImage*>(src)->scanLine(y); 00083 uchar* q2 = const_cast<TQImage*>(src)->scanLine(y+1 < src->height() ? y + 1 : 0); 00084 00085 for (int chan = 0; chan < 3; chan++) { 00086 b = dst->scanLine(y); 00087 int *l1 = (y&1) ? line2[chan] : line1[chan]; 00088 int *l2 = (y&1) ? line1[chan] : line2[chan]; 00089 if ( y == 0 ) { 00090 for (int i=0; i<sw; i++) 00091 l1[i] = q[i*4+chan+endian]; 00092 } 00093 if ( y+1 < src->height() ) { 00094 for (int i=0; i<sw; i++) 00095 l2[i] = q2[i*4+chan+endian]; 00096 } 00097 00098 // Bi-directional error diffusion 00099 if ( y&1 ) { 00100 for (x=0; x<sw; x++) { 00101 int pix = TQMAX(TQMIN(2, (l1[x] * 2 + 128)/ 255), 0); 00102 int err = l1[x] - pix * 255 / 2; 00103 pv[chan][x] = pix; 00104 00105 // Spread the error around... 00106 if ( x+1<sw ) { 00107 l1[x+1] += (err*7)>>4; 00108 l2[x+1] += err>>4; 00109 } 00110 l2[x]+=(err*5)>>4; 00111 if (x>1) 00112 l2[x-1]+=(err*3)>>4; 00113 } 00114 } else { 00115 for (x=sw; x-->0; ) { 00116 int pix = TQMAX(TQMIN(2, (l1[x] * 2 + 128)/ 255), 0); 00117 int err = l1[x] - pix * 255 / 2; 00118 pv[chan][x] = pix; 00119 00120 // Spread the error around... 00121 if ( x > 0 ) { 00122 l1[x-1] += (err*7)>>4; 00123 l2[x-1] += err>>4; 00124 } 00125 l2[x]+=(err*5)>>4; 00126 if (x+1 < sw) 00127 l2[x+1]+=(err*3)>>4; 00128 } 00129 } 00130 } 00131 00132 if (!endian) { 00133 for (x=0; x<sw; x++) 00134 *b++ = INDEXOF(pv[2][x],pv[1][x],pv[0][x]); 00135 } else { 00136 for (x=0; x<sw; x++) 00137 *b++ = INDEXOF(pv[0][x],pv[1][x],pv[2][x]); 00138 } 00139 00140 } 00141 00142 delete [] line1[0]; 00143 delete [] line2[0]; 00144 delete [] line1[1]; 00145 delete [] line2[1]; 00146 delete [] line1[2]; 00147 delete [] line2[2]; 00148 delete [] pv[0]; 00149 delete [] pv[1]; 00150 delete [] pv[2]; 00151 00152 #undef MAX_R 00153 #undef MAX_G 00154 #undef MAX_B 00155 #undef INDEXOF 00156 00157 return true; 00158 } 00159 00160 KPixmap::~KPixmap() 00161 { 00162 } 00163 00164 bool KPixmap::load( const TQString& fileName, const char *format, 00165 int conversion_flags ) 00166 { 00167 TQImageIO io( fileName, format ); 00168 00169 bool result = io.read(); 00170 00171 if ( result ) { 00172 detach(); 00173 result = convertFromImage( io.image(), conversion_flags ); 00174 } 00175 return result; 00176 } 00177 00178 bool KPixmap::load( const TQString& fileName, const char *format, 00179 ColorMode mode ) 00180 { 00181 int conversion_flags = 0; 00182 switch (mode) { 00183 case Color: 00184 conversion_flags |= ColorOnly; 00185 break; 00186 case Mono: 00187 conversion_flags |= MonoOnly; 00188 break; 00189 case LowColor: 00190 conversion_flags |= LowOnly; 00191 break; 00192 case WebColor: 00193 conversion_flags |= WebOnly; 00194 break; 00195 default: 00196 break;// Nothing. 00197 } 00198 return load( fileName, format, conversion_flags ); 00199 } 00200 00201 bool KPixmap::convertFromImage( const TQImage &img, ColorMode mode ) 00202 { 00203 int conversion_flags = 0; 00204 switch (mode) { 00205 case Color: 00206 conversion_flags |= ColorOnly; 00207 break; 00208 case Mono: 00209 conversion_flags |= MonoOnly; 00210 break; 00211 case LowColor: 00212 conversion_flags |= LowOnly; 00213 break; 00214 case WebColor: 00215 conversion_flags |= WebOnly; 00216 break; 00217 default: 00218 break; // Nothing. 00219 } 00220 return convertFromImage( img, conversion_flags ); 00221 } 00222 00223 bool KPixmap::convertFromImage( const TQImage &img, int conversion_flags ) 00224 { 00225 if ( img.isNull() ) { 00226 #if defined(CHECK_NULL) 00227 tqWarning( "KPixmap::convertFromImage: Cannot convert a null image" ); 00228 #endif 00229 return false; 00230 } 00231 detach(); // detach other references 00232 00233 int dd = defaultDepth(); 00234 00235 // If color mode not one of KPixmaps extra modes nothing to do 00236 if ( ( conversion_flags & KColorMode_Mask ) != LowOnly && 00237 ( conversion_flags & KColorMode_Mask ) != WebOnly ) { 00238 return TQPixmap::convertFromImage ( img, conversion_flags ); 00239 } 00240 00241 // If the default pixmap depth is not 8bpp, KPixmap color modes have no 00242 // effect. Ignore them and use AutoColor instead. 00243 if ( dd > 8 ) { 00244 if ( ( conversion_flags & KColorMode_Mask ) == LowOnly || 00245 ( conversion_flags & KColorMode_Mask ) == WebOnly ) 00246 conversion_flags = (conversion_flags & ~KColorMode_Mask) | Auto; 00247 return TQPixmap::convertFromImage ( img, conversion_flags ); 00248 } 00249 00250 if ( ( conversion_flags & KColorMode_Mask ) == LowOnly ) { 00251 // Here we skimp a little on the possible conversion modes 00252 // Don't offer ordered or threshold dither of RGB channels or 00253 // diffuse or ordered dither of alpha channel. It hardly seems 00254 // worth the effort for this specialized mode. 00255 00256 // If image uses icon palette don't dither it. 00257 if( img.numColors() > 0 && img.numColors() <=40 ) { 00258 if ( checkColorTable( img ) ) 00259 return TQPixmap::convertFromImage( img, TQPixmap::Auto ); 00260 } 00261 00262 TQBitmap mask; 00263 bool isMask = false; 00264 00265 TQImage image = img.convertDepth(32); 00266 TQImage tImage( image.width(), image.height(), 8, 256 ); 00267 00268 if( img.hasAlphaBuffer() ) { 00269 image.setAlphaBuffer( true ); 00270 tImage.setAlphaBuffer( true ); 00271 isMask = mask.convertFromImage( img.createAlphaMask() ); 00272 } 00273 00274 kdither_32_to_8( &image, &tImage ); 00275 00276 if( TQPixmap::convertFromImage( tImage ) ) { 00277 if ( isMask ) TQPixmap::setMask( mask ); 00278 return true; 00279 } else 00280 return false; 00281 } else { 00282 TQImage image = img.convertDepth( 32 ); 00283 image.setAlphaBuffer( img.hasAlphaBuffer() ); 00284 conversion_flags = (conversion_flags & ~ColorMode_Mask) | Auto; 00285 return TQPixmap::convertFromImage ( image, conversion_flags ); 00286 } 00287 } 00288 00289 static TQColor* kpixmap_iconPalette = 0; 00290 00291 bool KPixmap::checkColorTable( const TQImage &image ) 00292 { 00293 int i = 0; 00294 00295 if (kpixmap_iconPalette == 0) { 00296 kpixmap_iconPalette = new TQColor[40]; 00297 00298 // Standard palette 00299 kpixmap_iconPalette[i++] = red; 00300 kpixmap_iconPalette[i++] = green; 00301 kpixmap_iconPalette[i++] = blue; 00302 kpixmap_iconPalette[i++] = cyan; 00303 kpixmap_iconPalette[i++] = magenta; 00304 kpixmap_iconPalette[i++] = yellow; 00305 kpixmap_iconPalette[i++] = darkRed; 00306 kpixmap_iconPalette[i++] = darkGreen; 00307 kpixmap_iconPalette[i++] = darkBlue; 00308 kpixmap_iconPalette[i++] = darkCyan; 00309 kpixmap_iconPalette[i++] = darkMagenta; 00310 kpixmap_iconPalette[i++] = darkYellow; 00311 kpixmap_iconPalette[i++] = white; 00312 kpixmap_iconPalette[i++] = lightGray; 00313 kpixmap_iconPalette[i++] = gray; 00314 kpixmap_iconPalette[i++] = darkGray; 00315 kpixmap_iconPalette[i++] = black; 00316 00317 // Pastels 00318 kpixmap_iconPalette[i++] = TQColor( 255, 192, 192 ); 00319 kpixmap_iconPalette[i++] = TQColor( 192, 255, 192 ); 00320 kpixmap_iconPalette[i++] = TQColor( 192, 192, 255 ); 00321 kpixmap_iconPalette[i++] = TQColor( 255, 255, 192 ); 00322 kpixmap_iconPalette[i++] = TQColor( 255, 192, 255 ); 00323 kpixmap_iconPalette[i++] = TQColor( 192, 255, 255 ); 00324 00325 // Reds 00326 kpixmap_iconPalette[i++] = TQColor( 64, 0, 0 ); 00327 kpixmap_iconPalette[i++] = TQColor( 192, 0, 0 ); 00328 00329 // Oranges 00330 kpixmap_iconPalette[i++] = TQColor( 255, 128, 0 ); 00331 kpixmap_iconPalette[i++] = TQColor( 192, 88, 0 ); 00332 kpixmap_iconPalette[i++] = TQColor( 255, 168, 88 ); 00333 kpixmap_iconPalette[i++] = TQColor( 255, 220, 168 ); 00334 00335 // Blues 00336 kpixmap_iconPalette[i++] = TQColor( 0, 0, 192 ); 00337 00338 // Turquoise 00339 kpixmap_iconPalette[i++] = TQColor( 0, 64, 64 ); 00340 kpixmap_iconPalette[i++] = TQColor( 0, 192, 192 ); 00341 00342 // Yellows 00343 kpixmap_iconPalette[i++] = TQColor( 64, 64, 0 ); 00344 kpixmap_iconPalette[i++] = TQColor( 192, 192, 0 ); 00345 00346 // Greens 00347 kpixmap_iconPalette[i++] = TQColor( 0, 64, 0 ); 00348 kpixmap_iconPalette[i++] = TQColor( 0, 192, 0 ); 00349 00350 // Purples 00351 kpixmap_iconPalette[i++] = TQColor( 192, 0, 192 ); 00352 00353 // Greys 00354 kpixmap_iconPalette[i++] = TQColor( 88, 88, 88 ); 00355 kpixmap_iconPalette[i++] = TQColor( 48, 48, 48 ); 00356 kpixmap_iconPalette[i++] = TQColor( 220, 220, 220 ); 00357 00358 } 00359 00360 TQRgb* ctable = image.tqcolorTable(); 00361 00362 int ncols = image.numColors(); 00363 int j; 00364 00365 // Allow one failure which could be transparent background 00366 int failures = 0; 00367 00368 for ( i=0; i<ncols; i++ ) { 00369 for ( j=0; j<40; j++ ) { 00370 if ( kpixmap_iconPalette[j].red() == tqRed( ctable[i] ) && 00371 kpixmap_iconPalette[j].green() == tqGreen( ctable[i] ) && 00372 kpixmap_iconPalette[j].blue() == tqBlue( ctable[i] ) ) { 00373 break; 00374 } 00375 } 00376 00377 if ( j == 40 ) { 00378 failures ++; 00379 } 00380 } 00381 00382 return ( failures <= 1 ); 00383 00384 } 00385 00386 KPixmap::KPixmap(const TQPixmap& p) 00387 : TQPixmap(p) 00388 { 00389 }