jdk中解析BMP图片的过程
在com.sun.imageio.plugins.bmp包中的BMPImageReader类中
public void readHeader() throws IOException { if (gotHeader) return; if (iis == null) { throw new IllegalStateException("Input source not set!"); } int profileData = 0, profileSize = 0; this.metadata = new BMPMetadata(); iis.mark(); // read and check the magic marker byte[] marker = new byte[2]; iis.read(marker); if (marker[0] != 0x42 || marker[1] != 0x4d) throw new IllegalArgumentException(I18N.getString("BMPImageReader1")); // Read file size bitmapFileSize = iis.readUnsignedInt(); // skip the two reserved fields iis.skipBytes(4); // Offset to the bitmap from the beginning bitmapOffset = iis.readUnsignedInt(); // End File Header // Start BitmapCoreHeader long size = iis.readUnsignedInt(); if (size == 12) { width = iis.readShort(); height = iis.readShort(); } else { width = iis.readInt(); height = iis.readInt(); } metadata.width = width; metadata.height = height; int planes = iis.readUnsignedShort(); bitsPerPixel = iis.readUnsignedShort(); //metadata.colorPlane = planes; metadata.bitsPerPixel = (short)bitsPerPixel; // As BMP always has 3 rgb bands, except for Version 5, // which is bgra numBands = 3; if (size == 12) { // Windows 2.x and OS/2 1.x metadata.bmpVersion = VERSION_2; // Classify the image type if (bitsPerPixel == 1) { imageType = VERSION_2_1_BIT; } else if (bitsPerPixel == 4) { imageType = VERSION_2_4_BIT; } else if (bitsPerPixel == 8) { imageType = VERSION_2_8_BIT; } else if (bitsPerPixel == 24) { imageType = VERSION_2_24_BIT; } // Read in the palette int numberOfEntries = (int)((bitmapOffset - 14 - size) / 3); int sizeOfPalette = numberOfEntries*3; palette = new byte[sizeOfPalette]; iis.readFully(palette, 0, sizeOfPalette); metadata.palette = palette; metadata.paletteSize = numberOfEntries; } else { compression = iis.readUnsignedInt(); imageSize = iis.readUnsignedInt(); long xPelsPerMeter = iis.readInt(); long yPelsPerMeter = iis.readInt(); long colorsUsed = iis.readUnsignedInt(); long colorsImportant = iis.readUnsignedInt(); metadata.compression = (int)compression; metadata.xPixelsPerMeter = (int)xPelsPerMeter; metadata.yPixelsPerMeter = (int)yPelsPerMeter; metadata.colorsUsed = (int)colorsUsed; metadata.colorsImportant = (int)colorsImportant; if (size == 40) { // Windows 3.x and Windows NT switch((int)compression) { case BI_JPEG: case BI_PNG: metadata.bmpVersion = VERSION_3; imageType = VERSION_3_XP_EMBEDDED; break; case BI_RGB: // No compression case BI_RLE8: // 8-bit RLE compression case BI_RLE4: // 4-bit RLE compression // Read in the palette int numberOfEntries = (int)((bitmapOffset-14-size) / 4); int sizeOfPalette = numberOfEntries * 4; palette = new byte[sizeOfPalette]; iis.readFully(palette, 0, sizeOfPalette); metadata.palette = palette; metadata.paletteSize = numberOfEntries; if (bitsPerPixel == 1) { imageType = VERSION_3_1_BIT; } else if (bitsPerPixel == 4) { imageType = VERSION_3_4_BIT; } else if (bitsPerPixel == 8) { imageType = VERSION_3_8_BIT; } else if (bitsPerPixel == 24) { imageType = VERSION_3_24_BIT; } else if (bitsPerPixel == 16) { imageType = VERSION_3_NT_16_BIT; redMask = 0x7C00; greenMask = 0x3E0; blueMask = (1 << 5) - 1;// 0x1F; metadata.redMask = redMask; metadata.greenMask = greenMask; metadata.blueMask = blueMask; } else if (bitsPerPixel == 32) { imageType = VERSION_3_NT_32_BIT; redMask = 0x00FF0000; greenMask = 0x0000FF00; blueMask = 0x000000FF; metadata.redMask = redMask; metadata.greenMask = greenMask; metadata.blueMask = blueMask; } metadata.bmpVersion = VERSION_3; break; case BI_BITFIELDS: if (bitsPerPixel == 16) { imageType = VERSION_3_NT_16_BIT; } else if (bitsPerPixel == 32) { imageType = VERSION_3_NT_32_BIT; } // BitsField encoding redMask = (int)iis.readUnsignedInt(); greenMask = (int)iis.readUnsignedInt(); blueMask = (int)iis.readUnsignedInt(); metadata.redMask = redMask; metadata.greenMask = greenMask; metadata.blueMask = blueMask; if (colorsUsed != 0) { // there is a palette sizeOfPalette = (int)colorsUsed*4; palette = new byte[sizeOfPalette]; iis.readFully(palette, 0, sizeOfPalette); metadata.palette = palette; metadata.paletteSize = (int)colorsUsed; } metadata.bmpVersion = VERSION_3_NT; break; default: throw new RuntimeException(I18N.getString("BMPImageReader2")); } } else if (size == 108 || size == 124) { // Windows 4.x BMP if (size == 108) metadata.bmpVersion = VERSION_4; else if (size == 124) metadata.bmpVersion = VERSION_5; // rgb masks, valid only if comp is BI_BITFIELDS redMask = (int)iis.readUnsignedInt(); greenMask = (int)iis.readUnsignedInt(); blueMask = (int)iis.readUnsignedInt(); // Only supported for 32bpp BI_RGB argb alphaMask = (int)iis.readUnsignedInt(); long csType = iis.readUnsignedInt(); int redX = iis.readInt(); int redY = iis.readInt(); int redZ = iis.readInt(); int greenX = iis.readInt(); int greenY = iis.readInt(); int greenZ = iis.readInt(); int blueX = iis.readInt(); int blueY = iis.readInt(); int blueZ = iis.readInt(); long gammaRed = iis.readUnsignedInt(); long gammaGreen = iis.readUnsignedInt(); long gammaBlue = iis.readUnsignedInt(); if (size == 124) { metadata.intent = iis.readInt(); profileData = iis.readInt(); profileSize = iis.readInt(); iis.skipBytes(4); } metadata.colorSpace = (int)csType; if (csType == LCS_CALIBRATED_RGB) { // All the new fields are valid only for this case metadata.redX = redX; metadata.redY = redY; metadata.redZ = redZ; metadata.greenX = greenX; metadata.greenY = greenY; metadata.greenZ = greenZ; metadata.blueX = blueX; metadata.blueY = blueY; metadata.blueZ = blueZ; metadata.gammaRed = (int)gammaRed; metadata.gammaGreen = (int)gammaGreen; metadata.gammaBlue = (int)gammaBlue; } // Read in the palette int numberOfEntries = (int)((bitmapOffset-14-size) / 4); int sizeOfPalette = numberOfEntries*4; palette = new byte[sizeOfPalette]; iis.readFully(palette, 0, sizeOfPalette); metadata.palette = palette; metadata.paletteSize = numberOfEntries; switch ((int)compression) { case BI_JPEG: case BI_PNG: if (size == 108) { imageType = VERSION_4_XP_EMBEDDED; } else if (size == 124) { imageType = VERSION_5_XP_EMBEDDED; } break; default: if (bitsPerPixel == 1) { imageType = VERSION_4_1_BIT; } else if (bitsPerPixel == 4) { imageType = VERSION_4_4_BIT; } else if (bitsPerPixel == 8) { imageType = VERSION_4_8_BIT; } else if (bitsPerPixel == 16) { imageType = VERSION_4_16_BIT; if ((int)compression == BI_RGB) { redMask = 0x7C00; greenMask = 0x3E0; blueMask = 0x1F; } } else if (bitsPerPixel == 24) { imageType = VERSION_4_24_BIT; } else if (bitsPerPixel == 32) { imageType = VERSION_4_32_BIT; if ((int)compression == BI_RGB) { redMask = 0x00FF0000; greenMask = 0x0000FF00; blueMask = 0x000000FF; } } metadata.redMask = redMask; metadata.greenMask = greenMask; metadata.blueMask = blueMask; metadata.alphaMask = alphaMask; } } else { throw new RuntimeException(I18N.getString("BMPImageReader3")); } } if (height > 0) { // bottom up image isBottomUp = true; } else { // top down image isBottomUp = false; height = Math.abs(height); } // Reset Image Layout so there's only one tile. //Define the color space ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); if (metadata.colorSpace == PROFILE_LINKED || metadata.colorSpace == PROFILE_EMBEDDED) { iis.mark(); iis.skipBytes(profileData - size); byte[] profile = new byte[profileSize]; iis.readFully(profile, 0, profileSize); iis.reset(); try { if (metadata.colorSpace == PROFILE_LINKED) colorSpace = new ICC_ColorSpace(ICC_Profile.getInstance(new String(profile))); else colorSpace = new ICC_ColorSpace(ICC_Profile.getInstance(profile)); } catch (Exception e) { colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); } } if (bitsPerPixel == 0 || compression == BI_JPEG || compression == BI_PNG ) { // the colorModel and sampleModel will be initialzed // by the reader of embedded image colorModel = null; sampleModel = null; } else if (bitsPerPixel == 1 || bitsPerPixel == 4 || bitsPerPixel == 8) { // When number of bitsPerPixel is <= 8, we use IndexColorModel. numBands = 1; if (bitsPerPixel == 8) { int[] bandOffsets = new int[numBands]; for (int i = 0; i < numBands; i++) { bandOffsets[i] = numBands -1 -i; } sampleModel = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, width, height, numBands, numBands * width, bandOffsets); } else { // 1 and 4 bit pixels can be stored in a packed format. sampleModel = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, width, height, bitsPerPixel); } // Create IndexColorModel from the palette. byte r[], g[], b[]; if (imageType == VERSION_2_1_BIT || imageType == VERSION_2_4_BIT || imageType == VERSION_2_8_BIT) { size = palette.length/3; if (size > 256) { size = 256; } int off; r = new byte[(int)size]; g = new byte[(int)size]; b = new byte[(int)size]; for (int i=0; i<(int)size; i++) { off = 3 * i; b[i] = palette[off]; g[i] = palette[off+1]; r[i] = palette[off+2]; } } else { size = palette.length/4; if (size > 256) { size = 256; } int off; r = new byte[(int)size]; g = new byte[(int)size]; b = new byte[(int)size]; for (int i=0; i<size; i++) { off = 4 * i; b[i] = palette[off]; g[i] = palette[off+1]; r[i] = palette[off+2]; } } if (ImageUtil.isIndicesForGrayscale(r, g, b)) colorModel = ImageUtil.createColorModel(null, sampleModel); else colorModel = new IndexColorModel(bitsPerPixel, (int)size, r, g, b); } else if (bitsPerPixel == 16) { numBands = 3; sampleModel = new SinglePixelPackedSampleModel(DataBuffer.TYPE_USHORT, width, height, new int[] {redMask, greenMask, blueMask}); colorModel = new DirectColorModel(colorSpace, 16, redMask, greenMask, blueMask, 0, false, DataBuffer.TYPE_USHORT); } else if (bitsPerPixel == 32) { numBands = alphaMask == 0 ? 3 : 4; // The number of bands in the SampleModel is determined by // the length of the mask array passed in. int[] bitMasks = numBands == 3 ? new int[] {redMask, greenMask, blueMask} : new int[] {redMask, greenMask, blueMask, alphaMask}; sampleModel = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, width, height, bitMasks); colorModel = new DirectColorModel(colorSpace, 32, redMask, greenMask, blueMask, alphaMask, false, DataBuffer.TYPE_INT); } else { numBands = 3; // Create SampleModel int[] bandOffsets = new int[numBands]; for (int i = 0; i < numBands; i++) { bandOffsets[i] = numBands -1 -i; } sampleModel = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, width, height, numBands, numBands * width, bandOffsets); colorModel = ImageUtil.createColorModel(colorSpace, sampleModel); } originalSampleModel = sampleModel; originalColorModel = colorModel; // Reset to the start of bitmap; then jump to the //start of image data iis.reset(); iis.skipBytes(bitmapOffset); gotHeader = true; } public BufferedImage read(int imageIndex, ImageReadParam param) throws IOException { if (iis == null) { throw new IllegalStateException(I18N.getString("BMPImageReader5")); } checkIndex(imageIndex); clearAbortRequest(); processImageStarted(imageIndex); if (param == null) param = getDefaultReadParam(); //read header readHeader(); sourceRegion = new Rectangle(0, 0, 0, 0); destinationRegion = new Rectangle(0, 0, 0, 0); computeRegions(param, this.width, this.height, param.getDestination(), sourceRegion, destinationRegion); scaleX = param.getSourceXSubsampling(); scaleY = param.getSourceYSubsampling(); // If the destination band is set used it sourceBands = param.getSourceBands(); destBands = param.getDestinationBands(); seleBand = (sourceBands != null) && (destBands != null); noTransform = destinationRegion.equals(new Rectangle(0, 0, width, height)) || seleBand; if (!seleBand) { sourceBands = new int[numBands]; destBands = new int[numBands]; for (int i = 0; i < numBands; i++) destBands[i] = sourceBands[i] = i; } // If the destination is provided, then use it. Otherwise, create new one bi = param.getDestination(); // Get the image data. WritableRaster raster = null; if (bi == null) { if (sampleModel != null && colorModel != null) { sampleModel = sampleModel.createCompatibleSampleModel(destinationRegion.x + destinationRegion.width, destinationRegion.y + destinationRegion.height); if (seleBand) sampleModel = sampleModel.createSubsetSampleModel(sourceBands); raster = Raster.createWritableRaster(sampleModel, new Point()); bi = new BufferedImage(colorModel, raster, false, null); } } else { raster = bi.getWritableTile(0, 0); sampleModel = bi.getSampleModel(); colorModel = bi.getColorModel(); noTransform &= destinationRegion.equals(raster.getBounds()); } byte bdata[] = null; // buffer for byte data short sdata[] = null; // buffer for short data int idata[] = null; // buffer for int data // the sampleModel can be null in case of embedded image if (sampleModel != null) { if (sampleModel.getDataType() == DataBuffer.TYPE_BYTE) bdata = (byte[]) ((DataBufferByte)raster.getDataBuffer()).getData(); else if (sampleModel.getDataType() == DataBuffer.TYPE_USHORT) sdata = (short[]) ((DataBufferUShort)raster.getDataBuffer()).getData(); else if (sampleModel.getDataType() == DataBuffer.TYPE_INT) idata = (int[]) ((DataBufferInt)raster.getDataBuffer()).getData(); } // There should only be one tile. switch(imageType) { case VERSION_2_1_BIT: // no compression read1Bit(bdata); break; case VERSION_2_4_BIT: // no compression read4Bit(bdata); break; case VERSION_2_8_BIT: // no compression read8Bit(bdata); break; case VERSION_2_24_BIT: // no compression read24Bit(bdata); break; case VERSION_3_1_BIT: // 1-bit images cannot be compressed. read1Bit(bdata); break; case VERSION_3_4_BIT: switch((int)compression) { case BI_RGB: read4Bit(bdata); break; case BI_RLE4: readRLE4(bdata); break; default: throw new RuntimeException(I18N.getString("BMPImageReader1")); } break; case VERSION_3_8_BIT: switch((int)compression) { case BI_RGB: read8Bit(bdata); break; case BI_RLE8: readRLE8(bdata); break; default: throw new RuntimeException(I18N.getString("BMPImageReader1")); } break; case VERSION_3_24_BIT: // 24-bit images are not compressed read24Bit(bdata); break; case VERSION_3_NT_16_BIT: read16Bit(sdata); break; case VERSION_3_NT_32_BIT: read32Bit(idata); break; case VERSION_3_XP_EMBEDDED: case VERSION_4_XP_EMBEDDED: case VERSION_5_XP_EMBEDDED: bi = readEmbedded((int)compression, bi, param); break; case VERSION_4_1_BIT: read1Bit(bdata); break; case VERSION_4_4_BIT: switch((int)compression) { case BI_RGB: read4Bit(bdata); break; case BI_RLE4: readRLE4(bdata); break; default: throw new RuntimeException(I18N.getString("BMPImageReader1")); } case VERSION_4_8_BIT: switch((int)compression) { case BI_RGB: read8Bit(bdata); break; case BI_RLE8: readRLE8(bdata); break; default: throw new RuntimeException(I18N.getString("BMPImageReader1")); } break; case VERSION_4_16_BIT: read16Bit(sdata); break; case VERSION_4_24_BIT: read24Bit(bdata); break; case VERSION_4_32_BIT: read32Bit(idata); break; } if (abortRequested()) processReadAborted(); else processImageComplete(); return bi; }
当然这里还有对gif,jpg,bmp等常用图片的解析,有兴趣的可以去看下。