1 /*
2  * $RCSfile: JDKWorkarounds.java,v $
3  *
4  * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
5  *
6  * Use is subject to license terms.
7  *
8  * $Revision: 1.1 $
9  * $Date: 2005/02/11 04:57:00 $
10  * $State: Exp $
11  */
12 package com.lightcrafts.media.jai.util;
13 import java.awt.*;
14 import java.awt.image.*;
15 
16 //  Workaround Repository for JDK bugs.
17 
18 public final class JDKWorkarounds {
19 
JDKWorkarounds()20     private JDKWorkarounds() {}
21 
22     /**
23      * Faster implementation of setRect for bilevel Rasters
24      * with a SinglePixelPackedSampleModel and DataBufferByte.
25      * Based on sun.awt.image.BytePackedRaster.setDataElements
26      * (JDK1.3 beta version), with improvements.
27      */
setRectBilevel(WritableRaster dstRaster, Raster srcRaster, int dx, int dy)28     private static boolean setRectBilevel(WritableRaster dstRaster,
29                                           Raster srcRaster,
30                                           int dx, int dy) {
31         int width  = srcRaster.getWidth();
32         int height = srcRaster.getHeight();
33         int srcOffX = srcRaster.getMinX();
34         int srcOffY = srcRaster.getMinY();
35         int dstOffX = dx+srcOffX;
36         int dstOffY = dy+srcOffY;
37 
38         int dminX = dstRaster.getMinX();
39         int dminY = dstRaster.getMinY();
40         int dwidth = dstRaster.getWidth();
41         int dheight = dstRaster.getHeight();
42 
43         // Clip to dstRaster
44         if (dstOffX + width > dminX + dwidth) {
45             width = dminX + dwidth - dstOffX;
46         }
47         if (dstOffY + height > dminY + dheight) {
48             height = dminY + dheight - dstOffY;
49         }
50 
51         //
52         // This implementation works but is not as efficient as the one
53         // below which is commented out. In terms of performance, cobbling
54         // a 1728x2376 bit image with 128x144 tiles took the following
55         // amount of time for four cases:
56         //
57         // WritableRaster.setRect() 19756
58         // Aligned optimal case     5645
59         // Unaligned optimal case   6644
60         // Case using ImageUtil     7500
61         //
62         // So this case gives intermediate speed performance closer to the
63         // optimal case than to the JDK. It will likely use more memory
64         // however. On the other hand this approach covers all data types.
65         //
66         Rectangle rect = new Rectangle(dstOffX, dstOffY, width, height);
67         byte[] binaryData = ImageUtil.getPackedBinaryData(srcRaster, rect);
68         ImageUtil.setPackedBinaryData(binaryData, dstRaster, rect);
69 
70         /* XXX BEGIN: Commented out as it gives vertical lines in cobbling
71            data. This gives optimal performance for the case of byte-to-byte
72            data. For non-byte data the sub-optimal solution above (using
73            the ImageUtil packed routines) should be used. Note that this
74            commented out section includes a few bug fixes compared with the
75            original code in the previous SCCS version. bpb 6/21/2000
76         MultiPixelPackedSampleModel srcMPPSM =
77             (MultiPixelPackedSampleModel)srcRaster.getSampleModel();
78         MultiPixelPackedSampleModel dstMPPSM =
79             (MultiPixelPackedSampleModel)dstRaster.getSampleModel();
80 
81         DataBufferByte srcDBB = (DataBufferByte)srcRaster.getDataBuffer();
82         DataBufferByte dstDBB = (DataBufferByte)dstRaster.getDataBuffer();
83 
84         byte[] srcData = srcDBB.getData();
85         byte[] dstData = dstDBB.getData();
86 
87         int srcTransX = srcRaster.getSampleModelTranslateX();
88         int srcTransY = srcRaster.getSampleModelTranslateY();
89         int srcDataBitOffset = srcMPPSM.getDataBitOffset();
90         int srcScanlineStride = srcMPPSM.getScanlineStride();
91 
92         int srcYOffset = (srcOffY - srcTransY)*srcScanlineStride;
93         int srcXOffset = srcDataBitOffset + (srcOffX - srcTransX);
94 
95         int dstTransX = dstRaster.getSampleModelTranslateX();
96         int dstTransY = dstRaster.getSampleModelTranslateY();
97         int dstDataBitOffset = dstMPPSM.getDataBitOffset();
98         int dstScanlineStride = dstMPPSM.getScanlineStride();
99 
100         int dstYOffset = (dstOffY - dstTransY)*dstScanlineStride;
101         int dstXOffset = dstDataBitOffset + (dstOffX - dstTransX);
102 
103         int inbit = srcYOffset*8 + srcXOffset;
104         int outbit = dstYOffset*8 + dstXOffset;
105 
106         if ((inbit & 7) == (outbit & 7)) {
107             // Aligned case
108             int copybits = width;
109             int bits = inbit & 7;
110             if (bits != 0) {
111                 // Copy partial bytes on left
112                 int inbyte = inbit >> 3;
113                 int outbyte = outbit >> 3;
114                 int mask = 0xff >> bits;
115                 bits = 8 - bits;
116                 if (copybits < bits) {
117                     mask &= (mask << (8 - copybits));
118                     bits = copybits;
119                 }
120                 for (int j = 0; j < height; j++) {
121                     int element = dstData[outbyte];
122                     element &= ~mask;
123                     element |= (srcData[inbyte] & mask);
124                     dstData[outbyte] = (byte) element;
125                     inbyte += srcScanlineStride;
126                     outbyte += dstScanlineStride;
127                 }
128                 inbit += bits;
129                 outbit += bits;
130                 copybits -= bits;
131             }
132             if (copybits >= 8) {
133                 // Copy whole bytes
134                 int inbyte = inbit >> 3;
135                 int outbyte = outbit >> 3;
136                 int copybytes = copybits >> 3;
137 
138                 if (copybytes == srcScanlineStride &&
139                     srcScanlineStride == dstScanlineStride) {
140                     System.arraycopy(srcData, inbyte,
141                                      dstData, outbyte,
142                                      srcScanlineStride*height);
143                 } else {
144                     for (int j = 0; j < height; j++) {
145                         System.arraycopy(srcData, inbyte,
146                                          dstData, outbyte,
147                                          copybytes);
148                         inbyte += srcScanlineStride;
149                         outbyte += dstScanlineStride;
150                     }
151                 }
152                 bits = copybytes * 8;
153                 inbit += bits;
154                 outbit += bits;
155                 copybits -= bits;
156             }
157             if (copybits > 0) {
158                 // Copy partial bytes on right
159                 int inbyte = inbit >> 3;
160                 int outbyte = outbit >> 3;
161                 int mask = (0xff00 >> copybits) & 0xff;
162                 for (int j = 0; j < height; j++) {
163                     int element = dstData[outbyte];
164                     element &= ~mask;
165                     element |= (srcData[inbyte] & mask);
166                     dstData[outbyte] = (byte) element;
167                     inbyte += srcScanlineStride;
168                     outbyte += dstScanlineStride;
169                 }
170             }
171         } else {
172             // Unaligned case
173             for (int j = 0; j < height; j++) {
174                 int save_inbit = inbit;
175                 int save_outbit = outbit;
176                 int copybits = width;
177 
178                 int inbyte, outbyte;
179                 int mask;
180 
181                 int bits = outbit & 7;
182                 if (bits > 0) {
183                     inbyte = inbit >> 8;
184                     outbyte = outbit >> 8;
185                     mask = 0xff >> bits;
186 
187                     if (copybits < bits) {
188                         mask &= mask << (8 - copybits);
189                         bits = copybits;
190                     }
191                     int element = dstData[outbyte];
192                     element &= ~mask;
193                     element |= (srcData[inbyte] & mask);
194                     dstData[outbyte] = (byte) element;
195 
196                     inbit += bits;
197                     outbit += bits;
198                     copybits -= bits;
199                 }
200 
201                 if (copybits == 0) {
202                     continue;
203                 }
204 
205                 int shift0 = inbit & 7;
206                 int shift1 = 7 - shift0;
207                 int mask1 = 0xff >>> shift1;
208 
209                 inbyte = inbit >> 3;
210                 outbyte = outbit >> 3;
211 
212                 int srcData0 = srcData[inbyte];
213                 int lastIndex = srcData.length - 1;
214                 while (copybits >= 8 && inbyte < lastIndex) {
215                     int srcData1 = srcData[inbyte + 1];
216                     int val = (srcData0 << shift0) |
217                         ((srcData1 >>> shift1) & mask1);
218                     srcData0 = srcData1;
219                     dstData[outbyte] = (byte)val;
220 
221                     ++inbyte;
222                     ++outbyte;
223                     inbit += 8;
224                     outbit += 8;
225                     copybits -= 8;
226                 }
227 
228                 if (copybits > 0) {
229                     mask = (0xff00 >> copybits) & 0xff;
230 
231                     int element = dstData[outbyte];
232                     element &= ~mask;
233                     element |= ((srcData[inbyte] << shift0) & mask);
234                     dstData[outbyte] = (byte)(element & 0xFF);
235                 }
236 
237                 inbit = save_inbit + 8*srcScanlineStride;
238                 outbit = save_outbit + 8*dstScanlineStride;
239             }
240         }
241         XXX END */
242 
243         return true;
244     }
245 
246     // Workarounds for WritableRaster.setRect bug (4250270) in JDK 1.2.
247     // Also filed as bug 4250273 against JAI.
248 
setRect(WritableRaster dstRaster, Raster srcRaster)249     public static void setRect(WritableRaster dstRaster, Raster srcRaster) {
250         setRect(dstRaster, srcRaster, 0, 0);
251     }
252 
setRect(WritableRaster dstRaster, Raster srcRaster, int dx, int dy)253     public static void setRect(WritableRaster dstRaster, Raster srcRaster,
254                         int dx, int dy) {
255         // Special case for bilevel Rasters
256         SampleModel srcSampleModel = srcRaster.getSampleModel();
257         SampleModel dstSampleModel = dstRaster.getSampleModel();
258         if (srcSampleModel instanceof MultiPixelPackedSampleModel &&
259             dstSampleModel instanceof MultiPixelPackedSampleModel) {
260             MultiPixelPackedSampleModel srcMPPSM =
261                 (MultiPixelPackedSampleModel)srcSampleModel;
262             MultiPixelPackedSampleModel dstMPPSM =
263                 (MultiPixelPackedSampleModel)dstSampleModel;
264 
265             DataBuffer srcDB = srcRaster.getDataBuffer();
266             DataBuffer dstDB = srcRaster.getDataBuffer();
267 
268             if (srcDB instanceof DataBufferByte &&
269                 dstDB instanceof DataBufferByte &&
270                 srcMPPSM.getPixelBitStride() == 1 &&
271                 dstMPPSM.getPixelBitStride() == 1) {
272                 if (setRectBilevel(dstRaster, srcRaster, dx, dy)) {
273                     return;
274                 }
275             }
276         }
277 
278         // Use the regular JDK routines for everything else except
279         // float and double images.
280         int dataType = dstRaster.getSampleModel().getDataType();
281         if (dataType != DataBuffer.TYPE_FLOAT &&
282             dataType != DataBuffer.TYPE_DOUBLE) {
283             dstRaster.setRect(dx, dy, srcRaster);
284             return;
285         }
286 
287         int width  = srcRaster.getWidth();
288         int height = srcRaster.getHeight();
289         int srcOffX = srcRaster.getMinX();
290         int srcOffY = srcRaster.getMinY();
291         int dstOffX = dx+srcOffX;
292         int dstOffY = dy+srcOffY;
293 
294         int dminX = dstRaster.getMinX();
295         int dminY = dstRaster.getMinY();
296         int dwidth = dstRaster.getWidth();
297         int dheight = dstRaster.getHeight();
298 
299         // Clip to dstRaster
300         if (dstOffX + width > dminX + dwidth) {
301             width = dminX + dwidth - dstOffX;
302         }
303         if (dstOffY + height > dminY + dheight) {
304             height = dminY + dheight - dstOffY;
305         }
306 
307         switch (srcRaster.getSampleModel().getDataType()) {
308         case DataBuffer.TYPE_BYTE:
309         case DataBuffer.TYPE_SHORT:
310         case DataBuffer.TYPE_USHORT:
311         case DataBuffer.TYPE_INT:
312             int[] iData = null;
313             for (int startY=0; startY < height; startY++) {
314                 // Grab one scanline at a time
315                 iData =
316                     srcRaster.getPixels(srcOffX, srcOffY+startY, width, 1,
317                                         iData);
318                 dstRaster.setPixels(dstOffX, dstOffY+startY, width, 1, iData);
319             }
320             break;
321 
322         case DataBuffer.TYPE_FLOAT:
323             float[] fData = null;
324             for (int startY=0; startY < height; startY++) {
325                 fData =
326                     srcRaster.getPixels(srcOffX, srcOffY+startY, width, 1,
327                                         fData);
328                 dstRaster.setPixels(dstOffX, dstOffY+startY, width, 1, fData);
329             }
330             break;
331 
332         case DataBuffer.TYPE_DOUBLE:
333             double[] dData = null;
334             for (int startY=0; startY < height; startY++) {
335                 // Grab one scanline at a time
336                 dData =
337                     srcRaster.getPixels(srcOffX, srcOffY+startY, width, 1,
338                                         dData);
339                 dstRaster.setPixels(dstOffX, dstOffY+startY, width, 1, dData);
340             }
341             break;
342         }
343     }
344 
345     /**
346      * Workaround for JDK 1.3 bug 4326636 (bpb 30 March 2000).
347      *
348      * Check whether the given SampleModel and ColorModel are compatible.
349      *
350      * This is required because in JDK 1.3 the ComponentColorModel
351      * implementation of isCompatibleSampleModel() only checks whether
352      * the SampleModel is a ComponentSampleModel with the same transferType
353      * as the ColorModel. No check of the number of components or bit
354      * depth is effected.
355      *
356      * @throws IllegalArgumentException if either parameter is null.
357      */
areCompatibleDataModels(SampleModel sm, ColorModel cm)358     public static boolean areCompatibleDataModels(SampleModel sm,
359                                                   ColorModel cm) {
360         if(sm == null || cm == null) {
361             throw new
362                 IllegalArgumentException(JaiI18N.getString("JDKWorkarounds0"));
363         }
364 
365         // Call the method we should be using instead of this workaround.
366         // This checks the compatibility of the transferType and possibly
367         // other quantities.
368         if(!cm.isCompatibleSampleModel(sm)) {
369             return false;
370         }
371 
372         // This if-block adds the tests performed in
373         // ComponentColorModel.isCompatibleRaster() but not in
374         // ComponentColorModel.isCompatibleSampleModel().
375         // These tests might duplicate the implementation of some
376         // subclasses of ComponentColorModel.
377         if(cm instanceof ComponentColorModel) {
378             // Check the number of samples per pixel.
379             int numBands = sm.getNumBands();
380             if (numBands != cm.getNumComponents()) {
381                 return false;
382             }
383 
384             // Check adequate depth. This should work for
385             // FloatDoubleColorModel as well because
386             // SampleModel.getSampleSize() should return 32 or 64 as
387             // it gets the size from the DataBuffer object and
388             // ColorModel.getComponentSize() returns the number of bits
389             // which are set to 32 or 64 as a function of the transferType.
390             for (int b = 0; b < numBands; b++) {
391                 if (sm.getSampleSize(b) < cm.getComponentSize(b)) {
392                     return false;
393                 }
394             }
395         }
396 
397         // Got this far so return true.
398         return true;
399     }
400 }
401