blob: ae3e87639dba641347d2394f4edb91674d9c1ebe [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Rustem V. Rafikov
* @version $Revision: 1.3 $
*/
package org.apache.harmony.x.imageio.plugins.jpeg;
import com.android.internal.awt.ImageOutputStreamWrapper;
import javax.imageio.ImageWriter;
import javax.imageio.IIOImage;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.spi.ImageWriterSpi;
import javax.imageio.metadata.IIOMetadata;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.CompressFormat;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.awt.image.*;
import java.awt.*;
import java.awt.color.ColorSpace;
/**
* @author Rustem V. Rafikov
* @version $Revision: 1.3 $
*/
public class JPEGImageWriter extends ImageWriter {
// /* ???AWT: Debugging
private static final boolean DEBUG = false;
private static Bitmap bm;
public static Bitmap getBitmap() {
return bm;
}
private static BufferedImage bufImg;
public static BufferedImage getBufImage() {
return bufImg;
}
static private RenderedImage renImg;
static public RenderedImage getRenImage() {
return renImg;
}
// */
private long cinfo;
private RenderedImage image;
private Raster sourceRaster;
private WritableRaster scanRaster;
private int srcXOff = 0;
private int srcYOff = 0;
private int srcWidth;
private int srcHeight;
//-- y step for image subsampling
private int deltaY = 1;
//-- x step for image subsampling
private int deltaX = 1;
private ImageOutputStream ios;
public JPEGImageWriter(ImageWriterSpi imageWriterSpi) {
super(imageWriterSpi);
//???AWT: cinfo = initCompressionObj();
cinfo = System.currentTimeMillis();
}
static {
//???AWT
/*
System.loadLibrary("jpegencoder");
initWriterIds(ImageOutputStream.class);
*/
}
@Override
public void write(IIOMetadata iioMetadata, IIOImage iioImage, ImageWriteParam param)
throws IOException {
if (ios == null) {
throw new IllegalArgumentException("ios == null");
}
if (iioImage == null) {
throw new IllegalArgumentException("Image equals null");
}
RenderedImage img = null;
if (!iioImage.hasRaster()) {
img = iioImage.getRenderedImage();
if (img instanceof BufferedImage) {
sourceRaster = ((BufferedImage) img).getRaster();
} else {
sourceRaster = img.getData();
}
} else {
sourceRaster = iioImage.getRaster();
}
// AWT???: Debugging
if (DEBUG) {
if( img==null ) {
System.out.println("****J: Image is NULL");
} else {
renImg = img;
bufImg = (BufferedImage)img;
}
}
int numBands = sourceRaster.getNumBands();
int sourceIJGCs = img == null ? JPEGConsts.JCS_UNKNOW : getSourceCSType(img);
srcWidth = sourceRaster.getWidth();
srcHeight = sourceRaster.getHeight();
int destWidth = srcWidth;
int destHeight = srcHeight;
boolean progressive = false;
if (param != null) {
Rectangle reg = param.getSourceRegion();
if (reg != null) {
srcXOff = reg.x;
srcYOff = reg.y;
srcWidth = reg.width + srcXOff > srcWidth
? srcWidth - srcXOff
: reg.width;
srcHeight = reg.height + srcYOff > srcHeight
? srcHeight - srcYOff
: reg.height;
}
//-- TODO uncomment when JPEGImageWriteParam be implemented
//-- Only default progressive mode yet
// progressive = param.getProgressiveMode() == ImageWriteParam.MODE_DEFAULT;
//-- def is 1
deltaX = param.getSourceXSubsampling();
deltaY = param.getSourceYSubsampling();
//-- def is 0
int offsetX = param.getSubsamplingXOffset();
int offsetY = param.getSubsamplingYOffset();
srcXOff += offsetX;
srcYOff += offsetY;
srcWidth -= offsetX;
srcHeight -= offsetY;
destWidth = (srcWidth + deltaX - 1) / deltaX;
destHeight = (srcHeight + deltaY - 1) / deltaY;
}
//-- default DQTs (see JPEGQTable java doc and JPEG spec K1 & K2 tables)
//-- at http://www.w3.org/Graphics/JPEG/itu-t81.pdf
//-- Only figuring out how to set DQT in IJG library for future metadata
//-- support. IJG def tables are the same.
//JPEGQTable[] dqt = new JPEGQTable[2];
// int[][] dqt = null;
// int[][] dqt = new int[2][];
// dqt[0] = JPEGQTable.K1Div2Luminance.getTable();
// dqt[1] = JPEGQTable.K2Div2Chrominance.getTable();
//???AWT: I think we don't need this amymore
/*
//-- using default color space
//-- TODO: Take destination cs from param or use default if there is no cs
int destIJGCs = img == null ? JPEGConsts.JCS_UNKNOW : getDestinationCSType(img);
DataBufferByte dbuffer = new DataBufferByte(numBands * srcWidth);
scanRaster = Raster.createInterleavedRaster(dbuffer, srcWidth, 1,
numBands * srcWidth, numBands, JPEGConsts.BAND_OFFSETS[numBands], null);
encode(dbuffer.getData(), srcWidth, destWidth, destHeight, deltaX,
sourceIJGCs, destIJGCs, numBands, progressive,
null, cinfo);
*/
SampleModel model = sourceRaster.getSampleModel();
if (model instanceof SinglePixelPackedSampleModel) {
DataBufferInt ibuf = (DataBufferInt)sourceRaster.getDataBuffer();
int[] pixels = ibuf.getData();
// Create a bitmap with the pixel
bm = Bitmap.createBitmap(pixels, srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
// Use Bitmap.compress() to write the image
ImageOutputStreamWrapper iosw = new ImageOutputStreamWrapper(ios);
bm.compress(CompressFormat.JPEG, 100, iosw);
} else {
// ???AWT: Add support for other color models
throw new RuntimeException("Color model not supported yet");
}
}
@Override
public void dispose() {
super.dispose();
if (cinfo != 0) {
//???AWT: dispose(cinfo);
cinfo = 0;
ios = null;
}
}
public IIOMetadata getDefaultStreamMetadata(ImageWriteParam imageWriteParam) {
throw new UnsupportedOperationException("not supported yet");
}
public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageTypeSpecifier, ImageWriteParam imageWriteParam) {
throw new UnsupportedOperationException("not supported yet");
}
@Override
public IIOMetadata convertStreamMetadata(IIOMetadata iioMetadata, ImageWriteParam imageWriteParam) {
throw new UnsupportedOperationException("not supported yet");
}
@Override
public IIOMetadata convertImageMetadata(IIOMetadata iioMetadata, ImageTypeSpecifier imageTypeSpecifier, ImageWriteParam imageWriteParam) {
throw new UnsupportedOperationException("not supported yet");
}
@Override
public void setOutput(Object output) {
super.setOutput(output);
ios = (ImageOutputStream) output;
//???AWT: setIOS(ios, cinfo);
sourceRaster = null;
scanRaster = null;
srcXOff = 0;
srcYOff = 0;
srcWidth = 0;
srcHeight = 0;
deltaY = 1;
}
/**
* Frees resources
* @param structPointer
*/
//???AWT: private native void dispose(long structPointer);
/**
* Inits methods Ids for native to java callbacks
* @param iosClass
*/
//???AWT: private native static void initWriterIds(Class<ImageOutputStream> iosClass);
/**
* Inits compression objects
* @return pointer to the native structure
*/
//???AWT: private native long initCompressionObj();
/**
* Sets image output stream in IJG layer
* @param stream
*/
//???AWT: private native void setIOS(ImageOutputStream stream, long structPointer);
/**
* Runs encoding process.
*
* @param data image data buffer to encode
* @param srcWidth - source width
* @param width - destination width
* @param height destination height
* @param deltaX - x subsampling step
* @param inColorSpace - original color space
* @param outColorSpace - destination color space
* @param numBands - number of bands
* @param cinfo - native handler
* @return
*/
//???AWT:
/*
private native boolean encode(byte[] data, int srcWidth,
int width, int height, int deltaX,
int inColorSpace, int outColorSpace,
int numBands, boolean progressive,
int[][] dqt,
long cinfo);
*/
/**
* Callback for getting a next scanline
* @param scanline scan line number
*/
@SuppressWarnings("unused")
private void getScanLine(int scanline) {
//-- TODO: processImageProgress in ImageWriter
Raster child = sourceRaster.createChild(srcXOff,
srcYOff + scanline * deltaY, srcWidth, 1, 0, 0, null);
scanRaster.setRect(child);
}
/**
* Maps color space types to IJG color spaces
* @param image
* @return
*/
private int getSourceCSType(RenderedImage image) {
int type = JPEGConsts.JCS_UNKNOW;
ColorModel cm = image.getColorModel();
if (null == cm) {
return type;
}
if (cm instanceof IndexColorModel) {
throw new UnsupportedOperationException("IndexColorModel is not supported yet");
}
boolean hasAlpha = cm.hasAlpha();
ColorSpace cs = cm.getColorSpace();
switch(cs.getType()) {
case ColorSpace.TYPE_GRAY:
type = JPEGConsts.JCS_GRAYSCALE;
break;
case ColorSpace.TYPE_RGB:
type = hasAlpha ? JPEGConsts.JCS_RGBA : JPEGConsts.JCS_RGB;
break;
case ColorSpace.TYPE_YCbCr:
type = hasAlpha ? JPEGConsts.JCS_YCbCrA : JPEGConsts.JCS_YCbCr;
break;
case ColorSpace.TYPE_3CLR:
type = hasAlpha ? JPEGConsts.JCS_YCCA : JPEGConsts.JCS_YCC;
break;
case ColorSpace.TYPE_CMYK:
type = JPEGConsts.JCS_CMYK;
break;
}
return type;
}
/**
* Returns destination color space.
* (YCbCr[A] for RGB)
*
* @param image
* @return
*/
private int getDestinationCSType(RenderedImage image) {
int type = JPEGConsts.JCS_UNKNOW;
ColorModel cm = image.getColorModel();
if (null != cm) {
boolean hasAlpha = cm.hasAlpha();
ColorSpace cs = cm.getColorSpace();
switch(cs.getType()) {
case ColorSpace.TYPE_GRAY:
type = JPEGConsts.JCS_GRAYSCALE;
break;
case ColorSpace.TYPE_RGB:
type = hasAlpha ? JPEGConsts.JCS_YCbCrA : JPEGConsts.JCS_YCbCr;
break;
case ColorSpace.TYPE_YCbCr:
type = hasAlpha ? JPEGConsts.JCS_YCbCrA : JPEGConsts.JCS_YCbCr;
break;
case ColorSpace.TYPE_3CLR:
type = hasAlpha ? JPEGConsts.JCS_YCCA : JPEGConsts.JCS_YCC;
break;
case ColorSpace.TYPE_CMYK:
type = JPEGConsts.JCS_CMYK;
break;
}
}
return type;
}
public ImageWriteParam getDefaultWriteParam() {
return new JPEGImageWriteParam(getLocale());
}
}