~ubuntu-branches/ubuntu/intrepid/gwenview/intrepid

« back to all changes in this revision

Viewing changes to src/gvxpm.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Christopher Martin
  • Date: 2005-04-06 11:33:06 UTC
  • mfrom: (1.2.1 upstream) (2.1.1 warty)
  • Revision ID: james.westby@ubuntu.com-20050406113306-7zovl7z0io5bacpd
Tags: 1.2.0-1
* New upstream release.
  + Fixes crashes when using "Back" to navigate. (Closes: #301811)
* Enable KIPI support.
* Add a doc-base file for the handbook.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* This file is based on qt-3.3.2/src/qimage.cpp , plus it includes
 
2
 * a fix from qt-bugs@ issue #49934. Original copyright follows.
 
3
 */
 
4
/****************************************************************************
 
5
** 
 
6
**
 
7
** Implementation of QImage and QImageIO classes
 
8
**
 
9
** Created : 950207
 
10
**
 
11
** Copyright (C) 1992-2003 Trolltech AS.  All rights reserved.
 
12
**
 
13
** This file is part of the kernel module of the Qt GUI Toolkit.
 
14
**
 
15
** This file may be distributed under the terms of the Q Public License
 
16
** as defined by Trolltech AS of Norway and appearing in the file
 
17
** LICENSE.QPL included in the packaging of this file.
 
18
**
 
19
** This file may be distributed and/or modified under the terms of the
 
20
** GNU General Public License version 2 as published by the Free Software
 
21
** Foundation and appearing in the file LICENSE.GPL included in the
 
22
** packaging of this file.
 
23
**
 
24
** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
 
25
** licenses may use this file in accordance with the Qt Commercial License
 
26
** Agreement provided with the Software.
 
27
**
 
28
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
29
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
30
**
 
31
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
 
32
**   information about Qt Commercial License Agreements.
 
33
** See http://www.trolltech.com/qpl/ for QPL licensing information.
 
34
** See http://www.trolltech.com/gpl/ for GPL licensing information.
 
35
**
 
36
** Contact info@trolltech.com if any conditions of this licensing are
 
37
** not clear to you.
 
38
**
 
39
**********************************************************************/
 
40
 
 
41
/*
 
42
 
 
43
 This code is xpm loader copied from qimage.cpp, with changes making it
 
44
 possible to use this class from another thread. The problem is that
 
45
 QColor::setNamedColor() isn't reentrant without thread-safe Xlib,
 
46
 i.e. without XInitThreads() called. Current KDE applications
 
47
 don't call XInitThreads(), and since it needs to be the very first
 
48
 Xlib function called, and Gwenview is also a KPart, it's not possible
 
49
 to rely on Xlib being thread-safe.
 
50
 
 
51
 Changes are marked using #ifdef GV_XPM_CHANGES.
 
52
 
 
53
*/
 
54
 
 
55
#define GV_XPM_CHANGES
 
56
 
 
57
#include "gvxpm.h"
 
58
 
 
59
#include <qimage.h>
 
60
#include <qmap.h>
 
61
#include <qregexp.h>
 
62
 
 
63
#include "gvthreadgate.h"
 
64
 
 
65
 
 
66
// Qt code start ---------------------------
 
67
static QString fbname( const QString &fileName ) // get file basename (sort of)
 
68
{
 
69
    QString s = fileName;
 
70
    if ( !s.isEmpty() ) {
 
71
        int i;
 
72
        if ( (i = s.findRev('/')) >= 0 )
 
73
            s = s.mid( i );
 
74
        if ( (i = s.findRev('\\')) >= 0 )
 
75
            s = s.mid( i );
 
76
        QRegExp r( QString::fromLatin1("[a-zA-Z][a-zA-Z0-9_]*") );
 
77
        int p = r.search( s );
 
78
        if ( p == -1 )
 
79
            s.truncate( 0 );
 
80
        else
 
81
            s = s.mid( p, r.matchedLength() );
 
82
    }
 
83
    if ( s.isEmpty() )
 
84
        s = QString::fromLatin1( "dummy" );
 
85
    return s;
 
86
}
 
87
 
 
88
/*****************************************************************************
 
89
  XPM image read/write functions
 
90
 *****************************************************************************/
 
91
 
 
92
 
 
93
// Skip until ", read until the next ", return the rest in *buf
 
94
// Returns FALSE on error, TRUE on success
 
95
 
 
96
static bool read_xpm_string( QCString &buf, QIODevice *d,
 
97
                             const char * const *source, int &index )
 
98
{
 
99
    if ( source ) {
 
100
        buf = source[index++];
 
101
        return TRUE;
 
102
    }
 
103
 
 
104
    if ( buf.size() < 69 )          //# just an approximation
 
105
        buf.resize( 123 );
 
106
 
 
107
    buf[0] = '\0';
 
108
    int c;
 
109
    int i;
 
110
    while ( (c=d->getch()) != EOF && c != '"' ) { }
 
111
    if ( c == EOF ) {
 
112
        return FALSE;
 
113
    }
 
114
    i = 0;
 
115
    while ( (c=d->getch()) != EOF && c != '"' ) {
 
116
        if ( i == (int)buf.size() )
 
117
            buf.resize( i*2+42 );
 
118
        buf[i++] = c;
 
119
    }
 
120
    if ( c == EOF ) {
 
121
        return FALSE;
 
122
    }
 
123
 
 
124
    if ( i == (int)buf.size() ) // always use a 0 terminator
 
125
        buf.resize( i+1 );
 
126
    buf[i] = '\0';
 
127
    return TRUE;
 
128
}
 
129
 
 
130
 
 
131
 
 
132
static int nextColorSpec(const QCString & buf)
 
133
{
 
134
    int i = buf.find(" c ");
 
135
    if (i < 0)
 
136
        i = buf.find(" g ");
 
137
    if (i < 0)
 
138
        i = buf.find(" g4 ");
 
139
    if (i < 0)
 
140
        i = buf.find(" m ");
 
141
    if (i < 0)
 
142
        i = buf.find(" s ");
 
143
    return i;
 
144
}
 
145
 
 
146
//
 
147
// INTERNAL
 
148
//
 
149
// Reads an .xpm from either the QImageIO or from the QString *.
 
150
// One of the two HAS to be 0, the other one is used.
 
151
//
 
152
 
 
153
static void read_xpm_image_or_array( QImageIO * iio, const char * const * source,
 
154
                                     QImage & image)
 
155
{
 
156
    QCString buf;
 
157
    QIODevice *d = 0;
 
158
    buf.resize( 200 );
 
159
 
 
160
    int i, cpp, ncols, w, h, index = 0;
 
161
 
 
162
    if ( iio ) {
 
163
        iio->setStatus( 1 );
 
164
        d = iio ? iio->ioDevice() : 0;
 
165
        d->readLine( buf.data(), buf.size() );  // "/* XPM */"
 
166
        QRegExp r( QString::fromLatin1("/\\*.XPM.\\*/") );
 
167
        if ( buf.find(r) == -1 )
 
168
            return;                                     // bad magic
 
169
    } else if ( !source ) {
 
170
        return;
 
171
    }
 
172
 
 
173
    if ( !read_xpm_string( buf, d, source, index ) )
 
174
        return;
 
175
 
 
176
    if ( sscanf( buf, "%d %d %d %d", &w, &h, &ncols, &cpp ) < 4 )
 
177
        return;                                 // < 4 numbers parsed
 
178
 
 
179
    if ( cpp > 15 )
 
180
        return;
 
181
 
 
182
    if ( ncols > 256 ) {
 
183
        image.create( w, h, 32 );
 
184
    } else {
 
185
        image.create( w, h, 8, ncols );
 
186
    }
 
187
 
 
188
    if (image.isNull())
 
189
        return;
 
190
 
 
191
    QMap<QString, int> colorMap;
 
192
    int currentColor;
 
193
 
 
194
    for( currentColor=0; currentColor < ncols; ++currentColor ) {
 
195
        if ( !read_xpm_string( buf, d, source, index ) ) {
 
196
#if defined(QT_CHECK_RANGE)
 
197
            qWarning( "QImage: XPM color specification missing");
 
198
#endif
 
199
            return;
 
200
        }
 
201
        QString index;
 
202
        index = buf.left( cpp );
 
203
        buf = buf.mid( cpp ).simplifyWhiteSpace().lower();
 
204
        buf.prepend( " " );
 
205
        i = nextColorSpec(buf);
 
206
        if ( i < 0 ) {
 
207
#if defined(QT_CHECK_RANGE)
 
208
            qWarning( "QImage: XPM color specification is missing: %s", buf.data());
 
209
#endif
 
210
            return;     // no c/g/g4/m/s specification at all
 
211
        }
 
212
        buf = buf.mid( i+3 );
 
213
        // Strip any other colorspec
 
214
        int end = nextColorSpec(buf);
 
215
        if (end != -1)
 
216
            buf.truncate(end);
 
217
        buf = buf.stripWhiteSpace();
 
218
        if ( buf == "none" ) {
 
219
            image.setAlphaBuffer( TRUE );
 
220
            int transparentColor = currentColor;
 
221
            if ( image.depth() == 8 ) {
 
222
                image.setColor( transparentColor,
 
223
                                RGB_MASK & qRgb(198,198,198) );
 
224
                colorMap.insert( index, transparentColor );
 
225
            } else {
 
226
                QRgb rgb = RGB_MASK & qRgb(198,198,198);
 
227
                colorMap.insert( index, rgb );
 
228
            }
 
229
        } else {
 
230
            if ( ((buf.length()-1) % 3) && (buf[0] == '#') ) {
 
231
                buf.truncate (((buf.length()-1) / 4 * 3) + 1); // remove alpha channel left by imagemagick
 
232
            }
 
233
#ifdef GV_XPM_CHANGES
 
234
            QColor c = GVThreadGate::instance()->color( buf.data());
 
235
#else
 
236
            QColor c( buf.data() );
 
237
#endif
 
238
            if ( image.depth() == 8 ) {
 
239
                image.setColor( currentColor, 0xff000000 | c.rgb() );
 
240
                colorMap.insert( index, currentColor );
 
241
            } else {
 
242
                QRgb rgb = 0xff000000 | c.rgb();
 
243
                colorMap.insert( index, rgb );
 
244
            }
 
245
        }
 
246
    }
 
247
 
 
248
    // Read pixels
 
249
    for( int y=0; y<h; y++ ) {
 
250
        if ( !read_xpm_string( buf, d, source, index ) ) {
 
251
#if defined(QT_CHECK_RANGE)
 
252
            qWarning( "QImage: XPM pixels missing on image line %d", y);
 
253
#endif
 
254
            return;
 
255
        }
 
256
        if ( image.depth() == 8 ) {
 
257
            uchar *p = image.scanLine(y);
 
258
            uchar *d = (uchar *)buf.data();
 
259
            uchar *end = d + buf.length();
 
260
            int x;
 
261
            if ( cpp == 1 ) {
 
262
                char b[2];
 
263
                b[1] = '\0';
 
264
                for ( x=0; x<w && d<end; x++ ) {
 
265
                    b[0] = *d++;
 
266
                    *p++ = (uchar)colorMap[b];
 
267
                }
 
268
            } else {
 
269
                char b[16];
 
270
                b[cpp] = '\0';
 
271
                for ( x=0; x<w && d<end; x++ ) {
 
272
                    strncpy( b, (char *)d, cpp );
 
273
                    *p++ = (uchar)colorMap[b];
 
274
                    d += cpp;
 
275
                }
 
276
            }
 
277
        } else {
 
278
            QRgb *p = (QRgb*)image.scanLine(y);
 
279
            uchar *d = (uchar *)buf.data();
 
280
            uchar *end = d + buf.length();
 
281
            int x;
 
282
            char b[16];
 
283
            b[cpp] = '\0';
 
284
            for ( x=0; x<w && d<end; x++ ) {
 
285
                strncpy( b, (char *)d, cpp );
 
286
                *p++ = (QRgb)colorMap[b];
 
287
                d += cpp;
 
288
            }
 
289
        }
 
290
    }
 
291
    if ( iio ) {
 
292
        iio->setImage( image );
 
293
        iio->setStatus( 0 );                    // image ok
 
294
    }
 
295
}
 
296
 
 
297
 
 
298
static void read_xpm_image( QImageIO * iio )
 
299
{
 
300
    QImage i;
 
301
    (void)read_xpm_image_or_array( iio, 0, i );
 
302
    return;
 
303
}
 
304
 
 
305
 
 
306
static const char* xpm_color_name( int cpp, int index )
 
307
{
 
308
    static char returnable[5];
 
309
    static const char code[] = ".#abcdefghijklmnopqrstuvwxyzABCD"
 
310
                               "EFGHIJKLMNOPQRSTUVWXYZ0123456789";
 
311
    // cpp is limited to 4 and index is limited to 64^cpp
 
312
    if ( cpp > 1 ) {
 
313
        if ( cpp > 2 ) {
 
314
            if ( cpp > 3 ) {
 
315
                returnable[3] = code[index % 64];
 
316
                index /= 64;
 
317
            } else
 
318
                returnable[3] = '\0';
 
319
            returnable[2] = code[index % 64];
 
320
            index /= 64;
 
321
        } else
 
322
            returnable[2] = '\0';
 
323
        // the following 4 lines are a joke!
 
324
        if ( index == 0 )
 
325
            index = 64*44+21;
 
326
        else if ( index == 64*44+21 )
 
327
            index = 0;
 
328
        returnable[1] = code[index % 64];
 
329
        index /= 64;
 
330
    } else
 
331
        returnable[1] = '\0';
 
332
    returnable[0] = code[index];
 
333
 
 
334
    return returnable;
 
335
}
 
336
 
 
337
 
 
338
// write XPM image data
 
339
static void write_xpm_image( QImageIO * iio )
 
340
{
 
341
    if ( iio )
 
342
        iio->setStatus( 1 );
 
343
    else
 
344
        return;
 
345
 
 
346
    // ### 8-bit case could be made faster
 
347
    QImage image;
 
348
    if ( iio->image().depth() != 32 )
 
349
        image = iio->image().convertDepth( 32 );
 
350
    else
 
351
        image = iio->image();
 
352
 
 
353
    QMap<QRgb, int> colorMap;
 
354
 
 
355
    int w = image.width(), h = image.height(), ncolors = 0;
 
356
    int x, y;
 
357
 
 
358
    // build color table
 
359
    for( y=0; y<h; y++ ) {
 
360
        QRgb * yp = (QRgb *)image.scanLine( y );
 
361
        for( x=0; x<w; x++ ) {
 
362
            QRgb color = *(yp + x);
 
363
            if ( !colorMap.contains(color) )
 
364
                colorMap.insert( color, ncolors++ );
 
365
        }
 
366
    }
 
367
 
 
368
    // number of 64-bit characters per pixel needed to encode all colors
 
369
    int cpp = 1;
 
370
    for ( int k = 64; ncolors > k; k *= 64 ) {
 
371
        ++cpp;
 
372
        // limit to 4 characters per pixel
 
373
        // 64^4 colors is enough for a 4096x4096 image
 
374
         if ( cpp > 4)
 
375
            break;
 
376
    }
 
377
 
 
378
    QString line;
 
379
 
 
380
    // write header
 
381
    QTextStream s( iio->ioDevice() );
 
382
    s << "/* XPM */" << endl
 
383
      << "static char *" << fbname(iio->fileName()) << "[]={" << endl
 
384
      << "\"" << w << " " << h << " " << ncolors << " " << cpp << "\"";
 
385
 
 
386
    // write palette
 
387
    QMap<QRgb, int>::Iterator c = colorMap.begin();
 
388
    while ( c != colorMap.end() ) {
 
389
        QRgb color = c.key();
 
390
        if ( image.hasAlphaBuffer() && color == (color & RGB_MASK) )
 
391
            line.sprintf( "\"%s c None\"",
 
392
                          xpm_color_name(cpp, *c) );
 
393
        else
 
394
            line.sprintf( "\"%s c #%02x%02x%02x\"",
 
395
                          xpm_color_name(cpp, *c),
 
396
                          qRed(color),
 
397
                          qGreen(color),
 
398
                          qBlue(color) );
 
399
        ++c;
 
400
        s << "," << endl << line;
 
401
    }
 
402
 
 
403
    // write pixels, limit to 4 characters per pixel
 
404
    line.truncate( cpp*w );
 
405
    for( y=0; y<h; y++ ) {
 
406
        QRgb * yp = (QRgb *) image.scanLine( y );
 
407
        int cc = 0;
 
408
        for( x=0; x<w; x++ ) {
 
409
            int color = (int)(*(yp + x));
 
410
            QCString chars = xpm_color_name( cpp, colorMap[color] );
 
411
            line[cc++] = chars[0];
 
412
            if ( cpp > 1 ) {
 
413
                line[cc++] = chars[1];
 
414
                if ( cpp > 2 ) {
 
415
                    line[cc++] = chars[2];
 
416
                    if ( cpp > 3 ) {
 
417
                        line[cc++] = chars[3];
 
418
                    }
 
419
                }
 
420
            }
 
421
        }
 
422
        s << "," << endl << "\"" << line << "\"";
 
423
    }
 
424
    s << "};" << endl;
 
425
 
 
426
    iio->setStatus( 0 );
 
427
}
 
428
 
 
429
// Qt code end ---------------------------
 
430
 
 
431
GVXPM::GVXPM()
 
432
{
 
433
        QImageIO::inputFormats(); // trigger registration of Qt's handlers
 
434
        QImageIO::defineIOHandler( "XPM", "/\\*.XPM.\\*/", "T",
 
435
                                   read_xpm_image, write_xpm_image );
 
436
}