blob: b021d2f06285d945fee58df293e036efa2050684 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.imaging.formats.rgbe;
import java.io.IOException;
import java.io.InputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.imaging.ImageReadException;
import org.apache.commons.imaging.common.BinaryConstants;
import org.apache.commons.imaging.common.BinaryFileFunctions;
import org.apache.commons.imaging.common.BinaryInputStream;
import org.apache.commons.imaging.common.IImageMetadata;
import org.apache.commons.imaging.common.ImageMetadata;
import org.apache.commons.imaging.common.bytesource.ByteSource;
import org.apache.commons.imaging.util.Debug;
class RgbeInfo extends BinaryFileFunctions {
private static final Pattern RESOLUTION_STRING = Pattern.compile( "-Y (\\d+) \\+X (\\d+)" );
private final BinaryInputStream in;
private ImageMetadata metadata;
private int width = -1;
private int height = -1;
private static final byte[] TWO_TWO = new byte[]{ 0x2, 0x2 };
RgbeInfo( ByteSource byteSource ) throws IOException {
this.in = new BinaryInputStream( byteSource.getInputStream(), BinaryConstants.BYTE_ORDER_BIG_ENDIAN );
}
IImageMetadata getMetadata() throws IOException, ImageReadException {
if ( null == metadata ) {
readMetadata();
}
return metadata;
}
int getWidth() throws IOException, ImageReadException {
if ( -1 == width ) {
readDimensions();
}
return width;
}
int getHeight() throws IOException, ImageReadException {
if ( -1 == height ) {
readDimensions();
}
return height;
}
void close() {
try {
in.close();
} catch( IOException e ) {
Debug.debug( e );
}
}
private void readDimensions() throws IOException, ImageReadException {
getMetadata(); // Ensure we've read past this
InfoHeaderReader reader = new InfoHeaderReader( in );
String resolution = reader.readLine();
Matcher matcher = RESOLUTION_STRING.matcher( resolution );
if ( !matcher.matches() ) {
throw new ImageReadException(
"Invalid HDR resolution string. Only \"-Y N +X M\" is supported. Found \"" + resolution + "\"" );
}
height = Integer.parseInt( matcher.group( 1 ) );
width = Integer.parseInt( matcher.group( 2 ) );
}
private void readMetadata() throws IOException, ImageReadException {
in.readAndVerifyBytes( RgbeConstants.HEADER, "Not a valid HDR: Incorrect Header" );
InfoHeaderReader reader = new InfoHeaderReader( in );
if ( reader.readLine().length() != 0 ) {
throw new ImageReadException( "Not a valid HDR: Incorrect Header" );
}
metadata = new ImageMetadata();
String info = reader.readLine();
while ( info.length() != 0 ) {
int equals = info.indexOf( "=" );
if ( equals > 0 ) {
String variable = info.substring( 0, equals );
String value = info.substring( equals + 1 );
if ( "FORMAT".equals( value ) ) {
if ( !"32-bit_rle_rgbe".equals( value ) ) {
throw new ImageReadException(
"Only 32-bit_rle_rgbe images are supported, trying to read " + value );
}
}
metadata.add( variable, value );
} else {
metadata.add( "<command>", info );
}
info = reader.readLine();
}
}
public float[][] getPixelData() throws IOException, ImageReadException {
// Read into local variables to ensure that we have seeked into the file far enough
int height = getHeight();
int width = getWidth();
if ( width >= 32768 ) {
throw new ImageReadException( "Scan lines must be less than 32768 bytes long" );
}
byte[] scanLineBytes = convertShortToByteArray( width, BinaryConstants.BYTE_ORDER_BIG_ENDIAN );
byte[] rgbe = new byte[width * 4];
float[][] out = new float[3][width * height];
for ( int i = 0; i < height; i++ ) {
in.readAndVerifyBytes( TWO_TWO, "Scan line " + i + " expected to start with 0x2 0x2" );
in.readAndVerifyBytes( scanLineBytes, "Scan line " + i + " length expected" );
decompress( in, rgbe );
for ( int channel = 0; channel < 3; channel++ ) {
int channelOffset = channel * width;
int eOffset = 3 * width;
for ( int p = 0; p < width; p++ ) {
int mantissa = rgbe[p + eOffset] & 0xff;
int pos = p + i * width;
if ( 0 == mantissa ) {
out[channel][pos] = 0;
} else {
float mult = (float) Math.pow( 2, mantissa - ( 128 + 8 ) );
out[channel][pos] = ( ( rgbe[p + channelOffset] & 0xff ) + 0.5f ) * mult;
}
}
}
}
return out;
}
private static void decompress( InputStream in, byte[] out ) throws IOException {
int position = 0;
int total = out.length;
while ( position < total ) {
int n = in.read();
if ( n > 128 ) {
int value = in.read();
for ( int i = 0; i < ( n & 0x7f ); i++ ) {
out[position++] = (byte) value;
}
} else {
for ( int i = 0; i < n; i++ ) {
out[position++] = (byte) in.read();
}
}
}
}
}