File indexing completed on 2024-05-05 16:39:03
0001 /* This file is part of the KDE project 0002 Copyright (C) 2003,2009 Carsten Pfeiffer <pfeiffer@kde.org> 0003 0004 This program is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU General Public 0006 License as published by the Free Software Foundation, version 2. 0007 0008 This program is distributed in the hope that it will be useful, 0009 but WITHOUT ANY WARRANTY; without even the implied warranty of 0010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0011 General Public License for more details. 0012 0013 You should have received a copy of the GNU General Public License 0014 along with this program; see the file COPYING. If not, write to 0015 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0016 Boston, MA 02110-1301, USA. 0017 */ 0018 0019 #include "kuickimage.h" 0020 0021 #include <QImage> 0022 #include <QUrl> 0023 0024 #include "imagemods.h" 0025 0026 0027 KuickImage::KuickImage( const KuickFile * file, ImlibImage *im, ImlibData *id) 0028 : QObject( 0L ) 0029 { 0030 myFile = file; 0031 myOrigIm = 0L; 0032 myIm = im; 0033 myId = id; 0034 myPixmap = 0L; 0035 myWidth = im->rgb_width; 0036 myHeight = im->rgb_height; 0037 setDirty(true); 0038 0039 myOrigWidth = myWidth; 0040 myOrigHeight = myHeight; 0041 myRotation = ROT_0; 0042 myFlipMode = FlipNone; 0043 } 0044 0045 KuickImage::~KuickImage() 0046 { 0047 if (isModified()) 0048 { 0049 ImageMods::rememberFor( this ); 0050 } 0051 if ( myPixmap ) 0052 Imlib_free_pixmap( myId, myPixmap ); 0053 0054 if ( myOrigIm ) 0055 { 0056 Imlib_destroy_image( myId, myOrigIm ); 0057 Imlib_kill_image( myId, myIm ); // kill scaled image (### really? analyze!) 0058 } 0059 else 0060 Imlib_destroy_image( myId, myIm ); 0061 } 0062 0063 bool KuickImage::isModified() const 0064 { 0065 bool modified = myWidth != myOrigWidth || myHeight != myOrigHeight; 0066 modified |= (myFlipMode != FlipNone); 0067 modified |= (myRotation != ROT_0); 0068 return modified; 0069 } 0070 0071 0072 Pixmap& KuickImage::pixmap() 0073 { 0074 if ( myIsDirty ) 0075 renderPixmap(); 0076 0077 return myPixmap; 0078 } 0079 0080 0081 void KuickImage::renderPixmap() 0082 { 0083 if ( !myIsDirty ) 0084 return; 0085 0086 // qDebug("### rendering: %s", myFilename.latin1()); 0087 0088 if ( myPixmap ) 0089 Imlib_free_pixmap( myId, myPixmap ); 0090 0091 emit startRendering(); 0092 0093 // #ifndef NDEBUG 0094 // struct timeval tms1, tms2; 0095 // gettimeofday( &tms1, NULL ); 0096 // #endif 0097 0098 Imlib_render( myId, myIm, myWidth, myHeight ); 0099 myPixmap = Imlib_move_image( myId, myIm ); 0100 0101 // #ifndef NDEBUG 0102 // gettimeofday( &tms2, NULL ); 0103 // qDebug("*** rendering image: %s, took %ld ms", myFilename.latin1(), 0104 // (tms2.tv_usec - tms1.tv_usec)/1000); 0105 // #endif 0106 0107 emit stoppedRendering(); 0108 0109 myIsDirty = false; 0110 } 0111 0112 0113 void KuickImage::rotate( Rotation rot ) 0114 { 0115 if ( rot == ROT_180 ) { // rotate 180 degrees 0116 Imlib_flip_image_horizontal( myId, myIm ); 0117 Imlib_flip_image_vertical( myId, myIm ); 0118 } 0119 0120 else if ( rot == ROT_90 || rot == ROT_270 ) { 0121 qSwap( myWidth, myHeight ); 0122 Imlib_rotate_image( myId, myIm, -1 ); 0123 0124 if ( rot == ROT_90 ) // rotate 90 degrees 0125 Imlib_flip_image_horizontal( myId, myIm ); 0126 else if ( rot == ROT_270 ) // rotate 270 degrees 0127 Imlib_flip_image_vertical( myId, myIm ); 0128 } 0129 0130 myRotation = (Rotation) ((myRotation + rot) % 4); 0131 setDirty(true); 0132 } 0133 0134 0135 bool KuickImage::rotateAbs( Rotation rot ) 0136 { 0137 if ( myRotation == rot ) 0138 return false; 0139 0140 int diff = rot - myRotation; 0141 bool clockWise = (diff > 0); 0142 0143 switch( abs(diff) ) { 0144 case ROT_90: 0145 rotate( clockWise ? ROT_90 : ROT_270 ); 0146 break; 0147 case ROT_180: 0148 rotate( ROT_180 ); 0149 break; 0150 case ROT_270: 0151 rotate( clockWise ? ROT_270 : ROT_90 ); 0152 break; 0153 } 0154 0155 return true; 0156 } 0157 0158 void KuickImage::flip( FlipMode flipMode ) 0159 { 0160 if ( flipMode & FlipHorizontal ) 0161 Imlib_flip_image_horizontal( myId, myIm ); 0162 if ( flipMode & FlipVertical ) 0163 Imlib_flip_image_vertical( myId, myIm ); 0164 0165 myFlipMode = (FlipMode) (myFlipMode ^ flipMode); 0166 setDirty(true); 0167 } 0168 0169 bool KuickImage::flipAbs( int mode ) 0170 { 0171 if ( myFlipMode == mode ) 0172 return false; 0173 0174 bool changed = false; 0175 0176 if ( ((myFlipMode & FlipHorizontal) && !(mode & FlipHorizontal)) || 0177 (!(myFlipMode & FlipHorizontal) && (mode & FlipHorizontal)) ) { 0178 Imlib_flip_image_horizontal( myId, myIm ); 0179 changed = true; 0180 } 0181 0182 if ( ((myFlipMode & FlipVertical) && !(mode & FlipVertical)) || 0183 (!(myFlipMode & FlipVertical) && (mode & FlipVertical)) ) { 0184 Imlib_flip_image_vertical( myId, myIm ); 0185 changed = true; 0186 } 0187 0188 if ( changed ) { 0189 myFlipMode = (FlipMode) mode; 0190 setDirty(true); 0191 return true; 0192 } 0193 0194 return false; 0195 } 0196 0197 0198 void KuickImage::restoreOriginalSize() 0199 { 0200 if (myWidth == myOrigWidth && myHeight == myOrigHeight) 0201 return; 0202 0203 // qDebug("-- restoreOriginalSize"); 0204 0205 if ( myOrigIm != 0L ) 0206 { 0207 Imlib_destroy_image( myId, myIm ); 0208 myIm = myOrigIm; 0209 myOrigIm = 0L; 0210 } 0211 0212 myWidth = myOrigWidth; 0213 myHeight = myOrigHeight; 0214 setDirty(true); 0215 0216 if ( myRotation == ROT_90 || myRotation == ROT_270 ) 0217 qSwap( myWidth, myHeight ); 0218 } 0219 0220 void KuickImage::resize( int width, int height, KuickImage::ResizeMode mode ) 0221 { 0222 if ( myWidth == width && myHeight == height ) 0223 return; 0224 0225 if ( mode == KuickImage::SMOOTH ) 0226 { 0227 if ( !smoothResize( width, height ) ) 0228 fastResize( width, height ); 0229 } 0230 else 0231 { 0232 fastResize( width, height ); 0233 } 0234 } 0235 0236 0237 void KuickImage::fastResize( int width, int height ) 0238 { 0239 // qDebug("-- fastResize: %i x %i", width, height ); 0240 0241 // lazy resizing (only done when rendering pixmap) 0242 myWidth = width; 0243 myHeight = height; 0244 setDirty(true); 0245 } 0246 0247 bool KuickImage::smoothResize( int newWidth, int newHeight ) 0248 { 0249 // qDebug("-- smoothResize: %i x %i", newWidth, newHeight); 0250 0251 QImage *image = newQImage(); 0252 // Note: QImage::ScaleMin seems to have a bug (off-by-one, sometimes results in width being 1 pixel too small) 0253 QImage scaledImage = image->scaled(newWidth, newHeight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 0254 0255 delete image; 0256 0257 0258 ImlibImage *newIm = toImage( myId, scaledImage ); 0259 if ( newIm ) 0260 { 0261 if ( myOrigIm == 0 ) 0262 myOrigIm = myIm; 0263 0264 myIm = newIm; 0265 myWidth = newWidth; 0266 myHeight = newHeight; 0267 setDirty(true); 0268 return true; 0269 } 0270 0271 return false; 0272 } 0273 0274 QImage * KuickImage::newQImage() const 0275 { 0276 ImlibImage *im; 0277 0278 // qDebug("-- newQImage"); 0279 0280 if ( myOrigIm != 0L && myRotation == ROT_0 && myFlipMode == FlipNone ) 0281 { 0282 // use original image if no other modifications have been applied 0283 // ### use orig image always and reapply mods? 0284 im = myOrigIm; 0285 } 0286 else 0287 { 0288 im = myIm; 0289 } 0290 0291 int w = im->rgb_width; 0292 int h = im->rgb_height; 0293 0294 QImage *image = new QImage( w, h, QImage::Format_RGB32 ); 0295 uchar *rgb = im->rgb_data; 0296 // QRgb **destImageData = reinterpret_cast<QRgb**>( image->jumpTable() ); 0297 0298 0299 int byteIndex = 0; 0300 int destLineIndex = 0; 0301 int destByteIndex = 0; 0302 for ( int pixel = 0; pixel < (w * h); pixel++ ) 0303 { 0304 if ( pixel != 0 && (pixel % w) == 0 ) 0305 { 0306 destLineIndex++; 0307 destByteIndex = 0; 0308 } 0309 0310 uchar r = rgb[byteIndex++]; 0311 uchar g = rgb[byteIndex++]; 0312 uchar b = rgb[byteIndex++]; 0313 0314 QRgb rgbPixel = qRgb( r, g, b ); 0315 QRgb *destImageData = reinterpret_cast<QRgb*>( image->scanLine( destLineIndex )); 0316 destImageData[destByteIndex++] = rgbPixel; 0317 // destImageData[destLineIndex][destByteIndex++] = rgbPixel; 0318 } 0319 0320 return image; 0321 } 0322 0323 ImlibImage * KuickImage::toImage( ImlibData *id, QImage& image ) 0324 { 0325 if ( image.isNull() ) 0326 return 0L; 0327 0328 if ( image.depth() != 32 ) 0329 { 0330 image = image.convertToFormat(QImage::Format_RGB32); 0331 0332 if ( image.isNull() ) 0333 return 0L; 0334 } 0335 0336 // convert to 24 bpp (discard alpha) 0337 int numPixels = image.width() * image.height(); 0338 const int NUM_BYTES_NEW = 3; // 24 bpp 0339 uchar *newImageData = new uchar[numPixels * NUM_BYTES_NEW]; 0340 uchar *newData = newImageData; 0341 0342 int w = image.width(); 0343 int h = image.height(); 0344 0345 for (int y = 0; y < h; y++) { 0346 QRgb *scanLine = reinterpret_cast<QRgb *>( image.scanLine(y) ); 0347 for (int x = 0; x < w; x++) { 0348 const QRgb& pixel = scanLine[x]; 0349 *(newData++) = qRed(pixel); 0350 *(newData++) = qGreen(pixel); 0351 *(newData++) = qBlue(pixel); 0352 } 0353 } 0354 0355 ImlibImage *im = Imlib_create_image_from_data( id, newImageData, NULL, 0356 image.width(), image.height() ); 0357 0358 delete [] newImageData; 0359 0360 return im; 0361 } 0362 0363 0364 #if 0 0365 bool KuickImage::smoothResize( int newWidth, int newHeight ) 0366 { 0367 int numPixels = newWidth * newHeight; 0368 const int NUM_BYTES_NEW = 3; // 24 bpp 0369 uchar *newImageData = new uchar[numPixels * NUM_BYTES_NEW]; 0370 0371 // ### endianness 0372 // myIm : old image, old size 0373 0374 0375 ///////////////////////////////////////////////// 0376 // int w = myOrigWidth; //myViewport.width(); 0377 //int h = myOrigHeight; //myViewport.height(); 0378 0379 //QImage dst(w, h, myIm->depth(), myIm->numColors(), myIm->bitOrder()); 0380 0381 //QRgb *scanline; 0382 0383 int basis_ox, basis_oy, basis_xx, basis_yy; 0384 0385 // ### we only scale with a fixed factor for x and y anyway 0386 double scalex = newWidth / (double) myOrigWidth; 0387 double scaley = newHeight / (double) myOrigHeight; 0388 0389 // basis_ox=(int) (myViewport.left() * 4096.0 / scalex); 0390 // basis_oy=(int) (myViewport.top() * 4096.0 / scaley); 0391 basis_ox = 0; 0392 basis_oy = 0; 0393 basis_xx = (int) (4096.0 / scalex); 0394 basis_yy = (int) (4096.0 / scaley); 0395 0396 //qDebug("Basis: (%d, %d), (%d, 0), (0, %d)", basis_ox, basis_oy, basis_xx, basis_yy); 0397 0398 int x2, y2; 0399 0400 int max_x2 = (myOrigWidth << 12); 0401 int max_y2 = (myOrigHeight << 12); 0402 0403 // QRgb background = idata->backgroundColor.rgb(); 0404 0405 // QRgb **imdata = (QRgb **) myIm->jumpTable(); 0406 // QRgb *imdata = reinterpret_cast<QRgb*>( myIm->rgb_data ); 0407 uchar *imdata = myIm->rgb_data; 0408 0409 0410 int y = 0; 0411 0412 0413 // for (;;) //fill the top of the target pixmap with the background color 0414 // { 0415 // y2 = basis_oy + y * basis_yy; 0416 // 0417 // if ((y2 >= 0 && (y2 >> 12) < myIm->height()) || y >= h) 0418 // break; 0419 // 0420 // scanline = (QRgb*) dst.scanLine(y); 0421 // for (int i = 0; i < w; i++) 0422 // *(scanline++) = background; //qRgb(0,255,0); 0423 // y++; 0424 // } 0425 0426 for (; y < newHeight; y++) 0427 { 0428 // scanline = (QRgb*) dst.scanLine(y); 0429 0430 x2 = basis_ox; 0431 y2 = basis_oy + y * basis_yy; 0432 0433 if (y2 >= max_y2) 0434 break; 0435 0436 int x = 0; 0437 0438 // while ((x2 < 0 || (x2 >> 12) >= myIm->width()) && x < w) //fill the left of the target pixmap with the background color 0439 // { 0440 // *(scanline++) = background; //qRgb(0,0,255); 0441 // x2 += basis_xx; 0442 // x++; 0443 // } 0444 0445 int top = y2 >> 12; 0446 int bottom = top + 1; 0447 if (bottom >= myOrigHeight) 0448 bottom--; 0449 0450 // for (; x < w; x++) 0451 for (; x < newWidth; x++) // ### myOrigWidth orig 0452 { 0453 int left = x2 >> 12; 0454 int right = left + 1; 0455 0456 if (right >= myOrigWidth) 0457 right = myOrigWidth - 1; 0458 0459 unsigned int wx = x2 & 0xfff; //12 bits of precision for reasons which will become clear 0460 unsigned int wy = y2 & 0xfff; //12 bits of precision 0461 0462 unsigned int iwx = 0xfff - wx; 0463 unsigned int iwy = 0xfff - wy; 0464 0465 QRgb tl = 0, tr = 0, bl = 0, br = 0; 0466 int ind = 0; 0467 ind = (left + top * myOrigWidth) * 3; 0468 tl = (imdata[ind] << 16); 0469 tl |= (imdata[ind + 1] << 8); 0470 tl |= (imdata[ind + 2] << 0); 0471 int bar = imdata[ind + 2] << 8; 0472 bar = qBlue(bar); 0473 0474 ind = (right + top * myOrigWidth) * 3; 0475 tr = (imdata[ind] << 16); 0476 tr |= (imdata[ind + 1] << 8); 0477 tr |= (imdata[ind + 2] << 0); 0478 bar = imdata[ind + 2] << 8; 0479 0480 ind = (left + bottom * myOrigWidth) * 3; 0481 bl = (imdata[ind] << 16); 0482 bl |= (imdata[ind + 1] << 8); 0483 bl |= (imdata[ind + 2] << 0); 0484 bar = imdata[ind + 2] << 8; 0485 0486 ind = (right + bottom * myOrigWidth) * 3; 0487 br = (imdata[ind] << 16); 0488 br |= (imdata[ind + 1] << 8); 0489 br |= (imdata[ind + 2] << 0); 0490 // tl=imdata[top][left]; 0491 // tr=imdata[top][right]; 0492 // bl=imdata[bottom][left]; 0493 // br=imdata[bottom][right]; 0494 0495 /* 0496 tl=getValidPixel(myIm, left, top, x, y); //these calls are expensive 0497 tr=getValidPixel(myIm, right, top, x, y); //use them to debug segfaults in this function 0498 bl=getValidPixel(myIm, left, bottom, x, y); 0499 br=getValidPixel(myIm, right, bottom, x, y); 0500 */ 0501 0502 unsigned int r = (unsigned int) (qRed(tl) * iwx * iwy + qRed(tr) * wx* iwy + qRed(bl) * iwx * wy + qRed(br) * wx * wy); // NB 12+12+8 == 32 0503 unsigned int g = (unsigned int) (qGreen(tl) * iwx * iwy + qGreen(tr) * wx * iwy + qGreen(bl) * iwx * wy + qGreen(br) * wx * wy); 0504 unsigned int b = (unsigned int) (qBlue(tl) * iwx * iwy + qBlue(tr) * wx * iwy + qBlue(bl) * iwx * wy + qBlue(br) * wx * wy); 0505 0506 // ### endianness 0507 //we're actually off by one in 255 here! (254 instead of 255) 0508 int foo = r >> 24; 0509 foo = g >> 24; 0510 foo = b >> 24; 0511 newImageData[(y * newWidth * 3) + (x * 3) + 0] = (r >> 24); 0512 newImageData[(y * newWidth * 3) + (x * 3) + 1] = (g >> 24); 0513 newImageData[(y * newWidth * 3) + (x * 3) + 2] = (b >> 24); 0514 // *(scanline++) = qRgb(r >> 24, g >> 24, b >> 24); //we're actually off by one in 255 here 0515 0516 x2 += basis_xx; 0517 0518 if (x2 > max_x2) 0519 { 0520 x++; 0521 break; 0522 } 0523 0524 } 0525 0526 // while (x < w) //fill the right of each scanline with the background colour 0527 // { 0528 // *(scanline++) = background; //qRgb(255,0,0); 0529 // x++; 0530 // } 0531 } 0532 0533 // for (;;) //fill the bottom of the target pixmap with the background color 0534 // { 0535 // y2 = basis_oy + y * basis_yy; 0536 // 0537 // if (y >= h) 0538 // break; 0539 // 0540 // scanline = (QRgb*) dst.scanLine(y); 0541 // for (int i = 0; i < w; i++) 0542 // *(scanline++) = background; //qRgb(255,255,0); 0543 // y++; 0544 // } 0545 0546 // ### keep orig image somewhere but delete all scaled images! 0547 ImlibImage *newIm = Imlib_create_image_from_data( myId, newImageData, NULL, 0548 newWidth, newHeight ); 0549 delete[] newImageData; 0550 0551 if ( newIm ) 0552 { 0553 myScaledIm = newIm; 0554 myIsDirty = true; 0555 myWidth = newWidth; 0556 myHeight = newHeight; 0557 } 0558 0559 return myIm != 0L; 0560 // return dst.copy(); 0561 } 0562 #endif