Home | History | Annotate | Download | only in yuv420sp2rgb
      1 #include <stdio.h>
      2 #include <debug.h>
      3 #include <cmdline.h>
      4 #include <sys/types.h>
      5 #include <sys/stat.h>
      6 #include <sys/mman.h>
      7 #include <fcntl.h>
      8 #include <string.h>
      9 #include <errno.h>
     10 #include <unistd.h>
     11 
     12 #ifndef max
     13 #define max(a,b) ({typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _a : _b; })
     14 #define min(a,b) ({typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? _a : _b; })
     15 #endif
     16 
     17 #define CONVERT_TYPE_PPM 0
     18 #define CONVERT_TYPE_RGB 1
     19 #define CONVERT_TYPE_ARGB 2
     20 
     21 /*
     22    YUV 4:2:0 image with a plane of 8 bit Y samples followed by an interleaved
     23    U/V plane containing 8 bit 2x2 subsampled chroma samples.
     24    except the interleave order of U and V is reversed.
     25 
     26                         H V
     27    Y Sample Period      1 1
     28    U (Cb) Sample Period 2 2
     29    V (Cr) Sample Period 2 2
     30  */
     31 
     32 typedef struct rgb_context {
     33     unsigned char *buffer;
     34     int width;
     35     int height;
     36     int rotate;
     37     int i;
     38     int j;
     39     int size; /* for debugging */
     40 } rgb_context;
     41 
     42 typedef void (*rgb_cb)(
     43     unsigned char r,
     44     unsigned char g,
     45     unsigned char b,
     46     rgb_context *ctx);
     47 
     48 const int bytes_per_pixel = 2;
     49 
     50 static void color_convert_common(
     51     unsigned char *pY, unsigned char *pUV,
     52     int width, int height,
     53     unsigned char *buffer,
     54     int size, /* buffer size in bytes */
     55     int gray,
     56     int rotate,
     57     rgb_cb cb)
     58 {
     59 	int i, j;
     60 	int nR, nG, nB;
     61 	int nY, nU, nV;
     62     rgb_context ctx;
     63 
     64     ctx.buffer = buffer;
     65     ctx.size = size; /* debug */
     66     ctx.width = width;
     67     ctx.height = height;
     68     ctx.rotate = rotate;
     69 
     70     if (gray) {
     71         for (i = 0; i < height; i++) {
     72             for (j = 0; j < width; j++) {
     73                 nB = *(pY + i * width + j);
     74                 ctx.i = i;
     75                 ctx.j = j;
     76                 cb(nB, nB, nB, &ctx);
     77             }
     78         }
     79     } else {
     80         // YUV 4:2:0
     81         for (i = 0; i < height; i++) {
     82             for (j = 0; j < width; j++) {
     83                 nY = *(pY + i * width + j);
     84                 nV = *(pUV + (i/2) * width + bytes_per_pixel * (j/2));
     85                 nU = *(pUV + (i/2) * width + bytes_per_pixel * (j/2) + 1);
     86 
     87                 // Yuv Convert
     88                 nY -= 16;
     89                 nU -= 128;
     90                 nV -= 128;
     91 
     92                 if (nY < 0)
     93                     nY = 0;
     94 
     95                 // nR = (int)(1.164 * nY + 2.018 * nU);
     96                 // nG = (int)(1.164 * nY - 0.813 * nV - 0.391 * nU);
     97                 // nB = (int)(1.164 * nY + 1.596 * nV);
     98 
     99                 nB = (int)(1192 * nY + 2066 * nU);
    100                 nG = (int)(1192 * nY - 833 * nV - 400 * nU);
    101                 nR = (int)(1192 * nY + 1634 * nV);
    102 
    103                 nR = min(262143, max(0, nR));
    104                 nG = min(262143, max(0, nG));
    105                 nB = min(262143, max(0, nB));
    106 
    107                 nR >>= 10; nR &= 0xff;
    108                 nG >>= 10; nG &= 0xff;
    109                 nB >>= 10; nB &= 0xff;
    110 
    111                 ctx.i = i;
    112                 ctx.j = j;
    113                 cb(nR, nG, nB, &ctx);
    114             }
    115         }
    116     }
    117 }
    118 
    119 static void rgb16_cb(
    120     unsigned char r,
    121     unsigned char g,
    122     unsigned char b,
    123     rgb_context *ctx)
    124 {
    125     unsigned short *rgb16 = (unsigned short *)ctx->buffer;
    126     *(rgb16 + ctx->i * ctx->width + ctx->j) = b | (g << 5) | (r << 11);
    127 }
    128 
    129 static void common_rgb_cb(
    130     unsigned char r,
    131     unsigned char g,
    132     unsigned char b,
    133     rgb_context *ctx,
    134     int alpha)
    135 {
    136     unsigned char *out = ctx->buffer;
    137     int offset = 0;
    138     int bpp;
    139     int i = 0;
    140     switch(ctx->rotate) {
    141     case 0: /* no rotation */
    142         offset = ctx->i * ctx->width + ctx->j;
    143         break;
    144     case 1: /* 90 degrees */
    145         offset = ctx->height * (ctx->j + 1) - ctx->i;
    146         break;
    147     case 2: /* 180 degrees */
    148         offset = (ctx->height - 1 - ctx->i) * ctx->width + ctx->j;
    149         break;
    150     case 3: /* 270 degrees */
    151         offset = (ctx->width - 1 - ctx->j) * ctx->height + ctx->i;
    152         break;
    153     default:
    154         FAILIF(1, "Unexpected roation value %d!\n", ctx->rotate);
    155     }
    156 
    157     bpp = 3 + !!alpha;
    158     offset *= bpp;
    159     FAILIF(offset < 0, "point (%d, %d) generates a negative offset.\n", ctx->i, ctx->j);
    160     FAILIF(offset + bpp > ctx->size, "point (%d, %d) at offset %d exceeds the size %d of the buffer.\n",
    161            ctx->i, ctx->j,
    162            offset,
    163            ctx->size);
    164 
    165     out += offset;
    166 
    167     if (alpha) out[i++] = 0xff;
    168     out[i++] = r;
    169     out[i++] = g;
    170     out[i] = b;
    171 }
    172 
    173 static void rgb24_cb(
    174     unsigned char r,
    175     unsigned char g,
    176     unsigned char b,
    177     rgb_context *ctx)
    178 {
    179     return common_rgb_cb(r,g,b,ctx,0);
    180 }
    181 
    182 static void argb_cb(
    183     unsigned char r,
    184     unsigned char g,
    185     unsigned char b,
    186     rgb_context *ctx)
    187 {
    188     return common_rgb_cb(r,g,b,ctx,1);
    189 }
    190 
    191 static void convert(const char *infile,
    192                     const char *outfile,
    193                     int height,
    194                     int width,
    195                     int gray,
    196                     int type,
    197                     int rotate)
    198 {
    199     void *in, *out;
    200     int ifd, ofd, rc;
    201     int psz = getpagesize();
    202     static char header[1024];
    203     int header_size;
    204     size_t outsize;
    205 
    206     int bpp = 3;
    207     switch (type) {
    208     case CONVERT_TYPE_PPM:
    209         PRINT("encoding PPM\n");
    210         if (rotate & 1)
    211             header_size = snprintf(header, sizeof(header), "P6\n%d %d\n255\n", height, width);
    212         else
    213             header_size = snprintf(header, sizeof(header), "P6\n%d %d\n255\n", width, height);
    214 	break;
    215     case CONVERT_TYPE_RGB:
    216         PRINT("encoding raw RGB24\n");
    217         header_size = 0;
    218         break;
    219     case CONVERT_TYPE_ARGB:
    220         PRINT("encoding raw ARGB\n");
    221         header_size = 0;
    222         bpp = 4;
    223         break;
    224     }
    225 
    226     outsize = header_size + width * height * bpp;
    227     outsize = (outsize + psz - 1) & ~(psz - 1);
    228 
    229     INFO("Opening input file %s\n", infile);
    230     ifd = open(infile, O_RDONLY);
    231     FAILIF(ifd < 0, "open(%s) failed: %s (%d)\n",
    232            infile, strerror(errno), errno);
    233 
    234     INFO("Opening output file %s\n", outfile);
    235     ofd = open(outfile, O_RDWR | O_CREAT, 0664);
    236     FAILIF(ofd < 0, "open(%s) failed: %s (%d)\n",
    237            outfile, strerror(errno), errno);
    238 
    239     INFO("Memory-mapping input file %s\n", infile);
    240     in = mmap(0, width * height * 3 / 2, PROT_READ, MAP_PRIVATE, ifd, 0);
    241     FAILIF(in == MAP_FAILED, "could not mmap input file: %s (%d)\n",
    242            strerror(errno), errno);
    243 
    244     INFO("Truncating output file %s to %d bytes\n", outfile, outsize);
    245     FAILIF(ftruncate(ofd, outsize) < 0,
    246            "Could not truncate output file to required size: %s (%d)\n",
    247            strerror(errno), errno);
    248 
    249     INFO("Memory mapping output file %s\n", outfile);
    250     out = mmap(0, outsize, PROT_WRITE, MAP_SHARED, ofd, 0);
    251     FAILIF(out == MAP_FAILED, "could not mmap output file: %s (%d)\n",
    252            strerror(errno), errno);
    253 
    254     INFO("PPM header (%d) bytes:\n%s\n", header_size, header);
    255     FAILIF(write(ofd, header, header_size) != header_size,
    256            "Error wrinting PPM header: %s (%d)\n",
    257            strerror(errno), errno);
    258 
    259     INFO("Converting %dx%d YUV 4:2:0 to RGB24...\n", width, height);
    260     color_convert_common(in, in + width * height,
    261                          width, height,
    262                          out + header_size, outsize - header_size,
    263                          gray, rotate,
    264                          type == CONVERT_TYPE_ARGB ? argb_cb : rgb24_cb);
    265 }
    266 
    267 int verbose_flag;
    268 int quiet_flag;
    269 
    270 int main(int argc, char **argv) {
    271 
    272     char *infile, *outfile, *type;
    273     int height, width, gray, rotate;
    274     int cmdline_error = 0;
    275 
    276     /* Parse command-line arguments. */
    277 
    278     int first = get_options(argc, argv,
    279                             &outfile,
    280                             &height,
    281                             &width,
    282                             &gray,
    283                             &type,
    284                             &rotate,
    285                             &verbose_flag);
    286 
    287     if (first == argc) {
    288         ERROR("You must specify an input file!\n");
    289         cmdline_error++;
    290     }
    291     if (!outfile) {
    292         ERROR("You must specify an output file!\n");
    293         cmdline_error++;
    294     }
    295     if (height < 0 || width < 0) {
    296         ERROR("You must specify both image height and width!\n");
    297         cmdline_error++;
    298     }
    299 
    300     FAILIF(rotate % 90, "Rotation angle must be a multiple of 90 degrees!\n");
    301 
    302     rotate /= 90;
    303     rotate %= 4;
    304     if (rotate < 0) rotate += 4;
    305 
    306     if (cmdline_error) {
    307         print_help(argv[0]);
    308         exit(1);
    309     }
    310 
    311     infile = argv[first];
    312 
    313     INFO("input file: [%s]\n", infile);
    314     INFO("output file: [%s]\n", outfile);
    315     INFO("height: %d\n", height);
    316     INFO("width: %d\n", width);
    317     INFO("gray only: %d\n", gray);
    318     INFO("encode as: %s\n", type);
    319     INFO("rotation: %d\n", rotate);
    320 
    321     /* Convert the image */
    322 
    323     int conv_type;
    324     if (!type || !strcmp(type, "ppm"))
    325         conv_type = CONVERT_TYPE_PPM;
    326     else if (!strcmp(type, "rgb"))
    327         conv_type = CONVERT_TYPE_RGB;
    328     else if (!strcmp(type, "argb"))
    329         conv_type = CONVERT_TYPE_ARGB;
    330     else FAILIF(1, "Unknown encoding type %s.\n", type);
    331 
    332     convert(infile, outfile,
    333             height, width, gray,
    334             conv_type,
    335             rotate);
    336 
    337     free(outfile);
    338     return 0;
    339 }
    340