0

Via pure JS, I'm attempting to reorient a JPEG (to 1) and resize it to certain resolution benchmarks. I'm unable to get the logic right; images produced have the canvas in the right position/dimensions, but the image's aspect ratio messes up and doesn't fit the canvas.

For instance, here's the result of running my code on an image where the orientation had initially been set to 8:

enter image description here

My code correctly oriented it to 1, but couldn't fit the image to the canvas' dimensions.

How can I fix my code? Here's what I have:

var max_img_width = 400;
var wranges = [max_img_width, Math.round(0.8*max_img_width), Math.round(0.6*max_img_width),Math.round(0.4*max_img_width),Math.round(0.2*max_img_width)];

function reorient_and_resize(source_img, img_width, img_height, mime_type, orientation){

  if (4 < orientation && orientation < 9) {
    // switch around height and width
    var temp = null;
    temp = img_width;
    img_width = img_height;
    img_height = temp;
  }

  var new_width = null;
    switch (true) {
      case img_width < wranges[4]: new_width = wranges[4];break;
      case img_width < wranges[3]: new_width = wranges[4];break;
      case img_width < wranges[2]: new_width = wranges[3];break;
      case img_width < wranges[1]: new_width = wranges[2];break;
      case img_width < wranges[0]: new_width = wranges[1];break;
      default: new_width = wranges[0];break;
    }
  var wpercent = (new_width/img_width);
  var new_height = Math.round(img_height*wpercent);

  var canvas = document.createElement('canvas');
  canvas.width = new_width;
  canvas.height = new_height;
  var ctx = canvas.getContext("2d");

  // transform context before drawing image
  switch (orientation) {
    case 2: ctx.transform(-1, 0, 0, 1, new_width, 0); break;
    case 3: ctx.transform(-1, 0, 0, -1, new_width, new_height); break;
    case 4: ctx.transform(1, 0, 0, -1, 0, new_height); break;
    case 5: ctx.transform(0, 1, 1, 0, 0, 0); break;
    case 6: ctx.transform(0, 1, -1, 0, new_height, 0); break;
    case 7: ctx.transform(0, -1, -1, 0, new_height, new_width); break;
    case 8: ctx.transform(0, -1, 1, 0, 0, new_width); break;
    default: break;
  }

  ctx.drawImage(source_img, 0, 0, new_width, new_height);
  return dataURItoBlob(canvas.toDataURL(mime_type),mime_type);
}

I suspect my canvas transformations aren't working. Please advise.

Note: Let's stick to pure JS for the scope of this question, without recourse to any 3rd party libraries.


The image originally looked as follows. At this point, orientation is 1. I purposely set its orientation to 8 before testing my algorithm (and thereafter found erroneous results):

enter image description here

Hassan Baig
  • 15,055
  • 27
  • 102
  • 205

2 Answers2

0

I can't see where the error in your code is, but it's definitely possible:

var img = new Image();

img.onload = function() {
  var w = this.width;
  var h = this.height;

  var c = document.getElementById('canvas');
  c.width = h;
  c.height = w;
  var ctx = c.getContext('2d');

  ctx.transform(0, -1, 1, 0, 0, w);
  ctx.drawImage(this, 0, 0, w, h);
}

img.src = "https://i.stack.imgur.com/6GmiO.jpg";
canvas {
  border: 2px solid red;
}
<canvas id="canvas" />
Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • 1st Question: In this fiddle (https://jsfiddle.net/alnitak/La3c5bqL/) I'm tweaking line 14 `ctx.drawImage(this, w*0.5, 0, w*0.5, h*0.5);`. I've noticed that if I tweak the second parameter (currently `w*0.5`), the image translates **vertically**. E.g. try `w*0.4` to see what I mean. Why does it translate vertically? Doesn't that parameter set the `x` coordinate of where to place the image? In my mind, that should have been horizontal displacement. – Hassan Baig Feb 07 '18 at 14:39
  • 1
    @HassanBaig that's because the coordinates you're given are also subject to the transformation. You've rotated 90', so a change in X coordinate is now vertical. – Alnitak Feb 07 '18 at 15:04
  • 1
    @HassanBaig Note that with the given transform, the canvas's origin is now at the _bottom left_ of the visible area, with the X axis going vertically upwards (towards top-left) and the Y axis going to the left (towards bottom right). The `w` specified there is the _original_ image width, which in effect becomes its height once rotated. – Alnitak Feb 07 '18 at 15:33
0

Alnitak's answer pointed me in the right direction. I ended up redoing my resize and reorient function as follows:

function reorient_and_resize(source_img, img_width, img_height, mime_type, orientation){

  // flip width and height (when needed)
  switch (orientation) {
    case 2:
    case 3:
    case 4:
      break;
    case 5:
    case 6:
    case 7:
    case 8:
      var temp_width = null;
      temp_width = img_width;
      img_width = img_height;
      img_height = temp_width;
      break;
    default:
      break;
  }

  var new_width = null;
  switch (true) {
    case img_width < wranges[4]: new_width = wranges[4]; break;
    case img_width < wranges[3]: new_width = wranges[4]; break;
    case img_width < wranges[2]: new_width = wranges[3]; break;
    case img_width < wranges[1]: new_width = wranges[2]; break;
    case img_width < wranges[0]: new_width = wranges[1]; break;
    default: new_width = wranges[0]; break;
  }

  var wpercent = (new_width/img_width);
  var new_height = Math.round(img_height*wpercent);

  var canvas = document.createElement('canvas');
  canvas.width = new_width;
  canvas.height = new_height;

  var ctx = canvas.getContext('2d');
  // see for guidance: https://github.com/blueimp/JavaScript-Load-Image/blob/master/js/load-image-orientation.js
   switch (orientation) {
    case 2: 
      ctx.translate(new_width, 0);
      ctx.scale(-1, 1);
      break;//supported
    case 3:
      // rotate 180 degrees left
      ctx.translate(new_width, new_height);
      ctx.rotate(-Math.PI);
      break;//supported
    case 4: 
      // vertical flip
      ctx.translate(0, new_height);
      ctx.scale(1, -1);
      break;//supported
    case 5: 
      // vertical flip + 90 rotate right
      ctx.rotate(0.5 * Math.PI);
      ctx.scale(new_height/new_width,-new_width/new_height);
      break;//supported
    case 6: 
      // rotate 90 degrees right
      ctx.rotate(0.5 * Math.PI);
      ctx.translate(0, -new_width);
      ctx.scale(new_height/new_width,new_width/new_height);
      break;//supported
    case 7:
      // horizontal flip + 90 rotate right
      ctx.rotate(0.5 * Math.PI);
      ctx.translate(new_height, -new_width);
      ctx.scale(-new_height/new_width,new_width/new_height);
      break;
    case 8: 
      // rotate 90 degrees left
      ctx.translate(0, new_height);
      ctx.scale(new_width/new_height,new_height/new_width);
      ctx.rotate(-0.5 * Math.PI);
      break;
    default: 
      break;
  }

  ctx.drawImage(source_img, 0, 0, new_width, new_height);
  return dataURItoBlob(canvas.toDataURL(mime_type),mime_type);
}


// converting image data uri to a blob object
function dataURItoBlob(dataURI,mime_type) {
  var byteString = atob(dataURI.split(',')[1]);//supported
  var ab = new ArrayBuffer(byteString.length);//supported
  var ia = new Uint8Array(ab);//supported
  for (var i = 0; i < byteString.length; i++) { ia[i] = byteString.charCodeAt(i); }//supported
  return new Blob([ab], { type: mime_type });//supported
}

The big difference lies in how I'm treating the portion that reorients the image (after the line var ctx = canvas.getContext('2d');). The transformations were previously incorrect - but not any more.

Hassan Baig
  • 15,055
  • 27
  • 102
  • 205