diff --git a/libcopybit/Android.mk b/libcopybit/Android.mk new file mode 100644 index 0000000..f6a1b4b --- /dev/null +++ b/libcopybit/Android.mk @@ -0,0 +1,25 @@ +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +LOCAL_PATH:= $(call my-dir) +# HAL module implemenation, not prelinked and stored in +# hw/..so +include $(CLEAR_VARS) +LOCAL_PRELINK_MODULE := false +LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_SRC_FILES := copybit.c +LOCAL_MODULE := copybit.msm7k +include $(BUILD_SHARED_LIBRARY) diff --git a/libcopybit/copybit.c b/libcopybit/copybit.c new file mode 100644 index 0000000..168de4c --- /dev/null +++ b/libcopybit/copybit.c @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#define LOG_TAG "copybit" + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/******************************************************************************/ + +/** State information for each device instance */ +struct copybit_context_t { + struct copybit_device_t device; + int mFD; + uint8_t mAlpha; + uint8_t mFlags; +}; + +/** + * Common hardware methods + */ + +static int open_copybit(const struct hw_module_t* module, const char* name, + struct hw_device_t** device); + +static struct hw_module_methods_t copybit_module_methods = { + .open = open_copybit +}; + +/* + * The COPYBIT Module + */ +const struct copybit_module_t HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .version_major = 1, + .version_minor = 0, + .id = COPYBIT_HARDWARE_MODULE_ID, + .name = "QCT MSM7K COPYBIT Module", + .author = "Google, Inc.", + .methods = ©bit_module_methods, + } +}; + +/******************************************************************************/ + +/** min of int a, b */ +static inline int min(int a, int b) { + return (ab) ? a : b; +} + +/** scale each parameter by mul/div. Assume div isn't 0 */ +static inline void MULDIV(uint32_t *a, uint32_t *b, int mul, int div) { + if (mul != div) { + *a = (mul * *a) / div; + *b = (mul * *b) / div; + } +} + +/** Determine the intersection of lhs & rhs store in out */ +static void intersect(struct copybit_rect_t *out, + const struct copybit_rect_t *lhs, + const struct copybit_rect_t *rhs) { + out->l = max(lhs->l, rhs->l); + out->t = max(lhs->t, rhs->t); + out->r = min(lhs->r, rhs->r); + out->b = min(lhs->b, rhs->b); +} + +/** convert COPYBIT_FORMAT to MDP format */ +static int get_format(int format) { + switch (format) { + case COPYBIT_FORMAT_RGBA_8888: return MDP_RGBA_8888; + case COPYBIT_FORMAT_BGRA_8888: return MDP_BGRA_8888; + case COPYBIT_FORMAT_RGB_565: return MDP_RGB_565; + case COPYBIT_FORMAT_YCbCr_422_SP: return MDP_Y_CBCR_H2V1; + case COPYBIT_FORMAT_YCbCr_420_SP: return MDP_Y_CBCR_H2V2; + } + return -1; +} + +/** convert from copybit image to mdp image structure */ +static void set_image(struct mdp_img *img, + const struct copybit_image_t *rhs) { + img->width = rhs->w; + img->height = rhs->h; + img->format = get_format(rhs->format); + img->offset = rhs->offset; + img->memory_id = rhs->fd; +} + +/** setup rectangles */ +static void set_rects(struct copybit_context_t *dev, + struct mdp_blit_req *e, + const struct copybit_rect_t *dst, + const struct copybit_rect_t *src, + const struct copybit_rect_t *scissor) { + struct copybit_rect_t clip; + intersect(&clip, scissor, dst); + + e->dst_rect.x = clip.l; + e->dst_rect.y = clip.t; + e->dst_rect.w = clip.r - clip.l; + e->dst_rect.h = clip.b - clip.t; + + uint32_t W, H; + if (dev->mFlags & COPYBIT_TRANSFORM_ROT_90) { + e->src_rect.x = (clip.t - dst->t) + src->t; + e->src_rect.y = (dst->r - clip.r) + src->l; + e->src_rect.w = (clip.b - clip.t); + e->src_rect.h = (clip.r - clip.l); + W = dst->b - dst->t; + H = dst->r - dst->l; + } else { + e->src_rect.x = (clip.l - dst->l) + src->l; + e->src_rect.y = (clip.t - dst->t) + src->t; + e->src_rect.w = (clip.r - clip.l); + e->src_rect.h = (clip.b - clip.t); + W = dst->r - dst->l; + H = dst->b - dst->t; + } + MULDIV(&e->src_rect.x, &e->src_rect.w, src->r - src->l, W); + MULDIV(&e->src_rect.y, &e->src_rect.h, src->b - src->t, H); + if (dev->mFlags & COPYBIT_TRANSFORM_FLIP_V) { + e->src_rect.y = e->src.height - (e->src_rect.y + e->src_rect.h); + } + if (dev->mFlags & COPYBIT_TRANSFORM_FLIP_H) { + e->src_rect.x = e->src.width - (e->src_rect.x + e->src_rect.w); + } +} + +/** setup mdp request */ +static void set_infos(struct copybit_context_t *dev, struct mdp_blit_req *req) { + req->alpha = dev->mAlpha; + req->transp_mask = MDP_TRANSP_NOP; + req->flags = dev->mFlags; +} + +/** copy the bits */ +static int msm_copybit(struct copybit_context_t *dev, void const *list) +{ + int err = ioctl(dev->mFD, MSMFB_BLIT, + (struct mdp_blit_req_list const*)list); + LOGE_IF(err<0, "copyBits failed (%s)", strerror(errno)); + if (err == 0) + return 0; + else + return -errno; +} + +/*****************************************************************************/ + +/** Set a parameter to value */ +static int set_parameter_copybit( + struct copybit_device_t *dev, + int name, + int value) +{ + struct copybit_context_t* ctx = (struct copybit_context_t*)dev; + int status = 0; + if (ctx) { + switch(name) { + case COPYBIT_ROTATION_DEG: + switch (value) { + case 0: + ctx->mFlags &= ~0x7; + break; + case 90: + ctx->mFlags &= ~0x7; + ctx->mFlags |= MDP_ROT_90; + break; + case 180: + ctx->mFlags &= ~0x7; + ctx->mFlags |= MDP_ROT_180; + break; + case 270: + ctx->mFlags &= ~0x7; + ctx->mFlags |= MDP_ROT_270; + break; + default: + LOGE("Invalid value for COPYBIT_ROTATION_DEG"); + status = -EINVAL; + break; + } + break; + case COPYBIT_PLANE_ALPHA: + if (value < 0) value = 0; + if (value >= 256) value = 255; + ctx->mAlpha = value; + break; + case COPYBIT_DITHER: + if (value == COPYBIT_ENABLE) { + ctx->mFlags |= MDP_DITHER; + } else if (value == COPYBIT_DISABLE) { + ctx->mFlags &= ~MDP_DITHER; + } + break; + case COPYBIT_TRANSFORM: + ctx->mFlags &= ~0x7; + ctx->mFlags |= value & 0x7; + break; + default: + status = -EINVAL; + break; + } + } else { + status = -EINVAL; + } + return status; +} + +/** Get a static info value */ +static int get(struct copybit_device_t *dev, int name) +{ + struct copybit_context_t* ctx = (struct copybit_context_t*)dev; + int value; + if (ctx) { + switch(name) { + case COPYBIT_MINIFICATION_LIMIT: + value = 4; + break; + case COPYBIT_MAGNIFICATION_LIMIT: + value = 4; + break; + case COPYBIT_SCALING_FRAC_BITS: + value = 32; + break; + case COPYBIT_ROTATION_STEP_DEG: + value = 90; + break; + default: + value = -EINVAL; + } + } else { + value = -EINVAL; + } + return value; +} + +/** do a stretch blit type operation */ +static int stretch_copybit( + struct copybit_device_t *dev, + struct copybit_image_t const *dst, + struct copybit_image_t const *src, + struct copybit_rect_t const *dst_rect, + struct copybit_rect_t const *src_rect, + struct copybit_region_t const *region) +{ + struct copybit_context_t* ctx = (struct copybit_context_t*)dev; + int status = 0; + if (ctx) { + struct { + uint32_t count; + struct mdp_blit_req req[12]; + } list; + + if (ctx->mAlpha < 255) { + switch (src->format) { + // we don't support plane alpha with RGBA formats + case COPYBIT_FORMAT_RGBA_8888: + case COPYBIT_FORMAT_BGRA_8888: + case COPYBIT_FORMAT_RGBA_5551: + case COPYBIT_FORMAT_RGBA_4444: + return -EINVAL; + } + } + + const uint32_t maxCount = sizeof(list.req)/sizeof(list.req[0]); + const struct copybit_rect_t bounds = { 0, 0, dst->w, dst->h }; + struct copybit_rect_t clip; + list.count = 0; + status = 0; + while ((status == 0) && region->next(region, &clip)) { + intersect(&clip, &bounds, &clip); + set_infos(ctx, &list.req[list.count]); + set_image(&list.req[list.count].dst, dst); + set_image(&list.req[list.count].src, src); + set_rects(ctx, &list.req[list.count], dst_rect, src_rect, &clip); + if (++list.count == maxCount) { + status = msm_copybit(ctx, &list); + list.count = 0; + } + } + if ((status == 0) && list.count) { + status = msm_copybit(ctx, &list); + } + } else { + status = -EINVAL; + } + return status; +} + +/** Perform a blit type operation */ +static int blit_copybit( + struct copybit_device_t *dev, + struct copybit_image_t const *dst, + struct copybit_image_t const *src, + struct copybit_region_t const *region) +{ + struct copybit_rect_t dr = { 0, 0, dst->w, dst->h }; + struct copybit_rect_t sr = { 0, 0, src->w, src->h }; + return stretch_copybit(dev, dst, src, &dr, &sr, region); +} + +/*****************************************************************************/ + +/** Close the copybit device */ +static int close_copybit(struct hw_device_t *dev) +{ + struct copybit_context_t* ctx = (struct copybit_context_t*)dev; + if (ctx) { + close(ctx->mFD); + free(ctx); + } + return 0; +} + +/** Open a new instance of a copybit device using name */ +static int open_copybit(const struct hw_module_t* module, const char* name, + struct hw_device_t** device) +{ + int status = -EINVAL; + struct copybit_context_t *ctx = malloc(sizeof(struct copybit_context_t)); + memset(ctx, 0, sizeof(*ctx)); + + ctx->device.common.tag = HARDWARE_DEVICE_TAG; + ctx->device.common.version = 0; + ctx->device.common.module = module; + ctx->device.common.close = close_copybit; + ctx->device.set_parameter = set_parameter_copybit; + ctx->device.get = get; + ctx->device.blit = blit_copybit; + ctx->device.stretch = stretch_copybit; + ctx->mAlpha = MDP_ALPHA_NOP; + ctx->mFlags = 0; + ctx->mFD = open("/dev/graphics/fb0", O_RDWR, 0); + + if (ctx->mFD < 0) { + status = errno; + LOGE("Error opening frame buffer errno=%d (%s)", + status, strerror(status)); + status = -status; + } else { + struct fb_fix_screeninfo finfo; + if (ioctl(ctx->mFD, FBIOGET_FSCREENINFO, &finfo) == 0) { + if (strcmp(finfo.id, "msmfb") == 0) { + /* Success */ + status = 0; + } else { + LOGE("Error not msm frame buffer"); + status = -EINVAL; + } + } else { + LOGE("Error executing ioctl for screen info"); + status = -errno; + } + } + + if (status == 0) { + *device = &ctx->device.common; + } else { + close_copybit(&ctx->device.common); + } + return status; +} diff --git a/yuv420sp2rgb/Android.mk b/yuv420sp2rgb/Android.mk new file mode 100644 index 0000000..6e6220d --- /dev/null +++ b/yuv420sp2rgb/Android.mk @@ -0,0 +1,16 @@ +# Copyright 2005 Google Inc. All Rights Reserved. +# +# Android.mk for apriori +# + +LOCAL_PATH:= $(call my-dir) + +ifeq ($(TARGET_ARCH),arm) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := yuv420sp2rgb.c cmdline.c debug.c + +LOCAL_MODULE := yuv420sp2rgb + +include $(BUILD_HOST_EXECUTABLE) +endif diff --git a/yuv420sp2rgb/cmdline.c b/yuv420sp2rgb/cmdline.c new file mode 100644 index 0000000..3bfea99 --- /dev/null +++ b/yuv420sp2rgb/cmdline.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#include + +extern char *optarg; +extern int optind, opterr, optopt; + +static struct option long_options[] = { + {"output", required_argument, 0, 'o'}, + {"height", required_argument, 0, 'h'}, + {"width", required_argument, 0, 'w'}, + {"gray", no_argument, 0, 'g'}, + {"type", required_argument, 0, 't'}, + {"rotate", required_argument, 0, 'r'}, + {"verbose", no_argument, 0, 'V'}, + {"help", no_argument, 0, 1}, + {0, 0, 0, 0}, +}; + +/* This array must parallel long_options[] */ +static const char *descriptions[] = { + "output file", + "image height in pixels", + "image width in pixels", + "process the luma plane only", + "encode as one of { 'ppm', 'rgb', or 'argb' }", + "rotate (90, -90, 180 degrees)", + "print verbose output", + "print this help screen", +}; + +void print_help(const char *name) { + fprintf(stdout, + "Converts yuv 4:2:0 to rgb24 and generates a PPM file.\n" + "invokation:\n" + "\t%s infile --height --width --output [ --gray ] [ --rotate ] [ --verbose ]\n" + "\t%s infile --help\n", + name, name); + fprintf(stdout, "options:\n"); + struct option *opt = long_options; + const char **desc = descriptions; + while (opt->name) { + fprintf(stdout, "\t-%c/--%s%s: %s\n", + isprint(opt->val) ? opt->val : ' ', + opt->name, + (opt->has_arg ? " (argument)" : ""), + *desc); + opt++; + desc++; + } +} + +int get_options(int argc, char **argv, + char **outfile, + int *height, + int *width, + int *gray, + char **type, + int *rotate, + int *verbose) { + int c; + + ASSERT(outfile); *outfile = NULL; + ASSERT(height); *height = -1; + ASSERT(width); *width = -1; + ASSERT(gray); *gray = 0; + ASSERT(rotate); *rotate = 0; + ASSERT(verbose); *verbose = 0; + ASSERT(type); *type = NULL; + + while (1) { + /* getopt_long stores the option index here. */ + int option_index = 0; + + c = getopt_long (argc, argv, + "Vgo:h:w:r:t:", + long_options, + &option_index); + /* Detect the end of the options. */ + if (c == -1) break; + + if (isgraph(c)) { + INFO ("option -%c with value `%s'\n", c, (optarg ?: "(null)")); + } + +#define SET_STRING_OPTION(name) do { \ + ASSERT(optarg); \ + (*name) = strdup(optarg); \ +} while(0) + +#define SET_INT_OPTION(val) do { \ + ASSERT(optarg); \ + if (strlen(optarg) >= 2 && optarg[0] == '0' && optarg[1] == 'x') { \ + FAILIF(1 != sscanf(optarg+2, "%x", val), \ + "Expecting a hexadecimal argument!\n"); \ + } else { \ + FAILIF(1 != sscanf(optarg, "%d", val), \ + "Expecting a decimal argument!\n"); \ + } \ +} while(0) + + switch (c) { + case 0: + /* If this option set a flag, do nothing else now. */ + if (long_options[option_index].flag != 0) + break; + INFO ("option %s", long_options[option_index].name); + if (optarg) + INFO (" with arg %s", optarg); + INFO ("\n"); + break; + case 1: print_help(argv[0]); exit(1); break; + case 'o': + SET_STRING_OPTION(outfile); + break; + case 't': + SET_STRING_OPTION(type); + break; + case 'h': + SET_INT_OPTION(height); + break; + case 'w': + SET_INT_OPTION(width); + break; + case 'r': + SET_INT_OPTION(rotate); + break; + case 'g': *gray = 1; break; + case 'V': *verbose = 1; break; + case '?': + /* getopt_long already printed an error message. */ + break; + +#undef SET_STRING_OPTION +#undef SET_INT_OPTION + + default: + FAILIF(1, "Unknown option"); + } + } + + return optind; +} diff --git a/yuv420sp2rgb/cmdline.h b/yuv420sp2rgb/cmdline.h new file mode 100644 index 0000000..49760ad --- /dev/null +++ b/yuv420sp2rgb/cmdline.h @@ -0,0 +1,15 @@ +#ifndef CMDLINE_H +#define CMDLINE_H + +void print_help(const char *executable_name); + +extern int get_options(int argc, char **argv, + char **outfile, + int *height, + int *width, + int *gray, + char **type, + int *rotate, + int *verbose); + +#endif/*CMDLINE_H*/ diff --git a/yuv420sp2rgb/debug.c b/yuv420sp2rgb/debug.c new file mode 100644 index 0000000..263e09f --- /dev/null +++ b/yuv420sp2rgb/debug.c @@ -0,0 +1,38 @@ +#include +#include +#include + +#define NUM_COLS (32) + +int dump_hex_buffer(FILE *s, void *b, size_t len, size_t elsize) { + int num_nonprintable = 0; + int i, last; + char *pchr = (char *)b; + fputc('\n', s); + fprintf(s, "%p: ", b); + for (i = last = 0; i < len; i++) { + if (!elsize) { + if (i && !(i % 4)) fprintf(s, " "); + if (i && !(i % 8)) fprintf(s, " "); + } else { + if (i && !(i % elsize)) fprintf(s, " "); + } + + if (i && !(i % NUM_COLS)) { + while (last < i) { + if (isprint(pchr[last])) + fputc(pchr[last], s); + else { + fputc('.', s); + num_nonprintable++; + } + last++; + } + fprintf(s, " (%d)\n%p: ", i, b); + } + fprintf(s, "%02x", (unsigned char)pchr[i]); + } + if (i && (i % NUM_COLS)) fputs("\n", s); + return num_nonprintable; +} + diff --git a/yuv420sp2rgb/debug.h b/yuv420sp2rgb/debug.h new file mode 100644 index 0000000..9bbf47f --- /dev/null +++ b/yuv420sp2rgb/debug.h @@ -0,0 +1,90 @@ +#ifndef DEBUG_H +#define DEBUG_H + +#include +#include + +#define unlikely(expr) __builtin_expect (expr, 0) +#define likely(expr) __builtin_expect (expr, 1) + +#ifdef DEBUG + + #define FAILIF(cond, msg...) do { \ + if (unlikely(cond)) { \ + fprintf(stderr, "%s(%d): ", __FILE__, __LINE__); \ + fprintf(stderr, ##msg); \ + exit(1); \ + } \ +} while(0) + +/* Debug enabled */ + #define ASSERT(x) do { \ + if (unlikely(!(x))) { \ + fprintf(stderr, \ + "ASSERTION FAILURE %s:%d: [%s]\n", \ + __FILE__, __LINE__, #x); \ + exit(1); \ + } \ +} while(0) + +#else + + #define FAILIF(cond, msg...) do { \ + if (unlikely(cond)) { \ + fprintf(stderr, ##msg); \ + exit(1); \ + } \ +} while(0) + +/* No debug */ + #define ASSERT(x) do { } while(0) + +#endif/* DEBUG */ + +#define FAILIF_LIBELF(cond, function) \ + FAILIF(cond, "%s(): %s\n", #function, elf_errmsg(elf_errno())); + +static inline void *MALLOC(unsigned int size) { + void *m = malloc(size); + FAILIF(NULL == m, "malloc(%d) failed!\n", size); + return m; +} + +static inline void *CALLOC(unsigned int num_entries, unsigned int entry_size) { + void *m = calloc(num_entries, entry_size); + FAILIF(NULL == m, "calloc(%d, %d) failed!\n", num_entries, entry_size); + return m; +} + +static inline void *REALLOC(void *ptr, unsigned int size) { + void *m = realloc(ptr, size); + FAILIF(NULL == m, "realloc(%p, %d) failed!\n", ptr, size); + return m; +} + +static inline void FREE(void *ptr) { + free(ptr); +} + +static inline void FREEIF(void *ptr) { + if (ptr) FREE(ptr); +} + +#define PRINT(x...) do { \ + extern int quiet_flag; \ + if(likely(!quiet_flag)) \ + fprintf(stdout, ##x); \ +} while(0) + +#define ERROR PRINT + +#define INFO(x...) do { \ + extern int verbose_flag; \ + if(unlikely(verbose_flag)) \ + fprintf(stdout, ##x); \ +} while(0) + +/* Prints a hex and ASCII dump of the selected buffer to the selected stream. */ +int dump_hex_buffer(FILE *s, void *b, size_t l, size_t elsize); + +#endif/*DEBUG_H*/ diff --git a/yuv420sp2rgb/yuv420sp2rgb.c b/yuv420sp2rgb/yuv420sp2rgb.c new file mode 100644 index 0000000..c3fc29b --- /dev/null +++ b/yuv420sp2rgb/yuv420sp2rgb.c @@ -0,0 +1,337 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef max +#define max(a,b) ({typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _a : _b; }) +#define min(a,b) ({typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? _a : _b; }) +#endif + +#define CONVERT_TYPE_PPM 0 +#define CONVERT_TYPE_RGB 1 +#define CONVERT_TYPE_ARGB 2 + +/* + YUV 4:2:0 image with a plane of 8 bit Y samples followed by an interleaved + U/V plane containing 8 bit 2x2 subsampled chroma samples. + except the interleave order of U and V is reversed. + + H V + Y Sample Period 1 1 + U (Cb) Sample Period 2 2 + V (Cr) Sample Period 2 2 + */ + +typedef struct rgb_context { + unsigned char *buffer; + int width; + int height; + int rotate; + int i; + int j; + int size; /* for debugging */ +} rgb_context; + +typedef void (*rgb_cb)( + unsigned char r, + unsigned char g, + unsigned char b, + rgb_context *ctx); + +const int bytes_per_pixel = 2; + +static void color_convert_common( + unsigned char *pY, unsigned char *pUV, + int width, int height, + unsigned char *buffer, + int size, /* buffer size in bytes */ + int gray, + int rotate, + rgb_cb cb) +{ + int i, j; + int nR, nG, nB; + int nY, nU, nV; + rgb_context ctx; + + ctx.buffer = buffer; + ctx.size = size; /* debug */ + ctx.width = width; + ctx.height = height; + ctx.rotate = rotate; + + if (gray) { + for (i = 0; i < height; i++) { + for (j = 0; j < width; j++) { + nB = *(pY + i * width + j); + ctx.i = i; + ctx.j = j; + cb(nB, nB, nB, &ctx); + } + } + } else { + // YUV 4:2:0 + for (i = 0; i < height; i++) { + for (j = 0; j < width; j++) { + nY = *(pY + i * width + j); + nV = *(pUV + (i/2) * width + bytes_per_pixel * (j/2)); + nU = *(pUV + (i/2) * width + bytes_per_pixel * (j/2) + 1); + + // Yuv Convert + nY -= 16; + nU -= 128; + nV -= 128; + + if (nY < 0) + nY = 0; + + // nR = (int)(1.164 * nY + 2.018 * nU); + // nG = (int)(1.164 * nY - 0.813 * nV - 0.391 * nU); + // nB = (int)(1.164 * nY + 1.596 * nV); + + nB = (int)(1192 * nY + 2066 * nU); + nG = (int)(1192 * nY - 833 * nV - 400 * nU); + nR = (int)(1192 * nY + 1634 * nV); + + nR = min(262143, max(0, nR)); + nG = min(262143, max(0, nG)); + nB = min(262143, max(0, nB)); + + nR >>= 10; nR &= 0xff; + nG >>= 10; nG &= 0xff; + nB >>= 10; nB &= 0xff; + + ctx.i = i; + ctx.j = j; + cb(nR, nG, nB, &ctx); + } + } + } +} + +static void rgb16_cb( + unsigned char r, + unsigned char g, + unsigned char b, + rgb_context *ctx) +{ + unsigned short *rgb16 = (unsigned short *)ctx->buffer; + *(rgb16 + ctx->i * ctx->width + ctx->j) = b | (g << 5) | (r << 11); +} + +static void common_rgb_cb( + unsigned char r, + unsigned char g, + unsigned char b, + rgb_context *ctx, + int alpha) +{ + unsigned char *out = ctx->buffer; + int offset = 0; + int bpp; + int i = 0; + switch(ctx->rotate) { + case 0: /* no rotation */ + offset = ctx->i * ctx->width + ctx->j; + break; + case 1: /* 90 degrees */ + offset = ctx->height * (ctx->j + 1) - ctx->i; + break; + case 2: /* 180 degrees */ + offset = (ctx->height - 1 - ctx->i) * ctx->width + ctx->j; + break; + case 3: /* 270 degrees */ + offset = (ctx->width - 1 - ctx->j) * ctx->height + ctx->i; + break; + default: + FAILIF(1, "Unexpected roation value %d!\n", ctx->rotate); + } + + bpp = 3 + !!alpha; + offset *= bpp; + FAILIF(offset < 0, "point (%d, %d) generates a negative offset.\n", ctx->i, ctx->j); + FAILIF(offset + bpp > ctx->size, "point (%d, %d) at offset %d exceeds the size %d of the buffer.\n", + ctx->i, ctx->j, + offset, + ctx->size); + + out += offset; + + if (alpha) out[i++] = 0xff; + out[i++] = r; + out[i++] = g; + out[i] = b; +} + +static void rgb24_cb( + unsigned char r, + unsigned char g, + unsigned char b, + rgb_context *ctx) +{ + return common_rgb_cb(r,g,b,ctx,0); +} + +static void argb_cb( + unsigned char r, + unsigned char g, + unsigned char b, + rgb_context *ctx) +{ + return common_rgb_cb(r,g,b,ctx,1); +} + +static void convert(const char *infile, + const char *outfile, + int height, + int width, + int gray, + int type, + int rotate) +{ + void *in, *out; + int ifd, ofd, rc; + int psz = getpagesize(); + static char header[1024]; + int header_size; + size_t outsize; + + int bpp = 3; + switch (type) { + case CONVERT_TYPE_PPM: + if (rotate & 1) + header_size = snprintf(header, sizeof(header), "P6\n%d %d\n255\n", height, width); + else + header_size = snprintf(header, sizeof(header), "P6\n%d %d\n255\n", width, height); + case CONVERT_TYPE_RGB: + PRINT("encoding raw RGB24\n"); + header_size = 0; + break; + case CONVERT_TYPE_ARGB: + PRINT("encoding raw ARGB\n"); + header_size = 0; + bpp = 4; + break; + } + + outsize = header_size + width * height * bpp; + outsize = (outsize + psz - 1) & ~(psz - 1); + + INFO("Opening input file %s\n", infile); + ifd = open(infile, O_RDONLY); + FAILIF(ifd < 0, "open(%s) failed: %s (%d)\n", + infile, strerror(errno), errno); + + INFO("Opening output file %s\n", outfile); + ofd = open(outfile, O_RDWR | O_CREAT, 0664); + FAILIF(ofd < 0, "open(%s) failed: %s (%d)\n", + outfile, strerror(errno), errno); + + INFO("Memory-mapping input file %s\n", infile); + in = mmap(0, width * height * 3 / 2, PROT_READ, MAP_PRIVATE, ifd, 0); + FAILIF(in == MAP_FAILED, "could not mmap input file: %s (%d)\n", + strerror(errno), errno); + + INFO("Truncating output file %s to %d bytes\n", outfile, outsize); + FAILIF(ftruncate(ofd, outsize) < 0, + "Could not truncate output file to required size: %s (%d)\n", + strerror(errno), errno); + + INFO("Memory mapping output file %s\n", outfile); + out = mmap(0, outsize, PROT_WRITE, MAP_SHARED, ofd, 0); + FAILIF(out == MAP_FAILED, "could not mmap output file: %s (%d)\n", + strerror(errno), errno); + + INFO("PPM header (%d) bytes:\n%s\n", header_size, header); + FAILIF(write(ofd, header, header_size) != header_size, + "Error wrinting PPM header: %s (%d)\n", + strerror(errno), errno); + + INFO("Converting %dx%d YUV 4:2:0 to RGB24...\n", width, height); + color_convert_common(in, in + width * height, + width, height, + out + header_size, outsize - header_size, + gray, rotate, + type == CONVERT_TYPE_ARGB ? argb_cb : rgb24_cb); +} + +int verbose_flag; +int quiet_flag; + +int main(int argc, char **argv) { + + char *infile, *outfile, *type; + int height, width, gray, rotate; + int cmdline_error = 0; + + /* Parse command-line arguments. */ + + int first = get_options(argc, argv, + &outfile, + &height, + &width, + &gray, + &type, + &rotate, + &verbose_flag); + + if (first == argc) { + ERROR("You must specify an input file!\n"); + cmdline_error++; + } + if (!outfile) { + ERROR("You must specify an output file!\n"); + cmdline_error++; + } + if (height < 0 || width < 0) { + ERROR("You must specify both image height and width!\n"); + cmdline_error++; + } + + FAILIF(rotate % 90, "Rotation angle must be a multiple of 90 degrees!\n"); + + rotate /= 90; + rotate %= 4; + if (rotate < 0) rotate += 4; + + if (cmdline_error) { + print_help(argv[0]); + exit(1); + } + + infile = argv[first]; + + INFO("input file: [%s]\n", infile); + INFO("output file: [%s]\n", outfile); + INFO("height: %d\n", height); + INFO("width: %d\n", width); + INFO("gray only: %d\n", gray); + INFO("encode as: %s\n", type); + INFO("rotation: %d\n", rotate); + + /* Convert the image */ + + int conv_type; + if (!strcmp(type, "ppm")) + conv_type = CONVERT_TYPE_PPM; + else if (!strcmp(type, "rgb")) + conv_type = CONVERT_TYPE_RGB; + else if (!strcmp(type, "argb")) + conv_type = CONVERT_TYPE_ARGB; + else FAILIF(1, "Unknown encoding type %s.\n", type); + + convert(infile, outfile, + height, width, gray, + conv_type, + rotate); + + free(outfile); + return 0; +}