|
|
|
#include "kuickimage.h"
|
|
|
|
|
|
|
|
KuickImage::KuickImage( const KuickFile * file, ImlibImage *im, ImlibData *id)
|
|
|
|
: TQObject( 0L, 0L )
|
|
|
|
{
|
|
|
|
myFile = file;
|
|
|
|
myOrigIm = 0L;
|
|
|
|
myIm = im;
|
|
|
|
myId = id;
|
|
|
|
myPixmap = 0L;
|
|
|
|
myWidth = im->rgb_width;
|
|
|
|
myHeight = im->rgb_height;
|
|
|
|
myIsDirty = true;
|
|
|
|
|
|
|
|
myOrigWidth = myWidth;
|
|
|
|
myOrigHeight = myHeight;
|
|
|
|
myRotation = ROT_0;
|
|
|
|
myFlipMode = FlipNone;
|
|
|
|
}
|
|
|
|
|
|
|
|
KuickImage::~KuickImage()
|
|
|
|
{
|
|
|
|
if ( myPixmap )
|
|
|
|
Imlib_free_pixmap( myId, myPixmap );
|
|
|
|
|
|
|
|
if ( myOrigIm )
|
|
|
|
{
|
|
|
|
Imlib_destroy_image( myId, myOrigIm );
|
|
|
|
Imlib_kill_image( myId, myIm ); // kill scaled image (### really? analyze!)
|
|
|
|
}
|
|
|
|
else
|
|
|
|
Imlib_destroy_image( myId, myIm );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Pixmap& KuickImage::pixmap()
|
|
|
|
{
|
|
|
|
if ( myIsDirty )
|
|
|
|
renderPixmap();
|
|
|
|
|
|
|
|
return myPixmap;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KuickImage::renderPixmap()
|
|
|
|
{
|
|
|
|
if ( !myIsDirty )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// tqDebug("### rendering: %s", myFilename.latin1());
|
|
|
|
|
|
|
|
if ( myPixmap )
|
|
|
|
Imlib_free_pixmap( myId, myPixmap );
|
|
|
|
|
|
|
|
emit startRendering();
|
|
|
|
|
|
|
|
// #ifndef NDEBUG
|
|
|
|
// struct timeval tms1, tms2;
|
|
|
|
// gettimeofday( &tms1, NULL );
|
|
|
|
// #endif
|
|
|
|
|
|
|
|
Imlib_render( myId, myIm, myWidth, myHeight );
|
|
|
|
myPixmap = Imlib_move_image( myId, myIm );
|
|
|
|
|
|
|
|
// #ifndef NDEBUG
|
|
|
|
// gettimeofday( &tms2, NULL );
|
|
|
|
// tqDebug("*** rendering image: %s, took %ld ms", myFilename.latin1(),
|
|
|
|
// (tms2.tv_usec - tms1.tv_usec)/1000);
|
|
|
|
// #endif
|
|
|
|
|
|
|
|
emit stoppedRendering();
|
|
|
|
|
|
|
|
myIsDirty = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KuickImage::rotate( Rotation rot )
|
|
|
|
{
|
|
|
|
if ( rot == ROT_180 ) { // rotate 180 degrees
|
|
|
|
Imlib_flip_image_horizontal( myId, myIm );
|
|
|
|
Imlib_flip_image_vertical( myId, myIm );
|
|
|
|
}
|
|
|
|
|
|
|
|
else if ( rot == ROT_90 || rot == ROT_270 ) {
|
|
|
|
tqSwap( myWidth, myHeight );
|
|
|
|
Imlib_rotate_image( myId, myIm, -1 );
|
|
|
|
|
|
|
|
if ( rot == ROT_90 ) // rotate 90 degrees
|
|
|
|
Imlib_flip_image_horizontal( myId, myIm );
|
|
|
|
else if ( rot == ROT_270 ) // rotate 270 degrees
|
|
|
|
Imlib_flip_image_vertical( myId, myIm );
|
|
|
|
}
|
|
|
|
|
|
|
|
myRotation = (Rotation) ((myRotation + rot) % 4);
|
|
|
|
myIsDirty = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool KuickImage::rotateAbs( Rotation rot )
|
|
|
|
{
|
|
|
|
if ( myRotation == rot )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int diff = rot - myRotation;
|
|
|
|
bool clockWise = (diff > 0);
|
|
|
|
|
|
|
|
switch( abs(diff) ) {
|
|
|
|
case ROT_90:
|
|
|
|
rotate( clockWise ? ROT_90 : ROT_270 );
|
|
|
|
break;
|
|
|
|
case ROT_180:
|
|
|
|
rotate( ROT_180 );
|
|
|
|
break;
|
|
|
|
case ROT_270:
|
|
|
|
rotate( clockWise ? ROT_270 : ROT_90 );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KuickImage::flip( FlipMode flipMode )
|
|
|
|
{
|
|
|
|
if ( flipMode & FlipHorizontal )
|
|
|
|
Imlib_flip_image_horizontal( myId, myIm );
|
|
|
|
if ( flipMode & FlipVertical )
|
|
|
|
Imlib_flip_image_vertical( myId, myIm );
|
|
|
|
|
|
|
|
myFlipMode = (FlipMode) (myFlipMode ^ flipMode);
|
|
|
|
myIsDirty = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KuickImage::flipAbs( int mode )
|
|
|
|
{
|
|
|
|
if ( myFlipMode == mode )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bool changed = false;
|
|
|
|
|
|
|
|
if ( ((myFlipMode & FlipHorizontal) && !(mode & FlipHorizontal)) ||
|
|
|
|
(!(myFlipMode & FlipHorizontal) && (mode & FlipHorizontal)) ) {
|
|
|
|
Imlib_flip_image_horizontal( myId, myIm );
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ((myFlipMode & FlipVertical) && !(mode & FlipVertical)) ||
|
|
|
|
(!(myFlipMode & FlipVertical) && (mode & FlipVertical)) ) {
|
|
|
|
Imlib_flip_image_vertical( myId, myIm );
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( changed ) {
|
|
|
|
myFlipMode = (FlipMode) mode;
|
|
|
|
myIsDirty = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KuickImage::restoreOriginalSize()
|
|
|
|
{
|
|
|
|
if (myWidth == myOrigWidth && myHeight == myOrigHeight)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// tqDebug("-- restoreOriginalSize");
|
|
|
|
|
|
|
|
if ( myOrigIm != 0L )
|
|
|
|
{
|
|
|
|
Imlib_destroy_image( myId, myIm );
|
|
|
|
myIm = myOrigIm;
|
|
|
|
myOrigIm = 0L;
|
|
|
|
}
|
|
|
|
|
|
|
|
myWidth = myOrigWidth;
|
|
|
|
myHeight = myOrigHeight;
|
|
|
|
myIsDirty = true;
|
|
|
|
|
|
|
|
if ( myRotation == ROT_90 || myRotation == ROT_270 )
|
|
|
|
tqSwap( myWidth, myHeight );
|
|
|
|
}
|
|
|
|
|
|
|
|
void KuickImage::resize( int width, int height, KuickImage::ResizeMode mode )
|
|
|
|
{
|
|
|
|
if ( myWidth == width && myHeight == height )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( mode == KuickImage::SMOOTH )
|
|
|
|
{
|
|
|
|
if ( !smoothResize( width, height ) )
|
|
|
|
fastResize( width, height );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fastResize( width, height );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void KuickImage::fastResize( int width, int height )
|
|
|
|
{
|
|
|
|
// tqDebug("-- fastResize: %i x %i", width, height );
|
|
|
|
|
|
|
|
// lazy resizing (only done when rendering pixmap)
|
|
|
|
myWidth = width;
|
|
|
|
myHeight = height;
|
|
|
|
myIsDirty = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool KuickImage::smoothResize( int newWidth, int newHeight )
|
|
|
|
{
|
|
|
|
// tqDebug("-- smoothResize: %i x %i", newWidth, newHeight);
|
|
|
|
|
|
|
|
TQImage *image = newTQImage();
|
|
|
|
// Note: TQ_ScaleMin seems to have a bug (off-by-one, sometimes results in width being 1 pixel too small)
|
|
|
|
TQImage scaledImage = image->smoothScale(newWidth, newHeight, TQ_ScaleFree);
|
|
|
|
|
|
|
|
delete image;
|
|
|
|
|
|
|
|
|
|
|
|
ImlibImage *newIm = toImage( myId, scaledImage );
|
|
|
|
if ( newIm )
|
|
|
|
{
|
|
|
|
if ( myOrigIm == 0 )
|
|
|
|
myOrigIm = myIm;
|
|
|
|
|
|
|
|
myIm = newIm;
|
|
|
|
myWidth = newWidth;
|
|
|
|
myHeight = newHeight;
|
|
|
|
myIsDirty = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
TQImage * KuickImage::newTQImage() const
|
|
|
|
{
|
|
|
|
ImlibImage *im;
|
|
|
|
|
|
|
|
// tqDebug("-- newTQImage");
|
|
|
|
|
|
|
|
if ( myOrigIm != 0L && myRotation == ROT_0 && myFlipMode == FlipNone )
|
|
|
|
{
|
|
|
|
// use original image if no other modifications have been applied
|
|
|
|
// ### use orig image always and reapply mods?
|
|
|
|
im = myOrigIm;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
im = myIm;
|
|
|
|
}
|
|
|
|
|
|
|
|
int w = im->rgb_width;
|
|
|
|
int h = im->rgb_height;
|
|
|
|
|
|
|
|
TQImage *image = new TQImage( w, h, 32 );
|
|
|
|
uchar *rgb = im->rgb_data;
|
|
|
|
TQRgb **destImageData = reinterpret_cast<TQRgb**>( image->jumpTable() );
|
|
|
|
|
|
|
|
|
|
|
|
int byteIndex = 0;
|
|
|
|
int destLineIndex = 0;
|
|
|
|
int destByteIndex = 0;
|
|
|
|
for ( int pixel = 0; pixel < (w * h); pixel++ )
|
|
|
|
{
|
|
|
|
if ( pixel != 0 && (pixel % w) == 0 )
|
|
|
|
{
|
|
|
|
destLineIndex++;
|
|
|
|
destByteIndex = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uchar r = rgb[byteIndex++];
|
|
|
|
uchar g = rgb[byteIndex++];
|
|
|
|
uchar b = rgb[byteIndex++];
|
|
|
|
|
|
|
|
TQRgb rgbPixel = tqRgb( r, g, b );
|
|
|
|
destImageData[destLineIndex][destByteIndex++] = rgbPixel;
|
|
|
|
}
|
|
|
|
|
|
|
|
return image;
|
|
|
|
}
|
|
|
|
|
|
|
|
ImlibImage * KuickImage::toImage( ImlibData *id, TQImage& image )
|
|
|
|
{
|
|
|
|
if ( image.isNull() )
|
|
|
|
return 0L;
|
|
|
|
|
|
|
|
if ( image.depth() != 32 )
|
|
|
|
{
|
|
|
|
image.setAlphaBuffer(false);
|
|
|
|
image = image.convertDepth(32);
|
|
|
|
|
|
|
|
if ( image.isNull() )
|
|
|
|
return 0L;
|
|
|
|
}
|
|
|
|
|
|
|
|
// convert to 24 bpp (discard alpha)
|
|
|
|
int numPixels = image.width() * image.height();
|
|
|
|
const int NUM_BYTES_NEW = 3; // 24 bpp
|
|
|
|
uchar *newImageData = new uchar[numPixels * NUM_BYTES_NEW];
|
|
|
|
uchar *newData = newImageData;
|
|
|
|
|
|
|
|
int w = image.width();
|
|
|
|
int h = image.height();
|
|
|
|
|
|
|
|
for (int y = 0; y < h; y++) {
|
|
|
|
TQRgb *scanLine = reinterpret_cast<TQRgb *>( image.scanLine(y) );
|
|
|
|
for (int x = 0; x < w; x++) {
|
|
|
|
const TQRgb& pixel = scanLine[x];
|
|
|
|
*(newData++) = tqRed(pixel);
|
|
|
|
*(newData++) = tqGreen(pixel);
|
|
|
|
*(newData++) = tqBlue(pixel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ImlibImage *im = Imlib_create_image_from_data( id, newImageData, NULL,
|
|
|
|
image.width(), image.height() );
|
|
|
|
|
|
|
|
delete [] newImageData;
|
|
|
|
|
|
|
|
return im;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
bool KuickImage::smoothResize( int newWidth, int newHeight )
|
|
|
|
{
|
|
|
|
int numPixels = newWidth * newHeight;
|
|
|
|
const int NUM_BYTES_NEW = 3; // 24 bpp
|
|
|
|
uchar *newImageData = new uchar[numPixels * NUM_BYTES_NEW];
|
|
|
|
|
|
|
|
// ### endianness
|
|
|
|
// myIm : old image, old size
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////
|
|
|
|
// int w = myOrigWidth; //myViewport.width();
|
|
|
|
//int h = myOrigHeight; //myViewport.height();
|
|
|
|
|
|
|
|
//TQImage dst(w, h, myIm->depth(), myIm->numColors(), myIm->bitOrder());
|
|
|
|
|
|
|
|
//TQRgb *scanline;
|
|
|
|
|
|
|
|
int basis_ox, basis_oy, basis_xx, basis_yy;
|
|
|
|
|
|
|
|
// ### we only scale with a fixed factor for x and y anyway
|
|
|
|
double scalex = newWidth / (double) myOrigWidth;
|
|
|
|
double scaley = newHeight / (double) myOrigHeight;
|
|
|
|
|
|
|
|
// basis_ox=(int) (myViewport.left() * 4096.0 / scalex);
|
|
|
|
// basis_oy=(int) (myViewport.top() * 4096.0 / scaley);
|
|
|
|
basis_ox = 0;
|
|
|
|
basis_oy = 0;
|
|
|
|
basis_xx = (int) (4096.0 / scalex);
|
|
|
|
basis_yy = (int) (4096.0 / scaley);
|
|
|
|
|
|
|
|
//tqDebug("Basis: (%d, %d), (%d, 0), (0, %d)", basis_ox, basis_oy, basis_xx, basis_yy);
|
|
|
|
|
|
|
|
int x2, y2;
|
|
|
|
|
|
|
|
int max_x2 = (myOrigWidth << 12);
|
|
|
|
int max_y2 = (myOrigHeight << 12);
|
|
|
|
|
|
|
|
// TQRgb background = idata->backgroundColor.rgb();
|
|
|
|
|
|
|
|
// TQRgb **imdata = (TQRgb **) myIm->jumpTable();
|
|
|
|
// TQRgb *imdata = reinterpret_cast<TQRgb*>( myIm->rgb_data );
|
|
|
|
uchar *imdata = myIm->rgb_data;
|
|
|
|
|
|
|
|
|
|
|
|
int y = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// for (;;) //fill the top of the target pixmap with the background color
|
|
|
|
// {
|
|
|
|
// y2 = basis_oy + y * basis_yy;
|
|
|
|
//
|
|
|
|
// if ((y2 >= 0 && (y2 >> 12) < myIm->height()) || y >= h)
|
|
|
|
// break;
|
|
|
|
//
|
|
|
|
// scanline = (TQRgb*) dst.scanLine(y);
|
|
|
|
// for (int i = 0; i < w; i++)
|
|
|
|
// *(scanline++) = background; //tqRgb(0,255,0);
|
|
|
|
// y++;
|
|
|
|
// }
|
|
|
|
|
|
|
|
for (; y < newHeight; y++)
|
|
|
|
{
|
|
|
|
// scanline = (TQRgb*) dst.scanLine(y);
|
|
|
|
|
|
|
|
x2 = basis_ox;
|
|
|
|
y2 = basis_oy + y * basis_yy;
|
|
|
|
|
|
|
|
if (y2 >= max_y2)
|
|
|
|
break;
|
|
|
|
|
|
|
|
int x = 0;
|
|
|
|
|
|
|
|
// while ((x2 < 0 || (x2 >> 12) >= myIm->width()) && x < w) //fill the left of the target pixmap with the background color
|
|
|
|
// {
|
|
|
|
// *(scanline++) = background; //tqRgb(0,0,255);
|
|
|
|
// x2 += basis_xx;
|
|
|
|
// x++;
|
|
|
|
// }
|
|
|
|
|
|
|
|
int top = y2 >> 12;
|
|
|
|
int bottom = top + 1;
|
|
|
|
if (bottom >= myOrigHeight)
|
|
|
|
bottom--;
|
|
|
|
|
|
|
|
// for (; x < w; x++)
|
|
|
|
for (; x < newWidth; x++) // ### myOrigWidth orig
|
|
|
|
{
|
|
|
|
int left = x2 >> 12;
|
|
|
|
int right = left + 1;
|
|
|
|
|
|
|
|
if (right >= myOrigWidth)
|
|
|
|
right = myOrigWidth - 1;
|
|
|
|
|
|
|
|
unsigned int wx = x2 & 0xfff; //12 bits of precision for reasons which will become clear
|
|
|
|
unsigned int wy = y2 & 0xfff; //12 bits of precision
|
|
|
|
|
|
|
|
unsigned int iwx = 0xfff - wx;
|
|
|
|
unsigned int iwy = 0xfff - wy;
|
|
|
|
|
|
|
|
TQRgb tl = 0, tr = 0, bl = 0, br = 0;
|
|
|
|
int ind = 0;
|
|
|
|
ind = (left + top * myOrigWidth) * 3;
|
|
|
|
tl = (imdata[ind] << 16);
|
|
|
|
tl |= (imdata[ind + 1] << 8);
|
|
|
|
tl |= (imdata[ind + 2] << 0);
|
|
|
|
int bar = imdata[ind + 2] << 8;
|
|
|
|
bar = tqBlue(bar);
|
|
|
|
|
|
|
|
ind = (right + top * myOrigWidth) * 3;
|
|
|
|
tr = (imdata[ind] << 16);
|
|
|
|
tr |= (imdata[ind + 1] << 8);
|
|
|
|
tr |= (imdata[ind + 2] << 0);
|
|
|
|
bar = imdata[ind + 2] << 8;
|
|
|
|
|
|
|
|
ind = (left + bottom * myOrigWidth) * 3;
|
|
|
|
bl = (imdata[ind] << 16);
|
|
|
|
bl |= (imdata[ind + 1] << 8);
|
|
|
|
bl |= (imdata[ind + 2] << 0);
|
|
|
|
bar = imdata[ind + 2] << 8;
|
|
|
|
|
|
|
|
ind = (right + bottom * myOrigWidth) * 3;
|
|
|
|
br = (imdata[ind] << 16);
|
|
|
|
br |= (imdata[ind + 1] << 8);
|
|
|
|
br |= (imdata[ind + 2] << 0);
|
|
|
|
// tl=imdata[top][left];
|
|
|
|
// tr=imdata[top][right];
|
|
|
|
// bl=imdata[bottom][left];
|
|
|
|
// br=imdata[bottom][right];
|
|
|
|
|
|
|
|
/*
|
|
|
|
tl=getValidPixel(myIm, left, top, x, y); //these calls are expensive
|
|
|
|
tr=getValidPixel(myIm, right, top, x, y); //use them to debug segfaults in this function
|
|
|
|
bl=getValidPixel(myIm, left, bottom, x, y);
|
|
|
|
br=getValidPixel(myIm, right, bottom, x, y);
|
|
|
|
*/
|
|
|
|
|
|
|
|
unsigned int r = (unsigned int) (tqRed(tl) * iwx * iwy + tqRed(tr) * wx* iwy + tqRed(bl) * iwx * wy + tqRed(br) * wx * wy); // NB 12+12+8 == 32
|
|
|
|
unsigned int g = (unsigned int) (tqGreen(tl) * iwx * iwy + tqGreen(tr) * wx * iwy + tqGreen(bl) * iwx * wy + tqGreen(br) * wx * wy);
|
|
|
|
unsigned int b = (unsigned int) (tqBlue(tl) * iwx * iwy + tqBlue(tr) * wx * iwy + tqBlue(bl) * iwx * wy + tqBlue(br) * wx * wy);
|
|
|
|
|
|
|
|
// ### endianness
|
|
|
|
//we're actually off by one in 255 here! (254 instead of 255)
|
|
|
|
int foo = r >> 24;
|
|
|
|
foo = g >> 24;
|
|
|
|
foo = b >> 24;
|
|
|
|
newImageData[(y * newWidth * 3) + (x * 3) + 0] = (r >> 24);
|
|
|
|
newImageData[(y * newWidth * 3) + (x * 3) + 1] = (g >> 24);
|
|
|
|
newImageData[(y * newWidth * 3) + (x * 3) + 2] = (b >> 24);
|
|
|
|
// *(scanline++) = tqRgb(r >> 24, g >> 24, b >> 24); //we're actually off by one in 255 here
|
|
|
|
|
|
|
|
x2 += basis_xx;
|
|
|
|
|
|
|
|
if (x2 > max_x2)
|
|
|
|
{
|
|
|
|
x++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// while (x < w) //fill the right of each scanline with the background colour
|
|
|
|
// {
|
|
|
|
// *(scanline++) = background; //tqRgb(255,0,0);
|
|
|
|
// x++;
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
|
|
// for (;;) //fill the bottom of the target pixmap with the background color
|
|
|
|
// {
|
|
|
|
// y2 = basis_oy + y * basis_yy;
|
|
|
|
//
|
|
|
|
// if (y >= h)
|
|
|
|
// break;
|
|
|
|
//
|
|
|
|
// scanline = (TQRgb*) dst.scanLine(y);
|
|
|
|
// for (int i = 0; i < w; i++)
|
|
|
|
// *(scanline++) = background; //tqRgb(255,255,0);
|
|
|
|
// y++;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// ### keep orig image somewhere but delete all scaled images!
|
|
|
|
ImlibImage *newIm = Imlib_create_image_from_data( myId, newImageData, NULL,
|
|
|
|
newWidth, newHeight );
|
|
|
|
delete[] newImageData;
|
|
|
|
|
|
|
|
if ( newIm )
|
|
|
|
{
|
|
|
|
myScaledIm = newIm;
|
|
|
|
myIsDirty = true;
|
|
|
|
myWidth = newWidth;
|
|
|
|
myHeight = newHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
return myIm != 0L;
|
|
|
|
// return dst.copy();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "kuickimage.moc"
|