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