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
|
#ifndef QXCFI_H
#define QXCFI_H
#include <qimage.h>
#include <qimageformatplugin.h>
#include <qvaluestack.h>
#include <qvaluevector.h>
#include "gimp.h"
// 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);
}
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 );
};
#endif
|