001: /*
002: * $RCSfile: JDKWorkarounds.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.1 $
009: * $Date: 2005/02/11 04:57:00 $
010: * $State: Exp $
011: */
012: package com.sun.media.jai.util;
013:
014: import java.awt.*;
015: import java.awt.image.*;
016: import java.util.*;
017: import javax.media.jai.*;
018:
019: // Workaround Repository for JDK bugs.
020:
021: public final class JDKWorkarounds {
022:
023: private JDKWorkarounds() {
024: }
025:
026: /**
027: * Faster implementation of setRect for bilevel Rasters
028: * with a SinglePixelPackedSampleModel and DataBufferByte.
029: * Based on sun.awt.image.BytePackedRaster.setDataElements
030: * (JDK1.3 beta version), with improvements.
031: */
032: private static boolean setRectBilevel(WritableRaster dstRaster,
033: Raster srcRaster, int dx, int dy) {
034: int width = srcRaster.getWidth();
035: int height = srcRaster.getHeight();
036: int srcOffX = srcRaster.getMinX();
037: int srcOffY = srcRaster.getMinY();
038: int dstOffX = dx + srcOffX;
039: int dstOffY = dy + srcOffY;
040:
041: int dminX = dstRaster.getMinX();
042: int dminY = dstRaster.getMinY();
043: int dwidth = dstRaster.getWidth();
044: int dheight = dstRaster.getHeight();
045:
046: // Clip to dstRaster
047: if (dstOffX + width > dminX + dwidth) {
048: width = dminX + dwidth - dstOffX;
049: }
050: if (dstOffY + height > dminY + dheight) {
051: height = dminY + dheight - dstOffY;
052: }
053:
054: //
055: // This implementation works but is not as efficient as the one
056: // below which is commented out. In terms of performance, cobbling
057: // a 1728x2376 bit image with 128x144 tiles took the following
058: // amount of time for four cases:
059: //
060: // WritableRaster.setRect() 19756
061: // Aligned optimal case 5645
062: // Unaligned optimal case 6644
063: // Case using ImageUtil 7500
064: //
065: // So this case gives intermediate speed performance closer to the
066: // optimal case than to the JDK. It will likely use more memory
067: // however. On the other hand this approach covers all data types.
068: //
069: Rectangle rect = new Rectangle(dstOffX, dstOffY, width, height);
070: byte[] binaryData = ImageUtil.getPackedBinaryData(srcRaster,
071: rect);
072: ImageUtil.setPackedBinaryData(binaryData, dstRaster, rect);
073:
074: /* XXX BEGIN: Commented out as it gives vertical lines in cobbling
075: data. This gives optimal performance for the case of byte-to-byte
076: data. For non-byte data the sub-optimal solution above (using
077: the ImageUtil packed routines) should be used. Note that this
078: commented out section includes a few bug fixes compared with the
079: original code in the previous SCCS version. bpb 6/21/2000
080: MultiPixelPackedSampleModel srcMPPSM =
081: (MultiPixelPackedSampleModel)srcRaster.getSampleModel();
082: MultiPixelPackedSampleModel dstMPPSM =
083: (MultiPixelPackedSampleModel)dstRaster.getSampleModel();
084:
085: DataBufferByte srcDBB = (DataBufferByte)srcRaster.getDataBuffer();
086: DataBufferByte dstDBB = (DataBufferByte)dstRaster.getDataBuffer();
087:
088: byte[] srcData = srcDBB.getData();
089: byte[] dstData = dstDBB.getData();
090:
091: int srcTransX = srcRaster.getSampleModelTranslateX();
092: int srcTransY = srcRaster.getSampleModelTranslateY();
093: int srcDataBitOffset = srcMPPSM.getDataBitOffset();
094: int srcScanlineStride = srcMPPSM.getScanlineStride();
095:
096: int srcYOffset = (srcOffY - srcTransY)*srcScanlineStride;
097: int srcXOffset = srcDataBitOffset + (srcOffX - srcTransX);
098:
099: int dstTransX = dstRaster.getSampleModelTranslateX();
100: int dstTransY = dstRaster.getSampleModelTranslateY();
101: int dstDataBitOffset = dstMPPSM.getDataBitOffset();
102: int dstScanlineStride = dstMPPSM.getScanlineStride();
103:
104: int dstYOffset = (dstOffY - dstTransY)*dstScanlineStride;
105: int dstXOffset = dstDataBitOffset + (dstOffX - dstTransX);
106:
107: int inbit = srcYOffset*8 + srcXOffset;
108: int outbit = dstYOffset*8 + dstXOffset;
109:
110: if ((inbit & 7) == (outbit & 7)) {
111: // Aligned case
112: int copybits = width;
113: int bits = inbit & 7;
114: if (bits != 0) {
115: // Copy partial bytes on left
116: int inbyte = inbit >> 3;
117: int outbyte = outbit >> 3;
118: int mask = 0xff >> bits;
119: bits = 8 - bits;
120: if (copybits < bits) {
121: mask &= (mask << (8 - copybits));
122: bits = copybits;
123: }
124: for (int j = 0; j < height; j++) {
125: int element = dstData[outbyte];
126: element &= ~mask;
127: element |= (srcData[inbyte] & mask);
128: dstData[outbyte] = (byte) element;
129: inbyte += srcScanlineStride;
130: outbyte += dstScanlineStride;
131: }
132: inbit += bits;
133: outbit += bits;
134: copybits -= bits;
135: }
136: if (copybits >= 8) {
137: // Copy whole bytes
138: int inbyte = inbit >> 3;
139: int outbyte = outbit >> 3;
140: int copybytes = copybits >> 3;
141:
142: if (copybytes == srcScanlineStride &&
143: srcScanlineStride == dstScanlineStride) {
144: System.arraycopy(srcData, inbyte,
145: dstData, outbyte,
146: srcScanlineStride*height);
147: } else {
148: for (int j = 0; j < height; j++) {
149: System.arraycopy(srcData, inbyte,
150: dstData, outbyte,
151: copybytes);
152: inbyte += srcScanlineStride;
153: outbyte += dstScanlineStride;
154: }
155: }
156: bits = copybytes * 8;
157: inbit += bits;
158: outbit += bits;
159: copybits -= bits;
160: }
161: if (copybits > 0) {
162: // Copy partial bytes on right
163: int inbyte = inbit >> 3;
164: int outbyte = outbit >> 3;
165: int mask = (0xff00 >> copybits) & 0xff;
166: for (int j = 0; j < height; j++) {
167: int element = dstData[outbyte];
168: element &= ~mask;
169: element |= (srcData[inbyte] & mask);
170: dstData[outbyte] = (byte) element;
171: inbyte += srcScanlineStride;
172: outbyte += dstScanlineStride;
173: }
174: }
175: } else {
176: // Unaligned case
177: for (int j = 0; j < height; j++) {
178: int save_inbit = inbit;
179: int save_outbit = outbit;
180: int copybits = width;
181:
182: int inbyte, outbyte;
183: int mask;
184:
185: int bits = outbit & 7;
186: if (bits > 0) {
187: inbyte = inbit >> 8;
188: outbyte = outbit >> 8;
189: mask = 0xff >> bits;
190:
191: if (copybits < bits) {
192: mask &= mask << (8 - copybits);
193: bits = copybits;
194: }
195: int element = dstData[outbyte];
196: element &= ~mask;
197: element |= (srcData[inbyte] & mask);
198: dstData[outbyte] = (byte) element;
199:
200: inbit += bits;
201: outbit += bits;
202: copybits -= bits;
203: }
204:
205: if (copybits == 0) {
206: continue;
207: }
208:
209: int shift0 = inbit & 7;
210: int shift1 = 7 - shift0;
211: int mask1 = 0xff >>> shift1;
212:
213: inbyte = inbit >> 3;
214: outbyte = outbit >> 3;
215:
216: int srcData0 = srcData[inbyte];
217: int lastIndex = srcData.length - 1;
218: while (copybits >= 8 && inbyte < lastIndex) {
219: int srcData1 = srcData[inbyte + 1];
220: int val = (srcData0 << shift0) |
221: ((srcData1 >>> shift1) & mask1);
222: srcData0 = srcData1;
223: dstData[outbyte] = (byte)val;
224:
225: ++inbyte;
226: ++outbyte;
227: inbit += 8;
228: outbit += 8;
229: copybits -= 8;
230: }
231:
232: if (copybits > 0) {
233: mask = (0xff00 >> copybits) & 0xff;
234:
235: int element = dstData[outbyte];
236: element &= ~mask;
237: element |= ((srcData[inbyte] << shift0) & mask);
238: dstData[outbyte] = (byte)(element & 0xFF);
239: }
240:
241: inbit = save_inbit + 8*srcScanlineStride;
242: outbit = save_outbit + 8*dstScanlineStride;
243: }
244: }
245: XXX END */
246:
247: return true;
248: }
249:
250: // Workarounds for WritableRaster.setRect bug (4250270) in JDK 1.2.
251: // Also filed as bug 4250273 against JAI.
252:
253: public static void setRect(WritableRaster dstRaster,
254: Raster srcRaster) {
255: setRect(dstRaster, srcRaster, 0, 0);
256: }
257:
258: public static void setRect(WritableRaster dstRaster,
259: Raster srcRaster, int dx, int dy) {
260: // Special case for bilevel Rasters
261: SampleModel srcSampleModel = srcRaster.getSampleModel();
262: SampleModel dstSampleModel = dstRaster.getSampleModel();
263: if (srcSampleModel instanceof MultiPixelPackedSampleModel
264: && dstSampleModel instanceof MultiPixelPackedSampleModel) {
265: MultiPixelPackedSampleModel srcMPPSM = (MultiPixelPackedSampleModel) srcSampleModel;
266: MultiPixelPackedSampleModel dstMPPSM = (MultiPixelPackedSampleModel) dstSampleModel;
267:
268: DataBuffer srcDB = srcRaster.getDataBuffer();
269: DataBuffer dstDB = srcRaster.getDataBuffer();
270:
271: if (srcDB instanceof DataBufferByte
272: && dstDB instanceof DataBufferByte
273: && srcMPPSM.getPixelBitStride() == 1
274: && dstMPPSM.getPixelBitStride() == 1) {
275: if (setRectBilevel(dstRaster, srcRaster, dx, dy)) {
276: return;
277: }
278: }
279: }
280:
281: // Use the regular JDK routines for everything else except
282: // float and double images.
283: int dataType = dstRaster.getSampleModel().getDataType();
284: if (dataType != DataBuffer.TYPE_FLOAT
285: && dataType != DataBuffer.TYPE_DOUBLE) {
286: dstRaster.setRect(dx, dy, srcRaster);
287: return;
288: }
289:
290: int width = srcRaster.getWidth();
291: int height = srcRaster.getHeight();
292: int srcOffX = srcRaster.getMinX();
293: int srcOffY = srcRaster.getMinY();
294: int dstOffX = dx + srcOffX;
295: int dstOffY = dy + srcOffY;
296:
297: int dminX = dstRaster.getMinX();
298: int dminY = dstRaster.getMinY();
299: int dwidth = dstRaster.getWidth();
300: int dheight = dstRaster.getHeight();
301:
302: // Clip to dstRaster
303: if (dstOffX + width > dminX + dwidth) {
304: width = dminX + dwidth - dstOffX;
305: }
306: if (dstOffY + height > dminY + dheight) {
307: height = dminY + dheight - dstOffY;
308: }
309:
310: switch (srcRaster.getSampleModel().getDataType()) {
311: case DataBuffer.TYPE_BYTE:
312: case DataBuffer.TYPE_SHORT:
313: case DataBuffer.TYPE_USHORT:
314: case DataBuffer.TYPE_INT:
315: int[] iData = null;
316: for (int startY = 0; startY < height; startY++) {
317: // Grab one scanline at a time
318: iData = srcRaster.getPixels(srcOffX, srcOffY + startY,
319: width, 1, iData);
320: dstRaster.setPixels(dstOffX, dstOffY + startY, width,
321: 1, iData);
322: }
323: break;
324:
325: case DataBuffer.TYPE_FLOAT:
326: float[] fData = null;
327: for (int startY = 0; startY < height; startY++) {
328: fData = srcRaster.getPixels(srcOffX, srcOffY + startY,
329: width, 1, fData);
330: dstRaster.setPixels(dstOffX, dstOffY + startY, width,
331: 1, fData);
332: }
333: break;
334:
335: case DataBuffer.TYPE_DOUBLE:
336: double[] dData = null;
337: for (int startY = 0; startY < height; startY++) {
338: // Grab one scanline at a time
339: dData = srcRaster.getPixels(srcOffX, srcOffY + startY,
340: width, 1, dData);
341: dstRaster.setPixels(dstOffX, dstOffY + startY, width,
342: 1, dData);
343: }
344: break;
345: }
346: }
347:
348: /**
349: * Workaround for JDK 1.3 bug 4326636 (bpb 30 March 2000).
350: *
351: * Check whether the given SampleModel and ColorModel are compatible.
352: *
353: * This is required because in JDK 1.3 the ComponentColorModel
354: * implementation of isCompatibleSampleModel() only checks whether
355: * the SampleModel is a ComponentSampleModel with the same transferType
356: * as the ColorModel. No check of the number of components or bit
357: * depth is effected.
358: *
359: * @throws IllegalArgumentException if either parameter is null.
360: */
361: public static boolean areCompatibleDataModels(SampleModel sm,
362: ColorModel cm) {
363: if (sm == null || cm == null) {
364: throw new IllegalArgumentException(JaiI18N
365: .getString("JDKWorkarounds0"));
366: }
367:
368: // Call the method we should be using instead of this workaround.
369: // This checks the compatibility of the transferType and possibly
370: // other quantities.
371: if (!cm.isCompatibleSampleModel(sm)) {
372: return false;
373: }
374:
375: // This if-block adds the tests performed in
376: // ComponentColorModel.isCompatibleRaster() but not in
377: // ComponentColorModel.isCompatibleSampleModel().
378: // These tests might duplicate the implementation of some
379: // subclasses of ComponentColorModel.
380: if (cm instanceof ComponentColorModel) {
381: // Check the number of samples per pixel.
382: int numBands = sm.getNumBands();
383: if (numBands != cm.getNumComponents()) {
384: return false;
385: }
386:
387: // Check adequate depth. This should work for
388: // FloatDoubleColorModel as well because
389: // SampleModel.getSampleSize() should return 32 or 64 as
390: // it gets the size from the DataBuffer object and
391: // ColorModel.getComponentSize() returns the number of bits
392: // which are set to 32 or 64 as a function of the transferType.
393: for (int b = 0; b < numBands; b++) {
394: if (sm.getSampleSize(b) < cm.getComponentSize(b)) {
395: return false;
396: }
397: }
398: }
399:
400: // Got this far so return true.
401: return true;
402: }
403: }
|