]> git.parisson.com Git - pdf.js.git/commitdiff
Initial implementation of shading patterns. Radial shading is somewhat broken.
authorsbarman <sbarman@L3CWZ5T.(none)>
Tue, 5 Jul 2011 20:09:45 +0000 (15:09 -0500)
committersbarman <sbarman@L3CWZ5T.(none)>
Tue, 5 Jul 2011 20:09:45 +0000 (15:09 -0500)
pdf.js

diff --git a/pdf.js b/pdf.js
index f7a3359de8a9b76c95bb92d47ba46ee7c4de1fc8..733097c21a103e1ef396034e6afa0b30fa23d382 100644 (file)
--- a/pdf.js
+++ b/pdf.js
@@ -3940,10 +3940,6 @@ var CanvasGraphics = (function() {
         },
         setFillColor: function(/*...*/) {
             var cs = this.getFillColorSpace();
-            if (cs.name == "Pattern") {
-                TODO("implement Pattern fill");
-                return;
-            }
             var color = cs.getRgb(arguments);
             this.setFillRGBColor.apply(this, color);
         },
@@ -3952,28 +3948,43 @@ var CanvasGraphics = (function() {
 
             if (cs.name == "Pattern") {
                 var patternName = arguments[0];
-                if (IsName(patternName)) {
-                    var xref = this.xref;
-                    var patternRes = xref.fetchIfRef(this.res.get("Pattern"));
-                    if (!patternRes)
-                        error("Unable to find pattern resource");
-
-                    var pattern = xref.fetchIfRef(patternRes.get(patternName.name));
-                    var patternDict = IsStream(pattern) ? pattern.dict : pattern;
-                    var types = [null, this.tilingFill,
-                                   function() { TODO("Shading Patterns"); }];
-                    var typeNum = patternDict.get("PatternType");
-                    var patternFn = types[typeNum];
-                    if (!patternFn)
-                        error("Unhandled pattern type");
-                    patternFn.call(this, pattern, patternDict);
-                }
+                this.ctx.fillStyle = this.getPattern(patternName);
             } else {
                 // TODO real impl
                 this.setFillColor.apply(this, arguments);
             }
         },
-        tilingFill: function(pattern) {
+        getPattern: function(patternName) {
+            if (!IsName(patternName))
+                error("Bad args to getPattern");
+
+            var xref = this.xref;
+            var patternRes = xref.fetchIfRef(this.res.get("Pattern"));
+            if (!patternRes)
+                error("Unable to find pattern resource");
+
+            var pattern = xref.fetchIfRef(patternRes.get(patternName.name));
+            var patternDict = IsStream(pattern) ? pattern.dict : pattern;
+
+            var types = [null, this.getTilingPattern,
+                this.getShadingPattern];
+            var typeNum = patternDict.get("PatternType");
+            var patternFn = types[typeNum];
+            if (!patternFn)
+                error("Unhandled pattern type");
+            return patternFn.call(this, pattern, patternDict);
+        },
+        getShadingPattern: function(pattern) {
+            var matrix = pattern.get("Matrix");
+
+            this.save();
+            this.transform.apply(this, matrix);
+            var shading = this.getShading(pattern.get("Shading"));
+            this.restore();
+
+            return shading;
+        },
+        getTilingPattern: function(pattern) {
             function applyMatrix(point, m) {
                 var x = point[0] * m[0] + point[1] * m[2] + m[4];
                 var y = point[0] * m[1] + point[1] * m[3] + m[5];
@@ -4065,7 +4076,7 @@ var CanvasGraphics = (function() {
 
             TODO("Inverse pattern is painted");
             pattern = this.ctx.createPattern(tmpCanvas, "repeat");
-            this.ctx.fillStyle = pattern;
+            return pattern;
         },
         setStrokeGray: function(gray) {
             this.setStrokeRGBColor(gray, gray, gray);
@@ -4085,9 +4096,8 @@ var CanvasGraphics = (function() {
         setFillCMYKColor: function(c, m, y, k) {
             this.ctx.fillStyle = this.makeCssCmyk(c, m, y, k);
         },
-
         // Shading
-        shadingFill: function(entryRef) {
+        shadingFill: function(shadingName) {
             var xref = this.xref;
             var res = this.res;
 
@@ -4095,11 +4105,26 @@ var CanvasGraphics = (function() {
             if (!shadingRes)
                 error("No shading resource found");
 
-            var shading = xref.fetchIfRef(shadingRes.get(entryRef.name));
+            var shading = xref.fetchIfRef(shadingRes.get(shadingName.name));
             if (!shading)
                 error("No shading object found");
+            
+            var shadingFill = this.getShading(shading);
 
             this.save();
+            this.ctx.fillStyle = shadingFill;
+
+            // HACK to draw the gradient onto an infinite rectangle.
+            // PDF gradients are drawn across the entire image while
+            // Canvas only allows gradients to be drawn in a rectangle
+            // The following bug should allow us to remove this.
+            // https://bugzilla.mozilla.org/show_bug.cgi?id=664884
+            this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10);
+
+            this.restore();
+        },
+        getShading: function(shading) {
+            shading = this.xref.fetchIfRef(shading);
 
             var bbox = shading.get("BBox");
             if (bbox && IsArray(bbox) && 4 == bbox.length) {
@@ -4108,28 +4133,25 @@ var CanvasGraphics = (function() {
                 this.endPath();
             }
 
-            var cs = shading.get2("ColorSpace", "CS");
-            TODO("shading-fill color space");
-
             var background = shading.get("Background");
             if (background)
                 TODO("handle background colors");
 
+            var cs = shading.get2("ColorSpace", "CS");
+            cs = ColorSpace.parse(cs, this.xref, this.res);
+            
             var types = [null,
-                           this.fillFunctionShading,
-                           this.fillAxialShading,
-                           this.fillRadialShading];
+                         null,
+                         this.getAxialShading,
+                         this.getRadialShading];
 
             var typeNum = shading.get("ShadingType");
-            var fillFn = types[typeNum];
-            if (!fillFn)
+            var shadingFn = types[typeNum];
+            if (!shadingFn)
                 error("Unknown or NYI type of shading '"+ typeNum +"'");
-            fillFn.apply(this, [shading]);
-
-            this.restore();
+            return shadingFn.call(this, shading, cs);
         },
-
-        fillAxialShading: function(sh) {
+        getAxialShading: function(sh, cs) {
             var coordsArr = sh.get("Coords");
             var x0 = coordsArr[0], y0 = coordsArr[1],
                 x1 = coordsArr[2], y1 = coordsArr[3];
@@ -4160,24 +4182,61 @@ var CanvasGraphics = (function() {
             // if there are sharp color changes. Ideally, we would implement
             // the spec faithfully and add lossless optimizations.
             var step = (t1 - t0) / 10;
+            var diff = t1 - t0;
 
             for (var i = t0; i <= t1; i += step) {
-                var c = fn.func([i]);
-                gradient.addColorStop(i, this.makeCssRgb.apply(this, c));
+                var color = fn.func([i]);
+                var rgbColor = cs.getRgb(color);
+                gradient.addColorStop((i - t0) / diff,
+                        this.makeCssRgb.apply(this, rgbColor));
             }
 
-            this.ctx.fillStyle = gradient;
-
-            // HACK to draw the gradient onto an infinite rectangle.
-            // PDF gradients are drawn across the entire image while
-            // Canvas only allows gradients to be drawn in a rectangle
-            // The following bug should allow us to remove this.
-            // https://bugzilla.mozilla.org/show_bug.cgi?id=664884
-            this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10);
+            return gradient;
         },
+        getRadialShading: function(sh, cs) {
+            var coordsArr = sh.get("Coords");
+            var x0 = coordsArr[0], y0 = coordsArr[1],
+                r0 = coordsArr[2];
+            var x1 = coordsArr[3], y1 = coordsArr[4],
+                r1 = coordsArr[5];
+
+            var t0 = 0.0, t1 = 1.0;
+            if (sh.has("Domain")) {
+                var domainArr = sh.get("Domain");
+                t0 = domainArr[0], t1 = domainArr[1];
+            }
+
+            var extendStart = false, extendEnd = false;
+            if (sh.has("Extend")) {
+                var extendArr = sh.get("Extend");
+                extendStart = extendArr[0], extendEnd = extendArr[1];
+                TODO("Support extend");
+            }
+            var fnObj = sh.get("Function");
+            fnObj = this.xref.fetchIfRef(fnObj);
+            if (IsArray(fnObj))
+                error("No support for array of functions");
+            else if (!IsPDFFunction(fnObj))
+                error("Invalid function");
+            var fn = new PDFFunction(this.xref, fnObj);
+
+            var gradient = 
+                this.ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
 
-        fillRadialShading: function(sh) {
-            TODO("radial shading");
+            // 10 samples seems good enough for now, but probably won't work
+            // if there are sharp color changes. Ideally, we would implement
+            // the spec faithfully and add lossless optimizations.
+            var step = (t1 - t0) / 10;
+            var diff = t1 - t0;
+
+            for (var i = t0; i <= t1; i += step) {
+                var color = fn.func([i]);
+                var rgbColor = cs.getRgb(color);
+                gradient.addColorStop((i - t0) / diff, 
+                        this.makeCssRgb.apply(this, rgbColor));
+            }
+
+            return gradient;
         },
 
         // Images
@@ -4569,10 +4628,10 @@ var DeviceRgbCS = (function() {
         this.defaultColor = [0, 0, 0];
     }
     constructor.prototype = {
-        getRgb: function graycs_getRgb(color) {
+        getRgb: function rgbcs_getRgb(color) {
             return color;
         },
-        getRgbBuffer: function graycs_getRgbBuffer(input) {
+        getRgbBuffer: function rgbcs_getRgbBuffer(input) {
             return input;
         }
     };
@@ -4586,14 +4645,61 @@ var DeviceCmykCS = (function() {
         this.defaultColor = [0, 0, 0, 1];
     }
     constructor.prototype = {
-        getRgb: function graycs_getRgb(color) {
-            var c = color[0], y = color[1], m = color[2], k = color[3];
-            var ri = (1 - Math.min(1, c * (1 - k) + k)) | 0;
-            var gi = (1 - Math.min(1, m * (1 - k) + k)) | 0;
-            var bi = (1 - Math.min(1, y * (1 - k) + k)) | 0;
-            return [ri, gi, bi];
-        },
-        getRgbBuffer: function graycs_getRgbBuffer(colorBuf) {
+        getRgb: function cmykcs_getRgb(color) {
+            var c = color[0], m = color[1], y = color[2], k = color[3];
+            var c1 = 1 - c, m1 = 1 - m, y1 = 1 - y, k1 = 1 - k;
+
+            var x, r, g, b;
+            // this is a matrix multiplication, unrolled for performance
+            // code is taken from the poppler implementation
+            x = c1 * m1 * y1 * k1; // 0 0 0 0
+            r = g = b = x;
+            x = c1 * m1 * y1 * k;  // 0 0 0 1
+            r += 0.1373 * x;
+            g += 0.1216 * x;
+            b += 0.1255 * x;
+            x = c1 * m1 * y  * k1; // 0 0 1 0
+            r += x;
+            g += 0.9490 * x;
+            x = c1 * m1 * y  * k;  // 0 0 1 1
+            r += 0.1098 * x;
+            g += 0.1020 * x;
+            x = c1 * m  * y1 * k1; // 0 1 0 0
+            r += 0.9255 * x;
+            b += 0.5490 * x;
+            x = c1 * m  * y1 * k;  // 0 1 0 1
+            r += 0.1412 * x;
+            x = c1 * m  * y  * k1; // 0 1 1 0
+            r += 0.9294 * x;
+            g += 0.1098 * x;
+            b += 0.1412 * x;
+            x = c1 * m  * y  * k;  // 0 1 1 1
+            r += 0.1333 * x;
+            x = c  * m1 * y1 * k1; // 1 0 0 0
+            g += 0.6784 * x;
+            b += 0.9373 * x;
+            x = c  * m1 * y1 * k;  // 1 0 0 1
+            g += 0.0588 * x;
+            b += 0.1412 * x;
+            x = c  * m1 * y  * k1; // 1 0 1 0
+            g += 0.6510 * x;
+            b += 0.3137 * x;
+            x = c  * m1 * y  * k;  // 1 0 1 1
+            g += 0.0745 * x;
+            x = c  * m  * y1 * k1; // 1 1 0 0
+            r += 0.1804 * x;
+            g += 0.1922 * x;
+            b += 0.5725 * x;
+            x = c  * m  * y1 * k;  // 1 1 0 1
+            b += 0.0078 * x;
+            x = c  * m  * y  * k1; // 1 1 1 0
+            r += 0.2118 * x;
+            g += 0.2119 * x;
+            b += 0.2235 * x;
+
+            return [r, g, b];
+        },
+        getRgbBuffer: function cmykcs_getRgbBuffer(colorBuf) {
             error("conversion from rgb to cmyk not implemented for images");
             return colorBuf;
         }