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}