comparison dynamin/lodepng/decode.d @ 0:aa4efef0f0b1

Initial commit of code.
author Jordan Miner <jminer7@gmail.com>
date Mon, 15 Jun 2009 22:10:48 -0500
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:aa4efef0f0b1
1 // Written in the D programming language
2 // www.digitalmars.com/d/
3
4 /***************************************************************************************************
5 License:
6 Copyright (c) 2005-2007 Lode Vandevenne
7 All rights reserved.
8
9 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
10
11 - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.<br>
12 - 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.<br>
13 - 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.<br>
14
15 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.
16
17 Authors: Lode Vandevenne (original version in C++), Lutger Blijdestijn (D version) : lutger dot blijdestijn at gmail dot com.
18
19 About:
20 The decoder is small but sufficient for most purposes. It is compliant to the png specification and
21 has been tested with the png suite. The api is procedural and simple, meant to be easily integrated. It is compatible with the
22 Phobos and Tango libraries. For Tango, lodepng expects the zlib binding from phobos in etc.c.zlib.d<br>
23 This module publicly imports lodepng.Common, where you'll find the data types used by both the encoder
24 and decoder, as well as some utility and image format conversion routines.
25
26 Date: August 7, 2007
27
28 Features:
29 The following features are supported by the decoder:
30 <ul>
31 <li> conformant decoding of PNGs (all color types, bit depth, interlace mode, CRC checking, etc.)</li>
32 <li> support for translucent PNG's, including translucent palettes and color key</li>
33 <li> textual key-value meta-data</li>
34 <li> the following chunks are interpreted by the decoder
35 <ul>
36 <li>IHDR (image information)</li>
37 <li>PLTE (color palette)</li>
38 <li>IDAT (pixel data)</li>
39 <li>IEND (the final chunk)</li>
40 <li>tRNS (transparency for palettized images)</li>
41 <li>bKGD (suggested background color)</li>
42 <li>tEXt (uncompressed latin-1 key-value strings)</li>
43 <li>zTXt (compressed latin-1 key-value strings)</li>
44 <li>iTXt (utf8 key-value strings)</li>
45 </ul>
46 </li>
47 </UL>
48
49 Limitations:
50 The following features are not supported.
51 <ul>
52 <li> editing a PNG image (unless you use the decoder, then edit it, then use the
53 encoder, but ignored chunks will then be gone from the original image)</li>
54 <li> Streaming / progressive display. All data must be available and is processed in one call.</li>
55 <li> The following optional chunk types are not interpreted by the decoder
56 <ul>
57 <li>cHRM (device independent color info)
58 <li>gAMA (device independent color info)
59 <li>iCCP (device independent color info)
60 <li>sBIT (original number of significant bits)
61 <li>sRGB (device independent color info)
62 <li>pHYs (physical pixel dimensions)
63 <li>sPLT (suggested reduced palette)
64 <li>tIME (last image modification time)
65 </ul>
66 </li>
67 </ul>
68
69 Examples:
70 Here is an example how you could use LodePNG with opengl, see the api documentation for details.
71 ---
72 uint loadPNG(char[] filename)
73 {
74 uint textureID;
75
76 glEnable(GL_TEXTURE_2D);
77 glGenTextures(1, &textureID);
78 glBindTexture(GL_TEXTURE_2D, textureID);
79 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
80 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
81
82 PngInfo info;
83 ubyte[] image = decode32(cast(ubyte[])std.file.read(filename), info);
84
85 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, info.image.width, info.image.height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
86 image.ptr);
87 return textureID;
88 }
89 ---
90
91 References:
92 $(LINK2 http://members.gamedev.net/lode/projects/LodePNG/, Original lodepng) <br>
93 $(LINK2 http://www.w3.org/TR/PNG/, PNG Specification) <br>
94 $(LINK2 http://www.libpng.org/pub/png/pngsuite.html, PNG Suite: set of test images) <br>
95 $(LINK2 http://optipng.sourceforge.net/, OptiPNG: tool to experimentally optimize png images)
96 */
97
98 module dynamin.lodepng.decode;
99 import dynamin.lodepng.zlib_codec;
100 import dynamin.lodepng.util;
101 import std.intrinsic;
102
103 public import dynamin.lodepng.common;
104
105 /***************************************************************************************************
106 Parse png image header from memory. The first 33 bytes of the png file need to be available
107
108 throws: PngException
109
110 ***************************************************************************************************/
111 PngImage readHeader(in ubyte[] source)
112 {
113 ubyte interlace;
114 return readHeader(source, interlace);
115 }
116
117 /// ditto
118 PngImage readHeader(in ubyte[] source, out ubyte interlace)
119 in
120 {
121 assert(source.length >= HEADER_SIZE, "array is too small to contain png header");
122 }
123 body // see spec: http://www.w3.org/TR/PNG/#11IHDR
124 {
125 mixin(pngEnforce(`source.length >= HEADER_SIZE`, "png header data is too small"));
126 mixin(pngEnforce(`source[0..8] == [cast(ubyte)137, 80, 78, 71, 13, 10, 26, 10]`, "invalid png header "));
127 mixin(pngEnforce((toUint(source[12..16]) == IHDR).stringof, "invalid png header"));
128 mixin(pngEnforce(`checkCRC(source[29 .. 33], source[12 .. 29])`, "invalid CRC"));
129
130 PngImage result;
131 ubyte interlaceMethod;
132
133 with (result)
134 {
135 width = toUint(source[16..20]);
136 height = toUint(source[20..24]);
137 bitDepth = source[24];
138 colorType = cast(ColorType)source[25];
139 mixin(pngEnforce( `source[26] == 0`, "unsupported compression method in png header" ));
140 mixin(pngEnforce( `source[27] == 0`, "unsupported filter method in png header" ));
141 mixin(pngEnforce( `checkColorValidity(colorType, bitDepth)`, "invalid header: wrong color format" ));
142 interlaceMethod = source[28];
143 mixin(pngEnforce( `interlaceMethod < 2`, "invalid interlace method in png header" ));
144 bpp = numChannels(colorType) * bitDepth;
145 }
146 interlace = interlaceMethod;
147 return result;
148 }
149
150 /***************************************************************************************************
151 Decode source png file
152
153 If a buffer is provided, it may be used to store the result. See bufferSize for details.
154
155 Throws: PngException
156 Returns: Decoded image pixels. The color format of the resulting image is the
157 same as the source image, see lodepng.Common.convert and decode32 if a specific color format
158 is desired.
159
160 ***************************************************************************************************/
161 ubyte[] decode(in ubyte[] source, ref PngInfo info, ubyte[] buffer = null)
162 {
163 info.image = readHeader(source, info.interlace);
164
165 // holds interlaced filtered scanlines
166 ubyte[] ilaceBuffer;
167
168 // to allocate memory as needed
169 if (info.interlace == 1)
170 buffer.length = ((info.image.width * info.image.bpp + 7) / 8) * info.image.height + (info.image.height * 2); // guess
171 else
172 buffer.length = ((info.image.width * info.image.bpp + 7) / 8) * info.image.height + info.image.height;
173
174
175 if (info.interlace == 1)
176 ilaceBuffer.length = buffer.length - info.image.height;
177
178 auto inflator = DecodeStream.create(buffer);
179
180 foreach(chunk; StreamChunkIter(source[HEADER_SIZE - 1 .. $]))
181 {
182 switch(chunk.type)
183 {
184 case IDAT:
185 inflator(chunk.data);
186 break;
187 case PLTE:
188 mixin(pngEnforce(`chunk.data.length <= 256 * 3`, "palette size is too large"));
189 info.palette.length = chunk.data.length / 3;
190 foreach(index, ref ubyte[4] color; info.palette)
191 {
192 color[0..3] = chunk.data[index * 3 .. index * 3 + 3];
193 color[3] = 255;
194 }
195
196 break;
197 case tRNS:
198 if (chunk.data.length == 0)
199 break;
200 info.colorKey = true;
201 if (info.image.colorType == ColorType.Palette) // index-values
202 {
203 mixin(pngEnforce(`chunk.data.length <= info.palette.length`, "palette size is too large"));
204 foreach(index, alpha; chunk.data)
205 info.palette[index][3] = alpha;
206 }
207 else if (info.image.colorType == ColorType.RGB)
208 {
209 info.keyR = 256U * chunk.data[0] + chunk.data[1];
210 info.keyG = 256U * chunk.data[2] + chunk.data[3];
211 info.keyB = 256U * chunk.data[4] + chunk.data[5];
212 }
213 else if (info.image.colorType == ColorType.Greyscale)
214 {
215 info.keyR = 256U * chunk.data[0] + chunk.data[1];
216 }
217 else
218 assert(false);
219 break;
220 case bKGD:
221 if (info.image.colorType == ColorType.Palette || info.image.bitDepth == 16)
222 info.backgroundColor = chunk.data.dup;
223 else
224 {
225 info.backgroundColor.length = chunk.data.length / 2;
226 foreach(index, ref value; info.backgroundColor)
227 value = chunk.data[index * 2];
228 }
229 break;
230 case zTXt:
231 if (info.parseText)
232 {
233 if (info.textual is null)
234 info.textual = new PngText;
235 auto sep = strFind(cast(char[])chunk.data, 0);
236 if (sep > 0)
237 {
238 if (chunk.data[sep + 1] == 0)
239 info.textual[chunk.data[0..sep]] = chunk.data[sep + 2 .. $];
240 else
241 {
242 ubyte[] value;
243 auto decoder = DecodeStream.create(value);
244 decoder(chunk.data[sep + 2 .. $]);
245 info.textual[chunk.data[0..sep]] = value;
246 }
247 }
248 }
249 break;
250 case tEXt:
251 if (info.parseText)
252 {
253 if (info.textual is null)
254 info.textual = new PngText;
255 auto sep = strFind(cast(char[])chunk.data, 0);
256 if (sep > 0)
257 {
258 info.textual[chunk.data[0..sep]] = chunk.data[sep + 1 .. $];
259 }
260 }
261 break;
262 case iTXt:
263 if (info.parseText)
264 {
265 if (info.textual is null)
266 info.textual = new PngText;
267 auto sep = strFind(cast(char[])chunk.data, 0);
268 char[] keyword = cast(char[])chunk.data[0..sep];
269 bool compressed = chunk.data[sep + 1] == 0 ? false : true;
270 sep += strFind(cast(char[])chunk.data[sep + 3 .. $], 0) + 3;
271 sep += strFind(cast(char[])chunk.data[sep + 1 .. $], 0) + 1;
272 if (!compressed)
273 info.textual[keyword] = cast(char[])chunk.data[sep + 1..$];
274 else
275 {
276 ubyte[] value;
277 auto decoder = DecodeStream.create(value);
278 decoder(chunk.data[sep + 1..$]);
279 info.textual[keyword] = cast(char[])value;
280 }
281 }
282 break;
283 default:
284 mixin(pngEnforce(`(bt(&chunk.type, 6) < 0)`, "unrecognized critical chunk"));
285 break;
286 }
287 }
288 assert(inflator.hasEnded);
289 buffer = inflator();
290
291 return (info.interlace == 0) ? reconstruct(buffer, info.image)
292 : deinterlace(buffer, ilaceBuffer, info.image);
293 }
294
295 /***************************************************************************************************
296 Decode source png file to RGBA format
297
298 Throws: PngException
299 Returns: decoded image pixels in 32-bit RGBA format
300 ***************************************************************************************************/
301 ubyte[] decode32(in ubyte[] source, ref PngInfo info, ubyte[] buffer = null)
302 {
303 buffer = decode(source, info, buffer);
304 buffer = convert(buffer, info, ColorType.RGBA);
305 info.image.colorType = ColorType.RGBA;
306 info.image.bpp = 32;
307 info.image.bitDepth = 8;
308 info.palette.length = 0;
309 return buffer;
310 }
311
312
313
314
315 /***************************************************************************************************
316 Predict size of buffer needed for decoding
317
318 Estimate of the amount of heap memory needed to decode an image. Interlaced images, images
319 with a color format of less than 8 bits per pixel and the parsing of certain information
320 such as text will allocate more heap memory.
321 ***************************************************************************************************/
322 uint bufferSize( /+const+/ ref PngImage image)
323 {
324 return ((image.width * image.bpp + 7) / 8) * image.height + image.height;
325 }
326
327 ////////////////////////////////////////////////////////////////////////////////////////////////////
328 // //
329 // PRIVATE SECTION //
330 // //
331 ////////////////////////////////////////////////////////////////////////////////////////////////////
332
333 private:
334
335 const uint HEADER_SIZE = 34;
336
337 struct StreamChunkIter
338 {
339 int opApply(int delegate(ref Chunk chunk) visitor)
340 {
341 int result = 0;
342 uint pos = 0;
343 Chunk chunk;
344 while(pos + 12 <= stream.length)
345 {
346 chunk = Chunk.fromStream(stream[pos..$]);
347 if (chunk.type == IEND)
348 break;
349 result = visitor(chunk);
350 if (result)
351 return result;
352 pos += chunk.length;
353 }
354 return result;
355 }
356 ubyte[] stream;
357 }
358
359 //filter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works
360 //byte per byte (bytewidth = 1)precon is the previous filtered scanline, recon the result, scanline
361 //the current one
362 void unFilterFirstScanline(ubyte[] result, ubyte[] scanline, uint bytewidth, uint filterType)
363 in
364 {
365 assert(filterType >= 0 && filterType <= 4);
366 }
367 body
368 {
369 mixin(pngEnforce(`filterType >= 0 && filterType <= 4`, "wrong filter byte: image corrupt?"));
370 switch(filterType)
371 {
372 case 0:
373 for(uint i = 0; i < scanline.length; i++)
374 result[i] = scanline[i];
375 break;
376 case 1:
377 for(uint i = 0; i < bytewidth; i++)
378 result[i] = scanline[i];
379 for(uint i = bytewidth; i < scanline.length; i++)
380 result[i] = scanline[i] + result[i - bytewidth];
381 break;
382 case 2:
383 for(size_t i = 0; i < scanline.length; i++)
384 result[i] = scanline[i];
385 break;
386 case 3:
387 for(size_t i = 0; i < bytewidth; i++)
388 result[i] = scanline[i];
389 for(size_t i = bytewidth; i < scanline.length; i++)
390 result[i] = (scanline[i] + result[i - bytewidth]) / 2;
391 break;
392 case 4:
393 for(size_t i = 0; i < bytewidth; i++)
394 result[i] = scanline[i];
395 for(size_t i = bytewidth; i < scanline.length; i++)
396 result[i] = cast(ubyte)(scanline[i] + paethPredictor(result[i - bytewidth], 0, 0));
397 break;
398 default:
399 mixin(pngEnforce("false", "wrong type of filter"));
400 break;
401
402 }
403 }
404
405 //filter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, the filter works
406 //byte per byte (bytewidth = 1) precon is the previous filtered scanline, recon the result,
407 //scanline the current one
408 void unFilterScanline(ubyte[] result, ubyte[] scanline, ubyte[] previous, uint bytewidth,
409 uint filterType)
410 in
411 {
412 assert(filterType >= 0 && filterType <= 4);
413 }
414 body
415 {
416 switch(filterType)
417 {
418 case 4:
419 for(size_t i = 0; i < bytewidth; i++)
420 result[i] = cast(ubyte)(scanline[i] + previous[i] );
421 for(size_t i = bytewidth; i < scanline.length; i++)
422 result[i] = cast(ubyte)(scanline[i] +
423 paethPredictor(result[i - bytewidth],
424 previous[i],
425 previous[i - bytewidth]));
426 break;
427 case 3:
428 for(size_t i = 0; i < bytewidth; i++)
429 result[i] = scanline[i] + previous[i] / 2;
430 for(size_t i = bytewidth; i < scanline.length; i++)
431 result[i] = scanline[i] + ((result[i - bytewidth] + previous[i]) / 2);
432 break;
433 case 2:
434 for(size_t i = 0; i < scanline.length; i++)
435 result[i] = scanline[i] + previous[i];
436 break;
437 case 1:
438 for(size_t i = 0; i < bytewidth; i++)
439 result[i] = scanline[i];
440 for(size_t i = bytewidth; i < scanline.length; i++)
441 result[i] = scanline[i] + result[i - bytewidth];
442 break;
443 case 0:
444 for(uint i = 0; i < scanline.length; i++) result[i] = scanline[i];
445 break;
446 default:
447 mixin(pngEnforce("false", "wrong type of filter"));
448 break;
449 }
450 }
451
452 ubyte[] deinterlace(in ubyte[] scanlines, ref ubyte[] result, ref PngImage image)
453 {
454 const x = 0;
455 const y = 1;
456 uint bytewidth = (image.bpp + 7) / 8; // bytewidth is used for filtering
457 ubyte[] source;
458 ubyte[] scanlineo = new ubyte[image.width * bytewidth]; //"old" scanline
459 ubyte[] scanlinen = new ubyte[image.width * bytewidth]; //"new" scanline
460
461 uint[7][2] passDim;
462 passDim[x][0] = (image.width + 7) / 8; passDim[y][0] = (image.height + 7) / 8;
463 passDim[x][1] = (image.width + 3) / 8; passDim[y][1] = (image.height + 7) / 8;
464 passDim[x][2] = (image.width + 3) / 4; passDim[y][2] = (image.height + 3) / 8;
465 passDim[x][3] = (image.width + 1) / 4; passDim[y][3] = (image.height + 3) / 4;
466 passDim[x][4] = (image.width + 1) / 2; passDim[y][4] = (image.height + 1) / 4;
467 passDim[x][5] = (image.width + 0) / 2; passDim[y][5] = (image.height + 1) / 2;
468 passDim[x][6] = (image.width + 0) / 1; passDim[y][6] = (image.height + 0) / 2;
469
470 // locate start in source of each pass, note:
471 // (HACK) pass can be empty, sets both x and y axis to zero to recognize this in processPass
472
473 size_t[7] passstart;
474 passstart[0] = 0;
475 for(int i = 0; i < 6; i++)
476 {
477 if (passDim[y][i] * passDim[x][i] == 0)
478 passDim[y][i] = passDim[x][i] = 0;
479
480 passstart[i + 1] = passstart[i] + (passDim[y][i] * (1 + (passDim[x][i] * image.bpp + 7) / 8));
481 }
482
483 void adam7Pass( size_t passleft, size_t passtop, size_t spacex, size_t spacey,
484 size_t passw, size_t passh)
485 {
486 size_t linelength = 1 + ((image.bpp * passw + 7) / 8);
487 size_t linestart = 0;
488 uint filterType = source[0];
489
490 void placePixels(uint s)
491 {
492 if(image.bpp >= 8)
493 for(size_t i = 0; i < passw; i++)
494 for(size_t b = 0; b < bytewidth; b++) //b = current byte of this pixel
495 result[bytewidth * image.width * (passtop + spacey * s) + bytewidth *
496 (passleft + spacex * i) + b] = scanlinen[bytewidth * i + b];
497 else
498 {
499 for(size_t i = 0; i < passw; i++)
500 {
501 size_t outbitp = image.bpp * image.width * (passtop + spacey * s) +
502 image.bpp * (passleft + spacex * i);
503 for(size_t b = 0; b < image.bpp; b++) //b = current bit of this pixel
504 {
505 size_t obp = outbitp + b;
506 //where bitpos 0 refers to the LSB, bitpot 7 to the MSB of a byte
507 size_t obitpos = 7 - (obp & 0x7);
508 size_t bp = i * image.bpp + b;
509 //where bitpos 0 refers to the LSB, bitpot 7 to the MSB of a byte
510 size_t bitpos = 7 - (bp & 0x7);
511 uint _bit = (scanlinen[bp / 8] >> bitpos) & 1;
512 result[obp / 8] = cast(ubyte)((result[obp / 8] & ~(1 << obitpos)) |
513 (_bit << obitpos));
514 }
515 }
516 }
517 }
518
519 void swapScanlines()
520 {
521 ubyte[] temp = scanlinen;
522 scanlinen = scanlineo;
523 scanlineo = temp;
524 }
525
526 unFilterFirstScanline(scanlinen, source[linestart + 1 .. linestart + linelength],
527 bytewidth, filterType);
528 placePixels(0);
529 swapScanlines();
530
531 for(uint s = 1; s < passh; s++)
532 {
533 linestart = s * linelength;
534 filterType = source[linestart];
535
536 unFilterScanline(scanlinen, source[linestart + 1 .. linestart + linelength],
537 scanlineo, bytewidth, filterType);
538 placePixels(s);
539 swapScanlines();
540 }
541 }
542
543 void processPass(size_t pass, size_t left, size_t top, size_t sx, size_t sy)
544 {
545 // check if pass is empty
546 if (pass > 0 && (passDim[x][pass] * passDim[y][pass] == 0))
547 return;
548 source = scanlines[passstart[pass]..$];
549 adam7Pass( left, top, sx, sy, passDim[x][pass], passDim[y][pass]);
550 }
551
552
553 processPass(0, 0, 0, 8, 8);
554 processPass(1, 4, 0, 8, 8);
555 processPass(2, 0, 4, 4, 8);
556 processPass(3, 2, 0, 4, 4);
557 processPass(4, 0, 2, 2, 4);
558 processPass(5, 1, 0, 2, 2);
559 processPass(6, 0, 1, 1, 2);
560
561 return result;
562 }
563
564 ubyte[] reconstruct(ref ubyte[] buffer, /+const+/ ref PngImage image)
565 {
566 //filter and interlace
567 uint bytewidth = (image.bpp + 7) / 8; // bytewidth is used for filtering
568 uint outlength = image.height * image.width * bytewidth;
569
570
571 size_t linestart = 0; //start of current scanline
572
573 // bits are tightly packed, but scanlines are always padded to 1 byte bounderies:
574 uint scanlength = ((image.width * image.bpp) + 7) / 8;
575
576 mixin(pngEnforce( `buffer.length >= (scanlength + 1) * image.height && buffer.length > 0`, "invalid size of source data"));
577
578 uint filterType = buffer[linestart];
579
580
581 if(image.bpp >= 8) //byte per byte
582 {
583 filterType = buffer[linestart];
584 unFilterFirstScanline(buffer, buffer[1..1 + scanlength], bytewidth, filterType);
585
586 //go to start of next scanline
587 linestart += 1 + scanlength;
588
589 for(size_t s = 1; s < image.height; ++s)
590 {
591 filterType = buffer[linestart];
592 unFilterScanline(buffer[linestart - s..$],
593 buffer[linestart + 1 .. linestart + 1 + scanlength],
594 buffer[(s - 1) * image.width * bytewidth..$],
595 bytewidth, filterType);
596
597
598 //go to start of next scanline
599 linestart += 1 + scanlength;
600 }
601 }
602 else //less than 8 bits per pixel, so fill it up bit per bit
603 {
604 size_t obp = 0; //out bit pointer
605
606 ubyte[] templine = new ubyte[scanlength];
607 filterType = buffer[linestart];
608 unFilterFirstScanline(templine, buffer[1 .. 1 + scanlength], bytewidth, filterType);
609
610 linestart += 1 + scanlength;
611
612 //bp is here bit pointer in templine
613 for(size_t bp = 0; bp < image.width * image.bpp; bp++)
614 {
615 size_t obitpos = 7 - (obp & 0x7);
616 size_t bitpos = 7 - (bp & 0x7);
617 uint _bit = (templine[bp / 8] >> bitpos) & 1;
618 buffer[obp / 8] = cast(ubyte)((buffer[obp / 8] & ~(1 << obitpos)) |
619 (_bit << obitpos)); //set current bit
620 obp++;
621 }
622
623 for(size_t s = 1; s < image.height; ++s)
624 {
625 filterType = buffer[linestart];
626 unFilterScanline( templine,
627 buffer[linestart + 1 .. linestart + 1 + scanlength],
628 buffer[(s - 1) * scanlength..$],
629 bytewidth,
630 filterType);
631
632 //bp is here bit pointer in templine
633 for(size_t bp = 0; bp < image.width * image.bpp; bp++)
634 {
635 size_t obitpos = 7 - (obp & 0x7);
636 size_t bitpos = 7 - (bp & 0x7);
637 uint _bit = (templine[bp / 8] >> bitpos) & 1;
638 buffer[obp / 8] = cast(ubyte)((buffer[obp / 8] & ~(1 << obitpos)) |
639 (_bit << obitpos)); //set current bit
640 obp++;
641 }
642
643 //go to start of next scanline
644 linestart += 1 + ((image.width * image.bpp + 7) / 8);
645 }
646 }
647 return buffer;
648 }
649