1 /* Copyright (C) 2005-2011 Fabio Riccardi */ 2 3 package com.lightcrafts.jai.opimage; 4 5 import com.lightcrafts.mediax.jai.PointOpImage; 6 import com.lightcrafts.mediax.jai.ImageLayout; 7 import com.lightcrafts.mediax.jai.RasterAccessor; 8 import com.lightcrafts.mediax.jai.RasterFormatTag; 9 import com.lightcrafts.model.RGBColorSelection; 10 import com.lightcrafts.utils.LCMS; 11 import com.lightcrafts.jai.JAIContext; 12 13 import java.awt.image.*; 14 import java.awt.color.ColorSpace; 15 import java.awt.*; 16 import java.util.Map; 17 18 public class RGBColorSelectionMaskOpImage extends PointOpImage { 19 private static LCMS.Transform ts = new LCMS.Transform(new LCMS.Profile(JAIContext.linearProfile), LCMS.TYPE_RGB_16, 20 new LCMS.Profile(JAIContext.labProfile), LCMS.TYPE_Lab_16, 21 LCMS.INTENT_RELATIVE_COLORIMETRIC, 0); 22 23 private final RGBColorSelection colorSelection; 24 private final float L, a, b; 25 createLayout(RenderedImage source)26 private static ImageLayout createLayout(RenderedImage source) { 27 ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), 28 false, false, 29 Transparency.OPAQUE, DataBuffer.TYPE_BYTE); 30 31 ImageLayout layout = new ImageLayout(source); 32 layout.setColorModel(cm); 33 layout.setSampleModel(cm.createCompatibleSampleModel(source.getWidth(), source.getHeight())); 34 return layout; 35 } 36 RGBColorSelectionMaskOpImage(RenderedImage source, RGBColorSelection colorSelection, Map config)37 public RGBColorSelectionMaskOpImage(RenderedImage source, RGBColorSelection colorSelection, Map config) { 38 super(source, createLayout(source), config, true); 39 this.colorSelection = colorSelection; 40 short labColors[] = new short[3]; 41 42 ts.doTransform(new short[] {(short) (colorSelection.red * 0xffff), 43 (short) (colorSelection.green * 0xffff), 44 (short) (colorSelection.blue * 0xffff)}, labColors); 45 46 L = (0xffff & labColors[0]) / (float) 0xffff; 47 a = ((0xffff & labColors[1])) / (float) 0xffff; 48 b = ((0xffff & labColors[2])) / (float) 0xffff; 49 50 // System.out.println("RGBColorSelectionMaskOpImage - L:" + L + ", a:" + a + ", b: " + b + ", radius: " + colorSelection.radius); 51 } 52 53 @Override computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect)54 protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { 55 RasterFormatTag[] formatTags = getFormatTags(); 56 57 Raster source = sources[0]; 58 Rectangle srcRect = mapDestRect(destRect, 0); 59 60 RasterAccessor src = new RasterAccessor(source, srcRect, formatTags[0], 61 getSourceImage(0).getColorModel()); 62 RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); 63 64 int width = dst.getWidth(); 65 int height = dst.getHeight(); 66 67 int dstBandOffsets[] = dst.getBandOffsets(); 68 int dstLineStride = dst.getScanlineStride(); 69 70 int srcBandOffsets[] = src.getBandOffsets(); 71 int srcLineStride = src.getScanlineStride(); 72 73 int dstOffset = dstBandOffsets[0]; 74 75 float colorSelectionArray[] = { 76 L, a, b, 77 colorSelection.isColorEnabled ? colorSelection.radius : -1, 78 colorSelection.isLuminosityEnabled ? colorSelection.luminosityLower : 0, 79 colorSelection.isLuminosityEnabled ? colorSelection.luminosityLowerFeather : 0, 80 colorSelection.isLuminosityEnabled ? colorSelection.luminosityUpper : 1, 81 colorSelection.isLuminosityEnabled ? colorSelection.luminosityUpperFeather : 0 82 }; 83 84 if (src.getDataType() == DataBuffer.TYPE_INT && dst.getDataType() == DataBuffer.TYPE_INT) { 85 int srcData[] = src.getIntDataArray(0); 86 int dstData[] = dst.getIntDataArray(0); 87 nativeIntLoop(srcData, dstData, width, height, 88 srcBandOffsets, dstOffset, 89 srcLineStride, dstLineStride, 90 colorSelectionArray, colorSelection.isInverted); 91 } 92 else if (src.getDataType() == DataBuffer.TYPE_USHORT && dst.getDataType() == DataBuffer.TYPE_BYTE) { 93 short srcData[] = src.getShortDataArray(0); 94 byte dstData[] = dst.getByteDataArray(0); 95 nativeUshortLoop(srcData, dstData, width, height, 96 srcBandOffsets, dstOffset, 97 srcLineStride, dstLineStride, 98 colorSelectionArray, colorSelection.isInverted); 99 } 100 else { 101 throw new UnsupportedOperationException( 102 "Unsupported data type pair: " + src.getDataType() + ", " + dst.getDataType()); 103 } 104 dst.copyDataToRaster(); 105 } 106 nativeIntLoop(int[] srcData, int[] dstData, int width, int height, int[] srcBandOffsets, int dstOffset, int srcLineStride, int dstLineStride, float[] colorSelection, boolean invert)107 private native void nativeIntLoop(int[] srcData, int[] dstData, 108 int width, int height, 109 int[] srcBandOffsets, int dstOffset, 110 int srcLineStride, int dstLineStride, 111 float[] colorSelection, boolean invert); 112 nativeUshortLoop(short srcData[], byte dstData[], int width, int height, int srcBandOffsets[], int dstOffset, int srcLineStride, int dstLineStride, float colorSelection[], boolean invert)113 private native void nativeUshortLoop(short srcData[], byte dstData[], 114 int width, int height, 115 int srcBandOffsets[], int dstOffset, 116 int srcLineStride, int dstLineStride, 117 float colorSelection[], boolean invert); 118 ushortLoop(short srcData[], byte dstData[], int width, int height, int srcBandOffsets[], int dstOffset, int srcLineStride, int dstLineStride, float colorSelection[])119 private void ushortLoop(short srcData[], byte dstData[], 120 int width, int height, 121 int srcBandOffsets[], int dstOffset, 122 int srcLineStride, int dstLineStride, 123 float colorSelection[]) { 124 int srcROffset = srcBandOffsets[0]; 125 int srcGOffset = srcBandOffsets[1]; 126 int srcBOffset = srcBandOffsets[2]; 127 128 float sL = colorSelection[0]; 129 float sa = colorSelection[1]; 130 float sb = colorSelection[2]; 131 float radius = colorSelection[3]; 132 float luminosityLower = colorSelection[4]; 133 float luminosityLowerFeather = colorSelection[5]; 134 float luminosityUpper = colorSelection[6]; 135 float luminosityUpperFeather = colorSelection[7]; 136 137 for (int row = 0; row < height; row++) { 138 for (int col = 0; col < width; col++) { 139 float L = (0xffff & srcData[3 * col + row * srcLineStride + srcROffset]); 140 float a = ((0xffff & srcData[3 * col + row * srcLineStride + srcGOffset])) / (float) 0xffff; 141 float b = ((0xffff & srcData[3 * col + row * srcLineStride + srcBOffset])) / (float) 0xffff; 142 143 float brightnessMask, colorMask; 144 145 if (radius >= 0) { 146 final float rmin = 3 * radius / 16; 147 final float rmax = 5 * radius / 16; 148 149 float da = sa - a; 150 float db = sb - b; 151 float m = (float) Math.sqrt(da * da + db * db); 152 if (m < rmin) 153 colorMask = 1; 154 else if (m < rmax) 155 colorMask = (rmax - m) / (rmax - rmin); 156 else 157 colorMask = 0; 158 } else 159 colorMask = 1; 160 161 if (luminosityLower > 0 || luminosityUpper < 1) { 162 float luminosity = (float) ((Math.log1p(L / 0x100))/(8 * Math.log(2))); 163 164 if (luminosity >= luminosityLower && luminosity <= luminosityUpper) 165 brightnessMask = 1; 166 else if (luminosity >= (luminosityLower - luminosityLowerFeather) && luminosity < luminosityLower) 167 brightnessMask = (luminosity - (luminosityLower - luminosityLowerFeather))/luminosityLowerFeather; 168 else if (luminosity > luminosityUpper && luminosity <= (luminosityUpper + luminosityUpperFeather)) 169 brightnessMask = (luminosityUpper + luminosityUpperFeather - luminosity)/luminosityUpperFeather; 170 else 171 brightnessMask = 0; 172 173 colorMask *= brightnessMask; 174 } 175 176 dstData[col + row * dstLineStride + dstOffset] = (byte) (0xff * colorMask); 177 } 178 } 179 } 180 } 181