OSDN Git Service

auto import from //depot/cupcake/@135843
[android-x86/frameworks-native.git] / awt / org / apache / harmony / awt / gl / color / NativeImageFormat.java
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 /**
18  * @author Oleg V. Khaschansky
19  * @version $Revision$
20  */
21 package org.apache.harmony.awt.gl.color;
22
23 import java.awt.image.BufferedImage;
24 import java.awt.image.ColorModel;
25 import java.awt.image.ComponentSampleModel;
26 import java.awt.image.DataBuffer;
27 import java.awt.image.Raster;
28 import java.awt.image.SampleModel;
29 import java.awt.image.SinglePixelPackedSampleModel;
30 import java.util.ArrayList;
31
32 import org.apache.harmony.awt.gl.AwtImageBackdoorAccessor;
33 import org.apache.harmony.awt.internal.nls.Messages;
34
35
36 /**
37  * This class converts java color/sample models to the LCMS pixel formats.
38  * It also encapsulates all the information about the image format, which native CMM
39  * needs to have in order to read/write data.
40  *
41  * At present planar formats (multiple bands) are not supported
42  * and they are handled as a common (custom) case.
43  * Samples other than 1 - 7 bytes and multiple of 8 bits are
44  * also handled as custom (and won't be supported in the nearest future).
45  */
46 class NativeImageFormat {
47     //////////////////////////////////////////////
48     //  LCMS Pixel types
49     private static final int PT_ANY = 0;    // Don't check colorspace
50     // 1 & 2 are reserved
51     private static final int PT_GRAY     = 3;
52     private static final int PT_RGB      = 4;
53     // Skipping other since we don't use them here
54     ///////////////////////////////////////////////
55
56     // Conversion of predefined BufferedImage formats to LCMS formats
57     private static final int INT_RGB_LCMS_FMT =
58         colorspaceSh(PT_RGB)|
59         extraSh(1)|
60         channelsSh(3)|
61         bytesSh(1)|
62         doswapSh(1)|
63         swapfirstSh(1);
64
65     private static final int INT_ARGB_LCMS_FMT = INT_RGB_LCMS_FMT;
66
67     private static final int INT_BGR_LCMS_FMT =
68         colorspaceSh(PT_RGB)|
69         extraSh(1)|
70         channelsSh(3)|
71         bytesSh(1);
72
73     private static final int THREE_BYTE_BGR_LCMS_FMT =
74         colorspaceSh(PT_RGB)|
75         channelsSh(3)|
76         bytesSh(1)|
77         doswapSh(1);
78
79     private static final int FOUR_BYTE_ABGR_LCMS_FMT =
80         colorspaceSh(PT_RGB)|
81         extraSh(1)|
82         channelsSh(3)|
83         bytesSh(1)|
84         doswapSh(1);
85
86     private static final int BYTE_GRAY_LCMS_FMT =
87         colorspaceSh(PT_GRAY)|
88         channelsSh(1)|
89         bytesSh(1);
90
91     private static final int USHORT_GRAY_LCMS_FMT =
92         colorspaceSh(PT_GRAY)|
93         channelsSh(1)|
94         bytesSh(2);
95
96     // LCMS format packed into 32 bit value. For description
97     // of this format refer to LCMS documentation.
98     private int cmmFormat = 0;
99
100     // Dimensions
101     private int rows = 0;
102     private int cols = 0;
103
104     //  Scanline may contain some padding in the end
105     private int scanlineStride = -1;
106
107     private Object imageData;
108     // It's possible to have offset from the beginning of the array
109     private int dataOffset;
110
111     // Has the image alpha channel? If has - here its band band offset goes
112     private int alphaOffset = -1;
113
114     // initializes proper field IDs
115     private static native void initIDs();
116
117     static {
118         NativeCMM.loadCMM();
119         initIDs();
120     }
121
122     ////////////////////////////////////
123     // LCMS image format encoders
124     ////////////////////////////////////
125     private static int colorspaceSh(int s) {
126         return (s << 16);
127     }
128
129     private static int swapfirstSh(int s) {
130         return (s << 14);
131     }
132
133     private static int flavorSh(int s) {
134         return (s << 13);
135     }
136
137     private static int planarSh(int s) {
138         return (s << 12);
139     }
140
141     private static int endianSh(int s) {
142         return (s << 11);
143     }
144
145     private static int doswapSh(int s) {
146         return (s << 10);
147     }
148
149     private static int extraSh(int s) {
150         return (s << 7);
151     }
152
153     private static int channelsSh(int s) {
154         return (s << 3);
155     }
156
157     private static int bytesSh(int s) {
158         return s;
159     }
160     ////////////////////////////////////
161     // End of LCMS image format encoders
162     ////////////////////////////////////
163
164     // Accessors
165     Object getChannelData() {
166         return imageData;
167     }
168
169     int getNumCols() {
170         return cols;
171     }
172
173     int getNumRows() {
174         return rows;
175     }
176
177     // Constructors
178     public NativeImageFormat() {
179     }
180
181     /**
182      * Simple image layout for common case with
183      * not optimized workflow.
184      *
185      * For hifi colorspaces with 5+ color channels imgData
186      * should be <code>byte</code> array.
187      *
188      * For common colorspaces with up to 4 color channels it
189      * should be <code>short</code> array.
190      *
191      * Alpha channel is handled by caller, not by CMS.
192      *
193      * Color channels are in their natural order (not BGR but RGB).
194      *
195      * @param imgData - array of <code>byte</code> or <code>short</code>
196      * @param nChannels - number of channels
197      * @param nRows - number of scanlines in the image
198      * @param nCols - number of pixels in one row of the image
199      */
200     public NativeImageFormat(Object imgData, int nChannels, int nRows, int nCols) {
201         if (imgData instanceof short[]) {
202             cmmFormat |= bytesSh(2);
203         }
204         else if (imgData instanceof byte[]) {
205             cmmFormat |= bytesSh(1);
206         }
207         else
208             // awt.47=First argument should be byte or short array
209             throw new IllegalArgumentException(Messages.getString("awt.47")); //$NON-NLS-1$
210
211         cmmFormat |= channelsSh(nChannels);
212
213         rows = nRows;
214         cols = nCols;
215
216         imageData = imgData;
217
218         dataOffset = 0;
219     }
220
221     /**
222      * Deduces image format from the buffered image type
223      * or color and sample models.
224      * @param bi - image
225      * @return image format object
226      */
227     public static NativeImageFormat createNativeImageFormat(BufferedImage bi) {
228         NativeImageFormat fmt = new NativeImageFormat();
229
230         switch (bi.getType()) {
231             case BufferedImage.TYPE_INT_RGB: {
232                 fmt.cmmFormat = INT_RGB_LCMS_FMT;
233                 break;
234             }
235
236             case BufferedImage.TYPE_INT_ARGB:
237             case BufferedImage.TYPE_INT_ARGB_PRE: {
238                 fmt.cmmFormat = INT_ARGB_LCMS_FMT;
239                 fmt.alphaOffset = 3;
240                 break;
241             }
242
243             case BufferedImage.TYPE_INT_BGR: {
244                 fmt.cmmFormat = INT_BGR_LCMS_FMT;
245                 break;
246             }
247
248             case BufferedImage.TYPE_3BYTE_BGR: {
249                 fmt.cmmFormat = THREE_BYTE_BGR_LCMS_FMT;
250                 break;
251             }
252
253             case BufferedImage.TYPE_4BYTE_ABGR_PRE:
254             case BufferedImage.TYPE_4BYTE_ABGR: {
255                 fmt.cmmFormat = FOUR_BYTE_ABGR_LCMS_FMT;
256                 fmt.alphaOffset = 0;
257                 break;
258             }
259
260             case BufferedImage.TYPE_BYTE_GRAY: {
261                 fmt.cmmFormat = BYTE_GRAY_LCMS_FMT;
262                 break;
263             }
264
265             case BufferedImage.TYPE_USHORT_GRAY: {
266                 fmt.cmmFormat = USHORT_GRAY_LCMS_FMT;
267                 break;
268             }
269
270             case BufferedImage.TYPE_BYTE_BINARY:
271             case BufferedImage.TYPE_USHORT_565_RGB:
272             case BufferedImage.TYPE_USHORT_555_RGB:
273             case BufferedImage.TYPE_BYTE_INDEXED: {
274                 // A bunch of unsupported formats
275                 return null;
276             }
277
278             default:
279                 break; // Try to look at sample model and color model
280         }
281
282
283         if (fmt.cmmFormat == 0) {
284             ColorModel cm = bi.getColorModel();
285             SampleModel sm = bi.getSampleModel();
286
287             if (sm instanceof ComponentSampleModel) {
288                 ComponentSampleModel csm = (ComponentSampleModel) sm;
289                 fmt.cmmFormat = getFormatFromComponentModel(csm, cm.hasAlpha());
290                 fmt.scanlineStride = calculateScanlineStrideCSM(csm, bi.getRaster());
291             } else if (sm instanceof SinglePixelPackedSampleModel) {
292                 SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
293                 fmt.cmmFormat = getFormatFromSPPSampleModel(sppsm, cm.hasAlpha());
294                 fmt.scanlineStride = calculateScanlineStrideSPPSM(sppsm, bi.getRaster());
295             }
296
297             if (cm.hasAlpha())
298                 fmt.alphaOffset = calculateAlphaOffset(sm, bi.getRaster());
299         }
300
301         if (fmt.cmmFormat == 0)
302             return null;
303
304         if (!fmt.setImageData(bi.getRaster().getDataBuffer())) {
305             return null;
306         }
307
308         fmt.rows = bi.getHeight();
309         fmt.cols = bi.getWidth();
310
311         fmt.dataOffset = bi.getRaster().getDataBuffer().getOffset();
312
313         return fmt;
314     }
315
316     /**
317      * Deduces image format from the raster sample model.
318      * @param r - raster
319      * @return image format object
320      */
321     public static NativeImageFormat createNativeImageFormat(Raster r) {
322         NativeImageFormat fmt = new NativeImageFormat();
323         SampleModel sm = r.getSampleModel();
324
325         // Assume that there's no alpha
326         if (sm instanceof ComponentSampleModel) {
327             ComponentSampleModel csm = (ComponentSampleModel) sm;
328             fmt.cmmFormat = getFormatFromComponentModel(csm, false);
329             fmt.scanlineStride = calculateScanlineStrideCSM(csm, r);
330         } else if (sm instanceof SinglePixelPackedSampleModel) {
331             SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
332             fmt.cmmFormat = getFormatFromSPPSampleModel(sppsm, false);
333             fmt.scanlineStride = calculateScanlineStrideSPPSM(sppsm, r);
334         }
335
336         if (fmt.cmmFormat == 0)
337             return null;
338
339         fmt.cols = r.getWidth();
340         fmt.rows = r.getHeight();
341         fmt.dataOffset = r.getDataBuffer().getOffset();
342
343         if (!fmt.setImageData(r.getDataBuffer()))
344             return null;
345
346         return fmt;
347     }
348
349     /**
350      * Obtains LCMS format from the component sample model
351      * @param sm - sample model
352      * @param hasAlpha - true if there's an alpha channel
353      * @return LCMS format
354      */
355     private static int getFormatFromComponentModel(ComponentSampleModel sm, boolean hasAlpha) {
356         // Multiple data arrays (banks) not supported
357         int bankIndex = sm.getBankIndices()[0];
358         for (int i=1; i < sm.getNumBands(); i++) {
359             if (sm.getBankIndices()[i] != bankIndex) {
360                 return 0;
361             }
362         }
363
364         int channels = hasAlpha ? sm.getNumBands()-1 : sm.getNumBands();
365         int extra = hasAlpha ? 1 : 0;
366         int bytes = 1;
367         switch (sm.getDataType()) {
368             case DataBuffer.TYPE_BYTE:
369                 bytes = 1; break;
370             case DataBuffer.TYPE_SHORT:
371             case DataBuffer.TYPE_USHORT:
372                 bytes = 2; break;
373             case DataBuffer.TYPE_INT:
374                 bytes = 4; break;
375             case DataBuffer.TYPE_DOUBLE:
376                 bytes = 0; break;
377             default:
378                 return 0; // Unsupported data type
379         }
380
381         int doSwap = 0;
382         int swapFirst = 0;
383         boolean knownFormat = false;
384
385         int i;
386
387         // "RGBA"
388         for (i=0; i < sm.getNumBands(); i++) {
389             if (sm.getBandOffsets()[i] != i) break;
390         }
391         if (i == sm.getNumBands()) { // Ok, it is it
392             doSwap = 0;
393             swapFirst = 0;
394             knownFormat = true;
395         }
396
397         // "ARGB"
398         if (!knownFormat) {
399             for (i=0; i < sm.getNumBands()-1; i++) {
400                 if (sm.getBandOffsets()[i] != i+1) break;
401             }
402             if (sm.getBandOffsets()[i] == 0) i++;
403             if (i == sm.getNumBands()) { // Ok, it is it
404                 doSwap = 0;
405                 swapFirst = 1;
406                 knownFormat = true;
407             }
408         }
409
410         // "BGRA"
411         if (!knownFormat) {
412             for (i=0; i < sm.getNumBands()-1; i++) {
413                 if (sm.getBandOffsets()[i] != sm.getNumBands() - 2 - i) break;
414             }
415             if (sm.getBandOffsets()[i] == sm.getNumBands()-1) i++;
416             if (i == sm.getNumBands()) { // Ok, it is it
417                 doSwap = 1;
418                 swapFirst = 1;
419                 knownFormat = true;
420             }
421         }
422
423         // "ABGR"
424         if (!knownFormat) {
425             for (i=0; i < sm.getNumBands(); i++) {
426                 if (sm.getBandOffsets()[i] != sm.getNumBands() - 1 - i) break;
427             }
428             if (i == sm.getNumBands()) { // Ok, it is it
429                 doSwap = 1;
430                 swapFirst = 0;
431                 knownFormat = true;
432             }
433         }
434
435         // XXX - Planar formats are not supported yet
436         if (!knownFormat)
437             return 0;
438
439         return
440             channelsSh(channels) |
441             bytesSh(bytes) |
442             extraSh(extra) |
443             doswapSh(doSwap) |
444             swapfirstSh(swapFirst);
445     }
446
447     /**
448      * Obtains LCMS format from the single pixel packed sample model
449      * @param sm - sample model
450      * @param hasAlpha - true if there's an alpha channel
451      * @return LCMS format
452      */
453     private static int getFormatFromSPPSampleModel(SinglePixelPackedSampleModel sm,
454             boolean hasAlpha) {
455         // Can we extract bytes?
456         int mask = sm.getBitMasks()[0] >>> sm.getBitOffsets()[0];
457         if (!(mask == 0xFF || mask == 0xFFFF || mask == 0xFFFFFFFF))
458             return 0;
459
460         // All masks are same?
461         for (int i = 1; i < sm.getNumBands(); i++) {
462             if ((sm.getBitMasks()[i] >>> sm.getBitOffsets()[i]) != mask)
463                 return 0;
464         }
465
466         int pixelSize = 0;
467         // Check if data type is supported
468         if (sm.getDataType() == DataBuffer.TYPE_USHORT)
469             pixelSize = 2;
470         else if (sm.getDataType() == DataBuffer.TYPE_INT)
471             pixelSize = 4;
472         else
473             return 0;
474
475
476         int bytes = 0;
477         switch (mask) {
478             case 0xFF:
479                 bytes = 1;
480                 break;
481             case 0xFFFF:
482                 bytes = 2;
483                 break;
484             case 0xFFFFFFFF:
485                 bytes = 4;
486                 break;
487             default: return 0;
488         }
489
490
491         int channels = hasAlpha ? sm.getNumBands()-1 : sm.getNumBands();
492         int extra = hasAlpha ? 1 : 0;
493         extra +=  pixelSize/bytes - sm.getNumBands(); // Unused bytes?
494
495         // Form an ArrayList containing offset for each band
496         ArrayList<Integer> offsetsLst = new ArrayList<Integer>();
497         for (int k=0; k < sm.getNumBands(); k++) {
498             offsetsLst.add(new Integer(sm.getBitOffsets()[k]/(bytes*8)));
499         }
500
501         // Add offsets for unused space
502         for (int i=0; i<pixelSize/bytes; i++) {
503             if (offsetsLst.indexOf(new Integer(i)) < 0)
504                 offsetsLst.add(new Integer(i));
505         }
506
507         int offsets[] = new int[pixelSize/bytes];
508         for (int i=0; i<offsetsLst.size(); i++) {
509             offsets[i] = offsetsLst.get(i).intValue();
510         }
511
512         int doSwap = 0;
513         int swapFirst = 0;
514         boolean knownFormat = false;
515
516         int i;
517
518         // "RGBA"
519         for (i=0; i < pixelSize; i++) {
520             if (offsets[i] != i) break;
521         }
522         if (i == pixelSize) { // Ok, it is it
523             doSwap = 0;
524             swapFirst = 0;
525             knownFormat = true;
526         }
527
528         // "ARGB"
529         if (!knownFormat) {
530             for (i=0; i < pixelSize-1; i++) {
531                 if (offsets[i] != i+1) break;
532             }
533             if (offsets[i] == 0) i++;
534             if (i == pixelSize) { // Ok, it is it
535                 doSwap = 0;
536                 swapFirst = 1;
537                 knownFormat = true;
538             }
539         }
540
541         // "BGRA"
542         if (!knownFormat) {
543             for (i=0; i < pixelSize-1; i++) {
544                 if (offsets[i] != pixelSize - 2 - i) break;
545             }
546             if (offsets[i] == pixelSize-1) i++;
547             if (i == pixelSize) { // Ok, it is it
548                 doSwap = 1;
549                 swapFirst = 1;
550                 knownFormat = true;
551             }
552         }
553
554         // "ABGR"
555         if (!knownFormat) {
556             for (i=0; i < pixelSize; i++) {
557                 if (offsets[i] != pixelSize - 1 - i) break;
558             }
559             if (i == pixelSize) { // Ok, it is it
560                 doSwap = 1;
561                 swapFirst = 0;
562                 knownFormat = true;
563             }
564         }
565
566         // XXX - Planar formats are not supported yet
567         if (!knownFormat)
568             return 0;
569
570         return
571             channelsSh(channels) |
572             bytesSh(bytes) |
573             extraSh(extra) |
574             doswapSh(doSwap) |
575             swapfirstSh(swapFirst);
576     }
577
578     /**
579      * Obtains data array from the DataBuffer object
580      * @param db - data buffer
581      * @return - true if successful
582      */
583     private boolean setImageData(DataBuffer db) {
584         AwtImageBackdoorAccessor dbAccess = AwtImageBackdoorAccessor.getInstance();
585         try {
586             imageData = dbAccess.getData(db);
587         } catch (IllegalArgumentException e) {
588             return false; // Unknown data buffer type
589         }
590
591         return true;
592     }
593
594     /**
595      * Calculates scanline stride in bytes
596      * @param csm - component sample model
597      * @param r - raster
598      * @return scanline stride in bytes
599      */
600     private static int calculateScanlineStrideCSM(ComponentSampleModel csm, Raster r) {
601         if (csm.getScanlineStride() != csm.getPixelStride()*csm.getWidth()) {
602             int dataTypeSize = DataBuffer.getDataTypeSize(r.getDataBuffer().getDataType()) / 8;
603             return csm.getScanlineStride()*dataTypeSize;
604         }
605         return -1;
606     }
607
608     /**
609      * Calculates scanline stride in bytes
610      * @param sppsm - sample model
611      * @param r - raster
612      * @return scanline stride in bytes
613      */
614     private static int calculateScanlineStrideSPPSM(SinglePixelPackedSampleModel sppsm, Raster r) {
615         if (sppsm.getScanlineStride() != sppsm.getWidth()) {
616             int dataTypeSize = DataBuffer.getDataTypeSize(r.getDataBuffer().getDataType()) / 8;
617             return sppsm.getScanlineStride()*dataTypeSize;
618         }
619         return -1;
620     }
621
622     /**
623      * Calculates byte offset of the alpha channel from the beginning of the pixel data
624      * @param sm - sample model
625      * @param r - raster
626      * @return byte offset of the alpha channel
627      */
628     private static int calculateAlphaOffset(SampleModel sm, Raster r) {
629         if (sm instanceof ComponentSampleModel) {
630             ComponentSampleModel csm = (ComponentSampleModel) sm;
631             int dataTypeSize =
632                 DataBuffer.getDataTypeSize(r.getDataBuffer().getDataType()) / 8;
633             return
634                 csm.getBandOffsets()[csm.getBandOffsets().length - 1] * dataTypeSize;
635         } else if (sm instanceof SinglePixelPackedSampleModel) {
636             SinglePixelPackedSampleModel sppsm = (SinglePixelPackedSampleModel) sm;
637             return sppsm.getBitOffsets()[sppsm.getBitOffsets().length - 1] / 8;
638         } else {
639             return -1; // No offset, don't copy alpha
640         }
641     }
642 }