javaswingtransparencyimage-rotation

How would I go about rendering a transparent and rotated image in Java?


I cannot seem to figure out how to draw a transparent and rotated image. I need to be able to draw an image that is transparent and rotated to a certain degree.

I tried this code:

// draws an image that is rotated to a certain degree
public static void drawRotatedImage(BufferedImage image_, int x, int y, int degrees, float scale) {

    // graphics used for the utilities of drawing the image (processing)
    Graphics2D utilGraphics;

    // make rectangular image
    int radius = (int) Math.sqrt(image_.getWidth() * image_.getWidth() + image_.getHeight() * image_.getHeight());
    BufferedImage image1 = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_RGB);

    utilGraphics = image1.createGraphics();
// centers image
    utilGraphics.drawImage(image_, image1.getWidth() / 2 - image_.getWidth() / 2, image1.getHeight() / 2 - image_.getHeight() / 2, null);

    // scale image
    int nw = (int) (image1.getWidth() * scale);
    int nh = (int) (image1.getHeight() * scale);

    BufferedImage image = new BufferedImage(nw, nh, BufferedImage.TYPE_INT_RGB);
    utilGraphics.drawImage(image1, 0, 0, nw, nh, null);

    // Rotation information

    double rotationRequired = Math.toRadians (degrees);
    double locationX = image.getWidth() / 2;
    double locationY = image.getHeight() / 2;

    AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
    AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);

    ImageProducer filteredImgProd = new FilteredImageSource(op.filter(image, null).getSource(), filter);
    Image transparentImg = Toolkit.getDefaultToolkit().createImage(filteredImgProd);

    // Drawing the rotated image at the required drawing locations
    g2d.drawImage(Toolkit.getDefaultToolkit().createImage(transparentImg.getSource()), x, y, null);
}

The filter variable is defined as:

private static final ImageFilter filter = new RGBImageFilter() {
    int transparentColor = new Color(0, 0, 0, 0).getRGB() | 0x0000ffcc;

    public final int filterRGB(int x, int y, int rgb) {
        if ((rgb | 0x0000ffcc) == transparentColor) {
            return 0x0000ffcc & rgb;
        } else {
            return rgb;
        }
    }
};

Solution

  • This ...

        BufferedImage image = new BufferedImage(nw, nh, BufferedImage.TYPE_INT_RGB);
        centeredGraphics.drawImage(image1, 0, 0, nw, nh, null);
    

    You're creating a new BufferedImage (image), but you never actually paint anything to it, instead, you paint image1 to it's own Graphics context.

    Now, if you wanted a transparent image, you should have used...

    BufferedImage centeredImage = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_ARGB);
    

    instead of...

    BufferedImage centeredImage = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_RGB);
    

    And I never used g2d.drawImage(Toolkit.getDefaultToolkit().createImage(transparentImg.getSource()), x, y, null); as it just doesn't make sense to me (transparentImg is already an Image 🤷‍♂️)

    Now, having said all that, I would "suggest" you take each step individually, start by scaling the original image using something like Java: maintaining aspect ratio of JPanel background image and the rotate the image using something like Rotate a buffered image in Java (which will generate a image large enough to contain the rotated image)

    Also, if you "create" a Graphics context, you should also dispose of it when you no longer need it, otherwise you could end up with a memory leak.

    "Fixed" code...

    Just to be clear, I would still recommend sing ARGB instead of RGB for centeredImage as your filter workflow never seemed to work for, but I started with a transparent image anyway

    public Image rotateAndScaleImage(BufferedImage originalImage, int degrees, float scale) {
        // make rectangular image
        int radius = (int) Math.sqrt(originalImage.getWidth() * originalImage.getWidth() + originalImage.getHeight() * originalImage.getHeight());
        BufferedImage centeredImage = new BufferedImage(radius, radius, BufferedImage.TYPE_INT_RGB);
    
        Graphics2D graphics = centeredImage.createGraphics();
        // centers image
    
        int xPos = (centeredImage.getWidth() - originalImage.getWidth()) / 2;
        int yPos = (centeredImage.getHeight() - originalImage.getHeight()) / 2;
    
        graphics.drawImage(originalImage, xPos, yPos, null);
        graphics.dispose();
    
        // scale image
        int nw = (int) (centeredImage.getWidth() * scale);
        int nh = (int) (centeredImage.getHeight() * scale);
    
        BufferedImage image = new BufferedImage(nw, nh, BufferedImage.TYPE_INT_RGB);
        graphics = image.createGraphics();
        // No scaling is done ???
        graphics.drawImage(centeredImage, 0, 0, nw, nh, null);
    
        // Rotation information
        double rotationRequired = Math.toRadians(degrees);
        double locationX = centeredImage.getWidth() / 2;
        double locationY = centeredImage.getHeight() / 2;
    
        AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
        AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
    
        ImageProducer filteredImgProd = new FilteredImageSource(op.filter(centeredImage, null).getSource(), filter);
        Image transparentImg = Toolkit.getDefaultToolkit().createImage(filteredImgProd);
    
        return transparentImg;
    }
    
    private static final ImageFilter filter = new RGBImageFilter() {
        int transparentColor = new Color(0, 0, 0, 0).getRGB() | 0x0000ffcc;
    
        public final int filterRGB(int x, int y, int rgb) {
            if ((rgb | 0x0000ffcc) == transparentColor) {
                return 0x0000ffcc & rgb;
            } else {
                return rgb;
            }
        }
    };
    

    Oh, and I'm returning an Image because I painted directly to a component for testing