#include "fitsio2.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static long noutchar;
static long noutmax;
static int htrans(int a[], int nx, int ny);
static void digitize(int a[], int nx, int ny, int scale);
static int encode(char *outfile, long *nlen, int a[], int nx, int ny,
int scale);
static void shuffle(int a[], int n, int n2, int tmp[]);
static int htrans64(LONGLONG a[], int nx, int ny);
static void digitize64(LONGLONG a[], int nx, int ny, int scale);
static int encode64(char *outfile, long *nlen, LONGLONG a[], int nx, int ny,
int scale);
static void shuffle64(LONGLONG a[], int n, int n2, LONGLONG tmp[]);
static void writeint(char *outfile, int a);
static void writelonglong(char *outfile, LONGLONG a);
static int doencode(char *outfile, int a[], int nx, int ny,
unsigned char nbitplanes[3]);
static int doencode64(char *outfile, LONGLONG a[], int nx, int ny,
unsigned char nbitplanes[3]);
static int qwrite(char *file, char buffer[], int n);
static int qtree_encode(char *outfile, int a[], int n, int nqx, int nqy,
int nbitplanes);
static int qtree_encode64(char *outfile, LONGLONG a[], int n, int nqx, int nqy,
int nbitplanes);
static void start_outputing_bits(void);
static void done_outputing_bits(char *outfile);
static void output_nbits(char *outfile, int bits, int n);
static void qtree_onebit(int a[], int n, int nx, int ny, unsigned char b[],
int bit);
static void qtree_onebit64(LONGLONG a[], int n, int nx, int ny,
unsigned char b[], int bit);
static void qtree_reduce(unsigned char a[], int n, int nx, int ny,
unsigned char b[]);
static int bufcopy(unsigned char a[], int n, unsigned char buffer[], int *b,
int bmax);
static void write_bdirect(char *outfile, int a[], int n, int nqx, int nqy,
unsigned char scratch[], int bit);
static void write_bdirect64(char *outfile, LONGLONG a[], int n, int nqx,
int nqy, unsigned char scratch[], int bit);
static void output_nybble(char *outfile, int bits);
static void output_nnybble(char *outfile, int n, unsigned char array[]);
#define output_huffman(outfile, c) output_nbits(outfile, code[c], ncode[c])
#ifndef LONGLONG_TYPE
typedef long long LONGLONG;
typedef unsigned long long ULONGLONG;
#define LONGLONG_TYPE
#endif
#define DATA_COMPRESSION_ERR 413
#define DATA_DECOMPRESSION_ERR 414
void ffpmsg(const char *err_message){};
#define FFLOCK
#define FFUNLOCK
#define N_RANDOM 10000
#define MEMORY_ALLOCATION 113
#define NO_DITHER -1
#define SUBTRACTIVE_DITHER_1 1
#define SUBTRACTIVE_DITHER_2 2
int fits_init_randoms(void);
int fits_hcompress(int *a, int ny, int nx, int scale, char *output,
long *nbytes, int *status) {
int stat;
if (*status > 0)
return (*status);
stat = htrans(a, nx, ny);
if (stat) {
*status = stat;
return (*status);
}
digitize(a, nx, ny, scale);
FFLOCK;
noutmax = *nbytes;
*nbytes = 0;
stat = encode(output, nbytes, a, nx, ny, scale);
FFUNLOCK;
*status = stat;
return (*status);
}
int fits_hcompress64(LONGLONG *a, int ny, int nx, int scale, char *output,
long *nbytes, int *status) {
int stat;
if (*status > 0)
return (*status);
stat = htrans64(a, nx, ny);
if (stat) {
*status = stat;
return (*status);
}
digitize64(a, nx, ny, scale);
FFLOCK;
noutmax = *nbytes;
*nbytes = 0;
stat = encode64(output, nbytes, a, nx, ny, scale);
FFUNLOCK;
*status = stat;
return (*status);
}
static int htrans(int a[], int nx, int ny) {
int nmax, log2n, h0, hx, hy, hc, nxtop, nytop, i, j, k;
int oddx, oddy;
int shift, mask, mask2, prnd, prnd2, nrnd2;
int s10, s00;
int *tmp;
nmax = (nx > ny) ? nx : ny;
log2n = (int)(log((float)nmax) / log(2.0) + 0.5);
if (nmax > (1 << log2n)) {
log2n += 1;
}
tmp = (int *)malloc(((nmax + 1) / 2) * sizeof(int));
if (tmp == (int *)NULL) {
ffpmsg("htrans: insufficient memory");
return (DATA_COMPRESSION_ERR);
}
shift = 0;
mask = -2;
mask2 = mask << 1;
prnd = 1;
prnd2 = prnd << 1;
nrnd2 = prnd2 - 1;
nxtop = nx;
nytop = ny;
for (k = 0; k < log2n; k++) {
oddx = nxtop % 2;
oddy = nytop % 2;
for (i = 0; i < nxtop - oddx; i += 2) {
s00 = i * ny;
s10 = s00 + ny;
for (j = 0; j < nytop - oddy; j += 2) {
h0 = (a[s10 + 1] + a[s10] + a[s00 + 1] + a[s00]) >> shift;
hx = (a[s10 + 1] + a[s10] - a[s00 + 1] - a[s00]) >> shift;
hy = (a[s10 + 1] - a[s10] + a[s00 + 1] - a[s00]) >> shift;
hc = (a[s10 + 1] - a[s10] - a[s00 + 1] + a[s00]) >> shift;
a[s10 + 1] = hc;
a[s10] = ((hx >= 0) ? (hx + prnd) : hx) & mask;
a[s00 + 1] = ((hy >= 0) ? (hy + prnd) : hy) & mask;
a[s00] = ((h0 >= 0) ? (h0 + prnd2) : (h0 + nrnd2)) & mask2;
s00 += 2;
s10 += 2;
}
if (oddy) {
h0 = (a[s10] + a[s00]) << (1 - shift);
hx = (a[s10] - a[s00]) << (1 - shift);
a[s10] = ((hx >= 0) ? (hx + prnd) : hx) & mask;
a[s00] = ((h0 >= 0) ? (h0 + prnd2) : (h0 + nrnd2)) & mask2;
s00 += 1;
s10 += 1;
}
}
if (oddx) {
s00 = i * ny;
for (j = 0; j < nytop - oddy; j += 2) {
h0 = (a[s00 + 1] + a[s00]) << (1 - shift);
hy = (a[s00 + 1] - a[s00]) << (1 - shift);
a[s00 + 1] = ((hy >= 0) ? (hy + prnd) : hy) & mask;
a[s00] = ((h0 >= 0) ? (h0 + prnd2) : (h0 + nrnd2)) & mask2;
s00 += 2;
}
if (oddy) {
h0 = a[s00] << (2 - shift);
a[s00] = ((h0 >= 0) ? (h0 + prnd2) : (h0 + nrnd2)) & mask2;
}
}
for (i = 0; i < nxtop; i++) {
shuffle(&a[ny * i], nytop, 1, tmp);
}
for (j = 0; j < nytop; j++) {
shuffle(&a[j], nxtop, ny, tmp);
}
nxtop = (nxtop + 1) >> 1;
nytop = (nytop + 1) >> 1;
shift = 1;
mask = mask2;
prnd = prnd2;
mask2 = mask2 << 1;
prnd2 = prnd2 << 1;
nrnd2 = prnd2 - 1;
}
free(tmp);
return (0);
}
static int htrans64(LONGLONG a[], int nx, int ny) {
int nmax, log2n, nxtop, nytop, i, j, k;
int oddx, oddy;
int shift;
int s10, s00;
LONGLONG h0, hx, hy, hc, prnd, prnd2, nrnd2, mask, mask2;
LONGLONG *tmp;
nmax = (nx > ny) ? nx : ny;
log2n = (int)(log((float)nmax) / log(2.0) + 0.5);
if (nmax > (1 << log2n)) {
log2n += 1;
}
tmp = (LONGLONG *)malloc(((nmax + 1) / 2) * sizeof(LONGLONG));
if (tmp == (LONGLONG *)NULL) {
ffpmsg("htrans64: insufficient memory");
return (DATA_COMPRESSION_ERR);
}
shift = 0;
mask = (LONGLONG)-2;
mask2 = mask << 1;
prnd = (LONGLONG)1;
prnd2 = prnd << 1;
nrnd2 = prnd2 - 1;
nxtop = nx;
nytop = ny;
for (k = 0; k < log2n; k++) {
oddx = nxtop % 2;
oddy = nytop % 2;
for (i = 0; i < nxtop - oddx; i += 2) {
s00 = i * ny;
s10 = s00 + ny;
for (j = 0; j < nytop - oddy; j += 2) {
h0 = (a[s10 + 1] + a[s10] + a[s00 + 1] + a[s00]) >> shift;
hx = (a[s10 + 1] + a[s10] - a[s00 + 1] - a[s00]) >> shift;
hy = (a[s10 + 1] - a[s10] + a[s00 + 1] - a[s00]) >> shift;
hc = (a[s10 + 1] - a[s10] - a[s00 + 1] + a[s00]) >> shift;
a[s10 + 1] = hc;
a[s10] = ((hx >= 0) ? (hx + prnd) : hx) & mask;
a[s00 + 1] = ((hy >= 0) ? (hy + prnd) : hy) & mask;
a[s00] = ((h0 >= 0) ? (h0 + prnd2) : (h0 + nrnd2)) & mask2;
s00 += 2;
s10 += 2;
}
if (oddy) {
h0 = (a[s10] + a[s00]) << (1 - shift);
hx = (a[s10] - a[s00]) << (1 - shift);
a[s10] = ((hx >= 0) ? (hx + prnd) : hx) & mask;
a[s00] = ((h0 >= 0) ? (h0 + prnd2) : (h0 + nrnd2)) & mask2;
s00 += 1;
s10 += 1;
}
}
if (oddx) {
s00 = i * ny;
for (j = 0; j < nytop - oddy; j += 2) {
h0 = (a[s00 + 1] + a[s00]) << (1 - shift);
hy = (a[s00 + 1] - a[s00]) << (1 - shift);
a[s00 + 1] = ((hy >= 0) ? (hy + prnd) : hy) & mask;
a[s00] = ((h0 >= 0) ? (h0 + prnd2) : (h0 + nrnd2)) & mask2;
s00 += 2;
}
if (oddy) {
h0 = a[s00] << (2 - shift);
a[s00] = ((h0 >= 0) ? (h0 + prnd2) : (h0 + nrnd2)) & mask2;
}
}
for (i = 0; i < nxtop; i++) {
shuffle64(&a[ny * i], nytop, 1, tmp);
}
for (j = 0; j < nytop; j++) {
shuffle64(&a[j], nxtop, ny, tmp);
}
nxtop = (nxtop + 1) >> 1;
nytop = (nytop + 1) >> 1;
shift = 1;
mask = mask2;
prnd = prnd2;
mask2 = mask2 << 1;
prnd2 = prnd2 << 1;
nrnd2 = prnd2 - 1;
}
free(tmp);
return (0);
}
static void shuffle(int a[], int n, int n2, int tmp[]) {
int i;
int *p1, *p2, *pt;
pt = tmp;
p1 = &a[n2];
for (i = 1; i < n; i += 2) {
*pt = *p1;
pt += 1;
p1 += (n2 + n2);
}
p1 = &a[n2];
p2 = &a[n2 + n2];
for (i = 2; i < n; i += 2) {
*p1 = *p2;
p1 += n2;
p2 += (n2 + n2);
}
pt = tmp;
for (i = 1; i < n; i += 2) {
*p1 = *pt;
p1 += n2;
pt += 1;
}
}
static void shuffle64(LONGLONG a[], int n, int n2, LONGLONG tmp[]) {
int i;
LONGLONG *p1, *p2, *pt;
pt = tmp;
p1 = &a[n2];
for (i = 1; i < n; i += 2) {
*pt = *p1;
pt += 1;
p1 += (n2 + n2);
}
p1 = &a[n2];
p2 = &a[n2 + n2];
for (i = 2; i < n; i += 2) {
*p1 = *p2;
p1 += n2;
p2 += (n2 + n2);
}
pt = tmp;
for (i = 1; i < n; i += 2) {
*p1 = *pt;
p1 += n2;
pt += 1;
}
}
static void digitize(int a[], int nx, int ny, int scale) {
int d, *p;
if (scale <= 1)
return;
d = (scale + 1) / 2 - 1;
for (p = a; p <= &a[nx * ny - 1]; p++)
*p = ((*p > 0) ? (*p + d) : (*p - d)) / scale;
}
static void digitize64(LONGLONG a[], int nx, int ny, int scale) {
LONGLONG d, *p, scale64;
if (scale <= 1)
return;
d = (scale + 1) / 2 - 1;
scale64 = scale;
for (p = a; p <= &a[nx * ny - 1]; p++)
*p = ((*p > 0) ? (*p + d) : (*p - d)) / scale64;
}
static char code_magic[2] = {(char)0xDD, (char)0x99};
static int encode(char *outfile, long *nlength, int a[], int nx, int ny,
int scale) {
int nel, nx2, ny2, i, j, k, q, vmax[3], nsign, bits_to_go;
unsigned char nbitplanes[3];
unsigned char *signbits;
int stat;
noutchar =
0;
nel = nx * ny;
qwrite(outfile, code_magic, sizeof(code_magic));
writeint(outfile, nx);
writeint(outfile, ny);
writeint(outfile, scale);
writelonglong(outfile, (LONGLONG)a[0]);
a[0] = 0;
signbits = (unsigned char *)calloc(1, (nel + 7) / 8);
if (signbits == (unsigned char *)NULL) {
ffpmsg("encode: insufficient memory");
return (DATA_COMPRESSION_ERR);
}
nsign = 0;
bits_to_go = 8;
for (i = 0; i < nel; i++) {
if (a[i] > 0) {
signbits[nsign] <<= 1;
bits_to_go -= 1;
} else if (a[i] < 0) {
signbits[nsign] <<= 1;
signbits[nsign] |= 1;
bits_to_go -= 1;
a[i] = -a[i];
}
if (bits_to_go == 0) {
bits_to_go = 8;
nsign += 1;
}
}
if (bits_to_go != 8) {
signbits[nsign] <<= bits_to_go;
nsign += 1;
}
for (q = 0; q < 3; q++) {
vmax[q] = 0;
}
nx2 = (nx + 1) / 2;
ny2 = (ny + 1) / 2;
j = 0;
k = 0;
for (i = 0; i < nel; i++) {
q = (j >= ny2) + (k >= nx2);
if (vmax[q] < a[i])
vmax[q] = a[i];
if (++j >= ny) {
j = 0;
k += 1;
}
}
for (q = 0; q < 3; q++) {
for (nbitplanes[q] = 0; vmax[q] > 0;
vmax[q] = vmax[q] >> 1, nbitplanes[q]++)
;
}
if (0 == qwrite(outfile, (char *)nbitplanes, sizeof(nbitplanes))) {
*nlength = noutchar;
ffpmsg("encode: output buffer too small");
return (DATA_COMPRESSION_ERR);
}
stat = doencode(outfile, a, nx, ny, nbitplanes);
if (nsign > 0) {
if (0 == qwrite(outfile, (char *)signbits, nsign)) {
free(signbits);
*nlength = noutchar;
ffpmsg("encode: output buffer too small");
return (DATA_COMPRESSION_ERR);
}
}
free(signbits);
*nlength = noutchar;
if (noutchar >= noutmax) {
ffpmsg("encode: output buffer too small");
return (DATA_COMPRESSION_ERR);
}
return (stat);
}
static int encode64(char *outfile, long *nlength, LONGLONG a[], int nx, int ny,
int scale) {
int nel, nx2, ny2, i, j, k, q, nsign, bits_to_go;
LONGLONG vmax[3];
unsigned char nbitplanes[3];
unsigned char *signbits;
int stat;
noutchar =
0;
nel = nx * ny;
qwrite(outfile, code_magic, sizeof(code_magic));
writeint(outfile, nx);
writeint(outfile, ny);
writeint(outfile, scale);
writelonglong(outfile, a[0]);
a[0] = 0;
signbits = (unsigned char *)calloc(1, (nel + 7) / 8);
if (signbits == (unsigned char *)NULL) {
ffpmsg("encode64: insufficient memory");
return (DATA_COMPRESSION_ERR);
}
nsign = 0;
bits_to_go = 8;
for (i = 0; i < nel; i++) {
if (a[i] > 0) {
signbits[nsign] <<= 1;
bits_to_go -= 1;
} else if (a[i] < 0) {
signbits[nsign] <<= 1;
signbits[nsign] |= 1;
bits_to_go -= 1;
a[i] = -a[i];
}
if (bits_to_go == 0) {
bits_to_go = 8;
nsign += 1;
}
}
if (bits_to_go != 8) {
signbits[nsign] <<= bits_to_go;
nsign += 1;
}
for (q = 0; q < 3; q++) {
vmax[q] = 0;
}
nx2 = (nx + 1) / 2;
ny2 = (ny + 1) / 2;
j = 0;
k = 0;
for (i = 0; i < nel; i++) {
q = (j >= ny2) + (k >= nx2);
if (vmax[q] < a[i])
vmax[q] = a[i];
if (++j >= ny) {
j = 0;
k += 1;
}
}
for (q = 0; q < 3; q++) {
for (nbitplanes[q] = 0; vmax[q] > 0;
vmax[q] = vmax[q] >> 1, nbitplanes[q]++)
;
}
if (0 == qwrite(outfile, (char *)nbitplanes, sizeof(nbitplanes))) {
*nlength = noutchar;
ffpmsg("encode: output buffer too small");
return (DATA_COMPRESSION_ERR);
}
stat = doencode64(outfile, a, nx, ny, nbitplanes);
if (nsign > 0) {
if (0 == qwrite(outfile, (char *)signbits, nsign)) {
free(signbits);
*nlength = noutchar;
ffpmsg("encode: output buffer too small");
return (DATA_COMPRESSION_ERR);
}
}
free(signbits);
*nlength = noutchar;
if (noutchar >= noutmax) {
ffpmsg("encode64: output buffer too small");
return (DATA_COMPRESSION_ERR);
}
return (stat);
}
static void writeint(char *outfile, int a) {
int i;
unsigned char b[4];
for (i = 3; i >= 0; i--) {
b[i] = a & 0x000000ff;
a >>= 8;
}
for (i = 0; i < 4; i++)
qwrite(outfile, (char *)&b[i], 1);
}
static void writelonglong(char *outfile, LONGLONG a) {
int i;
unsigned char b[8];
for (i = 7; i >= 0; i--) {
b[i] = (unsigned char)(a & 0x000000ff);
a >>= 8;
}
for (i = 0; i < 8; i++)
qwrite(outfile, (char *)&b[i], 1);
}
static int qwrite(char *file, char buffer[], int n) {
if (noutchar + n > noutmax)
return (0);
memcpy(&file[noutchar], buffer, n);
noutchar += n;
return (n);
}
static int doencode(char *outfile, int a[], int nx, int ny,
unsigned char nbitplanes[3]) {
int nx2, ny2, stat;
nx2 = (nx + 1) / 2;
ny2 = (ny + 1) / 2;
start_outputing_bits();
stat = qtree_encode(outfile, &a[0], ny, nx2, ny2, nbitplanes[0]);
if (!stat)
stat = qtree_encode(outfile, &a[ny2], ny, nx2, ny / 2, nbitplanes[1]);
if (!stat)
stat = qtree_encode(outfile, &a[ny * nx2], ny, nx / 2, ny2, nbitplanes[1]);
if (!stat)
stat = qtree_encode(outfile, &a[ny * nx2 + ny2], ny, nx / 2, ny / 2,
nbitplanes[2]);
output_nybble(outfile, 0);
done_outputing_bits(outfile);
return (stat);
}
static int doencode64(char *outfile, LONGLONG a[], int nx, int ny,
unsigned char nbitplanes[3]) {
int nx2, ny2, stat;
nx2 = (nx + 1) / 2;
ny2 = (ny + 1) / 2;
start_outputing_bits();
stat = qtree_encode64(outfile, &a[0], ny, nx2, ny2, nbitplanes[0]);
if (!stat)
stat = qtree_encode64(outfile, &a[ny2], ny, nx2, ny / 2, nbitplanes[1]);
if (!stat)
stat =
qtree_encode64(outfile, &a[ny * nx2], ny, nx / 2, ny2, nbitplanes[1]);
if (!stat)
stat = qtree_encode64(outfile, &a[ny * nx2 + ny2], ny, nx / 2, ny / 2,
nbitplanes[2]);
output_nybble(outfile, 0);
done_outputing_bits(outfile);
return (stat);
}
static LONGLONG bitcount;
static int buffer2;
static int bits_to_go2;
static void start_outputing_bits(void) {
buffer2 = 0;
bits_to_go2 = 8;
bitcount = 0;
}
static void output_nbits(char *outfile, int bits, int n) {
static int mask[9] = {0, 1, 3, 7, 15, 31, 63, 127, 255};
buffer2 <<= n;
buffer2 |= (bits & (*(mask + n)));
bits_to_go2 -= n;
if (bits_to_go2 <= 0) {
outfile[noutchar] = ((buffer2 >> (-bits_to_go2)) & 0xff);
if (noutchar < noutmax)
noutchar++;
bits_to_go2 += 8;
}
bitcount += n;
}
static void output_nybble(char *outfile, int bits) {
buffer2 = (buffer2 << 4) | (bits & 15);
bits_to_go2 -= 4;
if (bits_to_go2 <= 0) {
outfile[noutchar] = ((buffer2 >> (-bits_to_go2)) & 0xff);
if (noutchar < noutmax)
noutchar++;
bits_to_go2 += 8;
}
bitcount += 4;
}
static void output_nnybble(char *outfile, int n, unsigned char array[]) {
int ii, jj, kk = 0, shift;
if (n == 1) {
output_nybble(outfile, (int)array[0]);
return;
}
if (bits_to_go2 <= 4) {
output_nybble(outfile, array[0]);
kk++;
if (n == 2)
{
output_nybble(outfile, (int)array[1]);
return;
}
}
shift = 8 - bits_to_go2;
jj = (n - kk) / 2;
if (bits_to_go2 == 8) {
buffer2 = 0;
for (ii = 0; ii < jj; ii++) {
outfile[noutchar] = ((array[kk] & 15) << 4) | (array[kk + 1] & 15);
kk += 2;
noutchar++;
}
} else {
for (ii = 0; ii < jj; ii++) {
buffer2 = (buffer2 << 8) | ((array[kk] & 15) << 4) | (array[kk + 1] & 15);
kk += 2;
outfile[noutchar] = ((buffer2 >> shift) & 0xff);
noutchar++;
}
}
bitcount += (8 * (ii - 1));
if (kk != n)
output_nybble(outfile, (int)array[n - 1]);
return;
}
static void done_outputing_bits(char *outfile) {
if (bits_to_go2 < 8) {
outfile[noutchar] = (buffer2 << bits_to_go2);
if (noutchar < noutmax)
noutchar++;
bitcount += bits_to_go2;
}
}
static int code[16] = {0x3e, 0x00, 0x01, 0x08, 0x02, 0x09, 0x1a, 0x1b,
0x03, 0x1c, 0x0a, 0x1d, 0x0b, 0x1e, 0x3f, 0x0c};
static int ncode[16] = {6, 3, 3, 4, 3, 4, 5, 5, 3, 5, 4, 5, 4, 5, 6, 4};
static int bitbuffer, bits_to_go3;
static int qtree_encode(char *outfile, int a[], int n, int nqx, int nqy,
int nbitplanes) {
int log2n, i, k, bit, b, bmax, nqmax, nqx2, nqy2, nx, ny;
unsigned char *scratch, *buffer;
nqmax = (nqx > nqy) ? nqx : nqy;
log2n = (int)(log((float)nqmax) / log(2.0) + 0.5);
if (nqmax > (1 << log2n)) {
log2n += 1;
}
nqx2 = (nqx + 1) / 2;
nqy2 = (nqy + 1) / 2;
bmax = (nqx2 * nqy2 + 1) / 2;
scratch = (unsigned char *)malloc(2 * bmax);
buffer = (unsigned char *)malloc(bmax);
if ((scratch == (unsigned char *)NULL) || (buffer == (unsigned char *)NULL)) {
ffpmsg("qtree_encode: insufficient memory");
return (DATA_COMPRESSION_ERR);
}
for (bit = nbitplanes - 1; bit >= 0; bit--) {
b = 0;
bitbuffer = 0;
bits_to_go3 = 0;
qtree_onebit(a, n, nqx, nqy, scratch, bit);
nx = (nqx + 1) >> 1;
ny = (nqy + 1) >> 1;
if (bufcopy(scratch, nx * ny, buffer, &b, bmax)) {
write_bdirect(outfile, a, n, nqx, nqy, scratch, bit);
goto bitplane_done;
}
for (k = 1; k < log2n; k++) {
qtree_reduce(scratch, ny, nx, ny, scratch);
nx = (nx + 1) >> 1;
ny = (ny + 1) >> 1;
if (bufcopy(scratch, nx * ny, buffer, &b, bmax)) {
write_bdirect(outfile, a, n, nqx, nqy, scratch, bit);
goto bitplane_done;
}
}
output_nybble(outfile, 0xF);
if (b == 0) {
if (bits_to_go3 > 0) {
output_nbits(outfile, bitbuffer & ((1 << bits_to_go3) - 1),
bits_to_go3);
} else {
output_huffman(outfile, 0);
}
} else {
if (bits_to_go3 > 0) {
output_nbits(outfile, bitbuffer & ((1 << bits_to_go3) - 1),
bits_to_go3);
}
for (i = b - 1; i >= 0; i--) {
output_nbits(outfile, buffer[i], 8);
}
}
bitplane_done:;
}
free(buffer);
free(scratch);
return (0);
}
static int qtree_encode64(char *outfile, LONGLONG a[], int n, int nqx, int nqy,
int nbitplanes) {
int log2n, i, k, bit, b, nqmax, nqx2, nqy2, nx, ny;
int bmax;
unsigned char *scratch, *buffer;
nqmax = (nqx > nqy) ? nqx : nqy;
log2n = (int)(log((float)nqmax) / log(2.0) + 0.5);
if (nqmax > (1 << log2n)) {
log2n += 1;
}
nqx2 = (nqx + 1) / 2;
nqy2 = (nqy + 1) / 2;
bmax = ((nqx2) * (nqy2) + 1) / 2;
scratch = (unsigned char *)malloc(2 * bmax);
buffer = (unsigned char *)malloc(bmax);
if ((scratch == (unsigned char *)NULL) || (buffer == (unsigned char *)NULL)) {
ffpmsg("qtree_encode64: insufficient memory");
return (DATA_COMPRESSION_ERR);
}
for (bit = nbitplanes - 1; bit >= 0; bit--) {
b = 0;
bitbuffer = 0;
bits_to_go3 = 0;
qtree_onebit64(a, n, nqx, nqy, scratch, bit);
nx = (nqx + 1) >> 1;
ny = (nqy + 1) >> 1;
if (bufcopy(scratch, nx * ny, buffer, &b, bmax)) {
write_bdirect64(outfile, a, n, nqx, nqy, scratch, bit);
goto bitplane_done;
}
for (k = 1; k < log2n; k++) {
qtree_reduce(scratch, ny, nx, ny, scratch);
nx = (nx + 1) >> 1;
ny = (ny + 1) >> 1;
if (bufcopy(scratch, nx * ny, buffer, &b, bmax)) {
write_bdirect64(outfile, a, n, nqx, nqy, scratch, bit);
goto bitplane_done;
}
}
output_nybble(outfile, 0xF);
if (b == 0) {
if (bits_to_go3 > 0) {
output_nbits(outfile, bitbuffer & ((1 << bits_to_go3) - 1),
bits_to_go3);
} else {
output_huffman(outfile, 0);
}
} else {
if (bits_to_go3 > 0) {
output_nbits(outfile, bitbuffer & ((1 << bits_to_go3) - 1),
bits_to_go3);
}
for (i = b - 1; i >= 0; i--) {
output_nbits(outfile, buffer[i], 8);
}
}
bitplane_done:;
}
free(buffer);
free(scratch);
return (0);
}
static int bufcopy(unsigned char a[], int n, unsigned char buffer[], int *b,
int bmax) {
int i;
for (i = 0; i < n; i++) {
if (a[i] != 0) {
bitbuffer |= code[a[i]] << bits_to_go3;
bits_to_go3 += ncode[a[i]];
if (bits_to_go3 >= 8) {
buffer[*b] = bitbuffer & 0xFF;
*b += 1;
if (*b >= bmax)
return (1);
bitbuffer >>= 8;
bits_to_go3 -= 8;
}
}
}
return (0);
}
static void qtree_onebit(int a[], int n, int nx, int ny, unsigned char b[],
int bit) {
int i, j, k;
int b0, b1, b2, b3;
int s10, s00;
b0 = 1 << bit;
b1 = b0 << 1;
b2 = b0 << 2;
b3 = b0 << 3;
k = 0;
for (i = 0; i < nx - 1; i += 2) {
s00 = n * i;
s10 = s00 + n;
for (j = 0; j < ny - 1; j += 2) {
b[k] = ((a[s10 + 1] & b0) | ((a[s10] << 1) & b1) |
((a[s00 + 1] << 2) & b2) | ((a[s00] << 3) & b3)) >>
bit;
k += 1;
s00 += 2;
s10 += 2;
}
if (j < ny) {
b[k] = (((a[s10] << 1) & b1) | ((a[s00] << 3) & b3)) >> bit;
k += 1;
}
}
if (i < nx) {
s00 = n * i;
for (j = 0; j < ny - 1; j += 2) {
b[k] = (((a[s00 + 1] << 2) & b2) | ((a[s00] << 3) & b3)) >> bit;
k += 1;
s00 += 2;
}
if (j < ny) {
b[k] = (((a[s00] << 3) & b3)) >> bit;
k += 1;
}
}
}
static void qtree_onebit64(LONGLONG a[], int n, int nx, int ny,
unsigned char b[], int bit) {
int i, j, k;
LONGLONG b0, b1, b2, b3;
int s10, s00;
b0 = ((LONGLONG)1) << bit;
b1 = b0 << 1;
b2 = b0 << 2;
b3 = b0 << 3;
k = 0;
for (i = 0; i < nx - 1; i += 2) {
s00 = n * i;
s10 = s00 + n;
for (j = 0; j < ny - 1; j += 2) {
b[k] =
(unsigned char)(((a[s10 + 1] & b0) | ((a[s10] << 1) & b1) |
((a[s00 + 1] << 2) & b2) | ((a[s00] << 3) & b3)) >>
bit);
k += 1;
s00 += 2;
s10 += 2;
}
if (j < ny) {
b[k] =
(unsigned char)((((a[s10] << 1) & b1) | ((a[s00] << 3) & b3)) >> bit);
k += 1;
}
}
if (i < nx) {
s00 = n * i;
for (j = 0; j < ny - 1; j += 2) {
b[k] =
(unsigned char)((((a[s00 + 1] << 2) & b2) | ((a[s00] << 3) & b3)) >>
bit);
k += 1;
s00 += 2;
}
if (j < ny) {
b[k] = (unsigned char)((((a[s00] << 3) & b3)) >> bit);
k += 1;
}
}
}
static void qtree_reduce(unsigned char a[], int n, int nx, int ny,
unsigned char b[]) {
int i, j, k;
int s10, s00;
k = 0;
for (i = 0; i < nx - 1; i += 2) {
s00 = n * i;
s10 = s00 + n;
for (j = 0; j < ny - 1; j += 2) {
b[k] = (a[s10 + 1] != 0) | ((a[s10] != 0) << 1) |
((a[s00 + 1] != 0) << 2) | ((a[s00] != 0) << 3);
k += 1;
s00 += 2;
s10 += 2;
}
if (j < ny) {
b[k] = ((a[s10] != 0) << 1) | ((a[s00] != 0) << 3);
k += 1;
}
}
if (i < nx) {
s00 = n * i;
for (j = 0; j < ny - 1; j += 2) {
b[k] = ((a[s00 + 1] != 0) << 2) | ((a[s00] != 0) << 3);
k += 1;
s00 += 2;
}
if (j < ny) {
b[k] = ((a[s00] != 0) << 3);
k += 1;
}
}
}
static void write_bdirect(char *outfile, int a[], int n, int nqx, int nqy,
unsigned char scratch[], int bit) {
output_nybble(outfile, 0x0);
qtree_onebit(a, n, nqx, nqy, scratch, bit);
output_nnybble(outfile, ((nqx + 1) / 2) * ((nqy + 1) / 2), scratch);
}
static void write_bdirect64(char *outfile, LONGLONG a[], int n, int nqx,
int nqy, unsigned char scratch[], int bit) {
output_nybble(outfile, 0x0);
qtree_onebit64(a, n, nqx, nqy, scratch, bit);
output_nnybble(outfile, ((nqx + 1) / 2) * ((nqy + 1) / 2), scratch);
}