KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > ext > awt > image > codec > tiff > TIFFImageEncoder


1 /*
2
3    Copyright 2001-2003 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.ext.awt.image.codec.tiff;
19
20 import java.awt.Rectangle JavaDoc;
21 import java.awt.color.ColorSpace JavaDoc;
22 import java.awt.image.BufferedImage JavaDoc;
23 import java.awt.image.ColorModel JavaDoc;
24 import java.awt.image.ComponentSampleModel JavaDoc;
25 import java.awt.image.DataBuffer JavaDoc;
26 import java.awt.image.DataBufferByte JavaDoc;
27 import java.awt.image.IndexColorModel JavaDoc;
28 import java.awt.image.MultiPixelPackedSampleModel JavaDoc;
29 import java.awt.image.Raster JavaDoc;
30 import java.awt.image.RenderedImage JavaDoc;
31 import java.awt.image.SampleModel JavaDoc;
32 import java.awt.image.WritableRaster JavaDoc;
33 import java.io.ByteArrayOutputStream JavaDoc;
34 import java.io.File JavaDoc;
35 import java.io.FileInputStream JavaDoc;
36 import java.io.IOException JavaDoc;
37 import java.io.OutputStream JavaDoc;
38 import java.io.RandomAccessFile JavaDoc;
39 import java.util.ArrayList JavaDoc;
40 import java.util.Iterator JavaDoc;
41 import java.util.SortedSet JavaDoc;
42 import java.util.TreeSet JavaDoc;
43 import java.util.zip.Deflater JavaDoc;
44
45 import org.apache.batik.ext.awt.image.codec.ImageEncodeParam;
46 import org.apache.batik.ext.awt.image.codec.ImageEncoderImpl;
47 import org.apache.batik.ext.awt.image.codec.SeekableOutputStream;
48
49 import com.sun.image.codec.jpeg.JPEGEncodeParam;
50 import com.sun.image.codec.jpeg.JPEGQTable;
51
52 /**
53  * A baseline TIFF writer. The writer outputs TIFF images in either Bilevel,
54  * Greyscale, Palette color or Full Color modes.
55  *
56  */

57 public class TIFFImageEncoder extends ImageEncoderImpl {
58
59     // Image Types
60
private static final int TIFF_UNSUPPORTED = -1;
61     private static final int TIFF_BILEVEL_WHITE_IS_ZERO = 0;
62     private static final int TIFF_BILEVEL_BLACK_IS_ZERO = 1;
63     private static final int TIFF_GRAY = 2;
64     private static final int TIFF_PALETTE = 3;
65     private static final int TIFF_RGB = 4;
66     private static final int TIFF_CMYK = 5;
67     private static final int TIFF_YCBCR = 6;
68     private static final int TIFF_CIELAB = 7;
69     private static final int TIFF_GENERIC = 8;
70
71     // Compression types
72
private static final int COMP_NONE = 1;
73     private static final int COMP_JPEG_TTN2 = 7;
74     private static final int COMP_PACKBITS = 32773;
75     private static final int COMP_DEFLATE = 32946;
76
77     // Incidental tags
78
private static final int TIFF_JPEG_TABLES = 347;
79     private static final int TIFF_YCBCR_SUBSAMPLING = 530;
80     private static final int TIFF_YCBCR_POSITIONING = 531;
81     private static final int TIFF_REF_BLACK_WHITE = 532;
82
83     // ExtraSamples types
84
private static final int EXTRA_SAMPLE_UNSPECIFIED = 0;
85     private static final int EXTRA_SAMPLE_ASSOCIATED_ALPHA = 1;
86     private static final int EXTRA_SAMPLE_UNASSOCIATED_ALPHA = 2;
87
88     // Default values
89
private static final int DEFAULT_ROWS_PER_STRIP = 8;
90
91     public TIFFImageEncoder(OutputStream JavaDoc output, ImageEncodeParam param) {
92     super(output, param);
93     if (this.param == null) {
94         this.param = new TIFFEncodeParam();
95     }
96     }
97
98     /**
99      * Encodes a RenderedImage and writes the output to the
100      * OutputStream associated with this ImageEncoder.
101      */

102     public void encode(RenderedImage JavaDoc im) throws IOException JavaDoc {
103         // Write the file header (8 bytes).
104
writeFileHeader();
105
106         // Get the encoding parameters.
107
TIFFEncodeParam encodeParam = (TIFFEncodeParam)param;
108
109     Iterator JavaDoc iter = encodeParam.getExtraImages();
110     if(iter != null) {
111             int ifdOffset = 8;
112         RenderedImage JavaDoc nextImage = im;
113             TIFFEncodeParam nextParam = encodeParam;
114             boolean hasNext;
115             do {
116                 hasNext = iter.hasNext();
117                 ifdOffset = encode(nextImage, nextParam, ifdOffset, !hasNext);
118             if(hasNext) {
119                     Object JavaDoc obj = iter.next();
120                     if(obj instanceof RenderedImage JavaDoc) {
121                         nextImage = (RenderedImage JavaDoc)obj;
122                         nextParam = encodeParam;
123                     } else if(obj instanceof Object JavaDoc[]) {
124                         Object JavaDoc[] o = (Object JavaDoc[])obj;
125                         nextImage = (RenderedImage JavaDoc)o[0];
126                         nextParam = (TIFFEncodeParam)o[1];
127                     }
128             }
129             } while(hasNext);
130         } else {
131         encode(im, encodeParam, 8, true);
132         }
133     }
134
135     private int encode(RenderedImage JavaDoc im, TIFFEncodeParam encodeParam,
136                        int ifdOffset, boolean isLast) throws IOException JavaDoc {
137     // Currently all images are stored uncompressed.
138
int compression = encodeParam.getCompression();
139
140     // Get tiled output preference.
141
boolean isTiled = encodeParam.getWriteTiled();
142
143         // Set bounds.
144
int minX = im.getMinX();
145         int minY = im.getMinY();
146         int width = im.getWidth();
147         int height = im.getHeight();
148
149         // Get SampleModel.
150
SampleModel JavaDoc sampleModel = im.getSampleModel();
151
152         // Retrieve and verify sample size.
153
int sampleSize[] = sampleModel.getSampleSize();
154         for(int i = 1; i < sampleSize.length; i++) {
155             if(sampleSize[i] != sampleSize[0]) {
156                 throw new Error JavaDoc("TIFFImageEncoder0");
157             }
158         }
159
160         // Check low bit limits.
161
int numBands = sampleModel.getNumBands();
162         if((sampleSize[0] == 1 || sampleSize[0] == 4) && numBands != 1) {
163             throw new Error JavaDoc("TIFFImageEncoder1");
164         }
165
166         // Retrieve and verify data type.
167
int dataType = sampleModel.getDataType();
168         switch(dataType) {
169         case DataBuffer.TYPE_BYTE:
170             if(sampleSize[0] != 1 && sampleSize[0] == 4 &&
171                sampleSize[0] != 8) {
172                 throw new Error JavaDoc("TIFFImageEncoder2");
173             }
174             break;
175         case DataBuffer.TYPE_SHORT:
176         case DataBuffer.TYPE_USHORT:
177             if(sampleSize[0] != 16) {
178                 throw new Error JavaDoc("TIFFImageEncoder3");
179             }
180             break;
181         case DataBuffer.TYPE_INT:
182         case DataBuffer.TYPE_FLOAT:
183             if(sampleSize[0] != 32) {
184                 throw new Error JavaDoc("TIFFImageEncoder4");
185             }
186             break;
187         default:
188         throw new Error JavaDoc("TIFFImageEncoder5");
189     }
190
191         boolean dataTypeIsShort =
192             dataType == DataBuffer.TYPE_SHORT ||
193             dataType == DataBuffer.TYPE_USHORT;
194
195     ColorModel JavaDoc colorModel = im.getColorModel();
196         if (colorModel != null &&
197             colorModel instanceof IndexColorModel JavaDoc &&
198             dataType != DataBuffer.TYPE_BYTE) {
199             // Don't support (unsigned) short palette-color images.
200
throw new Error JavaDoc("TIFFImageEncoder6");
201         }
202     IndexColorModel JavaDoc icm = null;
203     int sizeOfColormap = 0;
204         char colormap[] = null;
205
206         // Set image type.
207
int imageType = TIFF_UNSUPPORTED;
208         int numExtraSamples = 0;
209         int extraSampleType = EXTRA_SAMPLE_UNSPECIFIED;
210         if(colorModel instanceof IndexColorModel JavaDoc) { // Bilevel or palette
211
icm = (IndexColorModel JavaDoc)colorModel;
212             int mapSize = icm.getMapSize();
213
214             if(sampleSize[0] == 1 && numBands == 1) { // Bilevel image
215

216         if (mapSize != 2) {
217             throw new IllegalArgumentException JavaDoc(
218                     "TIFFImageEncoder7");
219         }
220
221         byte r[] = new byte[mapSize];
222         icm.getReds(r);
223         byte g[] = new byte[mapSize];
224         icm.getGreens(g);
225         byte b[] = new byte[mapSize];
226         icm.getBlues(b);
227
228         if ((r[0] & 0xff) == 0 &&
229             (r[1] & 0xff) == 255 &&
230             (g[0] & 0xff) == 0 &&
231             (g[1] & 0xff) == 255 &&
232             (b[0] & 0xff) == 0 &&
233             (b[1] & 0xff) == 255) {
234
235             imageType = TIFF_BILEVEL_BLACK_IS_ZERO;
236
237         } else if ((r[0] & 0xff) == 255 &&
238                (r[1] & 0xff) == 0 &&
239                (g[0] & 0xff) == 255 &&
240                (g[1] & 0xff) == 0 &&
241                (b[0] & 0xff) == 255 &&
242                (b[1] & 0xff) == 0) {
243             
244             imageType = TIFF_BILEVEL_WHITE_IS_ZERO;
245
246         } else {
247             imageType = TIFF_PALETTE;
248         }
249
250         } else if(numBands == 1) { // Non-bilevel image.
251
// Palette color image.
252
imageType = TIFF_PALETTE;
253         }
254     } else if(colorModel == null) {
255
256             if(sampleSize[0] == 1 && numBands == 1) { // bilevel
257
imageType = TIFF_BILEVEL_BLACK_IS_ZERO;
258             } else { // generic image
259
imageType = TIFF_GENERIC;
260                 if(numBands > 1) {
261                     numExtraSamples = numBands - 1;
262                 }
263             }
264
265         } else { // colorModel is non-null but not an IndexColorModel
266
ColorSpace JavaDoc colorSpace = colorModel.getColorSpace();
267
268             switch(colorSpace.getType()) {
269             case ColorSpace.TYPE_CMYK:
270                 imageType = TIFF_CMYK;
271                 break;
272             case ColorSpace.TYPE_GRAY:
273                 imageType = TIFF_GRAY;
274                 break;
275             case ColorSpace.TYPE_Lab:
276                 imageType = TIFF_CIELAB;
277                 break;
278             case ColorSpace.TYPE_RGB:
279                 if(compression == COMP_JPEG_TTN2 &&
280                    encodeParam.getJPEGCompressRGBToYCbCr()) {
281                     imageType = TIFF_YCBCR;
282                 } else {
283                     imageType = TIFF_RGB;
284                 }
285                 break;
286             case ColorSpace.TYPE_YCbCr:
287                 imageType = TIFF_YCBCR;
288                 break;
289             default:
290                 imageType = TIFF_GENERIC; // generic
291
break;
292             }
293
294             if(imageType == TIFF_GENERIC) {
295                 numExtraSamples = numBands - 1;
296             } else if(numBands > 1) {
297                 numExtraSamples = numBands - colorSpace.getNumComponents();
298             }
299
300             if(numExtraSamples == 1 && colorModel.hasAlpha()) {
301                 extraSampleType = colorModel.isAlphaPremultiplied() ?
302                     EXTRA_SAMPLE_ASSOCIATED_ALPHA :
303                     EXTRA_SAMPLE_UNASSOCIATED_ALPHA;
304             }
305         }
306
307         if(imageType == TIFF_UNSUPPORTED) {
308             throw new Error JavaDoc("TIFFImageEncoder8");
309         }
310
311         // Check JPEG compatibility.
312
if(compression == COMP_JPEG_TTN2) {
313             if(imageType == TIFF_PALETTE) {
314                 throw new Error JavaDoc("TIFFImageEncoder11");
315             } else if(!(sampleSize[0] == 8 &&
316                         (imageType == TIFF_GRAY ||
317                          imageType == TIFF_RGB ||
318                          imageType == TIFF_YCBCR))) {
319                 throw new Error JavaDoc("TIFFImageEncoder9");
320             }
321         }
322     
323     int photometricInterpretation = -1;
324     switch (imageType) {
325
326     case TIFF_BILEVEL_WHITE_IS_ZERO:
327         photometricInterpretation = 0;
328         break;
329
330     case TIFF_BILEVEL_BLACK_IS_ZERO:
331         photometricInterpretation = 1;
332         break;
333
334     case TIFF_GRAY:
335         case TIFF_GENERIC:
336         // Since the CS_GRAY colorspace is always of type black_is_zero
337
photometricInterpretation = 1;
338         break;
339
340     case TIFF_PALETTE:
341         photometricInterpretation = 3;
342
343         icm = (IndexColorModel JavaDoc)colorModel;
344         sizeOfColormap = icm.getMapSize();
345
346         byte r[] = new byte[sizeOfColormap];
347         icm.getReds(r);
348         byte g[] = new byte[sizeOfColormap];
349         icm.getGreens(g);
350         byte b[] = new byte[sizeOfColormap];
351         icm.getBlues(b);
352
353         int redIndex = 0, greenIndex = sizeOfColormap;
354         int blueIndex = 2 * sizeOfColormap;
355             colormap = new char[sizeOfColormap * 3];
356         for (int i=0; i<sizeOfColormap; i++) {
357                 colormap[redIndex++] = (char)(((r[i] << 8) | r[i]) & 0xffff);
358                 colormap[greenIndex++] = (char)(((g[i] << 8) | g[i]) & 0xffff);
359                 colormap[blueIndex++] = (char)(((b[i] << 8) | b[i]) & 0xffff);
360         }
361
362         sizeOfColormap *= 3;
363
364         break;
365
366     case TIFF_RGB:
367         photometricInterpretation = 2;
368         break;
369
370         case TIFF_CMYK:
371         photometricInterpretation = 5;
372             break;
373
374         case TIFF_YCBCR:
375         photometricInterpretation = 6;
376             break;
377
378         case TIFF_CIELAB:
379         photometricInterpretation = 8;
380             break;
381
382         default:
383             throw new Error JavaDoc("TIFFImageEncoder8");
384     }
385
386         // Initialize tile dimensions.
387
int tileWidth;
388         int tileHeight;
389         if(isTiled) {
390             tileWidth = encodeParam.getTileWidth() > 0 ?
391                 encodeParam.getTileWidth() : im.getTileWidth();
392             tileHeight = encodeParam.getTileHeight() > 0 ?
393                 encodeParam.getTileHeight() : im.getTileHeight();
394         } else {
395             tileWidth = width;
396             
397             tileHeight = encodeParam.getTileHeight() > 0 ?
398                 encodeParam.getTileHeight() : DEFAULT_ROWS_PER_STRIP;
399         }
400
401         // Re-tile for JPEG conformance if needed.
402
JPEGEncodeParam jep = null;
403         if(compression == COMP_JPEG_TTN2) {
404             // Get JPEGEncodeParam from encodeParam.
405
jep = encodeParam.getJPEGEncodeParam();
406
407             // Determine maximum subsampling.
408
int maxSubH = jep.getHorizontalSubsampling(0);
409             int maxSubV = jep.getVerticalSubsampling(0);
410             for(int i = 1; i < numBands; i++) {
411                 int subH = jep.getHorizontalSubsampling(i);
412                 if(subH > maxSubH) {
413                     maxSubH = subH;
414                 }
415                 int subV = jep.getVerticalSubsampling(i);
416                 if(subV > maxSubV) {
417                     maxSubV = subV;
418                 }
419             }
420
421             int factorV = 8*maxSubV;
422             tileHeight =
423                 (int)((float)tileHeight/(float)factorV + 0.5F)*factorV;
424             if(tileHeight < factorV) {
425                 tileHeight = factorV;
426             }
427
428             if(isTiled) {
429                 int factorH = 8*maxSubH;
430                 tileWidth =
431                     (int)((float)tileWidth/(float)factorH + 0.5F)*factorH;
432                 if(tileWidth < factorH) {
433                     tileWidth = factorH;
434                 }
435             }
436         }
437
438         int numTiles;
439         if(isTiled) {
440             // NB: Parentheses are used in this statement for correct rounding.
441
numTiles =
442                 ((width + tileWidth - 1)/tileWidth) *
443                 ((height + tileHeight - 1)/tileHeight);
444         } else {
445             numTiles = (int)Math.ceil((double)height/(double)tileHeight);
446         }
447
448     long tileByteCounts[] = new long[numTiles];
449
450     long bytesPerRow =
451             (long)Math.ceil((sampleSize[0] / 8.0) * tileWidth * numBands);
452
453     long bytesPerTile = bytesPerRow * tileHeight;
454
455     for (int i=0; i<numTiles; i++) {
456         tileByteCounts[i] = bytesPerTile;
457     }
458
459         if(!isTiled) {
460             // Last strip may have lesser rows
461
long lastStripRows = height - (tileHeight * (numTiles-1));
462             tileByteCounts[numTiles-1] = lastStripRows * bytesPerRow;
463         }
464
465     long totalBytesOfData = bytesPerTile * (numTiles - 1) +
466         tileByteCounts[numTiles-1];
467
468         // The data will be written after the IFD: create the array here
469
// but fill it in later.
470
long tileOffsets[] = new long[numTiles];
471
472     // Basic fields - have to be in increasing numerical order.
473
// ImageWidth 256
474
// ImageLength 257
475
// BitsPerSample 258
476
// Compression 259
477
// PhotoMetricInterpretation 262
478
// StripOffsets 273
479
// RowsPerStrip 278
480
// StripByteCounts 279
481
// XResolution 282
482
// YResolution 283
483
// ResolutionUnit 296
484

485     // Create Directory
486
SortedSet JavaDoc fields = new TreeSet JavaDoc();
487
488     // Image Width
489
fields.add(new TIFFField(TIFFImageDecoder.TIFF_IMAGE_WIDTH,
490                                  TIFFField.TIFF_LONG, 1,
491                                  new long[] {width}));
492
493     // Image Length
494
fields.add(new TIFFField(TIFFImageDecoder.TIFF_IMAGE_LENGTH,
495                                  TIFFField.TIFF_LONG, 1,
496                                  new long[] {height}));
497
498         char [] shortSampleSize = new char[numBands];
499         for (int i=0; i<numBands; i++)
500             shortSampleSize[i] = (char)sampleSize[i];
501     fields.add(new TIFFField(TIFFImageDecoder.TIFF_BITS_PER_SAMPLE,
502                                  TIFFField.TIFF_SHORT, numBands,
503                                  shortSampleSize));
504
505     fields.add(new TIFFField(TIFFImageDecoder.TIFF_COMPRESSION,
506                                  TIFFField.TIFF_SHORT, 1,
507                                  new char[] {(char)compression}));
508
509     fields.add(
510         new TIFFField(TIFFImageDecoder.TIFF_PHOTOMETRIC_INTERPRETATION,
511                           TIFFField.TIFF_SHORT, 1,
512                                  new char[] {(char)photometricInterpretation}));
513
514         if(!isTiled) {
515             fields.add(new TIFFField(TIFFImageDecoder.TIFF_STRIP_OFFSETS,
516                                      TIFFField.TIFF_LONG, numTiles,
517                                      tileOffsets));
518         }
519     
520     fields.add(new TIFFField(TIFFImageDecoder.TIFF_SAMPLES_PER_PIXEL,
521                                  TIFFField.TIFF_SHORT, 1,
522                                  new char[] {(char)numBands}));
523
524         if(!isTiled) {
525             fields.add(new TIFFField(TIFFImageDecoder.TIFF_ROWS_PER_STRIP,
526                                      TIFFField.TIFF_LONG, 1,
527                                      new long[] {tileHeight}));
528
529             fields.add(new TIFFField(TIFFImageDecoder.TIFF_STRIP_BYTE_COUNTS,
530                                      TIFFField.TIFF_LONG, numTiles,
531                                      tileByteCounts));
532         }
533
534     if (colormap != null) {
535         fields.add(new TIFFField(TIFFImageDecoder.TIFF_COLORMAP,
536                                      TIFFField.TIFF_SHORT, sizeOfColormap,
537                                      colormap));
538     }
539
540         if(isTiled) {
541             fields.add(new TIFFField(TIFFImageDecoder.TIFF_TILE_WIDTH,
542                                      TIFFField.TIFF_LONG, 1,
543                                      new long[] {tileWidth}));
544
545             fields.add(new TIFFField(TIFFImageDecoder.TIFF_TILE_LENGTH,
546                                      TIFFField.TIFF_LONG, 1,
547                                      new long[] {tileHeight}));
548
549             fields.add(new TIFFField(TIFFImageDecoder.TIFF_TILE_OFFSETS,
550                                      TIFFField.TIFF_LONG, numTiles,
551                                      tileOffsets));
552
553             fields.add(new TIFFField(TIFFImageDecoder.TIFF_TILE_BYTE_COUNTS,
554                                      TIFFField.TIFF_LONG, numTiles,
555                                      tileByteCounts));
556         }
557
558         if(numExtraSamples > 0) {
559             char[] extraSamples = new char[numExtraSamples];
560             for(int i = 0; i < numExtraSamples; i++) {
561                 extraSamples[i] = (char)extraSampleType;
562             }
563             fields.add(new TIFFField(TIFFImageDecoder.TIFF_EXTRA_SAMPLES,
564                                      TIFFField.TIFF_SHORT, numExtraSamples,
565                                      extraSamples));
566         }
567
568         // Data Sample Format Extension fields.
569
if(dataType != DataBuffer.TYPE_BYTE) {
570             // SampleFormat
571
char[] sampleFormat = new char[numBands];
572             if(dataType == DataBuffer.TYPE_FLOAT) {
573                 sampleFormat[0] = 3;
574             } else if(dataType == DataBuffer.TYPE_USHORT) {
575                 sampleFormat[0] = 1;
576             } else {
577                 sampleFormat[0] = 2;
578             }
579             for(int b = 1; b < numBands; b++) {
580                 sampleFormat[b] = sampleFormat[0];
581             }
582         fields.add(new TIFFField(TIFFImageDecoder.TIFF_SAMPLE_FORMAT,
583                                      TIFFField.TIFF_SHORT, numBands,
584                                      sampleFormat));
585
586             // NOTE: We don't bother setting the SMinSampleValue and
587
// SMaxSampleValue fields as these both default to the
588
// extrema of the respective data types. Probably we should
589
// check for the presence of the "extrema" property and
590
// use it if available.
591
}
592
593         // Initialize some JPEG variables.
594
com.sun.image.codec.jpeg.JPEGEncodeParam jpegEncodeParam = null;
595         com.sun.image.codec.jpeg.JPEGImageEncoder jpegEncoder = null;
596         int jpegColorID = 0;
597
598         if(compression == COMP_JPEG_TTN2) {
599
600             // Initialize JPEG color ID.
601
jpegColorID =
602                 com.sun.image.codec.jpeg.JPEGDecodeParam.COLOR_ID_UNKNOWN;
603             switch(imageType) {
604             case TIFF_GRAY:
605             case TIFF_PALETTE:
606                 jpegColorID =
607                     com.sun.image.codec.jpeg.JPEGDecodeParam.COLOR_ID_GRAY;
608                 break;
609             case TIFF_RGB:
610                 jpegColorID =
611                     com.sun.image.codec.jpeg.JPEGDecodeParam.COLOR_ID_RGB;
612                 break;
613             case TIFF_YCBCR:
614                 jpegColorID =
615                     com.sun.image.codec.jpeg.JPEGDecodeParam.COLOR_ID_YCbCr;
616                 break;
617             }
618
619             // Get the JDK encoding parameters.
620
Raster JavaDoc tile00 = im.getTile(0, 0);
621             jpegEncodeParam =
622                 com.sun.image.codec.jpeg.JPEGCodec.getDefaultJPEGEncodeParam(
623                     tile00, jpegColorID);
624
625             modifyEncodeParam(jep, jpegEncodeParam, numBands);
626
627             // Write an abbreviated tables-only stream to JPEGTables field.
628
jpegEncodeParam.setImageInfoValid(false);
629             jpegEncodeParam.setTableInfoValid(true);
630             ByteArrayOutputStream JavaDoc tableStream =
631               new ByteArrayOutputStream JavaDoc();
632             jpegEncoder =
633               com.sun.image.codec.jpeg.JPEGCodec.createJPEGEncoder(
634                 tableStream,
635                 jpegEncodeParam);
636             jpegEncoder.encode(tile00);
637             byte[] tableData = tableStream.toByteArray();
638             fields.add(new TIFFField(TIFF_JPEG_TABLES,
639                                      TIFFField.TIFF_UNDEFINED,
640                                      tableData.length,
641                                      tableData));
642
643             // Reset encoder so it's recreated below.
644
jpegEncoder = null;
645         }
646
647         if(imageType == TIFF_YCBCR) {
648             // YCbCrSubSampling: 2 is the default so we must write 1 as
649
// we do not (yet) do any subsampling.
650
char subsampleH = 1;
651             char subsampleV = 1;
652
653             // If JPEG, update values.
654
if(compression == COMP_JPEG_TTN2) {
655                 // Determine maximum subsampling.
656
subsampleH = (char)jep.getHorizontalSubsampling(0);
657                 subsampleV = (char)jep.getVerticalSubsampling(0);
658                 for(int i = 1; i < numBands; i++) {
659                     char subH = (char)jep.getHorizontalSubsampling(i);
660                     if(subH > subsampleH) {
661                         subsampleH = subH;
662                     }
663                     char subV = (char)jep.getVerticalSubsampling(i);
664                     if(subV > subsampleV) {
665                         subsampleV = subV;
666                     }
667                 }
668             }
669
670             fields.add(new TIFFField(TIFF_YCBCR_SUBSAMPLING,
671                                      TIFFField.TIFF_SHORT, 2,
672                                      new char[] {subsampleH, subsampleV}));
673
674
675             // YCbCr positioning.
676
fields.add(new TIFFField(TIFF_YCBCR_POSITIONING,
677                                      TIFFField.TIFF_SHORT, 1,
678                                      new int[] {compression == COMP_JPEG_TTN2 ?
679                                                 1 : 2}));
680
681             // Reference black/white.
682
long[][] refbw;
683             if(compression == COMP_JPEG_TTN2) {
684                 refbw =
685                     new long[][] { // no headroon/footroom
686
{0, 1}, {255, 1}, {128, 1}, {255, 1}, {128, 1}, {255, 1}
687                     };
688             } else {
689                 refbw =
690                     new long[][] { // CCIR 601.1 headroom/footroom (presumptive)
691
{15, 1}, {235, 1}, {128, 1}, {240, 1}, {128, 1}, {240, 1}
692                     };
693             }
694             fields.add(new TIFFField(TIFF_REF_BLACK_WHITE,
695                                      TIFFField.TIFF_RATIONAL, 6,
696                                      refbw));
697         }
698
699         // ---- No more automatically generated fields should be added
700
// after this point. ----
701

702         // Add extra fields specified via the encoding parameters.
703
TIFFField[] extraFields = encodeParam.getExtraFields();
704         if(extraFields != null) {
705             ArrayList JavaDoc extantTags = new ArrayList JavaDoc(fields.size());
706             Iterator JavaDoc fieldIter = fields.iterator();