WebGL: Add a class to abstract the status of the Image in texImage2D() and texSubImage2D()
https://bugs.webkit.org/show_bug.cgi?id=103606

Patch by Jun Jiang <jun.a.jiang@intel.com> on 2012-11-30
Reviewed by Kenneth Russell.

In texImage2D() and texSubImage2D() for WebGL, the status of the Image is extracted and kept in the function GraphicsContext3D::getImageData() but provides no interface or guaranteed way to use the status of the Image outside GraphicsContext3D::getImageData() safely. For example, you can not get the address of the raw Image data and operate it outside the scope of the GraphicsContext3D::getImageData() and there is at least one memory copy existed from the Image to the intermediate vector.
       This patch refactors the code by adding a ImageExtractor class to abstract and keep the status of the Image. The lifetime and validity of Image status are determined by the lifetime of the object instead of the scope of getImageData(). It provides flexibility on future optimizations.

No new tests. It is a code refactoring patch.

* html/canvas/WebGLRenderingContext.cpp:
(WebCore):
(WebCore::WebGLRenderingContext::texImage2DImpl):
(WebCore::WebGLRenderingContext::texSubImage2DImpl):
* platform/graphics/GraphicsContext3D.cpp:
(WebCore::GraphicsContext3D::ImageExtractor::ImageExtractor):
(WebCore):
(WebCore::GraphicsContext3D::packImageData):
* platform/graphics/GraphicsContext3D.h:
(WebCore):
(GraphicsContext3D):
(ImageExtractor):
(WebCore::GraphicsContext3D::ImageExtractor::extractSucceeded):
(WebCore::GraphicsContext3D::ImageExtractor::imagePixelData):
(WebCore::GraphicsContext3D::ImageExtractor::imageWidth):
(WebCore::GraphicsContext3D::ImageExtractor::imageHeight):
(WebCore::GraphicsContext3D::ImageExtractor::imageSourceFormat):
(WebCore::GraphicsContext3D::ImageExtractor::imageAlphaOp):
(WebCore::GraphicsContext3D::ImageExtractor::imageSourceUnpackAlignment):
* platform/graphics/NativeImagePtr.h:
* platform/graphics/cairo/GraphicsContext3DCairo.cpp:
(WebCore::GraphicsContext3D::ImageExtractor::~ImageExtractor):
(WebCore):
(WebCore::GraphicsContext3D::ImageExtractor::extractImage):
* platform/graphics/cg/GraphicsContext3DCG.cpp:
(WebCore::GraphicsContext3D::ImageExtractor::~ImageExtractor):
(WebCore):
(WebCore::GraphicsContext3D::ImageExtractor::extractImage):
* platform/graphics/clutter/GraphicsContext3DClutter.cpp:
(WebCore::GraphicsContext3D::ImageExtractor::~ImageExtractor):
(WebCore):
(WebCore::GraphicsContext3D::ImageExtractor::extractImage):
* platform/graphics/efl/GraphicsContext3DEfl.cpp:
(WebCore::GraphicsContext3D::ImageExtractor::~ImageExtractor):
(WebCore):
(WebCore::GraphicsContext3D::ImageExtractor::extractImage):
* platform/graphics/qt/GraphicsContext3DQt.cpp:
(WebCore::GraphicsContext3D::ImageExtractor::~ImageExtractor):
(WebCore):
(WebCore::GraphicsContext3D::ImageExtractor::extractImage):
* platform/graphics/skia/GraphicsContext3DSkia.cpp:
(WebCore::GraphicsContext3D::ImageExtractor::~ImageExtractor):
(WebCore):
(WebCore::GraphicsContext3D::ImageExtractor::extractImage):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@136282 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 437dbcc..12c0c1f 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,60 @@
+2012-11-30  Jun Jiang  <jun.a.jiang@intel.com>
+
+        WebGL: Add a class to abstract the status of the Image in texImage2D() and texSubImage2D()
+        https://bugs.webkit.org/show_bug.cgi?id=103606
+
+        Reviewed by Kenneth Russell.
+
+        In texImage2D() and texSubImage2D() for WebGL, the status of the Image is extracted and kept in the function GraphicsContext3D::getImageData() but provides no interface or guaranteed way to use the status of the Image outside GraphicsContext3D::getImageData() safely. For example, you can not get the address of the raw Image data and operate it outside the scope of the GraphicsContext3D::getImageData() and there is at least one memory copy existed from the Image to the intermediate vector.
+       This patch refactors the code by adding a ImageExtractor class to abstract and keep the status of the Image. The lifetime and validity of Image status are determined by the lifetime of the object instead of the scope of getImageData(). It provides flexibility on future optimizations.
+
+        No new tests. It is a code refactoring patch.
+
+        * html/canvas/WebGLRenderingContext.cpp:
+        (WebCore):
+        (WebCore::WebGLRenderingContext::texImage2DImpl):
+        (WebCore::WebGLRenderingContext::texSubImage2DImpl):
+        * platform/graphics/GraphicsContext3D.cpp:
+        (WebCore::GraphicsContext3D::ImageExtractor::ImageExtractor):
+        (WebCore):
+        (WebCore::GraphicsContext3D::packImageData):
+        * platform/graphics/GraphicsContext3D.h:
+        (WebCore):
+        (GraphicsContext3D):
+        (ImageExtractor):
+        (WebCore::GraphicsContext3D::ImageExtractor::extractSucceeded):
+        (WebCore::GraphicsContext3D::ImageExtractor::imagePixelData):
+        (WebCore::GraphicsContext3D::ImageExtractor::imageWidth):
+        (WebCore::GraphicsContext3D::ImageExtractor::imageHeight):
+        (WebCore::GraphicsContext3D::ImageExtractor::imageSourceFormat):
+        (WebCore::GraphicsContext3D::ImageExtractor::imageAlphaOp):
+        (WebCore::GraphicsContext3D::ImageExtractor::imageSourceUnpackAlignment):
+        * platform/graphics/NativeImagePtr.h:
+        * platform/graphics/cairo/GraphicsContext3DCairo.cpp:
+        (WebCore::GraphicsContext3D::ImageExtractor::~ImageExtractor):
+        (WebCore):
+        (WebCore::GraphicsContext3D::ImageExtractor::extractImage):
+        * platform/graphics/cg/GraphicsContext3DCG.cpp:
+        (WebCore::GraphicsContext3D::ImageExtractor::~ImageExtractor):
+        (WebCore):
+        (WebCore::GraphicsContext3D::ImageExtractor::extractImage):
+        * platform/graphics/clutter/GraphicsContext3DClutter.cpp:
+        (WebCore::GraphicsContext3D::ImageExtractor::~ImageExtractor):
+        (WebCore):
+        (WebCore::GraphicsContext3D::ImageExtractor::extractImage):
+        * platform/graphics/efl/GraphicsContext3DEfl.cpp:
+        (WebCore::GraphicsContext3D::ImageExtractor::~ImageExtractor):
+        (WebCore):
+        (WebCore::GraphicsContext3D::ImageExtractor::extractImage):
+        * platform/graphics/qt/GraphicsContext3DQt.cpp:
+        (WebCore::GraphicsContext3D::ImageExtractor::~ImageExtractor):
+        (WebCore):
+        (WebCore::GraphicsContext3D::ImageExtractor::extractImage):
+        * platform/graphics/skia/GraphicsContext3DSkia.cpp:
+        (WebCore::GraphicsContext3D::ImageExtractor::~ImageExtractor):
+        (WebCore):
+        (WebCore::GraphicsContext3D::ImageExtractor::extractImage):
+
 2012-11-30  Simon Fraser  <simon.fraser@apple.com>
 
         Make RenderLayer::updateLayerPosition() private
diff --git a/Source/WebCore/html/canvas/WebGLRenderingContext.cpp b/Source/WebCore/html/canvas/WebGLRenderingContext.cpp
index 70cb853..5a8dc95 100644
--- a/Source/WebCore/html/canvas/WebGLRenderingContext.cpp
+++ b/Source/WebCore/html/canvas/WebGLRenderingContext.cpp
@@ -3619,10 +3619,17 @@
     if (!validateSettableTexFormat("texImage2D", internalformat))
         return;
     Vector<uint8_t> data;
-    if (!m_context->extractImageData(image, format, type, flipY, premultiplyAlpha, m_unpackColorspaceConversion == GraphicsContext3D::NONE, data)) {
+    GraphicsContext3D::ImageExtractor imageExtractor(image, premultiplyAlpha, m_unpackColorspaceConversion == GraphicsContext3D::NONE);
+    if (!imageExtractor.extractSucceeded()) {
         synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texImage2D", "bad image data");
         return;
     }
+
+    if (!m_context->packImageData(image, imageExtractor.imagePixelData(), format, type, flipY, imageExtractor.imageAlphaOp(), imageExtractor.imageSourceFormat(), imageExtractor.imageWidth(), imageExtractor.imageHeight(), imageExtractor.imageSourceUnpackAlignment(), data)) {
+        synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texImage2D", "packImageData error");
+        return;
+    }
+
     if (m_unpackAlignment != 1)
         m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, 1);
     texImage2DBase(target, level, internalformat, image->width(), image->height(), 0,
@@ -3855,10 +3862,17 @@
     if (isContextLost())
         return;
     Vector<uint8_t> data;
-    if (!m_context->extractImageData(image, format, type, flipY, premultiplyAlpha, m_unpackColorspaceConversion == GraphicsContext3D::NONE, data)) {
+    GraphicsContext3D::ImageExtractor imageExtractor(image, premultiplyAlpha, m_unpackColorspaceConversion == GraphicsContext3D::NONE);  
+    if (!imageExtractor.extractSucceeded()) {
         synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texSubImage2D", "bad image");
         return;
     }
+
+    if (!m_context->packImageData(image, imageExtractor.imagePixelData(), format, type, flipY, imageExtractor.imageAlphaOp(), imageExtractor.imageSourceFormat(), imageExtractor.imageWidth(), imageExtractor.imageHeight(), imageExtractor.imageSourceUnpackAlignment(), data)) {
+        synthesizeGLError(GraphicsContext3D::INVALID_VALUE, "texImage2D", "packImageData error");
+        return;
+    }
+
     if (m_unpackAlignment != 1)
         m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, 1);
     texSubImage2DBase(target, level, xoffset, yoffset, image->width(), image->height(),
diff --git a/Source/WebCore/platform/graphics/GraphicsContext3D.cpp b/Source/WebCore/platform/graphics/GraphicsContext3D.cpp
index a98b546..ffa8ec6 100644
--- a/Source/WebCore/platform/graphics/GraphicsContext3D.cpp
+++ b/Source/WebCore/platform/graphics/GraphicsContext3D.cpp
@@ -168,30 +168,57 @@
     return GraphicsContext3D::NO_ERROR;
 }
 
-bool GraphicsContext3D::extractImageData(Image* image,
-                                         GC3Denum format,
-                                         GC3Denum type,
-                                         bool flipY,
-                                         bool premultiplyAlpha,
-                                         bool ignoreGammaAndColorProfile,
-                                         Vector<uint8_t>& data)
+GraphicsContext3D::ImageExtractor::ImageExtractor(Image* image, bool premultiplyAlpha, bool ignoreGammaAndColorProfile)
 {
-    if (!image)
+    m_image = image;
+    m_extractSucceeded = extractImage(premultiplyAlpha, ignoreGammaAndColorProfile);
+}
+
+bool GraphicsContext3D::packImageData(
+    Image* image,
+    const void* pixels,
+    GC3Denum format,
+    GC3Denum type,
+    bool flipY,
+    AlphaOp alphaOp,
+    SourceDataFormat sourceFormat, 
+    unsigned width,
+    unsigned height,
+    unsigned sourceUnpackAlignment,
+    Vector<uint8_t>& data)
+{
+    if (!pixels)
         return false;
-    if (!getImageData(image, format, type, premultiplyAlpha, ignoreGammaAndColorProfile, data))
+
+    unsigned packedSize;
+    // Output data is tightly packed (alignment == 1).
+    if (computeImageSizeInBytes(format, type, width, height, 1, &packedSize, 0) != GraphicsContext3D::NO_ERROR)
+        return false;
+    data.resize(packedSize);
+
+    if (!packPixels(reinterpret_cast<const uint8_t*>(pixels),
+        sourceFormat,
+        width,
+        height,
+        sourceUnpackAlignment,
+        format,
+        type,
+        alphaOp,
+        data.data()))
         return false;
     if (flipY) {
-        unsigned int componentsPerPixel, bytesPerComponent;
+        unsigned componentsPerPixel, bytesPerComponent;
         if (!computeFormatAndTypeParameters(format, type,
-                                            &componentsPerPixel,
-                                            &bytesPerComponent))
+            &componentsPerPixel,
+            &bytesPerComponent))
             return false;
         // The image data is tightly packed, and we upload it as such.
-        unsigned int unpackAlignment = 1;
-        flipVertically(data.data(), image->width(), image->height(),
-                       componentsPerPixel * bytesPerComponent,
-                       unpackAlignment);
+        unsigned unpackAlignment = 1;
+        flipVertically(data.data(), width, height,
+            componentsPerPixel * bytesPerComponent,
+            unpackAlignment);
     }
+
     if (ImageObserver *observer = image->imageObserver())
         observer->didDraw(image);
     return true;
diff --git a/Source/WebCore/platform/graphics/GraphicsContext3D.h b/Source/WebCore/platform/graphics/GraphicsContext3D.h
index 4ede0f8..95eea7a 100644
--- a/Source/WebCore/platform/graphics/GraphicsContext3D.h
+++ b/Source/WebCore/platform/graphics/GraphicsContext3D.h
@@ -26,6 +26,7 @@
 #ifndef GraphicsContext3D_h
 #define GraphicsContext3D_h
 
+#include "Image.h"
 #include "IntRect.h"
 #include "GraphicsTypes3D.h"
 #include "PlatformLayer.h"
@@ -101,6 +102,7 @@
 class HostWindow;
 class Image;
 class ImageBuffer;
+class ImageSource;
 class ImageData;
 class IntRect;
 class IntSize;
@@ -564,18 +566,6 @@
                                      unsigned int* imageSizeInBytes,
                                      unsigned int* paddingInBytes);
 
-    // Extracts the contents of the given Image into the passed Vector,
-    // packing the pixel data according to the given format and type,
-    // and obeying the flipY, premultiplyAlpha, and ignoreGammaAndColorProfile
-    // flags. Returns true upon success.
-    static bool extractImageData(Image*,
-                          GC3Denum format,
-                          GC3Denum type,
-                          bool flipY,
-                          bool premultiplyAlpha,
-                          bool ignoreGammaAndColorProfile,
-                          Vector<uint8_t>& data);
-
     // Extracts the contents of the given ImageData into the passed Vector,
     // packing the pixel data according to the given format and type,
     // and obeying the flipY and premultiplyAlpha flags. Returns true
@@ -874,35 +864,6 @@
 
     static unsigned getChannelBitsByFormat(GC3Denum);
 
-  private:
-    GraphicsContext3D(Attributes, HostWindow*, RenderStyle = RenderOffscreen);
-
-    // Each platform must provide an implementation of this method.
-    //
-    // Gets the data for the given Image into outputVector in the
-    // format specified by the (OpenGL-style) format and type
-    // arguments. Despite the fact that the outputVector contains
-    // uint8_t, if the format and type specify packed pixels, then
-    // it will essentially contain uint16_t after the extraction
-    // process.
-    //
-    // If premultiplyAlpha is true, the alpha channel, if any,
-    // will be multiplied into the color channels during the
-    // extraction process. This premultiplication occurs before
-    // any packing of pixel data.
-    //
-    // If ignoreGammaAndColorProfile is true, gamma correction and ICC
-    // profile won't be applied.
-    //
-    // No vertical flip of the image data is performed by this
-    // method.
-    static bool getImageData(Image*,
-                      GC3Denum format,
-                      GC3Denum type,
-                      bool premultiplyAlpha,
-                      bool ignoreGammaAndColorProfile,
-                      Vector<uint8_t>& outputVector);
-
     // Possible alpha operations that may need to occur during
     // pixel packing. FIXME: kAlphaDoUnmultiply is lossy and must
     // be removed.
@@ -912,6 +873,59 @@
         AlphaDoUnmultiply = 2
     };
 
+    // Packs the contents of the given Image which is passed in |pixels| into the passed Vector
+    // according to the given format and type, and obeying the flipY and premultiplyAlpha flags.
+    // Returns true upon success.
+    static bool packImageData(Image*, const void* pixels, GC3Denum format, GC3Denum type, bool flipY, AlphaOp, SourceDataFormat sourceFormat, unsigned width, unsigned height, unsigned sourceUnpackAlignment, Vector<uint8_t>& data);
+
+    class ImageExtractor {
+    public:
+        ImageExtractor(Image*, bool premultiplyAlpha, bool ignoreGammaAndColorProfile);
+
+        // Each platform must provide an implementation of this method to deallocate or release resources
+        // associated with the image if needed.
+        ~ImageExtractor();
+
+        bool extractSucceeded() { return m_extractSucceeded; }
+        const void* imagePixelData() { return m_imagePixelData; }
+        unsigned imageWidth() { return m_imageWidth; }
+        unsigned imageHeight() { return m_imageHeight; }
+        SourceDataFormat imageSourceFormat() { return m_imageSourceFormat; }
+        AlphaOp imageAlphaOp() { return m_alphaOp; }
+        unsigned imageSourceUnpackAlignment() { return m_imageSourceUnpackAlignment; } 
+    private:
+        // Each platform must provide an implementation of this method.
+        // Extracts the image and keeps track of its status, such as width, height, Source Alignment, format and AlphaOp etc
+        // needs to lock the resources or relevant data if needed
+        // Return true upon success
+        bool extractImage(bool premultiplyAlpha, bool ignoreGammaAndColorProfile);
+
+#if USE(SKIA)
+        OwnPtr<NativeImageSkia> m_nativeImage;
+        NativeImageSkia* m_skiaImage;
+#elif USE(CAIRO)
+        ImageSource* m_decoder;
+        RefPtr<cairo_surface_t> m_imageSurface;
+#elif USE(CG)
+        CGImageRef m_cgImage;
+        RetainPtr<CGImageRef> m_decodedImage;
+        RetainPtr<CFDataRef> m_pixelData;
+#elif PLATFORM(QT)
+        QImage m_qtImage;
+#endif
+        Image* m_image;
+        bool m_extractSucceeded;
+        const void* m_imagePixelData;
+        unsigned m_imageWidth;
+        unsigned m_imageHeight;
+        SourceDataFormat m_imageSourceFormat;
+        AlphaOp m_alphaOp;
+        unsigned m_imageSourceUnpackAlignment;
+    };
+
+private:
+    GraphicsContext3D(Attributes, HostWindow*, RenderStyle = RenderOffscreen);
+
     // Helper for getImageData which implements packing of pixel
     // data into the specified OpenGL destination format and type.
     // A sourceUnpackAlignment of zero indicates that the source
diff --git a/Source/WebCore/platform/graphics/NativeImagePtr.h b/Source/WebCore/platform/graphics/NativeImagePtr.h
index c98f8c2..0530ade 100644
--- a/Source/WebCore/platform/graphics/NativeImagePtr.h
+++ b/Source/WebCore/platform/graphics/NativeImagePtr.h
@@ -34,6 +34,7 @@
 #elif USE(CG)
 typedef struct CGImage* CGImageRef;
 #elif PLATFORM(QT)
+#include "NativeImageQt.h"
 #include <qglobal.h>
 QT_BEGIN_NAMESPACE
 class QPixmap;
@@ -41,6 +42,7 @@
 #elif USE(CAIRO)
 #include "NativeImageCairo.h"
 #elif USE(SKIA)
+#include "NativeImageSkia.h"
 namespace WebCore {
 class NativeImageSkia;
 }
diff --git a/Source/WebCore/platform/graphics/cairo/GraphicsContext3DCairo.cpp b/Source/WebCore/platform/graphics/cairo/GraphicsContext3DCairo.cpp
index 8ba049b..bd3bdfd 100644
--- a/Source/WebCore/platform/graphics/cairo/GraphicsContext3DCairo.cpp
+++ b/Source/WebCore/platform/graphics/cairo/GraphicsContext3DCairo.cpp
@@ -163,57 +163,61 @@
     ::glDeleteFramebuffers(1, &m_fbo);
 }
 
-bool GraphicsContext3D::getImageData(Image* image, unsigned int format, unsigned int type, bool premultiplyAlpha, bool ignoreGammaAndColorProfile, Vector<uint8_t>& outputVector)
+GraphicsContext3D::ImageExtractor::~ImageExtractor()
 {
-    if (!image)
+    if (m_decoder)
+        delete m_decoder;
+}
+
+bool GraphicsContext3D::ImageExtractor::extractImage(bool premultiplyAlpha, bool ignoreGammaAndColorProfile)
+{
+    if (!m_image)
         return false;
     // We need this to stay in scope because the native image is just a shallow copy of the data.
-    ImageSource decoder(premultiplyAlpha ? ImageSource::AlphaPremultiplied : ImageSource::AlphaNotPremultiplied,
-                        ignoreGammaAndColorProfile ? ImageSource::GammaAndColorProfileIgnored : ImageSource::GammaAndColorProfileApplied);
-    AlphaOp alphaOp = AlphaDoNothing;
-    RefPtr<cairo_surface_t> imageSurface;
-    if (image->data()) {
-        decoder.setData(image->data(), true);
+    m_decoder = new ImageSource(premultiplyAlpha ? ImageSource::AlphaPremultiplied : ImageSource::AlphaNotPremultiplied, ignoreGammaAndColorProfile ? ImageSource::GammaAndColorProfileIgnored : ImageSource::GammaAndColorProfileApplied);
+    if (!m_decoder)
+        return false;
+    ImageSource& decoder = *m_decoder;
+
+    m_alphaOp = AlphaDoNothing;
+    if (m_image->data()) {
+        decoder.setData(m_image->data(), true);
         if (!decoder.frameCount() || !decoder.frameIsCompleteAtIndex(0))
             return false;
         OwnPtr<NativeImageCairo> nativeImage = adoptPtr(decoder.createFrameAtIndex(0));
-        imageSurface = nativeImage->surface();
+        m_imageSurface = nativeImage->surface();
     } else {
-        NativeImageCairo* nativeImage = image->nativeImageForCurrentFrame();
-        imageSurface = (nativeImage) ? nativeImage->surface() : 0;
+        NativeImageCairo* nativeImage = m_image->nativeImageForCurrentFrame();
+        m_imageSurface = (nativeImage) ? nativeImage->surface() : 0;
         if (!premultiplyAlpha)
-            alphaOp = AlphaDoUnmultiply;
+            m_alphaOp = AlphaDoUnmultiply;
     }
 
-    if (!imageSurface)
+    if (!m_imageSurface)
         return false;
 
-    int width = cairo_image_surface_get_width(imageSurface.get());
-    int height = cairo_image_surface_get_height(imageSurface.get());
-    if (!width || !height)
+    m_imageWidth = cairo_image_surface_get_width(m_imageSurface.get());
+    m_imageHeight = cairo_image_surface_get_height(m_imageSurface.get());
+    if (!m_imageWidth || !m_imageHeight)
         return false;
 
-    if (cairo_image_surface_get_format(imageSurface.get()) != CAIRO_FORMAT_ARGB32)
+    if (cairo_image_surface_get_format(m_imageSurface.get()) != CAIRO_FORMAT_ARGB32)
         return false;
 
     unsigned int srcUnpackAlignment = 1;
-    size_t bytesPerRow = cairo_image_surface_get_stride(imageSurface.get());
+    size_t bytesPerRow = cairo_image_surface_get_stride(m_imageSurface.get());
     size_t bitsPerPixel = 32;
-    unsigned int padding = bytesPerRow - bitsPerPixel / 8 * width;
+    unsigned padding = bytesPerRow - bitsPerPixel / 8 * m_imageWidth;
     if (padding) {
         srcUnpackAlignment = padding + 1;
         while (bytesPerRow % srcUnpackAlignment)
             ++srcUnpackAlignment;
     }
 
-    unsigned int packedSize;
-    // Output data is tightly packed (alignment == 1).
-    if (computeImageSizeInBytes(format, type, width, height, 1, &packedSize, 0) != GraphicsContext3D::NO_ERROR)
-        return false;
-    outputVector.resize(packedSize);
-
-    return packPixels(cairo_image_surface_get_data(imageSurface.get()), SourceFormatBGRA8,
-                      width, height, srcUnpackAlignment, format, type, alphaOp, outputVector.data());
+    m_imagePixelData = cairo_image_surface_get_data(m_imageSurface.get());
+    m_imageSourceFormat = SourceFormatBGRA8;
+    m_imageSourceUnpackAlignment = srcUnpackAlignment;
+    return true;
 }
 
 void GraphicsContext3D::paintToCanvas(const unsigned char* imagePixels, int imageWidth, int imageHeight, int canvasWidth, int canvasHeight, PlatformContextCairo* context)
diff --git a/Source/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp b/Source/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp
index ac00c43..942cddb 100644
--- a/Source/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp
+++ b/Source/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp
@@ -89,41 +89,38 @@
     return formatTable[formatBase][(is16BitFormat ? 2 : 0) + (bigEndian ? 1 : 0)];
 }
 
-bool GraphicsContext3D::getImageData(Image* image,
-                                     GC3Denum format,
-                                     GC3Denum type,
-                                     bool premultiplyAlpha,
-                                     bool ignoreGammaAndColorProfile,
-                                     Vector<uint8_t>& outputVector)
+GraphicsContext3D::ImageExtractor::~ImageExtractor()
 {
-    if (!image)
+}
+
+bool GraphicsContext3D::ImageExtractor::extractImage(bool premultiplyAlpha, bool ignoreGammaAndColorProfile)
+{
+    if (!m_image)
         return false;
-    CGImageRef cgImage;
-    RetainPtr<CGImageRef> decodedImage;
-    bool hasAlpha = image->isBitmapImage() ? image->currentFrameHasAlpha() : true;
-    if ((ignoreGammaAndColorProfile || (hasAlpha && !premultiplyAlpha)) && image->data()) {
+    bool hasAlpha = m_image->isBitmapImage() ? m_image->currentFrameHasAlpha() : true;
+    if ((ignoreGammaAndColorProfile || (hasAlpha && !premultiplyAlpha)) && m_image->data()) {
         ImageSource decoder(ImageSource::AlphaNotPremultiplied,
                             ignoreGammaAndColorProfile ? ImageSource::GammaAndColorProfileIgnored : ImageSource::GammaAndColorProfileApplied);
-        decoder.setData(image->data(), true);
+        decoder.setData(m_image->data(), true);
         if (!decoder.frameCount())
             return false;
-        decodedImage.adoptCF(decoder.createFrameAtIndex(0));
-        cgImage = decodedImage.get();
+        m_decodedImage.adoptCF(decoder.createFrameAtIndex(0));
+        m_cgImage = m_decodedImage.get();
     } else
-        cgImage = image->nativeImageForCurrentFrame();
-    if (!cgImage)
+        m_cgImage = m_image->nativeImageForCurrentFrame();
+    if (!m_cgImage)
         return false;
 
-    size_t width = CGImageGetWidth(cgImage);
-    size_t height = CGImageGetHeight(cgImage);
-    if (!width || !height)
+    m_imageWidth = CGImageGetWidth(m_cgImage);
+    m_imageHeight = CGImageGetHeight(m_cgImage);
+    if (!m_imageWidth || !m_imageHeight)
         return false;
 
     // See whether the image is using an indexed color space, and if
     // so, re-render it into an RGB color space. The image re-packing
     // code requires color data, not color table indices, for the
     // image data.
-    CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage);
+    CGColorSpaceRef colorSpace = CGImageGetColorSpace(m_cgImage);
     CGColorSpaceModel model = CGColorSpaceGetModel(colorSpace);
     if (model == kCGColorSpaceModelIndexed) {
         RetainPtr<CGContextRef> bitmapContext;
@@ -131,7 +128,7 @@
         // the color table, which would allow us to avoid premultiplying the
         // alpha channel. Creation of a bitmap context with an alpha channel
         // doesn't seem to work unless it's premultiplied.
-        bitmapContext.adoptCF(CGBitmapContextCreate(0, width, height, 8, width * 4,
+        bitmapContext.adoptCF(CGBitmapContextCreate(0, m_imageWidth, m_imageHeight, 8, m_imageWidth * 4,
                                                     deviceRGBColorSpaceRef(),
                                                     kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
         if (!bitmapContext)
@@ -139,22 +136,22 @@
 
         CGContextSetBlendMode(bitmapContext.get(), kCGBlendModeCopy);
         CGContextSetInterpolationQuality(bitmapContext.get(), kCGInterpolationNone);
-        CGContextDrawImage(bitmapContext.get(), CGRectMake(0, 0, width, height), cgImage);
+        CGContextDrawImage(bitmapContext.get(), CGRectMake(0, 0, m_imageWidth, m_imageHeight), m_cgImage);
 
         // Now discard the original CG image and replace it with a copy from the bitmap context.
-        decodedImage.adoptCF(CGBitmapContextCreateImage(bitmapContext.get()));
-        cgImage = decodedImage.get();
+        m_decodedImage.adoptCF(CGBitmapContextCreateImage(bitmapContext.get()));
+        m_cgImage = m_decodedImage.get();
     }
 
-    size_t bitsPerComponent = CGImageGetBitsPerComponent(cgImage);
-    size_t bitsPerPixel = CGImageGetBitsPerPixel(cgImage);
+    size_t bitsPerComponent = CGImageGetBitsPerComponent(m_cgImage);
+    size_t bitsPerPixel = CGImageGetBitsPerPixel(m_cgImage);
     if (bitsPerComponent != 8 && bitsPerComponent != 16)
         return false;
     if (bitsPerPixel % bitsPerComponent)
         return false;
     size_t componentsPerPixel = bitsPerPixel / bitsPerComponent;
 
-    CGBitmapInfo bitInfo = CGImageGetBitmapInfo(cgImage);
+    CGBitmapInfo bitInfo = CGImageGetBitmapInfo(m_cgImage);
     bool bigEndianSource = false;
     // These could technically be combined into one large switch
     // statement, but we prefer not to so that we fail fast if we
@@ -194,18 +191,18 @@
         }
     }
 
-    AlphaOp neededAlphaOp = AlphaDoNothing;
+    m_alphaOp = AlphaDoNothing;
     AlphaFormat alphaFormat = AlphaFormatNone;
-    switch (CGImageGetAlphaInfo(cgImage)) {
+    switch (CGImageGetAlphaInfo(m_cgImage)) {
     case kCGImageAlphaPremultipliedFirst:
         if (!premultiplyAlpha)
-            neededAlphaOp = AlphaDoUnmultiply;
+            m_alphaOp = AlphaDoUnmultiply;
         alphaFormat = AlphaFormatFirst;
         break;
     case kCGImageAlphaFirst:
         // This path is only accessible for MacOS earlier than 10.6.4.
         if (premultiplyAlpha)
-            neededAlphaOp = AlphaDoPremultiply;
+            m_alphaOp = AlphaDoPremultiply;
         alphaFormat = AlphaFormatFirst;
         break;
     case kCGImageAlphaNoneSkipFirst:
@@ -214,12 +211,12 @@
         break;
     case kCGImageAlphaPremultipliedLast:
         if (!premultiplyAlpha)
-            neededAlphaOp = AlphaDoUnmultiply;
+            m_alphaOp = AlphaDoUnmultiply;
         alphaFormat = AlphaFormatLast;
         break;
     case kCGImageAlphaLast:
         if (premultiplyAlpha)
-            neededAlphaOp = AlphaDoPremultiply;
+            m_alphaOp = AlphaDoPremultiply;
         alphaFormat = AlphaFormatLast;
         break;
     case kCGImageAlphaNoneSkipLast:
@@ -231,33 +228,29 @@
     default:
         return false;
     }
-    SourceDataFormat srcDataFormat = getSourceDataFormat(componentsPerPixel, alphaFormat, bitsPerComponent == 16, bigEndianSource);
-    if (srcDataFormat == SourceFormatNumFormats)
+
+    m_imageSourceFormat = getSourceDataFormat(componentsPerPixel, alphaFormat, bitsPerComponent == 16, bigEndianSource);
+    if (m_imageSourceFormat == SourceFormatNumFormats)
         return false;
 
-    RetainPtr<CFDataRef> pixelData;
-    pixelData.adoptCF(CGDataProviderCopyData(CGImageGetDataProvider(cgImage)));
-    if (!pixelData)
+    m_pixelData.adoptCF(CGDataProviderCopyData(CGImageGetDataProvider(m_cgImage)));
+    if (!m_pixelData)
         return false;
-    const UInt8* rgba = CFDataGetBytePtr(pixelData.get());
 
-    unsigned int packedSize;
-    // Output data is tightly packed (alignment == 1).
-    if (computeImageSizeInBytes(format, type, width, height, 1, &packedSize, 0) != GraphicsContext3D::NO_ERROR)
-        return false;
-    outputVector.resize(packedSize);
+    m_imagePixelData = reinterpret_cast<const void*>(CFDataGetBytePtr(m_pixelData.get()));
 
     unsigned int srcUnpackAlignment = 0;
-    size_t bytesPerRow = CGImageGetBytesPerRow(cgImage);
-    unsigned int padding = bytesPerRow - bitsPerPixel / 8 * width;
+    size_t bytesPerRow = CGImageGetBytesPerRow(m_cgImage);
+    unsigned padding = bytesPerRow - bitsPerPixel / 8 * m_imageWidth;
     if (padding) {
         srcUnpackAlignment = padding + 1;
         while (bytesPerRow % srcUnpackAlignment)
             ++srcUnpackAlignment;
     }
-    bool rt = packPixels(rgba, srcDataFormat, width, height, srcUnpackAlignment,
-                         format, type, neededAlphaOp, outputVector.data());
-    return rt;
+
+    m_imageSourceUnpackAlignment = srcUnpackAlignment;
+
+    return true;
 }
 
 void GraphicsContext3D::paintToCanvas(const unsigned char* imagePixels, int imageWidth, int imageHeight, int canvasWidth, int canvasHeight, CGContextRef context)
diff --git a/Source/WebCore/platform/graphics/clutter/GraphicsContext3DClutter.cpp b/Source/WebCore/platform/graphics/clutter/GraphicsContext3DClutter.cpp
index 7df1ee9..76a36c0 100644
--- a/Source/WebCore/platform/graphics/clutter/GraphicsContext3DClutter.cpp
+++ b/Source/WebCore/platform/graphics/clutter/GraphicsContext3DClutter.cpp
@@ -56,7 +56,11 @@
     notImplemented();
 }
 
-bool GraphicsContext3D::getImageData(Image* image, unsigned int format, unsigned int type, bool premultiplyAlpha, bool ignoreGammaAndColorProfile, Vector<uint8_t>& outputVector)
+GraphicsContext3D::ImageExtractor::~ImageExtractor()
+{
+}
+
+bool GraphicsContext3D::ImageExtractor::extractImage(bool premultiplyAlpha, bool ignoreGammaAndColorProfile)
 {
     notImplemented();
     return false;
diff --git a/Source/WebCore/platform/graphics/efl/GraphicsContext3DEfl.cpp b/Source/WebCore/platform/graphics/efl/GraphicsContext3DEfl.cpp
index 2e2a493..4afe508 100644
--- a/Source/WebCore/platform/graphics/efl/GraphicsContext3DEfl.cpp
+++ b/Source/WebCore/platform/graphics/efl/GraphicsContext3DEfl.cpp
@@ -241,7 +241,11 @@
 }
 #endif
 
-bool GraphicsContext3D::getImageData(Image*, GC3Denum /* format */, GC3Denum /* type */, bool /* premultiplyAlpha */, bool /* ignoreGammaAndColorProfile */, Vector<uint8_t>& /* outputVector */)
+GraphicsContext3D::ImageExtractor::~ImageExtractor()
+{
+}
+
+bool GraphicsContext3D::ImageExtractor::extractImage(bool premultiplyAlpha, bool ignoreGammaAndColorProfile)
 {
     notImplemented();
     return false;
diff --git a/Source/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp b/Source/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp
index 237d47e..a50ceb9 100644
--- a/Source/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp
+++ b/Source/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp
@@ -486,57 +486,56 @@
 }
 #endif
 
-bool GraphicsContext3D::getImageData(Image* image,
-                                     GC3Denum format,
-                                     GC3Denum type,
-                                     bool premultiplyAlpha,
-                                     bool ignoreGammaAndColorProfile,
-                                     Vector<uint8_t>& outputVector)
+GraphicsContext3D::ImageExtractor::~ImageExtractor()
+{
+}
+
+bool GraphicsContext3D::ImageExtractor::extractImage(bool premultiplyAlpha, bool ignoreGammaAndColorProfile)
 {
     UNUSED_PARAM(ignoreGammaAndColorProfile);
-    if (!image)
+    if (!m_image)
         return false;
 
-    QImage qtImage;
     // Is image already loaded? If not, load it.
-    if (image->data())
-        qtImage = QImage::fromData(reinterpret_cast<const uchar*>(image->data()->data()), image->data()->size());
+    if (m_image->data())
+        m_qtImage = QImage::fromData(reinterpret_cast<const uchar*>(m_image->data()->data()), m_image->data()->size());
     else {
-        QPixmap* nativePixmap = image->nativeImageForCurrentFrame();
+        QPixmap* nativePixmap = m_image->nativeImageForCurrentFrame();
         if (!nativePixmap)
             return false;
 
         // With QPA, we can avoid a deep copy.
-        qtImage = *nativePixmap->handle()->buffer();
+        m_qtImage = *nativePixmap->handle()->buffer();
     }
 
-    AlphaOp alphaOp = AlphaDoNothing;
-    switch (qtImage.format()) {
+    m_alphaOp = AlphaDoNothing;
+    switch (m_qtImage.format()) {
     case QImage::Format_RGB32:
         // For opaque images, we should not premultiply or unmultiply alpha.
         break;
     case QImage::Format_ARGB32:
         if (premultiplyAlpha)
-            alphaOp = AlphaDoPremultiply;
+            m_alphaOp = AlphaDoPremultiply;
         break;
     case QImage::Format_ARGB32_Premultiplied:
         if (!premultiplyAlpha)
-            alphaOp = AlphaDoUnmultiply;
+            m_alphaOp = AlphaDoUnmultiply;
         break;
     default:
         // The image has a format that is not supported in packPixels. We have to convert it here.
-        qtImage = qtImage.convertToFormat(premultiplyAlpha ? QImage::Format_ARGB32_Premultiplied : QImage::Format_ARGB32);
+        m_qtImage = m_qtImage.convertToFormat(premultiplyAlpha ? QImage::Format_ARGB32_Premultiplied : QImage::Format_ARGB32);
         break;
     }
 
-    unsigned int packedSize;
-    // Output data is tightly packed (alignment == 1).
-    if (computeImageSizeInBytes(format, type, image->width(), image->height(), 1, &packedSize, 0) != GraphicsContext3D::NO_ERROR)
+    m_imageWidth = m_image->width();
+    m_imageHeight = m_image->height();
+    if (!m_imageWidth || !m_imageHeight)
         return false;
+    m_imagePixelData = m_qtImage.constBits();
+    m_imageSourceFormat = SourceFormatBGRA8;
+    m_imageSourceUnpackAlignment = 0;
 
-    outputVector.resize(packedSize);
-
-    return packPixels(qtImage.constBits(), SourceFormatBGRA8, image->width(), image->height(), 0, format, type, alphaOp, outputVector.data());
+    return true;
 }
 
 void GraphicsContext3D::setContextLostCallback(PassOwnPtr<ContextLostCallback>)
diff --git a/Source/WebCore/platform/graphics/skia/GraphicsContext3DSkia.cpp b/Source/WebCore/platform/graphics/skia/GraphicsContext3DSkia.cpp
index fea6adc..b3d1ea7 100644
--- a/Source/WebCore/platform/graphics/skia/GraphicsContext3DSkia.cpp
+++ b/Source/WebCore/platform/graphics/skia/GraphicsContext3DSkia.cpp
@@ -42,58 +42,56 @@
 
 namespace WebCore {
 
-bool GraphicsContext3D::getImageData(Image* image,
-                                     GC3Denum format,
-                                     GC3Denum type,
-                                     bool premultiplyAlpha,
-                                     bool ignoreGammaAndColorProfile,
-                                     Vector<uint8_t>& outputVector)
+GraphicsContext3D::ImageExtractor::~ImageExtractor()
 {
-    if (!image)
+    if (m_skiaImage)
+        m_skiaImage->bitmap().unlockPixels();
+}
+
+bool GraphicsContext3D::ImageExtractor::extractImage(bool premultiplyAlpha, bool ignoreGammaAndColorProfile)
+{
+    if (!m_image)
         return false;
-    OwnPtr<NativeImageSkia> pixels;
-    NativeImageSkia* skiaImage = image->nativeImageForCurrentFrame();
-    AlphaOp neededAlphaOp = AlphaDoNothing;
-    bool hasAlpha = skiaImage ? !skiaImage->bitmap().isOpaque() : true;
-    if ((!skiaImage || ignoreGammaAndColorProfile || (hasAlpha && !premultiplyAlpha)) && image->data()) {
+    m_skiaImage = m_image->nativeImageForCurrentFrame();
+    m_alphaOp = AlphaDoNothing;
+    bool hasAlpha = m_skiaImage ? !m_skiaImage->bitmap().isOpaque() : true;
+    if ((!m_skiaImage || ignoreGammaAndColorProfile || (hasAlpha && !premultiplyAlpha)) && m_image->data()) {
         // Attempt to get raw unpremultiplied image data.
         OwnPtr<ImageDecoder> decoder(adoptPtr(ImageDecoder::create(
-            *(image->data()), ImageSource::AlphaNotPremultiplied,
+            *(m_image->data()), ImageSource::AlphaNotPremultiplied,
             ignoreGammaAndColorProfile ? ImageSource::GammaAndColorProfileIgnored : ImageSource::GammaAndColorProfileApplied)));
         if (!decoder)
             return false;
-        decoder->setData(image->data(), true);
+        decoder->setData(m_image->data(), true);
         if (!decoder->frameCount())
             return false;
         ImageFrame* frame = decoder->frameBufferAtIndex(0);
         if (!frame || frame->status() != ImageFrame::FrameComplete)
             return false;
         hasAlpha = frame->hasAlpha();
-        pixels = adoptPtr(frame->asNewNativeImage());
-        if (!pixels.get() || !pixels->isDataComplete() || !pixels->bitmap().width() || !pixels->bitmap().height())
+        m_nativeImage = adoptPtr(frame->asNewNativeImage());
+        if (!m_nativeImage.get() || !m_nativeImage->isDataComplete() || !m_nativeImage->bitmap().width() || !m_nativeImage->bitmap().height())
             return false;
-        SkBitmap::Config skiaConfig = pixels->bitmap().config();
+        SkBitmap::Config skiaConfig = m_nativeImage->bitmap().config();
         if (skiaConfig != SkBitmap::kARGB_8888_Config)
             return false;
-        skiaImage = pixels.get();
+        m_skiaImage = m_nativeImage.get();
         if (hasAlpha && premultiplyAlpha)
-            neededAlphaOp = AlphaDoPremultiply;
+            m_alphaOp = AlphaDoPremultiply;
     } else if (!premultiplyAlpha && hasAlpha)
-        neededAlphaOp = AlphaDoUnmultiply;
-    if (!skiaImage)
+        m_alphaOp = AlphaDoUnmultiply;
+    if (!m_skiaImage)
         return false;
-    const SkBitmap& skiaImageRef = skiaImage->bitmap();
-    SkAutoLockPixels lock(skiaImageRef);
-    ASSERT(skiaImageRef.rowBytes() == skiaImageRef.width() * 4);
-    unsigned int packedSize;
-    // Output data is tightly packed (alignment == 1).
-    if (computeImageSizeInBytes(format, type, skiaImageRef.width(), skiaImageRef.height(), 1, &packedSize, 0) != GraphicsContext3D::NO_ERROR)
+
+    m_imageSourceFormat = SK_B32_SHIFT ? SourceFormatRGBA8 : SourceFormatBGRA8;
+    m_imageWidth = m_skiaImage->bitmap().width();
+    m_imageHeight = m_skiaImage->bitmap().height();
+    if (!m_imageWidth || !m_imageHeight)
         return false;
-    outputVector.resize(packedSize);
-    return packPixels(reinterpret_cast<const uint8_t*>(skiaImageRef.getPixels()),
-                      SK_B32_SHIFT ? SourceFormatRGBA8 : SourceFormatBGRA8,
-                      skiaImageRef.width(), skiaImageRef.height(), 0,
-                      format, type, neededAlphaOp, outputVector.data());
+    m_imageSourceUnpackAlignment = 0;
+    m_skiaImage->bitmap().lockPixels();
+    m_imagePixelData = m_skiaImage->bitmap().getPixels();
+    return true;
 }
 
 } // namespace WebCore