338 lines
9.0 KiB
C
338 lines
9.0 KiB
C
#include <stdio.h>
|
|
#include <debug.h>
|
|
#include <cmdline.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
|
|
#ifndef max
|
|
#define max(a,b) ({typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _a : _b; })
|
|
#define min(a,b) ({typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? _a : _b; })
|
|
#endif
|
|
|
|
#define CONVERT_TYPE_PPM 0
|
|
#define CONVERT_TYPE_RGB 1
|
|
#define CONVERT_TYPE_ARGB 2
|
|
|
|
/*
|
|
YUV 4:2:0 image with a plane of 8 bit Y samples followed by an interleaved
|
|
U/V plane containing 8 bit 2x2 subsampled chroma samples.
|
|
except the interleave order of U and V is reversed.
|
|
|
|
H V
|
|
Y Sample Period 1 1
|
|
U (Cb) Sample Period 2 2
|
|
V (Cr) Sample Period 2 2
|
|
*/
|
|
|
|
typedef struct rgb_context {
|
|
unsigned char *buffer;
|
|
int width;
|
|
int height;
|
|
int rotate;
|
|
int i;
|
|
int j;
|
|
int size; /* for debugging */
|
|
} rgb_context;
|
|
|
|
typedef void (*rgb_cb)(
|
|
unsigned char r,
|
|
unsigned char g,
|
|
unsigned char b,
|
|
rgb_context *ctx);
|
|
|
|
const int bytes_per_pixel = 2;
|
|
|
|
static void color_convert_common(
|
|
unsigned char *pY, unsigned char *pUV,
|
|
int width, int height,
|
|
unsigned char *buffer,
|
|
int size, /* buffer size in bytes */
|
|
int gray,
|
|
int rotate,
|
|
rgb_cb cb)
|
|
{
|
|
int i, j;
|
|
int nR, nG, nB;
|
|
int nY, nU, nV;
|
|
rgb_context ctx;
|
|
|
|
ctx.buffer = buffer;
|
|
ctx.size = size; /* debug */
|
|
ctx.width = width;
|
|
ctx.height = height;
|
|
ctx.rotate = rotate;
|
|
|
|
if (gray) {
|
|
for (i = 0; i < height; i++) {
|
|
for (j = 0; j < width; j++) {
|
|
nB = *(pY + i * width + j);
|
|
ctx.i = i;
|
|
ctx.j = j;
|
|
cb(nB, nB, nB, &ctx);
|
|
}
|
|
}
|
|
} else {
|
|
// YUV 4:2:0
|
|
for (i = 0; i < height; i++) {
|
|
for (j = 0; j < width; j++) {
|
|
nY = *(pY + i * width + j);
|
|
nV = *(pUV + (i/2) * width + bytes_per_pixel * (j/2));
|
|
nU = *(pUV + (i/2) * width + bytes_per_pixel * (j/2) + 1);
|
|
|
|
// Yuv Convert
|
|
nY -= 16;
|
|
nU -= 128;
|
|
nV -= 128;
|
|
|
|
if (nY < 0)
|
|
nY = 0;
|
|
|
|
// nR = (int)(1.164 * nY + 2.018 * nU);
|
|
// nG = (int)(1.164 * nY - 0.813 * nV - 0.391 * nU);
|
|
// nB = (int)(1.164 * nY + 1.596 * nV);
|
|
|
|
nB = (int)(1192 * nY + 2066 * nU);
|
|
nG = (int)(1192 * nY - 833 * nV - 400 * nU);
|
|
nR = (int)(1192 * nY + 1634 * nV);
|
|
|
|
nR = min(262143, max(0, nR));
|
|
nG = min(262143, max(0, nG));
|
|
nB = min(262143, max(0, nB));
|
|
|
|
nR >>= 10; nR &= 0xff;
|
|
nG >>= 10; nG &= 0xff;
|
|
nB >>= 10; nB &= 0xff;
|
|
|
|
ctx.i = i;
|
|
ctx.j = j;
|
|
cb(nR, nG, nB, &ctx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void rgb16_cb(
|
|
unsigned char r,
|
|
unsigned char g,
|
|
unsigned char b,
|
|
rgb_context *ctx)
|
|
{
|
|
unsigned short *rgb16 = (unsigned short *)ctx->buffer;
|
|
*(rgb16 + ctx->i * ctx->width + ctx->j) = b | (g << 5) | (r << 11);
|
|
}
|
|
|
|
static void common_rgb_cb(
|
|
unsigned char r,
|
|
unsigned char g,
|
|
unsigned char b,
|
|
rgb_context *ctx,
|
|
int alpha)
|
|
{
|
|
unsigned char *out = ctx->buffer;
|
|
int offset = 0;
|
|
int bpp;
|
|
int i = 0;
|
|
switch(ctx->rotate) {
|
|
case 0: /* no rotation */
|
|
offset = ctx->i * ctx->width + ctx->j;
|
|
break;
|
|
case 1: /* 90 degrees */
|
|
offset = ctx->height * (ctx->j + 1) - ctx->i;
|
|
break;
|
|
case 2: /* 180 degrees */
|
|
offset = (ctx->height - 1 - ctx->i) * ctx->width + ctx->j;
|
|
break;
|
|
case 3: /* 270 degrees */
|
|
offset = (ctx->width - 1 - ctx->j) * ctx->height + ctx->i;
|
|
break;
|
|
default:
|
|
FAILIF(1, "Unexpected roation value %d!\n", ctx->rotate);
|
|
}
|
|
|
|
bpp = 3 + !!alpha;
|
|
offset *= bpp;
|
|
FAILIF(offset < 0, "point (%d, %d) generates a negative offset.\n", ctx->i, ctx->j);
|
|
FAILIF(offset + bpp > ctx->size, "point (%d, %d) at offset %d exceeds the size %d of the buffer.\n",
|
|
ctx->i, ctx->j,
|
|
offset,
|
|
ctx->size);
|
|
|
|
out += offset;
|
|
|
|
if (alpha) out[i++] = 0xff;
|
|
out[i++] = r;
|
|
out[i++] = g;
|
|
out[i] = b;
|
|
}
|
|
|
|
static void rgb24_cb(
|
|
unsigned char r,
|
|
unsigned char g,
|
|
unsigned char b,
|
|
rgb_context *ctx)
|
|
{
|
|
return common_rgb_cb(r,g,b,ctx,0);
|
|
}
|
|
|
|
static void argb_cb(
|
|
unsigned char r,
|
|
unsigned char g,
|
|
unsigned char b,
|
|
rgb_context *ctx)
|
|
{
|
|
return common_rgb_cb(r,g,b,ctx,1);
|
|
}
|
|
|
|
static void convert(const char *infile,
|
|
const char *outfile,
|
|
int height,
|
|
int width,
|
|
int gray,
|
|
int type,
|
|
int rotate)
|
|
{
|
|
void *in, *out;
|
|
int ifd, ofd, rc;
|
|
int psz = getpagesize();
|
|
static char header[1024];
|
|
int header_size;
|
|
size_t outsize;
|
|
|
|
int bpp = 3;
|
|
switch (type) {
|
|
case CONVERT_TYPE_PPM:
|
|
if (rotate & 1)
|
|
header_size = snprintf(header, sizeof(header), "P6\n%d %d\n255\n", height, width);
|
|
else
|
|
header_size = snprintf(header, sizeof(header), "P6\n%d %d\n255\n", width, height);
|
|
case CONVERT_TYPE_RGB:
|
|
PRINT("encoding raw RGB24\n");
|
|
header_size = 0;
|
|
break;
|
|
case CONVERT_TYPE_ARGB:
|
|
PRINT("encoding raw ARGB\n");
|
|
header_size = 0;
|
|
bpp = 4;
|
|
break;
|
|
}
|
|
|
|
outsize = header_size + width * height * bpp;
|
|
outsize = (outsize + psz - 1) & ~(psz - 1);
|
|
|
|
INFO("Opening input file %s\n", infile);
|
|
ifd = open(infile, O_RDONLY);
|
|
FAILIF(ifd < 0, "open(%s) failed: %s (%d)\n",
|
|
infile, strerror(errno), errno);
|
|
|
|
INFO("Opening output file %s\n", outfile);
|
|
ofd = open(outfile, O_RDWR | O_CREAT, 0664);
|
|
FAILIF(ofd < 0, "open(%s) failed: %s (%d)\n",
|
|
outfile, strerror(errno), errno);
|
|
|
|
INFO("Memory-mapping input file %s\n", infile);
|
|
in = mmap(0, width * height * 3 / 2, PROT_READ, MAP_PRIVATE, ifd, 0);
|
|
FAILIF(in == MAP_FAILED, "could not mmap input file: %s (%d)\n",
|
|
strerror(errno), errno);
|
|
|
|
INFO("Truncating output file %s to %d bytes\n", outfile, outsize);
|
|
FAILIF(ftruncate(ofd, outsize) < 0,
|
|
"Could not truncate output file to required size: %s (%d)\n",
|
|
strerror(errno), errno);
|
|
|
|
INFO("Memory mapping output file %s\n", outfile);
|
|
out = mmap(0, outsize, PROT_WRITE, MAP_SHARED, ofd, 0);
|
|
FAILIF(out == MAP_FAILED, "could not mmap output file: %s (%d)\n",
|
|
strerror(errno), errno);
|
|
|
|
INFO("PPM header (%d) bytes:\n%s\n", header_size, header);
|
|
FAILIF(write(ofd, header, header_size) != header_size,
|
|
"Error wrinting PPM header: %s (%d)\n",
|
|
strerror(errno), errno);
|
|
|
|
INFO("Converting %dx%d YUV 4:2:0 to RGB24...\n", width, height);
|
|
color_convert_common(in, in + width * height,
|
|
width, height,
|
|
out + header_size, outsize - header_size,
|
|
gray, rotate,
|
|
type == CONVERT_TYPE_ARGB ? argb_cb : rgb24_cb);
|
|
}
|
|
|
|
int verbose_flag;
|
|
int quiet_flag;
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
char *infile, *outfile, *type;
|
|
int height, width, gray, rotate;
|
|
int cmdline_error = 0;
|
|
|
|
/* Parse command-line arguments. */
|
|
|
|
int first = get_options(argc, argv,
|
|
&outfile,
|
|
&height,
|
|
&width,
|
|
&gray,
|
|
&type,
|
|
&rotate,
|
|
&verbose_flag);
|
|
|
|
if (first == argc) {
|
|
ERROR("You must specify an input file!\n");
|
|
cmdline_error++;
|
|
}
|
|
if (!outfile) {
|
|
ERROR("You must specify an output file!\n");
|
|
cmdline_error++;
|
|
}
|
|
if (height < 0 || width < 0) {
|
|
ERROR("You must specify both image height and width!\n");
|
|
cmdline_error++;
|
|
}
|
|
|
|
FAILIF(rotate % 90, "Rotation angle must be a multiple of 90 degrees!\n");
|
|
|
|
rotate /= 90;
|
|
rotate %= 4;
|
|
if (rotate < 0) rotate += 4;
|
|
|
|
if (cmdline_error) {
|
|
print_help(argv[0]);
|
|
exit(1);
|
|
}
|
|
|
|
infile = argv[first];
|
|
|
|
INFO("input file: [%s]\n", infile);
|
|
INFO("output file: [%s]\n", outfile);
|
|
INFO("height: %d\n", height);
|
|
INFO("width: %d\n", width);
|
|
INFO("gray only: %d\n", gray);
|
|
INFO("encode as: %s\n", type);
|
|
INFO("rotation: %d\n", rotate);
|
|
|
|
/* Convert the image */
|
|
|
|
int conv_type;
|
|
if (!strcmp(type, "ppm"))
|
|
conv_type = CONVERT_TYPE_PPM;
|
|
else if (!strcmp(type, "rgb"))
|
|
conv_type = CONVERT_TYPE_RGB;
|
|
else if (!strcmp(type, "argb"))
|
|
conv_type = CONVERT_TYPE_ARGB;
|
|
else FAILIF(1, "Unknown encoding type %s.\n", type);
|
|
|
|
convert(infile, outfile,
|
|
height, width, gray,
|
|
conv_type,
|
|
rotate);
|
|
|
|
free(outfile);
|
|
return 0;
|
|
}
|