2011-03-15 John Bauman <jbauman@chromium.org>
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 16 Mar 2011 03:26:48 +0000 (03:26 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 16 Mar 2011 03:26:48 +0000 (03:26 +0000)
        Reviewed by Kenneth Russell.

        Non-premultiplied-alpha canvas attribute is ignore for toDataURL, drawImage, texImage2D
        https://bugs.webkit.org/show_bug.cgi?id=56238

        Add new test from webgl conformance test.

        * fast/canvas/webgl/premultiplyalpha-test-expected.txt: Added.
        * fast/canvas/webgl/premultiplyalpha-test.html: Added.
        * fast/canvas/webgl/resources/webgl-test-utils.js: Update to fix fillTexture
        (WebGLTestUtils):
        * platform/chromium/test_expectations.txt:
        * platform/mac-wk2/Skipped:
2011-03-15  John Bauman  <jbauman@chromium.org>

        Reviewed by Kenneth Russell.

        Non-premultiplied-alpha canvas attribute is ignore for toDataURL, drawImage, texImage2D
        https://bugs.webkit.org/show_bug.cgi?id=56238

       Attempt to get an ImageData (non-premultiplied) from a WebGL canvas
       instead of getting an ImageBuffer, so there's a chance the data can be
       passed straight through to the consumer with no premultiplication
       necessary. Fixes Chromium and Safari.

        Test: fast/canvas/webgl/premultiplyalpha-test.html

        * html/HTMLCanvasElement.cpp:
        (WebCore::HTMLCanvasElement::toDataURL):
        (WebCore::HTMLCanvasElement::getImageData):
        * html/HTMLCanvasElement.h:
        * html/canvas/WebGLRenderingContext.cpp:
        (WebCore::WebGLRenderingContext::paintRenderingResultsToImageData):
        (WebCore::WebGLRenderingContext::texImage2D):
        (WebCore::WebGLRenderingContext::texSubImage2D):
        * html/canvas/WebGLRenderingContext.h:
        * platform/graphics/GraphicsContext3D.h:
        * platform/graphics/ImageBuffer.h:
        * platform/graphics/cg/ImageBufferCG.cpp:
        (WebCore::CGImageToDataURL):
        (WebCore::ImageBuffer::toDataURL):
        (WebCore::ImageDataToDataURL):
        * platform/graphics/opengl/GraphicsContext3DOpenGL.cpp:
        (WebCore::GraphicsContext3D::validateAttributes):
        (WebCore::GraphicsContext3D::readRenderingResults):
        (WebCore::GraphicsContext3D::paintRenderingResultsToCanvas):
        (WebCore::GraphicsContext3D::paintRenderingResultsToImageData):
        * platform/graphics/qt/GraphicsContext3DQt.cpp:
        (WebCore::GraphicsContext3D::paintRenderingResultsToImageData):
        * platform/graphics/skia/ImageBufferSkia.cpp:
        (WebCore::ImageToDataURL):
        (WebCore::ImageBuffer::toDataURL):
        (WebCore::ImageDataToDataURL):
        * platform/image-encoders/skia/JPEGImageEncoder.cpp:
        (WebCore::preMultipliedBGRAtoRGB):
        (WebCore::RGBAtoRGB):
        (WebCore::encodePixels):
        (WebCore::JPEGImageEncoder::encode):
        * platform/image-encoders/skia/JPEGImageEncoder.h:
        * platform/image-encoders/skia/PNGImageEncoder.cpp:
        (WebCore::preMultipliedBGRAtoRGBA):
        (WebCore::encodePixels):
        (WebCore::PNGImageEncoder::encode):
        * platform/image-encoders/skia/PNGImageEncoder.h:
2011-03-15  John Bauman  <jbauman@chromium.org>

        Reviewed by Kenneth Russell.

        Non-premultiplied-alpha canvas attribute is ignore for toDataURL, drawImage, texImage2D
        https://bugs.webkit.org/show_bug.cgi?id=56238

        Add support for reading a webgl context into an ImageData.

        * src/GraphicsContext3DChromium.cpp:
        (WebCore::GraphicsContext3DInternal::paintRenderingResultsToCanvas):
        (WebCore::GraphicsContext3DInternal::paintRenderingResultsToImageData):
        * src/GraphicsContext3DInternal.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@81213 268f45cc-cd09-0410-ab3c-d52691b4dbfc

24 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/canvas/webgl/premultiplyalpha-test-expected.txt [new file with mode: 0644]
LayoutTests/fast/canvas/webgl/premultiplyalpha-test.html [new file with mode: 0644]
LayoutTests/fast/canvas/webgl/resources/webgl-test-utils.js
LayoutTests/platform/chromium/test_expectations.txt
LayoutTests/platform/mac-wk2/Skipped
Source/WebCore/ChangeLog
Source/WebCore/html/HTMLCanvasElement.cpp
Source/WebCore/html/HTMLCanvasElement.h
Source/WebCore/html/canvas/WebGLRenderingContext.cpp
Source/WebCore/html/canvas/WebGLRenderingContext.h
Source/WebCore/platform/graphics/GraphicsContext3D.h
Source/WebCore/platform/graphics/ImageBuffer.h
Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp
Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp
Source/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp
Source/WebCore/platform/graphics/skia/ImageBufferSkia.cpp
Source/WebCore/platform/image-encoders/skia/JPEGImageEncoder.cpp
Source/WebCore/platform/image-encoders/skia/JPEGImageEncoder.h
Source/WebCore/platform/image-encoders/skia/PNGImageEncoder.cpp
Source/WebCore/platform/image-encoders/skia/PNGImageEncoder.h
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/src/GraphicsContext3DChromium.cpp
Source/WebKit/chromium/src/GraphicsContext3DInternal.h

index 24616ff..e791721 100644 (file)
@@ -1,3 +1,19 @@
+2011-03-15  John Bauman  <jbauman@chromium.org>
+
+        Reviewed by Kenneth Russell.
+
+        Non-premultiplied-alpha canvas attribute is ignore for toDataURL, drawImage, texImage2D
+        https://bugs.webkit.org/show_bug.cgi?id=56238
+
+        Add new test from webgl conformance test.
+
+        * fast/canvas/webgl/premultiplyalpha-test-expected.txt: Added.
+        * fast/canvas/webgl/premultiplyalpha-test.html: Added.
+        * fast/canvas/webgl/resources/webgl-test-utils.js: Update to fix fillTexture
+        (WebGLTestUtils):
+        * platform/chromium/test_expectations.txt:
+        * platform/mac-wk2/Skipped:
+
 2011-03-15  David Levin  <levin@chromium.org>
 
         A little more clean-up due to r81168. Also, some adjustment of the results for Linux due to r81162.
diff --git a/LayoutTests/fast/canvas/webgl/premultiplyalpha-test-expected.txt b/LayoutTests/fast/canvas/webgl/premultiplyalpha-test-expected.txt
new file mode 100644 (file)
index 0000000..d44a54c
--- /dev/null
@@ -0,0 +1,37 @@
+Test the WebGL premultipledAlpha context creation flag.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+testing: premultipliedAlpha: true toDataURL: true
+PASS gl.getContextAttributes().premultipledAlpha is premultipledAlpha
+PASS getError was expected value: NO_ERROR : Should be no errors from setup.
+PASS getError was expected value: NO_ERROR : Should be no errors from drawing.
+PASS getError was expected value: NO_ERROR : Should be no errors from creating copy.
+PASS getError was expected value: NO_ERROR : Should be no errors from 2nd drawing.
+PASS should draw with 64,128,255,128
+
+testing: premultipliedAlpha: true toDataURL: false
+PASS gl.getContextAttributes().premultipledAlpha is premultipledAlpha
+PASS getError was expected value: NO_ERROR : Should be no errors from setup.
+PASS getError was expected value: NO_ERROR : Should be no errors from drawing.
+PASS getError was expected value: NO_ERROR : Should be no errors from creating copy.
+PASS getError was expected value: NO_ERROR : Should be no errors from 2nd drawing.
+PASS should draw with 64,128,255,128
+
+testing: premultipliedAlpha: false toDataURL: true
+PASS gl.getContextAttributes().premultipledAlpha is premultipledAlpha
+PASS getError was expected value: NO_ERROR : Should be no errors from setup.
+PASS getError was expected value: NO_ERROR : Should be no errors from drawing.
+PASS getError was expected value: NO_ERROR : Should be no errors from creating copy.
+PASS getError was expected value: NO_ERROR : Should be no errors from 2nd drawing.
+PASS should draw with 255,192,128,1
+
+testing: premultipliedAlpha: false toDataURL: false
+PASS gl.getContextAttributes().premultipledAlpha is premultipledAlpha
+PASS getError was expected value: NO_ERROR : Should be no errors from setup.
+PASS getError was expected value: NO_ERROR : Should be no errors from drawing.
+PASS getError was expected value: NO_ERROR : Should be no errors from creating copy.
+PASS getError was expected value: NO_ERROR : Should be no errors from 2nd drawing.
+PASS should draw with 255,192,128,1
+
diff --git a/LayoutTests/fast/canvas/webgl/premultiplyalpha-test.html b/LayoutTests/fast/canvas/webgl/premultiplyalpha-test.html
new file mode 100644 (file)
index 0000000..677a370
--- /dev/null
@@ -0,0 +1,147 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+  "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<title>Test the WebGL premultipledAlpha context creation flag.</title>
+<link rel="stylesheet" href="../../js/resources/js-test-style.css"/>
+<script src="../../js/resources/js-test-pre.js"></script>
+<script src="resources/webgl-test.js"> </script>
+<script src="resources/webgl-test-utils.js"> </script>
+</head>
+<body>
+<div id="description"></div><div id="console"></div>
+<script>
+var wtu = WebGLTestUtils;
+
+var tests = [
+  // If premultipledAlpha is true then
+  // [texture]           [canvas]             [dataURL]
+  // 32, 64, 128, 128 -> 64, 128, 255, 128 -> 64, 128, 255, 128
+  { creationAttributes: {},
+    sentColor: [32, 64, 128, 128],
+    expectedColor: [64, 128, 255, 128],
+    errorRange: 2,
+    useToDataURL: true,
+  },
+  // If premultipledAlpha is true then
+  // [texture]           [canvas]             [texture]
+  // 32, 64, 128, 128 -> 64, 128, 255, 128 -> 64, 128, 255, 128
+  { creationAttributes: {},
+    sentColor: [32, 64, 128, 128],
+    expectedColor: [64, 128, 255, 128],
+    errorRange: 2,
+    useToDataURL: false,
+  },
+  // If premultipledAlpha is false then
+  // [texture]           [canvas]            [dataURL]
+  // 255, 192, 128, 1 -> 255, 192, 128, 1 -> 255, 192, 128, 1
+  { creationAttributes: {premultipliedAlpha: false},
+    sentColor: [255, 192, 128, 1],
+    expectedColor: [255, 192, 128, 1],
+    errorRange: 0,
+    useToDataURL: true,
+  },
+  // If premultipledAlpha is false then
+  // [texture]           [canvas]            [texture]
+  // 255, 192, 128, 1 -> 255, 192, 128, 1 -> 255, 192, 128, 1
+  { creationAttributes: {premultipliedAlpha: false},
+    sentColor: [255, 192, 128, 1],
+    expectedColor: [255, 192, 128, 1],
+    errorRange: 0,
+    useToDataURL: false,
+  }
+];
+
+var g_count = 0;
+var gl;
+var canvas;
+var premultipledAlpha;
+
+if (window.layoutTestController) {
+      layoutTestController.waitUntilDone();
+      layoutTestController.overridePreference("WebKitWebGLEnabled", "1");
+}
+
+description("Test the WebGL premultipledAlpha context creation flag.");
+doNextTest();
+function doNextTest() {
+  if (g_count < tests.length) {
+     var test = tests[g_count++];
+     canvas = document.createElement("canvas");
+     gl = wtu.create3DContext(canvas, test.creationAttributes);
+     var premultipliedAlpha = test.creationAttributes.premultipliedAlpha != false;
+     debug("")
+     debug("testing: premultipliedAlpha: " + premultipliedAlpha + " toDataURL: " + test.useToDataURL);
+
+     shouldBe('gl.getContextAttributes().premultipledAlpha', 'premultipledAlpha');
+
+     var program = wtu.setupTexturedQuad(gl);
+
+     glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from setup.");
+     var tex = gl.createTexture();
+     wtu.fillTexture(gl, tex, 2, 2, test.sentColor, 0);
+     var loc = gl.getUniformLocation(program, "tex");
+     gl.uniform1i(loc, 0);
+     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+
+     wtu.drawQuad(gl);
+     glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from drawing.");
+
+     function loadTexture() {
+       var pngTex = gl.createTexture();
+       // not needed as it's the default
+       // gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
+       gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, false);
+       gl.bindTexture(gl.TEXTURE_2D, pngTex);
+       if (test.useToDataURL) {
+          // create texture from image
+          gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this);
+       } else {
+          // create texture from canvas
+          gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);
+       }
+       gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+       gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+       gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+       gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+       glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from creating copy.");
+       wtu.drawQuad(gl);
+       glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors from 2nd drawing.");
+       wtu.checkCanvas(
+          gl, test.expectedColor,
+          "should draw with " + test.expectedColor, test.errorRange);
+
+       doNextTest();
+     }
+
+     if (test.useToDataURL) {
+        // Load canvas into string using toDataURL
+        var png = canvas.toDataURL();
+        // Load string into the texture
+        var input = document.createElement("img");
+        input.onload = loadTexture;
+        input.src = png;
+     } else {
+        // Load canvas into the texture asynchronously (to prevent unbounded stack consumption)
+        setTimeout(loadTexture, 0);
+     }
+   } else {
+      successfullyParsed = true;
+      if (window.layoutTestController)
+         layoutTestController.notifyDone();
+      finishTest();
+   }
+}
+
+</script>
+
+<script>
+</script>
+
+</body>
+</html>
+
+
index f3e520f..dce509a 100644 (file)
@@ -106,7 +106,7 @@ var simpleTextureFragmentShader = '' +
   'uniform sampler2D tex;\n' +
   'varying vec2 texCoord;\n' +
   'void main() {\n' +
-  '    gl_FragColor = texture2D(tex, texCoord);\n' +
+  '    gl_FragData[0] = texture2D(tex, texCoord);\n' +
   '}\n';
 
 /**
@@ -262,16 +262,21 @@ var setupTexturedQuad = function(
  */
 var fillTexture = function(gl, tex, width, height, color, opt_level) {
   opt_level = opt_level || 0;
-  var canvas = document.createElement('canvas');
-  canvas.width = width;
-  canvas.height = height;
-  var ctx2d = canvas.getContext('2d');
-  ctx2d.fillStyle = "rgba(" + color[0] + "," + color[1] + "," + color[2] + "," + color[3] + ")";
-  ctx2d.fillRect(0, 0, width, height);
+  var numPixels = width * height;
+  var size = numPixels * 4;
+  var buf = new Uint8Array(size);
+  for (var ii = 0; ii < numPixels; ++ii) {
+    var off = ii * 4;
+    buf[off + 0] = color[0];
+    buf[off + 1] = color[1];
+    buf[off + 2] = color[2];
+    buf[off + 3] = color[3];
+  }
   gl.bindTexture(gl.TEXTURE_2D, tex);
   gl.texImage2D(
-      gl.TEXTURE_2D, opt_level, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);
-};
+      gl.TEXTURE_2D, opt_level, gl.RGBA, width, height, 0,
+      gl.RGBA, gl.UNSIGNED_BYTE, buf);
+  };
 
 /**
  * Creates a textures and fills it with a solid color
@@ -316,14 +321,17 @@ var drawQuad = function(gl, opt_color) {
  * @param {!Array.<number>} color The color to fill clear with before drawing. A
  *        4 element array where each element is in the range 0 to 255.
  * @param {string} msg Message to associate with success. Eg ("should be red").
+ * @param {number} errorRange Optional. Acceptable error in
+ *        color checking. 0 by default.
  */
-var checkCanvasRect = function(gl, x, y, width, height, color, msg) {
+var checkCanvasRect = function(gl, x, y, width, height, color, msg, errorRange) {
+  errorRange = errorRange || 0;
   var buf = new Uint8Array(width * height * 4);
   gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
   for (var i = 0; i < width * height; ++i) {
     var offset = i * 4;
     for (var j = 0; j < color.length; ++j) {
-      if (buf[offset + j] != color[j]) {
+      if (Math.abs(buf[offset + j] - color[j]) > errorRange) {
         testFailed(msg);
         var was = buf[offset + 0].toString();
         for (j = 1; j < color.length; ++j) {
@@ -343,9 +351,11 @@ var checkCanvasRect = function(gl, x, y, width, height, color, msg) {
  * @param {!Array.<number>} color The color to fill clear with before drawing. A
  *        4 element array where each element is in the range 0 to 255.
  * @param {string} msg Message to associate with success. Eg ("should be red").
+ * @param {number} errorRange Optional. Acceptable error in
+ *        color checking. 0 by default.
  */
-var checkCanvas = function(gl, color, msg) {
-  checkCanvasRect(gl, 0, 0, gl.canvas.width, gl.canvas.height, color, msg);
+var checkCanvas = function(gl, color, msg, errorRange) {
+  checkCanvasRect(gl, 0, 0, gl.canvas.width, gl.canvas.height, color, msg, errorRange);
 };
 
 /**
@@ -378,20 +388,15 @@ var loadTexture = function(gl, url, callback) {
  *     passed in one will be created.
  * @return {!WebGLContext} The created context.
  */
-var create3DContext = function(opt_canvas) {
+var create3DContext = function(opt_canvas, opt_attributes) {
   opt_canvas = opt_canvas || document.createElement("canvas");
   var context = null;
   try {
-    context = opt_canvas.getContext("experimental-webgl");
+    context = opt_canvas.getContext("webgl", opt_attributes);
   } catch(e) {}
   if (!context) {
     try {
-      context = opt_canvas.getContext("webkit-3d");
-    } catch(e) {}
-  }
-  if (!context) {
-    try {
-      context = opt_canvas.getContext("moz-webgl");
+      context = opt_canvas.getContext("experimental-webgl", opt_attributes);
     } catch(e) {}
   }
   if (!context) {
@@ -568,7 +573,7 @@ var setupWebGLWithShaders = function(
 
   // Bind attributes
   for (var i in attribs) {
-    gl.bindAttribLocation (program, parseInt(i), attribs[i]);
+    gl.bindAttribLocation (program, i, attribs[i]);
   }
 
   linkProgram(gl, program);
@@ -617,8 +622,19 @@ var readFileList = function(url) {
           str[0] != '#' &&
           str[0] != ";" &&
           str.substr(0, 2) != "//") {
-        new_url = prefix + str;
-        files = files.concat(readFileList(new_url));
+        var names = str.split(/ +/);
+        if (names.length == 1) {
+          new_url = prefix + str;
+          files = files.concat(readFileList(new_url));
+        } else {
+          var s = "";
+          var p = "";
+          for (var jj = 0; jj < names.length; ++jj) {
+            s += p + prefix + names[jj];
+            p = " ";
+          }
+          files.push(s);
+        }
       }
     }
   } else {
@@ -644,6 +660,11 @@ var loadShader = function(gl, shaderSource, shaderType) {
 
   // Load the shader source
   gl.shaderSource(shader, shaderSource);
+  var err = gl.getError();
+  if (err != gl.NO_ERROR) {
+    error("*** Error loading shader '" + shader + "':" + glEnumToString(gl, err));
+    return null;
+  }
 
   // Compile the shader
   gl.compileShader(shader);
index f97d10a..abed1ef 100755 (executable)
@@ -2353,6 +2353,8 @@ BUGWEBGL MAC LINUX : fast/canvas/webgl/glsl-conformance.html = CRASH
 BUGWEBGL GPU WIN DEBUG SLOW : fast/canvas/webgl/context-attributes-alpha-depth-stencil-antialias.html = PASS
 BUGWEBGL GPU WIN DEBUG SLOW : fast/canvas/webgl/index-validation-with-resized-buffer.html = PASS
 
+BUGWK56414 : fast/canvas/webgl/premultiplyalpha-test.html = TEXT
+
 // These were not fixed by the Mesa 7.9 upgrade. Need to investigate.
 BUGCR60651 WIN : fast/canvas/webgl/gl-object-get-calls.html = TIMEOUT PASS TEXT
 // BUGCR60651 WIN : fast/canvas/webgl/glsl-conformance.html = TIMEOUT FAIL
index 3ee0040..aba582b 100644 (file)
@@ -1100,6 +1100,7 @@ fast/canvas/webgl/null-object-behaviour.html
 fast/canvas/webgl/null-uniform-location.html
 fast/canvas/webgl/object-deletion-behaviour.html
 fast/canvas/webgl/point-size.html
+fast/canvas/webgl/premultiplyalpha-test.html
 fast/canvas/webgl/program-test.html
 fast/canvas/webgl/read-pixels-pack-alignment.html
 fast/canvas/webgl/read-pixels-test.html
index 2259509..c04611a 100644 (file)
@@ -1,3 +1,55 @@
+2011-03-15  John Bauman  <jbauman@chromium.org>
+
+        Reviewed by Kenneth Russell.
+
+        Non-premultiplied-alpha canvas attribute is ignore for toDataURL, drawImage, texImage2D
+        https://bugs.webkit.org/show_bug.cgi?id=56238
+
+       Attempt to get an ImageData (non-premultiplied) from a WebGL canvas
+       instead of getting an ImageBuffer, so there's a chance the data can be
+       passed straight through to the consumer with no premultiplication
+       necessary. Fixes Chromium and Safari.
+
+        Test: fast/canvas/webgl/premultiplyalpha-test.html
+
+        * html/HTMLCanvasElement.cpp:
+        (WebCore::HTMLCanvasElement::toDataURL):
+        (WebCore::HTMLCanvasElement::getImageData):
+        * html/HTMLCanvasElement.h:
+        * html/canvas/WebGLRenderingContext.cpp:
+        (WebCore::WebGLRenderingContext::paintRenderingResultsToImageData):
+        (WebCore::WebGLRenderingContext::texImage2D):
+        (WebCore::WebGLRenderingContext::texSubImage2D):
+        * html/canvas/WebGLRenderingContext.h:
+        * platform/graphics/GraphicsContext3D.h:
+        * platform/graphics/ImageBuffer.h:
+        * platform/graphics/cg/ImageBufferCG.cpp:
+        (WebCore::CGImageToDataURL):
+        (WebCore::ImageBuffer::toDataURL):
+        (WebCore::ImageDataToDataURL):
+        * platform/graphics/opengl/GraphicsContext3DOpenGL.cpp:
+        (WebCore::GraphicsContext3D::validateAttributes):
+        (WebCore::GraphicsContext3D::readRenderingResults):
+        (WebCore::GraphicsContext3D::paintRenderingResultsToCanvas):
+        (WebCore::GraphicsContext3D::paintRenderingResultsToImageData):
+        * platform/graphics/qt/GraphicsContext3DQt.cpp:
+        (WebCore::GraphicsContext3D::paintRenderingResultsToImageData):
+        * platform/graphics/skia/ImageBufferSkia.cpp:
+        (WebCore::ImageToDataURL):
+        (WebCore::ImageBuffer::toDataURL):
+        (WebCore::ImageDataToDataURL):
+        * platform/image-encoders/skia/JPEGImageEncoder.cpp:
+        (WebCore::preMultipliedBGRAtoRGB):
+        (WebCore::RGBAtoRGB):
+        (WebCore::encodePixels):
+        (WebCore::JPEGImageEncoder::encode):
+        * platform/image-encoders/skia/JPEGImageEncoder.h:
+        * platform/image-encoders/skia/PNGImageEncoder.cpp:
+        (WebCore::preMultipliedBGRAtoRGBA):
+        (WebCore::encodePixels):
+        (WebCore::PNGImageEncoder::encode):
+        * platform/image-encoders/skia/PNGImageEncoder.h:
+
 2011-03-15  Kevin Ollivier  <kevino@theolliviers.com>
 
         [wx] Build fix, only compile the methods when the INDEXED_DATABASE feature is enabled.
index c361e3d..6d41e55 100644 (file)
@@ -41,6 +41,7 @@
 #include "GraphicsContext.h"
 #include "HTMLNames.h"
 #include "ImageBuffer.h"
+#include "ImageData.h"
 #include "MIMETypeRegistry.h"
 #include "Page.h"
 #include "RenderHTMLCanvas.h"
@@ -338,15 +339,37 @@ String HTMLCanvasElement::toDataURL(const String& mimeType, const double* qualit
 
     String lowercaseMimeType = mimeType.lower();
 
-    makeRenderingResultsAvailable();
-
     // FIXME: Make isSupportedImageMIMETypeForEncoding threadsafe (to allow this method to be used on a worker thread).
     if (mimeType.isNull() || !MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(lowercaseMimeType))
-        return buffer()->toDataURL("image/png");
+        lowercaseMimeType = "image/png";
+
+#if PLATFORM(CG) || USE(SKIA)
+    // Try to get ImageData first, as that may avoid lossy conversions.
+    RefPtr<ImageData> imageData = getImageData();
+
+    if (imageData)
+        return ImageDataToDataURL(*imageData, lowercaseMimeType, quality);
+#endif
 
+    makeRenderingResultsAvailable();
+      
     return buffer()->toDataURL(lowercaseMimeType, quality);
 }
 
+PassRefPtr<ImageData> HTMLCanvasElement::getImageData()
+{
+    if (!m_context || !m_context->is3d())
+       return 0;
+
+#if ENABLE(WEBGL)    
+    WebGLRenderingContext* ctx = static_cast<WebGLRenderingContext*>(m_context.get());
+
+    return ctx->paintRenderingResultsToImageData();
+#else
+    return 0;
+#endif
+}
+
 IntRect HTMLCanvasElement::convertLogicalToDevice(const FloatRect& logicalRect) const
 {
     float left = floorf(logicalRect.x() * m_pageScaleFactor);
index a2a606d..8706c6b 100644 (file)
@@ -47,6 +47,7 @@ class CanvasRenderingContext;
 class GraphicsContext;
 class HTMLCanvasElement;
 class Image;
+class ImageData;
 class ImageBuffer;
 class IntSize;
 
@@ -105,6 +106,7 @@ public:
     ImageBuffer* buffer() const;
     Image* copiedImage() const;
     void clearCopiedImage();
+    PassRefPtr<ImageData> getImageData();
 
     IntRect convertLogicalToDevice(const FloatRect&) const;
     IntSize convertLogicalToDevice(const FloatSize&) const;
index cc7a23b..cb451cb 100644 (file)
@@ -495,6 +495,11 @@ void WebGLRenderingContext::paintRenderingResultsToCanvas()
     m_context->paintRenderingResultsToCanvas(this);
 }
 
+PassRefPtr<ImageData> WebGLRenderingContext::paintRenderingResultsToImageData()
+{
+    return m_context->paintRenderingResultsToImageData();
+}
+
 bool WebGLRenderingContext::paintsIntoCanvasBuffer() const
 {
     return m_context->paintsIntoCanvasBuffer();
@@ -3062,8 +3067,12 @@ void WebGLRenderingContext::texImage2D(GC3Denum target, GC3Dint level, GC3Denum
         return;
     }
     checkOrigin(canvas);
-    texImage2DImpl(target, level, internalformat, format, type, canvas->copiedImage(),
-                   m_unpackFlipY, m_unpackPremultiplyAlpha, ec);
+    RefPtr<ImageData> imageData = canvas->getImageData();
+    if (imageData)
+        texImage2D(target, level, internalformat, format, type, imageData.get(), ec);
+    else
+        texImage2DImpl(target, level, internalformat, format, type, canvas->copiedImage(),
+                       m_unpackFlipY, m_unpackPremultiplyAlpha, ec);
 }
 
 #if ENABLE(VIDEO)
@@ -3252,8 +3261,12 @@ void WebGLRenderingContext::texSubImage2D(GC3Denum target, GC3Dint level, GC3Din
         return;
     }
     checkOrigin(canvas);
-    texSubImage2DImpl(target, level, xoffset, yoffset, format, type, canvas->copiedImage(),
-                      m_unpackFlipY, m_unpackPremultiplyAlpha, ec);
+    RefPtr<ImageData> imageData = canvas->getImageData();
+    if (imageData)
+        texSubImage2D(target, level, xoffset, yoffset, format, type, imageData.get(), ec);
+    else
+        texSubImage2DImpl(target, level, xoffset, yoffset, format, type, canvas->copiedImage(),
+                          m_unpackFlipY, m_unpackPremultiplyAlpha, ec);
 }
 
 #if ENABLE(VIDEO)
index 13145c8..6b885d9 100644 (file)
@@ -286,6 +286,7 @@ public:
     void reshape(int width, int height);
 
     virtual void paintRenderingResultsToCanvas();
+    virtual PassRefPtr<ImageData> paintRenderingResultsToImageData();
 
     void removeObject(WebGLObject*);
     
index b9c83d9..455c389 100644 (file)
@@ -757,6 +757,7 @@ public:
 #endif
 
     void paintRenderingResultsToCanvas(CanvasRenderingContext* context);
+    PassRefPtr<ImageData> paintRenderingResultsToImageData();
 
 #if PLATFORM(QT)
     void paint(QPainter* painter, const QRect& rect) const;
@@ -859,6 +860,10 @@ public:
     // could not be honored based on the capabilities of the OpenGL
     // implementation.
     void validateAttributes();
+
+    // Read rendering results into a pixel array with the same format as the
+    // backbuffer.
+    void readRenderingResults(unsigned char* pixels, int pixelsSize);
 #endif
 
     int m_currentWidth, m_currentHeight;
index 48878da..860f574 100644 (file)
@@ -132,6 +132,10 @@ namespace WebCore {
         ImageBuffer(const IntSize&, ColorSpace colorSpace, RenderingMode renderingMode, bool& success);
     };
 
+#if PLATFORM(CG) || USE(SKIA)
+    String ImageDataToDataURL(const ImageData& input, const String& mimeType, const double* quality);
+#endif
+
 } // namespace WebCore
 
 #endif // ImageBuffer_h
index 7c8e313..3c8f959 100644 (file)
@@ -32,6 +32,7 @@
 #include "BitmapImage.h"
 #include "GraphicsContext.h"
 #include "GraphicsContextCG.h"
+#include "ImageData.h"
 #include "MIMETypeRegistry.h"
 #include <ApplicationServices/ApplicationServices.h>
 #include <wtf/Assertions.h>
@@ -498,21 +499,8 @@ static RetainPtr<CFStringRef> utiFromMIMEType(const String& mimeType)
 #endif
 }
 
-String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const
+static String CGImageToDataURL(CGImageRef image, const String& mimeType, const double* quality)
 {
-    ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
-
-    RetainPtr<CGImageRef> image;
-    if (!m_accelerateRendering)
-        image.adoptCF(CGBitmapContextCreateImage(context()->platformContext()));
-#if USE(IOSURFACE_CANVAS_BACKING_STORE)
-    else
-        image.adoptCF(wkIOSurfaceContextCreateImage(context()->platformContext()));
-#endif
-
-    if (!image)
-        return "data:,";
-
     RetainPtr<CFMutableDataRef> data(AdoptCF, CFDataCreateMutable(kCFAllocatorDefault, 0));
     if (!data)
         return "data:,";
@@ -533,7 +521,7 @@ String ImageBuffer::toDataURL(const String& mimeType, const double* quality) con
         imageProperties.adoptCF(CFDictionaryCreate(0, &key, &value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
     }
 
-    CGImageDestinationAddImage(destination.get(), image.get(), imageProperties.get());
+    CGImageDestinationAddImage(destination.get(), image, imageProperties.get());
     CGImageDestinationFinalize(destination.get());
 
     Vector<char> out;
@@ -541,4 +529,46 @@ String ImageBuffer::toDataURL(const String& mimeType, const double* quality) con
 
     return makeString("data:", mimeType, ";base64,", out);
 }
+
+String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const
+{
+    ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
+
+    RetainPtr<CGImageRef> image;
+    if (!m_accelerateRendering)
+        image.adoptCF(CGBitmapContextCreateImage(context()->platformContext()));
+#if USE(IOSURFACE_CANVAS_BACKING_STORE)
+    else
+        image.adoptCF(wkIOSurfaceContextCreateImage(context()->platformContext()));
+#endif
+
+    if (!image)
+        return "data:,";
+
+    return CGImageToDataURL(image.get(), mimeType, quality);
+}
+
+String ImageDataToDataURL(const ImageData& source, const String& mimeType, const double* quality)
+{
+    ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
+        
+    RetainPtr<CGImageRef> image;
+    RetainPtr<CGDataProviderRef> dataProvider;
+    
+    dataProvider.adoptCF(CGDataProviderCreateWithData(0, source.data()->data()->data(),
+                                                      4 * source.width() * source.height(), 0));
+    
+    if (!dataProvider)
+        return "data:,";
+
+    image.adoptCF(CGImageCreate(source.width(), source.height(), 8, 32, 4 * source.width(),
+                                CGColorSpaceCreateDeviceRGB(), kCGBitmapByteOrderDefault | kCGImageAlphaLast,
+                                dataProvider.get(), 0, false, kCGRenderingIntentDefault));
+                                
+        
+    if (!image)
+        return "data:,";
+
+    return CGImageToDataURL(image.get(), mimeType, quality);
+}
 } // namespace WebCore
index c224e20..2547b53 100644 (file)
@@ -38,6 +38,7 @@
 #include "GraphicsContext.h"
 #include "HTMLCanvasElement.h"
 #include "ImageBuffer.h"
+#include "ImageData.h"
 #include "Int32Array.h"
 #include "NotImplemented.h"
 #include "Uint8Array.h"
@@ -70,21 +71,11 @@ void GraphicsContext3D::validateAttributes()
         if (!isValidVendor || !std::strstr(extensions, "GL_EXT_framebuffer_multisample"))
             m_attrs.antialias = false;
     }
-    // FIXME: instead of enforcing premultipliedAlpha = true, implement the
-    // correct behavior when premultipliedAlpha = false is requested.
-    m_attrs.premultipliedAlpha = true;
 }
 
-void GraphicsContext3D::paintRenderingResultsToCanvas(CanvasRenderingContext* context)
+void GraphicsContext3D::readRenderingResults(unsigned char *pixels, int pixelsSize)
 {
-    HTMLCanvasElement* canvas = context->canvas();
-    ImageBuffer* imageBuffer = canvas->buffer();
-
-    int rowBytes = m_currentWidth * 4;
-    int totalBytes = rowBytes * m_currentHeight;
-
-    OwnArrayPtr<unsigned char> pixels = adoptArrayPtr(new unsigned char[totalBytes]);
-    if (!pixels)
+    if (pixelsSize < m_currentWidth * m_currentHeight * 4)
         return;
 
     makeContextCurrent();
@@ -111,18 +102,62 @@ void GraphicsContext3D::paintRenderingResultsToCanvas(CanvasRenderingContext* co
         mustRestorePackAlignment = true;
     }
 
-    ::glReadPixels(0, 0, m_currentWidth, m_currentHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels.get());
+    ::glReadPixels(0, 0, m_currentWidth, m_currentHeight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
 
     if (mustRestorePackAlignment)
         ::glPixelStorei(GL_PACK_ALIGNMENT, packAlignment);
 
     if (mustRestoreFBO)
         ::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_boundFBO);
+}
+
+void GraphicsContext3D::paintRenderingResultsToCanvas(CanvasRenderingContext* context)
+{
+    HTMLCanvasElement* canvas = context->canvas();
+    ImageBuffer* imageBuffer = canvas->buffer();
+
+    int rowBytes = m_currentWidth * 4;
+    int totalBytes = rowBytes * m_currentHeight;
+
+    OwnArrayPtr<unsigned char> pixels = adoptArrayPtr(new unsigned char[totalBytes]);
+    if (!pixels)
+        return;
+
+    readRenderingResults(pixels.get(), totalBytes);
+
+    if (!m_attrs.premultipliedAlpha) {
+        for (int i = 0; i < totalBytes; i += 4) {
+            // Premultiply alpha
+            pixels[i + 0] = std::min(255, pixels[i + 0] * pixels[i + 3] / 255);
+            pixels[i + 1] = std::min(255, pixels[i + 1] * pixels[i + 3] / 255);
+            pixels[i + 2] = std::min(255, pixels[i + 2] * pixels[i + 3] / 255);
+        }
+    }
 
     paintToCanvas(pixels.get(), m_currentWidth, m_currentHeight,
                   canvas->width(), canvas->height(), imageBuffer->context()->platformContext());
 }
 
+PassRefPtr<ImageData> GraphicsContext3D::paintRenderingResultsToImageData()
+{
+    // Reading premultiplied alpha would involve unpremultiplying, which is
+    // lossy
+    if (m_attrs.premultipliedAlpha)
+        return 0;
+
+    RefPtr<ImageData> imageData = ImageData::create(IntSize(m_currentWidth, m_currentHeight));
+    unsigned char* pixels = imageData->data()->data()->data();
+    int totalBytes = 4 * m_currentWidth * m_currentHeight;
+
+    readRenderingResults(pixels, totalBytes);
+
+    // Convert to RGBA
+    for (int i = 0; i < totalBytes; i += 4)
+        std::swap(pixels[i], pixels[i + 2]);
+
+    return imageData.release();
+}
+
 void GraphicsContext3D::reshape(int width, int height)
 {
     if (!m_contextObj)
index b849214..4241b00 100644 (file)
@@ -525,6 +525,13 @@ void GraphicsContext3D::paintRenderingResultsToCanvas(CanvasRenderingContext* co
     paint(painter, QRect(QPoint(0, 0), QSize(m_currentWidth, m_currentHeight)));
 }
 
+PassRefPtr<ImageData> GraphicsContext3D::paintRenderingResultsToImageData()
+{
+    // FIXME: This needs to be implemented for proper non-premultiplied-alpha
+    // support.
+    return 0;
+}
+
 void GraphicsContext3D::paint(QPainter* painter, const QRect& rect) const
 {
 #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
index 2721523..b89c68d 100644 (file)
@@ -341,6 +341,28 @@ void ImageBuffer::putPremultipliedImageData(ByteArray* source, const IntSize& so
     putImageData<Premultiplied>(source, sourceSize, sourceRect, destPoint, context()->platformContext()->canvas()->getDevice(), m_size);
 }
 
+template <typename T>
+static String ImageToDataURL(T& source, const String& mimeType, const double* quality)
+{
+    Vector<unsigned char> encodedImage;
+    if (mimeType == "image/jpeg") {
+        int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality;
+        if (quality && *quality >= 0.0 && *quality <= 1.0)
+            compressionQuality = static_cast<int>(*quality * 100 + 0.5);
+        if (!JPEGImageEncoder::encode(source, compressionQuality, &encodedImage))
+            return "data:,";
+    } else {
+        if (!PNGImageEncoder::encode(source, &encodedImage))
+            return "data:,";
+        ASSERT(mimeType == "image/png");
+    }
+
+    Vector<char> base64Data;
+    base64Encode(*reinterpret_cast<Vector<char>*>(&encodedImage), base64Data);
+
+    return makeString("data:", mimeType, ";base64,", base64Data);
+}
+
 String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const
 {
     ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType));
@@ -358,23 +380,13 @@ String ImageBuffer::toDataURL(const String& mimeType, const double* quality) con
         if (!device->readPixels(bounds, &bitmap))
             return "data:,";
     }
-    
-    if (mimeType == "image/jpeg") {
-        int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality;
-        if (quality && *quality >= 0.0 && *quality <= 1.0)
-            compressionQuality = static_cast<int>(*quality * 100 + 0.5);
-        if (!JPEGImageEncoder::encode(bitmap, compressionQuality, &encodedImage))
-            return "data:,";
-    } else {
-        if (!PNGImageEncoder::encode(bitmap, &encodedImage))
-            return "data:,";
-        ASSERT(mimeType == "image/png");
-    }
 
-    Vector<char> base64Data;
-    base64Encode(*reinterpret_cast<Vector<char>*>(&encodedImage), base64Data);
+    return ImageToDataURL(bitmap, mimeType, quality);
+}
 
-    return makeString("data:", mimeType, ";base64,", base64Data);
+String ImageDataToDataURL(const ImageData& source, const String& mimeType, const double* quality)
+{
+    return ImageToDataURL(source, mimeType, quality);
 }
 
 } // namespace WebCore
index be3c92a..5064e38 100644 (file)
@@ -31,6 +31,7 @@
 #include "config.h"
 #include "JPEGImageEncoder.h"
 
+#include "ImageData.h"
 #include "IntSize.h"
 #include "SkBitmap.h"
 #include "SkColorPriv.h"
@@ -79,22 +80,29 @@ static void handleError(j_common_ptr common)
     longjmp(*jumpBufferPtr, -1);
 }
 
-static void preMultipliedBGRAtoRGB(const SkPMColor* input, unsigned int pixels, unsigned char* output)
+static void preMultipliedBGRAtoRGB(const void* pixels, unsigned int pixelCount, unsigned char* output)
 {
-    for (; pixels-- > 0; ++input) {
+    const SkPMColor* input = static_cast<const SkPMColor*>(pixels);
+    for (; pixelCount-- > 0; ++input) {
         *output++ = SkGetPackedR32(*input);
         *output++ = SkGetPackedG32(*input);
         *output++ = SkGetPackedB32(*input);
     }
 }
 
-bool JPEGImageEncoder::encode(const SkBitmap& bitmap, int quality, Vector<unsigned char>* output)
+static void RGBAtoRGB(const unsigned char* input, unsigned int pixels, unsigned char* output)
 {
-    if (bitmap.config() != SkBitmap::kARGB_8888_Config)
-        return false; // Only support ARGB 32 bpp skia bitmaps.
+    for (; pixels-- > 0; input += 4) {
+        *output++ = input[0];
+        *output++ = input[1];
+        *output++ = input[2];
+    }
+}
 
-    SkAutoLockPixels bitmapLock(bitmap);
-    IntSize imageSize(bitmap.width(), bitmap.height());
+static bool encodePixels(const IntSize& inputSize, unsigned char* pixels, 
+                         bool premultiplied, int quality, Vector<unsigned char>* output)
+{
+    IntSize imageSize(inputSize);
     imageSize.clampNegativeToZero();
     JPEGOutputBuffer destination;
     destination.output = output;
@@ -126,12 +134,14 @@ bool JPEGImageEncoder::encode(const SkBitmap& bitmap, int quality, Vector<unsign
     jpeg_set_quality(&cinfo, quality, TRUE);
     jpeg_start_compress(&cinfo, TRUE);
 
-    const SkPMColor* pixels = static_cast<SkPMColor*>(bitmap.getPixels());
     row.resize(cinfo.image_width * cinfo.input_components);
     while (cinfo.next_scanline < cinfo.image_height) {
-        preMultipliedBGRAtoRGB(pixels, cinfo.image_width, row.data());
+        if (premultiplied)
+            preMultipliedBGRAtoRGB(pixels, cinfo.image_width, row.data());
+        else 
+            RGBAtoRGB(pixels, cinfo.image_width, row.data());
         jpeg_write_scanlines(&cinfo, row.dataSlot(), 1);
-        pixels += cinfo.image_width;
+        pixels += cinfo.image_width * 4;
     }
 
     jpeg_finish_compress(&cinfo);
@@ -139,4 +149,22 @@ bool JPEGImageEncoder::encode(const SkBitmap& bitmap, int quality, Vector<unsign
     return true;
 }
 
+bool JPEGImageEncoder::encode(const SkBitmap& bitmap, int quality, Vector<unsigned char>* output)
+{
+    if (bitmap.config() != SkBitmap::kARGB_8888_Config)
+        return false; // Only support ARGB 32 bpp skia bitmaps.
+
+    SkAutoLockPixels bitmapLock(bitmap);
+    IntSize imageSize(bitmap.width(), bitmap.height());
+
+    return encodePixels(imageSize, static_cast<unsigned char *>(bitmap.getPixels()),
+                        true, quality, output);
+}
+
+bool JPEGImageEncoder::encode(const ImageData& imageData, int quality, Vector<unsigned char>* output)
+{
+    return encodePixels(imageData.size(), imageData.data()->data()->data(),
+                        false, quality, output);
+}
+
 } // namespace WebCore
index f2ac52d..5408091 100644 (file)
@@ -37,10 +37,13 @@ class SkBitmap;
 
 namespace WebCore {
 
+class ImageData;
+
 class JPEGImageEncoder {
 public:
     // Encode the input bitmap with a compression quality in [0-100].
     static bool encode(const SkBitmap&, int quality, Vector<unsigned char>*);
+    static bool encode(const ImageData&, int quality, Vector<unsigned char>*);
 
     // For callers: provide a reasonable compression quality default.
     enum Quality { DefaultCompressionQuality = 92 };
index 78f737e..fdbb4ce 100644 (file)
@@ -31,6 +31,7 @@
 #include "config.h"
 #include "PNGImageEncoder.h"
 
+#include "ImageData.h"
 #include "IntSize.h"
 #include "SkBitmap.h"
 #include "SkColorPriv.h"
@@ -46,11 +47,12 @@ static void writeOutput(png_structp png, png_bytep data, png_size_t size)
     static_cast<Vector<unsigned char>*>(png->io_ptr)->append(data, size);
 }
 
-static void preMultipliedBGRAtoRGBA(const SkPMColor* input, int pixels, unsigned char* output)
+static void preMultipliedBGRAtoRGBA(const void* pixels, int pixelCount, unsigned char* output)
 {
     static const SkUnPreMultiply::Scale* scale = SkUnPreMultiply::GetScaleTable();
+    const SkPMColor* input = static_cast<const SkPMColor*>(pixels);
 
-    for (; pixels-- > 0; ++input) {
+    for (; pixelCount-- > 0; ++input) {
         const unsigned alpha = SkGetPackedA32(*input);
         if ((alpha != 0) && (alpha != 255)) {
             *output++ = SkUnPreMultiply::ApplyScale(scale[alpha], SkGetPackedR32(*input));
@@ -66,13 +68,10 @@ static void preMultipliedBGRAtoRGBA(const SkPMColor* input, int pixels, unsigned
     }
 }
 
-bool PNGImageEncoder::encode(const SkBitmap& bitmap, Vector<unsigned char>* output)
+static bool encodePixels(const IntSize& inputSize, unsigned char* pixels, 
+                         bool premultiplied, Vector<unsigned char>* output)
 {
-    if (bitmap.config() != SkBitmap::kARGB_8888_Config)
-        return false; // Only support ARGB 32 bpp skia bitmaps.
-
-    SkAutoLockPixels bitmapLock(bitmap);
-    IntSize imageSize(bitmap.width(), bitmap.height());
+    IntSize imageSize(inputSize);
     imageSize.clampNegativeToZero();
     Vector<unsigned char> row;
 
@@ -100,12 +99,14 @@ bool PNGImageEncoder::encode(const SkBitmap& bitmap, Vector<unsigned char>* outp
                  8, PNG_COLOR_TYPE_RGB_ALPHA, 0, 0, 0);
     png_write_info(png, info);
 
-    const SkPMColor* pixels = static_cast<SkPMColor*>(bitmap.getPixels());
-    row.resize(imageSize.width() * bitmap.bytesPerPixel());
+    row.resize(imageSize.width() * sizeof(SkPMColor));
     for (int y = 0; y < imageSize.height(); ++y) {
-        preMultipliedBGRAtoRGBA(pixels, imageSize.width(), row.data());
-        png_write_row(png, row.data());
-        pixels += imageSize.width();
+        if (premultiplied) {
+            preMultipliedBGRAtoRGBA(pixels, imageSize.width(), row.data());
+            png_write_row(png, row.data());
+        } else
+            png_write_row(png, pixels);
+        pixels += imageSize.width() * 4;
     }
 
     png_write_end(png, info);
@@ -113,4 +114,19 @@ bool PNGImageEncoder::encode(const SkBitmap& bitmap, Vector<unsigned char>* outp
     return true;
 }
 
+bool PNGImageEncoder::encode(const SkBitmap& bitmap, Vector<unsigned char>* output)
+{
+    if (bitmap.config() != SkBitmap::kARGB_8888_Config)
+        return false; // Only support ARGB 32 bpp skia bitmaps.
+
+    SkAutoLockPixels bitmapLock(bitmap);
+    IntSize imageSize(bitmap.width(), bitmap.height());
+    return encodePixels(imageSize, static_cast<unsigned char*>(bitmap.getPixels()), true, output);
+}
+
+bool PNGImageEncoder::encode(const ImageData& bitmap, Vector<unsigned char>* output)
+{
+    return encodePixels(bitmap.size(), bitmap.data()->data()->data(), false, output);
+}
+
 } // namespace WebCore
index b8dfec3..061d7ac 100644 (file)
@@ -37,10 +37,13 @@ class SkBitmap;
 
 namespace WebCore {
 
+class ImageData;
+
 // Interface for encoding PNG data. This is a wrapper around libpng.
 class PNGImageEncoder {
 public:
     static bool encode(const SkBitmap&, Vector<unsigned char>* output);
+    static bool encode(const ImageData&, Vector<unsigned char>* output);
 };
 
 } // namespace WebCore
index 45f6861..27dbab7 100644 (file)
@@ -1,3 +1,17 @@
+2011-03-15  John Bauman  <jbauman@chromium.org>
+
+        Reviewed by Kenneth Russell.
+
+        Non-premultiplied-alpha canvas attribute is ignore for toDataURL, drawImage, texImage2D
+        https://bugs.webkit.org/show_bug.cgi?id=56238
+
+        Add support for reading a webgl context into an ImageData.
+
+        * src/GraphicsContext3DChromium.cpp:
+        (WebCore::GraphicsContext3DInternal::paintRenderingResultsToCanvas):
+        (WebCore::GraphicsContext3DInternal::paintRenderingResultsToImageData):
+        * src/GraphicsContext3DInternal.h:
+
 2011-03-15  Sheriff Bot  <webkit.review.bot@gmail.com>
 
         Unreviewed, rolling out r81144.
index 3d937ac..8d12c4d 100644 (file)
@@ -200,6 +200,16 @@ void GraphicsContext3DInternal::paintRenderingResultsToCanvas(CanvasRenderingCon
 
     m_impl->readBackFramebuffer(pixels, 4 * m_impl->width() * m_impl->height());
 
+    if (!m_impl->getContextAttributes().premultipliedAlpha) {
+        size_t bufferSize = 4 * m_impl->width() * m_impl->height();
+
+        for (size_t i = 0; i < bufferSize; i += 4) {
+            pixels[i + 0] = std::min(255, pixels[i + 0] * pixels[i + 3] / 255);
+            pixels[i + 1] = std::min(255, pixels[i + 1] * pixels[i + 3] / 255);
+            pixels[i + 2] = std::min(255, pixels[i + 2] * pixels[i + 3] / 255);
+        }
+    }
+
 #if USE(SKIA)
     if (m_resizingBitmap.readyToDraw()) {
         // We need to draw the resizing bitmap into the canvas's backing store.
@@ -218,6 +228,23 @@ void GraphicsContext3DInternal::paintRenderingResultsToCanvas(CanvasRenderingCon
 #endif
 }
 
+PassRefPtr<ImageData> GraphicsContext3DInternal::paintRenderingResultsToImageData()
+{
+    if (m_impl->getContextAttributes().premultipliedAlpha)
+        return 0;
+    
+    RefPtr<ImageData> imageData = ImageData::create(IntSize(m_impl->width(), m_impl->height()));
+    unsigned char* pixels = imageData->data()->data()->data();
+    size_t bufferSize = 4 * m_impl->width() * m_impl->height();
+
+    m_impl->readBackFramebuffer(pixels, bufferSize);
+
+    for (size_t i = 0; i < bufferSize; i += 4)
+        std::swap(pixels[i], pixels[i + 2]);
+
+    return imageData.release();
+}
+
 bool GraphicsContext3DInternal::paintsIntoCanvasBuffer() const
 {
     // If the gpu compositor is on then skip the readback and software rendering path.
@@ -1059,6 +1086,7 @@ DELEGATE_TO_INTERNAL_6(vertexAttribPointer, GC3Duint, GC3Dint, GC3Denum, GC3Dboo
 DELEGATE_TO_INTERNAL_4(viewport, GC3Dint, GC3Dint, GC3Dsizei, GC3Dsizei)
 
 DELEGATE_TO_INTERNAL_1(paintRenderingResultsToCanvas, CanvasRenderingContext*)
+DELEGATE_TO_INTERNAL_R(paintRenderingResultsToImageData, PassRefPtr<ImageData>)
 
 bool GraphicsContext3D::paintsIntoCanvasBuffer() const
 {
index c8f7c7a..b517e60 100644 (file)
@@ -67,6 +67,7 @@ public:
     IntSize getInternalFramebufferSize();
 
     void paintRenderingResultsToCanvas(CanvasRenderingContext*);
+    PassRefPtr<ImageData> paintRenderingResultsToImageData();
     bool paintsIntoCanvasBuffer() const;
 
     void prepareTexture();