001/*
002 * $RCSfile: CLibImageWriter.java,v $
003 *
004 * 
005 * Copyright (c) 2005 Sun Microsystems, Inc. All  Rights Reserved.
006 * 
007 * Redistribution and use in source and binary forms, with or without
008 * modification, are permitted provided that the following conditions
009 * are met: 
010 * 
011 * - Redistribution of source code must retain the above copyright 
012 *   notice, this  list of conditions and the following disclaimer.
013 * 
014 * - Redistribution in binary form must reproduce the above copyright
015 *   notice, this list of conditions and the following disclaimer in 
016 *   the documentation and/or other materials provided with the
017 *   distribution.
018 * 
019 * Neither the name of Sun Microsystems, Inc. or the names of 
020 * contributors may be used to endorse or promote products derived 
021 * from this software without specific prior written permission.
022 * 
023 * This software is provided "AS IS," without a warranty of any 
024 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 
025 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 
026 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
027 * EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 
028 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 
029 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
030 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 
031 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
032 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
033 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
034 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
035 * POSSIBILITY OF SUCH DAMAGES. 
036 * 
037 * You acknowledge that this software is not designed or intended for 
038 * use in the design, construction, operation or maintenance of any 
039 * nuclear facility. 
040 *
041 * $Revision: 1.6 $
042 * $Date: 2007/02/06 22:14:59 $
043 * $State: Exp $
044 */
045package com.github.jaiimageio.impl.plugins.clib;
046
047import java.awt.Point;
048import java.awt.Rectangle;
049import java.awt.image.DataBuffer;
050import java.awt.image.DataBufferByte;
051import java.awt.image.DataBufferUShort;
052import java.awt.image.Raster;
053import java.awt.image.RenderedImage;
054import java.awt.image.SampleModel;
055import java.awt.image.WritableRaster;
056
057import javax.imageio.ImageTypeSpecifier;
058import javax.imageio.ImageWriteParam;
059import javax.imageio.ImageWriter;
060import javax.imageio.metadata.IIOMetadata;
061import javax.imageio.spi.ImageWriterSpi;
062
063public abstract class CLibImageWriter extends ImageWriter {
064    /**
065     * Returns the data array from the <code>DataBuffer</code>.
066     */
067    private static final Object getDataBufferData(DataBuffer db) {
068        Object data;
069
070        int dType = db.getDataType();
071        switch (dType) {
072        case DataBuffer.TYPE_BYTE:
073            data = ((DataBufferByte)db).getData();
074            break;
075        case DataBuffer.TYPE_USHORT:
076            data = ((DataBufferUShort)db).getData();
077            break;
078        default:
079            throw new IllegalArgumentException
080                (I18N.getString("Generic0")+" "+dType);
081        }
082
083        return data;
084    }
085
086    /**
087     * Returns a contiguous <code>Raster</code> of data over the specified
088     * <code>Rectangle</code>. If the region is a sub-region of a single
089     * tile, then a child of that tile will be returned. If the region
090     * overlaps more than one tile and has 8 bits per sample, then a
091     * pixel interleaved Raster having band offsets 0,1,... will be returned.
092     * Otherwise the Raster returned by <code>im.copyData(null)</code> will
093     * be returned.
094     */
095    private static final Raster getContiguousData(RenderedImage im,
096                                                  Rectangle region) {
097        if(im == null) {
098            throw new IllegalArgumentException("im == null");
099        } else if(region == null) {
100            throw new IllegalArgumentException("region == null");
101        }
102
103        Raster raster;
104        if(im.getNumXTiles() == 1 && im.getNumYTiles() == 1) {
105            // Image is not tiled so just get a reference to the tile.
106            raster = im.getTile(im.getMinTileX(), im.getMinTileY());
107
108            // Ensure result has requested coverage.
109            Rectangle bounds = raster.getBounds();
110            if (!bounds.equals(region)) {
111                raster = raster.createChild(region.x, region.y,
112                                            region.width, region.height,
113                                            region.x, region.y,
114                                            null);
115            }
116        } else {
117            // Image is tiled.
118
119            // Create an interleaved raster for copying for 8-bit case.
120            // This ensures that for RGB data the band offsets are {0,1,2}.
121            SampleModel sampleModel = im.getSampleModel();
122            WritableRaster target = sampleModel.getSampleSize(0) == 8 ?
123                Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
124                                               im.getWidth(),
125                                               im.getHeight(),
126                                               sampleModel.getNumBands(),
127                                               new Point(im.getMinX(),
128                                                         im.getMinY())) :
129                null;
130
131            // Copy the data.
132            raster = im.copyData(target);
133        }
134
135        return raster;
136    }
137
138    /**
139     * Subsamples and sub-bands the input <code>Raster</code> over a
140     * sub-region and stores the result in a <code>WritableRaster</code>.
141     *
142     * @param src The source <code>Raster</code>
143     * @param sourceBands The source bands to use; may be <code>null</code>
144     * @param subsampleX The subsampling factor along the horizontal axis.
145     * @param subsampleY The subsampling factor along the vertical axis.
146     * in which case all bands will be used.
147     * @param dst The destination <code>WritableRaster</code>.
148     * @throws IllegalArgumentException if <code>source</code> is
149     * <code>null</code> or empty, <code>dst</code> is <code>null</code>,
150     * <code>sourceBands.length</code> exceeds the number of bands in
151     * <code>source</code>, or <code>sourcBands</code> contains an element
152     * which is negative or greater than or equal to the number of bands
153     * in <code>source</code>.
154     */
155    private static void reformat(Raster source,
156                                 int[] sourceBands,
157                                 int subsampleX,
158                                 int subsampleY,
159                                 WritableRaster dst) {
160        // Check for nulls.
161        if(source == null) {
162            throw new IllegalArgumentException("source == null!");
163        } else if(dst == null) {
164            throw new IllegalArgumentException("dst == null!");
165        }
166
167        // Validate the source bounds. XXX is this needed?
168        Rectangle sourceBounds = source.getBounds();
169        if(sourceBounds.isEmpty()) {
170            throw new IllegalArgumentException
171                ("source.getBounds().isEmpty()!");
172        }
173
174        // Check sub-banding.
175        boolean isSubBanding = false;
176        int numSourceBands = source.getSampleModel().getNumBands();
177        if(sourceBands != null) {
178            if(sourceBands.length > numSourceBands) {
179                throw new IllegalArgumentException
180                    ("sourceBands.length > numSourceBands!");
181            }
182
183            boolean isRamp = sourceBands.length == numSourceBands;
184            for(int i = 0; i < sourceBands.length; i++) {
185                if(sourceBands[i] < 0 || sourceBands[i] >= numSourceBands) {
186                    throw new IllegalArgumentException
187                        ("sourceBands[i] < 0 || sourceBands[i] >= numSourceBands!");
188                } else if(sourceBands[i] != i) {
189                    isRamp = false;
190                }
191            }
192
193            isSubBanding = !isRamp;
194        }
195
196        // Allocate buffer for a single source row.
197        int sourceWidth = sourceBounds.width;
198        int[] pixels = new int[sourceWidth*numSourceBands];
199
200        // Initialize variables used in loop.
201        int sourceX = sourceBounds.x;
202        int sourceY = sourceBounds.y;
203        int numBands = sourceBands != null ?
204            sourceBands.length : numSourceBands;
205        int dstWidth = dst.getWidth();
206        int dstYMax = dst.getHeight() - 1;
207        int copyFromIncrement = numSourceBands*subsampleX;
208
209        // Loop over source rows, subsample each, and store in destination.
210        for(int dstY = 0; dstY <= dstYMax; dstY++) {
211            // Read one row.
212            source.getPixels(sourceX, sourceY, sourceWidth, 1, pixels);
213
214            // Copy within the same buffer by left shifting.
215            if(isSubBanding) {
216                int copyFrom = 0;
217                int copyTo = 0;
218                for(int i = 0; i < dstWidth; i++) {
219                    for(int j = 0; j < numBands; j++) {
220                        pixels[copyTo++] = pixels[copyFrom + sourceBands[j]];
221                    }
222                    copyFrom += copyFromIncrement;
223                }
224            } else {
225                int copyFrom = copyFromIncrement;
226                int copyTo = numSourceBands;
227                // Start from index 1 as no need to copy the first pixel.
228                for(int i = 1; i < dstWidth; i++) {
229                    int k = copyFrom;
230                    for(int j = 0; j < numSourceBands; j++) {
231                        pixels[copyTo++] = pixels[k++];
232                    }
233                    copyFrom += copyFromIncrement;
234                }
235            }
236
237            // Set the destionation row.
238            dst.setPixels(0, dstY, dstWidth, 1, pixels);
239
240            // Increment the source row.
241            sourceY += subsampleY;
242        }
243    }
244
245    protected CLibImageWriter(ImageWriterSpi originatingProvider) {
246        super(originatingProvider);
247    }
248
249    public IIOMetadata convertImageMetadata(IIOMetadata inData,
250                                            ImageTypeSpecifier imageType,
251                                            ImageWriteParam param) {
252        return null;
253    }
254
255    public IIOMetadata convertStreamMetadata(IIOMetadata inData,
256                                             ImageWriteParam param) {
257        return null;
258    }
259
260    public IIOMetadata
261        getDefaultImageMetadata(ImageTypeSpecifier imageType,
262                                ImageWriteParam param) {
263        return null;
264    }
265
266    public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) {
267        return null;
268    }
269
270    /* XXX
271    protected int getSignificantBits(RenderedImage image) {
272        SampleModel sampleModel = image.getSampleModel();
273        int numBands = sampleModel.getNumBands();
274        int[] sampleSize = sampleModel.getSampleSize();
275        int significantBits = sampleSize[0];
276        for(int i = 1; i < numBands; i++) {
277            significantBits = Math.max(significantBits, sampleSize[i]);
278        }
279
280        return significantBits;
281    }
282    */
283
284    // Code copied from ImageReader.java with ImageReadParam replaced
285    // by ImageWriteParam.
286    private static final Rectangle getSourceRegion(ImageWriteParam param,
287                                                   int sourceMinX,
288                                                   int sourceMinY,
289                                                   int srcWidth,
290                                                   int srcHeight) {
291        Rectangle sourceRegion =
292            new Rectangle(sourceMinX, sourceMinY, srcWidth, srcHeight);
293        if (param != null) {
294            Rectangle region = param.getSourceRegion();
295            if (region != null) {
296                sourceRegion = sourceRegion.intersection(region);
297            }
298
299            int subsampleXOffset = param.getSubsamplingXOffset();
300            int subsampleYOffset = param.getSubsamplingYOffset();
301            sourceRegion.x += subsampleXOffset;
302            sourceRegion.y += subsampleYOffset;
303            sourceRegion.width -= subsampleXOffset;
304            sourceRegion.height -= subsampleYOffset;
305        }
306
307        return sourceRegion;
308    }
309}