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
color_convert_common(unsigned char * pY,unsigned char * pUV,int width,int height,unsigned char * buffer,int size,int gray,int rotate,rgb_cb cb)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
rgb16_cb(unsigned char r,unsigned char g,unsigned char b,rgb_context * ctx)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
common_rgb_cb(unsigned char r,unsigned char g,unsigned char b,rgb_context * ctx,int alpha)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
rgb24_cb(unsigned char r,unsigned char g,unsigned char b,rgb_context * ctx)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
argb_cb(unsigned char r,unsigned char g,unsigned char b,rgb_context * ctx)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
convert(const char * infile,const char * outfile,int height,int width,int gray,int type,int rotate)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
main(int argc,char ** argv)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