How do I copy part of one image to another?
Asked Answered
O

4

6

I want to copy part of one image into another smaller one: in other words, copy a subrectangle.

I have a Graphics2D object for the source, I can make one for the target, and I know about targetGraphics2D.drawImage(Image img,....) , but how do I get that img from the sourceGraphics2D?


Answer (per aioobe): The source needs to be an Image rather than a Graphics2D.

Image.subImage() is the method for getting the relevant part of the source.

Operational answered 6/12, 2010 at 13:59 Comment(1)
Be aware that subImage() does not copy the underlying image data, at least with BufferedImage. So if you draw on the Image obtained by subImage() you also modify the original, while with target.createGraphics().drawImage(Image img, ...) you actually really make a copy.Hagler
K
0

First, some notes regarding Andreas_D answer below:

  • His code relies on sun.java2d.SunGraphics2D which is an internal and undocumented OpenJDK class. This means that, while it might compile and run on your computer, it might will probably break if you distribute the code to other people. For a detailed discussion, see the official statement regarding this.

  • The code relies on reflection to pry the internal class open which is a code smell in it self.

  • All in all, his approach is an example of exceptionally bad practice (both when it comes to programming style and when it comes to helping fellow programmers to use the API correctly)


how do I get that img from the sourceGraphics2D?

I suspect you misunderstood the responsibility of the Graphics2D class.

You use the Graphics2D class to draw on something. It is capable of drawing on a BufferedImage (if you got the graphics object from a buffered image), the screen (if you got it as an argument to your paintComponent method) or even a printer. So in other words, given a Graphics2D objects, there might not even exist an image!

So, as you probably understand, the Graphics2D API does not provide methods for getting hold of the underlying image. (Such method wouldn't make sense, the graphics object might be passing on lines and text being drawn to a printer!)

To get hold of a subimage you need to get hold of the underlying image which the given graphics object draws upon.

Katinka answered 6/12, 2010 at 14:7 Comment(2)
OK, I understand. The source needs to be an Image rather than a Graphics2D. Then, Image.subImage() is the method for getting the clip.Operational
Ah c'mon, there's always a way! (And in this case: the "pencil" remembers what it has drawn ;-) )Delorsedelos
Q
3

As Aioobe said, you're not going to get the image from the Graphics2D alone. But if your sourceGraphics2D is coming from a Swing component you could try invoking its paint methods with your own Graphics2D instance. From there you can copy the interesting sub-region.

This is kind of a hack but it should work assuming you're using Swing objects.

class CopySwingPaintingSubRegion extends TheSourceType
{
       private BufferedImage bufImg;

       public CopySwingPaintingSubRegion()
       {
          bufImg = new BufferedImage(...);

          //Draw the original image into our bufImg for copying
          super.paintComponent(bufImg.createGraphics());
       }

       public BufferedImage getSubImage(int x, int y, int w, int h)
       {
          return bufImg.getSubImage(x,y,w,h);
       }
}
Queue answered 6/12, 2010 at 14:57 Comment(0)
D
3

Short answer: Yes, you can

A Graphics2D object that has been created on a buffered Image knows the image but isn't willing to hand it back to you. If you don't mind using reflection, you can steal it back (reflection). The following code demonstrates the approach:

public class Graphics2DReflector {

   public static void main(String[] args) {
     // prepare the Graphics2D - note, we don't keep a ref to the image!
     final Graphics2D g2d = 
          new BufferedImage(100,100,BufferedImage.TYPE_INT_RGB).createGraphics();
     g2d.drawString("Reflected", 10, 50);


     JFrame frame = new JFrame("Reflected Image");
     // one Panel to show the image only known by g2d
     frame.getContentPane().add(new Panel() {
       @Overwrite
       public void paint(Graphics g) {
         try {
           SurfaceData data = ((SunGraphics2D) g2d).surfaceData;
           Field bufImg = BufImgSurfaceData.class.getDeclaredField("bufImg");
           bufImg.setAccessible(true);
           BufferedImage image = (BufferedImage) bufImg.get(data);
           g.drawImage(image,0,0,null);
         } catch (Exception oops) {
           oops.printStackTrace();
         }
       }
     });
     frame.setSize(200,200);
     frame.setVisible();
   }
}

Note: this depends on some Sun/Oracle classes and may not work with all Java VMs. At least it shows an approach and you may have to inspect the actual classes to get the fields.

Delorsedelos answered 6/12, 2010 at 15:4 Comment(2)
relying on both the sun.* package and reflection in the same answer.. hehe ;) You should probably at least list the circumstances under which the Graphics2D object actually is a SunGraphics2D? Read Why Developers Should Not Write Programs That Call 'sun' Packages.Katinka
@aiobe - Hey, it's a hack and I already added a general note for those limitations. You said ist doesn't work and I just replied: It is possible. Not portable at all but not impossible.Delorsedelos
V
1

If I understand it right, W3Schools has code to get part of an image to put on a canvas. If you need it as another image you could get it from that canvas.

I have one PNG image 20 tall by 200 wide that has 10 "cells or frames" - I use for animation and use the method below:

context.drawImage( img, sx, sy, swidth, sheight, x, y, width, height );
Viscoid answered 7/11, 2013 at 13:44 Comment(0)
K
0

First, some notes regarding Andreas_D answer below:

  • His code relies on sun.java2d.SunGraphics2D which is an internal and undocumented OpenJDK class. This means that, while it might compile and run on your computer, it might will probably break if you distribute the code to other people. For a detailed discussion, see the official statement regarding this.

  • The code relies on reflection to pry the internal class open which is a code smell in it self.

  • All in all, his approach is an example of exceptionally bad practice (both when it comes to programming style and when it comes to helping fellow programmers to use the API correctly)


how do I get that img from the sourceGraphics2D?

I suspect you misunderstood the responsibility of the Graphics2D class.

You use the Graphics2D class to draw on something. It is capable of drawing on a BufferedImage (if you got the graphics object from a buffered image), the screen (if you got it as an argument to your paintComponent method) or even a printer. So in other words, given a Graphics2D objects, there might not even exist an image!

So, as you probably understand, the Graphics2D API does not provide methods for getting hold of the underlying image. (Such method wouldn't make sense, the graphics object might be passing on lines and text being drawn to a printer!)

To get hold of a subimage you need to get hold of the underlying image which the given graphics object draws upon.

Katinka answered 6/12, 2010 at 14:7 Comment(2)
OK, I understand. The source needs to be an Image rather than a Graphics2D. Then, Image.subImage() is the method for getting the clip.Operational
Ah c'mon, there's always a way! (And in this case: the "pencil" remembers what it has drawn ;-) )Delorsedelos

© 2022 - 2024 — McMap. All rights reserved.