Home | History | Annotate | Download | only in jpeg
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 /**
     18  * @author Rustem V. Rafikov
     19  * @version $Revision: 1.3 $
     20  */
     21 package org.apache.harmony.x.imageio.plugins.jpeg;
     22 
     23 import com.android.internal.awt.ImageOutputStreamWrapper;
     24 
     25 import javax.imageio.ImageWriter;
     26 import javax.imageio.IIOImage;
     27 import javax.imageio.ImageTypeSpecifier;
     28 import javax.imageio.ImageWriteParam;
     29 import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
     30 import javax.imageio.stream.ImageOutputStream;
     31 import javax.imageio.spi.ImageWriterSpi;
     32 import javax.imageio.metadata.IIOMetadata;
     33 
     34 import android.graphics.Bitmap;
     35 import android.graphics.BitmapFactory;
     36 import android.graphics.Bitmap.CompressFormat;
     37 
     38 import java.io.File;
     39 import java.io.FileOutputStream;
     40 import java.io.IOException;
     41 import java.awt.image.*;
     42 import java.awt.*;
     43 import java.awt.color.ColorSpace;
     44 
     45 /**
     46  * @author Rustem V. Rafikov
     47  * @version $Revision: 1.3 $
     48  */
     49 public class JPEGImageWriter extends ImageWriter {
     50 
     51     // /* ???AWT: Debugging
     52     private static final boolean DEBUG = false;
     53     private static Bitmap bm;
     54     public static Bitmap getBitmap() {
     55         return bm;
     56     }
     57     private static BufferedImage bufImg;
     58     public static BufferedImage getBufImage() {
     59         return bufImg;
     60     }
     61     static private RenderedImage renImg;
     62     static public RenderedImage getRenImage() {
     63         return renImg;
     64     }
     65     // */
     66 
     67     private long cinfo;
     68     private RenderedImage image;
     69     private Raster sourceRaster;
     70     private WritableRaster scanRaster;
     71     private int srcXOff = 0;
     72     private int srcYOff = 0;
     73     private int srcWidth;
     74     private int srcHeight;
     75 
     76     //-- y step for image subsampling
     77     private int deltaY = 1;
     78     //-- x step for image subsampling
     79     private int deltaX = 1;
     80 
     81     private ImageOutputStream ios;
     82 
     83     public JPEGImageWriter(ImageWriterSpi imageWriterSpi) {
     84         super(imageWriterSpi);
     85         //???AWT: cinfo = initCompressionObj();
     86         cinfo = System.currentTimeMillis();
     87     }
     88 
     89     static {
     90         //???AWT
     91         /*
     92         System.loadLibrary("jpegencoder");
     93         initWriterIds(ImageOutputStream.class);
     94         */
     95     }
     96 
     97     @Override
     98     public void write(IIOMetadata iioMetadata, IIOImage iioImage, ImageWriteParam param)
     99             throws IOException {
    100 
    101         if (ios == null) {
    102             throw new IllegalArgumentException("ios == null");
    103         }
    104         if (iioImage == null) {
    105             throw new IllegalArgumentException("Image equals null");
    106         }
    107 
    108         RenderedImage img = null;
    109         if (!iioImage.hasRaster()) {
    110             img = iioImage.getRenderedImage();
    111             if (img instanceof BufferedImage) {
    112                 sourceRaster = ((BufferedImage) img).getRaster();
    113             } else {
    114                 sourceRaster = img.getData();
    115             }
    116         } else {
    117             sourceRaster = iioImage.getRaster();
    118         }
    119 
    120         // AWT???: Debugging
    121         if (DEBUG) {
    122             if( img==null ) {
    123                 System.out.println("****J: Image is NULL");
    124             } else {
    125                 renImg = img;
    126                 bufImg = (BufferedImage)img;
    127             }
    128         }
    129 
    130         int numBands = sourceRaster.getNumBands();
    131         int sourceIJGCs = img == null ? JPEGConsts.JCS_UNKNOW : getSourceCSType(img);
    132 
    133         srcWidth = sourceRaster.getWidth();
    134         srcHeight = sourceRaster.getHeight();
    135 
    136         int destWidth = srcWidth;
    137         int destHeight = srcHeight;
    138 
    139         boolean progressive = false;
    140 
    141         if (param != null) {
    142             Rectangle reg = param.getSourceRegion();
    143             if (reg != null) {
    144                 srcXOff = reg.x;
    145                 srcYOff = reg.y;
    146 
    147                 srcWidth = reg.width + srcXOff > srcWidth
    148                         ? srcWidth - srcXOff
    149                         : reg.width;
    150                 srcHeight = reg.height + srcYOff > srcHeight
    151                         ? srcHeight - srcYOff
    152                         : reg.height;
    153             }
    154 
    155             //-- TODO uncomment when JPEGImageWriteParam be implemented
    156             //-- Only default progressive mode yet
    157             // progressive = param.getProgressiveMode() ==  ImageWriteParam.MODE_DEFAULT;
    158 
    159             //-- def is 1
    160             deltaX = param.getSourceXSubsampling();
    161             deltaY = param.getSourceYSubsampling();
    162 
    163             //-- def is 0
    164             int offsetX = param.getSubsamplingXOffset();
    165             int offsetY = param.getSubsamplingYOffset();
    166 
    167             srcXOff += offsetX;
    168             srcYOff += offsetY;
    169             srcWidth -= offsetX;
    170             srcHeight -= offsetY;
    171 
    172             destWidth = (srcWidth + deltaX - 1) / deltaX;
    173             destHeight = (srcHeight + deltaY - 1) / deltaY;
    174         }
    175 
    176         //-- default DQTs (see JPEGQTable java doc and JPEG spec K1 & K2 tables)
    177         //-- at http://www.w3.org/Graphics/JPEG/itu-t81.pdf
    178         //-- Only figuring out how to set DQT in IJG library for future metadata
    179         //-- support. IJG def tables are the same.
    180         //JPEGQTable[] dqt = new JPEGQTable[2];
    181 //        int[][] dqt = null;
    182 //        int[][] dqt = new int[2][];
    183 //        dqt[0] = JPEGQTable.K1Div2Luminance.getTable();
    184 //        dqt[1] = JPEGQTable.K2Div2Chrominance.getTable();
    185 
    186         //???AWT: I think we don't need this amymore
    187         /*
    188         //-- using default color space
    189         //-- TODO: Take destination cs from param or use default if there is no cs
    190         int destIJGCs = img == null ? JPEGConsts.JCS_UNKNOW : getDestinationCSType(img);
    191 
    192         DataBufferByte dbuffer = new DataBufferByte(numBands * srcWidth);
    193 
    194         scanRaster = Raster.createInterleavedRaster(dbuffer, srcWidth, 1,
    195                 numBands * srcWidth, numBands, JPEGConsts.BAND_OFFSETS[numBands], null);
    196 
    197         encode(dbuffer.getData(), srcWidth, destWidth, destHeight, deltaX,
    198                 sourceIJGCs, destIJGCs, numBands, progressive,
    199                 null, cinfo);
    200         */
    201 
    202         SampleModel model = sourceRaster.getSampleModel();
    203 
    204         if (model instanceof SinglePixelPackedSampleModel) {
    205             DataBufferInt ibuf = (DataBufferInt)sourceRaster.getDataBuffer();
    206             int[] pixels = ibuf.getData();
    207 
    208             // Create a bitmap with the pixel
    209             bm = Bitmap.createBitmap(pixels, srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
    210 
    211             // Use Bitmap.compress() to write the image
    212             ImageOutputStreamWrapper iosw = new ImageOutputStreamWrapper(ios);
    213             bm.compress(CompressFormat.JPEG, 100, iosw);
    214         } else {
    215             // ???AWT: Add support for other color models
    216             throw new RuntimeException("Color model not supported yet");
    217         }
    218 
    219     }
    220 
    221     @Override
    222     public void dispose() {
    223         super.dispose();
    224         if (cinfo != 0) {
    225             //???AWT: dispose(cinfo);
    226             cinfo = 0;
    227             ios = null;
    228         }
    229     }
    230 
    231 
    232     public IIOMetadata getDefaultStreamMetadata(ImageWriteParam imageWriteParam) {
    233         throw new UnsupportedOperationException("not supported yet");
    234     }
    235 
    236     public IIOMetadata getDefaultImageMetadata(ImageTypeSpecifier imageTypeSpecifier, ImageWriteParam imageWriteParam) {
    237         throw new UnsupportedOperationException("not supported yet");
    238     }
    239 
    240     @Override
    241     public IIOMetadata convertStreamMetadata(IIOMetadata iioMetadata, ImageWriteParam imageWriteParam) {
    242         throw new UnsupportedOperationException("not supported yet");
    243     }
    244 
    245     @Override
    246     public IIOMetadata convertImageMetadata(IIOMetadata iioMetadata, ImageTypeSpecifier imageTypeSpecifier, ImageWriteParam imageWriteParam) {
    247         throw new UnsupportedOperationException("not supported yet");
    248     }
    249 
    250     @Override
    251     public void setOutput(Object output) {
    252         super.setOutput(output);
    253         ios = (ImageOutputStream) output;
    254         //???AWT: setIOS(ios, cinfo);
    255         sourceRaster = null;
    256         scanRaster = null;
    257         srcXOff = 0;
    258         srcYOff = 0;
    259         srcWidth = 0;
    260         srcHeight = 0;
    261         deltaY = 1;
    262     }
    263 
    264     /**
    265      * Frees resources
    266      * @param structPointer
    267      */
    268     //???AWT: private native void dispose(long structPointer);
    269 
    270     /**
    271      * Inits methods Ids for native to java callbacks
    272      * @param iosClass
    273      */
    274     //???AWT: private native static void initWriterIds(Class<ImageOutputStream> iosClass);
    275 
    276     /**
    277      * Inits compression objects
    278      * @return pointer to the native structure
    279      */
    280     //???AWT: private native long initCompressionObj();
    281 
    282     /**
    283      * Sets image output stream in IJG layer
    284      * @param stream
    285      */
    286     //???AWT: private native void setIOS(ImageOutputStream stream, long structPointer);
    287 
    288     /**
    289      * Runs encoding process.
    290      *
    291      * @param data image data buffer to encode
    292      * @param srcWidth - source width
    293      * @param width - destination width
    294      * @param height destination height
    295      * @param deltaX - x subsampling step
    296      * @param inColorSpace - original color space
    297      * @param outColorSpace - destination color space
    298      * @param numBands - number of bands
    299      * @param cinfo - native handler
    300      * @return
    301      */
    302     //???AWT:
    303     /*
    304     private native boolean encode(byte[] data, int srcWidth,
    305                                   int width, int height, int deltaX,
    306                                   int inColorSpace, int outColorSpace,
    307                                   int numBands, boolean progressive,
    308                                   int[][] dqt,
    309                                   long cinfo);
    310     */
    311 
    312     /**
    313      * Callback for getting a next scanline
    314      * @param scanline scan line number
    315      */
    316     @SuppressWarnings("unused")
    317     private void getScanLine(int scanline) {
    318         //-- TODO: processImageProgress in ImageWriter
    319         Raster child = sourceRaster.createChild(srcXOff,
    320                 srcYOff + scanline * deltaY, srcWidth, 1, 0, 0, null);
    321 
    322         scanRaster.setRect(child);
    323     }
    324 
    325     /**
    326      * Maps color space types to IJG color spaces
    327      * @param image
    328      * @return
    329      */
    330     private int getSourceCSType(RenderedImage image) {
    331         int type = JPEGConsts.JCS_UNKNOW;
    332         ColorModel cm = image.getColorModel();
    333 
    334         if (null == cm) {
    335             return type;
    336         }
    337 
    338         if (cm instanceof IndexColorModel) {
    339             throw new UnsupportedOperationException("IndexColorModel is not supported yet");
    340         }
    341 
    342         boolean hasAlpha = cm.hasAlpha();
    343         ColorSpace cs = cm.getColorSpace();
    344         switch(cs.getType()) {
    345             case ColorSpace.TYPE_GRAY:
    346                 type = JPEGConsts.JCS_GRAYSCALE;
    347                 break;
    348            case ColorSpace.TYPE_RGB:
    349                 type = hasAlpha ? JPEGConsts.JCS_RGBA : JPEGConsts.JCS_RGB;
    350                 break;
    351            case ColorSpace.TYPE_YCbCr:
    352                 type = hasAlpha ? JPEGConsts.JCS_YCbCrA : JPEGConsts.JCS_YCbCr;
    353                 break;
    354            case ColorSpace.TYPE_3CLR:
    355                  type = hasAlpha ? JPEGConsts.JCS_YCCA : JPEGConsts.JCS_YCC;
    356                  break;
    357            case ColorSpace.TYPE_CMYK:
    358                   type = JPEGConsts.JCS_CMYK;
    359                   break;
    360         }
    361         return type;
    362     }
    363 
    364     /**
    365      * Returns destination color space.
    366      * (YCbCr[A] for RGB)
    367      *
    368      * @param image
    369      * @return
    370      */
    371     private int getDestinationCSType(RenderedImage image) {
    372         int type = JPEGConsts.JCS_UNKNOW;
    373         ColorModel cm = image.getColorModel();
    374         if (null != cm) {
    375             boolean hasAlpha = cm.hasAlpha();
    376             ColorSpace cs = cm.getColorSpace();
    377 
    378             switch(cs.getType()) {
    379                 case ColorSpace.TYPE_GRAY:
    380                     type = JPEGConsts.JCS_GRAYSCALE;
    381                     break;
    382                case ColorSpace.TYPE_RGB:
    383                     type = hasAlpha ? JPEGConsts.JCS_YCbCrA : JPEGConsts.JCS_YCbCr;
    384                     break;
    385                case ColorSpace.TYPE_YCbCr:
    386                     type = hasAlpha ? JPEGConsts.JCS_YCbCrA : JPEGConsts.JCS_YCbCr;
    387                     break;
    388                case ColorSpace.TYPE_3CLR:
    389                      type = hasAlpha ? JPEGConsts.JCS_YCCA : JPEGConsts.JCS_YCC;
    390                      break;
    391                case ColorSpace.TYPE_CMYK:
    392                       type = JPEGConsts.JCS_CMYK;
    393                       break;
    394             }
    395         }
    396         return type;
    397     }
    398 
    399     public ImageWriteParam getDefaultWriteParam() {
    400         return new JPEGImageWriteParam(getLocale());
    401     }
    402 }
    403