/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.ebixio.virtmus.imgsrc;
import com.ebixio.util.Log;
import com.ebixio.virtmus.MusicPage;
import com.ebixio.virtmus.Utils;
import com.ebixio.virtmus.VirtMusKernel;
import com.ebixio.virtmus.options.Options;
import com.sun.media.jai.codec.FileSeekableStream;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.image.renderable.ParameterBlock;
import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;
import javax.media.jai.*;
import org.apache.batik.ext.awt.RenderingHintsKeyExt;
import org.openide.util.Exceptions;
/**
*
* @author Gabriel Burca
*/
public class GenericImg extends ImgSrc {
public GenericImg(File sourceFile) {
super(sourceFile);
}
@Override
public ImgType getImgType() {
String name = sourceFile.getName().toLowerCase();
if (name.endsWith(".png")) {
return ImgType.PNG;
} else if (name.endsWith("jpg") || name.endsWith("jpeg")) {
return ImgType.JPG;
} else {
return ImgType.OTHER;
}
}
void closeStream(FileSeekableStream stream) {
if (stream == null) {
return;
}
try {
stream.close();
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
@Override
public Dimension getDimension() {
if (dimension != null) {
return dimension;
} else {
FileSeekableStream stream = null;
try {
stream = new FileSeekableStream(sourceFile.toString());
} catch (IOException e) {
Log.log("MusicPage file: " + sourceFile.toString());
Log.log(e.toString());
closeStream(stream);
return new Dimension(1, 1);
}
// Create an operator to decode the image file
RenderedOp srcImg = JAI.create("stream", stream);
try {
dimension = srcImg.getBounds().getSize();
srcImg.dispose();
} catch (Exception e) {
Log.log("Bad file format: " + sourceFile.toString());
closeStream(stream);
return new Dimension(1, 1);
}
closeStream(stream);
return dimension;
}
}
@Override
public synchronized BufferedImage getImage(Dimension containerSize, Options.Rotation rotation, boolean fillSize, MusicPage page) {
RenderedOp srcImg, destImg;
Rectangle destSize;
// Acquiring the current Graphics Device and Graphics Configuration
GraphicsEnvironment graphEnv = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice graphDevice = graphEnv.getDefaultScreenDevice();
GraphicsConfiguration graphicConf = graphDevice.getDefaultConfiguration();
System.gc();
BufferedImage result = graphicConf.createCompatibleImage(containerSize.width, containerSize.height, Transparency.OPAQUE);
//BufferedImage result = new BufferedImage(containerSize.width, containerSize.height, BufferedImage.TYPE_INT_ARGB_PRE);
// TYPE_4BYTE_ABGR_PRE (instead of TYPE_INT_ARGB_PRE) REQUIRED for OpenGL
//BufferedImage result = new BufferedImage(containerSize.width, containerSize.height, BufferedImage.TYPE_4BYTE_ABGR_PRE);
Graphics2D g = result.createGraphics();
/** If a BUFFERED_IMAGE hint is not provided, the batik code issues the following warning:
* "Graphics2D from BufferedImage lacks BUFFERED_IMAGE hint"
*
* See: http://mail-archives.apache.org/mod_mbox/xmlgraphics-batik-dev/200603.mbox/%3C20060309110529.1B7C96ACA9@ajax%3E
* See: org.apache.batik.ext.awt.image.GraphicsUtil getDestination(Graphics2D g2d)
*/
RenderingHints renderingHints = new RenderingHints(
RenderingHintsKeyExt.KEY_BUFFERED_IMAGE,
new WeakReference<BufferedImage>(result));
g.addRenderingHints(renderingHints);
g.setColor(Color.BLACK);
g.fillRect(0, 0, result.getWidth(), result.getHeight());
g.setColor(Color.WHITE);
RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
RenderingHints interpHints = new RenderingHints(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
AffineTransform origXform = g.getTransform();
g.setTransform(rotation.getTransform(containerSize.getSize()));
switch (rotation) {
case Clockwise_90:
case Clockwise_270:
g.setRenderingHints(interpHints);
// Rotate the container size if the image is rotated sideways
destSize = new Rectangle(containerSize.height, containerSize.width);
break;
default:
destSize = new Rectangle(containerSize);
break;
}
FileSeekableStream stream;
try {
stream = new FileSeekableStream(sourceFile.toString());
} catch (IOException e) {
return errText(result, g, "Bad file.", destSize);
}
// Create an operator to decode the image file
srcImg = JAI.create("stream", stream);
float scale;
try {
//this.dimension = srcImg.getBounds().getSize();
scale = (float)Utils.scaleProportional(destSize, srcImg.getBounds());
} catch (Exception e) {
srcImg.dispose();
closeStream(stream);
return errText(result, g, "Bad file format.", destSize);
}
/* When using the "scale" operator to reduce the size of an image, the result is very poor,
* even with bicubic interpolation. SubsampleAverage gives much better results, but can
* only scale down an image.
*/
if (scale == 1.0) {
destImg = srcImg;
} else if (scale < 1.0 && Math.min(destSize.height, destSize.width) < 600) {
// For the SubsampleAverage operator, scale must be (0,1]
/** SubsampleAverage sometimes creates black horizontal lines which
* look almost like staff lines. This only happens at certain scale
* factors. We will therefore only use this for icons and thumbnails.
* Anything larger than that will be scaled below.
*/
destImg = JAI.create("SubsampleAverage", srcImg, (double)scale, (double)scale, qualityHints);
} else {
if (scale < 1.0) {
// We apply a mild low-pass filter first. See:
// http://archives.java.sun.com/cgi-bin/wa?A2=ind0311&L=jai-interest&P=15036
// http://www.leptonica.com/scaling.html
KernelJAI k;
k = VirtMusKernel.getKernel(1, 1, 6);
//k = VirtMusKernel.getKernel(0, 0, 1); // Identity kernel
destImg = JAI.create("Convolve", srcImg, k);
srcImg = destImg;
} else { // scale > 1.0
scale = Math.min(scale, 2.0F); // Don't zoom in more than 2x
}
// Create a bicubic interpolation object to be used with the "scale" operator
Interpolation interp = Interpolation.getInstance(Interpolation.INTERP_BICUBIC);
ParameterBlock params = new ParameterBlock();
params.addSource(srcImg);
params.add(scale); // x scale factor
params.add(scale); // y scale factor
params.add(0.0F); // x translation
params.add(0.0F); // y translation
params.add(interp); // interpolation method
destImg = JAI.create("scale", params);
}
srcImg = destImg;
Point destPt;
if (fillSize) {
destPt = Utils.centerItem(destSize, srcImg.getBounds());
} else {
destPt = new Point(0, 0);
}
AffineTransform newXform = g.getTransform();
newXform.concatenate(AffineTransform.getTranslateInstance(destPt.x, destPt.y));
g.setTransform(newXform);
// Image is already scaled. Draw it before applying the scaling transform.
g.drawImage(srcImg.getAsBufferedImage(), 0, 0, null);
// The annotations need to be scaled properly before being drawn.
newXform.concatenate(AffineTransform.getScaleInstance(scale, scale));
g.setTransform(newXform);
page.paintAnnotations(g);
g.setTransform(origXform);
Dimension dim = srcImg.getBounds().getSize();
srcImg.dispose();
closeStream(stream);
g.dispose();
if (fillSize) {
return result;
} else {
return result.getSubimage(0, 0, dim.width, dim.height);
}
}
@Override
public PlanarImage getFullImg() {
if (sourceFile.exists() && sourceFile.canRead()) {
return JAI.create("fileload", sourceFile.toString());
} else {
return null;
}
}
@Override
public File createImageFile() {
return sourceFile;
}
@Override
public void destroyImageFile() {
// Nothing to do.
}
}