001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Oleg V. Khaschansky
019: * @version $Revision$
020: *
021: * @date: Oct 14, 2005
022: */package java.awt.image;
023:
024: import java.awt.*;
025: import java.awt.geom.Rectangle2D;
026: import java.awt.geom.Point2D;
027: import java.util.Arrays;
028:
029: import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor;
030: import org.apache.harmony.awt.internal.nls.Messages;
031:
032: public class LookupOp implements BufferedImageOp, RasterOp {
033: private final LookupTable lut;
034: private RenderingHints hints;
035:
036: // TODO remove when this field is used
037: @SuppressWarnings("unused")
038: private final boolean canUseIpp;
039:
040: // We don't create levels/values when it is possible to reuse old
041: private int cachedLevels[];
042: private int cachedValues[];
043: // Number of channels for which cache is valid.
044: // If negative number of channels is same as positive but skipAlpha was specified
045: private int validForChannels;
046:
047: static int levelInitializer[] = new int[0x10000];
048:
049: static {
050: // TODO
051: // System.loadLibrary("imageops");
052:
053: for (int i = 1; i <= 0x10000; i++) {
054: levelInitializer[i - 1] = i;
055: }
056: }
057:
058: public LookupOp(LookupTable lookup, RenderingHints hints) {
059: if (lookup == null) {
060: throw new NullPointerException(Messages.getString(
061: "awt.01", "lookup")); //$NON-NLS-1$ //$NON-NLS-2$
062: }
063: lut = lookup;
064: this .hints = hints;
065: canUseIpp = lut instanceof ByteLookupTable
066: || lut instanceof ShortLookupTable;
067: }
068:
069: public final LookupTable getTable() {
070: return lut;
071: }
072:
073: public final RenderingHints getRenderingHints() {
074: return hints;
075: }
076:
077: public final Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
078: if (dstPt == null) {
079: dstPt = new Point2D.Float();
080: }
081:
082: dstPt.setLocation(srcPt);
083: return dstPt;
084: }
085:
086: public final Rectangle2D getBounds2D(Raster src) {
087: return src.getBounds();
088: }
089:
090: public final Rectangle2D getBounds2D(BufferedImage src) {
091: return getBounds2D(src.getRaster());
092: }
093:
094: public WritableRaster createCompatibleDestRaster(Raster src) {
095: return src.createCompatibleWritableRaster();
096: }
097:
098: public BufferedImage createCompatibleDestImage(BufferedImage src,
099: ColorModel dstCM) {
100: if (dstCM == null) {
101: dstCM = src.getColorModel();
102:
103: // Sync transfer type with LUT for component color model
104: if (dstCM instanceof ComponentColorModel) {
105: int transferType = dstCM.getTransferType();
106: if (lut instanceof ByteLookupTable) {
107: transferType = DataBuffer.TYPE_BYTE;
108: } else if (lut instanceof ShortLookupTable) {
109: transferType = DataBuffer.TYPE_SHORT;
110: }
111:
112: dstCM = new ComponentColorModel(dstCM.cs, dstCM
113: .hasAlpha(), dstCM.isAlphaPremultiplied,
114: dstCM.transparency, transferType);
115: }
116: }
117:
118: WritableRaster r = dstCM.isCompatibleSampleModel(src
119: .getSampleModel()) ? src.getRaster()
120: .createCompatibleWritableRaster(src.getWidth(),
121: src.getHeight()) : dstCM
122: .createCompatibleWritableRaster(src.getWidth(), src
123: .getHeight());
124:
125: return new BufferedImage(dstCM, r,
126: dstCM.isAlphaPremultiplied(), null);
127: }
128:
129: public final WritableRaster filter(Raster src, WritableRaster dst) {
130: if (dst == null) {
131: dst = createCompatibleDestRaster(src);
132: } else {
133: if (src.getNumBands() != dst.getNumBands()) {
134: throw new IllegalArgumentException(Messages
135: .getString("awt.237")); //$NON-NLS-1$ }
136: }
137: if (src.getWidth() != dst.getWidth()) {
138: throw new IllegalArgumentException(Messages
139: .getString("awt.28F")); //$NON-NLS-1$ }
140: }
141: if (src.getHeight() != dst.getHeight()) {
142: throw new IllegalArgumentException(Messages
143: .getString("awt.290")); //$NON-NLS-1$ }
144: }
145: }
146:
147: if (lut.getNumComponents() != 1
148: && lut.getNumComponents() != src.getNumBands()) {
149: // awt.238=The number of arrays in the LookupTable does not meet the restrictions
150: throw new IllegalArgumentException(Messages
151: .getString("awt.238")); //$NON-NLS-1$
152: }
153:
154: // TODO
155: // if (!canUseIpp || ippFilter(src, dst, BufferedImage.TYPE_CUSTOM, false) != 0)
156: if (slowFilter(src, dst, false) != 0) {
157: // awt.21F=Unable to transform source
158: throw new ImagingOpException(Messages.getString("awt.21F")); //$NON-NLS-1$
159: }
160:
161: return dst;
162: }
163:
164: public final BufferedImage filter(BufferedImage src,
165: BufferedImage dst) {
166: ColorModel srcCM = src.getColorModel();
167:
168: if (srcCM instanceof IndexColorModel) {
169: // awt.220=Source should not have IndexColorModel
170: throw new IllegalArgumentException(Messages
171: .getString("awt.220")); //$NON-NLS-1$
172: }
173:
174: // Check if the number of scaling factors matches the number of bands
175: int nComponents = srcCM.getNumComponents();
176: int nLUTComponents = lut.getNumComponents();
177: boolean skipAlpha;
178: if (srcCM.hasAlpha()) {
179: if (nLUTComponents == 1
180: || nLUTComponents == nComponents - 1) {
181: skipAlpha = true;
182: } else if (nLUTComponents == nComponents) {
183: skipAlpha = false;
184: } else {
185: // awt.229=Number of components in the LUT does not match the number of bands
186: throw new IllegalArgumentException(Messages
187: .getString("awt.229")); //$NON-NLS-1$
188: }
189: } else if (nLUTComponents == 1 || nLUTComponents == nComponents) {
190: skipAlpha = false;
191: } else {
192: // awt.229=Number of components in the LUT does not match the number of bands
193: throw new IllegalArgumentException(Messages
194: .getString("awt.229")); //$NON-NLS-1$
195: }
196:
197: BufferedImage finalDst = null;
198: if (dst == null) {
199: finalDst = dst;
200: dst = createCompatibleDestImage(src, null);
201: } else {
202: if (src.getWidth() != dst.getWidth()) {
203: throw new IllegalArgumentException(Messages
204: .getString("awt.291")); //$NON-NLS-1$
205: }
206:
207: if (src.getHeight() != dst.getHeight()) {
208: throw new IllegalArgumentException(Messages
209: .getString("awt.292")); //$NON-NLS-1$
210: }
211:
212: if (!srcCM.equals(dst.getColorModel())) {
213: // Treat BufferedImage.TYPE_INT_RGB and
214: // BufferedImage.TYPE_INT_ARGB as same
215: if (!((src.getType() == BufferedImage.TYPE_INT_RGB || src
216: .getType() == BufferedImage.TYPE_INT_ARGB) && (dst
217: .getType() == BufferedImage.TYPE_INT_RGB || dst
218: .getType() == BufferedImage.TYPE_INT_ARGB))) {
219: finalDst = dst;
220: dst = createCompatibleDestImage(src, null);
221: }
222: }
223: }
224:
225: // TODO
226: //if (!canUseIpp || ippFilter(src.getRaster(), dst.getRaster(), src.getType(), skipAlpha) != 0)
227: if (slowFilter(src.getRaster(), dst.getRaster(), skipAlpha) != 0) {
228: // awt.21F=Unable to transform source
229: throw new ImagingOpException(Messages.getString("awt.21F")); //$NON-NLS-1$
230: }
231:
232: if (finalDst != null) {
233: Graphics2D g = finalDst.createGraphics();
234: g.setComposite(AlphaComposite.Src);
235: g.drawImage(dst, 0, 0, null);
236: } else {
237: finalDst = dst;
238: }
239:
240: return dst;
241: }
242:
243: private final int slowFilter(Raster src, WritableRaster dst,
244: boolean skipAlpha) {
245: int minSrcX = src.getMinX();
246: int minDstX = dst.getMinX();
247: int minSrcY = src.getMinY();
248: int minDstY = dst.getMinY();
249:
250: int skippingChannels = skipAlpha ? 1 : 0;
251: int numBands2Process = src.getNumBands() - skippingChannels;
252:
253: int numBands = src.getNumBands();
254: int srcHeight = src.getHeight();
255: int srcWidth = src.getWidth();
256:
257: int[] pixels = null;
258: int offset = lut.getOffset();
259:
260: if (lut instanceof ByteLookupTable) {
261: byte[][] byteData = ((ByteLookupTable) lut).getTable();
262: pixels = src.getPixels(minSrcX, minSrcY, srcWidth,
263: srcHeight, pixels);
264:
265: if (lut.getNumComponents() != 1) {
266: for (int i = 0; i < pixels.length; i += numBands) {
267: for (int b = 0; b < numBands2Process; b++) {
268: pixels[i + b] = byteData[b][pixels[i + b]
269: - offset] & 0xFF;
270: }
271: }
272: } else {
273: for (int i = 0; i < pixels.length; i += numBands) {
274: for (int b = 0; b < numBands2Process; b++) {
275: pixels[i + b] = byteData[0][pixels[i + b]
276: - offset] & 0xFF;
277: }
278: }
279: }
280:
281: dst
282: .setPixels(minDstX, minDstY, srcWidth, srcHeight,
283: pixels);
284: } else if (lut instanceof ShortLookupTable) {
285: short[][] shortData = ((ShortLookupTable) lut).getTable();
286: pixels = src.getPixels(minSrcX, minSrcY, srcWidth,
287: srcHeight, pixels);
288:
289: if (lut.getNumComponents() != 1) {
290: for (int i = 0; i < pixels.length; i += numBands) {
291: for (int b = 0; b < numBands2Process; b++) {
292: pixels[i + b] = shortData[b][pixels[i + b]
293: - offset] & 0xFFFF;
294: }
295: }
296: } else {
297: for (int i = 0; i < pixels.length; i += numBands) {
298: for (int b = 0; b < numBands2Process; b++) {
299: pixels[i + b] = shortData[0][pixels[i + b]
300: - offset] & 0xFFFF;
301: }
302: }
303: }
304:
305: dst
306: .setPixels(minDstX, minDstY, srcWidth, srcHeight,
307: pixels);
308: } else {
309: int pixel[] = new int[src.getNumBands()];
310: int maxY = minSrcY + srcHeight;
311: int maxX = minSrcX + srcWidth;
312: for (int srcY = minSrcY, dstY = minDstY; srcY < maxY; srcY++, dstY++) {
313: for (int srcX = minSrcX, dstX = minDstX; srcX < maxX; srcX++, dstX++) {
314: src.getPixel(srcX, srcY, pixel);
315: lut.lookupPixel(pixel, pixel);
316: dst.setPixel(dstX, dstY, pixel);
317: }
318: }
319: }
320:
321: return 0;
322: }
323:
324: private final void createByteLevels(int channels,
325: boolean skipAlpha, int levels[], int values[],
326: int channelsOrder[]) {
327: byte data[][] = ((ByteLookupTable) lut).getTable();
328: int nLevels = data[0].length;
329: int offset = lut.getOffset();
330:
331: // Use one data array for all channels or use several data arrays
332: int dataIncrement = data.length > 1 ? 1 : 0;
333:
334: for (int ch = 0, dataIdx = 0; ch < channels; dataIdx += dataIncrement, ch++) {
335: int channelOffset = channelsOrder == null ? ch
336: : channelsOrder[ch];
337: int channelBase = nLevels * channelOffset;
338:
339: // Skip last channel if needed, zero values are OK -
340: // no changes to the channel information will be done in IPP
341: if ((channelOffset == channels - 1 && skipAlpha)
342: || (dataIdx >= data.length)) {
343: continue;
344: }
345:
346: System.arraycopy(levelInitializer, offset, levels,
347: channelBase, nLevels);
348: for (int from = 0, to = channelBase; from < nLevels; from++, to++) {
349: values[to] = data[dataIdx][from] & 0xFF;
350: }
351: }
352: }
353:
354: private final void createShortLevels(int channels,
355: boolean skipAlpha, int levels[], int values[],
356: int channelsOrder[]) {
357: short data[][] = ((ShortLookupTable) lut).getTable();
358: int nLevels = data[0].length;
359: int offset = lut.getOffset();
360:
361: // Use one data array for all channels or use several data arrays
362: int dataIncrement = data.length > 1 ? 1 : 0;
363:
364: for (int ch = 0, dataIdx = 0; ch < channels; dataIdx += dataIncrement, ch++) {
365: int channelOffset = channelsOrder == null ? ch
366: : channelsOrder[ch];
367:
368: // Skip last channel if needed, zero values are OK -
369: // no changes to the channel information will be done in IPP
370: if ((channelOffset == channels - 1 && skipAlpha)
371: || (dataIdx >= data.length)) {
372: continue;
373: }
374:
375: int channelBase = nLevels * channelOffset;
376: System.arraycopy(levelInitializer, offset, levels,
377: channelBase, nLevels);
378: for (int from = 0, to = channelBase; from < nLevels; from++, to++) {
379: values[to] = data[dataIdx][from] & 0xFFFF;
380: }
381: }
382: }
383:
384: // TODO remove when this method is used
385: @SuppressWarnings("unused")
386: private final int ippFilter(Raster src, WritableRaster dst,
387: int imageType, boolean skipAlpha) {
388: int res;
389:
390: int srcStride, dstStride;
391: int channels;
392: int offsets[] = null;
393: int channelsOrder[] = null;
394:
395: switch (imageType) {
396: case BufferedImage.TYPE_INT_ARGB:
397: case BufferedImage.TYPE_INT_ARGB_PRE:
398: case BufferedImage.TYPE_INT_RGB: {
399: channels = 4;
400: srcStride = src.getWidth() * 4;
401: dstStride = dst.getWidth() * 4;
402: channelsOrder = new int[] { 2, 1, 0, 3 };
403: break;
404: }
405:
406: case BufferedImage.TYPE_4BYTE_ABGR:
407: case BufferedImage.TYPE_4BYTE_ABGR_PRE:
408: case BufferedImage.TYPE_INT_BGR: {
409: channels = 4;
410: srcStride = src.getWidth() * 4;
411: dstStride = dst.getWidth() * 4;
412: break;
413: }
414:
415: case BufferedImage.TYPE_BYTE_GRAY: {
416: channels = 1;
417: srcStride = src.getWidth();
418: dstStride = dst.getWidth();
419: break;
420: }
421:
422: case BufferedImage.TYPE_3BYTE_BGR: {
423: channels = 3;
424: srcStride = src.getWidth() * 3;
425: dstStride = dst.getWidth() * 3;
426: channelsOrder = new int[] { 2, 1, 0 };
427: break;
428: }
429:
430: case BufferedImage.TYPE_USHORT_GRAY:
431: case BufferedImage.TYPE_USHORT_565_RGB:
432: case BufferedImage.TYPE_USHORT_555_RGB:
433: case BufferedImage.TYPE_BYTE_BINARY: {
434: return slowFilter(src, dst, skipAlpha);
435: }
436:
437: default: {
438: SampleModel srcSM = src.getSampleModel();
439: SampleModel dstSM = dst.getSampleModel();
440:
441: if (srcSM instanceof PixelInterleavedSampleModel
442: && dstSM instanceof PixelInterleavedSampleModel) {
443: // Check PixelInterleavedSampleModel
444: if (srcSM.getDataType() != DataBuffer.TYPE_BYTE
445: || dstSM.getDataType() != DataBuffer.TYPE_BYTE) {
446: return slowFilter(src, dst, skipAlpha);
447: }
448:
449: // Have IPP functions for 1, 3 and 4 channels
450: channels = srcSM.getNumBands();
451: if (!(channels == 1 || channels == 3 || channels == 4)) {
452: return slowFilter(src, dst, skipAlpha);
453: }
454:
455: srcStride = ((ComponentSampleModel) srcSM)
456: .getScanlineStride();
457: dstStride = ((ComponentSampleModel) dstSM)
458: .getScanlineStride();
459:
460: channelsOrder = ((ComponentSampleModel) srcSM)
461: .getBandOffsets();
462: } else if (srcSM instanceof SinglePixelPackedSampleModel
463: && dstSM instanceof SinglePixelPackedSampleModel) {
464: // Check SinglePixelPackedSampleModel
465: SinglePixelPackedSampleModel sppsm1 = (SinglePixelPackedSampleModel) srcSM;
466: SinglePixelPackedSampleModel sppsm2 = (SinglePixelPackedSampleModel) dstSM;
467:
468: channels = sppsm1.getNumBands();
469:
470: // TYPE_INT_RGB, TYPE_INT_ARGB...
471: if (sppsm1.getDataType() != DataBuffer.TYPE_INT
472: || sppsm2.getDataType() != DataBuffer.TYPE_INT
473: || !(channels == 3 || channels == 4)) {
474: return slowFilter(src, dst, skipAlpha);
475: }
476:
477: // Check compatibility of sample models
478: if (!Arrays.equals(sppsm1.getBitOffsets(), sppsm2
479: .getBitOffsets())
480: || !Arrays.equals(sppsm1.getBitMasks(), sppsm2
481: .getBitMasks())) {
482: return slowFilter(src, dst, skipAlpha);
483: }
484:
485: for (int i = 0; i < channels; i++) {
486: if (sppsm1.getSampleSize(i) != 8) {
487: return slowFilter(src, dst, skipAlpha);
488: }
489: }
490:
491: channelsOrder = new int[channels];
492: int bitOffsets[] = sppsm1.getBitOffsets();
493: for (int i = 0; i < channels; i++) {
494: channelsOrder[i] = bitOffsets[i] / 8;
495: }
496:
497: if (channels == 3) { // Don't skip channel now, could be optimized
498: channels = 4;
499: }
500:
501: srcStride = sppsm1.getScanlineStride() * 4;
502: dstStride = sppsm2.getScanlineStride() * 4;
503: } else {
504: return slowFilter(src, dst, skipAlpha);
505: }
506:
507: // Fill offsets if there's a child raster
508: if (src.getParent() != null || dst.getParent() != null) {
509: if (src.getSampleModelTranslateX() != 0
510: || src.getSampleModelTranslateY() != 0
511: || dst.getSampleModelTranslateX() != 0
512: || dst.getSampleModelTranslateY() != 0) {
513: offsets = new int[4];
514: offsets[0] = -src.getSampleModelTranslateX()
515: + src.getMinX();
516: offsets[1] = -src.getSampleModelTranslateY()
517: + src.getMinY();
518: offsets[2] = -dst.getSampleModelTranslateX()
519: + dst.getMinX();
520: offsets[3] = -dst.getSampleModelTranslateY()
521: + dst.getMinY();
522: }
523: }
524: }
525: }
526:
527: int levels[] = null, values[] = null;
528: int channelMultiplier = skipAlpha ? -1 : 1;
529: if (channelMultiplier * channels == validForChannels) { // use existing levels/values
530: levels = cachedLevels;
531: values = cachedValues;
532: } else { // create new levels/values
533: if (lut instanceof ByteLookupTable) {
534: byte data[][] = ((ByteLookupTable) lut).getTable();
535: levels = new int[channels * data[0].length];
536: values = new int[channels * data[0].length];
537: createByteLevels(channels, skipAlpha, levels, values,
538: channelsOrder);
539: } else if (lut instanceof ShortLookupTable) {
540: short data[][] = ((ShortLookupTable) lut).getTable();
541: levels = new int[channels * data[0].length];
542: values = new int[channels * data[0].length];
543: createShortLevels(channels, skipAlpha, levels, values,
544: channelsOrder);
545: }
546:
547: // cache levels/values
548: validForChannels = channelMultiplier * channels;
549: cachedLevels = levels;
550: cachedValues = values;
551: }
552:
553: Object srcData, dstData;
554: AwtImageBackdoorAccessor dbAccess = AwtImageBackdoorAccessor
555: .getInstance();
556: try {
557: srcData = dbAccess.getData(src.getDataBuffer());
558: dstData = dbAccess.getData(dst.getDataBuffer());
559: } catch (IllegalArgumentException e) {
560: return -1; // Unknown data buffer type
561: }
562:
563: res = ippLUT(srcData, src.getWidth(), src.getHeight(),
564: srcStride, dstData, dst.getWidth(), dst.getHeight(),
565: dstStride, levels, values, channels, offsets, false);
566:
567: return res;
568: }
569:
570: final static native int ippLUT(Object src, int srcWidth,
571: int srcHeight, int srcStride, Object dst, int dstWidth,
572: int dstHeight, int dstStride, int levels[], int values[],
573: int channels, int offsets[], boolean linear);
574: }
|