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