#ifdef PPM_CHROMA
#if PPM_CHROMA != 1 && PPM_CHROMA != 2
#error "compile with PPM_CHROMA=1 or PPM_CHROMA=2"
#endif
#else
#define PPM_CHROMA 2
#endif
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "zfp.h"
static void
clamp(int32* block, uint n)
{
uint i;
for (i = 0; i < n; i++) {
if (block[i] < 1 - (1 << 30))
block[i] = 1 - (1 << 30);
if (block[i] > (1 << 30) - 1)
block[i] = (1 << 30) - 1;
}
}
static void
rgb2ycocg(int32 ycocg[3][16], int32 rgb[3][16])
{
uint i;
for (i = 0; i < 16; i++) {
int32 r, g, b;
int32 y, co, cg, t;
r = rgb[0][i];
g = rgb[1][i];
b = rgb[2][i];
co = (r - b) >> 1;
t = b + co;
cg = (g - t) >> 1;
y = t + cg;
ycocg[0][i] = y;
ycocg[1][i] = co;
ycocg[2][i] = cg;
}
}
static void
ycocg2rgb(int32 rgb[3][16], int32 ycocg[3][16])
{
uint i;
for (i = 0; i < 16; i++) {
int32 r, g, b;
int32 y, co, cg, t;
y = ycocg[0][i];
co = ycocg[1][i];
cg = ycocg[2][i];
t = y - cg;
g = (cg << 1) + t;
b = t - co;
r = (co << 1) + b;
rgb[0][i] = r;
rgb[1][i] = g;
rgb[2][i] = b;
}
}
static void
fwd_lift(int32* p, uint s)
{
int32 x, y, z, w;
x = *p; p += s;
y = *p; p += s;
z = *p; p += s;
w = *p; p += s;
x += w; x >>= 1; w -= x;
z += y; z >>= 1; y -= z;
x += z; x >>= 1; z -= x;
w += y; w >>= 1; y -= w;
w += y >> 1; y -= w >> 1;
p -= s; *p = w;
p -= s; *p = z;
p -= s; *p = y;
p -= s; *p = x;
}
static void
inv_lift(int32* p, uint s)
{
int32 x, y, z, w;
x = *p; p += s;
y = *p; p += s;
z = *p; p += s;
w = *p; p += s;
y += w >> 1; w -= y >> 1;
y += w; w <<= 1; w -= y;
z += x; x <<= 1; x -= z;
y += z; z <<= 1; z -= y;
w += x; x <<= 1; x -= w;
p -= s; *p = w;
p -= s; *p = z;
p -= s; *p = y;
p -= s; *p = x;
}
static void
chroma_downsample(int32* block)
{
uint i, j;
for (j = 0; j < 4; j++)
fwd_lift(block + 4 * j, 1);
for (i = 0; i < 4; i++)
fwd_lift(block + 1 * i, 4);
#if PPM_CHROMA == 1
block[2] = block[4];
block[3] = block[5];
for (i = 4; i < 16; i++)
block[i] = 0;
inv_lift(block, 1);
clamp(block, 4);
#else
for (j = 0; j < 4; j++)
for (i = 0; i < 4; i++)
if (i >= 2 || j >= 2)
block[i + 4 * j] = 0;
for (i = 0; i < 4; i++)
inv_lift(block + 1 * i, 4);
for (j = 0; j < 4; j++)
inv_lift(block + 4 * j, 1);
clamp(block, 16);
#endif
}
static void
chroma_upsample(int32* block)
{
#if PPM_CHROMA == 1
uint i, j;
fwd_lift(block, 1);
block[4] = block[2];
block[5] = block[3];
block[2] = 0;
block[3] = 0;
for (i = 6; i < 16; i++)
block[i] = 0;
for (i = 0; i < 4; i++)
inv_lift(block + 1 * i, 4);
for (j = 0; j < 4; j++)
inv_lift(block + 4 * j, 1);
clamp(block, 16);
#else
clamp(block, 16);
#endif
}
int main(int argc, char* argv[])
{
double rate = 0;
uint nx, ny;
uint x, y;
uint k;
char line[0x100];
uchar* image;
zfp_field* field;
zfp_stream* zfp[3];
bitstream* stream;
void* buffer;
size_t bytes;
size_t size;
switch (argc) {
case 2:
if (sscanf(argv[1], "%lf", &rate) != 1)
goto usage;
break;
default:
usage:
fprintf(stderr, "Usage: ppm <rate|-precision> <input.ppm >output.ppm\n");
return EXIT_FAILURE;
}
if (!fgets(line, sizeof(line), stdin) || strcmp(line, "P6\n") ||
!fgets(line, sizeof(line), stdin) || sscanf(line, "%u%u", &nx, &ny) != 2 ||
!fgets(line, sizeof(line), stdin) || strcmp(line, "255\n")) {
fprintf(stderr, "error opening image\n");
return EXIT_FAILURE;
}
if ((nx & 3u) || (ny & 3u)) {
fprintf(stderr, "image dimensions must be multiples of four\n");
return EXIT_FAILURE;
}
image = malloc(3 * nx * ny);
if (!image) {
fprintf(stderr, "error allocating memory\n");
return EXIT_FAILURE;
}
if (fread(image, sizeof(*image), 3 * nx * ny, stdin) != 3 * nx * ny) {
fprintf(stderr, "error reading image\n");
return EXIT_FAILURE;
}
for (k = 0; k < 3; k++)
zfp[k] = zfp_stream_open(NULL);
if (rate < 0) {
for (k = 0; k < 3; k++)
zfp_stream_set_precision(zfp[k], (uint)floor(0.5 - rate));
}
else {
#if PPM_CHROMA == 1
double chroma_rate = floor(8 * rate / 3 + 0.5) / 4;
double luma_rate = rate - chroma_rate / 2;
zfp_stream_set_rate(zfp[0], luma_rate, zfp_type_int32, 2, zfp_false);
zfp_stream_set_rate(zfp[1], chroma_rate, zfp_type_int32, 1, zfp_false);
zfp_stream_set_rate(zfp[2], chroma_rate, zfp_type_int32, 1, zfp_false);
#else
double chroma_rate = floor(8 * rate / 3 + 0.5) / 16;
double luma_rate = rate - 2 * chroma_rate;
zfp_stream_set_rate(zfp[0], luma_rate, zfp_type_int32, 2, zfp_false);
zfp_stream_set_rate(zfp[1], chroma_rate, zfp_type_int32, 2, zfp_false);
zfp_stream_set_rate(zfp[2], chroma_rate, zfp_type_int32, 2, zfp_false);
#endif
}
bytes = 0;
field = zfp_field_2d(image, zfp_type_int32, nx, ny);
for (k = 0; k < 3; k++)
bytes += zfp_stream_maximum_size(zfp[k], field);
zfp_field_free(field);
buffer = malloc(bytes);
if (!buffer) {
fprintf(stderr, "error allocating memory\n");
return EXIT_FAILURE;
}
stream = stream_open(buffer, bytes);
for (k = 0; k < 3; k++)
zfp_stream_set_bit_stream(zfp[k], stream);
for (y = 0; y < ny; y += 4)
for (x = 0; x < nx; x += 4) {
uchar block[3][16];
int32 rgb[3][16];
int32 ycocg[3][16];
uint i, j, k;
for (k = 0; k < 3; k++)
for (j = 0; j < 4; j++)
for (i = 0; i < 4; i++)
block[k][i + 4 * j] = image[k + 3 * (x + i + nx * (y + j))];
for (k = 0; k < 3; k++)
zfp_promote_uint8_to_int32(rgb[k], block[k], 2);
rgb2ycocg(ycocg, rgb);
for (k = 1; k < 3; k++)
chroma_downsample(ycocg[k]);
#if PPM_CHROMA == 1
zfp_encode_block_int32_2(zfp[0], ycocg[0]);
zfp_encode_block_int32_1(zfp[1], ycocg[1]);
zfp_encode_block_int32_1(zfp[2], ycocg[2]);
#else
for (k = 0; k < 3; k++)
zfp_encode_block_int32_2(zfp[k], ycocg[k]);
#endif
}
zfp_stream_flush(zfp[0]);
size = zfp_stream_compressed_size(zfp[0]);
fprintf(stderr, "%u compressed bytes (%.2f bits/pixel)\n", (uint)size, (double)size * CHAR_BIT / (nx * ny));
zfp_stream_rewind(zfp[0]);
for (y = 0; y < ny; y += 4)
for (x = 0; x < nx; x += 4) {
uchar block[3][16];
int32 rgb[3][16];
int32 ycocg[3][16];
uint i, j, k;
#if PPM_CHROMA == 1
zfp_decode_block_int32_2(zfp[0], ycocg[0]);
zfp_decode_block_int32_1(zfp[1], ycocg[1]);
zfp_decode_block_int32_1(zfp[2], ycocg[2]);
#else
for (k = 0; k < 3; k++)
zfp_decode_block_int32_2(zfp[k], ycocg[k]);
#endif
for (k = 1; k < 3; k++)
chroma_upsample(ycocg[k]);
ycocg2rgb(rgb, ycocg);
for (k = 0; k < 3; k++)
zfp_demote_int32_to_uint8(block[k], rgb[k], 2);
for (k = 0; k < 3; k++)
for (j = 0; j < 4; j++)
for (i = 0; i < 4; i++)
image[k + 3 * (x + i + nx * (y + j))] = block[k][i + 4 * j];
}
for (k = 0; k < 3; k++)
zfp_stream_close(zfp[k]);
stream_close(stream);
free(buffer);
printf("P6\n");
printf("%u %u\n", nx, ny);
printf("255\n");
if (fwrite(image, sizeof(*image), 3 * nx * ny, stdout) != 3 * nx * ny) {
fprintf(stderr, "error writing image\n");
return EXIT_FAILURE;
}
free(image);
return 0;
}