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 
11 import java.awt.image.*;
12 import java.awt.*;
13 import java.util.Map;
14 
15 /**
16  * Copyright (C) Light Crafts, Inc.
17  * User: fabio
18  * Date: Mar 20, 2007
19  * Time: 4:32:46 PM
20  */
21 public class HDROpImage extends PointOpImage {
22     private static final int div = 2;
23     private static final int c = 0x10000 / div;
24 
25     private final double detail;
26     private short intensityTable[] = new short[c+1];
27     private short gammaTable[] = new short[0x10000];
28     private final double intensity;
29 
sigmoid(double x)30     private double sigmoid(double x) {
31         return x + intensity * x * (1 - 1 / (1 + Math.exp(-6*(x - 0.01))));
32     }
33 
HDROpImage(RenderedImage source, RenderedImage mask, double intensity, double gamma, double detail, Map config)34     public HDROpImage(RenderedImage source, RenderedImage mask, double intensity, double gamma, double detail, Map config) {
35         super(source, mask, new ImageLayout(source), config, true);
36 
37         int numBandsSrc = source.getSampleModel().getNumBands();
38         int numBandsMask = mask.getSampleModel().getNumBands();
39         int dataType = source.getSampleModel().getDataType();
40 
41         if (!(source.getSampleModel() instanceof PixelInterleavedSampleModel))
42             throw new UnsupportedOperationException("Unsupported sample model: " + source.getSampleModel().getClass());
43 
44         if (dataType != DataBuffer.TYPE_USHORT)
45             throw new UnsupportedOperationException("Unsupported data type: " + dataType);
46 
47         if (numBandsSrc != 3)
48             throw new UnsupportedOperationException("Only three-banded sources are supported: " + numBandsSrc);
49 
50         if (numBandsMask != 1)
51             throw new UnsupportedOperationException("Only single-banded masks are supported: " + numBandsMask);
52 
53         permitInPlaceOperation();
54 
55         this.detail = detail;
56         this.intensity = intensity;
57 
58         for (int i = 0; i < c+1; i++)
59             intensityTable[i] = (short) (c * sigmoid(i/(double)c));
60 
61         for (int i = 0; i < gammaTable.length; i++)
62             gammaTable[i] = (short) (0xFFFF * Math.pow(i / (double) 0xFFFF, gamma) + 0.5);
63     }
64 
computeRect(Raster[] sources, WritableRaster dest, Rectangle destRect)65     protected void computeRect(Raster[] sources,
66                                WritableRaster dest,
67                                Rectangle destRect) {
68         // Retrieve format tags.
69         RasterFormatTag[] formatTags = getFormatTags();
70 
71         RasterAccessor s1 = new RasterAccessor(sources[0], destRect,
72                                                formatTags[0],
73                                                getSourceImage(0).getColorModel());
74         RasterAccessor s2 = new RasterAccessor(sources[1], destRect,
75                                                formatTags[1],
76                                                getSourceImage(1).getColorModel());
77         RasterAccessor d = new RasterAccessor(dest, destRect,
78                                               formatTags[2], getColorModel());
79 
80         switch (d.getDataType()) {
81             case DataBuffer.TYPE_USHORT:
82                 computeRectUShort(s1, s2, d);
83                 break;
84             default:
85                 throw new UnsupportedOperationException("Unsupported data type: " + d.getDataType());
86         }
87 
88         if (d.needsClamping()) {
89             d.clampDataArrays();
90         }
91         d.copyDataToRaster();
92     }
93 
softLightBlendPixelsIntensity(int front, int back, short intensityTable[])94     private static int softLightBlendPixelsIntensity(int front, int back, short intensityTable[]) {
95         int m = front * back / c;
96         int s = c - (c - front) * (c - back) / c;
97         int p = 0xffff & intensityTable[back];
98         return (c - p) * m / c + (p * s) / c;
99     }
100 
softLightBlendPixels(int front, int back)101     private static int softLightBlendPixels(int front, int back) {
102         int m = front * back / c;
103         int s = c - (c - front) * (c - back) / c;
104         return (c - back) * m / c + (back * s) / c;
105     }
106 
computeRectUShort(RasterAccessor src, RasterAccessor mask, RasterAccessor dst)107     private void computeRectUShort(RasterAccessor src,
108                                    RasterAccessor mask,
109                                    RasterAccessor dst) {
110         int srcLineStride = src.getScanlineStride();
111         int srcPixelStride = src.getPixelStride();
112         int[] srcBandOffsets = src.getBandOffsets();
113         short[] srcData = src.getShortDataArrays()[0];
114 
115         int srcROffset = srcBandOffsets[0];
116         int srcGOffset = srcBandOffsets[1];
117         int srcBOffset = srcBandOffsets[2];
118 
119         int maskLineStride = mask.getScanlineStride();
120         int maskPixelStride = mask.getPixelStride();
121         int[] maskBandOffsets = mask.getBandOffsets();
122         short[] maskData = mask.getShortDataArrays()[0];
123 
124         int maskOffset = maskBandOffsets[0];
125 
126         int dstwidth = dst.getWidth();
127         int dstheight = dst.getHeight();
128         // int dstBands = dst.getNumBands();
129         int dstLineStride = dst.getScanlineStride();
130         int dstPixelStride = dst.getPixelStride();
131         int[] dstBandOffsets = dst.getBandOffsets();
132         short[] dstData = dst.getShortDataArrays()[0];
133 
134         int dstROffset = dstBandOffsets[0];
135         int dstGOffset = dstBandOffsets[1];
136         int dstBOffset = dstBandOffsets[2];
137 
138         int overlays = (int) Math.floor(detail);
139         int alpha = (int) (c * (detail - Math.floor(detail)));
140 
141         for (int row = 0; row < dstheight; row++) {
142             for (int col = 0; col < dstwidth; col++) {
143                 int r = (0xffff & srcData[srcPixelStride * col + row * srcLineStride + srcROffset]) / div;
144                 int g = (0xffff & srcData[srcPixelStride * col + row * srcLineStride + srcGOffset]) / div;
145                 int b = (0xffff & srcData[srcPixelStride * col + row * srcLineStride + srcBOffset]) / div;
146 
147                 int m = 0xffff & maskData[maskPixelStride * col + row * maskLineStride + maskOffset];
148 
149                 m = (0xffff & gammaTable[0xffff - m]) / div;
150 
151                 for (int i = 0; i < overlays; i++)
152                     m = softLightBlendPixels(g, m);
153 
154                 if (alpha > 0)
155                     m = softLightBlendPixels(g, m) * alpha / c + m * (c - alpha) / c;
156 
157                 int rr = div * softLightBlendPixelsIntensity(m, r, intensityTable);
158                 int gg = div * softLightBlendPixelsIntensity(m, g, intensityTable);
159                 int bb = div * softLightBlendPixelsIntensity(m, b, intensityTable);
160 
161                 dstData[dstPixelStride * col + row * dstLineStride + dstROffset] = ImageUtil.clampUShort(rr);
162                 dstData[dstPixelStride * col + row * dstLineStride + dstGOffset] = ImageUtil.clampUShort(gg);
163                 dstData[dstPixelStride * col + row * dstLineStride + dstBOffset] = ImageUtil.clampUShort(bb);
164             }
165         }
166     }
167 }
168