/* Copyright (C) 2005-2011 Fabio Riccardi */
package com.lightcrafts.jai.opimage;
import com.lightcrafts.mediax.jai.PointOpImage;
import com.lightcrafts.mediax.jai.ImageLayout;
import com.lightcrafts.model.RGBColorSelection;
import com.lightcrafts.utils.ColorScience;
import com.lightcrafts.utils.LCMS;
import com.lightcrafts.jai.JAIContext;
import java.awt.image.*;
import java.awt.color.ColorSpace;
import java.awt.*;
import java.util.Map;
import sun.awt.image.ShortInterleavedRaster;
import sun.awt.image.ByteInterleavedRaster;
public class RGBColorSelectionMaskOpImage extends PointOpImage {
private static LCMS.Transform ts = new LCMS.Transform(new LCMS.Profile(JAIContext.linearProfile), LCMS.TYPE_RGB_16,
new LCMS.Profile(JAIContext.labProfile), LCMS.TYPE_Lab_16,
LCMS.INTENT_RELATIVE_COLORIMETRIC, 0);
private final RGBColorSelection colorSelection;
private final float L, a, b;
private static ImageLayout createLayout(RenderedImage source) {
ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY),
false, false,
Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
ImageLayout layout = new ImageLayout(source);
layout.setColorModel(cm);
layout.setSampleModel(cm.createCompatibleSampleModel(source.getWidth(), source.getHeight()));
return layout;
}
public RGBColorSelectionMaskOpImage(RenderedImage source, RGBColorSelection colorSelection, Map config) {
super(source, createLayout(source), config, true);
this.colorSelection = colorSelection;
short labColors[] = new short[3];
ts.doTransform(new short[] {(short) (colorSelection.red * 0xffff),
(short) (colorSelection.green * 0xffff),
(short) (colorSelection.blue * 0xffff)}, labColors);
L = (0xffff & labColors[0]) / (float) 0xffff;
a = ((0xffff & labColors[1])) / (float) 0xffff;
b = ((0xffff & labColors[2])) / (float) 0xffff;
// System.out.println("RGBColorSelectionMaskOpImage - L:" + L + ", a:" + a + ", b: " + b + ", radius: " + colorSelection.radius);
}
protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) {
ShortInterleavedRaster src = (ShortInterleavedRaster) sources[0];
ByteInterleavedRaster dst = (ByteInterleavedRaster) dest;
dst = (ByteInterleavedRaster) dst.createChild(destRect.x, destRect.y, destRect.width, destRect.height,
destRect.x, destRect.y, null);
int width = dst.getWidth();
int height = dst.getHeight();
byte dstData[] = dst.getDataStorage();
int dstBandOffsets[] = dst.getDataOffsets();
int dstLineStride = dst.getScanlineStride();
// int dstPixelStride = dst.getPixelStride();
short srcData[] = src.getDataStorage();
int srcBandOffsets[] = src.getDataOffsets();
int srcLineStride = src.getScanlineStride();
// int srcPixelStride = src.getPixelStride();
int dstOffset = dstBandOffsets[0];
float colorSelectionArray[] = {
L, a, b,
colorSelection.isColorEnabled ? colorSelection.radius : -1,
colorSelection.isLuminosityEnabled ? colorSelection.luminosityLower : 0,
colorSelection.isLuminosityEnabled ? colorSelection.luminosityLowerFeather : 0,
colorSelection.isLuminosityEnabled ? colorSelection.luminosityUpper : 1,
colorSelection.isLuminosityEnabled ? colorSelection.luminosityUpperFeather : 0
};
nativeUshortLoop(srcData, dstData, width, height,
srcBandOffsets, dstOffset,
srcLineStride, dstLineStride,
colorSelectionArray, colorSelection.isInverted);
}
private native void nativeUshortLoop(short srcData[], byte dstData[],
int width, int height,
int srcBandOffsets[], int dstOffset,
int srcLineStride, int dstLineStride,
float colorSelection[], boolean invert);
private void ushortLoop(short srcData[], byte dstData[],
int width, int height,
int srcBandOffsets[], int dstOffset,
int srcLineStride, int dstLineStride,
float colorSelection[]) {
int srcROffset = srcBandOffsets[0];
int srcGOffset = srcBandOffsets[1];
int srcBOffset = srcBandOffsets[2];
float sL = colorSelection[0];
float sa = colorSelection[1];
float sb = colorSelection[2];
float radius = colorSelection[3];
float luminosityLower = colorSelection[4];
float luminosityLowerFeather = colorSelection[5];
float luminosityUpper = colorSelection[6];
float luminosityUpperFeather = colorSelection[7];
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
float L = (0xffff & srcData[3 * col + row * srcLineStride + srcROffset]);
float a = ((0xffff & srcData[3 * col + row * srcLineStride + srcGOffset])) / (float) 0xffff;
float b = ((0xffff & srcData[3 * col + row * srcLineStride + srcBOffset])) / (float) 0xffff;
float brightnessMask, colorMask;
if (radius >= 0) {
final float rmin = 3 * radius / 16;
final float rmax = 5 * radius / 16;
float da = sa - a;
float db = sb - b;
float m = (float) Math.sqrt(da * da + db * db);
if (m < rmin)
colorMask = 1;
else if (m < rmax)
colorMask = (rmax - m) / (rmax - rmin);
else
colorMask = 0;
} else
colorMask = 1;
if (luminosityLower > 0 || luminosityUpper < 1) {
float luminosity = (float) ((Math.log1p(L / 0x100))/(8 * Math.log(2)));
if (luminosity >= luminosityLower && luminosity <= luminosityUpper)
brightnessMask = 1;
else if (luminosity >= (luminosityLower - luminosityLowerFeather) && luminosity < luminosityLower)
brightnessMask = (luminosity - (luminosityLower - luminosityLowerFeather))/luminosityLowerFeather;
else if (luminosity > luminosityUpper && luminosity <= (luminosityUpper + luminosityUpperFeather))
brightnessMask = (luminosityUpper + luminosityUpperFeather - luminosity)/luminosityUpperFeather;
else
brightnessMask = 0;
colorMask *= brightnessMask;
}
dstData[col + row * dstLineStride + dstOffset] = (byte) (0xff * colorMask);
}
}
}
}