java 反转位图颜色

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4624531/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 07:16:16  来源:igfitidea点击:

Invert bitmap colors

javaandroidcolors

提问by Alex Orlov

I have the following problem. I have a charting program, and it's design is black, but the charts (that I get from the server as images) are light (it actually uses only 5 colors: red, green, white, black and gray).

我有以下问题。我有一个图表程序,它的设计是黑色的,但图表(我从服务器作为图像获得)很浅(它实际上只使用 5 种颜色:红色、绿色、白色、黑色和灰色)。

To fit with the design inversion does a good job, the only problem is that red and green are inverted also (green -> pink, red -> green).

为了配合设计反转做得很好,唯一的问题是红色和绿色也反转了(绿色 -> 粉红色,红色 -> 绿色)。

Is there a way to invert everything except those 2 colors, or a way to repaint those colors after inversion? And how costly are those operations (since I get the chart updates pretty often)?

有没有办法反转除这两种颜色之外的所有颜色,或者反转后重新绘制这些颜色的方法?这些操作的成本有多高(因为我经常更新图表)?

Thanks in advance :)

提前致谢 :)

UPDATE

更新

I tried replacing colors with setPixel method in a loop

我尝试在循环中用 setPixel 方法替换颜色

for(int x = 0 ;x < chart.getWidth();x++) {
        for(int y = 0;y < chart.getHeight();y++) {
            final int replacement = getColorReplacement(chart.getPixel(x, y));
            if(replacement != 0) {
                chart.setPixel(x, y, replacement);
            }
        }
    }

Unfortunetely, the method takes too long (~650ms), is there a faster way to do it, and will setPixels() method work faster?

不幸的是,该方法花费的时间太长(~650 毫秒),有没有更快的方法,并且 setPixels() 方法会更快地工作吗?

回答by Utyi

Manipulating a bitmap is much faster if you copy the image data into an int array by calling getPixels only once, and don't call any function inside the loop. Just manipulate the array, then call setPixels at the end.

如果通过仅调用一次 getPixels 将图像数据复制到 int 数组中,并且不在循环内调用任何函数,则操作位图的速度会快得多。只需操作数组,然后在最后调用 setPixels。

Something like that:

类似的东西:

int length = bitmap.getWidth()*bitmap.getHeight();
int[] array = new int[length];
bitmap.getPixels(array,0,bitmap.getWidth(),0,0,bitmap.getWidth(),bitmap.getHeight());
for (int i=0;i<length;i++){
// If the bitmap is in ARGB_8888 format
  if (array[i] == 0xff000000){
    array[i] = 0xffffffff;
  } else if ...
  }
}
bitmap.setPixels(array,0,bitmap.getWidth(),0,0,bitmap.getWidth(),bitmap.getHeight());

回答by dagnelies

If you have it available as BufferedImage, you can access its raster and edit it as you please.

如果您将其作为 可用BufferedImage,则可以访问其栅格并根据需要对其进行编辑。

WritableRaster raster = my_image.getRaster();

// Edit all the pixels you wanna change in the raster (green -> red, pink -> green)
// for (x,y) in ...
// raster.setPixel(x, y, ...) 

my_image.setData(raster);

回答by Gugussee

OK seen that you're really only using 5 colors it's quite easy.

好吧,看到你真的只使用了 5 种颜色,这很容易。

Regarding performances, I don't know about Android but I can tell you that in Java using setRGBis amazingly slower than getting back the data buffer and writing directly in the int[].

关于性能,我不了解 Android,但我可以告诉你,在 Java 中使用setRGB比取回数据缓冲区并直接写入 int[] 慢得多。

When I write "amazingly slower", to give you an idea, on OS X 10.4 the following code:

当我写“非常慢”时,给你一个想法,在 OS X 10.4 上,以下代码:

for ( int x = 0; x < width; x++ ) {
    for ( int y = 0; y < height; y++ ) {
        img.setRGB(x,y,0xFFFFFFFF);
    }
}

can be 100 times (!) slower than:

可以比以下速度慢 100 倍 (!)

for ( int x = 0; x < width; x++ ) {
    for ( int y = 0; y < height; y++ ) {
        array[y*width+x] = 0xFFFFFFFF;
    }
}

You read correctly: one hundred time. Measured on a Core 2 Duo / Mac Mini / OS X 10.4.

你没看错:一百次。在 Core 2 Duo / Mac Mini / OS X 10.4 上测得。

(of course you need to first get access to the underlying int[] array but hopefully this shouldn't be difficult)

(当然,您需要首先访问底层的 int[] 数组,但希望这不难)

I cannot stress enough that the problem ain't the two for loops: in both cases it's the same unoptimized for loops. So it's really setRGB that is the issue here.

我不能强调问题不是两个 for 循环:在这两种情况下,它都是相同的未优化 for 循环。所以真正的 setRGB 才是这里的问题。

I don't know it works on Android, but you probably should get rid of setRGB if you want something that performs well.

我不知道它适用于 Android,但如果您想要性能良好的东西,您可能应该摆脱 setRGB。

回答by Kevin Dion

A quick way would be to use AvoidXfermodeto repaint just those colors you want changed - you could then switch between any colors you want. You just need to do something like this:

一种快速的方法是使用AvoidXfermode重新绘制您想要更改的颜色——然后您可以在您想要的任何颜色之间切换。你只需要做这样的事情:

// will change red to green
Paint change1 = new Paint();
change1.setColor(Color.GREEN);
change1.setXfermode(new AvoidXfermode(Color.RED, 245, AvoidXfermode.Mode.TARGET));

Canvas c = new Canvas();
c.setBitmap(chart);
c.drawRect(0, 0, width, height, change1);

// rinse, repeat for other colors

You may need to play with the tolerance for the AvoidXfermode, but that should do what you want a lot faster than a per-pixel calculation. Also, make sureyour chart image is in ARGB8888 mode. By default, Android tends to work with images in RGB565 mode, which tends to mess up color calculations like you want to use - to be sure, you can make sure your image is both in ARGB8888 mode andmutable by calling Bitmap chart = chartFromServer.copy(Config.ARGB_8888, true);before you setup the Xfermode.

您可能需要调整 的容差AvoidXfermode,但这应该比逐像素计算快得多。另外,请确保您的图表图像处于 ARGB8888 模式。默认情况下,Android 倾向于使用 RGB565 模式下的图像,这往往会像您想使用的那样混淆颜色计算 - 可以肯定的是,您可以通过在设置 Xfermode 之前调用来确保您的图像处于 ARGB8888 模式并且是可变的Bitmap chart = chartFromServer.copy(Config.ARGB_8888, true);.

Clarification: to change other colors, you wouldn't have to re-load the images all over again, you would just have to create other Paints with the appropriate colors you want changed like so:

澄清:要更改其他颜色,您不必再次重新加载图像,您只需要使用您想要更改的适当颜色创建其他油漆,如下所示:

// changes green to red
Paint change1 = new Paint();
change1.setColor(Color.GREEN);
change1.setXfermode(new AvoidXfermode(Color.RED, 245, AvoidXfermode.Mode.TARGET));

// changes white to blue
Paint change2 = new Paint();
change2.setColor(Color.BLUE);
change2.setXfermode(new AvoidXfermode(Color.WHITE, 245, AvoidXfermode.Mode.TARGET));

// ... other Paints with other changes you want to apply to this image

Canvas c = new Canvas();
c.setBitmap(chart);
c.drawRect(0, 0, width, height, change1);
c.drawRect(0, 0, width, height, change2);
//...
c.drawRect(0, 0, width, height, changeN);