switched background rendering to use patterns

This commit is contained in:
Niklas von Hertzen 2013-01-03 22:25:35 +02:00
parent 65b4bdf282
commit 053a0a4787
6 changed files with 55 additions and 141 deletions

View File

@ -323,18 +323,16 @@
_html2canvas.Generate.Gradient = function(src, bounds) { _html2canvas.Generate.Gradient = function(src, bounds) {
var canvas = document.createElement('canvas'), var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'), ctx = canvas.getContext('2d'),
gradient, grad, i, len, img; gradient, grad, i, len;
canvas.width = bounds.width; canvas.width = bounds.width;
canvas.height = bounds.height; canvas.height = bounds.height;
// TODO: add support for multi defined background gradients (like radial gradient example in background.html) // TODO: add support for multi defined background gradients
gradient = _html2canvas.Generate.parseGradient(src, bounds); gradient = _html2canvas.Generate.parseGradient(src, bounds);
img = new Image(); if(gradient) {
if(gradient.type === 'linear') {
if(gradient){
if(gradient.type === 'linear'){
grad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1); grad = ctx.createLinearGradient(gradient.x0, gradient.y0, gradient.x1, gradient.y1);
for (i = 0, len = gradient.colorStops.length; i < len; i+=1) { for (i = 0, len = gradient.colorStops.length; i < len; i+=1) {
@ -349,8 +347,7 @@
ctx.fillStyle = grad; ctx.fillStyle = grad;
ctx.fillRect(0, 0, bounds.width, bounds.height); ctx.fillRect(0, 0, bounds.width, bounds.height);
img.src = canvas.toDataURL(); } else if(gradient.type === 'circle') {
} else if(gradient.type === 'circle'){
grad = ctx.createRadialGradient(gradient.cx, gradient.cy, 0, gradient.cx, gradient.cy, gradient.rx); grad = ctx.createRadialGradient(gradient.cx, gradient.cy, 0, gradient.cx, gradient.cy, gradient.rx);
@ -366,8 +363,7 @@
ctx.fillStyle = grad; ctx.fillStyle = grad;
ctx.fillRect(0, 0, bounds.width, bounds.height); ctx.fillRect(0, 0, bounds.width, bounds.height);
img.src = canvas.toDataURL(); } else if(gradient.type === 'ellipse') {
} else if(gradient.type === 'ellipse'){
// draw circle // draw circle
var canvasRadial = document.createElement('canvas'), var canvasRadial = document.createElement('canvas'),
@ -378,7 +374,7 @@
canvasRadial.width = canvasRadial.height = di; canvasRadial.width = canvasRadial.height = di;
grad = ctxRadial.createRadialGradient(gradient.rx, gradient.ry, 0, gradient.rx, gradient.ry, ri); grad = ctxRadial.createRadialGradient(gradient.rx, gradient.ry, 0, gradient.rx, gradient.ry, ri);
for (i = 0, len = gradient.colorStops.length; i < len; i+=1) { for (i = 0, len = gradient.colorStops.length; i < len; i+=1) {
try { try {
grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color); grad.addColorStop(gradient.colorStops[i].stop, gradient.colorStops[i].color);
@ -393,21 +389,12 @@
ctx.fillStyle = gradient.colorStops[i - 1].color; ctx.fillStyle = gradient.colorStops[i - 1].color;
ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(canvasRadial, gradient.cx - gradient.rx, gradient.cy - gradient.ry, 2 * gradient.rx, 2 * gradient.ry);
imgRadial = new Image();
imgRadial.onload = function() { // wait until the image is filled
// transform circle to ellipse
ctx.drawImage(imgRadial, gradient.cx - gradient.rx, gradient.cy - gradient.ry, 2 * gradient.rx, 2 * gradient.ry);
img.src = canvas.toDataURL();
};
imgRadial.src = canvasRadial.toDataURL();
} }
} }
return img; return canvas;
}; };
_html2canvas.Generate.ListAlpha = function(number) { _html2canvas.Generate.ListAlpha = function(number) {

View File

@ -740,118 +740,27 @@ _html2canvas.Parse = function (images, options) {
numDraws+=1; numDraws+=1;
} }
function renderBackgroundSlice (ctx, image, x, y, width, height, elx, ely){
var sourceX = (elx - x > 0) ? elx-x :0,
sourceY= (ely - y > 0) ? ely-y : 0;
drawImage(
ctx,
image,
Math.floor(sourceX), // source X
Math.floor(sourceY), // source Y
Math.ceil(width-sourceX), // source Width
Math.ceil(height-sourceY), // source Height
Math.ceil(x+sourceX), // destination X
Math.ceil(y+sourceY), // destination Y
Math.ceil(width-sourceX), // destination width
Math.ceil(height-sourceY) // destination height
);
}
function renderBackgroundRepeat(ctx, image, backgroundPosition, bounds) { function renderBackgroundRepeat(ctx, image, backgroundPosition, bounds) {
var bgy, var offsetX = Math.round(bounds.left + backgroundPosition.left),
height, offsetY = Math.round(bounds.top + backgroundPosition.top);
add,
h;
backgroundPosition.top -= Math.ceil(backgroundPosition.top / image.height) * image.height; ctx.createPattern(image);
ctx.translate(offsetX, offsetY);
for(bgy = (bounds.top + backgroundPosition.top); bgy < (bounds.height + bounds.top); bgy = Math.floor(bgy+image.height) - add) { ctx.fill();
h = Math.min(image.height,(bounds.height + bounds.top) - bgy); ctx.translate(-offsetX, -offsetY);
height = (Math.floor(bgy + image.height) > h + bgy) ? (h + bgy) - bgy : image.height;
if (bgy < bounds.top){
add = bounds.top - bgy;
bgy = bounds.top;
} else {
add = 0;
}
renderBackgroundRepeatX(ctx, image, backgroundPosition, bounds.left, bgy, bounds.width, height);
if (add > 0){
backgroundPosition.top += add;
}
}
} }
function renderBackgroundNoRepeat(ctx, image, backgroundPosition, x, y, w, h) { function backgroundRepeatShape(ctx, image, backgroundPosition, bounds, left, top, width, height) {
var bgdw = w - backgroundPosition.left, var args = [];
bgdh = h - backgroundPosition.top, args.push(["line", Math.round(left), Math.round(top)]);
bgsx = backgroundPosition.left, args.push(["line", Math.round(left + width), Math.round(top)]);
bgsy = backgroundPosition.top, args.push(["line", Math.round(left + width), Math.round(height + top)]);
bgdx = backgroundPosition.left + x, args.push(["line", Math.round(left), Math.round(height + top)]);
bgdy = backgroundPosition.top + y; createShape(ctx, args);
ctx.save();
if (bgsx<0){ ctx.clip();
bgsx = Math.abs(bgsx); renderBackgroundRepeat(ctx, image, backgroundPosition, bounds);
bgdx += bgsx; ctx.restore();
bgdw = Math.min(w,image.width-bgsx);
} else {
bgdw = Math.min(bgdw,image.width);
bgsx = 0;
}
if (bgsy < 0){
bgsy = Math.abs(bgsy);
bgdy += bgsy;
bgdh = Math.min(h, image.height - bgsy);
} else {
bgdh = Math.min(bgdh, image.height);
bgsy = 0;
}
if (bgdh > 0 && bgdw > 0){
drawImage(
ctx,
image,
bgsx,
bgsy,
bgdw,
bgdh,
bgdx,
bgdy,
bgdw,
bgdh
);
}
}
function renderBackgroundRepeatY (ctx, image, backgroundPosition, x, y, w, h){
var height,
width = Math.min(image.width, w),
bgy;
backgroundPosition.top -= Math.ceil(backgroundPosition.top / image.height) * image.height;
for (bgy = y + backgroundPosition.top; bgy < h + y; bgy = Math.round(bgy + image.height)){
height = (Math.floor(bgy + image.height) > h + y) ? (h+y) - bgy : image.height;
renderBackgroundSlice(ctx, image, x + backgroundPosition.left, bgy,width, height, x, y);
}
}
function renderBackgroundRepeatX(ctx, image, backgroundPosition, x, y, w, h){
var height = Math.min(image.height, h),
width,
bgx;
backgroundPosition.left -= Math.ceil(backgroundPosition.left / image.width) * image.width;
for (bgx = x + backgroundPosition.left; bgx < w + x; bgx = Math.round(bgx + image.width)) {
width = (Math.floor(bgx + image.width) > w + x) ? (w + x) - bgx : image.width;
renderBackgroundSlice(ctx, image, bgx,(y + backgroundPosition.top), width, height, x, y);
}
} }
function renderBackgroundColor(ctx, backgroundBounds, bgcolor) { function renderBackgroundColor(ctx, backgroundBounds, bgcolor) {
@ -870,15 +779,18 @@ _html2canvas.Parse = function (images, options) {
backgroundRepeat = getCSS(el, "backgroundRepeat").split(",")[0]; backgroundRepeat = getCSS(el, "backgroundRepeat").split(",")[0];
switch (backgroundRepeat) { switch (backgroundRepeat) {
case "repeat-x": case "repeat-x":
renderBackgroundRepeatX(ctx, image, backgroundPosition, bounds.left, bounds.top, bounds.width, bounds.height); backgroundRepeatShape(ctx, image, backgroundPosition, bounds,
bounds.left, bounds.top + backgroundPosition.top, 99999, image.height);
break; break;
case "repeat-y": case "repeat-y":
renderBackgroundRepeatY(ctx, image, backgroundPosition, bounds.left, bounds.top, bounds.width, bounds.height); backgroundRepeatShape(ctx, image, backgroundPosition, bounds,
bounds.left + backgroundPosition.left, bounds.top, image.width, 99999);
break; break;
case "no-repeat": case "no-repeat":
renderBackgroundNoRepeat(ctx, image, backgroundPosition, bounds.left, bounds.top, bounds.width, bounds.height); backgroundRepeatShape(ctx, image, backgroundPosition, bounds,
bounds.left + backgroundPosition.left, bounds.top + backgroundPosition.top, image.width, image.height);
break; break;
default: default:
@ -960,8 +872,9 @@ _html2canvas.Parse = function (images, options) {
borders = stack.borders, borders = stack.borders,
ctx = stack.ctx, ctx = stack.ctx,
backgroundBounds = getBackgroundBounds(borders, bounds, stack.clip), backgroundBounds = getBackgroundBounds(borders, bounds, stack.clip),
borderData = parseBorders(element, bounds, borders), borderData = parseBorders(element, bounds, borders);
clipPath = createShape(ctx, borderData.clip);
createShape(ctx, borderData.clip);
ctx.save(); ctx.save();
ctx.clip(); ctx.clip();

View File

@ -119,16 +119,14 @@ _html2canvas.Preload = function( options ) {
// opera throws exception on external-content.html // opera throws exception on external-content.html
try { try {
background_image = _html2canvas.Util.getCSS(el, 'backgroundImage'); background_image = _html2canvas.Util.getCSS(el, 'backgroundImage');
}catch(e) { } catch(e) {
h2clog("html2canvas: failed to get background-image - Exception: " + e.message); h2clog("html2canvas: failed to get background-image - Exception: " + e.message);
} }
if ( background_image && background_image !== "1" && background_image !== "none" ) { if (background_image && background_image !== "1" && background_image !== "none") {
// TODO add multi image background support // TODO add multi image background support
if (/^(-webkit|-o|-moz|-ms|linear)-/.test( background_image )) { if (/^(-webkit|-o|-moz|-ms|linear)-/.test( background_image )) {
img = _html2canvas.Generate.Gradient(background_image, _html2canvas.Util.Bounds( el ) );
img = _html2canvas.Generate.Gradient( background_image, _html2canvas.Util.Bounds( el ) );
if ( img !== undefined ){ if ( img !== undefined ){
images[background_image] = { images[background_image] = {

View File

@ -11,6 +11,13 @@ function h2cRenderContext(width, height) {
'arguments': arguments 'arguments': arguments
}); });
}, },
translate: function() {
storage.push({
type: "function",
name: "translate",
'arguments': arguments
});
},
fill: function() { fill: function() {
storage.push({ storage.push({
type: "function", type: "function",
@ -39,6 +46,13 @@ function h2cRenderContext(width, height) {
'arguments': arguments 'arguments': arguments
}); });
}, },
createPattern: function() {
storage.push({
type: "function",
name: "createPattern",
'arguments': arguments
});
},
drawShape: function() { drawShape: function() {
var shape = []; var shape = [];

View File

@ -145,6 +145,8 @@ _html2canvas.Renderer.Canvas = function( options ) {
if (!usingFlashcanvas || renderItem['arguments'][0] + renderItem['arguments'][2] < flashMaxSize && renderItem['arguments'][1] + renderItem['arguments'][3] < flashMaxSize) { if (!usingFlashcanvas || renderItem['arguments'][0] + renderItem['arguments'][2] < flashMaxSize && renderItem['arguments'][1] + renderItem['arguments'][3] < flashMaxSize) {
ctx.fillRect.apply( ctx, renderItem['arguments'] ); ctx.fillRect.apply( ctx, renderItem['arguments'] );
} }
} else if (renderItem.name === "createPattern") {
ctx.fillStyle = ctx.createPattern(renderItem['arguments'][0], "repeat");
} else if (renderItem.name === "drawShape") { } else if (renderItem.name === "drawShape") {
createShape(renderItem['arguments']); createShape(renderItem['arguments']);
} else if (renderItem.name === "fillText") { } else if (renderItem.name === "fillText") {

View File

@ -13,7 +13,7 @@ var h2cSelector, h2cOptions;
document.write(srcStart + '/tests/assets/jquery.plugin.html2canvas.js' + scrEnd); document.write(srcStart + '/tests/assets/jquery.plugin.html2canvas.js' + scrEnd);
var html2canvas = ['Core', 'Generate', 'Parse', 'Preload', 'Queue', 'Renderer', 'Util', 'Support', 'Font', 'renderers/Canvas'], i; var html2canvas = ['Core', 'Generate', 'Parse', 'Preload', 'Queue', 'Renderer', 'Util', 'Support', 'Font', 'renderers/Canvas'], i;
for (i = 0; i < html2canvas.length; ++i) { for (i = 0; i < html2canvas.length; ++i) {
document.write(srcStart + '/src/' + html2canvas[i] + '.js' + scrEnd); document.write(srcStart + '/src/' + html2canvas[i] + '.js?' + Math.random() + scrEnd);
} }
window.onload = function() { window.onload = function() {
h2cSelector = [document.body]; h2cSelector = [document.body];