/* Copyright (C) 2005-2011 Fabio Riccardi */
package com.lightcrafts.image.metadata.makernotes;
import java.awt.image.RenderedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import com.lightcrafts.image.BadImageFileException;
import com.lightcrafts.image.ImageInfo;
import com.lightcrafts.image.metadata.*;
import com.lightcrafts.image.metadata.providers.*;
import com.lightcrafts.image.metadata.values.*;
import com.lightcrafts.image.types.JPEGImageType;
import com.lightcrafts.utils.bytebuffer.ArrayByteBuffer;
import com.lightcrafts.utils.NumberUtil;
import com.lightcrafts.utils.TextUtil;
import static com.lightcrafts.image.metadata.EXIFConstants.*;
import static com.lightcrafts.image.metadata.ImageMetaType.*;
import static com.lightcrafts.image.metadata.makernotes.MinoltaTags.*;
import static com.lightcrafts.image.types.JPEGConstants.*;
/**
* A <code>MinoltaDirectory</code> is-an {@link ImageMetadataDirectory} for
* holding Minolta-specific maker-note metadata.
*
* @author Paul J. Lucas [paul@lightcrafts.com]
*/
@SuppressWarnings({"CloneableClassWithoutClone"})
public final class MinoltaDirectory extends MakerNotesDirectory
implements FocalLengthProvider, LensProvider, ShutterSpeedProvider,
PreviewImageProvider {
////////// public /////////////////////////////////////////////////////////
/**
* {@inheritDoc}
*/
public float getFocalLength() {
final ImageMetaValue value = getValue( MINOLTA_CS_FOCAL_LENGTH );
return value != null ? value.getFloatValue() : 0;
}
/**
* {@inheritDoc}
*/
public String getLens() {
return hasTagValueLabelFor( getValue( MINOLTA_LENS_ID ) );
}
/**
* Gets the name of this directory.
*
* @return Always returns "Minolta".
*/
public String getName() {
return "Minolta";
}
/**
* {@inheritDoc}
*/
public RenderedImage getPreviewImage( ImageInfo imageInfo, int maxWidth,
int maxHeight )
throws BadImageFileException, IOException
{
//
// See the comment in MRWImageType.getPreviewImage().
//
final ImageMetaValue offsetValue =
getValue( MINOLTA_PREVIEW_IMAGE_START );
final ImageMetaValue lengthValue =
getValue( MINOLTA_PREVIEW_IMAGE_LENGTH );
if ( offsetValue == null || lengthValue == null )
return null;
final int offset = offsetValue.getIntValue();
final int length = lengthValue.getIntValue();
if ( offset <= 0 || length <= 0 )
return null;
final byte[] buf = imageInfo.getByteBuffer().getBytes(
offset
+ 1 // JPEG_MARKER_BYTE
+ 1 // JPEG_SOI_MARKER
+ 1 // JPEG_MARKER_BYTE
+ 1 // JPEG_APP1_MARKER
+ 2 // APP1 length
+ EXIF_HEADER_START_SIZE,
length
);
//
// Patch the JPEG preview image.
//
if ( buf[1] == JPEG_SOI_MARKER && buf[2] == JPEG_MARKER_BYTE )
buf[0] = JPEG_MARKER_BYTE;
// TODO: verify that this is actually an sRGB image, what about Adobe RGB shooting, etc.?
return JPEGImageType.getImageFromBuffer(
new ArrayByteBuffer( buf ), new LongMetaValue( 0 ), 0, lengthValue,
maxWidth, maxHeight
);
}
/**
* {@inheritDoc}
*/
public float getShutterSpeed() {
final ImageMetaValue value = getValue( MINOLTA_CS_SHUTTER_SPEED );
return value != null ? value.getFloatValue() : 0;
}
/**
* {@inheritDoc}
*/
public ImageMetaTagInfo getTagInfoFor( Integer id ) {
return m_tagsByID.get( id );
}
/**
* {@inheritDoc}
*/
public ImageMetaTagInfo getTagInfoFor( String name ) {
return m_tagsByName.get( name );
}
/**
* Puts a key/value pair into this directory. For a Minolta tag that has
* subfields, expands the values into multiple key/value pairs.
*
* @param tagID The metadata tag ID (the key).
* @param value The value to put.
* @see #valueToString(ImageMetaValue)
*/
public void putValue( Integer tagID, ImageMetaValue value ) {
switch ( tagID ) {
case MINOLTA_CAMERA_SETTINGS: {
final String model = getOwningMetadata().getCameraMake( true );
if ( model != null && model.contains( "DIMAGE X31" ) ) {
//
// These camera settings aren't for the DiIMAGE X31.
//
break;
}
// no break;
}
case MINOLTA_CAMERA_SETTINGS_OLD:
explodeSubfields( tagID, 0, value, true );
break;
case MINOLTA_COMPRESSED_IMAGE_SIZE:
if ( value.getLongValue() == 0 )
return;
break;
case MINOLTA_CS_APERTURE:
case MINOLTA_CS_MAX_APERTURE: {
final int n = value.getIntValue();
value = new FloatMetaValue(
(float)NumberUtil.tenths( Math.pow( 2, n / 16.0 - 0.5 ) )
);
break;
}
case MINOLTA_CS_BRIGHTNESS: {
final int n = value.getIntValue();
value = new FloatMetaValue(
(float)NumberUtil.tenths( n / 8.0 - 6 )
);
break;
}
case MINOLTA_CS_COLOR_BALANCE_BLUE:
case MINOLTA_CS_COLOR_BALANCE_GREEN:
case MINOLTA_CS_COLOR_BALANCE_RED:
case MINOLTA_CS_FOCAL_LENGTH: {
final int n = value.getIntValue();
value = new FloatMetaValue(
(float)NumberUtil.tenths( n / 256.0 )
);
break;
}
case MINOLTA_CS_COLOR_PROFILE: {
final String model = getOwningMetadata().getCameraMake( true );
if ( model != null && !model.contains( "DIMAGE 7HI" ) ) {
//
// This is for the DiMAGE 7Hi only.
//
break;
}
// no break;
}
case MINOLTA_CS_DATE: {
final long n = value.getLongValue();
//
// Even though most Minolta maker note data is big-endian, the
// date is little-endian.
//
final StringBuilder sb = new StringBuilder();
sb.append( (n & 0xFFFF0000) >> 16 );
sb.append( TextUtil.zeroPad( (int)((n & 0x0000FF00) >> 8), 10, 2 ) );
sb.append( TextUtil.zeroPad( (int)(n & 0x000000FF), 10, 2 ) );
try {
value = new DateMetaValue( sb.toString() );
}
catch ( IllegalArgumentException e ) {
return;
}
break;
}
case MINOLTA_CS_EXPOSURE_COMPENSATION: {
final int n = value.getIntValue();
value = new FloatMetaValue(
(float)NumberUtil.tenths( n / 3.0 - 2 )
);
break;
}
case MINOLTA_CS_FLASH_EXPOSURE_COMP: {
final int n = value.getIntValue();
value = new FloatMetaValue(
(float)NumberUtil.tenths( (n - 6) / 3.0 )
);
break;
}
case MINOLTA_CS_ISO: {
final int n = value.getIntValue();
value = new FloatMetaValue(
(float)NumberUtil.tenths(
Math.pow( 2, (n / 8.0 - 1) * 3.125 )
)
);
break;
}
case MINOLTA_CS_SHUTTER_SPEED: {
final int n = value.getIntValue();
value = new FloatMetaValue(
(float)Math.pow( 2, (48 - n) / 8.0 )
);
break;
}
case MINOLTA_CS_TIME: {
final long n = value.getLongValue();
//
// Even though most Minolta maker note data is big-endian, the
// time is little-endian.
//
final StringBuilder sb = new StringBuilder();
sb.append( TextUtil.zeroPad( (int)((n & 0x00FF0000) >> 16), 10, 2 ) );
sb.append( ':' );
sb.append( TextUtil.zeroPad( (int)((n & 0x0000FF00) >> 8), 10, 2 ) );
sb.append( ':' );
sb.append( TextUtil.zeroPad( (int)(n & 0x000000FF), 10, 2 ) );
value = new StringMetaValue( sb.toString() );
break;
}
case MINOLTA_IMAGE_SIZE: {
//
// This tag (0x0103) is Quality on the DiMAGE A2 or 7Hi but
// ImageSize for others (except the A200). Since both quality
// and image size information are obtainable elsewhere, just
// ignore this tag because it's more trouble than it's worth.
//
return;
}
}
super.putValue( tagID, value );
}
/**
* {@inheritDoc}
*/
public String valueToString( ImageMetaValue value ) {
switch ( value.getOwningTagID() ) {
case MINOLTA_COMPRESSED_IMAGE_SIZE:
return TextUtil.quantify( value.getLongValue() );
case MINOLTA_CS_FOCAL_LENGTH:
return value.getStringValue() + "mm"; // TODO: localize "mm"
case MINOLTA_CS_FOCUS_DISTANCE: {
final int n = value.getIntValue();
return n > 0 ? TextUtil.tenths( n / 1000.0 ) : "infinite";
}
case MINOLTA_CS_SHUTTER_SPEED:
return MetadataUtil.shutterSpeedString( value.getFloatValue() );
case MINOLTA_LENS_ID: {
final String label = hasTagValueLabelFor( MINOLTA_LENS_ID );
return label != null ? label : "unknown"; // TODO: localize
}
default:
return super.valueToString( value );
}
}
////////// protected //////////////////////////////////////////////////////
/**
* Get the {@link ResourceBundle} to use for tags.
*
* @return Returns said {@link ResourceBundle}.
*/
protected ResourceBundle getTagLabelBundle() {
return m_tagBundle;
}
/**
* {@inheritDoc}
*/
protected Class<? extends ImageMetaTags> getTagsInterface() {
return MinoltaTags.class;
}
////////// private ////////////////////////////////////////////////////////
/**
* Add the tag mappings.
*
* @param id The tag's ID.
* @param name The tag's name.
* @param type The tag's {@link ImageMetaType}.
*/
private static void add( int id, String name, ImageMetaType type ) {
final ImageMetaTagInfo tagInfo =
new ImageMetaTagInfo( id, name, type, false );
m_tagsByID.put( id, tagInfo );
m_tagsByName.put( name, tagInfo );
}
/**
* This is where the actual labels for the tags are.
*/
private static final ResourceBundle m_tagBundle = ResourceBundle.getBundle(
"com.lightcrafts.image.metadata.makernotes.MinoltaTags"
);
/**
* A mapping of tags by ID.
*/
private static final Map<Integer,ImageMetaTagInfo> m_tagsByID =
new HashMap<Integer,ImageMetaTagInfo>();
/**
* A mapping of tags by name.
*/
private static final Map<String,ImageMetaTagInfo> m_tagsByName =
new HashMap<String,ImageMetaTagInfo>();
static {
add( MINOLTA_CAMERA_SETTINGS, "CameraSettings", META_UNDEFINED );
add( MINOLTA_CAMERA_SETTINGS_OLD, "CameraSettingsOld", META_UNDEFINED );
add( MINOLTA_COLOR_MODE, "ColorMode", META_ULONG );
add( MINOLTA_COMPRESSED_IMAGE_SIZE, "CompressedImageSize", META_ULONG );
add( MINOLTA_CS_APERTURE, "CSAperture", META_UNKNOWN );
add( MINOLTA_CS_BRACKET_STEP, "CSBracketStep", META_UNKNOWN );
add( MINOLTA_CS_BRIGHTNESS, "CSBrightness", META_UNKNOWN );
add( MINOLTA_CS_BW_FILTER, "CSBWFilter", META_UNKNOWN );
add( MINOLTA_CS_COLOR_BALANCE_BLUE, "CSColorBalanceBlue", META_UNKNOWN );
add( MINOLTA_CS_COLOR_BALANCE_GREEN, "CSColorBalanceGreen", META_UNKNOWN );
add( MINOLTA_CS_COLOR_BALANCE_RED, "CSColorBalanceRed", META_UNKNOWN );
add( MINOLTA_CS_COLOR_FILTER, "CSColorFilter", META_UNKNOWN );
add( MINOLTA_CS_COLOR_MODE, "CSColorMode", META_UNKNOWN );
add( MINOLTA_CS_COLOR_PROFILE, "CSColorProfile", META_UNKNOWN );
add( MINOLTA_CS_CONTRAST, "CSContrast", META_UNKNOWN );
add( MINOLTA_CS_DATE, "CSDate", META_DATE );
add( MINOLTA_CS_DEC_POSITION, "CSDecPosition", META_UNKNOWN );
add( MINOLTA_CS_DIGITAL_ZOOM, "CSDigitalZoom", META_UNKNOWN );
add( MINOLTA_CS_DRIVE_MODE, "CSDriveMode", META_UNKNOWN );
add( MINOLTA_CS_EXPOSURE_COMPENSATION, "CSExposureCompensation", META_UNKNOWN );
add( MINOLTA_CS_EXPOSURE_MODE, "CSExposureMode", META_UNKNOWN );
add( MINOLTA_CS_FILE_NUMBER_MEMORY, "CSFileNumberMemory", META_UNKNOWN );
add( MINOLTA_CS_FLASH_EXPOSURE_COMP, "CSFlashExposureComp", META_UNKNOWN );
add( MINOLTA_CS_FLASH_FIRED, "CSFlashFired", META_UNKNOWN );
add( MINOLTA_CS_FLASH_MODE, "CSFlashMode", META_UNKNOWN );
add( MINOLTA_CS_FOCAL_LENGTH, "CSFocalLength", META_UNKNOWN );
add( MINOLTA_CS_FOCUS_AREA, "CSFocusArea", META_UNKNOWN );
add( MINOLTA_CS_FOCUS_DISTANCE, "CSFocusDistance", META_UNKNOWN );
add( MINOLTA_CS_FOCUS_MODE, "CSFocusMode", META_UNKNOWN );
add( MINOLTA_CS_FOLDER_NAME, "CSFolderName", META_UNKNOWN );
add( MINOLTA_CS_IMAGE_SIZE, "CSImageSize", META_UNKNOWN );
add( MINOLTA_CS_INTERNAL_FLASH, "CSInternalFlash", META_UNKNOWN );
add( MINOLTA_CS_INTERVAL_LENGTH, "CSIntervalLength", META_UNKNOWN );
add( MINOLTA_CS_INTERVAL_MODE, "CSIntervalMode", META_UNKNOWN );
add( MINOLTA_CS_INTERVAL_NUMBER, "CSIntervalNumber", META_UNKNOWN );
add( MINOLTA_CS_ISO, "CSISO", META_UNKNOWN );
add( MINOLTA_CS_ISO_SETTING, "CSISOSetting", META_UNKNOWN );
add( MINOLTA_CS_LAST_FILE_NUMBER, "CSLastFileNumber", META_UNKNOWN );
add( MINOLTA_CS_MACRO_MODE, "CSMacroMode", META_UNKNOWN );
add( MINOLTA_CS_MAX_APERTURE, "CSMaxAperture", META_UNKNOWN );
add( MINOLTA_CS_METERING_MODE, "CSMeteringMode", META_UNKNOWN );
add( MINOLTA_CS_MODEL, "CSModel", META_UNKNOWN );
add( MINOLTA_CS_QUALITY, "CSQuality", META_UNKNOWN );
add( MINOLTA_CS_SATURATION, "CSSaturation", META_UNKNOWN );
add( MINOLTA_CS_SHARPNESS, "CSSharpness", META_UNKNOWN );
add( MINOLTA_CS_SHUTTER_SPEED, "CSShutterSpeed", META_UNKNOWN );
add( MINOLTA_CS_SPOT_FOCUS_X, "CSSpotFocusX", META_UNKNOWN );
add( MINOLTA_CS_SPOT_FOCUS_Y, "CSSpotFocusY", META_UNKNOWN );
add( MINOLTA_CS_SUBJECT_PROGRAM, "CSSubjectProgram", META_UNKNOWN );
add( MINOLTA_CS_TIME, "CSTime", META_UNKNOWN );
add( MINOLTA_CS_WHITE_BALANCE, "CSWhiteBalance", META_UNKNOWN );
add( MINOLTA_CS_WIDE_FOCUS_ZONE, "CSWideFocusZone", META_UNKNOWN );
add( MINOLTA_IMAGE_SIZE, "ImageSize", META_ULONG );
add( MINOLTA_IMAGE_STABILIZATION, "ImageStabilization", META_ULONG );
add( MINOLTA_LENS_ID, "LensID", META_ULONG );
add( MINOLTA_MAKER_NOTES_VERSION, "MakerNotesVersion", META_UNKNOWN );
add( MINOLTA_PREVIEW_IMAGE_LENGTH, "PreviewImageLength", META_ULONG );
add( MINOLTA_PREVIEW_IMAGE_START, "PreviewImageStart", META_ULONG );
add( MINOLTA_QUALITY, "Quality", META_ULONG );
add( MINOLTA_QUALITY_2, "Quality2", META_ULONG );
add( MINOLTA_SCENE_MODE, "SceneMode", META_ULONG );
add( MINOLTA_ZONE_MATCHING, "ZoneMatching", META_ULONG );
}
}
/* vim:set et sw=4 ts=4: */