admin管理员组文章数量:1201401
I'm working on some simple image manipulation functions with canvas. The user uploads an image, is able to rotate and crop it and then clicks ok. The image is then split in half with each half drawn mirrored to two canvas elements, like this:
Original
Mirrored
It all works great in Chrome, Firefox, IE and Android devices. Safari won't play nice though. All the image manipulation works fine except the split function. It does draw to one of the canvas elements, but the other is just black. I've tried changing the drawImage code around, but I just can't get it to work.
Here's the function:
function splitImage(canvas, context, image, isLeftSide) {
canvas.width = img.width;
canvas.height = img.height;
context.save();
if(isLeftSide) {
context.drawImage(
image,
image.width / 2,
0,
image.width,
image.height,
canvas.width / 2,
0,
canvas.width,
canvas.height
);
context.scale(-1, 1);
context.drawImage(
image,
image.width / 2,
0,
image.width,
image.height,
-canvas.width / 2,
0,
canvas.width,
canvas.height
);
} else {
context.drawImage(
image,
0,
0,
image.width / 2,
image.height,
0,
0,
canvas.width / 2,
canvas.height
);
context.scale(-1, 1);
context.drawImage(
image,
0,
0,
image.width / 2,
image.height,
-canvas.width,
0,
canvas.width / 2,
canvas.height
);
}
context.restore();
download(canvas);
}
To be exact, it's the drawImage operations inside the if(isLeftSide) that doesn't work in Safari.
Any ideas?
Edit: It doesn't seem to work on iOS devices either. I've read that Safari and iOS devices might run out of memory when working with large images. To counteract this (and reduce some lag) I've added a resize function. The image is resized to a maximum of 800 px width and 800 px height if necessary, keeping the aspect ratio intact. This is done before any other image manipulation, but hasn't made any difference.
The resize function:
function resizeImage() {
var size = 800;
if(imgTemp.width > size && imgTemp.width >= imgTemp.height) {
imgTemp.height = (imgTemp.height / imgTemp.width) * size;
imgTemp.width = size;
} else if (imgTemp.height > size && imgTemp.height > imgTemp.width) {
imgTemp.width = (imgTemp.width / imgTemp.height) * size;
imgTemp.height = size;
}
}
I'm working on some simple image manipulation functions with canvas. The user uploads an image, is able to rotate and crop it and then clicks ok. The image is then split in half with each half drawn mirrored to two canvas elements, like this:
Original
Mirrored
It all works great in Chrome, Firefox, IE and Android devices. Safari won't play nice though. All the image manipulation works fine except the split function. It does draw to one of the canvas elements, but the other is just black. I've tried changing the drawImage code around, but I just can't get it to work.
Here's the function:
function splitImage(canvas, context, image, isLeftSide) {
canvas.width = img.width;
canvas.height = img.height;
context.save();
if(isLeftSide) {
context.drawImage(
image,
image.width / 2,
0,
image.width,
image.height,
canvas.width / 2,
0,
canvas.width,
canvas.height
);
context.scale(-1, 1);
context.drawImage(
image,
image.width / 2,
0,
image.width,
image.height,
-canvas.width / 2,
0,
canvas.width,
canvas.height
);
} else {
context.drawImage(
image,
0,
0,
image.width / 2,
image.height,
0,
0,
canvas.width / 2,
canvas.height
);
context.scale(-1, 1);
context.drawImage(
image,
0,
0,
image.width / 2,
image.height,
-canvas.width,
0,
canvas.width / 2,
canvas.height
);
}
context.restore();
download(canvas);
}
To be exact, it's the drawImage operations inside the if(isLeftSide) that doesn't work in Safari.
Any ideas?
Edit: It doesn't seem to work on iOS devices either. I've read that Safari and iOS devices might run out of memory when working with large images. To counteract this (and reduce some lag) I've added a resize function. The image is resized to a maximum of 800 px width and 800 px height if necessary, keeping the aspect ratio intact. This is done before any other image manipulation, but hasn't made any difference.
The resize function:
function resizeImage() {
var size = 800;
if(imgTemp.width > size && imgTemp.width >= imgTemp.height) {
imgTemp.height = (imgTemp.height / imgTemp.width) * size;
imgTemp.width = size;
} else if (imgTemp.height > size && imgTemp.height > imgTemp.width) {
imgTemp.width = (imgTemp.width / imgTemp.height) * size;
imgTemp.height = size;
}
}
Share
Improve this question
edited Apr 3, 2017 at 11:20
krabban
asked Feb 19, 2016 at 8:50
krabbankrabban
1481 silver badge7 bronze badges
0
1 Answer
Reset to default 26The bug occurs when drawImage()
is called out of the bounds of the sourceImage.
You have to double check that the source width and source height are always smaller or equal to the image's width and height :
So for the first if block :
var sourceX = image.width/2;
var sourceY = 0;
var sourceWidth = image.width - sourceX; // you're in the bounds
var sourceHeight = image.height;
var destX = canvas.width/2;
var destY = 0;
var destWidth = canvas.width;
var destHeight = canvas.height;
ctx.drawImage(image, sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight);
Or as a one-liner :
ctx.drawImage(image, image.width/2, 0, image.width - (image.width/2), image.height, canvas.width/2, 0, canvas.width, canvas.height);
Ps: For a recent project I had to make a complete monkey-patch over this Safari bug. You can find it in this gist, and in below code-snippet:
const canvas = document.getElementById( "canvas" );
const ctx = canvas.getContext( "2d" );
ctx.fillRect( 0, 0, 80, 80 );
ctx.drawImage( canvas, -100, -100, 180, 180, 30, 30, 90, 90 );
<canvas id="canvas" width="300" height="300"></canvas>
<script>
// drawImage monkey-patch for Safari
(()=> {
if( !needPoly() ) { return; }
const proto = CanvasRenderingContext2D.prototype;
const original = proto.drawImage;
if( !original ) {
console.error( "This script requires a basic implementation of drawImage" );
return;
}
proto.drawImage = function drawImage( source, x, y ) { // length: 3
const will_crop = arguments.length === 9;
if( !will_crop ) {
return original.apply( this, [...arguments] );
}
const safe_rect = getSafeRect( ...arguments );
if( isEmptyRect( safe_rect ) ) {
return;
}
return original.apply( this, safe_rect );
}
function needPoly() {
const ctx = document.createElement( "canvas" ).getContext( "2d" );
ctx.fillRect( 0, 0, 40, 40 );
ctx.drawImage( ctx.canvas, -40, -40, 80, 80, 50, 50, 20, 20 );
const img = ctx.getImageData( 50, 50, 30, 30 ); // 10px around expected square
const data = new Uint32Array( img.data.buffer );
const colorAt = (x, y) => data[ y * img.width + x ];
const transparents = [ [ 9, 9 ], [ 20, 9 ], [ 9, 20 ], [ 20, 20 ] ];
const blacks = [ [ 10, 10 ], [ 19, 10 ], [ 10, 19 ], [ 19, 19 ] ];
return transparents.some( ([ x, y ]) => colorAt( x, y ) !== 0x00000000 ) ||
blacks.some( ([ x, y ]) => colorAt( x, y ) === 0x00000000 )
}
function getSafeRect( image, sx, sy, sw, sh, dx, dy, dw, dh ) {
const { width, height } = getSourceDimensions( image );
if( sw < 0 ) {
sx += sw;
sw = Math.abs( sw );
}
if( sh < 0 ) {
sy += sh;
sh = Math.abs( sh );
}
if( dw < 0 ) {
dx += dw;
dw = Math.abs( dw );
}
if( dh < 0 ) {
dy += dh;
dh = Math.abs( dh );
}
const x1 = Math.max( sx, 0 );
const x2 = Math.min( sx + sw, width );
const y1 = Math.max( sy, 0 );
const y2 = Math.min( sy + sh, height );
const w_ratio = dw / sw;
const h_ratio = dh / sh;
return [
image,
x1,
y1,
x2 - x1,
y2 - y1,
sx < 0 ? dx - (sx * w_ratio) : dx,
sy < 0 ? dy - (sy * h_ratio) : dy,
(x2 - x1) * w_ratio,
(y2 - y1) * h_ratio
];
}
function isEmptyRect( args ) {
// sw, sh, dw, dh
return [ 3, 4, 7, 8 ].some( (index) => !args[ index ] );
}
function getSourceDimensions( source ) {
const sourceIs = ( type ) => {
const constructor = globalThis[ type ];
return constructor && (source instanceof constructor);
};
if( sourceIs( "HTMLImageElement" ) ) {
return { width: source.naturalWidth, height: source.naturalHeight };
}
else if( sourceIs( "HTMLVideoElement" ) ) {
return { width: source.videoWidth, height: source.videoHeight };
}
else if( sourceIs( "SVGImageElement" ) ) {
throw new TypeError( "SVGImageElement isn't yet supported as source image.", "UnsupportedError" );
}
else if( sourceIs( "HTMLCanvasElement" ) || sourceIs( "ImageBitmap" ) ) {
return source;
}
}
})();
</script>
本文标签: javascriptCropping with drawImage not working in SafariStack Overflow
版权声明:本文标题:javascript - Cropping with drawImage not working in Safari - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1738583330a2101289.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论