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.
gwenview/src/gvcore/xpm.cpp

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,
TQT_RGB_MASK & tqRgb(198,198,198) );
colorMap.insert( index, transparentColor );
} else {
TQRgb rgb = TQT_RGB_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 & TQT_RGB_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