You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
440 lines
11 KiB
440 lines
11 KiB
/* This file is based on qt-3.3.2/src/qimage.cpp , plus it includes
|
|
* a fix from qt-bugs@ issue #49934. Original copyright follows.
|
|
*/
|
|
/****************************************************************************
|
|
**
|
|
**
|
|
** Implementation of TQImage and TQImageIO classes
|
|
**
|
|
** Created : 950207
|
|
**
|
|
** Copyright (C) 1992-2003 Trolltech AS. All rights reserved.
|
|
**
|
|
** This file is part of the kernel module of the TQt GUI Toolkit.
|
|
**
|
|
** This file may be distributed under the terms of the Q Public License
|
|
** as defined by Trolltech AS of Norway and appearing in the file
|
|
** LICENSE.TQPL included in the packaging of this file.
|
|
**
|
|
** This file may be distributed and/or modified under the terms of the
|
|
** GNU General Public License version 2 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
|
** packaging of this file.
|
|
**
|
|
** Licensees holding valid TQt Enterprise Edition or TQt Professional Edition
|
|
** licenses may use this file in accordance with the TQt Commercial License
|
|
** Agreement provided with the Software.
|
|
**
|
|
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
|
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
**
|
|
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
|
|
** information about TQt Commercial License Agreements.
|
|
** See http://www.trolltech.com/qpl/ for TQPL licensing information.
|
|
** See http://www.trolltech.com/gpl/ for GPL licensing information.
|
|
**
|
|
** Contact info@trolltech.com if any conditions of this licensing are
|
|
** not clear to you.
|
|
**
|
|
**********************************************************************/
|
|
|
|
/*
|
|
|
|
This code is xpm loader copied from qimage.cpp, with changes making it
|
|
possible to use this class from another thread. The problem is that
|
|
TQColor::setNamedColor() isn't reentrant without thread-safe Xlib,
|
|
i.e. without XInitThreads() called. Current KDE applications
|
|
don't call XInitThreads(), and since it needs to be the very first
|
|
Xlib function called, and Gwenview is also a KPart, it's not possible
|
|
to rely on Xlib being thread-safe.
|
|
|
|
Changes are marked using #ifdef GV_XPM_CHANGES.
|
|
|
|
*/
|
|
|
|
#define GV_XPM_CHANGES
|
|
|
|
#include "xpm.h"
|
|
|
|
#include <tqimage.h>
|
|
#include <tqmap.h>
|
|
#include <tqregexp.h>
|
|
|
|
#include "threadgate.h"
|
|
|
|
namespace Gwenview {
|
|
|
|
// TQt code start ---------------------------
|
|
static TQString fbname( const TQString &fileName ) // get file basename (sort of)
|
|
{
|
|
TQString s = fileName;
|
|
if ( !s.isEmpty() ) {
|
|
int i;
|
|
if ( (i = s.findRev('/')) >= 0 )
|
|
s = s.mid( i );
|
|
if ( (i = s.findRev('\\')) >= 0 )
|
|
s = s.mid( i );
|
|
TQRegExp r( TQString::fromLatin1("[a-zA-Z][a-zA-Z0-9_]*") );
|
|
int p = r.search( s );
|
|
if ( p == -1 )
|
|
s.truncate( 0 );
|
|
else
|
|
s = s.mid( p, r.matchedLength() );
|
|
}
|
|
if ( s.isEmpty() )
|
|
s = TQString::fromLatin1( "dummy" );
|
|
return s;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
XPM image read/write functions
|
|
*****************************************************************************/
|
|
|
|
|
|
// Skip until ", read until the next ", return the rest in *buf
|
|
// Returns FALSE on error, TRUE on success
|
|
|
|
static bool read_xpm_string( TQCString &buf, TQIODevice *d,
|
|
const char * const *source, int &index )
|
|
{
|
|
if ( source ) {
|
|
buf = source[index++];
|
|
return TRUE;
|
|
}
|
|
|
|
if ( buf.size() < 69 ) //# just an approximation
|
|
buf.resize( 123 );
|
|
|
|
buf[0] = '\0';
|
|
int c;
|
|
int i;
|
|
while ( (c=d->getch()) != EOF && c != '"' ) { }
|
|
if ( c == EOF ) {
|
|
return FALSE;
|
|
}
|
|
i = 0;
|
|
while ( (c=d->getch()) != EOF && c != '"' ) {
|
|
if ( i == (int)buf.size() )
|
|
buf.resize( i*2+42 );
|
|
buf[i++] = c;
|
|
}
|
|
if ( c == EOF ) {
|
|
return FALSE;
|
|
}
|
|
|
|
if ( i == (int)buf.size() ) // always use a 0 terminator
|
|
buf.resize( i+1 );
|
|
buf[i] = '\0';
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
static int nextColorSpec(const TQCString & buf)
|
|
{
|
|
int i = buf.find(" c ");
|
|
if (i < 0)
|
|
i = buf.find(" g ");
|
|
if (i < 0)
|
|
i = buf.find(" g4 ");
|
|
if (i < 0)
|
|
i = buf.find(" m ");
|
|
if (i < 0)
|
|
i = buf.find(" s ");
|
|
return i;
|
|
}
|
|
|
|
//
|
|
// INTERNAL
|
|
//
|
|
// Reads an .xpm from either the TQImageIO or from the TQString *.
|
|
// One of the two HAS to be 0, the other one is used.
|
|
//
|
|
|
|
static void read_xpm_image_or_array( TQImageIO * iio, const char * const * source,
|
|
TQImage & image)
|
|
{
|
|
TQCString buf;
|
|
TQIODevice *d = 0;
|
|
buf.resize( 200 );
|
|
|
|
int i, cpp, ncols, w, h, index = 0;
|
|
|
|
if ( iio ) {
|
|
iio->setStatus( 1 );
|
|
d = iio ? iio->ioDevice() : 0;
|
|
d->readLine( buf.data(), buf.size() ); // "/* XPM */"
|
|
TQRegExp r( TQString::fromLatin1("/\\*.XPM.\\*/") );
|
|
if ( buf.find(r) == -1 )
|
|
return; // bad magic
|
|
} else if ( !source ) {
|
|
return;
|
|
}
|
|
|
|
if ( !read_xpm_string( buf, d, source, index ) )
|
|
return;
|
|
|
|
if ( sscanf( buf, "%d %d %d %d", &w, &h, &ncols, &cpp ) < 4 )
|
|
return; // < 4 numbers parsed
|
|
|
|
if ( cpp > 15 )
|
|
return;
|
|
|
|
if ( ncols > 256 ) {
|
|
image.create( w, h, 32 );
|
|
} else {
|
|
image.create( w, h, 8, ncols );
|
|
}
|
|
|
|
if (image.isNull())
|
|
return;
|
|
|
|
TQMap<TQString, int> colorMap;
|
|
int currentColor;
|
|
|
|
for( currentColor=0; currentColor < ncols; ++currentColor ) {
|
|
if ( !read_xpm_string( buf, d, source, index ) ) {
|
|
#if defined(TQT_CHECK_RANGE)
|
|
tqWarning( "TQImage: XPM color specification missing");
|
|
#endif
|
|
return;
|
|
}
|
|
TQString index;
|
|
index = buf.left( cpp );
|
|
buf = buf.mid( cpp ).simplifyWhiteSpace().lower();
|
|
buf.prepend( " " );
|
|
i = nextColorSpec(buf);
|
|
if ( i < 0 ) {
|
|
#if defined(TQT_CHECK_RANGE)
|
|
tqWarning( "TQImage: XPM color specification is missing: %s", buf.data());
|
|
#endif
|
|
return; // no c/g/g4/m/s specification at all
|
|
}
|
|
buf = buf.mid( i+3 );
|
|
// Strip any other colorspec
|
|
int end = nextColorSpec(buf);
|
|
if (end != -1)
|
|
buf.truncate(end);
|
|
buf = buf.stripWhiteSpace();
|
|
if ( buf == "none" ) {
|
|
image.setAlphaBuffer( TRUE );
|
|
int transparentColor = currentColor;
|
|
if ( image.depth() == 8 ) {
|
|
image.setColor( transparentColor,
|
|
TQRGB_MASK & tqRgb(198,198,198) );
|
|
colorMap.insert( index, transparentColor );
|
|
} else {
|
|
TQRgb rgb = TQRGB_MASK & tqRgb(198,198,198);
|
|
colorMap.insert( index, rgb );
|
|
}
|
|
} else {
|
|
if ( ((buf.length()-1) % 3) && (buf[0] == '#') ) {
|
|
buf.truncate (((buf.length()-1) / 4 * 3) + 1); // remove alpha channel left by imagemagick
|
|
}
|
|
#ifdef GV_XPM_CHANGES
|
|
TQColor c = ThreadGate::instance()->color( buf.data());
|
|
#else
|
|
TQColor c( buf.data() );
|
|
#endif
|
|
if ( image.depth() == 8 ) {
|
|
image.setColor( currentColor, 0xff000000 | c.rgb() );
|
|
colorMap.insert( index, currentColor );
|
|
} else {
|
|
TQRgb rgb = 0xff000000 | c.rgb();
|
|
colorMap.insert( index, rgb );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Read pixels
|
|
for( int y=0; y<h; y++ ) {
|
|
if ( !read_xpm_string( buf, d, source, index ) ) {
|
|
#if defined(TQT_CHECK_RANGE)
|
|
tqWarning( "TQImage: XPM pixels missing on image line %d", y);
|
|
#endif
|
|
return;
|
|
}
|
|
if ( image.depth() == 8 ) {
|
|
uchar *p = image.scanLine(y);
|
|
uchar *d = (uchar *)buf.data();
|
|
uchar *end = d + buf.length();
|
|
int x;
|
|
if ( cpp == 1 ) {
|
|
char b[2];
|
|
b[1] = '\0';
|
|
for ( x=0; x<w && d<end; x++ ) {
|
|
b[0] = *d++;
|
|
*p++ = (uchar)colorMap[b];
|
|
}
|
|
} else {
|
|
char b[16];
|
|
b[cpp] = '\0';
|
|
for ( x=0; x<w && d<end; x++ ) {
|
|
strncpy( b, (char *)d, cpp );
|
|
*p++ = (uchar)colorMap[b];
|
|
d += cpp;
|
|
}
|
|
}
|
|
} else {
|
|
TQRgb *p = (TQRgb*)image.scanLine(y);
|
|
uchar *d = (uchar *)buf.data();
|
|
uchar *end = d + buf.length();
|
|
int x;
|
|
char b[16];
|
|
b[cpp] = '\0';
|
|
for ( x=0; x<w && d<end; x++ ) {
|
|
strncpy( b, (char *)d, cpp );
|
|
*p++ = (TQRgb)colorMap[b];
|
|
d += cpp;
|
|
}
|
|
}
|
|
}
|
|
if ( iio ) {
|
|
iio->setImage( image );
|
|
iio->setStatus( 0 ); // image ok
|
|
}
|
|
}
|
|
|
|
|
|
static void read_xpm_image( TQImageIO * iio )
|
|
{
|
|
TQImage i;
|
|
(void)read_xpm_image_or_array( iio, 0, i );
|
|
return;
|
|
}
|
|
|
|
|
|
static const char* xpm_color_name( int cpp, int index )
|
|
{
|
|
static char returnable[5];
|
|
static const char code[] = ".#abcdefghijklmnopqrstuvwxyzABCD"
|
|
"EFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
// cpp is limited to 4 and index is limited to 64^cpp
|
|
if ( cpp > 1 ) {
|
|
if ( cpp > 2 ) {
|
|
if ( cpp > 3 ) {
|
|
returnable[3] = code[index % 64];
|
|
index /= 64;
|
|
} else
|
|
returnable[3] = '\0';
|
|
returnable[2] = code[index % 64];
|
|
index /= 64;
|
|
} else
|
|
returnable[2] = '\0';
|
|
// the following 4 lines are a joke!
|
|
if ( index == 0 )
|
|
index = 64*44+21;
|
|
else if ( index == 64*44+21 )
|
|
index = 0;
|
|
returnable[1] = code[index % 64];
|
|
index /= 64;
|
|
} else
|
|
returnable[1] = '\0';
|
|
returnable[0] = code[index];
|
|
|
|
return returnable;
|
|
}
|
|
|
|
|
|
// write XPM image data
|
|
static void write_xpm_image( TQImageIO * iio )
|
|
{
|
|
if ( iio )
|
|
iio->setStatus( 1 );
|
|
else
|
|
return;
|
|
|
|
// ### 8-bit case could be made faster
|
|
TQImage image;
|
|
if ( iio->image().depth() != 32 )
|
|
image = iio->image().convertDepth( 32 );
|
|
else
|
|
image = iio->image();
|
|
|
|
TQMap<TQRgb, int> colorMap;
|
|
|
|
int w = image.width(), h = image.height(), ncolors = 0;
|
|
int x, y;
|
|
|
|
// build color table
|
|
for( y=0; y<h; y++ ) {
|
|
TQRgb * yp = (TQRgb *)image.scanLine( y );
|
|
for( x=0; x<w; x++ ) {
|
|
TQRgb color = *(yp + x);
|
|
if ( !colorMap.contains(color) )
|
|
colorMap.insert( color, ncolors++ );
|
|
}
|
|
}
|
|
|
|
// number of 64-bit characters per pixel needed to encode all colors
|
|
int cpp = 1;
|
|
for ( int k = 64; ncolors > k; k *= 64 ) {
|
|
++cpp;
|
|
// limit to 4 characters per pixel
|
|
// 64^4 colors is enough for a 4096x4096 image
|
|
if ( cpp > 4)
|
|
break;
|
|
}
|
|
|
|
TQString line;
|
|
|
|
// write header
|
|
TQTextStream s( iio->ioDevice() );
|
|
s << "/* XPM */" << endl
|
|
<< "static char *" << fbname(iio->fileName()) << "[]={" << endl
|
|
<< "\"" << w << " " << h << " " << ncolors << " " << cpp << "\"";
|
|
|
|
// write palette
|
|
TQMap<TQRgb, int>::Iterator c = colorMap.begin();
|
|
while ( c != colorMap.end() ) {
|
|
TQRgb color = c.key();
|
|
if ( image.hasAlphaBuffer() && color == (color & TQRGB_MASK) )
|
|
line.sprintf( "\"%s c None\"",
|
|
xpm_color_name(cpp, *c) );
|
|
else
|
|
line.sprintf( "\"%s c #%02x%02x%02x\"",
|
|
xpm_color_name(cpp, *c),
|
|
tqRed(color),
|
|
tqGreen(color),
|
|
tqBlue(color) );
|
|
++c;
|
|
s << "," << endl << line;
|
|
}
|
|
|
|
// write pixels, limit to 4 characters per pixel
|
|
line.truncate( cpp*w );
|
|
for( y=0; y<h; y++ ) {
|
|
TQRgb * yp = (TQRgb *) image.scanLine( y );
|
|
int cc = 0;
|
|
for( x=0; x<w; x++ ) {
|
|
int color = (int)(*(yp + x));
|
|
TQCString chars = xpm_color_name( cpp, colorMap[color] );
|
|
line[cc++] = chars[0];
|
|
if ( cpp > 1 ) {
|
|
line[cc++] = chars[1];
|
|
if ( cpp > 2 ) {
|
|
line[cc++] = chars[2];
|
|
if ( cpp > 3 ) {
|
|
line[cc++] = chars[3];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
s << "," << endl << "\"" << line << "\"";
|
|
}
|
|
s << "};" << endl;
|
|
|
|
iio->setStatus( 0 );
|
|
}
|
|
|
|
// TQt code end ---------------------------
|
|
|
|
XPM::XPM()
|
|
{
|
|
TQImageIO::inputFormats(); // trigger registration of TQt's handlers
|
|
TQImageIO::defineIOHandler( "XPM", "/\\*.XPM.\\*/", "T",
|
|
read_xpm_image, write_xpm_image );
|
|
}
|
|
|
|
} // namespace
|