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: */
045: package com.sun.media.imageioimpl.plugins.clib;
046:
047: import java.awt.Point;
048: import java.awt.Rectangle;
049: import java.awt.color.ColorSpace;
050: import java.awt.geom.AffineTransform;
051: import java.awt.image.AffineTransformOp;
052: import java.awt.image.ColorModel;
053: import java.awt.image.ComponentSampleModel;
054: import java.awt.image.DataBuffer;
055: import java.awt.image.DataBufferByte;
056: import java.awt.image.DataBufferUShort;
057: import java.awt.image.IndexColorModel;
058: import java.awt.image.MultiPixelPackedSampleModel;
059: import java.awt.image.PixelInterleavedSampleModel;
060: import java.awt.image.Raster;
061: import java.awt.image.RenderedImage;
062: import java.awt.image.SampleModel;
063: import java.awt.image.SinglePixelPackedSampleModel;
064: import java.awt.image.WritableRaster;
065: import java.io.IOException;
066: import javax.imageio.IIOImage;
067: import javax.imageio.ImageWriter;
068: import javax.imageio.ImageWriteParam;
069: import javax.imageio.ImageTypeSpecifier;
070: import javax.imageio.metadata.IIOMetadata;
071: import javax.imageio.spi.ImageWriterSpi;
072: import com.sun.medialib.codec.jiio.Constants;
073: import com.sun.medialib.codec.jiio.mediaLibImage;
074:
075: public abstract class CLibImageWriter extends ImageWriter {
076: /**
077: * Returns the data array from the <code>DataBuffer</code>.
078: */
079: private static final Object getDataBufferData(DataBuffer db) {
080: Object data;
081:
082: int dType = db.getDataType();
083: switch (dType) {
084: case DataBuffer.TYPE_BYTE:
085: data = ((DataBufferByte) db).getData();
086: break;
087: case DataBuffer.TYPE_USHORT:
088: data = ((DataBufferUShort) db).getData();
089: break;
090: default:
091: throw new IllegalArgumentException(I18N
092: .getString("Generic0")
093: + " " + dType);
094: }
095:
096: return data;
097: }
098:
099: /**
100: * Returns the mediaLib type enum given the Java2D type enum.
101: */
102: private static final int getMediaLibDataType(int dataType) {
103: int mlibType;
104:
105: switch (dataType) {
106: case DataBuffer.TYPE_BYTE:
107: mlibType = mediaLibImage.MLIB_BYTE;
108: break;
109: case DataBuffer.TYPE_USHORT:
110: mlibType = mediaLibImage.MLIB_USHORT;
111: break;
112: default:
113: throw new IllegalArgumentException(I18N
114: .getString("Generic0")
115: + " " + dataType);
116: }
117:
118: return mlibType;
119: }
120:
121: /**
122: * Returns the mediaLib format enum given the <code>SampleModel</code>
123: * and <code>ColorModel</code> of an image. If the format cannot be
124: * determined to be anything more specific, the value
125: * <code>Constants.MLIB_FORMAT_UNKNOWN</code> will be returned.
126: *
127: * @param sampleModel The <code>SampleModel</code> describing the
128: * layout of the <code>DataBuffer</code>; may be <code>null</code>.
129: * @param colorModel The <code>ColorModel</code> describing the
130: * mapping of the samples in a pixel to a color.
131: *
132: * @throws IllegalArgumentExcaption if <code>sampleModel</code> is
133: * <code>null</code>.
134: *
135: * @return One of the <code>Constants.MLIB_FORMAT</code> constants.
136: */
137: private static final int getMediaLibFormat(SampleModel sampleModel,
138: ColorModel colorModel) {
139: if (sampleModel == null) {
140: throw new IllegalArgumentException("sampleModel == null!");
141: }
142:
143: int mlibFormat = Constants.MLIB_FORMAT_UNKNOWN;
144:
145: if (sampleModel instanceof SinglePixelPackedSampleModel
146: && sampleModel.getNumBands() == 4 && colorModel != null
147: && colorModel.hasAlpha()) {
148: int[] masks = ((SinglePixelPackedSampleModel) sampleModel)
149: .getBitMasks();
150: if (masks[3] == 0xff000000) {
151: if (masks[0] == 0xff && masks[1] == 0xff00
152: && masks[2] == 0xff0000) {
153: mlibFormat = Constants.MLIB_FORMAT_PACKED_ABGR;
154: } else if (masks[0] == 0xff0000 && masks[1] == 0xff00
155: && masks[2] == 0xff) {
156: mlibFormat = Constants.MLIB_FORMAT_PACKED_ARGB;
157: }
158: }
159: } else if (sampleModel instanceof ComponentSampleModel) {
160: ComponentSampleModel csm = (ComponentSampleModel) sampleModel;
161: int bandOffsets[] = csm.getBandOffsets();
162: int pixelStride = csm.getPixelStride();
163:
164: if (pixelStride == bandOffsets.length) {
165: int numBands = pixelStride; // for clarity
166:
167: boolean hasOneBank = true;
168: int bankIndices[] = csm.getBankIndices();
169: for (int i = 1; i < bankIndices.length; i++) {
170: if (bankIndices[i] != bankIndices[0]) {
171: hasOneBank = false;
172: }
173: }
174:
175: if (hasOneBank) {
176: if (colorModel instanceof IndexColorModel) {
177: mlibFormat = Constants.MLIB_FORMAT_INDEXED;
178: } else if (numBands == 1) {
179: mlibFormat = Constants.MLIB_FORMAT_GRAYSCALE;
180: } else if (numBands == 2 && bandOffsets[0] == 0
181: && bandOffsets[1] == 1) {
182: mlibFormat = Constants.MLIB_FORMAT_GRAYSCALE_ALPHA;
183: } else if (numBands == 3) {
184: int csType = colorModel != null ? colorModel
185: .getColorSpace().getType()
186: : ColorSpace.TYPE_RGB;
187: if (csType == ColorSpace.TYPE_RGB) {
188: if (bandOffsets[0] == 2
189: && bandOffsets[1] == 1
190: && bandOffsets[2] == 0) {
191: mlibFormat = Constants.MLIB_FORMAT_BGR;
192: } else if (bandOffsets[0] == 0
193: && bandOffsets[1] == 1
194: && bandOffsets[2] == 2) {
195: mlibFormat = Constants.MLIB_FORMAT_RGB;
196: }
197: } else if (csType == ColorSpace.TYPE_Yxy
198: && bandOffsets[0] == 0
199: && bandOffsets[1] == 1
200: && bandOffsets[2] == 2) {
201: mlibFormat = Constants.MLIB_FORMAT_YCC;
202: }
203: } else if (numBands == 4) {
204: int csType = colorModel != null ? colorModel
205: .getColorSpace().getType()
206: : ColorSpace.TYPE_RGB;
207: if (csType == ColorSpace.TYPE_RGB) {
208: if (bandOffsets[3] == 0) {
209: if (bandOffsets[0] == 3
210: && bandOffsets[1] == 2
211: && bandOffsets[2] == 1) {
212: mlibFormat = Constants.MLIB_FORMAT_ABGR;
213: } else if (bandOffsets[0] == 1
214: && bandOffsets[1] == 2
215: && bandOffsets[2] == 3) {
216: mlibFormat = Constants.MLIB_FORMAT_ARGB;
217: }
218: } else if (bandOffsets[3] == 3) {
219: if (bandOffsets[0] == 0
220: && bandOffsets[1] == 1
221: && bandOffsets[2] == 2) {
222: mlibFormat = Constants.MLIB_FORMAT_RGBA;
223: } else if (bandOffsets[0] == 2
224: && bandOffsets[1] == 1
225: && bandOffsets[2] == 0) {
226: mlibFormat = Constants.MLIB_FORMAT_BGRA;
227: }
228: }
229: } else if (csType == ColorSpace.TYPE_CMYK
230: && bandOffsets[0] == 0
231: && bandOffsets[1] == 1
232: && bandOffsets[2] == 2
233: && bandOffsets[3] == 3) {
234: mlibFormat = Constants.MLIB_FORMAT_CMYK;
235: } else if (csType == ColorSpace.TYPE_Yxy
236: && bandOffsets[0] == 0
237: && bandOffsets[1] == 1
238: && bandOffsets[2] == 2
239: && bandOffsets[3] == 3) {
240: if (colorModel != null
241: && colorModel.hasAlpha()) {
242: mlibFormat = Constants.MLIB_FORMAT_YCCA;
243: } else {
244: mlibFormat = Constants.MLIB_FORMAT_YCCK;
245: }
246: }
247: }
248: }
249: }
250: }
251:
252: return mlibFormat;
253: }
254:
255: /**
256: * Returns a contiguous <code>Raster</code> of data over the specified
257: * <code>Rectangle</code>. If the region is a sub-region of a single
258: * tile, then a child of that tile will be returned. If the region
259: * overlaps more than one tile and has 8 bits per sample, then a
260: * pixel interleaved Raster having band offsets 0,1,... will be returned.
261: * Otherwise the Raster returned by <code>im.copyData(null)</code> will
262: * be returned.
263: */
264: private static final Raster getContiguousData(RenderedImage im,
265: Rectangle region) {
266: if (im == null) {
267: throw new IllegalArgumentException("im == null");
268: } else if (region == null) {
269: throw new IllegalArgumentException("region == null");
270: }
271:
272: Raster raster;
273: if (im.getNumXTiles() == 1 && im.getNumYTiles() == 1) {
274: // Image is not tiled so just get a reference to the tile.
275: raster = im.getTile(im.getMinTileX(), im.getMinTileY());
276:
277: // Ensure result has requested coverage.
278: Rectangle bounds = raster.getBounds();
279: if (!bounds.equals(region)) {
280: raster = raster.createChild(region.x, region.y,
281: region.width, region.height, region.x,
282: region.y, null);
283: }
284: } else {
285: // Image is tiled.
286:
287: // Create an interleaved raster for copying for 8-bit case.
288: // This ensures that for RGB data the band offsets are {0,1,2}.
289: SampleModel sampleModel = im.getSampleModel();
290: WritableRaster target = sampleModel.getSampleSize(0) == 8 ? Raster
291: .createInterleavedRaster(DataBuffer.TYPE_BYTE, im
292: .getWidth(), im.getHeight(), sampleModel
293: .getNumBands(), new Point(im.getMinX(), im
294: .getMinY()))
295: : null;
296:
297: // Copy the data.
298: raster = im.copyData(target);
299: }
300:
301: return raster;
302: }
303:
304: /**
305: * Subsamples and sub-bands the input <code>Raster</code> over a
306: * sub-region and stores the result in a <code>WritableRaster</code>.
307: *
308: * @param src The source <code>Raster</code>
309: * @param sourceBands The source bands to use; may be <code>null</code>
310: * @param subsampleX The subsampling factor along the horizontal axis.
311: * @param subsampleY The subsampling factor along the vertical axis.
312: * in which case all bands will be used.
313: * @param dst The destination <code>WritableRaster</code>.
314: * @throws IllegalArgumentException if <code>source</code> is
315: * <code>null</code> or empty, <code>dst</code> is <code>null</code>,
316: * <code>sourceBands.length</code> exceeds the number of bands in
317: * <code>source</code>, or <code>sourcBands</code> contains an element
318: * which is negative or greater than or equal to the number of bands
319: * in <code>source</code>.
320: */
321: private static void reformat(Raster source, int[] sourceBands,
322: int subsampleX, int subsampleY, WritableRaster dst) {
323: // Check for nulls.
324: if (source == null) {
325: throw new IllegalArgumentException("source == null!");
326: } else if (dst == null) {
327: throw new IllegalArgumentException("dst == null!");
328: }
329:
330: // Validate the source bounds. XXX is this needed?
331: Rectangle sourceBounds = source.getBounds();
332: if (sourceBounds.isEmpty()) {
333: throw new IllegalArgumentException(
334: "source.getBounds().isEmpty()!");
335: }
336:
337: // Check sub-banding.
338: boolean isSubBanding = false;
339: int numSourceBands = source.getSampleModel().getNumBands();
340: if (sourceBands != null) {
341: if (sourceBands.length > numSourceBands) {
342: throw new IllegalArgumentException(
343: "sourceBands.length > numSourceBands!");
344: }
345:
346: boolean isRamp = sourceBands.length == numSourceBands;
347: for (int i = 0; i < sourceBands.length; i++) {
348: if (sourceBands[i] < 0
349: || sourceBands[i] >= numSourceBands) {
350: throw new IllegalArgumentException(
351: "sourceBands[i] < 0 || sourceBands[i] >= numSourceBands!");
352: } else if (sourceBands[i] != i) {
353: isRamp = false;
354: }
355: }
356:
357: isSubBanding = !isRamp;
358: }
359:
360: // Allocate buffer for a single source row.
361: int sourceWidth = sourceBounds.width;
362: int[] pixels = new int[sourceWidth * numSourceBands];
363:
364: // Initialize variables used in loop.
365: int sourceX = sourceBounds.x;
366: int sourceY = sourceBounds.y;
367: int numBands = sourceBands != null ? sourceBands.length
368: : numSourceBands;
369: int dstWidth = dst.getWidth();
370: int dstYMax = dst.getHeight() - 1;
371: int copyFromIncrement = numSourceBands * subsampleX;
372:
373: // Loop over source rows, subsample each, and store in destination.
374: for (int dstY = 0; dstY <= dstYMax; dstY++) {
375: // Read one row.
376: source.getPixels(sourceX, sourceY, sourceWidth, 1, pixels);
377:
378: // Copy within the same buffer by left shifting.
379: if (isSubBanding) {
380: int copyFrom = 0;
381: int copyTo = 0;
382: for (int i = 0; i < dstWidth; i++) {
383: for (int j = 0; j < numBands; j++) {
384: pixels[copyTo++] = pixels[copyFrom
385: + sourceBands[j]];
386: }
387: copyFrom += copyFromIncrement;
388: }
389: } else {
390: int copyFrom = copyFromIncrement;
391: int copyTo = numSourceBands;
392: // Start from index 1 as no need to copy the first pixel.
393: for (int i = 1; i < dstWidth; i++) {
394: int k = copyFrom;
395: for (int j = 0; j < numSourceBands; j++) {
396: pixels[copyTo++] = pixels[k++];
397: }
398: copyFrom += copyFromIncrement;
399: }
400: }
401:
402: // Set the destionation row.
403: dst.setPixels(0, dstY, dstWidth, 1, pixels);
404:
405: // Increment the source row.
406: sourceY += subsampleY;
407: }
408: }
409:
410: protected CLibImageWriter(ImageWriterSpi originatingProvider) {
411: super (originatingProvider);
412: }
413:
414: public IIOMetadata convertImageMetadata(IIOMetadata inData,
415: ImageTypeSpecifier imageType, ImageWriteParam param) {
416: return null;
417: }
418:
419: public IIOMetadata convertStreamMetadata(IIOMetadata inData,
420: ImageWriteParam param) {
421: return null;
422: }
423:
424: public IIOMetadata getDefaultImageMetadata(
425: ImageTypeSpecifier imageType, ImageWriteParam param) {
426: return null;
427: }
428:
429: public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) {
430: return null;
431: }
432:
433: /* XXX
434: protected int getSignificantBits(RenderedImage image) {
435: SampleModel sampleModel = image.getSampleModel();
436: int numBands = sampleModel.getNumBands();
437: int[] sampleSize = sampleModel.getSampleSize();
438: int significantBits = sampleSize[0];
439: for(int i = 1; i < numBands; i++) {
440: significantBits = Math.max(significantBits, sampleSize[i]);
441: }
442:
443: return significantBits;
444: }
445: */
446:
447: // Code copied from ImageReader.java with ImageReadParam replaced
448: // by ImageWriteParam.
449: private static final Rectangle getSourceRegion(
450: ImageWriteParam param, int sourceMinX, int sourceMinY,
451: int srcWidth, int srcHeight) {
452: Rectangle sourceRegion = new Rectangle(sourceMinX, sourceMinY,
453: srcWidth, srcHeight);
454: if (param != null) {
455: Rectangle region = param.getSourceRegion();
456: if (region != null) {
457: sourceRegion = sourceRegion.intersection(region);
458: }
459:
460: int subsampleXOffset = param.getSubsamplingXOffset();
461: int subsampleYOffset = param.getSubsamplingYOffset();
462: sourceRegion.x += subsampleXOffset;
463: sourceRegion.y += subsampleYOffset;
464: sourceRegion.width -= subsampleXOffset;
465: sourceRegion.height -= subsampleYOffset;
466: }
467:
468: return sourceRegion;
469: }
470:
471: /**
472: * Returns a <code>mediaLibImage</code> for a specific encoder to use
473: * to encode <code>image</code>.
474: *
475: * @param image The image to encode.
476: * @param param The write parameters.
477: * @param allowBilevel Whether bilevel images are allowed. A bilevel
478: * image must have one 1-bit sample per pixel, have data type
479: * <code>DataBuffer.TYE_BYTE</code>, and have a
480: * <code>MultiPixelPackedSampleModel</code>.
481: * @param supportedFormats An array containing constan values from
482: * the set of <code>mediaLibImage.MLIB_FORMAT</code> enums.
483: *
484: * @throws IllegalArgumentException if <code>supportedFormats</code>
485: * is <code>null</code>.
486: *
487: * @return A <code>mediaLibImage in a format capable of being written
488: * by the encoder.
489: */
490: protected mediaLibImage getMediaLibImage(RenderedImage image,
491: ImageWriteParam param, boolean allowBilevel,
492: int[] supportedFormats) {
493: if (supportedFormats == null) {
494: throw new IllegalArgumentException(
495: "supportedFormats == null!");
496: }
497:
498: // Determine the source region.
499: Rectangle sourceRegion = getSourceRegion(param,
500: image.getMinX(), image.getMinY(), image.getWidth(),
501: image.getHeight());
502:
503: if (sourceRegion.isEmpty()) {
504: throw new IllegalArgumentException("sourceRegion.isEmpty()");
505: }
506:
507: // Check whether reformatting is necessary to conform to mediaLib
508: // image format (packed bilevel if allowed or ((G|I)|(RGB))[A]).
509:
510: // Flag indicating need to reformat data.
511: boolean reformatData = false;
512:
513: // Flag indicating bilevel data.
514: boolean isBilevel = false;
515:
516: // Value indicating the mediaLib image format.
517: int mediaLibFormat = Constants.MLIB_FORMAT_UNKNOWN;
518:
519: // Get the SampleModel.
520: SampleModel sampleModel = image.getSampleModel();
521:
522: // Get the number of bands.
523: int numSourceBands = sampleModel.getNumBands();
524:
525: // Get the source sub-banding array.
526: int[] sourceBands = param != null ? param.getSourceBands()
527: : null;
528:
529: // Check for non-nominal sub-banding.
530: int numBands;
531: if (sourceBands != null) {
532: numBands = sourceBands.length;
533: if (numBands != numSourceBands) {
534: // The number of bands must be the same.
535: reformatData = true;
536: } else {
537: // The band order must not change.
538: for (int i = 0; i < numSourceBands; i++) {
539: if (sourceBands[i] != i) {
540: reformatData = true;
541: break;
542: }
543: }
544: }
545: } else {
546: numBands = numSourceBands;
547: }
548:
549: // If sub-banding does not dictate reformatting, check subsampling..
550: if (!reformatData
551: && param != null
552: && (param.getSourceXSubsampling() != 1 || param
553: .getSourceXSubsampling() != 1)) {
554: reformatData = true;
555: }
556:
557: // If sub-banding does not dictate reformatting check SampleModel.
558: if (!reformatData) {
559: if (allowBilevel
560: && sampleModel.getNumBands() == 1
561: && sampleModel.getSampleSize(0) == 1
562: && sampleModel instanceof MultiPixelPackedSampleModel
563: && sampleModel.getDataType() == DataBuffer.TYPE_BYTE) {
564: // Need continguous packed bits.
565: MultiPixelPackedSampleModel mppsm = (MultiPixelPackedSampleModel) sampleModel;
566: if (mppsm.getPixelBitStride() == 1) {
567: isBilevel = true;
568: } else {
569: reformatData = true;
570: }
571: } else {
572: // Set the mediaLib format flag.
573: mediaLibFormat = getMediaLibFormat(sampleModel, image
574: .getColorModel());
575:
576: // Set the data reformatting flag.
577: reformatData = true;
578: int len = supportedFormats.length;
579: for (int i = 0; i < len; i++) {
580: if (mediaLibFormat == supportedFormats[i]) {
581: reformatData = false;
582: break;
583: }
584: }
585: }
586: }
587:
588: // Variable for the eventual destination data.
589: Raster raster = null;
590:
591: if (reformatData) {
592: // Determine the maximum bit depth.
593: int[] sampleSize = sampleModel.getSampleSize();
594: int bitDepthMax = sampleSize[0];
595: for (int i = 1; i < numSourceBands; i++) {
596: bitDepthMax = Math.max(bitDepthMax, sampleSize[i]);
597: }
598:
599: // Set the data type as a function of bit depth.
600: int dataType;
601: if (bitDepthMax <= 8) {
602: dataType = DataBuffer.TYPE_BYTE;
603: } else if (bitDepthMax <= 16) {
604: dataType = DataBuffer.TYPE_USHORT;
605: } else {
606: throw new UnsupportedOperationException(I18N
607: .getString("CLibImageWriter0")
608: + " " + bitDepthMax);
609: }
610:
611: // Determine the width and height.
612: int width;
613: int height;
614: if (param != null) {
615: int subsampleX = param.getSourceXSubsampling();
616: int subsampleY = param.getSourceYSubsampling();
617: width = (sourceRegion.width + subsampleX - 1)
618: / subsampleX;
619: height = (sourceRegion.height + subsampleY - 1)
620: / subsampleY;
621: } else {
622: width = sourceRegion.width;
623: height = sourceRegion.height;
624: }
625:
626: // Load a ramp for band offsets.
627: int[] newBandOffsets = new int[numBands];
628: for (int i = 0; i < numBands; i++) {
629: newBandOffsets[i] = i;
630: }
631:
632: // Create a new SampleModel.
633: SampleModel newSampleModel;
634: if (allowBilevel && sampleModel.getNumBands() == 1
635: && bitDepthMax == 1) {
636: // Bilevel image.
637: newSampleModel = new MultiPixelPackedSampleModel(
638: dataType, width, height, 1);
639: isBilevel = true;
640: } else {
641: // Pixel interleaved image.
642: newSampleModel = new PixelInterleavedSampleModel(
643: dataType, width, height, newBandOffsets.length,
644: width * numSourceBands, newBandOffsets);
645: }
646:
647: // Create a new Raster at (0,0).
648: WritableRaster newRaster = Raster.createWritableRaster(
649: newSampleModel, null);
650:
651: // Populate the new Raster.
652: if (param != null
653: && (param.getSourceXSubsampling() != 1 || param
654: .getSourceXSubsampling() != 1)) {
655: // Subsampling, possibly with sub-banding.
656: reformat(getContiguousData(image, sourceRegion),
657: sourceBands, param.getSourceXSubsampling(),
658: param.getSourceYSubsampling(), newRaster);
659: } else if (sourceBands == null
660: && image.getSampleModel().getClass().isInstance(
661: newSampleModel)
662: && newSampleModel.getTransferType() == image
663: .getSampleModel().getTransferType()) {
664: // Neither subsampling nor sub-banding.
665: WritableRaster translatedChild = newRaster
666: .createWritableTranslatedChild(sourceRegion.x,
667: sourceRegion.y);
668: // Use copyData() to avoid potentially cobbling the entire
669: // source region into an extra Raster via getData().
670: image.copyData(translatedChild);
671: } else {
672: // Cannot use copyData() so use getData() to retrieve and
673: // possibly sub-band the source data and use setRect().
674: WritableRaster translatedChild = newRaster
675: .createWritableTranslatedChild(sourceRegion.x,
676: sourceRegion.y);
677: Raster sourceRaster = getContiguousData(image,
678: sourceRegion);
679: if (sourceBands != null) {
680: // Copy only the requested bands.
681: sourceRaster = sourceRaster
682: .createChild(sourceRegion.x,
683: sourceRegion.y, sourceRegion.width,
684: sourceRegion.height,
685: sourceRegion.x, sourceRegion.y,
686: sourceBands);
687: }
688:
689: // Get the region from the image and set it into the Raster.
690: translatedChild.setRect(sourceRaster);
691: }
692:
693: // Replace Raster and SampleModel.
694: raster = newRaster;
695: sampleModel = newRaster.getSampleModel();
696: } else { // !reformatData
697: // No reformatting needed.
698: raster = getContiguousData(image, sourceRegion)
699: .createTranslatedChild(0, 0);
700: sampleModel = raster.getSampleModel();
701:
702: // Update mediaLibFormat indicator in case getContiguousData()
703: // has changed the layout of the data.
704: mediaLibFormat = getMediaLibFormat(sampleModel, image
705: .getColorModel());
706: }
707:
708: // The mediaLib image.
709: mediaLibImage mlibImage = null;
710:
711: // Create a mediaLibImage with reference to the Raster data.
712: if (isBilevel) {
713: // Bilevel image: either is was already bilevel or was
714: // formatted to bilevel.
715:
716: MultiPixelPackedSampleModel mppsm = ((MultiPixelPackedSampleModel) sampleModel);
717:
718: // Get the line stride.
719: int stride = mppsm.getScanlineStride();
720:
721: // Determine the offset to the start of the data.
722: int offset = raster.getDataBuffer().getOffset()
723: - raster.getSampleModelTranslateY() * stride
724: - raster.getSampleModelTranslateX() / 8
725: + mppsm.getOffset(0, 0);
726:
727: // Get a reference to the internal data array.
728: Object bitData = getDataBufferData(raster.getDataBuffer());
729:
730: mlibImage = new mediaLibImage(mediaLibImage.MLIB_BIT, 1,
731: raster.getWidth(), raster.getHeight(), stride,
732: offset, (byte) mppsm.getBitOffset(0), bitData);
733: } else {
734: // If the image is not bilevel then it has to be component.
735: ComponentSampleModel csm = (ComponentSampleModel) sampleModel;
736:
737: // Set the mediaLib data type
738: int mlibDataType = getMediaLibDataType(sampleModel
739: .getDataType());
740:
741: // Get a reference to the internal data array.
742: Object data = getDataBufferData(raster.getDataBuffer());
743:
744: // Get the line stride.
745: int stride = csm.getScanlineStride();
746:
747: // Determine the offset of the first sample from the offset
748: // indicated by the (x,y) coordinates. This offset is the
749: // minimum valued offset, not the offset of, e.g., red (index 0)
750: // as the Raster is by now in a contiguous format that
751: // the encoder is guaranteed to handle regardless of whether
752: // the smallest offset is to the, e.g., red band.
753: int[] bandOffsets = csm.getBandOffsets();
754: int minBandOffset = bandOffsets[0];
755: for (int i = 1; i < bandOffsets.length; i++) {
756: if (bandOffsets[i] < minBandOffset) {
757: minBandOffset = bandOffsets[i];
758: }
759: }
760:
761: // Determine the offset to the start of the data. The
762: // sampleModelTranslate parameters are the translations from
763: // Raster to SampleModel coordinates and must be subtracted
764: // from the Raster coordinates.
765: int offset = (raster.getMinY() - raster
766: .getSampleModelTranslateY())
767: * stride
768: + (raster.getMinX() - raster
769: .getSampleModelTranslateX())
770: * numSourceBands + minBandOffset;
771:
772: // Create the image.
773: mlibImage = !reformatData
774: && mediaLibFormat != Constants.MLIB_FORMAT_UNKNOWN ? new mediaLibImage(
775: mlibDataType, numSourceBands, raster.getWidth(),
776: raster.getHeight(), stride, offset, mediaLibFormat,
777: data)
778: : new mediaLibImage(mlibDataType, numSourceBands,
779: raster.getWidth(), raster.getHeight(),
780: stride, offset, data);
781: }
782:
783: return mlibImage;
784: }
785: }
|