Java:如何快速复制BufferedImage的像素?(包括单元测试)

19

我想从源BufferedImage中复制(矩形区域的)ARGB值到目标BufferedImage中。不应该进行任何合成:如果我复制具有ARGB值0x8000BE50(alpha值为128)的像素,则目标像素必须完全覆盖为0x8000BE50。

我有一个非常精确的问题,并且我编写了一个单元测试来展示我需要什么。该单元测试是完全功能和自包含的,正在正常通过并且正好做我想要的事情。

但是,我想要一种更快且更节省内存的方法来替换 copySrcIntoDstAt (...)。

这就是我的问题的全部内容:我不是在追求以更快的方式“填充”图像(我所做的只是为了演示单元测试)。 我只是想知道什么是快速且内存高效的方法来实现它(即快速而且不创建不必要的对象)。

我制作的概念验证实现显然非常节省内存,但速度较慢(对于每个像素执行一个getRGB和一个setRGB)。

大致上,我有这个:(其中A表示复制之前目标图像中对应的像素)

AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA

我想要这样:

AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAABBBBAAA
AAAAAAAAAAAAABBBBAAA
AAAAAAAAAAAAAAAAAAAA

'B'代表来自src图像的像素。

请注意,我正在寻找copySrcIntoDstAt(...)方法的精确替代品,而不是API链接/引用。

import org.junit.Test;

import java.awt.image.BufferedImage;

import static org.junit.Assert.*;

public class TestCopy {

    private static final int COL1 = 0x8000BE50;  // alpha at 128
    private static final int COL2 = 0x1732FE87;  // alpha at  23 

    @Test
    public void testPixelsCopy() {
        final BufferedImage src = new BufferedImage(  5,  5, BufferedImage.TYPE_INT_ARGB );
        final BufferedImage dst = new BufferedImage( 20, 20, BufferedImage.TYPE_INT_ARGB );
        convenienceFill( src, COL1 );
        convenienceFill( dst, COL2 );
        copySrcIntoDstAt( src, dst, 3, 4 );
        for (int x = 0; x < dst.getWidth(); x++) {
            for (int y = 0; y < dst.getHeight(); y++) {
                if ( x >= 3 && x <= 7 && y >= 4 && y <= 8 ) {
                    assertEquals( COL1, dst.getRGB(x,y) );
                } else {
                    assertEquals( COL2, dst.getRGB(x,y) );
                }
            }
        }
    }

    // clipping is unnecessary
    private static void copySrcIntoDstAt(
            final BufferedImage src,
            final BufferedImage dst,
            final int dx,
            final int dy
    ) {
        // TODO: replace this by a much more efficient method
        for (int x = 0; x < src.getWidth(); x++) {
            for (int y = 0; y < src.getHeight(); y++) {
                dst.setRGB( dx + x, dy + y, src.getRGB(x,y) );
            }
        }
    }

    // This method is just a convenience method, there's
    // no point in optimizing this method, this is not what
    // this question is about
    private static void convenienceFill(
            final BufferedImage bi,
            final int color
    ) {
        for (int x = 0; x < bi.getWidth(); x++) {
            for (int y = 0; y < bi.getHeight(); y++) {
                bi.setRGB( x, y, color );
            }
        }
    }

}
1个回答

23
private static void copySrcIntoDstAt(final BufferedImage src,
        final BufferedImage dst, final int dx, final int dy) {
    int[] srcbuf = ((DataBufferInt) src.getRaster().getDataBuffer()).getData();
    int[] dstbuf = ((DataBufferInt) dst.getRaster().getDataBuffer()).getData();
    int width = src.getWidth();
    int height = src.getHeight();
    int dstoffs = dx + dy * dst.getWidth();
    int srcoffs = 0;
    for (int y = 0 ; y < height ; y++ , dstoffs+= dst.getWidth(), srcoffs += width ) {
        System.arraycopy(srcbuf, srcoffs , dstbuf, dstoffs, width);
    }
}

1
+1 非常好...我从未想过使用多个 System.arraycopy 是正确的方法。我理解每个 BufferedImage 总是有一个光栅,这里没有对象创建吧!?这真的很好,但同时也有点奇怪:我本来期望有一些预先存在的东西可以自动完成工作,而不必手动进行 for 循环和 System.arraycopy。但是,是的,真的很好(顺便试了一下,看起来没问题 :) - SyntaxT3rr0r
@WizardOfOds 谢谢,你强调了“更快和更节省内存”的部分,某种类型的循环总是需要移动偏移量。在不进行复制和创建对象的情况下操作内部结构,我怀疑是否存在一种非 JNI 方法能够表现得更好。 - stacker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接