1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
|
#ifndef QXCFI_H
#define QXCFI_H
#include <qimage.h>
#include <qimageformatplugin.h>
#include <qvaluestack.h>
#include <qvaluevector.h>
#include "gimp.h"
namespace Gwenview {
// Safe readBlock helper functions
class SafeDataStream {
public:
SafeDataStream(QIODevice* device)
: mDevice(device), mFailed(false) {}
bool failed() const { return mFailed; }
QIODevice* device() const { return mDevice; }
SafeDataStream& readRawBytes(char* data, uint length) {
if (mFailed) return *this;
int read_length=mDevice->readBlock(data, length);
if (read_length==-1) mFailed=true;
if ((uint)read_length!=length) mFailed=true;
return *this;
}
SafeDataStream& operator>>(Q_INT8& value) {
return readRawBytes((char*)&value, 1);
}
SafeDataStream& operator>>(Q_UINT32& value) {
if (mFailed) return *this;
uchar *p = (uchar *)(&value);
char b[4];
if (mDevice->readBlock( b, 4 )==4) {
*p++ = b[3];
*p++ = b[2];
*p++ = b[1];
*p = b[0];
} else {
mFailed=true;
}
return *this;
}
SafeDataStream& operator>>(Q_INT32& value) {
return *this >>((Q_UINT32&)value);
}
SafeDataStream& operator>>(float& value) {
return *this >>((Q_UINT32&)value);
}
SafeDataStream& operator>>(char*& value) {
if (mFailed) return *this;
Q_UINT32 len;
*this >> len;
if (mFailed) return *this;
if ( len == 0 ) {
value = 0;
return *this;
}
if (mDevice->atEnd() ) {
value = 0;
mFailed=true;
return *this;
}
value = new char[len];
Q_CHECK_PTR( value );
if ( !value ) {
mFailed=true;
return *this;
}
return readRawBytes(value, len);
}
SafeDataStream& readBytes(char*& data, uint& len) {
if (mFailed) return *this;
*this >> len;
if (mFailed) return *this;
data=new char[len];
Q_CHECK_PTR( data );
if ( !data ) {
mFailed=true;
return *this;
}
return readRawBytes(data, len);
}
// This method is usefull to debug with gdb. Do not inline it!
int at() const;
private:
QIODevice* mDevice;
bool mFailed;
};
//! Plug-in for loading a GIMP XCF image file directly.
/*!
* This class uses the Qt 3.0 Image format plug-in loader to provide
* the ability to read The GIMP XCF image files. This plug-in will
* be dynamically loaded as needed.
*/
class XCFImageFormat : public QImageFormatPlugin {
/*!
* Each layer in an XCF file is stored as a matrix of
* 64-pixel by 64-pixel images. The GIMP has a sophisticated
* method of handling very large images as well as implementing
* parallel processing on a tile-by-tile basis. Here, though,
* we just read them in en-masse and store them in a matrix.
*/
typedef QValueVector< QValueVector< QImage > > Tiles;
/*!
* Each GIMP image is composed of one or more layers. A layer can
* be one of any three basic types: RGB, grayscale or indexed. With an
* optional alpha channel, there are six possible types altogether.
*
* Note: there is only ever one instance of this structure. The
* layer info is discarded after it is merged into the final QImage.
*/
struct Layer {
Q_UINT32 width; //!< Width of the layer
Q_UINT32 height; //!< Height of the layer
Q_INT32 type; //!< Type of the layer (GimpImageType)
char* name; //!< Name of the layer
Q_UINT32 hierarchy_offset; //!< File position of Tile hierarchy
Q_UINT32 mask_offset; //!< File position of mask image
uint nrows; //!< Number of rows of tiles (y direction)
uint ncols; //!< Number of columns of tiles (x direction)
Tiles image_tiles; //!< The basic image
//! For Grayscale and Indexed images, the alpha channel is stored
//! separately (in this data structure, anyway).
Tiles alpha_tiles;
Tiles mask_tiles; //!< The layer mask (optional)
//! Additional information about a layer mask.
struct {
Q_UINT32 opacity;
Q_UINT32 visible;
Q_UINT32 show_masked;
uchar red, green, blue;
Q_UINT32 tattoo;
} mask_channel;
bool active; //!< Is this layer the active layer?
Q_UINT32 opacity; //!< The opacity of the layer
Q_UINT32 visible; //!< Is the layer visible?
Q_UINT32 linked; //!< Is this layer linked (geometrically)
Q_UINT32 preserve_transparency; //!< Preserve alpha when drawing on layer?
Q_UINT32 apply_mask; //!< Apply the layer mask?
Q_UINT32 edit_mask; //!< Is the layer mask the being edited?
Q_UINT32 show_mask; //!< Show the layer mask rather than the image?
Q_INT32 x_offset; //!< x offset of the layer relative to the image
Q_INT32 y_offset; //!< y offset of the layer relative to the image
Q_UINT32 mode; //!< Combining mode of layer (LayerModeEffects)
Q_UINT32 tattoo; //!< (unique identifier?)
//! As each tile is read from the file, it is buffered here.
uchar tile[TILE_WIDTH * TILE_HEIGHT * sizeof(QRgb)];
//! The data from tile buffer is copied to the Tile by this
//! method. Depending on the type of the tile (RGB, Grayscale,
//! Indexed) and use (image or mask), the bytes in the buffer are
//! copied in different ways.
void (*assignBytes)( Layer& layer, uint i, uint j );
//! Construct a layer.
Layer ( void ) : name( 0 ) {}
//! Destruct the layer.
~Layer ( void ) { if ( name != 0 ) delete[] name; }
};
/*!
* The in-memory representation of the XCF Image. It contains a few
* metadata items, but is mostly a container for the layer information.
*/
struct XCFImage {
Q_UINT32 width; //!< width of the XCF image
Q_UINT32 height; //!< height of the XCF image
Q_INT32 type; //!< type of the XCF image (GimpImageBaseType)
Q_UINT8 compression; //!< tile compression method (CompressionType)
float x_resolution; //!< x resolution in dots per inch
float y_resolution; //!< y resolution in dots per inch
Q_INT32 tattoo; //!< (unique identifier?)
Q_UINT32 unit; //!< Units of The GIMP (inch, mm, pica, etc...)
Q_INT32 num_colors; //!< number of colors in an indexed image
QValueVector< QRgb > palette; //!< indexed image color palette
int num_layers; //!< number of layers
Layer layer; //!< most recently read layer
bool initialized; //!< Is the QImage initialized?
QImage image; //!< final QImage
//! Simple constructor.
XCFImage ( void ) : initialized( false ) {}
};
//! The bottom-most layer is copied into the final QImage by this
//! routine.
typedef void (*PixelCopyOperation) ( Layer& layer, uint i, uint j, int k, int l,
QImage& image, int m, int n );
//! Higher layers are merged into the the final QImage by this routine.
typedef void (*PixelMergeOperation) ( Layer& layer, uint i, uint j, int k, int l,
QImage& image, int m, int n );
//! In layer DISSOLVE mode, a random number is chosen to compare to a
//! pixel's alpha. If the alpha is greater than the random number, the
//! pixel is drawn. This table merely contains the random number seeds
//! for each ROW of an image. Therefore, the random numbers chosen
//! are consistent from run to run.
static int random_table[RANDOM_TABLE_SIZE];
//! This table provides the add_pixel saturation values (i.e. 250 + 250 = 255).
static int add_lut[256][256];
//! Layer mode static data.
typedef struct {
bool affect_alpha; //!< Does this mode affect the source alpha?
} LayerModes;
//! Array of layer mode structures for the modes described by
//! LayerModeEffects.
static LayerModes layer_modes[];
public:
/*!
* The constructor for the XCF image loader. This initializes the
* tables used in the layer merging routines.
*/
XCFImageFormat ();
/*!
* The image loader makes no (direct) use of dynamic memory
* and the Qt infrastructure takes care of constructing and destructing
* the loader so there is not much to do here.
*/
~XCFImageFormat () {}
/*!
* You can query Qt about the types of image file formats it knows about
* via QImage::inputFormats or QImage::inputFormatList().
* This method returns "xcf".
*/
QStringList keys () const {
return QStringList() << "XCF";
}
/*!
* This method installs the XCF reader on demand.
*/
bool installIOHandler ( const QString& );
static void registerFormat();
private:
static void readXCF ( QImageIO* image_io );
#ifdef TMP_WRITE
static void writeXCF ( QImageIO* ) {}
#endif
static void initializeImage ( XCFImage& xcf_image );
static void composeTiles ( XCFImage& xcf_image );
static bool loadImageProperties ( SafeDataStream& xcf_io, XCFImage& image );
static bool loadLayer ( SafeDataStream& xcf_io, XCFImage& xcf_image );
static bool loadLayerProperties ( SafeDataStream& xcf_io, Layer& layer );
static bool loadChannelProperties ( SafeDataStream& xcf_io, Layer& layer );
static bool loadHierarchy ( SafeDataStream& xcf_io, Layer& layer );
static bool loadMask ( SafeDataStream& xcf_io, Layer& layer );
static bool loadLevel ( SafeDataStream& xcf_io, Layer& layer, Q_INT32 bpp );
static bool loadTileRLE ( SafeDataStream& xcf_io, uchar* tile, int size,
int data_length, Q_INT32 bpp );
static bool loadProperty ( SafeDataStream& xcf_io, PropType& type,
QByteArray& bytes );
static void setGrayPalette ( QImage& image );
static void setPalette ( XCFImage& xcf_image, QImage& image );
static void assignImageBytes ( Layer& layer, uint i, uint j );
static void assignMaskBytes ( Layer& layer, uint i, uint j );
static void copyLayerToImage ( XCFImage& xcf_image );
static void copyRGBToRGB ( Layer& layer, uint i, uint j, int k, int l,
QImage& image, int m, int n );
static void copyGrayToGray ( Layer& layer, uint i, uint j, int k, int l,
QImage& image, int m, int n );
static void copyGrayToRGB ( Layer& layer, uint i, uint j, int k, int l,
QImage& image, int m, int n );
static void copyGrayAToRGB ( Layer& layer, uint i, uint j, int k, int l,
QImage& image, int m, int n );
static void copyIndexedToIndexed ( Layer& layer, uint i, uint j, int k, int l,
QImage& image, int m, int n );
static void copyIndexedAToIndexed ( Layer& layer, uint i, uint j, int k, int l,
QImage& image, int m, int n );
static void copyIndexedAToRGB ( Layer& layer, uint i, uint j, int k, int l,
QImage& image, int m, int n );
static void mergeLayerIntoImage ( XCFImage& xcf_image );
static void mergeRGBToRGB ( Layer& layer, uint i, uint j, int k, int l,
QImage& image, int m, int n );
static void mergeGrayToGray ( Layer& layer, uint i, uint j, int k, int l,
QImage& image, int m, int n );
static void mergeGrayAToGray ( Layer& layer, uint i, uint j, int k, int l,
QImage& image, int m, int n );
static void mergeGrayToRGB ( Layer& layer, uint i, uint j, int k, int l,
QImage& image, int m, int n );
static void mergeGrayAToRGB ( Layer& layer, uint i, uint j, int k, int l,
QImage& image, int m, int n );
static void mergeIndexedToIndexed ( Layer& layer, uint i, uint j, int k, int l,
QImage& image, int m, int n );
static void mergeIndexedAToIndexed ( Layer& layer, uint i, uint j, int k, int l,
QImage& image, int m, int n );
static void mergeIndexedAToRGB ( Layer& layer, uint i, uint j, int k, int l,
QImage& image, int m, int n );
static void dissolveRGBPixels ( QImage& image, int x, int y );
static void dissolveAlphaPixels ( QImage& image, int x, int y );
};
} // namespace
#endif
|