我在Android上使用renderscript来编辑照片,目前由于Android上的纹理大小限制和内存限制,如果我尝试使用设备相机拍摄的照片,应用程序将会崩溃。
我首先想到的是使用BitmapRegionDecoder并将大照片拼贴成可管理的片段,通过renderscript编辑它们并一次保存一个,然后使用PNGJ将它们拼接在一起 - PNG解码和编码库允许将部分PNG图像写入磁盘,因此我的内存中没有完整的图像。
这样可以正常工作,但将它拼接在一起需要相当长的时间 - 大约1分钟的猜测。
我还应该考虑其他解决方案吗?如果有解决方案,我可以更改为JPEG,但我还没有找到它。基本上我正在寻找BitmapRegionDecoder的另一面,一个BitmapRegionEncoder。
为了清楚起见,我不想调整图像大小。
答案 0 :(得分:4)
BitmapRegionDecoder
以水平条纹加载图像。下面的代码假设它是PNG并使用PNGJ将元数据复制到新图像,但添加对JPEG的支持应该不会太困难。PNG版this image(4850x3635px)在Nexus 5上需要12秒才能使用一个简单的RS过滤器(去饱和)。
void processPng(String forig,String fdest) {
try {
Allocation inAllocation = null;
Allocation outAllocation = null;
final int block_height = 64;
FileInputStream orig = new FileInputStream(forig);
FileInputStream orig2 = new FileInputStream(forig);
FileOutputStream dest = new FileOutputStream(fdest);
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(orig, false);
Rect blockRect = new Rect();
PngReader pngr = new PngReader(orig2);
PngWriter pngw = new PngWriter(dest, pngr.imgInfo);
pngw.copyChunksFrom(pngr.getChunksList());
// keep compression quick
pngw.getPixelsWriter().setDeflaterCompLevel(1);
int channels = 3; // needles to say, this should not be hardcoded
int width = pngr.imgInfo.samplesPerRow / channels;
int height = pngr.imgInfo.rows;
pngr.close(); // don't need it anymore
blockRect.left = 0;
blockRect.right = width;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap blockBitmap;
byte []bytes = new byte[width * block_height * 4];
byte []byteline = new byte[width * channels];
for (int row = 0; row <= height / block_height; row++) {
int h;
// are we nearing the end?
if((row + 1) * block_height <= height)
h = block_height;
else {
h = height - row * block_height;
// so that new, smaller Allocations are created
inAllocation = outAllocation = null;
}
blockRect.top = row * block_height;
blockRect.bottom = row * block_height + h;
blockBitmap = decoder.decodeRegion(blockRect, options);
if(inAllocation == null)
inAllocation = Allocation.createFromBitmap(mRS, blockBitmap);
if(outAllocation == null)
{
Type.Builder TypeDir = new Type.Builder(mRS, Element.U8_4(mRS));
TypeDir.setX(width).setY(h);
outAllocation = Allocation.createTyped(mRS, TypeDir.create());
}
inAllocation.copyFrom(blockBitmap);
mScript.forEach_saturation(inAllocation, outAllocation);
outAllocation.copyTo(bytes);
int idx = 0;
for(int raster = 0; raster < h; raster++) {
for(int m = 0; m < width; m++)
{
byteline[m * channels] = bytes[idx++];
byteline[m * channels + 1] = bytes[idx++];
byteline[m * channels + 2] = bytes[idx++];
idx++;
}
ImageLineByte line = new ImageLineByte(pngr.imgInfo, byteline);
pngw.writeRow(line);
}
}
pngw.end();
} catch (IOException e)
{
Log.d("BIG", "File io problem");
}
}
答案 1 :(得分:0)
基于@MiloslawSmyk答案,这是加载大JPEG并将其保存为PNGJ的版本:
fun processPng(forig: String, fdest: String) {
try {
val blockHeight = 64
val orig = FileInputStream(forig)
val dest = FileOutputStream(fdest)
val decoder = BitmapRegionDecoder.newInstance(orig, false)
val blockRect = Rect()
val channels = 3 // needles to say, this should not be hardcoded
val sizeOptions = BitmapFactory.Options().apply {
inJustDecodeBounds = true
}
BitmapFactory.decodeFile(forig, sizeOptions)
val height: Int = sizeOptions.outHeight
val width: Int = sizeOptions.outWidth
val pngw = PngWriter(dest, ImageInfo(width, height, 8, false))
// keep compression quick
pngw.pixelsWriter.deflaterCompLevel = 1
blockRect.left = 0
blockRect.right = width
val options = BitmapFactory.Options().apply {
inPreferredConfig = Bitmap.Config.ARGB_8888
}
var blockBitmap: Bitmap
val byteLine = ByteArray(width * channels)
for (row in 0..height / blockHeight) {
// are we nearing the end?
val h: Int = if ((row + 1) * blockHeight <= height)
blockHeight
else {
height - row * blockHeight
}
blockRect.top = row * blockHeight
blockRect.bottom = row * blockHeight + h
blockBitmap = decoder.decodeRegion(blockRect, options)
// convert bitmap into byte array
val size = blockBitmap.rowBytes * blockBitmap.height
val byteBuffer = ByteBuffer.allocate(size)
blockBitmap.copyPixelsToBuffer(byteBuffer)
val bytes = byteBuffer.array()
var idx = 0
for (raster in 0 until h) {
for (m in 0 until width) {
byteLine[m * channels] = bytes[idx++]
byteLine[m * channels + 1] = bytes[idx++]
byteLine[m * channels + 2] = bytes[idx++]
idx++
}
val line = ImageLineByte(pngw.imgInfo, byteLine)
pngw.writeRow(line)
}
}
pngw.end()
} catch (e: IOException) {
Log.d("BIG", "File io problem")
}
}