/*
* Copyright 2008-2011 the original author or authors.
*
* Licensed 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 com.nominanuda.jai;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import static java.awt.RenderingHints.KEY_INTERPOLATION;
import static java.awt.RenderingHints.VALUE_INTERPOLATION_BILINEAR;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.MemoryCacheImageOutputStream;
import com.nominanuda.code.Nullable;
import com.nominanuda.lang.Check;
import com.nominanuda.lang.Tuple2;
public class ImageAwtTransformer {
public Tuple2<BufferedImage, String> readImageAndFormat(InputStream is) throws IOException, IllegalArgumentException {
ImageInputStream iis = ImageIO.createImageInputStream(is);
Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
if (!readers.hasNext()) {
iis.close();
throw new IllegalArgumentException("unsupported image type");
}
ImageReader reader = readers.next();
String inputFormat = reader.getFormatName();
reader.setInput(iis, true, true);
BufferedImage src = reader.read(0);
reader.dispose();
iis.close();
return new Tuple2<BufferedImage, String>(src, inputFormat.toLowerCase());
}
public long transform(InputStream is, OutputStream os, @Nullable Integer targetWidth,
@Nullable Integer targetHeigth, @Nullable String outputFormat,
boolean allowDistort) throws IOException, IllegalArgumentException {
Tuple2<BufferedImage, String> biAndFmt = readImageAndFormat(is);
BufferedImage src = biAndFmt.get0();
String inputFormat = biAndFmt.get1();
if (outputFormat == null) {
outputFormat = inputFormat;
}
return transform(src, os, targetWidth, targetHeigth, outputFormat, allowDistort);
}
public long transform(InputStream is, OutputStream os, @Nullable String outputFormat, int[] g/*see clipCalc*/) throws IOException, IllegalArgumentException {
Tuple2<BufferedImage, String> biAndFmt = readImageAndFormat(is);
BufferedImage src = biAndFmt.get0();
String inputFormat = biAndFmt.get1();
if (outputFormat == null) {
outputFormat = inputFormat;
}
return transform(src, os, outputFormat, g);
}
public long transform(BufferedImage src, OutputStream os, @Nullable Integer _targetWidth,
@Nullable Integer targetHeigth, String outputFormat,
boolean allowDistort) throws IOException, IllegalArgumentException {
Integer targetWidth = Check.ifNull(_targetWidth, src.getWidth());
int[] g = clipCalc(src.getWidth(), src.getHeight(), targetWidth, targetHeigth, allowDistort);
return transform(src, os, outputFormat, g);
}
public long transform(BufferedImage src, OutputStream os, String outputFormat, int[] g/*see clipCalc*/) throws IOException {
BufferedImage bi = new BufferedImage(g[4], g[5], BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D) bi.getGraphics();
g2d.setRenderingHint(KEY_INTERPOLATION, VALUE_INTERPOLATION_BILINEAR);
g2d.drawImage(
src.getSubimage(g[0], g[1], g[2] - g[0], g[3] - g[1]),
0, 0, g[4], g[5], null);
g2d.dispose();
bi.flush();
MemoryCacheImageOutputStream ios = new MemoryCacheImageOutputStream(os);
try {
Check.illegalargument.assertTrue(
ImageIO.write(bi, outputFormat, ios), "could not perform conversion");
long len = ios.length();
return len;
} finally {
ios.close();
}
}
/**
*
* @param sw
* source canvas width
* @param sh
* source canvas height
* @param dw
* dest canvas width
* @param dh
* dest canvas height (nullable)
* @param allowDistort
* @return six integers that represent P1(0,1) P2(2,3) W(4) H(5) where P1
* and P2 are the src clipping points and W,H are the actual dest
* width/height
*/
protected int[] clipCalc(int sw, int sh, int dw, @Nullable Integer dh, boolean allowDistort/*, boolean allowExpand */) {
Check.notNull(dw);
boolean allowExpand = allowDistort;
if (dh == null) {// full
return sw < dw ? new int[] { 0, 0, sw, sh, sw, sh } : new int[] {
0, 0, sw, sh, dw, scale(sh, dw, sw) };
} else if (allowDistort && allowExpand) {
return new int[] { 0, 0, sw, sh, dw, dh };
} else {// clever scaling
if (dw > sw && dh > sh) {
return new int[] { 0, 0, sw, sh, sw, sh };
} else if (dh > sh) {
int margin = approx((double) (sw - dw) / 2);
return new int[] { 0 + margin, 0, sw - margin, sh, dw, sh };
} else if (dw > sw) {
int margin = approx((double) (sh - dh) / 2);
return new int[] { 0, 0 + margin, sw, sh - margin, sw, dh };
} else {
double xRatio = (double) sw / (double) dw;
double yRatio = (double) sh / (double) dh;
if (xRatio > yRatio) {
int margin = approx((double) (sw - scale(dw, sh, dh)) / 2);
return new int[] { 0 + margin, 0, sw - margin, sh, dw, dh };
} else {
int margin = approx((double) (sh - scale(dh, sw, dw)) / 2);
return new int[] { 0, 0 + margin, sw, sh - margin, dw, dh };
}
}
}
}
private int scale(int ori, int x, int y) {
double a = ori, b = x, c = y;
return approx(a * b / c);
}
private int approx(double d) {
return new Double(d).intValue();
}
}