@param createpreview, if <code>true</code> create also a preview image.
*/
protected void createThumbnail( VolumeBase volume, boolean createPreview ) {
log.debug( "Creating thumbnail for " + uid );
ODMGXAWrapper txw = new ODMGXAWrapper();
txw.lock( this, Transaction.WRITE );
// Maximum size of the thumbnail
int maxThumbWidth = 100;
int maxThumbHeight = 100;
checkCropBounds();
/*
Determine the minimum size for the instance used for thumbnail creation
to get decent image quality.
The cropped portion of the image must be roughly the same
resolution as the intended thumbnail.
*/
double cropWidth = cropMaxX - cropMinX;
cropWidth = ( cropWidth > 0.000001 ) ? cropWidth : 1.0;
double cropHeight = cropMaxY - cropMinY;
cropHeight = ( cropHeight > 0.000001 ) ? cropHeight : 1.0;
int minInstanceWidth = (int)(((double)maxThumbWidth)/cropWidth);
int minInstanceHeight = (int)(((double)maxThumbHeight)/cropHeight);
int minInstanceSide = Math.max( minInstanceWidth, minInstanceHeight );
// Find the original image to use as a staring point
EnumSet<ImageOperations> allowedOps = EnumSet.allOf( ImageOperations.class );
if ( createPreview ) {
// We need to create also the preview image, so we need original.
allowedOps = EnumSet.noneOf( ImageOperations.class );
minInstanceWidth = 1024;
minInstanceHeight = 1024;
}
ImageInstance original = this.getPreferredInstance( EnumSet.noneOf( ImageOperations.class ),
allowedOps, minInstanceWidth, minInstanceHeight );
if ( original == null ) {
// If there are no uncorrupted instances, no thumbnail can be created
log.warn( "Error - no original image was found!!!" );
txw.commit();
return;
}
txw.lock( original, Transaction.READ );
log.debug( "Found original, reading it..." );
/*
We try to ensure that the thumbnail is actually from the original image
by comparing aspect ratio of it to original. This is not a perfect check
but it will usually catch the most typical errors (like having a the original
rotated by RAW conversion SW but still the original EXIF thumbnail.
*/
double origAspect = this.getAspect(
original.getWidth(),
original.getHeight(), 1.0 );
double aspectAccuracy = 0.01;
// First, check if there is a thumbnail in image header
RenderedImage origImage = null;
// Read the image
RenderedImage thumbImage = null;
RenderedImage previewImage = null;
try {
File imageFile = original.getImageFile();
PhotovaultImageFactory imgFactory = new PhotovaultImageFactory();
PhotovaultImage img = imgFactory.create( imageFile, false, false );
if ( channelMap != null ) {
img.setColorAdjustment( channelMap );
}
if ( img instanceof RawImage ) {
RawImage ri = (RawImage) img;
ri.setRawSettings( rawSettings );
}
if ( createPreview ) {
// Calculate preview image size
int previewWidth = img.getWidth();
int previewHeight = img.getHeight();
while ( previewWidth > 2048 || previewHeight > 2048 ) {
previewWidth >>= 1;
previewHeight >>=1;
}
previewImage = img.getRenderedImage( previewWidth, previewHeight, false );
}
img.setCropBounds( this.getCropBounds() );
img.setRotation( prefRotation - original.getRotated() );
thumbImage = img.getRenderedImage( maxThumbWidth, maxThumbHeight, true );
} catch ( Exception e ) {
log.warn( "Error reading image: " + e.getMessage() );
// TODO: If we aborted here due to image writing problem we would have
// problems later with non-existing transaction. We should really
// rethink the error handling logic in the whole function. Anyway, we
// haven't changed anything yet so we can safely commit the tx.
txw.commit();
return;
}
log.debug( "Done, finding name" );
// Find where to store the file in the target volume
File thumbnailFile = volume.getInstanceName( this, "jpg" );
log.debug( "name = " + thumbnailFile.getName() );
try {
saveInstance( thumbnailFile, thumbImage );
if ( thumbImage instanceof PlanarImage ) {
((PlanarImage)thumbImage).dispose();
System.gc();
}
} catch (PhotovaultException ex) {
log.error( "error writing thumbnail for " + original.getImageFile().getAbsolutePath() +
": " + ex.getMessage() );
// TODO: If we abort here due to image writing problem we will have
// problems later with non-existing transaction. We should really
// rethink the error handling login in the whole function. Anyway, we
// haven't changed anything yet so we can safely commit the tx.
txw.commit();
return;
}
// add the created instance to this persistent object
ImageInstance thumbInstance = addInstance( volume, thumbnailFile,
ImageInstance.INSTANCE_TYPE_THUMBNAIL );
thumbInstance.setRotated( prefRotation -original.getRotated() );
thumbInstance.setCropBounds( getCropBounds() );
thumbInstance.setColorChannelMapping( channelMap );
thumbInstance.setRawSettings( rawSettings );
log.debug( "Loading thumbnail..." );
thumbnail = Thumbnail.createThumbnail( this, thumbnailFile );
oldThumbnail = null;
log.debug( "Thumbnail loaded" );
if ( createPreview ) {
File previewFile = volume.getInstanceName( this, "jpg" );
try {
saveInstance( previewFile, previewImage );
if ( previewImage instanceof PlanarImage ) {
((PlanarImage)previewImage).dispose();
System.gc();
}
} catch (PhotovaultException ex) {
log.error( "error writing preview for " + original.getImageFile().getAbsolutePath() +
": " + ex.getMessage() );
// TODO: If we abort here due to image writing problem we will have
// problems later with non-existing transaction. We should really
// rethink the error handling login in the whole function. Anyway, we
// haven't changed anything yet so we can safely commit the tx.
txw.commit();
return;
}
ImageInstance previewInstance = addInstance( volume, previewFile,
ImageInstance.INSTANCE_TYPE_MODIFIED );
previewInstance.setColorChannelMapping( channelMap );
previewInstance.setRawSettings( rawSettings );
}
txw.commit();
}