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.color.ColorSpace; 050import java.awt.geom.AffineTransform; 051import java.awt.image.AffineTransformOp; 052import java.awt.image.ColorModel; 053import java.awt.image.ComponentSampleModel; 054import java.awt.image.DataBuffer; 055import java.awt.image.DataBufferByte; 056import java.awt.image.DataBufferUShort; 057import java.awt.image.IndexColorModel; 058import java.awt.image.MultiPixelPackedSampleModel; 059import java.awt.image.PixelInterleavedSampleModel; 060import java.awt.image.Raster; 061import java.awt.image.RenderedImage; 062import java.awt.image.SampleModel; 063import java.awt.image.SinglePixelPackedSampleModel; 064import java.awt.image.WritableRaster; 065import java.io.IOException; 066import javax.imageio.IIOImage; 067import javax.imageio.ImageWriter; 068import javax.imageio.ImageWriteParam; 069import javax.imageio.ImageTypeSpecifier; 070import javax.imageio.metadata.IIOMetadata; 071import javax.imageio.spi.ImageWriterSpi; 072 073public abstract class CLibImageWriter extends ImageWriter { 074 /** 075 * Returns the data array from the <code>DataBuffer</code>. 076 */ 077 private static final Object getDataBufferData(DataBuffer db) { 078 Object data; 079 080 int dType = db.getDataType(); 081 switch (dType) { 082 case DataBuffer.TYPE_BYTE: 083 data = ((DataBufferByte)db).getData(); 084 break; 085 case DataBuffer.TYPE_USHORT: 086 data = ((DataBufferUShort)db).getData(); 087 break; 088 default: 089 throw new IllegalArgumentException 090 (I18N.getString("Generic0")+" "+dType); 091 } 092 093 return data; 094 } 095 096 /** 097 * Returns a contiguous <code>Raster</code> of data over the specified 098 * <code>Rectangle</code>. If the region is a sub-region of a single 099 * tile, then a child of that tile will be returned. If the region 100 * overlaps more than one tile and has 8 bits per sample, then a 101 * pixel interleaved Raster having band offsets 0,1,... will be returned. 102 * Otherwise the Raster returned by <code>im.copyData(null)</code> will 103 * be returned. 104 */ 105 private static final Raster getContiguousData(RenderedImage im, 106 Rectangle region) { 107 if(im == null) { 108 throw new IllegalArgumentException("im == null"); 109 } else if(region == null) { 110 throw new IllegalArgumentException("region == null"); 111 } 112 113 Raster raster; 114 if(im.getNumXTiles() == 1 && im.getNumYTiles() == 1) { 115 // Image is not tiled so just get a reference to the tile. 116 raster = im.getTile(im.getMinTileX(), im.getMinTileY()); 117 118 // Ensure result has requested coverage. 119 Rectangle bounds = raster.getBounds(); 120 if (!bounds.equals(region)) { 121 raster = raster.createChild(region.x, region.y, 122 region.width, region.height, 123 region.x, region.y, 124 null); 125 } 126 } else { 127 // Image is tiled. 128 129 // Create an interleaved raster for copying for 8-bit case. 130 // This ensures that for RGB data the band offsets are {0,1,2}. 131 SampleModel sampleModel = im.getSampleModel(); 132 WritableRaster target = sampleModel.getSampleSize(0) == 8 ? 133 Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, 134 im.getWidth(), 135 im.getHeight(), 136 sampleModel.getNumBands(), 137 new Point(im.getMinX(), 138 im.getMinY())) : 139 null; 140 141 // Copy the data. 142 raster = im.copyData(target); 143 } 144 145 return raster; 146 } 147 148 /** 149 * Subsamples and sub-bands the input <code>Raster</code> over a 150 * sub-region and stores the result in a <code>WritableRaster</code>. 151 * 152 * @param src The source <code>Raster</code> 153 * @param sourceBands The source bands to use; may be <code>null</code> 154 * @param subsampleX The subsampling factor along the horizontal axis. 155 * @param subsampleY The subsampling factor along the vertical axis. 156 * in which case all bands will be used. 157 * @param dst The destination <code>WritableRaster</code>. 158 * @throws IllegalArgumentException if <code>source</code> is 159 * <code>null</code> or empty, <code>dst</code> is <code>null</code>, 160 * <code>sourceBands.length</code> exceeds the number of bands in 161 * <code>source</code>, or <code>sourcBands</code> contains an element 162 * which is negative or greater than or equal to the number of bands 163 * in <code>source</code>. 164 */ 165 private static void reformat(Raster source, 166 int[] sourceBands, 167 int subsampleX, 168 int subsampleY, 169 WritableRaster dst) { 170 // Check for nulls. 171 if(source == null) { 172 throw new IllegalArgumentException("source == null!"); 173 } else if(dst == null) { 174 throw new IllegalArgumentException("dst == null!"); 175 } 176 177 // Validate the source bounds. XXX is this needed? 178 Rectangle sourceBounds = source.getBounds(); 179 if(sourceBounds.isEmpty()) { 180 throw new IllegalArgumentException 181 ("source.getBounds().isEmpty()!"); 182 } 183 184 // Check sub-banding. 185 boolean isSubBanding = false; 186 int numSourceBands = source.getSampleModel().getNumBands(); 187 if(sourceBands != null) { 188 if(sourceBands.length > numSourceBands) { 189 throw new IllegalArgumentException 190 ("sourceBands.length > numSourceBands!"); 191 } 192 193 boolean isRamp = sourceBands.length == numSourceBands; 194 for(int i = 0; i < sourceBands.length; i++) { 195 if(sourceBands[i] < 0 || sourceBands[i] >= numSourceBands) { 196 throw new IllegalArgumentException 197 ("sourceBands[i] < 0 || sourceBands[i] >= numSourceBands!"); 198 } else if(sourceBands[i] != i) { 199 isRamp = false; 200 } 201 } 202 203 isSubBanding = !isRamp; 204 } 205 206 // Allocate buffer for a single source row. 207 int sourceWidth = sourceBounds.width; 208 int[] pixels = new int[sourceWidth*numSourceBands]; 209 210 // Initialize variables used in loop. 211 int sourceX = sourceBounds.x; 212 int sourceY = sourceBounds.y; 213 int numBands = sourceBands != null ? 214 sourceBands.length : numSourceBands; 215 int dstWidth = dst.getWidth(); 216 int dstYMax = dst.getHeight() - 1; 217 int copyFromIncrement = numSourceBands*subsampleX; 218 219 // Loop over source rows, subsample each, and store in destination. 220 for(int dstY = 0; dstY <= dstYMax; dstY++) { 221 // Read one row. 222 source.getPixels(sourceX, sourceY, sourceWidth, 1, pixels); 223 224 // Copy within the same buffer by left shifting. 225 if(isSubBanding) { 226 int copyFrom = 0; 227 int copyTo = 0; 228 for(int i = 0; i < dstWidth; i++) { 229 for(int j = 0; j < numBands; j++) { 230 pixels[copyTo++] = pixels[copyFrom + sourceBands[j]]; 231 } 232 copyFrom += copyFromIncrement; 233 } 234 } else { 235 int copyFrom = copyFromIncrement; 236 int copyTo = numSourceBands; 237 // Start from index 1 as no need to copy the first pixel. 238 for(int i = 1; i < dstWidth; i++) { 239 int k = copyFrom; 240 for(int j = 0; j < numSourceBands; j++) { 241 pixels[copyTo++] = pixels[k++]; 242 } 243 copyFrom += copyFromIncrement; 244 } 245 } 246 247 // Set the destionation row. 248 dst.setPixels(0, dstY, dstWidth, 1, pixels); 249 250 // Increment the source row. 251 sourceY += subsampleY; 252 } 253 } 254 255 protected CLibImageWriter(ImageWriterSpi originatingProvider) { 256 super(originatingProvider); 257 } 258 259 public IIOMetadata convertImageMetadata(IIOMetadata inData, 260 ImageTypeSpecifier imageType, 261 ImageWriteParam param) { 262 return null; 263 } 264 265 public IIOMetadata convertStreamMetadata(IIOMetadata inData, 266 ImageWriteParam param) { 267 return null; 268 } 269 270 public IIOMetadata 271 getDefaultImageMetadata(ImageTypeSpecifier imageType, 272 ImageWriteParam param) { 273 return null; 274 } 275 276 public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) { 277 return null; 278 } 279 280 /* XXX 281 protected int getSignificantBits(RenderedImage image) { 282 SampleModel sampleModel = image.getSampleModel(); 283 int numBands = sampleModel.getNumBands(); 284 int[] sampleSize = sampleModel.getSampleSize(); 285 int significantBits = sampleSize[0]; 286 for(int i = 1; i < numBands; i++) { 287 significantBits = Math.max(significantBits, sampleSize[i]); 288 } 289 290 return significantBits; 291 } 292 */ 293 294 // Code copied from ImageReader.java with ImageReadParam replaced 295 // by ImageWriteParam. 296 private static final Rectangle getSourceRegion(ImageWriteParam param, 297 int sourceMinX, 298 int sourceMinY, 299 int srcWidth, 300 int srcHeight) { 301 Rectangle sourceRegion = 302 new Rectangle(sourceMinX, sourceMinY, srcWidth, srcHeight); 303 if (param != null) { 304 Rectangle region = param.getSourceRegion(); 305 if (region != null) { 306 sourceRegion = sourceRegion.intersection(region); 307 } 308 309 int subsampleXOffset = param.getSubsamplingXOffset(); 310 int subsampleYOffset = param.getSubsamplingYOffset(); 311 sourceRegion.x += subsampleXOffset; 312 sourceRegion.y += subsampleYOffset; 313 sourceRegion.width -= subsampleXOffset; 314 sourceRegion.height -= subsampleYOffset; 315 } 316 317 return sourceRegion; 318 } 319}