Java Image I/O API 가이드:4 - Image I/O 플러그 인의 작성 - xrath.com 에서 번역됨

목차 | 전의 항목 | 다음의 항목 | JavaTM Image I/O API 가이드

## 4.4 기입 플러그 인의 작성

> MyFormatImageWriterSpi

> `MyFormatImageWriterSpi` 클래스는, 전의 섹션으로 설명한 `MyFormatImageReaderSpi` 클래스와 같은 역할을 완수합니다. 다만, 특정의 스트림을 읽어들일 수 있을지 어떨지를 판별하는 것이 아니라, 메모리내의 이미지를 기입할 수 있을지 어떨지를 판별하지 않으면 안됩니다. 또, 실제의 이미지를 입수하기 전에 기입해 플러그 인을 선택할 수 있도록(듯이) 하기 위한(해), 이미지 그 자체를 조사하는 대신에,`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;
>     		}
>     	}

> `getDefaultWriteParam` 로부터 반환되는 `ImageWriteParam` 는, 기입 플러그 인의 기능에 근거해 커스터마이즈 하지 않으면 안됩니다. 이 기입 플러그 인은, 타일링, 진보론자 방식의 인코딩, 및 압축을 지원하고 있지 않기 때문에, 각각 `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` 를 작성하는 것으로 처리할 수 있습니다. 간단하게 하기 위한(해), 소스 이미지로부터 1 개의 `Raster` 를 추출합니다. 소스 이미지가 타일링 되고 있는 경우는, 필요에 따라서 작은 `Raster` 를 추출하는 것으로 메모리를 절약할 수 있습니다.

>     	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` 인수로부터 이메이지메타데이타를 추출해,`convertImageMetadata` 를 호출해 메타데이타를 `MyFormatMetadata` 객체로 변환해 보겠습니다. 그 결과가 `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` 메소드를 사용해 한 번에 1 행씩 int 배열에 카피합니다. 그 후, 그러한 값을 수평 서브 샘플링 계수를 사용해 서브 샘플링 해, 그 결과를 byte 배열에 카피해, 그 배열을 1 회의 write 호출로 출력에 기입합니다. 그리고, 소스행을 수직 서브 샘플링 계수의 분만큼 인크리먼트(increment) 해, 소스 영역의 끝까지 반복합니다. 마지막으로, 출력 스트림을 플래시 합니다.

>     			// 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);
>     		}
>     	}
>     }

목차 | 전의 항목 | 다음의 항목

Copyright © 2001 Sun Microsystems, Inc. All Rights Reserved.