📄 pngimagewriter.java
字号:
// Create row buffers int samplesPerByte = 8/metadata.IHDR_bitDepth; int numSamples = width*numBands; int[] samples = new int[numSamples]; int bytesPerRow = hpixels*numBands; if (metadata.IHDR_bitDepth < 8) { bytesPerRow = (bytesPerRow + samplesPerByte - 1)/samplesPerByte; } else if (metadata.IHDR_bitDepth == 16) { bytesPerRow *= 2; } IndexColorModel icm_gray_alpha = null; if (metadata.IHDR_colorType == PNGImageReader.PNG_COLOR_GRAY_ALPHA && image.getColorModel() instanceof IndexColorModel) { // reserve space for alpha samples bytesPerRow *= 2; // will be used to calculate alpha value for the pixel icm_gray_alpha = (IndexColorModel)image.getColorModel(); } currRow = new byte[bytesPerRow + bpp]; prevRow = new byte[bytesPerRow + bpp]; filteredRows = new byte[5][bytesPerRow + bpp]; int bitDepth = metadata.IHDR_bitDepth; for (int row = minY + yOffset; row < minY + height; row += ySkip) { Rectangle rect = new Rectangle(minX, row, width, 1); Raster ras = image.getData(rect); if (sourceBands != null) { ras = ras.createChild(minX, row, width, 1, minX, row, sourceBands); } ras.getPixels(minX, row, width, 1, samples); if (image.getColorModel().isAlphaPremultiplied()) { WritableRaster wr = ras.createCompatibleWritableRaster(); wr.setPixels(wr.getMinX(), wr.getMinY(), wr.getWidth(), wr.getHeight(), samples); image.getColorModel().coerceData(wr, false); wr.getPixels(wr.getMinX(), wr.getMinY(), wr.getWidth(), wr.getHeight(), samples); } // Reorder palette data if necessary int[] paletteOrder = metadata.PLTE_order; if (paletteOrder != null) { for (int i = 0; i < numSamples; i++) { samples[i] = paletteOrder[samples[i]]; } } int count = bpp; // leave first 'bpp' bytes zero int pos = 0; int tmp = 0; switch (bitDepth) { case 1: case 2: case 4: // Image can only have a single band int mask = samplesPerByte - 1; for (int s = xOffset; s < numSamples; s += xSkip) { byte val = scale0[samples[s]]; tmp = (tmp << bitDepth) | val; if ((pos++ & mask) == mask) { currRow[count++] = (byte)tmp; tmp = 0; pos = 0; } } // Left shift the last byte if ((pos & mask) != 0) { tmp <<= ((8/bitDepth) - pos)*bitDepth; currRow[count++] = (byte)tmp; } break; case 8: if (numBands == 1) { for (int s = xOffset; s < numSamples; s += xSkip) { currRow[count++] = scale0[samples[s]]; if (icm_gray_alpha != null) { currRow[count++] = scale0[icm_gray_alpha.getAlpha(0xff & samples[s])]; } } } else { for (int s = xOffset; s < numSamples; s += xSkip) { for (int b = 0; b < numBands; b++) { currRow[count++] = scale[b][samples[s + b]]; } } } break; case 16: for (int s = xOffset; s < numSamples; s += xSkip) { for (int b = 0; b < numBands; b++) { currRow[count++] = scaleh[b][samples[s + b]]; currRow[count++] = scalel[b][samples[s + b]]; } } break; } // Perform filtering int filterType = rowFilter.filterRow(metadata.IHDR_colorType, currRow, prevRow, filteredRows, bytesPerRow, bpp); os.write(filterType); os.write(filteredRows[filterType], bpp, bytesPerRow); // Swap current and previous rows byte[] swap = currRow; currRow = prevRow; prevRow = swap; pixelsDone += hpixels; processImageProgress(100.0F*pixelsDone/totalPixels); // If write has been aborted, just return; // processWriteAborted will be called later if (abortRequested()) { return; } } } // Use sourceXOffset, etc. private void write_IDAT(RenderedImage image) throws IOException { IDATOutputStream ios = new IDATOutputStream(stream, 32768); if (metadata.IHDR_interlaceMethod == 1) { for (int i = 0; i < 7; i++) { encodePass(ios, image, PNGImageReader.adam7XOffset[i], PNGImageReader.adam7YOffset[i], PNGImageReader.adam7XSubsampling[i], PNGImageReader.adam7YSubsampling[i]); if (abortRequested()) { break; } } } else { encodePass(ios, image, 0, 0, 1, 1); } ios.finish(); } private void writeIEND() throws IOException { ChunkStream cs = new ChunkStream(PNGImageReader.IEND_TYPE, stream); cs.finish(); } // Check two int arrays for value equality, always returns false // if either array is null private boolean equals(int[] s0, int[] s1) { if (s0 == null || s1 == null) { return false; } if (s0.length != s1.length) { return false; } for (int i = 0; i < s0.length; i++) { if (s0[i] != s1[i]) { return false; } } return true; } // Initialize the scale/scale0 or scaleh/scalel arrays to // hold the results of scaling an input value to the desired // output bit depth private void initializeScaleTables(int[] sampleSize) { int bitDepth = metadata.IHDR_bitDepth; // If the existing tables are still valid, just return if (bitDepth == scalingBitDepth && equals(sampleSize, this.sampleSize)) { return; } // Compute new tables this.sampleSize = sampleSize; this.scalingBitDepth = bitDepth; int maxOutSample = (1 << bitDepth) - 1; if (bitDepth <= 8) { scale = new byte[numBands][]; for (int b = 0; b < numBands; b++) { int maxInSample = (1 << sampleSize[b]) - 1; int halfMaxInSample = maxInSample/2; scale[b] = new byte[maxInSample + 1]; for (int s = 0; s <= maxInSample; s++) { scale[b][s] = (byte)((s*maxOutSample + halfMaxInSample)/maxInSample); } } scale0 = scale[0]; scaleh = scalel = null; } else { // bitDepth == 16 // Divide scaling table into high and low bytes scaleh = new byte[numBands][]; scalel = new byte[numBands][]; for (int b = 0; b < numBands; b++) { int maxInSample = (1 << sampleSize[b]) - 1; int halfMaxInSample = maxInSample/2; scaleh[b] = new byte[maxInSample + 1]; scalel[b] = new byte[maxInSample + 1]; for (int s = 0; s <= maxInSample; s++) { int val = (s*maxOutSample + halfMaxInSample)/maxInSample; scaleh[b][s] = (byte)(val >> 8); scalel[b][s] = (byte)(val & 0xff); } } scale = null; scale0 = null; } } public void write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) throws IIOException { if (stream == null) { throw new IllegalStateException("output == null!"); } if (image == null) { throw new IllegalArgumentException("image == null!"); } if (image.hasRaster()) { throw new UnsupportedOperationException("image has a Raster!"); } RenderedImage im = image.getRenderedImage(); SampleModel sampleModel = im.getSampleModel(); this.numBands = sampleModel.getNumBands(); // Set source region and subsampling to default values this.sourceXOffset = im.getMinX(); this.sourceYOffset = im.getMinY(); this.sourceWidth = im.getWidth(); this.sourceHeight = im.getHeight(); this.sourceBands = null; this.periodX = 1; this.periodY = 1; if (param != null) { // Get source region and subsampling factors Rectangle sourceRegion = param.getSourceRegion(); if (sourceRegion != null) { Rectangle imageBounds = new Rectangle(im.getMinX(), im.getMinY(), im.getWidth(), im.getHeight()); // Clip to actual image bounds sourceRegion = sourceRegion.intersection(imageBounds); sourceXOffset = sourceRegion.x; sourceYOffset = sourceRegion.y; sourceWidth = sourceRegion.width; sourceHeight = sourceRegion.height; } // Adjust for subsampling offsets int gridX = param.getSubsamplingXOffset(); int gridY = param.getSubsamplingYOffset(); sourceXOffset += gridX; sourceYOffset += gridY; sourceWidth -= gridX; sourceHeight -= gridY; // Get subsampling factors periodX = param.getSourceXSubsampling(); periodY = param.getSourceYSubsampling(); int[] sBands = param.getSourceBands(); if (sBands != null) { sourceBands = sBands; numBands = sourceBands.length; } } // Compute output dimensions int destWidth = (sourceWidth + periodX - 1)/periodX; int destHeight = (sourceHeight + periodY - 1)/periodY; if (destWidth <= 0 || destHeight <= 0) { throw new IllegalArgumentException("Empty source region!"); } // Compute total number of pixels for progress notification this.totalPixels = destWidth*destHeight; this.pixelsDone = 0; // Create metadata IIOMetadata imd = image.getMetadata(); if (imd != null) { metadata = (PNGMetadata)convertImageMetadata(imd, ImageTypeSpecifier.createFromRenderedImage(im), null); } else { metadata = new PNGMetadata(); } if (param != null) { // Use Adam7 interlacing if set in write param switch (param.getProgressiveMode()) { case ImageWriteParam.MODE_DEFAULT: metadata.IHDR_interlaceMethod = 1; break; case ImageWriteParam.MODE_DISABLED: metadata.IHDR_interlaceMethod = 0; break; // MODE_COPY_FROM_METADATA should alreay be taken care of // MODE_EXPLICIT is not allowed } } // Initialize bitDepth and colorType metadata.initialize(new ImageTypeSpecifier(im), numBands); // Overwrite IHDR width and height values with values from image metadata.IHDR_width = destWidth; metadata.IHDR_height = destHeight; this.bpp = numBands*((metadata.IHDR_bitDepth == 16) ? 2 : 1); // Initialize scaling tables for this image initializeScaleTables(sampleModel.getSampleSize()); clearAbortRequest(); processImageStarted(0); try { write_magic(); write_IHDR(); write_cHRM(); write_gAMA(); write_iCCP(); write_sBIT(); write_sRGB(); write_PLTE(); write_hIST(); write_tRNS(); write_bKGD(); write_pHYs(); write_sPLT(); write_tIME(); write_tEXt(); write_iTXt(); write_zTXt(); writeUnknownChunks(); write_IDAT(im); if (abortRequested()) { processWriteAborted(); } else { // Finish up and inform the listeners we are done writeIEND(); processImageComplete(); } } catch (IOException e) { throw new IIOException("I/O error writing PNG file!", e); } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -