Android - 如何在以编程方式加载或保存JPEG文件时获取或设置(打印)DPI(每英寸点数)?

时间:2014-02-19 11:29:01

标签: android image printing bitmap dpi

我在Android 4.0及更高版本上开发了一款应用。 (该应用不支持4.0以下的Android版本[Ice Cream Sandwich])。

该问题与各种图像(例如jpeg或png)格式的(打印)DPI有关。

此问题与SCREEN DPI或各种Android设备的大小无关。它也与在屏幕尺寸上显示设备上的位图无关。

我使用以下代码在“位图”中加载图像文件。然后我一直在裁剪它并使用jpegCompression将其保存为JPEG格式的另一个文件。我已经能够通过以下代码执行此操作,但我无法获取加载的DPI或设置保存的图像文件的DPI。

所以我有两个问题。

1)如何在“Bitmap”中加载后,在JPEG文件中获取(打印)DPI?

2)在保存新生成的“位图”时,如何在JPEG文件中再次设置DPI?

以下是代码的一部分供参考。

    FileInputStream inputStream = new FileInputStream(theSourcePhotoFilePathName);

    Bitmap bitmap = null;
    BitmapRegionDecoder decoder = null;

    BitmapFactory.Options options = new BitmapFactory.Options();        
    options.inSampleSize = 1;
    options.inDensity = 300;   // Tried this but not working.

    try {
        decoder = BitmapRegionDecoder.newInstance(in, false);
        bitmap = decoder.decodeRegion(region, options);      // the region has cropping coordinates.
    } catch (IllegalArgumentException e){
        Log.d("First Activity", "Failed to recycle bitmap for rect=" + region, e);
    } catch (IOException e) {
        Log.d("First Activity", "Failed to decode into rect=" + region, e);
    } finally {
        if (decoder != null) decoder.recycle();
    }

    inputStream.close();
    inputStream = null;

    FileOutputStream fos = new FileOutputStream( theTargetTempFolderDestFilePath );
    bitmap.compress(CompressFormat.JPEG, jpegCompressionRatio, fos);
    fos.flush();
    fos.close();
    fos = null;

我试图通过谷歌搜索从stackoverflow和其他网站找到,但无法得到正确的相关答案。所以我决定在这个论坛上提问。

欢迎您提示和建议。

桑杰。

2 个答案:

答案 0 :(得分:14)

为方便起见,这里准备复制并粘贴方法:

public static void saveBitmapToJpg(Bitmap bitmap, File file, int dpi) throws IOException {
    ByteArrayOutputStream imageByteArray = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, imageByteArray);
    byte[] imageData = imageByteArray.toByteArray();

    setDpi(imageData, dpi);

    FileOutputStream fileOutputStream = new FileOutputStream(file);
    fileOutputStream.write(imageData);
    fileOutputStream.close();
}

private static void setDpi(byte[] imageData, int dpi) {
    imageData[13] = 1;
    imageData[14] = (byte) (dpi >> 8);
    imageData[15] = (byte) (dpi & 0xff);
    imageData[16] = (byte) (dpi >> 8);
    imageData[17] = (byte) (dpi & 0xff);
}

保存的文件将正确设置图像DPI 值:

enter image description here

答案 1 :(得分:5)

您指的是作为JPEG文件的一部分编写的DPI元数据吗?我最近一直在努力,但想出了一个解决方案。如果你已经解决了,那么这个答案可以帮助那些遇到同样问题的人。

在Android中将位图压缩为JPEG时,会将其保存为JFIF段格式。请参阅此处的文章(http://en.wikipedia.org/wiki/JPEG_File_Interchange_Format)。我还附上了一个在十六进制编辑器中打开的Android jpeg图像的屏幕截图,以便您可以看到它是如何匹配的。

enter image description here

要编辑该值,您需要首先创建一个将存储Bitmap.compress()的byte []数组。这是我的代码的一部分,我只是这样做(输入是源Bitmap)。

ByteArrayOutputStream uploadImageByteArray = new ByteArrayOutputStream();
input.compress(Bitmap.CompressFormat.JPEG, 100, uploadImageByteArray);
byte[] uploadImageData = uploadImageByteArray.toByteArray();

基于JFIF结构,您需要编辑字节数组中的第13,14,15,16和17个索引。 13指定密度类型,X分辨率的第14和第15,以及保持Y分辨率的第16和第17。就我而言,我将其更改为500 dpi作为元数据中的X和Y分辨率。这转换为0000 0001 1111 0100,其为1F4十六进制。然后我将byte []写入文件,将其从手机复制到我的计算机,并在查看图像属性时验证了详细信息。

                uploadImageData[13] = 00000001;
                uploadImageData[14] = 00000001;
                uploadImageData[15] = (byte) 244
                uploadImageData[16] =  00000001;
                uploadImageData[17] = (byte) 244


                 File image = new  File(Environment
                .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
                .getAbsolutePath() + "/meta Images/", imageFileName);
                FileOutputStream sdCardOutput = new FileOutputStream(image);
                sdCardOutput.write(uploadImageData);

注意: Java使用带符号的字节系统,如果没有编译器咆哮,您就无法输入127以上的任何二进制值。我遇到了这个问题,输入F4作为一个字节。解决方案是将您的值转换为十进制,然后使用(byte)强制转换。

希望这有帮助!