#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: PRINT("encoding PPM\n"); 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); break; 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 (!type || !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; }