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