int FindRoot(Node* tree, int length) { int i = length; while(GETN(tree, i).parnode != -1) { i = GETN(tree, i).parnode; } return i; }
static int UDFPartition( uint8_t *data, uint16_t *Flags, uint16_t *Number, char *Contents, uint32_t *Start, uint32_t *Length ) { *Flags = GETN2(20); *Number = GETN2(22); GETN(24, 32, Contents); *Start = GETN4(188); *Length = GETN4(192); return 0; }
static int UDFPartition( quint8 *data, quint16 *Flags, quint16 *Number, char *Contents, quint32 *Start, quint32 *Length ) { *Flags = GETN2(20); *Number = GETN2(22); GETN(24, 32, Contents); *Start = GETN4(188); *Length = GETN4(192); return 0; }
// post order // This is a minor version of Boxer function used for packing void MiniBoxer(Node* tree, int child, int parent) { if(GETN(tree, child).cutline == 'V') { GETN(tree, child).width = SUM(RIGHTC(tree, child).width, LEFTC(tree, child).width); GETN(tree, child).height = MAX(RIGHTC(tree, child).height, LEFTC(tree, child).height); } else { GETN(tree, child).height = SUM(RIGHTC(tree, child).height, LEFTC(tree, child).height); GETN(tree, child).width = MAX(RIGHTC(tree, child).width, LEFTC(tree, child).width); } if(GETN(tree, parent).cutline == 'V') { GETN(tree, parent).width = SUM(RIGHTC(tree, parent).width, LEFTC(tree, parent).width); GETN(tree, parent).height = MAX(RIGHTC(tree, parent).height, LEFTC(tree, parent).height); } else { GETN(tree, parent).height = SUM(RIGHTC(tree, parent).height, LEFTC(tree, parent).height); GETN(tree, parent).width = MAX(RIGHTC(tree, parent).width, LEFTC(tree, parent).width); } }
// Set min height and width of Boxes void Boxer(Node* tree, int n) { if(GETN(tree, n).cutline == '-') { return; } Boxer(tree, GETN(tree, n).left); Boxer(tree, GETN(tree, n).right); if(GETN(tree, n).cutline == 'V') { GETN(tree, n).width = SUM(RIGHTC(tree, n).width, LEFTC(tree, n).width); GETN(tree, n).height = MAX(RIGHTC(tree, n).height, LEFTC(tree, n).height); } else { GETN(tree, n).height = SUM(RIGHTC(tree, n).height, LEFTC(tree, n).height); GETN(tree, n).width = MAX(RIGHTC(tree, n).width, LEFTC(tree, n).width); } return; }
int main(int argc, char** argv) { if(argc < 3) { printf("Not enough arguments.\n"); return EXIT_FAILURE; } int numrecs; int length; Node* tree = LoadFile(argv[1], &length, &numrecs); if(tree == NULL) { return EXIT_FAILURE; } clock_t pack_start = clock(); int root = FindRoot(tree, length); Pack(tree, root); clock_t pack_end = clock(); double packtime = (double) (pack_end - pack_start) / CLOCKS_PER_SEC; printf("Width: %le\n", GETN(tree, root).width); printf("Height: %le\n\n", GETN(tree, root).height); printf("X-coordinate: %le\n", GETN(tree, numrecs).xcoord); printf("Y-coordinate: %le\n\n", GETN(tree, numrecs).ycoord); printf("Elapsed time: %le\n", packtime); printf("\n"); Box bests = { GETN(tree, root).width, GETN(tree, root).height }; clock_t reroot_start = clock(); // void ReRoot(Node* tree, int root, Box* min, int nogo_root) ReRoot(tree, root, &bests, GETN(tree, root).right); // deal with root's left child ReRoot(tree, root, &bests, GETN(tree, root).left); // deal with root's right child clock_t reroot_end = clock(); double reroottime = (double) (reroot_end - reroot_start) / CLOCKS_PER_SEC; // Re Rooting printf("Best width: %le\n", bests.width); printf("Best height: %le\n\n", bests.height); printf("Elapsed time for re-rooting: %le\n", reroottime); int error_code = SaveFile(argv[2], tree, numrecs); return error_code; }
PRStatus gif_write(gif_struct *gs, const PRUint8 *buf, PRUint32 len) { if (!gs) return PR_FAILURE; /* If we fail, some upstream data provider ignored the zero return value from il_gif_write_ready() which says not to send any more data to this stream until the delay timeout fires. */ if ((len != 0) && (gs->gathered >= MAX_READ_AHEAD)) return PR_FAILURE; const PRUint8 *q, *p = buf, *ep = buf + len; q = nsnull; /* Initialize to shut up gcc warnings */ while (p <= ep) { switch (gs->state) { case gif_lzw: if (do_lzw(gs, q) < 0) { gs->state = gif_error; break; } GETN(1, gif_sub_block); break; case gif_lzw_start: { /* Initialize LZW parser/decoder */ gs->datasize = *q; if (gs->datasize >= MAX_LZW_BITS) { gs->state = gif_error; break; } gs->clear_code = 1 << gs->datasize; gs->avail = gs->clear_code + 2; gs->oldcode = -1; gs->codesize = gs->datasize + 1; gs->codemask = (1 << gs->codesize) - 1; gs->datum = gs->bits = 0; if (gs->clear_code >= MAX_BITS) { gs->state = gif_error; break; } /* init the tables */ for (int i = 0; i < gs->clear_code; i++) gs->suffix[i] = i; gs->stackp = gs->stack; GETN(1, gif_sub_block); } break; /* We're positioned at the very start of the file. */ case gif_init: { GETN(3, gif_type); break; } /* All GIF files begin with "GIF87a" or "GIF89a" */ case gif_type: { if (strncmp((char*)q, "GIF", 3)) { gs->state = gif_error; break; } GETN(3, gif_version); } break; case gif_version: { if (!strncmp((char*)q, "89a", 3)) { gs->version = 89; } else if (!strncmp((char*)q, "87a", 3)) { gs->version = 87; } else { gs->state = gif_error; break; } GETN(7, gif_global_header); } break; case gif_global_header: { /* This is the height and width of the "screen" or * frame into which images are rendered. The * individual images can be smaller than the * screen size and located with an origin anywhere * within the screen. */ gs->screen_width = GETINT16(q); gs->screen_height = GETINT16(q + 2); gs->screen_bgcolor = q[5]; gs->global_colormap_size = 2<<(q[4]&0x07); // XXX make callback nsGIFDecoder2::BeginGIF( gs->clientptr, gs->screen_width, gs->screen_height, gs->screen_bgcolor); if (q[4] & 0x80) /* global map */ /* 3 bytes for each entry in the global colormap */ GETN(gs->global_colormap_size*3, gif_global_colormap); else GETN(1, gif_image_start); // q[6] = Pixel Aspect Ratio // Not used // float aspect = (float)((q[6] + 15) / 64.0); } break; case gif_global_colormap: { memcpy(gs->global_colormap, q, 3 * gs->global_colormap_size); GETN(1, gif_image_start); } break; case gif_image_start: { if (*q == ';') { /* terminator */ gs->state = gif_done; break; } if (*q == '!') { /* extension */ GETN(2, gif_extension); break; } /* If we get anything other than ',' (image separator), '!' * (extension), or ';' (trailer), there is extraneous data * between blocks. The GIF87a spec tells us to keep reading * until we find an image separator, but GIF89a says such * a file is corrupt. We follow GIF89a and bail out. */ if (*q != ',') { if (gs->images_decoded > 0) { /* The file is corrupt, but one or more images have * been decoded correctly. In this case, we proceed * as if the file were correctly terminated and set * the state to gif_done, so the GIF will display. */ gs->state = gif_done; } else { /* No images decoded, there is nothing to display. */ gs->state = gif_error; } break; } else GETN(9, gif_image_header); } break; case gif_extension: { int len = gs->count = q[1]; gstate es = gif_skip_block; switch (*q) { case 0xf9: es = gif_control_extension; break; case 0x01: // ignoring plain text extension break; case 0xff: es = gif_application_extension; break; case 0xfe: es = gif_consume_comment; break; } if (len) GETN(len, es); else GETN(1, gif_image_start); } break; case gif_consume_block: if (!*q) GETN(1, gif_image_start); else GETN(*q, gif_skip_block); break; case gif_skip_block: GETN(1, gif_consume_block); break; case gif_control_extension: { if (*q & 0x1) { gs->tpixel = q[3]; gs->is_transparent = PR_TRUE; } else { gs->is_transparent = PR_FALSE; // ignoring gfx control extension } gs->disposal_method = (gdispose)(((*q) >> 2) & 0x7); // Some specs say 3rd bit (value 4), other specs say value 3 // Let's choose 3 (the more popular) if (gs->disposal_method == 4) gs->disposal_method = (gdispose)3; gs->delay_time = GETINT16(q + 1) * 10; GETN(1, gif_consume_block); } break; case gif_comment_extension: { gs->count = *q; if (gs->count) GETN(gs->count, gif_consume_comment); else GETN(1, gif_image_start); } break; case gif_consume_comment: GETN(1, gif_comment_extension); break; case gif_application_extension: /* Check for netscape application extension */ if (!strncmp((char*)q, "NETSCAPE2.0", 11) || !strncmp((char*)q, "ANIMEXTS1.0", 11)) GETN(1, gif_netscape_extension_block); else GETN(1, gif_consume_block); break; /* Netscape-specific GIF extension: animation looping */ case gif_netscape_extension_block: if (*q) GETN(*q, gif_consume_netscape_extension); else GETN(1, gif_image_start); break; /* Parse netscape-specific application extensions */ case gif_consume_netscape_extension: { int netscape_extension = q[0] & 7; /* Loop entire animation specified # of times. Only read the loop count during the first iteration. */ if (netscape_extension == 1) { gs->loop_count = GETINT16(q + 1); /* Zero loop count is infinite animation loop request */ if (gs->loop_count == 0) gs->loop_count = -1; GETN(1, gif_netscape_extension_block); } /* Wait for specified # of bytes to enter buffer */ else if (netscape_extension == 2) { // Don't do this, this extension doesn't exist (isn't used at all) // and doesn't do anything, as our streaming/buffering takes care of it all... // See: http://semmix.pl/color/exgraf/eeg24.htm GETN(1, gif_netscape_extension_block); } else gs->state = gif_error; // 0,3-7 are yet to be defined netscape // extension codes break; } case gif_image_header: { PRUintn height, width; /* Get image offsets, with respect to the screen origin */ gs->x_offset = GETINT16(q); gs->y_offset = GETINT16(q + 2); /* Get image width and height. */ width = GETINT16(q + 4); height = GETINT16(q + 6); /* Work around broken GIF files where the logical screen * size has weird width or height. We assume that GIF87a * files don't contain animations. */ if ((gs->images_decoded == 0) && ((gs->screen_height < height) || (gs->screen_width < width) || (gs->version == 87))) { gs->screen_height = height; gs->screen_width = width; gs->x_offset = 0; gs->y_offset = 0; nsGIFDecoder2::BeginGIF(gs->clientptr, gs->screen_width, gs->screen_height, gs->screen_bgcolor); } /* Work around more broken GIF files that have zero image width or height */ if (!height || !width) { height = gs->screen_height; width = gs->screen_width; if (!height || !width) { gs->state = gif_error; break; } } gs->height = height; gs->width = width; nsGIFDecoder2::BeginImageFrame(gs->clientptr, gs->images_decoded + 1, /* Frame number, 1-n */ gs->x_offset, /* X offset in logical screen */ gs->y_offset, /* Y offset in logical screen */ width, height); /* This case will never be taken if this is the first image */ /* being decoded. If any of the later images are larger */ /* than the screen size, we need to reallocate buffers. */ if (gs->screen_width < width) { /* XXX Deviant! */ gs->rowbuf = (PRUint8*)PR_REALLOC(gs->rowbuf, width); if (!gs->rowbuf) { gs->state = gif_oom; break; } gs->screen_width = width; if (gs->screen_height < gs->height) gs->screen_height = gs->height; } else { if (!gs->rowbuf) gs->rowbuf = (PRUint8*)PR_MALLOC(gs->screen_width); } if (!gs->rowbuf) { gs->state = gif_oom; break; } if (q[8] & 0x40) { gs->interlaced = PR_TRUE; gs->ipass = 1; } else { gs->interlaced = PR_FALSE; gs->ipass = 0; } if (gs->images_decoded == 0) { gs->progressive_display = PR_TRUE; } else { /* Overlaying interlaced, transparent GIFs over existing image data using the Haeberli display hack requires saving the underlying image in order to avoid jaggies at the transparency edges. We are unprepared to deal with that, so don't display such images progressively */ gs->progressive_display = PR_FALSE; } /* Clear state from last image */ gs->irow = 0; gs->rows_remaining = gs->height; gs->rowend = gs->rowbuf + gs->width; gs->rowp = gs->rowbuf; /* bits per pixel is 1<<((q[8]&0x07) + 1); */ if (q[8] & 0x80) /* has a local colormap? */ { int num_colors = 2 << (q[8] & 0x7); // If current local_colormap is not big enough, force reallocation if (num_colors > gs->local_colormap_size) PR_FREEIF(gs->local_colormap); gs->local_colormap_size = num_colors; /* Switch to the new local palette after it loads */ gs->is_local_colormap_defined = PR_TRUE; GETN(gs->local_colormap_size * 3, gif_image_colormap); } else { /* Switch back to the global palette */ gs->is_local_colormap_defined = PR_FALSE; GETN(1, gif_lzw_start); } } break; case gif_image_colormap: { PRUint8 *map = gs->local_colormap; if (!map) { map = gs->local_colormap = (PRUint8*)PR_MALLOC(3 * gs->local_colormap_size); if (!map) { gs->state = gif_oom; break; } } memcpy(map, q, 3 * gs->local_colormap_size); GETN(1, gif_lzw_start); } break; case gif_sub_block: { if ((gs->count = *q) != 0) /* Still working on the same image: Process next LZW data block */ { /* Make sure there are still rows left. If the GIF data */ /* is corrupt, we may not get an explicit terminator. */ if (gs->rows_remaining == 0) { /* This is an illegal GIF, but we remain tolerant. */ #ifdef DONT_TOLERATE_BROKEN_GIFS gs->state = gif_error; break; #else GETN(1, gif_sub_block); #endif } GETN(gs->count, gif_lzw); } else /* See if there are any more images in this sequence. */ { gs->images_decoded++; nsGIFDecoder2::EndImageFrame(gs->clientptr, gs->images_decoded, gs->delay_time); /* Clear state from this image */ gs->is_transparent = PR_FALSE; /* An image can specify a delay time before which to display subsequent images. */ if (gs->delay_time < MINIMUM_DELAY_TIME) gs->delay_time = MINIMUM_DELAY_TIME; GETN(1, gif_image_start); } } break; case gif_done: nsGIFDecoder2::EndGIF(gs->clientptr, gs->loop_count); return PR_SUCCESS; break; case gif_delay: case gif_gather: { PRInt32 gather_remaining; PRInt32 request_size = gs->gather_request_size; { gather_remaining = request_size - gs->gathered; /* Do we already have enough data in the accumulation buffer to satisfy the request ? (This can happen after we transition from the gif_delay state.) */ if (gather_remaining <= 0) { gs->gathered -= request_size; q = gs->gather_head; gs->gather_head += request_size; gs->state = gs->post_gather_state; break; } /* Shift remaining data to the head of the buffer */ if (gs->gathered && (gs->gather_head != gs->hold)) { memmove(gs->hold, gs->gather_head, gs->gathered); gs->gather_head = gs->hold; } /* If we add the data just handed to us by the netlib to what we've already gathered, is there enough to satisfy the current request ? */ if ((ep - p) >= gather_remaining) { if (gs->gathered) { /* finish a prior gather */ char *hold = (char*)gs->hold; BlockAllocCat(hold, gs->gathered, (char*)p, gather_remaining); gs->hold = (PRUint8*)hold; q = gs->gather_head = gs->hold; gs->gathered = 0; } else q = p; p += gather_remaining; gs->state = gs->post_gather_state; } else { char *hold = (char*)gs->hold; BlockAllocCat(hold, gs->gathered, (char*)p, ep - p); gs->hold = (PRUint8*)hold; gs->gather_head = gs->hold; gs->gathered += ep-p; return PR_SUCCESS; } } } break; // Handle out of memory errors case gif_oom: return PR_FAILURE; // Handle general errors case gif_error: nsGIFDecoder2::EndGIF(gs->clientptr, gs->loop_count); return PR_SUCCESS; case gif_stop_animating: return PR_SUCCESS; // We shouldn't ever get here. default: break; } } return PR_SUCCESS; }
// Parse incoming GIF data stream into internal data structures. SkCodec::Result SkGifImageReader::parse(SkGifImageReader::SkGIFParseQuery query) { if (m_parseCompleted) { return SkCodec::kSuccess; } if (SkGIFLoopCountQuery == query && m_loopCount != cLoopCountNotSeen) { // Loop count has already been parsed. return SkCodec::kSuccess; } // SkGIFSizeQuery and SkGIFFrameCountQuery are negative, so this is only meaningful when >= 0. const int lastFrameToParse = (int) query; if (lastFrameToParse >= 0 && (int) m_frames.size() > lastFrameToParse && m_frames[lastFrameToParse]->isComplete()) { // We have already parsed this frame. return SkCodec::kSuccess; } while (true) { if (!m_streamBuffer.buffer(m_bytesToConsume)) { // The stream does not yet have enough data. return SkCodec::kIncompleteInput; } switch (m_state) { case SkGIFLZW: { SkASSERT(!m_frames.empty()); auto* frame = m_frames.back().get(); frame->addLzwBlock(m_streamBuffer.markPosition(), m_bytesToConsume); GETN(1, SkGIFSubBlock); break; } case SkGIFLZWStart: { SkASSERT(!m_frames.empty()); auto* currentFrame = m_frames.back().get(); currentFrame->setDataSize(this->getOneByte()); GETN(1, SkGIFSubBlock); break; } case SkGIFType: { const char* currentComponent = m_streamBuffer.get(); // All GIF files begin with "GIF87a" or "GIF89a". if (!memcmp(currentComponent, "GIF89a", 6)) m_version = 89; else if (!memcmp(currentComponent, "GIF87a", 6)) m_version = 87; else { // This prevents attempting to continue reading this invalid stream. GETN(0, SkGIFDone); return SkCodec::kInvalidInput; } GETN(7, SkGIFGlobalHeader); break; } case SkGIFGlobalHeader: { const unsigned char* currentComponent = reinterpret_cast<const unsigned char*>(m_streamBuffer.get()); // This is the height and width of the "screen" or frame into which // images are rendered. The individual images can be smaller than // the screen size and located with an origin anywhere within the // screen. // Note that we don't inform the client of the size yet, as it might // change after we read the first frame's image header. fScreenWidth = GETINT16(currentComponent); fScreenHeight = GETINT16(currentComponent + 2); const int globalColorMapColors = 2 << (currentComponent[4] & 0x07); if ((currentComponent[4] & 0x80) && globalColorMapColors > 0) { /* global map */ m_globalColorMap.setNumColors(globalColorMapColors); GETN(SK_BYTES_PER_COLORMAP_ENTRY * globalColorMapColors, SkGIFGlobalColormap); break; } GETN(1, SkGIFImageStart); break; } case SkGIFGlobalColormap: { m_globalColorMap.setTablePosition(m_streamBuffer.markPosition()); GETN(1, SkGIFImageStart); break; } case SkGIFImageStart: { const char currentComponent = m_streamBuffer.get()[0]; if (currentComponent == '!') { // extension. GETN(2, SkGIFExtension); break; } if (currentComponent == ',') { // image separator. GETN(9, SkGIFImageHeader); break; } // If we get anything other than ',' (image separator), '!' // (extension), or ';' (trailer), there is extraneous data // between blocks. The GIF87a spec tells us to keep reading // until we find an image separator, but GIF89a says such // a file is corrupt. We follow Mozilla's implementation and // proceed as if the file were correctly terminated, so the // GIF will display. GETN(0, SkGIFDone); break; } case SkGIFExtension: { const unsigned char* currentComponent = reinterpret_cast<const unsigned char*>(m_streamBuffer.get()); size_t bytesInBlock = currentComponent[1]; SkGIFState exceptionState = SkGIFSkipBlock; switch (*currentComponent) { case 0xf9: // The GIF spec mandates that the GIFControlExtension header block length is 4 bytes, exceptionState = SkGIFControlExtension; // and the parser for this block reads 4 bytes, so we must enforce that the buffer // contains at least this many bytes. If the GIF specifies a different length, we // allow that, so long as it's larger; the additional data will simply be ignored. bytesInBlock = std::max(bytesInBlock, static_cast<size_t>(4)); break; // The GIF spec also specifies the lengths of the following two extensions' headers // (as 12 and 11 bytes, respectively). Because we ignore the plain text extension entirely // and sanity-check the actual length of the application extension header before reading it, // we allow GIFs to deviate from these values in either direction. This is important for // real-world compatibility, as GIFs in the wild exist with application extension headers // that are both shorter and longer than 11 bytes. case 0x01: // ignoring plain text extension break; case 0xff: exceptionState = SkGIFApplicationExtension; break; case 0xfe: exceptionState = SkGIFConsumeComment; break; } if (bytesInBlock) GETN(bytesInBlock, exceptionState); else GETN(1, SkGIFImageStart); break; } case SkGIFConsumeBlock: { const unsigned char currentComponent = this->getOneByte(); if (!currentComponent) GETN(1, SkGIFImageStart); else GETN(currentComponent, SkGIFSkipBlock); break; } case SkGIFSkipBlock: { GETN(1, SkGIFConsumeBlock); break; } case SkGIFControlExtension: { const unsigned char* currentComponent = reinterpret_cast<const unsigned char*>(m_streamBuffer.get()); addFrameIfNecessary(); SkGIFFrameContext* currentFrame = m_frames.back().get(); if (*currentComponent & 0x1) currentFrame->setTransparentPixel(currentComponent[3]); // We ignore the "user input" bit. // NOTE: This relies on the values in the FrameDisposalMethod enum // matching those in the GIF spec! int rawDisposalMethod = ((*currentComponent) >> 2) & 0x7; switch (rawDisposalMethod) { case 1: case 2: case 3: currentFrame->setDisposalMethod((SkCodecAnimation::DisposalMethod) rawDisposalMethod); break; case 4: // Some specs say that disposal method 3 is "overwrite previous", others that setting // the third bit of the field (i.e. method 4) is. We map both to the same value. currentFrame->setDisposalMethod(SkCodecAnimation::DisposalMethod::kRestorePrevious); break; default: // Other values use the default. currentFrame->setDisposalMethod(SkCodecAnimation::DisposalMethod::kKeep); break; } currentFrame->setDuration(GETINT16(currentComponent + 1) * 10); GETN(1, SkGIFConsumeBlock); break; } case SkGIFCommentExtension: { const unsigned char currentComponent = this->getOneByte(); if (currentComponent) GETN(currentComponent, SkGIFConsumeComment); else GETN(1, SkGIFImageStart); break; } case SkGIFConsumeComment: { GETN(1, SkGIFCommentExtension); break; } case SkGIFApplicationExtension: { // Check for netscape application extension. if (m_bytesToConsume == 11) { const unsigned char* currentComponent = reinterpret_cast<const unsigned char*>(m_streamBuffer.get()); if (!memcmp(currentComponent, "NETSCAPE2.0", 11) || !memcmp(currentComponent, "ANIMEXTS1.0", 11)) GETN(1, SkGIFNetscapeExtensionBlock); } if (m_state != SkGIFNetscapeExtensionBlock) GETN(1, SkGIFConsumeBlock); break; } // Netscape-specific GIF extension: animation looping. case SkGIFNetscapeExtensionBlock: { const int currentComponent = this->getOneByte(); // SkGIFConsumeNetscapeExtension always reads 3 bytes from the stream; we should at least wait for this amount. if (currentComponent) GETN(std::max(3, currentComponent), SkGIFConsumeNetscapeExtension); else GETN(1, SkGIFImageStart); break; } // Parse netscape-specific application extensions case SkGIFConsumeNetscapeExtension: { const unsigned char* currentComponent = reinterpret_cast<const unsigned char*>(m_streamBuffer.get()); int netscapeExtension = currentComponent[0] & 7; // Loop entire animation specified # of times. Only read the loop count during the first iteration. if (netscapeExtension == 1) { m_loopCount = GETINT16(currentComponent + 1); // Zero loop count is infinite animation loop request. if (!m_loopCount) m_loopCount = SkCodec::kRepetitionCountInfinite; GETN(1, SkGIFNetscapeExtensionBlock); if (SkGIFLoopCountQuery == query) { m_streamBuffer.flush(); return SkCodec::kSuccess; } } else if (netscapeExtension == 2) { // Wait for specified # of bytes to enter buffer. // Don't do this, this extension doesn't exist (isn't used at all) // and doesn't do anything, as our streaming/buffering takes care of it all... // See: http://semmix.pl/color/exgraf/eeg24.htm GETN(1, SkGIFNetscapeExtensionBlock); } else { // 0,3-7 are yet to be defined netscape extension codes // This prevents attempting to continue reading this invalid stream. GETN(0, SkGIFDone); return SkCodec::kInvalidInput; } break; } case SkGIFImageHeader: { int height, width, xOffset, yOffset; const unsigned char* currentComponent = reinterpret_cast<const unsigned char*>(m_streamBuffer.get()); /* Get image offsets, with respect to the screen origin */ xOffset = GETINT16(currentComponent); yOffset = GETINT16(currentComponent + 2); /* Get image width and height. */ width = GETINT16(currentComponent + 4); height = GETINT16(currentComponent + 6); // Some GIF files have frames that don't fit in the specified // overall image size. For the first frame, we can simply enlarge // the image size to allow the frame to be visible. We can't do // this on subsequent frames because the rest of the decoding // infrastructure assumes the image size won't change as we // continue decoding, so any subsequent frames that are even // larger will be cropped. // Luckily, handling just the first frame is sufficient to deal // with most cases, e.g. ones where the image size is erroneously // set to zero, since usually the first frame completely fills // the image. if (currentFrameIsFirstFrame()) { fScreenHeight = std::max(fScreenHeight, yOffset + height); fScreenWidth = std::max(fScreenWidth, xOffset + width); } // NOTE: Chromium placed this block after setHeaderDefined, down // below we returned true when asked for the size. So Chromium // created an image which would fail. Is this the correct behavior? // We choose to return false early, so we will not create an // SkCodec. // Work around more broken GIF files that have zero image width or // height. if (!height || !width) { height = fScreenHeight; width = fScreenWidth; if (!height || !width) { // This prevents attempting to continue reading this invalid stream. GETN(0, SkGIFDone); return SkCodec::kInvalidInput; } } const bool isLocalColormapDefined = SkToBool(currentComponent[8] & 0x80); // The three low-order bits of currentComponent[8] specify the bits per pixel. const int numColors = 2 << (currentComponent[8] & 0x7); if (currentFrameIsFirstFrame()) { const int transPix = m_frames.empty() ? SkGIFColorMap::kNotFound : m_frames[0]->transparentPixel(); if (this->hasTransparency(transPix, isLocalColormapDefined, numColors)) { m_firstFrameHasAlpha = true; } else { const bool frameIsSubset = xOffset > 0 || yOffset > 0 || width < fScreenWidth || height < fScreenHeight; m_firstFrameHasAlpha = frameIsSubset; } } addFrameIfNecessary(); SkGIFFrameContext* currentFrame = m_frames.back().get(); currentFrame->setHeaderDefined(); if (query == SkGIFSizeQuery) { // The decoder needs to stop, so we return here, before // flushing the buffer. Next time through, we'll be in the same // state, requiring the same amount in the buffer. return SkCodec::kSuccess; } currentFrame->setXYWH(xOffset, yOffset, width, height); currentFrame->setInterlaced(SkToBool(currentComponent[8] & 0x40)); // Overlaying interlaced, transparent GIFs over // existing image data using the Haeberli display hack // requires saving the underlying image in order to // avoid jaggies at the transparency edges. We are // unprepared to deal with that, so don't display such // images progressively. Which means only the first // frame can be progressively displayed. // FIXME: It is possible that a non-transparent frame // can be interlaced and progressively displayed. currentFrame->setProgressiveDisplay(currentFrameIsFirstFrame()); if (isLocalColormapDefined) { currentFrame->localColorMap().setNumColors(numColors); GETN(SK_BYTES_PER_COLORMAP_ENTRY * numColors, SkGIFImageColormap); break; } setAlphaAndRequiredFrame(currentFrame); GETN(1, SkGIFLZWStart); break; } case SkGIFImageColormap: { SkASSERT(!m_frames.empty()); auto* currentFrame = m_frames.back().get(); auto& cmap = currentFrame->localColorMap(); cmap.setTablePosition(m_streamBuffer.markPosition()); setAlphaAndRequiredFrame(currentFrame); GETN(1, SkGIFLZWStart); break; } case SkGIFSubBlock: { const size_t bytesInBlock = this->getOneByte(); if (bytesInBlock) GETN(bytesInBlock, SkGIFLZW); else { // Finished parsing one frame; Process next frame. SkASSERT(!m_frames.empty()); // Note that some broken GIF files do not have enough LZW blocks to fully // decode all rows but we treat it as frame complete. m_frames.back()->setComplete(); GETN(1, SkGIFImageStart); if (lastFrameToParse >= 0 && (int) m_frames.size() > lastFrameToParse) { m_streamBuffer.flush(); return SkCodec::kSuccess; } } break; } case SkGIFDone: { m_parseCompleted = true; return SkCodec::kSuccess; } default: // We shouldn't ever get here. // This prevents attempting to continue reading this invalid stream. GETN(0, SkGIFDone); return SkCodec::kInvalidInput; break; } // switch m_streamBuffer.flush(); } }
void nsGIFDecoder2::WriteInternal(const char *aBuffer, uint32_t aCount, DecodeStrategy) { NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!"); // These variables changed names, and renaming would make a much bigger patch :( const uint8_t *buf = (const uint8_t *)aBuffer; uint32_t len = aCount; const uint8_t *q = buf; // Add what we have sofar to the block // If previous call to me left something in the hold first complete current block // Or if we are filling the colormaps, first complete the colormap uint8_t* p = (mGIFStruct.state == gif_global_colormap) ? (uint8_t*)mGIFStruct.global_colormap : (mGIFStruct.state == gif_image_colormap) ? (uint8_t*)mColormap : (mGIFStruct.bytes_in_hold) ? mGIFStruct.hold : nullptr; if (len == 0 && buf == nullptr) { // We've just gotten the frame we asked for. Time to use the data we // stashed away. len = mGIFStruct.bytes_in_hold; q = buf = p; } else if (p) { // Add what we have sofar to the block uint32_t l = std::min(len, mGIFStruct.bytes_to_consume); memcpy(p+mGIFStruct.bytes_in_hold, buf, l); if (l < mGIFStruct.bytes_to_consume) { // Not enough in 'buf' to complete current block, get more mGIFStruct.bytes_in_hold += l; mGIFStruct.bytes_to_consume -= l; return; } // Point 'q' to complete block in hold (or in colormap) q = p; } // Invariant: // 'q' is start of current to be processed block (hold, colormap or buf) // 'bytes_to_consume' is number of bytes to consume from 'buf' // 'buf' points to the bytes to be consumed from the input buffer // 'len' is number of bytes left in input buffer from position 'buf'. // At entrance of the for loop will 'buf' will be moved 'bytes_to_consume' // to point to next buffer, 'len' is adjusted accordingly. // So that next round in for loop, q gets pointed to the next buffer. for (;len >= mGIFStruct.bytes_to_consume; q=buf, mGIFStruct.bytes_in_hold = 0) { // Eat the current block from the buffer, q keeps pointed at current block buf += mGIFStruct.bytes_to_consume; len -= mGIFStruct.bytes_to_consume; switch (mGIFStruct.state) { case gif_lzw: if (!DoLzw(q)) { mGIFStruct.state = gif_error; break; } GETN(1, gif_sub_block); break; case gif_lzw_start: { // Make sure the transparent pixel is transparent in the colormap if (mGIFStruct.is_transparent) { // Save old value so we can restore it later if (mColormap == mGIFStruct.global_colormap) mOldColor = mColormap[mGIFStruct.tpixel]; mColormap[mGIFStruct.tpixel] = 0; } /* Initialize LZW parser/decoder */ mGIFStruct.datasize = *q; const int clear_code = ClearCode(); if (mGIFStruct.datasize > MAX_LZW_BITS || clear_code >= MAX_BITS) { mGIFStruct.state = gif_error; break; } mGIFStruct.avail = clear_code + 2; mGIFStruct.oldcode = -1; mGIFStruct.codesize = mGIFStruct.datasize + 1; mGIFStruct.codemask = (1 << mGIFStruct.codesize) - 1; mGIFStruct.datum = mGIFStruct.bits = 0; /* init the tables */ for (int i = 0; i < clear_code; i++) mGIFStruct.suffix[i] = i; mGIFStruct.stackp = mGIFStruct.stack; GETN(1, gif_sub_block); } break; /* All GIF files begin with "GIF87a" or "GIF89a" */ case gif_type: if (!strncmp((char*)q, "GIF89a", 6)) { mGIFStruct.version = 89; } else if (!strncmp((char*)q, "GIF87a", 6)) { mGIFStruct.version = 87; } else { mGIFStruct.state = gif_error; break; } GETN(7, gif_global_header); break; case gif_global_header: /* This is the height and width of the "screen" or * frame into which images are rendered. The * individual images can be smaller than the * screen size and located with an origin anywhere * within the screen. */ mGIFStruct.screen_width = GETINT16(q); mGIFStruct.screen_height = GETINT16(q + 2); mGIFStruct.global_colormap_depth = (q[4]&0x07) + 1; if (IsSizeDecode()) { MOZ_ASSERT(!mGIFOpen, "Gif should not be open at this point"); PostSize(mGIFStruct.screen_width, mGIFStruct.screen_height); return; } // screen_bgcolor is not used //mGIFStruct.screen_bgcolor = q[5]; // q[6] = Pixel Aspect Ratio // Not used // float aspect = (float)((q[6] + 15) / 64.0); if (q[4] & 0x80) { /* global map */ // Get the global colormap const uint32_t size = (3 << mGIFStruct.global_colormap_depth); if (len < size) { // Use 'hold' pattern to get the global colormap GETN(size, gif_global_colormap); break; } // Copy everything, go to colormap state to do CMS correction memcpy(mGIFStruct.global_colormap, buf, size); buf += size; len -= size; GETN(0, gif_global_colormap); break; } GETN(1, gif_image_start); break; case gif_global_colormap: // Everything is already copied into global_colormap // Convert into Cairo colors including CMS transformation ConvertColormap(mGIFStruct.global_colormap, 1<<mGIFStruct.global_colormap_depth); GETN(1, gif_image_start); break; case gif_image_start: switch (*q) { case GIF_TRAILER: mGIFStruct.state = gif_done; break; case GIF_EXTENSION_INTRODUCER: GETN(2, gif_extension); break; case GIF_IMAGE_SEPARATOR: GETN(9, gif_image_header); break; default: /* If we get anything other than GIF_IMAGE_SEPARATOR, * GIF_EXTENSION_INTRODUCER, or GIF_TRAILER, there is extraneous data * between blocks. The GIF87a spec tells us to keep reading * until we find an image separator, but GIF89a says such * a file is corrupt. We follow GIF89a and bail out. */ if (mGIFStruct.images_decoded > 0) { /* The file is corrupt, but one or more images have * been decoded correctly. In this case, we proceed * as if the file were correctly terminated and set * the state to gif_done, so the GIF will display. */ mGIFStruct.state = gif_done; } else { /* No images decoded, there is nothing to display. */ mGIFStruct.state = gif_error; } } break; case gif_extension: mGIFStruct.bytes_to_consume = q[1]; if (mGIFStruct.bytes_to_consume) { switch (*q) { case GIF_GRAPHIC_CONTROL_LABEL: // The GIF spec mandates that the GIFControlExtension header block length is 4 bytes, // and the parser for this block reads 4 bytes, so we must enforce that the buffer // contains at least this many bytes. If the GIF specifies a different length, we // allow that, so long as it's larger; the additional data will simply be ignored. mGIFStruct.state = gif_control_extension; mGIFStruct.bytes_to_consume = std::max(mGIFStruct.bytes_to_consume, 4u); break; // The GIF spec also specifies the lengths of the following two extensions' headers // (as 12 and 11 bytes, respectively). Because we ignore the plain text extension entirely // and sanity-check the actual length of the application extension header before reading it, // we allow GIFs to deviate from these values in either direction. This is important for // real-world compatibility, as GIFs in the wild exist with application extension headers // that are both shorter and longer than 11 bytes. case GIF_APPLICATION_EXTENSION_LABEL: mGIFStruct.state = gif_application_extension; break; case GIF_PLAIN_TEXT_LABEL: mGIFStruct.state = gif_skip_block; break; case GIF_COMMENT_LABEL: mGIFStruct.state = gif_consume_comment; break; default: mGIFStruct.state = gif_skip_block; } } else { GETN(1, gif_image_start); } break; case gif_consume_block: if (!*q) GETN(1, gif_image_start); else GETN(*q, gif_skip_block); break; case gif_skip_block: GETN(1, gif_consume_block); break; case gif_control_extension: mGIFStruct.is_transparent = *q & 0x1; mGIFStruct.tpixel = q[3]; mGIFStruct.disposal_method = ((*q) >> 2) & 0x7; // Some specs say 3rd bit (value 4), other specs say value 3 // Let's choose 3 (the more popular) if (mGIFStruct.disposal_method == 4) mGIFStruct.disposal_method = 3; mGIFStruct.delay_time = GETINT16(q + 1) * 10; GETN(1, gif_consume_block); break; case gif_comment_extension: if (*q) GETN(*q, gif_consume_comment); else GETN(1, gif_image_start); break; case gif_consume_comment: GETN(1, gif_comment_extension); break; case gif_application_extension: /* Check for netscape application extension */ if (mGIFStruct.bytes_to_consume == 11 && (!strncmp((char*)q, "NETSCAPE2.0", 11) || !strncmp((char*)q, "ANIMEXTS1.0", 11))) GETN(1, gif_netscape_extension_block); else GETN(1, gif_consume_block); break; /* Netscape-specific GIF extension: animation looping */ case gif_netscape_extension_block: if (*q) // We might need to consume 3 bytes in // gif_consume_netscape_extension, so make sure we have at least that. GETN(std::max(3, static_cast<int>(*q)), gif_consume_netscape_extension); else GETN(1, gif_image_start); break; /* Parse netscape-specific application extensions */ case gif_consume_netscape_extension: switch (q[0] & 7) { case 1: /* Loop entire animation specified # of times. Only read the loop count during the first iteration. */ mGIFStruct.loop_count = GETINT16(q + 1); GETN(1, gif_netscape_extension_block); break; case 2: /* Wait for specified # of bytes to enter buffer */ // Don't do this, this extension doesn't exist (isn't used at all) // and doesn't do anything, as our streaming/buffering takes care of it all... // See: http://semmix.pl/color/exgraf/eeg24.htm GETN(1, gif_netscape_extension_block); break; default: // 0,3-7 are yet to be defined netscape extension codes mGIFStruct.state = gif_error; } break; case gif_image_header: { /* Get image offsets, with respect to the screen origin */ mGIFStruct.x_offset = GETINT16(q); mGIFStruct.y_offset = GETINT16(q + 2); /* Get image width and height. */ mGIFStruct.width = GETINT16(q + 4); mGIFStruct.height = GETINT16(q + 6); if (!mGIFStruct.images_decoded) { /* Work around broken GIF files where the logical screen * size has weird width or height. We assume that GIF87a * files don't contain animations. */ if ((mGIFStruct.screen_height < mGIFStruct.height) || (mGIFStruct.screen_width < mGIFStruct.width) || (mGIFStruct.version == 87)) { mGIFStruct.screen_height = mGIFStruct.height; mGIFStruct.screen_width = mGIFStruct.width; mGIFStruct.x_offset = 0; mGIFStruct.y_offset = 0; } // Create the image container with the right size. BeginGIF(); if (HasError()) { // Setting the size led to an error. mGIFStruct.state = gif_error; return; } // If we were doing a size decode, we're done if (IsSizeDecode()) return; } /* Work around more broken GIF files that have zero image width or height */ if (!mGIFStruct.height || !mGIFStruct.width) { mGIFStruct.height = mGIFStruct.screen_height; mGIFStruct.width = mGIFStruct.screen_width; if (!mGIFStruct.height || !mGIFStruct.width) { mGIFStruct.state = gif_error; break; } } /* Depth of colors is determined by colormap */ /* (q[8] & 0x80) indicates local colormap */ /* bits per pixel is (q[8]&0x07 + 1) when local colormap is set */ uint32_t depth = mGIFStruct.global_colormap_depth; if (q[8] & 0x80) depth = (q[8]&0x07) + 1; uint32_t realDepth = depth; while (mGIFStruct.tpixel >= (1 << realDepth) && (realDepth < 8)) { realDepth++; } // Mask to limit the color values within the colormap mColorMask = 0xFF >> (8 - realDepth); BeginImageFrame(realDepth); if (NeedsNewFrame()) { // We now need a new frame from the decoder framework. We leave all our // data in the buffer as if it wasn't consumed, copy to our hold and return // to the decoder framework. uint32_t size = len + mGIFStruct.bytes_to_consume + mGIFStruct.bytes_in_hold; if (size) { if (SetHold(q, mGIFStruct.bytes_to_consume + mGIFStruct.bytes_in_hold, buf, len)) { // Back into the decoder infrastructure so we can get called again. GETN(9, gif_image_header_continue); return; } } break; } else { // FALL THROUGH } } case gif_image_header_continue: { // While decoders can reuse frames, we unconditionally increment // mGIFStruct.images_decoded when we're done with a frame, so we both can // and need to zero out the colormap and image data after every new frame. memset(mImageData, 0, mImageDataLength); if (mColormap) { memset(mColormap, 0, mColormapSize); } if (!mGIFStruct.images_decoded) { // Send a onetime invalidation for the first frame if it has a y-axis offset. // Otherwise, the area may never be refreshed and the placeholder will remain // on the screen. (Bug 37589) if (mGIFStruct.y_offset > 0) { nsIntRect r(0, 0, mGIFStruct.screen_width, mGIFStruct.y_offset); PostInvalidation(r); } } if (q[8] & 0x40) { mGIFStruct.interlaced = true; mGIFStruct.ipass = 1; } else { mGIFStruct.interlaced = false; mGIFStruct.ipass = 0; } /* Only apply the Haeberli display hack on the first frame */ mGIFStruct.progressive_display = (mGIFStruct.images_decoded == 0); /* Clear state from last image */ mGIFStruct.irow = 0; mGIFStruct.rows_remaining = mGIFStruct.height; mGIFStruct.rowp = mImageData; /* Depth of colors is determined by colormap */ /* (q[8] & 0x80) indicates local colormap */ /* bits per pixel is (q[8]&0x07 + 1) when local colormap is set */ uint32_t depth = mGIFStruct.global_colormap_depth; if (q[8] & 0x80) depth = (q[8]&0x07) + 1; uint32_t realDepth = depth; while (mGIFStruct.tpixel >= (1 << realDepth) && (realDepth < 8)) { realDepth++; } if (q[8] & 0x80) /* has a local colormap? */ { mGIFStruct.local_colormap_size = 1 << depth; if (!mGIFStruct.images_decoded) { // First frame has local colormap, allocate space for it // as the image frame doesn't have its own palette mColormapSize = sizeof(uint32_t) << realDepth; if (!mGIFStruct.local_colormap) { mGIFStruct.local_colormap = (uint32_t*)moz_xmalloc(mColormapSize); } mColormap = mGIFStruct.local_colormap; } const uint32_t size = 3 << depth; if (mColormapSize > size) { // Clear the notfilled part of the colormap memset(((uint8_t*)mColormap) + size, 0, mColormapSize - size); } if (len < size) { // Use 'hold' pattern to get the image colormap GETN(size, gif_image_colormap); break; } // Copy everything, go to colormap state to do CMS correction memcpy(mColormap, buf, size); buf += size; len -= size; GETN(0, gif_image_colormap); break; } else { /* Switch back to the global palette */ if (mGIFStruct.images_decoded) { // Copy global colormap into the palette of current frame memcpy(mColormap, mGIFStruct.global_colormap, mColormapSize); } else { mColormap = mGIFStruct.global_colormap; } } GETN(1, gif_lzw_start); } break; case gif_image_colormap: // Everything is already copied into local_colormap // Convert into Cairo colors including CMS transformation ConvertColormap(mColormap, mGIFStruct.local_colormap_size); GETN(1, gif_lzw_start); break; case gif_sub_block: mGIFStruct.count = *q; if (mGIFStruct.count) { /* Still working on the same image: Process next LZW data block */ /* Make sure there are still rows left. If the GIF data */ /* is corrupt, we may not get an explicit terminator. */ if (!mGIFStruct.rows_remaining) { #ifdef DONT_TOLERATE_BROKEN_GIFS mGIFStruct.state = gif_error; break; #else /* This is an illegal GIF, but we remain tolerant. */ GETN(1, gif_sub_block); #endif if (mGIFStruct.count == GIF_TRAILER) { /* Found a terminator anyway, so consider the image done */ GETN(1, gif_done); break; } } GETN(mGIFStruct.count, gif_lzw); } else { /* See if there are any more images in this sequence. */ EndImageFrame(); GETN(1, gif_image_start); } break; case gif_done: MOZ_ASSERT(!IsSizeDecode(), "Size decodes shouldn't reach gif_done"); FinishInternal(); goto done; case gif_error: PostDataError(); return; // We shouldn't ever get here. default: break; } } // if an error state is set but no data remains, code flow reaches here if (mGIFStruct.state == gif_error) { PostDataError(); return; } // Copy the leftover into mGIFStruct.hold if (len) { // Add what we have sofar to the block if (mGIFStruct.state != gif_global_colormap && mGIFStruct.state != gif_image_colormap) { if (!SetHold(buf, len)) { PostDataError(); return; } } else { uint8_t* p = (mGIFStruct.state == gif_global_colormap) ? (uint8_t*)mGIFStruct.global_colormap : (uint8_t*)mColormap; memcpy(p, buf, len); mGIFStruct.bytes_in_hold = len; } mGIFStruct.bytes_to_consume -= len; } // We want to flush before returning if we're on the first frame done: if (!mGIFStruct.images_decoded) { FlushImageData(); mLastFlushedRow = mCurrentRow; mLastFlushedPass = mCurrentPass; } return; }
nsresult nsGIFDecoder2::GifWrite(const PRUint8 *buf, PRUint32 len) { if (!buf || !len) return NS_ERROR_FAILURE; const PRUint8 *q = buf; // Add what we have sofar to the block // If previous call to me left something in the hold first complete current block // Or if we are filling the colormaps, first complete the colormap PRUint8* p = (mGIFStruct.state == gif_global_colormap) ? (PRUint8*)mGIFStruct.global_colormap : (mGIFStruct.state == gif_image_colormap) ? (PRUint8*)mColormap : (mGIFStruct.bytes_in_hold) ? mGIFStruct.hold : nsnull; if (p) { // Add what we have sofar to the block PRUint32 l = PR_MIN(len, mGIFStruct.bytes_to_consume); memcpy(p+mGIFStruct.bytes_in_hold, buf, l); if (l < mGIFStruct.bytes_to_consume) { // Not enough in 'buf' to complete current block, get more mGIFStruct.bytes_in_hold += l; mGIFStruct.bytes_to_consume -= l; return NS_OK; } // Reset hold buffer count mGIFStruct.bytes_in_hold = 0; // Point 'q' to complete block in hold (or in colormap) q = p; } // Invariant: // 'q' is start of current to be processed block (hold, colormap or buf) // 'bytes_to_consume' is number of bytes to consume from 'buf' // 'buf' points to the bytes to be consumed from the input buffer // 'len' is number of bytes left in input buffer from position 'buf'. // At entrance of the for loop will 'buf' will be moved 'bytes_to_consume' // to point to next buffer, 'len' is adjusted accordingly. // So that next round in for loop, q gets pointed to the next buffer. for (;len >= mGIFStruct.bytes_to_consume; q=buf) { // Eat the current block from the buffer, q keeps pointed at current block buf += mGIFStruct.bytes_to_consume; len -= mGIFStruct.bytes_to_consume; switch (mGIFStruct.state) { case gif_lzw: if (!DoLzw(q)) { mGIFStruct.state = gif_error; break; } GETN(1, gif_sub_block); break; case gif_lzw_start: { // Make sure the transparent pixel is transparent in the colormap if (mGIFStruct.is_transparent) { // Save old value so we can restore it later if (mColormap == mGIFStruct.global_colormap) mOldColor = mColormap[mGIFStruct.tpixel]; mColormap[mGIFStruct.tpixel] = 0; } /* Initialize LZW parser/decoder */ mGIFStruct.datasize = *q; const int clear_code = ClearCode(); if (mGIFStruct.datasize > MAX_LZW_BITS || clear_code >= MAX_BITS) { mGIFStruct.state = gif_error; break; } mGIFStruct.avail = clear_code + 2; mGIFStruct.oldcode = -1; mGIFStruct.codesize = mGIFStruct.datasize + 1; mGIFStruct.codemask = (1 << mGIFStruct.codesize) - 1; mGIFStruct.datum = mGIFStruct.bits = 0; /* init the tables */ for (int i = 0; i < clear_code; i++) mGIFStruct.suffix[i] = i; mGIFStruct.stackp = mGIFStruct.stack; GETN(1, gif_sub_block); } break; /* All GIF files begin with "GIF87a" or "GIF89a" */ case gif_type: if (!strncmp((char*)q, "GIF89a", 6)) { mGIFStruct.version = 89; } else if (!strncmp((char*)q, "GIF87a", 6)) { mGIFStruct.version = 87; } else { mGIFStruct.state = gif_error; break; } GETN(7, gif_global_header); break; case gif_global_header: /* This is the height and width of the "screen" or * frame into which images are rendered. The * individual images can be smaller than the * screen size and located with an origin anywhere * within the screen. */ mGIFStruct.screen_width = GETINT16(q); mGIFStruct.screen_height = GETINT16(q + 2); mGIFStruct.global_colormap_depth = (q[4]&0x07) + 1; // screen_bgcolor is not used //mGIFStruct.screen_bgcolor = q[5]; // q[6] = Pixel Aspect Ratio // Not used // float aspect = (float)((q[6] + 15) / 64.0); if (q[4] & 0x80) { /* global map */ // Get the global colormap const PRUint32 size = (3 << mGIFStruct.global_colormap_depth); if (len < size) { // Use 'hold' pattern to get the global colormap GETN(size, gif_global_colormap); break; } // Copy everything, go to colormap state to do CMS correction memcpy(mGIFStruct.global_colormap, buf, size); buf += size; len -= size; GETN(0, gif_global_colormap); break; } GETN(1, gif_image_start); break; case gif_global_colormap: // Everything is already copied into global_colormap // Convert into Cairo colors including CMS transformation ConvertColormap(mGIFStruct.global_colormap, 1<<mGIFStruct.global_colormap_depth); GETN(1, gif_image_start); break; case gif_image_start: switch (*q) { case GIF_TRAILER: mGIFStruct.state = gif_done; break; case GIF_EXTENSION_INTRODUCER: GETN(2, gif_extension); break; case GIF_IMAGE_SEPARATOR: GETN(9, gif_image_header); break; default: /* If we get anything other than GIF_IMAGE_SEPARATOR, * GIF_EXTENSION_INTRODUCER, or GIF_TRAILER, there is extraneous data * between blocks. The GIF87a spec tells us to keep reading * until we find an image separator, but GIF89a says such * a file is corrupt. We follow GIF89a and bail out. */ if (mGIFStruct.images_decoded > 0) { /* The file is corrupt, but one or more images have * been decoded correctly. In this case, we proceed * as if the file were correctly terminated and set * the state to gif_done, so the GIF will display. */ mGIFStruct.state = gif_done; } else { /* No images decoded, there is nothing to display. */ mGIFStruct.state = gif_error; } } break; case gif_extension: mGIFStruct.bytes_to_consume = q[1]; if (mGIFStruct.bytes_to_consume) { switch (*q) { case GIF_GRAPHIC_CONTROL_LABEL: mGIFStruct.state = gif_control_extension; break; case GIF_APPLICATION_EXTENSION_LABEL: mGIFStruct.state = gif_application_extension; break; case GIF_COMMENT_LABEL: mGIFStruct.state = gif_consume_comment; break; default: mGIFStruct.state = gif_skip_block; } } else { GETN(1, gif_image_start); } break; case gif_consume_block: if (!*q) GETN(1, gif_image_start); else GETN(*q, gif_skip_block); break; case gif_skip_block: GETN(1, gif_consume_block); break; case gif_control_extension: mGIFStruct.is_transparent = *q & 0x1; mGIFStruct.tpixel = q[3]; mGIFStruct.disposal_method = ((*q) >> 2) & 0x7; // Some specs say 3rd bit (value 4), other specs say value 3 // Let's choose 3 (the more popular) if (mGIFStruct.disposal_method == 4) mGIFStruct.disposal_method = 3; mGIFStruct.delay_time = GETINT16(q + 1) * 10; GETN(1, gif_consume_block); break; case gif_comment_extension: if (*q) GETN(*q, gif_consume_comment); else GETN(1, gif_image_start); break; case gif_consume_comment: GETN(1, gif_comment_extension); break; case gif_application_extension: /* Check for netscape application extension */ if (!strncmp((char*)q, "NETSCAPE2.0", 11) || !strncmp((char*)q, "ANIMEXTS1.0", 11)) GETN(1, gif_netscape_extension_block); else GETN(1, gif_consume_block); break; /* Netscape-specific GIF extension: animation looping */ case gif_netscape_extension_block: if (*q) GETN(*q, gif_consume_netscape_extension); else GETN(1, gif_image_start); break; /* Parse netscape-specific application extensions */ case gif_consume_netscape_extension: switch (q[0] & 7) { case 1: /* Loop entire animation specified # of times. Only read the loop count during the first iteration. */ mGIFStruct.loop_count = GETINT16(q + 1); /* Zero loop count is infinite animation loop request */ if (mGIFStruct.loop_count == 0) mGIFStruct.loop_count = -1; GETN(1, gif_netscape_extension_block); break; case 2: /* Wait for specified # of bytes to enter buffer */ // Don't do this, this extension doesn't exist (isn't used at all) // and doesn't do anything, as our streaming/buffering takes care of it all... // See: http://semmix.pl/color/exgraf/eeg24.htm GETN(1, gif_netscape_extension_block); break; default: // 0,3-7 are yet to be defined netscape extension codes mGIFStruct.state = gif_error; } break; case gif_image_header: { /* Get image offsets, with respect to the screen origin */ mGIFStruct.x_offset = GETINT16(q); mGIFStruct.y_offset = GETINT16(q + 2); /* Get image width and height. */ mGIFStruct.width = GETINT16(q + 4); mGIFStruct.height = GETINT16(q + 6); if (!mGIFStruct.images_decoded) { /* Work around broken GIF files where the logical screen * size has weird width or height. We assume that GIF87a * files don't contain animations. */ if ((mGIFStruct.screen_height < mGIFStruct.height) || (mGIFStruct.screen_width < mGIFStruct.width) || (mGIFStruct.version == 87)) { mGIFStruct.screen_height = mGIFStruct.height; mGIFStruct.screen_width = mGIFStruct.width; mGIFStruct.x_offset = 0; mGIFStruct.y_offset = 0; } // Create the image container with the right size. BeginGIF(); // If we were doing header-only, we're done if (mFlags & imgIDecoder::DECODER_FLAG_HEADERONLY) return NS_OK; } /* Work around more broken GIF files that have zero image width or height */ if (!mGIFStruct.height || !mGIFStruct.width) { mGIFStruct.height = mGIFStruct.screen_height; mGIFStruct.width = mGIFStruct.screen_width; if (!mGIFStruct.height || !mGIFStruct.width) { mGIFStruct.state = gif_error; break; } } /* Depth of colors is determined by colormap */ /* (q[8] & 0x80) indicates local colormap */ /* bits per pixel is (q[8]&0x07 + 1) when local colormap is set */ PRUint32 depth = mGIFStruct.global_colormap_depth; if (q[8] & 0x80) depth = (q[8]&0x07) + 1; PRUint32 realDepth = depth; while (mGIFStruct.tpixel >= (1 << realDepth) && (realDepth < 8)) { realDepth++; } // Mask to limit the color values within the colormap mColorMask = 0xFF >> (8 - realDepth); nsresult rv = BeginImageFrame(realDepth); if (NS_FAILED(rv) || !mImageData) { mGIFStruct.state = gif_error; break; } if (q[8] & 0x40) { mGIFStruct.interlaced = PR_TRUE; mGIFStruct.ipass = 1; } else { mGIFStruct.interlaced = PR_FALSE; mGIFStruct.ipass = 0; } /* Only apply the Haeberli display hack on the first frame */ mGIFStruct.progressive_display = (mGIFStruct.images_decoded == 0); /* Clear state from last image */ mGIFStruct.irow = 0; mGIFStruct.rows_remaining = mGIFStruct.height; mGIFStruct.rowp = mImageData; /* bits per pixel is q[8]&0x07 */ if (q[8] & 0x80) /* has a local colormap? */ { mGIFStruct.local_colormap_size = 1 << depth; if (!mGIFStruct.images_decoded) { // First frame has local colormap, allocate space for it // as the image frame doesn't have its own palette mColormapSize = sizeof(PRUint32) << realDepth; if (!mGIFStruct.local_colormap) { mGIFStruct.local_colormap = (PRUint32*)PR_MALLOC(mColormapSize); if (!mGIFStruct.local_colormap) { mGIFStruct.state = gif_oom; break; } } mColormap = mGIFStruct.local_colormap; } const PRUint32 size = 3 << depth; if (mColormapSize > size) { // Clear the notfilled part of the colormap memset(((PRUint8*)mColormap) + size, 0, mColormapSize - size); } if (len < size) { // Use 'hold' pattern to get the image colormap GETN(size, gif_image_colormap); break; } // Copy everything, go to colormap state to do CMS correction memcpy(mColormap, buf, size); buf += size; len -= size; GETN(0, gif_image_colormap); break; } else { /* Switch back to the global palette */ if (mGIFStruct.images_decoded) { // Copy global colormap into the palette of current frame memcpy(mColormap, mGIFStruct.global_colormap, mColormapSize); } else { mColormap = mGIFStruct.global_colormap; } } GETN(1, gif_lzw_start); } break; case gif_image_colormap: // Everything is already copied into local_colormap // Convert into Cairo colors including CMS transformation ConvertColormap(mColormap, mGIFStruct.local_colormap_size); GETN(1, gif_lzw_start); break; case gif_sub_block: mGIFStruct.count = *q; if (mGIFStruct.count) { /* Still working on the same image: Process next LZW data block */ /* Make sure there are still rows left. If the GIF data */ /* is corrupt, we may not get an explicit terminator. */ if (!mGIFStruct.rows_remaining) { #ifdef DONT_TOLERATE_BROKEN_GIFS mGIFStruct.state = gif_error; break; #else /* This is an illegal GIF, but we remain tolerant. */ GETN(1, gif_sub_block); #endif if (mGIFStruct.count == GIF_TRAILER) { /* Found a terminator anyway, so consider the image done */ GETN(1, gif_done); break; } } GETN(mGIFStruct.count, gif_lzw); } else { /* See if there are any more images in this sequence. */ EndImageFrame(); GETN(1, gif_image_start); } break; case gif_done: EndGIF(/* aSuccess = */ PR_TRUE); return NS_OK; break; case gif_error: EndGIF(/* aSuccess = */ PR_FALSE); return NS_ERROR_FAILURE; break; // Handle out of memory errors case gif_oom: return NS_ERROR_OUT_OF_MEMORY; // We shouldn't ever get here. default: break; } } // if an error state is set but no data remains, code flow reaches here if (mGIFStruct.state == gif_error) { EndGIF(/* aSuccess = */ PR_FALSE); return NS_ERROR_FAILURE; } // Copy the leftover into mGIFStruct.hold mGIFStruct.bytes_in_hold = len; if (len) { // Add what we have sofar to the block PRUint8* p = (mGIFStruct.state == gif_global_colormap) ? (PRUint8*)mGIFStruct.global_colormap : (mGIFStruct.state == gif_image_colormap) ? (PRUint8*)mColormap : mGIFStruct.hold; memcpy(p, buf, len); mGIFStruct.bytes_to_consume -= len; } return NS_OK; }
// Set coords of Boxes void Cooder(Node* tree, int n) { if(GETN(tree, n).cutline == '-') { return; } else if(GETN(tree, n).cutline == 'H') { LEFTC(tree, n).xcoord = GETN(tree, n).xcoord; LEFTC(tree, n).ycoord = RIGHTC(tree, n).height + GETN(tree, n).ycoord; RIGHTC(tree, n).ycoord = GETN(tree, n).ycoord; RIGHTC(tree, n).xcoord = GETN(tree, n).xcoord; } else { RIGHTC(tree, n).ycoord = GETN(tree, n).ycoord; RIGHTC(tree, n).xcoord = LEFTC(tree, n).width + GETN(tree, n).xcoord; LEFTC(tree, n).ycoord = GETN(tree, n).ycoord; LEFTC(tree, n).xcoord = GETN(tree, n).xcoord; } Cooder(tree, GETN(tree, n).left); Cooder(tree, GETN(tree, n).right); return; }
void ReRoot(Node* tree, int root, Box* min, int nogo_root) { // printf("LOG: root:%d r:%d l:%d\n", root, GETN(tree, root).left, GETN(tree, root).right); int nroot; // will be used to store the next root Box root_box = { GETN(tree, root).width, GETN(tree, root).height }; if(LEFTC(tree, root).cutline == '-') { return; } if(nogo_root == GETN(tree, root).right) { // if we cannot take right means we must take left // New root is left nroot = GETN(tree, root).left; Box lroot_box = { LEFTC(tree, root).width, LEFTC(tree, root).height }; // Root L - L // Change the tree structure GETN(tree, root).left = GETN(tree, nroot).right; GETN(tree, nroot).right = root; //Calculate dimensions for the boxes MiniBoxer(tree, root, nroot); SmallerBox(&GETN(tree, nroot), min); // Check if smaller box ReRoot(tree, nroot, min, root); // // UnRoot GETN(tree, nroot).right = GETN(tree, root).left; GETN(tree, root).left = nroot; GETN(tree, nroot).width = lroot_box.width; GETN(tree, nroot).height = lroot_box.height; GETN(tree, root).width = root_box.width; GETN(tree, root).height = root_box.height; // Root L - R // Change the tree structure GETN(tree, root).left = GETN(tree, nroot).left; GETN(tree, nroot).left = root; MiniBoxer(tree, root, nroot); SmallerBox(&GETN(tree, nroot), min); // Check if smaller box ReRoot(tree, nroot, min, root); // UnRoot GETN(tree, nroot).left = GETN(tree, root).left; GETN(tree, root).left = nroot; GETN(tree, nroot).width = lroot_box.width; GETN(tree, nroot).height = lroot_box.height; GETN(tree, root).width = root_box.width; GETN(tree, root).height = root_box.height; return; } // Base case on right if(RIGHTC(tree, root).cutline == '-') { return; } if(nogo_root == GETN(tree, root).left) { // if we cannot take left means we take right // Taking the Right Side nroot = GETN(tree, root).right; Box rroot_box = { RIGHTC(tree, root).width, RIGHTC(tree, root).height }; // Root R - L // Change the tree structure GETN(tree, root).right = GETN(tree, nroot).right; GETN(tree, nroot).right = root; MiniBoxer(tree, root, nroot); SmallerBox(&GETN(tree, nroot), min); // Check if smaller box ReRoot(tree, nroot, min, root); // UnRoot GETN(tree, nroot).right = GETN(tree, root).right; GETN(tree, root).right = nroot; GETN(tree, nroot).width = rroot_box.width; GETN(tree, nroot).height = rroot_box.height; GETN(tree, root).width = root_box.width; GETN(tree, root).height = root_box.height; // Root R - R // Change the tree structure GETN(tree, root).right = GETN(tree, nroot).left; GETN(tree, nroot).left = root; // Calc new box size MiniBoxer(tree, root, nroot); SmallerBox(&GETN(tree, nroot), min); // Check if smaller box ReRoot(tree, nroot, min, root); // UnRoot GETN(tree, nroot).left = GETN(tree, root).right; GETN(tree, root).right = nroot; GETN(tree, nroot).width = rroot_box.width; GETN(tree, nroot).height = rroot_box.height; GETN(tree, root).width = root_box.width; GETN(tree, root).height = root_box.height; return; } }