Changeset 27896 in webkit
- Timestamp:
- Nov 19, 2007 4:22:19 AM (16 years ago)
- Location:
- trunk/WebCore
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebCore/ChangeLog
r27895 r27896 1 2007-11-19 Peter Kasting <zerodpx@gmail.com> 2 3 Reviewed by Darin Adler. 4 5 http://bugs.webkit.org/show_bug.cgi?id=15974 6 GIF decoding should respect frames' specified disposal methods. 7 8 * platform/image-decoders/ImageDecoder.h: 9 (WebCore::RGBA32Buffer::): 10 (WebCore::RGBA32Buffer::RGBA32Buffer): 11 (WebCore::RGBA32Buffer::disposalMethod): 12 (WebCore::RGBA32Buffer::setDisposalMethod): 13 * platform/image-decoders/gif/GIFImageDecoder.cpp: 14 (WebCore::GIFImageDecoder::frameBufferAtIndex): 15 (WebCore::GIFImageDecoder::initFrameBuffer): 16 (WebCore::GIFImageDecoder::prepEmptyFrameBuffer): 17 (WebCore::GIFImageDecoder::haveDecodedRow): 18 (WebCore::GIFImageDecoder::frameComplete): 19 * platform/image-decoders/gif/GIFImageDecoder.h: 20 * platform/image-decoders/gif/GIFImageReader.cpp: 21 (GIFImageReader::read): 22 * platform/image-decoders/gif/GIFImageReader.h: 23 (GIFFrameReader::GIFFrameReader): 24 1 25 2007-11-18 Brady Eidson <beidson@apple.com> 2 26 -
trunk/WebCore/platform/image-decoders/ImageDecoder.h
r27186 r27896 42 42 public: 43 43 enum FrameStatus { FrameEmpty, FramePartial, FrameComplete }; 44 enum FrameDisposalMethod { 45 // If you change the numeric values of these, make sure you audit all 46 // users, as some users may cast raw values to/from these constants. 47 DisposeNotSpecified = 0, // Leave frame in framebuffer 48 DisposeKeep = 1, // Leave frame in framebuffer 49 DisposeOverwriteBgcolor = 2, // Clear frame to transparent 50 DisposeOverwritePrevious = 3, // Clear frame to previous framebuffer contents 51 }; 44 52 45 53 RGBA32Buffer() : m_height(0), m_status(FrameEmpty), m_duration(0), 46 m_ includeInNextFrame(false), m_hasAlpha(false)54 m_disposalMethod(DisposeNotSpecified), m_hasAlpha(false) 47 55 {} 48 56 … … 53 61 FrameStatus status() const { return m_status; } 54 62 unsigned duration() const { return m_duration; } 55 bool includeInNextFrame() const { return m_includeInNextFrame; }63 FrameDisposalMethod disposalMethod() const { return m_disposalMethod; } 56 64 bool hasAlpha() const { return m_hasAlpha; } 57 65 … … 60 68 void setStatus(FrameStatus s) { m_status = s; } 61 69 void setDuration(unsigned duration) { m_duration = duration; } 62 void set IncludeInNextFrame(bool n) { m_includeInNextFrame = n; }70 void setDisposalMethod(FrameDisposalMethod method) { m_disposalMethod = method; } 63 71 void setHasAlpha(bool alpha) { m_hasAlpha = alpha; } 64 72 … … 87 95 FrameStatus m_status; // Whether or not this frame is completely finished decoding. 88 96 unsigned m_duration; // The animation delay. 89 bool m_includeInNextFrame; // Whether or not the next buffer should be initially populated with our data.97 FrameDisposalMethod m_disposalMethod; // What to do with this frame's data when initializing the next frame. 90 98 bool m_hasAlpha; // Whether or not any of the pixels in the buffer have transparency. 91 99 }; -
trunk/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp
r27642 r27896 159 159 RGBA32Buffer* GIFImageDecoder::frameBufferAtIndex(size_t index) 160 160 { 161 if (index < 0 || index>= frameCount())161 if (index >= frameCount()) 162 162 return 0; 163 163 … … 195 195 } 196 196 197 void GIFImageDecoder::initFrameBuffer(RGBA32Buffer& buffer, 198 RGBA32Buffer* previousBuffer, 199 bool compositeWithPreviousFrame) 197 void GIFImageDecoder::initFrameBuffer(unsigned frameIndex) 200 198 { 201 199 // Initialize the frame rect in our buffer. … … 209 207 frameRect.setHeight(m_size.height() - m_reader->frameYOffset()); 210 208 211 buffer.setRect(frameRect); 212 213 bool isSubRect = (frameRect.x() > 0 || frameRect.y() > 0 || 214 frameRect.width() < m_size.width() || 215 frameRect.height() < m_size.height()); 209 RGBA32Buffer* const buffer = &m_frameBufferCache[frameIndex]; 210 buffer->setRect(frameRect); 216 211 217 // Let's resize our buffer now to the correct width/height and then 218 // initialize portions of it if needed. 219 RGBA32Array& bytes = buffer.bytes(); 220 221 // If the disposal method of the previous frame said to stick around, then we need 222 // to copy that frame into our frame. We also dont want to have any impact on 223 // anything outside our frame's rect, so if we don't overlay the entire image, 224 // then also composite with the previous frame. 225 if (previousBuffer && (compositeWithPreviousFrame || isSubRect)) { 226 bytes = previousBuffer->bytes(); 227 buffer.ensureHeight(m_size.height()); 228 buffer.setHasAlpha(previousBuffer->hasAlpha()); 229 } 230 else // Resize to the width and height of the image. 231 bytes.resize(m_size.width() * m_size.height()); 232 233 if (isSubRect) { 234 // We need to go ahead and initialize the first frame to make sure 235 // that areas outside the subrect start off transparent. 236 if (!previousBuffer) { 237 bytes.fill(0); 238 buffer.setHasAlpha(true); 239 } else if (!compositeWithPreviousFrame) { 240 // Now this is an interesting case. In the case where we fill 241 // the entire image, we effectively do a full clear of the image (and thus 242 // don't have to initialize anything in our buffer). 243 // 244 // However in the case where we only fill a piece of the image, two problems occur: 245 // (1) We need to wipe out the area occupied by the previous frame, which 246 // could also have been a subrect. 247 // (2) Anything outside the previous frame's rect *and* outside our current 248 // frame's rect should be left alone. 249 // We have handled (2) by just initializing our buffer from the previous frame. 250 // Our subrect will correctly overwrite the previous frame's contents as we 251 // decode rows. However that still leaves the problem of having to wipe out 252 // the area occupied by the previous frame that does not overlap with 253 // the new frame. 254 if (previousBuffer->rect() != frameRect) { 255 // We have to restore the entire previous subframe with the first frame's contents. 256 RGBA32Buffer* firstBuffer = &m_frameBufferCache[0]; 257 bool sawAlpha = buffer.hasAlpha(); 258 IntRect prevRect = previousBuffer->rect(); 259 unsigned end = prevRect.y() + prevRect.height(); 260 261 // Given that we allocate buffers to be the same size as previous buffers, 262 // I think this assert should be valid. 263 ASSERT(IntRect(IntPoint(0,0), m_size).contains(firstBuffer->rect())); 264 265 for (unsigned i = prevRect.y(); i < end; i++) { 266 unsigned* curr = buffer.bytes().data() + (i * m_size.width() + prevRect.x()); 267 unsigned* orig = firstBuffer->bytes().data() + (i * m_size.width() + prevRect.x()); 268 unsigned* end = curr + prevRect.width(); 269 unsigned* origEnd = orig + firstBuffer->rect().width(); 270 271 while (curr != end && orig != origEnd) { 272 if (!sawAlpha) { 273 sawAlpha = true; 274 buffer.setHasAlpha(true); 275 } 276 *curr++ = *orig++; 277 } 278 } 212 if (frameIndex == 0) { 213 // This is the first frame, so we're not relying on any previous data. 214 prepEmptyFrameBuffer(buffer); 215 } else { 216 // The starting state for this frame depends on the previous frame's 217 // disposal method. 218 // 219 // Frames that use the DisposeOverwritePrevious method are effectively 220 // no-ops in terms of changing the starting state of a frame compared to 221 // the starting state of the previous frame, so skip over them. (If the 222 // first frame specifies this method, it will get treated like 223 // DisposeOverwriteBgcolor below and reset to a completely empty image.) 224 const RGBA32Buffer* prevBuffer = &m_frameBufferCache[--frameIndex]; 225 RGBA32Buffer::FrameDisposalMethod prevMethod = 226 prevBuffer->disposalMethod(); 227 while ((frameIndex > 0) && 228 (prevMethod == RGBA32Buffer::DisposeOverwritePrevious)) { 229 prevBuffer = &m_frameBufferCache[--frameIndex]; 230 prevMethod = prevBuffer->disposalMethod(); 231 } 232 233 if ((prevMethod == RGBA32Buffer::DisposeNotSpecified) || 234 (prevMethod == RGBA32Buffer::DisposeKeep)) { 235 // Preserve the last frame as the starting state for this frame. 236 buffer->bytes() = prevBuffer->bytes(); 237 } else { 238 // We want to clear the previous frame to transparent, without 239 // affecting pixels in the image outside of the frame. 240 const IntRect& prevRect = prevBuffer->rect(); 241 if ((frameIndex == 0) || 242 prevRect.contains(IntRect(IntPoint(0, 0), m_size))) { 243 // Clearing the first frame, or a frame the size of the whole 244 // image, results in a completely empty image. 245 prepEmptyFrameBuffer(buffer); 246 } else { 247 // Copy the whole previous buffer, then clear just its frame. 248 buffer->bytes() = prevBuffer->bytes(); 249 SkBitmap& bitmap = buffer->bitmap(); 250 for (int y = prevRect.y(); y < prevRect.bottom(); ++y) { 251 unsigned* const currentRow = 252 buffer.bytes().data() + (y * m_size.width()); 253 for (int x = prevRect.x(); x < prevRect.right(); ++x) 254 buffer->setRGBA(currentRow + x, 0, 0, 0, 0); 255 } 256 if ((prevRect.width() > 0) && (prevRect.height() > 0)) 257 buffer->setHasAlpha(true); 279 258 } 280 259 } … … 282 261 283 262 // Update our status to be partially complete. 284 buffer.setStatus(RGBA32Buffer::FramePartial); 263 buffer->setStatus(RGBA32Buffer::FramePartial); 264 265 // Reset the alpha pixel tracker for this frame. 266 m_currentBufferSawAlpha = false; 267 } 268 269 void GIFImageDecoder::prepEmptyFrameBuffer(RGBA32Buffer* buffer) const 270 { 271 buffer->bytes().resize(m_size.width() * m_size.height()); 272 buffer->bytes().fill(0); 273 buffer->setHasAlpha(true); 285 274 } 286 275 … … 291 280 unsigned repeatCount) // How many times to repeat the row 292 281 { 293 // Resize to the width and height of the image.282 // Initialize the frame if necessary. 294 283 RGBA32Buffer& buffer = m_frameBufferCache[frameIndex]; 295 RGBA32Buffer* previousBuffer = (frameIndex > 0) ? &m_frameBufferCache[frameIndex-1] : 0;296 bool compositeWithPreviousFrame = previousBuffer && previousBuffer->includeInNextFrame();297 298 284 if (buffer.status() == RGBA32Buffer::FrameEmpty) 299 initFrameBuffer( buffer, previousBuffer, compositeWithPreviousFrame);285 initFrameBuffer(frameIndex); 300 286 301 287 // Do nothing for bogus data. … … 322 308 unsigned char* currentRowByte = rowBuffer; 323 309 324 bool hasAlpha = m_reader->isTransparent();325 bool sawAlpha = false;326 310 while (currentRowByte != rowEnd && currDst < dstEnd) { 327 if ((! hasAlpha|| *currentRowByte != m_reader->transparentPixel()) && *currentRowByte < colorMapSize) {311 if ((!m_reader->isTransparent() || *currentRowByte != m_reader->transparentPixel()) && *currentRowByte < colorMapSize) { 328 312 unsigned colorIndex = *currentRowByte * 3; 329 313 unsigned red = colorMap[colorIndex]; … … 332 316 RGBA32Buffer::setRGBA(*currDst, red, green, blue, 255); 333 317 } else { 334 if (!sawAlpha) { 335 sawAlpha = true; 336 buffer.setHasAlpha(true); 337 } 338 339 if (!compositeWithPreviousFrame) 340 RGBA32Buffer::setRGBA(*currDst, 0, 0, 0, 0); 318 m_currentBufferSawAlpha = true; 341 319 } 342 320 currDst++; … … 364 342 } 365 343 366 void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, bool includeInNextFrame)344 void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, RGBA32Buffer::FrameDisposalMethod disposalMethod) 367 345 { 368 346 RGBA32Buffer& buffer = m_frameBufferCache[frameIndex]; 347 buffer.ensureHeight(m_size.height()); 369 348 buffer.setStatus(RGBA32Buffer::FrameComplete); 370 349 buffer.setDuration(frameDuration); 371 buffer.setIncludeInNextFrame(includeInNextFrame); 372 buffer.ensureHeight(m_size.height()); 350 buffer.setDisposalMethod(disposalMethod); 351 352 if (!m_currentBufferSawAlpha) { 353 // The whole frame was non-transparent, so it's possible that the entire 354 // resulting buffer was non-transparent, and we can setHasAlpha(false). 355 if (buffer.rect().contains(IntRect(IntPoint(0, 0), m_size))) { 356 buffer.setHasAlpha(false); 357 } else if (frameIndex > 0) { 358 // Tricky case. This frame does not have alpha only if everywhere 359 // outside its rect doesn't have alpha. To know whether this is 360 // true, we check the start state of the frame -- if it doesn't have 361 // alpha, we're safe. 362 // 363 // First skip over prior DisposeOverwritePrevious frames (since they 364 // don't affect the start state of this frame) the same way we do in 365 // initFrameBuffer(). 366 const RGBA32Buffer* prevBuffer = &m_frameBufferCache[--frameIndex]; 367 while ((frameIndex > 0) && 368 (prevBuffer->disposalMethod() == 369 RGBA32Buffer::DisposeOverwritePrevious)) 370 prevBuffer = &m_frameBufferCache[--frameIndex]; 371 372 // Now, if we're at a DisposeNotSpecified or DisposeKeep frame, then 373 // we can say we have no alpha if that frame had no alpha. But 374 // since in initFrameBuffer() we already copied that frame's alpha 375 // state into the current frame's, we need do nothing at all here. 376 // 377 // The only remaining case is a DisposeOverwriteBgcolor frame. If 378 // it had no alpha, and its rect is contained in the current frame's 379 // rect, we know the current frame has no alpha. 380 if ((prevBuffer->disposalMethod() == 381 RGBA32Buffer::DisposeOverwriteBgcolor) && 382 !prevBuffer->hasAlpha() && 383 buffer.rect().contains(prevBuffer->rect())) 384 buffer.setHasAlpha(false); 385 } 386 } 373 387 } 374 388 -
trunk/WebCore/platform/image-decoders/gif/GIFImageDecoder.h
r25368 r27896 66 66 void haveDecodedRow(unsigned frameIndex, unsigned char* rowBuffer, unsigned char* rowEnd, unsigned rowNumber, 67 67 unsigned repeatCount); 68 void frameComplete(unsigned frameIndex, unsigned frameDuration, bool includeInNextFrame);68 void frameComplete(unsigned frameIndex, unsigned frameDuration, RGBA32Buffer::FrameDisposalMethod disposalMethod); 69 69 void gifComplete(); 70 70 71 71 private: 72 // Called to initialize a new frame buffer (potentially compositing it 73 // with the previous frame and/or clearing bits in our image based off 74 // the previous frame as well). 75 void initFrameBuffer(RGBA32Buffer& buffer, 76 RGBA32Buffer* previousBuffer, 77 bool compositeWithPreviousFrame); 72 // Called to initialize the frame buffer with the given index, based on the 73 // previous frame's disposal method. 74 void initFrameBuffer(unsigned frameIndex); 75 76 // A helper for initFrameBuffer(), this sets the size of the buffer, and 77 // fills it with transparent pixels. 78 void prepEmptyFrameBuffer(RGBA32Buffer* buffer) const; 78 79 79 80 bool m_frameCountValid; 81 bool m_currentBufferSawAlpha; 80 82 mutable GIFImageDecoderPrivate* m_reader; 81 83 }; -
trunk/WebCore/platform/image-decoders/gif/GIFImageReader.cpp
r27346 r27896 640 640 // ignoring gfx control extension 641 641 } 642 frame_reader->disposal_method = (gdispose)(((*q) >> 2) & 0x7); 642 // NOTE: This relies on the values in the FrameDisposalMethod enum 643 // matching those in the GIF spec! 644 frame_reader->disposal_method = (WebCore::RGBA32Buffer::FrameDisposalMethod)(((*q) >> 2) & 0x7); 643 645 // Some specs say 3rd bit (value 4), other specs say value 3 644 646 // Let's choose 3 (the more popular) 645 647 if (frame_reader->disposal_method == 4) 646 frame_reader->disposal_method = (gdispose)3;648 frame_reader->disposal_method = WebCore::RGBA32Buffer::DisposeOverwritePrevious; 647 649 frame_reader->delay_time = GETINT16(q + 1) * 10; 648 650 } … … 881 883 if (clientptr && frame_reader) 882 884 clientptr->frameComplete(images_decoded - 1, frame_reader->delay_time, 883 frame_reader->disposal_method == DISPOSE_KEEP);885 frame_reader->disposal_method); 884 886 885 887 /* Clear state from this image */ -
trunk/WebCore/platform/image-decoders/gif/GIFImageReader.h
r13835 r27896 77 77 } gstate; 78 78 79 /* "Disposal" method indicates how the image should be handled in the80 framebuffer before the subsequent image is displayed. */81 typedef enum82 {83 DISPOSE_NOT_SPECIFIED = 0,84 DISPOSE_KEEP = 1, /* Leave it in the framebuffer */85 DISPOSE_OVERWRITE_BGCOLOR = 2, /* Overwrite with background color */86 DISPOSE_OVERWRITE_PREVIOUS = 3 /* Save-under */87 } gdispose;88 89 79 struct GIFFrameReader { 90 80 /* LZW decoder state machine */ … … 112 102 unsigned int height, width; 113 103 int tpixel; /* Index of transparent pixel */ 114 gdisposedisposal_method; /* Restore to background, leave in place, etc.*/104 WebCore::RGBA32Buffer::FrameDisposalMethod disposal_method; /* Restore to background, leave in place, etc.*/ 115 105 unsigned char *local_colormap; /* Per-image colormap */ 116 106 int local_colormap_size; /* Size of local colormap array. */ … … 141 131 x_offset = y_offset = width = height = 0; 142 132 tpixel = 0; 143 disposal_method = DISPOSE_NOT_SPECIFIED;133 disposal_method = WebCore::RGBA32Buffer::DisposeNotSpecified; 144 134 145 135 local_colormap = 0;
Note: See TracChangeset
for help on using the changeset viewer.