Section 5.2. Copying and Transforming Pictures


[Page 142 (continued)]

5.2. Copying and Transforming Pictures

We can even copy from one picture to another. We're going to end up keeping track of a source picture that we take pixels from and a target picture that we're going to set pixels in. Actually, we don't copy the pixelswe simply make the pixels in the target the same color as the pixels in the source. Copying pixels requires us to keep track of multiple index variables: The (x, y) position in the source and the (x, y) in the target.

What's exciting about copying pixels is that making some small changes in how we deal with the index variables leads to not only copying the image but transforming it. In this section, we're going to talk about copying, cropping, rotating, and scaling pictures.


[Page 143]

> FileChooser.getMediaPath("temple.jpg") "C:/intro-prog-java/mediasources/temple.jpg" > String fileName = FileChooser.getMediaPath("temple.jpg"); > Picture temple = new Picture(fileName);


Our target will be the paper-sized JPEG file in the mediasources directory, which is 7 x 9.5 inches, which will fit on a 9 x 11.5 inch lettersize piece of paper with one inch margins.

> String paperFile = FileChooser.getMediaPath("7inx95in.jpg"); > Picture paperPicture = new Picture(paperFile); > paperPicture.show(); > System.out.println(paperPicture.getWidth()); 504 > System.out.println(paperPicture.getHeight()); 684


5.2.1. Copying

To copy a picture we simply make sure that we increment sourceX and targetX variables (the source and target index variables for the X axis) together, and the sourceY and targetY variables together. We can initialize more than one variable in the initialization area of a for loop and change more than one variable in the change area.

Here's a program for copying a picture of Katie to the current picture.

Program 23. Copying a Picture to the Current Picture
(This item is displayed on pages 143 - 144 in the print version)

/**  * Method to copy the picture of Katie to the  * upper left corner of the current picture  */ public void copyKatie() {   String sourceFile =     FileChooser.getMediaPath("KatieFancy.jpg");   Picture sourcePicture = new Picture(sourceFile);   Pixel sourcePixel = null;   Pixel targetPixel = null;   // loop through the columns   for (int sourceX = 0, targetX=0;        sourceX < sourcePicture.getWidth();        sourceX++, targetX++)   {     // loop through the rows     for (int sourceY = 0, targetY =0;          sourceY < sourcePicture.getHeight(); 
[Page 144]
sourceY++, targetY++) { // set the target pixel color to the source pixel color sourcePixel = sourcePicture.getPixel(sourceX,sourceY); targetPixel = this.getPixel(targetX,targetY); targetPixel.setColor(sourcePixel.getColor()); } } }


To use this method create a picture from the file that has a blank paper-sized picture. The picture of Katie will be copied to the top-left corner of the blank picture Figure 5.7.

> String fileName = FileChooser.getMediaPath("7inx95in.jpg"); > Picture targetPicture = new Picture(fileName); > targetPicture.show(); > targetPicture.copyKatie(); > targetPicture.show();


Figure 5.7. Copying a picture to a canvas.


This method copies a picture of Katie to the canvas (blank picture) (Figure 5.7). Here's how it works:

  • The first two lines are just setting up the source (sourcePicture).

  • Then we have the declaration of variables to keep track of the target and source pixels.


  • [Page 145]
  • Next comes the loop for managing the x index variables, sourceX for the source picture and targetX for the target (current) picture. The for loop declares both variables and initializes them to 0. You can have more than one variable declared and initialized in the initialization area of a for loop, just separate them with commas. Next the continuation test checks if the sourceX is less than the width of the source picture. Finally, in the change area, we increment both the sourceX and targetX variables each time after the statements in the body of the loop have been executed. You can change more than one variable in the change area as long as you separate the changes with commas. The for loop for looping through the columns is:

    for (int sourceX = 0, targetX=0;      sourceX < sourcePicture.getWidth();      sourceX++, targetX++)

  • Inside the loop for the X variables is the loop for the Y variables. It has a very similar structure, since it's goal is to keep targetY and sourceY in synch in exactly the same way.

    for (int sourceY = 0, targetY=0;      sourceY < sourcePicture.getHeight();      sourceY++, targetY++)

    It's inside the Y loop that we actually get the color from the source pixel and set the corresponding pixel in the target (current picture) to the same color.

Of course, we don't have to copy from (0, 0) in the source to (0, 0) in the target. We can easily copy to another location in the target picture. All we have to do is to change where the target X and Y coordinates start. The rest stays exactly the same (Figure 5.8).

Figure 5.8. Copying a picture midway into a canvas.
(This item is displayed on page 146 in the print version)


Program 24. Copy Elsewhere into the Current Picture
(This item is displayed on pages 145 - 146 in the print version)

/**  * Method to copy the picture of Katie to (100,100) in the  * current picture  */ public void copyKatieMidway() {   String sourceFile =     FileChooser.getMediaPath("KatieFancy.jpg");   Picture sourcePicture = new Picture(sourceFile);   Pixel sourcePixel = null;   Pixel targetPixel = null;   // loop through the columns   for (int sourceX = 0, targetX=100;        sourceX < sourcePicture.getWidth();        sourceX++, targetX++)   { 
[Page 146]
// loop through the rows for (int sourceY = 0, targetY =100; sourceY < sourcePicture.getHeight(); sourceY++, targetY++) { // set the target pixel color to the source pixel color sourcePixel = sourcePicture.getPixel(sourceX,sourceY); targetPixel = this.getPixel(targetX,targetY); targetPixel.setColor(sourcePixel.getColor()); } } }


To try this method create the target picture from the blank paper-sized picture file, invoke the method on it, and show the result. The picture of Katie will be copied with the upper left corner at (100, 100).

> String fileName = FileChooser.getMediaPath("7inx95in.jpg"); > Picture targetPicture = new Picture(fileName); > targetPicture.copyKatieMidway(); > targetPicture.show();


Similarly, we don't have to copy a whole picture. Cropping is taking only part of a picture out of the whole picture. Digitally, that's just a matter of changing your start and end coordinates. To grab just Katie's face out of the picture, we only have to figure out the upper-left corner of a rectangle enclosing her face and use that as the starting values for sourceX and sourceY. We also need to determine the bottom-right corner of the rectangle enclosing her face and use that as the stopping x and y values (Figure 5.9). We can use the picture explorer to determine these values. The upper-left corner of the rectangle enclosing the face is at (70, 3), and the bottom-right corner is at (135, 80).


[Page 147]

Figure 5.9. Copying part of a picture onto a canvas.


Program 25. Cropping a Picture onto a Canvas
(This item is displayed on pages 147 - 148 in the print version)

/**  * Method to copy just Katie's face to the current picture  */ public void copyKatiesFace() {   String sourceFile =     FileChooser.getMediaPath("KatieFancy.jpg");   Picture sourcePicture = new Picture(sourceFile);   Pixel sourcePixel = null;   Pixel targetPixel = null;   // loop through the columns   for (int sourceX = 70, targetX = 100;        sourceX < 135; sourceX++, targetX++) 
[Page 148]
{ // loop through the rows for (int sourceY = 3, targetY = 100; sourceY < 80; sourceY++, targetY++) { // set the target pixel color to the source pixel color sourcePixel = sourcePicture.getPixel(sourceX,sourceY); targetPixel = this.getPixel(targetX,targetY); targetPixel.setColor(sourcePixel.getColor()); } } }


To try this method, create the target picture from the blank paper-sized picture file, invoke the method on it, and show the result. Just Katie's face will be copied to the target picture with the upper-left corner at (100, 100).

> String fileName = FileChooser.getMediaPath("7inx95in.jpg"); > Picture targetPicture = new Picture(fileName); > targetPicture.copyKatiesFace(); > targetPicture.show();


Try to copy part of another picture to the blank paper-sized picture file. What do you need to change and what can stay the same?

How does that work?

Let's look at a small example to see what's going on in the copying program. We start out with a source and a target, and copy from x = 0, y = 0 to x = 3, y = 1.

We then increment both the sourceY and targetY, and copy again.


[Page 149]

We continue down the column, incrementing both Y index variables.

When done with that column, we increment the X index variables and move on to the next column, until we copy every pixel.

5.2.2. Creating a Collage

In the mediasources folder are a couple images of flowers (Figure 5.10), each 100 pixels wide. Let's make a collage of them, by combining several of our effects to create different flowers. We'll copy them all into the blank image 640x480.jpg. All we really have to do is to copy the pixel colors to the right places.

Figure 5.10. Flowers in the mediasources folder.


Program 26. Creating a Collage by Copying Flowers
(This item is displayed on pages 149 - 151 in the print version)

/**  * Method to copy flower pictures to create a collage.  * All the flower pictures will be lined up near the  * bottom of the current picture (5 pixels from the bottom)  */ 
[Page 150]
public void copyFlowers() { // create the flower pictures Picture flower1Picture = new Picture(FileChooser.getMediaPath("flower1.jpg")); Picture flower2Picture = new Picture(FileChooser.getMediaPath("flower2.jpg")); // declare the source and target pixel variables Pixel sourcePixel = null; Pixel targetPixel = null; // save the heights of the two pictures int flower1Height = flower1Picture.getHeight(); int flower2Height = flower2Picture.getHeight(); /* copy the first flower picture to 5 pixels from the bottom * left corner of the current picture */ for (int sourceX = 0, targetX = 0; sourceX < flower1Picture.getWidth(); sourceX++, targetX++) { for (int sourceY = 0, targetY = this.getHeight() - flower1Height - 5; sourceY < flower1Picture.getHeight(); sourceY++, targetY++) { sourcePixel = flower1Picture.getPixel(sourceX,sourceY); targetPixel = this.getPixel(targetX,targetY); targetPixel.setColor(sourcePixel.getColor()); } } // copy the flower2 picture starting with x = 100 for (int sourceX = 0, targetX = 100; sourceX < flower2Picture.getWidth(); sourceX++, targetX++) { for (int sourceY = 0, targetY = this.getHeight() - flower2Height - 5; sourceY < flower2Picture.getHeight(); sourceY++, targetY++) { sourcePixel = flower2Picture.getPixel(sourceX,sourceY); targetPixel = this.getPixel(targetX,targetY); targetPixel.setColor(sourcePixel.getColor()); } } // copy the flower1 negated to x = 200
[Page 151]
flower1Picture.negate(); for (int sourceX = 0, targetX = 200; sourceX < flower1Picture.getWidth(); sourceX++, targetX++) { for (int sourceY = 0, targetY = this.getHeight() - flower1Height - 5; sourceY < flower1Picture.getHeight(); sourceY++, targetY++) { sourcePixel = flower1Picture.getPixel(sourceX,sourceY); targetPixel = this.getPixel(targetX,targetY); targetPixel.setColor(sourcePixel.getColor()); } } // clear the blue in flower 2 picture and add at x=300 flower2Picture.clearBlue(); for (int sourceX = 0, targetX = 300; sourceX < flower2Picture.getWidth(); sourceX++, targetX++) { for (int sourceY = 0, targetY = this.getHeight() - flower2Height - 5; sourceY < flower2Picture.getHeight(); sourceY++, targetY++) { sourcePixel = flower2Picture.getPixel(sourceX,sourceY); targetPixel = this.getPixel(targetX,targetY); targetPixel.setColor(sourcePixel.getColor()); } } // copy the negated flower 1 to x=400 for (int sourceX = 0, targetX = 400; sourceX < flower1Picture.getWidth(); sourceX++, targetX++) { for (int sourceY = 0, targetY = this.getHeight() - flower1Height - 5; sourceY < flower1Picture.getHeight(); sourceY++, targetY++) { sourcePixel = flower1Picture.getPixel(sourceX,sourceY); targetPixel = this.getPixel(targetX,targetY); targetPixel.setColor(sourcePixel.getColor()); } } }


Here's how we run the collage (Figure 5.11):

> String fileName = FileChooser.getMediaPath("7inx95in.jpg"); 
[Page 152]
> Picture targetPicture = new Picture(fileName); > targetPicture.copyFlowers(); > targetPicture.show();


Figure 5.11. Collage of flowers.


This method is long and repetitive, which makes it hard to read. One of the ways to improve it is to pull out pieces of code that perform the same task and make these new methods. Each time we add a new picture to our canvas, the only things changing are the picture to be added and the targetX. The targetY is always calculated the same way as the height of the canvas minus the height of the picture being copied minus 5.

Let's write a new more general method that is passed the picture to copy and the x location to start copying it to. For the y location let's put it 5 pixels from the bottom of the current picture.

Program 27. General Copy Method
(This item is displayed on pages 152 - 154 in the print version)

/**  * Method that will copy all of the passed source picture into  * the current picture object starting with the left corner  * given by xStart.  It will put the sourcePicture at 5 pixels  * from the bottom of this picture  * @param sourcePicture the picture object to copy  * @param xStart the x position to start the copy in the target  */ public void copyPictureTo(Picture sourcePicture,                           int xStart) 
[Page 153]
{ Pixel sourcePixel = null; Pixel targetPixel = null; // loop through the columns for (int sourceX = 0, targetX = xStart; sourceX < sourcePicture.getWidth(); sourceX++, targetX++) { // loop through the rows for (int sourceY = 0, targetY = this.getHeight() - sourcePicture.getHeight() - 5; sourceY < sourcePicture.getHeight(); sourceY++, targetY++) { sourcePixel = sourcePicture.getPixel(sourceX,sourceY); targetPixel = this.getPixel(targetX,targetY); targetPixel.setColor(sourcePixel.getColor()); } } } /** * Method to copy two flowers in a pattern to the * bottom (5 pixels from bottom) of the current picture */ public void copyFlowersBetter() { // create the flower pictures Picture flower1Picture = new Picture(FileChooser.getMediaPath("flower1.jpg")); Picture flower2Picture = new Picture(FileChooser.getMediaPath("flower2.jpg")); // copy the first flower picture to near the // bottom left corner of the canvas this.copyPictureTo(flower1Picture,0); /* copy the flower2 picture starting with * x = 100 in the canvas */ this.copyPictureTo(flower2Picture,100); // copy the flower1 negated to x = 200 in the canvas flower1Picture.negate(); this.copyPictureTo(flower1Picture,200); /* clear the blue in flower 2 picture and * add at x=300 in the canvas */
[Page 154]
flower2Picture.clearBlue(); this.copyPictureTo(flower2Picture,300); // copy the negated flower 1 to x=400 this.copyPictureTo(flower1Picture,400); }


The method copyFlowersBetter is much easier to read and understand now. And we now have a method copyPictureTo which is easy to reuse.

We can even make a more general copy method, which takes both the starting x and starting y values for the target picture and copies the passed source picture into the current picture with the source picture's upper-left corner at the passed starting x and y values in the target.

/**   * Method that will copy all of the passed source picture into   * the current picture object starting with the left corner   * given by xStart, yStart   * @param sourcePicture the picture object to copy   * @param xStart the x position to start the copy into on the   * target   * @param yStart the y position to start the copy into on the   * target   */  public void copyPictureTo(Picture sourcePicture,                            int xStart,                            int yStart)  {    Pixel sourcePixel = null;    Pixel targetPixel = null;    // loop through the columns    for (int sourceX = 0, targetX = xStart;         sourceX < sourcePicture.getWidth();         sourceX++, targetX++)    {      // loop through the rows      for (int sourceY = 0,           targetY = yStart;           sourceY < sourcePicture.getHeight();           sourceY++, targetY++)      {        sourcePixel = sourcePicture.getPixel(sourceX,sourceY);        targetPixel = this.getPixel(targetX,targetY);        targetPixel.setColor(sourcePixel.getColor());      }    }  }



[Page 155]

Notice that you can have two methods with the same names (copyPictureTo) and you don't have any trouble when you compile. How can that be? Java allows you to have many methods with the same method name as long as the parameters are different. The first copyPictureTo method took a Picture object and an int. The second copyPictureTo method took a Picture object, and two int values. So the two methods have a different number of parameters. Having more than one method with the same name but different parameters is called overloading. It doesn't really matter what you name the parameters. What matters is the types. Two methods with the same name are allowed if the number of parameters is different, or the types of the parameters are different, or the order of the parameter types is different. A method signature is the method name and the parameter list. So the method signatures need to be different in order for a method to be overloaded. The return type is not part of the method signature. Having two methods with the same name and same parameter list but different return types is not allowed.

5.2.3. Blending Pictures

When we create collages by copying, any overlap typically means that one picture shows over another. The last picture painted on is the one that appears. But it doesn't have to be that way. We can blend pictures by multiplying their colors and adding them. This gives us the effect of transparency.

We know that 100% of something is the whole thing. 50% of one and 50% of another would also add up to 100%. In the program below, we blend a picture of the two sisters with an overlap of 50 (the width of Katie minus 150) columns of pixels (Figure 5.12) onto the current picture.

Figure 5.12. Blending the picture of Katie and Jenny.



[Page 156]

Program 28. Blending Two Pictures
(This item is displayed on pages 156 - 157 in the print version)

/**  * Method to blend two sisters together onto the current  * picture  */ public void blendPictures() {   // create the sister pictures   Picture katiePicture =     new Picture(FileChooser.getMediaPath("KatieFancy.jpg"));   Picture jennyPicture =     new Picture(FileChooser.getMediaPath("JenParty.jpg"));   // declare the source and target pixel variables   Pixel katiePixel = null;   Pixel jennyPixel = null;   Pixel targetPixel = null;   /* declare the target x and source x since we will need    * the values after the for loop    */   int sourceX = 0;   int targetX = 0;   // copy the first 150 pixels of katie to the canvas   for (; sourceX < 150; sourceX++, targetX++)   {     for (int sourceY=0, targetY=0;          sourceY < katiePicture.getHeight();          sourceY++, targetY++)     {       katiePixel = katiePicture.getPixel(sourceX,sourceY);       targetPixel = this.getPixel(targetX,targetY);       targetPixel.setColor(katiePixel.getColor());     }   }   /* copy 50% of katie and 50% of jenny till    * the end of katie's width    */   for (; sourceX < katiePicture.getWidth();        sourceX++, targetX++)   {     for (int sourceY=0,targetY=0;          sourceY < katiePicture.getHeight();          sourceY++, targetY++)     {       katiePixel = katiePicture.getPixel(sourceX,sourceY);       jennyPixel =          jennyPicture.getPixel(sourceX - 150,sourceY); 
[Page 157]
targetPixel = this.getPixel(targetX,targetY); targetPixel.setColor( new Color((int) (katiePixel.getRed() * 0.5 + jennyPixel.getRed() * 0.5), (int) (katiePixel.getGreen() * 0.5 + jennyPixel.getGreen() * 0.5), (int) (katiePixel.getBlue() * 0.5 + jennyPixel.getBlue() * 0.5))); } } // copy the rest of Jenny sourceX = sourceX - 150; for (; sourceX < jennyPicture.getWidth(); sourceX++, targetX++) { for (int sourceY = 0, targetY = 0; sourceY < jennyPicture.getHeight(); sourceY++, targetY++) { jennyPixel = jennyPicture.getPixel(sourceX,sourceY); targetPixel = this.getPixel(targetX,targetY); targetPixel.setColor(jennyPixel.getColor()); } } }


To try this out, create a picture object using the blank 640 by 480 file and invoke the method on that. Show the result.

> String fileName = FileChooser.getMediaPath("640x480.jpg"); > Picture picture = new Picture(fileName); > picture.blendPictures(); > picture.show();


Making it Work Tip: Optional Parts of the for Loop

Notice that we are missing the initialization area in the for loops in the method blendPictures(). Also notice that we moved the declaration of sourceX and sourceY outside the loops. This is because we want to keep the values around after the first loop ends. The initialization area of a for loop is optional (the ';' is not optional). In fact, the initialization area, continuation test, and change area are all optional. You could code a for loop as for (;;) but that isn't terribly useful. It would execute the body of the loop forever. This is one way to create an infinite loop.


5.2.4. Rotation

Transformations to the image occur by using the index variables differently or incrementing them differently, but otherwise keeping the same program. Let's rotate Katie 90 degrees to the left. What does that mean? Let's try it with something simple first. You can write some numbers in a table on a piece of paper and then rotate it left and then read the new table to see where the old numbers were moved to (Figure 5.13). Notice that the columns become the rows and the rows the columns but it isn't as simple as just using the source x for the target y and the source y for the target x.


[Page 158]

Figure 5.13. Rotating some numbers in a table to the left 90 degrees.


Value (0, 0) in the source moves to (0, 2) in the target. Value (0, 1) in the source moves to (1, 2) in the target. Value (1, 0) in the source moves to (0, 1) in the target. Value (1, 1) in the source moves to (1, 1) in the target. Value (2, 0) in the source moves to (0, 0) in the target. Value (2, 1) in the source moves to (1, 0) in the target. So the first column values move into the bottom row and the last column values move into the top row. Also notice that the target x value is the same as the source y value.

We will do the rotation by looping through the pixels in the usual way and getting the source pixel in the usual way, but the target pixel's x value will be the source y and the target pixel's y value will be width of the source picture1the source x (Figure 5.14).

Figure 5.14. Copying a picture to a blank page rotated to the left 90 degrees.
(This item is displayed on page 159 in the print version)


Program 29. Rotating a Picture Left 90 Degrees
(This item is displayed on pages 158 - 159 in the print version)

/**  * Method to copy the picture of Katie but rotate  * her left 90 degrees on the current picture  */ public void copyKatieLeftRotation()  {    String sourceFile =      FileChooser.getMediaPath("KatieFancy.jpg");    Picture sourcePicture = new Picture(sourceFile);    Pixel sourcePixel = null;    Pixel targetPixel = null;    // loop through the columns    for (int sourceX = 0;         sourceX < sourcePicture.getWidth();         sourceX++)    {      // loop through the rows      for (int sourceY = 0;           sourceY < sourcePicture.getHeight();           sourceY++) 
[Page 159]
{ // set the target pixel color to the source pixel color sourcePixel = sourcePicture.getPixel(sourceX,sourceY); targetPixel = this.getPixel(sourceY, sourcePicture.getWidth() - 1 - sourceX); targetPixel.setColor(sourcePixel.getColor()); } } }


To try this out, create a picture from the blank paper-sized file and then invoke the method on it. Show the result (see Figure 5.14).

> String fileName = FileChooser.getMediaPath("7inx95in.jpg"); > Picture picture = new Picture(fileName); > picture.copyKatieLeftRotation(); > picture.show();


How does that work?

Rotating starts with the same source and target, and even the same variable values, but since we use the target X and Y differently, we get a different effect.


[Page 160]

Now, as we increment the Y variables, we're moving down the source, but across the target from left to right. As we increment the X variables we're moving across the source but up the target.

The source x and source y are both 0. The target x is equal to the source y thus it is also 0. But, the target y is equal to the width of the source picture minus 1 minus the source x. The width of the source picture is 3, which means that the target y is 3 1 0, which is 2. Thus we copy the color of the source pixel at (0, 0) to the target pixel at (0, 2).

The source y is incremented by the inner loop to 1 and tested against the height of the source picture (2). Since it is less than the height we do the body of the inner loop. Now the source x is 0 and the source y is 1. The target x is equal to the source y which means it is 1. The target y is equal to the width of the source picture minus 1 minus the source y. The width of the source picture is 3, thus the target y is 3 1 0, which is 2. Thus we copy the color of the source pixel at (0, 1) to the target pixel at (1, 2).


[Page 161]

The source y is incremented by the inner loop to 2 and tested against the height of the source picture (2). Since it is not less than the height the inner loop finishes and the source x is incremented to 1 by the outer loop. The inner loop starts and sets the source y to 0. Now the source x is 1 and the source y is 0. The target x is equal to the source y, which means that it is also 0. The target y is equal to the width of the source picture minus 1 minus the source y. The width of the source picture is 3, thus the target y is 3 1 1 which is 1. Thus we copy the color of the source pixel at (1, 0) to the target pixel at (0, 1).

The inner loop will increment source y and so the next color will be copied from (1, 1) to (1, 1). Then, the inner loop will stop again and source x will be incremented by 1 to 2. The next color will be copied from (2, 0) to (0, 0). The inner loop will increment source y so the next color will be copied from (2, 1) to (1, 0). At this point source x will be incremented to 3 which is not less than the width of the source picture (3) and the nested loop will stop.

5.2.5. Scaling

A very common transformation for pictures is to scale them. Scaling up means to make them larger, and scaling them down makes them smaller. It's common to scale a 1-megapixel or 3-megapixel picture down to a smaller size to make it easier to use on the Web or to send via e-mail. Smaller pictures require less disk space, and thus less network bandwidth, and thus are faster to upload or download.

Scaling a picture requires the use of sampling which we'll also use with sounds later. To scale a picture smaller we are going to take every other pixel when copying from the source to the target. To scale a picture larger we are going to take every pixel twice.

Scaling the picture down is the easier method. We will use a picture of one of the CS graduate students at Georgia Tech, Jakita N. Owensby (jakita.jpg). Her picture is 768 (width) by 768 (height). Instead of incrementing the source X and Y variables by 1, we simply increment by 2. We divide the amount of space by 2, since we'll fill half as much roomour width will be 768/2 and the height will be 768/2. The result is a smaller picture of Jakita on the blank 640 by 480 picture (Figure 5.15).

Figure 5.15. Scaling the picture of Jakita (a CS graduate student at Georgia Tech) down.



[Page 162]

Program 30. Scaling a Picture Down (Smaller)

/**   * Method to copy the picture of Jakita but smaller   * (half as big) to the current picture   */ public void copyJakitaSmaller() {   Picture jakitaPicture =     new Picture(FileChooser.getMediaPath("jakita.jpg"));   Pixel sourcePixel = null;   Pixel targetPixel = null;   // loop through the columns   for (int sourceX = 0, targetX=0;        sourceX < jakitaPicture.getWidth();        sourceX+=2, targetX++)   {     // loop through the rows     for (int sourceY=0, targetY=0;          sourceY < jakitaPicture.getHeight();          sourceY+=2, targetY++)     {       sourcePixel = jakitaPicture.getPixel(sourceX,sourceY);       targetPixel = this.getPixel(targetX,targetY);       targetPixel.setColor(sourcePixel.getColor());     }   } }


To try this out create a picture object using the blank 640 by 480 file and invoke the method on that. Show the result.

> Picture p = new Picture(FileChooser.getMediaPath("640x480.jpg")); > p.copyJakitaSmaller(); > p.show();


Scaling up the picture (making it larger) is a little trickier. We want to take every pixel twice. What we're going to do is to increment the source index variables by 0.5. Now, we can't reference pixel 1.5. But if we reference (int) 1.5 we'll get 1 again, and that'll work. The sequence of 1, 1.5, 2, 2.5 ... will become 1,1,2,2 ... The result is a larger form of the picture (Figure 5.16). Let's try this on rose.jpg which is 320 by 240 so scaling it up will result in a picture that is 640 by 480.

Figure 5.16. Scaling up a picture.
(This item is displayed on page 163 in the print version)


Program 31. Scaling the Picture Up (Larger)
(This item is displayed on pages 162 - 163 in the print version)

/**   * Method to copy a flower but scaled to 2x normal size   * onto the current picture   */ 
[Page 163]
public void copyFlowerLarger() { Picture flowerPicture = new Picture(FileChooser.getMediaPath("rose.jpg")); Pixel sourcePixel = null; Pixel targetPixel = null; // loop through the columns for (double sourceX = 0, targetX=0; sourceX < flowerPicture.getWidth(); sourceX = sourceX + 0.5, targetX++) { // loop through the rows for (double sourceY=0, targetY=0; sourceY < flowerPicture.getHeight(); sourceY = sourceY + 0.5, targetY++) { sourcePixel = flowerPicture.getPixel((int) sourceX,(int) sourceY); targetPixel = this.getPixel((int) targetX,(int) targetY); targetPixel.setColor(sourcePixel.getColor()); } } }


To try this out create a picture object using the blank 640 by 480 file and invoke the method on that. Show the result.

> String fileName = FileChooser.getMediaPath("640x480.jpg"); > Picture picture = new Picture(fileName); > picture.copyFlowerLarger(); > picture.show();



[Page 164]
How did that work?

We start from the same place as the original code for copying a picture. Say we are copying from the source picture starting at (0, 0) and copying to the target picture starting at (3, 1). First we will copy the color of the pixel at (0, 0) in the source picture to (3, 1) in the target picture.

When we increment sourceY by 0.5, the actual value will be 0.5 but the (int) value is 0 so we end up referring to the same pixel in the source, but the target has moved on to the next pixel. So we will copy the color of the pixel at (0, 0) to (3, 2).

When we increment sourceY a second time by 0.5, it will now equal 1.0, so we now move on to the next pixel in the source. So we will copy the color of the pixel at (0, 1) to (3, 3).


[Page 165]

Again, when the sourceY is incremented by 0.5, the actual value will be 1.5, but the (int) of that is 1, so we will copy from (0, 1) to (3, 4).

And eventually, we cover every pixel. Notice that the end result is degradedit's choppier than the original. Each pixel is copied four times: twice in the x direction and twice in the y direction.

This would work fine for scaling up by a factor of 2. What if we wanted to scale up by a factor of 3? We could add 1/3 each time through the loop but what is 1/3? It is represented in a double as 0.3333333333333333. Casting this to int would give us 0 for the source. When we add the factor to sourceX we would get 0.6666666666666666. This would also result in 0 when we cast to int. When we next add the factor we should get 0.999999999999999 which is still less than one and thus the cast to int would still result in 0. So we would copy the source pixel four times instead of three times. So this approach won't work as a general algorithm. It will work for scaling up a picture by an even amount but not by an odd amount.

You might want to be able to scale up a picture without always using the canvas picture as a target picture. One way to create a Picture object is to pass in a width and height: new Picture(width,height). This will create a blank picture of the passed width and height (both specified in pixels). new Picture(640,480) would create a picture object that is 640 pixels wide by 480 pixels talljust like the canvas.

Here is a more general method that will scale up the current picture object by some passed number of times. It creates a new Picture object of the desired width and height. It loops through all the source pixels and for each source pixel it copies it the passed number of times in both the x and y direction to the target picture. The hard part is determining the x and y values for the target picture. The source pixel at (0, 0) should be copied to the target picture (0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2). The source pixel at (1, 0) should be copied to (0, 3), (0, 4), (0, 5), (1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5). The target x and y is based on the source x and y times the number of times each pixel is copied plus the current x and y indices.


[Page 166]

Since this method creates a picture object that it changes, we want to be able to return this object so that we can refer to it again. How do we return something from a method? Well, we need to say what type of thing we are returning and then we have to actually return it. You specify the type of thing that the method returns in the method declaration. Up to now we have been using the keyword void to say that the method doesn't return anything. So, change the void to be the type of thing that you are returning. Since this method will return an object of the Picture class the type is Picture. At the end of the method use the keyword return followed by what you want to return. The compiler will check that the type of the thing you actually return matches the type you used in the method declaration. If it doesn't you will get a compile error.

Program 32. General Scale Up Method
(This item is displayed on pages 166 - 167 in the print version)

/**  * Method to create a new picture that is scaled up by the  * passed number of times.  * @return the new scaled up picture  */ public Picture scaleUp(int numTimes) {   Picture targetPicture =     new Picture(this.getWidth() * numTimes,                 this.getHeight() * numTimes);   Pixel sourcePixel = null;   Pixel targetPixel = null;   int targetX = 0;   int targetY = 0;   // loop through the source picture columns   for (int sourceX = 0;        sourceX < this.getWidth();        sourceX++) 
[Page 167]
{ // loop through the source picture rows for (int sourceY=0; sourceY < this.getHeight(); sourceY++) { // get the source pixel sourcePixel = this.getPixel(sourceX,sourceY); // loop copying to the target y for (int indexY = 0; indexY < numTimes; indexY++) { // loop copying to the target x for (int indexX = 0; indexX < numTimes; indexX++) { targetX = sourceX * numTimes + indexX; targetY = sourceY * numTimes + indexY; targetPixel = targetPicture.getPixel(targetX, targetY); targetPixel.setColor(sourcePixel.getColor()); } } } } return targetPicture; }


Since the method scaleUp returns the resulting scaled Picture object we had better save a reference to the Picture object to be able to refer to it again.

> Picture p = new Picture(FileChooser.getMediaPath("flower1.jpg")); > p = p.scaleUp(2); > p.explore();


Since this method creates a new Picture object and copies the scaled picture into that new Picture object and then returns the new Picture object if you want to see the result you will have to save a reference to the resulting picture. You can reuse variables like p, but realize that you will no longer have a reference to the original Picture object. Of course, you could have declared a new variable to hold the scaled picture. It would also be of type Picture.

> String fileName = FileChooser.getMediaPath("flower1.jpg"); > Picture origPicture = new Picture(fileName); > Picture scaledPicture = origPicture.scaleUp(2); > scaledPicture.show(); > origPicture.show();




Introduction to Computing & Programming Algebra in Java(c) A Multimedia Approach
Introduction to Computing & Programming Algebra in Java(c) A Multimedia Approach
ISBN: N/A
EAN: N/A
Year: 2007
Pages: 191

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net