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