Diff
Modified: trunk/LayoutTests/ChangeLog (170674 => 170675)
--- trunk/LayoutTests/ChangeLog 2014-07-01 23:22:03 UTC (rev 170674)
+++ trunk/LayoutTests/ChangeLog 2014-07-01 23:26:12 UTC (rev 170675)
@@ -1,3 +1,29 @@
+2014-07-01 Dean Jackson <d...@apple.com>
+
+ [iOS] Subsampled JPEG images do not draw correctly via the canvas APIs
+ https://bugs.webkit.org/show_bug.cgi?id=134513
+ <rdar://problem/12078860>
+ <rdar://problem/16745393>
+
+ Reviewed by Tim Horton.
+
+ Add Canvas2D and WebGL tests that exercise a very large JPEG image.
+
+ The WebGL test is mostly copied from the WebGL test suite, so please
+ excuse the coding style.
+
+ * fast/canvas/image-potential-subsample-expected.txt: Added.
+ * fast/canvas/image-potential-subsample.html: Added.
+ * fast/canvas/resources/image-8000x8000.jpg: Added.
+ * fast/canvas/webgl/resources/tex-image-and-sub-image-2d-with-potentially-subsampled-image.js: Added.
+ (.init):
+ (.runOneIteration):
+ (.runTestOnImage):
+ (.runTest):
+ (generateTest):
+ * fast/canvas/webgl/tex-image-and-sub-image-2d-with-potentially-subsampled-image-expected.txt: Added.
+ * fast/canvas/webgl/tex-image-and-sub-image-2d-with-potentially-subsampled-image.html: Added.
+
2014-07-01 Chris Fleizach <cfleiz...@apple.com>
AX: HTML indeterminate IDL attribute not mapped to checkbox value=2
Added: trunk/LayoutTests/fast/canvas/image-potential-subsample-expected.txt (0 => 170675)
--- trunk/LayoutTests/fast/canvas/image-potential-subsample-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/canvas/image-potential-subsample-expected.txt 2014-07-01 23:26:12 UTC (rev 170675)
@@ -0,0 +1,5 @@
+PASS: Pixel (10, 10) was close to 255,0,0,255
+
+PASS: Pixel (200, 200) was close to 0,255,0,255
+
+PASS: Pixel (390, 390) was close to 0,0,255,255
Property changes on: trunk/LayoutTests/fast/canvas/image-potential-subsample-expected.txt
___________________________________________________________________
Added: svn:mime-type
Added: svn:keywords
Added: svn:eol-style
Added: trunk/LayoutTests/fast/canvas/image-potential-subsample.html (0 => 170675)
--- trunk/LayoutTests/fast/canvas/image-potential-subsample.html (rev 0)
+++ trunk/LayoutTests/fast/canvas/image-potential-subsample.html 2014-07-01 23:26:12 UTC (rev 170675)
@@ -0,0 +1,61 @@
+<script>
+
+if (window.testRunner) {
+ window.testRunner.dumpAsText();
+ window.testRunner.waitUntilDone();
+}
+
+function getPixel(canvas, x, y) {
+ var context = canvas.getContext("2d");
+ var data = "" y, 1, 1);
+ if (!data)
+ return [-1, -1, -1, -1];
+
+ var result = new Array(data.data.length)
+ for (var i = 0; i < data.data.length; i++)
+ result[i] = data.data[i];
+
+ return result;
+}
+
+function checkArrayValues(a1, a2, tolerance) {
+ for (var i = 0, length = a1.length; i < length; i++) {
+ if (Math.abs(a1[i] - a2[i]) > tolerance)
+ return false;
+ }
+ return true;
+}
+
+function pixelShouldBe(canvas, x, y, color, tolerance) {
+ var output = document.createElement("p");
+ var value = getPixel(canvas, x, y);
+ if (checkArrayValues(color, value, tolerance))
+ output.innerText = "PASS: Pixel (" + x + ", " + y + ") was close to " + color;
+ else
+ output.innerText = "FAIL: Pixel (" + x + ", " + y + ") was " + value + ". Expected " + color;
+ document.body.appendChild(output);
+}
+
+function run() {
+ var canvas = document.createElement("canvas");
+ document.body.appendChild(canvas);
+ canvas.width = 400;
+ canvas.height = 400;
+ var ctx = canvas.getContext("2d");
+ var img = new Image()
+ img.addEventListener("load", function() {
+ ctx.fillStyle = "red";
+ ctx.fillRect(0, 0, 400, 400);
+ ctx.drawImage(img, 0, 0, 400, 400);
+ pixelShouldBe(canvas, 10, 10, [255, 0, 0, 255], 5);
+ pixelShouldBe(canvas, 200, 200, [0, 255, 0, 255], 5);
+ pixelShouldBe(canvas, 390, 390, [0, 0, 255, 255], 5);
+
+ if (window.testRunner)
+ testRunner.notifyDone();
+ }, false)
+ img.src = ""
+}
+
+window.addEventListener("load", run, false);
+</script>
Property changes on: trunk/LayoutTests/fast/canvas/image-potential-subsample.html
___________________________________________________________________
Added: svn:mime-type
Added: svn:keywords
Added: svn:eol-style
Added: trunk/LayoutTests/fast/canvas/resources/image-8000x8000.jpg
(Binary files differ)
Property changes on: trunk/LayoutTests/fast/canvas/resources/image-8000x8000.jpg
___________________________________________________________________
Added: svn:mime-type
Added: trunk/LayoutTests/fast/canvas/webgl/resources/tex-image-and-sub-image-2d-with-potentially-subsampled-image.js (0 => 170675)
--- trunk/LayoutTests/fast/canvas/webgl/resources/tex-image-and-sub-image-2d-with-potentially-subsampled-image.js (rev 0)
+++ trunk/LayoutTests/fast/canvas/webgl/resources/tex-image-and-sub-image-2d-with-potentially-subsampled-image.js 2014-07-01 23:26:12 UTC (rev 170675)
@@ -0,0 +1,93 @@
+function generateTest(pixelFormat, pixelType, pathToTestRoot, prologue) {
+ var wtu = WebGLTestUtils;
+ var gl = null;
+ var textureLoc = null;
+ var successfullyParsed = false;
+ var imgCanvas;
+ var red = [255, 0, 0];
+ var green = [0, 255, 0];
+ var blue = [0, 0, 255];
+
+ var init = function()
+ {
+ if (window.initNonKhronosFramework) {
+ window.initNonKhronosFramework(true);
+ }
+
+ description('Verify texImage2D and texSubImage2D code paths with potentially subsampled images (' + pixelFormat + '/' + pixelType + ')');
+
+ gl = wtu.create3DContext("example");
+
+ if (!prologue(gl)) {
+ finishTest();
+ return;
+ }
+
+ var program = wtu.setupTexturedQuad(gl);
+
+ gl.clearColor(0,0,0,1);
+ gl.clearDepth(1);
+
+ textureLoc = gl.getUniformLocation(program, "tex");
+
+ wtu.loadTexture(gl, pathToTestRoot + "/../resources/image-8000x8000.jpg", runTest);
+ }
+
+ function runOneIteration(image, useTexSubImage2D, flipY, topColor, middleColor, bottomColor)
+ {
+ debug('Testing ' + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') +
+ ' with flipY=' + flipY);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+ var texture = gl.createTexture();
+ // Bind the texture to texture unit 0
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ // Set up texture parameters
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ 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);
+ // Set up pixel store parameters
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY);
+ gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);
+ gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE);
+ // Upload the image into the texture
+ if (useTexSubImage2D) {
+ // Initialize the texture to black first
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl[pixelFormat], image.width, image.height, 0,
+ gl[pixelFormat], gl[pixelType], null);
+ gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl[pixelFormat], gl[pixelType], image);
+ } else {
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl[pixelFormat], gl[pixelFormat], gl[pixelType], image);
+ }
+
+ // Point the uniform sampler to texture unit 0
+ gl.uniform1i(textureLoc, 0);
+ // Draw the triangles
+ wtu.drawQuad(gl, [0, 0, 0, 255]);
+
+ // Check the pixels at the top left, middle and bottom right.
+
+ debug("Checking upper left corner");
+ wtu.checkCanvasRect(gl, 2, gl.canvas.height - 2, 1, 1, topColor, "shouldBe " + topColor, 5);
+ debug("Checking middle");
+ wtu.checkCanvasRect(gl, gl.canvas.width / 2, gl.canvas.height / 2, 1, 1, middleColor, "shouldBe " + middleColor, 5);
+ debug("Checking bottom right corner");
+ wtu.checkCanvasRect(gl, gl.canvas.width - 2, 2, 1, 1, bottomColor, "shouldBe " + bottomColor, 5);
+ }
+
+ function runTestOnImage(image) {
+ runOneIteration(image, false, true, red, green, blue);
+ runOneIteration(image, false, false, green, green, green);
+ runOneIteration(image, true, true, red, green, blue);
+ runOneIteration(image, true, false, green, green, green);
+ }
+
+ function runTest(image)
+ {
+ runTestOnImage(image);
+ glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ finishTest();
+ }
+
+ return init;
+}
Property changes on: trunk/LayoutTests/fast/canvas/webgl/resources/tex-image-and-sub-image-2d-with-potentially-subsampled-image.js
___________________________________________________________________
Added: svn:mime-type
Added: svn:keywords
Added: svn:eol-style
Added: trunk/LayoutTests/fast/canvas/webgl/tex-image-and-sub-image-2d-with-potentially-subsampled-image-expected.txt (0 => 170675)
--- trunk/LayoutTests/fast/canvas/webgl/tex-image-and-sub-image-2d-with-potentially-subsampled-image-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/canvas/webgl/tex-image-and-sub-image-2d-with-potentially-subsampled-image-expected.txt 2014-07-01 23:26:12 UTC (rev 170675)
@@ -0,0 +1,37 @@
+Verify texImage2D and texSubImage2D code paths with potentially subsampled images (RGBA/UNSIGNED_BYTE)
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+Testing texImage2D with flipY=true
+Checking upper left corner
+PASS shouldBe 255,0,0
+Checking middle
+PASS shouldBe 0,255,0
+Checking bottom right corner
+PASS shouldBe 0,0,255
+Testing texImage2D with flipY=false
+Checking upper left corner
+PASS shouldBe 0,255,0
+Checking middle
+PASS shouldBe 0,255,0
+Checking bottom right corner
+PASS shouldBe 0,255,0
+Testing texSubImage2D with flipY=true
+Checking upper left corner
+PASS shouldBe 255,0,0
+Checking middle
+PASS shouldBe 0,255,0
+Checking bottom right corner
+PASS shouldBe 0,0,255
+Testing texSubImage2D with flipY=false
+Checking upper left corner
+PASS shouldBe 0,255,0
+Checking middle
+PASS shouldBe 0,255,0
+Checking bottom right corner
+PASS shouldBe 0,255,0
+PASS getError was expected value: NO_ERROR : should be no errors
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Property changes on: trunk/LayoutTests/fast/canvas/webgl/tex-image-and-sub-image-2d-with-potentially-subsampled-image-expected.txt
___________________________________________________________________
Added: svn:mime-type
Added: svn:keywords
Added: svn:eol-style
Added: trunk/LayoutTests/fast/canvas/webgl/tex-image-and-sub-image-2d-with-potentially-subsampled-image.html (0 => 170675)
--- trunk/LayoutTests/fast/canvas/webgl/tex-image-and-sub-image-2d-with-potentially-subsampled-image.html (rev 0)
+++ trunk/LayoutTests/fast/canvas/webgl/tex-image-and-sub-image-2d-with-potentially-subsampled-image.html 2014-07-01 23:26:12 UTC (rev 170675)
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src=""
+<script src=""
+<script src=""
+<script src=""
+<script>
+function testPrologue(gl) {
+ return true;
+}
+</script>
+</head>
+<body _onload_='generateTest("RGBA", "UNSIGNED_BYTE", ".", testPrologue)()'>
+<canvas id="example" width="200px" height="200px"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+</body>
+</html>
Property changes on: trunk/LayoutTests/fast/canvas/webgl/tex-image-and-sub-image-2d-with-potentially-subsampled-image.html
___________________________________________________________________
Added: svn:mime-type
Added: svn:keywords
Added: svn:eol-style
Modified: trunk/Source/WebCore/ChangeLog (170674 => 170675)
--- trunk/Source/WebCore/ChangeLog 2014-07-01 23:22:03 UTC (rev 170674)
+++ trunk/Source/WebCore/ChangeLog 2014-07-01 23:26:12 UTC (rev 170675)
@@ -1,3 +1,40 @@
+2014-07-01 Dean Jackson <d...@apple.com>
+
+ [iOS] Subsampled JPEG images do not draw correctly via the canvas APIs
+ https://bugs.webkit.org/show_bug.cgi?id=134513
+ <rdar://problem/12078860>
+ <rdar://problem/16745393>
+
+ Reviewed by Tim Horton.
+
+ Subsampled images (e.g. JPEG) were not consistently using
+ their original dimensions and subsampled dimensions. This caused
+ things like texImage2D to pack the pixels incorrectly, or drawImage
+ to squish the rendering.
+
+ Renamed m_scale to m_subsamplingScale on FrameData.
+
+ Tests: fast/canvas/image-potential-subsample.html
+ fast/canvas/webgl/tex-image-and-sub-image-2d-with-potentially-subsampled-image.html
+
+ * platform/graphics/BitmapImage.cpp:
+ (WebCore::BitmapImage::cacheFrame): Rename to m_subsamplingScale.
+ (WebCore::BitmapImage::frameAtIndex): Ditto.
+ * platform/graphics/BitmapImage.h:
+ (WebCore::FrameData::FrameData): Ditto.
+ * platform/graphics/cg/BitmapImageCG.cpp:
+ (WebCore::FrameData::clear): Ditto.
+ (WebCore::BitmapImage::BitmapImage): Ditto.
+ (WebCore::BitmapImage::draw): Use a scaledSrcRect that reflects the subsampled size,
+ rather than assuming the srcRect accurately reflects how many pixels we have
+ in the Bitmap.
+ (WebCore::BitmapImage::copyUnscaledFrameAtIndex):
+ * platform/graphics/cg/GraphicsContext3DCG.cpp:
+ (WebCore::GraphicsContext3D::ImageExtractor::extractImage): Similar fix, although this
+ time we just ask the image decoder to take into account the subsampled size
+ when it is "generating" a frame, causing it to use the bitmap data it has already
+ decoded.
+
2014-07-01 Joseph Pecoraro <pecor...@apple.com>
Web Inspector: Selected DOM element highlights invisible near bottom of the viewport (topContentInset?)
Modified: trunk/Source/WebCore/platform/graphics/BitmapImage.cpp (170674 => 170675)
--- trunk/Source/WebCore/platform/graphics/BitmapImage.cpp 2014-07-01 23:22:03 UTC (rev 170674)
+++ trunk/Source/WebCore/platform/graphics/BitmapImage.cpp 2014-07-01 23:26:12 UTC (rev 170675)
@@ -166,7 +166,7 @@
#if PLATFORM(IOS)
m_frames[index].m_frame = m_source.createFrameAtIndex(index, &scaleHint);
- m_frames[index].m_scale = scaleHint;
+ m_frames[index].m_subsamplingScale = scaleHint;
#else
m_frames[index].m_frame = m_source.createFrameAtIndex(index);
#endif
@@ -431,7 +431,7 @@
if (index >= m_frames.size() || !m_frames[index].m_frame)
cacheFrame(index, scaleHint);
- else if (std::min(1.0f, scaleHint) > m_frames[index].m_scale) {
+ else if (std::min(1.0f, scaleHint) > m_frames[index].m_subsamplingScale) {
// If the image is already cached, but at too small a size, re-decode a larger version.
int sizeChange = -m_frames[index].m_frameBytes;
ASSERT(static_cast<int>(m_decodedSize) + sizeChange >= 0);
Modified: trunk/Source/WebCore/platform/graphics/BitmapImage.h (170674 => 170675)
--- trunk/Source/WebCore/platform/graphics/BitmapImage.h 2014-07-01 23:22:03 UTC (rev 170674)
+++ trunk/Source/WebCore/platform/graphics/BitmapImage.h 2014-07-01 23:26:12 UTC (rev 170675)
@@ -71,7 +71,7 @@
: m_frame(0)
, m_orientation(DefaultImageOrientation)
#if PLATFORM(IOS)
- , m_scale(0)
+ , m_subsamplingScale(0)
, m_haveInfo(false)
#endif
, m_duration(0)
@@ -94,7 +94,7 @@
NativeImagePtr m_frame;
ImageOrientation m_orientation;
#if PLATFORM(IOS)
- float m_scale;
+ float m_subsamplingScale;
bool m_haveInfo;
#endif
float m_duration;
Modified: trunk/Source/WebCore/platform/graphics/cg/BitmapImageCG.cpp (170674 => 170675)
--- trunk/Source/WebCore/platform/graphics/cg/BitmapImageCG.cpp 2014-07-01 23:22:03 UTC (rev 170674)
+++ trunk/Source/WebCore/platform/graphics/cg/BitmapImageCG.cpp 2014-07-01 23:26:12 UTC (rev 170675)
@@ -61,7 +61,7 @@
#if PLATFORM(IOS)
m_frameBytes = 0;
- m_scale = 1;
+ m_subsamplingScale = 1;
m_haveInfo = false;
#endif
@@ -114,7 +114,7 @@
m_frames[0].m_haveMetadata = true;
#if PLATFORM(IOS)
- m_frames[0].m_scale = 1;
+ m_frames[0].m_subsamplingScale = 1;
#endif
checkForSolidColor();
@@ -199,11 +199,12 @@
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator compositeOp, BlendMode blendMode, ImageOrientationDescription description)
{
CGImageRef image;
-#if !PLATFORM(IOS)
- startAnimation();
+ FloatRect srcRectForCurrentFrame = srcRect;
- image = frameAtIndex(m_currentFrame);
-#else
+#if PLATFORM(IOS)
+ if (m_originalSize.width() && m_originalSize.height())
+ srcRectForCurrentFrame.scale(m_size.width() / static_cast<float>(m_originalSize.width()), m_size.height() / static_cast<float>(m_originalSize.height()));
+
startAnimation(DoNotCatchUp);
CGRect transformedDestinationRect = CGRectApplyAffineTransform(destRect, CGContextGetCTM(ctxt->platformContext()));
@@ -212,10 +213,15 @@
if (CGContextGetType(ctxt->platformContext()) == kCGContextTypePDF)
imagePossiblyCopied = adoptCF(copyUnscaledFrameAtIndex(m_currentFrame));
else
- imagePossiblyCopied = frameAtIndex(m_currentFrame, std::min<float>(1.0f, std::max(transformedDestinationRect.size.width / srcRect.width(), transformedDestinationRect.size.height / srcRect.height())));
-
+ imagePossiblyCopied = frameAtIndex(m_currentFrame, std::min<float>(1.0f, std::max(transformedDestinationRect.size.width / srcRectForCurrentFrame.width(), transformedDestinationRect.size.height / srcRectForCurrentFrame.height())));
+
image = imagePossiblyCopied.get();
+#else
+ startAnimation();
+
+ image = frameAtIndex(m_currentFrame);
#endif
+
if (!image) // If it's too early we won't have an image yet.
return;
@@ -226,7 +232,7 @@
float scale = 1;
#if PLATFORM(IOS)
- scale = m_frames[m_currentFrame].m_scale;
+ scale = m_frames[m_currentFrame].m_subsamplingScale;
#endif
FloatSize selfSize = currentFrameSize();
ImageOrientation orientation;
@@ -234,7 +240,7 @@
if (description.respectImageOrientation() == RespectImageOrientation)
orientation = frameOrientationAtIndex(m_currentFrame);
- ctxt->drawNativeImage(image, selfSize, styleColorSpace, destRect, srcRect, scale, compositeOp, blendMode, orientation);
+ ctxt->drawNativeImage(image, selfSize, styleColorSpace, destRect, srcRectForCurrentFrame, scale, compositeOp, blendMode, orientation);
if (imageObserver())
imageObserver()->didDraw(this);
@@ -249,7 +255,7 @@
if (index >= m_frames.size() || !m_frames[index].m_frame)
cacheFrame(index, 1);
- if (m_frames[index].m_scale == 1 && !m_source.isSubsampled())
+ if (m_frames[index].m_subsamplingScale == 1 && !m_source.isSubsampled())
return CGImageRetain(m_frames[index].m_frame);
return m_source.createFrameAtIndex(index);
Modified: trunk/Source/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp (170674 => 170675)
--- trunk/Source/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp 2014-07-01 23:22:03 UTC (rev 170674)
+++ trunk/Source/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp 2014-07-01 23:26:12 UTC (rev 170675)
@@ -332,7 +332,17 @@
decoder.setData(m_image->data(), true);
if (!decoder.frameCount())
return false;
+#if PLATFORM(IOS)
+ float scaleHint = 1;
+ if (m_image->isBitmapImage()) {
+ IntSize originalSize = toBitmapImage(m_image)->originalSize();
+ if (originalSize.width() && originalSize.height())
+ scaleHint = std::min<float>(1.0f, std::max(m_image->size().width() / originalSize.width(), m_image->size().width() / originalSize.height()));
+ }
+ m_decodedImage = adoptCF(decoder.createFrameAtIndex(0, &scaleHint));
+#else
m_decodedImage = adoptCF(decoder.createFrameAtIndex(0));
+#endif
m_cgImage = m_decodedImage.get();
} else
m_cgImage = m_image->nativeImageForCurrentFrame();