1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
\r
2 // This file is part of the "Irrlicht Engine".
\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
\r
5 #include "CImageLoaderPSD.h"
\r
7 #ifdef _IRR_COMPILE_WITH_PSD_LOADER_
\r
9 #include "IReadFile.h"
\r
12 #include "irrString.h"
\r
22 CImageLoaderPSD::CImageLoaderPSD()
\r
25 setDebugName("CImageLoaderPSD");
\r
30 //! returns true if the file maybe is able to be loaded by this class
\r
31 //! based on the file extension (e.g. ".tga")
\r
32 bool CImageLoaderPSD::isALoadableFileExtension(const io::path& filename) const
\r
34 return core::hasFileExtension ( filename, "psd" );
\r
39 //! returns true if the file maybe is able to be loaded by this class
\r
40 bool CImageLoaderPSD::isALoadableFileFormat(io::IReadFile* file) const
\r
46 file->read(&type, sizeof(u8)*3);
\r
47 return (type[2]==2); // we currently only handle tgas of type 2.
\r
52 //! creates a surface from the file
\r
53 IImage* CImageLoaderPSD::loadImage(io::IReadFile* file) const
\r
58 file->read(&header, sizeof(PsdHeader));
\r
60 #ifndef __BIG_ENDIAN__
\r
61 header.version = os::Byteswap::byteswap(header.version);
\r
62 header.channels = os::Byteswap::byteswap(header.channels);
\r
63 header.height = os::Byteswap::byteswap(header.height);
\r
64 header.width = os::Byteswap::byteswap(header.width);
\r
65 header.depth = os::Byteswap::byteswap(header.depth);
\r
66 header.mode = os::Byteswap::byteswap(header.mode);
\r
69 if (header.signature[0] != '8' ||
\r
70 header.signature[1] != 'B' ||
\r
71 header.signature[2] != 'P' ||
\r
72 header.signature[3] != 'S')
\r
75 if (header.version != 1)
\r
77 os::Printer::log("Unsupported PSD file version", file->getFileName(), ELL_ERROR);
\r
81 if (header.mode != 3 || header.depth != 8)
\r
83 os::Printer::log("Unsupported PSD color mode or depth.\n", file->getFileName(), ELL_ERROR);
\r
87 // skip color mode data
\r
90 file->read(&l, sizeof(u32));
\r
91 #ifndef __BIG_ENDIAN__
\r
92 l = os::Byteswap::byteswap(l);
\r
94 if (!file->seek(l, true))
\r
96 os::Printer::log("Error seeking file pos to image resources.\n", file->getFileName(), ELL_ERROR);
\r
100 // skip image resources
\r
102 file->read(&l, sizeof(u32));
\r
103 #ifndef __BIG_ENDIAN__
\r
104 l = os::Byteswap::byteswap(l);
\r
106 if (!file->seek(l, true))
\r
108 os::Printer::log("Error seeking file pos to layer and mask.\n", file->getFileName(), ELL_ERROR);
\r
112 // skip layer & mask
\r
114 file->read(&l, sizeof(u32));
\r
115 #ifndef __BIG_ENDIAN__
\r
116 l = os::Byteswap::byteswap(l);
\r
118 if (!file->seek(l, true))
\r
120 os::Printer::log("Error seeking file pos to image data section.\n", file->getFileName(), ELL_ERROR);
\r
126 u16 compressionType;
\r
127 file->read(&compressionType, sizeof(u16));
\r
128 #ifndef __BIG_ENDIAN__
\r
129 compressionType = os::Byteswap::byteswap(compressionType);
\r
132 if (compressionType != 1 && compressionType != 0)
\r
134 os::Printer::log("Unsupported psd compression mode.\n", file->getFileName(), ELL_ERROR);
\r
138 // create image data block
\r
140 imageData = new u32[header.width * header.height];
\r
144 if (compressionType == 0)
\r
145 res = readRawImageData(file, header, imageData); // RAW image data
\r
147 res = readRLEImageData(file, header, imageData); // RLE compressed data
\r
149 video::IImage* image = 0;
\r
154 image = new CImage(ECF_A8R8G8B8,
\r
155 core::dimension2d<u32>(header.width, header.height), imageData);
\r
159 delete [] imageData;
\r
166 bool CImageLoaderPSD::readRawImageData(io::IReadFile* file, const PsdHeader& header, u32* imageData) const
\r
168 u8* tmpData = new u8[header.width * header.height];
\r
170 for (s32 channel=0; channel<header.channels && channel < 3; ++channel)
\r
172 if (!file->read(tmpData, sizeof(c8) * header.width * header.height))
\r
174 os::Printer::log("Error reading color channel\n", file->getFileName(), ELL_ERROR);
\r
178 s16 shift = getShiftFromChannel((c8)channel, header);
\r
181 u32 mask = 0xff << shift;
\r
183 for (u32 x=0; x<header.width; ++x)
\r
185 for (u32 y=0; y<header.height; ++y)
\r
187 s32 index = x + y*header.width;
\r
188 imageData[index] = ~(~imageData[index] | mask);
\r
189 imageData[index] |= tmpData[index] << shift;
\r
201 bool CImageLoaderPSD::readRLEImageData(io::IReadFile* file, const PsdHeader& header, u32* imageData) const
\r
203 /* If the compression code is 1, the image data
\r
204 starts with the byte counts for all the scan lines in the channel
\r
205 (LayerBottom LayerTop), with each count stored as a two
\r
206 byte value. The RLE compressed data follows, with each scan line
\r
207 compressed separately. The RLE compression is the same compres-sion
\r
208 algorithm used by the Macintosh ROM routine PackBits, and
\r
210 If the Layer's Size, and therefore the data, is odd, a pad byte will
\r
211 be inserted at the end of the row.
\r
215 A pseudo code fragment to unpack might look like this:
\r
217 Loop until you get the number of unpacked bytes you are expecting:
\r
218 Read the next source byte into n.
\r
219 If n is between 0 and 127 inclusive, copy the next n+1 bytes literally.
\r
220 Else if n is between -127 and -1 inclusive, copy the next byte -n+1
\r
222 Else if n is -128, noop.
\r
225 In the inverse routine, it is best to encode a 2-byte repeat run as a replicate run
\r
226 except when preceded and followed by a literal run. In that case, it is best to merge
\r
227 the three runs into one literal run. Always encode 3-byte repeats as replicate runs.
\r
228 That is the essence of the algorithm. Here are some additional rules:
\r
229 - Pack each row separately. Do not compress across row boundaries.
\r
230 - The number of uncompressed bytes per row is defined to be (ImageWidth + 7)
\r
231 / 8. If the uncompressed bitmap is required to have an even number of bytes per
\r
232 row, decompress into word-aligned buffers.
\r
233 - If a run is larger than 128 bytes, encode the remainder of the run as one or more
\r
234 additional replicate runs.
\r
235 When PackBits data is decompressed, the result should be interpreted as per com-pression
\r
236 type 1 (no compression).
\r
239 u8* tmpData = new u8[header.width * header.height];
\r
240 u16 *rleCount= new u16 [header.height * header.channels];
\r
244 for (u32 y=0; y<header.height * header.channels; ++y)
\r
246 if (!file->read(&rleCount[y], sizeof(u16)))
\r
249 delete [] rleCount;
\r
250 os::Printer::log("Error reading rle rows\n", file->getFileName(), ELL_ERROR);
\r
254 #ifndef __BIG_ENDIAN__
\r
255 rleCount[y] = os::Byteswap::byteswap(rleCount[y]);
\r
257 size += rleCount[y];
\r
260 s8 *buf = new s8[size];
\r
261 if (!file->read(buf, size))
\r
263 delete [] rleCount;
\r
266 os::Printer::log("Error reading rle rows\n", file->getFileName(), ELL_ERROR);
\r
270 u16 *rcount=rleCount;
\r
277 // decompress packbit rle
\r
279 for (s32 channel=0; channel<header.channels; channel++)
\r
281 for (u32 y=0; y<header.height; ++y, ++rcount)
\r
284 dest = &tmpData[y*header.width];
\r
286 while (bytesRead < *rcount)
\r
319 s16 shift = getShiftFromChannel((c8)channel, header);
\r
323 u32 mask = 0xff << shift;
\r
325 for (u32 x=0; x<header.width; ++x)
\r
326 for (u32 y=0; y<header.height; ++y)
\r
328 s32 index = x + y*header.width;
\r
329 imageData[index] = ~(~imageData[index] | mask);
\r
330 imageData[index] |= tmpData[index] << shift;
\r
335 delete [] rleCount;
\r
343 s16 CImageLoaderPSD::getShiftFromChannel(c8 channelNr, const PsdHeader& header) const
\r
354 return header.channels == 4 ? 24 : -1; // ?
\r
356 return 24; // alpha
\r
364 //! creates a loader which is able to load tgas
\r
365 IImageLoader* createImageLoaderPSD()
\r
367 return new CImageLoaderPSD();
\r
371 } // end namespace video
\r
372 } // end namespace irr
\r