/*
* Copyright 2015 Laszlo Balazs-Csiki
*
* This file is part of Pixelitor. Pixelitor is free software: you
* can redistribute it and/or modify it under the terms of the GNU
* General Public License, version 3 as published by the Free
* Software Foundation.
*
* Pixelitor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Pixelitor. If not, see <http://www.gnu.org/licenses/>.
*/
package pixelitor.filters.lookup;
import com.jhlabs.image.PixelUtils;
import pixelitor.utils.ImageUtils;
import java.awt.RenderingHints;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ColorModel;
import java.awt.image.DataBufferInt;
import java.awt.image.LookupOp;
import java.awt.image.ShortLookupTable;
/**
* Performs 4-5 times faster than java.awt.image.LookupOp
*/
public class FastLookupOp implements BufferedImageOp {
private final ShortLookupTable lookupTable;
public FastLookupOp(ShortLookupTable lookupTable) {
this.lookupTable = lookupTable;
}
@Override
public BufferedImage filter(BufferedImage src, BufferedImage dst) {
boolean packedInt = ImageUtils.hasPackedIntArray(src);
if (packedInt) {
boolean simple = !src.isAlphaPremultiplied();
DataBufferInt srcDataBuffer = (DataBufferInt) src.getRaster().getDataBuffer();
int[] srcData = srcDataBuffer.getData();
DataBufferInt destDataBuffer = (DataBufferInt) dst.getRaster().getDataBuffer();
int[] destData = destDataBuffer.getData();
int length = srcData.length;
assert length == destData.length;
short[][] table = lookupTable.getTable();
for (int i = 0; i < length; i++) {
int rgb = srcData[i];
int a = (rgb >>> 24) & 0xFF;
int r = (rgb >>> 16) & 0xFF;
int g = (rgb >>> 8) & 0xFF;
int b = (rgb) & 0xFF;
if (a == 255 || simple) {
r = table[0][r];
g = table[1][g];
b = table[2][b];
} else if (a == 0) {
r = 0;
g = 0;
b = 0;
} else {
// unpremultiply
float f = 255.0f / a;
int ur = (int) (r * f);
int ug = (int) (g * f);
int ub = (int) (b * f);
// TODO these checks shouldn't be necessary
if (ur > 255) {
ur = 255;
}
if (ug > 255) {
ug = 255;
}
if (ub > 255) {
ub = 255;
}
// lookup
ur = table[0][ur];
ug = table[1][ug];
ub = table[2][ub];
// premultiply
float f2 = a * (1.0f / 255.0f);
r = (int) (ur * f2);
g = (int) (ug * f2);
b = (int) (ub * f2);
r = PixelUtils.clamp(r);
g = PixelUtils.clamp(g);
b = PixelUtils.clamp(b);
}
destData[i] = (a << 24) | (r << 16) | (g << 8) | b;
}
} else { // fall back to a normal LookupOp
BufferedImageOp lookupOp = new LookupOp(lookupTable, null);
lookupOp.filter(src, dst);
}
return dst;
}
@Override
public Rectangle2D getBounds2D(BufferedImage src) {
return null;
}
@Override
public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel destCM) {
return null;
}
@Override
public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
return null;
}
@Override
public RenderingHints getRenderingHints() {
return null;
}
}