CanvasRenderingContext2D.drawImage should ignore the EXIF orientation if the image-orientation is none
https://bugs.webkit.org/show_bug.cgi?id=209849

Patch by Said Abou-Hallawa <sabouhallawa@apple.com> on 2020-04-06
Reviewed by Darin Adler.

Source/WebCore:

drawImage() will get the image-orientation of the HTMLImageElement from
its computed style. This will be passed to GraphicsContext::drawImage()
in the ImagePaintingOptions. Previously we were passing FromImage always.

Test: fast/images/image-orientation-none-canvas.html

* html/canvas/CanvasRenderingContext2DBase.cpp:
(WebCore::CanvasRenderingContext2DBase::drawImage):
* html/canvas/CanvasRenderingContext2DBase.h:

LayoutTests:

* fast/images/image-orientation-none-canvas-expected.html: Added.
* fast/images/image-orientation-none-canvas.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@259567 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 03f1276..cf601bd 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,13 @@
+2020-04-06  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        CanvasRenderingContext2D.drawImage should ignore the EXIF orientation if the image-orientation is none
+        https://bugs.webkit.org/show_bug.cgi?id=209849
+
+        Reviewed by Darin Adler.
+
+        * fast/images/image-orientation-none-canvas-expected.html: Added.
+        * fast/images/image-orientation-none-canvas.html: Added.
+
 2020-04-05  Manuel Rego Casasnovas  <rego@igalia.com>
 
         Computed style for "outline-offset" is wrong when "outline-style" is "none"
diff --git a/LayoutTests/fast/images/image-orientation-none-canvas-expected.html b/LayoutTests/fast/images/image-orientation-none-canvas-expected.html
new file mode 100644
index 0000000..e1dc592
--- /dev/null
+++ b/LayoutTests/fast/images/image-orientation-none-canvas-expected.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<style>
+    div.container {
+        display: inline-block;
+        margin-right: 20px;
+        margin-bottom: 10px;
+        width: 100px;
+        vertical-align: top;
+    }
+    div.box {
+        width: 102px;
+        height: 52px;
+    }
+    canvas {
+        border: 1px solid black;
+        width: 100px;
+        height: 50px;
+    }
+</style>
+<body>
+    <b>CanvasRenderingContext2D.drawImage() should ignore the image's EXIF orientation if its style image-orientation is set to "none".</b>
+    <br>
+    <br>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas1"></canvas>
+        </div>
+        <br>Normal
+    </div>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas2"></canvas>
+        </div>
+        <br>Flipped horizontally
+    </div>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas3"></canvas>
+        </div>
+        <br>Rotated 180&deg;
+    </div>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas4"></canvas>
+        </div>
+        <br>Flipped vertically
+    </div>
+    <br>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas5"></canvas>
+        </div>
+        <br>Rotated 90&deg; CCW and flipped vertically
+    </div>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas6"></canvas>
+        </div>
+        <br>Rotated 90&deg; CCW
+    </div>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas7"></canvas>
+        </div>
+        <br>Rotated 90&deg; CW and flipped vertically
+    </div>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas8"></canvas>
+        </div>
+        <br>Rotated 90&deg; CW
+    </div>
+    <br>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas9"></canvas>
+        </div>
+        <br>Undefined (invalid value)
+    </div>
+    <script>
+        if (window.testRunner)
+            window.testRunner.waitUntilDone();
+
+        window.onload = function() {
+            var image = new Image;
+            image.src = "resources/exif-orientation-1-ul.jpg";
+            image.decode().then(() => {
+                let canvases = document.querySelectorAll("canvas");
+                for (const canvas of canvases) {
+                    canvas.width = canvas.offsetWidth - 2;
+                    canvas.height = canvas.offsetHeight - 2;
+                    let context = canvas.getContext("2d");
+                    context.drawImage(image, 0, 0, canvas.width, canvas.height);
+                }
+                if (window.testRunner)
+                    window.testRunner.notifyDone();
+            });
+        }
+    </script>
+</body>
diff --git a/LayoutTests/fast/images/image-orientation-none-canvas.html b/LayoutTests/fast/images/image-orientation-none-canvas.html
new file mode 100644
index 0000000..c1da234
--- /dev/null
+++ b/LayoutTests/fast/images/image-orientation-none-canvas.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<style>
+    div.container {
+        display: inline-block;
+        margin-right: 20px;
+        margin-bottom: 10px;
+        width: 100px;
+        vertical-align: top;
+    }
+    div.box {
+        width: 102px;
+        height: 52px;
+    }
+    canvas {
+        border: 1px solid black;
+        width: 100px;
+        height: 50px;
+    }
+</style>
+<body>
+    <b>CanvasRenderingContext2D.drawImage() should ignore the image's EXIF orientation if its style image-orientation is set to "none".</b>
+    <br>
+    <br>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas1"></canvas>
+        </div>
+        <br>Normal
+    </div>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas2"></canvas>
+        </div>
+        <br>Flipped horizontally
+    </div>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas3"></canvas>
+        </div>
+        <br>Rotated 180&deg;
+    </div>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas4"></canvas>
+        </div>
+        <br>Flipped vertically
+    </div>
+    <br>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas5"></canvas>
+        </div>
+        <br>Rotated 90&deg; CCW and flipped vertically
+    </div>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas6"></canvas>
+        </div>
+        <br>Rotated 90&deg; CCW
+    </div>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas7"></canvas>
+        </div>
+        <br>Rotated 90&deg; CW and flipped vertically
+    </div>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas8"></canvas>
+        </div>
+        <br>Rotated 90&deg; CW
+    </div>
+    <br>
+    <div class ="container">
+        <div class ="box">
+            <canvas id="canvas9"></canvas>
+        </div>
+        <br>Undefined (invalid value)
+    </div>
+    <script>
+        if (window.testRunner)
+            window.testRunner.waitUntilDone();
+
+        window.onload = function() {
+            var names = [
+                { resource: "resources/exif-orientation-1-ul.jpg",  id : "canvas1" },
+                { resource: "resources/exif-orientation-2-ur.jpg",  id : "canvas2" },
+                { resource: "resources/exif-orientation-3-lr.jpg",  id : "canvas3" },
+                { resource: "resources/exif-orientation-4-lol.jpg", id : "canvas4" },
+                { resource: "resources/exif-orientation-5-lu.jpg",  id : "canvas5" },
+                { resource: "resources/exif-orientation-6-ru.jpg",  id : "canvas6" },
+                { resource: "resources/exif-orientation-7-rl.jpg",  id : "canvas7" },
+                { resource: "resources/exif-orientation-8-llo.jpg", id : "canvas8" },
+                { resource: "resources/exif-orientation-9-u.jpg",   id : "canvas9" }
+            ];
+
+            var drawCount = 0;
+
+            names.forEach(function(name) {
+                var image = new Image;
+                image.style.imageOrientation = "none";
+                image.style.display = "none";
+                image.src = name.resource;
+                document.body.appendChild(image);
+                image.decode().then(() => {
+                    let canvas = document.getElementById(name.id);
+                    canvas.width = canvas.offsetWidth - 2;
+                    canvas.height = canvas.offsetHeight - 2;
+                    let context = canvas.getContext("2d");
+                    context.drawImage(image, 0, 0, canvas.width, canvas.height);
+                    if (++drawCount == names.length) {
+                        if (window.testRunner)
+                            window.testRunner.notifyDone();
+                    }
+                });
+            });
+        }
+    </script>
+</body>
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 03172d5..b6621ef 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,20 @@
+2020-04-06  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        CanvasRenderingContext2D.drawImage should ignore the EXIF orientation if the image-orientation is none
+        https://bugs.webkit.org/show_bug.cgi?id=209849
+
+        Reviewed by Darin Adler.
+
+        drawImage() will get the image-orientation of the HTMLImageElement from
+        its computed style. This will be passed to GraphicsContext::drawImage()
+        in the ImagePaintingOptions. Previously we were passing FromImage always.
+
+        Test: fast/images/image-orientation-none-canvas.html
+
+        * html/canvas/CanvasRenderingContext2DBase.cpp:
+        (WebCore::CanvasRenderingContext2DBase::drawImage):
+        * html/canvas/CanvasRenderingContext2DBase.h:
+
 2020-04-05  Rob Buis  <rbuis@igalia.com>
 
         Remove code that has no effect from loadURL
diff --git a/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp b/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp
index d47235c..7c33eef 100644
--- a/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp
+++ b/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.cpp
@@ -1464,7 +1464,11 @@
         return { };
     FloatRect imageRect = FloatRect(FloatPoint(), size(imageElement, ImageSizeType::BeforeDevicePixelRatio));
 
-    auto result = drawImage(imageElement.document(), imageElement.cachedImage(), imageElement.renderer(), imageRect, srcRect, dstRect, op, blendMode);
+    auto orientation = ImageOrientation::FromImage;
+    if (auto* computedStyle = imageElement.computedStyle())
+        orientation = computedStyle->imageOrientation();
+
+    auto result = drawImage(imageElement.document(), imageElement.cachedImage(), imageElement.renderer(), imageRect, srcRect, dstRect, op, blendMode, orientation);
 
     if (!result.hasException())
         checkOrigin(&imageElement);
@@ -1487,7 +1491,7 @@
 }
 #endif
 
-ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(Document& document, CachedImage* cachedImage, const RenderObject* renderer, const FloatRect& imageRect, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator& op, const BlendMode& blendMode)
+ExceptionOr<void> CanvasRenderingContext2DBase::drawImage(Document& document, CachedImage* cachedImage, const RenderObject* renderer, const FloatRect& imageRect, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator& op, const BlendMode& blendMode, ImageOrientation orientation)
 {
     if (!std::isfinite(dstRect.x()) || !std::isfinite(dstRect.y()) || !std::isfinite(dstRect.width()) || !std::isfinite(dstRect.height())
         || !std::isfinite(srcRect.x()) || !std::isfinite(srcRect.y()) || !std::isfinite(srcRect.width()) || !std::isfinite(srcRect.height()))
@@ -1543,7 +1547,7 @@
         downcast<BitmapImage>(*image).updateFromSettings(document.settings());
     }
 
-    ImagePaintingOptions options = { op, blendMode, ImageOrientation::FromImage };
+    ImagePaintingOptions options = { op, blendMode, orientation };
 
     if (rectContainsCanvas(normalizedDstRect)) {
         c->drawImage(*image, normalizedDstRect, normalizedSrcRect, options);
diff --git a/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.h b/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.h
index c6a880b..a9e58ea 100644
--- a/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.h
+++ b/Source/WebCore/html/canvas/CanvasRenderingContext2DBase.h
@@ -333,7 +333,7 @@
     ExceptionOr<void> drawImage(HTMLImageElement&, const FloatRect& srcRect, const FloatRect& dstRect);
     ExceptionOr<void> drawImage(HTMLImageElement&, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator&, const BlendMode&);
     ExceptionOr<void> drawImage(CanvasBase&, const FloatRect& srcRect, const FloatRect& dstRect);
-    ExceptionOr<void> drawImage(Document&, CachedImage*, const RenderObject*, const FloatRect& imageRect, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator&, const BlendMode&);
+    ExceptionOr<void> drawImage(Document&, CachedImage*, const RenderObject*, const FloatRect& imageRect, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator&, const BlendMode&, ImageOrientation = ImageOrientation::FromImage);
 #if ENABLE(VIDEO)
     ExceptionOr<void> drawImage(HTMLVideoElement&, const FloatRect& srcRect, const FloatRect& dstRect);
 #endif