/*
* Copyright 2013, Morten Nobel-Joergensen
*
* License: The BSD 3-Clause License
* http://opensource.org/licenses/BSD-3-Clause
*/
package com.mortennobel.imagescaling;
import java.awt.image.BufferedImage;
/**
* The idea of this class is to provide fast (and inaccurate) rescaling method
* suitable for creating thumbnails.
*
* Note that the algorithm assumes that the source image is significant larger
* than the destination image
*/
public class ThumbnailRescaleOp extends AdvancedResizeOp {
public static enum Sampling {
S_1SAMPLE(new float[][]{{0.5f,0.5f}}),
S_2X2_RGSS(new float[][]{
{0.6f,0.2f},
{0.2f,0.4f},
{0.8f,0.6f},
{0.4f,0.8f},
}),
S_8ROCKS(new float[][]{
{0/6f,2/6f},
{2/6f,1/6f},
{4/6f,0/6f},
{5/6f,2/6f},
{6/6f,4/6f},
{4/6f,5/6f},
{2/6f,6/6f},
{1/6f,4/6f},
})
;
final float[][] points;
final int rightshift;
Sampling(float[][] points) {
this.points = points;
rightshift = Integer.numberOfTrailingZeros(points.length);
}
}
private Sampling sampling = Sampling.S_8ROCKS;
public ThumbnailRescaleOp(int destWidth, int destHeight) {
this(DimensionConstrain.createAbsolutionDimension(destWidth, destHeight));
}
public ThumbnailRescaleOp(DimensionConstrain dimensionConstrain) {
super(dimensionConstrain);
}
protected BufferedImage doFilter(BufferedImage src, BufferedImage dest, int dstWidth, int dstHeight) {
int numberOfChannels = ImageUtils.nrChannels(src);
BufferedImage out;
if (dest!=null && dstWidth==dest.getWidth() && dstHeight==dest.getHeight()){
out = dest;
}else{
out = new BufferedImage(dstWidth, dstHeight, numberOfChannels==4?BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
}
float scaleX = src.getWidth()/(float)dstWidth;
float scaleY = src.getHeight()/(float)dstHeight;
float[][] scaledSampling = new float[sampling.points.length][2];
for (int i=0;i<sampling.points.length;i++){
float[] point=sampling.points[i];
final float ROUNDING_ERROR_MARGIN = 0.0001f;
scaledSampling[i][0] = point[0]*scaleX+ROUNDING_ERROR_MARGIN;
scaledSampling[i][1] = point[1]*scaleY+ROUNDING_ERROR_MARGIN;
}
int maxSrcX = src.getWidth()-1;
int maxSrcY = src.getHeight()-1;
float srcY = 0;
for (int dstY=0;dstY<dstHeight;dstY++,srcY+=scaleY){
float srcX = 0;
for (int dstX=0;dstX<dstWidth;dstX++,srcX+=scaleX){
int r = 0, g = 0, b = 0, a = 0;
for (float[] point:scaledSampling){
int x = (int) (srcX+point[0]);
int y = (int) (srcY+point[1]);
x = Math.max(0,Math.min(x, maxSrcX));
y = Math.max(0,Math.min(y, maxSrcY));
int rgb = src.getRGB(x,y);
b += rgb&0xff;
rgb = rgb>>>8;
g += rgb&0xff;
rgb = rgb>>>8;
r += rgb&0xff;
rgb = rgb>>>8;
a += rgb&0xff;
}
r = r>>sampling.rightshift;
g = g>>sampling.rightshift;
b = b>>sampling.rightshift;
a = a>>sampling.rightshift;
int rgb = (a<<24)+(r<<16)+(g<<8)+b;
out.setRGB(dstX, dstY, rgb);
}
}
return out;
}
public void setSampling(Sampling sampling) {
this.sampling = sampling;
}
}