001: /*
002: * $RCSfile: J2KImageWriterCodecLib.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.3 $
042: * $Date: 2006/09/22 23:07:25 $
043: * $State: Exp $
044: */
045: package com.sun.media.imageioimpl.plugins.jpeg2000;
046:
047: import java.awt.Point;
048: import java.awt.Rectangle;
049: import java.awt.image.ColorModel;
050: import java.awt.image.DataBuffer;
051: import java.awt.image.DataBufferByte;
052: import java.awt.image.IndexColorModel;
053: import java.awt.image.Raster;
054: import java.awt.image.RenderedImage;
055: import java.awt.image.SampleModel;
056: import java.awt.image.WritableRaster;
057:
058: import java.io.IOException;
059:
060: import java.util.Arrays;
061: import java.util.List;
062:
063: import javax.imageio.IIOImage;
064: import javax.imageio.IIOException;
065: import javax.imageio.ImageTypeSpecifier;
066: import javax.imageio.ImageWriteParam;
067: import javax.imageio.ImageWriter;
068: import javax.imageio.metadata.IIOMetadata;
069: import javax.imageio.metadata.IIOMetadataFormatImpl;
070: import javax.imageio.metadata.IIOMetadataNode;
071: import javax.imageio.metadata.IIOInvalidTreeException;
072: import javax.imageio.spi.ImageWriterSpi;
073: import javax.imageio.stream.ImageOutputStream;
074:
075: import com.sun.media.imageio.plugins.jpeg2000.J2KImageWriteParam;
076: import com.sun.media.imageioimpl.common.ImageUtil;
077: import org.w3c.dom.Node;
078: import org.w3c.dom.NodeList;
079:
080: import com.sun.medialib.codec.jp2k.CompParams;
081: import com.sun.medialib.codec.jp2k.Constants;
082: import com.sun.medialib.codec.jp2k.Encoder;
083: import com.sun.medialib.codec.jp2k.Params;
084: import com.sun.medialib.codec.jp2k.Size;
085: import com.sun.medialib.codec.jiio.*;
086:
087: public class J2KImageWriterCodecLib extends ImageWriter {
088: /** When the writing is aborted, <code>RenderedImageSrc</code> throws a
089: * <code>RuntimeException</code>.
090: */
091: public static String WRITE_ABORTED = "Write aborted.";
092:
093: /** The output stream to write into */
094: private ImageOutputStream stream = null;
095:
096: /** The metadata format object. */
097: private J2KMetadataFormat format;
098:
099: /** medialib encoder. */
100: private Encoder encoder;
101:
102: /** size parameters for medialib. */
103: private Size size;
104:
105: /** The tile width for encoding */
106: private int tileWidth;
107:
108: /** The tile height for encoding */
109: private int tileHeight;
110:
111: /** The tile grid offset for encoding */
112: private int tileXOffset, tileYOffset;
113:
114: /** The source -> destination transformation */
115: private int scaleX, scaleY, xOffset, yOffset;
116:
117: /** The source bands to be encoded. */
118: private int[] sourceBands = null;
119:
120: /** The number of components in the image */
121: private int numComp;
122:
123: private RenderedImage input;
124: private J2KImageWriteParam param;
125:
126: /** The input source raster. */
127: private Raster inputRaster;
128:
129: private Rectangle destinationRegion = null;
130:
131: private SampleModel sampleModel;
132:
133: /** Coordinate transform or sub selection is needed before encoding. */
134: private boolean noTransform = true;
135: private boolean noSubband = true;
136:
137: /** Indicates a <code>raster</code> rather than a <code>RenderedImage</code>
138: * to be encoded.
139: */
140: private boolean writeRaster = false;
141:
142: /** Constructs <code>J2KImageWriter</code> based on the provided
143: * <code>ImageWriterSpi</code>.
144: */
145: public J2KImageWriterCodecLib(ImageWriterSpi originator) {
146: super (originator);
147: }
148:
149: public void setOutput(Object output) {
150: super .setOutput(output); // validates output
151: if (output != null) {
152: if (!(output instanceof ImageOutputStream))
153: throw new IllegalArgumentException(I18N
154: .getString("J2KImageWriter0"));
155: this .stream = (ImageOutputStream) output;
156: } else
157: this .stream = null;
158: }
159:
160: public ImageWriteParam getDefaultWriteParam() {
161: return new J2KImageWriteParam();
162: }
163:
164: public IIOMetadata convertStreamMetadata(IIOMetadata inData,
165: ImageWriteParam param) {
166: return null;
167: }
168:
169: public void write(IIOMetadata streamMetadata, IIOImage image,
170: ImageWriteParam param) throws java.io.IOException {
171: if (stream == null) {
172: throw new IllegalStateException(I18N
173: .getString("J2KImageWriterMedialib1"));
174: }
175: if (image == null) {
176: throw new IllegalArgumentException(I18N
177: .getString("J2KImageWriterMedialib2"));
178: }
179: clearAbortRequest();
180: processImageStarted(0);
181: encoder = new Encoder(stream);
182:
183: writeRaster = image.hasRaster();
184: ColorModel colorModel = null;
185:
186: if (writeRaster) {
187: inputRaster = image.getRaster();
188: sampleModel = inputRaster.getSampleModel();
189: } else {
190: input = image.getRenderedImage();
191: sampleModel = input.getSampleModel();
192: colorModel = input.getColorModel();
193: }
194:
195: if (param == null)
196: param = new J2KImageWriteParam();
197:
198: if (param instanceof J2KImageWriteParam) {
199: J2KImageWriteParam j2kParam = (J2KImageWriteParam) param;
200: if (!writeRaster
201: && input.getColorModel() instanceof IndexColorModel) {
202: j2kParam.setLossless(true);
203: j2kParam.setEncodingRate(Double.MAX_VALUE);
204: j2kParam.setFilter(J2KImageWriteParam.FILTER_53);
205: } else if (j2kParam.getEncodingRate() == Double.MAX_VALUE) {
206: j2kParam.setLossless(true);
207: j2kParam.setFilter(J2KImageWriteParam.FILTER_53);
208: }
209: }
210: setParameters(param);
211:
212: Rectangle sourceRegion = param.getSourceRegion();
213: if (sourceRegion == null) {
214: if (writeRaster)
215: sourceRegion = inputRaster.getBounds();
216: else
217: sourceRegion = new Rectangle(input.getMinX(), input
218: .getMinY(), input.getWidth(), input.getHeight());
219: } else {
220: if (writeRaster)
221: sourceRegion = sourceRegion.intersection(inputRaster
222: .getBounds());
223: else
224: sourceRegion = sourceRegion.intersection(new Rectangle(
225: input.getMinX(), input.getMinY(), input
226: .getWidth(), input.getHeight()));
227: }
228:
229: if (sourceRegion.isEmpty())
230: throw new RuntimeException(I18N
231: .getString("J2KImageWriterCodecLib0"));
232:
233: try {
234: tileWidth = param.getTileWidth();
235: tileHeight = param.getTileHeight();
236: tileXOffset = param.getTileGridXOffset();
237: tileYOffset = param.getTileGridYOffset();
238: } catch (IllegalStateException e) {
239: param.setTilingMode(ImageWriteParam.MODE_EXPLICIT);
240: if (writeRaster) {
241: param.setTiling(inputRaster.getWidth(), inputRaster
242: .getHeight(), inputRaster.getMinX(),
243: inputRaster.getMinY());
244: } else {
245: param.setTiling(input.getTileWidth(), input
246: .getTileHeight(), input.getTileGridXOffset(),
247: input.getTileGridYOffset());
248: }
249: tileWidth = param.getTileWidth();
250: tileHeight = param.getTileHeight();
251: tileXOffset = param.getTileGridXOffset();
252: tileYOffset = param.getTileGridYOffset();
253: }
254:
255: scaleX = param.getSourceXSubsampling();
256: scaleY = param.getSourceYSubsampling();
257: xOffset = param.getSubsamplingXOffset();
258: yOffset = param.getSubsamplingYOffset();
259:
260: sourceRegion.translate(xOffset, yOffset);
261: sourceRegion.width -= xOffset;
262: sourceRegion.height -= yOffset;
263:
264: xOffset = sourceRegion.x % scaleX;
265: yOffset = sourceRegion.y % scaleY;
266:
267: int minX = sourceRegion.x / scaleX;
268: int minY = sourceRegion.y / scaleY;
269:
270: int w = (sourceRegion.width + scaleX - 1) / scaleX;
271: int h = (sourceRegion.height + scaleY - 1) / scaleY;
272:
273: tileXOffset += (minX - tileXOffset) / tileWidth * tileWidth;
274: tileYOffset += (minY - tileYOffset) / tileHeight * tileHeight;
275:
276: destinationRegion = new Rectangle(minX, minY, w, h);
277:
278: if (!destinationRegion.equals(sourceRegion)
279: || tileWidth != sampleModel.getWidth()
280: || tileHeight != sampleModel.getHeight()
281: || (!writeRaster && (tileXOffset != input
282: .getTileGridXOffset() || tileYOffset != input
283: .getTileGridYOffset()))
284: || (writeRaster && (tileXOffset != inputRaster
285: .getMinX() || tileYOffset != inputRaster
286: .getMinY())))
287: noTransform = false;
288:
289: numComp = sampleModel.getNumBands();
290: sourceBands = param.getSourceBands();
291: if (sourceBands != null) {
292: sampleModel = sampleModel
293: .createSubsetSampleModel(sourceBands);
294: colorModel = null;
295: noSubband = false;
296: } else {
297: sourceBands = new int[numComp];
298: for (int i = 0; i < numComp; i++)
299: sourceBands[i] = i;
300: }
301:
302: numComp = sourceBands.length;
303:
304: sampleModel = sampleModel.createCompatibleSampleModel(
305: tileWidth, tileHeight);
306:
307: setSize();
308:
309: setCompParameters(colorModel, sampleModel, param);
310:
311: encoder.setMode(Constants.JP2K_COMPOSITE_TILE);
312:
313: /* XXX
314: J2KMetadata metadata = (J2KMetadata)image.getMetadata();
315: ImageTypeSpecifier type = null;
316: if (colorModel != null)
317: type = new ImageTypeSpecifier(colorModel, sampleModel);
318:
319: J2KMetadata metadata1 =
320: new J2KMetadata(colorModel, sampleModel, destinationRegion.width,
321: destinationRegion.height, param, this);
322:
323: if (metadata == null)
324: metadata = metadata1;
325: else
326: metadata.mergeTree("com_sun_media_imageio_plugins_jpeg2000_image_1.0",
327: metadata1.getAsTree("com_sun_media_imageio_plugins_jpeg2000_image_1.0"));
328: */
329:
330: //write the metadata
331: if (!((J2KImageWriteParam) param).getWriteCodeStreamOnly()) {
332: IIOMetadata inMetadata = image.getMetadata();
333:
334: J2KMetadata metadata1 = new J2KMetadata(colorModel,
335: sampleModel, destinationRegion.width,
336: destinationRegion.height, param, this );
337:
338: J2KMetadata metadata = null;
339:
340: if (inMetadata == null) {
341: metadata = metadata1;
342: } else {
343: // Convert the input metadata tree to a J2KMetadata.
344: if (colorModel != null) {
345: ImageTypeSpecifier imageType = new ImageTypeSpecifier(
346: colorModel, sampleModel);
347: metadata = (J2KMetadata) convertImageMetadata(
348: inMetadata, imageType, param);
349: } else {
350: String metaFormat = null;
351: List metaFormats = Arrays.asList(inMetadata
352: .getMetadataFormatNames());
353: if (metaFormats
354: .contains(J2KMetadata.nativeMetadataFormatName)) {
355: // Initialize from native image metadata format.
356: metaFormat = J2KMetadata.nativeMetadataFormatName;
357: } else if (inMetadata
358: .isStandardMetadataFormatSupported()) {
359: // Initialize from standard metadata form of the
360: // input tree.
361: metaFormat = IIOMetadataFormatImpl.standardMetadataFormatName;
362: }
363:
364: metadata = new J2KMetadata();
365: if (metaFormat != null) {
366: metadata.setFromTree(metaFormat, inMetadata
367: .getAsTree(metaFormat));
368: }
369: }
370:
371: metadata
372: .mergeTree(
373: J2KMetadata.nativeMetadataFormatName,
374: metadata1
375: .getAsTree(J2KMetadata.nativeMetadataFormatName));
376: }
377:
378: writeMetadata(metadata);
379: } else {
380: encoder.setEncodeCodeStreamOnly();
381: }
382:
383: for (int y = getMinTileY(); y <= getMaxTileY(); y++) {
384: for (int x = getMinTileX(); x <= getMaxTileX(); x++) {
385: Raster currentTile = getTile(x, y);
386: int sourceFormatTag = MediaLibAccessor
387: .findCompatibleTag(currentTile);
388:
389: MediaLibAccessor accessor = new MediaLibAccessor(
390: currentTile, currentTile.getBounds(),
391: sourceFormatTag, true);
392: mediaLibImage[] mlImage = accessor.getMediaLibImages();
393:
394: encoder.encode(mlImage, x + y * size.nxtiles);
395: float percentage = (x + y * size.nxtiles + 1.0F)
396: / (size.nxtiles * size.nytiles);
397: processImageProgress(percentage * 100.0F);
398: }
399: }
400: }
401:
402: public IIOMetadata getDefaultImageMetadata(
403: ImageTypeSpecifier imageType, ImageWriteParam param) {
404: return new J2KMetadata(imageType, param, this );
405: }
406:
407: public IIOMetadata getDefaultStreamMetadata(ImageWriteParam param) {
408: return null;
409: }
410:
411: public IIOMetadata convertImageMetadata(IIOMetadata inData,
412: ImageTypeSpecifier imageType, ImageWriteParam param) {
413: // Check arguments.
414: if (inData == null) {
415: throw new IllegalArgumentException("inData == null!");
416: }
417: if (imageType == null) {
418: throw new IllegalArgumentException("imageType == null!");
419: }
420:
421: // If it's one of ours, return a clone.
422: if (inData instanceof J2KMetadata) {
423: return (IIOMetadata) ((J2KMetadata) inData).clone();
424: }
425:
426: try {
427: J2KMetadata outData = new J2KMetadata();
428:
429: List formats = Arrays.asList(inData
430: .getMetadataFormatNames());
431:
432: String format = null;
433: if (formats.contains(J2KMetadata.nativeMetadataFormatName)) {
434: // Initialize from native image metadata format.
435: format = J2KMetadata.nativeMetadataFormatName;
436: } else if (inData.isStandardMetadataFormatSupported()) {
437: // Initialize from standard metadata form of the input tree.
438: format = IIOMetadataFormatImpl.standardMetadataFormatName;
439: }
440:
441: if (format != null) {
442: outData.setFromTree(format, inData.getAsTree(format));
443: return outData;
444: }
445: } catch (IIOInvalidTreeException e) {
446: return null;
447: }
448:
449: return null;
450: }
451:
452: public boolean canWriteRasters() {
453: return true;
454: }
455:
456: public synchronized void abort() {
457: super .abort();
458: }
459:
460: public void reset() {
461: // reset local Java structures
462: super .reset();
463: stream = null;
464: }
465:
466: /** This method wraps the protected method <code>abortRequested</code>
467: * to allow the abortions be monitored by <code>J2KRenderedImage</code>.
468: */
469: public boolean getAbortRequest() {
470: return abortRequested();
471: }
472:
473: private void checkSampleModel(SampleModel sm) {
474: int type = sm.getDataType();
475:
476: if (type < DataBuffer.TYPE_BYTE || type > DataBuffer.TYPE_INT)
477: throw new IllegalArgumentException(I18N
478: .getString("J2KImageWriter5"));
479: if (sm.getNumBands() > 16384)
480: throw new IllegalArgumentException(I18N
481: .getString("J2KImageWriter6"));
482: }
483:
484: private void writeMetadata(J2KMetadata metadata) throws IOException {
485: if (metadata == null)
486: return;
487:
488: IIOMetadataNode root = (IIOMetadataNode) metadata
489: .getAsTree("com_sun_media_imageio_plugins_jpeg2000_image_1.0");
490: if (root == null)
491: return;
492: format = (J2KMetadataFormat) metadata
493: .getMetadataFormat("com_sun_media_imageio_plugins_jpeg2000_image_1.0");
494: writeSuperBox(root);
495: }
496:
497: private void writeSuperBox(IIOMetadataNode node) throws IOException {
498: NodeList list = node.getChildNodes();
499:
500: String name = node.getNodeName();
501: if (name.startsWith("JPEG2000")) {
502: /*
503: int length = computeLength(node);
504: byte[] data = new byte[length];
505: generateSuperBoxContent(node, data, 0);
506: com.sun.medialib.codec.jp2k.Box box =
507: new com.sun.medialib.codec.jp2k.Box();
508: box.data = data;
509: box.type = Box.getTypeInt((String)Box.getTypeByName(name));
510: encoder.encodeSuperBox(box.type, new com.sun.medialib.codec.jp2k.Box[]{box});
511: return;
512: */
513: /*
514: com.sun.medialib.codec.jp2k.Box box =
515: new com.sun.medialib.codec.jp2k.Box();
516: box.type = Box.getTypeInt((String)Box.getTypeByName(name));
517: encoder.encodeSuperBox(box.type, null);
518: */
519: }
520:
521: for (int i = 0; i < list.getLength(); i++) {
522: IIOMetadataNode child = (IIOMetadataNode) list.item(i);
523:
524: name = child.getNodeName();
525: if (name.startsWith("JPEG2000") && format.isLeaf(name))
526: writeBox(child);
527: else
528: writeSuperBox(child);
529: }
530: }
531:
532: private void writeBox(IIOMetadataNode node) throws IOException {
533: com.sun.medialib.codec.jp2k.Box mlibBox = new com.sun.medialib.codec.jp2k.Box();
534: mlibBox.type = Box.getTypeInt((String) Box.getAttribute(node,
535: "Type"));
536: Box box = Box.createBox(mlibBox.type, node);
537: mlibBox.data = box.getContent();
538: encoder.encodeBox(mlibBox);
539: }
540:
541: private int computeLength(IIOMetadataNode root) {
542: NodeList list = root.getChildNodes();
543: int length = 0;
544: for (int i = 0; i < list.getLength(); i++) {
545: IIOMetadataNode node = (IIOMetadataNode) list.item(i);
546: String name = node.getNodeName();
547:
548: if (format.isLeaf(name)) {
549: String s = (String) Box.getAttribute(node, "Length");
550: length += new Integer(s).intValue();
551: } else
552: length += computeLength(node);
553:
554: }
555:
556: return length
557: + (root.getNodeName().startsWith("JPEG2000") ? 8 : 0);
558: }
559:
560: private int generateSuperBoxContent(IIOMetadataNode root,
561: byte[] data, int pos) throws IOException {
562: String name = root.getNodeName();
563: if (name.startsWith("JPEG2000")) {
564: int length = computeLength(root);
565: Box.copyInt(data, pos, length);
566: pos += 4;
567: int type = Box.getTypeInt((String) Box.getTypeByName(name));
568: Box.copyInt(data, pos, type);
569: pos += 4;
570: }
571:
572: NodeList list = root.getChildNodes();
573: for (int i = 0; i < list.getLength(); i++) {
574: IIOMetadataNode node = (IIOMetadataNode) list.item(i);
575: name = node.getNodeName();
576:
577: if (format.isLeaf(name)) {
578: int type = Box.getTypeInt((String) Box.getAttribute(
579: node, "Type"));
580: Box box = Box.createBox(type, node);
581: byte[] data1 = box.getContent();
582: Box.copyInt(data, pos, data1.length + 8);
583: pos += 4;
584:
585: Box.copyInt(data, pos, type);
586: pos += 4;
587: System.arraycopy(data1, 0, data, pos, data1.length);
588: pos += data1.length;
589: } else {
590: pos = generateSuperBoxContent(node, data, pos);
591: }
592: }
593:
594: return pos;
595: }
596:
597: private Raster getTile(int tileX, int tileY) {
598: int sx = tileXOffset + tileX * tileWidth;
599: int sy = tileYOffset + tileY * tileHeight;
600: Rectangle bounds = new Rectangle(sx, sy, tileWidth, tileHeight);
601:
602: if (writeRaster) {
603: bounds = bounds.intersection(destinationRegion);
604: if (noTransform) {
605: return inputRaster.createChild(bounds.x, bounds.y,
606: bounds.width, bounds.height, bounds.x,
607: bounds.y, sourceBands);
608: }
609:
610: sx = bounds.x;
611: sy = bounds.y;
612: WritableRaster ras = Raster.createWritableRaster(
613: sampleModel, new Point(sx, sy));
614:
615: int x = mapToSourceX(sx);
616: int y = mapToSourceY(sy);
617:
618: int minY = inputRaster.getMinY();
619: int maxY = inputRaster.getMinY() + inputRaster.getHeight();
620:
621: int cTileWidth = bounds.width;
622:
623: int length = (cTileWidth - 1) * scaleX + 1;
624:
625: for (int j = 0; j < bounds.height; j++, sy++, y += scaleY) {
626: if (y < minY || y >= maxY)
627: continue;
628: Raster source = inputRaster.createChild(x, y, length,
629: 1, x, y, null);
630: int tempX = sx;
631: for (int i = 0, offset = x; i < cTileWidth; i++, tempX++, offset += scaleX) {
632: for (int k = 0; k < numComp; k++) {
633: int p = source.getSample(offset, y,
634: sourceBands[k]);
635: ras.setSample(tempX, sy, k, p);
636: }
637: }
638: }
639:
640: return ras;
641:
642: } else {
643: if (noTransform) {
644: Raster ras = input.getTile(tileX, tileY);
645: if (destinationRegion.contains(bounds) && noSubband)
646: return ras;
647: else {
648: bounds = bounds.intersection(destinationRegion);
649: return ras.createChild(bounds.x, bounds.y,
650: bounds.width, bounds.height, bounds.x,
651: bounds.y, sourceBands);
652: }
653: }
654:
655: bounds = bounds.intersection(destinationRegion);
656: sx = bounds.x;
657: sy = bounds.y;
658:
659: WritableRaster ras = Raster.createWritableRaster(
660: sampleModel, new Point(sx, sy));
661:
662: int x = mapToSourceX(sx);
663: int y = mapToSourceY(sy);
664:
665: int minY = input.getMinY();
666: int maxY = input.getMinY() + input.getHeight();
667:
668: int cTileWidth = bounds.width;
669: int length = (cTileWidth - 1) * scaleX + 1;
670:
671: for (int j = 0; j < bounds.height; j++, sy++, y += scaleY) {
672: if (y < minY || y >= maxY)
673: continue;
674:
675: Raster source = input.getData(new Rectangle(x, y,
676: length, 1));
677:
678: int tempX = sx;
679: for (int i = 0, offset = x; i < cTileWidth; i++, tempX++, offset += scaleX) {
680: for (int k = 0; k < numComp; k++) {
681: int p = source.getSample(offset, y,
682: sourceBands[k]);
683: ras.setSample(tempX, sy, k, p);
684: }
685: }
686: }
687: return ras;
688: }
689: }
690:
691: private int mapToSourceX(int x) {
692: return x * scaleX + xOffset;
693: }
694:
695: private int mapToSourceY(int y) {
696: return y * scaleY + yOffset;
697: }
698:
699: private int getMinTileX() {
700: return ToTile(destinationRegion.x, tileXOffset, tileWidth);
701: }
702:
703: private int getMaxTileX() {
704: return ToTile(
705: destinationRegion.x + destinationRegion.width - 1,
706: tileXOffset, tileWidth);
707: }
708:
709: private int getMinTileY() {
710: return ToTile(destinationRegion.y, tileYOffset, tileHeight);
711: }
712:
713: private int getMaxTileY() {
714: return ToTile(destinationRegion.y + destinationRegion.height
715: - 1, tileYOffset, tileHeight);
716: }
717:
718: private static int ToTile(int pos, int tileOffset, int tileSize) {
719: pos -= tileOffset;
720: if (pos < 0) {
721: pos += 1 - tileSize; // force round to -infinity (ceiling)
722: }
723: return pos / tileSize;
724: }
725:
726: private void setSize() {
727: size = new Size();
728: size.csize = numComp;
729: size.nxtiles = getMaxTileX() - getMinTileX() + 1;
730: size.nytiles = getMaxTileY() - getMinTileY() + 1;
731:
732: size.xosize = destinationRegion.x;
733: size.yosize = destinationRegion.y;
734: size.xsize = destinationRegion.width + destinationRegion.x;
735: size.ysize = destinationRegion.height + destinationRegion.y;
736: size.xtosize = tileXOffset;
737: size.ytosize = tileYOffset;
738: size.xtsize = tileWidth;
739: size.ytsize = tileHeight;
740:
741: encoder.setSize(size);
742: }
743:
744: private void setCompParameters(ColorModel colorModel,
745: SampleModel sampleModel, ImageWriteParam compParamArg) {
746: // Check the parameters.
747: if (colorModel == null
748: && sampleModel == null
749: && (compParamArg == null || !(compParamArg instanceof J2KImageWriteParam))) {
750: return;
751: }
752:
753: // Get the bit depths.
754: int[] bitDepths = null;
755: boolean isSigned = false;
756: if (colorModel != null) {
757: bitDepths = colorModel.getComponentSize();
758: isSigned = colorModel.getTransferType() == DataBuffer.TYPE_SHORT;
759: } else if (sampleModel != null) {
760: bitDepths = sampleModel.getSampleSize();
761: isSigned = sampleModel.getDataType() == DataBuffer.TYPE_SHORT;
762: }
763:
764: // Get the number of decomposition levels.
765: int numDecompositionLevels = -1;
766: if (compParamArg != null) {
767: // Cast is safe due to parameter check above.
768: numDecompositionLevels = ((J2KImageWriteParam) compParamArg)
769: .getNumDecompositionLevels();
770: }
771:
772: // Return if nothing to set.
773: if (bitDepths == null && numDecompositionLevels == -1)
774: return;
775:
776: // Check for unequal bit depths.
777: boolean bitDepthVaries = false;
778: if (bitDepths != null) {
779: for (int i = 1; i < bitDepths.length; i++) {
780: if (bitDepths[i] != bitDepths[0]) {
781: bitDepthVaries = true;
782: break;
783: }
784: }
785: }
786:
787: CompParams cp = encoder.getCompParams(null, -1);
788:
789: // Update the COD segment if needed.
790: if ((numDecompositionLevels != -1 && numDecompositionLevels != cp.maxlvls)
791: || (bitDepths != null && ((isSigned ? 0x80 : 0x00) | (bitDepths[0] - 1)) != cp.depth)) {
792:
793: if (numDecompositionLevels != -1) {
794: cp.maxlvls = numDecompositionLevels;
795: }
796:
797: // Set the main COD bit depth to bitDepths[0].
798: if (bitDepths != null) {
799: cp.depth = (isSigned ? 0x80 : 0x00)
800: | (bitDepths[0] - 1);
801: }
802:
803: encoder.setCompParams(cp, -1);
804: }
805:
806: // Update COC segments if needed.
807: if (bitDepthVaries) { // only true if bitDepths != null
808: // Loop over component zero even though unnecessary.
809: for (int i = 0; i < numComp; i++) {
810: cp = encoder.getCompParams(null, i);
811:
812: if (numDecompositionLevels != -1) {
813: cp.maxlvls = numDecompositionLevels;
814: }
815:
816: cp.depth = (isSigned ? 0x80 : 0x00)
817: | (bitDepths[i] - 1);
818:
819: encoder.setCompParams(cp, i);
820: }
821: }
822: }
823:
824: private void setParameters(ImageWriteParam paramArg) {
825: if (paramArg == null
826: || !(paramArg instanceof J2KImageWriteParam)) {
827: return;
828: }
829:
830: J2KImageWriteParam param = (J2KImageWriteParam) paramArg;
831:
832: // set the rate
833: double rate = param.getEncodingRate();
834: if (rate != Double.MAX_VALUE) {
835: // convert the rate to the medialib definition
836: rate /= ImageUtil.getElementSize(sampleModel);
837: encoder.setRate(rate, 0);
838: } else
839: encoder.setRate(0.0, 0);
840:
841: Params params = new Params();
842:
843: // set the component transformation flag
844: params.enablemct = param.getComponentTransformation() ? Constants.JP2K_MCT_ENABLE
845: : Constants.JP2K_MCT_DISABLE;
846:
847: // set coding style
848: if (param.getEPH())
849: params.cstyle |= Constants.JP2K_COD_EPH;
850: if (param.getSOP())
851: params.cstyle |= Constants.JP2K_COD_SOP;
852:
853: // set the wavelet filter type
854: if (J2KImageWriteParam.FILTER_53.equals(param.getFilter()))
855: params.wavemode = Constants.JP2K_WAVEMODE_53;
856: else if (J2KImageWriteParam.FILTER_97.equals(param.getFilter()))
857: params.wavemode = Constants.JP2K_WAVEMODE_97;
858:
859: //Set the progressive mode
860: String progressiveType = param.getProgressionType();
861:
862: if ("layer".equals(progressiveType))
863: params.prgorder = Constants.JP2K_COD_LRCPPRG;
864: if ("res".equals(progressiveType))
865: params.prgorder = Constants.JP2K_COD_RLCPPRG;
866: if ("res-pos".equals(progressiveType))
867: params.prgorder = Constants.JP2K_COD_RPCLPRG;
868: if ("pos-comp".equals(progressiveType))
869: params.prgorder = Constants.JP2K_COD_PCRLPRG;
870: if ("comp-pos".equals(progressiveType))
871: params.prgorder = Constants.JP2K_COD_CPRLPRG;
872:
873: encoder.setParams(params);
874: }
875: }
|