KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lowagie > text > pdf > codec > BmpImage


1 /*
2  * Copyright 2003 by Paulo Soares.
3  *
4  * The contents of this file are subject to the Mozilla Public License Version 1.1
5  * (the "License"); you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at http://www.mozilla.org/MPL/
7  *
8  * Software distributed under the License is distributed on an "AS IS" basis,
9  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
10  * for the specific language governing rights and limitations under the License.
11  *
12  * The Original Code is 'iText, a free JAVA-PDF library'.
13  *
14  * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
15  * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
16  * All Rights Reserved.
17  * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
18  * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
19  *
20  * Contributor(s): all the names of the contributors are added in the source code
21  * where applicable.
22  *
23  * Alternatively, the contents of this file may be used under the terms of the
24  * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
25  * provisions of LGPL are applicable instead of those above. If you wish to
26  * allow use of your version of this file only under the terms of the LGPL
27  * License and not to allow others to use your version of this file under
28  * the MPL, indicate your decision by deleting the provisions above and
29  * replace them with the notice and other provisions required by the LGPL.
30  * If you do not delete the provisions above, a recipient may use your version
31  * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
32  *
33  * This library is free software; you can redistribute it and/or modify it
34  * under the terms of the MPL as stated above or under the terms of the GNU
35  * Library General Public License as published by the Free Software Foundation;
36  * either version 2 of the License, or any later version.
37  *
38  * This library is distributed in the hope that it will be useful, but WITHOUT
39  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
40  * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
41  * details.
42  *
43  * If you didn't download this code from the following link, you should check if
44  * you aren't using an obsolete version:
45  * http://www.lowagie.com/iText/
46  *
47  *
48  * The original JAI codecs have the following license
49  *
50  * Copyright (c) 2001 Sun Microsystems, Inc. All Rights Reserved.
51  *
52  * Redistribution and use in source and binary forms, with or without
53  * modification, are permitted provided that the following conditions are met:
54  *
55  * -Redistributions of source code must retain the above copyright notice, this
56  * list of conditions and the following disclaimer.
57  *
58  * -Redistribution in binary form must reproduct the above copyright notice,
59  * this list of conditions and the following disclaimer in the documentation
60  * and/or other materials provided with the distribution.
61  *
62  * Neither the name of Sun Microsystems, Inc. or the names of contributors may
63  * be used to endorse or promote products derived from this software without
64  * specific prior written permission.
65  *
66  * This software is provided "AS IS," without a warranty of any kind. ALL
67  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
68  * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
69  * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
70  * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
71  * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
72  * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
73  * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
74  * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
75  * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
76  * POSSIBILITY OF SUCH DAMAGES.
77  *
78  * You acknowledge that Software is not designed,licensed or intended for use in
79  * the design, construction, operation or maintenance of any nuclear facility.
80  */

81 package com.lowagie.text.pdf.codec;
82
83 import java.io.BufferedInputStream JavaDoc;
84 import java.io.ByteArrayInputStream JavaDoc;
85 import java.io.IOException JavaDoc;
86 import java.io.InputStream JavaDoc;
87 import java.net.URL JavaDoc;
88 import java.util.HashMap JavaDoc;
89
90 import com.lowagie.text.BadElementException;
91 import com.lowagie.text.ExceptionConverter;
92 import com.lowagie.text.Image;
93 import com.lowagie.text.ImgRaw;
94 import com.lowagie.text.Utilities;
95 import com.lowagie.text.pdf.PdfArray;
96 import com.lowagie.text.pdf.PdfDictionary;
97 import com.lowagie.text.pdf.PdfName;
98 import com.lowagie.text.pdf.PdfNumber;
99 import com.lowagie.text.pdf.PdfString;
100
101 /** Reads a BMP image. All types of BMP can be read.
102  * <p>
103  * It is based in the JAI codec.
104  *
105  * @author Paulo Soares (psoares@consiste.pt)
106  */

107 public class BmpImage {
108     
109     // BMP variables
110
private InputStream JavaDoc inputStream;
111     private long bitmapFileSize;
112     private long bitmapOffset;
113     private long compression;
114     private long imageSize;
115     private byte palette[];
116     private int imageType;
117     private int numBands;
118     private boolean isBottomUp;
119     private int bitsPerPixel;
120     private int redMask, greenMask, blueMask, alphaMask;
121     public HashMap JavaDoc properties = new HashMap JavaDoc();
122     private long xPelsPerMeter;
123     private long yPelsPerMeter;
124     // BMP Image types
125
private static final int VERSION_2_1_BIT = 0;
126     private static final int VERSION_2_4_BIT = 1;
127     private static final int VERSION_2_8_BIT = 2;
128     private static final int VERSION_2_24_BIT = 3;
129     
130     private static final int VERSION_3_1_BIT = 4;
131     private static final int VERSION_3_4_BIT = 5;
132     private static final int VERSION_3_8_BIT = 6;
133     private static final int VERSION_3_24_BIT = 7;
134     
135     private static final int VERSION_3_NT_16_BIT = 8;
136     private static final int VERSION_3_NT_32_BIT = 9;
137     
138     private static final int VERSION_4_1_BIT = 10;
139     private static final int VERSION_4_4_BIT = 11;
140     private static final int VERSION_4_8_BIT = 12;
141     private static final int VERSION_4_16_BIT = 13;
142     private static final int VERSION_4_24_BIT = 14;
143     private static final int VERSION_4_32_BIT = 15;
144     
145     // Color space types
146
private static final int LCS_CALIBRATED_RGB = 0;
147     private static final int LCS_sRGB = 1;
148     private static final int LCS_CMYK = 2;
149     
150     // Compression Types
151
private static final int BI_RGB = 0;
152     private static final int BI_RLE8 = 1;
153     private static final int BI_RLE4 = 2;
154     private static final int BI_BITFIELDS = 3;
155     
156     int width;
157     int height;
158     
159     BmpImage(InputStream JavaDoc is, boolean noHeader, int size) throws IOException JavaDoc {
160         bitmapFileSize = size;
161         bitmapOffset = 0;
162         process(is, noHeader);
163     }
164     
165     /** Reads a BMP from an url.
166      * @param url the url
167      * @throws IOException on error
168      * @return the image
169      */

170     public static Image getImage(URL JavaDoc url) throws IOException JavaDoc {
171         InputStream JavaDoc is = null;
172         try {
173             is = url.openStream();
174             Image img = getImage(is);
175             img.setUrl(url);
176             return img;
177         }
178         finally {
179             if (is != null) {
180                 is.close();
181             }
182         }
183     }
184     
185     /** Reads a BMP from a stream. The stream is not closed.
186      * @param is the stream
187      * @throws IOException on error
188      * @return the image
189      */

190     public static Image getImage(InputStream JavaDoc is) throws IOException JavaDoc {
191         return getImage(is, false, 0);
192     }
193     
194     /** Reads a BMP from a stream. The stream is not closed.
195      * The BMP may not have a header and be considered as a plain DIB.
196      * @param is the stream
197      * @param noHeader true to process a plain DIB
198      * @param size the size of the DIB. Not used for a BMP
199      * @throws IOException on error
200      * @return the image
201      */

202     public static Image getImage(InputStream JavaDoc is, boolean noHeader, int size) throws IOException JavaDoc {
203         BmpImage bmp = new BmpImage(is, noHeader, size);
204         try {
205             Image img = bmp.getImage();
206             img.setDpi((int)((double)bmp.xPelsPerMeter * 0.0254), (int)((double)bmp.yPelsPerMeter * 0.0254));
207             img.setOriginalType(Image.ORIGINAL_BMP);
208             return img;
209         }
210         catch (BadElementException be) {
211             throw new ExceptionConverter(be);
212         }
213     }
214     
215     /** Reads a BMP from a file.
216      * @param file the file
217      * @throws IOException on error
218      * @return the image
219      */

220     public static Image getImage(String JavaDoc file) throws IOException JavaDoc {
221         return getImage(Utilities.toURL(file));
222     }
223     
224     /** Reads a BMP from a byte array.
225      * @param data the byte array
226      * @throws IOException on error
227      * @return the image
228      */

229     public static Image getImage(byte data[]) throws IOException JavaDoc {
230         ByteArrayInputStream JavaDoc is = new ByteArrayInputStream JavaDoc(data);
231         Image img = getImage(is);
232         img.setOriginalData(data);
233         return img;
234     }
235
236     
237     protected void process(InputStream JavaDoc stream, boolean noHeader) throws IOException JavaDoc {
238         if (noHeader || stream instanceof BufferedInputStream JavaDoc) {
239             inputStream = stream;
240         } else {
241             inputStream = new BufferedInputStream JavaDoc(stream);
242         }
243         if (!noHeader) {
244             // Start File Header
245
if (!(readUnsignedByte(inputStream) == 'B' &&
246             readUnsignedByte(inputStream) == 'M')) {
247                 throw new
248                 RuntimeException JavaDoc("Invalid magic value for BMP file.");
249             }
250
251             // Read file size
252
bitmapFileSize = readDWord(inputStream);
253
254             // Read the two reserved fields
255
readWord(inputStream);
256             readWord(inputStream);
257
258             // Offset to the bitmap from the beginning
259
bitmapOffset = readDWord(inputStream);
260
261             // End File Header
262
}
263         // Start BitmapCoreHeader
264
long size = readDWord(inputStream);
265         
266         if (size == 12) {
267             width = readWord(inputStream);
268             height = readWord(inputStream);
269         } else {
270             width = readLong(inputStream);
271             height = readLong(inputStream);
272         }
273         
274         int planes = readWord(inputStream);
275         bitsPerPixel = readWord(inputStream);
276         
277         properties.put("color_planes", new Integer JavaDoc(planes));
278         properties.put("bits_per_pixel", new Integer JavaDoc(bitsPerPixel));
279         
280         // As BMP always has 3 rgb bands, except for Version 5,
281
// which is bgra
282
numBands = 3;
283         if (bitmapOffset == 0)
284             bitmapOffset = size;
285         if (size == 12) {
286             // Windows 2.x and OS/2 1.x
287
properties.put("bmp_version", "BMP v. 2.x");
288             
289             // Classify the image type
290
if (bitsPerPixel == 1) {
291                 imageType = VERSION_2_1_BIT;
292             } else if (bitsPerPixel == 4) {
293                 imageType = VERSION_2_4_BIT;
294             } else if (bitsPerPixel == 8) {
295                 imageType = VERSION_2_8_BIT;
296             } else if (bitsPerPixel == 24) {
297                 imageType = VERSION_2_24_BIT;
298             }
299             
300             // Read in the palette
301
int numberOfEntries = (int)((bitmapOffset-14-size) / 3);
302             int sizeOfPalette = numberOfEntries*3;
303             if (bitmapOffset == size) {
304                 switch (imageType) {
305                     case VERSION_2_1_BIT:
306                         sizeOfPalette = 2 * 3;
307                         break;
308                     case VERSION_2_4_BIT:
309                         sizeOfPalette = 16 * 3;
310                         break;
311                     case VERSION_2_8_BIT:
312                         sizeOfPalette = 256 * 3;
313                         break;
314                     case VERSION_2_24_BIT:
315                         sizeOfPalette = 0;
316                         break;
317                 }
318                 bitmapOffset = size + sizeOfPalette;
319             }
320             readPalette(sizeOfPalette);
321         } else {
322             
323             compression = readDWord(inputStream);
324             imageSize = readDWord(inputStream);
325             xPelsPerMeter = readLong(inputStream);
326             yPelsPerMeter = readLong(inputStream);
327             long colorsUsed = readDWord(inputStream);
328             long colorsImportant = readDWord(inputStream);
329             
330             switch((int)compression) {
331                 case BI_RGB:
332                     properties.put("compression", "BI_RGB");
333                     break;
334                     
335                 case BI_RLE8:
336                     properties.put("compression", "BI_RLE8");
337                     break;
338                     
339                 case BI_RLE4:
340                     properties.put("compression", "BI_RLE4");
341                     break;
342                     
343                 case BI_BITFIELDS:
344                     properties.put("compression", "BI_BITFIELDS");
345                     break;
346             }
347             
348             properties.put("x_pixels_per_meter", new Long JavaDoc(xPelsPerMeter));
349             properties.put("y_pixels_per_meter", new Long JavaDoc(yPelsPerMeter));
350             properties.put("colors_used", new Long JavaDoc(colorsUsed));
351             properties.put("colors_important", new Long JavaDoc(colorsImportant));
352             
353             if (size == 40) {
354                 // Windows 3.x and Windows NT
355
switch((int)compression) {
356                     
357                     case BI_RGB: // No compression
358
case BI_RLE8: // 8-bit RLE compression
359
case BI_RLE4: // 4-bit RLE compression
360

361                         if (bitsPerPixel == 1) {
362                             imageType = VERSION_3_1_BIT;
363                         } else if (bitsPerPixel == 4) {
364                             imageType = VERSION_3_4_BIT;
365                         } else if (bitsPerPixel == 8) {
366                             imageType = VERSION_3_8_BIT;
367                         } else if (bitsPerPixel == 24) {
368                             imageType = VERSION_3_24_BIT;
369                         } else if (bitsPerPixel == 16) {
370                             imageType = VERSION_3_NT_16_BIT;
371                             redMask = 0x7C00;
372                             greenMask = 0x3E0;
373                             blueMask = 0x1F;
374                             properties.put("red_mask", new Integer JavaDoc(redMask));
375                             properties.put("green_mask", new Integer JavaDoc(greenMask));
376                             properties.put("blue_mask", new Integer JavaDoc(blueMask));
377                         } else if (bitsPerPixel == 32) {
378                             imageType = VERSION_3_NT_32_BIT;
379                             redMask = 0x00FF0000;
380                             greenMask = 0x0000FF00;
381                             blueMask = 0x000000FF;
382                             properties.put("red_mask", new Integer JavaDoc(redMask));
383                             properties.put("green_mask", new Integer JavaDoc(greenMask));
384                             properties.put("blue_mask", new Integer JavaDoc(blueMask));
385                         }
386
387                         // Read in the palette
388
int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
389                         int sizeOfPalette = numberOfEntries*4;
390                         if (bitmapOffset == size) {
391                             switch (imageType) {
392                                 case VERSION_3_1_BIT:
393                                     sizeOfPalette = (int)(colorsUsed == 0 ? 2 : colorsUsed) * 4;
394                                     break;
395                                 case VERSION_3_4_BIT:
396                                     sizeOfPalette = (int)(colorsUsed == 0 ? 16 : colorsUsed) * 4;
397                                     break;
398                                 case VERSION_3_8_BIT:
399                                     sizeOfPalette = (int)(colorsUsed == 0 ? 256 : colorsUsed) * 4;
400                                     break;
401                                 default:
402                                     sizeOfPalette = 0;
403                                     break;
404                             }
405                             bitmapOffset = size + sizeOfPalette;
406                         }
407                         readPalette(sizeOfPalette);
408                                                 
409                         properties.put("bmp_version", "BMP v. 3.x");
410                         break;
411                         
412                     case BI_BITFIELDS:
413                         
414                         if (bitsPerPixel == 16) {
415                             imageType = VERSION_3_NT_16_BIT;
416                         } else if (bitsPerPixel == 32) {
417                             imageType = VERSION_3_NT_32_BIT;
418                         }
419                         
420                         // BitsField encoding
421
redMask = (int)readDWord(inputStream);
422                         greenMask = (int)readDWord(inputStream);
423                         blueMask = (int)readDWord(inputStream);
424                         
425                         properties.put("red_mask", new Integer JavaDoc(redMask));
426                         properties.put("green_mask", new Integer JavaDoc(greenMask));
427                         properties.put("blue_mask", new Integer JavaDoc(blueMask));
428                         
429                         if (colorsUsed != 0) {
430                             // there is a palette
431
sizeOfPalette = (int)colorsUsed*4;
432                             readPalette(sizeOfPalette);
433                         }
434                         
435                         properties.put("bmp_version", "BMP v. 3.x NT");
436                         break;
437                         
438                     default:
439                         throw new
440                         RuntimeException JavaDoc("Invalid compression specified in BMP file.");
441                 }
442             } else if (size == 108) {
443                 // Windows 4.x BMP
444

445                 properties.put("bmp_version", "BMP v. 4.x");
446                 
447                 // rgb masks, valid only if comp is BI_BITFIELDS
448
redMask = (int)readDWord(inputStream);
449                 greenMask = (int)readDWord(inputStream);
450                 blueMask = (int)readDWord(inputStream);
451                 // Only supported for 32bpp BI_RGB argb
452
alphaMask = (int)readDWord(inputStream);
453                 long csType = readDWord(inputStream);
454                 int redX = readLong(inputStream);
455                 int redY = readLong(inputStream);
456                 int redZ = readLong(inputStream);
457                 int greenX = readLong(inputStream);
458                 int greenY = readLong(inputStream);
459                 int greenZ = readLong(inputStream);
460                 int blueX = readLong(inputStream);
461                 int blueY = readLong(inputStream);
462                 int blueZ = readLong(inputStream);
463                 long gammaRed = readDWord(inputStream);
464                 long gammaGreen = readDWord(inputStream);
465                 long gammaBlue = readDWord(inputStream);
466                 
467                 if (bitsPerPixel == 1) {
468                     imageType = VERSION_4_1_BIT;
469                 } else if (bitsPerPixel == 4) {
470                     imageType = VERSION_4_4_BIT;
471                 } else if (bitsPerPixel == 8) {
472                     imageType = VERSION_4_8_BIT;
473                 } else if (bitsPerPixel == 16) {
474                     imageType = VERSION_4_16_BIT;
475                     if ((int)compression == BI_RGB) {
476                         redMask = 0x7C00;
477                         greenMask = 0x3E0;
478                         blueMask = 0x1F;
479                     }
480                 } else if (bitsPerPixel == 24) {
481                     imageType = VERSION_4_24_BIT;
482                 } else if (bitsPerPixel == 32) {
483                     imageType = VERSION_4_32_BIT;
484                     if ((int)compression == BI_RGB) {
485                         redMask = 0x00FF0000;
486                         greenMask = 0x0000FF00;
487                         blueMask = 0x000000FF;
488                     }
489                 }
490                 
491                 properties.put("red_mask", new Integer JavaDoc(redMask));
492                 properties.put("green_mask", new Integer JavaDoc(greenMask));
493                 properties.put("blue_mask", new Integer JavaDoc(blueMask));
494                 properties.put("alpha_mask", new Integer JavaDoc(alphaMask));
495
496                 // Read in the palette
497
int numberOfEntries = (int)((bitmapOffset-14-size) / 4);
498                 int sizeOfPalette = numberOfEntries*4;
499                 if (bitmapOffset == size) {
500                     switch (imageType) {
501                         case VERSION_4_1_BIT:
502                             sizeOfPalette = (int)(colorsUsed == 0 ? 2 : colorsUsed) * 4;
503                             break;
504                         case VERSION_4_4_BIT:
505                             sizeOfPalette = (int)(colorsUsed == 0 ? 16 : colorsUsed) * 4;
506                             break;
507                         case VERSION_4_8_BIT:
508                             sizeOfPalette = (int)(colorsUsed == 0 ? 256 : colorsUsed) * 4;
509                             break;
510                         default:
511                             sizeOfPalette = 0;
512                             break;
513                     }
514                     bitmapOffset = size + sizeOfPalette;
515                 }
516                 readPalette(sizeOfPalette);
517                 
518                 switch((int)csType) {
519                     case LCS_CALIBRATED_RGB:
520                         // All the new fields are valid only for this case
521
properties.put("color_space", "LCS_CALIBRATED_RGB");
522                         properties.put("redX", new Integer JavaDoc(redX));
523                         properties.put("redY", new Integer JavaDoc(redY));
524                         properties.put("redZ", new Integer JavaDoc(redZ));
525                         properties.put("greenX", new Integer JavaDoc(greenX));
526                         properties.put("greenY", new Integer JavaDoc(greenY));
527                         properties.put("greenZ", new Integer JavaDoc(greenZ));
528                         properties.put("blueX", new Integer JavaDoc(blueX));
529                         properties.put("blueY", new Integer JavaDoc(blueY));
530                         properties.put("blueZ", new Integer JavaDoc(blueZ));
531                         properties.put("gamma_red", new Long JavaDoc(gammaRed));
532                         properties.put("gamma_green", new Long JavaDoc(gammaGreen));
533                         properties.put("gamma_blue", new Long JavaDoc(gammaBlue));
534                         
535                         // break;
536
throw new
537                         RuntimeException JavaDoc("Not implemented yet.");
538                         
539                     case LCS_sRGB:
540                         // Default Windows color space
541
properties.put("color_space", "LCS_sRGB");
542                         break;
543                         
544                     case LCS_CMYK:
545                         properties.put("color_space", "LCS_CMYK");
546                         // break;
547
throw new
548                         RuntimeException JavaDoc("Not implemented yet.");
549                 }
550                 
551             } else {
552                 properties.put("bmp_version", "BMP v. 5.x");
553                 throw new
554                 RuntimeException JavaDoc("BMP version 5 not implemented yet.");
555             }
556         }
557         
558         if (height > 0) {
559             // bottom up image
560
isBottomUp = true;
561         } else {
562             // top down image
563
isBottomUp = false;
564             height = Math.abs(height);
565         }
566         // When number of bitsPerPixel is <= 8, we use IndexColorModel.
567
if (bitsPerPixel == 1 || bitsPerPixel == 4 || bitsPerPixel == 8) {
568             
569             numBands = 1;
570             
571             
572             // Create IndexColorModel from the palette.
573
byte r[], g[], b[];
574             int sizep;
575             if (imageType == VERSION_2_1_BIT ||
576             imageType == VERSION_2_4_BIT ||
577             imageType == VERSION_2_8_BIT) {
578                 
579                 sizep = palette.length/3;
580                 
581                 if (sizep > 256) {
582                     sizep = 256;
583                 }
584                 
585                 int off;
586                 r = new byte[sizep];
587                 g = new byte[sizep];
588                 b = new byte[sizep];
589                 for (int i=0; i<sizep; i++) {
590                     off = 3 * i;
591                     b[i] = palette[off];
592                     g[i] = palette[off+1];
593                     r[i] = palette[off+2];
594                 }
595             } else {
596                 sizep = palette.length/4;
597                 
598                 if (sizep > 256) {
599                     sizep = 256;
600                 }
601                 
602                 int off;
603                 r = new byte[sizep];
604                 g = new byte[sizep];
605                 b = new byte[sizep];
606                 for (int i=0; i<sizep; i++) {
607                     off = 4 * i;
608                     b[i] = palette[off];
609                     g[i] = palette[off+1];
610                     r[i] = palette[off+2];
611                 }
612             }
613             
614         } else if (bitsPerPixel == 16) {
615             numBands = 3;
616         } else if (bitsPerPixel == 32) {
617             numBands = alphaMask == 0 ? 3 : 4;
618             
619             // The number of bands in the SampleModel is determined by
620
// the length of the mask array passed in.
621
} else {
622             numBands = 3;
623         }
624     }
625     
626     private byte[] getPalette(int group) {
627         if (palette == null)
628             return null;
629         byte np[] = new byte[palette.length / group * 3];
630         int e = palette.length / group;
631         for (int k = 0; k < e; ++k) {
632             int src = k * group;
633             int dest = k * 3;
634             np[dest + 2] = palette[src++];
635             np[dest + 1] = palette[src++];
636             np[dest] = palette[src];
637         }
638         return np;
639     }
640     
641     private Image getImage() throws IOException JavaDoc, BadElementException {
642         byte bdata[] = null; // buffer for byte data
643

644         // if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE)
645
// bdata = (byte[])((DataBufferByte)tile.getDataBuffer()).getData();
646
// else if (sampleModel.getDataType() == DataBuffer.TYPE_USHORT)
647
// sdata = (short[])((DataBufferUShort)tile.getDataBuffer()).getData();
648
// else if (sampleModel.getDataType() == DataBuffer.TYPE_INT)
649
// idata = (int[])((DataBufferInt)tile.getDataBuffer()).getData();
650

651         // There should only be one tile.
652
switch(imageType) {
653             
654             case VERSION_2_1_BIT:
655                 // no compression
656
return read1Bit(3);
657                 
658             case VERSION_2_4_BIT:
659                 // no compression
660
return read4Bit(3);
661                 
662             case VERSION_2_8_BIT:
663                 // no compression
664
return read8Bit(3);
665                 
666             case VERSION_2_24_BIT:
667                 // no compression
668
bdata = new byte[width * height * 3];
669                 read24Bit(bdata);
670                 return new ImgRaw(width, height, 3, 8, bdata);
671                 
672             case VERSION_3_1_BIT:
673                 // 1-bit images cannot be compressed.
674
return read1Bit(4);
675                 
676             case VERSION_3_4_BIT:
677                 switch((int)compression) {
678                     case BI_RGB:
679                         return read4Bit(4);
680                         
681                     case BI_RLE4:
682                         return readRLE4();
683                         
684                     default:
685                         throw new
686                         RuntimeException JavaDoc("Invalid compression specified for BMP file.");
687                 }
688                 
689             case VERSION_3_8_BIT:
690                 switch((int)compression) {
691                     case BI_RGB:
692                         return read8Bit(4);
693                         
694                     case BI_RLE8:
695                         return readRLE8();
696                         
697                     default:
698                         throw new
699                         RuntimeException JavaDoc("Invalid compression specified for BMP file.");
700                 }
701                 
702             case VERSION_3_24_BIT:
703                 // 24-bit images are not compressed
704
bdata = new byte[width * height * 3];
705                 read24Bit(bdata);
706                 return new ImgRaw(width, height, 3, 8, bdata);
707                 
708             case VERSION_3_NT_16_BIT:
709                 return read1632Bit(false);
710                 
711             case VERSION_3_NT_32_BIT:
712                 return read1632Bit(true);
713                 
714             case VERSION_4_1_BIT:
715                 return read1Bit(4);
716                 
717             case VERSION_4_4_BIT:
718                 switch((int)compression) {
719                     
720                     case BI_RGB:
721                         return read4Bit(4);
722                         
723                     case BI_RLE4:
724                         return readRLE4();
725                         
726                     default:
727                         throw new
728                         RuntimeException JavaDoc("Invalid compression specified for BMP file.");
729                 }
730                 
731             case VERSION_4_8_BIT:
732                 switch((int)compression) {
733                     
734                     case BI_RGB:
735                         return read8Bit(4);
736                         
737                     case BI_RLE8:
738                         return readRLE8();
739                         
740                     default:
741                         throw new
742                         RuntimeException JavaDoc("Invalid compression specified for BMP file.");
743                 }
744                 
745             case VERSION_4_16_BIT:
746                 return read1632Bit(false);
747                 
748             case VERSION_4_24_BIT:
749                 bdata = new byte[width * height * 3];
750                 read24Bit(bdata);
751                 return new ImgRaw(width, height, 3, 8, bdata);
752                 
753             case VERSION_4_32_BIT:
754                 return read1632Bit(true);
755         }
756         return null;
757     }
758     
759     private Image indexedModel(byte bdata[], int bpc, int paletteEntries) throws BadElementException {
760         Image img = new ImgRaw(width, height, 1, bpc, bdata);
761         PdfArray colorspace = new PdfArray();
762         colorspace.add(PdfName.INDEXED);
763         colorspace.add(PdfName.DEVICERGB);
764         byte np[] = getPalette(paletteEntries);
765         int len = np.length;
766         colorspace.add(new PdfNumber(len / 3 - 1));
767         colorspace.add(new PdfString(np));
768         PdfDictionary ad = new PdfDictionary();
769         ad.put(PdfName.COLORSPACE, colorspace);
770         img.setAdditional(ad);
771         return img;
772     }
773     
774     private void readPalette(int sizeOfPalette) throws IOException JavaDoc {
775         if (sizeOfPalette == 0) {
776             return;
777         }
778
779         palette = new byte[sizeOfPalette];
780         int bytesRead = 0;
781         while (bytesRead < sizeOfPalette) {
782             int r = inputStream.read(palette, bytesRead, sizeOfPalette - bytesRead);
783             if (r < 0) {
784                 throw new RuntimeException JavaDoc("incomplete palette");
785             }
786             bytesRead += r;
787         }
788         properties.put("palette", palette);
789     }
790     
791     // Deal with 1 Bit images using IndexColorModels
792
private Image read1Bit(int paletteEntries) throws IOException JavaDoc, BadElementException {
793         byte bdata[] = new byte[((width + 7) / 8) * height];
794         int padding = 0;
795         int bytesPerScanline = (int)Math.ceil((double)width/8.0);
796         
797         int remainder = bytesPerScanline % 4;
798         if (remainder != 0) {
799             padding = 4 - remainder;
800         }
801         
802         int imSize = (bytesPerScanline + padding) * height;
803         
804         // Read till we have the whole image
805
byte values[] = new byte[imSize];
806         int bytesRead = 0;
807         while (bytesRead < imSize) {
808             bytesRead += inputStream.read(values, bytesRead,
809             imSize - bytesRead);
810         }
811         
812         if (isBottomUp) {
813             
814             // Convert the bottom up image to a top down format by copying
815
// one scanline from the bottom to the top at a time.
816

817             for (int i=0; i<height; i++) {
818                 System.arraycopy(values,
819                 imSize - (i+1)*(bytesPerScanline + padding),
820                 bdata,
821                 i*bytesPerScanline, bytesPerScanline);
822             }
823         } else {
824             
825             for (int i=0; i<height; i++) {
826                 System.arraycopy(values,
827                 i * (bytesPerScanline + padding),
828                 bdata,
829                 i * bytesPerScanline,
830                 bytesPerScanline);
831             }
832         }
833         return indexedModel(bdata, 1, paletteEntries);
834     }
835     
836     // Method to read a 4 bit BMP image data
837
private Image read4Bit(int paletteEntries) throws IOException JavaDoc, BadElementException {
838         byte bdata[] = new byte[((width + 1) / 2) * height];
839         
840         // Padding bytes at the end of each scanline
841
int padding = 0;
842         
843         int bytesPerScanline = (int)Math.ceil((double)width/2.0);
844         int remainder = bytesPerScanline % 4;
845         if (remainder != 0) {
846             padding = 4 - remainder;
847         }
848         
849         int imSize = (bytesPerScanline + padding) * height;
850         
851         // Read till we have the whole image
852
byte values[] = new byte[imSize];
853         int bytesRead = 0;
854         while (bytesRead < imSize) {
855             bytesRead += inputStream.read(values, bytesRead,
856             imSize - bytesRead);
857         }
858         
859         if (isBottomUp) {
860             
861             // Convert the bottom up image to a top down format by copying
862
// one scanline from the bottom to the top at a time.
863
for (int i=0; i<height; i++) {
864                 System.arraycopy(values,
865                 imSize - (i+1)*(bytesPerScanline + padding),
866                 bdata,
867                 i*bytesPerScanline,
868                 bytesPerScanline);
869             }
870         } else {
871             for (int i=0; i<height; i++) {
872                 System.arraycopy(values,
873                 i * (bytesPerScanline + padding),
874                 bdata,
875                 i * bytesPerScanline,
876                 bytesPerScanline);
877             }
878         }
879         return indexedModel(bdata, 4, paletteEntries);
880     }
881     
882     // Method to read 8 bit BMP image data
883
private Image read8Bit(int paletteEntries) throws IOException JavaDoc, BadElementException {
884         byte bdata[] = new byte[width * height];
885         // Padding bytes at the end of each scanline
886
int padding = 0;
887         
888         // width * bitsPerPixel should be divisible by 32
889
int bitsPerScanline = width * 8;
890         if ( bitsPerScanline%32 != 0) {
891             padding = (bitsPerScanline/32 + 1)*32 - bitsPerScanline;
892             padding = (int)Math.ceil(padding/8.0);
893         }
894         
895         int imSize = (width + padding) * height;
896         
897         // Read till we have the whole image
898
byte values[] = new byte[imSize];
899         int bytesRead = 0;
900         while (bytesRead < imSize) {
901             bytesRead += inputStream.read(values, bytesRead, imSize - bytesRead);
902         }
903         
904         if (isBottomUp) {
905             
906             // Convert the bottom up image to a top down format by copying
907
// one scanline from the bottom to the top at a time.
908
for (int i=0; i<height; i++) {
909                 System.arraycopy(values,
910                 imSize - (i+1) * (width + padding),
911                 bdata,
912                 i * width,
913                 width);
914             }
915         } else {
916             for (int i=0; i<height; i++) {
917                 System.arraycopy(values,
918                 i * (width + padding),
919                 bdata,
920                 i * width,
921                 width);
922             }
923         }
924         return indexedModel(bdata, 8, paletteEntries);
925     }
926     
927     // Method to read 24 bit BMP image data
928
private void read24Bit(byte[] bdata) {
929         // Padding bytes at the end of each scanline
930
int padding = 0;
931         
932         // width * bitsPerPixel should be divisible by 32
933
int bitsPerScanline = width * 24;
934         if ( bitsPerScanline%32 != 0) {
935             padding = (bitsPerScanline/32 + 1)*32 - bitsPerScanline;
936             padding = (int)Math.ceil(padding/8.0);
937         }
938         
939         
940         int imSize = ((width * 3 + 3) / 4 * 4) * height;
941         // Read till we have the whole image
942
byte values[] = new byte[imSize];
943         try {
944             int bytesRead = 0;
945             while (bytesRead < imSize) {
946                 int r = inputStream.read(values, bytesRead,
947                 imSize - bytesRead);
948                 if (r < 0)
949                     break;
950                 bytesRead += r;
951             }
952         } catch (IOException JavaDoc ioe) {
953             throw new ExceptionConverter(ioe);
954         }
955         
956         int l=0, count;
957         
958         if (isBottomUp) {
959             int max = width*height*3-1;
960             
961             count = -padding;
962             for (int i=0; i<height; i++) {
963                 l = max - (i+1)*width*3 + 1;
964                 count += padding;
965                 for (int j=0; j<width; j++) {
966                     bdata[l + 2] = values[count++];
967                     bdata[l + 1] = values[count++];
968                     bdata[l] = values[count++];
969                     l += 3;
970                 }
971             }
972         } else {
973             count = -padding;
974             for (int i=0; i<height; i++) {
975                 count += padding;
976                 for (int j=0; j<width; j++) {
977                     bdata[l + 2] = values[count++];
978                     bdata[l + 1] = values[count++];
979                     bdata[l] = values[count++];
980                     l += 3;
981                 }
982             }
983         }
984     }
985     
986     private int findMask(int mask) {
987         int k = 0;
988         for (; k < 32; ++k) {
989             if ((mask & 1) == 1)
990                 break;
991             mask >>>= 1;
992         }
993         return mask;
994     }
995     
996     private int findShift(int mask) {
997         int k = 0;
998         for (; k < 32; ++k) {
999             if ((mask & 1) == 1)
1000                break;
1001            mask >>>= 1;
1002        }
1003        return k;
1004    }
1005    
1006    private Image read1632Bit(boolean is32) throws IOException JavaDoc, BadElementException {
1007        
1008        int red_mask = findMask(redMask);
1009        int red_shift = findShift(redMask);
1010        int red_factor = red_mask + 1;
1011        int green_mask = findMask(greenMask);
1012        int green_shift = findShift(greenMask);
1013        int green_factor = green_mask + 1;
1014        int blue_mask = findMask(blueMask);
1015        int blue_shift = findShift(blueMask);
1016        int blue_factor = blue_mask + 1;
1017        byte bdata[] = new byte[width * height * 3];
1018        // Padding bytes at the end of each scanline
1019
int padding = 0;
1020        
1021        if (!is32) {
1022        // width * bitsPerPixel should be divisible by 32
1023
int bitsPerScanline = width * 16;
1024            if ( bitsPerScanline%32 != 0) {
1025                padding = (bitsPerScanline/32 + 1)*32 - bitsPerScanline;
1026                padding = (int)Math.ceil(padding/8.0);
1027            }
1028        }
1029        
1030        int imSize = (int)imageSize;
1031        if (imSize == 0) {
1032            imSize = (int)(bitmapFileSize - bitmapOffset);
1033        }
1034        
1035        int l=0;
1036        int v;
1037        if (isBottomUp) {
1038            for (int i=height - 1; i >= 0; --i) {
1039                l = width * 3 * i;
1040                for (int j=0; j<width; j++) {
1041                    if (is32)
1042                        v = (int)readDWord(inputStream);
1043                    else
1044                        v = readWord(inputStream);
1045                    bdata[l++] = (byte)(((v >>> red_shift) & red_mask) * 256 / red_factor);
1046                    bdata[l++] = (byte)(((v >>> green_shift) & green_mask) * 256 / green_factor);
1047                    bdata[l++] = (byte)(((v >>> blue_shift) & blue_mask) * 256 / blue_factor);
1048                }
1049                for (int m=0; m<padding; m++) {
1050                    inputStream.read();
1051                }
1052            }
1053        } else {
1054            for (int i=0; i<height; i++) {
1055                for (int j=0; j<width; j++) {
1056                    if (is32)
1057                        v = (int)readDWord(inputStream);
1058                    else
1059                        v = readWord(inputStream);
1060                    bdata[l++] = (byte)(((v >>> red_shift) & red_mask) * 256 / red_factor);
1061                    bdata[l++] = (byte)(((v >>> green_shift) & green_mask) * 256 / green_factor);
1062                    bdata[l++] = (byte)(((v >>> blue_shift) & blue_mask) * 256 / blue_factor);
1063                }
1064                for (int m=0; m<padding; m++) {
1065                    inputStream.read();
1066                }
1067            }
1068        }
1069        return new ImgRaw(width, height, 3, 8, bdata);
1070    }
1071    
1072    private Image readRLE8() throws IOException JavaDoc, BadElementException {
1073        
1074        // If imageSize field is not provided, calculate it.
1075
int imSize = (int)imageSize;
1076        if (imSize == 0) {
1077            imSize = (int)(bitmapFileSize - bitmapOffset);
1078        }
1079        
1080        // Read till we have the whole image
1081
byte values[] = new byte[imSize];
1082        int bytesRead = 0;
1083        while (bytesRead < imSize) {
1084            bytesRead += inputStream.read(values, bytesRead,
1085            imSize - bytesRead);
1086        }
1087        
1088        // Since data is compressed, decompress it
1089
byte val[] = decodeRLE(true, values);
1090        
1091        // Uncompressed data does not have any padding
1092
imSize = width * height;
1093        
1094        if (isBottomUp) {
1095            
1096            // Convert the bottom up image to a top down format by copying
1097
// one scanline from the bottom to the top at a time.
1098
// int bytesPerScanline = (int)Math.ceil((double)width/8.0);
1099
byte temp[] = new byte[val.length];
1100            int bytesPerScanline = width;
1101            for (int i=0; i<height; i++) {
1102                System.arraycopy(val,
1103                imSize - (i+1)*(bytesPerScanline),
1104                temp,
1105                i*bytesPerScanline, bytesPerScanline);
1106            }
1107            val = temp;
1108        }
1109        return indexedModel(val, 8, 4);
1110    }
1111    
1112    private Image readRLE4() throws IOException JavaDoc, BadElementException {
1113        
1114        // If imageSize field is not specified, calculate it.
1115
int imSize = (int)imageSize;
1116        if (imSize == 0) {
1117            imSize = (int)(bitmapFileSize - bitmapOffset);
1118        }
1119        
1120        // Read till we have the whole image
1121
byte values[] = new byte[imSize];
1122        int bytesRead = 0;
1123        while (bytesRead < imSize) {
1124            bytesRead += inputStream.read(values, bytesRead,
1125            imSize - bytesRead);
1126        }
1127        
1128        // Decompress the RLE4 compressed data.
1129
byte val[] = decodeRLE(false, values);
1130        
1131        // Invert it as it is bottom up format.
1132
if (isBottomUp) {
1133            
1134            byte inverted[] = val;
1135            val = new byte[width * height];
1136            int l = 0, index, lineEnd;
1137            
1138            for (int i = height-1; i >= 0; i--) {
1139                index = i * width;
1140                lineEnd = l + width;
1141                while(l != lineEnd) {
1142                    val[l++] = inverted[index++];
1143                }
1144            }
1145        }
1146        int stride = ((width + 1) / 2);
1147        byte bdata[] = new byte[stride * height];
1148        int ptr = 0;
1149        int sh = 0;
1150        for (int h = 0; h < height; ++h) {
1151            for (int w = 0; w < width; ++w) {
1152                if ((w & 1) == 0)
1153                    bdata[sh + w / 2] = (byte)(val[ptr++] << 4);
1154                else
1155                    bdata[sh + w / 2] |= (byte)(val[ptr++] & 0x0f);
1156            }
1157            sh += stride;
1158        }
1159        return indexedModel(bdata, 4, 4);
1160    }
1161    
1162    private byte[] decodeRLE(boolean is8, byte values[]) {
1163        byte val[] = new byte[width * height];
1164        try {
1165            int ptr = 0;
1166            int x = 0;
1167            int q = 0;
1168            for (int y = 0; y < height && ptr < values.length;) {
1169                int count = values[ptr++] & 0xff;
1170                if (count != 0) {
1171                    // encoded mode
1172
int bt = values[ptr++] & 0xff;
1173                    if (is8) {
1174                        for (int i = count; i != 0; --i) {
1175                            val[q++] = (byte)bt;
1176                        }
1177                    }
1178                    else {
1179                        for (int i = 0; i < count; ++i) {
1180                            val[q++] = (byte)((i & 1) == 1 ? (bt & 0x0f) : ((bt >>> 4) & 0x0f));
1181                        }
1182                    }
1183                    x += count;
1184                }
1185                else {
1186                    // escape mode
1187
count = values[ptr++] & 0xff;
1188                    if (count == 1)
1189                        break;
1190                    switch (count) {
1191                        case 0:
1192                            x = 0;
1193                            ++y;
1194                            q = y * width;
1195                            break;
1196                        case 2:
1197                            // delta mode
1198
x += values[ptr++] & 0xff;
1199                            y += values[ptr++] & 0xff;
1200                            q = y * width + x;
1201                            break;
1202                        default:
1203                            // absolute mode
1204
if (is8) {
1205                                for (int i = count; i != 0; --i)
1206                                    val[q++] = (byte)(values[ptr++] & 0xff);
1207                            }
1208                            else {
1209                                int bt = 0;
1210                                for (int i = 0; i < count; ++i) {
1211                                    if ((i & 1) == 0)
1212                                        bt = values[ptr++] & 0xff;
1213                                    val[q++] = (byte)((i & 1) == 1 ? (bt & 0x0f) : ((bt >>> 4) & 0x0f));
1214                                }
1215                            }
1216                            x += count;
1217                            // read pad byte
1218
if (is8) {
1219                                if ((count & 1) == 1)
1220                                    ++ptr;
1221                            }
1222                            else {
1223                                if ((count & 3) == 1 || (count & 3) == 2)
1224                                    ++ptr;
1225                            }
1226                            break;
1227                    }
1228                }
1229            }
1230        }
1231        catch (RuntimeException JavaDoc e) {
1232            //empty on purpose
1233
}
1234        
1235        return val;
1236    }
1237    
1238    // Windows defined data type reading methods - everything is little endian
1239

1240    // Unsigned 8 bits
1241
private int readUnsignedByte(InputStream JavaDoc stream) throws IOException JavaDoc {
1242        return (stream.read() & 0xff);
1243    }
1244    
1245    // Unsigned 2 bytes
1246
private int readUnsignedShort(InputStream JavaDoc stream) throws IOException JavaDoc {
1247        int b1 = readUnsignedByte(stream);
1248        int b2 = readUnsignedByte(stream);
1249        return ((b2 << 8) | b1) & 0xffff;
1250    }
1251    
1252    // Signed 16 bits
1253
private int readShort(InputStream JavaDoc stream) throws IOException JavaDoc {
1254        int b1 = readUnsignedByte(stream);
1255        int b2 = readUnsignedByte(stream);
1256        return (b2 << 8) | b1;
1257    }
1258    
1259    // Unsigned 16 bits
1260
private int readWord(InputStream JavaDoc stream) throws IOException JavaDoc {
1261        return readUnsignedShort(stream);
1262    }
1263    
1264    // Unsigned 4 bytes
1265
private long readUnsignedInt(InputStream JavaDoc stream) throws IOException JavaDoc {
1266        int b1 = readUnsignedByte(stream);
1267        int b2 = readUnsignedByte(stream);
1268        int b3 = readUnsignedByte(stream);
1269        int b4 = readUnsignedByte(stream);
1270        long l = (long)((b4 << 24) | (b3 << 16) | (b2 << 8) | b1);
1271        return l & 0xffffffff;
1272    }
1273    
1274    // Signed 4 bytes
1275
private int readInt(InputStream JavaDoc stream) throws IOException JavaDoc {
1276        int b1 = readUnsignedByte(stream);
1277        int b2 = readUnsignedByte(stream);
1278        int b3 = readUnsignedByte(stream);
1279        int b4 = readUnsignedByte(stream);
1280        return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
1281    }
1282    
1283    // Unsigned 4 bytes
1284
private long readDWord(InputStream JavaDoc stream) throws IOException JavaDoc {
1285        return readUnsignedInt(stream);
1286    }
1287    
1288    // 32 bit signed value
1289
private int readLong(InputStream JavaDoc stream) throws IOException JavaDoc {
1290        return readInt(stream);
1291    }
1292}
1293
Popular Tags