diff options
author | Mike Reed <> | 2009-03-24 22:30:39 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-24 22:30:39 -0700 |
commit | 6d20683c4bff963e2e8ca340cd968db81a9b81ec (patch) | |
tree | 9dd4d9df57aa09c4f95e1191b0d519ea7a9de928 | |
parent | 145f9f956cb5adf1c9f165e7376644e0cadd6792 (diff) | |
download | external_webkit-6d20683c4bff963e2e8ca340cd968db81a9b81ec.zip external_webkit-6d20683c4bff963e2e8ca340cd968db81a9b81ec.tar.gz external_webkit-6d20683c4bff963e2e8ca340cd968db81a9b81ec.tar.bz2 |
Automated import from //branches/donutburger/...@142335,142335
-rw-r--r-- | Android.mk | 1 | ||||
-rw-r--r-- | WebCore/Android.mk | 6 | ||||
-rw-r--r-- | WebCore/WebCorePrefixAndroid.h | 3 | ||||
-rw-r--r-- | WebCore/platform/graphics/BitmapImage.cpp | 4 | ||||
-rw-r--r-- | WebCore/platform/graphics/ImageSource.h | 6 | ||||
-rw-r--r-- | WebCore/platform/graphics/android/ImageAndroid.cpp | 21 | ||||
-rw-r--r-- | WebCore/platform/graphics/android/ImageSourceAndroid.cpp | 148 | ||||
-rw-r--r-- | WebCore/platform/graphics/skia/NativeImageSkia.cpp | 5 | ||||
-rw-r--r-- | WebCore/platform/image-decoders/skia/GIFImageDecoder.cpp | 55 | ||||
-rw-r--r-- | WebCore/platform/image-decoders/skia/ImageDecoder.h | 10 |
10 files changed, 224 insertions, 35 deletions
@@ -121,6 +121,7 @@ LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/WebCore/platform/graphics \ $(LOCAL_PATH)/WebCore/platform/graphics/android \ $(LOCAL_PATH)/WebCore/platform/graphics/network \ + $(LOCAL_PATH)/WebCore/platform/graphics/skia \ $(LOCAL_PATH)/WebCore/platform/graphics/transforms \ $(LOCAL_PATH)/WebCore/platform/image-decoders \ $(LOCAL_PATH)/WebCore/platform/network \ diff --git a/WebCore/Android.mk b/WebCore/Android.mk index 5b0880e..7b909e3 100644 --- a/WebCore/Android.mk +++ b/WebCore/Android.mk @@ -651,6 +651,8 @@ LOCAL_SRC_FILES := \ \ platform/graphics/WidthIterator.cpp \ \ + platform/graphics/skia/NativeImageSkia.cpp \ + \ platform/graphics/transforms/MatrixTransformOperation.cpp \ platform/graphics/transforms/RotateTransformOperation.cpp \ platform/graphics/transforms/ScaleTransformOperation.cpp \ @@ -658,6 +660,10 @@ LOCAL_SRC_FILES := \ platform/graphics/transforms/TransformOperations.cpp \ platform/graphics/transforms/TransformationMatrix.cpp \ platform/graphics/transforms/TranslateTransformOperation.cpp \ + \ + platform/image-decoders/skia/GIFImageDecoder.cpp \ + platform/image-decoders/skia/GIFImageReader.cpp \ + \ platform/network/AuthenticationChallengeBase.cpp \ platform/network/Credential.cpp \ platform/network/FormData.cpp \ diff --git a/WebCore/WebCorePrefixAndroid.h b/WebCore/WebCorePrefixAndroid.h index d69c122..e1f0650 100644 --- a/WebCore/WebCorePrefixAndroid.h +++ b/WebCore/WebCorePrefixAndroid.h @@ -162,3 +162,6 @@ typedef unsigned char flex_uint8_t; // needs additional flavor or parameter to know that it can't be ignored, // and/or script engine must keep whether event was user initiated. #define ANDROID_SCROLL_ON_GOTO_ANCHOR + +// Animated GIF support. +#define ANDROID_ANIMATED_GIF diff --git a/WebCore/platform/graphics/BitmapImage.cpp b/WebCore/platform/graphics/BitmapImage.cpp index 45b32ab..68863df 100644 --- a/WebCore/platform/graphics/BitmapImage.cpp +++ b/WebCore/platform/graphics/BitmapImage.cpp @@ -262,6 +262,10 @@ bool BitmapImage::shouldAnimate() void BitmapImage::startAnimation(bool catchUpIfNecessary) { +#ifdef ANDROID_ANIMATED_GIF + // We can't ever seem to keep up, so always let us just show the next frame + catchUpIfNecessary = false; +#endif if (m_frameTimer || !shouldAnimate() || frameCount() <= 1) return; diff --git a/WebCore/platform/graphics/ImageSource.h b/WebCore/platform/graphics/ImageSource.h index 55e0c5a..07cc2c2 100644 --- a/WebCore/platform/graphics/ImageSource.h +++ b/WebCore/platform/graphics/ImageSource.h @@ -71,9 +71,15 @@ typedef ImageDecoderQt* NativeImageSourcePtr; typedef QPixmap* NativeImagePtr; #elif PLATFORM(SGL) class String; +#ifdef ANDROID_ANIMATED_GIF +class ImageDecoder; +#endif struct NativeImageSourcePtr { SkString m_url; PrivateAndroidImageSourceRec* m_image; +#ifdef ANDROID_ANIMATED_GIF + ImageDecoder* m_gifDecoder; +#endif }; typedef const Vector<char>* NativeBytePtr; typedef SkBitmapRef* NativeImagePtr; diff --git a/WebCore/platform/graphics/android/ImageAndroid.cpp b/WebCore/platform/graphics/android/ImageAndroid.cpp index da52d67..3a3312f 100644 --- a/WebCore/platform/graphics/android/ImageAndroid.cpp +++ b/WebCore/platform/graphics/android/ImageAndroid.cpp @@ -67,8 +67,6 @@ bool FrameData::clear(bool clearMetadata) if (m_frame) { m_frame->unref(); m_frame = 0; - m_duration = 0.; - m_hasAlpha = true; return true; } return false; @@ -157,11 +155,16 @@ void BitmapImage::checkForSolidColor() void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp) { + if (!m_source.initialized()) + return; + + startAnimation(); + SkBitmapRef* image = this->nativeImageForCurrentFrame(); if (!image) { // If it's too early we won't have an image yet. return; } - + // in case we get called with an incomplete bitmap const SkBitmap& bitmap = image->bitmap(); if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) { @@ -172,12 +175,12 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, #endif return; } - + SkIRect srcR; SkRect dstR; float invScaleX = (float)bitmap.width() / image->origWidth(); float invScaleY = (float)bitmap.height() / image->origHeight(); - + android_setrect(&dstR, dstRect); android_setrect_scaled(&srcR, srcRect, invScaleX, invScaleY); if (srcR.isEmpty() || dstR.isEmpty()) { @@ -188,16 +191,14 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, #endif return; } - + SkCanvas* canvas = ctxt->platformContext()->mCanvas; SkPaint paint; - + paint.setFilterBitmap(true); paint.setPorterDuffXfermode(android_convert_compositeOp(compositeOp)); canvas->drawBitmapRect(bitmap, &srcR, dstR, &paint); - - startAnimation(); - + #ifdef TRACE_SUBSAMPLED_BITMAPS if (bitmap.width() != image->origWidth() || bitmap.height() != image->origHeight()) { diff --git a/WebCore/platform/graphics/android/ImageSourceAndroid.cpp b/WebCore/platform/graphics/android/ImageSourceAndroid.cpp index 4bf8f8e..4c6a246 100644 --- a/WebCore/platform/graphics/android/ImageSourceAndroid.cpp +++ b/WebCore/platform/graphics/android/ImageSourceAndroid.cpp @@ -25,7 +25,6 @@ #include "config.h" #include "BitmapAllocatorAndroid.h" -#include "ImageDecoder.h" #include "ImageSource.h" #include "IntSize.h" #include "NotImplemented.h" @@ -33,11 +32,18 @@ #include "PlatformString.h" #include "SkBitmapRef.h" -#include "SkImageRef.h" #include "SkImageDecoder.h" +#include "SkImageRef.h" #include "SkStream.h" #include "SkTemplates.h" +#ifdef ANDROID_ANIMATED_GIF + #include "EmojiFont.h" + #include "skia/GIFImageDecoder.h" + + using namespace android; +#endif + SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src); //#define TRACE_SUBSAMPLE_BITMAPS @@ -95,14 +101,24 @@ namespace WebCore { ImageSource::ImageSource() { m_decoder.m_image = NULL; +#ifdef ANDROID_ANIMATED_GIF + m_decoder.m_gifDecoder = 0; +#endif } ImageSource::~ImageSource() { delete m_decoder.m_image; +#ifdef ANDROID_ANIMATED_GIF + delete m_decoder.m_gifDecoder; +#endif } bool ImageSource::initialized() const { - return m_decoder.m_image != NULL; + return +#ifdef ANDROID_ANIMATED_GIF + m_decoder.m_gifDecoder || +#endif + m_decoder.m_image != NULL; } static int computeSampleSize(const SkBitmap& bitmap) { @@ -164,11 +180,32 @@ void ImageSource::setURL(const String& url) } } +#ifdef ANDROID_ANIMATED_GIF +// we only animate small GIFs for now, to save memory +// also, we only support this in Japan, hence the Emoji check +static bool should_use_animated_gif(int width, int height) { + return EmojiFont::IsAvailable() && + width <= 32 && height <= 32; +} +#endif + void ImageSource::setData(SharedBuffer* data, bool allDataReceived) { - if (NULL == m_decoder.m_image) { +#ifdef ANDROID_ANIMATED_GIF + // This is only necessary if we allow ourselves to partially decode GIF + if (m_decoder.m_gifDecoder + && !m_decoder.m_gifDecoder->failed()) { + m_decoder.m_gifDecoder->setData(data, allDataReceived); + return; + } +#endif + if (NULL == m_decoder.m_image +#ifdef ANDROID_ANIMATED_GIF + && !m_decoder.m_gifDecoder +#endif + ) { SkBitmap tmp; - + SkMemoryStream stream(data->data(), data->size(), false); SkImageDecoder* codec = SkImageDecoder::Factory(&stream); SkAutoTDelete<SkImageDecoder> ad(codec); @@ -180,6 +217,25 @@ void ImageSource::setData(SharedBuffer* data, bool allDataReceived) int origW = tmp.width(); int origH = tmp.height(); + +#ifdef ANDROID_ANIMATED_GIF + // First, check to see if this is an animated GIF + const Vector<char>& buffer = data->buffer(); + const char* contents = buffer.data(); + if (buffer.size() > 3 && strncmp(contents, "GIF8", 4) == 0 && + should_use_animated_gif(origW, origH)) { + // This means we are looking at a GIF, so create special + // GIF Decoder + // Need to wait for all data received if we are assigning an + // allocator (which we are not at the moment). + if (!m_decoder.m_gifDecoder /*&& allDataReceived*/) + m_decoder.m_gifDecoder = new GIFImageDecoder(); + if (!m_decoder.m_gifDecoder->failed()) + m_decoder.m_gifDecoder->setData(data, allDataReceived); + return; + } +#endif + int sampleSize = computeSampleSize(tmp); if (sampleSize > 1) { codec->setSampleSize(sampleSize); @@ -222,11 +278,20 @@ void ImageSource::setData(SharedBuffer* data, bool allDataReceived) bool ImageSource::isSizeAvailable() { - return m_decoder.m_image != NULL; + return +#ifdef ANDROID_ANIMATED_GIF + (m_decoder.m_gifDecoder + && m_decoder.m_gifDecoder->isSizeAvailable()) || +#endif + m_decoder.m_image != NULL; } IntSize ImageSource::size() const { +#ifdef ANDROID_ANIMATED_GIF + if (m_decoder.m_gifDecoder) + return m_decoder.m_gifDecoder->size(); +#endif if (m_decoder.m_image) { return IntSize(m_decoder.m_image->origWidth(), m_decoder.m_image->origHeight()); } @@ -235,19 +300,40 @@ IntSize ImageSource::size() const int ImageSource::repetitionCount() { +#ifdef ANDROID_ANIMATED_GIF + if (m_decoder.m_gifDecoder) + return m_decoder.m_gifDecoder->repetitionCount(); + if (!m_decoder.m_image) return 0; +#endif return 1; // A property with value 0 means loop forever. } size_t ImageSource::frameCount() const { +#ifdef ANDROID_ANIMATED_GIF + if (m_decoder.m_gifDecoder) { + return m_decoder.m_gifDecoder->failed() ? 0 + : m_decoder.m_gifDecoder->frameCount(); + } +#endif // i.e. 0 frames if we're not decoded, or 1 frame if we are return m_decoder.m_image != NULL; } 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; + return new SkBitmapRef(buffer->bitmap()); + } +#else SkASSERT(index == 0); +#endif SkASSERT(m_decoder.m_image != NULL); m_decoder.m_image->ref(); return m_decoder.m_image; @@ -255,8 +341,18 @@ SkBitmapRef* ImageSource::createFrameAtIndex(size_t index) float ImageSource::frameDurationAtIndex(size_t index) { - SkASSERT(index == 0); 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 @@ -268,7 +364,21 @@ float ImageSource::frameDurationAtIndex(size_t index) 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 @@ -285,12 +395,32 @@ bool ImageSource::frameHasAlphaAtIndex(size_t index) 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; } void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* data, bool allDataReceived) { +#ifdef ANDROID_ANIMATED_GIF + if (!destroyAll) { + if (m_decoder.m_gifDecoder) + m_decoder.m_gifDecoder->clearFrameBufferCache(clearBeforeFrame); + return; + } + + delete m_decoder.m_gifDecoder; + m_decoder.m_gifDecoder = 0; + if (data) + setData(data, allDataReceived); +#endif // do nothing, since the cache is managed elsewhere } @@ -303,6 +433,10 @@ IntSize ImageSource::frameSizeAtIndex(size_t index) const String ImageSource::filenameExtension() const { // FIXME: need to add virtual to our decoders to return "jpg/png/gif/..." +#ifdef ANDROID_ANIMATED_GIF + if (m_decoder.m_gifDecoder) + return m_decoder.m_gifDecoder->filenameExtension(); +#endif return String(); } diff --git a/WebCore/platform/graphics/skia/NativeImageSkia.cpp b/WebCore/platform/graphics/skia/NativeImageSkia.cpp index e59d1e2..477be05 100644 --- a/WebCore/platform/graphics/skia/NativeImageSkia.cpp +++ b/WebCore/platform/graphics/skia/NativeImageSkia.cpp @@ -30,7 +30,9 @@ #include "config.h" +#if PLATFORM(SKIA) #include "skia/ext/image_operations.h" +#endif #include "NativeImageSkia.h" #include "SkiaUtils.h" @@ -63,9 +65,10 @@ bool NativeImageSkia::hasResizedBitmap(int w, int h) const SkBitmap NativeImageSkia::resizedBitmap(int w, int h) const { +#if PLATFORM(SKIA) if (m_resizedImage.width() != w || m_resizedImage.height() != h) m_resizedImage = skia::ImageOperations::Resize(*this, skia::ImageOperations::RESIZE_LANCZOS3, w, h); - +#endif return m_resizedImage; } diff --git a/WebCore/platform/image-decoders/skia/GIFImageDecoder.cpp b/WebCore/platform/image-decoders/skia/GIFImageDecoder.cpp index 38bfa97..2d77943 100644 --- a/WebCore/platform/image-decoders/skia/GIFImageDecoder.cpp +++ b/WebCore/platform/image-decoders/skia/GIFImageDecoder.cpp @@ -193,6 +193,7 @@ void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) // can be asked to clear more frames than we currently have. if (m_frameBufferCache.isEmpty()) return; // Nothing to do. + // The "-1" here is tricky. It does not mean that |clearBeforeFrame| is the // last frame we wish to preserve, but rather that we never want to clear // the very last frame in the cache: it's empty (so clearing it is @@ -203,21 +204,36 @@ void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) // this case. clearBeforeFrame = std::min(clearBeforeFrame, m_frameBufferCache.size() - 1); const Vector<RGBA32Buffer>::iterator end(m_frameBufferCache.begin() + clearBeforeFrame); - for (Vector<RGBA32Buffer>::iterator i(m_frameBufferCache.begin()); i != end; ++i) { - if (i->status() == RGBA32Buffer::FrameEmpty) - continue; // Nothing to do. - - // The layout of frames is: - // [empty frames][complete frames][partial frame][empty frames] - // ...where each of these groups may be empty. We should not clear a - // partial frame since that's what's being decoded right now, and we - // also should not clear the last complete frame, since it may be needed - // when constructing the next frame. Note that "i + 1" is safe since - // i < end < m_frameBufferCache.end(). - if ((i->status() == RGBA32Buffer::FramePartial) || ((i + 1)->status() != RGBA32Buffer::FrameComplete)) - break; - - i->clear(); + + // We need to preserve frames such that: + // * We don't clear |end| + // * We don't clear the frame we're currently decoding + // * We don't clear any frame from which a future initFrameBuffer() call + // will copy bitmap data + // All other frames can be cleared. Because of the constraints on when + // ImageSource::clear() can be called (see ImageSource.h), we're guaranteed + // not to have non-empty frames after the frame we're currently decoding. + // So, scan backwards from |end| as follows: + // * If the frame is empty, we're still past any frames we care about. + // * If the frame is complete, but is DisposeOverwritePrevious, we'll + // skip over it in future initFrameBuffer() calls. We can clear it + // unless it's |end|, and keep scanning. For any other disposal method, + // stop scanning, as we've found the frame initFrameBuffer() will need + // next. + // * If the frame is partial, we're decoding it, so don't clear it; if it + // has a disposal method other than DisposeOverwritePrevious, stop + // scanning, as we'll only need this frame when decoding the next one. + Vector<RGBA32Buffer>::iterator i(end); + for (; (i != m_frameBufferCache.begin()) && ((i->status() == RGBA32Buffer::FrameEmpty) || (i->disposalMethod() == RGBA32Buffer::DisposeOverwritePrevious)); --i) { + if ((i->status() == RGBA32Buffer::FrameComplete) && (i != end)) + i->clear(); + } + + // Now |i| holds the last frame we need to preserve; clear prior frames. + for (Vector<RGBA32Buffer>::iterator j(m_frameBufferCache.begin()); j != i; ++j) { + ASSERT(j->status() != RGBA32Buffer::FramePartial); + if (j->status() != RGBA32Buffer::FrameEmpty) + j->clear(); } } @@ -278,7 +294,6 @@ bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex) // first frame specifies this method, it will get treated like // DisposeOverwriteBgcolor below and reset to a completely empty image.) const RGBA32Buffer* prevBuffer = &m_frameBufferCache[--frameIndex]; - ASSERT(prevBuffer->status() == RGBA32Buffer::FrameComplete); RGBA32Buffer::FrameDisposalMethod prevMethod = prevBuffer->disposalMethod(); while ((frameIndex > 0) @@ -286,6 +301,7 @@ bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex) prevBuffer = &m_frameBufferCache[--frameIndex]; prevMethod = prevBuffer->disposalMethod(); } + ASSERT(prevBuffer->status() == RGBA32Buffer::FrameComplete); if ((prevMethod == RGBA32Buffer::DisposeNotSpecified) || (prevMethod == RGBA32Buffer::DisposeKeep)) { @@ -310,6 +326,9 @@ bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex) // Unnecessary (but safe); see comments on the similar call above. buffer->setHasAlpha(prevBuffer->hasAlpha()); SkBitmap& bitmap = buffer->bitmap(); +#ifdef ANDROID_ANIMATED_GIF + SkAutoLockPixels alp(bitmap); +#endif for (int y = prevRect.y(); y < prevRect.bottom(); ++y) { for (int x = prevRect.x(); x < prevRect.right(); ++x) buffer->setRGBA(bitmap.getAddr32(x, y), 0, 0, 0, 0); @@ -361,6 +380,10 @@ void GIFImageDecoder::haveDecodedRow(unsigned frameIndex, if (!colorMap) return; +#ifdef ANDROID_ANIMATED_GIF + // Lock the pixels properly. Should be submitted back to webkit. + SkAutoLockPixels alp(buffer.bitmap()); +#endif // 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. // diff --git a/WebCore/platform/image-decoders/skia/ImageDecoder.h b/WebCore/platform/image-decoders/skia/ImageDecoder.h index 9988a3e..b983315 100644 --- a/WebCore/platform/image-decoders/skia/ImageDecoder.h +++ b/WebCore/platform/image-decoders/skia/ImageDecoder.h @@ -39,6 +39,9 @@ #include <wtf/Vector.h> #include "SkBitmap.h" +#ifdef ANDROID_ANIMATED_GIF +#include "SkColor.h" +#endif namespace WebCore { @@ -169,7 +172,7 @@ namespace WebCore { bmp.setConfig(SkBitmap::kARGB_8888_Config, width, height); if (!bmp.allocPixels()) return false; // Allocation failure, maybe the bitmap was too big. - + // Clear the image. bmp.eraseARGB(0, 0, 0, 0); @@ -199,6 +202,10 @@ namespace WebCore { static void setRGBA(uint32_t* dest, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { // We store this data pre-multiplied. +#ifdef ANDROID_ANIMATED_GIF + // Chrome should take this change as well. + *dest = SkPreMultiplyARGB(a, r, g, b); +#else if (a == 0) *dest = 0; else { @@ -210,6 +217,7 @@ namespace WebCore { } *dest = (a << 24 | r << 16 | g << 8 | b); } +#endif } void setRGBA(int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) |