From 897ac256fc6a0ba7d20bfbfe3f6a67aed6a72b6d Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Wed, 22 Jun 2011 20:49:33 +0200 Subject: [PATCH] Merge ImageCanvasProxy and CanvasProxy. Add support for rendering multiple canvas objects on the worker and assemble them again on the main thread. --- canvas_proxy.js | 79 ++++++++++++++++++++++++++++++---------------- viewer_worker.html | 59 ++++++++++++++++++++++++---------- worker.js | 2 +- 3 files changed, 95 insertions(+), 45 deletions(-) diff --git a/canvas_proxy.js b/canvas_proxy.js index 823757492..ed209f126 100644 --- a/canvas_proxy.js +++ b/canvas_proxy.js @@ -1,23 +1,23 @@ -var ImageCanvasProxyCounter = 0; -function ImageCanvasProxy(width, height) { - this.id = ImageCanvasProxyCounter++; - this.width = width; - this.height = height; - - // Using `Uint8ClampedArray` seems to be the type of ImageData - at least - // Firebug tells me so. - this.imgData = { - data: Uint8ClampedArray(width * height * 4) - }; -} - -ImageCanvasProxy.prototype.putImageData = function(imgData) { - // this.ctx.putImageData(imgData, 0, 0); -} - -ImageCanvasProxy.prototype.getCanvas = function() { - return this; -} +// var ImageCanvasProxyCounter = 0; +// function ImageCanvasProxy(width, height) { +// this.id = ImageCanvasProxyCounter++; +// this.width = width; +// this.height = height; +// +// // Using `Uint8ClampedArray` seems to be the type of ImageData - at least +// // Firebug tells me so. +// this.imgData = { +// data: Uint8ClampedArray(width * height * 4) +// }; +// } +// +// ImageCanvasProxy.prototype.putImageData = function(imgData) { +// // this.ctx.putImageData(imgData, 0, 0); +// } +// +// ImageCanvasProxy.prototype.getCanvas = function() { +// return this; +// } var JpegStreamProxyCounter = 0; // WebWorker Proxy for JpegStream. @@ -61,7 +61,10 @@ function GradientProxy(stack, x0, y0, x1, y1) { } } +var canvasProxyCounter = 0; function CanvasProxy(width, height) { + this.id = canvasProxyCounter++; + var stack = this.$stack = []; // Dummy context exposed. @@ -73,12 +76,15 @@ function CanvasProxy(width, height) { return ctx; } + this.getCanvas = function() { + return this; + } + // Expose only the minimum of the canvas object - there is no dom to do // more here. - ctx.canvas = { - width: width, - height: height - } + this.width = width; + this.height = height; + ctx.canvas = this; var ctxFunc = [ "createRadialGradient", @@ -127,9 +133,23 @@ function CanvasProxy(width, height) { return new GradientProxy(stack, x0, y0, x1, y1); } + ctx.getImageData = function(x, y, w, h) { + return { + width: w, + height: h, + data: Uint8ClampedArray(w * h * 4) + }; + } + + ctx.putImageData = function(data, x, y, width, height) { + stack.push(["$putImageData", [data, x, y, width, height]]); + } + ctx.drawImage = function(image, x, y, width, height, sx, sy, swidth, sheight) { - if (image instanceof ImageCanvasProxy) { - stack.push(["$drawCanvas", [image.imgData, x, y, image.width, image.height]]); + if (image instanceof CanvasProxy) { + // Send the image/CanvasProxy to the main thread. + image.flush(); + stack.push(["$drawCanvas", [image.id, x, y, sx, sy, swidth, sheight]]); } else if(image instanceof JpegStreamProxy) { stack.push(["$drawImage", [image.id, x, y, sx, sy, swidth, sheight]]) } else { @@ -214,6 +234,11 @@ function CanvasProxy(width, height) { CanvasProxy.prototype.flush = function() { postMessage("canvas_proxy_stack"); - postMessage(this.$stack); + postMessage({ + id: this.id, + stack: this.$stack, + width: this.width, + height: this.height + }); this.$stack.length = 0; } diff --git a/viewer_worker.html b/viewer_worker.html index 83c41e6e0..c7041bcbd 100644 --- a/viewer_worker.html +++ b/viewer_worker.html @@ -13,7 +13,8 @@ function toc(msg) { } var myWorker = new Worker('worker.js'); -var images = {}; +var imagesList = {}; +var canvasList = {}; var gradient; var currentX = 0; @@ -41,27 +42,40 @@ var special = { currentX += this.measureText(text).width; }, - "$drawCanvas": function(data, x, y, width, height) { + "$putImageData": function(imageData, x, y) { // Ugly: getImageData is called here only to get an object of the right // shape - we are not interessted in the data, as we set it the line // afterwards to something custome. // Can we do better here? - var imgData = ctx.getImageData(0, 0, width, height); - imgData.data = data; - ctx.putImageData(imgData, x, y); + var imgData = this.getImageData(0, 0, imageData.width, imageData.height); + imgData.data = imageData.data; + this.putImageData(imgData, x, y); }, "$drawImage": function(id, x, y, sx, sy, swidth, sheight) { - var image = images[id]; + var image = imagesList[id]; if (!image) { throw "Image not found"; } - ctx.drawImage(image, x, y, image.width, image.height, + this.drawImage(image, x, y, image.width, image.height, sx, sy, swidth, sheight); }, + "$drawCanvas": function(id, x, y, sx, sy, swidth, sheight) { + var canvas = canvasList[id]; + if (!canvas) { + throw "Canvas not found"; + } + if (sheight != null) { + this.drawImage(canvas, x, y, canvas.width, canvas.height, + sx, sy, swidth, sheight); + } else { + this.drawImage(canvas, x, y, canvas.width, canvas.height); + } + }, + "$createLinearGradient": function(x0, y0, x1, y1) { - gradient = ctx.createLinearGradient(x0, y0, x1, y1); + gradient = this.createLinearGradient(x0, y0, x1, y1); }, "$addColorStop": function(i, rgba) { @@ -69,16 +83,17 @@ var special = { }, "$fillStyleGradient": function() { - ctx.fillStyle = gradient; + this.fillStyle = gradient; }, "$strokeStyleGradient": function() { - ctx.strokeStyle = gradient; + this.strokeStyle = gradient; } } var gStack; -function renderProxyCanvas(stack) { +function renderProxyCanvas(canvas, stack) { + var ctx = canvas.getContext("2d"); for (var i = 0; i < stack.length; i++) { // for (var i = 0; i < 1000; i++) { var opp = stack[i]; @@ -135,7 +150,7 @@ myWorker.onmessage = function(event) { case JPEG_STREAM: var img = new Image(); img.src = "data:image/jpeg;base64," + window.btoa(data.str); - images[data.id] = img; + imagesList[data.id] = img; console.log("got image", data.id) break; @@ -171,16 +186,25 @@ myWorker.onmessage = function(event) { break; case CANVAS_PROXY_STACK: - var stack = data; + var id = data.id; + var stack = data.stack; gStack = stack; - console.log("canvas stack size", stack.length) + + // Check if there is already a canvas with the given id. If not, + // create a new canvas. + if (!canvasList[id]) { + var newCanvas = document.createElement("canvas"); + newCanvas.width = data.width; + newCanvas.height = data.height; + canvasList[id] = newCanvas; + } // There might be fonts that need to get loaded. Shedule the // rendering at the end of the event queue ensures this. setTimeout(function() { - tic(); - renderProxyCanvas(stack); - toc("canvas rendering") + if (id == 0) tic(); + renderProxyCanvas(canvasList[id], stack); + if (id == 0) toc("canvas rendering") }, 0); onMessageState = WAIT; break; @@ -234,6 +258,7 @@ function prevPage() { window.onload = function() { window.canvas = document.getElementById("canvas"); window.ctx = canvas.getContext("2d"); + canvasList[0] = window.canvas; open("compressed.tracemonkey-pldi-09.pdf"); } diff --git a/worker.js b/worker.js index 59ad8edea..e59e37155 100644 --- a/worker.js +++ b/worker.js @@ -49,7 +49,7 @@ onmessage = function(event) { // page.compile will collect all fonts for us, once we have loaded them // we can trigger the actual page rendering with page.display var fonts = []; - var gfx = new CanvasGraphics(canvas.getContext("2d"), ImageCanvasProxy); + var gfx = new CanvasGraphics(canvas.getContext("2d"), CanvasProxy); page.compile(gfx, fonts); // Inspect fonts and translate the missing one.