Image Processing 2. Simple transformation

2 min read June 09, 2025 #image processing #javascript

In this article, we'll implement flipping and 90°/270° rotations for images. But before doing that, let's explore image pixel data and how to access it in JavaScript. logo2.jpg

Images#

This is quite straightforward: an image is a collection of colored dots called pixels. An image has width, height, and other parameters, which we'll discuss later.
You can think of an image as a 2D array. To get the bottom-right pixel, simply subtract 1 from both width and height (remember arrays are indexed from zero and we typically access rows first and then the columns):

pixel = img[height - 1][width - 1].

kenny_2d.png

Although this representation seems convenient, in practice, an image is often represented as a 1D array of length width*height:

kenny_1d.png Look what they did to Kenny

Here, the bottom-right pixel is obtained as follows: pixel = img[img.length - 1]. To get a pixel at coordinate (x, y), you need to move by y rows and x positions: (y * width + x).

kenny_index.png

Getting Pixels from <img>#

To get a pixel array from an <img> tag, you need to:

  1. Obtain the img tag object
  2. Get or create a canvas
  3. Set the canvas size to match the image dimensions
  4. Render the image
// <img id="image1" src="..." />
// img = getImageData('image1');
function getImageData(el) {
  const canvas = document.createElement('canvas');
  const context = canvas.getContext('2d');
  const img = document.getElementById(el);
  canvas.width = img.width;
  canvas.height = img.height;
  context.drawImage(img, 0, 0);
  return context.getImageData(0, 0, img.width, img.height);
}

However, due to security restrictions, you can only render images in the canvas that support CORS, or encode the image in base64. I'll use the latter approach in my examples.

Flipping and 90°/270° Rotation#

We don't need to split pixels into color components yet, because flipping and rotating involve manipulating pixel positions. Recall computer science lessons about matrix operations and transposition, that's exactly what should be done.

Horizontal Flip: For each position x, draw the pixel from position (width - x - 1):

for (let y = 0; y < height; y++) {
  for (let x = 0; x < width; x++) {
    dst[y * width + x] = src[y * width + (width - x - 1)];
  }
}

Vertical Flip: For each position y, draw the pixel from position (height - y - 1):

for (let y = 0; y < height; y++) {
  for (let x = 0; x < width; x++) {
    dst[y * width + x] = src[(height - y - 1)  * width + x];
  }
}

90° Rotation: Here, width becomes height and height becomes width, so we need to create a new canvas with adjusted dimensions:

let newWidth = height;
let newHeight = width;
for (let y = 0; y < newHeight; y++) {
  for (let x = 0; x < newWidth; x++) {
    dst[y * newWidth + x] = src[(height - x - 1) * width + y];
  }
}

270° Rotation:

let newWidth = height;
let newHeight = width;
for (let y = 0; y < newHeight; y++) {
  for (let x = 0; x < newWidth; x++) {
    dst[y * newWidth + x] = src[x * width + y];
  }
}

I'll leave implementing simultaneous horizontal + vertical flips as a homework exercise. The solution will be in the next article.