Iliyan Malchev | 7abfb48 | 2009-04-23 13:14:13 -0700 | [diff] [blame] | 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 | } |