| 1 |
/** |
|---|
| 2 |
LodePNG version 20070102 |
|---|
| 3 |
|
|---|
| 4 |
Copyright (c) 2005-2007 Lode Vandevenne |
|---|
| 5 |
All rights reserved. |
|---|
| 6 |
|
|---|
| 7 |
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: |
|---|
| 8 |
|
|---|
| 9 |
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. |
|---|
| 10 |
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. |
|---|
| 11 |
* Neither the name of Lode Vandevenne nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. |
|---|
| 12 |
|
|---|
| 13 |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|---|
| 14 |
|
|---|
| 15 |
Port to D by Lutger Blijdestijn on 5 januari 2007.<br> |
|---|
| 16 |
The port is close to the original, but the api has changed. I have taken out the class and made the api into two functions instead. |
|---|
| 17 |
Also, the Encoder is (not yet) ported. Implementation-wise zlib routines are replaced by std.zlib, but the rest is the same. |
|---|
| 18 |
Here follows part of the original documentation, slightly modified to reflect the D port.<br> |
|---|
| 19 |
You can email me at: lutger dot blijdenstijn at gmail dot com |
|---|
| 20 |
|
|---|
| 21 |
About: |
|---|
| 22 |
|
|---|
| 23 |
PNG is a file format to store raster images losslessly with good compression, |
|---|
| 24 |
supporting different color types. It's patent-free. Because the compression is |
|---|
| 25 |
lossless and it supports an alpha channel, PNG is a very good file format |
|---|
| 26 |
to store textures and tilesets for computer games. |
|---|
| 27 |
|
|---|
| 28 |
LodePNG is a PNG codec according to the Portable Network Graphics (PNG) |
|---|
| 29 |
Specification (Second Edition) - W3C Recommendation 10 November 2003. The |
|---|
| 30 |
specification can be found on line at http://www.w3.org/TR/2003/REC-PNG-20031110 |
|---|
| 31 |
|
|---|
| 32 |
The most recent version of LodePNG can currently be found at http://student.kuleuven.be/~m0216922/pngloader/ |
|---|
| 33 |
However, this location is temporary and won't exist anymore in 2007. |
|---|
| 34 |
|
|---|
| 35 |
LodePNG exists out of the source code file lodepng.d |
|---|
| 36 |
|
|---|
| 37 |
LodePNG is simple but only supports the basic requirements. In the tradeoff |
|---|
| 38 |
between functionality and simplicity, LodePNG is on the side of simplicity. |
|---|
| 39 |
To achieve this simplicity, the following design choices were made: There |
|---|
| 40 |
are no dependencies on any external library. To decode PNGs, there's a Decoder |
|---|
| 41 |
class (edit: in D port no class) that can convert any PNG file data into an RGBA image buffer with a single |
|---|
| 42 |
function call. |
|---|
| 43 |
|
|---|
| 44 |
This all makes LodePNG suitable for loading textures in games, raytracers, |
|---|
| 45 |
intros, or for loading images into programs that require them only for simple |
|---|
| 46 |
usage. It's less suitable for full fledged image editors, loading PNGs over |
|---|
| 47 |
network (since this decoder requires all the image data to be available before |
|---|
| 48 |
the decoding can begin), life-critical systems, ... Even though it contains |
|---|
| 49 |
a conformant decoder and encoder (not ported for D yet), it's still not a conformant editor, |
|---|
| 50 |
because unknown chunks are discarded. |
|---|
| 51 |
|
|---|
| 52 |
Because LodePNG is BSD licensed, it's allowed to use these source files in any |
|---|
| 53 |
project, as long as you include the above copyright message and conditions |
|---|
| 54 |
somewhere in the release of your program. |
|---|
| 55 |
|
|---|
| 56 |
<b>Supported features</b><br> |
|---|
| 57 |
The following features are supported by the decoder: |
|---|
| 58 |
<UL> |
|---|
| 59 |
<li> conformant decoding of PNGs with any color type, bit depth and interlace mode</li> |
|---|
| 60 |
<li> converting the result to 32-bit RGBA pixel data</li> |
|---|
| 61 |
<li> decoding a png image from a buffer.</li> |
|---|
| 62 |
<li> support for translucent PNG's, including translucent palettes and color key</li> |
|---|
| 63 |
<li> zlib decompression (inflate) (via phobos in D)</li> |
|---|
| 64 |
<li> 64 bit color (untested due to lack of such images)</li> |
|---|
| 65 |
<li> the following chunks are interpreted by the decoder |
|---|
| 66 |
<ul> |
|---|
| 67 |
<li>IHDR (header information)</li> |
|---|
| 68 |
<li>PLTE (color palette)</li> |
|---|
| 69 |
<li>IDAT (pixel data)</li> |
|---|
| 70 |
<li>IEND (the final chunk)</li> |
|---|
| 71 |
<li>tRNS (transparency for palettized images)</li> |
|---|
| 72 |
<li>bKGD (suggested background color)</li> |
|---|
| 73 |
</ul> |
|---|
| 74 |
</li> |
|---|
| 75 |
</UL> |
|---|
| 76 |
|
|---|
| 77 |
<b>Features not supported</b><br> |
|---|
| 78 |
The following features are _not_ supported. |
|---|
| 79 |
<ul> |
|---|
| 80 |
<li> editing a PNG image (unless you use the decoder, then edit it, then use the |
|---|
| 81 |
encoder, but ignored chunks will then be gone from the original image) |
|---|
| 82 |
<li> CRC checks of chunks |
|---|
| 83 |
<li> ADLER32 checksum of zlib data |
|---|
| 84 |
<li> partial loading. All data must be available and is processed in one call. |
|---|
| 85 |
<li> The following optional chunk types are not interpreted by the decoder <ul> |
|---|
| 86 |
<li>cHRM (device independent color info) |
|---|
| 87 |
<li>gAMA (device independent color info) |
|---|
| 88 |
<li>iCCP (device independent color info) |
|---|
| 89 |
<li>sBIT (original number of significant bits) |
|---|
| 90 |
<li>sRGB (device independent color info) |
|---|
| 91 |
<li>tEXt (textual information) |
|---|
| 92 |
<li>zTXt (compressed textual information) |
|---|
| 93 |
<li>iTXt (international textual information) |
|---|
| 94 |
<li>hIST (palette histogram) |
|---|
| 95 |
<li>pHYs (physical pixel dimensions) |
|---|
| 96 |
<li>sPLT (suggested reduced palette) |
|---|
| 97 |
<li>tIME (last image modification time) |
|---|
| 98 |
</ul> |
|---|
| 99 |
</ul> |
|---|
| 100 |
|
|---|
| 101 |
Usage: |
|---|
| 102 |
|
|---|
| 103 |
The basic steps of decoding a PNG file with LodePNG are as follows: |
|---|
| 104 |
<OL> |
|---|
| 105 |
<LI> Load the PNG image file from harddisk and store the bytes of the file in an ubyte[] |
|---|
| 106 |
with the same size as the file. |
|---|
| 107 |
<LI> Declare another ubyte[] that will contain the pixel data. |
|---|
| 108 |
<LI> Call the decode function of it with the above arrays as parameters and store the |
|---|
| 109 |
resulting Info struct. |
|---|
| 110 |
<LI> Check for errors with in Info.error. |
|---|
| 111 |
<LI> If no error occurred, the raw pixel data is now stored in the output array. |
|---|
| 112 |
</OL> |
|---|
| 113 |
|
|---|
| 114 |
To decode a PNG file, you can use one of the two decode functions. |
|---|
| 115 |
The function decode32 will store the pixel data in 32 bit RGBA format, no |
|---|
| 116 |
matter what color format was used in the PNG file. The function decodeGeneric |
|---|
| 117 |
will store the pixel data in the same color format as in the file. A more |
|---|
| 118 |
detailed description of both functions, as well as the optional loadFile |
|---|
| 119 |
function, is given below. |
|---|
| 120 |
*/ |
|---|
| 121 |
module png; |
|---|
| 122 |
import std.zlib; |
|---|
| 123 |
|
|---|
| 124 |
/**Information about the PNG image is returned in the form of an Info struct. |
|---|
| 125 |
|
|---|
| 126 |
After using decodeGeneric or decode32, please always check the error value |
|---|
| 127 |
to see if everything went ok. |
|---|
| 128 |
*/ |
|---|
| 129 |
struct Info |
|---|
| 130 |
{ |
|---|
| 131 |
/** Below is a table that gives the meaning of the different values. |
|---|
| 132 |
|
|---|
| 133 |
In the port to D, some of these errors have become obsolete as zlib has replaced a few routines and will spit out |
|---|
| 134 |
exceptions instead. I have not yet removed these. |
|---|
| 135 |
|
|---|
| 136 |
<li> 0: no error, everything went ok |
|---|
| 137 |
<li> 1: there is no PNG loaded yet, so it makes no sense trying to read any information values |
|---|
| 138 |
<li> 10: while huffman decoding: end of input memory reached without endcode |
|---|
| 139 |
<li> 11: while huffman decoding: error in code tree made it jump outside of tree |
|---|
| 140 |
<li> 13: problem while processing dynamic deflate block |
|---|
| 141 |
<li> 14: problem while processing dynamic deflate block |
|---|
| 142 |
<li> 15: problem while processing dynamic deflate block |
|---|
| 143 |
<li> 16: unexisting code while processing dynamic deflate block |
|---|
| 144 |
<li> 17: while inflating: end of out buffer memory reached |
|---|
| 145 |
<li> 18: while inflating: invalid distance code |
|---|
| 146 |
<li> 19: while inflating: end of out buffer memory reached |
|---|
| 147 |
<li> 20: invalid deflate block BTYPE encountered |
|---|
| 148 |
<li> 21: NLEN is not ones complement of LEN in a deflate block |
|---|
| 149 |
<li> 22: while inflating: end of out buffer memory reached. |
|---|
| 150 |
This can happen if the inflated deflate data is longer than the amount of bytes required to fill up |
|---|
| 151 |
all the pixels of the image, given the color depth and image dimensions. Something that doesn't |
|---|
| 152 |
happen in a normal, well encoded, PNG image. |
|---|
| 153 |
<li> 23: while inflating: end of in buffer memory reached |
|---|
| 154 |
<li> 24: invalid FCHECK in zlib header |
|---|
| 155 |
<li> 25: invalid compression method in zlib header |
|---|
| 156 |
<li> 26: FDICT encountered in zlib header while it's not used for PNG |
|---|
| 157 |
<li> 27: PNG file is smaller than a PNG header |
|---|
| 158 |
<li> 28: incorrect PNG signature (the first 8 bytes of the PNG file) |
|---|
| 159 |
Maybe it's not a PNG, or a PNG file that got corrupted so that the header indicates the corruption. |
|---|
| 160 |
<li> 29: first chunk is not the header chunk |
|---|
| 161 |
<li> 30: chunk length too large, chunk broken off at end of file |
|---|
| 162 |
<li> 31: illegal PNG color type |
|---|
| 163 |
<li> 32: illegal PNG compression method |
|---|
| 164 |
<li> 33: illegal PNG filter method |
|---|
| 165 |
<li> 34: illegal PNG interlace method |
|---|
| 166 |
<li> 35: chunk length of a chunk is too large or the chunk too small |
|---|
| 167 |
<li> 36: illegal PNG filter type encountered |
|---|
| 168 |
<li> 37: illegal bit depth for this color type given |
|---|
| 169 |
<li> 38: the palette is too big (more than 256 colors) |
|---|
| 170 |
<li> 39: more palette alpha values given in tRNS, than there are colors in the palette |
|---|
| 171 |
<li> 40: tRNS chunk has wrong size for greyscale image |
|---|
| 172 |
<li> 41: tRNS chunk has wrong size for RGB image |
|---|
| 173 |
<li> 42: tRNS chunk appeared while it was not allowed for this color type |
|---|
| 174 |
<li> 43: bKGD chunk has wrong size for palette image |
|---|
| 175 |
<li> 44: bKGD chunk has wrong size for greyscale image |
|---|
| 176 |
<li> 45: bKGD chunk has wrong size for RGB image |
|---|
| 177 |
<li> 46: value encountered in indexed image is larger than the palette size (bitdepth == 8) |
|---|
| 178 |
<li> 47: value encountered in indexed image is larger than the palette size (bitdepth < 8) |
|---|
| 179 |
<li> 48: the input data is empty. Maybe a PNG file you tried to load doesn't exist or is in the wrong path. |
|---|
| 180 |
<li> 49: jumped past memory while generating dynamic huffman tree |
|---|
| 181 |
<li> 50: jumped past memory while generating dynamic huffman tree |
|---|
| 182 |
<li> 51: jumped past memory while inflating huffman block |
|---|
| 183 |
<li> 52: jumped past memory while inflating |
|---|
| 184 |
<li> 53: size of zlib data too small to contain header |
|---|
| 185 |
<li> 54: CHAR_BITS is smaller than 8 on this platform, an 8-bit datatype is needed to store the file and pixel data |
|---|
| 186 |
<li>55: jumped past tree while generating huffman tree, this could be when the code |
|---|
| 187 |
lengths are not of an optimal_ tree, which causes there to be more nodes than |
|---|
| 188 |
the arrays used can support (e.g. if there are 19 codes a tree has 18 nodes, |
|---|
| 189 |
but if all 19 code lengths are 7, there will be much more nodes), this may be a |
|---|
| 190 |
BUG that has to be fixed though I haven't actually encountered PNGs causing |
|---|
| 191 |
this problem outside the experimentation environment |
|---|
| 192 |
<li> 56: As of LodePNG version 20061209, the input and output parameter of the decode functions got swapped! |
|---|
| 193 |
Try to swap the input and output parameter in the decode function call. (first out, then in) |
|---|
| 194 |
If they are already correct and you get this error, then see description of error 48. |
|---|
| 195 |
*/ |
|---|
| 196 |
int error = 1; //the error value of the decode attempt |
|---|
| 197 |
|
|---|
| 198 |
// dimensions of the image |
|---|
| 199 |
size_t w; /// width of the image in pixels |
|---|
| 200 |
size_t h; /// height of the image in pixels |
|---|
| 201 |
|
|---|
| 202 |
//pixel color info |
|---|
| 203 |
uint bitDepth; /// bits per sample |
|---|
| 204 |
uint bpp; /// bits per pixel |
|---|
| 205 |
uint colorChannels; /// amount of channels |
|---|
| 206 |
|
|---|
| 207 |
//palette info |
|---|
| 208 |
uint paletteSize; /// size of the palette (palette.size() / 4) |
|---|
| 209 |
ubyte[] palette; /// palette in RGBARGBA... order |
|---|
| 210 |
|
|---|
| 211 |
//transparent color key info |
|---|
| 212 |
bool colorKey; /// is a transparent color key given? |
|---|
| 213 |
ubyte keyR; /// red/greyscale component of color key |
|---|
| 214 |
ubyte keyG; /// green component of color key |
|---|
| 215 |
ubyte keyB; /// blue component of color key |
|---|
| 216 |
|
|---|
| 217 |
//PNG specific information (general) |
|---|
| 218 |
uint colorType; /// color type of the original PNG file |
|---|
| 219 |
uint compressionMethod; /// compression method of the original file |
|---|
| 220 |
uint filterMethod; /// filter method of the original file |
|---|
| 221 |
uint interlaceMethod; /// interlace method of the original file |
|---|
| 222 |
|
|---|
| 223 |
//PNG specific information (bKGD chunk) |
|---|
| 224 |
bool backgroundColor; /// is a suggested background color given? |
|---|
| 225 |
ubyte backgroundR; /// red component of sugg. background color |
|---|
| 226 |
ubyte backgroundG; /// green component of sugg. background color |
|---|
| 227 |
ubyte backgroundB; /// blue component of sugg. background color |
|---|
| 228 |
} |
|---|
| 229 |
/**This function converts the given compressed PNG data into uncompressed pixel |
|---|
| 230 |
data. The original color type is preserved, which can make it more difficult |
|---|
| 231 |
to interpret the pixel data since you have to take into account what color |
|---|
| 232 |
type it is. |
|---|
| 233 |
|
|---|
| 234 |
For normal usage of LodePNG, this function is *not* recommended, but in special |
|---|
| 235 |
cases where you know beforehand what color type the PNG image has it can provide |
|---|
| 236 |
an increase in efficiency. Use decode32 instead if you want any type of PNG |
|---|
| 237 |
to be easily displayed! The decode32 function first calls decodeGeneric |
|---|
| 238 |
and then does the job of converting all possible color types to RGBA. |
|---|
| 239 |
|
|---|
| 240 |
Params: |
|---|
| 241 |
_in = This is used to give the original file data. |
|---|
| 242 |
Provide a vector filled with the contents of a PNG file. The contents of |
|---|
| 243 |
this buffer will be modified during the decoding process, so it cannot be |
|---|
| 244 |
reused. If you need the file contents a second time, make a copy before |
|---|
| 245 |
decoding the PNG or reload the file. |
|---|
| 246 |
|
|---|
| 247 |
_out = This is used to return the pixel data. |
|---|
| 248 |
Provide an empty vector. The vector will be resized and the uncompressed |
|---|
| 249 |
pixel data will be stored in the buffer. The image data will be of the same |
|---|
| 250 |
color type as the PNG image. Pixels are always stored row by row. The first |
|---|
| 251 |
byte of the data contains the first byte of the pixel of the top left of |
|---|
| 252 |
the image. 1-bit images or other formats with less than one byte per pixel |
|---|
| 253 |
will be stored bit per bit. In that case, scanlines don't necessarily start |
|---|
| 254 |
at the boundary of a byte. If RGB or RGBA is used, color components per pixel |
|---|
| 255 |
are successive bits/bytes in the order R, G, B, A. |
|---|
| 256 |
*/ |
|---|
| 257 |
Info decodeGeneric(inout ubyte[] _out, inout ubyte[] _in) |
|---|
| 258 |
{ |
|---|
| 259 |
Info info; |
|---|
| 260 |
//read the information from the header and store it in the Info |
|---|
| 261 |
void readPngHeader(ubyte* _in, size_t inlength) |
|---|
| 262 |
{ |
|---|
| 263 |
if(inlength < 29) { info.error = 27; return; } //error: the data length is smaller than the length of the header |
|---|
| 264 |
|
|---|
| 265 |
if(_in[0] != 137 || _in[1] != 80 || _in[2] != 78 || _in[3] != 71 || _in[4] != 13 || _in[5] != 10 || _in[6] != 26 || _in[7] != 10) { info.error = 28; return; } //error: the first 8 bytes are not the correct PNG signature |
|---|
| 266 |
if(_in[12] != 73 || _in[13] != 72 || _in[14] != 68 || _in[15] != 82) { info.error = 29; return; } //error: it doesn't start with a IHDR chunk! |
|---|
| 267 |
|
|---|
| 268 |
//read the values given in the header |
|---|
| 269 |
info.w = 256 * 256 * 256 * _in[16] + 256 * 256 * _in[17] + 256 * _in[18] + _in[19]; |
|---|
| 270 |
info.h = 256 * 256 * 256 * _in[20] + 256 * 256 * _in[21] + 256 * _in[22] + _in[23]; |
|---|
| 271 |
info.bitDepth = _in[24]; |
|---|
| 272 |
info.colorType = _in[25]; |
|---|
| 273 |
info.compressionMethod = _in[26]; |
|---|
| 274 |
info.filterMethod = _in[27]; |
|---|
| 275 |
info.interlaceMethod = _in[28]; |
|---|
| 276 |
//The 4 CRC bytes are ignored |
|---|
| 277 |
|
|---|
| 278 |
if(info.compressionMethod != 0) { info.error = 32; return; } //error: only compression method 0 is allowed in the specification |
|---|
| 279 |
if(info.filterMethod != 0) { info.error = 33; return; } //error: only filter method 0 is allowed in the specification |
|---|
| 280 |
if(info.interlaceMethod > 1) { info.error = 34; return; } //error: only interlace methods 0 and 1 exist in the specification |
|---|
| 281 |
|
|---|
| 282 |
//check if bit depth is valid for this color type, if not give error 37 "illegal bit depth for this color type given" |
|---|
| 283 |
uint bd = info.bitDepth; //longer variable name makes code below more easily readable |
|---|
| 284 |
switch(info.colorType) |
|---|
| 285 |
{ |
|---|
| 286 |
case 0: if(bd != 1 && bd != 2 && bd != 4 && bd != 8 && bd != 16) { info.error = 37; return; } break; |
|---|
| 287 |
case 2: if( bd != 8 && bd != 16) { info.error = 37; return; } break; |
|---|
| 288 |
case 3: if(bd != 1 && bd != 2 && bd != 4 && bd != 8 ) { info.error = 37; return; } break; |
|---|
| 289 |
case 4: if( bd != 8 && bd != 16) { info.error = 37; return; } break; |
|---|
| 290 |
case 6: if( bd != 8 && bd != 16) { info.error = 37; return; } break; |
|---|
| 291 |
default: { info.error = 31; return; } break; //error: invalid color type |
|---|
| 292 |
} |
|---|
| 293 |
} |
|---|
| 294 |
|
|---|
| 295 |
//filter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works byte per byte (bytewidth = 1) |
|---|
| 296 |
//precon is the previous filtered scanline, recon the result, scanline the current one |
|---|
| 297 |
void unFilterScanline(ubyte[] recon, ubyte[] scanline, ubyte[] precon, bool top, size_t bytewidth, uint filterType, size_t length) |
|---|
| 298 |
{ |
|---|
| 299 |
switch(filterType) |
|---|
| 300 |
{ |
|---|
| 301 |
case 0: |
|---|
| 302 |
for(size_t i = 0; i < length; i++) recon[i] = scanline[i]; |
|---|
| 303 |
break; |
|---|
| 304 |
case 1: |
|---|
| 305 |
if(top) |
|---|
| 306 |
{ |
|---|
| 307 |
for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i]; |
|---|
| 308 |
for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth]; |
|---|
| 309 |
} |
|---|
| 310 |
else |
|---|
| 311 |
{ |
|---|
| 312 |
for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i]; |
|---|
| 313 |
for(size_t i = bytewidth; i < length ; i++) recon[i] = scanline[i] + recon[i - bytewidth]; |
|---|
| 314 |
} |
|---|
| 315 |
break; |
|---|
| 316 |
case 2: |
|---|
| 317 |
if(top) for(size_t i = 0; i < length; i++) recon[i] = scanline[i]; |
|---|
| 318 |
else for(size_t i = 0; i < length; i++) recon[i] = scanline[i] + precon[i]; |
|---|
| 319 |
break; |
|---|
| 320 |
case 3: |
|---|
| 321 |
if(top) |
|---|
| 322 |
{ |
|---|
| 323 |
for(size_t i = 0; i < length; i++) recon[i] = scanline[i]; |
|---|
| 324 |
for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth] >> 1; |
|---|
| 325 |
} |
|---|
| 326 |
else |
|---|
| 327 |
{ |
|---|
| 328 |
for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i] + precon[i] / 2; |
|---|
| 329 |
for(size_t i = bytewidth; i < length; i++) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1); |
|---|
| 330 |
} |
|---|
| 331 |
break; |
|---|
| 332 |
case 4: |
|---|
| 333 |
if(top) |
|---|
| 334 |
{ |
|---|
| 335 |
for(size_t i = 0; i < bytewidth; i++) recon[i] = scanline[i]; |
|---|
| 336 |
for(size_t i = bytewidth; i < length; i++) recon[i] = cast(ubyte)(scanline[i] + paethPredictor(recon[i - bytewidth], 0, 0)); |
|---|
| 337 |
} |
|---|
| 338 |
else |
|---|
| 339 |
{ |
|---|
| 340 |
for(size_t i = 0; i < bytewidth; i++) recon[i] = cast(ubyte)(scanline[i] + paethPredictor(0, precon[i], 0)); |
|---|
| 341 |
for(size_t i = bytewidth; i < length; i++) recon[i] = cast(ubyte)(scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); |
|---|
| 342 |
} |
|---|
| 343 |
break; |
|---|
| 344 |
default: info.error = 36; return; //error: unexisting filter type given |
|---|
| 345 |
} |
|---|
| 346 |
} |
|---|
| 347 |
|
|---|
| 348 |
//filter and reposition the pixels into the output when the image is Adam7 interlaced. This function can only do it after the full image is already decoded. The out buffer must have the correct allocated memory size already. |
|---|
| 349 |
void adam7Pass(ubyte[] _out, ubyte[] _in, ubyte[] scanlinen, ubyte[] scanlineo, size_t w, size_t bytewidth, size_t passleft, size_t passtop, size_t spacex, size_t spacey, size_t passw, size_t passh, uint bpp) |
|---|
| 350 |
{ |
|---|
| 351 |
for(uint s = 0; s < passh; s++) |
|---|
| 352 |
{ |
|---|
| 353 |
size_t linelength = 1 + ((bpp * passw + 7) >> 3); |
|---|
| 354 |
size_t linestart = s * linelength; //position where we read the filterType: at the start of the scanline |
|---|
| 355 |
uint filterType = _in[linestart]; |
|---|
| 356 |
|
|---|
| 357 |
unFilterScanline(scanlinen, _in[linestart + 1..$], scanlineo, (s < 1), bytewidth, filterType, (w * bpp + 7) >> 3); |
|---|
| 358 |
if(info.error) return; |
|---|
| 359 |
|
|---|
| 360 |
//put the filtered pixels in the output image |
|---|
| 361 |
if(bpp >= 8) |
|---|
| 362 |
{ |
|---|
| 363 |
for(size_t i = 0; i < passw; i++) |
|---|
| 364 |
for(size_t b = 0; b < bytewidth; b++) //b = current byte of this pixel |
|---|
| 365 |
{ |
|---|
| 366 |
_out[bytewidth * w * (passtop + spacey * s) + bytewidth * (passleft + spacex * i) + b] = scanlinen[bytewidth * i + b]; |
|---|
| 367 |
} |
|---|
| 368 |
} |
|---|
| 369 |
else |
|---|
| 370 |
{ |
|---|
| 371 |
for(size_t i = 0; i < passw; i++) |
|---|
| 372 |
{ |
|---|
| 373 |
size_t outbitp = bpp * w * (passtop + spacey * s) + bpp * (passleft + spacex * i); |
|---|
| 374 |
for(size_t b = 0; b < bpp; b++) //b = current bit of this pixel |
|---|
| 375 |
{ |
|---|
| 376 |
size_t obp = outbitp + b; |
|---|
| 377 |
size_t obitpos = 7 - (obp & 0x7); //where bitpos 0 refers to the LSB, bitpot 7 to the MSB of a byte |
|---|
| 378 |
size_t bp = i * bpp + b; |
|---|
| 379 |
size_t bitpos = 7 - (bp & 0x7); //where bitpos 0 refers to the LSB, bitpot 7 to the MSB of a byte |
|---|
| 380 |
uint _bit = (scanlinen[bp >> 3] >> bitpos) & 1; |
|---|
| 381 |
_out[obp >> 3] = cast(ubyte)((_out[obp >> 3] & ~(1 << obitpos)) | (_bit << obitpos)); |
|---|
| 382 |
} |
|---|
| 383 |
} |
|---|
| 384 |
} |
|---|
| 385 |
|
|---|
| 386 |
//swap the two buffer pointers "scanline old" and "scanline new" |
|---|
| 387 |
ubyte[] temp = scanlinen; |
|---|
| 388 |
scanlinen = scanlineo; |
|---|
| 389 |
scanlineo = temp; |
|---|
| 390 |
} |
|---|
| 391 |
} |
|---|
| 392 |
|
|---|
| 393 |
void resetParameters() |
|---|
| 394 |
{ |
|---|
| 395 |
info.error = 0; //initially no error happened yet |
|---|
| 396 |
info.paletteSize = info.backgroundColor = info.colorKey = 0; //initialize info variables that aren't necessarily set later on |
|---|
| 397 |
} |
|---|
| 398 |
|
|---|
| 399 |
//if(CHAR_BIT < 8) { info.error = 54; return; } // not relevant in D, type sizes are guaranteed |
|---|
| 400 |
if(_in.length == 0 && _out.length > 0) { info.error = 56; return info; } //the given input data is empty but the output data not |
|---|
| 401 |
if(_in.length == 0) { info.error = 48; return info; } //the given data is empty |
|---|
| 402 |
|
|---|
| 403 |
resetParameters(); //when decoding a new PNG image, make sure all parameters created after previous decoding are reset |
|---|
| 404 |
|
|---|
| 405 |
readPngHeader(_in.ptr, _in.length); |
|---|
| 406 |
if(info.error) return info; |
|---|
| 407 |
|
|---|
| 408 |
size_t pos = 33; //first byte of the first chunk after the header |
|---|
| 409 |
size_t dataPos = 0; //at this position in the in buffer the new data will be stored. |
|---|
| 410 |
size_t dataStart = 0; |
|---|
| 411 |
|
|---|
| 412 |
bool IEND = 0; |
|---|
| 413 |
while(!IEND) //loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. IDAT data is put at the start of the in buffer |
|---|
| 414 |
{ |
|---|
| 415 |
//get chunk length |
|---|
| 416 |
if(pos + 8 >= _in.length) { info.error = 30; return info; } //error: size of the in buffer too small to contain next chunk |
|---|
| 417 |
size_t chunkLength = 256 * 256 * 256 * _in[pos] + 256 * 256 * _in[pos + 1] + 256 * _in[pos + 2] + _in[pos + 3]; pos += 4; |
|---|
| 418 |
if(pos + chunkLength >= _in.length) { info.error = 35; return info; } //error: size of the in buffer too small to contain next chunk |
|---|
| 419 |
|
|---|
| 420 |
//IDAT chunk, containing compressed image data |
|---|
| 421 |
if(_in[pos + 0] == 'I' && _in[pos + 1] == 'D' && _in[pos + 2] == 'A' && _in[pos + 3] == 'T') |
|---|
| 422 |
{ |
|---|
| 423 |
pos += 4; |
|---|
| 424 |
if(dataPos == 0) { dataStart = pos; dataPos = dataStart + chunkLength; pos += chunkLength; } //possible efficiency increase by not copying data from first chunk |
|---|
| 425 |
else for(size_t i = 0; i < chunkLength; i++) _in[dataPos++] = _in[pos++]; //multiple data chunks are added behind each other this way |
|---|
| 426 |
} |
|---|
| 427 |
//IEND chunk |
|---|
| 428 |
else if(_in[pos + 0] == 'I' && _in[pos + 1] == 'E' && _in[pos + 2] == 'N' && _in[pos + 3] == 'D') |
|---|
| 429 |
{ |
|---|
| 430 |
IEND = 1; |
|---|
| 431 |
} |
|---|
| 432 |
//palette chunk (PLTE) |
|---|
| 433 |
else if(_in[pos + 0] == 'P' && _in[pos + 1] == 'L' && _in[pos + 2] == 'T' && _in[pos + 3] == 'E') |
|---|
| 434 |
{ |
|---|
| 435 |
pos += 4; //go after the 4 letters |
|---|
| 436 |
info.paletteSize = chunkLength / 3; |
|---|
| 437 |
if(info.paletteSize > 256) { info.error = 38; return info; } //error: palette too big |
|---|
| 438 |
info.palette.length = 4 * info.paletteSize; |
|---|
| 439 |
for(size_t i = 0; i < info.paletteSize; i++) |
|---|
| 440 |
{ |
|---|
| 441 |
info.palette[i * 4 + 0] = _in[pos++]; //R |
|---|
| 442 |
info.palette[i * 4 + 1] = _in[pos++]; //G |
|---|
| 443 |
info.palette[i * 4 + 2] = _in[pos++]; //B |
|---|
| 444 |
info.palette[i * 4 + 3] = 255; //alpha |
|---|
| 445 |
} |
|---|
| 446 |
} |
|---|
| 447 |
//palette transparency chunk (tRNS) |
|---|
| 448 |
else if(_in[pos + 0] == 't' && _in[pos + 1] == 'R' && _in[pos + 2] == 'N' && _in[pos + 3] == 'S') |
|---|
| 449 |
{ |
|---|
| 450 |
pos += 4; //go after the 4 letters |
|---|
| 451 |
if(info.colorType == 3) |
|---|
| 452 |
{ |
|---|
| 453 |
if(chunkLength > info.paletteSize) { info.error = 39; return info; } //error: more alpha values given than there are palette entries |
|---|
| 454 |
for(size_t i = 0; i < chunkLength; i++) info.palette[i * 4 + 3] = _in[pos++]; |
|---|
| 455 |
} |
|---|
| 456 |
else if(info.colorType == 0) |
|---|
| 457 |
{ |
|---|
| 458 |
if(chunkLength != 2) { info.error = 40; return info; } //error: this chunk must be 2 bytes for greyscale image |
|---|
| 459 |
info.colorKey = 1; |
|---|
| 460 |
info.keyR = 256 * _in[pos] + _in[pos + 1]; pos += 2; |
|---|
| 461 |
} |
|---|
| 462 |
else if(info.colorType == 2) |
|---|
| 463 |
{ |
|---|
| 464 |
if(chunkLength != 6) { info.error = 41; return info; } //error: this chunk must be 6 bytes for RGB image |
|---|
| 465 |
info.colorKey = 1; |
|---|
| 466 |
info.keyR = 256 * _in[pos] + _in[pos + 1]; pos += 2; |
|---|
| 467 |
info.keyG = 256 * _in[pos] + _in[pos + 1]; pos += 2; |
|---|
| 468 |
info.keyB = 256 * _in[pos] + _in[pos + 1]; pos += 2; |
|---|
| 469 |
} |
|---|
| 470 |
else { info.error = 42; return info; } //error: tRNS chunk not allowed for other color models |
|---|
| 471 |
} |
|---|
| 472 |
//background color chunk (bKGD) |
|---|
| 473 |
else if(_in[pos + 0] == 'b' && _in[pos + 1] == 'K' && _in[pos + 2] == 'G' && _in[pos + 3] == 'D') |
|---|
| 474 |
{ |
|---|
| 475 |
pos += 4; //go after the 4 letters |
|---|
| 476 |
if(info.colorType == 3) |
|---|
| 477 |
{ |
|---|
| 478 |
if(chunkLength != 1) { info.error = 43; return info; } //error: this chunk must be 1 byte for indexed color image |
|---|
| 479 |
info.backgroundColor = 1; |
|---|
| 480 |
info.backgroundR = _in[pos++]; |
|---|
| 481 |
} |
|---|
| 482 |
else if(info.colorType == 0 || info.colorType == 4) |
|---|
| 483 |
{ |
|---|
| 484 |
if(chunkLength != 2) { info.error = 44; return info; } //error: this chunk must be 2 bytes for greyscale image |
|---|
| 485 |
info.backgroundColor = 1; |
|---|
| 486 |
info.backgroundR = 256 * _in[pos] + _in[pos + 1]; pos += 2; |
|---|
| 487 |
} |
|---|
| 488 |
else if(info.colorType == 2 || info.colorType == 6) |
|---|
| 489 |
{ |
|---|
| 490 |
if(chunkLength != 6) { info.error = 45; return info; } //error: this chunk must be 6 bytes for greyscale image |
|---|
| 491 |
info.backgroundColor = 1; |
|---|
| 492 |
info.backgroundR = 256 * _in[pos] + _in[pos + 1]; pos += 2; |
|---|
| 493 |
info.backgroundG = 256 * _in[pos] |
|---|