我正在编写一个Android应用程序,它必须根据收到的事件绘制加载的位图图像的确定部分。
我需要绘制(或更改当前颜色)位图图像的单个部分,而不更改图像的其余部分。
假设我有一辆车,它被许多部分分开:车门,车窗,车轮等。 每次事件(从网络接收)到达时,我都需要使用事件数据指定的颜色更改该特定部分的颜色。 实现这一目标的最佳技术是什么?
我首先考虑过FloodFill,正如SO中许多线程所建议的那样,但考虑到消息接收速度非常快(每秒几个),我担心它会降低性能,因为它似乎是CPU密集型算法。 / p>
我还考虑过拥有相同图像的多个片段,每个片段都有不同的颜色,并在正确的时间显示正确的一个,但是汽车至少有10个不同的部分,每个部分都可以涂4-6个颜色,所以我最终得到了几十张图片,处理起来是不切实际的,更不用说浪费记忆了。
那么,还有其他方法吗?
答案 0 :(得分:0)
最快的方法是使用着色器。你需要使用OpenGL ES 2(一些Androids只支持ES 1)。您需要一个与要更改的图像大小相同的临时位图。将其设置为目标。在着色器中,从采样器中检索绑定到要更改的图像的像素。如果它在您想要更改的颜色的小容差范围内,请将gl_FragColor设置为新颜色,否则只需将gl_FragColor设置为从采样器检索的颜色。您需要将所需的颜色和新颜色作为带有al_set_shader_float_vector
的vec4传递到着色器中。最快的方法是保留2个位图并在它们之间交换,作为每次颜色更改时使用的“主要位置”。
如果无法使用着色器,则必须锁定位图并替换颜色。使用al_lock_bitmap
锁定它,然后您可以使用al_get_pixel
和al_put_pixel
更改颜色。完成后al_unlock_bitmap
。您还可以避免使用al_get_pixel / al_put_pixel并手动访问内存,这将更快。如果您使用格式ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE
锁定位图,那么内存的布局如下:
int w = al_get_bitmap_width(bitmap);
int h = al_get_bitmap_height(bitmap);
for (int y = 0; y < h; y++) {
unsigned char *p = locked_region->data + locked_region->pitch * y;
for (int x = 0; x < w; x++) {
unsigned char r = p[0];
unsigned char g = p[1];
unsigned char b = p[2];
unsigned char a = p[3];
/* change r, g, b, a here if they match */
p[0] = r;
p[1] = g;
p[2] = b;
p[3] = a;
p += 4;
}
}
建议您以创建它的格式锁定图像。这意味着选择一个像我提到的那样容易的图像,否则循环的内部部分会变得更复杂。像素格式的ABGR_8888部分描述了数据的布局。 ABGR告诉组件的顺序。如果您要将像素读入单个存储单元(在这种情况下为int,但它的工作方式与短路相同)则位模式为AAAAAAAABBBBBBBBGGGGGGGGRRRRRRRR
。但是,当您一次读取一个字节时,大多数机器都是小端,这意味着小端首先出现。这就是为什么在我的示例代码中p [0]是红色的。 8888部分告诉每个组件有多少位。