PageRenderTime 128ms CodeModel.GetById 18ms app.highlight 100ms RepoModel.GetById 1ms app.codeStats 1ms

/decoders/image/png.d

http://github.com/wilkie/djehuty
D | 2722 lines | 380 code | 163 blank | 2179 comment | 66 complexity | c1b69102854802f699bc98aacc78bb5e MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1module decoders.image.png;
   2
   3import graphics.bitmap;
   4
   5import core.stream;
   6import core.string;
   7import core.endian;
   8import core.definitions;
   9
  10import decoders.image.decoder;
  11import decoders.decoder;
  12import decoders.binary.zlib;
  13
  14import io.console;
  15
  16// Section: Codecs/Image
  17
  18// Description: The PNG Codec
  19class PNGDecoder : ImageDecoder {
  20	override string name() {
  21		return "Portable Network Graphics";
  22	}
  23
  24	StreamData decode(Stream stream, ref Bitmap view) {
  25		ImageFrameDescription imageDesc;
  26		bool hasMultipleFrames;
  27
  28		Stream streamToDecode;
  29
  30		streamToDecode = new Stream();
  31
  32		uint* ptr_start;		//ptr of the first pixel
  33
  34		uint* ptr;			//current ptr in image data
  35		uint* ptr_max_line;	//ptr of the next line
  36		uint* ptr_max_page;	//ptr outside of image bounds
  37
  38		ulong ptr_len;
  39
  40		uint psamp;
  41		uint nsamp;
  42
  43		ubyte curByte, recon;
  44
  45		uint palIndex;
  46
  47		int p;
  48		int pa;
  49		int pb;
  50		int pc;
  51
  52		float a;
  53
  54		for (;;) {
  55			switch(decoderState) {
  56			case PNG_STATE_INIT_PROGRESS:
  57
  58				ptrPos = 0;
  59				ptrLine = 0;
  60
  61				pngUncompressedData = new Stream();
  62
  63				decoderNextState = PNG_STATE_DECODE_READ_FILTER_TYPE;
  64
  65				// READ HEADERS //
  66			case PNG_STATE_INIT:
  67
  68				if(!(stream.read(&pngHeader, 8))) {
  69					return StreamData.Required;
  70				}
  71
  72				// DETERMINE VALIDITY OF FILE //
  73
  74				if (!(pngHeader[0] == 0x89 &&
  75					pngHeader[1] == 0x50 &&
  76					pngHeader[2] == 0x4E &&
  77					pngHeader[3] == 0x47 &&
  78					pngHeader[4] == 0x0D &&
  79					pngHeader[5] == 0x0A &&
  80					pngHeader[6] == 0x1A &&
  81					pngHeader[7] == 0x0a)) {
  82					//Header is corrupt
  83	//				Console.putln("header corrupt");
  84	//				Console.putln("png - header corrupt\n");
  85					return StreamData.Invalid;
  86				}
  87
  88				pngPaletteCount = 0;
  89
  90				decoderState = PNG_STATE_READ_CHUNK_HEADER;
  91
  92
  93
  94
  95
  96
  97				// READ CHUNK HEADER //
  98
  99			case PNG_STATE_READ_CHUNK_HEADER:
 100
 101				//Console.putln("png - reading chunk\n");
 102
 103				if(!(stream.read(&pngChunkHeader, _djehuty_image_png_chunk_header.sizeof))) {
 104					return StreamData.Required;
 105				}
 106
 107				pngChunkHeader.pngChunkLength = FromBigEndian32(pngChunkHeader.pngChunkLength);
 108	//			Console.putln(toString(pngChunkHeader.pngChunkLength) ~ "\n");
 109
 110				switch(pngChunkHeader.pngChunkType) {
 111				case PNG_CHUNK_IHDR:
 112
 113					//////////Console.putln("png - IHDR\n");
 114
 115					decoderState = PNG_STATE_READ_IHDR;
 116					continue;
 117
 118				case PNG_CHUNK_PLTE:
 119
 120					//////////Console.putln("png - PLTE\n");
 121
 122					decoderState = PNG_STATE_READ_PLTE;
 123					continue;
 124
 125				case PNG_CHUNK_IDAT:
 126
 127					//Console.putln("png - IDAT\n");
 128
 129					decoderState = PNG_STATE_READ_IDAT;
 130					continue;
 131
 132				case PNG_CHUNK_IEND:
 133					//Console.putln("png - IEND\n");
 134	//			Console.putln("IEND"); //, streamToDecode);
 135
 136					streamToDecode.rewind();
 137				//Console.putln("rewound");
 138
 139					if (zlibDecompressor is null) {
 140						zlibDecompressor = new ZLIBDecoder();
 141					}
 142
 143					if (zlibDecompressor.decode(streamToDecode, pngUncompressedData) == StreamData.Complete) {
 144						//Console.putln("zlib");
 145
 146						//while (pngUncompressedData.getRemaining())
 147						//{
 148						//	pngUncompressedData.Readubyte(curByte);
 149							////////////Console.putln((":") + toString(curByte) + ("\n"));
 150						//}
 151
 152						//pngUncompressedData.rewind();
 153
 154						view.lockBuffer(cast(void**)&ptr_start, ptr_len);
 155
 156						ptr = ptr_start;
 157
 158						ptr += (pngIHDR.pngWidth * ptrLine);
 159						ptr_max_line = ptr + pngIHDR.pngWidth;
 160
 161						ptr_max_page = ptr_start + (ptr_len / 4);
 162
 163						ptr += ptrPos;
 164
 165						//Console.putln("png - reading filter type!!!\n");
 166
 167						decoderState = PNG_STATE_DECODE_READ_FILTER_TYPE;
 168
 169					}
 170					else {
 171						//Console.putln("zlib");
 172
 173						return StreamData.Complete;
 174
 175					}
 176
 177					continue;
 178
 179				default:
 180
 181	//				Console.putln("png - unknown chunk\n");
 182					decoderState = PNG_STATE_SKIP_CHUNK;
 183				}
 184
 185				continue;
 186
 187				// SKIPS CHUNK AND CRC INFO
 188			case PNG_STATE_SKIP_CHUNK:
 189
 190				//////////Console.putln(("png - skipping chunk of length: ") + toString(pngChunkHeader.pngChunkLength) + ("\n"));
 191
 192				if(!(stream.skip(pngChunkHeader.pngChunkLength + 4))) {
 193					return StreamData.Required;
 194				}
 195
 196				decoderState = PNG_STATE_READ_CHUNK_HEADER;
 197
 198				continue;
 199
 200			case PNG_STATE_READ_CHUNK_CRC:
 201
 202				//////////Console.putln("png - checking CRC\n");
 203
 204				if(!(stream.read(&pngChunkCRC, 4))) {
 205					return StreamData.Required;
 206				}
 207
 208				// CHECK CRC
 209
 210				// READ ANOTHER CHUNK
 211				decoderState = PNG_STATE_READ_CHUNK_HEADER;
 212				continue;
 213
 214			case PNG_STATE_READ_IHDR:
 215				if(!(stream.read(&pngIHDR, _djehuty_image_png_ihdr.sizeof))) {
 216					return StreamData.Required;
 217				}
 218
 219				pngIHDR.pngWidth = FromBigEndian32(pngIHDR.pngWidth);
 220				pngIHDR.pngHeight = FromBigEndian32(pngIHDR.pngHeight);
 221
 222				pngPaletteCount = 0;
 223
 224				// determine whether png is valid by this header
 225
 226				switch(pngIHDR.pngColorType) {
 227					//Greyscale
 228				case 0:
 229					switch (pngIHDR.pngBitDepth) {
 230					case 1:
 231					case 2:
 232					case 4:
 233					case 8:
 234					case 16:
 235	//					Console.putln(("png - greyscale - ") ~ toString(pngIHDR.pngBitDepth) ~ (" bpp\n"));
 236						break;
 237
 238					default:
 239	//					Console.putln("png - invalid color, bit depth combination\n");
 240						break;
 241					}
 242					break;
 243
 244					//Truecolour
 245				case 2:
 246					switch (pngIHDR.pngBitDepth)
 247					{
 248					case 8:
 249					case 16:
 250	//					Console.putln(("png - truecolour - ") ~ toString(pngIHDR.pngBitDepth) ~ (" bpp\n"));
 251						break;
 252
 253					default:
 254	//					Console.putln("png - invalid color, bit depth combination\n");
 255						break;
 256					}
 257					break;
 258
 259					//Indexed-colour
 260				case 3:
 261					switch (pngIHDR.pngBitDepth) {
 262					case 1:
 263					case 2:
 264					case 4:
 265					case 8:
 266	//					Console.putln(("png - Indexed-colour - ") ~ toString(pngIHDR.pngBitDepth) ~ (" bpp\n"));
 267						break;
 268
 269					default:
 270	//					Console.putln("png - invalid color, bit depth combination\n");
 271						break;
 272					}
 273					break;
 274
 275					//Greyscale with alpha
 276				case 4:
 277					switch (pngIHDR.pngBitDepth) {
 278					case 8:
 279					case 16:
 280	//					Console.putln(("png - greyscale with alpha - ") ~ toString(pngIHDR.pngBitDepth) ~ (" bpp\n"));
 281						break;
 282
 283					default:
 284	//					Console.putln("png - invalid color, bit depth combination\n");
 285						break;
 286					}
 287					break;
 288
 289					//Truecolour with alpha
 290				case 6:
 291					switch (pngIHDR.pngBitDepth) {
 292					case 8:
 293					case 16:
 294	//					Console.putln(("png - truecolour with alpha - ") ~ toString(pngIHDR.pngBitDepth) ~ (" bpp\n"));
 295						break;
 296
 297					default:
 298	//					Console.putln("png - invalid color, bit depth combination\n");
 299						break;
 300					}
 301					break;
 302
 303				default:
 304	//				Console.putln("png - invalid color type\n");
 305					return StreamData.Invalid;
 306
 307				}
 308
 309				if (pngIHDR.pngFilterMethod != 0) {
 310					//////Console.putln("png - unsupported filter method\n");
 311					return StreamData.Invalid;
 312				}
 313
 314				if (pngIHDR.pngCompressionMethod != 0) {
 315					//////Console.putln("png - unsupported compression method\n");
 316					return StreamData.Invalid;
 317				}
 318
 319				if (pngIHDR.pngInterlaceMethod) {
 320					////Console.putln("png - Adam7 interlacing\n");
 321
 322					// SET UP INTERLACE PASS DIMENSIONS
 323
 324					// THAT IS, HOW MUCH DATA WILL BE IN EACH PASS, HOW MUCH
 325					// WILL BE IN EACH SCANLINE FOR EACH PASS
 326
 327					pngInterlacePass = 0;
 328					pngInterlaceCurLine = 0;
 329
 330					// EQUATION FOR INTERLACE WIDTH: (width, height refer to dimensions of final image)
 331
 332					// 1st pass: ceiling(width / 8)
 333					// 2nd pass: ceiling((width - 4) / 8)
 334					// 3rd pass: ceiling(width / 4)
 335					// 4th pass: ceiling((width - 2) / 4)
 336					// 5th pass: ceiling(width / 2)
 337					// 6th pass: ceiling((width - 1) / 2)
 338					// 7th pass: width
 339
 340					// EQUATION FOR INTERLACE HEIGHT:
 341
 342					// 1st, 2nd pass: ceiling(height / 8)
 343					// 3rd pass: ceiling((height - 4) / 8)
 344					// 4th pass: ceiling(height / 4)
 345					// 5th pass: ceiling((height - 2) / 4)
 346					// 6th pass: ceiling(height / 2)
 347					// 7th pass: ceiling((height - 1) / 2)
 348
 349					pngInterlaceWidths[0] = cast(uint)(cast(float)pngIHDR.pngWidth / 8);
 350					if (pngIHDR.pngWidth % 8) {
 351						pngInterlaceWidths[0]++;
 352					}
 353
 354					if (pngIHDR.pngWidth <= 4) {
 355						pngInterlaceWidths[1] = 0;
 356					}
 357					else {
 358						pngInterlaceWidths[1] = cast(uint)((cast(float)pngIHDR.pngWidth - 4) / 8);
 359						if ((pngIHDR.pngWidth - 4) % 8) {
 360							pngInterlaceWidths[1]++;
 361						}
 362					}
 363
 364					pngInterlaceWidths[2] = cast(uint)(cast(float)pngIHDR.pngWidth / 4);
 365					if (pngIHDR.pngWidth % 4) {
 366						pngInterlaceWidths[2]++;
 367					}
 368
 369					if (pngIHDR.pngWidth <= 2) {
 370						pngInterlaceWidths[3] = 0;
 371					}
 372					else {
 373						pngInterlaceWidths[3] = cast(uint)((cast(float)pngIHDR.pngWidth - 2) / 4);
 374						if ((pngIHDR.pngWidth - 2) % 4) {
 375							pngInterlaceWidths[3]++;
 376						}
 377					}
 378
 379					pngInterlaceWidths[4] = cast(uint)(cast(float)pngIHDR.pngWidth / 2);
 380					if (pngIHDR.pngWidth % 2) {
 381						pngInterlaceWidths[4]++;
 382					}
 383
 384					if (pngIHDR.pngWidth <= 1) {
 385						pngInterlaceWidths[5] = 0;
 386					}
 387					else {
 388						pngInterlaceWidths[5] = cast(uint)((cast(float)pngIHDR.pngWidth - 1) / 2);
 389						if ((pngIHDR.pngWidth - 1) % 2) {
 390							pngInterlaceWidths[5]++;
 391						}
 392					}
 393
 394					pngInterlaceWidths[6] = pngIHDR.pngWidth;
 395
 396					pngInterlaceHeights[0] = cast(uint)(cast(float)pngIHDR.pngHeight / 8);
 397					if (pngIHDR.pngHeight % 8) {
 398						pngInterlaceHeights[0]++;
 399					}
 400					pngInterlaceHeights[1] = pngInterlaceHeights[0];
 401
 402					if (pngIHDR.pngHeight <= 4) {
 403						pngInterlaceWidths[2] = 0;
 404						pngInterlaceHeights[2] = 0;
 405					}
 406					else {
 407						pngInterlaceHeights[2] = cast(uint)((cast(float)pngIHDR.pngHeight - 4) / 8);
 408						if ((pngIHDR.pngHeight - 4) % 8) {
 409							pngInterlaceHeights[2]++;
 410						}
 411					}
 412
 413					pngInterlaceHeights[3] = cast(uint)(cast(float)pngIHDR.pngHeight / 4);
 414					if (pngIHDR.pngHeight % 4) {
 415						pngInterlaceHeights[3]++;
 416					}
 417
 418					if (pngIHDR.pngHeight <= 2) {
 419						pngInterlaceWidths[4] = 0;
 420						pngInterlaceHeights[4] = 0;
 421					}
 422					else {
 423						pngInterlaceHeights[4] = cast(uint)((cast(float)pngIHDR.pngHeight - 2) / 4);
 424						if ((pngIHDR.pngHeight - 2) % 4) {
 425							pngInterlaceHeights[4]++;
 426						}
 427					}
 428
 429					pngInterlaceHeights[5] = cast(uint)(cast(float)pngIHDR.pngHeight / 2);
 430					if (pngIHDR.pngHeight % 2) {
 431						pngInterlaceHeights[5]++;
 432					}
 433
 434					if (pngIHDR.pngHeight <= 1) {
 435						pngInterlaceWidths[6] = 0;
 436						pngInterlaceHeights[6] = 0;
 437					}
 438					else {
 439						pngInterlaceHeights[6] = cast(uint)((cast(float)pngIHDR.pngHeight - 1) / 2);
 440						if ((pngIHDR.pngHeight - 1) % 2) {
 441							pngInterlaceHeights[6]++;
 442						}
 443					}
 444
 445					for (p=0; p < 7; p++) {
 446	//					Console.putln(toString(p) + (": ") + toString(pngInterlaceWidths[p]) + (" x ") + toString(pngInterlaceHeights[p]) + ("\n"));
 447					}
 448
 449				}
 450				else {
 451	//				Console.putln("png - no interlacing\n");
 452				}
 453
 454				// calculate quick reference 'image type' //
 455				pngImageType = (((pngIHDR.pngColorType + 1) << 16) + pngIHDR.pngBitDepth);
 456
 457				// set the image renderer state in the decoder process //
 458				if (pngIHDR.pngInterlaceMethod) {
 459					pngRenderState = PNG_STATE_RENDER_INTERLACED_STATE_BASE + pngImageType;
 460				}
 461				else {
 462					pngRenderState = PNG_STATE_RENDER_STATE_BASE + pngImageType;
 463
 464	//			Console.putln("eh!", pngRenderState, PNG_STATE_RENDER_STATE_BASE + PNG_TRUECOLOUR_ALPHA_8BPP);
 465				}
 466
 467				//Console.putln(("png - ") + toString(pngIHDR.pngWidth) + (" x ") + toString(pngIHDR.pngHeight) + ("\n"));
 468
 469//	printf("type: %d\n", pngIHDR.pngWidth);
 470				view.create(pngIHDR.pngWidth, pngIHDR.pngHeight);
 471
 472				// CALCULATE THE NUMBER OF BYTES WE WILL BE READING
 473				switch(pngIHDR.pngColorType) {
 474				case 3: // INDEXED_COLOUR (also 1 sample per pixel, samples are indices)
 475				case 0: // GREYSCALE (1 sample per pixel)
 476					switch(pngIHDR.pngBitDepth) {
 477						case 1:
 478							pngExpectedBytes = 1 + cast(uint)((cast(float)pngIHDR.pngWidth / 8) + 0.5);
 479							pngNumSamples = 1;
 480							break;
 481						case 2:
 482							pngExpectedBytes = 1 + cast(uint)((cast(float)pngIHDR.pngWidth / 4) + 0.5);
 483							pngNumSamples = 1;
 484							break;
 485						case 4:
 486							pngExpectedBytes = 1 + cast(uint)((cast(float)pngIHDR.pngWidth / 2) + 0.5);
 487							pngNumSamples = 1;
 488							break;
 489						case 8:
 490							pngExpectedBytes = pngIHDR.pngWidth;
 491							pngNumSamples = 1;
 492							break;
 493						case 16:
 494							pngExpectedBytes = pngIHDR.pngWidth * 2;
 495							pngNumSamples = 2;
 496							break;
 497						default: break;
 498					}
 499
 500					break;
 501
 502				case 2: // TRUE_COLOUR
 503
 504					switch(pngIHDR.pngBitDepth) {
 505						case 8:
 506							pngExpectedBytes = pngIHDR.pngWidth * 3;
 507							pngNumSamples = 3;
 508							break;
 509						case 16:
 510							pngExpectedBytes = pngIHDR.pngWidth * (3 * 2);
 511							pngNumSamples = 6;
 512							break;
 513						default: break;
 514					}
 515					break;
 516				case 4: // GREYSCALE_ALPHA
 517					switch(pngIHDR.pngBitDepth) {
 518						case 8:
 519							pngExpectedBytes = pngIHDR.pngWidth * 2;
 520							pngNumSamples = 2;
 521							break;
 522						case 16:
 523							pngExpectedBytes = pngIHDR.pngWidth * (2 * 2);
 524							pngNumSamples = 4;
 525							break;
 526						default: break;
 527					}
 528					break;
 529				case 6: // TRUE_COLOUR_ALPHA
 530
 531					switch(pngIHDR.pngBitDepth) {
 532						case 8:
 533							pngExpectedBytes = pngIHDR.pngWidth * 4;
 534							pngNumSamples = 4;
 535							break;
 536						case 16:
 537							pngExpectedBytes = pngIHDR.pngWidth * (4 * 2);
 538							pngNumSamples = 8;
 539							break;
 540						default: break;
 541					}
 542					break;
 543
 544
 545					default: break;
 546				}
 547
 548				// INIT DECODER DATA
 549
 550				for ( pngCounter = 0; pngCounter < 8; pngCounter++) {
 551					////Console.putln(("png - ") + toString(pngIHDR.pngWidth) + (" x ") + toString(pngIHDR.pngHeight) + ("\n"));
 552					pngBytes[pngCounter] = new ubyte[pngExpectedBytes];
 553					pngBytes[pngCounter][0..pngExpectedBytes] = 0;
 554				}
 555
 556				nsamp = 0;
 557				psamp = 0;
 558
 559				pngCounter = -1;
 560
 561				decoderState = PNG_STATE_READ_CHUNK_CRC;
 562
 563				continue;
 564
 565			case PNG_STATE_READ_PLTE:
 566
 567				// GET NUMBER OF PALETTE ENTRIES
 568
 569				// LOOK AT CHUNK DATA LENGTH, DIVIDE BY THREE
 570				// IF THERE IS A REMAINDER, THIS PNG IS INVALID
 571
 572				pngPaletteCount = (pngChunkHeader.pngChunkLength % 3);
 573
 574				if (pngPaletteCount) {
 575					//////////Console.putln("png - PLTE - invalid palette chunk\n");
 576				}
 577
 578				pngPaletteCount = pngChunkHeader.pngChunkLength / 3;
 579
 580				if (pngPaletteCount > 256) {
 581					//////////Console.putln("png - PLTE - too many entries in palette\n");
 582					return StreamData.Invalid;
 583				}
 584
 585				if (pngPaletteCount == 0) {
 586					//////////Console.putln("png - PLTE - empty palette, proceeding anyway\n");
 587					decoderState = PNG_STATE_READ_CHUNK_CRC;
 588					continue;
 589				}
 590
 591			case PNG_STATE_READ_PLTE_ENTRIES:
 592
 593				if(!(stream.read(&pngPalette, pngPaletteCount*3))) {
 594					return StreamData.Required;
 595				}
 596
 597				// build pngPaletteRealized //
 598				for (palIndex = 0; palIndex < pngPaletteCount; palIndex++) {
 599					pngPaletteRealized[palIndex] = (0xFF000000) | (pngPalette[palIndex].blue) | (pngPalette[palIndex].green << 8) | (pngPalette[palIndex].red << 16);
 600				}
 601
 602				for ( ; palIndex < 256; palIndex++) {
 603					pngPaletteRealized[palIndex] = 0;
 604				}
 605
 606				decoderState = PNG_STATE_READ_CHUNK_CRC;
 607				continue;
 608
 609			case PNG_STATE_READ_IDAT:
 610
 611				// GET THE CONTENTS OF THE CHUNK DATA
 612
 613				//////////Console.putln(toString(pngChunkHeader.pngChunkLength) + ("chunk header\n"));
 614
 615				//streamToDecode.clear();
 616
 617				decoderState = PNG_STATE_FILL_IDAT;
 618
 619			case PNG_STATE_FILL_IDAT:
 620	//				Console.putln("fill idat");
 621
 622	//			if(!(stream.read(streamToDecode, pngChunkHeader.pngChunkLength)))
 623				if(!(streamToDecode.append(stream, pngChunkHeader.pngChunkLength))) {
 624					return StreamData.Required;
 625				}
 626	//				Console.putln("fill idat");
 627
 628				ubyte b;
 629
 630
 631				//Console.putln("pos: ", streamToDecode.getPosition());
 632
 633				//////////Console.putln(toString(streamToDecode.getLength()) + ("\n"));
 634
 635				//streamToDecode.rewind();
 636
 637				//////////Console.putln(toString(byte) + ("oo\n"));
 638
 639				//if (stream.PushRestriction(stream.getPosition(), pngChunkHeader.pngChunkLength))
 640				//{
 641				//////////Console.putln(toString(stream.getLength()) + ("!!!!\n"));
 642				//}
 643				//////////Console.putln(toString(stream.getLength()) + ("oo\n"));
 644
 645				/*
 646				if (zlibCodec.decode(streamToDecode, pngUncompressedData, pngCompressionProgress) == StreamData.Complete)
 647				{
 648
 649					//while (pngUncompressedData.getRemaining())
 650					//{
 651					//	pngUncompressedData.Readubyte(curByte);
 652						////////////Console.putln((":") + toString(curByte) + ("\n"));
 653					//}
 654
 655					//pngUncompressedData.rewind();
 656
 657					view.lockBuffer((void**)&ptr_start, ptr_len);
 658
 659					ptr = ptr_start;
 660
 661					ptr += (pngIHDR.pngWidth * ptrLine);
 662					ptr_max_line = ptr + pngIHDR.pngWidth;
 663
 664					ptr_max_page = ptr_start + (ptr_len / 4);
 665
 666					ptr += ptrPos;
 667
 668					//if (decoderNextState == PNG_STATE_DECODE_READ_FILTER_TYPE)
 669					//{
 670						//////////Console.putln("png - reading filter type\n");
 671					//}
 672					//else
 673					//{
 674						//////////Console.putln("png - returning to scanline render\n");
 675					//}
 676
 677					decoderState = decoderNextState;
 678				}
 679				else
 680				{
 681					decoderState = PNG_STATE_READ_CHUNK_CRC;
 682				}//*/ decoderState = PNG_STATE_READ_CHUNK_CRC;
 683
 684
 685				//stream.PopRestriction();
 686
 687				//////////Console.putln(toString(stream.getLength()) + ("xx\n"));
 688
 689				//////////Console.putln(toString(pngUncompressedData.getRemaining()) + ("\n") + toString(pngChunkHeader.pngChunkLength) + ("\n"));
 690
 691				//stream.skip(pngChunkHeader.pngChunkLength);
 692
 693				continue;
 694
 695			case PNG_STATE_DECODE_READ_FILTER_TYPE:
 696
 697				//Console.putln("read filter type");
 698
 699	//			pngUncompressedData.rewind();
 700
 701				if (!(pngUncompressedData.read(&pngFilterType, 1))) {
 702					// need more compress data from IDAT blocks
 703					//////////Console.putln("IDAT empty\n");
 704					decoderState = PNG_STATE_READ_CHUNK_HEADER;
 705					continue;
 706				}
 707
 708				//Console.putln("done filter type");
 709
 710				switch (pngFilterType) {
 711				case 0:
 712	//				Console.putln("\npng - filter type - none\n");
 713					decoderState = PNG_STATE_UNFILTER_NONE;
 714					decoderSubState = PNG_STATE_UNFILTER_NONE;
 715					break;
 716				case 1:
 717	//				Console.putln("\npng - filter type - Sub\n");
 718					decoderState = PNG_STATE_UNFILTER_SUB;
 719					decoderSubState = PNG_STATE_UNFILTER_SUB;
 720					break;
 721				case 2:
 722	//				Console.putln("\npng - filter type - Up\n");
 723					decoderState = PNG_STATE_UNFILTER_UP;
 724					decoderSubState = PNG_STATE_UNFILTER_UP;
 725					break;
 726				case 3:
 727	//				Console.putln("\npng - filter type - Average\n");
 728					decoderState = PNG_STATE_UNFILTER_AVERAGE;
 729					decoderSubState = PNG_STATE_UNFILTER_AVERAGE;
 730					break;
 731				case 4:
 732	//				Console.putln("\npng - filter type - Paeth\n");
 733					decoderState = PNG_STATE_UNFILTER_PAETH;
 734					decoderSubState = PNG_STATE_UNFILTER_PAETH;
 735					break;
 736				default:
 737	//				Console.putln(("\npng - invalid filter type") ~ toString(pngFilterType) ~ ("\n"));
 738					view.unlockBuffer();
 739					return StreamData.Invalid;
 740				}
 741
 742				// set the filter state in the decoder process, should we be interrupted
 743				pngFilterState = decoderState;
 744
 745				for (p = 0; p < pngNumSamples; p++) {
 746					pngPriorPixel[p] = 0;
 747					pngPriorScannedByte[p] = 0;
 748				}
 749
 750				nsamp = 0;
 751				psamp = 0;
 752
 753				pngCounter = -1;
 754
 755				continue;
 756
 757
 758
 759
 760
 761
 762
 763
 764			// FILTER STATES //
 765
 766
 767
 768			case PNG_STATE_UNFILTER_NONE:
 769
 770				// check for decoder termination for the current scanline //
 771
 772				if (ptrPos >= pngIHDR.pngWidth) {
 773					// WE ARE DONE
 774
 775					if (pngIHDR.pngInterlaceMethod) {
 776						pngInterlaceCurLine++;
 777
 778						if (pngInterlaceCurLine == pngInterlaceHeights[pngInterlacePass]) {
 779							pngInterlaceCurLine = 0;
 780
 781							// we are entering a new interlace pass
 782							//////Console.putln("png - interlaced - entering new interlace pass\n");
 783
 784							// reset the prior scanline array
 785							for ( pngCounter = 0; pngCounter < 8; pngCounter++) {
 786								pngBytes[pngCounter][0..pngExpectedBytes] = 0;
 787							}
 788
 789							do {
 790								pngInterlacePass++;
 791							} while ((pngInterlacePass < 7) && (pngInterlaceWidths[pngInterlacePass] == 0));
 792
 793
 794							if (pngInterlacePass >= 7) {
 795								// We are done decoding
 796
 797								view.unlockBuffer();
 798
 799								return StreamData.Complete;
 800							}
 801
 802							ptrLine = pngInterlaceStartsY[pngInterlacePass];
 803						}
 804						else {
 805							ptrLine+=pngInterlaceIncrementsY[pngInterlacePass];
 806						}
 807
 808						ptrPos = pngInterlaceStartsX[pngInterlacePass];
 809
 810						ptr = ptr_start + ((pngIHDR.pngWidth * ptrLine) + ptrPos);
 811					}
 812					else {
 813						ptrPos = 0;
 814						ptrLine++;
 815					}
 816
 817					pngCounter = -1;
 818
 819					decoderState = PNG_STATE_DECODE_READ_FILTER_TYPE;
 820
 821					if (ptrLine >= pngIHDR.pngHeight) {
 822	//					Console.putln("done?\n");
 823
 824						view.unlockBuffer();
 825
 826	//					Console.putln("done!\n");
 827
 828						return StreamData.Complete;
 829
 830					}
 831
 832					continue;
 833				}
 834
 835				// READ IN DECODED BYTE
 836
 837				if (!(pngUncompressedData.read(&curByte, 1))) {
 838					decoderNextState = decoderState;
 839					//////////Console.putln("png - requiring more data in IDAT\n");
 840					view.unlockBuffer();
 841					decoderState = PNG_STATE_READ_CHUNK_CRC;
 842					continue;
 843				}
 844
 845				// UNFILTER
 846
 847				pngCounter++;
 848
 849				pngCurComponent[psamp] = curByte;
 850				pngBytes[psamp][nsamp] = curByte;
 851
 852			//	Console.put(pngCurComponent[psamp], " ");
 853
 854
 855				psamp++;
 856				if (psamp == pngNumSamples) {
 857					nsamp++;
 858					psamp = 0;
 859	//				Console.putln("renderstate");
 860					decoderState = pngRenderState;
 861				}
 862
 863				// go to the next state
 864				continue;
 865
 866
 867
 868
 869				// SUB FILTER //
 870
 871			case PNG_STATE_UNFILTER_SUB:
 872
 873				// check for decoder termination for the current scanline //
 874
 875				if (ptrPos >= pngIHDR.pngWidth) {
 876					// WE ARE DONE
 877
 878					if (pngIHDR.pngInterlaceMethod) {
 879						pngInterlaceCurLine++;
 880
 881						if (pngInterlaceCurLine == pngInterlaceHeights[pngInterlacePass]) {
 882							pngInterlaceCurLine = 0;
 883
 884							// we are entering a new interlace pass
 885							//////Console.putln("png - interlaced - entering new interlace pass\n");
 886
 887							// reset the prior scanline array
 888							for ( pngCounter = 0; pngCounter < 8; pngCounter++) {
 889								pngBytes[pngCounter][0..pngExpectedBytes] = 0;
 890							}
 891
 892							pngInterlacePass++;
 893
 894							if (pngInterlacePass == 7) {
 895								// We are done decoding
 896
 897								view.unlockBuffer();
 898
 899								return StreamData.Complete;
 900							}
 901
 902							ptrLine = pngInterlaceStartsY[pngInterlacePass];
 903						}
 904						else {
 905							ptrLine+=pngInterlaceIncrementsY[pngInterlacePass];
 906						}
 907
 908						ptrPos = pngInterlaceStartsX[pngInterlacePass];
 909
 910						ptr = ptr_start + ((pngIHDR.pngWidth * ptrLine) + ptrPos);
 911					}
 912					else {
 913						ptrPos = 0;
 914						ptrLine++;
 915					}
 916
 917					pngCounter = -1;
 918
 919					decoderState = PNG_STATE_DECODE_READ_FILTER_TYPE;
 920
 921					if (ptrLine >= pngIHDR.pngHeight) {
 922						view.unlockBuffer();
 923
 924						return StreamData.Complete;
 925
 926					}
 927
 928					continue;
 929				}
 930
 931				// decode a scanline using SUB filter
 932
 933				if (!(pngUncompressedData.read(&curByte, 1))) {
 934					decoderNextState = decoderState;
 935					//////////Console.putln("png - requiring more data in IDAT\n");
 936					view.unlockBuffer();
 937					decoderState = PNG_STATE_READ_CHUNK_CRC;
 938					continue;
 939				}
 940
 941				pngCounter++;
 942
 943				pngPriorPixel[psamp] += curByte;
 944				pngCurComponent[psamp] = pngPriorPixel[psamp];
 945				pngBytes[psamp][nsamp] = cast(ubyte)pngCurComponent[psamp];
 946
 947			//	Console.put(pngCurComponent[psamp], " ");
 948
 949				psamp++;
 950				if (psamp == pngNumSamples) {
 951					nsamp++;
 952					psamp = 0;
 953	//				Console.putln("renderstate");
 954					decoderState = pngRenderState;
 955				}
 956
 957
 958				// go to the next state
 959				continue;
 960
 961			case PNG_STATE_UNFILTER_UP:
 962
 963				// check for decoder termination for the current scanline //
 964
 965				if (ptrPos >= pngIHDR.pngWidth) {
 966					// WE ARE DONE
 967
 968					if (pngIHDR.pngInterlaceMethod) {
 969						pngInterlaceCurLine++;
 970
 971						if (pngInterlaceCurLine == pngInterlaceHeights[pngInterlacePass]) {
 972							pngInterlaceCurLine = 0;
 973
 974							// we are entering a new interlace pass
 975							//////Console.putln("png - interlaced - entering new interlace pass\n");
 976
 977							// reset the prior scanline array
 978							for ( pngCounter = 0; pngCounter < 8; pngCounter++) {
 979								pngBytes[pngCounter][0..pngExpectedBytes] = 0;
 980							}
 981
 982							pngInterlacePass++;
 983
 984							if (pngInterlacePass == 7) {
 985								// We are done decoding
 986
 987								view.unlockBuffer();
 988
 989								return StreamData.Complete;
 990							}
 991
 992							ptrLine = pngInterlaceStartsY[pngInterlacePass];
 993						}
 994						else {
 995							ptrLine+=pngInterlaceIncrementsY[pngInterlacePass];
 996						}
 997
 998						ptrPos = pngInterlaceStartsX[pngInterlacePass];
 999
1000						ptr = ptr_start + ((pngIHDR.pngWidth * ptrLine) + ptrPos);
1001					}
1002					else {
1003						ptrPos = 0;
1004						ptrLine++;
1005					}
1006
1007					pngCounter = -1;
1008
1009					decoderState = PNG_STATE_DECODE_READ_FILTER_TYPE;
1010
1011					if (ptrLine >= pngIHDR.pngHeight) {
1012
1013						view.unlockBuffer();
1014
1015						return StreamData.Complete;
1016
1017					}
1018
1019					continue;
1020				}
1021
1022				// decode a scanline using UP filter
1023
1024				if (!(pngUncompressedData.read(&curByte, 1))) {
1025					decoderNextState = decoderState;
1026					//////////Console.putln("png - requiring more data in IDAT\n");
1027					view.unlockBuffer();
1028					decoderState = PNG_STATE_READ_CHUNK_CRC;
1029					continue;
1030				}
1031
1032				pngCounter++;
1033
1034
1035
1036			//	Console.put(pngBytes[psamp][nsamp], "+", curByte, "=");
1037
1038				pngBytes[psamp][nsamp] += curByte;
1039
1040			//	Console.put(pngBytes[psamp][nsamp], " ");
1041
1042				pngCurComponent[psamp] = pngBytes[psamp][nsamp];
1043
1044				psamp++;
1045				if (psamp == pngNumSamples) {
1046					nsamp++;
1047					psamp = 0;
1048	//				Console.putln("renderstate");
1049					decoderState = pngRenderState;
1050				}
1051
1052				// go to the next state
1053				continue;
1054
1055			case PNG_STATE_UNFILTER_AVERAGE:
1056				// check for decoder termination for the current scanline //
1057
1058				if (ptrPos >= pngIHDR.pngWidth) {
1059					// WE ARE DONE
1060
1061					if (pngIHDR.pngInterlaceMethod) {
1062
1063						pngInterlaceCurLine++;
1064
1065						if (pngInterlaceCurLine == pngInterlaceHeights[pngInterlacePass]) {
1066							pngInterlaceCurLine = 0;
1067
1068							// we are entering a new interlace pass
1069							//////Console.putln("png - interlaced - entering new interlace pass\n");
1070
1071							// reset the prior scanline array
1072							for ( pngCounter = 0; pngCounter < 8; pngCounter++) {
1073								pngBytes[pngCounter][0..pngExpectedBytes] = 0;
1074							}
1075
1076							pngInterlacePass++;
1077
1078							if (pngInterlacePass == 7) {
1079								// We are done decoding
1080
1081								view.unlockBuffer();
1082
1083								return StreamData.Complete;
1084							}
1085
1086							ptrLine = pngInterlaceStartsY[pngInterlacePass];
1087						}
1088						else {
1089							ptrLine+=pngInterlaceIncrementsY[pngInterlacePass];
1090						}
1091
1092						ptrPos = pngInterlaceStartsX[pngInterlacePass];
1093
1094						ptr = ptr_start + ((pngIHDR.pngWidth * ptrLine) + ptrPos);
1095					}
1096					else {
1097						ptrPos = 0;
1098						ptrLine++;
1099					}
1100
1101					pngCounter = -1;
1102
1103					decoderState = PNG_STATE_DECODE_READ_FILTER_TYPE;
1104
1105					if (ptrLine >= pngIHDR.pngHeight) {
1106
1107						view.unlockBuffer();
1108
1109						return StreamData.Complete;
1110
1111					}
1112
1113					continue;
1114				}
1115
1116				// decode a scanline using AVERAGE filter
1117
1118				if (!(pngUncompressedData.read(&curByte, 1))) {
1119					decoderNextState = decoderState;
1120					//////////Console.putln("png - requiring more data in IDAT\n");
1121					view.unlockBuffer();
1122					decoderState = PNG_STATE_READ_CHUNK_CRC;
1123					continue;
1124				}
1125
1126				pngCounter++;
1127
1128				pngCurComponent[psamp] = ((cast(uint)pngPriorPixel[psamp] + cast(uint)pngBytes[psamp][nsamp]) / 2);
1129				pngCurComponent[psamp] += curByte;
1130				pngBytes[psamp][nsamp] = cast(ubyte)pngCurComponent[psamp];
1131				pngCurComponent[psamp] = pngBytes[psamp][nsamp];
1132
1133				pngPriorPixel[psamp] = cast(ubyte)pngCurComponent[psamp];
1134
1135			//	Console.put(pngCurComponent[psamp], " ");
1136
1137				psamp++;
1138				if (psamp == pngNumSamples) {
1139					nsamp++;
1140					psamp = 0;
1141	//				Console.putln("renderstate");
1142					decoderState = pngRenderState;
1143				}
1144
1145				// go to the next state
1146				continue;
1147
1148			case PNG_STATE_UNFILTER_PAETH:
1149
1150				// UNFILTER A SCANLINE
1151
1152				// READ IN DECODED BYTE
1153
1154				if (ptrPos >= pngIHDR.pngWidth) {
1155					// WE ARE DONE
1156
1157					if (pngIHDR.pngInterlaceMethod) {
1158
1159						pngInterlaceCurLine++;
1160
1161						if (pngInterlaceCurLine == pngInterlaceHeights[pngInterlacePass]) {
1162							// we are entering a new interlace pass
1163							pngInterlaceCurLine = 0;
1164
1165							// reset the prior scanline array
1166							for ( pngCounter = 0; pngCounter < 8; pngCounter++) {
1167								pngBytes[pngCounter][0..pngExpectedBytes] = 0;
1168							}
1169
1170							pngInterlacePass++;
1171
1172							if (pngInterlacePass == 7) {
1173								// We are done decoding
1174
1175								view.unlockBuffer();
1176
1177								return StreamData.Complete;
1178							}
1179
1180							ptrLine = pngInterlaceStartsY[pngInterlacePass];
1181						}
1182						else {
1183							ptrLine+=pngInterlaceIncrementsY[pngInterlacePass];
1184						}
1185
1186						ptrPos = pngInterlaceStartsX[pngInterlacePass];
1187
1188						ptr = ptr_start + ((pngIHDR.pngWidth * ptrLine) + ptrPos);
1189					}
1190					else {
1191						ptrPos = 0;
1192						ptrLine++;
1193					}
1194
1195					pngCounter = -1;
1196
1197					decoderState = PNG_STATE_DECODE_READ_FILTER_TYPE;
1198
1199					if (ptrLine >= pngIHDR.pngHeight) {
1200
1201						view.unlockBuffer();
1202
1203						return StreamData.Complete;
1204
1205					}
1206
1207					continue;
1208				}
1209
1210				if (!(pngUncompressedData.read(&curByte, 1))) {
1211					decoderNextState = decoderState;
1212					//////////Console.putln("png - requiring more data in IDAT\n");
1213					view.unlockBuffer();
1214					decoderState = PNG_STATE_READ_CHUNK_CRC;
1215					continue;
1216				}
1217
1218				// UNFILTER
1219				pngCounter++;
1220
1221				pngPriorScannedComp = pngPriorScannedByte[psamp];
1222				pngPriorScannedByte[psamp] = pngBytes[psamp][nsamp];
1223
1224				p = cast(int)pngPriorPixel[psamp] + cast(int)pngPriorScannedByte[psamp] - cast(int)pngPriorScannedComp;
1225
1226				if (p > cast(int)pngPriorPixel[psamp]) {
1227					pa = p - cast(int)pngPriorPixel[psamp];
1228				}
1229				else {
1230					pa = cast(int)pngPriorPixel[psamp] - p;
1231				}
1232
1233				if (p > cast(int)pngPriorScannedByte[psamp]) {
1234					pb = p - cast(int)pngPriorScannedByte[psamp];
1235				}
1236				else {
1237					pb = cast(int)pngPriorScannedByte[psamp] - p;
1238				}
1239
1240				if (p > cast(int)pngPriorScannedComp) {
1241					pc = p - cast(int)pngPriorScannedComp;
1242				}
1243				else {
1244					pc = cast(int)pngPriorScannedComp - p;
1245				}
1246
1247				if (pa <= pb && pa <= pc) {
1248					pngPaethPredictor = pngPriorPixel[psamp];
1249				}
1250				else if (pb <= pc) {
1251					pngPaethPredictor = pngPriorScannedByte[psamp];
1252				}
1253				else {
1254					pngPaethPredictor = pngPriorScannedComp;
1255				}
1256
1257				recon = cast(ubyte)(curByte + pngPaethPredictor);
1258
1259				////////////Console.putln(("pr: ") + toString(pngPaethPredictor) + (" f(x): ") + toString(recon) + ("\n"));
1260
1261				pngPriorPixel[psamp] = recon;
1262				pngCurComponent[psamp] = recon;
1263
1264				pngBytes[psamp][nsamp] = cast(ubyte)pngCurComponent[psamp];
1265
1266		//		Console.put(pngCurComponent[psamp], " ");
1267
1268				////////////Console.putln(toString(curByte) + (" --> "));
1269				////////////Console.putln(toString(pngCurComponent[psamp]) + (" a:") + toString(a) + (" b:") + toString(b) + (" c:") + toString(c) + (" pa:") + toString(pa) + (" pb:") + toString(pb) + (" pc:") + toString(pc) + (" p:") + toString(pngPaethPredictor) + ("\n"));
1270
1271				////////////Console.putln(("result->") + toString(pngCurComponent[psamp]) + ("\n"));
1272
1273				psamp++;
1274				if (psamp == pngNumSamples)
1275				{
1276					nsamp++;
1277					psamp = 0;
1278	//				Console.putln("renderstate");
1279					decoderState = pngRenderState;
1280				}
1281
1282				// go to the next state
1283				continue;
1284
1285				// RENDER STATES //
1286
1287			case PNG_STATE_RENDER_STATE_BASE + PNG_GREYSCALE_1BPP:
1288
1289				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
1290
1291				decoderState = pngFilterState;
1292
1293				// WE WILL ADD 8 PIXELS, IF WE CAN
1294
1295				pngCurSample = png1BPP[pngCurComponent[0] >> 7];
1296
1297				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1298
1299				ptr++;
1300				ptrPos++;
1301
1302				if (ptrPos == pngIHDR.pngWidth) {
1303					continue;
1304				}
1305
1306				pngCurSample = png1BPP[(pngCurComponent[0] >> 6) & 1];
1307
1308				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1309
1310				ptr++;
1311				ptrPos++;
1312
1313				if (ptrPos == pngIHDR.pngWidth) {
1314					continue;
1315				}
1316
1317				pngCurSample = png1BPP[(pngCurComponent[0] >> 5) & 1];
1318
1319				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1320
1321				ptr++;
1322				ptrPos++;
1323
1324				if (ptrPos == pngIHDR.pngWidth) {
1325					continue;
1326				}
1327
1328				pngCurSample= png1BPP[(pngCurComponent[0] >> 4) & 1];
1329
1330				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1331
1332				ptr++;
1333				ptrPos++;
1334
1335				if (ptrPos == pngIHDR.pngWidth) {
1336					continue;
1337				}
1338
1339				pngCurSample = png1BPP[(pngCurComponent[0] >> 3) & 1];
1340
1341				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1342
1343				ptr++;
1344				ptrPos++;
1345
1346				if (ptrPos == pngIHDR.pngWidth) {
1347					continue;
1348				}
1349
1350				pngCurSample = png1BPP[(pngCurComponent[0] >> 2) & 1];
1351
1352				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1353
1354				ptr++;
1355				ptrPos++;
1356
1357				if (ptrPos == pngIHDR.pngWidth) {
1358					continue;
1359				}
1360
1361				pngCurSample = png1BPP[(pngCurComponent[0] >> 1) & 1];
1362
1363				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1364
1365				ptr++;
1366				ptrPos++;
1367
1368				if (ptrPos == pngIHDR.pngWidth) {
1369					continue;
1370				}
1371
1372				pngCurSample = png1BPP[pngCurComponent[0] & 1];
1373
1374				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1375
1376				ptr++;
1377				ptrPos++;
1378
1379				continue;
1380
1381			case PNG_STATE_RENDER_STATE_BASE + PNG_GREYSCALE_2BPP:
1382
1383				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
1384
1385				decoderState = pngFilterState;
1386
1387				// WE WILL ADD 4 PIXELS, IF WE CAN
1388
1389				pngCurSample = png2BPP[pngCurComponent[0] >> 6];
1390
1391				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1392
1393				ptr++;
1394				ptrPos++;
1395
1396				if (ptrPos == pngIHDR.pngWidth) {
1397					continue;
1398				}
1399
1400				pngCurSample = png2BPP[(pngCurComponent[0] >> 4) & 0x3];
1401
1402				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1403
1404				ptr++;
1405				ptrPos++;
1406
1407				if (ptrPos == pngIHDR.pngWidth) {
1408					continue;
1409				}
1410
1411				pngCurSample = png2BPP[(pngCurComponent[0] >> 2) & 0x3];
1412
1413				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1414
1415				ptr++;
1416				ptrPos++;
1417
1418				if (ptrPos == pngIHDR.pngWidth) {
1419					continue;
1420				}
1421
1422				pngCurSample = png2BPP[pngCurComponent[0] & 0x3];
1423
1424				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1425
1426				ptr++;
1427				ptrPos++;
1428
1429				continue;
1430
1431			case PNG_STATE_RENDER_STATE_BASE + PNG_GREYSCALE_4BPP:
1432
1433				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
1434
1435				decoderState = pngFilterState;
1436
1437				// WE WILL ADD 2 PIXELS, IF WE CAN
1438
1439				pngCurSample = png4BPP[pngCurComponent[0] >> 4];
1440
1441				ptr[0] = 0xFF000000 | (pngCurSample) | (pngCurSample << 8) | (pngCurSample << 16);
1442				ptr++;
1443				ptrPos++;
1444
1445				if (ptrPos == pngIHDR.pngWidth) {
1446					continue;
1447				}
1448
1449				pngCurSample = png4BPP[pngCurComponent[0] & 0xF];
1450
1451				ptr[0] = 0xFF000000 | (pngCurSample) | (pngCurSample << 8) | (pngCurSample << 16);
1452				ptr++;
1453				ptrPos++;
1454
1455				continue;
1456
1457			case PNG_STATE_RENDER_STATE_BASE + PNG_GREYSCALE_8BPP:
1458
1459				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
1460
1461				decoderState = pngFilterState;
1462
1463				// JUST ADD THE PIXEL
1464
1465				ptr[0] = view.rgbTouint(pngCurComponent[0], pngCurComponent[0], pngCurComponent[0]);
1466
1467				ptr++;
1468				ptrPos++;
1469
1470				continue;
1471
1472			case PNG_STATE_RENDER_STATE_BASE + PNG_GREYSCALE_16BPP:
1473
1474				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
1475
1476				decoderState = pngFilterState;
1477
1478				// JUST ADD THE PIXEL
1479
1480				pngCurComponent[0] = cast(ubyte)(((cast(float)((pngCurComponent[0] << 8) | pngCurComponent[1]) / cast(float)0xFFFF) * cast(float)0xFF) + 0.5);
1481
1482				ptr[0] = view.rgbTouint(pngCurComponent[0], pngCurComponent[0], pngCurComponent[0]);
1483
1484				ptr++;
1485				ptrPos++;
1486
1487				continue;
1488
1489
1490
1491			case PNG_STATE_RENDER_STATE_BASE + PNG_TRUECOLOUR_8BPP:
1492
1493				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
1494
1495				decoderState = pngFilterState;
1496
1497				// JUST ADD THE PIXEL
1498
1499				ptr[0] = view.rgbTouint(pngCurComponent[0], pngCurComponent[1], pngCurComponent[2]);
1500
1501				ptr++;
1502				ptrPos++;
1503
1504				continue;
1505
1506			case PNG_STATE_RENDER_STATE_BASE + PNG_TRUECOLOUR_16BPP:
1507
1508				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
1509
1510				decoderState = pngFilterState;
1511
1512
1513				pngCurComponent[0] = cast(ubyte)(((cast(float)((pngCurComponent[0] << 8) | pngCurComponent[1]) / cast(float)0xFFFF) * cast(float)0xFF) + 0.5);
1514				pngCurComponent[1] = cast(ubyte)(((cast(float)((pngCurComponent[2] << 8) | pngCurComponent[3]) / cast(float)0xFFFF) * cast(float)0xFF) + 0.5);
1515				pngCurComponent[2] = cast(ubyte)(((cast(float)((pngCurComponent[4] << 8) | pngCurComponent[5]) / cast(float)0xFFFF) * cast(float)0xFF) + 0.5);
1516
1517
1518				ptr[0] = view.rgbTouint(pngCurComponent[0], pngCurComponent[1], pngCurComponent[2]);
1519
1520				ptr++;
1521				ptrPos++;
1522
1523				continue;
1524
1525			case PNG_STATE_RENDER_STATE_BASE + PNG_INDEXED_COLOUR_1BPP:
1526
1527				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
1528
1529	//			Console.putln("index?\n");
1530
1531				decoderState = pngFilterState;
1532
1533				// WE WILL ADD 8 PIXELS, IF WE CAN
1534
1535				pngCurSample = pngCurComponent[0] >> 7;
1536
1537				if (pngCurSample >= pngPaletteCount) {
1538					pngCurSample = 0;
1539				}
1540
1541				ptr[0] = pngPaletteRealized[pngCurSample];
1542				ptr++;
1543				ptrPos++;
1544
1545				if (ptrPos == pngIHDR.pngWidth) {
1546					continue;
1547				}
1548
1549				pngCurSample = (pngCurComponent[0] >> 6) & 1;
1550
1551				if (pngCurSample >= pngPaletteCount) {
1552					pngCurSample = 0;
1553				}
1554
1555				ptr[0] = pngPaletteRealized[pngCurSample];
1556				ptr++;
1557				ptrPos++;
1558
1559				if (ptrPos == pngIHDR.pngWidth) {
1560					continue;
1561				}
1562
1563				pngCurSample = (pngCurComponent[0] >> 5) & 1;
1564
1565				if (pngCurSample >= pngPaletteCount) {
1566					pngCurSample = 0;
1567				}
1568
1569				ptr[0] = pngPaletteRealized[pngCurSample];
1570				ptr++;
1571				ptrPos++;
1572
1573				if (ptrPos == pngIHDR.pngWidth) {
1574					continue;
1575				}
1576
1577				pngCurSample = (pngCurComponent[0] >> 4) & 1;
1578
1579				if (pngCurSample >= pngPaletteCount) {
1580					pngCurSample = 0;
1581				}
1582
1583				ptr[0] = pngPaletteRealized[pngCurSample];
1584				ptr++;
1585				ptrPos++;
1586
1587				if (ptrPos == pngIHDR.pngWidth) {
1588					continue;
1589				}
1590
1591				pngCurSample = (pngCurComponent[0] >> 3) & 1;
1592
1593				if (pngCurSample >= pngPaletteCount) {
1594					pngCurSample = 0;
1595				}
1596
1597				ptr[0] = pngPaletteRealized[pngCurSample];
1598				ptr++;
1599				ptrPos++;
1600
1601				if (ptrPos == pngIHDR.pngWidth) {
1602					continue;
1603				}
1604
1605				pngCurSample = (pngCurComponent[0] >> 2) & 1;
1606
1607				if (pngCurSample >= pngPaletteCount) {
1608					pngCurSample = 0;
1609				}
1610
1611				ptr[0] = pngPaletteRealized[pngCurSample];
1612				ptr++;
1613				ptrPos++;
1614
1615				if (ptrPos == pngIHDR.pngWidth) {
1616					continue;
1617				}
1618
1619				pngCurSample = (pngCurComponent[0] >> 1) & 1;
1620
1621				if (pngCurSample >= pngPaletteCount) {
1622					pngCurSample = 0;
1623				}
1624
1625				ptr[0] = pngPaletteRealized[pngCurSample];
1626				ptr++;
1627				ptrPos++;
1628
1629				if (ptrPos == pngIHDR.pngWidth) {
1630					continue;
1631				}
1632
1633				pngCurSample = pngCurComponent[0] & 1;
1634
1635				if (pngCurSample >= pngPaletteCount) {
1636					pngCurSample = 0;
1637				}
1638
1639				ptr[0] = pngPaletteRealized[pngCurSample];
1640				ptr++;
1641				ptrPos++;
1642
1643				continue;
1644
1645			case PNG_STATE_RENDER_STATE_BASE + PNG_INDEXED_COLOUR_2BPP:
1646
1647	//			Console.putln("hey!\n");
1648
1649				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
1650
1651				decoderState = pngFilterState;
1652
1653				// WE WILL ADD 4 PIXELS, IF WE CAN
1654
1655				pngCurSample = pngCurComponent[0] >> 6;
1656
1657				if (pngCurSample >= pngPaletteCount) {
1658					pngCurSample = 0;
1659				}
1660	//			Console.putln("1st pixel...\n");
1661
1662				ptr[0] = pngPaletteRealized[pngCurSample];
1663				ptr++;
1664				ptrPos++;
1665
1666	//			Console.putln("1st pixel!\n");
1667
1668				if (ptrPos >= pngIHDR.pngWidth) {
1669					continue;
1670				}
1671
1672				pngCurSample = (pngCurComponent[0] >> 4) & 0x3;
1673
1674				if (pngCurSample >= pngPaletteCount) {
1675					pngCurSample = 0;
1676				}
1677
1678	//			Console.putln("2nd pixel...\n");
1679
1680				ptr[0] = pngPaletteRealized[pngCurSample];
1681				ptr++;
1682				ptrPos++;
1683
1684	//			Console.putln("2nd pixel!\n");
1685
1686				if (ptrPos >= pngIHDR.pngWidth) {
1687					continue;
1688				}
1689
1690				pngCurSample = (pngCurComponent[0] >> 2) & 0x3;
1691
1692				if (pngCurSample >= pngPaletteCount) {
1693					pngCurSample = 0;
1694				}
1695
1696	//			Console.putln("3rd pixel...\n");
1697
1698				ptr[0] = pngPaletteRealized[pngCurSample];
1699				ptr++;
1700				ptrPos++;
1701
1702	//			Console.putln("3rd pixel!\n");
1703
1704				if (ptrPos >= pngIHDR.pngWidth) {
1705					continue;
1706				}
1707
1708				pngCurSample = pngCurComponent[0] & 0x3;
1709
1710				if (pngCurSample >= pngPaletteCount) {
1711					pngCurSample = 0;
1712				}
1713
1714	//			Console.putln("4th pixel...\n");
1715
1716				ptr[0] = pngPaletteRealized[pngCurSample];
1717				ptr++;
1718				ptrPos++;
1719
1720	//			Console.putln("4th pixel!\n");
1721
1722				continue;
1723
1724			case PNG_STATE_RENDER_STATE_BASE + PNG_INDEXED_COLOUR_4BPP:
1725
1726				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
1727
1728				decoderState = pngFilterState;
1729
1730				// WE WILL ADD 2 PIXELS, IF WE CAN
1731
1732				pngCurSample = pngCurComponent[0] >> 4;
1733
1734				if (pngCurSample >= pngPaletteCount) {
1735					pngCurSample = 0;
1736				}
1737
1738				ptr[0] = pngPaletteRealized[pngCurSample];
1739				ptr++;
1740				ptrPos++;
1741
1742				if (ptrPos == pngIHDR.pngWidth) {
1743					continue;
1744				}
1745
1746				pngCurSample = pngCurComponent[0] & 0xF;
1747
1748				if (pngCurSample >= pngPaletteCount) {
1749					pngCurSample = 0;
1750				}
1751
1752				ptr[0] = pngPaletteRealized[pngCurSample];
1753				ptr++;
1754				ptrPos++;
1755
1756				continue;
1757
1758			case PNG_STATE_RENDER_STATE_BASE + PNG_INDEXED_COLOUR_8BPP:
1759
1760				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
1761
1762				decoderState = pngFilterState;
1763
1764				if (pngCurComponent[0] >= pngPaletteCount) {
1765					pngCurComponent[0] = 0;
1766				}
1767
1768				ptr[0] = pngPaletteRealized[pngCurComponent[0]];
1769				ptr++;
1770				ptrPos++;
1771
1772				continue;
1773
1774
1775
1776			case PNG_STATE_RENDER_STATE_BASE + PNG_GREYSCALE_ALPHA_8BPP:
1777
1778				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
1779
1780				decoderState = pngFilterState;
1781
1782				// JUST ADD THE PIXEL
1783
1784				// PRE MULTIPLY ALPHA WITH R, G, B
1785				a = cast(float)pngCurComponent[1];
1786				a /= cast(float)0xFF;
1787
1788				a *= pngCurComponent[0];
1789
1790				pngCurComponent[0] = cast(ubyte)a;
1791				pngCurComponent[0] %= 256;
1792
1793
1794				ptr[0] = view.rgbaTouint(pngCurComponent[0], pngCurComponent[0], pngCurComponent[0], pngCurComponent[1]);
1795
1796				ptr++;
1797				ptrPos++;
1798
1799				continue;
1800
1801			case PNG_STATE_RENDER_STATE_BASE + PNG_GREYSCALE_ALPHA_16BPP:
1802
1803				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
1804
1805				decoderState = pngFilterState;
1806
1807				pngCurComponent[0] = cast(ubyte)(((cast(float)((pngCurComponent[0] << 8) | pngCurComponent[1]) / cast(float)0xFFFF) * cast(float)0xFF) + 0.5);
1808				pngCurComponent[1] = cast(ubyte)(((cast(float)((pngCurComponent[2] << 8) | pngCurComponent[3]) / cast(float)0xFFFF) * cast(float)0xFF) + 0.5);
1809
1810				// JUST ADD THE PIXEL
1811
1812				// PRE MULTIPLY ALPHA WITH R, G, B
1813				a = cast(float)pngCurComponent[1];
1814				a /= cast(float)0xFF;
1815
1816				a *= pngCurComponent[0];
1817
1818				pngCurComponent[0] = cast(ubyte)a;
1819				pngCurComponent[0] %= 256;
1820
1821
1822				ptr[0] = view.rgbaTouint(pngCurComponent[0], pngCurComponent[0], pngCurComponent[0], pngCurComponent[1]);
1823
1824				ptr++;
1825				ptrPos++;
1826
1827				continue;
1828
1829
1830
1831			case PNG_STATE_RENDER_STATE_BASE + PNG_TRUECOLOUR_ALPHA_8BPP:
1832
1833				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
1834
1835				decoderState = pngFilterState;
1836
1837				// PRE MULTIPLY ALPHA WITH R, G, B
1838
1839				ptr[0] = view.rgbaTouint(pngCurComponent[0], pngCurComponent[1], pngCurComponent[2], pngCurComponent[3]);
1840
1841				ptr++;
1842				ptrPos++;
1843
1844				continue;
1845
1846			case PNG_STATE_RENDER_STATE_BASE + PNG_TRUECOLOUR_ALPHA_16BPP:
1847
1848				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
1849
1850				decoderState = pngFilterState;
1851
1852				// combine the components and place them in 0, 1, 2, and 3 only
1853				// that is, truncate every two bytes into 4 bytes
1854
1855				pngCurComponent[0] = cast(ubyte)(((cast(float)((pngCurComponent[0] << 8) | pngCurComponent[1]) / cast(float)0xFFFF) * cast(float)0xFF) + 0.5);
1856				pngCurComponent[1] = cast(ubyte)(((cast(float)((pngCurComponent[2] << 8) | pngCurComponent[3]) / cast(float)0xFFFF) * cast(float)0xFF) + 0.5);
1857				pngCurComponent[2] = cast(ubyte)(((cast(float)((pngCurComponent[4] << 8) | pngCurComponent[5]) / cast(float)0xFFFF) * cast(float)0xFF) + 0.5);
1858				pngCurComponent[3] = cast(ubyte)(((cast(float)((pngCurComponent[6] << 8) | pngCurComponent[7]) / cast(float)0xFFFF) * cast(float)0xFF) + 0.5);
1859
1860				ptr[0] = view.rgbaTouint(pngCurComponent[0], pngCurComponent[1], pngCurComponent[2], pngCurComponent[3]);
1861
1862				ptr++;
1863				ptrPos++;
1864
1865				continue;
1866
1867				// INTERLACED RENDERING ... THESE ARE SEPARATE STATES //
1868
1869			case PNG_STATE_RENDER_INTERLACED_STATE_BASE + PNG_GREYSCALE_1BPP:
1870
1871				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
1872
1873				decoderState = pngFilterState;
1874
1875				// WE WILL ADD 8 PIXELS, IF WE CAN
1876
1877				pngCurSample = png1BPP[pngCurComponent[0] >> 7];
1878
1879				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1880
1881				// INCREMENT WITH RESPECT TO INTERLACING
1882
1883				ptr += pngInterlaceIncrementsX[pngInterlacePass];
1884				ptrPos += pngInterlaceIncrementsX[pngInterlacePass];
1885
1886				if (ptrPos == pngIHDR.pngWidth) {
1887					continue;
1888				}
1889
1890				pngCurSample = png1BPP[(pngCurComponent[0] >> 6) & 1];
1891
1892				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1893
1894				// INCREMENT WITH RESPECT TO INTERLACING
1895
1896				ptr += pngInterlaceIncrementsX[pngInterlacePass];
1897				ptrPos += pngInterlaceIncrementsX[pngInterlacePass];
1898
1899				if (ptrPos == pngIHDR.pngWidth) {
1900					continue;
1901				}
1902
1903				pngCurSample = png1BPP[(pngCurComponent[0] >> 5) & 1];
1904
1905				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1906
1907				// INCREMENT WITH RESPECT TO INTERLACING
1908
1909				ptr += pngInterlaceIncrementsX[pngInterlacePass];
1910				ptrPos += pngInterlaceIncrementsX[pngInterlacePass];
1911
1912				if (ptrPos == pngIHDR.pngWidth) {
1913					continue;
1914				}
1915
1916				pngCurSample= png1BPP[(pngCurComponent[0] >> 4) & 1];
1917
1918				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1919
1920				// INCREMENT WITH RESPECT TO INTERLACING
1921
1922				ptr += pngInterlaceIncrementsX[pngInterlacePass];
1923				ptrPos += pngInterlaceIncrementsX[pngInterlacePass];
1924
1925				if (ptrPos == pngIHDR.pngWidth) {
1926					continue;
1927				}
1928
1929				pngCurSample = png1BPP[(pngCurComponent[0] >> 3) & 1];
1930
1931				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1932
1933				// INCREMENT WITH RESPECT TO INTERLACING
1934
1935				ptr += pngInterlaceIncrementsX[pngInterlacePass];
1936				ptrPos += pngInterlaceIncrementsX[pngInterlacePass];
1937
1938				if (ptrPos == pngIHDR.pngWidth) {
1939					continue;
1940				}
1941
1942				pngCurSample = png1BPP[(pngCurComponent[0] >> 2) & 1];
1943
1944				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1945
1946				// INCREMENT WITH RESPECT TO INTERLACING
1947
1948				ptr += pngInterlaceIncrementsX[pngInterlacePass];
1949				ptrPos += pngInterlaceIncrementsX[pngInterlacePass];
1950
1951				if (ptrPos == pngIHDR.pngWidth) {
1952					continue;
1953				}
1954
1955				pngCurSample = png1BPP[(pngCurComponent[0] >> 1) & 1];
1956
1957				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1958
1959				// INCREMENT WITH RESPECT TO INTERLACING
1960
1961				ptr += pngInterlaceIncrementsX[pngInterlacePass];
1962				ptrPos += pngInterlaceIncrementsX[pngInterlacePass];
1963
1964				if (ptrPos == pngIHDR.pngWidth) {
1965					continue;
1966				}
1967
1968				pngCurSample = png1BPP[pngCurComponent[0] & 1];
1969
1970				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1971
1972				// INCREMENT WITH RESPECT TO INTERLACING
1973
1974				ptr += pngInterlaceIncrementsX[pngInterlacePass];
1975				ptrPos += pngInterlaceIncrementsX[pngInterlacePass];
1976
1977
1978
1979
1980				continue;
1981
1982			case PNG_STATE_RENDER_INTERLACED_STATE_BASE + PNG_GREYSCALE_2BPP:
1983
1984				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
1985
1986				decoderState = pngFilterState;
1987
1988				// WE WILL ADD 4 PIXELS, IF WE CAN
1989
1990				pngCurSample = png2BPP[pngCurComponent[0] >> 6];
1991
1992				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
1993
1994				// INCREMENT WITH RESPECT TO INTERLACING
1995
1996				ptr += pngInterlaceIncrementsX[pngInterlacePass];
1997				ptrPos += pngInterlaceIncrementsX[pngInterlacePass];
1998
1999				if (ptrPos == pngIHDR.pngWidth) {
2000					continue;
2001				}
2002
2003				pngCurSample = png2BPP[(pngCurComponent[0] >> 4) & 0x3];
2004
2005				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
2006
2007				// INCREMENT WITH RESPECT TO INTERLACING
2008
2009				ptr += pngInterlaceIncrementsX[pngInterlacePass];
2010				ptrPos += pngInterlaceIncrementsX[pngInterlacePass];
2011
2012				if (ptrPos == pngIHDR.pngWidth) {
2013					continue;
2014				}
2015
2016				pngCurSample = png2BPP[(pngCurComponent[0] >> 2) & 0x3];
2017
2018				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
2019
2020				// INCREMENT WITH RESPECT TO INTERLACING
2021
2022				ptr += pngInterlaceIncrementsX[pngInterlacePass];
2023				ptrPos += pngInterlaceIncrementsX[pngInterlacePass];
2024
2025				if (ptrPos == pngIHDR.pngWidth) {
2026					continue;
2027				}
2028
2029				pngCurSample = png2BPP[pngCurComponent[0] & 0x3];
2030
2031				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
2032
2033				// INCREMENT WITH RESPECT TO INTERLACING
2034
2035				ptr += pngInterlaceIncrementsX[pngInterlacePass];
2036				ptrPos += pngInterlaceIncrementsX[pngInterlacePass];
2037
2038				continue;
2039
2040			case PNG_STATE_RENDER_INTERLACED_STATE_BASE + PNG_GREYSCALE_4BPP:
2041
2042				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
2043
2044				decoderState = pngFilterState;
2045
2046				// WE WILL ADD 2 PIXELS, IF WE CAN
2047
2048				pngCurSample = png4BPP[pngCurComponent[0] >> 4];
2049
2050				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
2051
2052				// INCREMENT WITH RESPECT TO INTERLACING
2053
2054				ptr += pngInterlaceIncrementsX[pngInterlacePass];
2055				ptrPos += pngInterlaceIncrementsX[pngInterlacePass];
2056
2057				if (ptrPos == pngIHDR.pngWidth) {
2058					continue;
2059				}
2060
2061				pngCurSample = png4BPP[pngCurComponent[0] & 0xF];
2062
2063				ptr[0] = view.rgbTouint(pngCurSample, pngCurSample, pngCurSample);
2064
2065				// INCREMENT WITH RESPECT TO INTERLACING
2066
2067				ptr += pngInterlaceIncrementsX[pngInterlacePass];
2068				ptrPos += pngInterlaceIncrementsX[pngInterlacePass];
2069
2070				continue;
2071
2072			case PNG_STATE_RENDER_INTERLACED_STATE_BASE + PNG_GREYSCALE_8BPP:
2073
2074				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
2075
2076				decoderState = pngFilterState;
2077
2078				// JUST ADD THE PIXEL
2079
2080				ptr[0] = view.rgbTouint(pngCurComponent[0], pngCurComponent[0], pngCurComponent[0]);
2081
2082				// INCREMENT WITH RESPECT TO INTERLACING
2083
2084				ptr += pngInterlaceIncrementsX[pngInterlacePass];
2085				ptrPos += pngInterlaceIncrementsX[pngInterlacePass];
2086
2087				continue;
2088
2089			case PNG_STATE_RENDER_INTERLACED_STATE_BASE + PNG_GREYSCALE_16BPP:
2090
2091				// GO BACK TO FILTER ANOTHER PIECE OF THE DATA
2092
2093				decoderState = pngFilterState;
2094
2095				// JUST OUTPUT THE PIXEL
2096
2097				pngCurComponent[0] = cast(ubyte)(((cast(float)((pngCurComponent[0] << 8) | pngCurComponent[1]) / cast(float)0xFFFF) * cast(float)0xFF) + 0.5);

Large files files are truncated, but you can click here to view the full file