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
32typedef 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
42typedef void (*rgb_cb)(
43    unsigned char r,
44    unsigned char g,
45    unsigned char b,
46    rgb_context *ctx);
47
48const int bytes_per_pixel = 2;
49
50static 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
119static 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
129static 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
173static 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
182static 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
191static 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
267int verbose_flag;
268int quiet_flag;
269
270int 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