To process images such as photos, we need to be able to load photo files into an image object, read and set pixels values of the image, and save the image back into a photo file.
We use the class java.awt.image.BufferedImage to store image data. You can create such an object by saying:
import java.awt.image.BufferedImage val img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)
Or you can load an image from a file using:
import javax.imageio.ImageIO val photo1 = ImageIO.read(new File("photo.jpg"))
The result of this call is a again a BufferedImage object. You can find the width and height of this image using the following methods:
printf("Photo size is %d x %d\n, photo1.getWidth, photo1.getHeight)
You can save an image object into a file using:
ImageIO.write(photo2, "jpg", new File("test.jpg"))or, if you prefer PNG format:
ImageIO.write(photo2, "png", new File("test.png"))
The pixels of a java.awt.image.BufferedImage object can be read and changed using the getRGB and setRGB methods, see below.
The following small script reads a file photo.jpg, get its dimensions, creates a new empty image of the same size, copy the pixels from the old image img to the new image out (mirroring horizontally), draws a red line diagonally across the photo, and finally saves the new image in a file test.jpg (image.scala):
import java.io.File import javax.imageio.ImageIO import java.awt.image.BufferedImage def phototest(img: BufferedImage): BufferedImage = { // obtain width and height of image val w = img.getWidth val h = img.getHeight // create new image of the same size val out = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB) // copy pixels (mirror horizontally) for (x <- 0 until w) for (y <- 0 until h) out.setRGB(x, y, img.getRGB(w - x - 1, y) & 0xffffff) // draw red diagonal line for (x <- 0 until (h min w)) out.setRGB(x, x, 0xff0000) out } def test() { // read original image, and obtain width and height val photo1 = ImageIO.read(new File("photo.jpg")) val photo2 = phototest(photo1) // save image to file "test.jpg" ImageIO.write(photo2, "jpg", new File("test.jpg")) } test()
The method getRGB(x: Int, y: Int): Int returns the color of the pixel at position (x, y), the method setRGB(x: Int, y: Int, color: Int) sets the color of this pixel.
Colors are represented as Int objects. The three components red, green, and blue are "packed" together into one integer. Each component has 8 bits, and therefore can have a value between 0 and 255. The packed color is a 32-bit integer, whose bits look like this:
tttt tttt rrrr rrrr gggg gggg bbbb bbbbThe top 8 bits are either zero, or represent the "transparency" of the pixel. We will not use these transparency bits. The next 8 bits represent red, the next 8 bits represent green, and the last 8 bits represent blue. This is why this representation is called "RGB".
Given red, green, and blue components with values in the range 0 to 255, we can pack them together like this:
val color = (red * 65536) + (green * 256) + blueGiven a packed integer color, we can extract the three components as follows:
val red = (color & 0xff0000) / 65536 val green = (color & 0xff00) / 256 val blue = (color & 0xff)(The & operator here is the bitwise-and operator. It makes sure that only the bits we are interested in are used.)