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.ColorSelection; 10 import com.lightcrafts.utils.ColorScience; 11 12 import java.awt.image.*; 13 import java.awt.color.ColorSpace; 14 import java.awt.*; 15 import java.util.Map; 16 17 public class ColorSelectionMaskOpImage extends PointOpImage { 18 private final ColorSelection colorSelection; 19 createLayout(RenderedImage source)20 private static ImageLayout createLayout(RenderedImage source) { 21 ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_GRAY), 22 false, false, 23 Transparency.OPAQUE, DataBuffer.TYPE_BYTE); 24 25 ImageLayout layout = new ImageLayout(source); 26 layout.setColorModel(cm); 27 layout.setSampleModel(cm.createCompatibleSampleModel(source.getWidth(), source.getHeight())); 28 return layout; 29 } 30 ColorSelectionMaskOpImage(RenderedImage source, ColorSelection colorSelection, Map config)31 public ColorSelectionMaskOpImage(RenderedImage source, ColorSelection colorSelection, Map config) { 32 super(source, createLayout(source), config, true); 33 this.colorSelection = colorSelection; 34 } 35 36 @Override computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect)37 protected void computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect) { 38 // Retrieve format tags. 39 RasterFormatTag[] formatTags = getFormatTags(); 40 41 Raster source = sources[0]; 42 Rectangle srcRect = mapDestRect(destRect, 0); 43 44 RasterAccessor src = new RasterAccessor(source, srcRect, formatTags[0], 45 getSourceImage(0).getColorModel()); 46 RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel()); 47 48 int width = dst.getWidth(); 49 int height = dst.getHeight(); 50 51 byte dstData[] = dst.getByteDataArray(0); 52 int dstBandOffsets[] = dst.getBandOffsets(); 53 int dstLineStride = dst.getScanlineStride(); 54 55 short srcData[] = src.getShortDataArray(0); 56 int srcBandOffsets[] = src.getBandOffsets(); 57 int srcLineStride = src.getScanlineStride(); 58 59 int dstOffset = dstBandOffsets[0]; 60 61 float colorSelectionArray[] = { 62 colorSelection.isHueEnabled ? colorSelection.hueLower : 0, 63 colorSelection.isHueEnabled ? colorSelection.hueLowerFeather : 0, 64 colorSelection.isHueEnabled ? colorSelection.hueUpper : 1, 65 colorSelection.isHueEnabled ? colorSelection.hueUpperFeather : 0, 66 colorSelection.isLuminosityEnabled ? colorSelection.luminosityLower : 0, 67 colorSelection.isLuminosityEnabled ? colorSelection.luminosityLowerFeather : 0, 68 colorSelection.isLuminosityEnabled ? colorSelection.luminosityUpper : 1, 69 colorSelection.isLuminosityEnabled ? colorSelection.luminosityUpperFeather : 0 70 }; 71 72 float wr = ColorScience.Wr; 73 float wg = ColorScience.Wg; 74 float wb = ColorScience.Wb; 75 76 nativeUshortLoop(srcData, dstData, width, height, 77 srcBandOffsets, dstOffset, 78 srcLineStride, dstLineStride, 79 colorSelectionArray, wr, wg, wb); 80 } 81 82 private static final int sMath_scale = 0x8000; 83 private static final int sMath_PI = (int) (sMath_scale * Math.PI); 84 private static final int sqrt3d2 = (int) (sMath_scale * Math.sqrt(3) / 2); // 0.866... 85 arctan2(int y, int x)86 static int arctan2(int y, int x) { 87 final int coeff_1 = sMath_PI / 4; 88 final int coeff_2 = 3 * coeff_1; 89 final int abs_y = Math.abs(y) + 1; // kludge to prevent 0/0 condition 90 final int angle; 91 92 if (x >= 0) { 93 int r = (sMath_scale * (x - abs_y)) / (x + abs_y); 94 angle = coeff_1 - coeff_1 * r / sMath_scale; 95 } else { 96 int r = (sMath_scale * (x + abs_y)) / (abs_y - x); 97 angle = coeff_2 - coeff_1 * r / sMath_scale; 98 } 99 100 return y < 0 ? -angle : angle; 101 } 102 hue(int r, int g, int b)103 public static int hue(int r, int g, int b) { 104 int x = r - (g+b) / 2; 105 int y = (sqrt3d2 * (g-b)) / sMath_scale; 106 int hue = arctan2(y, x); 107 if (hue < 0) 108 hue += 2 * sMath_PI; 109 return hue; 110 } 111 arctan2(float y, float x)112 static float arctan2(float y, float x) { 113 final float coeff_1 = (float) Math.PI / 4; 114 final float coeff_2 = 3 * coeff_1; 115 final float abs_y = Math.abs(y) + 1e-10f; // kludge to prevent 0/0 condition 116 float angle; 117 118 if (x >= 0) { 119 float r = (x - abs_y) / (x + abs_y); 120 angle = coeff_1 - coeff_1 * r; 121 } else { 122 float r = (x + abs_y) / (abs_y - x); 123 angle = coeff_2 - coeff_1 * r; 124 } 125 126 return y < 0 ? -angle : angle; 127 } 128 hue(float r, float g, float b)129 public static float hue(float r, float g, float b) { 130 float x = r - (g+b) / 2; 131 float y = ((g-b) * (float) Math.sqrt(3) / 2); 132 float hue = arctan2(y, x); 133 if (hue < 0) 134 hue += 2 * Math.PI; 135 return hue; 136 } 137 nativeUshortLoop(short srcData[], byte dstData[], int width, int height, int srcBandOffsets[], int dstOffset, int srcLineStride, int dstLineStride, float colorSelection[], float wr, float wg, float wb)138 private native void nativeUshortLoop(short srcData[], byte dstData[], 139 int width, int height, 140 int srcBandOffsets[], int dstOffset, 141 int srcLineStride, int dstLineStride, 142 float colorSelection[], float wr, float wg, float wb); 143 ushortLoop(short srcData[], byte dstData[], int width, int height, int srcBandOffsets[], int dstOffset, int srcLineStride, int dstLineStride, float colorSelection[], float wr, float wg, float wb)144 private void ushortLoop(short srcData[], byte dstData[], 145 int width, int height, 146 int srcBandOffsets[], int dstOffset, 147 int srcLineStride, int dstLineStride, 148 float colorSelection[], float wr, float wg, float wb) { 149 int srcROffset = srcBandOffsets[0]; 150 int srcGOffset = srcBandOffsets[1]; 151 int srcBOffset = srcBandOffsets[2]; 152 153 float hueLower = colorSelection[0]; 154 float hueLowerFeather = colorSelection[1]; 155 float hueUpper = colorSelection[2]; 156 float hueUpperFeather = colorSelection[3]; 157 float luminosityLower = colorSelection[4]; 158 float luminosityLowerFeather = colorSelection[5]; 159 float luminosityUpper = colorSelection[6]; 160 float luminosityUpperFeather = colorSelection[7]; 161 162 int hueOffset = 0; 163 164 if (hueLower < 0 || hueLower - hueLowerFeather < 0 || hueUpper < 0) { 165 hueLower += 1; 166 hueUpper += 1; 167 hueOffset = 1; 168 } else if (hueLower > 1 || hueUpper + hueUpperFeather > 1 || hueUpper > 1) { 169 hueOffset = -1; 170 } 171 172 for (int row = 0; row < height; row++) { 173 for (int col = 0; col < width; col++) { 174 float r = 0xffff & srcData[3 * col + row * srcLineStride + srcROffset]; 175 float g = 0xffff & srcData[3 * col + row * srcLineStride + srcGOffset]; 176 float b = 0xffff & srcData[3 * col + row * srcLineStride + srcBOffset]; 177 178 // float hue = hue(r / (float) 0xffff, g / (float) 0xffff, b / (float) 0xffff) / (float) (2 * Math.PI); 179 180 float cmax = (r > g) ? r : g; 181 if (b > cmax) cmax = b; 182 float cmin = (r < g) ? r : g; 183 if (b < cmin) cmin = b; 184 185 float saturation; 186 if (cmax != 0) 187 saturation = (cmax - cmin) / cmax; 188 else 189 saturation = 0; 190 191 float brightnessMask, colorMask; 192 193 final float stmin = 0.01f; 194 final float stmax = 0.02f; 195 196 if (saturation > stmin) { 197 float hue = hue(r, g, b) / (float) (2 * Math.PI); 198 199 if (hueOffset == 1 && hue < hueLower - hueLowerFeather) 200 hue += 1; 201 else if (hueOffset == -1 && hue < 0.5) 202 hue += 1; 203 204 if (hue >= hueLower && hue <= hueUpper) 205 colorMask = 1; 206 else if (hue >= (hueLower - hueLowerFeather) && hue < hueLower) 207 colorMask = (hue - (hueLower - hueLowerFeather))/hueLowerFeather; 208 else if (hue > hueUpper && hue <= (hueUpper + hueUpperFeather)) 209 colorMask = (hueUpper + hueUpperFeather - hue)/hueUpperFeather; 210 else 211 colorMask = 0; 212 213 if (saturation < stmax) 214 colorMask *= (saturation - stmin) / (stmax - stmin); 215 } else 216 colorMask = 0; 217 218 float luminosity = (float) (Math.log1p((wr * r + wg * g + wb * b)/0x100) / (8 * Math.log(2))); 219 220 if (luminosity >= luminosityLower && luminosity <= luminosityUpper) 221 brightnessMask = 1; 222 else if (luminosity >= (luminosityLower - luminosityLowerFeather) && luminosity < luminosityLower) 223 brightnessMask = (luminosity - (luminosityLower - luminosityLowerFeather))/luminosityLowerFeather; 224 else if (luminosity > luminosityUpper && luminosity <= (luminosityUpper + luminosityUpperFeather)) 225 brightnessMask = (luminosityUpper + luminosityUpperFeather - luminosity)/luminosityUpperFeather; 226 else 227 brightnessMask = 0; 228 229 colorMask *= brightnessMask; 230 231 dstData[col + row * dstLineStride + dstOffset] = (byte) (0xff * colorMask); 232 } 233 } 234 } 235 } 236