Canvas Rotate, toDataUrl, and then Crop is ruining image qualityCSS Display an Image Resized and CroppedResizing an image in an HTML5 canvasaccessing cache pictures phonegapRotating image cuts cornersHTML5 Canvas Drawing Multiple PNG images with transparencyCropping and resizing an image results in blank image in a Phonegap application on iOSConvert canvas into image with fix size in HTML5HTML 5 Canvas] Image quality when I resizeResize image and rotate canvas 90 degreesObtaining unclipped canvas information for transfer to another canvas
How can I, as DM, avoid the Conga Line of Death occurring when implementing some form of flanking rule?
Do you waste sorcery points if you try to apply metamagic to a spell from a scroll but fail to cast it?
Do I have to take mana from my deck or hand when tapping a dual land?
Does Doodling or Improvising on the Piano Have Any Benefits?
"Oh no!" in Latin
Sigmoid with a slope but no asymptotes?
Given this phrasing in the lease, when should I pay my rent?
Should I assume I have passed probation?
Air travel with refrigerated insulin
Can I say "fingers" when referring to toes?
How to make money from a browser who sees 5 seconds into the future of any web page?
When and why was runway 07/25 at Kai Tak removed?
Can I cause damage to electrical appliances by unplugging them when they are turned on?
How do I prevent inappropriate ads from appearing in my game?
How do you justify more code being written by following clean code practices?
Why is participating in the European Parliamentary elections used as a threat?
What the heck is gets(stdin) on site coderbyte?
How to leave product feedback on macOS?
How to make a list of partial sums using forEach
Do I have to know the General Relativity theory to understand the concept of inertial frame?
Alignment of six matrices
SOQL query causes internal Salesforce error
What's the name of the logical fallacy where a debater extends a statement far beyond the original statement to make it true?
Did I make a mistake by ccing email to boss to others?
Canvas Rotate, toDataUrl, and then Crop is ruining image quality
CSS Display an Image Resized and CroppedResizing an image in an HTML5 canvasaccessing cache pictures phonegapRotating image cuts cornersHTML5 Canvas Drawing Multiple PNG images with transparencyCropping and resizing an image results in blank image in a Phonegap application on iOSConvert canvas into image with fix size in HTML5HTML 5 Canvas] Image quality when I resizeResize image and rotate canvas 90 degreesObtaining unclipped canvas information for transfer to another canvas
I have an image that I'm allowing users to rotate 90 degrees in any direction. Every time they rotate, I use canvas to perform the image manipulations and then save the data returned by the canvas.toDataURL("image/png", 1)
.
The problem is that the image quality decreases every time I rotate the image.
My end goal is to rotate a rectangular image without losing image quality and also saving the new data url.
function rotateAndSave(image: HTMLImageElement, degrees: number): string {
const imageWidth = image.naturalWidth;
const imageHeight = image.naturalHeight;
const startedHorizontalEndedVertical = imageWidth > imageHeight;
const canvasSize = startedHorizontalEndedVertical ? imageWidth : imageHeight;
const canvas = document.createElement("canvas");
canvas.width = canvasSize;
canvas.height = canvasSize;
const ctx = canvas.getContext("2d");
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// center and rotate canvas
const translateCanvas = canvasSize / 2;
ctx.translate(translateCanvas, translateCanvas);
ctx.rotate(degrees * Math.PI / 180);
// draw from center
const translateImageX = startedHorizontalEndedVertical ? -translateCanvas : (-imageWidth / 2);
const translateImageY = startedHorizontalEndedVertical ? (-imageHeight / 2) : -translateCanvas;
ctx.drawImage(image, translateImageX, translateImageY);
// I got 'cropPlusExport' from another stackoverflow question.
function cropPlusExport(img, cropX, cropY, cropWidth, cropHeight)
// create a temporary canvas sized to the cropped size
const canvas1 = document.createElement('canvas');
canvas1.width = cropWidth;
canvas1.height = cropHeight;
const ctx1 = canvas1.getContext('2d');
ctx1.setTransform(1, 0, 0, 1, 0, 0);
ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
// use the extended from of drawImage to draw the
// cropped area to the temp canvas
ctx1.drawImage(img, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
return canvas1.toDataURL("image/png", 1);
// Start Cropping
let squareImage = new Image();
squareImage.src = canvas.toDataURL("image/png", 1);
squareImage.onload = () =>
const sx = startedHorizontalEndedVertical ? ((canvasSize - imageHeight) / 2) : 0;
const sy = startedHorizontalEndedVertical ? 0 : ((canvasSize - imageWidth) / 2);
const sw = imageHeight;
const sh = imageWidth;
const data = cropPlusExport(squareImage, sx, sy, sw, sh);
// Update DOM via angular binding...
const dataUrl = data.split(",")[1];
this.imageSource = dataUrl;
squareImage = null;
example html
<div class="view">
<img [src]="imageSource" />
</div>
Keep in mind that I am cropping to the natural width and height of the image. So, what's weird is that if I don't crop, then the image quality doesn't change but when I do crop, the image quality changes.
javascript image image-processing canvas
add a comment |
I have an image that I'm allowing users to rotate 90 degrees in any direction. Every time they rotate, I use canvas to perform the image manipulations and then save the data returned by the canvas.toDataURL("image/png", 1)
.
The problem is that the image quality decreases every time I rotate the image.
My end goal is to rotate a rectangular image without losing image quality and also saving the new data url.
function rotateAndSave(image: HTMLImageElement, degrees: number): string {
const imageWidth = image.naturalWidth;
const imageHeight = image.naturalHeight;
const startedHorizontalEndedVertical = imageWidth > imageHeight;
const canvasSize = startedHorizontalEndedVertical ? imageWidth : imageHeight;
const canvas = document.createElement("canvas");
canvas.width = canvasSize;
canvas.height = canvasSize;
const ctx = canvas.getContext("2d");
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// center and rotate canvas
const translateCanvas = canvasSize / 2;
ctx.translate(translateCanvas, translateCanvas);
ctx.rotate(degrees * Math.PI / 180);
// draw from center
const translateImageX = startedHorizontalEndedVertical ? -translateCanvas : (-imageWidth / 2);
const translateImageY = startedHorizontalEndedVertical ? (-imageHeight / 2) : -translateCanvas;
ctx.drawImage(image, translateImageX, translateImageY);
// I got 'cropPlusExport' from another stackoverflow question.
function cropPlusExport(img, cropX, cropY, cropWidth, cropHeight)
// create a temporary canvas sized to the cropped size
const canvas1 = document.createElement('canvas');
canvas1.width = cropWidth;
canvas1.height = cropHeight;
const ctx1 = canvas1.getContext('2d');
ctx1.setTransform(1, 0, 0, 1, 0, 0);
ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
// use the extended from of drawImage to draw the
// cropped area to the temp canvas
ctx1.drawImage(img, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
return canvas1.toDataURL("image/png", 1);
// Start Cropping
let squareImage = new Image();
squareImage.src = canvas.toDataURL("image/png", 1);
squareImage.onload = () =>
const sx = startedHorizontalEndedVertical ? ((canvasSize - imageHeight) / 2) : 0;
const sy = startedHorizontalEndedVertical ? 0 : ((canvasSize - imageWidth) / 2);
const sw = imageHeight;
const sh = imageWidth;
const data = cropPlusExport(squareImage, sx, sy, sw, sh);
// Update DOM via angular binding...
const dataUrl = data.split(",")[1];
this.imageSource = dataUrl;
squareImage = null;
example html
<div class="view">
<img [src]="imageSource" />
</div>
Keep in mind that I am cropping to the natural width and height of the image. So, what's weird is that if I don't crop, then the image quality doesn't change but when I do crop, the image quality changes.
javascript image image-processing canvas
add a comment |
I have an image that I'm allowing users to rotate 90 degrees in any direction. Every time they rotate, I use canvas to perform the image manipulations and then save the data returned by the canvas.toDataURL("image/png", 1)
.
The problem is that the image quality decreases every time I rotate the image.
My end goal is to rotate a rectangular image without losing image quality and also saving the new data url.
function rotateAndSave(image: HTMLImageElement, degrees: number): string {
const imageWidth = image.naturalWidth;
const imageHeight = image.naturalHeight;
const startedHorizontalEndedVertical = imageWidth > imageHeight;
const canvasSize = startedHorizontalEndedVertical ? imageWidth : imageHeight;
const canvas = document.createElement("canvas");
canvas.width = canvasSize;
canvas.height = canvasSize;
const ctx = canvas.getContext("2d");
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// center and rotate canvas
const translateCanvas = canvasSize / 2;
ctx.translate(translateCanvas, translateCanvas);
ctx.rotate(degrees * Math.PI / 180);
// draw from center
const translateImageX = startedHorizontalEndedVertical ? -translateCanvas : (-imageWidth / 2);
const translateImageY = startedHorizontalEndedVertical ? (-imageHeight / 2) : -translateCanvas;
ctx.drawImage(image, translateImageX, translateImageY);
// I got 'cropPlusExport' from another stackoverflow question.
function cropPlusExport(img, cropX, cropY, cropWidth, cropHeight)
// create a temporary canvas sized to the cropped size
const canvas1 = document.createElement('canvas');
canvas1.width = cropWidth;
canvas1.height = cropHeight;
const ctx1 = canvas1.getContext('2d');
ctx1.setTransform(1, 0, 0, 1, 0, 0);
ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
// use the extended from of drawImage to draw the
// cropped area to the temp canvas
ctx1.drawImage(img, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
return canvas1.toDataURL("image/png", 1);
// Start Cropping
let squareImage = new Image();
squareImage.src = canvas.toDataURL("image/png", 1);
squareImage.onload = () =>
const sx = startedHorizontalEndedVertical ? ((canvasSize - imageHeight) / 2) : 0;
const sy = startedHorizontalEndedVertical ? 0 : ((canvasSize - imageWidth) / 2);
const sw = imageHeight;
const sh = imageWidth;
const data = cropPlusExport(squareImage, sx, sy, sw, sh);
// Update DOM via angular binding...
const dataUrl = data.split(",")[1];
this.imageSource = dataUrl;
squareImage = null;
example html
<div class="view">
<img [src]="imageSource" />
</div>
Keep in mind that I am cropping to the natural width and height of the image. So, what's weird is that if I don't crop, then the image quality doesn't change but when I do crop, the image quality changes.
javascript image image-processing canvas
I have an image that I'm allowing users to rotate 90 degrees in any direction. Every time they rotate, I use canvas to perform the image manipulations and then save the data returned by the canvas.toDataURL("image/png", 1)
.
The problem is that the image quality decreases every time I rotate the image.
My end goal is to rotate a rectangular image without losing image quality and also saving the new data url.
function rotateAndSave(image: HTMLImageElement, degrees: number): string {
const imageWidth = image.naturalWidth;
const imageHeight = image.naturalHeight;
const startedHorizontalEndedVertical = imageWidth > imageHeight;
const canvasSize = startedHorizontalEndedVertical ? imageWidth : imageHeight;
const canvas = document.createElement("canvas");
canvas.width = canvasSize;
canvas.height = canvasSize;
const ctx = canvas.getContext("2d");
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// center and rotate canvas
const translateCanvas = canvasSize / 2;
ctx.translate(translateCanvas, translateCanvas);
ctx.rotate(degrees * Math.PI / 180);
// draw from center
const translateImageX = startedHorizontalEndedVertical ? -translateCanvas : (-imageWidth / 2);
const translateImageY = startedHorizontalEndedVertical ? (-imageHeight / 2) : -translateCanvas;
ctx.drawImage(image, translateImageX, translateImageY);
// I got 'cropPlusExport' from another stackoverflow question.
function cropPlusExport(img, cropX, cropY, cropWidth, cropHeight)
// create a temporary canvas sized to the cropped size
const canvas1 = document.createElement('canvas');
canvas1.width = cropWidth;
canvas1.height = cropHeight;
const ctx1 = canvas1.getContext('2d');
ctx1.setTransform(1, 0, 0, 1, 0, 0);
ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
// use the extended from of drawImage to draw the
// cropped area to the temp canvas
ctx1.drawImage(img, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
return canvas1.toDataURL("image/png", 1);
// Start Cropping
let squareImage = new Image();
squareImage.src = canvas.toDataURL("image/png", 1);
squareImage.onload = () =>
const sx = startedHorizontalEndedVertical ? ((canvasSize - imageHeight) / 2) : 0;
const sy = startedHorizontalEndedVertical ? 0 : ((canvasSize - imageWidth) / 2);
const sw = imageHeight;
const sh = imageWidth;
const data = cropPlusExport(squareImage, sx, sy, sw, sh);
// Update DOM via angular binding...
const dataUrl = data.split(",")[1];
this.imageSource = dataUrl;
squareImage = null;
example html
<div class="view">
<img [src]="imageSource" />
</div>
Keep in mind that I am cropping to the natural width and height of the image. So, what's weird is that if I don't crop, then the image quality doesn't change but when I do crop, the image quality changes.
javascript image image-processing canvas
javascript image image-processing canvas
asked Mar 7 at 3:03
christo8989christo8989
2,51221830
2,51221830
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
Canvas drawing is lossy, and rotating an image induce hard modifications of the pixels. So indeed, if you start always from the last state, you'll end up adding more and more artifacts to your image.
Simply store the original image somewhere and always start from there instead of using the modified version.
// will fire in a loop
img.onload = e => elem.rotateAndSave(1);
const elem =
// store a copy of the original image
originalimage: img.cloneNode(),
angle: 0,
rotateAndSave(degrees)
// always use the stored original image
const image = this.originalimage;
// keep track of current transform
this.angle += degrees;
const imageWidth = image.naturalWidth;
const imageHeight = image.naturalHeight;
const startedHorizontalEndedVertical = imageWidth > imageHeight;
const canvasSize = startedHorizontalEndedVertical ? imageWidth : imageHeight;
const canvas = document.createElement("canvas");
canvas.width = canvasSize;
canvas.height = canvasSize;
const ctx = canvas.getContext("2d");
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// center and rotate canvas
const translateCanvas = canvasSize / 2;
ctx.translate(translateCanvas, translateCanvas);
ctx.rotate(this.angle * Math.PI / 180);
// draw from center
const translateImageX = startedHorizontalEndedVertical ? -translateCanvas : (-imageWidth / 2);
const translateImageY = startedHorizontalEndedVertical ? (-imageHeight / 2) : -translateCanvas;
ctx.drawImage(image, translateImageX, translateImageY);
// I got 'cropPlusExport' from another stackoverflow question.
function cropPlusExport(img, cropX, cropY, cropWidth, cropHeight)
// create a temporary canvas sized to the cropped size
const canvas1 = document.createElement('canvas');
canvas1.width = cropWidth;
canvas1.height = cropHeight;
const ctx1 = canvas1.getContext('2d');
ctx1.setTransform(1, 0, 0, 1, 0, 0);
ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
// use the extended from of drawImage to draw the
// cropped area to the temp canvas
ctx1.drawImage(img, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
return canvas1.toDataURL("image/png", 1);
// Start Cropping
let squareImage = new Image();
squareImage.src = canvas.toDataURL("image/png", 1);
squareImage.onload = () =>
const sx = startedHorizontalEndedVertical ? ((canvasSize - imageHeight) / 2) : 0;
const sy = startedHorizontalEndedVertical ? 0 : ((canvasSize - imageWidth) / 2);
const sw = imageHeight;
const sh = imageWidth;
const data = cropPlusExport(squareImage, sx, sy, sw, sh);
// Update DOM via angular binding...
img.src = data;
;
<img crossorigin src="https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg" id="img">
There is so much loss in one rotation that even this solution is not plausible with my constraints. It feels like there is a way to reduce the artifacts but maybe not. For instance, if I just rotate the image, quality is fine. But cropping after rotating is not? And, just cropping a non-rotated image is fine? Why is it so bad when the two are put together (especially since rotate and crop are done on two different canvases)?
– christo8989
Mar 7 at 14:19
Also, if the image is a square, quality is fine.
– christo8989
Mar 7 at 14:23
? You have some css applied on your image? Quality is fine for me in the snippet.
– Kaiido
Mar 7 at 14:30
Well, I still have to save the image, so if they rotate and then refresh, the image would get pretty bad after about 4 rotations. Not exactly what I want to put in production code.
– christo8989
Mar 7 at 14:37
1
Then save the original image and the current transform. Without knowing what you are doing we can't help you. And there is just nothing you can do to preserve original quality of an image when you rotate it.
– Kaiido
Mar 7 at 23:25
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55035406%2fcanvas-rotate-todataurl-and-then-crop-is-ruining-image-quality%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
Canvas drawing is lossy, and rotating an image induce hard modifications of the pixels. So indeed, if you start always from the last state, you'll end up adding more and more artifacts to your image.
Simply store the original image somewhere and always start from there instead of using the modified version.
// will fire in a loop
img.onload = e => elem.rotateAndSave(1);
const elem =
// store a copy of the original image
originalimage: img.cloneNode(),
angle: 0,
rotateAndSave(degrees)
// always use the stored original image
const image = this.originalimage;
// keep track of current transform
this.angle += degrees;
const imageWidth = image.naturalWidth;
const imageHeight = image.naturalHeight;
const startedHorizontalEndedVertical = imageWidth > imageHeight;
const canvasSize = startedHorizontalEndedVertical ? imageWidth : imageHeight;
const canvas = document.createElement("canvas");
canvas.width = canvasSize;
canvas.height = canvasSize;
const ctx = canvas.getContext("2d");
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// center and rotate canvas
const translateCanvas = canvasSize / 2;
ctx.translate(translateCanvas, translateCanvas);
ctx.rotate(this.angle * Math.PI / 180);
// draw from center
const translateImageX = startedHorizontalEndedVertical ? -translateCanvas : (-imageWidth / 2);
const translateImageY = startedHorizontalEndedVertical ? (-imageHeight / 2) : -translateCanvas;
ctx.drawImage(image, translateImageX, translateImageY);
// I got 'cropPlusExport' from another stackoverflow question.
function cropPlusExport(img, cropX, cropY, cropWidth, cropHeight)
// create a temporary canvas sized to the cropped size
const canvas1 = document.createElement('canvas');
canvas1.width = cropWidth;
canvas1.height = cropHeight;
const ctx1 = canvas1.getContext('2d');
ctx1.setTransform(1, 0, 0, 1, 0, 0);
ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
// use the extended from of drawImage to draw the
// cropped area to the temp canvas
ctx1.drawImage(img, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
return canvas1.toDataURL("image/png", 1);
// Start Cropping
let squareImage = new Image();
squareImage.src = canvas.toDataURL("image/png", 1);
squareImage.onload = () =>
const sx = startedHorizontalEndedVertical ? ((canvasSize - imageHeight) / 2) : 0;
const sy = startedHorizontalEndedVertical ? 0 : ((canvasSize - imageWidth) / 2);
const sw = imageHeight;
const sh = imageWidth;
const data = cropPlusExport(squareImage, sx, sy, sw, sh);
// Update DOM via angular binding...
img.src = data;
;
<img crossorigin src="https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg" id="img">
There is so much loss in one rotation that even this solution is not plausible with my constraints. It feels like there is a way to reduce the artifacts but maybe not. For instance, if I just rotate the image, quality is fine. But cropping after rotating is not? And, just cropping a non-rotated image is fine? Why is it so bad when the two are put together (especially since rotate and crop are done on two different canvases)?
– christo8989
Mar 7 at 14:19
Also, if the image is a square, quality is fine.
– christo8989
Mar 7 at 14:23
? You have some css applied on your image? Quality is fine for me in the snippet.
– Kaiido
Mar 7 at 14:30
Well, I still have to save the image, so if they rotate and then refresh, the image would get pretty bad after about 4 rotations. Not exactly what I want to put in production code.
– christo8989
Mar 7 at 14:37
1
Then save the original image and the current transform. Without knowing what you are doing we can't help you. And there is just nothing you can do to preserve original quality of an image when you rotate it.
– Kaiido
Mar 7 at 23:25
add a comment |
Canvas drawing is lossy, and rotating an image induce hard modifications of the pixels. So indeed, if you start always from the last state, you'll end up adding more and more artifacts to your image.
Simply store the original image somewhere and always start from there instead of using the modified version.
// will fire in a loop
img.onload = e => elem.rotateAndSave(1);
const elem =
// store a copy of the original image
originalimage: img.cloneNode(),
angle: 0,
rotateAndSave(degrees)
// always use the stored original image
const image = this.originalimage;
// keep track of current transform
this.angle += degrees;
const imageWidth = image.naturalWidth;
const imageHeight = image.naturalHeight;
const startedHorizontalEndedVertical = imageWidth > imageHeight;
const canvasSize = startedHorizontalEndedVertical ? imageWidth : imageHeight;
const canvas = document.createElement("canvas");
canvas.width = canvasSize;
canvas.height = canvasSize;
const ctx = canvas.getContext("2d");
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// center and rotate canvas
const translateCanvas = canvasSize / 2;
ctx.translate(translateCanvas, translateCanvas);
ctx.rotate(this.angle * Math.PI / 180);
// draw from center
const translateImageX = startedHorizontalEndedVertical ? -translateCanvas : (-imageWidth / 2);
const translateImageY = startedHorizontalEndedVertical ? (-imageHeight / 2) : -translateCanvas;
ctx.drawImage(image, translateImageX, translateImageY);
// I got 'cropPlusExport' from another stackoverflow question.
function cropPlusExport(img, cropX, cropY, cropWidth, cropHeight)
// create a temporary canvas sized to the cropped size
const canvas1 = document.createElement('canvas');
canvas1.width = cropWidth;
canvas1.height = cropHeight;
const ctx1 = canvas1.getContext('2d');
ctx1.setTransform(1, 0, 0, 1, 0, 0);
ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
// use the extended from of drawImage to draw the
// cropped area to the temp canvas
ctx1.drawImage(img, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
return canvas1.toDataURL("image/png", 1);
// Start Cropping
let squareImage = new Image();
squareImage.src = canvas.toDataURL("image/png", 1);
squareImage.onload = () =>
const sx = startedHorizontalEndedVertical ? ((canvasSize - imageHeight) / 2) : 0;
const sy = startedHorizontalEndedVertical ? 0 : ((canvasSize - imageWidth) / 2);
const sw = imageHeight;
const sh = imageWidth;
const data = cropPlusExport(squareImage, sx, sy, sw, sh);
// Update DOM via angular binding...
img.src = data;
;
<img crossorigin src="https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg" id="img">
There is so much loss in one rotation that even this solution is not plausible with my constraints. It feels like there is a way to reduce the artifacts but maybe not. For instance, if I just rotate the image, quality is fine. But cropping after rotating is not? And, just cropping a non-rotated image is fine? Why is it so bad when the two are put together (especially since rotate and crop are done on two different canvases)?
– christo8989
Mar 7 at 14:19
Also, if the image is a square, quality is fine.
– christo8989
Mar 7 at 14:23
? You have some css applied on your image? Quality is fine for me in the snippet.
– Kaiido
Mar 7 at 14:30
Well, I still have to save the image, so if they rotate and then refresh, the image would get pretty bad after about 4 rotations. Not exactly what I want to put in production code.
– christo8989
Mar 7 at 14:37
1
Then save the original image and the current transform. Without knowing what you are doing we can't help you. And there is just nothing you can do to preserve original quality of an image when you rotate it.
– Kaiido
Mar 7 at 23:25
add a comment |
Canvas drawing is lossy, and rotating an image induce hard modifications of the pixels. So indeed, if you start always from the last state, you'll end up adding more and more artifacts to your image.
Simply store the original image somewhere and always start from there instead of using the modified version.
// will fire in a loop
img.onload = e => elem.rotateAndSave(1);
const elem =
// store a copy of the original image
originalimage: img.cloneNode(),
angle: 0,
rotateAndSave(degrees)
// always use the stored original image
const image = this.originalimage;
// keep track of current transform
this.angle += degrees;
const imageWidth = image.naturalWidth;
const imageHeight = image.naturalHeight;
const startedHorizontalEndedVertical = imageWidth > imageHeight;
const canvasSize = startedHorizontalEndedVertical ? imageWidth : imageHeight;
const canvas = document.createElement("canvas");
canvas.width = canvasSize;
canvas.height = canvasSize;
const ctx = canvas.getContext("2d");
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// center and rotate canvas
const translateCanvas = canvasSize / 2;
ctx.translate(translateCanvas, translateCanvas);
ctx.rotate(this.angle * Math.PI / 180);
// draw from center
const translateImageX = startedHorizontalEndedVertical ? -translateCanvas : (-imageWidth / 2);
const translateImageY = startedHorizontalEndedVertical ? (-imageHeight / 2) : -translateCanvas;
ctx.drawImage(image, translateImageX, translateImageY);
// I got 'cropPlusExport' from another stackoverflow question.
function cropPlusExport(img, cropX, cropY, cropWidth, cropHeight)
// create a temporary canvas sized to the cropped size
const canvas1 = document.createElement('canvas');
canvas1.width = cropWidth;
canvas1.height = cropHeight;
const ctx1 = canvas1.getContext('2d');
ctx1.setTransform(1, 0, 0, 1, 0, 0);
ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
// use the extended from of drawImage to draw the
// cropped area to the temp canvas
ctx1.drawImage(img, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
return canvas1.toDataURL("image/png", 1);
// Start Cropping
let squareImage = new Image();
squareImage.src = canvas.toDataURL("image/png", 1);
squareImage.onload = () =>
const sx = startedHorizontalEndedVertical ? ((canvasSize - imageHeight) / 2) : 0;
const sy = startedHorizontalEndedVertical ? 0 : ((canvasSize - imageWidth) / 2);
const sw = imageHeight;
const sh = imageWidth;
const data = cropPlusExport(squareImage, sx, sy, sw, sh);
// Update DOM via angular binding...
img.src = data;
;
<img crossorigin src="https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg" id="img">
Canvas drawing is lossy, and rotating an image induce hard modifications of the pixels. So indeed, if you start always from the last state, you'll end up adding more and more artifacts to your image.
Simply store the original image somewhere and always start from there instead of using the modified version.
// will fire in a loop
img.onload = e => elem.rotateAndSave(1);
const elem =
// store a copy of the original image
originalimage: img.cloneNode(),
angle: 0,
rotateAndSave(degrees)
// always use the stored original image
const image = this.originalimage;
// keep track of current transform
this.angle += degrees;
const imageWidth = image.naturalWidth;
const imageHeight = image.naturalHeight;
const startedHorizontalEndedVertical = imageWidth > imageHeight;
const canvasSize = startedHorizontalEndedVertical ? imageWidth : imageHeight;
const canvas = document.createElement("canvas");
canvas.width = canvasSize;
canvas.height = canvasSize;
const ctx = canvas.getContext("2d");
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// center and rotate canvas
const translateCanvas = canvasSize / 2;
ctx.translate(translateCanvas, translateCanvas);
ctx.rotate(this.angle * Math.PI / 180);
// draw from center
const translateImageX = startedHorizontalEndedVertical ? -translateCanvas : (-imageWidth / 2);
const translateImageY = startedHorizontalEndedVertical ? (-imageHeight / 2) : -translateCanvas;
ctx.drawImage(image, translateImageX, translateImageY);
// I got 'cropPlusExport' from another stackoverflow question.
function cropPlusExport(img, cropX, cropY, cropWidth, cropHeight)
// create a temporary canvas sized to the cropped size
const canvas1 = document.createElement('canvas');
canvas1.width = cropWidth;
canvas1.height = cropHeight;
const ctx1 = canvas1.getContext('2d');
ctx1.setTransform(1, 0, 0, 1, 0, 0);
ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
// use the extended from of drawImage to draw the
// cropped area to the temp canvas
ctx1.drawImage(img, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
return canvas1.toDataURL("image/png", 1);
// Start Cropping
let squareImage = new Image();
squareImage.src = canvas.toDataURL("image/png", 1);
squareImage.onload = () =>
const sx = startedHorizontalEndedVertical ? ((canvasSize - imageHeight) / 2) : 0;
const sy = startedHorizontalEndedVertical ? 0 : ((canvasSize - imageWidth) / 2);
const sw = imageHeight;
const sh = imageWidth;
const data = cropPlusExport(squareImage, sx, sy, sw, sh);
// Update DOM via angular binding...
img.src = data;
;
<img crossorigin src="https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg" id="img">
// will fire in a loop
img.onload = e => elem.rotateAndSave(1);
const elem =
// store a copy of the original image
originalimage: img.cloneNode(),
angle: 0,
rotateAndSave(degrees)
// always use the stored original image
const image = this.originalimage;
// keep track of current transform
this.angle += degrees;
const imageWidth = image.naturalWidth;
const imageHeight = image.naturalHeight;
const startedHorizontalEndedVertical = imageWidth > imageHeight;
const canvasSize = startedHorizontalEndedVertical ? imageWidth : imageHeight;
const canvas = document.createElement("canvas");
canvas.width = canvasSize;
canvas.height = canvasSize;
const ctx = canvas.getContext("2d");
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// center and rotate canvas
const translateCanvas = canvasSize / 2;
ctx.translate(translateCanvas, translateCanvas);
ctx.rotate(this.angle * Math.PI / 180);
// draw from center
const translateImageX = startedHorizontalEndedVertical ? -translateCanvas : (-imageWidth / 2);
const translateImageY = startedHorizontalEndedVertical ? (-imageHeight / 2) : -translateCanvas;
ctx.drawImage(image, translateImageX, translateImageY);
// I got 'cropPlusExport' from another stackoverflow question.
function cropPlusExport(img, cropX, cropY, cropWidth, cropHeight)
// create a temporary canvas sized to the cropped size
const canvas1 = document.createElement('canvas');
canvas1.width = cropWidth;
canvas1.height = cropHeight;
const ctx1 = canvas1.getContext('2d');
ctx1.setTransform(1, 0, 0, 1, 0, 0);
ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
// use the extended from of drawImage to draw the
// cropped area to the temp canvas
ctx1.drawImage(img, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
return canvas1.toDataURL("image/png", 1);
// Start Cropping
let squareImage = new Image();
squareImage.src = canvas.toDataURL("image/png", 1);
squareImage.onload = () =>
const sx = startedHorizontalEndedVertical ? ((canvasSize - imageHeight) / 2) : 0;
const sy = startedHorizontalEndedVertical ? 0 : ((canvasSize - imageWidth) / 2);
const sw = imageHeight;
const sh = imageWidth;
const data = cropPlusExport(squareImage, sx, sy, sw, sh);
// Update DOM via angular binding...
img.src = data;
;
<img crossorigin src="https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg" id="img">
// will fire in a loop
img.onload = e => elem.rotateAndSave(1);
const elem =
// store a copy of the original image
originalimage: img.cloneNode(),
angle: 0,
rotateAndSave(degrees)
// always use the stored original image
const image = this.originalimage;
// keep track of current transform
this.angle += degrees;
const imageWidth = image.naturalWidth;
const imageHeight = image.naturalHeight;
const startedHorizontalEndedVertical = imageWidth > imageHeight;
const canvasSize = startedHorizontalEndedVertical ? imageWidth : imageHeight;
const canvas = document.createElement("canvas");
canvas.width = canvasSize;
canvas.height = canvasSize;
const ctx = canvas.getContext("2d");
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, canvas.width, canvas.height);
// center and rotate canvas
const translateCanvas = canvasSize / 2;
ctx.translate(translateCanvas, translateCanvas);
ctx.rotate(this.angle * Math.PI / 180);
// draw from center
const translateImageX = startedHorizontalEndedVertical ? -translateCanvas : (-imageWidth / 2);
const translateImageY = startedHorizontalEndedVertical ? (-imageHeight / 2) : -translateCanvas;
ctx.drawImage(image, translateImageX, translateImageY);
// I got 'cropPlusExport' from another stackoverflow question.
function cropPlusExport(img, cropX, cropY, cropWidth, cropHeight)
// create a temporary canvas sized to the cropped size
const canvas1 = document.createElement('canvas');
canvas1.width = cropWidth;
canvas1.height = cropHeight;
const ctx1 = canvas1.getContext('2d');
ctx1.setTransform(1, 0, 0, 1, 0, 0);
ctx1.clearRect(0, 0, canvas1.width, canvas1.height);
// use the extended from of drawImage to draw the
// cropped area to the temp canvas
ctx1.drawImage(img, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
return canvas1.toDataURL("image/png", 1);
// Start Cropping
let squareImage = new Image();
squareImage.src = canvas.toDataURL("image/png", 1);
squareImage.onload = () =>
const sx = startedHorizontalEndedVertical ? ((canvasSize - imageHeight) / 2) : 0;
const sy = startedHorizontalEndedVertical ? 0 : ((canvasSize - imageWidth) / 2);
const sw = imageHeight;
const sh = imageWidth;
const data = cropPlusExport(squareImage, sx, sy, sw, sh);
// Update DOM via angular binding...
img.src = data;
;
<img crossorigin src="https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg" id="img">
answered Mar 7 at 4:13
KaiidoKaiido
44.4k467107
44.4k467107
There is so much loss in one rotation that even this solution is not plausible with my constraints. It feels like there is a way to reduce the artifacts but maybe not. For instance, if I just rotate the image, quality is fine. But cropping after rotating is not? And, just cropping a non-rotated image is fine? Why is it so bad when the two are put together (especially since rotate and crop are done on two different canvases)?
– christo8989
Mar 7 at 14:19
Also, if the image is a square, quality is fine.
– christo8989
Mar 7 at 14:23
? You have some css applied on your image? Quality is fine for me in the snippet.
– Kaiido
Mar 7 at 14:30
Well, I still have to save the image, so if they rotate and then refresh, the image would get pretty bad after about 4 rotations. Not exactly what I want to put in production code.
– christo8989
Mar 7 at 14:37
1
Then save the original image and the current transform. Without knowing what you are doing we can't help you. And there is just nothing you can do to preserve original quality of an image when you rotate it.
– Kaiido
Mar 7 at 23:25
add a comment |
There is so much loss in one rotation that even this solution is not plausible with my constraints. It feels like there is a way to reduce the artifacts but maybe not. For instance, if I just rotate the image, quality is fine. But cropping after rotating is not? And, just cropping a non-rotated image is fine? Why is it so bad when the two are put together (especially since rotate and crop are done on two different canvases)?
– christo8989
Mar 7 at 14:19
Also, if the image is a square, quality is fine.
– christo8989
Mar 7 at 14:23
? You have some css applied on your image? Quality is fine for me in the snippet.
– Kaiido
Mar 7 at 14:30
Well, I still have to save the image, so if they rotate and then refresh, the image would get pretty bad after about 4 rotations. Not exactly what I want to put in production code.
– christo8989
Mar 7 at 14:37
1
Then save the original image and the current transform. Without knowing what you are doing we can't help you. And there is just nothing you can do to preserve original quality of an image when you rotate it.
– Kaiido
Mar 7 at 23:25
There is so much loss in one rotation that even this solution is not plausible with my constraints. It feels like there is a way to reduce the artifacts but maybe not. For instance, if I just rotate the image, quality is fine. But cropping after rotating is not? And, just cropping a non-rotated image is fine? Why is it so bad when the two are put together (especially since rotate and crop are done on two different canvases)?
– christo8989
Mar 7 at 14:19
There is so much loss in one rotation that even this solution is not plausible with my constraints. It feels like there is a way to reduce the artifacts but maybe not. For instance, if I just rotate the image, quality is fine. But cropping after rotating is not? And, just cropping a non-rotated image is fine? Why is it so bad when the two are put together (especially since rotate and crop are done on two different canvases)?
– christo8989
Mar 7 at 14:19
Also, if the image is a square, quality is fine.
– christo8989
Mar 7 at 14:23
Also, if the image is a square, quality is fine.
– christo8989
Mar 7 at 14:23
? You have some css applied on your image? Quality is fine for me in the snippet.
– Kaiido
Mar 7 at 14:30
? You have some css applied on your image? Quality is fine for me in the snippet.
– Kaiido
Mar 7 at 14:30
Well, I still have to save the image, so if they rotate and then refresh, the image would get pretty bad after about 4 rotations. Not exactly what I want to put in production code.
– christo8989
Mar 7 at 14:37
Well, I still have to save the image, so if they rotate and then refresh, the image would get pretty bad after about 4 rotations. Not exactly what I want to put in production code.
– christo8989
Mar 7 at 14:37
1
1
Then save the original image and the current transform. Without knowing what you are doing we can't help you. And there is just nothing you can do to preserve original quality of an image when you rotate it.
– Kaiido
Mar 7 at 23:25
Then save the original image and the current transform. Without knowing what you are doing we can't help you. And there is just nothing you can do to preserve original quality of an image when you rotate it.
– Kaiido
Mar 7 at 23:25
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55035406%2fcanvas-rotate-todataurl-and-then-crop-is-ruining-image-quality%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown