blob: ad0c01a3a70373eeb5ef94e6da3885b18142b73e [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.java2d.pipe;
27
28import java.awt.color.ColorSpace;
29import java.awt.image.AffineTransformOp;
30import java.awt.image.BufferedImage;
31import java.awt.image.BufferedImageOp;
32import java.awt.image.BufferedImageOp;
33import java.awt.image.ByteLookupTable;
34import java.awt.image.ColorModel;
35import java.awt.image.ConvolveOp;
36import java.awt.image.IndexColorModel;
37import java.awt.image.Kernel;
38import java.awt.image.LookupOp;
39import java.awt.image.LookupTable;
40import java.awt.image.RescaleOp;
41import java.awt.image.ShortLookupTable;
42import sun.java2d.SurfaceData;
43import sun.java2d.loops.CompositeType;
44import static sun.java2d.pipe.BufferedOpCodes.*;
45
46public class BufferedBufImgOps {
47
48 public static void enableBufImgOp(RenderQueue rq, SurfaceData srcData,
49 BufferedImage srcImg,
50 BufferedImageOp biop)
51 {
52 if (biop instanceof ConvolveOp) {
53 enableConvolveOp(rq, srcData, (ConvolveOp)biop);
54 } else if (biop instanceof RescaleOp) {
55 enableRescaleOp(rq, srcData, srcImg, (RescaleOp)biop);
56 } else if (biop instanceof LookupOp) {
57 enableLookupOp(rq, srcData, srcImg, (LookupOp)biop);
58 } else {
59 throw new InternalError("Unknown BufferedImageOp");
60 }
61 }
62
63 public static void disableBufImgOp(RenderQueue rq, BufferedImageOp biop) {
64 if (biop instanceof ConvolveOp) {
65 disableConvolveOp(rq);
66 } else if (biop instanceof RescaleOp) {
67 disableRescaleOp(rq);
68 } else if (biop instanceof LookupOp) {
69 disableLookupOp(rq);
70 } else {
71 throw new InternalError("Unknown BufferedImageOp");
72 }
73 }
74
75/**************************** ConvolveOp support ****************************/
76
77 public static boolean isConvolveOpValid(ConvolveOp cop) {
78 Kernel kernel = cop.getKernel();
79 int kw = kernel.getWidth();
80 int kh = kernel.getHeight();
81 // REMIND: we currently can only handle 3x3 and 5x5 kernels,
82 // but hopefully this is just a temporary restriction;
83 // see native shader comments for more details
84 if (!(kw == 3 && kh == 3) && !(kw == 5 && kh == 5)) {
85 return false;
86 }
87 return true;
88 }
89
90 private static void enableConvolveOp(RenderQueue rq,
91 SurfaceData srcData,
92 ConvolveOp cop)
93 {
94 // assert rq.lock.isHeldByCurrentThread();
95 boolean edgeZero =
96 cop.getEdgeCondition() == ConvolveOp.EDGE_ZERO_FILL;
97 Kernel kernel = cop.getKernel();
98 int kernelWidth = kernel.getWidth();
99 int kernelHeight = kernel.getHeight();
100 int kernelSize = kernelWidth * kernelHeight;
101 int sizeofFloat = 4;
102 int totalBytesRequired = 4 + 8 + 12 + (kernelSize * sizeofFloat);
103
104 RenderBuffer buf = rq.getBuffer();
105 rq.ensureCapacityAndAlignment(totalBytesRequired, 4);
106 buf.putInt(ENABLE_CONVOLVE_OP);
107 buf.putLong(srcData.getNativeOps());
108 buf.putInt(edgeZero ? 1 : 0);
109 buf.putInt(kernelWidth);
110 buf.putInt(kernelHeight);
111 buf.put(kernel.getKernelData(null));
112 }
113
114 private static void disableConvolveOp(RenderQueue rq) {
115 // assert rq.lock.isHeldByCurrentThread();
116 RenderBuffer buf = rq.getBuffer();
117 rq.ensureCapacity(4);
118 buf.putInt(DISABLE_CONVOLVE_OP);
119 }
120
121/**************************** RescaleOp support *****************************/
122
123 public static boolean isRescaleOpValid(RescaleOp rop,
124 BufferedImage srcImg)
125 {
126 int numFactors = rop.getNumFactors();
127 ColorModel srcCM = srcImg.getColorModel();
128
129 if (srcCM instanceof IndexColorModel) {
130 throw new
131 IllegalArgumentException("Rescaling cannot be "+
132 "performed on an indexed image");
133 }
134 if (numFactors != 1 &&
135 numFactors != srcCM.getNumColorComponents() &&
136 numFactors != srcCM.getNumComponents())
137 {
138 throw new IllegalArgumentException("Number of scaling constants "+
139 "does not equal the number of"+
140 " of color or color/alpha "+
141 " components");
142 }
143
144 int csType = srcCM.getColorSpace().getType();
145 if (csType != ColorSpace.TYPE_RGB &&
146 csType != ColorSpace.TYPE_GRAY)
147 {
148 // Not prepared to deal with other color spaces
149 return false;
150 }
151
152 if (numFactors == 2 || numFactors > 4) {
153 // Not really prepared to handle this at the native level, so...
154 return false;
155 }
156
157 return true;
158 }
159
160 private static void enableRescaleOp(RenderQueue rq,
161 SurfaceData srcData,
162 BufferedImage srcImg,
163 RescaleOp rop)
164 {
165 // assert rq.lock.isHeldByCurrentThread();
166 ColorModel srcCM = srcImg.getColorModel();
167 boolean nonPremult =
168 srcCM.hasAlpha() &&
169 srcCM.isAlphaPremultiplied();
170
171 /*
172 * Note: The user-provided scale factors and offsets are arranged
173 * in R/G/B/A order, regardless of the raw data order of the
174 * underlying Raster/DataBuffer. The source image data is ultimately
175 * converted into RGBA data when uploaded to an OpenGL texture
176 * (even for TYPE_GRAY), so the scale factors and offsets are already
177 * in the order expected by the native OpenGL code.
178 *
179 * However, the offsets provided by the user are in a range dictated
180 * by the size of each color/alpha band in the source image. For
181 * example, for 8/8/8 data each offset is in the range [0,255],
182 * for 5/5/5 data each offset is in the range [0,31], and so on.
183 * The OpenGL shader only thinks in terms of [0,1], so below we need
184 * to normalize the user-provided offset values into the range [0,1].
185 */
186 int numFactors = rop.getNumFactors();
187 float[] origScaleFactors = rop.getScaleFactors(null);
188 float[] origOffsets = rop.getOffsets(null);
189
190 // To make things easier, we will always pass all four bands
191 // down to native code...
192 float[] normScaleFactors;
193 float[] normOffsets;
194
195 if (numFactors == 1) {
196 normScaleFactors = new float[4];
197 normOffsets = new float[4];
198 for (int i = 0; i < 3; i++) {
199 normScaleFactors[i] = origScaleFactors[0];
200 normOffsets[i] = origOffsets[0];
201 }
202 // Leave alpha untouched...
203 normScaleFactors[3] = 1.0f;
204 normOffsets[3] = 0.0f;
205 } else if (numFactors == 3) {
206 normScaleFactors = new float[4];
207 normOffsets = new float[4];
208 for (int i = 0; i < 3; i++) {
209 normScaleFactors[i] = origScaleFactors[i];
210 normOffsets[i] = origOffsets[i];
211 }
212 // Leave alpha untouched...
213 normScaleFactors[3] = 1.0f;
214 normOffsets[3] = 0.0f;
215 } else { // (numFactors == 4)
216 normScaleFactors = origScaleFactors;
217 normOffsets = origOffsets;
218 }
219
220 // The user-provided offsets are specified in the range
221 // of each source color band, but the OpenGL shader only wants
222 // to deal with data in the range [0,1], so we need to normalize
223 // each offset value to the range [0,1] here.
224 if (srcCM.getNumComponents() == 1) {
225 // Gray data
226 int nBits = srcCM.getComponentSize(0);
227 int maxValue = (1 << nBits) - 1;
228 for (int i = 0; i < 3; i++) {
229 normOffsets[i] /= maxValue;
230 }
231 } else {
232 // RGB(A) data
233 for (int i = 0; i < srcCM.getNumComponents(); i++) {
234 int nBits = srcCM.getComponentSize(i);
235 int maxValue = (1 << nBits) - 1;
236 normOffsets[i] /= maxValue;
237 }
238 }
239
240 int sizeofFloat = 4;
241 int totalBytesRequired = 4 + 8 + 4 + (4 * sizeofFloat * 2);
242
243 RenderBuffer buf = rq.getBuffer();
244 rq.ensureCapacityAndAlignment(totalBytesRequired, 4);
245 buf.putInt(ENABLE_RESCALE_OP);
246 buf.putLong(srcData.getNativeOps());
247 buf.putInt(nonPremult ? 1 : 0);
248 buf.put(normScaleFactors);
249 buf.put(normOffsets);
250 }
251
252 private static void disableRescaleOp(RenderQueue rq) {
253 // assert rq.lock.isHeldByCurrentThread();
254 RenderBuffer buf = rq.getBuffer();
255 rq.ensureCapacity(4);
256 buf.putInt(DISABLE_RESCALE_OP);
257 }
258
259/**************************** LookupOp support ******************************/
260
261 public static boolean isLookupOpValid(LookupOp lop,
262 BufferedImage srcImg)
263 {
264 LookupTable table = lop.getTable();
265 int numComps = table.getNumComponents();
266 ColorModel srcCM = srcImg.getColorModel();
267
268 if (srcCM instanceof IndexColorModel) {
269 throw new
270 IllegalArgumentException("LookupOp cannot be "+
271 "performed on an indexed image");
272 }
273 if (numComps != 1 &&
274 numComps != srcCM.getNumComponents() &&
275 numComps != srcCM.getNumColorComponents())
276 {
277 throw new IllegalArgumentException("Number of arrays in the "+
278 " lookup table ("+
279 numComps+
280 ") is not compatible with"+
281 " the src image: "+srcImg);
282 }
283
284 int csType = srcCM.getColorSpace().getType();
285 if (csType != ColorSpace.TYPE_RGB &&
286 csType != ColorSpace.TYPE_GRAY)
287 {
288 // Not prepared to deal with other color spaces
289 return false;
290 }
291
292 if (numComps == 2 || numComps > 4) {
293 // Not really prepared to handle this at the native level, so...
294 return false;
295 }
296
297 // The LookupTable spec says that "all arrays must be the
298 // same size" but unfortunately the constructors do not
299 // enforce that. Also, our native code only works with
300 // arrays no larger than 256 elements, so check both of
301 // these restrictions here.
302 if (table instanceof ByteLookupTable) {
303 byte[][] data = ((ByteLookupTable)table).getTable();
304 for (int i = 1; i < data.length; i++) {
305 if (data[i].length > 256 ||
306 data[i].length != data[i-1].length)
307 {
308 return false;
309 }
310 }
311 } else if (table instanceof ShortLookupTable) {
312 short[][] data = ((ShortLookupTable)table).getTable();
313 for (int i = 1; i < data.length; i++) {
314 if (data[i].length > 256 ||
315 data[i].length != data[i-1].length)
316 {
317 return false;
318 }
319 }
320 } else {
321 return false;
322 }
323
324 return true;
325 }
326
327 private static void enableLookupOp(RenderQueue rq,
328 SurfaceData srcData,
329 BufferedImage srcImg,
330 LookupOp lop)
331 {
332 // assert rq.lock.isHeldByCurrentThread();
333 boolean nonPremult =
334 srcImg.getColorModel().hasAlpha() &&
335 srcImg.isAlphaPremultiplied();
336
337 LookupTable table = lop.getTable();
338 int numBands = table.getNumComponents();
339 int offset = table.getOffset();
340 int bandLength;
341 int bytesPerElem;
342 boolean shortData;
343
344 if (table instanceof ShortLookupTable) {
345 short[][] data = ((ShortLookupTable)table).getTable();
346 bandLength = data[0].length;
347 bytesPerElem = 2;
348 shortData = true;
349 } else { // (table instanceof ByteLookupTable)
350 byte[][] data = ((ByteLookupTable)table).getTable();
351 bandLength = data[0].length;
352 bytesPerElem = 1;
353 shortData = false;
354 }
355
356 // Adjust the LUT length so that it ends on a 4-byte boundary
357 int totalLutBytes = numBands * bandLength * bytesPerElem;
358 int paddedLutBytes = (totalLutBytes + 3) & (~3);
359 int padding = paddedLutBytes - totalLutBytes;
360 int totalBytesRequired = 4 + 8 + 20 + paddedLutBytes;
361
362 RenderBuffer buf = rq.getBuffer();
363 rq.ensureCapacityAndAlignment(totalBytesRequired, 4);
364 buf.putInt(ENABLE_LOOKUP_OP);
365 buf.putLong(srcData.getNativeOps());
366 buf.putInt(nonPremult ? 1 : 0);
367 buf.putInt(shortData ? 1 : 0);
368 buf.putInt(numBands);
369 buf.putInt(bandLength);
370 buf.putInt(offset);
371 if (shortData) {
372 short[][] data = ((ShortLookupTable)table).getTable();
373 for (int i = 0; i < numBands; i++) {
374 buf.put(data[i]);
375 }
376 } else {
377 byte[][] data = ((ByteLookupTable)table).getTable();
378 for (int i = 0; i < numBands; i++) {
379 buf.put(data[i]);
380 }
381 }
382 if (padding != 0) {
383 buf.position(buf.position() + padding);
384 }
385 }
386
387 private static void disableLookupOp(RenderQueue rq) {
388 // assert rq.lock.isHeldByCurrentThread();
389 RenderBuffer buf = rq.getBuffer();
390 rq.ensureCapacity(4);
391 buf.putInt(DISABLE_LOOKUP_OP);
392 }
393}