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