/// <summary> /// when a stream encounters an image control block, control is given to this function /// to parse it /// </summary> private void parseGraphicControlBlock() { GIFImage image = new GIFImage(); images.Add(image); //the block size should be 0x04 byte blockSize = getByte(); if (blockSize == 0x04) { //several settings are stored as flags in a single byte byte flags = getByte(); //parse the disposal method image.disposalMethod = 0; if ((flags >> 3 & 1) == 1) { image.disposalMethod += 1; } if ((flags >> 4 & 1) == 1) { image.disposalMethod += 2; } if ((flags >> 5 & 1) == 1) { image.disposalMethod += 4; } //the user input flag image.userInput = (flags >> 6 & 1) == 1; //the transparency color flag image.transparencyColorFlag = (flags >> 7 & 1) == 1; //next we parse the delay, stored as 1/100th's of a second image.delay = getUI16(); //not completely sure how the transparency index works, although if the transparency color flag //is not set, then it should be skipped image.transparencyIndex = getByte(); //finally its the block terminator, should be 0x00 if (getByte() != 0x00) { //error handling } } else { //error handling } }
private void parseImageBlock() { GIFImage image; //image was added by graphic control extension if (images[images.Count() - 1].hasData == false) { image = images[images.Count() - 1]; } else { image = new GIFImage(); images.Add(image); } image.hasData = true; //get the left, top, width and height of the image image.left = getUI16(); image.top = getUI16(); image.width = getUI16(); image.height = getUI16(); //next is a bunch of flags used for the image byte flags = getByte(); bool hasLocalColorTable = (flags & 1) == 1; bool interlace = (flags >> 1 & 1) == 1; if (interlace) { //error handling } bool sort = (flags >> 2 & 1) == 1; //might not be needed Color[] localColorTable = null; //get the size of the local color table if (hasLocalColorTable) { byte tableExponent = 1; if ((flags >> 5 & 1) == 1) { tableExponent += 1; } if ((flags >> 6 & 1) == 1) { tableExponent += 2; } if ((flags >> 7 & 1) == 1) { tableExponent += 4; } uint localColorTableSize = (uint)Math.Pow(2, (double)tableExponent); //initialize the global color table localColorTable = new Color[localColorTableSize]; //now we can run through the entries for (int a = 0; a < localColorTableSize; a++) { localColorTable[a] = Color.FromArgb(getByte(), getByte(), getByte()); } } //decode the image and store the indices //Adapted from John Cristy's ImageMagick. int[] tableIndices = new int[width * height]; int MaxStackSize = 4096; //2^12 // LZW decoder working arrays int[] prefix; int[] suffix; int[] pixelStack; int NullCode = -1; int npix = image.width * image.height; int available; int clear; int code_mask; //applied to datum to only get the bits we want int code_size; int end_of_information; int in_code; int old_code; //stores the code that was read before the current one int bits; //number of bits read when reading the current code int code; //stores the current code int count; //stores the number of bytes left to be read in the current block int i; int datum; //stores the current code + a few extra bits (apply mask to get code) int data_size; //initial number of bits int first; //stores the first code in the block that was read (or the first code after a clear) int stackPointer; //stackPointer is used to refer to the top of the pixel stack int bi; //index of the current byte int pi; prefix = new int[MaxStackSize]; suffix = new int[MaxStackSize]; pixelStack = new int[MaxStackSize + 1]; data_size = getByte(); //get the initial number of bits clear = 1 << data_size; //same as 2^data_size end_of_information = clear + 1; available = clear + 2; //first free index in the dictionary alphabet + clear + end_of_information old_code = NullCode; code_size = data_size + 1; code_mask = (1 << code_size) - 1; //causes code_mask to be full of 1's. for (code = 0; code < clear; code++) { prefix[code] = 0; //initialize the dictionary with the alphabet. suffix[code] = code; } // Decode GIF pixel stream. datum = bits = count = first = stackPointer = pi = bi = 0; for (i = 0; i < npix;) { if (stackPointer == 0) { if (bits < code_size) { // Load bytes until there are enough bits for a code. if (count == 0) { // Read a new data block. count = readBlock(); if (count <= 0) { break; } bi = 0; } //needed for codes with more than 8 bits datum += ((int)(block[bi]) & 0xff) << bits; bits += 8; bi++; count--; continue; } //if we have reached here, datum contains enough bits to read the code // Get the next code. //apply the mask to datum to get only the code code = datum & code_mask; //remove the code from datum, leaving the bits that havent been read datum >>= code_size; //reflex this change in the current bit count bits -= code_size; // Interpret the code if ((code > available) || (code == end_of_information)) { break; } if (code == clear) { // Reset decoder. code_size = data_size + 1; code_mask = (1 << code_size) - 1; available = clear + 2; old_code = NullCode; continue; } //this is true when we are reading the first byte, or if a clear is encountered if (old_code == NullCode) { //get the data out of the dictionary, and add it to the pixel stack pixelStack[stackPointer++] = suffix[code]; //set the last code and first code to this code old_code = code; first = code; continue; } //if its not the first code, we have hit here in_code = code; //if this is true, we have found a code that isnt in the dictionary if (code == available) { pixelStack[stackPointer++] = first; code = old_code; } //this loop adds data to the pixel stack for multiple character dictionary entries while (code > clear) { pixelStack[stackPointer++] = suffix[code]; code = prefix[code]; } //get the least significant byte of the element in the dictionary and store it in first first = (suffix[code]) & 0xff; // Add a new string to the string table, if (available >= MaxStackSize) { break; } pixelStack[stackPointer++] = first; prefix[available] = old_code; suffix[available] = first; available++; if (((available & code_mask) == 0) && (available < MaxStackSize)) { code_size++; code_mask += available; } old_code = in_code; } // Pop a pixel off the pixel stack. stackPointer--; tableIndices[pi++] = pixelStack[stackPointer]; i++; } for (i = pi; i < npix; i++) { tableIndices[i] = 0; // clear missing pixels } //convert the table full of indices, to the table full of colours Color[] colorTableToUse = null; if (hasLocalColorTable) { colorTableToUse = localColorTable; } else if (GlobalColorTableIsUsed) { colorTableToUse = globalColorTable; } else { //error handling } image.pixels = new Color[npix]; for (int j = 0; j < npix; j++) { image.pixels[j] = colorTableToUse[tableIndices[j]]; } //finally we parse the termination block if (getByte() != 0x00) { throw new Exception("unexpected end of image block"); } }