PageRenderTime 101ms CodeModel.GetById 2ms app.highlight 91ms RepoModel.GetById 1ms app.codeStats 0ms

/decoders/image/gif.d

http://github.com/wilkie/djehuty
D | 1346 lines | 834 code | 341 blank | 171 comment | 164 complexity | 87c6705490f11b01e4bead89781e08fb MD5 | raw file
   1/*
   2 * gif.d
   3 *
   4 * This module implements the GIF 89a standard.
   5 *
   6 * Author: Dave Wilkinson
   7 *
   8 */
   9
  10module decoders.image.gif;
  11
  12import graphics.bitmap;
  13
  14import core.string;
  15import core.stream;
  16import core.color;
  17import core.definitions;
  18
  19import decoders.image.decoder;
  20import decoders.decoder;
  21
  22// For debugging
  23import io.console;
  24
  25// The structures define structures found in the standard and utilized
  26// when reading the file or stream.
  27private {
  28	align(1) struct _djehuty_image_gif_header {
  29		ubyte gifSignature[3];
  30		ubyte gifVersion[3];
  31	}
  32
  33	align(1) struct _djehuty_image_gif_logical_screen {
  34		ushort gifLogicalScreenWidth;
  35		ushort gifLogicalScreenHeight;
  36		ubyte gifPackedFields;
  37		ubyte gifBackgroundColorIndex;
  38		ubyte gifPixelAspectRatio;
  39	}
  40
  41	align(1) struct _djehuty_image_gif_color {
  42		ubyte red;
  43		ubyte green;
  44		ubyte blue;
  45	}
  46
  47	align(1) struct _djehuty_image_gif_image_descriptor {
  48		ushort gifImageLeftPos;
  49		ushort gifImageTopPos;
  50		ushort gifImageWidth;
  51		ushort gifImageHeight;
  52		ubyte gifPackedFields;
  53	}
  54
  55	align(1) struct _djehuty_image_gif_graphics_extension {
  56		ubyte gifBlockSize;
  57
  58		ubyte gifPackedFields;
  59		ushort gifDelayTime;
  60		ubyte gifTransparentColorIndex;
  61
  62		ubyte gifBlockTerminator;
  63	}
  64
  65	struct _djehuty_image_gif_lzw_dictionary_entry {
  66		short code;
  67		short hops;
  68		short output;
  69	}
  70
  71	const ubyte gifMasks[] =
  72	[
  73
  74	/* STARTBYTE, INTERMEDIATE BYTE, LAST BYTE */
  75	/* 8 STEPS PER BIT COUNT */
  76
  77	 /* 2 masks */
  78	0x3, 0x0, 0x0, 0x6, 0x0, 0x0, 0xC, 0x0, 0x0, 0x18, 0x0, 0x0, 0x30, 0x0, 0x0, 0x60, 0x0, 0x0, 0xC0, 0x0, 0x0, 0x80, 0x1, 0x0,
  79	 /* 3 masks */
  80	0x7, 0x0, 0x0, 0xE, 0x0, 0x0, 0x1C, 0x0, 0x0, 0x38, 0x0, 0x0, 0x70, 0x0, 0x0, 0xE0, 0x0, 0x0, 0xC0, 0x1, 0x0, 0x80, 0x3, 0x0,
  81	 /* 4 masks */
  82	0xF, 0x0, 0x0, 0x1E, 0x0, 0x0, 0x3C, 0x0, 0x0, 0x78, 0x0, 0x0, 0xF0, 0x0, 0x0, 0xE0, 0x1, 0x0, 0xC0, 0x3, 0x0, 0x80, 0x7, 0x0,
  83	 /* 5 masks */
  84	0x1F, 0x0, 0x0, 0x3E, 0x0, 0x0, 0x7C, 0x0, 0x0, 0xF8, 0x0, 0x0, 0xF0, 0x1, 0x0, 0xE0, 0x3, 0x0, 0xC0, 0x7, 0x0, 0x80, 0xF, 0x0,
  85	 /* 6 masks */
  86	0x3F, 0x0, 0x0, 0x7E, 0x0, 0x0, 0xFC, 0x0, 0x0, 0xF8, 0x1, 0x0, 0xF0, 0x3, 0x0, 0xE0, 0x7, 0x0, 0xC0, 0xF, 0x0, 0x80, 0x1F, 0x0,
  87	 /* 7 masks */
  88	0x7F, 0x0, 0x0, 0xFE, 0x0, 0x0, 0xFC, 0x1, 0x0, 0xF8, 0x3, 0x0, 0xF0, 0x7, 0x0, 0xE0, 0xF, 0x0, 0xC0, 0x1F, 0x0, 0x80, 0x3F, 0x0,
  89	 /* 8 masks */
  90	0xFF, 0x0, 0x0, 0xFE, 0x1, 0x0, 0xFC, 0x3, 0x0, 0xF8, 0x7, 0x0, 0xF0, 0xF, 0x0, 0xE0, 0x1F, 0x0, 0xC0, 0x3F, 0x0, 0x80, 0x7F, 0x0,
  91	 /* 9 masks */
  92	0xFF, 0x1, 0x0, 0xFE, 0x3, 0x0, 0xFC, 0x7, 0x0, 0xF8, 0xF, 0x0, 0xF0, 0x1F, 0x0, 0xE0, 0x3F, 0x0, 0xC0, 0x7F, 0x0, 0x80, 0xFF, 0x0,
  93	 /* 10 masks */
  94	0xFF, 0x3, 0x0, 0xFE, 0x7, 0x0, 0xFC, 0xF, 0x0, 0xF8, 0x1F, 0x0, 0xF0, 0x3F, 0x0, 0xE0, 0x7F, 0x0, 0xC0, 0xFF, 0x0, 0x80, 0xFF, 0x1,
  95	 /* 11 masks */
  96	0xFF, 0x7, 0x0, 0xFE, 0xF, 0x0, 0xFC, 0x1F, 0x0, 0xF8, 0x3F, 0x0, 0xF0, 0x7F, 0x0, 0xE0, 0xFF, 0x0, 0xC0, 0xFF, 0x1, 0x80, 0xFF, 0x3,
  97	 /* 12 masks */
  98	0xFF, 0xF, 0x0, 0xFE, 0x1F, 0x0, 0xFC, 0x3F, 0x0, 0xF8, 0x7F, 0x0, 0xF0, 0xFF, 0x0, 0xE0, 0xFF, 0x1, 0xC0, 0xFF, 0x3, 0x80, 0xFF, 0x7
  99
 100	];
 101
 102	//global constants
 103	const int _djehuty_image_gif_size_of_global_color_table_ref[] = [2,4,8,16,32,64,128,256];
 104
 105	// _decoder States
 106	const auto GIF_STATE_INIT						= 0;
 107
 108	const auto GIF_STATE_READ_HEADERS				= 1;
 109	const auto GIF_STATE_READ_GRAPHIC_CONTROL		= 2;
 110
 111	const auto GIF_STATE_DECODE_IMAGE				= 3;
 112	const auto GIF_STATE_READ_LOCAL_COLOR_TABLE		= 4;
 113
 114	const auto GIF_STATE_INIT_DECODER				= 5;
 115	const auto GIF_STATE_READ_LZW_CODESIZE			= 6;
 116
 117	const auto GIF_STATE_DECODE						= 7;
 118}
 119
 120// Description: The GIF Codec
 121class GIFDecoder : ImageDecoder {
 122	override string name() {
 123		return "Graphics Interchange Format";
 124	}
 125
 126	override StreamData decode(Stream stream, ref Bitmap view) {
 127		ImageFrameDescription imageDesc;
 128
 129		// will read headers and such
 130
 131		StreamData ret = StreamData.Accepted;
 132
 133		ret = _decoder(stream, view, imageDesc);
 134
 135		if (ret == StreamData.Accepted) {
 136			// the image frame will be next
 137			// stop, since we got what we needed
 138			// which is the first frame
 139
 140			// but this signals that we have more than one frame
 141			return StreamData.Accepted;
 142		}
 143
 144		return ret;
 145	}
 146
 147	override StreamData decodeFrame(Stream stream, ref Bitmap view, ref ImageFrameDescription imageDesc) {
 148		// will read headers and such
 149
 150		StreamData ret = _decoder(stream, view, imageDesc);
 151
 152		if (ret == StreamData.Complete) {
 153			// we are done
 154			imageDesc.clearFirst = gifFirstClear;
 155			imageDesc.clearColor = gifFirstClearColor;
 156			imageDesc.time = gifFirstTime;
 157
 158			return StreamData.Complete;
 159		}
 160
 161		return ret;
 162	}
 163
 164protected:
 165
 166	StreamData _decoder(ref Stream stream, ref Bitmap view, ref ImageFrameDescription imageDesc) {
 167		uint q;
 168
 169		ushort gifCode;
 170		ushort gifCodeTemp;
 171
 172		ulong ptr_len;
 173
 174		for (;;) {
 175			// READ HEADERS
 176			switch(decoderState) {
 177
 178				// READ HEADERS //
 179
 180			case GIF_STATE_INIT:
 181
 182				gifGraphicControl.gifBlockSize = 0;
 183
 184				gifIsFirst = 1;
 185
 186				decoderSubState = 0;
 187				decoderState = GIF_STATE_READ_HEADERS;
 188
 189			case GIF_STATE_READ_HEADERS:
 190				// READ FILE HEADER
 191
 192				switch(decoderSubState) {
 193
 194				// READ GIF HEADER
 195				case 0:
 196
 197					if (!stream.read(&gifHeader, _djehuty_image_gif_header.sizeof)) {
 198						return StreamData.Required;
 199					}
 200
 201					if (!(gifHeader.gifSignature[0] == 'G' &&
 202						gifHeader.gifSignature[1] == 'I' &&
 203						gifHeader.gifSignature[2] == 'F')) {
 204						return StreamData.Invalid;
 205					}
 206
 207					if (!(gifHeader.gifVersion[0] == '8' &&
 208						gifHeader.gifVersion[1] == '9' &&
 209						gifHeader.gifVersion[2] == 'a')) {
 210						return StreamData.Invalid;
 211					}
 212
 213					gifVersion = 1; // 89a
 214
 215					decoderSubState = 1;
 216
 217				// READ LOGICAL SCREEN DESCRIPTOR
 218				case 1:
 219
 220					if(!(stream.read(&gifScreen, _djehuty_image_gif_logical_screen.sizeof))) {
 221						return StreamData.Required;
 222					}
 223
 224					// DETERMINE WHETHER OR NOT WE GET A GLOBAL COLOR TABLE
 225					if (gifScreen.gifPackedFields & 128) {
 226						// global color table present
 227
 228						gifGlobalColorTableSize = _djehuty_image_gif_size_of_global_color_table_ref[ gifScreen.gifPackedFields & 0x7 ];
 229
 230						decoderSubState = 2;
 231					}
 232					else {
 233						// global color table not present
 234
 235						gifGlobalColorTableSize = 0;
 236
 237						decoderSubState = 0;
 238
 239						gifGraphicControl.gifBlockSize = 0;
 240
 241						decoderState = GIF_STATE_READ_GRAPHIC_CONTROL;
 242
 243						break;
 244					}
 245
 246				// READ IN GLOBAL COLOR TABLE WHEN PRESENT
 247				case 2:
 248					//Global Color Table is present; load it
 249					if(!(stream.read(gifGlobalColorTable.ptr, 3 * gifGlobalColorTableSize))) {
 250						return StreamData.Required;
 251					}
 252
 253					// Compute a 32bit argb color
 254					for (q=0; q<gifGlobalColorTableSize; q++) {
 255						gifGlobalColorTableComputed[q] = 0xFF000000 | (gifGlobalColorTable[q].red << 16) | ((gifGlobalColorTable[q].green << 8) | (gifGlobalColorTable[q].blue));
 256					}
 257
 258					gifGlobalColorTableSize = 0;
 259
 260					decoderSubState = 0;
 261
 262					gifGraphicControl.gifBlockSize = 0;
 263
 264					decoderState = GIF_STATE_READ_GRAPHIC_CONTROL;
 265
 266					break;
 267
 268				default: break;
 269				}
 270
 271				continue;
 272
 273			// READS IN ALL EXTENSIONS
 274			case GIF_STATE_READ_GRAPHIC_CONTROL:
 275
 276				switch(decoderSubState) {
 277				//READ EXTENSION INTRODUCER
 278				case 0:
 279					if(!(stream.read(&gifExtensionIntroducer, 1))) {
 280						return StreamData.Required;
 281					}
 282
 283					if (gifExtensionIntroducer == 0x3B) {
 284						// no more blocks
 285						return StreamData.Complete;
 286					}
 287					else if (gifExtensionIntroducer == 0x21) {
 288						//this is an extension
 289						decoderSubState = 1;
 290					}
 291					else if (gifExtensionIntroducer == 0x2C) {
 292						// the image is next
 293						decoderState = GIF_STATE_DECODE_IMAGE;
 294
 295						if (gifGraphicControl.gifBlockSize == 4) {
 296							if (gifIsFirst) {
 297
 298								gifFirstTime = (gifGraphicControl.gifDelayTime * 10);
 299
 300								if ((gifGraphicControl.gifPackedFields & 0x1C) == 0x08) {
 301									gifFirstClear = 1;
 302								}
 303								else {
 304									gifFirstClear = 0;
 305								}
 306
 307								if (gifScreen.gifBackgroundColorIndex >= gifGlobalColorTableSize) {
 308									gifFirstClearColor = Color.Black;
 309								}
 310								else {
 311									// if TRANSPARENT is set, clear color is transparent
 312									if ((gifGraphicControl.gifBlockSize == 4) && (gifGraphicControl.gifPackedFields & 1) ) {
 313										gifFirstClearColor = Color.Black;
 314									}
 315									else {
 316										gifFirstClearColor.red =
 317										  cast(double)gifGlobalColorTable[gifScreen.gifBackgroundColorIndex].red / 255.0;
 318										gifFirstClearColor.green =
 319										  cast(double)gifGlobalColorTable[gifScreen.gifBackgroundColorIndex].green / 255.0;
 320										gifFirstClearColor.blue =
 321										  cast(double)gifGlobalColorTable[gifScreen.gifBackgroundColorIndex].blue / 255.0;
 322									}
 323								}
 324							}
 325							else {
 326								imageDesc.time = (gifGraphicControl.gifDelayTime * 10);
 327
 328								if ((gifGraphicControl.gifPackedFields & 0x1C) == 0x08) {
 329									imageDesc.clearFirst = 1;
 330								}
 331								else {
 332									imageDesc.clearFirst = 0;
 333								}
 334
 335								if (gifScreen.gifBackgroundColorIndex >= gifGlobalColorTableSize) {
 336									imageDesc.clearColor = Color.Black;
 337								}
 338								else {
 339									// iF TRANSPARENT is set, clear color is transparent
 340									if ((gifGraphicControl.gifBlockSize == 4) && (gifGraphicControl.gifPackedFields & 1) ) {
 341										imageDesc.clearColor = Color.Black;
 342									}
 343									else {
 344										imageDesc.clearColor.red =
 345										  cast(double)gifGlobalColorTable[gifScreen.gifBackgroundColorIndex].red / 255.0;
 346										imageDesc.clearColor.green =
 347										  cast(double)gifGlobalColorTable[gifScreen.gifBackgroundColorIndex].green / 255.0;
 348										imageDesc.clearColor.blue =
 349										  cast(double)gifGlobalColorTable[gifScreen.gifBackgroundColorIndex].blue / 255.0;
 350									}
 351								}
 352							}
 353						}
 354						else {
 355							if (gifIsFirst) {
 356								gifFirstClear = 0;
 357							}
 358							else {
 359								imageDesc.clearFirst = 0;
 360							}
 361						}
 362
 363						// image data is next, there is another frame
 364						return StreamData.Accepted; // indicate frame is next
 365					}
 366
 367					break;
 368
 369				//READ EXTENSION LABEL
 370				case 1:
 371
 372					//otherwise, it is the table's lzw minimum code size
 373					if(!(stream.read(&gifExtensionLabel, 1))) {
 374						return StreamData.Required;
 375					}
 376
 377					if (gifExtensionLabel == 0xF9) {
 378						// IS A GRAPHIC CONTROL EXTENSION
 379						decoderSubState = 2;
 380					}
 381					else {
 382						// READ THE BLOCK SIZE
 383						// SKIP THAT MANY BYTES
 384						decoderSubState = 3;
 385					}
 386					break;
 387
 388				//READ IN GRAPHIC CONTROL EXTENSION
 389				case 2:
 390
 391					if(!(stream.read(&gifGraphicControl, _djehuty_image_gif_graphics_extension.sizeof))) {
 392						return StreamData.Required;
 393					}
 394
 395					// IT SHOULD BE 4, BUT WE WILL SET IT AS SUCH ANYWAY
 396					// THIS WILL BE OUR CHECK FOR WHEN THE STRUCTURE IS FILLED
 397					gifGraphicControl.gifBlockSize = 4;
 398
 399					decoderSubState = 0;
 400
 401					break;
 402
 403
 404				//SKIP IRREVALVENT EXTENSION DATA
 405
 406				//FIND EXTENSION SIZE
 407				case 3:
 408					if (!(stream.read(&gifExtensionLabel, 1))) {
 409						return StreamData.Required;
 410					}
 411
 412					decoderSubState = 4;
 413
 414				//SKIP EXTENSION LABEL
 415				case 4:
 416					if(!(stream.skip(gifExtensionLabel))) {
 417						return StreamData.Required;
 418					}
 419
 420					decoderSubState = 5;
 421
 422				//SKIP EXTENSION DATA BLOCKS
 423				//READ IN SIZE OF DATA BLOCK
 424				case 5:
 425					// NOW READ IN ALL SUB-DATA BLOCKS
 426					// AND SKIP THEM
 427					if (!(stream.read(&gifExtensionLabel, 1))) {
 428						return StreamData.Required;
 429					}
 430
 431					decoderSubState = 6;
 432
 433				case 6:
 434
 435					if (gifExtensionLabel > 0) {
 436						stream.skip(gifExtensionLabel);
 437						decoderSubState = 5;
 438					}
 439					else {
 440						decoderSubState = 0;
 441					}
 442
 443				default: break;
 444				}
 445
 446				continue;
 447
 448			// READ IN IMAGE DESCRIPTOR
 449			case GIF_STATE_DECODE_IMAGE:
 450				//DecodeImage(stream, view, imageDesc, idp);
 451				//return StreamData.Required;
 452
 453				// READ IN IMAGE DESCRIPTOR
 454
 455				if(!(stream.read(&gifImage, _djehuty_image_gif_image_descriptor.sizeof))) {
 456					return StreamData.Required;
 457				}
 458
 459				if (gifImage.gifPackedFields & 128) {
 460					// local color table present
 461
 462					gifLocalColorTableSize = _djehuty_image_gif_size_of_global_color_table_ref[ gifImage.gifPackedFields & 0x7 ];
 463					decoderState = GIF_STATE_READ_LOCAL_COLOR_TABLE;
 464
 465					// ... note we will drop through ... //
 466				}
 467				else {
 468					decoderState = GIF_STATE_INIT_DECODER;
 469
 470					// local color table is not present
 471
 472					gifLocalColorTableSize = 0;
 473					gifCurColorTable = gifGlobalColorTableComputed.ptr;
 474
 475					continue;
 476				}
 477
 478				// ... drop through WHEN there is a local color table ... //
 479
 480			// READ IN LOCAL COLOR TABLE WHEN PRESENT
 481			case GIF_STATE_READ_LOCAL_COLOR_TABLE:
 482				//local Color Table is present
 483
 484				if(!(stream.read(gifLocalColorTable.ptr, 3 * gifLocalColorTableSize))) {
 485					return StreamData.Required;
 486				}
 487
 488				//compute values
 489				uint i;
 490
 491				for (i=0; i<gifLocalColorTableSize; i++) {
 492					gifLocalColorTableComputed[i] = 0xFF000000 | (gifLocalColorTable[i].red << 16) | ((gifLocalColorTable[i].green << 8) | (gifLocalColorTable[i].blue));
 493				}
 494
 495				gifCurColorTable = gifLocalColorTableComputed.ptr;
 496
 497				decoderState = GIF_STATE_INIT_DECODER;
 498
 499			case GIF_STATE_INIT_DECODER:
 500				// UPDATE Transparent Color INDEX IN THE COLOR TABLE (if present)
 501				if ((gifGraphicControl.gifBlockSize == 4) && (gifGraphicControl.gifPackedFields & 1)) {
 502					gifCurColorTable[gifGraphicControl.gifTransparentColorIndex] = 0;
 503				}
 504
 505				// UPDATE FRAME DESCRIPTION
 506				imageDesc.xoffset = gifImage.gifImageLeftPos;
 507				imageDesc.yoffset = gifImage.gifImageTopPos;
 508
 509				// CHECK TO SEE IF IMAGE IS INTERLACED
 510				gifIsInterlaced = (gifImage.gifPackedFields & 64);
 511
 512				decoderState = GIF_STATE_READ_LZW_CODESIZE;
 513
 514			case GIF_STATE_READ_LZW_CODESIZE:
 515
 516				// READ IN THE LZW MINIMUM CODE SIZE
 517
 518				if (!(stream.read(&gifExtensionIntroducer, 1))) {
 519					return StreamData.Required;
 520				}
 521
 522				if (gifExtensionIntroducer < 2)
 523				{
 524					// incorrect lzw min size
 525					return StreamData.Invalid;
 526				}
 527
 528				// we start from code size + 1 when reading in data
 529				// so we increment the code size
 530				gifExtensionIntroducer++;
 531
 532				// set the default dictionary size
 533				gifDictionarySize = _djehuty_image_gif_size_of_global_color_table_ref[gifExtensionIntroducer-2];
 534
 535				// BUILD INITIAL LZW DICTIONARY
 536
 537				//i will iterate through the dictionary
 538				for (short i=0; i<gifDictionarySize+2; i++) {
 539					gifDictionary[i].code = 0;
 540					gifDictionary[i].hops = 0;
 541					gifDictionary[i].output = i;
 542				}
 543
 544				gifClearCode = cast(ushort)gifDictionarySize;
 545				gifEOICode = cast(ushort)(gifDictionarySize + 1);
 546
 547				// N WILL BE THE NEXT CODE
 548				lzw_nextEntry = cast(ushort)(gifClearCode + 2);
 549
 550				// bound the next entry to gifDictionarySize * 2
 551				gifDictionarySize *= 2;
 552
 553				gifCodeSize = gifExtensionIntroducer;
 554				gifStartCodeSize = gifCodeSize;
 555
 556				// ESTABLISH MASKS
 557				gifCurMaskArray = cast(ubyte*)&gifMasks[(gifCodeSize - 2) * 24];
 558				gifCurMaskIndex = 0;
 559				gifCurMaskIndexComp = 0;
 560
 561				gifMaskStart = gifCurMaskArray[0];
 562				gifMaskIntermediate = gifCurMaskArray[1];
 563				gifMaskEnd = gifCurMaskArray[2];
 564
 565				lzw_isFirstEntry = 0;
 566
 567				gifInterlaceState = 0; //the state of the interlacing
 568
 569				ptrPos = 0;
 570				ptrLine = 0;
 571
 572				gifBlockSize = 0;
 573				gifBlockCounter = 0;
 574
 575				view.create(gifImage.gifImageWidth, gifImage.gifImageHeight);
 576
 577				decoderState = GIF_STATE_DECODE;
 578				decoderSubState = 0;
 579
 580				// ... drop through ... //
 581
 582			case GIF_STATE_DECODE:
 583				// start decoding
 584
 585				view.lockBuffer(cast(void**)&ptr_start, ptr_len);
 586
 587				ptr = ptr_start;
 588
 589				ptr += (gifImage.gifImageWidth * ptrLine);
 590				ptr_max_line = ptr + gifImage.gifImageWidth;
 591
 592				ptr_max_page = ptr_start + (ptr_len / 4);
 593
 594				ptr += ptrPos;
 595
 596				while(decoderState == GIF_STATE_DECODE) {
 597
 598					switch(decoderSubState) {
 599
 600					//READ IN BLOCK SIZE
 601					case 0:
 602
 603						gifLastBlockSize = gifBlockSize;
 604						gifBlockSize=0;
 605
 606						decoderSubState = 1;
 607
 608					case 1:
 609
 610						if (!(stream.read(&gifBlockSize, 1))) {
 611							view.unlockBuffer();
 612							return StreamData.Required;
 613						}
 614
 615						if (gifBlockSize == 0) {
 616							// block terminator found (end of blocks)
 617
 618							// PERHAPS THERE WILL BE ANOTHER FRAME
 619							decoderState = GIF_STATE_READ_GRAPHIC_CONTROL;
 620							decoderSubState = 0;
 621
 622							view.unlockBuffer();
 623
 624							break;
 625						}
 626						else {
 627							decoderSubState = 2;
 628
 629							// ... drop through ... //
 630						}
 631
 632					// READ IN IMAGE DATA BLOCK
 633					// APPEND, IF NEEDED, TO OLD BLOCK
 634					case 2:
 635
 636						// decode this image block
 637
 638						if ((gifBlockCounter==gifLastBlockSize-1)) {
 639							gifImageData[0] = gifImageData[gifBlockCounter];
 640							if(!(stream.read(&gifImageData[1], gifBlockSize))) {
 641								view.unlockBuffer();
 642								return StreamData.Required;
 643							}
 644							gifBlockSize++;
 645						}
 646						else if ((gifBlockCounter==gifLastBlockSize-2)) {
 647							gifImageData[0] = gifImageData[gifBlockCounter];
 648							gifImageData[1] = gifImageData[gifBlockCounter+1];
 649							if (!(stream.read(&gifImageData[2], gifBlockSize))) {
 650								view.unlockBuffer();
 651								return StreamData.Required;
 652							}
 653							gifBlockSize+=2;
 654						}
 655						else {
 656							if (!(stream.read(gifImageData.ptr, gifBlockSize))) {
 657								view.unlockBuffer();
 658								return StreamData.Required;
 659							}
 660						}
 661
 662						gifBlockCounter = 0;
 663
 664						gifCode = 0;
 665						gifCodeTemp = 0;
 666
 667						//get a code
 668						decoderSubState = 3;
 669
 670						if (gifIsInterlaced) {
 671							decoderNextSubState = 5;
 672						}
 673						else {
 674							//DECODER FOR NON INTERLACED IMAGES
 675							decoderNextSubState = 4;
 676						}
 677
 678
 679					// READ IN CODE
 680					case 3:
 681
 682						// get next code
 683						uint old_blockcounter = gifBlockCounter;
 684
 685						gifCode = cast(ushort)(gifImageData[gifBlockCounter] & gifMaskStart);
 686
 687						// reading in a code
 688
 689						if (gifCurMaskIndex) {
 690							gifCode >>= gifCurMaskIndex;
 691						}
 692
 693						if (gifMaskIntermediate) {
 694							// go to next byte
 695							gifBlockCounter++;
 696							if (gifBlockCounter>=gifBlockSize) {
 697								gifBlockCounter = old_blockcounter;
 698
 699								decoderSubState = 0;
 700								break;
 701							}
 702
 703							gifCodeTemp = cast(ushort)(gifImageData[gifBlockCounter] & gifMaskIntermediate);
 704
 705							if (8 - gifCurMaskIndex) {
 706								gifCodeTemp <<= (8 - gifCurMaskIndex);
 707							}
 708
 709							gifCode |= gifCodeTemp;
 710						}
 711						else {
 712							//goto next byte when gifMaskStart's first bit is 1
 713							if (gifMaskStart & 128) {
 714								//goto next byte
 715								gifBlockCounter++;
 716								if (gifBlockCounter>=gifBlockSize) {
 717									gifBlockCounter=old_blockcounter;
 718
 719									decoderSubState = 0;
 720									break;
 721								}
 722							}
 723						}
 724
 725						if (gifMaskEnd) {
 726							// go to ultimate byte
 727							gifBlockCounter++;
 728							if (gifBlockCounter>=gifBlockSize) {
 729								gifBlockCounter=old_blockcounter;
 730
 731								decoderSubState = 0;
 732								break;
 733							}
 734
 735							gifCodeTemp = cast(ushort)(gifImageData[gifBlockCounter] & gifMaskEnd);
 736
 737							if (8-gifCurMaskIndex) {
 738								gifCodeTemp <<= (8-gifCurMaskIndex);
 739							}
 740
 741							gifCodeTemp <<= 8;
 742
 743							gifCode |= gifCodeTemp;
 744						}
 745						else {
 746							//goto next byte when gifMaskIntermediate's first bit is 1
 747							if (gifMaskIntermediate & 128) {
 748								//goto next byte
 749								gifBlockCounter++;
 750								if (gifBlockCounter>=gifBlockSize) {
 751									gifBlockCounter=old_blockcounter;
 752
 753									decoderSubState = 0;
 754									break;
 755								}
 756							}
 757						}
 758
 759						// set next mask index
 760						gifCurMaskIndex = (gifCurMaskIndex + gifCodeSize) % 8;
 761						gifCurMaskIndexComp = gifCurMaskIndex * 3;
 762
 763						gifMaskStart = gifCurMaskArray[gifCurMaskIndexComp];
 764						gifMaskIntermediate = gifCurMaskArray[gifCurMaskIndexComp+1];
 765						gifMaskEnd = gifCurMaskArray[gifCurMaskIndexComp+2];
 766
 767						// GOTO LZW DECODER
 768						decoderSubState = decoderNextSubState;
 769
 770						break;
 771
 772					// DECODER (non-interlaced)
 773					case 4:
 774
 775						decoderSubState = 3;
 776
 777						// THIS IS THE LZW DECOMPRESSOR FOR UNINTERLACED IMAGES
 778
 779						// INTERPRET gifCode
 780
 781						if (gifCode == gifEOICode) {
 782							// stop decoding (End Of Image)
 783
 784							decoderNextState = 0;
 785
 786							break;
 787						}
 788
 789						if (lzw_isFirstEntry==0) {
 790							//init LZW decompressor	(first entry)
 791
 792							lzw_isFirstEntry=1;
 793
 794							lzw_curEntry = gifCode;
 795
 796							// INTERPRET CODE AS PIXEL
 797
 798							if (gifCode < gifClearCode) {
 799								ptr[0] = gifCurColorTable[gifCode];
 800
 801								ptr++;
 802								ptrPos++;
 803
 804								// READ CODE
 805								break;
 806							}
 807						}
 808
 809						if (gifCode == gifClearCode) {
 810							//CLEAR CODE (reset dictionary)
 811							lzw_nextEntry = cast(ushort)(gifClearCode + 2);
 812
 813							gifDictionarySize = gifClearCode * 2;
 814
 815							gifCodeSize = gifStartCodeSize;
 816
 817							//update mask array
 818							gifCurMaskArray = cast(ubyte*)&gifMasks[(gifCodeSize - 2) * 24];
 819
 820							gifMaskStart = gifCurMaskArray[gifCurMaskIndexComp];
 821							gifMaskIntermediate = gifCurMaskArray[gifCurMaskIndexComp+1];
 822							gifMaskEnd = gifCurMaskArray[gifCurMaskIndexComp+2];
 823
 824							lzw_isFirstEntry = 0;
 825
 826							// READ CODE
 827							break;
 828						}
 829
 830						if (gifCode >= lzw_nextEntry) {
 831							// PRINT OUT LAST CODE FROM DICTIONARY
 832							gifCodeTemp = lzw_curEntry;
 833							q = gifDictionary[gifCodeTemp].hops;
 834
 835							for (;;) {
 836								ptr[q] = gifCurColorTable[gifDictionary[gifCodeTemp].output];
 837
 838								if (gifCodeTemp < gifClearCode) { break; }
 839
 840								gifCodeTemp = cast(ushort)gifDictionary[gifCodeTemp].code;
 841								q--;
 842							}
 843							ptr += gifDictionary[lzw_curEntry].hops + 1;
 844
 845							ptr[0] = gifCurColorTable[gifDictionary[gifCodeTemp].output];
 846							ptr++;
 847
 848							ptrPos += gifDictionary[lzw_curEntry].hops + 2;
 849						}
 850						else {
 851							// print code from dictionary
 852							gifCodeTemp = gifCode;
 853							q = gifDictionary[gifCodeTemp].hops;
 854
 855							for (;;) {
 856								ptr[q] = gifCurColorTable[gifDictionary[gifCodeTemp].output];
 857
 858								if (gifCodeTemp < gifClearCode) { break; }
 859
 860								gifCodeTemp = cast(ushort)gifDictionary[gifCodeTemp].code;
 861								q--;
 862							}
 863
 864							ptr += gifDictionary[gifCode].hops+1;
 865							ptrPos += gifDictionary[gifCode].hops+1;
 866						}
 867
 868						if (lzw_nextEntry < 4096) {
 869							// add this entry to the dictionary
 870							gifDictionary[lzw_nextEntry].code = cast(short)lzw_curEntry;
 871							gifDictionary[lzw_nextEntry].output = cast(short)gifDictionary[gifCodeTemp].output;
 872							gifDictionary[lzw_nextEntry].hops = cast(short)(gifDictionary[lzw_curEntry].hops + 1);
 873
 874							lzw_nextEntry++;
 875
 876							lzw_curEntry = gifCode;
 877
 878							if (lzw_nextEntry != 4096) {
 879								if (lzw_nextEntry >= gifDictionarySize) {
 880									gifDictionarySize *= 2;
 881									gifCodeSize++;
 882
 883									//update mask array
 884									gifCurMaskArray = cast(ubyte*)&gifMasks[(gifCodeSize - 2) * 24];
 885
 886									gifMaskStart = gifCurMaskArray[gifCurMaskIndexComp];
 887									gifMaskIntermediate = gifCurMaskArray[gifCurMaskIndexComp+1];
 888									gifMaskEnd = gifCurMaskArray[gifCurMaskIndexComp+2];
 889								}
 890							}
 891						}
 892
 893						// READ CODE
 894
 895						break;
 896
 897
 898
 899
 900
 901					// DECODER (interlaced)
 902					case 5:
 903
 904						decoderSubState = 3;
 905
 906						// THIS IS THE LZW DECOMPRESSOR FOR UNINTERLACED IMAGES
 907
 908						// INTERPRET gifCode
 909
 910						if (gifCode == gifEOICode) {
 911							// stop decoding (End Of Image)
 912
 913							decoderNextState = 0;
 914
 915							break;
 916						}
 917
 918						if (lzw_isFirstEntry==0) {
 919							//init LZW decompressor	(first entry)
 920
 921							lzw_isFirstEntry=1;
 922
 923							lzw_curEntry = gifCode;
 924
 925							// INTERPRET CODE AS PIXEL
 926
 927							if (gifCode < gifClearCode) {
 928								ptr[0] = gifCurColorTable[gifCode];
 929
 930								// draw surrounding pixels?
 931								if (gifInterlaceState < 3) {
 932									uint* ptrInterlaced = ptr + gifImage.gifImageWidth;
 933
 934									uint* ptrLast = void;
 935
 936									switch (gifInterlaceState) {
 937										case 0:
 938											ptrLast = ptrInterlaced + (gifImage.gifImageWidth * 7);
 939											break;
 940										case 1:
 941											ptrLast = ptrInterlaced + (gifImage.gifImageWidth * 3);
 942											break;
 943										case 2:
 944										default:
 945											ptrLast = ptrInterlaced + (gifImage.gifImageWidth * 1);
 946											break;
 947									}
 948
 949									if (ptr_max_page < ptrLast) { ptrLast = ptr_max_page; }
 950
 951									for ( ; ptrInterlaced < ptrLast ; ptrInterlaced += gifImage.gifImageWidth) {
 952										ptrInterlaced[0] = gifCurColorTable[gifDictionary[gifCodeTemp].output];
 953									}
 954								}
 955
 956								ptr++;
 957								ptrPos++;
 958
 959								if (ptr == ptr_max_line) {
 960									//change the line
 961									ptrPos=0;
 962									ptrLine++;
 963									_interlaceIncrement();
 964								}
 965
 966								// READ CODE
 967								break;
 968							}
 969						}
 970
 971						if (gifCode == gifClearCode) {
 972							//CLEAR CODE
 973							lzw_nextEntry = cast(ushort)(gifClearCode + 2);
 974
 975							gifDictionarySize = gifClearCode * 2;
 976
 977							gifCodeSize = gifStartCodeSize;
 978
 979							//update mask array
 980							gifCurMaskArray = cast(ubyte*)&gifMasks[(gifCodeSize - 2) * 24];
 981
 982							gifMaskStart = gifCurMaskArray[gifCurMaskIndexComp];
 983							gifMaskIntermediate = gifCurMaskArray[gifCurMaskIndexComp+1];
 984							gifMaskEnd = gifCurMaskArray[gifCurMaskIndexComp+2];
 985
 986							lzw_isFirstEntry = 0;
 987
 988							// READ CODE
 989							break;
 990						}
 991
 992						if (gifCode >= lzw_nextEntry) {
 993							// PRINT OUT LAST CODE FROM DICTIONARY
 994							gifCodeTemp = lzw_curEntry;
 995							q = gifDictionary[gifCodeTemp].hops;
 996
 997							for (;;) {
 998								gifUncompressed[q] = gifCurColorTable[gifDictionary[gifCodeTemp].output];
 999
1000								if (gifCodeTemp < gifClearCode) { break; }
1001
1002								gifCodeTemp = cast(ushort)gifDictionary[gifCodeTemp].code;
1003								q--;
1004							}
1005
1006							ptr[0] = gifCurColorTable[gifDictionary[gifCodeTemp].output];
1007
1008							// draw surrounding pixels?
1009							if (gifInterlaceState < 3) {
1010								uint* ptrInterlaced = ptr + gifImage.gifImageWidth;
1011
1012								uint* ptrLast = void;
1013
1014								switch (gifInterlaceState) {
1015									case 0:
1016										ptrLast = ptrInterlaced + (gifImage.gifImageWidth * 7);
1017										break;
1018									case 1:
1019										ptrLast = ptrInterlaced + (gifImage.gifImageWidth * 3);
1020										break;
1021									case 2:
1022									default:
1023										ptrLast = ptrInterlaced + (gifImage.gifImageWidth * 1);
1024										break;
1025								}
1026
1027								if (ptr_max_page < ptrLast) { ptrLast = ptr_max_page; }
1028
1029								for ( ; ptrInterlaced < ptrLast ; ptrInterlaced += gifImage.gifImageWidth) {
1030									ptrInterlaced[0] = gifCurColorTable[gifDictionary[gifCodeTemp].output];
1031								}
1032							}
1033
1034							ptr++;
1035							ptrPos++;
1036
1037							if (ptr == ptr_max_line) {
1038								//change the line
1039								ptrPos=0;
1040
1041								// draw surrounding pixels?
1042								if (gifInterlaceState < 3) {
1043									uint* ptrInterlaced = ptr + gifImage.gifImageWidth;
1044
1045									uint* ptrLast = void;
1046
1047									switch (gifInterlaceState) {
1048										case 0:
1049											ptrLast = ptrInterlaced + (gifImage.gifImageWidth * 7);
1050											break;
1051										case 1:
1052											ptrLast = ptrInterlaced + (gifImage.gifImageWidth * 3);
1053											break;
1054										case 2:
1055										default:
1056											ptrLast = ptrInterlaced + (gifImage.gifImageWidth * 1);
1057											break;
1058									}
1059
1060									if (ptr_max_page < ptrLast) { ptrLast = ptr_max_page; }
1061
1062									for ( ; ptrInterlaced < ptrLast ; ptrInterlaced += gifImage.gifImageWidth) {
1063										ptrInterlaced[0] = gifCurColorTable[gifDictionary[gifCodeTemp].output];
1064									}
1065								}
1066
1067								ptrLine++;
1068								_interlaceIncrement();
1069							}
1070
1071							for (q=0; q<=gifDictionary[lzw_curEntry].hops; q++) {
1072								ptr[0] = gifUncompressed[q];
1073
1074								// draw surrounding pixels?
1075								if (gifInterlaceState < 3) {
1076									uint* ptrInterlaced = ptr + gifImage.gifImageWidth;
1077
1078									uint* ptrLast = void;
1079
1080									switch (gifInterlaceState) {
1081										case 0:
1082											ptrLast = ptrInterlaced + (gifImage.gifImageWidth * 7);
1083											break;
1084										case 1:
1085											ptrLast = ptrInterlaced + (gifImage.gifImageWidth * 3);
1086											break;
1087										case 2:
1088										default:
1089											ptrLast = ptrInterlaced + (gifImage.gifImageWidth * 1);
1090											break;
1091									}
1092
1093									if (ptr_max_page < ptrLast) { ptrLast = ptr_max_page; }
1094
1095									for ( ; ptrInterlaced < ptrLast ; ptrInterlaced += gifImage.gifImageWidth) {
1096										ptrInterlaced[0] = gifCurColorTable[gifDictionary[gifCodeTemp].output];
1097									}
1098								}
1099
1100								ptr++;
1101								ptrPos++;
1102
1103								if (ptr == ptr_max_line) {
1104									//change the line
1105									ptrPos=0;
1106									ptrLine++;
1107									_interlaceIncrement();
1108								}
1109							}
1110						}
1111						else
1112						{
1113							// PRINT OUT CODE FROM DICTIONARY
1114							gifCodeTemp = gifCode;
1115							q = gifDictionary[gifCodeTemp].hops;
1116
1117							for (;;) {
1118								gifUncompressed[q] = gifCurColorTable[gifDictionary[gifCodeTemp].output];
1119
1120								if (gifCodeTemp < gifClearCode) { break; }
1121
1122								gifCodeTemp = cast(ushort)gifDictionary[gifCodeTemp].code;
1123								q--;
1124							}
1125
1126							for (q=0; q<=gifDictionary[gifCode].hops; q++) {
1127								ptr[0] = gifUncompressed[q];
1128
1129								// draw surrounding pixels?
1130								if (gifInterlaceState < 3) {
1131									uint* ptrInterlaced = ptr + gifImage.gifImageWidth;
1132
1133									uint* ptrLast = void;
1134
1135									switch (gifInterlaceState) {
1136										case 0:
1137											ptrLast = ptrInterlaced + (gifImage.gifImageWidth * 7);
1138											break;
1139										case 1:
1140											ptrLast = ptrInterlaced + (gifImage.gifImageWidth * 3);
1141											break;
1142										case 2:
1143										default:
1144											ptrLast = ptrInterlaced + (gifImage.gifImageWidth * 1);
1145											break;
1146									}
1147
1148									if (ptr_max_page < ptrLast) { ptrLast = ptr_max_page; }
1149
1150									for ( ; ptrInterlaced < ptrLast ; ptrInterlaced += gifImage.gifImageWidth) {
1151										ptrInterlaced[0] = gifCurColorTable[gifDictionary[gifCodeTemp].output];
1152									}
1153								}
1154
1155								ptr++;
1156								ptrPos++;
1157
1158								if (ptr == ptr_max_line) {
1159									//change the line
1160									ptrPos = 0;
1161									ptrLine++;
1162									_interlaceIncrement();
1163								}
1164							}
1165						}
1166
1167						if (lzw_nextEntry < 4096) {
1168							// add this entry to the dictionary
1169							gifDictionary[lzw_nextEntry].code = cast(short)lzw_curEntry;
1170							gifDictionary[lzw_nextEntry].output = gifDictionary[gifCodeTemp].output;
1171							gifDictionary[lzw_nextEntry].hops = cast(short)(gifDictionary[lzw_curEntry].hops + 1);
1172
1173							lzw_nextEntry++;
1174
1175							lzw_curEntry = gifCode;
1176
1177							if (lzw_nextEntry != 4096) {
1178								if (lzw_nextEntry >= gifDictionarySize) {
1179									gifDictionarySize *= 2;
1180									gifCodeSize++;
1181
1182									//update mask array
1183									gifCurMaskArray = cast(ubyte*)&gifMasks[(gifCodeSize - 2) * 24];
1184
1185									gifMaskStart = gifCurMaskArray[gifCurMaskIndexComp];
1186									gifMaskIntermediate = gifCurMaskArray[gifCurMaskIndexComp+1];
1187									gifMaskEnd = gifCurMaskArray[gifCurMaskIndexComp+2];
1188								}
1189							}
1190						}
1191
1192						// READ CODE
1193
1194						break;
1195
1196					default: break;
1197					}
1198				}
1199
1200				continue;
1201
1202			default:
1203				break;
1204			}
1205			break;
1206		}
1207		return StreamData.Invalid;
1208	}
1209
1210	void _interlaceIncrement() {
1211		//ptr will be at the end of the current row
1212		//essentially in the beginning of the next row
1213
1214		//if it had gone through row 0, it will now be
1215		//in row 1, and as such, will only have to
1216		//increment n-1 rows, where n is the number of
1217		//rows that the current state is interlaced
1218
1219		switch (gifInterlaceState) {
1220		case 0:
1221		case 1:
1222			//increase 8 lines
1223			ptrLine += 7;
1224			ptr += (7 * gifImage.gifImageWidth);
1225			break;
1226		case 2:
1227			//increase 4 lines
1228			ptrLine += 3;
1229			ptr += (3 * gifImage.gifImageWidth);
1230			break;
1231		case 3:
1232			//increase 2 lines
1233			ptrLine ++;
1234			ptr += (gifImage.gifImageWidth);
1235			break;
1236		default:
1237			//eh?
1238			break;
1239		}
1240
1241		if (ptr >= ptr_max_page) {
1242			//we start over again
1243			gifInterlaceState++;
1244
1245			switch(gifInterlaceState) {
1246			case 1:
1247				//start at row 4
1248				ptrLine = 4;
1249				ptr = ptr_start + (gifImage.gifImageWidth * 4);
1250				break;
1251			case 2:
1252				//start at row 2
1253				ptrLine = 2;
1254				ptr = ptr_start + (gifImage.gifImageWidth * 2);
1255				break;
1256			case 3:
1257				//start at row 1
1258				ptrLine = 1;
1259				ptr = ptr_start + (gifImage.gifImageWidth);
1260				break;
1261			default:
1262				//eh?
1263				break;
1264			}
1265		}
1266		ptr_max_line = ptr + gifImage.gifImageWidth;
1267	}
1268
1269	uint gifIsFirst;
1270
1271	uint gifFirstTime;
1272	uint gifFirstClear;
1273	Color gifFirstClearColor;
1274
1275	uint gifHeadersLoaded;
1276
1277	ubyte gifMaskStart;
1278	ubyte gifMaskIntermediate;
1279	ubyte gifMaskEnd;
1280
1281	// starting index into the array
1282	ubyte* gifCurMaskArray;
1283	// current index within subsection
1284	uint gifCurMaskIndex;
1285	uint gifCurMaskIndexComp;
1286
1287	//the pointer to the current color table in use
1288	uint* gifCurColorTable;
1289
1290	uint gifUncompressed[4096];
1291
1292	uint gifCodeSize;
1293	uint gifStartCodeSize;
1294
1295	_djehuty_image_gif_header gifHeader;
1296	_djehuty_image_gif_logical_screen gifScreen;
1297	_djehuty_image_gif_image_descriptor gifImage;
1298
1299	_djehuty_image_gif_color gifGlobalColorTable[256];
1300	uint gifGlobalColorTableComputed[256];
1301	uint gifGlobalColorTableSize;
1302
1303	_djehuty_image_gif_color gifLocalColorTable[256];
1304	uint gifLocalColorTableComputed[256];
1305	uint gifLocalColorTableSize;
1306
1307	_djehuty_image_gif_graphics_extension gifGraphicControl;
1308
1309	_djehuty_image_gif_lzw_dictionary_entry gifDictionary[4096];
1310	uint gifDictionarySize;
1311
1312	uint gifVersion;
1313
1314	uint gifIsInterlaced;
1315
1316	uint gifBlockSize;
1317	uint gifLastBlockSize;
1318
1319	uint gifBlockCounter;
1320
1321	ushort gifCurCode;
1322	ushort gifClearCode;
1323	ushort gifEOICode;
1324
1325	ubyte gifImageData[259]; //256 of block, 3 more for extra padding
1326	ubyte gifImageLeftOver;
1327	uint gifBlockCount;
1328
1329	ubyte gifExtensionIntroducer;
1330	ubyte gifExtensionLabel;
1331
1332	int gifInterlaceState;
1333
1334	ushort lzw_nextEntry;
1335	ushort lzw_curEntry;
1336	ushort lzw_isFirstEntry;
1337
1338	uint* ptr_start;		//ptr of the first pixel
1339
1340	uint* ptr;			//current ptr in image data
1341	uint* ptr_max_line;	//ptr of the next line
1342	uint* ptr_max_page;	//ptr outside of image bounds
1343
1344	uint ptrLine;		//the current scan line of the image (y)
1345	uint ptrPos;		//the current pixel of the line (x)
1346}