All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nom.tam.fits.compression.algorithm.hcompress.HDecompress Maven / Gradle / Ivy

package nom.tam.fits.compression.algorithm.hcompress;

import java.nio.ByteBuffer;

/*
 * #%L
 * nom.tam FITS library
 * %%
 * Copyright (C) 1996 - 2024 nom-tam-fits
 * %%
 * This is free and unencumbered software released into the public domain.
 *
 * Anyone is free to copy, modify, publish, use, compile, sell, or
 * distribute this software, either in source code form or as a compiled
 * binary, for any purpose, commercial or non-commercial, and by any
 * means.
 *
 * In jurisdictions that recognize copyright laws, the author or authors
 * of this software dedicate any and all copyright interest in the
 * software to the public domain. We make this dedication for the benefit
 * of the public at large and to the detriment of our heirs and
 * successors. We intend this dedication to be an overt act of
 * relinquishment in perpetuity of all present and future rights to this
 * software under copyright law.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 * #L%
 */

import static nom.tam.fits.compression.algorithm.hcompress.HCompress.BITS_OF_1_BYTE;
import static nom.tam.fits.compression.algorithm.hcompress.HCompress.BITS_OF_1_NYBBLE;
import static nom.tam.fits.compression.algorithm.hcompress.HCompress.BYTE_MASK;
import static nom.tam.fits.compression.algorithm.hcompress.HCompress.NYBBLE_MASK;
import static nom.tam.fits.compression.algorithm.hcompress.HCompress.ROUNDING_HALF;

/**
 * (for internal use) A hierarchical data decompression algoritm, e.g. for the Hubble Data Archive and the STScI
 * Digital Sky Survey.
 * 

* The original decompression code was written by R. White at the STScI and included (ported to c and adapted) in * cfitsio by William Pence, NASA/GSFC. That code was then ported to java by R. van Nieuwenhoven. Later it was massively * refactored to harmonize the different compression algorithms and reduce the duplicate code pieces without obscuring * the algorithm itself as far as possible. The original site for the algorithm is *

*

* See http://www.stsci.edu/software/hcompress.html *

* * @author Richard White * @author William Pence * @author Richard van Nieuwenhoven */ public class HDecompress { private static class LongArrayPointer { private final long[] a; private int offset; LongArrayPointer(long[] tmp) { a = tmp; offset = 0; } public void bitOr(int i, long planeVal) { a[offset + i] |= planeVal; } public LongArrayPointer copy(int extraOffset) { LongArrayPointer intAP = new LongArrayPointer(a); intAP.offset = offset + extraOffset; return intAP; } public long get() { return a[offset]; } public long get(int i) { return a[offset + i]; } public void set(int i, long value) { a[offset + i] = value; } public void set(long value) { a[offset] = value; } } private static final byte[] CODE_MAGIC = {(byte) 0xDD, (byte) 0x99}; private static final int[] MASKS = {0, 1, 3, 7, 15, 31, 63, 127, 255}; private static final byte ZERO = 0; private static final byte BIT_ONE = 1; private static final byte BIT_TWO = 2; private static final byte BIT_THREE = 4; private static final byte BIT_FOUR = 8; /** * these N constants are obscuring the algorithm and should get some explaining javadoc if somebody understands the * algorithm. */ private static final int N03 = 3; private static final int N04 = 4; private static final int N05 = 5; private static final int N06 = 6; private static final int N07 = 7; private static final int N08 = 8; private static final int N09 = 9; private static final int N10 = 10; private static final int N11 = 11; private static final int N12 = 12; private static final int N13 = 13; private static final int N14 = 14; private static final int N15 = 15; private static final int N26 = 26; private static final int N27 = 27; private static final int N28 = 28; private static final int N29 = 29; private static final int N30 = 30; private static final int N31 = 31; private static final int N62 = 62; private static final int N63 = 63; /** * Number of bits still in buffer */ private int bitsToGo; /** Bits waiting to be input */ private int buffer2; private int nx; private int ny; private int scale; /** * log2n is log2 of max(nx,ny) rounded up to next power of 2 */ private int calculateLog2N(int nmax) { int log2n; log2n = (int) (Math.log(nmax) / Math.log(2.0) + ROUNDING_HALF); if (nmax > 1 << log2n) { log2n++; } return log2n; } /** * char *infile; input file long *a; address of output tiledImageOperation [nx][ny] int *nx,*ny; size of output * tiledImageOperation int *scale; scale factor for digitization * * @param infile * @param a */ private void decode64(ByteBuffer infile, LongArrayPointer a) { byte[] nbitplanes = new byte[N03]; byte[] tmagic = new byte[2]; /* * File starts either with special 2-byte magic code or with FITS keyword "SIMPLE =" */ infile.get(tmagic); /* * check for correct magic code value */ if (tmagic[0] != CODE_MAGIC[0] || tmagic[1] != CODE_MAGIC[1]) { throw new RuntimeException("Compression error"); } nx = infile.getInt(); /* x size of image */ ny = infile.getInt(); /* y size of image */ scale = infile.getInt(); /* scale factor for digitization */ /* sum of all pixels */ long sumall = infile.getLong(); /* # bits in quadrants */ infile.get(nbitplanes); dodecode64(infile, a, nbitplanes); /* * put sum of all pixels back into pixel 0 */ a.set(0, sumall); } /** * decompress the input byte stream using the H-compress algorithm input - input tiledImageOperation of compressed * bytes a - pre-allocated tiledImageOperation to hold the output uncompressed image nx - returned X axis size ny - * returned Y axis size NOTE: the nx and ny dimensions as defined within this code are reversed from the usual FITS * notation. ny is the fastest varying dimension, which is usually considered the X axis in the FITS image display * * @param input the input buffer to decompress * @param smooth should the image be smoothed * @param aa the resulting long tiledImageOperation */ public void decompress(ByteBuffer input, boolean smooth, long[] aa) { LongArrayPointer a = new LongArrayPointer(aa); /* decode the input tiledImageOperation */ decode64(input, a); /* * Un-Digitize */ undigitize64(a); /* * Inverse H-transform */ hinv64(a, smooth); } /** * long a[]; int nx,ny; Array dimensions are [nx][ny] unsigned char nbitplanes[3]; Number of bit planes in quadrants */ private int dodecode64(ByteBuffer infile, LongArrayPointer a, byte[] nbitplanes) { int nel = nx * ny; int nx2 = (nx + 1) / 2; int ny2 = (ny + 1) / 2; /* * initialize a to zero */ for (int i = 0; i < nel; i++) { a.set(i, 0); } /* * Initialize bit input */ startInputingBits(); /* * read bit planes for each quadrant */ qtreeDecode64(infile, a.copy(0), ny, nx2, ny2, nbitplanes[0]); qtreeDecode64(infile, a.copy(ny2), ny, nx2, ny / 2, nbitplanes[1]); qtreeDecode64(infile, a.copy(ny * nx2), ny, nx / 2, ny2, nbitplanes[1]); qtreeDecode64(infile, a.copy(ny * nx2 + ny2), ny, nx / 2, ny / 2, nbitplanes[2]); /* * make sure there is an EOF symbol (nybble=0) at end */ if (inputNybble(infile) != 0) { throw new RuntimeException("Compression error"); } /* * now get the sign bits Re-initialize bit input */ startInputingBits(); for (int i = 0; i < nel; i++) { if (a.get(i) != 0) { if (inputBit(infile) != 0) { a.set(i, -a.get(i)); } } } return 0; } /** * int smooth; 0 for no smoothing, else smooth during inversion int scale; used if smoothing is specified */ private int hinv64(LongArrayPointer a, boolean smooth) { int nmax = nx > ny ? nx : ny; int log2n = calculateLog2N(nmax); // get temporary storage for shuffling elements long[] tmp = new long[(nmax + 1) / 2]; // set up masks, rounding parameters int shift = 1; long bit0 = (long) 1 << log2n - 1; long bit1 = bit0 << 1; long bit2 = bit0 << 2; long mask0 = -bit0; long mask1 = mask0 << 1; long mask2 = mask0 << 2; long prnd0 = bit0 >> 1; long prnd1 = bit1 >> 1; long prnd2 = bit2 >> 1; long nrnd0 = prnd0 - 1; long nrnd1 = prnd1 - 1; long nrnd2 = prnd2 - 1; // round h0 to multiple of bit2 a.set(0, a.get(0) + (a.get(0) >= 0 ? prnd2 : nrnd2) & mask2); // do log2n expansions We're indexing a as a 2-D tiledImageOperation // with dimensions // (nx,ny). int nxtop = 1; int nytop = 1; int nxf = nx; int nyf = ny; int c = 1 << log2n; int i; for (int k = log2n - 1; k >= 0; k--) { // this somewhat cryptic code generates the sequence ntop[k-1] = // (ntop[k]+1)/2, where ntop[log2n] = n c = c >> 1; nxtop = nxtop << 1; nytop = nytop << 1; if (nxf <= c) { nxtop--; } else { nxf -= c; } if (nyf <= c) { nytop--; } else { nyf -= c; } // double shift and fix nrnd0 (because prnd0=0) on last pass if (k == 0) { nrnd0 = 0; shift = 2; } // unshuffle in each dimension to interleave coefficients for (i = 0; i < nxtop; i++) { unshuffle64(a.copy(ny * i), nytop, 1, tmp); } for (int j = 0; j < nytop; j++) { unshuffle64(a.copy(j), nxtop, ny, tmp); } // smooth by interpolating coefficients if SMOOTH != 0 if (smooth) { hsmooth64(a, nxtop, nytop); } int oddx = nxtop % 2; int oddy = nytop % 2; for (i = 0; i < nxtop - oddx; i += 2) { int s00 = ny * i; /* s00 is index of a[i,j] */ int s10 = s00 + ny; /* s10 is index of a[i+1,j] */ for (int j = 0; j < nytop - oddy; j += 2) { long h0 = a.get(s00); long hx = a.get(s10); long hy = a.get(s00 + 1); long hc = a.get(s10 + 1); // round hx and hy to multiple of bit1, hc to multiple of // bit0 h0 is already a multiple of bit2 hx = hx + (hx >= 0 ? prnd1 : nrnd1) & mask1; hy = hy + (hy >= 0 ? prnd1 : nrnd1) & mask1; hc = hc + (hc >= 0 ? prnd0 : nrnd0) & mask0; // propagate bit0 of hc to hx,hy long lowbit0 = hc & bit0; hx = hx >= 0 ? hx - lowbit0 : hx + lowbit0; hy = hy >= 0 ? hy - lowbit0 : hy + lowbit0; // Propagate bits 0 and 1 of hc,hx,hy to h0. This could be // simplified if we assume h0>0, but then the inversion // would not be lossless for images with negative pixels. long lowbit1 = (hc ^ hx ^ hy) & bit1; h0 = h0 >= 0 ? h0 + lowbit0 - lowbit1 : h0 + (lowbit0 == 0 ? lowbit1 : lowbit0 - lowbit1); // Divide sums by 2 (4 last time) a.set(s10 + 1, h0 + hx + hy + hc >> shift); a.set(s10, h0 + hx - hy - hc >> shift); a.set(s00 + 1, h0 - hx + hy - hc >> shift); a.set(s00, h0 - hx - hy + hc >> shift); s00 += 2; s10 += 2; } if (oddy != 0) { // do last element in row if row length is odd s00+1, s10+1 // are off edge long h0 = a.get(s00); long hx = a.get(s10); hx = (hx >= 0 ? hx + prnd1 : hx + nrnd1) & mask1; long lowbit1 = hx & bit1; h0 = h0 >= 0 ? h0 - lowbit1 : h0 + lowbit1; a.set(s10, h0 + hx >> shift); a.set(s00, h0 - hx >> shift); } } if (oddx != 0) { // do last row if column length is odd s10, s10+1 are off edge int s00 = ny * i; for (int j = 0; j < nytop - oddy; j += 2) { long h0 = a.get(s00); long hy = a.get(s00 + 1); hy = (hy >= 0 ? hy + prnd1 : hy + nrnd1) & mask1; long lowbit1 = hy & bit1; h0 = h0 >= 0 ? h0 - lowbit1 : h0 + lowbit1; a.set(s00 + 1, h0 + hy >> shift); a.set(s00, h0 - hy >> shift); s00 += 2; } if (oddy != 0) { // do corner element if both row and column lengths are odd // s00+1, s10, s10+1 are off edge long h0 = a.get(s00); a.set(s00, h0 >> shift); } } // divide all the masks and rounding values by 2 bit1 = bit0; bit0 = bit0 >> 1; mask1 = mask0; mask0 = mask0 >> 1; prnd1 = prnd0; prnd0 = prnd0 >> 1; nrnd1 = nrnd0; nrnd0 = prnd0 - 1; } return 0; } /** * long a[]; tiledImageOperation of H-transform coefficients int nxtop,nytop; size of coefficient block to use int * ny; actual 1st dimension of tiledImageOperation int scale; truncation scale factor that was used */ private void hsmooth64(LongArrayPointer a, int nxtop, int nytop) { int i, j; int ny2, s10, s00; long hm, h0, hp, hmm, hpm, hmp, hpp, hx2, hy2, diff, dmax, dmin, s, smax, m1, m2; /* * Maximum change in coefficients is determined by scale factor. Since we rounded during division (see * digitize.c), the biggest permitted change is scale/2. */ smax = scale >> 1; if (smax <= 0) { return; } ny2 = ny << 1; /* * We're indexing a as a 2-D tiledImageOperation with dimensions (nxtop,ny) of which only (nxtop,nytop) are * used. The coefficients on the edge of the tiledImageOperation are not adjusted (which is why the loops below * start at 2 instead of 0 and end at nxtop-2 instead of nxtop.) */ /* * Adjust x difference hx */ for (i = 2; i < nxtop - 2; i += 2) { s00 = ny * i; /* s00 is index of a[i,j] */ s10 = s00 + ny; /* s10 is index of a[i+1,j] */ for (j = 0; j < nytop; j += 2) { /* * hp is h0 (mean value) in next x zone, hm is h0 in previous x zone */ hm = a.get(s00 - ny2); h0 = a.get(s00); hp = a.get(s00 + ny2); /* * diff = 8 * hx slope that would match h0 in neighboring zones */ diff = hp - hm; /* * monotonicity constraints on diff */ dmax = Math.max(Math.min(hp - h0, h0 - hm), 0) << 2; dmin = Math.min(Math.max(hp - h0, h0 - hm), 0) << 2; /* * if monotonicity would set slope = 0 then don't change hx. note dmax>=0, dmin<=0. */ if (dmin < dmax) { diff = Math.max(Math.min(diff, dmax), dmin); /* * Compute change in slope limited to range +/- smax. Careful with rounding negative numbers when * using shift for divide by 8. */ s = diff - (a.get(s10) << N03); s = s >= 0 ? s >> N03 : s + N07 >> N03; s = Math.max(Math.min(s, smax), -smax); a.set(s10, a.get(s10) + s); } s00 += 2; s10 += 2; } } /* * Adjust y difference hy */ for (i = 0; i < nxtop; i += 2) { s00 = ny * i + 2; s10 = s00 + ny; for (j = 2; j < nytop - 2; j += 2) { hm = a.get(s00 - 2); h0 = a.get(s00); hp = a.get(s00 + 2); diff = hp - hm; dmax = Math.max(Math.min(hp - h0, h0 - hm), 0) << 2; dmin = Math.min(Math.max(hp - h0, h0 - hm), 0) << 2; if (dmin < dmax) { diff = Math.max(Math.min(diff, dmax), dmin); s = diff - (a.get(s00 + 1) << N03); s = s >= 0 ? s >> N03 : s + N07 >> N03; s = Math.max(Math.min(s, smax), -smax); a.set(s00 + 1, a.get(s00 + 1) + s); } s00 += 2; s10 += 2; } } /* * Adjust curvature difference hc */ for (i = 2; i < nxtop - 2; i += 2) { s00 = ny * i + 2; s10 = s00 + ny; for (j = 2; j < nytop - 2; j += 2) { /* * ------------------ y | hmp | | hpp | | ------------------ | | | h0 | | | ------------------ -------x * | hmm | | hpm | ------------------ */ hmm = a.get(s00 - ny2 - 2); hpm = a.get(s00 + ny2 - 2); hmp = a.get(s00 - ny2 + 2); hpp = a.get(s00 + ny2 + 2); h0 = a.get(s00); /* * diff = 64 * hc value that would match h0 in neighboring zones */ diff = hpp + hmm - hmp - hpm; /* * 2 times x,y slopes in this zone */ hx2 = a.get(s10) << 1; hy2 = a.get(s00 + 1) << 1; /* * monotonicity constraints on diff */ m1 = Math.min(Math.max(hpp - h0, 0) - hx2 - hy2, Math.max(h0 - hpm, 0) + hx2 - hy2); m2 = Math.min(Math.max(h0 - hmp, 0) - hx2 + hy2, Math.max(hmm - h0, 0) + hx2 + hy2); dmax = Math.min(m1, m2) << BITS_OF_1_NYBBLE; m1 = Math.max(Math.min(hpp - h0, 0) - hx2 - hy2, Math.min(h0 - hpm, 0) + hx2 - hy2); m2 = Math.max(Math.min(h0 - hmp, 0) - hx2 + hy2, Math.min(hmm - h0, 0) + hx2 + hy2); dmin = Math.max(m1, m2) << BITS_OF_1_NYBBLE; /* * if monotonicity would set slope = 0 then don't change hc. note dmax>=0, dmin<=0. */ if (dmin < dmax) { diff = Math.max(Math.min(diff, dmax), dmin); /* * Compute change in slope limited to range +/- smax. Careful with rounding negative numbers when * using shift for divide by 64. */ s = diff - (a.get(s10 + 1) << N06); s = s >= 0 ? s >> N06 : s + N63 >> N06; s = Math.max(Math.min(s, smax), -smax); a.set(s10 + 1, a.get(s10 + 1) + s); } s00 += 2; s10 += 2; } } } private int inputBit(ByteBuffer infile) { if (bitsToGo == 0) { /* Read the next byte if no */ buffer2 = infile.get() & BYTE_MASK; bitsToGo = BITS_OF_1_BYTE; } /* * Return the next bit */ bitsToGo--; return buffer2 >> bitsToGo & 1; } /* * Huffman decoding for fixed codes Coded values range from 0-15 Huffman code values (hex): 3e, 00, 01, 08, 02, 09, * 1a, 1b, 03, 1c, 0a, 1d, 0b, 1e, 3f, 0c and number of bits in each code: 6, 3, 3, 4, 3, 4, 5, 5, 3, 5, 4, 5, 4, 5, * 6, 4 */ private int inputHuffman(ByteBuffer infile) { int c; /* * get first 3 bits to start */ c = inputNbits(infile, N03); if (c < N04) { /* * this is all we need return 1,2,4,8 for c=0,1,2,3 */ return 1 << c; } /* * get the next bit */ c = inputBit(infile) | c << 1; if (c < N13) { /* * OK, 4 bits is enough */ switch (c) { case N08: return N03; case N09: return N05; case N10: return N10; case N11: return N12; case N12: return N15; default: } } /* * get yet another bit */ c = inputBit(infile) | c << 1; if (c < N31) { /* * OK, 5 bits is enough */ switch (c) { case N26: return N06; case N27: return N07; case N28: return N09; case N29: return N11; case N30: return N13; default: } } /* * need the 6th bit */ c = inputBit(infile) | c << 1; if (c == N62) { return 0; } return N14; } private int inputNbits(ByteBuffer infile, int n) { if (bitsToGo < n) { /* * need another byte's worth of bits */ buffer2 = buffer2 << BITS_OF_1_BYTE | infile.get() & BYTE_MASK; bitsToGo += BITS_OF_1_BYTE; } /* * now pick off the first n bits */ bitsToGo -= n; /* there was a slight gain in speed by replacing the following line */ /* return( (buffer2>>bits_to_go) & ((1<> bitsToGo & MASKS[n]; } /* INITIALIZE BIT INPUT */ private int inputNnybble(ByteBuffer infile, int n, byte[] array) { /* * copy n 4-bit nybbles from infile to the lower 4 bits of tiledImageOperation */ int ii, kk, shift1, shift2; /* * forcing byte alignment doesn;t help, and even makes it go slightly slower if (bits_to_go != 8) * input_nbits(infile, bits_to_go); */ if (n == 1) { array[0] = (byte) inputNybble(infile); return 0; } if (bitsToGo == BITS_OF_1_BYTE) { /* * already have 2 full nybbles in buffer2, so backspace the infile tiledImageOperation to reuse last char */ infile.position(infile.position() - 1); bitsToGo = 0; } /* bits_to_go now has a value in the range 0 - 7. After adding */ /* another byte, bits_to_go effectively will be in range 8 - 15 */ shift1 = bitsToGo + BITS_OF_1_NYBBLE; /* * shift1 will be in range 4 - 11 */ shift2 = bitsToGo; /* shift2 will be in range 0 - 7 */ kk = 0; /* special case */ if (bitsToGo == 0) { for (ii = 0; ii < n / 2; ii++) { /* * refill the buffer with next byte */ buffer2 = buffer2 << BITS_OF_1_BYTE | infile.get() & BYTE_MASK; array[kk] = (byte) (buffer2 >> BITS_OF_1_NYBBLE & NYBBLE_MASK); array[kk + 1] = (byte) (buffer2 & NYBBLE_MASK); /* * no shift required */ kk += 2; } } else { for (ii = 0; ii < n / 2; ii++) { /* * refill the buffer with next byte */ buffer2 = buffer2 << BITS_OF_1_BYTE | infile.get() & BYTE_MASK; array[kk] = (byte) (buffer2 >> shift1 & NYBBLE_MASK); array[kk + 1] = (byte) (buffer2 >> shift2 & NYBBLE_MASK); kk += 2; } } if (ii * 2 != n) { /* have to read last odd byte */ array[n - 1] = (byte) inputNybble(infile); } return buffer2 >> bitsToGo & NYBBLE_MASK; } private int inputNybble(ByteBuffer infile) { if (bitsToGo < BITS_OF_1_NYBBLE) { /* * need another byte's worth of bits */ buffer2 = buffer2 << BITS_OF_1_BYTE | infile.get() & BYTE_MASK; bitsToGo += BITS_OF_1_BYTE; } /* * now pick off the first 4 bits */ bitsToGo -= BITS_OF_1_NYBBLE; return buffer2 >> bitsToGo & NYBBLE_MASK; } /** * Copy 4-bit values from a[(nx+1)/2,(ny+1)/2] to b[nx,ny], expanding each value to 2x2 pixels and inserting into * bitplane BIT of B. A,B may NOT be same tiledImageOperation (it wouldn't make sense to be inserting bits into the * same tiledImageOperation anyway.) */ private void qtreeBitins64(byte[] a, int lnx, int lny, LongArrayPointer b, int n, int bit) { int i, j, s00; long planeVal = 1L << bit; // expand each 2x2 block ByteBuffer k = ByteBuffer.wrap(a); /* k is index of a[i/2,j/2] */ for (i = 0; i < lnx - 1; i += 2) { s00 = n * i; /* s00 is index of b[i,j] */ // Note: this code appears to run very slightly faster on a 32-bit // linux machine using s00+n rather than the s10 intermediate // variable // s10 = s00+n; *//* s10 is index of b[i+1,j] for (j = 0; j < lny - 1; j += 2) { byte value = k.get(); if ((value & BIT_ONE) != ZERO) { b.bitOr(s00 + n + 1, planeVal); } if ((value & BIT_TWO) != ZERO) { b.bitOr(s00 + n, planeVal); } if ((value & BIT_THREE) != ZERO) { b.bitOr(s00 + 1, planeVal); } if ((value & BIT_FOUR) != ZERO) { b.bitOr(s00, planeVal); } // b.bitOr(s10+1, ((LONGLONG) ( a[k] & 1)) << bit; b.bitOr(s10 , // ((((LONGLONG)a[k])>>1) & 1) << bit; b.bitOr(s00+1, // ((((LONGLONG)a[k])>>2) & 1) << bit; b.bitOr(s00 // ,((((LONGLONG)a[k])>>3) & 1) << bit; s00 += 2; /* s10 += 2; */ } if (j < lny) { // row size is odd, do last element in row s00+1, s10+1 are off // edge byte value = k.get(); if ((value & BIT_TWO) != ZERO) { b.bitOr(s00 + n, planeVal); } if ((value & BIT_FOUR) != ZERO) { b.bitOr(s00, planeVal); } // b.bitOr(s10 , ((((LONGLONG)a[k])>>1) & 1) << bit; b.bitOr(s00 // , ((((LONGLONG)a[k])>>3) & 1) << bit; } } if (i < lnx) { // column size is odd, do last row s10, s10+1 are off edge s00 = n * i; for (j = 0; j < lny - 1; j += 2) { byte value = k.get(); if ((value & BIT_THREE) != ZERO) { b.bitOr(s00 + 1, planeVal); } if ((value & BIT_FOUR) != ZERO) { b.bitOr(s00, planeVal); } // b.bitOr(s00+1, ((((LONGLONG)a[k])>>2) & 1) << bit; // b.bitOr(s00 , ((((LONGLONG)a[k])>>3) & 1) << bit; s00 += 2; } if (j < lny) { // both row and column size are odd, do corner element s00+1, // s10, s10+1 are off edge if ((k.get() & BIT_FOUR) != ZERO) { b.bitOr(s00, planeVal); } // b.bitOr(s00 , ((((LONGLONG)a[k])>>3) & 1) << bit; } } } /** * copy 4-bit values from a[(nx+1)/2,(ny+1)/2] to b[nx,ny], expanding each value to 2x2 pixels a,b may be same * tiledImageOperation */ private void qtreeCopy(byte[] a, int lnx, int lny, byte[] b, int n) { int i, j, k, nx2, ny2; int s00, s10; // first copy 4-bit values to b start at end in case a,b are same // tiledImageOperation nx2 = (lnx + 1) / 2; ny2 = (lny + 1) / 2; k = ny2 * (nx2 - 1) + ny2 - 1; /* k is index of a[i,j] */ for (i = nx2 - 1; i >= 0; i--) { s00 = 2 * (n * i + ny2 - 1); /* s00 is index of b[2*i,2*j] */ for (j = ny2 - 1; j >= 0; j--) { b[s00] = a[k]; k--; s00 -= 2; } } for (i = 0; i < lnx - 1; i += 2) { // now expand each 2x2 block // Note: Unlike the case in qtree_bitins, this code runs faster on a // 32-bit linux machine using the s10 intermediate variable, rather // that using s00+n. Go figure! s00 = n * i; // s00 is index of b[i,j] s10 = s00 + n; // s10 is index of b[i+1,j] for (j = 0; j < lny - 1; j += 2) { b[s10 + 1] = (b[s00] & BIT_ONE) == ZERO ? ZERO : BIT_ONE; b[s10] = (b[s00] & BIT_TWO) == ZERO ? ZERO : BIT_ONE; b[s00 + 1] = (b[s00] & BIT_THREE) == ZERO ? ZERO : BIT_ONE; b[s00] = (b[s00] & BIT_FOUR) == ZERO ? ZERO : BIT_ONE; s00 += 2; s10 += 2; } if (j < lny) { // row size is odd, do last element in row s00+1, s10+1 are off // edge not worth converting this to use 16 case statements b[s10] = (byte) (b[s00] >> 1 & 1); b[s00] = (byte) (b[s00] >> N03 & 1); } } if (i < lnx) { // column size is odd, do last row s10, s10+1 are off edge s00 = n * i; for (j = 0; j < lny - 1; j += 2) { // not worth converting this to use 16 case statements b[s00 + 1] = (byte) (b[s00] >> 2 & 1); b[s00] = (byte) (b[s00] >> N03 & 1); s00 += 2; } if (j < lny) { // both row and column size are odd, do corner element s00+1, // s10, s10+1 are off edge not worth converting this to use 16 // case statements b[s00] = (byte) (b[s00] >> N03 & 1); } } } /** * char *infile; long a[]; a is 2-D tiledImageOperation with dimensions (n,n) int n; length of full row in a int * nqx; partial length of row to decode int nqy; partial length of column (<=n) int nbitplanes; number of bitplanes * to decode */ private int qtreeDecode64(ByteBuffer infile, LongArrayPointer a, int n, int nqx, int nqy, int nbitplanes) { int k, bit, b; int nx2, ny2, nfx, nfy, c; byte[] scratch; /* * log2n is log2 of max(nqx,nqy) rounded up to next power of 2 */ int nqmax = nqx > nqy ? nqx : nqy; int log2n = calculateLog2N(nqmax); /* * allocate scratch tiledImageOperation for working space */ int nqx2 = (nqx + 1) / 2; int nqy2 = (nqy + 1) / 2; scratch = new byte[nqx2 * nqy2]; /* * now decode each bit plane, starting at the top A is assumed to be initialized to zero */ for (bit = nbitplanes - 1; bit >= 0; bit--) { /* * Was bitplane was quadtree-coded or written directly? */ b = inputNybble(infile); if (b == 0) { /* * bit map was written directly */ readBdirect64(infile, a, n, nqx, nqy, scratch, bit); } else if (b != NYBBLE_MASK) { throw new RuntimeException("Compression error"); } else { /* * bitmap was quadtree-coded, do log2n expansions read first code */ scratch[0] = (byte) inputHuffman(infile); /* * now do log2n expansions, reading codes from file as necessary */ nx2 = 1; ny2 = 1; nfx = nqx; nfy = nqy; c = 1 << log2n; for (k = 1; k < log2n; k++) { /* * this somewhat cryptic code generates the sequence n[k-1] = (n[k]+1)/2 where n[log2n]=nqx or nqy */ c = c >> 1; nx2 = nx2 << 1; ny2 = ny2 << 1; if (nfx <= c) { nx2--; } else { nfx -= c; } if (nfy <= c) { ny2--; } else { nfy -= c; } qtreeExpand(infile, scratch, nx2, ny2, scratch); } /* * now copy last set of 4-bit codes to bitplane bit of tiledImageOperation a */ qtreeBitins64(scratch, nqx, nqy, a, n, bit); } } return 0; } /* * do one quadtree expansion step on tiledImageOperation a[(nqx+1)/2,(nqy+1)/2] results put into b[nqx,nqy] (which * may be the same as a) */ private void qtreeExpand(ByteBuffer infile, byte[] a, int nx2, int ny2, byte[] b) { int i; /* * first copy a to b, expanding each 4-bit value */ qtreeCopy(a, nx2, ny2, b, ny2); /* * now read new 4-bit values into b for each non-zero element */ for (i = nx2 * ny2 - 1; i >= 0; i--) { if (b[i] != 0) { b[i] = (byte) inputHuffman(infile); } } } private void readBdirect64(ByteBuffer infile, LongArrayPointer a, int n, int nqx, int nqy, byte[] scratch, int bit) { /* * read bit image packed 4 pixels/nybble */ /* * int i; for (i = 0; i < ((nqx+1)/2) * ((nqy+1)/2); i++) { scratch[i] = input_nybble(infile); } */ inputNnybble(infile, (nqx + 1) / 2 * ((nqy + 1) / 2), scratch); /* * insert in bitplane BIT of image A */ qtreeBitins64(scratch, nqx, nqy, a, n, bit); } /* * ########################################################################## ## */ private void startInputingBits() { /* * Buffer starts out with no bits in it */ bitsToGo = 0; } private void undigitize64(LongArrayPointer a) { long scale64; /* * multiply by scale */ if (scale <= 1) { return; } scale64 = scale; /* * use a 64-bit int for efficiency in the big loop */ for (int index = 0; index < a.a.length; index++) { a.a[index] = a.a[index] * scale64; } } /** * long a[]; tiledImageOperation to shuffle int n; number of elements to shuffle int n2; second dimension long * tmp[]; scratch storage */ private void unshuffle64(LongArrayPointer a, int n, int n2, long[] tmp) { int i; int nhalf; LongArrayPointer p1, p2, pt; /* * copy 2nd half of tiledImageOperation to tmp */ nhalf = n + 1 >> 1; pt = new LongArrayPointer(tmp); p1 = a.copy(n2 * nhalf); /* pointer to a[i] */ for (i = nhalf; i < n; i++) { pt.set(p1.get()); p1.offset += n2; pt.offset++; } /* * distribute 1st half of tiledImageOperation to even elements */ p2 = a.copy(n2 * (nhalf - 1)); /* pointer to a[i] */ p1 = a.copy(n2 * (nhalf - 1) << 1); /* pointer to a[2*i] */ for (i = nhalf - 1; i >= 0; i--) { p1.set(p2.get()); p2.offset -= n2; p1.offset -= n2 + n2; } /* * now distribute 2nd half of tiledImageOperation (in tmp) to odd elements */ pt = new LongArrayPointer(tmp); p1 = a.copy(n2); /* pointer to a[i] */ for (i = 1; i < n; i += 2) { p1.set(pt.get()); p1.offset += n2 + n2; pt.offset++; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy