BMPImageEncoder.java
上传用户:btjssb159
上传日期:2018-01-04
资源大小:241k
文件大小:14k
源码类别:

DNA

开发平台:

Java

  1. /*
  2.  * Copyright (c) 2001 Sun Microsystems, Inc. All Rights Reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without 
  5.  * modification, are permitted provided that the following conditions are met:
  6.  * 
  7.  * -Redistributions of source code must retain the above copyright notice, this 
  8.  * list of conditions and the following disclaimer.
  9.  *
  10.  * -Redistribution in binary form must reproduct the above copyright notice,
  11.  * this list of conditions and the following disclaimer in the documentation
  12.  * and/or other materials provided with the distribution.
  13.  * 
  14.  * Neither the name of Sun Microsystems, Inc. or the names of contributors may
  15.  * be used to endorse or promote products derived from this software without
  16.  * specific prior written permission.
  17.  * 
  18.  * This software is provided "AS IS," without a warranty of any kind. ALL
  19.  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
  20.  * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
  21.  * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
  22.  * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
  23.  * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
  24.  * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
  25.  * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
  26.  * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
  27.  * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
  28.  * POSSIBILITY OF SUCH DAMAGES.
  29.  * 
  30.  * You acknowledge that Software is not designed,licensed or intended for use in 
  31.  * the design, construction, operation or maintenance of any nuclear facility.
  32.  */
  33. import java.io.OutputStream;
  34. import java.io.IOException;
  35. import java.awt.image.Raster;
  36. import java.awt.image.DataBuffer;
  37. import java.awt.image.DataBufferByte;
  38. import java.awt.image.RenderedImage;
  39. import java.awt.image.SampleModel;
  40. import java.awt.Rectangle;
  41. import java.awt.image.PixelInterleavedSampleModel;
  42. import java.awt.image.MultiPixelPackedSampleModel;
  43. import java.awt.image.ColorModel;
  44. import java.awt.image.IndexColorModel;
  45. /**
  46.  * An ImageEncoder for the various versions of the BMP image file format.
  47.  *
  48.  * Unless specified otherwise by the BMPDecodeParam object passed to the
  49.  * constructor, Version 3 will be the default version used.
  50.  * 
  51.  * <p>If the image to be encoded has an IndexColorModel and can be encoded
  52.  * using upto 8 bits per pixel, the image will be written out as a Palette
  53.  * color image with an appropriate number of bits per pixel. For example an
  54.  * image having a 256 color IndexColorModel will be written out as a Palette
  55.  * image with 8 bits per pixel while one with a 16 color palette will be 
  56.  * written out as a Palette image with 4 bits per pixel. For all other images,
  57.  * the 24 bit image format will be used.
  58.  *
  59.  *
  60.  */
  61. public class BMPImageEncoder extends ImageEncoderImpl {
  62.     private OutputStream output;
  63.     private int version;
  64.     private boolean isCompressed, isTopDown;
  65.     private int w, h;
  66.     /**
  67.      * An ImageEncoder for the BMP file format.
  68.      *
  69.      * @param output       The OutputStream to write to.
  70.      * @param param        The BMPEncodeParam object.
  71.      */
  72.     public BMPImageEncoder(OutputStream output, ImageEncodeParam param) {
  73. super(output, param);
  74. this.output = output;
  75. BMPEncodeParam bmpParam;
  76. if (param == null) {
  77.     // Use default valued BMPEncodeParam
  78.     bmpParam = new BMPEncodeParam();
  79. } else {
  80.     bmpParam = (BMPEncodeParam)param;
  81. }
  82. this.version = bmpParam.getVersion();
  83. this.isCompressed = bmpParam.isCompressed();
  84. this.isTopDown = bmpParam.isTopDown();
  85.     }
  86.     /**
  87.      * Encodes a RenderedImage and writes the output to the
  88.      * OutputStream associated with this ImageEncoder.
  89.      */
  90.     public void encode(RenderedImage im) throws IOException {
  91. // Get image dimensions
  92. int minX = im.getMinX();
  93.         int minY = im.getMinY();
  94. w = im.getWidth();
  95. h = im.getHeight();
  96. // Default is using 24 bits per pixel.
  97. int bitsPerPixel = 24;
  98. boolean isPalette = false;
  99. int paletteEntries = 0;
  100. IndexColorModel icm = null;
  101.  
  102. SampleModel sm = im.getSampleModel();
  103. int numBands = sm.getNumBands();
  104. ColorModel cm = im.getColorModel();
  105. if (numBands != 1 && numBands != 3) {
  106.     throw new 
  107. IllegalArgumentException(JaiI18N.getString("BMPImageEncoder1"));
  108. }
  109. int sampleSize[] = sm.getSampleSize();
  110. if (sampleSize[0] > 8) {
  111.     throw new RuntimeException(JaiI18N.getString("BMPImageEncoder2"));
  112. }
  113. for (int i=1; i<sampleSize.length; i++) {
  114.     if (sampleSize[i] != sampleSize[0]) {
  115. throw 
  116.     new RuntimeException(JaiI18N.getString("BMPImageEncoder3"));
  117.     }
  118. }
  119. // Float and Double data cannot be written in a BMP format.
  120. int dataType = sm.getTransferType();
  121.         if ((dataType == DataBuffer.TYPE_USHORT) ||
  122.     (dataType == DataBuffer.TYPE_SHORT) ||
  123.     (dataType == DataBuffer.TYPE_INT) ||
  124.     (dataType == DataBuffer.TYPE_FLOAT) ||
  125.             (dataType == DataBuffer.TYPE_DOUBLE)) {
  126.             throw new RuntimeException(JaiI18N.getString("BMPImageEncoder0"));
  127.         }
  128. // Number of bytes that a scanline for the image written out will have.
  129. int destScanlineBytes = w * numBands;
  130. // Currently we only write out uncompressed data, so ignore 
  131. int compression = 0;
  132. byte r[] = null, g[] = null, b[] = null, a[] = null;
  133. if (cm instanceof IndexColorModel) {
  134.     isPalette = true;
  135.     icm = (IndexColorModel)cm;
  136.     paletteEntries = icm.getMapSize();
  137.     if (paletteEntries <= 2) {
  138. bitsPerPixel = 1;
  139. destScanlineBytes = (int)Math.ceil((double)w/8.0);
  140.     } else if (paletteEntries <= 16) {
  141. bitsPerPixel = 4;
  142. destScanlineBytes = (int)Math.ceil((double)w/2.0);
  143.     } else if (paletteEntries <= 256) {
  144. bitsPerPixel = 8;
  145.     } else {
  146. // Cannot be written as a Palette image. So write out as 
  147. // 24 bit image.
  148. bitsPerPixel = 24;
  149. isPalette = false;
  150. paletteEntries = 0;
  151. destScanlineBytes = w * 3;
  152.     }
  153.     if (isPalette == true) {
  154. r = new byte[paletteEntries];
  155. g = new byte[paletteEntries];
  156. b = new byte[paletteEntries];
  157. a = new byte[paletteEntries];
  158. icm.getAlphas(a);
  159. icm.getReds(r);
  160. icm.getGreens(g);
  161. icm.getBlues(b);
  162.     }
  163. } else {
  164.     
  165.     // Grey scale images
  166.     if (numBands == 1) {
  167. isPalette = true;
  168. paletteEntries = 256;
  169. // int sampleSize[] = sm.getSampleSize();
  170. bitsPerPixel = sampleSize[0];
  171. destScanlineBytes = (int)Math.ceil((double)(w * bitsPerPixel) /
  172.    8.0);
  173. r = new byte[256];
  174. g = new byte[256];
  175. b = new byte[256];
  176. a = new byte[256];
  177. for (int i = 0; i < 256; i++) {
  178.     r[i] = (byte)i;
  179.     g[i] = (byte)i;
  180.     b[i] = (byte)i;
  181.     a[i] = (byte)i;
  182. }
  183.     }
  184. }
  185. // actual writing of image data
  186. int fileSize = 0;
  187. int offset = 0;
  188. int headerSize = 0;
  189. int imageSize = 0;
  190. int xPelsPerMeter = 0;
  191. int yPelsPerMeter = 0;
  192. int colorsUsed = 0;
  193. int colorsImportant = paletteEntries;
  194. int padding = 0;
  195. // Calculate padding for each scanline
  196. int remainder = destScanlineBytes % 4;
  197. if (remainder != 0) {
  198.     padding = 4 - remainder;
  199. }
  200. switch (version) {     
  201. case BMPEncodeParam.VERSION_2:
  202.     offset = 26 + paletteEntries * 3;
  203.     headerSize = 12;
  204.     imageSize = (destScanlineBytes + padding) * h;
  205.     fileSize = imageSize + offset;
  206.     throw new 
  207. RuntimeException(JaiI18N.getString("BMPImageEncoder5"));
  208.     //break;
  209.     
  210. case BMPEncodeParam.VERSION_3:
  211.     // FileHeader is 14 bytes, BitmapHeader is 40 bytes, 
  212.     // add palette size and that is where the data will begin
  213.     offset = 54 + paletteEntries * 4;
  214.     imageSize = (destScanlineBytes + padding) * h;
  215.     fileSize = imageSize + offset;
  216.     headerSize = 40;
  217.     break;
  218. case BMPEncodeParam.VERSION_4:
  219.     headerSize = 108;
  220.     throw new
  221. RuntimeException(JaiI18N.getString("BMPImageEncoder5"));
  222.     // break;
  223. }
  224. writeFileHeader(fileSize, offset);
  225. writeInfoHeader(headerSize, bitsPerPixel);
  226. // compression
  227. writeDWord(compression);
  228. // imageSize
  229. writeDWord(imageSize);
  230. // xPelsPerMeter
  231. writeDWord(xPelsPerMeter);
  232. // yPelsPerMeter
  233. writeDWord(yPelsPerMeter);
  234. // Colors Used
  235. writeDWord(colorsUsed);
  236. // Colors Important
  237. writeDWord(colorsImportant);
  238. // palette
  239. if (isPalette == true) {
  240.     // write palette
  241.     switch(version) {
  242. // has 3 field entries
  243.     case BMPEncodeParam.VERSION_2:
  244. for (int i=0; i<paletteEntries; i++) {
  245.     output.write(b[i]);
  246.     output.write(g[i]);
  247.     output.write(r[i]);
  248. }
  249. break;
  250. // has 4 field entries
  251.     default:
  252. for (int i=0; i<paletteEntries; i++) {
  253.     output.write(b[i]);
  254.     output.write(g[i]);
  255.     output.write(r[i]);
  256.     output.write(a[i]);
  257. }
  258. break;
  259.     }
  260.     
  261. } // else no palette
  262. // Writing of actual image data
  263. int scanlineBytes = w * numBands;
  264. // Buffer for up to 8 rows of pixels
  265. int[] pixels = new int[8 * scanlineBytes];
  266.         // Also create a buffer to hold one line of the data
  267.         // to be written to the file, so we can use array writes.
  268.         byte[] bpixels = new byte[destScanlineBytes];
  269. int l;
  270. if (!isTopDown) {
  271.     // Process 8 rows at a time so all but the first will have a
  272.     // multiple of 8 rows.
  273.     int lastRow = minY + h;
  274.     for (int row = (lastRow-1); row >= minY; row -= 8) {
  275. // Number of rows being read
  276. int rows = Math.min(8, row - minY + 1);
  277. // Get the pixels
  278. Raster src = im.getData(new Rectangle(minX, row - rows + 1, 
  279.       w, rows));
  280. src.getPixels(minX, row - rows + 1, w, rows, pixels);
  281. l = 0;
  282. // Last possible position in the pixels array
  283. int max = scanlineBytes * rows - 1;
  284. for (int i=0; i<rows; i++) {
  285.     // Beginning of each scanline in the pixels array    
  286.     l = max - (i+1) * scanlineBytes + 1;
  287.     writePixels(l, scanlineBytes, bitsPerPixel, pixels,
  288. bpixels, padding, numBands, icm);
  289. }
  290.     }
  291. } else {
  292.     // Process 8 rows at a time so all but the last will have a
  293.     // multiple of 8 rows.
  294.     int lastRow = minY + h;
  295.     for (int row = minY; row < lastRow; row += 8) {
  296. int rows = Math.min(8, lastRow - row);
  297. // Get the pixels
  298. Raster src = im.getData(new Rectangle(minX, row, 
  299.       w, rows));
  300. src.getPixels(minX, row, w, rows, pixels);
  301. l=0;
  302. for (int i=0; i<rows; i++) {
  303.     writePixels(l, scanlineBytes, bitsPerPixel, pixels, 
  304. bpixels, padding, numBands, icm);
  305. }
  306.     }
  307. }
  308.     }
  309.         
  310.     private void writePixels(int l, int scanlineBytes, int bitsPerPixel, 
  311.      int pixels[], byte bpixels[], 
  312.                              int padding, int numBands,
  313.      IndexColorModel icm) throws IOException {
  314. int pixel = 0;
  315.         int k = 0;
  316. switch (bitsPerPixel) {
  317. case 1:
  318.     for (int j=0; j<scanlineBytes/8; j++) {
  319. bpixels[k++] = (byte)((pixels[l++]  << 7) |
  320.                                       (pixels[l++]  << 6) |
  321.                                       (pixels[l++]  << 5) |
  322.                                       (pixels[l++]  << 4) |
  323.                                       (pixels[l++]  << 3) |
  324.                                       (pixels[l++]  << 2) |
  325.                                       (pixels[l++]  << 1) |
  326.                                        pixels[l++]);
  327.     }
  328.     
  329.             // Partially filled last byte, if any
  330.             if (scanlineBytes%8 > 0) {
  331.                 pixel = 0;
  332.                 for (int j=0; j<scanlineBytes%8; j++) {
  333.                     pixel |= (pixels[l++] << (7 - j));
  334.                 }
  335.                 bpixels[k++] = (byte)pixel;
  336.             }
  337.             output.write(bpixels, 0, (scanlineBytes+7)/8); 
  338.     break;
  339.     
  340. case 4:
  341.     for (int j=0; j<scanlineBytes/2; j++) {
  342.                 pixel = (pixels[l++] << 4) | pixels[l++];
  343.                 bpixels[k++] = (byte)pixel;
  344.     }
  345.             // Put the last pixel of odd-length lines in the 4 MSBs
  346.             if ((scanlineBytes%2) == 1) {
  347.                 pixel = pixels[l] << 4;
  348.                 bpixels[k++] = (byte)pixel;
  349.             }
  350.             output.write(bpixels, 0, (scanlineBytes+1)/2);
  351.     break;
  352.     
  353. case 8:
  354.     for (int j=0; j<scanlineBytes; j++) {
  355.                 bpixels[j] = (byte)pixels[l++];
  356.     }
  357.             output.write(bpixels, 0, scanlineBytes);
  358.     break;
  359.     
  360. case 24:
  361.     if (numBands == 3) {
  362. for (int j=0; j<scanlineBytes; j+=3) {
  363.     // Since BMP needs BGR format
  364.                     bpixels[k++] = (byte)(pixels[l+2]);
  365.                     bpixels[k++] = (byte)(pixels[l+1]);
  366.                     bpixels[k++] = (byte)(pixels[l]);
  367.     l+=3;
  368. }
  369.                 output.write(bpixels, 0, scanlineBytes);
  370.     } else {
  371. // Case where IndexColorModel had > 256 colors.  
  372. int entries = icm.getMapSize();
  373. byte r[] = new byte[entries];
  374. byte g[] = new byte[entries];
  375. byte b[] = new byte[entries];
  376. icm.getReds(r);
  377. icm.getGreens(g);
  378. icm.getBlues(b);
  379. int index;
  380. for (int j=0; j<scanlineBytes; j++) {
  381.     index = pixels[l];
  382.                     bpixels[k++] = b[index];
  383.                     bpixels[k++] = g[index];
  384.                     bpixels[k++] = b[index];
  385.     l++;
  386. }
  387.                 output.write(bpixels, 0, scanlineBytes*3);
  388.     }
  389.     break;
  390.     
  391. }
  392. // Write out the padding
  393. for(k=0; k<padding; k++) {
  394.     output.write(0);
  395. }
  396.     }    
  397.     private void writeFileHeader(int fileSize, int offset) throws IOException {
  398. // magic value
  399. output.write('B');                     
  400. output.write('M');
  401. // File size
  402. writeDWord(fileSize);
  403. // reserved1 and reserved2
  404. output.write(0);
  405. output.write(0);
  406. output.write(0);
  407. output.write(0);
  408. // offset to image data
  409. writeDWord(offset);
  410.     }
  411.     
  412.     private void writeInfoHeader(int headerSize, int bitsPerPixel) 
  413. throws IOException {
  414. // size of header
  415. writeDWord(headerSize);
  416. // width
  417. writeDWord(w);
  418. // height
  419. writeDWord(h);
  420. // number of planes
  421. writeWord(1);
  422. // Bits Per Pixel
  423. writeWord(bitsPerPixel);
  424.     }
  425.     
  426.     // Methods for little-endian writing
  427.     public void writeWord(int word) throws IOException {
  428. output.write(word & 0xff);
  429. output.write((word & 0xff00) >> 8);
  430.     }
  431.     
  432.     public void writeDWord(int dword) throws IOException {
  433. output.write(dword & 0xff);
  434. output.write((dword & 0xff00) >> 8);
  435. output.write((dword & 0xff0000) >> 16);
  436. output.write((dword & 0xff000000) >> 24);
  437.     } 
  438. }