~ubuntu-branches/ubuntu/edgy/gwenview/edgy

« back to all changes in this revision

Viewing changes to src/gvxpm.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2005-12-06 17:59:19 UTC
  • mfrom: (1.1.3 upstream) (2.1.1 sarge)
  • Revision ID: james.westby@ubuntu.com-20051206175919-8yv9nfmw0pws6p52
Tags: 1.3.1-0ubuntu1
* Sync with Debian
* Edit kde.mk for .pot generation

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
 
}