如何在Android中编辑和保存大型位图?目前将其写为瓷砖,但加入它们的速度很慢

时间:2015-08-12 15:49:26

标签: android bitmap renderscript

我在Android上使用renderscript来编辑照片,目前由于Android上的纹理大小限制和内存限制,如果我尝试使用设备相机拍摄的照片,应用程序将会崩溃。

我首先想到的是使用BitmapRegionDecoder并将大照片拼贴成可管理的片段,通过renderscript编辑它们并一次保存一个,然后使用PNGJ将它们拼接在一起 - PNG解码和编码库允许将部分PNG图像写入磁盘,因此我的内存中没有完整的图像。

这样可以正常工作,但将它拼接在一起需要相当长的时间 - 大约1分钟的猜测。

我还应该考虑其他解决方案吗?如果有解决方案,我可以更改为JPEG,但我还没有找到它。基本上我正在寻找BitmapRegionDecoder的另一面,一个BitmapRegionEncoder。

为了清楚起见,我不想调整图像大小。

2 个答案:

答案 0 :(得分:4)

  1. 使用BitmapRegionDecoder以水平条纹加载图像。下面的代码假设它是PNG并使用PNGJ将元数据复制到新图像,但添加对JPEG的支持应该不会太困难。
  2. 使用Renderscript处理每个条带。
  3. 使用PNGJ保存。不要使用高压缩,否则会慢慢爬行。
  4. 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")
    }

}