Scaling images

Hi all, I know there are simpler ways to do this in Java but I’m writing a method to scale an image represented by pixel array. This is what I’ve managed to knock up so far with guess work:


public void scale(double factor) {
		int newWidth = (int) Math.abs(width * factor);
		int newHeight = (int) Math.abs(height * factor);
		int[] temp = new int[newWidth * newHeight];
		for(int y = 0; y < height; y++) {
			for(int x = 0; x < width; x++) {
				int newX = (int) (x * factor);
				int newY = (int) (y * factor);
				if(newX < 0) newX += newWidth;
				if(newY < 0) newY += newHeight;
				if(newX < 0 || newY < 0 || newX >= newWidth || newY >= newHeight) continue;
				temp[(int) (newY * newWidth + newX)] = pixels[y * width + x];
			}
		}
		width = newWidth;
		height = newHeight;
		pixels = temp.clone();
	}

This is inside a bitmap class which has its own pixel array. This method creates a new pixel array of the correct size, then loops through every pixel and scales each pixel according to the scale factor. It works fine scaling down, but when scaling up it creates a grid-like effect where pixel aren’t being filled as it doesn’t scale up. I tried this the other way round by looping through each pixel in the new array and mapping pixels from the old array, which works fine when mapping up, but doesn’t work for scaling down.
Does anyone know how I could modify this algorithm to cater for scaling in both directions? Thanks

MODIFIED:


public void TESTscale(double factor) {
		int newWidth = (int) Math.abs(width * factor);
		int newHeight = (int) Math.abs(height * factor);
		int[] temp = new int[newWidth * newHeight];
		double xRatio = width / (double) newWidth;
		double yRatio = height / (double) newHeight;
		double newX, newY;
		for(int y = 0; y < newHeight; y++) {
			for(int x = 0; x < newWidth; x++) {
				newX = Math.floor(x * xRatio);
				newY = Math.floor(y * yRatio);
				temp[y * newWidth + x] = pixels[(int) (newY * width + newX)];
			}
		}
		width = newWidth;
		height = newHeight;
		pixels = temp.clone();
	}

Paul

Nearest-neighbour scaling:
http://tech-algorithm.com/articles/nearest-neighbor-image-scaling/

Of course the best thing would be to use BufferedImage, which leads to far fewer lines of code, potentially hardware-accelerated scaling, and much greater flexibility (i.e. different image types, different interpolation algorithms, etc).

Hi dave, thanks for the quick reply! I tried the first solution on that web page to check that it would work, but it still appears to have a grid effect. :frowning: I’ve updated my original post with the updated code. Any ideas what I’ve done wrong?

Ah, apologies dave. I was still calling the unmodified method. :stuck_out_tongue: Works great. Although it doesn’t allow me to scale the image by negative numbers, which I was planning to use for mirroring an image. Any idea how I would add that functionality?

Ah, got it working. :slight_smile: For anyone who’s interested here’s what I have:


public void TESTscale(double factor) {
		int newWidth = (int) Math.abs(width * factor);
		int newHeight = (int) Math.abs(height * factor);
		int[] temp = new int[newWidth * newHeight];
		double xRatio = width / (double) newWidth;
		double yRatio = height / (double) newHeight;
		double newX, newY;
		for(int y = 0; y < newHeight; y++) {
			for(int x = 0; x < newWidth; x++) {
				newX = Math.floor(x / factor);
				newY = Math.floor(y / factor);
				if(newX < 0) newX += width;
				if(newY < 0) newY += height;
				if(newX<0 || newY<0 || newX >= width || newY >= height) continue;
				temp[y * newWidth + x] = pixels[(int) (newY * width + newX)];
			}
		}
		width = newWidth;
		height = newHeight;
		pixels = temp.clone();
	}

Also if anyone can think of a more elegant way to write it, I’d appreciate anymore help. :stuck_out_tongue:

Paul

I have this:

	public static Image scale(Image i, int scale) {
		Image img = new Image(i.width * scale, i.height * scale);
		
		for (int y = 0; y < img.height; y++) {	
			for (int x = 0; x < img.width; x++) {
				int src = i.pixels[(x / scale) + (y / scale) * i.width];
				if (src < 0) img.pixels[x + y * img.width] = src;
			}
		}
		
		return img;
	}

With “Image” being your usual pixel-array.
But i’m pretty sure it doesn’t work when scaling images down (who needs it anyways?)