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.RasterFormatTag;
8 import com.lightcrafts.mediax.jai.RasterAccessor;
9 import com.lightcrafts.media.jai.util.ImageUtil;
10 import com.lightcrafts.utils.ColorScience;
11 
12 import java.awt.image.*;
13 import java.awt.*;
14 import java.util.Map;
15 
16 /**
17  * Copyright (C) Light Crafts, Inc.
18  * User: fabio
19  * Date: Mar 20, 2007
20  * Time: 4:32:46 PM
21  */
22 public class HDROpImage2 extends PointOpImage {
23     private static final int div = 2;
24     private static final int c = 0x10000 / div;
25 
26     private final double detail;
27     private final double shadows;
28     private final double highlights;
29 
HDROpImage2(RenderedImage source, RenderedImage mask, double shadows, double highlights, double detail, Map config)30     public HDROpImage2(RenderedImage source, RenderedImage mask, double shadows, double highlights, double detail, Map config) {
31         super(source, mask, new ImageLayout(source), config, true);
32 
33         int numBandsSrc = source.getSampleModel().getNumBands();
34         int numBandsMask = mask.getSampleModel().getNumBands();
35         int dataType = source.getSampleModel().getDataType();
36 
37         if (!(source.getSampleModel() instanceof PixelInterleavedSampleModel))
38             throw new UnsupportedOperationException("Unsupported sample model: " + source.getSampleModel().getClass());
39 
40         if (dataType != DataBuffer.TYPE_USHORT)
41             throw new UnsupportedOperationException("Unsupported data type: " + dataType);
42 
43         if (numBandsSrc != 3)
44             throw new UnsupportedOperationException("Only three-banded sources are supported: " + numBandsSrc);
45 
46         if (numBandsMask != 1 && numBandsMask != 3)
47             throw new UnsupportedOperationException("Only single-banded or three-banded masks are supported: " + numBandsMask);
48 
49         permitInPlaceOperation();
50 
51         this.detail = 1 + (detail - 1) / 5;
52         this.shadows = 1 + (shadows - 1) / 5;
53         this.highlights = highlights;
54     }
55 
computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect)56     protected void computeRect(Raster[] sources,
57                                WritableRaster dest,
58                                Rectangle destRect) {
59         // Retrieve format tags.
60         RasterFormatTag[] formatTags = getFormatTags();
61 
62         RasterAccessor s1 = new RasterAccessor(sources[0], destRect,
63                                                formatTags[0],
64                                                getSourceImage(0).getColorModel());
65         RasterAccessor s2 = new RasterAccessor(sources[1], destRect,
66                                                formatTags[1],
67                                                getSourceImage(1).getColorModel());
68         RasterAccessor d = new RasterAccessor(dest, destRect,
69                                               formatTags[2], getColorModel());
70 
71         switch (d.getDataType()) {
72             case DataBuffer.TYPE_USHORT:
73                 computeRectUShort(s1, s2, d);
74                 break;
75             default:
76                 throw new UnsupportedOperationException("Unsupported data type: " + d.getDataType());
77         }
78 
79         if (d.needsClamping()) {
80             d.clampDataArrays();
81         }
82         d.copyDataToRaster();
83     }
84 
softLightBlendPixelsIntensity(int front, int back, short intensityTable[])85     private static int softLightBlendPixelsIntensity(int front, int back, short intensityTable[]) {
86         int m = front * back / c;
87         int s = c - (c - front) * (c - back) / c;
88         int p = 0xffff & intensityTable[back];
89         return (c - p) * m / c + (p * s) / c;
90     }
91 
softLightBlendPixels(int front, int back)92     private static int softLightBlendPixels(int front, int back) {
93         int m = front * back / c;
94         int s = c - (c - front) * (c - back) / c;
95         return (c - back) * m / c + (back * s) / c;
96     }
97 
softLightBlendPixels(double front, double back)98     private static double softLightBlendPixels(double front, double back) {
99         double m = front * back;
100         // double s = 1 - (1 - front) * (1 - back);
101         return (1 - back) * m * m; // + (back * s);
102     }
103 
screenBlendPixels(int front, int back)104     private static int screenBlendPixels(int front, int back) {
105         return c - (c - front) * (c - back) / c;
106     }
107 
screenBlendPixelsIntensity(int front, int back, short intensityTable[])108     private static int screenBlendPixelsIntensity(int front, int back, short intensityTable[]) {
109         int s = c - (c - front) * (c - back) / c;
110         int p = 0xffff & intensityTable[back];
111         return (p * s) / c;
112     }
113 
114 //    int wr = (int) (0x2000 * ColorScience.Wr);
115 //    int wg = (int) (0x2000 * ColorScience.Wg);
116 //    int wb = (int) (0x2000 * ColorScience.Wb);
117 
cBlendLoop(short[] srcData, short[] maskData, short[] dstData, int[] srcBandOffsets, int[] maskBandOffsets, int[] dstBandOffsets, int dstwidth, int dstheight, int srcLineStride, int srcPixelStride, int maskLineStride, int maskPixelStride, int dstLineStride, int dstPixelStride, float shadows, float detail, float highlights, float wr, float wg, float wb)118     private static native void cBlendLoop(short[] srcData, short[] maskData, short[] dstData,
119                                           int[] srcBandOffsets, int[] maskBandOffsets, int[] dstBandOffsets,
120                                           int dstwidth, int dstheight, int srcLineStride, int srcPixelStride,
121                                           int maskLineStride, int maskPixelStride, int dstLineStride, int dstPixelStride,
122                                           float shadows, float detail, float highlights, float wr, float wg, float wb);
123 
blendLoop(short[] srcData, short[] maskData, short[] dstData, int[] srcBandOffsets, int[] maskBandOffsets, int[] dstBandOffsets, int dstwidth, int dstheight, int srcLineStride, int srcPixelStride, int maskLineStride, int maskPixelStride, int dstLineStride, int dstPixelStride, float shadows, float detail, float highlights, float wr, float wg, float wb)124     private static void blendLoop(short[] srcData, short[] maskData, short[] dstData,
125                                   int[] srcBandOffsets, int[] maskBandOffsets, int[] dstBandOffsets,
126                                   int dstwidth, int dstheight, int srcLineStride, int srcPixelStride,
127                                   int maskLineStride, int maskPixelStride, int dstLineStride, int dstPixelStride,
128                                   float shadows, float detail, float highlights, float wr, float wg, float wb) {
129         int srcROffset = srcBandOffsets[0];
130         int srcGOffset = srcBandOffsets[1];
131         int srcBOffset = srcBandOffsets[2];
132 
133         int maskOffset1, maskOffset2, maskOffset3;
134 
135         if (maskBandOffsets.length == 3) {
136             maskOffset1 = maskBandOffsets[0];
137             maskOffset2 = maskBandOffsets[1];
138             maskOffset3 = maskBandOffsets[2];
139         } else
140             maskOffset1 = maskOffset2 = maskOffset3 = maskBandOffsets[0];
141 
142         int dstROffset = dstBandOffsets[0];
143         int dstGOffset = dstBandOffsets[1];
144         int dstBOffset = dstBandOffsets[2];
145 
146         for (int row = 0; row < dstheight; row++) {
147             for (int col = 0; col < dstwidth; col++) {
148                 int r = (0xffff & srcData[srcPixelStride * col + row * srcLineStride + srcROffset]);
149                 int g = (0xffff & srcData[srcPixelStride * col + row * srcLineStride + srcGOffset]);
150                 int b = (0xffff & srcData[srcPixelStride * col + row * srcLineStride + srcBOffset]);
151 
152                 double m = (0xffff & maskData[maskPixelStride * col + row * maskLineStride + maskOffset1])/(double) 0xffff;
153                 if (maskBandOffsets.length == 3) {
154                     double um = (0xffff & maskData[maskPixelStride * col + row * maskLineStride + maskOffset2])/(double) 0xffff;
155                     um = Math.min(um*um, 1);
156 
157                     double bm = (0xffff & maskData[maskPixelStride * col + row * maskLineStride + maskOffset3])/(double) 0xffff;
158                     m = um * m + (1-um) * bm;
159                 }
160 
161                 double y = (wr * r + wg * g + wb * b) / 0xffff;
162 
163                 // y = (1-m) * y * highlights + (1-highlights) * y;
164 
165 //                double ly = Math.log(y);
166 //                double lm = Math.log(m);
167 //                double diff = ly - lm;
168 
169                 // double diff = Math.log(y/m);
170 
171                 // double mm = Math.exp(lm / shadows + diff * detail);
172 
173                 double mm = Math.pow(m, 1/shadows) * Math.pow(y/m, detail);
174 
175                 // double ratio = (Math.sqrt(1-m) * highlights + (1-highlights)) * mm / y;
176 
177                 double compressedHilights = softLightBlendPixels(1 - m, y);
178 
179                 double ratio = (compressedHilights * highlights + (1-highlights)) * mm / y;
180 
181                 r *= ratio;
182                 g *= ratio;
183                 b *= ratio;
184 
185                 dstData[dstPixelStride * col + row * dstLineStride + dstROffset] = ImageUtil.clampUShort(r);
186                 dstData[dstPixelStride * col + row * dstLineStride + dstGOffset] = ImageUtil.clampUShort(g);
187                 dstData[dstPixelStride * col + row * dstLineStride + dstBOffset] = ImageUtil.clampUShort(b);
188             }
189         }
190     }
191 
computeRectUShort(RasterAccessor src, RasterAccessor mask, RasterAccessor dst)192     private void computeRectUShort(RasterAccessor src,
193                                    RasterAccessor mask,
194                                    RasterAccessor dst) {
195         int srcLineStride = src.getScanlineStride();
196         int srcPixelStride = src.getPixelStride();
197         int[] srcBandOffsets = src.getBandOffsets();
198         short[] srcData = src.getShortDataArrays()[0];
199 
200         int maskLineStride = mask.getScanlineStride();
201         int maskPixelStride = mask.getPixelStride();
202         int[] maskBandOffsets = mask.getBandOffsets();
203         short[] maskData = mask.getShortDataArrays()[0];
204 
205         int dstwidth = dst.getWidth();
206         int dstheight = dst.getHeight();
207         // int dstBands = dst.getNumBands();
208         int dstLineStride = dst.getScanlineStride();
209         int dstPixelStride = dst.getPixelStride();
210         int[] dstBandOffsets = dst.getBandOffsets();
211         short[] dstData = dst.getShortDataArrays()[0];
212 
213         cBlendLoop(srcData, maskData, dstData,
214                   srcBandOffsets, maskBandOffsets, dstBandOffsets,
215                   dstwidth, dstheight, srcLineStride, srcPixelStride,
216                   maskLineStride, maskPixelStride, dstLineStride, dstPixelStride,
217                   (float) shadows, (float) detail, (float) highlights,
218                   ColorScience.Wr, ColorScience.Wg, ColorScience.Wb);
219     }
220 }
221