001: /*
002: * $RCSfile: TIFFT4Compressor.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.2 $
042: * $Date: 2006/04/11 22:10:37 $
043: * $State: Exp $
044: */
045: package com.sun.media.imageioimpl.plugins.tiff;
046:
047: import com.sun.media.imageio.plugins.tiff.BaselineTIFFTagSet;
048: import com.sun.media.imageio.plugins.tiff.TIFFCompressor;
049: import com.sun.media.imageio.plugins.tiff.TIFFField;
050: import com.sun.media.imageio.plugins.tiff.TIFFTag;
051: import java.io.IOException;
052: import javax.imageio.IIOException;
053: import javax.imageio.metadata.IIOMetadata;
054:
055: /**
056: *
057: */
058: public class TIFFT4Compressor extends TIFFFaxCompressor {
059:
060: private boolean is1DMode = false;
061: private boolean isEOLAligned = false;
062:
063: public TIFFT4Compressor() {
064: super ("CCITT T.4", BaselineTIFFTagSet.COMPRESSION_CCITT_T_4,
065: true);
066: }
067:
068: /**
069: * Sets the value of the <code>metadata</code> field.
070: *
071: * <p> The implementation in this class also sets local options
072: * from the T4_OPTIONS field if it exists, and if it doesn't, adds
073: * it with default values.</p>
074: *
075: * @param metadata the <code>IIOMetadata</code> object for the
076: * image being written.
077: *
078: * @see #getMetadata()
079: */
080: public void setMetadata(IIOMetadata metadata) {
081: super .setMetadata(metadata);
082:
083: if (metadata instanceof TIFFImageMetadata) {
084: TIFFImageMetadata tim = (TIFFImageMetadata) metadata;
085: TIFFField f = tim
086: .getTIFFField(BaselineTIFFTagSet.TAG_T4_OPTIONS);
087: if (f != null) {
088: int options = f.getAsInt(0);
089: is1DMode = (options & 0x1) == 0;
090: isEOLAligned = (options & 0x4) == 0x4;
091: } else {
092: long[] oarray = new long[1];
093: oarray[0] = (isEOLAligned ? 0x4 : 0x0)
094: | (is1DMode ? 0x0 : 0x1);
095:
096: BaselineTIFFTagSet base = BaselineTIFFTagSet
097: .getInstance();
098: TIFFField T4Options = new TIFFField(base
099: .getTag(BaselineTIFFTagSet.TAG_T4_OPTIONS),
100: TIFFTag.TIFF_LONG, 1, oarray);
101: tim.rootIFD.addTIFFField(T4Options);
102: }
103: }
104: }
105:
106: /**
107: * Encode a buffer of data using CCITT T.4 Compression also known as
108: * Group 3 facsimile compression.
109: *
110: * @param is1DMode Whether to perform one-dimensional encoding.
111: * @param isEOLAligned Whether EOL bit sequences should be padded.
112: * @param data The row of data to compress.
113: * @param lineStride Byte step between the same sample in different rows.
114: * @param colOffset Bit offset within first <code>data[rowOffset]</code>.
115: * @param width Number of bits in the row.
116: * @param height Number of rows in the buffer.
117: * @param compData The compressed data.
118: *
119: * @return The number of bytes saved in the compressed data array.
120: */
121: public int encodeT4(boolean is1DMode, boolean isEOLAligned,
122: byte[] data, int lineStride, int colOffset, int width,
123: int height, byte[] compData) {
124: //
125: // ao, a1, a2 are bit indices in the current line
126: // b1 and b2 are bit indices in the reference line (line above)
127: // color is the current color (WHITE or BLACK)
128: //
129: byte[] refData = data;
130: int lineAddr = 0;
131: int outIndex = 0;
132:
133: initBitBuf();
134:
135: int KParameter = 2;
136: for (int numRows = 0; numRows < height; numRows++) {
137: if (is1DMode || (numRows % KParameter) == 0) { // 1D encoding
138: // Write EOL+1
139: outIndex += addEOL(is1DMode, isEOLAligned, true,
140: compData, outIndex);
141:
142: // Encode row
143: outIndex += encode1D(data, lineAddr, colOffset, width,
144: compData, outIndex);
145: } else { // 2D encoding.
146: // Write EOL+0
147: outIndex += addEOL(is1DMode, isEOLAligned, false,
148: compData, outIndex);
149:
150: // Set reference to previous line
151: int refAddr = lineAddr - lineStride;
152:
153: // Encode row
154: int a0 = colOffset;
155: int last = a0 + width;
156:
157: int testbit = ((data[lineAddr + (a0 >>> 3)] & 0xff) >>> (7 - (a0 & 0x7))) & 0x1;
158: int a1 = testbit != 0 ? a0 : nextState(data, lineAddr,
159: a0, last);
160:
161: testbit = ((refData[refAddr + (a0 >>> 3)] & 0xff) >>> (7 - (a0 & 0x7))) & 0x1;
162: int b1 = testbit != 0 ? a0 : nextState(refData,
163: refAddr, a0, last);
164:
165: // The current color is set to WHITE at line start
166: int color = WHITE;
167:
168: while (true) {
169: int b2 = nextState(refData, refAddr, b1, last);
170: if (b2 < a1) { // pass mode
171: outIndex += add2DBits(compData, outIndex, pass,
172: 0);
173: a0 = b2;
174: } else {
175: int tmp = b1 - a1 + 3;
176: if ((tmp <= 6) && (tmp >= 0)) { // vertical mode
177: outIndex += add2DBits(compData, outIndex,
178: vert, tmp);
179: a0 = a1;
180: } else { // horizontal mode
181: int a2 = nextState(data, lineAddr, a1, last);
182: outIndex += add2DBits(compData, outIndex,
183: horz, 0);
184: outIndex += add1DBits(compData, outIndex,
185: a1 - a0, color);
186: outIndex += add1DBits(compData, outIndex,
187: a2 - a1, color ^ 1);
188: a0 = a2;
189: }
190: }
191: if (a0 >= last) {
192: break;
193: }
194: color = ((data[lineAddr + (a0 >>> 3)] & 0xff) >>> (7 - (a0 & 0x7))) & 0x1;
195: a1 = nextState(data, lineAddr, a0, last);
196: b1 = nextState(refData, refAddr, a0, last);
197: testbit = ((refData[refAddr + (b1 >>> 3)] & 0xff) >>> (7 - (b1 & 0x7))) & 0x1;
198: if (testbit == color) {
199: b1 = nextState(refData, refAddr, b1, last);
200: }
201: }
202: }
203:
204: // Skip to next line.
205: lineAddr += lineStride;
206: }
207:
208: for (int i = 0; i < 6; i++) {
209: outIndex += addEOL(is1DMode, isEOLAligned, true, compData,
210: outIndex);
211: }
212:
213: //
214: // flush all pending bits
215: //
216: while (ndex > 0) {
217: compData[outIndex++] = (byte) (bits >>> 24);
218: bits <<= 8;
219: ndex -= 8;
220: }
221:
222: // Flip the bytes if inverse fill was requested.
223: if (inverseFill) {
224: for (int i = 0; i < outIndex; i++) {
225: compData[i] = TIFFFaxDecompressor.flipTable[compData[i] & 0xff];
226: }
227: }
228:
229: return outIndex;
230: }
231:
232: public int encode(byte[] b, int off, int width, int height,
233: int[] bitsPerSample, int scanlineStride) throws IOException {
234: if (bitsPerSample.length != 1 || bitsPerSample[0] != 1) {
235: throw new IIOException(
236: "Bits per sample must be 1 for T4 compression!");
237: }
238:
239: // This initial buffer size is based on an alternating 1-0
240: // pattern generating the most bits when converted to code
241: // words: 9 bits out for each pair of bits in. So the number
242: // of bit pairs is determined, multiplied by 9, converted to
243: // bytes, and a ceil() is taken to account for fill bits at the
244: // end of each line. The "2" addend accounts for the case
245: // of the pattern beginning with black. The buffer is intended
246: // to hold only a single row.
247:
248: int maxBits = 9 * ((width + 1) / 2) + 2;
249: int bufSize = (maxBits + 7) / 8;
250:
251: // Calculate the maximum row as the G3-1D size plus the EOL,
252: // multiply this by the number of rows in the tile, and add
253: // 6 EOLs for the RTC (return to control).
254: bufSize = height * (bufSize + 2) + 12;
255:
256: byte[] compData = new byte[bufSize];
257:
258: int bytes = encodeT4(is1DMode, isEOLAligned, b, scanlineStride,
259: 8 * off, width, height, compData);
260:
261: stream.write(compData, 0, bytes);
262: return bytes;
263: }
264: }
|