b=436932, add mozImageSmoothingEnabled property to canvas; r+sr=roc
authorVladimir Vukicevic <vladimir@pobox.com>
Thu, 09 Oct 2008 14:49:16 -0700
changeset 20227 c6e54a6530ec542a08e6660cd2d2da4193ea756b
parent 20226 63c6632d85986a434ddf977b2545fd90b769aa29
child 20228 6a224120250dd74f99cc1455a34bceefad08613c
push idunknown
push userunknown
push dateunknown
bugs436932
milestone1.9.1b2pre
b=436932, add mozImageSmoothingEnabled property to canvas; r+sr=roc
content/canvas/src/nsCanvasRenderingContext2D.cpp
content/canvas/test/Makefile.in
content/canvas/test/test_2d.imageSmoothing.html
dom/public/idl/canvas/nsIDOMCanvasRenderingContext2D.idl
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -172,18 +172,18 @@ class nsCanvasGradient : public nsIDOMCa
 public:
     NS_DECLARE_STATIC_IID_ACCESSOR(NS_CANVASGRADIENT_PRIVATE_IID)
 
     nsCanvasGradient(gfxPattern* pat, nsICSSParser* cssparser)
         : mPattern(pat), mCSSParser(cssparser)
     {
     }
 
-    void Apply(gfxContext* ctx) {
-        ctx->SetPattern(mPattern);
+    gfxPattern* GetPattern() {
+        return mPattern;
     }
 
     /* nsIDOMCanvasGradient */
     NS_IMETHOD AddColorStop (float offset,
                              const nsAString& colorstr)
     {
         nscolor color;
 
@@ -235,18 +235,18 @@ public:
                     nsIPrincipal* principalForSecurityCheck,
                     PRBool forceWriteOnly)
         : mPattern(pat),
           mPrincipal(principalForSecurityCheck),
           mForceWriteOnly(forceWriteOnly)
     {
     }
 
-    void Apply(gfxContext* ctx) {
-        ctx->SetPattern(mPattern);
+    gfxPattern* GetPattern() {
+        return mPattern;
     }
     
     nsIPrincipal* Principal() { return mPrincipal; }
     PRBool GetForceWriteOnly() { return mForceWriteOnly; }
 
     NS_DECL_ISUPPORTS
 
 protected:
@@ -531,26 +531,29 @@ protected:
 
     // state stack handling
     class ContextState {
     public:
         ContextState() : shadowOffset(0.0, 0.0),
                          globalAlpha(1.0),             
                          shadowBlur(0.0),
                          textAlign(TEXT_ALIGN_START),
-                         textBaseline(TEXT_BASELINE_ALPHABETIC) { }
+                         textBaseline(TEXT_BASELINE_ALPHABETIC),
+                         imageSmoothingEnabled(PR_TRUE)
+        { }
 
         ContextState(const ContextState& other)
             : shadowOffset(other.shadowOffset),
               globalAlpha(other.globalAlpha),
               shadowBlur(other.shadowBlur),
               font(other.font),
               fontGroup(other.fontGroup),
               textAlign(other.textAlign),
-              textBaseline(other.textBaseline)
+              textBaseline(other.textBaseline),
+              imageSmoothingEnabled(other.imageSmoothingEnabled)
         {
             for (int i = 0; i < STYLE_MAX; i++) {
                 colorStyles[i] = other.colorStyles[i];
                 gradientStyles[i] = other.gradientStyles[i];
                 patternStyles[i] = other.patternStyles[i];
             }
         }
 
@@ -586,16 +589,18 @@ protected:
         nsString font;
         nsRefPtr<gfxFontGroup> fontGroup;
         TextAlign textAlign;
         TextBaseline textBaseline;
 
         nscolor colorStyles[STYLE_MAX];
         nsCOMPtr<nsCanvasGradient> gradientStyles[STYLE_MAX];
         nsCOMPtr<nsCanvasPattern> patternStyles[STYLE_MAX];
+
+        PRPackedBool imageSmoothingEnabled;
     };
 
     nsTArray<ContextState> mStyleStack;
 
     inline ContextState& CurrentState() {
         return mStyleStack[mSaveCount];
     }
 
@@ -839,22 +844,31 @@ nsCanvasRenderingContext2D::ApplyStyle(S
 
     nsCanvasPattern* pattern = CurrentState().patternStyles[aWhichStyle];
     if (pattern) {
         if (!mCanvasElement)
             return;
 
         DoDrawImageSecurityCheck(pattern->Principal(),
                                  pattern->GetForceWriteOnly());
-        pattern->Apply(mThebes);
+
+        gfxPattern* gpat = pattern->GetPattern();
+
+        if (CurrentState().imageSmoothingEnabled)
+            gpat->SetFilter(1 /*CAIRO_FILTER_GOOD*/);
+        else
+            gpat->SetFilter(0 /*CAIRO_FILTER_FAST*/);
+
+        mThebes->SetPattern(gpat);
         return;
     }
 
     if (CurrentState().gradientStyles[aWhichStyle]) {
-        CurrentState().gradientStyles[aWhichStyle]->Apply(mThebes);
+        gfxPattern* gpat = CurrentState().gradientStyles[aWhichStyle]->GetPattern();
+        mThebes->SetPattern(gpat);
         return;
     }
 
     gfxRGBA color(CurrentState().colorStyles[aWhichStyle]);
     if (aUseGlobalAlpha)
         color.a *= CurrentState().globalAlpha;
 
     mThebes->SetColor(color);
@@ -2925,16 +2939,21 @@ nsCanvasRenderingContext2D::DrawImage()
     }
     
     matrix.Translate(gfxPoint(sx, sy));
     matrix.Scale(sw/dw, sh/dh);
 
     pattern = new gfxPattern(imgsurf);
     pattern->SetMatrix(matrix);
 
+    if (CurrentState().imageSmoothingEnabled)
+        pattern->SetFilter(1 /*CAIRO_FILTER_GOOD*/);
+    else
+        pattern->SetFilter(0 /*CAIRO_FILTER_FAST*/);
+
     pathSR.Save();
 
     {
         gfxContextAutoSaveRestore autoSR(mThebes);
         mThebes->Translate(gfxPoint(dx, dy));
         mThebes->SetPattern(pattern);
 
         gfxRect clip(0, 0, dw, dh);
@@ -3796,11 +3815,27 @@ nsCanvasRenderingContext2D::CreateImageD
         return NS_ERROR_FAILURE;
 
     jsval *retvalPtr;
     ncc->GetRetValPtr(&retvalPtr);
     *retvalPtr = OBJECT_TO_JSVAL(result);
     ncc->SetReturnValueWasSet(PR_TRUE);
 
     return NS_OK;
-
+}
+
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::GetMozImageSmoothingEnabled(PRBool *retVal)
+{
+    *retVal = CurrentState().imageSmoothingEnabled;
+    return NS_OK;
 }
 
+NS_IMETHODIMP
+nsCanvasRenderingContext2D::SetMozImageSmoothingEnabled(PRBool val)
+{
+    if (val != CurrentState().imageSmoothingEnabled) {
+        CurrentState().imageSmoothingEnabled = val;
+        DirtyAllStyles();
+    }
+
+    return NS_OK;
+}
--- a/content/canvas/test/Makefile.in
+++ b/content/canvas/test/Makefile.in
@@ -694,16 +694,17 @@ include $(topsrcdir)/config/rules.mk
 	test_2d.shadow.alpha.2.html \
 	test_2d.shadow.alpha.3.html \
 	test_2d.shadow.alpha.4.html \
 	test_2d.shadow.alpha.5.html \
 	test_2d.shadow.composite.1.html \
 	test_2d.shadow.composite.2.html \
 	test_2d.shadow.composite.3.html \
 	test_2d.shadow.composite.4.html \
+	test_2d.imageRenderingQuality.html \
 	test_bug397524.html \
 	test_bug405982.html \
 	test_text.font.html \
 	test_text.textAlign.html \
 	test_text.textBaseline.html \
 	test_text.measure.html \
 	test_text.space.replace.html \
 	image_transparent50.png \
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/test_2d.imageSmoothing.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Canvas test: 2d.imageRenderingQuality</title>
+<script src="/MochiKit/MochiKit.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="text/javascript">
+
+var _deferred = false;
+
+function setup() {
+  var c2 = document.getElementById("c2");
+  var cx2 = c2.getContext("2d");
+
+  cx2.fillStyle = "red";
+  cx2.fillRect(0, 0, 2, 2);
+
+  cx2.fillStyle = "rgb(0,255,0)";
+  cx2.fillRect(0, 0, 1, 1);
+}
+
+function f() {
+  setup();
+
+  var c = document.getElementById("c");
+  var c2 = document.getElementById("c2");
+
+  var cx = c.getContext("2d");
+
+  ok(cx.mozImageSmoothingEnabled == true, "initial mozImageSmoothingEnabled is true");
+
+  // check that mozImageSmoothingEnabled is part of the context
+  cx.save();
+  cx.mozImageSmoothingEnabled = false;
+  ok(cx.mozImageSmoothingEnabled == false, "mozImageSmoothingEnabled is false after setting");
+  cx.restore();
+  ok(cx.mozImageSmoothingEnabled == true, "mozImageSmoothingEnabled is true after restore");
+
+  // check that false works
+  cx.mozImageSmoothingEnabled = false;
+
+  cx.scale(10,10);
+  cx.drawImage(c2, 0, 0);
+
+  // this should be all red
+  var data = cx.getImageData(9, 9, 1, 1);
+  var pixels = data.data;
+  ok (pixels[0] == 0 &&
+      pixels[1] == 255 &&
+      pixels[2] == 0 &&
+      pixels[3] == 255,
+      "pixel is " + pixels.toSource() + " (expected [0,255,0,255])");
+
+  if (!_deferred) SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+MochiKit.DOM.addLoadEvent(f);
+</script>
+</head>
+<body>
+<canvas id="c" width="10" height="10"></canvas><br>
+<canvas style="visibility: hidden" id="c2" width="2" height="2"></canvas>
+</body>
+</html>
--- a/dom/public/idl/canvas/nsIDOMCanvasRenderingContext2D.idl
+++ b/dom/public/idl/canvas/nsIDOMCanvasRenderingContext2D.idl
@@ -156,16 +156,20 @@ interface nsIDOMCanvasRenderingContext2D
   // ImageData = { width: #, height: #, data: [r, g, b, a, ...] }
 
   void getImageData();
   void putImageData();
 
   // ImageData createImageData(in float w, in float h);
   void createImageData();
 
+  // image smoothing mode -- if disabled, images won't be smoothed
+  // if scaled.
+  attribute boolean mozImageSmoothingEnabled;
+
   /**
    * Renders a region of a window into the canvas.  The contents of
    * the window's viewport are rendered, ignoring viewport clipping
    * and scrolling.
    *
    * @param x
    * @param y
    * @param w