001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2025 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas/ 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see https://www.gnu.org/licenses/. 027 */ 028package edu.wisc.ssec.mcidasv.util; 029 030import java.io.OutputStream; 031import java.io.IOException; 032 033//============================================================================== 034// Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott. 035// K Weiner 12/00 036 037class LZWEncoder { 038 039 private static final int EOF = -1; 040 041 private int imgW, imgH; 042 private byte[] pixAry; 043 private int initCodeSize; 044 private int remaining; 045 private int curPixel; 046 047 // GIFCOMPR.C - GIF Image compression routines 048 // 049 // Lempel-Ziv compression based on 'compress'. GIF modifications by 050 // David Rowley (mgardi@watdcsu.waterloo.edu) 051 052 // General DEFINEs 053 054 static final int BITS = 12; 055 056 static final int HSIZE = 5003; // 80% occupancy 057 058 // GIF Image compression - modified 'compress' 059 // 060 // Based on: compress.c - File compression ala IEEE Computer, June 1984. 061 // 062 // By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) 063 // Jim McKie (decvax!mcvax!jim) 064 // Steve Davies (decvax!vax135!petsd!peora!srd) 065 // Ken Turkowski (decvax!decwrl!turtlevax!ken) 066 // James A. Woods (decvax!ihnp4!ames!jaw) 067 // Joe Orost (decvax!vax135!petsd!joe) 068 069 int n_bits; // number of bits/code 070 int maxbits = BITS; // user settable max # bits/code 071 int maxcode; // maximum code, given n_bits 072 int maxmaxcode = 1 << BITS; // should NEVER generate this code 073 074 int[] htab = new int[HSIZE]; 075 int[] codetab = new int[HSIZE]; 076 077 int hsize = HSIZE; // for dynamic table sizing 078 079 int free_ent = 0; // first unused entry 080 081 // block compression parameters -- after all codes are used up, 082 // and compression rate changes, start over. 083 boolean clear_flg = false; 084 085 // Algorithm: use open addressing double hashing (no chaining) on the 086 // prefix code / next character combination. We do a variant of Knuth's 087 // algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime 088 // secondary probe. Here, the modular division first probe is gives way 089 // to a faster exclusive-or manipulation. Also do block compression with 090 // an adaptive reset, whereby the code table is cleared when the compression 091 // ratio decreases, but after the table fills. The variable-length output 092 // codes are re-sized at this point, and a special CLEAR code is generated 093 // for the decompressor. Late addition: construct the table according to 094 // file size for noticeable speed improvement on small files. Please direct 095 // questions about this implementation to ames!jaw. 096 097 int g_init_bits; 098 099 int ClearCode; 100 int EOFCode; 101 102 // output 103 // 104 // Output the given code. 105 // Inputs: 106 // code: A n_bits-bit integer. If == -1, then EOF. This assumes 107 // that n_bits =< wordsize - 1. 108 // Outputs: 109 // Outputs code to the file. 110 // Assumptions: 111 // Chars are 8 bits long. 112 // Algorithm: 113 // Maintain a BITS character long buffer (so that 8 codes will 114 // fit in it exactly). Use the VAX insv instruction to insert each 115 // code in turn. When the buffer fills up empty it and start over. 116 117 int cur_accum = 0; 118 int cur_bits = 0; 119 120 int masks[] = 121 { 122 0x0000, 123 0x0001, 124 0x0003, 125 0x0007, 126 0x000F, 127 0x001F, 128 0x003F, 129 0x007F, 130 0x00FF, 131 0x01FF, 132 0x03FF, 133 0x07FF, 134 0x0FFF, 135 0x1FFF, 136 0x3FFF, 137 0x7FFF, 138 0xFFFF }; 139 140 // Number of characters so far in this 'packet' 141 int a_count; 142 143 // Define the storage for the packet accumulator 144 byte[] accum = new byte[256]; 145 146 //---------------------------------------------------------------------------- 147 LZWEncoder(int width, int height, byte[] pixels, int color_depth) { 148 imgW = width; 149 imgH = height; 150 pixAry = pixels; 151 initCodeSize = Math.max(2, color_depth); 152 } 153 154 // Add a character to the end of the current packet, and if it is 254 155 // characters, flush the packet to disk. 156 void char_out(byte c, OutputStream outs) throws IOException { 157 accum[a_count++] = c; 158 if (a_count >= 254) 159 flush_char(outs); 160 } 161 162 // Clear out the hash table 163 164 // table clear for block compress 165 void cl_block(OutputStream outs) throws IOException { 166 cl_hash(hsize); 167 free_ent = ClearCode + 2; 168 clear_flg = true; 169 170 output(ClearCode, outs); 171 } 172 173 // reset code table 174 void cl_hash(int hsize) { 175 for (int i = 0; i < hsize; ++i) 176 htab[i] = -1; 177 } 178 179 void compress(int init_bits, OutputStream outs) throws IOException { 180 int fcode; 181 int i /* = 0 */; 182 int c; 183 int ent; 184 int disp; 185 int hsize_reg; 186 int hshift; 187 188 // Set up the globals: g_init_bits - initial number of bits 189 g_init_bits = init_bits; 190 191 // Set up the necessary values 192 clear_flg = false; 193 n_bits = g_init_bits; 194 maxcode = MAXCODE(n_bits); 195 196 ClearCode = 1 << (init_bits - 1); 197 EOFCode = ClearCode + 1; 198 free_ent = ClearCode + 2; 199 200 a_count = 0; // clear packet 201 202 ent = nextPixel(); 203 204 hshift = 0; 205 for (fcode = hsize; fcode < 65536; fcode *= 2) 206 ++hshift; 207 hshift = 8 - hshift; // set hash code range bound 208 209 hsize_reg = hsize; 210 cl_hash(hsize_reg); // clear hash table 211 212 output(ClearCode, outs); 213 214 outer_loop : while ((c = nextPixel()) != EOF) { 215 fcode = (c << maxbits) + ent; 216 i = (c << hshift) ^ ent; // xor hashing 217 218 if (htab[i] == fcode) { 219 ent = codetab[i]; 220 continue; 221 } else if (htab[i] >= 0) // non-empty slot 222 { 223 disp = hsize_reg - i; // secondary hash (after G. Knott) 224 if (i == 0) 225 disp = 1; 226 do { 227 if ((i -= disp) < 0) 228 i += hsize_reg; 229 230 if (htab[i] == fcode) { 231 ent = codetab[i]; 232 continue outer_loop; 233 } 234 } while (htab[i] >= 0); 235 } 236 output(ent, outs); 237 ent = c; 238 if (free_ent < maxmaxcode) { 239 codetab[i] = free_ent++; // code -> hashtable 240 htab[i] = fcode; 241 } else 242 cl_block(outs); 243 } 244 // Put out the final code. 245 output(ent, outs); 246 output(EOFCode, outs); 247 } 248 249 //---------------------------------------------------------------------------- 250 void encode(OutputStream os) throws IOException { 251 os.write(initCodeSize); // write "initial code size" byte 252 253 remaining = imgW * imgH; // reset navigation variables 254 curPixel = 0; 255 256 compress(initCodeSize + 1, os); // compress and write the pixel data 257 258 os.write(0); // write block terminator 259 } 260 261 // Flush the packet to disk, and reset the accumulator 262 void flush_char(OutputStream outs) throws IOException { 263 if (a_count > 0) { 264 outs.write(a_count); 265 outs.write(accum, 0, a_count); 266 a_count = 0; 267 } 268 } 269 270 final int MAXCODE(int n_bits) { 271 return (1 << n_bits) - 1; 272 } 273 274 //---------------------------------------------------------------------------- 275 // Return the next pixel from the image 276 //---------------------------------------------------------------------------- 277 private int nextPixel() { 278 if (remaining == 0) 279 return EOF; 280 281 --remaining; 282 283 byte pix = pixAry[curPixel++]; 284 285 return pix & 0xff; 286 } 287 288 void output(int code, OutputStream outs) throws IOException { 289 cur_accum &= masks[cur_bits]; 290 291 if (cur_bits > 0) 292 cur_accum |= (code << cur_bits); 293 else 294 cur_accum = code; 295 296 cur_bits += n_bits; 297 298 while (cur_bits >= 8) { 299 char_out((byte) (cur_accum & 0xff), outs); 300 cur_accum >>= 8; 301 cur_bits -= 8; 302 } 303 304 // If the next entry is going to be too big for the code size, 305 // then increase it, if possible. 306 if (free_ent > maxcode || clear_flg) { 307 if (clear_flg) { 308 maxcode = MAXCODE(n_bits = g_init_bits); 309 clear_flg = false; 310 } else { 311 ++n_bits; 312 if (n_bits == maxbits) 313 maxcode = maxmaxcode; 314 else 315 maxcode = MAXCODE(n_bits); 316 } 317 } 318 319 if (code == EOFCode) { 320 // At EOF, write the rest of the buffer. 321 while (cur_bits > 0) { 322 char_out((byte) (cur_accum & 0xff), outs); 323 cur_accum >>= 8; 324 cur_bits -= 8; 325 } 326 327 flush_char(outs); 328 } 329 } 330}