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













0















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.










share|improve this question


























    0















    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.










    share|improve this question
























      0












      0








      0








      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.










      share|improve this question














      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






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Mar 7 at 3:03









      christo8989christo8989

      2,51221830




      2,51221830






















          1 Answer
          1






          active

          oldest

          votes


















          1














          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">








          share|improve this answer























          • 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










          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
          );



          );













          draft saved

          draft discarded


















          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









          1














          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">








          share|improve this answer























          • 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















          1














          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">








          share|improve this answer























          • 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













          1












          1








          1







          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">








          share|improve this answer













          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">






          share|improve this answer












          share|improve this answer



          share|improve this answer










          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

















          • 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



















          draft saved

          draft discarded
















































          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.




          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          Save data to MySQL database using ExtJS and PHP [closed]2019 Community Moderator ElectionHow can I prevent SQL injection in PHP?Which MySQL data type to use for storing boolean valuesPHP: Delete an element from an arrayHow do I connect to a MySQL Database in Python?Should I use the datetime or timestamp data type in MySQL?How to get a list of MySQL user accountsHow Do You Parse and Process HTML/XML in PHP?Reference — What does this symbol mean in PHP?How does PHP 'foreach' actually work?Why shouldn't I use mysql_* functions in PHP?

          Compiling GNU Global with universal-ctags support Announcing the arrival of Valued Associate #679: Cesar Manara Planned maintenance scheduled April 23, 2019 at 23:30 UTC (7:30pm US/Eastern) Data science time! April 2019 and salary with experience The Ask Question Wizard is Live!Tags for Emacs: Relationship between etags, ebrowse, cscope, GNU Global and exuberant ctagsVim and Ctags tips and trickscscope or ctags why choose one over the other?scons and ctagsctags cannot open option file “.ctags”Adding tag scopes in universal-ctagsShould I use Universal-ctags?Universal ctags on WindowsHow do I install GNU Global with universal ctags support using Homebrew?Universal ctags with emacsHow to highlight ctags generated by Universal Ctags in Vim?

          Add ONERROR event to image from jsp tldHow to add an image to a JPanel?Saving image from PHP URLHTML img scalingCheck if an image is loaded (no errors) with jQueryHow to force an <img> to take up width, even if the image is not loadedHow do I populate hidden form field with a value set in Spring ControllerStyling Raw elements Generated from JSP tagds with Jquery MobileLimit resizing of images with explicitly set width and height attributeserror TLD use in a jsp fileJsp tld files cannot be resolved