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 java.io.IOException; 048 049import javax.imageio.IIOException; 050import javax.imageio.metadata.IIOMetadata; 051 052import com.github.jaiimageio.plugins.tiff.BaselineTIFFTagSet; 053import com.github.jaiimageio.plugins.tiff.TIFFField; 054import com.github.jaiimageio.plugins.tiff.TIFFTag; 055 056/** 057 * 058 */ 059public class TIFFT4Compressor extends TIFFFaxCompressor { 060 061 private boolean is1DMode = false; 062 private boolean isEOLAligned = false; 063 064 public TIFFT4Compressor() { 065 super("CCITT T.4", BaselineTIFFTagSet.COMPRESSION_CCITT_T_4, 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.getTIFFField(BaselineTIFFTagSet.TAG_T4_OPTIONS); 086 if (f != null) { 087 int options = f.getAsInt(0); 088 is1DMode = (options & 0x1) == 0; 089 isEOLAligned = (options & 0x4) == 0x4; 090 } else { 091 long[] oarray = new long[1]; 092 oarray[0] = (isEOLAligned ? 0x4 : 0x0) | 093 (is1DMode ? 0x0 : 0x1); 094 095 BaselineTIFFTagSet base = BaselineTIFFTagSet.getInstance(); 096 TIFFField T4Options = 097 new TIFFField(base.getTag(BaselineTIFFTagSet.TAG_T4_OPTIONS), 098 TIFFTag.TIFF_LONG, 099 1, 100 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, 122 boolean isEOLAligned, 123 byte[] data, 124 int lineStride, 125 int colOffset, 126 int width, 127 int height, 128 byte[] compData) 129 { 130 // 131 // ao, a1, a2 are bit indices in the current line 132 // b1 and b2 are bit indices in the reference line (line above) 133 // color is the current color (WHITE or BLACK) 134 // 135 byte[] refData = data; 136 int lineAddr = 0; 137 int outIndex = 0; 138 139 initBitBuf(); 140 141 int KParameter = 2; 142 for(int numRows = 0; numRows < height; numRows++) { 143 if(is1DMode || (numRows % KParameter) == 0) { // 1D encoding 144 // Write EOL+1 145 outIndex += addEOL(is1DMode, isEOLAligned, true, 146 compData, outIndex); 147 148 // Encode row 149 outIndex += encode1D(data, lineAddr, colOffset, width, 150 compData, outIndex); 151 } else { // 2D encoding. 152 // Write EOL+0 153 outIndex += addEOL(is1DMode, isEOLAligned, false, 154 compData, outIndex); 155 156 // Set reference to previous line 157 int refAddr = lineAddr - lineStride; 158 159 // Encode row 160 int a0 = colOffset; 161 int last = a0 + width; 162 163 int testbit = 164 ((data[lineAddr + (a0>>>3)]&0xff) >>> 165 (7-(a0 & 0x7))) & 0x1; 166 int a1 = testbit != 0 ? 167 a0 : nextState(data, lineAddr, a0, last); 168 169 testbit = ((refData[refAddr + (a0>>>3)]&0xff) >>> 170 (7-(a0 & 0x7))) & 0x1; 171 int b1 = testbit != 0 ? 172 a0 : nextState(refData, refAddr, a0, last); 173 174 // The current color is set to WHITE at line start 175 int color = WHITE; 176 177 while(true) { 178 int b2 = nextState(refData, refAddr, b1, last); 179 if(b2 < a1) { // pass mode 180 outIndex += add2DBits(compData, outIndex, pass, 0); 181 a0 = b2; 182 } else { 183 int tmp = b1 - a1 + 3; 184 if((tmp <= 6) && (tmp >= 0)) { // vertical mode 185 outIndex += 186 add2DBits(compData, outIndex, vert, tmp); 187 a0 = a1; 188 } else { // horizontal mode 189 int a2 = nextState(data, lineAddr, a1, last); 190 outIndex += 191 add2DBits(compData, outIndex, horz, 0); 192 outIndex += 193 add1DBits(compData, outIndex, a1-a0, color); 194 outIndex += 195 add1DBits(compData, outIndex, a2-a1, color^1); 196 a0 = a2; 197 } 198 } 199 if(a0 >= last) { 200 break; 201 } 202 color = ((data[lineAddr + (a0>>>3)]&0xff) >>> 203 (7-(a0 & 0x7))) & 0x1; 204 a1 = nextState(data, lineAddr, a0, last); 205 b1 = nextState(refData, refAddr, a0, last); 206 testbit = ((refData[refAddr + (b1>>>3)]&0xff) >>> 207 (7-(b1 & 0x7))) & 0x1; 208 if(testbit == color) { 209 b1 = nextState(refData, refAddr, b1, last); 210 } 211 } 212 } 213 214 // Skip to next line. 215 lineAddr += lineStride; 216 } 217 218 for(int i = 0; i < 6; i++) { 219 outIndex += addEOL(is1DMode, isEOLAligned, true, 220 compData, outIndex); 221 } 222 223 // 224 // flush all pending bits 225 // 226 while(ndex > 0) { 227 compData[outIndex++] = (byte)(bits >>> 24); 228 bits <<= 8; 229 ndex -= 8; 230 } 231 232 // Flip the bytes if inverse fill was requested. 233 if(inverseFill) { 234 for(int i = 0; i < outIndex; i++) { 235 compData[i] = TIFFFaxDecompressor.flipTable[compData[i]&0xff]; 236 } 237 } 238 239 return outIndex; 240 } 241 242 public int encode(byte[] b, int off, 243 int width, int height, 244 int[] bitsPerSample, 245 int scanlineStride) throws IOException { 246 if (bitsPerSample.length != 1 || bitsPerSample[0] != 1) { 247 throw new IIOException( 248 "Bits per sample must be 1 for T4 compression!"); 249 } 250 251 // This initial buffer size is based on an alternating 1-0 252 // pattern generating the most bits when converted to code 253 // words: 12 bits out for each pair of bits in. So the number 254 // of bit pairs is determined, multiplied by 12, converted to 255 // bytes, and a ceil() is taken to account for fill bits at the 256 // end of each line. The "2" addend accounts for the case 257 // of the pattern beginning with black. The buffer is intended 258 // to hold only a single row. 259 260 // Note: the max pattern len is 12: Horizontal mode 001 + M(a0a1) + M(a1a2) 261 // where for 2 bits M(a0a1) and M(a1a2) can be 9 262 263 int maxBits = 12*((width + 1)/2) + 2; 264 int bufSize = (maxBits + 7)/8; 265 266 // Calculate the maximum row as the G3-1D size plus the EOL, 267 // multiply this by the number of rows in the tile, and add 268 // 6 EOLs for the RTC (return to control). 269 bufSize = height*(bufSize + 2) + 12; 270 271 byte[] compData = new byte[bufSize]; 272 273 int bytes = encodeT4(is1DMode, 274 isEOLAligned, 275 b, scanlineStride, 8*off, 276 width, height, 277 compData); 278 279 stream.write(compData, 0, bytes); 280 return bytes; 281 } 282}