java - 仅使用 API 将灰度图像绘制到另一个 BufferedImage 的 alpha 带

标签 java graphics bufferedimage alpha-transparency compositing

我有两个 BufferedImage:一个是 TYPE_INT_ARGB,另一个是 TYPE_BYTE_GRAY。如何仅使用 API 将整个彩色图像的 alpha 带替换为灰度图像,而不干扰 RGB 值?

final int width = 200;
final int height = 200;

final BufferedImage colorImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
final BufferedImage grayscaleImg = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
Graphics2D g = colorImg.createGraphics();
// flip some mystical switches
// g.drawImage( grayscaleImg into colorImg's alpha band )
g.dispose();

我可以通过屏蔽和复制字节来手动完成此操作,如下所示:

WritableRaster clrRaster = colorImg.getRaster();
DataBufferInt clrBuffer = (DataBufferInt) clrRaster.getDataBuffer();
int[] clrData = clrBuffer.getData();

WritableRaster grayRaster = grayscaleImg.getRaster();
DataBufferByte grayBuffer = (DataBufferByte) grayRaster.getDataBuffer();
byte[] grayData = grayBuffer.getData();

int pixel, alphaBits;
for(int i = 0; i < clrData.length; i++) {
    pixel = clrData[i] & 0x00ffffff;
    alphaBits = (int)grayData[i] << 24;
    clrData[i] = pixel | alphaBits;
}

但是 API 方式是什么?

更新 #1 示例图像:输入灰度 alpha、输入彩色猫、输出带有孔的彩色猫。 grayscale of donut color cat merge result

生成的图像具有输出颜色 Alpha 中的灰度。在照片编辑器中查看生成的图像,您会看到中间的孔实际上是透明的。

private void injectAlphaIntoColor() {
    try {
        // BEWARE: this code does not check if both images are the same
        // width and height. May get out of bounds exception if w&h are 
        // different.


        BufferedImage cat = ImageIO.read(new File("s:/temp/cat5.png"));
        BufferedImage gray = ImageIO.read(new File("s:/temp/layermask.png"));

        // convert color cat to TYPE_INT_ARGB
        BufferedImage color = new BufferedImage(cat.getWidth(), cat.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = color.createGraphics();
        g.drawImage(cat, 0, 0, null);
        g.dispose();

        final WritableRaster clrRaster = color.getRaster();
        final DataBufferInt clrBuffer = (DataBufferInt) clrRaster.getDataBuffer();
        final int[] clrData = clrBuffer.getData();

        final WritableRaster grayRaster = gray.getRaster();
        final DataBufferByte grayBuffer = (DataBufferByte) grayRaster.getDataBuffer();
        final byte[] grayData = grayBuffer.getData();

        int pixel, alphaBits;

        // manually put each grayscale pixel into each color pixel's alpha
        for(int i = 0; i < clrData.length; i++) {
            pixel = clrData[i] & 0x00ffffff;
            alphaBits = (int)grayData[i] << 24;
            clrData[i] = pixel | alphaBits;
        }

        ImageIO.write(color, "png", new File("s:/temp/3rd_output.png"));
    } 

    catch (IOException ex) {
        System.out.println(ex.getMessage());
    }
}

为了重申我最初的目标,Java API 的方式是什么来完成上述代码的功能?

最佳答案

啊,你想使用蒙版剪掉图像的一部分,我的错。

简单的解决方案 - 首先使用基于 alpha 的蒙版。但我假设你没有这个选择。我确实尝试找到一个可以做到这一点的解决方案,但相反,偶然发现了 this example相反。

它能够产生您似乎正在寻找的结果

Cutout

(蓝色是面板​​的背景色)

核心功能是为了...

public void applyGrayscaleMaskToAlpha(BufferedImage image, BufferedImage mask) {
    int width = image.getWidth();
    int height = image.getHeight();

    int[] imagePixels = image.getRGB(0, 0, width, height, null, 0, width);
    int[] maskPixels = mask.getRGB(0, 0, width, height, null, 0, width);

    for (int i = 0; i < imagePixels.length; i++) {
        int color = imagePixels[i] & 0x00ffffff; // Mask preexisting alpha
        int alpha = maskPixels[i] << 24; // Shift blue to alpha
        imagePixels[i] = color | alpha;
    }

    image.setRGB(0, 0, width, height, imagePixels, 0, width);
}

但作为一个简单的可运行示例......

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
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;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception ex) {
                }

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

    public void applyGrayscaleMaskToAlpha(BufferedImage image, BufferedImage mask) {
        int width = image.getWidth();
        int height = image.getHeight();

        int[] imagePixels = image.getRGB(0, 0, width, height, null, 0, width);
        int[] maskPixels = mask.getRGB(0, 0, width, height, null, 0, width);

        for (int i = 0; i < imagePixels.length; i++) {
            int color = imagePixels[i] & 0x00ffffff; // Mask preexisting alpha
            int alpha = maskPixels[i] << 24; // Shift blue to alpha
            imagePixels[i] = color | alpha;
        }

        image.setRGB(0, 0, width, height, imagePixels, 0, width);
    }

    public class TestPane extends JPanel {

        private BufferedImage master;
        private BufferedImage mask;

        public TestPane() {
            setBackground(Color.BLUE);
            try {
                master = ImageIO.read(new File("..."));
                mask = ImageIO.read(new File("..."));

                applyGrayscaleMaskToAlpha(master, mask);
            } catch (IOException exp) {
                exp.printStackTrace();
            }
        }

        @Override
        public Dimension getPreferredSize() {
            Dimension size = super.getPreferredSize();
            if (master != null && mask != null) {
                size = new Dimension(master.getWidth() + mask.getWidth(), Math.max(master.getHeight(), mask.getHeight()));
            }
            return size;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            int x = (getWidth() - (master.getWidth() + mask.getWidth())) / 2;
            int y = (getHeight() - master.getHeight()) / 2;
            g.drawImage(master, x, y, this);

            x += mask.getWidth();
            y = (getHeight() - mask.getHeight()) / 2;
            g.drawImage(mask, x, y, this);
        }

    }

}

抱歉造成困惑

更新 - 没有“操作数据缓冲区”

I was looking for the Java API way of doing the same job without manipuating the bits

好吧,所以我也更喜欢从灰度图像生成基于 alpha 的图像的解决方案,它更适合整体 Graphics 2D API。所以,多读一点之后of this question which keeps on giving ,我偶然发现了这个想法......

public static BufferedImage grayScaleToTransparency(BufferedImage master) {
    ImageFilter filter = new RGBImageFilter() {
        public final int filterRGB(int x, int y, int rgb) {
            return (rgb << 16) & 0xFF000000;
        }
    };

    ImageProducer ip = new FilteredImageSource(master.getSource(), filter);
    Image img = Toolkit.getDefaultToolkit().createImage(ip);
    
    BufferedImage buffer = createCompatibleImage(img.getWidth(null), img.getHeight(null), Transparency.TRANSLUCENT);
    Graphics2D g2d = buffer.createGraphics();
    g2d.drawImage(img, 0, 0, null);
    g2d.dispose();
    
    return buffer;
}

现在,可能可以使用 BufferedImageFilterBufferedImageOp 来实现这一点,但我没有时间或经验来进一步研究它。

使用这种技术我能够生产......

Masked progression

原始图像|原始(灰度)蒙版 |基于 Alpha 的掩模 |蒙版图像

同样,蓝色是面板​​的背景颜色。

import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.awt.image.ImageProducer;
import java.awt.image.RGBImageFilter;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception ex) {
                }

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

    public static GraphicsConfiguration getGraphicsConfiguration() {
        return GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
    }

    public static BufferedImage createCompatibleImage(int width, int height, int transparency) {
        BufferedImage image = getGraphicsConfiguration().createCompatibleImage(width, height, transparency);
        image.coerceData(true);
        return image;
    }

    public static void applyQualityRenderingHints(Graphics2D g2d) {
        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
    }

    public static BufferedImage applyMask(BufferedImage master, BufferedImage mask) {
        int imgWidth = master.getWidth();
        int imgHeight = master.getHeight();

        BufferedImage imgMask = createCompatibleImage(imgWidth, imgHeight, Transparency.TRANSLUCENT);
        Graphics2D g2 = imgMask.createGraphics();
        applyQualityRenderingHints(g2);

        g2.drawImage(mask, 0, 0, null);
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, 1f));
        g2.drawImage(master, 0, 0, null);
        g2.dispose();

        return imgMask;
    }

    public static BufferedImage grayScaleToTransparency(BufferedImage master) {
        ImageFilter filter = new RGBImageFilter() {
            public final int filterRGB(int x, int y, int rgb) {
                return (rgb << 16) & 0xFF000000;
            }
        };

        ImageProducer ip = new FilteredImageSource(master.getSource(), filter);
        Image img = Toolkit.getDefaultToolkit().createImage(ip);

        BufferedImage buffer = createCompatibleImage(img.getWidth(null), img.getHeight(null), Transparency.TRANSLUCENT);
        Graphics2D g2d = buffer.createGraphics();
        g2d.drawImage(img, 0, 0, null);
        g2d.dispose();

        return buffer;
    }

    public class TestPane extends JPanel {

        private BufferedImage master;
        private BufferedImage originalMask;
        private BufferedImage alphaMask;
        private BufferedImage masked;

        public TestPane() {
            setBackground(Color.BLUE);
            try {
                master = ImageIO.read(new File("/Users/swhitehead/Downloads/lIceL.png"));
                originalMask = ImageIO.read(new File("/Users/swhitehead/Downloads/MXmFp.png"));
                alphaMask = grayScaleToTransparency(originalMask);
                masked = applyMask(master, alphaMask);
//                tinted = tint(master, mask);
            } catch (IOException exp) {
                exp.printStackTrace();
            }
        }

        protected int desiredWidth() {
            return master.getWidth() + originalMask.getWidth() + alphaMask.getWidth() + masked.getWidth();
        }

        protected int desiredHeight() {
            return Math.max(Math.max(Math.max(master.getHeight(), originalMask.getHeight()), alphaMask.getHeight()), masked.getHeight());
        }

        @Override
        public Dimension getPreferredSize() {
            Dimension size = super.getPreferredSize();
            if (master != null && originalMask != null) {
                size = new Dimension(desiredWidth(),
                        desiredHeight());
            }
            return size;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            int x = (getWidth() - desiredWidth()) / 2;
            int y = (getHeight() - master.getHeight()) / 2;
            g.drawImage(master, x, y, this);

            x += originalMask.getWidth();
            y = (getHeight() - originalMask.getHeight()) / 2;
            g.drawImage(originalMask, x, y, this);

            x += alphaMask.getWidth();
            y = (getHeight() - alphaMask.getHeight()) / 2;
            g.drawImage(alphaMask, x, y, this);

            x += masked.getWidth();
            y = (getHeight() - masked.getHeight()) / 2;
            g.drawImage(masked, x, y, this);
        }

    }

}

关于java - 仅使用 API 将灰度图像绘制到另一个 BufferedImage 的 alpha 带,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47446922/

相关文章:

java - PreparedStatements 或 callableStatements

java - 使用 java.util.concurrent 模糊图像,但是,生成的图像完全是黑色的

java - 如何在java中翻转BufferedImage

java - Hibernate 无法正确从数据库读取对象

java - 在 MapReduce 中使用 MultipleTextOutputFormat 控制输出文件名

android - 在Android的OpenGL ES中运行时创建大型纹理的最有效方法

java - JOptionPane 在 JFrame 上保持重叠值

java - 尽管我使用了 Swing Timer,但在 JPanel 上从动画绘制 Wave 时 GUI 卡住

java - Firebase 邀请 : How to send referral code with Firebase Invites

performance - 为什么绘制小于 1.5 像素粗的线条比绘制 10 像素粗的线条慢两倍?