Android位图多线程处理-不安全吗?

3

我希望在我的Android应用程序中处理位图 - 由于位图可能非常大,因此我使用多线程执行更快的操作。这是我的代码(Runnable子类的一部分):

@Override
public void run() {
    int imageHeight = filter.getBitmap().getHeight();
    int start = threadNumber * imageHeight / threadCount;
    int stop = (threadNumber + 1) * imageHeight / threadCount;
    for (int j = start; j < stop; j++) {
        filter.processLine(j);
    }
}
//...
protected void processLine(int lineNumber)
{
    int width = bitmap.getWidth();
    int pixels[] = new int[width];
    bitmap.getPixels(pixels, 0, width, 0, lineNumber, width, 1);
    for (int i = 0; i < width; i++) {
        pixels[i] = processPixel(pixels[i]);
    }
    bitmap.setPixels(pixels, 0, width, 0, lineNumber, width, 1);
}

当我在线程池中只使用1个线程时,一切正常。不幸的是,当我使用与处理器核心数相等的线程数(我的设备上为4)时,结果如下所示(对于灰度滤镜): issue 看起来有时候:
- bitmap.getPixels(...)不起作用,因为输出中有黑线 - bitmap.setPixels(...)不起作用,因为输出中有未更改的行
我是对的吗?这些函数是线程不安全的吗?我应该如何执行多线程位图过滤以使其快速且线程安全?
1个回答

4

Android提供的2D图像处理是由Skia库提供的,这个库也被Chrome所使用。

很难找到明确的答案。 "Skia不是线程安全的,虽然SkBitmap[由Bitmap使用]是线程安全的..." 我不知道该怎么想。我浏览了一堆晦涩难懂的JNI/C++代码,这是我能提供的最好的建议(似乎比其他人都要多):

我认为你应该在整个位图上调用bitmap.getPixels()。然后将结果数组分成若干部分,并在不同的线程中进行处理。当所有线程完成时,重新组装结果并调用bitmap.setPixels()

看起来bitmap.getPixels()bitmap.setPixels()只是简单的memcpy(),但实际上底层还有许多其他操作,如引用计数、图像缓存、颜色空间转换、预乘等等。将Bitmap方法从并发处理中移除应该可以避免问题的出现。


这是我的最终答案。删除了之前的回答。 - kris larson
我已经尝试了您的解决方案,使用bitmap.getPixels(),但它也没有正常工作 - 存在未处理图像的碎片。不幸的是,我撤销了这些更改。也许以后我会再次编写此代码并显示输出结果。 - Mariusz Jaskółka
当你这样做时,请同时发布执行线程的代码。干杯 - kris larson
我认为NDK可能是一个不错的选择。你可以寻找第三方2D图像处理库,但你仍然需要将像素复制到位图中,以便用户能够看到图像。唉。 - kris larson
使用同步部分的解决方案比单线程处理慢得多。请再次编辑您的帖子,我会将其标记为正确。 - Mariusz Jaskółka
显示剩余2条评论

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