How to disable java.awt.Graphics.fillRect(int x, int y, int width, int height)'s effect?

半世苍凉 提交于 2019-12-29 09:17:09

问题


It's the original image:

I use java.awt.Graphics.fillRect(int x, int y, int width, int height) to add a coloured rectangle on the image.

Graphics imageGraphics = image.createGraphics();
Color color = new Color(0,0,0,100);
imageGraphics.setColor(color);
imageGraphics.fillRect(0, 0, 800, 600);

So the image has been inverted and looks like this:

After that,I want to clear the black transparent rectangle partly and show the original image. imageGraphics.clearRect(100,100,100,100); But the effect is like this:

What my requirement is:

I want to know why it doesn't work and is there any other way to realize it?


回答1:


Remember, painting is destructive. It might be possible to use AlphaComposite to achieve this result, but a simpler solution might be to simple constructive a compound shape and paint that instead.

The following example creates two Rectangles, one been the area we want to fill and one been the area we want to show, the second is then subtracted from the first (to create the window) and then the result is painted on top of the image

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class ShowArea {

    public static void main(String[] args) {
        new ShowArea();
    }

    public ShowArea() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private BufferedImage img;

        public TestPane() {
            try {
                img = ImageIO.read(new File("sample.png"));
                Rectangle bounds = new Rectangle(0, 0, img.getWidth(), img.getHeight());
                Rectangle clip = new Rectangle(150, 10, 100, 100);

                Area area = new Area(bounds);
                area.subtract(new Area(clip));

                Graphics2D g2d = img.createGraphics();
                g2d.setColor(Color.BLACK);
                g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
                g2d.fill(area);
                g2d.dispose();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return img == null ? new Dimension(200, 200) :  new Dimension(img.getWidth(), img.getHeight());
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            int x = (getWidth() - img.getWidth()) / 2;
            int y = (getHeight() - img.getHeight()) / 2;
            g2d.drawImage(img, x, y, this);
            g2d.dispose();
        }

    }

}

If I were doing some kind of paint program, I would do this all within the paintComponent method or in some way that it didn't effect the original image, otherwise you've basic destroyed the image until you re-load it

Another solution might be to take a copy of the original area you want to keep and repaint it back on top after, for example...

img = ImageIO.read(new File("sample.png"));
// This is the portion of the image we want to save...
BufferedImage cutout = img.getSubimage(150, 10, 100, 100);
// This is the area we want to paint over...
Rectangle bounds = new Rectangle(0, 0, img.getWidth(), img.getHeight());

Graphics2D g2d = img.createGraphics();
g2d.setColor(Color.BLACK);
// Save the current Composite so we can reset it...
Composite comp = g2d.getComposite();
// Apply the composite and fill the area...
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.fill(area);
// Reset the composite 
g2d.setComposite(comp);
// Draw the part of the image we saved previously...
g2d.drawImage(cutout, 150, 10, this);
g2d.dispose();



回答2:


You cant do that the way you're trying to.

It can be done by creating a BufferedImage filled with Color(0,0,0,200), then in that image draw rectangle with color Color(0,0,0,200) and then apply it on image. Remember, that drawing filledRectange is not a "undo operation"- it is written on pixels and it can't be undone.

EDIT

        Icon imageIcon = new javax.swing.ImageIcon("image.jpg");
        BufferedImage mask = new BufferedImage(imageIcon.getIconWidth(), imageIcon.getIconHeight(), BufferedImage.TYPE_4BYTE_ABGR);
        BufferedImage image = new BufferedImage(imageIcon.getIconWidth(), imageIcon.getIconHeight(), BufferedImage.TYPE_4BYTE_ABGR);
        imageIcon.paintIcon(null, image.getGraphics(), 0, 0);
        Graphics maskGraphics = mask.getGraphics();
        //drawing grey background
        maskGraphics.setColor(new Color(0, 0, 0, 120));
        maskGraphics.fillRect(0, 0, mask.getWidth(), mask.getHeight());
        //drawing black frame
        maskGraphics.setColor(new Color(0, 0, 0, 255));
        maskGraphics.drawRect(99, 99, 301, 301);
        //drawing original image window
        maskGraphics.drawImage(image, 100, 100, 400, 400, 100, 100, 400, 400, null);
        //apply mask on image
        new ImageIcon(mask).paintIcon(null, image.getGraphics(), 0, 0);
        //result presentation
        label.setIcon(new ImageIcon(image));




回答3:


Yes this is easily doable. The problem is that any draw operation is destructive, what you see is what you get, the information that was painted upon is lost.

Something like this where you store a subimage, do your paint operation then draw the subimage back on top.

public class Q23709070 {

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        Panel p = new Panel();

        frame.getContentPane().add(p);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    static class Panel extends JPanel {
        int w = 400;
        int h = 400;
        int x = 100;
        int y = 100;
        BufferedImage img;
        BufferedImage subImg;
        BufferedImage save;

        public Panel() {
            try {
                img = ImageIO.read(getClass().getResourceAsStream("0qzCf.jpg"));
            } catch (IOException e) {
            }
            subImg = img.getSubimage(x, y, w, h);

        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            Color g2dColor = g2d.getColor();
            Color fillColor = new Color(0, 0, 0, 100);

            g2d.drawImage(img, 0, 0, null);
            g2d.setColor(fillColor);
            g2d.fillRect(0, 0, img.getWidth(), img.getHeight());
            g2d.drawImage(subImg, x, y, null);
            g2d.setColor(g2dColor);

            if (save == null) {
                save = new BufferedImage(img.getWidth(), img.getHeight(),
                        img.getType());
                this.paint(save.getGraphics());
                try {
                    ImageIO.write(save, "jpg", new File("save.jpg"));
                } catch (IOException e) {
                }
            }

        }

        @Override
        @Transient
        public Dimension getPreferredSize() {
            return new Dimension(img.getWidth(), img.getHeight());
        }
    }
}

Rendering



来源:https://stackoverflow.com/questions/23709070/how-to-disable-java-awt-graphics-fillrectint-x-int-y-int-width-int-heights

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!