1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.imaging.formats.tiff.write;
18
19 import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.DEFAULT_TIFF_BYTE_ORDER;
20 import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_CCITT_1D;
21 import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_CCITT_GROUP_3;
22 import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_CCITT_GROUP_4;
23 import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_LZW;
24 import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_PACKBITS;
25 import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_UNCOMPRESSED;
26 import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_COMPRESSION_DEFLATE_ADOBE;
27 import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_FLAG_T6_OPTIONS_UNCOMPRESSED_MODE;
28 import static org.apache.commons.imaging.formats.tiff.constants.TiffConstants.TIFF_HEADER_SIZE;
29
30 import java.awt.image.BufferedImage;
31 import java.awt.image.ColorModel;
32 import java.io.IOException;
33 import java.io.OutputStream;
34 import java.nio.ByteOrder;
35 import java.nio.charset.StandardCharsets;
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.List;
41 import java.util.Map;
42
43 import org.apache.commons.imaging.ImageWriteException;
44 import org.apache.commons.imaging.PixelDensity;
45 import org.apache.commons.imaging.common.BinaryOutputStream;
46 import org.apache.commons.imaging.common.PackBits;
47 import org.apache.commons.imaging.common.RationalNumber;
48 import org.apache.commons.imaging.common.itu_t4.T4AndT6Compression;
49 import org.apache.commons.imaging.common.mylzw.MyLzwCompressor;
50 import org.apache.commons.imaging.common.ZlibDeflate;
51 import org.apache.commons.imaging.formats.tiff.TiffElement;
52 import org.apache.commons.imaging.formats.tiff.TiffImageData;
53 import org.apache.commons.imaging.formats.tiff.TiffImagingParameters;
54 import org.apache.commons.imaging.formats.tiff.constants.ExifTagConstants;
55 import org.apache.commons.imaging.formats.tiff.constants.TiffDirectoryConstants;
56 import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants;
57
58 public abstract class TiffImageWriterBase {
59
60 protected final ByteOrder byteOrder;
61
62 public TiffImageWriterBase() {
63 this.byteOrder = DEFAULT_TIFF_BYTE_ORDER;
64 }
65
66 public TiffImageWriterBase(final ByteOrder byteOrder) {
67 this.byteOrder = byteOrder;
68 }
69
70 protected static int imageDataPaddingLength(final int dataLength) {
71 return (4 - (dataLength % 4)) % 4;
72 }
73
74 public abstract void write(OutputStream os, TiffOutputSet outputSet)
75 throws IOException, ImageWriteException;
76
77 protected TiffOutputSummary validateDirectories(final TiffOutputSet outputSet)
78 throws ImageWriteException {
79 final List<TiffOutputDirectory> directories = outputSet.getDirectories();
80
81 if (directories.isEmpty()) {
82 throw new ImageWriteException("No directories.");
83 }
84
85 TiffOutputDirectory exifDirectory = null;
86 TiffOutputDirectory gpsDirectory = null;
87 TiffOutputDirectory interoperabilityDirectory = null;
88 TiffOutputField exifDirectoryOffsetField = null;
89 TiffOutputField gpsDirectoryOffsetField = null;
90 TiffOutputField interoperabilityDirectoryOffsetField = null;
91
92 final List<Integer> directoryIndices = new ArrayList<>();
93 final Map<Integer, TiffOutputDirectory> directoryTypeMap = new HashMap<>();
94 for (final TiffOutputDirectory directory : directories) {
95 final int dirType = directory.type;
96 directoryTypeMap.put(dirType, directory);
97
98
99
100 if (dirType < 0) {
101 switch (dirType) {
102 case TiffDirectoryConstants.DIRECTORY_TYPE_EXIF:
103 if (exifDirectory != null) {
104 throw new ImageWriteException(
105 "More than one EXIF directory.");
106 }
107 exifDirectory = directory;
108 break;
109
110 case TiffDirectoryConstants.DIRECTORY_TYPE_GPS:
111 if (gpsDirectory != null) {
112 throw new ImageWriteException(
113 "More than one GPS directory.");
114 }
115 gpsDirectory = directory;
116 break;
117
118 case TiffDirectoryConstants.DIRECTORY_TYPE_INTEROPERABILITY:
119 if (interoperabilityDirectory != null) {
120 throw new ImageWriteException(
121 "More than one Interoperability directory.");
122 }
123 interoperabilityDirectory = directory;
124 break;
125 default:
126 throw new ImageWriteException("Unknown directory: "
127 + dirType);
128 }
129 } else {
130 if (directoryIndices.contains(dirType)) {
131 throw new ImageWriteException(
132 "More than one directory with index: " + dirType
133 + ".");
134 }
135 directoryIndices.add(dirType);
136
137 }
138
139 final HashSet<Integer> fieldTags = new HashSet<>();
140 final List<TiffOutputField> fields = directory.getFields();
141 for (final TiffOutputField field : fields) {
142 if (fieldTags.contains(field.tag)) {
143 throw new ImageWriteException("Tag ("
144 + field.tagInfo.getDescription()
145 + ") appears twice in directory.");
146 }
147 fieldTags.add(field.tag);
148
149 if (field.tag == ExifTagConstants.EXIF_TAG_EXIF_OFFSET.tag) {
150 if (exifDirectoryOffsetField != null) {
151 throw new ImageWriteException(
152 "More than one Exif directory offset field.");
153 }
154 exifDirectoryOffsetField = field;
155 } else if (field.tag == ExifTagConstants.EXIF_TAG_INTEROP_OFFSET.tag) {
156 if (interoperabilityDirectoryOffsetField != null) {
157 throw new ImageWriteException(
158 "More than one Interoperability directory offset field.");
159 }
160 interoperabilityDirectoryOffsetField = field;
161 } else if (field.tag == ExifTagConstants.EXIF_TAG_GPSINFO.tag) {
162 if (gpsDirectoryOffsetField != null) {
163 throw new ImageWriteException(
164 "More than one GPS directory offset field.");
165 }
166 gpsDirectoryOffsetField = field;
167 }
168 }
169
170 }
171
172 if (directoryIndices.isEmpty()) {
173 throw new ImageWriteException("Missing root directory.");
174 }
175
176
177
178 directoryIndices.sort(null);
179
180 TiffOutputDirectory previousDirectory = null;
181 for (int i = 0; i < directoryIndices.size(); i++) {
182 final Integer index = directoryIndices.get(i);
183 if (index != i) {
184 throw new ImageWriteException("Missing directory: " + i + ".");
185 }
186
187
188 final TiffOutputDirectory directory = directoryTypeMap.get(index);
189 if (null != previousDirectory) {
190 previousDirectory.setNextDirectory(directory);
191 }
192 previousDirectory = directory;
193 }
194
195 final TiffOutputDirectory rootDirectory = directoryTypeMap.get(
196 TiffDirectoryConstants.DIRECTORY_TYPE_ROOT);
197
198
199 final TiffOutputSummarymats/tiff/write/TiffOutputSummary.html#TiffOutputSummary">TiffOutputSummary result = new TiffOutputSummary(byteOrder,
200 rootDirectory, directoryTypeMap);
201
202 if (interoperabilityDirectory == null
203 && interoperabilityDirectoryOffsetField != null) {
204
205 throw new ImageWriteException(
206 "Output set has Interoperability Directory Offset field, but no Interoperability Directory");
207 }
208 if (interoperabilityDirectory != null) {
209 if (exifDirectory == null) {
210 exifDirectory = outputSet.addExifDirectory();
211 }
212
213 if (interoperabilityDirectoryOffsetField == null) {
214 interoperabilityDirectoryOffsetField =
215 TiffOutputField.createOffsetField(
216 ExifTagConstants.EXIF_TAG_INTEROP_OFFSET,
217 byteOrder);
218 exifDirectory.add(interoperabilityDirectoryOffsetField);
219 }
220
221 result.add(interoperabilityDirectory,
222 interoperabilityDirectoryOffsetField);
223 }
224
225
226 if (exifDirectory == null && exifDirectoryOffsetField != null) {
227
228 throw new ImageWriteException(
229 "Output set has Exif Directory Offset field, but no Exif Directory");
230 }
231 if (exifDirectory != null) {
232 if (exifDirectoryOffsetField == null) {
233 exifDirectoryOffsetField = TiffOutputField.createOffsetField(
234 ExifTagConstants.EXIF_TAG_EXIF_OFFSET, byteOrder);
235 rootDirectory.add(exifDirectoryOffsetField);
236 }
237
238 result.add(exifDirectory, exifDirectoryOffsetField);
239 }
240
241 if (gpsDirectory == null && gpsDirectoryOffsetField != null) {
242
243 throw new ImageWriteException(
244 "Output set has GPS Directory Offset field, but no GPS Directory");
245 }
246 if (gpsDirectory != null) {
247 if (gpsDirectoryOffsetField == null) {
248 gpsDirectoryOffsetField = TiffOutputField.createOffsetField(
249 ExifTagConstants.EXIF_TAG_GPSINFO, byteOrder);
250 rootDirectory.add(gpsDirectoryOffsetField);
251 }
252
253 result.add(gpsDirectory, gpsDirectoryOffsetField);
254 }
255
256 return result;
257
258
259 }
260
261 private static final int MAX_PIXELS_FOR_RGB = 1024*1024;
262
263
264
265
266
267 private boolean checkForActualAlpha(final BufferedImage src){
268
269
270 final int width = src.getWidth();
271 final int height = src.getHeight();
272 int nRowsPerRead = MAX_PIXELS_FOR_RGB/width;
273 if(nRowsPerRead<1){
274 nRowsPerRead = 1;
275 }
276 final int nReads = (height+nRowsPerRead-1)/nRowsPerRead;
277 final int []argb = new int[nRowsPerRead*width];
278 for(int iRead=0; iRead<nReads; iRead++){
279 final int i0 = iRead*nRowsPerRead;
280 final int i1 = i0+nRowsPerRead>height? height: i0+nRowsPerRead;
281 src.getRGB(0, i0, width, i1-i0, argb, 0, width);
282 final int n = (i1-i0)*width;
283 for(int i=0; i<n; i++){
284 if((argb[i]&0xff000000)!=0xff000000){
285 return true;
286 }
287 }
288 }
289 return false;
290 }
291
292 private void applyPredictor(final int width, final int bytesPerSample, final byte[] b) {
293 final int nBytesPerRow = bytesPerSample * width;
294 final int nRows = b.length / nBytesPerRow;
295 for (int iRow = 0; iRow < nRows; iRow++) {
296 final int offset = iRow * nBytesPerRow;
297 for (int i = nBytesPerRow-1; i >= bytesPerSample; i--) {
298 b[offset + i] -= b[offset + i - bytesPerSample];
299 }
300 }
301 }
302
303 public void writeImage(final BufferedImage src, final OutputStream os, TiffImagingParameters params)
304 throws ImageWriteException, IOException {
305 TiffOutputSet userExif = params.getOutputSet();
306
307 String xmpXml = params.getXmpXml();
308
309 PixelDensity pixelDensity = params.getPixelDensity();
310 if (pixelDensity == null) {
311 pixelDensity = PixelDensity.createFromPixelsPerInch(72, 72);
312 }
313
314 final int width = src.getWidth();
315 final int height = src.getHeight();
316
317
318
319
320
321
322
323
324
325
326 final ColorModel cModel = src.getColorModel();
327 final boolean hasAlpha = cModel.hasAlpha() && checkForActualAlpha(src);
328
329
330
331
332
333
334
335
336
337
338 int compression = TIFF_COMPRESSION_LZW;
339 short predictor = TiffTagConstants.PREDICTOR_VALUE_NONE;
340
341 int stripSizeInBits = 64000;
342 Integer compressionParameter = params.getCompression();
343 if (compressionParameter != null) {
344 compression = compressionParameter;
345 final Integer stripSizeInBytes = params.getLzwCompressionBlockSize();
346 if (stripSizeInBytes != null) {
347 if (stripSizeInBytes < 8000) {
348 throw new ImageWriteException(
349 "Block size parameter " + stripSizeInBytes
350 + " is less than 8000 minimum");
351 }
352 stripSizeInBits = stripSizeInBytes * 8;
353 }
354 }
355
356 int samplesPerPixel;
357 int bitsPerSample;
358 int photometricInterpretation;
359 if (compression == TIFF_COMPRESSION_CCITT_1D
360 || compression == TIFF_COMPRESSION_CCITT_GROUP_3
361 || compression == TIFF_COMPRESSION_CCITT_GROUP_4) {
362 samplesPerPixel = 1;
363 bitsPerSample = 1;
364 photometricInterpretation = 0;
365 } else {
366 samplesPerPixel = hasAlpha? 4: 3;
367 bitsPerSample = 8;
368 photometricInterpretation = 2;
369 }
370
371 int rowsPerStrip = stripSizeInBits / (width * bitsPerSample * samplesPerPixel);
372 rowsPerStrip = Math.max(1, rowsPerStrip);
373
374 final byte[][] strips = getStrips(src, samplesPerPixel, bitsPerSample, rowsPerStrip);
375
376
377
378
379
380
381
382 int t4Options = 0;
383 int t6Options = 0;
384 if (compression == TIFF_COMPRESSION_CCITT_1D) {
385 for (int i = 0; i < strips.length; i++) {
386 strips[i] = T4AndT6Compression.compressModifiedHuffman(
387 strips[i], width, strips[i].length / ((width + 7) / 8));
388 }
389 } else if (compression == TIFF_COMPRESSION_CCITT_GROUP_3) {
390 final Integer t4Parameter = params.getT4Options();
391 if (t4Parameter != null) {
392 t4Options = t4Parameter.intValue();
393 }
394 t4Options &= 0x7;
395 final boolean is2D = (t4Options & 1) != 0;
396 final boolean usesUncompressedMode = (t4Options & 2) != 0;
397 if (usesUncompressedMode) {
398 throw new ImageWriteException(
399 "T.4 compression with the uncompressed mode extension is not yet supported");
400 }
401 final boolean hasFillBitsBeforeEOL = (t4Options & 4) != 0;
402 for (int i = 0; i < strips.length; i++) {
403 if (is2D) {
404 strips[i] = T4AndT6Compression.compressT4_2D(strips[i],
405 width, strips[i].length / ((width + 7) / 8),
406 hasFillBitsBeforeEOL, rowsPerStrip);
407 } else {
408 strips[i] = T4AndT6Compression.compressT4_1D(strips[i],
409 width, strips[i].length / ((width + 7) / 8),
410 hasFillBitsBeforeEOL);
411 }
412 }
413 } else if (compression == TIFF_COMPRESSION_CCITT_GROUP_4) {
414 final Integer t6Parameter = params.getT6Options();
415 if (t6Parameter != null) {
416 t6Options = t6Parameter.intValue();
417 }
418 t6Options &= 0x4;
419 final boolean usesUncompressedMode = (t6Options & TIFF_FLAG_T6_OPTIONS_UNCOMPRESSED_MODE) != 0;
420 if (usesUncompressedMode) {
421 throw new ImageWriteException(
422 "T.6 compression with the uncompressed mode extension is not yet supported");
423 }
424 for (int i = 0; i < strips.length; i++) {
425 strips[i] = T4AndT6Compression.compressT6(strips[i], width,
426 strips[i].length / ((width + 7) / 8));
427 }
428 } else if (compression == TIFF_COMPRESSION_PACKBITS) {
429 for (int i = 0; i < strips.length; i++) {
430 strips[i] = new PackBits().compress(strips[i]);
431 }
432 } else if (compression == TIFF_COMPRESSION_LZW) {
433 predictor = TiffTagConstants.PREDICTOR_VALUE_HORIZONTAL_DIFFERENCING;
434 for (int i = 0; i < strips.length; i++) {
435 final byte[] uncompressed = strips[i];
436 this.applyPredictor(width, samplesPerPixel, strips[i]);
437
438 final int LZW_MINIMUM_CODE_SIZE = 8;
439 final MyLzwCompressorn/mylzw/MyLzwCompressor.html#MyLzwCompressor">MyLzwCompressor compressor = new MyLzwCompressor(
440 LZW_MINIMUM_CODE_SIZE, ByteOrder.BIG_ENDIAN, true);
441 final byte[] compressed = compressor.compress(uncompressed);
442 strips[i] = compressed;
443 }
444 } else if (compression == TIFF_COMPRESSION_DEFLATE_ADOBE) {
445 predictor = TiffTagConstants.PREDICTOR_VALUE_HORIZONTAL_DIFFERENCING;
446 for (int i = 0; i < strips.length; i++) {
447 this.applyPredictor(width, samplesPerPixel, strips[i]);
448 strips[i] = ZlibDeflate.compress(strips[i]);
449 }
450 } else if (compression == TIFF_COMPRESSION_UNCOMPRESSED) {
451
452 } else {
453 throw new ImageWriteException(
454 "Invalid compression parameter (Only CCITT 1D/Group 3/Group 4, LZW, Packbits, Zlib Deflate and uncompressed supported).");
455 }
456
457 final TiffElement.DataElement[] imageData = new TiffElement.DataElement[strips.length];
458 for (int i = 0; i < strips.length; i++) {
459 imageData[i] = new TiffImageData.Data(0, strips[i].length, strips[i]);
460 }
461
462 final TiffOutputSetrmats/tiff/write/TiffOutputSet.html#TiffOutputSet">TiffOutputSet outputSet = new TiffOutputSet(byteOrder);
463 final TiffOutputDirectory directory = outputSet.addRootDirectory();
464
465
466
467 {
468
469 directory.add(TiffTagConstants.TIFF_TAG_IMAGE_WIDTH, width);
470 directory.add(TiffTagConstants.TIFF_TAG_IMAGE_LENGTH, height);
471 directory.add(TiffTagConstants.TIFF_TAG_PHOTOMETRIC_INTERPRETATION,
472 (short) photometricInterpretation);
473 directory.add(TiffTagConstants.TIFF_TAG_COMPRESSION,
474 (short) compression);
475 directory.add(TiffTagConstants.TIFF_TAG_SAMPLES_PER_PIXEL,
476 (short) samplesPerPixel);
477
478 switch (samplesPerPixel) {
479 case 3:
480 directory.add(TiffTagConstants.TIFF_TAG_BITS_PER_SAMPLE,
481 (short) bitsPerSample, (short) bitsPerSample,
482 (short) bitsPerSample);
483 break;
484 case 4:
485 directory.add(TiffTagConstants.TIFF_TAG_BITS_PER_SAMPLE,
486 (short) bitsPerSample, (short) bitsPerSample,
487 (short) bitsPerSample, (short) bitsPerSample);
488 directory.add(TiffTagConstants.TIFF_TAG_EXTRA_SAMPLES,
489 (short)TiffTagConstants.EXTRA_SAMPLE_UNASSOCIATED_ALPHA);
490 break;
491 case 1:
492 directory.add(TiffTagConstants.TIFF_TAG_BITS_PER_SAMPLE,
493 (short) bitsPerSample);
494 break;
495 default:
496 break;
497 }
498
499
500
501
502
503
504
505
506
507
508
509
510
511 directory.add(TiffTagConstants.TIFF_TAG_ROWS_PER_STRIP,
512 rowsPerStrip);
513 if (pixelDensity.isUnitless()) {
514 directory.add(TiffTagConstants.TIFF_TAG_RESOLUTION_UNIT,
515 (short) 0);
516 directory.add(TiffTagConstants.TIFF_TAG_XRESOLUTION,
517 RationalNumber.valueOf(pixelDensity.getRawHorizontalDensity()));
518 directory.add(TiffTagConstants.TIFF_TAG_YRESOLUTION,
519 RationalNumber.valueOf(pixelDensity.getRawVerticalDensity()));
520 } else if (pixelDensity.isInInches()) {
521 directory.add(TiffTagConstants.TIFF_TAG_RESOLUTION_UNIT,
522 (short) 2);
523 directory.add(TiffTagConstants.TIFF_TAG_XRESOLUTION,
524 RationalNumber.valueOf(pixelDensity.horizontalDensityInches()));
525 directory.add(TiffTagConstants.TIFF_TAG_YRESOLUTION,
526 RationalNumber.valueOf(pixelDensity.verticalDensityInches()));
527 } else {
528 directory.add(TiffTagConstants.TIFF_TAG_RESOLUTION_UNIT,
529 (short) 1);
530 directory.add(TiffTagConstants.TIFF_TAG_XRESOLUTION,
531 RationalNumber.valueOf(pixelDensity.horizontalDensityCentimetres()));
532 directory.add(TiffTagConstants.TIFF_TAG_YRESOLUTION,
533 RationalNumber.valueOf(pixelDensity.verticalDensityCentimetres()));
534 }
535 if (t4Options != 0) {
536 directory.add(TiffTagConstants.TIFF_TAG_T4_OPTIONS, t4Options);
537 }
538 if (t6Options != 0) {
539 directory.add(TiffTagConstants.TIFF_TAG_T6_OPTIONS, t6Options);
540 }
541
542 if (null != xmpXml) {
543 final byte[] xmpXmlBytes = xmpXml.getBytes(StandardCharsets.UTF_8);
544 directory.add(TiffTagConstants.TIFF_TAG_XMP, xmpXmlBytes);
545 }
546
547 if(predictor==TiffTagConstants.PREDICTOR_VALUE_HORIZONTAL_DIFFERENCING){
548 directory.add(TiffTagConstants.TIFF_TAG_PREDICTOR, predictor);
549 }
550
551 }
552
553 final TiffImageData tiffImageData = new TiffImageData.Strips(imageData,
554 rowsPerStrip);
555 directory.setTiffImageData(tiffImageData);
556
557 if (userExif != null) {
558 combineUserExifIntoFinalExif(userExif, outputSet);
559 }
560
561 write(os, outputSet);
562 }
563
564 private void combineUserExifIntoFinalExif(final TiffOutputSet userExif,
565 final TiffOutputSet outputSet) throws ImageWriteException {
566 final List<TiffOutputDirectory> outputDirectories = outputSet.getDirectories();
567 outputDirectories.sort(TiffOutputDirectory.COMPARATOR);
568 for (final TiffOutputDirectory userDirectory : userExif.getDirectories()) {
569 final int location = Collections.binarySearch(outputDirectories,
570 userDirectory, TiffOutputDirectory.COMPARATOR);
571 if (location < 0) {
572 outputSet.addDirectory(userDirectory);
573 } else {
574 final TiffOutputDirectory outputDirectory = outputDirectories.get(location);
575 for (final TiffOutputField userField : userDirectory.getFields()) {
576 if (outputDirectory.findField(userField.tagInfo) == null) {
577 outputDirectory.add(userField);
578 }
579 }
580 }
581 }
582 }
583
584 private byte[][] getStrips(final BufferedImage src, final int samplesPerPixel,
585 final int bitsPerSample, final int rowsPerStrip) {
586 final int width = src.getWidth();
587 final int height = src.getHeight();
588
589 final int stripCount = (height + rowsPerStrip - 1) / rowsPerStrip;
590
591 byte[][] result;
592 {
593 result = new byte[stripCount][];
594
595 int remainingRows = height;
596
597 for (int i = 0; i < stripCount; i++) {
598 final int rowsInStrip = Math.min(rowsPerStrip, remainingRows);
599 remainingRows -= rowsInStrip;
600
601 final int bitsInRow = bitsPerSample * samplesPerPixel * width;
602 final int bytesPerRow = (bitsInRow + 7) / 8;
603 final int bytesInStrip = rowsInStrip * bytesPerRow;
604
605 final byte[] uncompressed = new byte[bytesInStrip];
606
607 int counter = 0;
608 int y = i * rowsPerStrip;
609 final int stop = i * rowsPerStrip + rowsPerStrip;
610
611 for (; (y < height) && (y < stop); y++) {
612 int bitCache = 0;
613 int bitsInCache = 0;
614 for (int x = 0; x < width; x++) {
615 final int rgb = src.getRGB(x, y);
616 final int red = 0xff & (rgb >> 16);
617 final int green = 0xff & (rgb >> 8);
618 final int blue = 0xff & (rgb >> 0);
619
620 if (bitsPerSample == 1) {
621 int sample = (red + green + blue) / 3;
622 if (sample > 127) {
623 sample = 0;
624 } else {
625 sample = 1;
626 }
627 bitCache <<= 1;
628 bitCache |= sample;
629 bitsInCache++;
630 if (bitsInCache == 8) {
631 uncompressed[counter++] = (byte) bitCache;
632 bitCache = 0;
633 bitsInCache = 0;
634 }
635 } else if(samplesPerPixel==4){
636 uncompressed[counter++] = (byte) red;
637 uncompressed[counter++] = (byte) green;
638 uncompressed[counter++] = (byte) blue;
639 uncompressed[counter++] = (byte) (rgb>>24);
640 }else {
641
642 uncompressed[counter++] = (byte) red;
643 uncompressed[counter++] = (byte) green;
644 uncompressed[counter++] = (byte) blue;
645 }
646 }
647 if (bitsInCache > 0) {
648 bitCache <<= (8 - bitsInCache);
649 uncompressed[counter++] = (byte) bitCache;
650 }
651 }
652
653 result[i] = uncompressed;
654 }
655
656 }
657
658 return result;
659 }
660
661 protected void writeImageFileHeader(final BinaryOutputStream bos)
662 throws IOException {
663 writeImageFileHeader(bos, TIFF_HEADER_SIZE);
664 }
665
666 protected void writeImageFileHeader(final BinaryOutputStream bos,
667 final long offsetToFirstIFD) throws IOException {
668 if (byteOrder == ByteOrder.LITTLE_ENDIAN) {
669 bos.write('I');
670 bos.write('I');
671 } else {
672 bos.write('M');
673 bos.write('M');
674 }
675
676 bos.write2Bytes(42);
677
678 bos.write4Bytes((int) offsetToFirstIFD);
679 }
680
681 }