首页 > 解决方案 > 如何在 Java 中使图像的矩形部分变暗?

问题描述

该代码创建一个带有 JPanel 的 JFrame,它在其上绘制从文件加载的图像。目的是使图片的矩形区域(例如红色正方形)显得比其他区域更暗。我假设这可能涉及获取图像的子图像,遍历像素数组,缩放它们,然后将该子图像绘制到 JPanel 上,但我不知道如何使用 Java API 来做到这一点。

package SpriteEditor_Tests;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;

public class ImageTestApp extends JFrame
{
    public BufferedImage image;
    int x1 = 50;
    int x2 = 100;
    int y1 = 50;
    int y2 = 100;
    public static void main (String [] args)
    {
        new ImageTestApp();
    }

    public ImageTestApp()
    {
        setTitle("Image Test App");
        try
        {
            image = ImageIO.read(new File("C:/Users/Paul/Desktop/derp.png"));
        }
        catch (IOException io)
        {
            System.out.println("IO caught");    System.exit(0);
        }
        setSize(500,500);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
        add(new ImageDisplay());
    }

    class ImageDisplay extends JPanel
    {
        public void paintComponent(Graphics g)
        {
            g.drawImage(image, -100, -100, getWidth(), getHeight(), Color.RED, null);
            g.setColor(Color.RED);
            g.drawRect(x1, y1, Math.abs(x2 - x1), Math.abs(y2 - y1));
        }
    }
}

在此处输入图像描述

标签: javagraphics

解决方案


一个“简单”的解决方案是创建一个新实例,Color并应用所需的 alpha 并填充您想要变暗的区域。

如果您有想要使用的颜色,这很好,但是当我想使用预定义的颜色时,就没有那么简单了。相反,我更喜欢使用 an AlphaComposite,因为它给了我一些优势。

简单的

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

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

    public Test() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private BufferedImage background;

        public TestPane() {
            try {
                background = ImageIO.read(getClass().getResource("/images/background.jpg"));
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        @Override
        public Dimension getPreferredSize() {
            if (background == null) {
                return new Dimension(200, 200);
            }
            return new Dimension(background.getWidth(), background.getHeight());
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (background == null) {
                return;
            }
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.drawImage(background, 0, 0, this);

            int x = (getWidth() - 100) / 2;
            int y = (getHeight() - 100) / 2;

            Rectangle rect = new Rectangle(x, y, 200, 200);

            g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
            g2d.fill(rect);

            g2d.dispose();
            g2d = (Graphics2D) g.create();

            g2d.setColor(Color.RED);
            g2d.drawRect(x, y, 200, 200);

            g2d.dispose();
        }

    }
}

现在,如果要生成一个带有变暗的新图像,您可以遵循相同的基本概念,但不是绘制到组件Graphics上下文,而是直接绘制到BufferedImagesGraphics内容。这就是GraphicsAPI 抽象性质的奇妙力量。

不要忘记,当你重写一个方法时,你必须要么接管它的所有职责,要么调用它的super实现。

paintComponent做一些基本但重要的工作,您应该确保super.paintComponent在开始执行自定义绘画之前致电,这将减少任何问题的可能性。

单独变暗每个像素

好的,相反,如果您想单独使矩形中的每个像素变暗,这将变得“有点”复杂,但并不难。

经过大量时间和测试,我决定使用跟随算法来加深给定的颜色。这将把颜色推向“黑色”,你越暗,这是一些算法不做的。

public static Color darken(Color color, double fraction) {
    int red = (int) Math.round(Math.max(0, color.getRed() - 255 * fraction));
    int green = (int) Math.round(Math.max(0, color.getGreen() - 255 * fraction));
    int blue = (int) Math.round(Math.max(0, color.getBlue() - 255 * fraction));

    int alpha = color.getAlpha();

    return new Color(red, green, blue, alpha);
}

然后,您所要做的就是获取像素的颜色,将其变暗并重新应用。

对于这个例子,我实际上使用了一个单独的子图像,但是你可以直接对父图像做

BufferedImage subImage = background.getSubimage(x, y, 200, 200);
for (int row = 0; row < subImage.getHeight(); row++) {
    for (int col = 0; col < subImage.getWidth(); col++) {
        int packedPixel = subImage.getRGB(col, row);
        Color color = new Color(packedPixel, true);
        color = darken(color, 0.5);
        subImage.setRGB(col, row, color.getRGB());
    }
}

现在,在有人从我的喉咙里跳下来之前,不,这不是最高效的方法,但它已经摆脱了“打包”像素值的困扰(因为我永远不记得如何解压这些:P)和我的大部分 API 代码是基于Color无论如何的使用

可运行的示例...

暗像素

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class Test {

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

    public Test() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setVisible(true);
            }
        });
    }

    public static Color darken(Color color, double fraction) {

        int red = (int) Math.round(Math.max(0, color.getRed() - 255 * fraction));
        int green = (int) Math.round(Math.max(0, color.getGreen() - 255 * fraction));
        int blue = (int) Math.round(Math.max(0, color.getBlue() - 255 * fraction));

        int alpha = color.getAlpha();

        return new Color(red, green, blue, alpha);

    }

    public class TestPane extends JPanel {

        private BufferedImage background;
        private BufferedImage darkended;

        public TestPane() {
            try {
                background = ImageIO.read(getClass().getResource("/images/background.jpg"));
                int x = (background.getWidth() - 100) / 2;
                int y = (background.getHeight() - 100) / 2;

                BufferedImage subImage = background.getSubimage(x, y, 200, 200);
                for (int row = 0; row < subImage.getHeight(); row++) {
                    for (int col = 0; col < subImage.getWidth(); col++) {
                        int packedPixel = subImage.getRGB(col, row);
                        Color color = new Color(packedPixel, true);
                        color = darken(color, 0.5);
                        subImage.setRGB(col, row, color.getRGB());
                    }
                }
                darkended = subImage;
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        @Override
        public Dimension getPreferredSize() {
            if (background == null) {
                return new Dimension(200, 200);
            }
            return new Dimension(background.getWidth(), background.getHeight());
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (background == null) {
                return;
            }
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.drawImage(background, 0, 0, this);

            int x = (getWidth() - 100) / 2;
            int y = (getHeight() - 100) / 2;

            g2d.drawImage(darkended, x, y, this);

            g2d.setColor(Color.RED);
            g2d.drawRect(x, y, 200, 200);

            g2d.dispose();
        }

    }
}

推荐阅读