Spec-Zone .ru
спецификации, руководства, описания, API
|
СОДЕРЖАНИЕ | ПРЕДЫДУЩИЙ | NEXT |
MyFormatImageWriterSpi
вызов играет подобную роль к MyFormatImageReaderSpi
класс обсуждается в предыдущем разделе. Однако, вместо того, чтобы быть ответственным за определение, может ли данный поток быть считан, он должен deterine, может ли изображение в памяти быть записано. Вместо того, чтобы осматривать изображение непосредственно, ImageTypeSpecifier
используется так, чтобы писатели могли быть выбраны прежде, чем фактическое изображение доступно. package com.mycompany.imageio; import java.io.IOException; import java.util.Locale; import javax.imageio.ImageWriter; import javax.imageio.ImageTypeSpecifier; import javax.imageio.spi.ImageWriterSpi; import javax.imageio.stream.ImageInputStream; public class MyFormatImageWriterSpi extends ImageWriterSpi { static final String vendorName = "My Company"; static final String version = "1.0_beta33_build9467"; static final String writerClassName = "com.mycompany.imageio.MyFormatImageWriter"; static final String[] names = { "myformat" }; static final String[] suffixes = { "myf" }; static final String[] MIMETypes = { "image/x-myformat" }; static final String[] readerSpiNames = { "com.mycompany.imageio.MyFormatImageReaderSpi" }; static final boolean supportsStandardStreamMetadataFormat = false; static final String nativeStreamMetadataFormatName = null; static final String nativeStreamMetadataFormatClassName = null; static final String[] extraStreamMetadataFormatNames = null; static final String[] extraStreamMetadataFormatClassNames = null; static final boolean supportsStandardImageMetadataFormat = false; static final String nativeImageMetadataFormatName = "com.mycompany.imageio.MyFormatMetadata_1.0"; static final String nativeImageMetadataFormatClassName = "com.mycompany.imageio.MyFormatMetadata"; static final String[] extraImageMetadataFormatNames = null; static final String[] extraImageMetadataFormatClassNames = null; public MyFormatImageWriterSpi() { super(vendorName, version, names, suffixes, MIMETypes, writerClassName, STANDARD_OUTPUT_TYPE, // Write to ImageOutputStreams readerSpiNames, supportsStandardStreamMetadataFormat, nativeStreamMetadataFormatName, nativeStreamMetadataFormatClassName, extraStreamMetadataFormatNames, extraStreamMetadataFormatClassNames, supportsStandardImageMetadataFormat, nativeImageMetadataFormatName, nativeImageMetadataFormatClassName, extraImageMetadataFormatNames, extraImageMetadataFormatClassNames); } public boolean canEncodeImage(ImageTypeSpecifier imageType) { int bands = imageType.getNumBands(); return bands == 1 || bands == 3; } public String getDescription(Locale locale) { // Localize as appropriate return "Description goes here"; } public ImageWriter createWriterInstance(Object extension) { return new MyFormatImageWriter(this); } }MyFormatImageWriter
package com.mycompany.imageio; import java.awt.Rectangle; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.io.IOException; import java.util.Iterator; import javax.imageio.IIOException; import javax.imageio.IIOImage; import javax.imageio.ImageTypeSpecifier; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.metadata.IIOMetadata; import javax.imageio.spi.ImageWriterSpi; import javax.imageio.stream.ImageOutputStream; public class MyFormatImageWriter extends ImageWriter { ImageOutputStream stream = null; public MyFormatImageWriter(ImageWriterSpi originatingProvider) { super(originatingProvider); } public void setOutput(Object output) { super.setOutput(output); if (output != null) { if (!(output instanceof ImageOutputStream)) { throw new IllegalArgumentException ("output not an ImageOutputStream!"); } this.stream = (ImageOutputStream)output; } else { this.stream = null; } }
ImageWriteParam
возвращенный getDefaultWriteParam
должен быть настроен основанный на возможностях писателя. Так как этот писатель не поддерживает мозаичное размещение, progessive кодирование, или сжатие, мы передаем в значениях false
или null
как соответствующий: // Tiling, progressive encoding, compression are disabled by default public ImageWriteParam getDefaultWriteParam() { return new ImageWriteParam(getLocale()); }Формат только обрабатывает метаданные изображения. convertImageMetadata метод делает очень немного; это могло быть определено, чтобы интерпретировать классы метаданных, используемые другими плагинами.
public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) { return null; } public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageType, ImageWriteParam param) { return new MyFormatMetadata(); } public IIOMetadata convertStreamMetadata(IIOMetadata inData, ImageWriteParam param) { return null; } public IIOMetadata convertImageMetadata(IIOMetadata inData, ImageTypeSpecifier imageType, ImageWriteParam param) { // We only understand our own metadata if (inData instanceof MyFormatMetadata) { return inData; } else { return null; } }Фактическая запись изображения требует сначала применения исходной области, исходных полос, и факторов подвыборки от
ImageWriteParam
. Исходная область и исходные полосы могут быть обработаны, создавая дочерний элемент Raster
. Для простоты мы извлекаем сингл Raster
из исходного изображения. Если исходное изображение размещается рядом, мы можем сохранить память, извлекая меньший Raster
s как необходимый. public void write(IIOMetadata streamMetadata, IIOImage image, ImageWriteParam param) throws IIOException { RenderedImage im = image.getRenderedImage(); Rectangle sourceRegion = new Rectangle(0, 0, im.getWidth(), im.getHeight()); int sourceXSubsampling = 1; int sourceYSubsampling = 1; int[] sourceBands = null; if (param != null) { sourceRegion = sourceRegion.intersection(param.getSourceRegion()); sourceXSubsampling = param.getSourceXSubsampling(); sourceYSubsampling = param.getSourceYSubsampling(); sourceBands = param.getSourceBands(); int subsampleXOffset = param.getSubsamplingXOffset(); int subsampleYOffset = param.getSubsamplingYOffset(); sourceRegion.x += subsampleXOffset; sourceRegion.y += subsampleYOffset; sourceRegion.width -= subsampleXOffset; sourceRegion.height -= subsampleYOffset; } // Grab a Raster containing the region of interest int width = sourceRegion.width; int height = sourceRegion.height; Raster imRas = im.getData(sourceRegion); int numBands = imRas.getNumBands(); // Check that sourceBands values are in range if (sourceBands != null) { for (int i = 0; i < sourceBands.length; i++) { if (sourceBands[i] >= numBands) { throw new IllegalArgumentException("bad band!"); } } } // Translate imRas to start at (0, 0) and subset the bands imRas = imRas.createChild(sourceRegion.x, sourceRegion.y, width, height, 0, 0, sourceBands); // Reduce width and height according to subsampling factors width = (width + sourceXSubsampling - 1)/sourceXSubsampling; height = (height + sourceYSubsampling - 1)/sourceYSubsampling; // Assume 1 band image is grayscale, 3 band image is RGB int colorType; if (numBands == 1) { colorType = MyFormatImageReader.COLOR_TYPE_GRAY; } else if (numBands == 3) { colorType = MyFormatImageReader.COLOR_TYPE_RGB; } else { throw new IIOException("Image must have 1 or 3 bands!"); }Как только размерности изображения и цветной тип изображения были установлены, плагин готов записать заголовок файла:
try { byte[] signature = { (byte)'m', (byte)'y', (byte)'f', (byte)'o', (byte)'r', (byte)'m', (byte)'a', (byte)'t' }; // Output header information stream.write(signature); stream.write(`\n'); stream.writeInt(width); stream.writeInt(height); stream.writeByte(colorType); stream.write(`\n');Затем, плагин извлекает метаданные изображения из
write
метод IIOImage
параметр, и попытки преобразовать это в a MyFormatMetadata
объект, вызывая convertImageMetadata
. Если результат не -null
, ключевые слова и значения извлекаются из метаданных и пишутся выводу: // Attempt to convert metadata, if present IIOMetadata imd = image.getMetadata(); MyFormatMetadata metadata = null; if (imd != null) { ImageTypeSpecifier type = ImageTypeSpecifier.createFromRenderedImage(im); metadata = (MyFormatMetadata)convertImageMetadata(imd, type, null); } // Output metadata if present if (metadata != null) { Iterator keywordIter = metadata.keywords.iterator(); Iterator valueIter = metadata.values.iterator(); while (keywordIter.hasNext()) { String keyword = (String)keywordIter.next(); String value = (String)valueIter.next(); stream.writeUTF(keyword); stream.write(`\n'); stream.writeUTF(value); stream.write(`\n'); } } stream.writeUTF("END"); stream.write(`\n');Наконец, плагин готов начать писать пиксельные данные. Изображение
Raster
копируется в международный массив, одну строку во время, используя getPixels
метод. Затем эти значения подвыбираются, используя горизонтальный фактор подвыборки, и копируются в байтовый массив, который пишется выводу с единственным вызовом записи. Исходная строка тогда постепенно увеличивается вертикальным фактором подвыборки, пока конец исходной области не достигается, и поток вывода сбрасывается: // Output (subsampled) pixel values int rowLength = width*numBands; int xSkip = sourceXSubsampling*numBands; int[] rowPixels = imRas.getPixels(0, 0, width, 1, (int[])null); byte[] rowSamples = new byte[rowLength]; // Output every (sourceYSubsampling)^th row for (int y = 0; y < height; y += sourceYSubsampling) { imRas.getPixels(0, y, width, 1, rowPixels); // Subsample horizontally and convert to bytes int count = 0; for (int x = 0; x < width; x += xSkip) { if (colorType == MyFormatImageReader.COLOR_TYPE_GRAY) { rowSamples[count++] = (byte)rowPixels[x]; } else { rowSamples[count++] = (byte)rowPixels[x]; rowSamples[count++] = (byte)rowPixels[x + 1]; rowSamples[count++] = (byte)rowPixels[x + 2]; } } // Output a row's worth of bytes stream.write(rowSamples, 0, width*numBands); } stream.flush(); } catch (IOException e) { throw new IIOException("I/O error!", e); } } }