001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Rustem V. Rafikov
019: * @version $Revision: 1.3 $
020: */package org.apache.harmony.x.imageio.plugins.jpeg;
021:
022: import javax.imageio.ImageWriter;
023: import javax.imageio.IIOImage;
024: import javax.imageio.ImageTypeSpecifier;
025: import javax.imageio.ImageWriteParam;
026: import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
027: import javax.imageio.stream.ImageOutputStream;
028: import javax.imageio.spi.ImageWriterSpi;
029: import javax.imageio.metadata.IIOMetadata;
030:
031: import org.apache.harmony.luni.util.NotImplementedException;
032:
033: import java.io.IOException;
034: import java.awt.image.*;
035: import java.awt.*;
036: import java.awt.color.ColorSpace;
037:
038: /**
039: * @author Rustem V. Rafikov
040: * @version $Revision: 1.3 $
041: */
042: public class JPEGImageWriter extends ImageWriter {
043:
044: private long cinfo;
045: private Raster sourceRaster;
046: private WritableRaster scanRaster;
047: private int srcXOff = 0;
048: private int srcYOff = 0;
049: private int srcWidth;
050: private int srcHeight;
051:
052: //-- y step for image subsampling
053: private int deltaY = 1;
054: //-- x step for image subsampling
055: private int deltaX = 1;
056:
057: private ImageOutputStream ios;
058:
059: public JPEGImageWriter(ImageWriterSpi imageWriterSpi) {
060: super (imageWriterSpi);
061: cinfo = initCompressionObj();
062: }
063:
064: static {
065: System.loadLibrary("jpegencoder");
066: initWriterIds(ImageOutputStream.class);
067: }
068:
069: @Override
070: public void write(IIOMetadata iioMetadata, IIOImage iioImage,
071: ImageWriteParam param) throws IOException {
072:
073: if (ios == null) {
074: throw new IllegalArgumentException("ios == null");
075: }
076:
077: RenderedImage img = null;
078: if (!iioImage.hasRaster()) {
079: img = iioImage.getRenderedImage();
080: if (img instanceof BufferedImage) {
081: sourceRaster = ((BufferedImage) img).getRaster();
082: } else {
083: sourceRaster = img.getData();
084: }
085: } else {
086: sourceRaster = iioImage.getRaster();
087: }
088:
089: int numBands = sourceRaster.getNumBands();
090: int sourceIJGCs = img == null ? JPEGConsts.JCS_UNKNOW
091: : getSourceCSType(img);
092:
093: srcWidth = sourceRaster.getWidth();
094: srcHeight = sourceRaster.getHeight();
095:
096: int destWidth = srcWidth;
097: int destHeight = srcHeight;
098:
099: boolean progressive = false;
100:
101: if (param != null) {
102: Rectangle reg = param.getSourceRegion();
103: if (reg != null) {
104: srcXOff = reg.x;
105: srcYOff = reg.y;
106:
107: srcWidth = reg.width + srcXOff > srcWidth ? srcWidth
108: - srcXOff : reg.width;
109: srcHeight = reg.height + srcYOff > srcHeight ? srcHeight
110: - srcYOff
111: : reg.height;
112: }
113:
114: //-- TODO uncomment when JPEGImageWriteParam be implemented
115: //-- Only default progressive mode yet
116: // progressive = param.getProgressiveMode() == ImageWriteParam.MODE_DEFAULT;
117:
118: //-- def is 1
119: deltaX = param.getSourceXSubsampling();
120: deltaY = param.getSourceYSubsampling();
121:
122: //-- def is 0
123: int offsetX = param.getSubsamplingXOffset();
124: int offsetY = param.getSubsamplingYOffset();
125:
126: srcXOff += offsetX;
127: srcYOff += offsetY;
128: srcWidth -= offsetX;
129: srcHeight -= offsetY;
130:
131: destWidth = (srcWidth + deltaX - 1) / deltaX;
132: destHeight = (srcHeight + deltaY - 1) / deltaY;
133: }
134:
135: //-- default DQTs (see JPEGQTable java doc and JPEG spec K1 & K2 tables)
136: //-- at http://www.w3.org/Graphics/JPEG/itu-t81.pdf
137: //-- Only figuring out how to set DQT in IJG library for future metadata
138: //-- support. IJG def tables are the same.
139: //JPEGQTable[] dqt = new JPEGQTable[2];
140: // int[][] dqt = null;
141: // int[][] dqt = new int[2][];
142: // dqt[0] = JPEGQTable.K1Div2Luminance.getTable();
143: // dqt[1] = JPEGQTable.K2Div2Chrominance.getTable();
144:
145: //-- using default color space
146: //-- TODO: Take destination cs from param or use default if there is no cs
147: int destIJGCs = img == null ? JPEGConsts.JCS_UNKNOW
148: : getDestinationCSType(img);
149:
150: DataBufferByte dbuffer = new DataBufferByte(numBands * srcWidth);
151:
152: scanRaster = Raster.createInterleavedRaster(dbuffer, srcWidth,
153: 1, numBands * srcWidth, numBands,
154: JPEGConsts.BAND_OFFSETS[numBands], null);
155:
156: encode(dbuffer.getData(), srcWidth, destWidth, destHeight,
157: deltaX, sourceIJGCs, destIJGCs, numBands, progressive,
158: null, cinfo);
159: }
160:
161: @Override
162: public void dispose() {
163: super .dispose();
164: if (cinfo != 0) {
165: dispose(cinfo);
166: cinfo = 0;
167: ios = null;
168: }
169: }
170:
171: public IIOMetadata getDefaultStreamMetadata(
172: ImageWriteParam imageWriteParam)
173: throws NotImplementedException {
174: // TODO: implement
175: throw new NotImplementedException();
176: }
177:
178: public IIOMetadata getDefaultImageMetadata(
179: ImageTypeSpecifier imageTypeSpecifier,
180: ImageWriteParam imageWriteParam)
181: throws NotImplementedException {
182: // TODO: implement
183: throw new NotImplementedException();
184: }
185:
186: @Override
187: public IIOMetadata convertStreamMetadata(IIOMetadata iioMetadata,
188: ImageWriteParam imageWriteParam)
189: throws NotImplementedException {
190: // TODO: implement
191: throw new NotImplementedException();
192: }
193:
194: @Override
195: public IIOMetadata convertImageMetadata(IIOMetadata iioMetadata,
196: ImageTypeSpecifier imageTypeSpecifier,
197: ImageWriteParam imageWriteParam)
198: throws NotImplementedException {
199: // TODO: implement
200: throw new NotImplementedException();
201: }
202:
203: @Override
204: public void setOutput(Object output) {
205: super .setOutput(output);
206: ios = (ImageOutputStream) output;
207: setIOS(ios, cinfo);
208: sourceRaster = null;
209: scanRaster = null;
210: srcXOff = 0;
211: srcYOff = 0;
212: srcWidth = 0;
213: srcHeight = 0;
214: deltaY = 1;
215: }
216:
217: /**
218: * Frees resources
219: * @param structPointer
220: */
221: private native void dispose(long structPointer);
222:
223: /**
224: * Inits methods Ids for native to java callbacks
225: * @param iosClass
226: */
227: private native static void initWriterIds(
228: Class<ImageOutputStream> iosClass);
229:
230: /**
231: * Inits compression objects
232: * @return pointer to the native structure
233: */
234: private native long initCompressionObj();
235:
236: /**
237: * Sets image output stream in IJG layer
238: * @param stream
239: */
240: private native void setIOS(ImageOutputStream stream,
241: long structPointer);
242:
243: /**
244: * Runs encoding process.
245: *
246: * @param data image data buffer to encode
247: * @param srcWidth - source width
248: * @param width - destination width
249: * @param height destination height
250: * @param deltaX - x subsampling step
251: * @param inColorSpace - original color space
252: * @param outColorSpace - destination color space
253: * @param numBands - number of bands
254: * @param cinfo - native handler
255: * @return
256: */
257: private native boolean encode(byte[] data, int srcWidth, int width,
258: int height, int deltaX, int inColorSpace,
259: int outColorSpace, int numBands, boolean progressive,
260: int[][] dqt, long cinfo);
261:
262: /**
263: * Callback for getting a next scanline
264: * @param scanline scan line number
265: */
266: @SuppressWarnings("unused")
267: private void getScanLine(int scanline) {
268: //-- TODO: processImageProgress in ImageWriter
269: Raster child = sourceRaster.createChild(srcXOff, srcYOff
270: + scanline * deltaY, srcWidth, 1, 0, 0, null);
271:
272: scanRaster.setRect(child);
273: }
274:
275: /**
276: * Maps color space types to IJG color spaces
277: * @param image
278: * @return
279: */
280: private int getSourceCSType(RenderedImage image) {
281: int type = JPEGConsts.JCS_UNKNOW;
282: ColorModel cm = image.getColorModel();
283:
284: if (null == cm) {
285: return type;
286: }
287:
288: if (cm instanceof IndexColorModel) {
289: // TODO: implement
290: throw new UnsupportedOperationException(
291: "IndexColorModel is not supported yet");
292: }
293:
294: boolean hasAlpha = cm.hasAlpha();
295: ColorSpace cs = cm.getColorSpace();
296: switch (cs.getType()) {
297: case ColorSpace.TYPE_GRAY:
298: type = JPEGConsts.JCS_GRAYSCALE;
299: break;
300: case ColorSpace.TYPE_RGB:
301: type = hasAlpha ? JPEGConsts.JCS_RGBA : JPEGConsts.JCS_RGB;
302: break;
303: case ColorSpace.TYPE_YCbCr:
304: type = hasAlpha ? JPEGConsts.JCS_YCbCrA
305: : JPEGConsts.JCS_YCbCr;
306: break;
307: case ColorSpace.TYPE_3CLR:
308: type = hasAlpha ? JPEGConsts.JCS_YCCA : JPEGConsts.JCS_YCC;
309: break;
310: case ColorSpace.TYPE_CMYK:
311: type = JPEGConsts.JCS_CMYK;
312: break;
313: }
314: return type;
315: }
316:
317: /**
318: * Returns destination color space.
319: * (YCbCr[A] for RGB)
320: *
321: * @param image
322: * @return
323: */
324: private int getDestinationCSType(RenderedImage image) {
325: int type = JPEGConsts.JCS_UNKNOW;
326: ColorModel cm = image.getColorModel();
327: if (null != cm) {
328: boolean hasAlpha = cm.hasAlpha();
329: ColorSpace cs = cm.getColorSpace();
330:
331: switch (cs.getType()) {
332: case ColorSpace.TYPE_GRAY:
333: type = JPEGConsts.JCS_GRAYSCALE;
334: break;
335: case ColorSpace.TYPE_RGB:
336: type = hasAlpha ? JPEGConsts.JCS_YCbCrA
337: : JPEGConsts.JCS_YCbCr;
338: break;
339: case ColorSpace.TYPE_YCbCr:
340: type = hasAlpha ? JPEGConsts.JCS_YCbCrA
341: : JPEGConsts.JCS_YCbCr;
342: break;
343: case ColorSpace.TYPE_3CLR:
344: type = hasAlpha ? JPEGConsts.JCS_YCCA
345: : JPEGConsts.JCS_YCC;
346: break;
347: case ColorSpace.TYPE_CMYK:
348: type = JPEGConsts.JCS_CMYK;
349: break;
350: }
351: }
352: return type;
353: }
354:
355: public ImageWriteParam getDefaultWriteParam() {
356: return new JPEGImageWriteParam(getLocale());
357: }
358: }
|