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