View Javadoc
1   package nom.tam.fits.compression.algorithm.hcompress;
2   
3   import java.nio.ByteBuffer;
4   import java.nio.LongBuffer;
5   
6   /*
7    * #%L
8    * nom.tam FITS library
9    * %%
10   * Copyright (C) 1996 - 2024 nom-tam-fits
11   * %%
12   * This is free and unencumbered software released into the public domain.
13   *
14   * Anyone is free to copy, modify, publish, use, compile, sell, or
15   * distribute this software, either in source code form or as a compiled
16   * binary, for any purpose, commercial or non-commercial, and by any
17   * means.
18   *
19   * In jurisdictions that recognize copyright laws, the author or authors
20   * of this software dedicate any and all copyright interest in the
21   * software to the public domain. We make this dedication for the benefit
22   * of the public at large and to the detriment of our heirs and
23   * successors. We intend this dedication to be an overt act of
24   * relinquishment in perpetuity of all present and future rights to this
25   * software under copyright law.
26   *
27   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
30   * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
31   * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
32   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
33   * OTHER DEALINGS IN THE SOFTWARE.
34   * #L%
35   */
36  
37  /**
38   * <p>
39   * (<i>for internal use</i>) A hierarchical data compression algoritm, used by the Hubble Data Archive and the STScI
40   * Digital Sky Survey.
41   * </p>
42   * <p>
43   * The original compression code was written by Richard White at the STScI and included (ported to c and adapted) in
44   * cfitsio by William Pence, NASA/GSFC. That code was then ported to java by R. van Nieuwenhoven. Later it was massively
45   * refactored to harmonize the different compression algorithms and reduce the duplicate code pieces without obscuring
46   * the algorithm itself as far as possible. The original site for the algorithm is
47   * </p>
48   * <p>
49   * See <a href="http://www.stsci.edu/software/hcompress.html">http://www.stsci.edu /software/hcompress.html</a>
50   * </p>
51   * 
52   * @author Richard White
53   * @author William Pence
54   * @author Richard van Nieuwenhoven
55   * 
56   * @see    HDecompress
57   * @see    HCompressorOption
58   */
59  @SuppressWarnings("javadoc")
60  public class HCompress {
61  
62      private static final int HTRANS_START_MASK = -2;
63  
64      protected static final double ROUNDING_HALF = 0.5;
65  
66      protected static final int BITS_OF_1_BYTE = 8;
67  
68      protected static final int BITS_OF_1_NYBBLE = 4;
69  
70      protected static final int BYTE_MASK = 0xff;
71  
72      protected static final int NYBBLE_MASK = 0xF;
73  
74      /**
75       * to be refactored to a good name.
76       */
77      private static final int N3 = 3;
78  
79      private static final int[] BITS_MASK = {0, 1, 3, 7, 15, 31, 63, 127, 255};
80  
81      /*
82       * Huffman code values and number of bits in each code
83       */
84      private static final int[] CODE = {0x3e, 0x00, 0x01, 0x08, 0x02, 0x09, 0x1a, 0x1b, 0x03, 0x1c, 0x0a, 0x1d, 0x0b, 0x1e,
85              0x3f, 0x0c};
86  
87      private static final byte[] CODE_MAGIC = {(byte) 0xDD, (byte) 0x99};
88  
89      private static final int[] NCODE = {6, 3, 3, 4, 3, 4, 5, 5, 3, 5, 4, 5, 4, 5, 6, 4};
90  
91      /**
92       * variables for bit output to buffer when Huffman coding
93       */
94      private int bitbuffer;
95  
96      /** Number of bits free in buffer */
97      private int bitsToGo2;
98  
99      private int bitsToGo3;
100 
101     /** Bits buffered for output */
102     private int buffer2;
103 
104     private int b2i(boolean b) {
105         return b ? 1 : 0;
106     }
107 
108     private int bufcopy(byte[] a, int n, byte[] buffer, int b, long bmax) {
109         int i;
110 
111         for (i = 0; i < n; i++) {
112             if (a[i] != 0) {
113                 /*
114                  * add Huffman code for a[i] to buffer
115                  */
116                 bitbuffer |= CODE[a[i]] << bitsToGo3;
117                 bitsToGo3 += NCODE[a[i]];
118                 if (bitsToGo3 >= BITS_OF_1_BYTE) {
119                     buffer[b] = (byte) (bitbuffer & BYTE_MASK);
120                     b++;
121                     /*
122                      * return warning code if we fill buffer
123                      */
124                     if (b >= bmax) {
125                         return b;
126                     }
127                     bitbuffer >>= BITS_OF_1_BYTE;
128                     bitsToGo3 -= BITS_OF_1_BYTE;
129                 }
130             }
131         }
132         return b;
133     }
134 
135     protected void compress(long[] aa, int ny, int nx, int scale, ByteBuffer output) {
136         /*
137          * compress the input image using the H-compress algorithm a - input image tiledImageOperation nx - size of X
138          * axis of image ny - size of Y axis of image scale - quantization scale factor. Larger values results in more
139          * (lossy) compression scale = 0 does lossless compression output - pre-allocated tiledImageOperation to hold
140          * the output compressed stream of bytes nbyts - input value = size of the output buffer; returned value = size
141          * of the compressed byte stream, in bytes NOTE: the nx and ny dimensions as defined within this code are
142          * reversed from the usual FITS notation. ny is the fastest varying dimension, which is usually considered the X
143          * axis in the FITS image display
144          */
145 
146         /* H-transform */
147         htrans(aa, nx, ny);
148 
149         LongBuffer a = LongBuffer.wrap(aa);
150 
151         /* digitize */
152         digitize(a, 0, nx, ny, scale);
153 
154         /* encode and write to output tiledImageOperation */
155         encode(output, a, nx, ny, scale);
156 
157     }
158 
159     private LongBuffer copy(LongBuffer a, int i) {
160         a.position(i);
161         return a.slice();
162     }
163 
164     private void digitize(LongBuffer a, int aOffset, int nx, int ny, long scale) {
165         /*
166          * round to multiple of scale
167          */
168         if (scale <= 1) {
169             return;
170         }
171         long d = (scale + 1L) / 2L - 1L;
172         for (int index = 0; index < a.limit(); index++) {
173             long current = a.get(index);
174             a.put(index, (current > 0 ? current + d : current - d) / scale);
175         }
176     }
177 
178     /**
179      * encode pixels.
180      * 
181      * @param compressedBytes compressed data
182      * @param pixels          pixels to compress
183      * @param nx              image width dimension
184      * @param ny              image height dimension
185      * @param nbitplanes      Number of bit planes in quadrants
186      */
187     private void doEncode(ByteBuffer compressedBytes, LongBuffer pixels, int nx, int ny, byte[] nbitplanes) {
188 
189         int nx2 = (nx + 1) / 2;
190         int ny2 = (ny + 1) / 2;
191         /*
192          * Initialize bit output
193          */
194         startOutputtingBits();
195         /*
196          * write out the bit planes for each quadrant
197          */
198         qtreeEncode(compressedBytes, copy(pixels, 0), ny, nx2, ny2, nbitplanes[0]);
199 
200         qtreeEncode(compressedBytes, copy(pixels, ny2), ny, nx2, ny / 2, nbitplanes[1]);
201 
202         qtreeEncode(compressedBytes, copy(pixels, ny * nx2), ny, nx / 2, ny2, nbitplanes[1]);
203 
204         qtreeEncode(compressedBytes, copy(pixels, ny * nx2 + ny2), ny, nx / 2, ny / 2, nbitplanes[2]);
205         /*
206          * Add zero as an EOF symbol
207          */
208         outputNybble(compressedBytes, 0);
209         doneOutputtingBits(compressedBytes);
210 
211     }
212 
213     private void doneOutputtingBits(ByteBuffer outfile) {
214         if (bitsToGo2 < BITS_OF_1_BYTE) {
215             /* putc(buffer2<<bits_to_go2,outfile); */
216 
217             outfile.put((byte) (buffer2 << bitsToGo2));
218         }
219     }
220 
221     private int encode(ByteBuffer compressedBytes, LongBuffer a, int nx, int ny, int scale) {
222         long[] vmax = new long[N3];
223         byte[] nbitplanes = new byte[N3];
224         // initialize the number of compressed bytes that have been written
225         long noutchar = 0;
226         int nel = nx * ny;
227         /*
228          * write magic value
229          */
230         compressedBytes.put(CODE_MAGIC);
231         compressedBytes.putInt(nx); /* size of image */
232         compressedBytes.putInt(ny);
233         compressedBytes.putInt(scale); /* scale factor for digitization */
234         /*
235          * write first value of A (sum of all pixels -- the only value which does not compress well)
236          */
237         compressedBytes.putLong(a.get(0));
238 
239         a.put(0, 0);
240         /*
241          * allocate tiledImageOperation for sign bits and save values, 8 per byte
242          */
243         byte[] signbits = new byte[(nel + BITS_OF_1_BYTE - 1) / BITS_OF_1_BYTE];
244 
245         int nsign = 0;
246         int bitsToGo = BITS_OF_1_BYTE;
247         signbits[0] = 0;
248         for (int i = 0; i < nel; i++) {
249             if (a.get(i) > 0) {
250                 /*
251                  * positive element, put zero at end of buffer
252                  */
253                 signbits[nsign] <<= 1;
254                 bitsToGo--;
255             } else if (a.get(i) < 0) {
256                 /*
257                  * negative element, shift in a one
258                  */
259                 signbits[nsign] <<= 1;
260                 signbits[nsign] |= 1;
261                 bitsToGo--;
262                 /*
263                  * replace a by absolute value
264                  */
265                 a.put(i, -a.get(i));
266             }
267             if (bitsToGo == 0) {
268                 /*
269                  * filled up this byte, go to the next one
270                  */
271                 bitsToGo = BITS_OF_1_BYTE;
272                 nsign++;
273                 signbits[nsign] = 0;
274             }
275         }
276         if (bitsToGo != BITS_OF_1_BYTE) {
277             /*
278              * some bits in last element move bits in last byte to bottom and increment nsign
279              */
280             signbits[nsign] <<= bitsToGo;
281             nsign++;
282         }
283         /*
284          * calculate number of bit planes for 3 quadrants quadrant 0=bottom left, 1=bottom right or top left, 2=top
285          * right,
286          */
287         for (int q = 0; q < N3; q++) {
288             vmax[q] = 0;
289         }
290         /*
291          * get maximum absolute value in each quadrant
292          */
293         int nx2 = (nx + 1) / 2;
294         int ny2 = (ny + 1) / 2;
295         int j = 0; /* column counter */
296         int k = 0; /* row counter */
297         for (int i = 0; i < nel; i++) {
298             int q = (j >= ny2 ? 1 : 0) + (k >= nx2 ? 1 : 0);
299             if (vmax[q] < a.get(i)) {
300                 vmax[q] = a.get(i);
301             }
302             if (++j >= ny) {
303                 j = 0;
304                 k++;
305             }
306         }
307         /*
308          * now calculate number of bits for each quadrant
309          */
310 
311         /* this is a more efficient way to do this, */
312 
313         for (int q = 0; q < N3; q++) {
314             nbitplanes[q] = 0;
315             while (vmax[q] > 0) {
316                 vmax[q] = vmax[q] >> 1;
317                 nbitplanes[q]++;
318             }
319         }
320 
321         /*
322          * write nbitplanes
323          */
324         compressedBytes.put(nbitplanes, 0, nbitplanes.length);
325 
326         /*
327          * write coded tiledImageOperation
328          */
329         doEncode(compressedBytes, a, nx, ny, nbitplanes);
330         /*
331          * write sign bits
332          */
333 
334         if (nsign > 0) {
335             compressedBytes.put(signbits, 0, nsign);
336         }
337         return (int) noutchar;
338 
339     }
340 
341     private int htrans(long[] a, int nx, int ny) {
342         /*
343          * log2n is log2 of max(nx,ny) rounded up to next power of 2
344          */
345         int nmax = nx > ny ? nx : ny;
346         int log2n = log2n(nmax);
347         if (nmax > 1 << log2n) {
348             log2n++;
349         }
350         /*
351          * get temporary storage for shuffling elements
352          */
353         long[] tmp = new long[(nmax + 1) / 2];
354 
355         /*
356          * set up rounding and shifting masks
357          */
358         long shift = 0;
359         long mask = HTRANS_START_MASK;
360         long mask2 = mask << 1;
361         long prnd = 1;
362         long prnd2 = prnd << 1;
363         long nrnd2 = prnd2 - 1;
364         /*
365          * do log2n reductions We're indexing a as a 2-D tiledImageOperation with dimensions (nx,ny).
366          */
367         int nxtop = nx;
368         int nytop = ny;
369 
370         for (int k = 0; k < log2n; k++) {
371             int oddx = nxtop % 2;
372             int oddy = nytop % 2;
373             int i = 0;
374             for (; i < nxtop - oddx; i += 2) {
375                 int s00 = i * ny; /* s00 is index of a[i,j] */
376                 int s10 = s00 + ny; /* s10 is index of a[i+1,j] */
377                 for (int j = 0; j < nytop - oddy; j += 2) {
378                     /*
379                      * Divide h0,hx,hy,hc by 2 (1 the first time through).
380                      */
381                     long h0 = a[s10 + 1] + a[s10] + a[s00 + 1] + a[s00] >> shift;
382                     long hx = a[s10 + 1] + a[s10] - a[s00 + 1] - a[s00] >> shift;
383                     long hy = a[s10 + 1] - a[s10] + a[s00 + 1] - a[s00] >> shift;
384                     long hc = a[s10 + 1] - a[s10] - a[s00 + 1] + a[s00] >> shift;
385 
386                     /*
387                      * Throw away the 2 bottom bits of h0, bottom bit of hx,hy. To get rounding to be same for positive
388                      * and negative numbers, nrnd2 = prnd2 - 1.
389                      */
390                     a[s10 + 1] = hc;
391                     a[s10] = (hx >= 0 ? hx + prnd : hx) & mask;
392                     a[s00 + 1] = (hy >= 0 ? hy + prnd : hy) & mask;
393                     a[s00] = (h0 >= 0 ? h0 + prnd2 : h0 + nrnd2) & mask2;
394                     s00 += 2;
395                     s10 += 2;
396                 }
397                 if (oddy != 0) {
398                     /*
399                      * do last element in row if row length is odd s00+1, s10+1 are off edge
400                      */
401                     long h0 = a[s10] + a[s00] << 1 - shift;
402                     long hx = a[s10] - a[s00] << 1 - shift;
403                     a[s10] = (hx >= 0 ? hx + prnd : hx) & mask;
404                     a[s00] = (h0 >= 0 ? h0 + prnd2 : h0 + nrnd2) & mask2;
405                     s00++;
406                     s10++;
407                 }
408             }
409             if (oddx != 0) {
410                 /*
411                  * do last row if column length is odd s10, s10+1 are off edge
412                  */
413                 int s00 = i * ny;
414                 for (int j = 0; j < nytop - oddy; j += 2) {
415                     long h0 = a[s00 + 1] + a[s00] << 1 - shift;
416                     long hy = a[s00 + 1] - a[s00] << 1 - shift;
417                     a[s00 + 1] = (hy >= 0 ? hy + prnd : hy) & mask;
418                     a[s00] = (h0 >= 0 ? h0 + prnd2 : h0 + nrnd2) & mask2;
419                     s00 += 2;
420                 }
421                 if (oddy != 0) {
422                     /*
423                      * do corner element if both row and column lengths are odd s00+1, s10, s10+1 are off edge
424                      */
425                     long h0 = a[s00] << 2 - shift;
426                     a[s00] = (h0 >= 0 ? h0 + prnd2 : h0 + nrnd2) & mask2;
427                 }
428             }
429             /*
430              * now shuffle in each dimension to group coefficients by order
431              */
432             // achtung eigenlich pointer nach a
433             for (i = 0; i < nxtop; i++) {
434                 shuffle(a, ny * i, nytop, 1, tmp);
435             }
436             for (int j = 0; j < nytop; j++) {
437                 shuffle(a, j, nxtop, ny, tmp);
438             }
439             /*
440              * image size reduced by 2 (round up if odd)
441              */
442             nxtop = nxtop + 1 >> 1;
443             nytop = nytop + 1 >> 1;
444             /*
445              * divisor doubles after first reduction
446              */
447             shift = 1;
448             /*
449              * masks, rounding values double after each iteration
450              */
451             mask = mask2;
452             prnd = prnd2;
453             mask2 = mask2 << 1;
454             prnd2 = prnd2 << 1;
455             nrnd2 = prnd2 - 1;
456         }
457         return 0;
458     }
459 
460     private int log2n(int nqmax) {
461         return (int) (Math.log(nqmax) / Math.log(2.0) + ROUNDING_HALF);
462     }
463 
464     private void outputNbits(ByteBuffer outfile, int bits, int n) {
465         /* AND mask for the right-most n bits */
466 
467         /*
468          * insert bits at end of buffer
469          */
470         buffer2 <<= n;
471         /* buffer2 |= ( bits & ((1<<n)-1) ); */
472         buffer2 |= bits & BITS_MASK[n];
473         bitsToGo2 -= n;
474         if (bitsToGo2 <= 0) {
475             /*
476              * buffer2 full, put out top 8 bits
477              */
478 
479             outfile.put((byte) (buffer2 >> -bitsToGo2 & BYTE_MASK));
480 
481             bitsToGo2 += BITS_OF_1_BYTE;
482         }
483     }
484 
485     private void outputNnybble(ByteBuffer outfile, int n, byte[] array) {
486         /*
487          * pack the 4 lower bits in each element of the tiledImageOperation into the outfile tiledImageOperation
488          */
489 
490         int ii, jj, kk = 0, shift;
491 
492         if (n == 1) {
493             outputNybble(outfile, array[0]);
494             return;
495         }
496         /*
497          * forcing byte alignment doesn;t help, and even makes it go slightly slower if (bits_to_go2 != 8)
498          * output_nbits(outfile, kk, bits_to_go2);
499          */
500         if (bitsToGo2 <= BITS_OF_1_NYBBLE) {
501             /* just room for 1 nybble; write it out separately */
502             outputNybble(outfile, array[0]);
503             kk++; /* index to next tiledImageOperation element */
504 
505             if (n == 2) {
506                 // only 1 more nybble to write out
507                 outputNybble(outfile, array[1]);
508                 return;
509             }
510         }
511 
512         /* bits_to_go2 is now in the range 5 - 8 */
513         shift = BITS_OF_1_BYTE - bitsToGo2;
514 
515         /*
516          * now write out pairs of nybbles; this does not affect value of bits_to_go2
517          */
518         jj = (n - kk) / 2;
519 
520         if (bitsToGo2 == BITS_OF_1_BYTE) {
521             /* special case if nybbles are aligned on byte boundary */
522             /* this actually seems to make very little difference in speed */
523             buffer2 = 0;
524             for (ii = 0; ii < jj; ii++) {
525                 outfile.put((byte) ((array[kk] & NYBBLE_MASK) << BITS_OF_1_NYBBLE | array[kk + 1] & NYBBLE_MASK));
526                 kk += 2;
527             }
528         } else {
529             for (ii = 0; ii < jj; ii++) {
530                 buffer2 = buffer2 << BITS_OF_1_BYTE | (array[kk] & NYBBLE_MASK) << BITS_OF_1_NYBBLE
531                         | array[kk + 1] & NYBBLE_MASK;
532                 kk += 2;
533 
534                 /*
535                  * buffer2 full, put out top 8 bits
536                  */
537 
538                 outfile.put((byte) (buffer2 >> shift & BYTE_MASK));
539             }
540         }
541 
542         /* write out last odd nybble, if present */
543         if (kk != n) {
544             outputNybble(outfile, array[n - 1]);
545         }
546     }
547 
548     private void outputNybble(ByteBuffer outfile, int bits) {
549         /*
550          * insert 4 bits at end of buffer
551          */
552         buffer2 = buffer2 << BITS_OF_1_NYBBLE | bits & NYBBLE_MASK;
553         bitsToGo2 -= BITS_OF_1_NYBBLE;
554         if (bitsToGo2 <= 0) {
555             /*
556              * buffer2 full, put out top 8 bits
557              */
558 
559             outfile.put((byte) (buffer2 >> -bitsToGo2 & BYTE_MASK));
560 
561             bitsToGo2 += BITS_OF_1_BYTE;
562         }
563     }
564 
565     /**
566      * macros to write out 4-bit nybble, Huffman code for this value
567      */
568     private int qtreeEncode(ByteBuffer outfile, LongBuffer a, int n, int nqx, int nqy, int nbitplanes) {
569 
570         /*
571          * int a[]; int n; physical dimension of row in a int nqx; length of row int nqy; length of column (<=n) int
572          * nbitplanes; number of bit planes to output
573          */
574 
575         int log2n, i, k, bit, b, nqmax, nqx2, nqy2, nx, ny;
576         long bmax;
577         byte[] scratch, buffer;
578 
579         /*
580          * log2n is log2 of max(nqx,nqy) rounded up to next power of 2
581          */
582         nqmax = nqx > nqy ? nqx : nqy;
583         log2n = log2n(nqmax);
584         if (nqmax > 1 << log2n) {
585             log2n++;
586         }
587         /*
588          * initialize buffer point, max buffer size
589          */
590         nqx2 = (nqx + 1) / 2;
591         nqy2 = (nqy + 1) / 2;
592         bmax = (nqx2 * nqy2 + 1) / 2;
593         /*
594          * We're indexing A as a 2-D tiledImageOperation with dimensions (nqx,nqy). Scratch is 2-D with dimensions
595          * (nqx/2,nqy/2) rounded up. Buffer is used to store string of codes for output.
596          */
597         scratch = new byte[(int) (2 * bmax)];
598         buffer = new byte[(int) bmax];
599 
600         /*
601          * now encode each bit plane, starting with the top
602          */
603         bitplane_done: for (bit = nbitplanes - 1; bit >= 0; bit--) {
604             /*
605              * initial bit buffer
606              */
607             b = 0;
608             bitbuffer = 0;
609             bitsToGo3 = 0;
610             /*
611              * on first pass copy A to scratch tiledImageOperation
612              */
613             qtreeOnebit(a, n, nqx, nqy, scratch, bit);
614             nx = nqx + 1 >> 1;
615             ny = nqy + 1 >> 1;
616             /*
617              * copy non-zero values to output buffer, which will be written in reverse order
618              */
619             b = bufcopy(scratch, nx * ny, buffer, b, bmax);
620             if (b >= bmax) {
621                 /*
622                  * quadtree is expanding data, change warning code and just fill buffer with bit-map
623                  */
624                 writeBdirect(outfile, a, n, nqx, nqy, scratch, bit);
625                 continue bitplane_done;
626             }
627             /*
628              * do log2n reductions
629              */
630             for (k = 1; k < log2n; k++) {
631                 qtreeReduce(scratch, ny, nx, ny, scratch);
632                 nx = nx + 1 >> 1;
633                 ny = ny + 1 >> 1;
634                 b = bufcopy(scratch, nx * ny, buffer, b, bmax);
635                 if (b >= bmax) {
636                     writeBdirect(outfile, a, n, nqx, nqy, scratch, bit);
637                     continue bitplane_done;
638                 }
639             }
640             /*
641              * OK, we've got the code in buffer Write quadtree warning code, then write buffer in reverse order
642              */
643             outputNybble(outfile, NYBBLE_MASK);
644             if (b == 0) {
645                 if (bitsToGo3 > 0) {
646                     /*
647                      * put out the last few bits
648                      */
649                     outputNbits(outfile, bitbuffer & (1 << bitsToGo3) - 1, bitsToGo3);
650                 } else {
651                     /*
652                      * have to write a zero nybble if there are no 1's in tiledImageOperation
653                      */
654                     outputNbits(outfile, CODE[0], NCODE[0]);
655                 }
656             } else {
657                 if (bitsToGo3 > 0) {
658                     /*
659                      * put out the last few bits
660                      */
661                     outputNbits(outfile, bitbuffer & (1 << bitsToGo3) - 1, bitsToGo3);
662                 }
663                 for (i = b - 1; i >= 0; i--) {
664                     outputNbits(outfile, buffer[i], BITS_OF_1_BYTE);
665                 }
666             }
667         }
668         return 0;
669     }
670 
671     private void qtreeOnebit(LongBuffer a, int n, int nx, int ny, byte[] b, int bit) {
672         int i, j, k;
673         long b0, b1, b2, b3;
674         int s10, s00;
675 
676         /*
677          * use selected bit to get amount to shift
678          */
679         b0 = 1L << bit;
680         b1 = b0 << 1;
681         b2 = b1 << 1;
682         b3 = b2 << 1;
683         k = 0; /* k is index of b[i/2,j/2] */
684         for (i = 0; i < nx - 1; i += 2) {
685             s00 = n * i; /* s00 is index of a[i,j] */
686             /*
687              * tried using s00+n directly in the statements, but this had no effect on performance
688              */
689             s10 = s00 + n; /* s10 is index of a[i+1,j] */
690             for (j = 0; j < ny - 1; j += 2) {
691 
692                 b[k] = (byte) ((a.get(s10 + 1) & b0 //
693                         | a.get(s10) << 1 & b1 //
694                         | a.get(s00 + 1) << 2 & b2 //
695                         | a.get(s00) << N3 & b3) >> bit);
696 
697                 k++;
698                 s00 += 2;
699                 s10 += 2;
700             }
701             if (j < ny) {
702                 /*
703                  * row size is odd, do last element in row s00+1,s10+1 are off edge
704                  */
705                 b[k] = (byte) ((a.get(s10) << 1 & b1 | a.get(s00) << N3 & b3) >> bit);
706                 k++;
707             }
708         }
709         if (i < nx) {
710             /*
711              * column size is odd, do last row s10,s10+1 are off edge
712              */
713             s00 = n * i;
714             for (j = 0; j < ny - 1; j += 2) {
715                 b[k] = (byte) ((a.get(s00 + 1) << 2 & b2 | a.get(s00) << N3 & b3) >> bit);
716                 k++;
717                 s00 += 2;
718             }
719             if (j < ny) {
720                 /*
721                  * both row and column size are odd, do corner element s00+1, s10, s10+1 are off edge
722                  */
723                 b[k] = (byte) ((a.get(s00) << N3 & b3) >> bit);
724                 k++;
725             }
726         }
727     }
728 
729     private void qtreeReduce(byte[] a, int n, int nx, int ny, byte[] b) {
730         int i, j, k;
731         int s10, s00;
732 
733         k = 0; /* k is index of b[i/2,j/2] */
734         for (i = 0; i < nx - 1; i += 2) {
735             s00 = n * i; /* s00 is index of a[i,j] */
736             s10 = s00 + n; /* s10 is index of a[i+1,j] */
737             for (j = 0; j < ny - 1; j += 2) {
738                 b[k] = (byte) (b2i(a[s10 + 1] != 0) | b2i(a[s10] != 0) << 1 | b2i(a[s00 + 1] != 0) << 2
739                         | b2i(a[s00] != 0) << N3);
740                 k++;
741                 s00 += 2;
742                 s10 += 2;
743             }
744             if (j < ny) {
745                 /*
746                  * row size is odd, do last element in row s00+1,s10+1 are off edge
747                  */
748                 b[k] = (byte) (b2i(a[s10] != 0) << 1 | b2i(a[s00] != 0) << N3);
749                 k++;
750             }
751         }
752         if (i < nx) {
753             /*
754              * column size is odd, do last row s10,s10+1 are off edge
755              */
756             s00 = n * i;
757             for (j = 0; j < ny - 1; j += 2) {
758                 b[k] = (byte) (b2i(a[s00 + 1] != 0) << 2 | b2i(a[s00] != 0) << N3);
759                 k++;
760                 s00 += 2;
761             }
762             if (j < ny) {
763                 /*
764                  * both row and column size are odd, do corner element s00+1, s10, s10+1 are off edge
765                  */
766                 b[k] = (byte) (b2i(a[s00] != 0) << N3);
767                 k++;
768             }
769         }
770     }
771 
772     private void shuffle(long[] a, int aOffset, int n, int n2, long[] tmp) {
773 
774         /*
775          * int a[]; tiledImageOperation to shuffle int n; number of elements to shuffle int n2; second dimension int
776          * tmp[]; scratch storage
777          */
778 
779         int i;
780         long[] p1, p2, pt;
781         int p1Offset;
782         int ptOffset;
783         int p2Offset;
784         /*
785          * copy odd elements to tmp
786          */
787         pt = tmp;
788         ptOffset = 0;
789         p1 = a;
790         p1Offset = aOffset + n2;
791         for (i = 1; i < n; i += 2) {
792             pt[ptOffset] = p1[p1Offset];
793             ptOffset++;
794             p1Offset += n2 + n2;
795         }
796         /*
797          * compress even elements into first half of A
798          */
799         p1 = a;
800         p1Offset = aOffset + n2;
801         p2 = a;
802         p2Offset = aOffset + n2 + n2;
803         for (i = 2; i < n; i += 2) {
804             p1[p1Offset] = p2[p2Offset];
805             p1Offset += n2;
806             p2Offset += n2 + n2;
807         }
808         /*
809          * put odd elements into 2nd half
810          */
811         pt = tmp;
812         ptOffset = 0;
813         for (i = 1; i < n; i += 2) {
814             p1[p1Offset] = pt[ptOffset];
815             p1Offset += n2;
816             ptOffset++;
817         }
818     }
819 
820     private void startOutputtingBits() {
821         buffer2 = 0; /* Buffer is empty to start */
822         bitsToGo2 = BITS_OF_1_BYTE; /* with */
823     }
824 
825     private void writeBdirect(ByteBuffer outfile, LongBuffer a, int n, int nqx, int nqy, byte[] scratch, int bit) {
826 
827         /*
828          * Write the direct bitmap warning code
829          */
830         outputNybble(outfile, 0x0);
831         /*
832          * Copy A to scratch tiledImageOperation (again!), packing 4 bits/nybble
833          */
834         qtreeOnebit(a, n, nqx, nqy, scratch, bit);
835         /*
836          * write to outfile
837          */
838         /*
839          * int i; for (i = 0; i < ((nqx+1)/2) * ((nqy+1)/2); i++) { output_nybble(outfile,scratch[i]); }
840          */
841         outputNnybble(outfile, (nqx + 1) / 2 * ((nqy + 1) / 2), scratch);
842 
843     }
844 
845 }