bool ImageSource::frameHasAlphaAtIndex(size_t index) { #ifdef ANDROID_ANIMATED_GIF if (m_decoder.m_gifDecoder) { if (!m_decoder.m_gifDecoder->supportsAlpha()) return false; RGBA32Buffer* buffer = m_decoder.m_gifDecoder->frameBufferAtIndex(index); if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return false; return buffer->hasAlpha(); } #else SkASSERT(0 == index); #endif if (NULL == m_decoder.m_image) return true; // if we're not sure, assume the worse-case const PrivateAndroidImageSourceRec& decoder = *m_decoder.m_image; // if we're 16bit, we know even without all the data available if (decoder.bitmap().getConfig() == SkBitmap::kRGB_565_Config) return false; if (!decoder.fAllDataReceived) return true; // if we're not sure, assume the worse-case return !decoder.bitmap().isOpaque(); }
bool ImageSource::frameIsCompleteAtIndex(size_t index) { if (!m_decoder) return false; RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index); return buffer && buffer->status() == RGBA32Buffer::FrameComplete; }
NativeImagePtr ImageSource::createFrameAtIndex(size_t index) { if (!m_decoder) return 0; RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index); if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return 0; return buffer->asNewNativeImage(); }
float ImageSource::frameDurationAtIndex(size_t index) { if (!m_decoder) return 0; RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index); if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return 0; return buffer->duration() / 1000.0f; }
bool ImageSource::frameHasAlphaAtIndex(size_t index) { if (!m_decoder || !m_decoder->supportsAlpha()) return false; RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index); if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return false; return buffer->hasAlpha(); }
bool ImageSource::frameIsCompleteAtIndex(size_t index) { #ifdef ANDROID_ANIMATED_GIF if (m_decoder.m_gifDecoder) { RGBA32Buffer* buffer = m_decoder.m_gifDecoder->frameBufferAtIndex(index); return buffer && buffer->status() == RGBA32Buffer::FrameComplete; } #else SkASSERT(0 == index); #endif return m_decoder.m_image && m_decoder.m_image->fAllDataReceived; }
RGBA32Buffer* BMPImageDecoder::frameBufferAtIndex(size_t index) { if (index) return 0; if (m_frameBufferCache.isEmpty()) m_frameBufferCache.resize(1); RGBA32Buffer* buffer = &m_frameBufferCache.first(); if (buffer->status() != RGBA32Buffer::FrameComplete) decode(false); return buffer; }
RGBA32Buffer* RxIImageDecoder::frameBufferAtIndex(size_t index) { if (index) return 0; RGBA32Buffer* frame = getUntouchedFrameBufferAtIndex(index); if (!frame) return 0; if (frame->status() != RGBA32Buffer::FrameComplete) decode(false); return frame; }
NativeImagePtr ImageSource::createFrameAtIndex(size_t index) { if (!m_decoder) return 0; RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index); if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return 0; return cairo_image_surface_create_for_data((unsigned char*)buffer->bytes().data(), CAIRO_FORMAT_ARGB32, size().width(), buffer->height(), size().width()*4); }
NativeImagePtr ImageSource::createFrameAtIndex(size_t index) { if (!m_decoder) return 0; // Note that the buffer can have NULL bytes even when it is marked as // non-empty. It seems "FrameEmpty" is only set before the frame has been // initialized. If it is decoded and it happens to be empty, it will be // marked as "FrameComplete" but will still have NULL bytes. RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index); if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return 0; // Copy the bitmap. The pixel data is refcounted internally by SkBitmap, so // this doesn't cost much. return buffer->asNewNativeImage(); }
float ImageSource::frameDurationAtIndex(size_t index) { if (!m_decoder) return 0; RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index); if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return 0; float duration = buffer->duration() / 1000.0f; // Follow other ports (and WinIE's) behavior to slow annoying ads that // specify a 0 duration. if (duration < 0.051f) return 0.100f; return duration; }
float ImageSource::frameDurationAtIndex(size_t index) { if (!m_decoder) return 0; RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index); if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return 0; // Many annoying ads specify a 0 duration to make an image flash as quickly // as possible. We follow WinIE's behavior and use a duration of 100 ms // for any frames that specify a duration of <= 50 ms. See // <http://bugs.webkit.org/show_bug.cgi?id=14413> or Radar 4051389 for // more. const float duration = buffer->duration() / 1000.0f; return (duration < 0.051f) ? 0.100f : duration; }
NativeImagePtr ImageSource::createFrameAtIndex(size_t index) { if (!initialized()) return 0; if (!m_decoder) return 0; RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index); if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return 0; // Cairo does not like zero height images. // If we have a zero height image, just pretend we don't have enough data yet. if (!size().height()) return 0; return buffer->asNewNativeImage(); }
SkBitmapRef* ImageSource::createFrameAtIndex(size_t index) { #ifdef ANDROID_ANIMATED_GIF if (m_decoder.m_gifDecoder) { RGBA32Buffer* buffer = m_decoder.m_gifDecoder->frameBufferAtIndex(index); if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return 0; SkBitmap& bitmap = buffer->bitmap(); SkPixelRef* pixelRef = bitmap.pixelRef(); if (pixelRef) pixelRef->setURI(m_decoder.m_url); return new SkBitmapRef(bitmap); } #else SkASSERT(index == 0); #endif SkASSERT(m_decoder.m_image != NULL); m_decoder.m_image->ref(); return m_decoder.m_image; }
float ImageSource::frameDurationAtIndex(size_t index) { float duration = 0; #ifdef ANDROID_ANIMATED_GIF if (m_decoder.m_gifDecoder) { RGBA32Buffer* buffer = m_decoder.m_gifDecoder->frameBufferAtIndex(index); if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return 0; duration = buffer->duration() / 1000.0f; } #else SkASSERT(index == 0); #endif // Many annoying ads specify a 0 duration to make an image flash as quickly as possible. // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify // a duration of <= 10 ms. See gfxImageFrame::GetTimeout in Gecko or Radar 4051389 for more. if (duration <= 0.010f) duration = 0.100f; return duration; }
NativeImagePtr ImageSource::createFrameAtIndex(size_t index) { if (!initialized()) return 0; if (!m_decoder) return 0; RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index); if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return 0; // Cairo does not like zero height images. // If we have a zero height image, just pretend we don't have enough data yet. if (!buffer->height()) return 0; return cairo_image_surface_create_for_data((unsigned char*)buffer->bytes().data(), CAIRO_FORMAT_ARGB32, size().width(), buffer->height(), size().width()*4); }
NativeImagePtr ImageSource::createFrameAtIndex(size_t index) { if (!m_decoder) return 0; RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index); if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return 0; IntRect imageRect = buffer->rect(); unsigned char* bytes = (unsigned char*)buffer->bytes().data(); long colorSize = buffer->bytes().size(); typedef wxPixelData<wxBitmap, wxAlphaPixelFormat> PixelData; int width = size().width(); int height = size().height(); wxBitmap* bmp = new wxBitmap(width, height, 32); PixelData data(*bmp); int rowCounter = 0; long pixelCounter = 0; PixelData::Iterator p(data); PixelData::Iterator rowStart = p; // NB: It appears that the data is in BGRA format instead of RGBA format. // This code works properly on both ppc and intel, meaning the issue is // likely not an issue of byte order getting mixed up on different archs. for (long i = 0; i < buffer->bytes().size()*4; i+=4) { p.Red() = bytes[i+2]; p.Green() = bytes[i+1]; p.Blue() = bytes[i+0]; p.Alpha() = bytes[i+3]; p++; pixelCounter++; if ( (pixelCounter % width ) == 0 ) { rowCounter++; p = rowStart; p.MoveTo(data, 0, rowCounter); } } bmp->UseAlpha(); ASSERT(bmp->IsOk()); return bmp; }
NativeImagePtr ImageSource::createFrameAtIndex(size_t index) { if (!m_decoder) return 0; RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index); if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return 0; DEBUG( "ImageSource::createFrameAtIndex() %i %i %i\n", size().width(), buffer->height(), buffer->bytes().size() ); os::Bitmap* pcBitmap = new os::Bitmap( size().width(), buffer->height(), os::CS_RGBA32 ); memcpy( pcBitmap->LockRaster(), reinterpret_cast<unsigned char*>(buffer->bytes().data()), ( buffer->height() * size().width() ) * 4 ); pcBitmap->UnlockRaster(); return( pcBitmap ); }
NativeImagePtr ImageSource::createFrameAtIndex(size_t index) { if (!initialized()) return 0; if (!m_decoder) return 0; RGBA32Buffer* buffer = m_decoder->frameBufferAtIndex(index); if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return 0; // Cairo does not like zero height images. // If we have a zero height image, just pretend we don't have enough data yet. if (!buffer->height()) return 0; /* <lab126> */ struct timespec startTime; KINDLE_BEGIN(Kindle_Debug_Perf) clock_gettime(CLOCK_MONOTONIC, &startTime); KINDLE_END() if (m_imageDitherType == USE_SIMPLE_ALGORITHM) { int imageWidth = size().width(); int imageHeight = buffer->height(); if(!dither_inited) for(int i=0; i<3333; i++) dither_buffer[i]=rand()%17; for (int n = 0; n < imageHeight; n++) { for (int m = 0; m < imageWidth; m++) { int rgb=buffer->bytes().data()[ (n*imageWidth) + m ]; int r = (rgb >> 16) & 0xff; int g = (rgb >> 8) & 0xff; int b = rgb & 0xff; int gray = (r == g && g == b) ? r : ((77 * r + 150 * g + 29 * b) >> 8); dither_index=(dither_index+1)%3333; int ngray=gray+dither_buffer[dither_index]; ngray-=ngray%17; if(ngray>0xff) ngray=0xff; buffer->bytes().data()[ (n*imageWidth) + m ] = (rgb&STRIP_ALPHA)|(ngray<<16)|(ngray<<8)|ngray; } } }
bool decode(const Vector<char>& data, RxIImageDecoder::RxIFormat fmt) { int i = 0; int j = 0; bool result = false; RGBA32Buffer* frameBuffer = 0; unsigned char* rgbData = 0; if (!m_memoryManager) return false; m_memoryReader = new MemReader((unsigned char*)data.data(), data.size()); // The rest of the code expects that the first 8 bytes has already been read. // The signature for these files is 8 bytes. m_memoryReader->SetOffset(8); switch (fmt) { case RxIImageDecoder::FormatRdi: m_rxi = RdiBirth(m_memoryManager, m_memoryReader); break; case RxIImageDecoder::FormatRgi: m_rxi = RgiBirth(m_memoryManager, m_memoryReader); break; case RxIImageDecoder::FormatRpi: m_rxi = RpiBirth(m_memoryManager, m_memoryReader); break; case RxIImageDecoder::FormatRwi: m_rxi = RwiBirth(m_memoryManager, m_memoryReader); break; case RxIImageDecoder::FormatUnknown: ASSERT_NOT_REACHED(); break; } if (!m_rxi) { delete m_memoryReader; m_memoryReader = 0; return false; } m_format = fmt; switch (fmt) { case RxIImageDecoder::FormatRdi: result = RdiGetWH(m_rxi, &m_w, &m_h); break; case RxIImageDecoder::FormatRgi: result = RgiGetWH(m_rxi, &m_w, &m_h); break; case RxIImageDecoder::FormatRpi: result = RpiGetWH(m_rxi, &m_w, &m_h); break; case RxIImageDecoder::FormatRwi: result = RwiGetWH(m_rxi, &m_w, &m_h); break; case RxIImageDecoder::FormatUnknown: ASSERT_NOT_REACHED(); break; } if (!result) { delete m_memoryReader; m_memoryReader = 0; return false; } // We can fill in the size now that the header is available. if (!m_decoder->setSize(m_w, m_h)) return m_decoder->setFailed(); frameBuffer = m_decoder->getUntouchedFrameBufferAtIndex(0); if (!frameBuffer) { delete m_memoryReader; m_memoryReader = 0; m_sizeInitialized = false; return false; } if (frameBuffer->status() == RGBA32Buffer::FrameEmpty) { if (!frameBuffer->setSize(m_w, m_h)) return m_decoder->setFailed(); frameBuffer->setStatus(RGBA32Buffer::FramePartial); if ( fmt == RxIImageDecoder::FormatRpi ) { // RPI format has alpha which we must support. frameBuffer->setHasAlpha(true); } else { frameBuffer->setHasAlpha(false); } // the frame always fills the entire image. frameBuffer->setRect(IntRect(IntPoint(0, 0), m_decoder->size())); } // note that we don't cleanup frameBuffer at all since we didn't allocate it. if ( fmt == RxIImageDecoder::FormatRgi ) { rgbData = (unsigned char*) malloc(m_w * m_h * 3); } else if ( fmt == RxIImageDecoder::FormatRpi ) { // extra channel is for alpha rgbData = (unsigned char*) malloc(m_w * m_h * 4); } MemWriter mw(m_w * m_h * 3); switch (fmt) { case RxIImageDecoder::FormatRdi: result = RdiGetRGB(m_rxi, &mw); break; case RxIImageDecoder::FormatRgi: result = RgiGetRGB(m_rxi, rgbData); break; case RxIImageDecoder::FormatRpi: result = RpiGetRGB(m_rxi, rgbData); break; case RxIImageDecoder::FormatRwi: result = RwiGetRGB(m_rxi, &mw); break; case RxIImageDecoder::FormatUnknown: ASSERT_NOT_REACHED(); break; } if (result) { unsigned char* ptr = 0; if ((fmt == RxIImageDecoder::FormatRdi) || (fmt == RxIImageDecoder::FormatRwi)) ptr = (unsigned char*)mw.GetBuffer(); else ptr = rgbData; for (i = 0; i < m_h; ++i) { for (j = 0; j < m_w; ++j) { unsigned char r, g, b; unsigned char a = 0xFF; r = *ptr; ptr++; g = *ptr; ptr++; b = *ptr; ptr++; if ( fmt == RxIImageDecoder::FormatRpi ) { a = *ptr; ptr++; } frameBuffer->setRGBA(j, i, r, g, b, a); } } } m_decoder->rxiComplete(); // cleaning up free(rgbData); rgbData = 0; delete m_memoryReader; m_memoryReader = 0; m_sizeInitialized = false; switch (fmt) { case RxIImageDecoder::FormatRdi: RdiDeath(m_rxi); break; case RxIImageDecoder::FormatRgi: RgiDeath(m_rxi); break; case RxIImageDecoder::FormatRpi: RpiDeath(m_rxi); break; case RxIImageDecoder::FormatRwi: RwiDeath(m_rxi); break; case RxIImageDecoder::FormatUnknown: ASSERT_NOT_REACHED(); break; } m_rxi = 0; return true; }
void GIFImageDecoder::haveDecodedRow(unsigned frameIndex, unsigned char* rowBuffer, // Pointer to single scanline temporary buffer unsigned char* rowEnd, unsigned rowNumber, // The row index unsigned repeatCount) // How many times to repeat the row { // Resize to the width and height of the image. RGBA32Buffer& buffer = m_frameBufferCache[frameIndex]; RGBA32Buffer* previousBuffer = (frameIndex > 0) ? &m_frameBufferCache[frameIndex-1] : 0; bool compositeWithPreviousFrame = previousBuffer && previousBuffer->includeInNextFrame(); if (buffer.status() == RGBA32Buffer::FrameEmpty) initFrameBuffer(buffer, previousBuffer, compositeWithPreviousFrame); if (rowBuffer == 0) return; unsigned colorMapSize; unsigned char* colorMap; m_reader->getColorMap(colorMap, colorMapSize); if (!colorMap) return; // The buffers that we draw are the entire image's width and height, so a final output frame is // width * height RGBA32 values in size. // // A single GIF frame, however, can be smaller than the entire image, i.e., it can represent some sub-rectangle // within the overall image. The rows we are decoding are within this // sub-rectangle. This means that if the GIF frame's sub-rectangle is (x,y,w,h) then row 0 is really row // y, and each row goes from x to x+w. unsigned dstPos = (m_reader->frameYOffset() + rowNumber) * m_size.width() + m_reader->frameXOffset(); unsigned* dst = buffer.bytes().data() + dstPos; unsigned* currDst = dst; unsigned char* currentRowByte = rowBuffer; bool hasAlpha = m_reader->isTransparent(); bool sawAlpha = false; while (currentRowByte != rowEnd) { if ((!hasAlpha || *currentRowByte != m_reader->transparentPixel()) && *currentRowByte < colorMapSize) { unsigned colorIndex = *currentRowByte * 3; unsigned red = colorMap[colorIndex]; unsigned green = colorMap[colorIndex + 1]; unsigned blue = colorMap[colorIndex + 2]; RGBA32Buffer::setRGBA(*currDst, red, green, blue, 255); } else { if (!sawAlpha) { sawAlpha = true; buffer.setHasAlpha(true); } if (!compositeWithPreviousFrame) RGBA32Buffer::setRGBA(*currDst, 0, 0, 0, 0); } currDst++; currentRowByte++; } if (repeatCount > 1) { // Copy the row |repeatCount|-1 times. unsigned size = (currDst - dst) * sizeof(unsigned); unsigned width = m_size.width(); unsigned* end = buffer.bytes().data() + width * m_size.height(); currDst = dst + width; for (unsigned i = 1; i < repeatCount; i++) { if (currDst + size > end) // Protect against a buffer overrun from a bogus repeatCount. break; memcpy(currDst, dst, size); currDst += width; } } // Our partial height is rowNumber + 1, e.g., row 2 is the 3rd row, so that's a height of 3. // Adding in repeatCount - 1 to rowNumber + 1 works out to just be rowNumber + repeatCount. buffer.ensureHeight(rowNumber + repeatCount); }
void GIFImageDecoder::initFrameBuffer(RGBA32Buffer& buffer, RGBA32Buffer* previousBuffer, bool compositeWithPreviousFrame) { // Initialize the frame rect in our buffer. IntRect frameRect(m_reader->frameXOffset(), m_reader->frameYOffset(), m_reader->frameWidth(), m_reader->frameHeight()); buffer.setRect(frameRect); bool isSubRect = (frameRect.x() > 0 || frameRect.y() > 0 || frameRect.width() < m_size.width() || frameRect.height() < m_size.height()); // Let's resize our buffer now to the correct width/height and then // initialize portions of it if needed. RGBA32Array& bytes = buffer.bytes(); // If the disposal method of the previous frame said to stick around, then we need // to copy that frame into our frame. We also dont want to have any impact on // anything outside our frame's rect, so if we don't overlay the entire image, // then also composite with the previous frame. if (previousBuffer && (compositeWithPreviousFrame || isSubRect)) { bytes = previousBuffer->bytes(); buffer.ensureHeight(m_size.height()); buffer.setHasAlpha(previousBuffer->hasAlpha()); } else // Resize to the width and height of the image. bytes.resize(m_size.width() * m_size.height()); if (isSubRect) { // We need to go ahead and initialize the first frame to make sure // that areas outside the subrect start off transparent. if (!previousBuffer) { bytes.fill(0); buffer.setHasAlpha(true); } else if (!compositeWithPreviousFrame) { // Now this is an interesting case. In the case where we fill // the entire image, we effectively do a full clear of the image (and thus // don't have to initialize anything in our buffer). // // However in the case where we only fill a piece of the image, two problems occur: // (1) We need to wipe out the area occupied by the previous frame, which // could also have been a subrect. // (2) Anything outside the previous frame's rect *and* outside our current // frame's rect should be left alone. // We have handled (2) by just initializing our buffer from the previous frame. // Our subrect will correctly overwrite the previous frame's contents as we // decode rows. However that still leaves the problem of having to wipe out // the area occupied by the previous frame that does not overlap with // the new frame. if (previousBuffer->rect() != frameRect) { // We have to clear out the entire previous subframe. bool sawAlpha = buffer.hasAlpha(); IntRect prevRect = previousBuffer->rect(); unsigned end = prevRect.y() + prevRect.height(); unsigned* src; for (unsigned i = prevRect.y(); i < end; i++) { unsigned* curr = buffer.bytes().data() + (i * m_size.width() + prevRect.x()); unsigned* end = curr + prevRect.width(); while (curr != end) { if (!sawAlpha) { sawAlpha = true; buffer.setHasAlpha(true); } RGBA32Buffer::setRGBA(*curr++, 0, 0, 0, 0); } } } } } // Update our status to be partially complete. buffer.setStatus(RGBA32Buffer::FramePartial); }