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.jai.JAIContext;
10 
11 import java.awt.image.DataBuffer;
12 import java.awt.image.RenderedImage;
13 import java.awt.image.Raster;
14 import java.awt.image.WritableRaster;
15 import java.awt.*;
16 import java.awt.color.ICC_ProfileRGB;
17 import java.awt.color.ICC_Profile;
18 import java.awt.color.ColorSpace;
19 import java.util.Map;
20 
21 import Jama.Matrix;
22 
23 /**
24  * Copyright (C) Light Crafts, Inc.
25  * User: fabio
26  * Date: Mar 20, 2007
27  * Time: 4:32:46 PM
28  */
29 public class IntVibranceOpImage extends PointOpImage {
30     private final int transform[][] = new int[3][3];
31     private final int toLinearsRGB[][] = new int[3][3];
32     private final boolean saturationIncrease;
33 
34     private static final int sMath_scale = 0x8000;
35     private static final int sMath_PI = (int) (sMath_scale * Math.PI);
36 
37     /*
38      * fast integer arctan2 implementation.
39      * see: http://www.dspguru.com/comp.dsp/tricks/alg/fxdatan2.htm
40      */
41 
arctan2(int y, int x)42     static int arctan2(int y, int x) {
43         final int coeff_1 = sMath_PI / 4;
44         final int coeff_2 = 3 * coeff_1;
45         final int abs_y = Math.abs(y) + 1;      // kludge to prevent 0/0 condition
46         final int angle;
47 
48         if (x >= 0) {
49             int r = (sMath_scale * (x - abs_y)) / (x + abs_y);
50             angle = coeff_1 - coeff_1 * r / sMath_scale;
51         } else {
52             int r = (sMath_scale * (x + abs_y)) / (abs_y - x);
53             angle = coeff_2 - coeff_1 * r / sMath_scale;
54         }
55 
56         return y < 0 ? -angle : angle;
57     }
58 
IntVibranceOpImage(RenderedImage source, float transform[][], Map config)59     public IntVibranceOpImage(RenderedImage source, float transform[][], Map config) {
60         super(source, new ImageLayout(source), config, true);
61         permitInPlaceOperation();
62 
63         for (int i = 0; i < 3; i++)
64             for (int j = 0; j < 3; j++)
65                 this.transform[i][j] = (int) (sMath_scale * transform[i][j]);
66 
67         saturationIncrease = transform[0][0] > 1;
68 
69         ICC_ProfileRGB linRGB = (ICC_ProfileRGB) ICC_Profile.getInstance(ColorSpace.CS_LINEAR_RGB);
70         Matrix XYZtoLinsRGB = new Matrix(linRGB.getMatrix()).inverse();
71         Matrix CIERGBtoXYZ = new Matrix(((ICC_ProfileRGB) JAIContext.linearProfile).getMatrix());
72         double CIERGBtoLinsRGB[][] = XYZtoLinsRGB.times(CIERGBtoXYZ).getArray();
73 
74         for (int i = 0; i < 3; i++)
75             for (int j = 0; j < 3; j++)
76                 toLinearsRGB[i][j] = (int) (sMath_scale * CIERGBtoLinsRGB[i][j]);
77 
78     }
79 
computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect)80     protected void computeRect(Raster[] sources,
81                                WritableRaster dest,
82                                Rectangle destRect) {
83         // Retrieve format tags.
84         RasterFormatTag[] formatTags = getFormatTags();
85 
86         RasterAccessor src = new RasterAccessor(sources[0], destRect, formatTags[0],
87                 getSourceImage(0).getColorModel());
88         RasterAccessor dst = new RasterAccessor(dest, destRect, formatTags[1], getColorModel());
89 
90         switch (dst.getDataType()) {
91             case DataBuffer.TYPE_USHORT:
92                 ushortLoop(src, dst);
93                 break;
94             default:
95                 throw new UnsupportedOperationException("Unsupported data type: " + dst.getDataType());
96         }
97     }
98 
ushortLoop(RasterAccessor src, RasterAccessor dst)99     protected void ushortLoop(RasterAccessor src, RasterAccessor dst) {
100         int width = src.getWidth();
101         int height = src.getHeight();
102 
103         short dstData[] = dst.getShortDataArray(0);
104         int dstBandOffsets[] = dst.getBandOffsets();
105         int dstLineStride = dst.getScanlineStride();
106         int dstPixelStride = dst.getPixelStride();
107 
108         short srcData[] = src.getShortDataArray(0);
109         int srcBandOffsets[] = src.getBandOffsets();
110         int srcLineStride = src.getScanlineStride();
111         int srcPixelStride = src.getPixelStride();
112 
113         int srcROffset = srcBandOffsets[0];
114         int srcGOffset = srcBandOffsets[1];
115         int srcBOffset = srcBandOffsets[2];
116 
117         int dstROffset = dstBandOffsets[0];
118         int dstGOffset = dstBandOffsets[1];
119         int dstBOffset = dstBandOffsets[2];
120 
121         final int sqrt3d2 = (int) (sMath_scale * Math.sqrt(3) / 2); // 0.866...
122 
123         for (int row = 0; row < height; row++) {
124             for (int col = 0; col < width; col++) {
125                 int srcPixOffset = srcPixelStride * col + row * srcLineStride;
126                 int r = (0xffff & srcData[srcPixOffset + srcROffset]) / 2;
127                 int g = (0xffff & srcData[srcPixOffset + srcGOffset]) / 2;
128                 int b = (0xffff & srcData[srcPixOffset + srcBOffset]) / 2;
129 
130                 int lr = (toLinearsRGB[0][0] * r + toLinearsRGB[0][1] * g + toLinearsRGB[0][2] * b) / sMath_scale;
131                 int lg = (toLinearsRGB[1][0] * r + toLinearsRGB[1][1] * g + toLinearsRGB[1][2] * b) / sMath_scale;
132                 int lb = (toLinearsRGB[2][0] * r + toLinearsRGB[2][1] * g + toLinearsRGB[2][2] * b) / sMath_scale;
133 
134                 int x = lr - (lg+lb) / 2;
135                 int y = (sqrt3d2 * (lg-lb)) / sMath_scale;
136 
137                 int hue = arctan2(x, y) + sMath_PI;
138 
139                 if (hue < 0)
140                     hue += 2 * sMath_PI;
141 
142                 if (hue > 4 * sMath_PI / 3)
143                     hue -= 4 * sMath_PI / 3;
144                 else if (hue > 2 * sMath_PI / 3)
145                     hue -= 2 * sMath_PI / 3;
146 
147                 int mask = sMath_scale / 2 + (sMath_scale - (sMath_scale * Math.abs(sMath_PI / 6 - hue)) / (sMath_PI / 3)) / 2;
148 
149                 if (saturationIncrease) {
150                     int min = Math.min(r, Math.min(g, b));
151                     int max = Math.max(r, Math.max(g, b));
152 
153                     int saturation = max != 0 ? sMath_scale - sMath_scale * min / max : 0;
154                     mask = mask * (sMath_scale - saturation * saturation / sMath_scale) / sMath_scale;
155                 }
156 
157                 int rr = (transform[0][0] * r + transform[0][1] * g + transform[0][2] * b) / sMath_scale;
158                 int gg = (transform[1][0] * r + transform[1][1] * g + transform[1][2] * b) / sMath_scale;
159                 int bb = (transform[2][0] * r + transform[2][1] * g + transform[2][2] * b) / sMath_scale;
160 
161                 rr = 2 * ((sMath_scale - mask) * r / sMath_scale + rr * mask / sMath_scale);
162                 gg = 2 * ((sMath_scale - mask) * g / sMath_scale + gg * mask / sMath_scale);
163                 bb = 2 * ((sMath_scale - mask) * b / sMath_scale + bb * mask / sMath_scale);
164 
165                 int dstPixOffset = dstPixelStride * col + row * dstLineStride;
166                 dstData[dstPixOffset + dstROffset] = (short) (rr < 0 ? 0 : rr > 0xffff ? 0xffff : rr);
167                 dstData[dstPixOffset + dstGOffset] = (short) (gg < 0 ? 0 : gg > 0xffff ? 0xffff : gg);
168                 dstData[dstPixOffset + dstBOffset] = (short) (bb < 0 ? 0 : bb > 0xffff ? 0xffff : bb);
169             }
170         }
171     }
172 }
173