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}