1 | #ifndef XCF_H |
2 | #define XCF_H |
3 | /* |
4 | * qxcfi.cpp: A Qt 3 plug-in for reading GIMP XCF image files |
5 | * Copyright (C) 2001 lignum Computing, Inc. <allen@lignumcomputing.com> |
6 | * Copyright (C) 2004 Melchior FRANZ <mfranz@kde.org> |
7 | * |
8 | * This plug-in is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU Lesser General Public |
10 | * License as published by the Free Software Foundation; either |
11 | * version 2.1 of the License, or (at your option) any later version. |
12 | * |
13 | * This library is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * Lesser General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU Lesser General Public |
19 | * License along with this library; if not, write to the Free Software |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | * |
22 | */ |
23 | |
24 | |
25 | #include <QtGui/QImageIOPlugin> |
26 | #include <QtGui/QImage> |
27 | #include <QtCore/QIODevice> |
28 | #include <QtCore/QVector> |
29 | |
30 | #include "gimp.h" |
31 | |
32 | class XCFHandler : public QImageIOHandler |
33 | { |
34 | public: |
35 | XCFHandler(); |
36 | |
37 | bool canRead() const; |
38 | bool read(QImage *image); |
39 | bool write(const QImage &image); |
40 | |
41 | QByteArray name() const; |
42 | |
43 | static bool canRead(QIODevice *device); |
44 | }; |
45 | |
46 | const float INCHESPERMETER = (100.0f / 2.54f); |
47 | |
48 | /*! |
49 | * Each layer in an XCF file is stored as a matrix of |
50 | * 64-pixel by 64-pixel images. The GIMP has a sophisticated |
51 | * method of handling very large images as well as implementing |
52 | * parallel processing on a tile-by-tile basis. Here, though, |
53 | * we just read them in en-masse and store them in a matrix. |
54 | */ |
55 | typedef QVector<QVector<QImage> > Tiles; |
56 | |
57 | |
58 | |
59 | class XCFImageFormat { |
60 | public: |
61 | XCFImageFormat(); |
62 | bool readXCF(QIODevice *device, QImage *image); |
63 | |
64 | |
65 | private: |
66 | /*! |
67 | * Each GIMP image is composed of one or more layers. A layer can |
68 | * be one of any three basic types: RGB, grayscale or indexed. With an |
69 | * optional alpha channel, there are six possible types altogether. |
70 | * |
71 | * Note: there is only ever one instance of this structure. The |
72 | * layer info is discarded after it is merged into the final QImage. |
73 | */ |
74 | class Layer { |
75 | public: |
76 | quint32 width; //!< Width of the layer |
77 | quint32 height; //!< Height of the layer |
78 | qint32 type; //!< Type of the layer (GimpImageType) |
79 | char* name; //!< Name of the layer |
80 | quint32 hierarchy_offset; //!< File position of Tile hierarchy |
81 | quint32 mask_offset; //!< File position of mask image |
82 | |
83 | uint nrows; //!< Number of rows of tiles (y direction) |
84 | uint ncols; //!< Number of columns of tiles (x direction) |
85 | |
86 | Tiles image_tiles; //!< The basic image |
87 | //! For Grayscale and Indexed images, the alpha channel is stored |
88 | //! separately (in this data structure, anyway). |
89 | Tiles alpha_tiles; |
90 | Tiles mask_tiles; //!< The layer mask (optional) |
91 | |
92 | //! Additional information about a layer mask. |
93 | struct { |
94 | quint32 opacity; |
95 | quint32 visible; |
96 | quint32 show_masked; |
97 | uchar red, green, blue; |
98 | quint32 tattoo; |
99 | } mask_channel; |
100 | |
101 | bool active; //!< Is this layer the active layer? |
102 | quint32 opacity; //!< The opacity of the layer |
103 | quint32 visible; //!< Is the layer visible? |
104 | quint32 linked; //!< Is this layer linked (geometrically) |
105 | quint32 preserve_transparency; //!< Preserve alpha when drawing on layer? |
106 | quint32 apply_mask; //!< Apply the layer mask? |
107 | quint32 edit_mask; //!< Is the layer mask the being edited? |
108 | quint32 show_mask; //!< Show the layer mask rather than the image? |
109 | qint32 x_offset; //!< x offset of the layer relative to the image |
110 | qint32 y_offset; //!< y offset of the layer relative to the image |
111 | quint32 mode; //!< Combining mode of layer (LayerModeEffects) |
112 | quint32 tattoo; //!< (unique identifier?) |
113 | |
114 | //! As each tile is read from the file, it is buffered here. |
115 | uchar tile[TILE_WIDTH * TILE_HEIGHT * sizeof(QRgb)]; |
116 | |
117 | //! The data from tile buffer is copied to the Tile by this |
118 | //! method. Depending on the type of the tile (RGB, Grayscale, |
119 | //! Indexed) and use (image or mask), the bytes in the buffer are |
120 | //! copied in different ways. |
121 | void (*assignBytes)(Layer& layer, uint i, uint j); |
122 | |
123 | Layer(void) : name(0) {} |
124 | ~Layer(void) { delete[] name; } |
125 | }; |
126 | |
127 | |
128 | /*! |
129 | * The in-memory representation of the XCF Image. It contains a few |
130 | * metadata items, but is mostly a container for the layer information. |
131 | */ |
132 | class XCFImage { |
133 | public: |
134 | quint32 width; //!< width of the XCF image |
135 | quint32 height; //!< height of the XCF image |
136 | qint32 type; //!< type of the XCF image (GimpImageBaseType) |
137 | |
138 | quint8 compression; //!< tile compression method (CompressionType) |
139 | float x_resolution; //!< x resolution in dots per inch |
140 | float y_resolution; //!< y resolution in dots per inch |
141 | qint32 tattoo; //!< (unique identifier?) |
142 | quint32 unit; //!< Units of The GIMP (inch, mm, pica, etc...) |
143 | qint32 num_colors; //!< number of colors in an indexed image |
144 | QVector<QRgb> palette; //!< indexed image color palette |
145 | |
146 | int num_layers; //!< number of layers |
147 | Layer layer; //!< most recently read layer |
148 | |
149 | bool initialized; //!< Is the QImage initialized? |
150 | QImage image; //!< final QImage |
151 | |
152 | XCFImage(void) : initialized(false) {} |
153 | }; |
154 | |
155 | |
156 | //! In layer DISSOLVE mode, a random number is chosen to compare to a |
157 | //! pixel's alpha. If the alpha is greater than the random number, the |
158 | //! pixel is drawn. This table merely contains the random number seeds |
159 | //! for each ROW of an image. Therefore, the random numbers chosen |
160 | //! are consistent from run to run. |
161 | static int random_table[RANDOM_TABLE_SIZE]; |
162 | static bool random_table_initialized; |
163 | |
164 | //! This table is used as a shared grayscale ramp to be set on grayscale |
165 | //! images. This is because Qt does not differentiate between indexed and |
166 | //! grayscale images. |
167 | static QVector<QRgb> grayTable; |
168 | |
169 | //! This table provides the add_pixel saturation values (i.e. 250 + 250 = 255). |
170 | //static int add_lut[256][256]; - this is so lame waste of 256k of memory |
171 | static int add_lut( int, int ); |
172 | |
173 | //! The bottom-most layer is copied into the final QImage by this |
174 | //! routine. |
175 | typedef void (*PixelCopyOperation)(Layer& layer, uint i, uint j, int k, int l, |
176 | QImage& image, int m, int n); |
177 | |
178 | //! Higher layers are merged into the final QImage by this routine. |
179 | typedef void (*PixelMergeOperation)(Layer& layer, uint i, uint j, int k, int l, |
180 | QImage& image, int m, int n); |
181 | |
182 | //! Layer mode static data. |
183 | typedef struct { |
184 | bool affect_alpha; //!< Does this mode affect the source alpha? |
185 | } LayerModes; |
186 | |
187 | //! Array of layer mode structures for the modes described by |
188 | //! LayerModeEffects. |
189 | static const LayerModes layer_modes[]; |
190 | |
191 | bool loadImageProperties(QDataStream& xcf_io, XCFImage& image); |
192 | bool loadProperty(QDataStream& xcf_io, PropType& type, QByteArray& bytes); |
193 | bool loadLayer(QDataStream& xcf_io, XCFImage& xcf_image); |
194 | bool loadLayerProperties(QDataStream& xcf_io, Layer& layer); |
195 | bool composeTiles(XCFImage& xcf_image); |
196 | void setGrayPalette(QImage& image); |
197 | void setPalette(XCFImage& xcf_image, QImage& image); |
198 | static void assignImageBytes(Layer& layer, uint i, uint j); |
199 | bool loadHierarchy(QDataStream& xcf_io, Layer& layer); |
200 | bool loadLevel(QDataStream& xcf_io, Layer& layer, qint32 bpp); |
201 | static void assignMaskBytes(Layer& layer, uint i, uint j); |
202 | bool loadMask(QDataStream& xcf_io, Layer& layer); |
203 | bool loadChannelProperties(QDataStream& xcf_io, Layer& layer); |
204 | bool initializeImage(XCFImage& xcf_image); |
205 | bool loadTileRLE(QDataStream& xcf_io, uchar* tile, int size, |
206 | int data_length, qint32 bpp); |
207 | |
208 | static void copyLayerToImage(XCFImage& xcf_image); |
209 | static void copyRGBToRGB(Layer& layer, uint i, uint j, int k, int l, |
210 | QImage& image, int m, int n); |
211 | static void copyGrayToGray(Layer& layer, uint i, uint j, int k, int l, |
212 | QImage& image, int m, int n); |
213 | static void copyGrayToRGB(Layer& layer, uint i, uint j, int k, int l, |
214 | QImage& image, int m, int n); |
215 | static void copyGrayAToRGB(Layer& layer, uint i, uint j, int k, int l, |
216 | QImage& image, int m, int n); |
217 | static void copyIndexedToIndexed(Layer& layer, uint i, uint j, int k, int l, |
218 | QImage& image, int m, int n); |
219 | static void copyIndexedAToIndexed(Layer& layer, uint i, uint j, int k, int l, |
220 | QImage& image, int m, int n); |
221 | static void copyIndexedAToRGB(Layer& layer, uint i, uint j, int k, int l, |
222 | QImage& image, int m, int n); |
223 | |
224 | static void mergeLayerIntoImage(XCFImage& xcf_image); |
225 | static void mergeRGBToRGB(Layer& layer, uint i, uint j, int k, int l, |
226 | QImage& image, int m, int n); |
227 | static void mergeGrayToGray(Layer& layer, uint i, uint j, int k, int l, |
228 | QImage& image, int m, int n); |
229 | static void mergeGrayAToGray(Layer& layer, uint i, uint j, int k, int l, |
230 | QImage& image, int m, int n); |
231 | static void mergeGrayToRGB(Layer& layer, uint i, uint j, int k, int l, |
232 | QImage& image, int m, int n); |
233 | static void mergeGrayAToRGB(Layer& layer, uint i, uint j, int k, int l, |
234 | QImage& image, int m, int n); |
235 | static void mergeIndexedToIndexed(Layer& layer, uint i, uint j, int k, int l, |
236 | QImage& image, int m, int n); |
237 | static void mergeIndexedAToIndexed(Layer& layer, uint i, uint j, int k, int l, |
238 | QImage& image, int m, int n); |
239 | static void mergeIndexedAToRGB(Layer& layer, uint i, uint j, int k, int l, |
240 | QImage& image, int m, int n); |
241 | |
242 | static void initializeRandomTable(); |
243 | static void dissolveRGBPixels(QImage& image, int x, int y); |
244 | static void dissolveAlphaPixels(QImage& image, int x, int y); |
245 | }; |
246 | |
247 | #endif |
248 | |