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 }