Is there a equivalent of Android's BitmapFactory.Options isDecodeBounds for TIFF in Java/JAI?

有些话、适合烂在心里 提交于 2019-12-23 02:57:17

问题


I am trying to improve the performance of our system (a Java app running in Tomcat) and now the bottleneck is in one operation, we need to read and return dimension of tiff images, so we use JAI's ImageDecoder and use

ImageDecoder decoder = ImageCodec.createImageDecoder("TIFF", input, param);
RenderedImage r = decoder.decodeAsRenderedImage();
int width = r.getWidth();
int height = r.getHeight();

From sampling data, a lot of time is spent in createImageDecoder. My assumption (without going to source code of ImageCodec) is it's probably trying to decode the input stream.

Coming from Android land, I am hoping there is a similar solution to just decode bounds like setting BitmapFactory.Options.inJustDecodeBounds = true but so far no luck in finding any other library like that. (I am aware that tiff support on Android is missing in AOSP, but that's topic for another day.)

Anyone know a library that does this? Or is there a way to achieve similar goal using JAI/ImageIO?


回答1:


It looks like the tiff file format groups this information together in a header, so you could just read the data from the file yourself:

private static Dimension getTiffDimensions(InputStream tiffFile) throws IOException {
    ReadableByteChannel channel = Channels.newChannel(tiffFile);

    ByteBuffer buffer = ByteBuffer.allocate(12);

    forceRead(channel, buffer, 8);
    byte endian = buffer.get();
    if(endian != buffer.get() || (endian != 'I' && endian != 'M')) {
        throw new IOException("Not a tiff file.");
    }

    buffer.order(endian == 'I' ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
    if(buffer.getShort() != 42) {
        throw new IOException("Not a tiff file.");
    }

    // Jump to the first image directory. Note that we've already read 8 bytes.
    tiffFile.skip(buffer.getInt() - 8);

    int width = -1;
    int height = -1;
    // The first two bytes of the IFD are the number of fields.
    forceRead(channel, buffer, 2);
    for(int fieldCount = buffer.getShort(); fieldCount > 0 && (width < 0 || height < 0); --fieldCount) {
        forceRead(channel, buffer, 12);
        switch(buffer.getShort()) {
        case 0x0100: // Image width
            width = readField(buffer);
            break;
        case 0x0101: // Image "length", i.e. height
            height = readField(buffer);
            break;
        }
    }
    return new Dimension(width, height);
}

private static void forceRead(ReadableByteChannel channel, ByteBuffer buffer, int n) throws IOException {
    buffer.position(0);
    buffer.limit(n);

    while(buffer.hasRemaining()) {
        channel.read(buffer);
    }
    buffer.flip();
}

private static int readField(ByteBuffer buffer) {
    int type = buffer.getShort();
    int count = buffer.getInt();

    if(count != 1) {
        throw new RuntimeException("Expected a count of 1 for the given field.");
    }

    switch(type) {
    case 3: // word
        return buffer.getShort();
    case 4: // int
        return buffer.getInt();
    default: // char (not used here)
        return buffer.get() & 0xFF;
    }
}

I've tested this with a few different tiff files (run length encoded black & white, color with transparency) and it seems to work fine. Depending on the layout of your tiff file it may have to read a lot of the stream before it finds the size (one of the files I tested, saved by Apple's Preview, had this data at the end of the file).



来源:https://stackoverflow.com/questions/15645174/is-there-a-equivalent-of-androids-bitmapfactory-options-isdecodebounds-for-tiff

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!