This repository has been archived on 2024-08-17. You can view files and clone it, but cannot push or open issues or pull requests.
freeswitch_rs/include/switch_core_video.c

3914 lines
104 KiB
C

/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Seven Du <dujinfang@gmail.com>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Anthony Minessale II <anthm@freeswitch.org>
*
*
* switch_core_video.c -- Core Video
*
*/
#ifdef SWITCH_HAVE_VPX
#include "vpx/vpx_image.h"
#if VPX_IMAGE_ABI_VERSION != (4)
#error VPX_IMAGE_ABI_VERSION is not (4)
#endif
#endif
#include <switch.h>
#include <switch_utf8.h>
#ifdef SWITCH_HAVE_YUV
#include <libyuv.h>
#endif
// #define HAVE_LIBGD
#ifdef HAVE_LIBGD
#include <gd.h>
#endif
#define STB_IMAGE_IMPLEMENTATION
#include "../libs/stb/stb_image.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "../libs/stb/stb_image_write.h"
#ifdef SWITCH_HAVE_YUV
static inline void switch_img_get_yuv_pixel(switch_image_t *img, switch_yuv_color_t *yuv, int x, int y);
#endif
static inline void switch_img_get_rgb_pixel(switch_image_t *img, switch_rgb_color_t *rgb, int x, int y);
/*!\brief Convert RGB color to YUV
*
* \param[in] rgb RGB color pointer
* \param[out] yuv YUV color pointer
*/
#ifdef SWITCH_HAVE_YUV
static inline void switch_color_rgb2yuv(switch_rgb_color_t *rgb, switch_yuv_color_t *yuv);
#endif
/*!\brief Convert YUV color to RGB
*
* \param[in] yuv YUV color pointer
* \param[out] rgb RGB color pointer
*/
#ifdef SWITCH_HAVE_YUV
static inline void switch_color_yuv2rgb(switch_yuv_color_t *yuv, switch_rgb_color_t *rgb);
#endif
/*!\brief compute distance between two colors
*
* \param[in] c1 RGB color1
* \param[in] c2 RGB color2
*/
static inline int switch_color_distance(switch_rgb_color_t *c1, switch_rgb_color_t *c2);
/*!\brief compute distance between a color and a list of colors
*
* \param[in] c1 RGB color1
* \param[in] clist RGB color list
* \param[in] count number of colors in list
* \param[in] threshold hint of target threshold to stop processing list
*/
static inline int switch_color_distance_multi(switch_rgb_color_t *c1, switch_rgb_color_t *clist, int count, uint32_t *thresholds);
/*!\brief Draw a pixel on an image
*
* \param[in] img Image descriptor
* \param[in] x leftmost pos
* \param[in] y topmost pos
* \param[in] color RGB color
*/
static inline void switch_img_draw_pixel(switch_image_t *img, int x, int y, switch_rgb_color_t *color);
struct pos_el {
switch_img_position_t pos;
const char *name;
};
static struct pos_el POS_TABLE[] = {
{POS_LEFT_TOP, "left-top"},
{POS_LEFT_MID, "left-mid"},
{POS_LEFT_BOT, "left-bot"},
{POS_CENTER_TOP, "center-top"},
{POS_CENTER_MID, "center-mid"},
{POS_CENTER_BOT, "center-bot"},
{POS_RIGHT_TOP, "right-top"},
{POS_RIGHT_MID, "right-mid"},
{POS_RIGHT_BOT, "right-bot"},
{POS_NONE, "none"},
{POS_NONE, NULL}
};
SWITCH_DECLARE(switch_img_position_t) parse_img_position(const char *name)
{
switch_img_position_t r = POS_NONE;
int i;
switch_assert(name);
for(i = 0; POS_TABLE[i].name; i++) {
if (!strcasecmp(POS_TABLE[i].name, name)) {
r = POS_TABLE[i].pos;
break;
}
}
return r;
}
struct fit_el {
switch_img_fit_t fit;
const char *name;
};
static struct fit_el IMG_FIT_TABLE[] = {
{SWITCH_FIT_SIZE, "fit-size"},
{SWITCH_FIT_SCALE, "fit-scale"},
{SWITCH_FIT_SIZE_AND_SCALE, "fit-size-and-scale"},
{SWITCH_FIT_NECESSARY, "fit-necessary"},
{SWITCH_FIT_NONE, NULL}
};
SWITCH_DECLARE(switch_img_fit_t) parse_img_fit(const char *name)
{
switch_img_fit_t r = SWITCH_FIT_SIZE;
int i;
switch_assert(name);
for(i = 0; IMG_FIT_TABLE[i].name; i++) {
if (!strcasecmp(IMG_FIT_TABLE[i].name, name)) {
r = IMG_FIT_TABLE[i].fit;
break;
}
}
return r;
}
SWITCH_DECLARE(switch_bool_t) switch_core_has_video(void)
{
#ifdef SWITCH_HAVE_VPX
#ifdef SWITCH_HAVE_YUV
return SWITCH_TRUE;
#else
return SWITCH_FALSE;
#endif
#else
return SWITCH_FALSE;
#endif
}
SWITCH_DECLARE(switch_image_t *)switch_img_alloc(switch_image_t *img,
switch_img_fmt_t fmt,
unsigned int d_w,
unsigned int d_h,
unsigned int align)
{
#ifdef SWITCH_HAVE_VPX
switch_image_t *r = NULL;
#ifdef HAVE_LIBGD
if (fmt == SWITCH_IMG_FMT_GD) {
gdImagePtr gd = gdImageCreateTrueColor(d_w, d_h);
if (!gd) return NULL;
switch_img_free(&img);
img = (switch_image_t *)vpx_img_alloc(NULL, SWITCH_IMG_FMT_ARGB, 1, 1, 1);
if (!img) {
gdImageDestroy(gd);
return NULL;
}
img->user_priv = gd;
img->d_w = d_w;
img->d_h = d_h;
img->fmt = SWITCH_IMG_FMT_GD;
return img;
}
#endif
switch_assert(d_w > 0);
switch_assert(d_h > 0);
r = (switch_image_t *)vpx_img_alloc((vpx_image_t *)img, (vpx_img_fmt_t)fmt, d_w, d_h, align);
switch_assert(r);
switch_assert(r->d_w == d_w);
switch_assert(r->d_h == d_h);
return r;
#else
return NULL;
#endif
}
SWITCH_DECLARE(switch_image_t *)switch_img_wrap(switch_image_t *img,
switch_img_fmt_t fmt,
unsigned int d_w,
unsigned int d_h,
unsigned int align,
unsigned char *img_data)
{
#ifdef SWITCH_HAVE_VPX
return (switch_image_t *)vpx_img_wrap((vpx_image_t *)img, (vpx_img_fmt_t)fmt, d_w, d_h, align, img_data);
#else
return NULL;
#endif
}
SWITCH_DECLARE(int) switch_img_set_rect(switch_image_t *img,
unsigned int x,
unsigned int y,
unsigned int w,
unsigned int h)
{
#ifdef SWITCH_HAVE_VPX
return vpx_img_set_rect((vpx_image_t *)img, x, y, w, h);
#else
return 0;
#endif
}
SWITCH_DECLARE(void) switch_img_rotate(switch_image_t **img, switch_image_rotation_mode_t mode)
{
#ifdef SWITCH_HAVE_YUV
switch_image_t *tmp_img;
switch_assert(img);
if ((*img)->fmt != SWITCH_IMG_FMT_I420) return;
if (mode == SRM_90 || mode == SRM_270) {
tmp_img = switch_img_alloc(NULL, (*img)->fmt, (*img)->d_h, (*img)->d_w, 1);
} else {
tmp_img = switch_img_alloc(NULL, (*img)->fmt, (*img)->d_w, (*img)->d_h, 1);
}
switch_assert(tmp_img);
I420Rotate((*img)->planes[SWITCH_PLANE_Y], (*img)->stride[SWITCH_PLANE_Y],
(*img)->planes[SWITCH_PLANE_U], (*img)->stride[SWITCH_PLANE_U],
(*img)->planes[SWITCH_PLANE_V], (*img)->stride[SWITCH_PLANE_V],
tmp_img->planes[SWITCH_PLANE_Y], tmp_img->stride[SWITCH_PLANE_Y],
tmp_img->planes[SWITCH_PLANE_U], tmp_img->stride[SWITCH_PLANE_U],
tmp_img->planes[SWITCH_PLANE_V], tmp_img->stride[SWITCH_PLANE_V],
(*img)->d_w, (*img)->d_h, (int)mode);
switch_img_free(img);
*img = tmp_img;
#endif
}
SWITCH_DECLARE(void) switch_img_free(switch_image_t **img)
{
#ifdef SWITCH_HAVE_VPX
if (img && *img) {
if ((*img)->fmt == SWITCH_IMG_FMT_GD) {
#ifdef HAVE_LIBGD
gdImageDestroy((gdImagePtr)(*img)->user_priv);
#endif
} else {
if ((int)(intptr_t)(*img)->user_priv != 1) {
switch_safe_free((*img)->user_priv);
}
}
switch_assert((*img)->fmt <= SWITCH_IMG_FMT_I44016);
switch_assert((*img)->d_w <= 7860 && (*img)->d_w > 0);
switch_assert((*img)->d_h <= 4320 && (*img)->d_h > 0);
vpx_img_free((vpx_image_t *)*img);
*img = NULL;
}
#endif
}
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif
#ifdef SWITCH_HAVE_YUV
static void switch_img_patch_rgb_noalpha(switch_image_t *IMG, switch_image_t *img, int x, int y)
{
int i;
if (img->fmt == SWITCH_IMG_FMT_ARGB && IMG->fmt == SWITCH_IMG_FMT_ARGB) {
int max_w = MIN(img->d_w, IMG->d_w - abs(x));
int max_h = MIN(img->d_h, IMG->d_h - abs(y));
int j;
uint8_t alpha;
switch_rgb_color_t *rgb, *RGB;
for (i = 0; i < max_h; i++) {
for (j = 0; j < max_w; j++) {
rgb = (switch_rgb_color_t *)(img->planes[SWITCH_PLANE_PACKED] + i * img->stride[SWITCH_PLANE_PACKED] + j * 4);
RGB = (switch_rgb_color_t *)(IMG->planes[SWITCH_PLANE_PACKED] + (y + i) * IMG->stride[SWITCH_PLANE_PACKED] + (x + j) * 4);
alpha = rgb->a;
if (RGB->a == 0) {
*RGB = *rgb;
continue;
}
if (alpha == 255) {
*RGB = *rgb;
continue;
}
if (alpha > 0) {
uint8_t delta1, delta2, delta;
delta1 = 255 - RGB->a;
delta2 = 255 - rgb->a;
delta = (delta1 * delta2) >> 8;
RGB->r = ((RGB->r * RGB->a) + (rgb->r * rgb->a)) / (RGB->a + rgb->a);
RGB->g = ((RGB->g * RGB->a) + (rgb->g * rgb->a)) / (RGB->a + rgb->a);
RGB->b = ((RGB->b * RGB->a) + (rgb->b * rgb->a)) / (RGB->a + rgb->a);
RGB->a = 255 - delta;
}
}
}
}
}
#endif
SWITCH_DECLARE(void) switch_img_attenuate(switch_image_t *img)
{
#ifdef SWITCH_HAVE_YUV
if (img->fmt != SWITCH_IMG_FMT_ARGB) {
return;
}
if (img->user_priv) return;
img->user_priv = (void *)(intptr_t)1;
ARGBAttenuate(img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED],
img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED], img->d_w, img->d_h);
#else
return;
#endif
}
SWITCH_DECLARE(void) switch_img_patch_rgb(switch_image_t *IMG, switch_image_t *img, int x, int y, switch_bool_t noalpha)
{
#ifdef SWITCH_HAVE_YUV
int i;
if (noalpha) {
switch_img_patch_rgb_noalpha(IMG, img, x, y);
return;
}
if (img->fmt == SWITCH_IMG_FMT_ARGB && IMG->fmt == SWITCH_IMG_FMT_ARGB) {
uint8_t* src_argb0 = img->planes[SWITCH_PLANE_PACKED];
int src_stride_argb0 = img->stride[SWITCH_PLANE_PACKED];
uint8_t* src_argb1 = IMG->planes[SWITCH_PLANE_PACKED];
int src_stride_argb1 = IMG->stride[SWITCH_PLANE_PACKED];
uint8_t* dst_argb = IMG->planes[SWITCH_PLANE_PACKED];
int dst_stride_argb = IMG->stride[SWITCH_PLANE_PACKED];
int width = MIN(img->d_w, IMG->d_w - abs(x));
int height = MIN(img->d_h, IMG->d_h - abs(y));
void (*ARGBBlendRow)(const uint8_t* src_argb, const uint8_t* src_argb1, uint8_t* dst_argb, int width) = GetARGBBlend();
// switch_img_attenuate(img);
// Coalesce rows. we have same size images, treat as a single row
if (src_stride_argb0 == width * 4 &&
src_stride_argb1 == width * 4 &&
x == 0 && y == 0) {
width *= height;
height = 1;
src_stride_argb0 = src_stride_argb1 = dst_stride_argb = 0;
}
if (y) {
src_argb1 += (y * IMG->d_w * 4);
dst_argb += (y * IMG->d_w * 4);
}
if (x) {
src_argb1 += (x * 4);
dst_argb += (x * 4);
}
for (i = 0; i < height; ++i) {
ARGBBlendRow(src_argb0, src_argb1, dst_argb, width);
src_argb0 += src_stride_argb0;
src_argb1 += src_stride_argb1;
dst_argb += dst_stride_argb;
}
}
#endif
}
SWITCH_DECLARE(void) switch_img_patch(switch_image_t *IMG, switch_image_t *img, int x, int y)
{
int i, len, max_h;
int xoff = 0, yoff = 0;
if (img->fmt == SWITCH_IMG_FMT_ARGB && IMG->fmt == SWITCH_IMG_FMT_ARGB) {
switch_img_patch_rgb(IMG, img, x, y, SWITCH_FALSE);
return;
}
switch_assert(IMG->fmt == SWITCH_IMG_FMT_I420);
if (img->fmt == SWITCH_IMG_FMT_ARGB) {
int max_w = MIN(img->d_w, IMG->d_w - abs(x));
int max_h = MIN(img->d_h, IMG->d_h - abs(y));
int j;
uint8_t alpha;
switch_rgb_color_t *rgb;
for (i = 0; i < max_h; i++) {
for (j = 0; j < max_w; j++) {
rgb = (switch_rgb_color_t *)(img->planes[SWITCH_PLANE_PACKED] + i * img->stride[SWITCH_PLANE_PACKED] + j * 4);
alpha = rgb->a;
if (alpha == 255) {
switch_img_draw_pixel(IMG, x + j, y + i, rgb);
} else if (alpha != 0) {
switch_rgb_color_t RGB = { 0 };
switch_img_get_rgb_pixel(IMG, &RGB, x + j, y + i);
RGB.a = 255;
RGB.r = ((RGB.r * (255 - alpha)) >> 8) + ((rgb->r * alpha) >> 8);
RGB.g = ((RGB.g * (255 - alpha)) >> 8) + ((rgb->g * alpha) >> 8);
RGB.b = ((RGB.b * (255 - alpha)) >> 8) + ((rgb->b * alpha) >> 8);
switch_img_draw_pixel(IMG, x + j, y + i, &RGB);
}
}
}
return;
#ifdef HAVE_LIBGD
} else if (img->fmt == SWITCH_IMG_FMT_GD) {
gdImagePtr gd = (gdImagePtr)img->user_priv;
switch_rgb_color_t rgb_color;
int pixel;
int i, j;
switch_assert(gd);
if (!gd->trueColor) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "GD is experimental, only true color image is supported\n");
return;
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "truecolor: %d alpha: %d, transparent? %d\n", gd->trueColor, gd->saveAlphaFlag, gd->transparent);
for(i = 0; i < img->d_h; i++) {
for(j = 0; j < img->d_w; j++) {
pixel = gd->tpixels[i][j];
rgb_color.a = 255; // TODO: handle transparent
rgb_color.r = gdTrueColorGetRed(pixel);
rgb_color.g = gdTrueColorGetGreen(pixel);
rgb_color.b = gdTrueColorGetBlue(pixel);
switch_img_draw_pixel(IMG, x + j, y + i, &rgb_color);
}
}
return;
#endif
}
if (x < 0) {
xoff = -x;
x = 0;
}
if (y < 0) {
yoff = -y;
y = 0;
}
max_h = MIN(y + img->d_h - yoff, IMG->d_h);
len = MIN(img->d_w - xoff, IMG->d_w - x);
if (x & 0x1) { x++; len--; }
if (y & 0x1) y++;
if (len <= 0) return;
for (i = y; i < max_h; i++) {
memcpy(IMG->planes[SWITCH_PLANE_Y] + IMG->stride[SWITCH_PLANE_Y] * i + x, img->planes[SWITCH_PLANE_Y] + img->stride[SWITCH_PLANE_Y] * (i - y + yoff) + xoff, len);
}
if ((len & 1) && (x + len) < img->d_w - 1) len++;
len /= 2;
for (i = y; i < max_h; i += 2) {
memcpy(IMG->planes[SWITCH_PLANE_U] + IMG->stride[SWITCH_PLANE_U] * (i / 2) + x / 2, img->planes[SWITCH_PLANE_U] + img->stride[SWITCH_PLANE_U] * ((i - y + yoff) / 2) + xoff / 2, len);
memcpy(IMG->planes[SWITCH_PLANE_V] + IMG->stride[SWITCH_PLANE_V] * (i / 2) + x / 2, img->planes[SWITCH_PLANE_V] + img->stride[SWITCH_PLANE_V] * ((i - y + yoff) / 2) + xoff / 2, len);
}
}
SWITCH_DECLARE(void) switch_img_patch_rect(switch_image_t *IMG, int X, int Y, switch_image_t *img, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
#ifdef SWITCH_HAVE_VPX
switch_image_t *tmp = NULL;
uint8_t *data;
if (x >= img->d_w || y >= img->d_h) return;
if (w == img->d_w && h == img->d_h) {
switch_img_patch(IMG, img, X, Y);
return;
}
if (!(img->fmt & SWITCH_IMG_FMT_PLANAR)) {
data = img->planes[SWITCH_PLANE_PACKED];
} else {
data = img->planes[SWITCH_PLANE_Y];
}
if (img->d_w == img->stride[0]) {
tmp = (switch_image_t *)vpx_img_wrap(NULL, img->fmt, img->d_w, img->d_h, 1, data);
} else {
switch_img_copy(img, &tmp);
}
if (!tmp) return;
w = MIN(img->d_w - x, w);
h = MIN(img->d_h - y, h);
if (!switch_img_set_rect(tmp, x, y, w, h)) {
switch_img_patch(IMG, tmp, X, Y);
}
switch_img_free(&tmp);
#endif
}
SWITCH_DECLARE(void) switch_img_copy(switch_image_t *img, switch_image_t **new_img)
{
#ifdef SWITCH_HAVE_YUV
switch_img_fmt_t new_fmt = img->fmt;
switch_assert(img);
switch_assert(new_img);
if (img->fmt != SWITCH_IMG_FMT_I420 && img->fmt != SWITCH_IMG_FMT_ARGB) return;
if (*new_img) {
new_fmt = (*new_img)->fmt;
if ((*new_img)->fmt != SWITCH_IMG_FMT_I420 && (*new_img)->fmt != SWITCH_IMG_FMT_ARGB && (*new_img)->fmt != SWITCH_IMG_FMT_ARGB_LE) return;
if (img->d_w != (*new_img)->d_w || img->d_h != (*new_img)->d_h ) {
new_fmt = (*new_img)->fmt;
switch_img_free(new_img);
}
}
if (*new_img == NULL) {
*new_img = switch_img_alloc(NULL, new_fmt, img->d_w, img->d_h, 1);
}
switch_assert(*new_img);
if (img->fmt == SWITCH_IMG_FMT_I420) {
if (new_fmt == SWITCH_IMG_FMT_I420) {
I420Copy(img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U],
img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V],
(*new_img)->planes[SWITCH_PLANE_Y], (*new_img)->stride[SWITCH_PLANE_Y],
(*new_img)->planes[SWITCH_PLANE_U], (*new_img)->stride[SWITCH_PLANE_U],
(*new_img)->planes[SWITCH_PLANE_V], (*new_img)->stride[SWITCH_PLANE_V],
img->d_w, img->d_h);
} else if (new_fmt == SWITCH_IMG_FMT_ARGB) {
I420ToARGB(img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U],
img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V],
(*new_img)->planes[SWITCH_PLANE_PACKED], (*new_img)->stride[SWITCH_PLANE_PACKED],
img->d_w, img->d_h);
} else if (new_fmt == SWITCH_IMG_FMT_ARGB_LE) {
I420ToABGR(img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U],
img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V],
(*new_img)->planes[SWITCH_PLANE_PACKED], (*new_img)->stride[SWITCH_PLANE_PACKED],
img->d_w, img->d_h);
}
} else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
if (new_fmt == SWITCH_IMG_FMT_ARGB) {
ARGBCopy(img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED],
(*new_img)->planes[SWITCH_PLANE_PACKED], (*new_img)->stride[SWITCH_PLANE_PACKED],
img->d_w, img->d_h);
} else if (new_fmt == SWITCH_IMG_FMT_I420) {
ARGBToI420(img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED],
(*new_img)->planes[SWITCH_PLANE_Y], (*new_img)->stride[SWITCH_PLANE_Y],
(*new_img)->planes[SWITCH_PLANE_U], (*new_img)->stride[SWITCH_PLANE_U],
(*new_img)->planes[SWITCH_PLANE_V], (*new_img)->stride[SWITCH_PLANE_V],
img->d_w, img->d_h);
}
}
#else
return;
#endif
}
SWITCH_DECLARE(void) switch_img_rotate_copy(switch_image_t *img, switch_image_t **new_img, switch_image_rotation_mode_t mode)
{
switch_assert(img);
switch_assert(new_img);
#ifdef SWITCH_HAVE_YUV
if (img->fmt != SWITCH_IMG_FMT_I420) abort();
if (*new_img != NULL) {
if (img->fmt != (*new_img)->fmt || img->d_w != (*new_img)->d_w || img->d_h != (*new_img)->d_h) {
switch_img_free(new_img);
}
}
if (*new_img == NULL) {
if (mode == SRM_90 || mode == SRM_270) {
*new_img = switch_img_alloc(NULL, img->fmt, img->d_h, img->d_w, 1);
} else {
*new_img = switch_img_alloc(NULL, img->fmt, img->d_w, img->d_h, 1);
}
}
switch_assert(*new_img);
I420Rotate(img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U],
img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V],
(*new_img)->planes[SWITCH_PLANE_Y], (*new_img)->stride[SWITCH_PLANE_Y],
(*new_img)->planes[SWITCH_PLANE_U], (*new_img)->stride[SWITCH_PLANE_U],
(*new_img)->planes[SWITCH_PLANE_V], (*new_img)->stride[SWITCH_PLANE_V],
img->d_w, img->d_h, (int)mode);
#else
return;
#endif
}
SWITCH_DECLARE(switch_image_t *) switch_img_copy_rect(switch_image_t *img, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
{
#ifdef SWITCH_HAVE_VPX
switch_image_t *new_img = NULL, *tmp;
uint8_t *data;
switch_assert(img);
if (x >= img->d_w || y >= img->d_h) return NULL;
if (!(img->fmt & SWITCH_IMG_FMT_PLANAR)) {
data = img->planes[SWITCH_PLANE_PACKED];
} else {
data = img->planes[SWITCH_PLANE_Y];
}
tmp = (switch_image_t *)vpx_img_wrap(NULL, img->fmt, img->d_w, img->d_h, 1, data);
if (!tmp) return NULL;
w = MIN(img->d_w - x, w);
h = MIN(img->d_h - y, h);
if (!switch_img_set_rect(tmp, x, y, w, h)) {
switch_img_copy(tmp, &new_img);
}
switch_img_free(&tmp);
return new_img;
#else
return NULL;
#endif
}
#if 0
static inline void switch_core_rgb2xyz(switch_rgb_color_t *rgb, switch_xyz_color_t *xyz)
{
double r, g, b;
r = (double)rgb->r / 255;
g = (double)rgb->g / 255;
b = (double)rgb->b / 255;
if ( r > 0.04045 ) {
r = ( ( r + 0.055 ) / 1.055 );
r = pow(r, 2.4);
} else {
r = r / 12.92;
}
if ( g > 0.04045 ) {
g = ( ( g + 0.055 ) / 1.055 );
g = pow(g, 2.4);
} else {
g = g / 12.92;
}
if ( b > 0.04045 ) {
b = ( ( b + 0.055 ) / 1.055 );
b = pow(b, 2.4);
} else {
b = b / 12.92;
}
r = r * 100;
g = g * 100;
b = b * 100;
//Observer. = 2degrees, Illuminant = D65
xyz->x = r * 0.4124 + g * 0.3576 + b * 0.1805;
xyz->y = r * 0.2126 + g * 0.7152 + b * 0.0722;
xyz->z = r * 0.0193 + g * 0.1192 + b * 0.9505;
}
#define SVMAX(a,b) ((a) > (b) ? (a) : (b))
#define SVMAX3(a,b,c) (SVMAX((a), SVMAX((b),(c))))
#define SVMIN(a,b) ((a) < (b) ? (a) : (b))
#define SVMIN3(a,b,c) (SVMIN((a), SVMIN((b),(c))))
static inline void switch_core_rgb2hsl(switch_rgb_color_t *rgb, switch_hsl_color_t *hsl)
{
double r, g, b, max, min;
r = (double)rgb->r / 255;
g = (double)rgb->g / 255;
b = (double)rgb->b / 255;
max = SVMAX3(r, g, b);
min = SVMIN3(r, g, b);
hsl->l = (max + min) / 2;
if (max != min) {
double d = max - min;
hsl->s = hsl->l > 0.5f ? d / (2 - max - min) : d / (max + min);
if (max == r) {
hsl->h = (g - b) / (max - min);
} else if(max == g) {
hsl->h = 2.0 + ((b - r) / (max - min));
} else {
hsl->h = 4.0 + ((r - g) / (max - min));
}
} else {
hsl->h = hsl->s = 0;
}
hsl->h = round(hsl->h * 60);
if (hsl->h < 0) hsl->h += 360;
hsl->s *= 100;
hsl->l *= 100;
}
static inline void switch_core_rgb2lab(switch_rgb_color_t *rgb, switch_lab_color_t *lab)
{
double x,y,z;
double r = rgb->r;
double g = rgb->g;
double b = rgb->b;
r=r>10.31475 ? 1.474000611989649e-6 * pow(r+14.025 , 2.4) : r * 0.0003035269835488375;
g=g>10.31475 ? 1.474000611989649e-6 * pow(g+14.025 , 2.4) : g * 0.0003035269835488375;
b=b>10.31475 ? 1.474000611989649e-6 * pow(b+14.025 , 2.4) : b * 0.0003035269835488375;
x=r * 0.43394994055572506 + g * 0.3762097699033109 + b * 0.18984028954096394;
y=r * 0.2126729 + g * 0.7151522 + b * 0.0721750;
z=r * 0.017756582753965265 + g * 0.10946796102238182 + b * 0.8727754562236529;
x = x > 0.008856452 ? pow(x , 0.3333333333333333) : 7.787037037037037 * x + 0.13793103448275862;
y = y > 0.008856452 ? pow(y , 0.3333333333333333) : 7.787037037037037 * y + 0.13793103448275862;
z = z > 0.008856452 ? pow(z , 0.3333333333333333) : 7.787037037037037 * z + 0.13793103448275862;
lab->l = 116 * y - 16;
lab->a = 500 * (x - y);
lab->b = 200 * (y - z);
}
/// Computes the CIEDE2000 color-difference between two Lab colors
/// Based on the article:
/// The CIEDE2000 Color-Difference Formula: Implementation Notes,
/// Supplementary Test Data, and Mathematical Observations,", G. Sharma,
/// W. Wu, E. N. Dalal, submitted to Color Research and Application,
/// January 2004.
/// Available at http://www.ece.rochester.edu/~/gsharma/ciede2000/
/// Based on the C++ implementation by Ofir Pele, The Hebrew University of Jerusalem 2010.
//
static inline double switch_CIEDE2000(switch_lab_color_t *lab1, switch_lab_color_t *lab2)
{
double Lstd = lab1->l;
double astd = lab1->a;
double bstd = lab1->b;
double pi = M_PI;
double Lsample = lab2->l;
double asample = lab2->a;
double bsample = lab2->b;
//double _kL = 1.0;
//double _kC = 1.0;
//double _kH = 1.0;
double Cabstd= sqrt(astd*astd+bstd*bstd);
double Cabsample= sqrt(asample*asample+bsample*bsample);
double Cabarithmean= (Cabstd + Cabsample)/2.0;
double G= 0.5*( 1.0 - sqrt( pow(Cabarithmean,7.0)/(pow(Cabarithmean,7.0) + pow(25.0,7.0))));
double apstd= (1.0+G)*astd; // aprime in paper
double apsample= (1.0+G)*asample; // aprime in paper
double Cpsample= sqrt(apsample*apsample+bsample*bsample);
double Cpstd= sqrt(apstd*apstd+bstd*bstd);
// Compute product of chromas
double Cpprod= (Cpsample*Cpstd);
double hpsample, dL, dC, dhp, dH, Lp, Cp;
double hp, Lpm502, Sl, Sc, T, Sh, delthetarad, Rc, RT;
// Ensure hue is between 0 and 2pi
double hpstd= atan2(bstd,apstd);
if (hpstd<0) hpstd+= 2.0*pi; // rollover ones that come -ve
hpsample= atan2(bsample,apsample);
if (hpsample<0) hpsample+= 2.0*pi;
if ( (fabs(apsample)+fabs(bsample))==0.0) hpsample= 0.0;
dL= (Lsample-Lstd);
dC= (Cpsample-Cpstd);
// Computation of hue difference
dhp= (hpsample-hpstd);
if (dhp>pi) dhp-= 2.0*pi;
if (dhp<-pi) dhp+= 2.0*pi;
// set chroma difference to zero if the product of chromas is zero
if (Cpprod == 0.0) dhp= 0.0;
// Note that the defining equations actually need
// signed Hue and chroma differences which is different
// from prior color difference formulae
dH= 2.0*sqrt(Cpprod)*sin(dhp/2.0);
//%dH2 = 4*Cpprod.*(sin(dhp/2)).^2;
// weighting functions
Lp= (Lsample+Lstd)/2.0;
Cp= (Cpstd+Cpsample)/2.0;
// Average Hue Computation
// This is equivalent to that in the paper but simpler programmatically.
// Note average hue is computed in radians and converted to degrees only
// where needed
hp= (hpstd+hpsample)/2.0;
// Identify positions for which abs hue diff exceeds 180 degrees
if ( fabs(hpstd-hpsample) > pi ) hp-= pi;
// rollover ones that come -ve
if (hp<0) hp+= 2.0*pi;
// Check if one of the chroma values is zero, in which case set
// mean hue to the sum which is equivalent to other value
if (Cpprod==0.0) hp= hpsample+hpstd;
Lpm502= (Lp-50.0)*(Lp-50.0);;
Sl= 1.0+0.015*Lpm502/sqrt(20.0+Lpm502);
Sc= 1.0+0.045*Cp;
T= 1.0 - 0.17*cos(hp - pi/6.0) + 0.24*cos(2.0*hp) + 0.32*cos(3.0*hp+pi/30.0) - 0.20*cos(4.0*hp-63.0*pi/180.0);
Sh= 1.0 + 0.015*Cp*T;
delthetarad= (30.0*pi/180.0)*exp(- pow(( (180.0/pi*hp-275.0)/25.0),2.0));
Rc= 2.0*sqrt(pow(Cp,7.0)/(pow(Cp,7.0) + pow(25.0,7.0)));
RT= -sin(2.0*delthetarad)*Rc;
// The CIE 00 color difference
return sqrt( pow((dL/Sl),2.0) + pow((dC/Sc),2.0) + pow((dH/Sh),2.0) + RT*(dC/Sc)*(dH/Sh) );
}
#endif
static inline int switch_color_distance(switch_rgb_color_t *c1, switch_rgb_color_t *c2)
{
int cr, cg, cb;
int cr2, cg2, cb2;
double a, b;
int aa, bb, r;
cr = c1->r - c2->r;
cg = c1->g - c2->g;
cb = c1->b - c2->b;
if (!cr && !cg && !cb) return 0;
cr2 = c1->r/2 - c2->r/2;
cg2 = c1->g/2 - c2->g/2;
cb2 = c1->b/2 - c2->b/2;
a = ((2*cr*cr) + (4*cg*cg) + (3*cb*cb));
b = ((2*cr2*cr2) + (4*cg2*cg2) + (3*cb2*cb2));
aa = (int)a;
bb = (int)b*25;
r = (((bb*4)+(aa))/9)/100;
return r;
}
static inline int switch_color_distance_multi(switch_rgb_color_t *c1, switch_rgb_color_t *clist, int count, uint32_t *thresholds)
{
int x = 0, hits = 0;
for (x = 0; x < count; x++) {
int distance = switch_color_distance(c1, &clist[x]);
if (distance <= thresholds[x]) {
hits++;
break;
}
}
return hits;
}
struct switch_chromakey_s {
switch_image_t *cache_img;
switch_rgb_color_t mask[CHROMAKEY_MAX_MASK];
uint32_t thresholds[CHROMAKEY_MAX_MASK];
int mask_len;
switch_shade_t autocolor;
uint32_t dft_thresh;
uint32_t dft_thresh_squared;
uint32_t rr;
uint32_t gg;
uint32_t bb;
uint32_t color_count;
switch_rgb_color_t auto_color;
int no_cache;
int frames_read;
};
SWITCH_DECLARE(switch_shade_t) switch_chromakey_str2shade(switch_chromakey_t *ck, const char *shade_name)
{
switch_shade_t shade = SWITCH_SHADE_NONE;
if (!strcasecmp(shade_name, "red")) {
shade = SWITCH_SHADE_RED;
} else if (!strcasecmp(shade_name, "green")) {
shade = SWITCH_SHADE_GREEN;
} else if (!strcasecmp(shade_name, "blue")) {
shade = SWITCH_SHADE_BLUE;
} else if (!strcasecmp(shade_name, "auto")) {
shade = SWITCH_SHADE_AUTO;
}
return shade;
}
SWITCH_DECLARE(void) switch_chromakey_set_default_threshold(switch_chromakey_t *ck, uint32_t threshold)
{
int i;
ck->dft_thresh = threshold;
ck->dft_thresh_squared = threshold * threshold;
for (i = 0; i < ck->mask_len; i++) {
if (!ck->thresholds[i]) ck->thresholds[i] = ck->dft_thresh_squared;
}
}
SWITCH_DECLARE(switch_status_t) switch_chromakey_clear_colors(switch_chromakey_t *ck)
{
switch_assert(ck);
ck->autocolor = SWITCH_SHADE_NONE;
ck->mask_len = 0;
memset(ck->mask, 0, sizeof(ck->mask[0]) * CHROMAKEY_MAX_MASK);
memset(ck->thresholds, 0, sizeof(ck->thresholds[0]) * CHROMAKEY_MAX_MASK);
ck->no_cache = 1;
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(switch_status_t) switch_chromakey_autocolor(switch_chromakey_t *ck, switch_shade_t autocolor, uint32_t threshold)
{
switch_assert(ck);
switch_chromakey_clear_colors(ck);
ck->autocolor = autocolor;
ck->dft_thresh = threshold;
ck->dft_thresh_squared = threshold * threshold;
switch_img_free(&ck->cache_img);
ck->no_cache = 90;
memset(&ck->auto_color, 0, sizeof(ck->auto_color));
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(switch_status_t) switch_chromakey_add_color(switch_chromakey_t *ck, switch_rgb_color_t *color, uint32_t threshold)
{
switch_assert(ck);
if (ck->mask_len == CHROMAKEY_MAX_MASK) {
return SWITCH_STATUS_FALSE;
}
ck->mask[ck->mask_len] = *color;
ck->thresholds[ck->mask_len] = threshold * threshold;
ck->mask_len++;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Adding color %d:%d:%d #%.2x%.2x%.2x\n",
ck->auto_color.r, ck->auto_color.g, ck->auto_color.b, ck->auto_color.r, ck->auto_color.g, ck->auto_color.b);
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(switch_status_t) switch_chromakey_destroy(switch_chromakey_t **ckP)
{
switch_chromakey_t *ck;
switch_assert(ckP);
ck = *ckP;
*ckP = NULL;
if (ck) {
switch_img_free(&ck->cache_img);
free(ck);
}
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(switch_status_t) switch_chromakey_create(switch_chromakey_t **ckP)
{
switch_chromakey_t *ck;
switch_assert(ckP);
switch_zmalloc(ck, sizeof(*ck));
*ckP = ck;
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(switch_image_t *) switch_chromakey_cache_image(switch_chromakey_t *ck)
{
switch_assert(ck);
return ck->cache_img;
}
static inline int get_max(switch_rgb_color_t *c1)
{
if (c1->r > c1->g && c1->r > c1->b) {
return 1;
}
if (c1->g > c1->r && c1->g > c1->b) {
return 2;
}
if (c1->b > c1->r && c1->b > c1->g) {
return 3;
}
return 0;
}
static inline int switch_color_dom_cmp(switch_rgb_color_t *c1, switch_rgb_color_t *c2)
{
int c1_max = get_max(c1);
int c2_max = get_max(c2);
if (c1_max && c1_max == c2_max) return 1;
return 0;
}
static inline int switch_color_distance_literal(switch_rgb_color_t *c1, switch_rgb_color_t *c2, int distance)
{
int r = abs(c1->r - c2->r);
int g = abs(c1->g - c2->g);
int b = abs(c1->b - c2->b);
if (r < distance && g < distance && b < distance) return 1;
return 0;
}
static inline int switch_color_distance_cheap(switch_rgb_color_t *c1, switch_rgb_color_t *c2)
{
int r = abs(c1->r - c2->r);
int g = abs(c1->g - c2->g);
int b = abs(c1->b - c2->b);
if (r < 5 && g < 5 && b < 5) return 0;
return (3*r) + (4*g) + (3*b);
}
static inline void get_dom(switch_shade_t autocolor, switch_rgb_color_t *color, int *domP, int *aP, int *bP)
{
int dom, a, b;
switch(autocolor) {
case SWITCH_SHADE_RED:
dom = color->r;
a = color->g;
b = color->b;
break;
case SWITCH_SHADE_GREEN:
dom = color->g;
a = color->r;
b = color->b;
break;
case SWITCH_SHADE_BLUE:
dom = color->b;
a = color->r;
b = color->g;
break;
default:
dom = 0;
a = 0;
b = 0;
break;
}
*domP = dom;
*aP = a;
*bP = b;
}
SWITCH_DECLARE(void) switch_chromakey_process(switch_chromakey_t *ck, switch_image_t *img)
{
uint8_t *pixel, *last_pixel = NULL, *cache_pixel = NULL, *end_pixel = NULL;
int last_hits = 0;
switch_image_t *cache_img;
int same = 0;
int same_same = 0;
#ifdef DEBUG_CHROMA
int other_img_cached = 0, color_cached = 0, checked = 0, hit_total = 0, total_pixel = 0, delta_hits = 0;
#endif
switch_assert(ck);
switch_assert(img);
if (img->fmt != SWITCH_IMG_FMT_ARGB) return;
pixel = img->planes[SWITCH_PLANE_PACKED];
cache_img = ck->cache_img;
ck->cache_img = NULL;
ck->frames_read++;
if ((ck->frames_read % 300) == 0) {
ck->no_cache = 2;
}
if (cache_img && (cache_img->d_w != img->d_w || cache_img->d_h != img->d_h)) {
switch_img_free(&cache_img);
}
if (cache_img) {
cache_pixel = cache_img->planes[SWITCH_PLANE_PACKED];
}
end_pixel = (img->planes[SWITCH_PLANE_PACKED] + img->d_w * img->d_h * 4);
if (ck->autocolor) {
ck->color_count = 0;
ck->rr = ck->gg = ck->bb = 0;
}
for (; pixel < end_pixel; pixel += 4) {
switch_rgb_color_t *color = (switch_rgb_color_t *)pixel;
switch_rgb_color_t *last_color = (switch_rgb_color_t *)last_pixel;
int hits = 0;
#ifdef DEBUG_CHROMA
total_pixel++;
#endif
if (!ck->no_cache && cache_img && cache_pixel) {
switch_rgb_color_t *cache_color = (switch_rgb_color_t *)cache_pixel;
if (switch_color_distance_cheap(color, cache_color) < 5) {
#ifdef DEBUG_CHROMA
other_img_cached++;
#endif
color->a = cache_color->a;
goto end;
}
}
if (last_color) {
if (switch_color_distance_cheap(color, last_color) < 5) {
hits = last_hits;
#ifdef DEBUG_CHROMA
color_cached++;
#endif
same++;
} else {
same = 0;
}
}
if (!hits) {
if (ck->autocolor) {
int dom, a, b;
get_dom(ck->autocolor, color, &dom, &a, &b);
if (ck->autocolor != SWITCH_SHADE_AUTO) {
//printf("WTF %d\n", ck->dft_thresh);
int tol = ck->dft_thresh;
int a_tol = tol/6;
int b_tol = tol/6;
if (dom > a && dom > b && dom > tol) {
if (dom - a > a_tol && dom - b > b_tol) {
hits = 1;
}
}
}
}
if (!hits && ck->mask_len) {
hits = switch_color_distance_multi(color, ck->mask, ck->mask_len, ck->thresholds);
}
#ifdef DEBUG_CHROMA
checked++;
#endif
}
end:
if (same > 100 && last_color && switch_color_dom_cmp(color, last_color)) {
same_same++;
} else {
same_same = 0;
}
if (!hits && ck->autocolor == SWITCH_SHADE_AUTO && (same > 300 || same_same > 50) && ck->no_cache) {
ck->color_count++;
ck->rr += color->r;
ck->gg += color->g;
ck->bb += color->b;
}
if (cache_pixel) {
cache_pixel += 4;
}
if (hits) {
#ifdef DEBUG_CHROMA
hit_total++;
#endif
color->a = 0;
}
last_pixel = pixel;
last_hits = hits;
}
if (ck->color_count > 1000) {
switch_rgb_color_t *last_color = NULL;
int skip = 0;
int dom, a, b;
ck->auto_color.r = ck->rr / ck->color_count;
ck->auto_color.g = ck->gg / ck->color_count;
ck->auto_color.b = ck->bb / ck->color_count;
if (ck->mask_len) {
int i = 0;
for (i = 0; i < ck->mask_len; i++) {
last_color = &ck->mask[i];
get_dom(ck->autocolor, &ck->auto_color, &dom, &a, &b);
if (switch_color_distance_literal(&ck->auto_color, last_color, 10) || !switch_color_dom_cmp(&ck->auto_color, last_color) || (dom - a < 50 || dom - b < 50)) {
skip = 1;
break;
}
}
}
if (!ck->mask_len || !skip) {
switch_chromakey_add_color(ck, &ck->auto_color, ck->dft_thresh);
}
}
if (ck->no_cache > 0 && ck->mask_len) {
ck->no_cache--;
}
#ifdef DEBUG_CHROMA
printf("total %d: other img cache %d color cache %d Checked %d Hit Total %d Delta hits: %d\n", total_pixel, other_img_cached, color_cached, checked, hit_total, delta_hits);
#endif
if (!ck->no_cache) {
switch_img_copy(img, &ck->cache_img);
}
switch_img_free(&cache_img);
return;
}
SWITCH_DECLARE(void) switch_img_chromakey(switch_image_t *img, switch_rgb_color_t *mask, int threshold)
{
switch_rgb_color_t *pixel, *last_pixel = NULL;
int last_threshold = 0;
switch_assert(img);
if (img->fmt != SWITCH_IMG_FMT_ARGB) return;
pixel = (switch_rgb_color_t *)img->planes[SWITCH_PLANE_PACKED];
for (; pixel < ((switch_rgb_color_t *)img->planes[SWITCH_PLANE_PACKED] + img->d_w * img->d_h); pixel++) {
int threshold = 0;
if (last_pixel && (*(uint32_t *)pixel & 0xFFFFFF) == (*(uint32_t *)last_pixel & 0xFFFFFF)) {
threshold = last_threshold;
} else {
threshold = switch_color_distance(pixel, mask);
}
last_threshold = threshold;
last_pixel = pixel;
if (threshold) {
pixel->a = 0;
}
}
return;
}
static inline void switch_img_draw_pixel(switch_image_t *img, int x, int y, switch_rgb_color_t *color)
{
#ifdef SWITCH_HAVE_YUV
switch_yuv_color_t yuv = {0};
if (x < 0 || y < 0 || x >= img->d_w || y >= img->d_h) return;
if (img->fmt == SWITCH_IMG_FMT_I420) {
switch_color_rgb2yuv(color, &yuv);
img->planes[SWITCH_PLANE_Y][y * img->stride[SWITCH_PLANE_Y] + x] = yuv.y;
if (((x & 0x1) == 0) && ((y & 0x1) == 0)) {// only draw on even position
img->planes[SWITCH_PLANE_U][y / 2 * img->stride[SWITCH_PLANE_U] + x / 2] = yuv.u;
img->planes[SWITCH_PLANE_V][y / 2 * img->stride[SWITCH_PLANE_V] + x / 2] = yuv.v;
}
} else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
*((switch_rgb_color_t *)img->planes[SWITCH_PLANE_PACKED] + img->d_w * y + x) = *color;
}
#endif
}
SWITCH_DECLARE(void) switch_img_fill_noalpha(switch_image_t *img, int x, int y, int w, int h, switch_rgb_color_t *color)
{
#ifdef SWITCH_HAVE_YUV
int i;
if (img->fmt == SWITCH_IMG_FMT_ARGB) {
int max_w = img->d_w;
int max_h = img->d_h;
int j;
switch_rgb_color_t *rgb;
for (i = 0; i < max_h; i++) {
for (j = 0; j < max_w; j++) {
rgb = (switch_rgb_color_t *)(img->planes[SWITCH_PLANE_PACKED] + i * img->stride[SWITCH_PLANE_PACKED] + j * 4);
if (rgb->a != 0) {
continue;
}
*rgb = *color;
}
}
}
#endif
}
SWITCH_DECLARE(void) switch_img_8bit(switch_image_t *img)
{
#ifdef SWITCH_HAVE_YUV
int i;
if (img->fmt == SWITCH_IMG_FMT_ARGB) {
int max_w = img->d_w;
int max_h = img->d_h;
int j;
switch_rgb_color_t *rgb;
uint32_t *bytes;
for (i = 0; i < max_h; i++) {
for (j = 0; j < max_w; j++) {
rgb = (switch_rgb_color_t *)(img->planes[SWITCH_PLANE_PACKED] + i * img->stride[SWITCH_PLANE_PACKED] + j * 4);
//if (rgb);
if (!rgb->a) continue;;
//rgb->r = rgb->r & 0xE0, rgb->g = rgb->g & 0xE0, rgb->b = rgb->b & 0xC0;
bytes = (uint32_t *) rgb;
#if SWITCH_BYTE_ORDER == __BIG_ENDIAN
*bytes = *bytes & 0xE0E0C0FF;
#else
*bytes = *bytes & 0xFFC0E0E0;
#endif
}
}
} else if (img->fmt == SWITCH_IMG_FMT_I420) {
switch_image_t *tmp_img = switch_img_alloc(NULL, SWITCH_IMG_FMT_ARGB, img->d_w, img->d_h, 1);
I420ToARGB(img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U],
img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V],
tmp_img->planes[SWITCH_PLANE_PACKED], tmp_img->stride[SWITCH_PLANE_PACKED],
img->d_w, img->d_h);
switch_img_8bit(tmp_img);
ARGBToI420(tmp_img->planes[SWITCH_PLANE_PACKED], tmp_img->stride[SWITCH_PLANE_PACKED],
img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U],
img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V],
tmp_img->d_w, tmp_img->d_h);
switch_img_free(&tmp_img);
}
#endif
}
SWITCH_DECLARE(void) switch_img_sepia(switch_image_t *img, int x, int y, int w, int h)
{
#ifdef SWITCH_HAVE_YUV
if (x < 0 || y < 0 || x >= img->d_w || y >= img->d_h) return;
if (img->fmt == SWITCH_IMG_FMT_ARGB) {
ARGBSepia(img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED], x, y, w, h);
} else if (img->fmt == SWITCH_IMG_FMT_I420) {
int len, i, max_h;
max_h = MIN(y + h, img->d_h);
len = MIN(w, img->d_w - x);
if (x & 1) { x++; len--; }
if (y & 1) y++;
if (len <= 0) return;
if ((len & 1) && (x + len) < img->d_w - 1) len++;
len /= 2;
for (i = y; i < max_h; i += 2) {
memset(img->planes[SWITCH_PLANE_U] + img->stride[SWITCH_PLANE_U] * (i / 2) + x / 2, 108, len);
memset(img->planes[SWITCH_PLANE_V] + img->stride[SWITCH_PLANE_V] * (i / 2) + x / 2, 137, len);
}
}
#endif
}
SWITCH_DECLARE(void) switch_img_gray(switch_image_t *img, int x, int y, int w, int h)
{
#ifdef SWITCH_HAVE_YUV
if (x < 0 || y < 0 || x >= img->d_w || y >= img->d_h) return;
if (img->fmt == SWITCH_IMG_FMT_ARGB) {
ARGBGray(img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED], x, y, w, h);
} else if (img->fmt == SWITCH_IMG_FMT_I420) {
int len, i, max_h;
max_h = MIN(y + h, img->d_h);
len = MIN(w, img->d_w - x);
if (x & 1) { x++; len--; }
if (y & 1) y++;
if (len <= 0) return;
if ((len & 1) && (x + len) < img->d_w - 1) len++;
len /= 2;
for (i = y; i < max_h; i += 2) {
memset(img->planes[SWITCH_PLANE_U] + img->stride[SWITCH_PLANE_U] * (i / 2) + x / 2, 128, len);
memset(img->planes[SWITCH_PLANE_V] + img->stride[SWITCH_PLANE_V] * (i / 2) + x / 2, 128, len);
}
}
#endif
}
SWITCH_DECLARE(void) switch_img_fill(switch_image_t *img, int x, int y, int w, int h, switch_rgb_color_t *color)
{
#ifdef SWITCH_HAVE_YUV
int len, i, max_h;
switch_yuv_color_t yuv_color;
if (x < 0 || y < 0 || x >= img->d_w || y >= img->d_h) return;
if (img->fmt == SWITCH_IMG_FMT_I420) {
switch_color_rgb2yuv(color, &yuv_color);
max_h = MIN(y + h, img->d_h);
len = MIN(w, img->d_w - x);
if (x & 1) { x++; len--; }
if (y & 1) y++;
if (len <= 0) return;
for (i = y; i < max_h; i++) {
memset(img->planes[SWITCH_PLANE_Y] + img->stride[SWITCH_PLANE_Y] * i + x, yuv_color.y, len);
}
if ((len & 1) && (x + len) < img->d_w - 1) len++;
len /= 2;
for (i = y; i < max_h; i += 2) {
memset(img->planes[SWITCH_PLANE_U] + img->stride[SWITCH_PLANE_U] * (i / 2) + x / 2, yuv_color.u, len);
memset(img->planes[SWITCH_PLANE_V] + img->stride[SWITCH_PLANE_V] * (i / 2) + x / 2, yuv_color.v, len);
}
} else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
for (i = 0; i < img->d_w; i++) {
*((switch_rgb_color_t *)img->planes[SWITCH_PLANE_PACKED] + i) = *color;
}
for (i = 1; i < img->d_h; i++) {
memcpy( img->planes[SWITCH_PLANE_PACKED] + i * img->d_w * 4,
img->planes[SWITCH_PLANE_PACKED], img->d_w * 4);
}
}
#endif
}
#ifdef SWITCH_HAVE_YUV
static inline void switch_img_get_yuv_pixel(switch_image_t *img, switch_yuv_color_t *yuv, int x, int y)
{
// switch_assert(img->fmt == SWITCH_IMG_FMT_I420);
if (x < 0 || y < 0 || x >= img->d_w || y >= img->d_h) return;
yuv->y = *(img->planes[SWITCH_PLANE_Y] + img->stride[SWITCH_PLANE_Y] * y + x);
yuv->u = *(img->planes[SWITCH_PLANE_U] + img->stride[SWITCH_PLANE_U] * (y / 2) + x / 2);
yuv->v = *(img->planes[SWITCH_PLANE_V] + img->stride[SWITCH_PLANE_V] * (y / 2) + x / 2);
}
#endif
static inline void switch_img_get_rgb_pixel(switch_image_t *img, switch_rgb_color_t *rgb, int x, int y)
{
#ifdef SWITCH_HAVE_YUV
if (x < 0 || y < 0 || x >= img->d_w || y >= img->d_h) return;
if (img->fmt == SWITCH_IMG_FMT_I420) {
switch_yuv_color_t yuv = {0};
switch_img_get_yuv_pixel(img, &yuv, x, y);
switch_color_yuv2rgb(&yuv, rgb);
} else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
*rgb = *((switch_rgb_color_t *)img->planes[SWITCH_PLANE_PACKED] + img->d_w * y + x);
}
#endif
}
SWITCH_DECLARE(void) switch_img_overlay(switch_image_t *IMG, switch_image_t *img, int x, int y, uint8_t percent)
{
int i, j, len, max_h;
switch_rgb_color_t RGB = {0}, rgb = {0}, c = {0};
int xoff = 0, yoff = 0;
uint8_t alpha = (int8_t)((255 * percent) / 100);
switch_assert(IMG->fmt == SWITCH_IMG_FMT_I420);
if (x < 0) {
xoff = -x;
x = 0;
}
if (y < 0) {
yoff = -y;
y = 0;
}
max_h = MIN(y + img->d_h - yoff, IMG->d_h);
len = MIN(img->d_w - xoff, IMG->d_w - x);
if (x & 1) { x++; len--; }
if (y & 1) y++;
if (len <= 0) return;
for (i = y; i < max_h; i++) {
for (j = 0; j < len; j++) {
switch_img_get_rgb_pixel(IMG, &RGB, x + j, i);
switch_img_get_rgb_pixel(img, &rgb, j + xoff, i - y + yoff);
if (rgb.a > 0) {
c.r = ((RGB.r * (255 - alpha)) >> 8) + ((rgb.r * alpha) >> 8);
c.g = ((RGB.g * (255 - alpha)) >> 8) + ((rgb.g * alpha) >> 8);
c.b = ((RGB.b * (255 - alpha)) >> 8) + ((rgb.b * alpha) >> 8);
} else {
c.r = RGB.r;
c.g = RGB.g;
c.b = RGB.b;
}
switch_img_draw_pixel(IMG, x + j, i, &c);
}
}
}
static uint8_t scv_art[14][16] = {
{0x00, 0x7E, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x00},
{0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00},
{0x00, 0x7E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x7E, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7E, 0x00},
{0x00, 0x7E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x7E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x7E, 0x00},
{0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00},
{0x00, 0x7E, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x7E, 0x00},
{0x00, 0x7E, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7E, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x00},
{0x00, 0x7E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00},
{0x00, 0x7E, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x00},
{0x00, 0x7E, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x7E, 0x00},
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}, /*.*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*:*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*-*/
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* */
};
static void scv_tag(void *buffer, int w, int x, int y, uint8_t n)
{
int i = 0, j=0;
uint8_t *p = buffer;
if (n > 13) return;
for(i=0; i<8; i++) {
for (j=0; j<16; j++) {
*( p + (y + j) * w + (x + i)) = (scv_art[n][j] & 0x80 >> i) ? 0xFF : 0x00;
}
}
}
SWITCH_DECLARE(void) switch_img_add_text(void *buffer, int w, int x, int y, char *s)
{
while (*s) {
int index;
if (x > w - 8) break;
switch (*s) {
case '.': index = 10; break;
case ':': index = 11; break;
case '-': index = 12; break;
case ' ': index = 13; break;
default:
index = *s - 0x30;
}
scv_tag(buffer, w, x, y, index);
x += 8;
s++;
}
}
SWITCH_DECLARE(void) switch_color_set_rgb(switch_rgb_color_t *color, const char *str)
{
if (zstr(str)) return;
if ((*str) == '#' && strlen(str) == 7) {
unsigned int r, g, b;
sscanf(str, "#%02x%02x%02x", &r, &g, &b);
color->r = r;
color->g = g;
color->b = b;
} else {
if (!strcmp(str, "red")) {
color->r = 255;
color->g = 0;
color->b = 0;
} else if (!strcmp(str, "green")) {
color->r = 0;
color->g = 255;
color->b = 0;
} else if (!strcmp(str, "blue")) {
color->r = 0;
color->g = 0;
color->b = 255;
}
}
color->a = 255;
}
#ifdef SWITCH_HAVE_YUV
static inline void switch_color_rgb2yuv(switch_rgb_color_t *rgb, switch_yuv_color_t *yuv)
{
yuv->y = ( ( 66 * rgb->r + 129 * rgb->g + 25 * rgb->b + 128) >> 8) + 16;
yuv->u = ( ( -38 * rgb->r - 74 * rgb->g + 112 * rgb->b + 128) >> 8) + 128;
yuv->v = ( ( 112 * rgb->r - 94 * rgb->g - 18 * rgb->b + 128) >> 8) + 128;
//yuv->y = (uint8_t)(((rgb->r * 4897) >> 14) + ((rgb->g * 9611) >> 14) + ((rgb->b * 1876) >> 14));
//yuv->u = (uint8_t)(- ((rgb->r * 2766) >> 14) - ((5426 * rgb->g) >> 14) + rgb->b / 2 + 128);
//yuv->v = (uint8_t)(rgb->r / 2 -((6855 * rgb->g) >> 14) - ((rgb->b * 1337) >> 14) + 128);
}
#endif
#define CLAMP(val) MAX(0, MIN(val, 255))
#ifdef SWITCH_HAVE_YUV
static inline void switch_color_yuv2rgb(switch_yuv_color_t *yuv, switch_rgb_color_t *rgb)
{
#if 0
int C = yuv->y - 16;
int D = yuv->u - 128;
int E = yuv->v - 128;
rgb->r = CLAMP((298 * C + 409 * E + 128) >> 8);
rgb->g = CLAMP((298 * C - 100 * D - 208 * E + 128) >> 8);
rgb->b = CLAMP((298 * C + 516 * D + 128) >> 8);
#endif
rgb->a = 255;
rgb->r = CLAMP( yuv->y + ((22457 * (yuv->v-128)) >> 14));
rgb->g = CLAMP((yuv->y - ((715 * (yuv->v-128)) >> 10) - ((5532 * (yuv->u-128)) >> 14)));
rgb->b = CLAMP((yuv->y + ((28384 * (yuv->u-128)) >> 14)));
}
#endif
SWITCH_DECLARE(void) switch_color_set_yuv(switch_yuv_color_t *color, const char *str)
{
#ifdef SWITCH_HAVE_YUV
switch_rgb_color_t rgb = { 0 };
switch_color_set_rgb(&rgb, str);
switch_color_rgb2yuv(&rgb, color);
#endif
}
#if SWITCH_HAVE_FREETYPE
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#endif
#define MAX_GRADIENT 8
struct switch_img_txt_handle_s {
#if SWITCH_HAVE_FREETYPE
FT_Library library;
FT_Face face;
#endif
char *font_family;
double angle;
uint16_t font_size;
switch_rgb_color_t color;
switch_rgb_color_t bgcolor;
switch_image_t *img;
switch_memory_pool_t *pool;
int free_pool;
switch_rgb_color_t gradient_table[MAX_GRADIENT];
switch_bool_t use_bgcolor;
};
static void init_gradient_table(switch_img_txt_handle_t *handle)
{
int i;
switch_rgb_color_t *color;
switch_rgb_color_t *c1 = &handle->bgcolor;
switch_rgb_color_t *c2 = &handle->color;
for (i = 0; i < MAX_GRADIENT; i++) {
color = &handle->gradient_table[i];
color->r = c1->r + (c2->r - c1->r) * i / MAX_GRADIENT;
color->g = c1->g + (c2->g - c1->g) * i / MAX_GRADIENT;
color->b = c1->b + (c2->b - c1->b) * i / MAX_GRADIENT;
color->a = 255;
}
}
SWITCH_DECLARE(switch_status_t) switch_img_txt_handle_create(switch_img_txt_handle_t **handleP, const char *font_family,
const char *font_color, const char *bgcolor, uint16_t font_size, double angle, switch_memory_pool_t *pool)
{
int free_pool = 0;
switch_img_txt_handle_t *new_handle;
if (!pool) {
free_pool = 1;
switch_core_new_memory_pool(&pool);
}
new_handle = switch_core_alloc(pool, sizeof(*new_handle));
#if SWITCH_HAVE_FREETYPE
if (FT_Init_FreeType(&new_handle->library)) {
return SWITCH_STATUS_FALSE;
}
/*#else
return SWITCH_STATUS_FALSE; */
#endif
new_handle->pool = pool;
new_handle->free_pool = free_pool;
if (zstr(font_family)) {
font_family = switch_core_sprintf(new_handle->pool, "%s%s%s",SWITCH_GLOBAL_dirs.fonts_dir, SWITCH_PATH_SEPARATOR, "FreeSans.ttf");
}
if (!switch_is_file_path(font_family)) {
new_handle->font_family = switch_core_sprintf(new_handle->pool, "%s%s%s",SWITCH_GLOBAL_dirs.fonts_dir, SWITCH_PATH_SEPARATOR, font_family);
} else {
new_handle->font_family = switch_core_strdup(new_handle->pool, font_family);
}
if (switch_file_exists(new_handle->font_family, new_handle->pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Font %s does not exist\n", new_handle->font_family);
#if SWITCH_HAVE_FREETYPE
if (new_handle->library) {
FT_Done_FreeType(new_handle->library);
new_handle->library = NULL;
}
#endif
if (free_pool) {
switch_core_destroy_memory_pool(&pool);
}
*handleP = NULL;
return SWITCH_STATUS_FALSE;
}
new_handle->font_size = font_size;
new_handle->angle = angle;
switch_color_set_rgb(&new_handle->color, font_color);
switch_color_set_rgb(&new_handle->bgcolor, bgcolor);
init_gradient_table(new_handle);
*handleP = new_handle;
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(void) switch_img_txt_handle_destroy(switch_img_txt_handle_t **handleP)
{
switch_img_txt_handle_t *old_handle;
switch_memory_pool_t *pool;
switch_assert(handleP);
old_handle = *handleP;
*handleP = NULL;
if (!old_handle) return;
#if SWITCH_HAVE_FREETYPE
if (old_handle->library) {
FT_Done_FreeType(old_handle->library);
old_handle->library = NULL;
}
#endif
pool = old_handle->pool;
if (old_handle->free_pool) {
switch_core_destroy_memory_pool(&pool);
pool = NULL;
old_handle = NULL;
}
}
#if SWITCH_HAVE_FREETYPE
static void draw_bitmap(switch_img_txt_handle_t *handle, switch_image_t *img, FT_Bitmap* bitmap, FT_Int x, FT_Int y)
{
FT_Int i, j, p, q;
FT_Int x_max = x + bitmap->width;
FT_Int y_max = y + bitmap->rows;
if (bitmap->width == 0) return;
switch (bitmap->pixel_mode) {
case FT_PIXEL_MODE_GRAY: // it should always be GRAY since we use FT_LOAD_RENDER?
break;
case FT_PIXEL_MODE_NONE:
case FT_PIXEL_MODE_MONO:
{
for ( j = y, q = 0; j < y_max; j++, q++ ) {
for ( i = x, p = 0; i < x_max; i++, p++ ) {
uint8_t byte;
int linesize = ((bitmap->width - 1) / 8 + 1) * 8;
if ( i < 0 || j < 0 || i >= img->d_w || j >= img->d_h) continue;
byte = bitmap->buffer[(q * linesize + p) / 8];
if ((byte >> (7 - (p % 8))) & 0x1) {
switch_img_draw_pixel(img, i, j, &handle->color);
}
}
}
return;
}
case FT_PIXEL_MODE_GRAY2:
case FT_PIXEL_MODE_GRAY4:
case FT_PIXEL_MODE_LCD:
case FT_PIXEL_MODE_LCD_V:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "unsupported pixel mode %d\n", bitmap->pixel_mode);
return;
}
for ( i = x, p = 0; i < x_max; i++, p++ ) {
for ( j = y, q = 0; j < y_max; j++, q++ ) {
int gradient = bitmap->buffer[q * bitmap->width + p];
if ( i < 0 || j < 0 || i >= img->d_w || j >= img->d_h) continue;
if (handle->use_bgcolor) {
switch_img_draw_pixel(img, i, j, &handle->gradient_table[gradient * MAX_GRADIENT / 256]);
} else {
switch_rgb_color_t rgb_color = {0};
switch_rgb_color_t c;
switch_img_get_rgb_pixel(img, &rgb_color, i, j);
if (rgb_color.a > 0) {
c.a = rgb_color.a * gradient / 255;
c.r = ((rgb_color.r * (255 - gradient)) >> 8) + ((handle->color.r * gradient) >> 8);
c.g = ((rgb_color.g * (255 - gradient)) >> 8) + ((handle->color.g * gradient) >> 8);
c.b = ((rgb_color.b * (255 - gradient)) >> 8) + ((handle->color.b * gradient) >> 8);
} else {
c.a = gradient;
c.r = handle->color.r;
c.g = handle->color.g;
c.b = handle->color.b;
}
switch_img_draw_pixel(img, i, j, &c);
}
}
}
}
#endif
SWITCH_DECLARE(uint32_t) switch_img_txt_handle_render(switch_img_txt_handle_t *handle, switch_image_t *img,
int x, int y, const char *text,
const char *font_family, const char *font_color,
const char *bgcolor, uint16_t font_size, double angle)
{
#if SWITCH_HAVE_FREETYPE
FT_GlyphSlot slot;
FT_Matrix matrix; /* transformation matrix */
FT_Vector pen; /* untransformed origin */
FT_Error error;
//int target_height;
int index = 0;
FT_ULong ch;
FT_Face face;
uint32_t width = 0;
int this_x = 0, last_x = 0, space = 0;
uint32_t ret;
if (zstr(text)) return 0;
if (!handle) return 0;
switch_assert(!img || img->fmt == SWITCH_IMG_FMT_I420 || img->fmt == SWITCH_IMG_FMT_ARGB);
if (font_family) {
handle->font_family = switch_core_strdup(handle->pool, font_family);
} else {
font_family = handle->font_family;
}
if (font_size) {
handle->font_size = font_size;
} else {
font_size = handle->font_size;
}
if (font_color) {
switch_color_set_rgb(&handle->color, font_color);
}
if (bgcolor) {
switch_color_set_rgb(&handle->bgcolor, bgcolor);
handle->use_bgcolor = SWITCH_TRUE;
} else {
handle->use_bgcolor = SWITCH_FALSE;
}
handle->angle = angle;
//angle = 0; (45.0 / 360 ) * 3.14159 * 2;
//target_height = img->d_h;
error = FT_New_Face(handle->library, font_family, 0, &face); /* create face object */
if (error) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to open font %s\n", font_family);
return 0;
}
error = FT_Set_Char_Size(face, 64 * font_size, 0, 96, 0); /* set character size */
if (error) return 0;
slot = face->glyph;
if (handle->use_bgcolor && slot->bitmap.pixel_mode != FT_PIXEL_MODE_MONO) {
init_gradient_table(handle);
}
/* set up matrix */
matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );
pen.x = x;
pen.y = y;
while(*(text + index)) {
ch = switch_u8_get_char((char *)text, &index);
if (ch == '\n') {
pen.x = x;
pen.y += (font_size + font_size / 4);
continue;
}
/* set transformation */
FT_Set_Transform(face, &matrix, &pen);
/* load glyph image into the slot (erase previous one) */
error = FT_Load_Char(face, ch, FT_LOAD_RENDER);
if (error) continue;
this_x = pen.x + slot->bitmap_left;
if (img) {
/* now, draw to our target surface (convert position) */
draw_bitmap(handle, img, &slot->bitmap, this_x, pen.y - slot->bitmap_top + font_size);
}
if (last_x) {
space = this_x - last_x;
} else {
space = 0;
}
last_x = this_x;
width += space;
/* increment pen position */
pen.x += slot->advance.x >> 6;
pen.y += slot->advance.y >> 6;
}
ret = width + slot->bitmap.width * 5;
FT_Done_Face(face);
return ret;
#else
return 0;
#endif
}
SWITCH_DECLARE(switch_image_t *) switch_img_write_text_img(int w, int h, switch_bool_t full, const char *text)
{
const char *fg ="#cccccc";
const char *bg = "#142e55";
// const char *bg = NULL; // use a NULL bg for transparent
const char *font_face = NULL;
const char *fontsz = "4%";
char *txt = "Value Optimized Out!";
int argc = 0;
char *argv[6] = { 0 };
switch_rgb_color_t bgcolor = { 0 };
int pre_width = 0, width = 0, font_size = 0, height = 0;
char *duptxt = strdup(text);
switch_img_txt_handle_t *txthandle = NULL;
switch_image_t *txtimg = NULL;
int x = 0, y = 0;
if (!duptxt) {
return NULL;
}
if (strchr(text, ':')) {
argc = switch_split(duptxt, ':', argv);
if (argc > 0 && !zstr(argv[0])) {
fg = argv[0];
}
if (argc > 1 && !zstr(argv[1])) {
bg = argv[1];
if (!strcasecmp(bg, "transparent")) {
bg = NULL;
}
}
if (argc > 2 && !zstr(argv[2])) {
font_face = argv[2];
}
if (argc > 3 && !zstr(argv[3])) {
fontsz = argv[3];
}
if (argc > 4) {
txt = argv[4];
}
} else txt = duptxt;
if (!txt) txt = duptxt;
if (strrchr(fontsz, '%')) {
font_size = 1 + ((int) (float)h * (atof(fontsz) / 100.0f));
} else {
font_size = atoi(fontsz);
}
switch_url_decode(txt);
while (*txt == ' ') txt++;
while (end_of(txt) == ' ') end_of(txt) = '\0';
switch_img_txt_handle_create(&txthandle, font_face, fg, bg, font_size, 0, NULL);
pre_width = switch_img_txt_handle_render(txthandle,
NULL,
font_size / 2, font_size / 2,
txt, NULL, fg, bg, 0, 0);
height = font_size * 2;
if (full && w > width) {
width = w;
} else {
width = pre_width;
}
if (width == 0 || height == 0) {
txtimg = NULL;
goto done;
}
//if (bg) {
// txtimg = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, width, height, 1);
// switch_assert(txtimg);
// switch_img_fill(txtimg, 0, 0, txtimg->d_w, txtimg->d_h, &bgcolor);
//} else {
txtimg = switch_img_alloc(NULL, SWITCH_IMG_FMT_ARGB, width, height, 1);
switch_assert(txtimg);
//memset(txtimg->planes[SWITCH_PLANE_PACKED], 0, width * height * 4);
if (bg) {
switch_color_set_rgb(&bgcolor, bg);
switch_img_fill(txtimg, 0, 0, txtimg->d_w, txtimg->d_h, &bgcolor);
} else {
memset(txtimg->planes[SWITCH_PLANE_PACKED], 0, width * height * 4);
}
//}
x = font_size / 2;
y = font_size / 2;
if (full) {
x = (txtimg->d_w / 2) - (pre_width / 2);
}
switch_img_txt_handle_render(txthandle,
txtimg,
x, y,
txt, NULL, fg, bg, 0, 0);
done:
switch_img_txt_handle_destroy(&txthandle);
switch_safe_free(duptxt);
return txtimg;
}
/* WARNING:
patch a big IMG with a rect hole, note this function is WIP ......
It ONLY works when the hole is INSIDE the big IMG and the place the small img will patch to,
more sanity checks need to be decided
*/
SWITCH_DECLARE(void) switch_img_patch_hole(switch_image_t *IMG, switch_image_t *img, int x, int y, switch_image_rect_t *rect)
{
int i, len;
switch_assert(img->fmt == SWITCH_IMG_FMT_I420);
switch_assert(IMG->fmt == SWITCH_IMG_FMT_I420);
len = MIN(img->d_w, IMG->d_w - x);
if (len <= 0) return;
for (i = y; i < (y + img->d_h) && i < IMG->d_h; i++) {
if (rect && i >= rect->y && i < (rect->y + rect->h)) {
int size = rect->x > x ? rect->x - x : 0;
memcpy(IMG->planes[SWITCH_PLANE_Y] + IMG->stride[SWITCH_PLANE_Y] * i + x, img->planes[SWITCH_PLANE_Y] + img->stride[SWITCH_PLANE_Y] * (i - y), size);
size = MIN(img->d_w - rect->w - size, IMG->d_w - (rect->x + rect->w));
memcpy(IMG->planes[SWITCH_PLANE_Y] + IMG->stride[SWITCH_PLANE_Y] * i + rect->x + rect->w, img->planes[SWITCH_PLANE_Y] + img->stride[SWITCH_PLANE_Y] * (i - y) + rect->w + (rect->x - x), size);
} else {
memcpy(IMG->planes[SWITCH_PLANE_Y] + IMG->stride[SWITCH_PLANE_Y] * i + x, img->planes[SWITCH_PLANE_Y] + img->stride[SWITCH_PLANE_Y] * (i - y), len);
}
}
len /= 2;
for (i = y; i < (y + img->d_h) && i < IMG->d_h; i += 2) {
if (rect && i > rect->y && i < (rect->y + rect->h)) {
int size = rect->x > x ? rect->x - x : 0;
size /= 2;
memcpy(IMG->planes[SWITCH_PLANE_U] + IMG->stride[SWITCH_PLANE_U] * (i / 2) + x / 2, img->planes[SWITCH_PLANE_U] + img->stride[SWITCH_PLANE_U] * ((i - y) / 2), size);
memcpy(IMG->planes[SWITCH_PLANE_V] + IMG->stride[SWITCH_PLANE_V] * (i / 2) + x / 2, img->planes[SWITCH_PLANE_V] + img->stride[SWITCH_PLANE_V] * ((i - y) / 2), size);
size = MIN(img->d_w - rect->w - size, IMG->d_w - (rect->x + rect->w)) / 2;
memcpy(IMG->planes[SWITCH_PLANE_U] + IMG->stride[SWITCH_PLANE_U] * (i / 2) + (rect->x + rect->w) / 2, img->planes[SWITCH_PLANE_U] + img->stride[SWITCH_PLANE_U] * ((i - y) / 2) + (rect->w + (rect->x - x)) / 2, size);
memcpy(IMG->planes[SWITCH_PLANE_V] + IMG->stride[SWITCH_PLANE_V] * (i / 2) + (rect->x + rect->w) / 2, img->planes[SWITCH_PLANE_V] + img->stride[SWITCH_PLANE_V] * ((i - y) / 2) + (rect->w + (rect->x - x)) / 2, size);
} else {
memcpy(IMG->planes[SWITCH_PLANE_U] + IMG->stride[SWITCH_PLANE_U] * (i / 2) + x / 2, img->planes[SWITCH_PLANE_U] + img->stride[SWITCH_PLANE_U] * ((i - y) / 2), len);
memcpy(IMG->planes[SWITCH_PLANE_V] + IMG->stride[SWITCH_PLANE_V] * (i / 2) + x / 2, img->planes[SWITCH_PLANE_V] + img->stride[SWITCH_PLANE_V] * ((i - y) / 2), len);
}
}
}
#define SWITCH_IMG_MAX_WIDTH 1920 * 4
#define SWITCH_IMG_MAX_HEIGHT 1080 * 4
#if !defined(SWITCH_HAVE_YUV)
#undef SWITCH_HAVE_PNG
#endif
#ifdef SWITCH_HAVE_PNG
// WIP png functions, need furthur tweak/check to make sure it works on all png files and errors are properly detected and reported
// #define PNG_DEBUG 3
#define PNG_SKIP_SETJMP_CHECK
#include <png.h>
#ifdef PNG_SIMPLIFIED_READ_SUPPORTED /* available from libpng 1.6.0 */
struct switch_png_opaque_s {
png_image png;
png_bytep buffer;
};
SWITCH_DECLARE(switch_status_t) switch_png_open(switch_png_t **pngP, const char *file_name)
{
switch_png_t *use_png;
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_zmalloc(use_png, sizeof(*use_png));
switch_zmalloc(use_png->pvt, sizeof(struct switch_png_opaque_s));
use_png->pvt->png.version = PNG_IMAGE_VERSION;
if (!png_image_begin_read_from_file(&use_png->pvt->png, file_name)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error read PNG %s\n", file_name);
switch_goto_status(SWITCH_STATUS_FALSE, end);
}
use_png->pvt->png.format = PNG_FORMAT_ARGB;
use_png->pvt->buffer = malloc(PNG_IMAGE_SIZE(use_png->pvt->png));
switch_assert(use_png->pvt->buffer);
if (!png_image_finish_read(&use_png->pvt->png, NULL/*background*/, use_png->pvt->buffer, 0, NULL)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error read PNG %s\n", file_name);
switch_goto_status(SWITCH_STATUS_FALSE, end);
}
use_png->w = use_png->pvt->png.width;
use_png->h = use_png->pvt->png.height;
end:
if (status == SWITCH_STATUS_SUCCESS) {
*pngP = use_png;
} else {
switch_png_free(&use_png);
*pngP = NULL;
}
return status;
}
SWITCH_DECLARE(void) switch_png_free(switch_png_t **pngP)
{
switch_png_t *use_png;
if (pngP) {
use_png = *pngP;
*pngP = NULL;
png_image_free(&use_png->pvt->png);
switch_safe_free(use_png->pvt->buffer);
switch_safe_free(use_png->pvt);
switch_safe_free(use_png);
}
}
SWITCH_DECLARE(switch_status_t) switch_png_patch_img(switch_png_t *use_png, switch_image_t *img, int x, int y)
{
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_rgb_color_t *rgb_color;
int i, j;
switch_assert(use_png);
for (i = 0; i < use_png->pvt->png.height; i++) {
for (j = 0; j < use_png->pvt->png.width; j++) {
rgb_color = (switch_rgb_color_t *)use_png->pvt->buffer + i * use_png->pvt->png.width + j;
if (rgb_color->a) { // todo, mux alpha with the underlying pixel
switch_img_draw_pixel(img, x + j, y + i, rgb_color);
}
}
}
return status;
}
#else /* libpng < 1.6.0 */
SWITCH_DECLARE(switch_status_t) switch_png_open(switch_png_t **pngP, const char *file_name)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NOT IMPLEMENTED\n");
return SWITCH_STATUS_FALSE;
}
SWITCH_DECLARE(void) switch_png_free(switch_png_t **pngP)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NOT IMPLEMENTED\n");
}
SWITCH_DECLARE(switch_status_t) switch_png_patch_img(switch_png_t *use_png, switch_image_t *img, int x, int y)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NOT IMPLEMENTED\n");
return SWITCH_STATUS_FALSE;
}
#endif
#ifdef PNG_SIMPLIFIED_READ_SUPPORTED /* available from libpng 1.6.0 */
static switch_image_t *png2img(png_image *png, switch_img_fmt_t img_fmt)
{
png_bytep buffer = NULL;
switch_image_t *img = NULL;
png->version = PNG_IMAGE_VERSION;
if (img_fmt == SWITCH_IMG_FMT_I420) {
png->format = PNG_FORMAT_RGB;
} else if (img_fmt == SWITCH_IMG_FMT_ARGB) {
#if SWITCH_BYTE_ORDER == __BIG_ENDIAN
png->format = PNG_FORMAT_ARGB;
#else
png->format = PNG_FORMAT_BGRA;
#endif
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unsupported image format: %x\n", img_fmt);
goto err;
}
buffer = malloc(PNG_IMAGE_SIZE(*png));
switch_assert(buffer);
if (!png_image_finish_read(png, NULL/*background*/, buffer, 0, NULL)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error read png\n");
goto err;
}
if (png->width > SWITCH_IMG_MAX_WIDTH || png->height > SWITCH_IMG_MAX_HEIGHT) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PNG is too large! %dx%d\n", png->width, png->height);
goto err;
}
img = switch_img_alloc(NULL, img_fmt, png->width, png->height, 1);
switch_assert(img);
if (img_fmt == SWITCH_IMG_FMT_I420) {
RAWToI420(buffer, png->width * 3,
img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U],
img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V],
png->width, png->height);
} else if (img_fmt == SWITCH_IMG_FMT_ARGB){
ARGBCopy(buffer, png->width * 4,
img->planes[SWITCH_PLANE_PACKED], png->width * 4,
png->width, png->height);
}
err:
png_image_free(png);
switch_safe_free(buffer);
return img;
}
SWITCH_DECLARE(switch_image_t *) switch_img_read_png(const char* file_name, switch_img_fmt_t img_fmt)
{
png_image png = { 0 };
png.version = PNG_IMAGE_VERSION;
if (!png_image_begin_read_from_file(&png, file_name)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error open png: %s\n", file_name);
return NULL;
}
return png2img(&png, img_fmt);
}
SWITCH_DECLARE(switch_image_t *) switch_img_read_png_from_memory(void *mem, size_t size, switch_img_fmt_t img_fmt)
{
png_image png = { 0 };
png.version = PNG_IMAGE_VERSION;
if (!png_image_begin_read_from_memory(&png, mem, size)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error open png from memory\n");
return NULL;
}
return png2img(&png, img_fmt);
}
#else /* libpng < 1.6.0 */
// ref: most are out-dated, man libpng :)
// http://zarb.org/~gc/html/libpng.html
// http://www.libpng.org/pub/png/book/toc.html
// http://www.vias.org/pngguide/chapter01_03_02.html
// http://www.libpng.org/pub/png/libpng-1.2.5-manual.html
// ftp://ftp.oreilly.com/examples/9781565920583/CDROM/SOFTWARE/SOURCE/LIBPNG/EXAMPLE.C
SWITCH_DECLARE(switch_image_t *) switch_img_read_png(const char* file_name, switch_img_fmt_t img_fmt)
{
png_byte header[8]; // 8 is the maximum size that can be checked
png_bytep *row_pointers = NULL;
int y;
int width, height;
png_byte color_type;
png_byte bit_depth;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
//int number_of_passes;
int row_bytes;
png_color_8p sig_bit;
png_byte *buffer = NULL;
switch_image_t *img = NULL;
FILE *fp;
if (img_fmt != SWITCH_IMG_FMT_I420 && img_fmt != SWITCH_IMG_FMT_ARGB) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Only ARGB and I420 are supported, you want 0x%x\n", img_fmt);
return NULL;
}
/* open file and test for it being a png */
fp = fopen(file_name, "rb");
if (!fp) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "File %s could not be opened for reading\n", file_name);
goto end;
}
fread(header, 1, 8, fp);
if (png_sig_cmp(header, 0, 8)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "File %s is not recognized as a PNG file\n", file_name);
goto end;
}
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "png_create_read_struct failed\n");
goto end;
}
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "png_create_info_struct failed\n");
goto end;
}
if (setjmp(png_jmpbuf(png_ptr))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during init_io\n");
goto end;
}
png_init_io(png_ptr, fp);
png_set_sig_bytes(png_ptr, 8);
png_read_info(png_ptr, info_ptr);
width = png_get_image_width(png_ptr, info_ptr);
height = png_get_image_height(png_ptr, info_ptr);
color_type = png_get_color_type(png_ptr, info_ptr);
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
//number_of_passes = png_set_interlace_handling(png_ptr);
/* set up the transformations you want. Note that these are
all optional. Only call them if you want them */
/* expand paletted colors into true rgb */
if (color_type == PNG_COLOR_TYPE_PALETTE) {
png_set_expand(png_ptr);
}
/* expand grayscale images to the full 8 bits */
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
png_set_expand(png_ptr);
}
/* expand images with transparency to full alpha channels */
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
png_set_expand(png_ptr);
}
/* Set the background color to draw transparent and alpha images over */
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) {
// png_get_bKGD(png_ptr, info_ptr, &my_background);
// png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
} else {
// png_color_16 my_background = { 0 }; //{index,r, g, b, grey}
// png_color_16 my_background = {0, 99, 99, 99, 0};
// png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
}
/* tell libpng to handle the gamma conversion for you */
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) {
// png_set_gamma(png_ptr, screen_gamma, info_ptr->gamma);
} else {
// png_set_gamma(png_ptr, screen_gamma, 0.45);
}
/* tell libpng to strip 16 bit depth files down to 8 bits */
if (bit_depth == 16) {
png_set_strip_16(png_ptr);
}
#if 0
/* dither rgb files down to 8 bit palettes & reduce palettes
to the number of colors available on your screen */
if (0 && color_type & PNG_COLOR_MASK_COLOR) {
if (png_get_valid(png_ptr, info_ptr, & PNG_INFO_PLTE)) {
png_set_dither(png_ptr, info_ptr->palette,
info_ptr->num_palette, max_screen_colors,
info_ptr->histogram);
} else {
png_color std_color_cube[MAX_SCREEN_COLORS] =
{/* ... colors ... */};
png_set_dither(png_ptr, std_color_cube, MAX_SCREEN_COLORS,
MAX_SCREEN_COLORS, NULL);
}
}
#endif
/* invert monocrome files */
if (bit_depth == 1 && color_type == PNG_COLOR_TYPE_GRAY) {
// png_set_invert(png_ptr);
}
png_get_sBIT(png_ptr, info_ptr, &sig_bit);
/* shift the pixels down to their true bit depth */
// if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT) && (bit_depth > (*sig_bit).red)) {
// png_set_shift(png_ptr, sig_bit);
// }
/* pack pixels into bytes */
if (bit_depth < 8) {
png_set_packing(png_ptr);
}
/* flip the rgb pixels to bgr */
if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
// png_set_bgr(png_ptr);
}
/* swap bytes of 16 bit files to least significant bit first */
if (bit_depth == 16) {
png_set_swap(png_ptr);
}
if (0 && color_type & PNG_COLOR_MASK_ALPHA) {
if (setjmp(png_jmpbuf(png_ptr))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error!!!!\n");
goto end;
}
png_set_strip_alpha(png_ptr);
}
if (setjmp(png_jmpbuf(png_ptr))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Error during read_updated_info\n");
goto end;
}
if (color_type == PNG_COLOR_TYPE_PALETTE) {
png_set_palette_to_rgb(png_ptr);
}
png_read_update_info(png_ptr, info_ptr);
color_type = png_get_color_type(png_ptr, info_ptr);
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "color_type: 0x%x\n", color_type);
if (width > SWITCH_IMG_MAX_WIDTH || height > SWITCH_IMG_MAX_HEIGHT) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PNG is too large! %dx%d\n", width, height);
}
row_bytes = png_get_rowbytes(png_ptr, info_ptr);
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "size: %dx%d row_bytes:%d color_type:%d bit_dept:%d\n", width, height, row_bytes, color_type, bit_depth);
row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
switch_assert(row_pointers);
buffer = (png_byte *)malloc(row_bytes * height);
switch_assert(buffer);
for (y = 0; y < height; y++) {
row_pointers[y] = buffer + row_bytes * y;
}
/* read file */
if (setjmp(png_jmpbuf(png_ptr))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during read_image");
goto end;
}
png_read_image(png_ptr, row_pointers);
if (color_type == PNG_COLOR_TYPE_RGBA) {
if (row_bytes > width * 4) {
for(y = 1; y < height; y++) {
memcpy(buffer + y * width * 4, row_pointers[y], width * 4);
}
}
img = switch_img_alloc(NULL, img_fmt, width, height, 1);
switch_assert(img);
if (img_fmt == SWITCH_IMG_FMT_I420) {
ABGRToI420(buffer, width * 4,
img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U],
img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V],
width, height);
} else if (img_fmt == SWITCH_IMG_FMT_ARGB) {
BGRAToARGB(buffer, width * 4,
img->planes[SWITCH_PLANE_PACKED], width * 4,
width, height);
}
} else if (color_type == PNG_COLOR_TYPE_RGB) {
if (row_bytes > width * 3) {
for(y = 1; y < height; y++) {
memcpy(buffer + y * width * 3, row_pointers[y], width * 3);
}
}
if (img_fmt == SWITCH_IMG_FMT_ARGB) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No alpha channel in image [%s], fallback to I420\n", file_name);
img_fmt = SWITCH_IMG_FMT_I420;
}
img = switch_img_alloc(NULL, img_fmt, width, height, 1);
switch_assert(img);
RAWToI420(buffer, width * 3,
img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U],
img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V],
width, height);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unsupported color type: %d\n", png_get_color_type(png_ptr, info_ptr));
}
end:
switch_safe_free(buffer);
switch_safe_free(row_pointers);
if (fp) fclose(fp);
if (info_ptr) png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return img;
}
SWITCH_DECLARE(switch_image_t *) switch_img_read_png_from_memory(void *mem, size_t size, switch_img_fmt_t img_fmt)
{
return NULL;
}
#endif
static void my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
{
switch_buffer_t *data_buffer = (switch_buffer_t *)png_get_io_ptr(png_ptr);
switch_buffer_write(data_buffer, data, length);
}
static void my_png_flush(png_structp png_ptr)
{
}
SWITCH_DECLARE(switch_status_t) switch_img_data_url_png(switch_image_t *img, char **urlP)
{
int width, height;
png_byte color_type;
png_byte bit_depth;
png_structp png_ptr;
png_infop info_ptr;
png_bytep *row_pointers = NULL;
int row_bytes;
int y;
png_byte *buffer = NULL;
switch_status_t status = SWITCH_STATUS_FALSE;
switch_buffer_t *data_buffer = NULL;
unsigned char *head;
width = img->d_w;
height = img->d_h;
bit_depth = 8;
color_type = PNG_COLOR_TYPE_RGB;
if (img->fmt == SWITCH_IMG_FMT_ARGB) {
color_type = PNG_COLOR_TYPE_RGBA;
}
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "png_create_write_struct failed");
goto end;
}
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "png_create_info_struct failed");
goto end;
}
if (setjmp(png_jmpbuf(png_ptr))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during init_io");
goto end;
}
switch_buffer_create_dynamic(&data_buffer, 1024, 1024, 0);
png_set_write_fn(png_ptr, data_buffer, my_png_write_data, my_png_flush);
//png_init_io(png_ptr, fp);
if (setjmp(png_jmpbuf(png_ptr))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during writing header");
goto end;
}
png_set_IHDR(png_ptr, info_ptr, width, height,
bit_depth, color_type, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_write_info(png_ptr, info_ptr);
row_bytes = png_get_rowbytes(png_ptr, info_ptr);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "size: %dx%d row_bytes:%d color_type:%d bit_dept:%d\n", width, height, row_bytes, color_type, bit_depth);
buffer = (png_byte *)malloc(row_bytes * height);
switch_assert(buffer);
row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
switch_assert(row_pointers);
for (y = 0; y < height; y++) {
row_pointers[y] = buffer + row_bytes * y;
}
if (img->fmt == SWITCH_IMG_FMT_I420) {
I420ToRAW( img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U],
img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V],
buffer, width * 3,
width, height);
} else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
ARGBToABGR(img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED],
buffer, row_bytes, width, height);
}
if (setjmp(png_jmpbuf(png_ptr))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during writing bytes");
goto end;
}
//png_set_rows(png_ptr, info_ptr, row_pointers);
//png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
png_write_image(png_ptr, row_pointers);
if (setjmp(png_jmpbuf(png_ptr))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during end of write");
goto end;
}
png_write_end(png_ptr, info_ptr);
if ((head = (unsigned char *) switch_buffer_get_head_pointer(data_buffer))) {
switch_size_t olen = 0, blen = 0;
unsigned char *out = NULL;
const char *header = "data:image/png;base64,";
blen = switch_buffer_len(data_buffer);
olen = blen * 4;
if (olen > strlen(header) + 1) {
switch_zmalloc(out, olen);
switch_snprintf((char *)out, strlen(header) + 1, header);
switch_b64_encode(head, blen, out + strlen(header), olen - strlen(header));
*urlP = (char *)out;
} else {
status = SWITCH_STATUS_MEMERR;
goto end;
}
}
status = SWITCH_STATUS_SUCCESS;
end:
if (status != SWITCH_STATUS_SUCCESS) {
*urlP = NULL;
}
switch_buffer_destroy(&data_buffer);
switch_safe_free(buffer);
switch_safe_free(row_pointers);
png_destroy_write_struct(&png_ptr, &info_ptr);
return status;
}
#ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED /* available from libpng 1.6.0 */
SWITCH_DECLARE(switch_status_t) switch_img_write_png(switch_image_t *img, char* file_name)
{
png_image png = { 0 };
png_bytep buffer = NULL;
switch_status_t status = SWITCH_STATUS_SUCCESS;
if (img->fmt == SWITCH_IMG_FMT_I420) {
png.format = PNG_FORMAT_RGB;
buffer = malloc(img->d_w * img->d_h * 3);
switch_assert(buffer);
I420ToRAW(img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U],
img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V],
buffer, img->d_w * 3,
img->d_w, img->d_h);
} else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
#if SWITCH_BYTE_ORDER == __BIG_ENDIAN
png.format = PNG_FORMAT_ARGB;
#else
png.format = PNG_FORMAT_BGRA;
#endif
buffer = img->planes[SWITCH_PLANE_PACKED];
}
png.version = PNG_IMAGE_VERSION;
png.width = img->d_w;
png.height = img->d_h;
if (!png_image_write_to_file(&png, file_name, 0, buffer, 0, NULL)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error write PNG %s\n", file_name);
status = SWITCH_STATUS_FALSE;
}
if (img->fmt == SWITCH_IMG_FMT_I420) {
free(buffer);
}
return status;
}
#else
SWITCH_DECLARE(switch_status_t) switch_img_write_png(switch_image_t *img, char* file_name)
{
int width, height;
png_byte color_type;
png_byte bit_depth;
png_structp png_ptr;
png_infop info_ptr;
png_bytep *row_pointers = NULL;
int row_bytes;
int y;
png_byte *buffer = NULL;
FILE *fp = NULL;
switch_status_t status = SWITCH_STATUS_FALSE;
width = img->d_w;
height = img->d_h;
bit_depth = 8;
color_type = PNG_COLOR_TYPE_RGB;
if (img->fmt == SWITCH_IMG_FMT_ARGB) {
color_type = PNG_COLOR_TYPE_RGBA;
}
fp = fopen(file_name, "wb");
if (!fp) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "File %s could not be opened for writing", file_name);
goto end;
}
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "png_create_write_struct failed");
goto end;
}
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "png_create_info_struct failed");
goto end;
}
if (setjmp(png_jmpbuf(png_ptr))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during init_io");
goto end;
}
png_init_io(png_ptr, fp);
if (setjmp(png_jmpbuf(png_ptr))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during writing header");
goto end;
}
png_set_IHDR(png_ptr, info_ptr, width, height,
bit_depth, color_type, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_write_info(png_ptr, info_ptr);
row_bytes = png_get_rowbytes(png_ptr, info_ptr);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "size: %dx%d row_bytes:%d color_type:%d bit_dept:%d\n", width, height, row_bytes, color_type, bit_depth);
row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
switch_assert(row_pointers);
buffer = (png_byte *)malloc(row_bytes * height);
switch_assert(buffer);
for (y = 0; y < height; y++) {
row_pointers[y] = buffer + row_bytes * y;
}
if (img->fmt == SWITCH_IMG_FMT_I420) {
I420ToRAW( img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U],
img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V],
buffer, width * 3,
width, height);
} else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
ARGBToABGR(img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED],
buffer, row_bytes, width, height);
}
if (setjmp(png_jmpbuf(png_ptr))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during writing bytes");
goto end;
}
png_write_image(png_ptr, row_pointers);
if (setjmp(png_jmpbuf(png_ptr))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during end of write");
goto end;
}
png_write_end(png_ptr, NULL);
status = SWITCH_STATUS_SUCCESS;
end:
switch_safe_free(buffer);
switch_safe_free(row_pointers);
fclose(fp);
png_destroy_write_struct(&png_ptr, &info_ptr);
return status;
}
#endif
#else
SWITCH_DECLARE(switch_status_t) switch_img_patch_png(switch_image_t *img, int x, int y, const char *file_name)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This function is not available, libpng not installed\n");
return SWITCH_STATUS_FALSE;
}
SWITCH_DECLARE(switch_image_t *) switch_img_read_png(const char* file_name, switch_img_fmt_t img_fmt)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This function is not available, libpng not installed\n");
return NULL;
}
SWITCH_DECLARE(switch_status_t) switch_img_write_png(switch_image_t *img, char* file_name)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This function is not available, libpng not installed\n");
return SWITCH_STATUS_FALSE;
}
SWITCH_DECLARE(switch_status_t) switch_img_data_url_png(switch_image_t *img, char **urlP)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This function is not available, libpng not installed\n");
return SWITCH_STATUS_FALSE;
}
#endif
SWITCH_DECLARE(switch_image_t *) switch_img_read_from_file(const char* file_name, switch_img_fmt_t img_fmt)
{
#ifdef SWITCH_HAVE_YUV
int width = 0, height = 0, channels = 0;
int comp = STBI_rgb;
unsigned char *data = NULL;
if (img_fmt == SWITCH_IMG_FMT_I420) {
comp = STBI_rgb;
} else if (img_fmt == SWITCH_IMG_FMT_ARGB) {
comp = STBI_rgb_alpha;
} else {
return NULL;
}
data = stbi_load(file_name, &width, &height, &channels, comp);
// switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%dx%d channels=%d\n", width, height, channels);
if (data && width > 0 && height > 0) {
switch_image_t *img = switch_img_alloc(NULL, img_fmt, width, height, 1);
switch_assert(img);
if (img_fmt == SWITCH_IMG_FMT_I420) {
RAWToI420(data, width * 3,
img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U],
img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V],
width, height);
} else if (img_fmt == SWITCH_IMG_FMT_ARGB) {
#if SWITCH_BYTE_ORDER == __BIG_ENDIAN
RGBAToARGB(data, width * 4, img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED], width, height);
#else
ABGRToARGB(data, width * 4, img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED], width, height);
#endif
}
stbi_image_free(data);
return img;
} else if (data) {
stbi_image_free(data);
}
#endif
return NULL;
}
SWITCH_DECLARE(switch_status_t) switch_img_write_to_file(switch_image_t *img, const char* file_name, int quality)
{
#ifndef SWITCH_HAVE_YUV
return SWITCH_STATUS_FALSE;
#else
int comp = STBI_rgb;
unsigned char *data = NULL;
const char *ext = strrchr(file_name, '.');
int stride_in_bytes = 0;
int ret = 0;
if (!ext) return SWITCH_STATUS_FALSE;
ext++;
if (img->fmt == SWITCH_IMG_FMT_I420) {
comp = STBI_rgb;
stride_in_bytes = img->d_w * 3;
data = malloc(stride_in_bytes * img->d_h);
switch_assert(data);
I420ToRAW(img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U],
img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V],
data, stride_in_bytes,
img->d_w, img->d_h);
} else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
comp = STBI_rgb_alpha;
stride_in_bytes = img->d_w * 4;
data = malloc(stride_in_bytes * img->d_h);
switch_assert(data);
#if SWITCH_BYTE_ORDER == __BIG_ENDIAN
ARGBToRGBA(img->planes[SWITCH_PLANE_PACKED], stride_in_bytes, data, stride_in_bytes, img->d_w, img->d_h);
#else
ARGBToABGR(img->planes[SWITCH_PLANE_PACKED], stride_in_bytes, data, stride_in_bytes, img->d_w, img->d_h);
#endif
} else {
return SWITCH_STATUS_FALSE;
}
if (!strcasecmp(ext, "png")) {
ret = stbi_write_png(file_name, img->d_w, img->d_h, comp, (const void *)data, stride_in_bytes);
} else if (!strcasecmp(ext, "jpg") || !strcasecmp(ext, "jpeg")) {
ret = stbi_write_jpg(file_name, img->d_w, img->d_h, comp, (const void *)data, quality);
} else if (!strcasecmp(ext, "bmp")) {
ret = stbi_write_bmp(file_name, img->d_w, img->d_h, comp, (const void *)data);
} else if (!strcasecmp(ext, "tga")) {
ret = stbi_write_tga(file_name, img->d_w, img->d_h, comp, (const void *)data);
} else if (!strcasecmp(ext, "hdr")) {
ret = stbi_write_hdr(file_name, img->d_w, img->d_h, comp, (const float *)data);
} else {
ret = 0;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unsupported file format [%s]", ext);
}
free(data);
return ret ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
#endif
}
typedef struct data_url_context_s {
const char *type;
char **urlP;
} data_url_context_t;
#ifdef SWITCH_HAVE_YUV
static void data_url_write_func(void *context, void *data, int size)
{
switch_buffer_t *buffer = (switch_buffer_t *)context;
switch_buffer_write(buffer, data, size);
}
#endif
SWITCH_DECLARE(switch_status_t) switch_img_data_url(switch_image_t *img, char **urlP, const char *type, int quality)
{
#ifndef SWITCH_HAVE_YUV
return SWITCH_STATUS_FALSE;
#else
int comp = STBI_rgb;
unsigned char *data = NULL;
int stride_in_bytes = 0;
int ret = 0;
switch_buffer_t *buffer = NULL;
const char *header = NULL;
int header_len = 0;
if (!type) return SWITCH_STATUS_FALSE;
if (img->fmt == SWITCH_IMG_FMT_I420) {
comp = STBI_rgb;
stride_in_bytes = img->d_w * 3;
data = malloc(stride_in_bytes * img->d_h);
switch_assert(data);
I420ToRAW(img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
img->planes[SWITCH_PLANE_U], img->stride[SWITCH_PLANE_U],
img->planes[SWITCH_PLANE_V], img->stride[SWITCH_PLANE_V],
data, stride_in_bytes,
img->d_w, img->d_h);
} else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
comp = STBI_rgb_alpha;
stride_in_bytes = img->d_w * 4;
data = malloc(stride_in_bytes * img->d_h);
switch_assert(data);
#if SWITCH_BYTE_ORDER == __BIG_ENDIAN
ARGBToRGBA(img->planes[SWITCH_PLANE_PACKED], stride_in_bytes, data, stride_in_bytes, img->d_w, img->d_h);
#else
ARGBToABGR(img->planes[SWITCH_PLANE_PACKED], stride_in_bytes, data, stride_in_bytes, img->d_w, img->d_h);
#endif
} else {
return SWITCH_STATUS_FALSE;
}
switch_buffer_create_dynamic(&buffer, 1024, 1024, 0);
if (!strcmp(type, "png")) {
header = "data:image/png;base64,";
ret = stbi_write_png_to_func(data_url_write_func, (void *)buffer, img->d_w, img->d_h, comp, (const void *)data, stride_in_bytes);
} else if (!strcmp(type, "jpeg") || !strcmp(type, "jpeg")) {
header = "data:image/jpeg;base64,";
ret = stbi_write_jpg_to_func(data_url_write_func, (void *)buffer, img->d_w, img->d_h, comp, (const void *)data, quality);
} else {
ret = 0;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unsupported file format [%s]\n", type);
}
if (ret && switch_buffer_inuse(buffer) > 0) {
switch_size_t blen = switch_buffer_inuse(buffer);
switch_size_t olen = blen * 4 + strlen(header) + 1;
uint8_t *data = switch_buffer_get_head_pointer(buffer);
unsigned char *out = NULL;
switch_zmalloc(out, olen);
header_len = strlen(header);
memcpy(out, header, header_len);
switch_b64_encode(data, blen, out + header_len, olen - header_len);
*urlP = (char *)out;
}
free(data);
switch_buffer_destroy(&buffer);
return ret ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
#endif /* SWITCH_HAVE_YUV */
}
SWITCH_DECLARE(switch_status_t) switch_img_letterbox(switch_image_t *img, switch_image_t **imgP, int width, int height, const char *color)
{
int img_w = 0, img_h = 0;
double screen_aspect = 0, img_aspect = 0;
int x_pos = 0;
int y_pos = 0;
switch_image_t *IMG = NULL, *scale_img = NULL;
switch_rgb_color_t bgcolor = { 0 };
switch_assert(imgP);
*imgP = NULL;
if (img->d_w == width && img->d_h == height) {
switch_img_copy(img, imgP);
return SWITCH_STATUS_SUCCESS;
}
IMG = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, width, height, 1);
switch_color_set_rgb(&bgcolor, color);
switch_img_fill(IMG, 0, 0, IMG->d_w, IMG->d_h, &bgcolor);
img_w = IMG->d_w;
img_h = IMG->d_h;
screen_aspect = (double) IMG->d_w / IMG->d_h;
img_aspect = (double) img->d_w / img->d_h;
if (screen_aspect > img_aspect) {
img_w = img_aspect * IMG->d_h;
x_pos = (IMG->d_w - img_w) / 2;
} else if (screen_aspect < img_aspect) {
img_h = IMG->d_w / img_aspect;
y_pos = (IMG->d_h - img_h) / 2;
}
switch_img_scale(img, &scale_img, img_w, img_h);
switch_img_patch(IMG, scale_img, x_pos, y_pos);
switch_img_free(&scale_img);
*imgP = IMG;
return SWITCH_STATUS_SUCCESS;
}
SWITCH_DECLARE(void) switch_img_calc_fit(switch_image_t *src, int width, int height, int *new_wP, int *new_hP)
{
int new_w, new_h;
switch_assert(src);
new_w = src->d_w;
new_h = src->d_h;
if (src->d_w < width && src->d_h < height) {
float rw = (float)new_w / width;
float rh = (float)new_h / height;
if (rw > rh) {
new_h = (int)((float)new_h / rw);
new_w = width;
} else {
new_w = (int)((float)new_w / rh);
new_h = height;
}
} else {
while(new_w > width || new_h > height) {
if (new_w > width) {
double m = (double) width / new_w;
new_w = width;
new_h = (int) (new_h * m);
} else {
double m = (double) height / new_h;
new_h = height;
new_w = (int) (new_w * m);
}
}
}
*new_wP = new_w;
*new_hP = new_h;
}
SWITCH_DECLARE(switch_status_t) switch_img_fit(switch_image_t **srcP, int width, int height, switch_img_fit_t fit)
{
switch_image_t *src, *tmp = NULL;
int new_w = 0, new_h = 0;
switch_assert(srcP);
switch_assert(width && height);
src = *srcP;
if (!src || (src->d_w == width && src->d_h == height)) {
return SWITCH_STATUS_SUCCESS;
}
if (fit == SWITCH_FIT_NECESSARY && src->d_w <= width && src->d_h < height) {
return SWITCH_STATUS_SUCCESS;
}
if (fit == SWITCH_FIT_SCALE) {
switch_img_scale(src, &tmp, width, height);
switch_img_free(&src);
*srcP = tmp;
return SWITCH_STATUS_SUCCESS;
}
switch_img_calc_fit(src, width, height, &new_w, &new_h);
if (new_w && new_h) {
if (switch_img_scale(src, &tmp, new_w, new_h) == SWITCH_STATUS_SUCCESS) {
switch_img_free(&src);
*srcP = tmp;
if (fit == SWITCH_FIT_SIZE_AND_SCALE) {
src = *srcP;
tmp = NULL;
switch_img_scale(src, &tmp, width, height);
switch_img_free(&src);
*srcP = tmp;
}
return SWITCH_STATUS_SUCCESS;
}
}
return SWITCH_STATUS_FALSE;
}
#ifdef SWITCH_HAVE_YUV
static inline uint32_t switch_img_fmt2fourcc(switch_img_fmt_t fmt)
{
uint32_t fourcc;
switch(fmt) {
case SWITCH_IMG_FMT_NONE: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_RGB24: fourcc = (uint32_t)FOURCC_24BG; break;
case SWITCH_IMG_FMT_RGB32: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_RGB565: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_RGB555: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_UYVY: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_YUY2: fourcc = (uint32_t)FOURCC_YUY2; break;
case SWITCH_IMG_FMT_YVYU: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_BGR24: fourcc = (uint32_t)FOURCC_RAW ; break;
case SWITCH_IMG_FMT_RGB32_LE: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_ARGB: fourcc = (uint32_t)FOURCC_ARGB; break;
case SWITCH_IMG_FMT_ARGB_LE: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_RGB565_LE: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_RGB555_LE: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_YV12: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_I420: fourcc = (uint32_t)FOURCC_I420; break;
case SWITCH_IMG_FMT_VPXYV12: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_VPXI420: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_I422: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_I444: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_I440: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_444A: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_I42016: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_I42216: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_I44416: fourcc = (uint32_t)FOURCC_ANY ; break;
case SWITCH_IMG_FMT_I44016: fourcc = (uint32_t)FOURCC_ANY ; break;
default: fourcc = (uint32_t)FOURCC_ANY;
}
return fourcc;
}
#endif
SWITCH_DECLARE(switch_status_t) switch_img_to_raw(switch_image_t *src, void *dest, int stride, switch_img_fmt_t fmt)
{
#ifdef SWITCH_HAVE_YUV
uint32_t fourcc;
int ret;
switch_assert(dest);
fourcc = switch_img_fmt2fourcc(fmt);
if (fourcc == FOURCC_ANY) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unsupported format: %d\n", fmt);
return SWITCH_STATUS_FALSE;
}
if (src->fmt == SWITCH_IMG_FMT_I420) {
ret = ConvertFromI420(src->planes[0], src->stride[0],
src->planes[1], src->stride[1],
src->planes[2], src->stride[2],
dest, stride,
src->d_w, src->d_h,
fourcc);
} else if (src->fmt == SWITCH_IMG_FMT_ARGB && fmt == src->fmt) {
ret = ARGBCopy(src->planes[SWITCH_PLANE_PACKED], src->stride[SWITCH_PLANE_PACKED],
dest, stride,
src->d_w, src->d_h);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Convertion not supported %d -> %d\n", src->fmt, fmt);
return SWITCH_STATUS_FALSE;
}
return ret == 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
#else
return SWITCH_STATUS_FALSE;
#endif
}
SWITCH_DECLARE(switch_status_t) switch_img_from_raw(switch_image_t **destP, void *src, switch_img_fmt_t fmt, int width, int height)
{
#ifdef SWITCH_HAVE_YUV
uint32_t fourcc;
int ret = -1;
switch_image_t *dest = NULL;
if (!destP) {
return SWITCH_STATUS_FALSE;
}
dest = *destP;
fourcc = switch_img_fmt2fourcc(fmt);
if (fourcc == FOURCC_ANY) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unsupported format: %d\n", fmt);
return SWITCH_STATUS_FALSE;
}
if (!dest && width > 0 && height > 0) dest = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, width, height, 1);
if (!dest) return SWITCH_STATUS_FALSE;
if (width == 0 || height == 0) {
width = dest->d_w;
height = dest->d_h;
}
/*
int ConvertToI420(const uint8_t* src_frame, size_t src_size,
uint8_t* dst_y, int dst_stride_y,
uint8_t* dst_u, int dst_stride_u,
uint8_t* dst_v, int dst_stride_v,
int crop_x, int crop_y,
int src_width, int src_height,
int crop_width, int crop_height,
enum RotationMode rotation,
uint32 format);
src_size is only used when FOURCC_MJPG which we don't support so always 0
*/
if (dest->fmt == SWITCH_IMG_FMT_I420) {
ret = ConvertToI420(src, 0,
dest->planes[0], dest->stride[0],
dest->planes[1], dest->stride[1],
dest->planes[2], dest->stride[2],
0, 0,
width, height,
width, height,
0, fourcc);
} else if (dest->fmt == SWITCH_IMG_FMT_ARGB) {
ConvertToARGB(src, 0,
dest->planes[0], width * 4,
0, 0,
width, height,
width, height,
0, fourcc);
}
*destP = dest;
return ret == 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
#else
return SWITCH_STATUS_FALSE;
#endif
}
SWITCH_DECLARE(switch_status_t) switch_img_scale(switch_image_t *src, switch_image_t **destP, int width, int height)
{
#ifdef SWITCH_HAVE_YUV
switch_image_t *dest = NULL;
int ret = 0;
if (!destP) {
return SWITCH_STATUS_FALSE;
}
dest = *destP;
switch_assert(width > 0);
switch_assert(height > 0);
if (dest && src->fmt != dest->fmt) switch_img_free(&dest);
if (!dest) dest = switch_img_alloc(NULL, src->fmt, width, height, 1);
if (src->fmt == SWITCH_IMG_FMT_I420) {
ret = I420Scale(src->planes[0], src->stride[0],
src->planes[1], src->stride[1],
src->planes[2], src->stride[2],
src->d_w, src->d_h,
dest->planes[0], dest->stride[0],
dest->planes[1], dest->stride[1],
dest->planes[2], dest->stride[2],
width, height,
kFilterBox);
} else if (src->fmt == SWITCH_IMG_FMT_ARGB) {
ret = ARGBScale(src->planes[SWITCH_PLANE_PACKED], src->d_w * 4,
src->d_w, src->d_h,
dest->planes[SWITCH_PLANE_PACKED], width * 4,
width, height,
kFilterBox);
}
*destP = dest;
if (ret != 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Scaling Error: ret: %d\n", ret);
return SWITCH_STATUS_FALSE;
}
return SWITCH_STATUS_SUCCESS;
#else
return SWITCH_STATUS_FALSE;
#endif
}
SWITCH_DECLARE(switch_status_t) switch_img_mirror(switch_image_t *src, switch_image_t **destP)
{
#ifdef SWITCH_HAVE_YUV
switch_image_t *dest = NULL;
int ret = 0;
if (!destP) {
return SWITCH_STATUS_FALSE;
}
dest = *destP;
if (dest && src->fmt != dest->fmt) switch_img_free(&dest);
if (!dest) dest = switch_img_alloc(NULL, src->fmt, src->d_w, src->d_h, 1);
if (src->fmt == SWITCH_IMG_FMT_I420) {
ret = I420Mirror(src->planes[0], src->stride[0],
src->planes[1], src->stride[1],
src->planes[2], src->stride[2],
dest->planes[0], dest->stride[0],
dest->planes[1], dest->stride[1],
dest->planes[2], dest->stride[2],
src->d_w, src->d_h);
} else if (src->fmt == SWITCH_IMG_FMT_ARGB) {
ret = ARGBMirror(src->planes[SWITCH_PLANE_PACKED], src->d_w * 4,
dest->planes[SWITCH_PLANE_PACKED], src->d_w * 4,
src->d_w, src->d_h);
}
*destP = dest;
if (ret != 0) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Mirror Error: ret: %d\n", ret);
return SWITCH_STATUS_FALSE;
}
return SWITCH_STATUS_SUCCESS;
#else
return SWITCH_STATUS_FALSE;
#endif
}
SWITCH_DECLARE(void) switch_img_find_position(switch_img_position_t pos, int sw, int sh, int iw, int ih, int *xP, int *yP)
{
switch(pos) {
case POS_NONE:
case POS_LEFT_TOP:
*xP = 0;
*yP = 0;
break;
case POS_LEFT_MID:
*xP = 0;
*yP = (sh - ih) / 2;
break;
case POS_LEFT_BOT:
*xP = 0;
*yP = (sh - ih);
break;
case POS_CENTER_TOP:
*xP = (sw - iw) / 2;
*yP = 0;
break;
case POS_CENTER_MID:
*xP = (sw - iw) / 2;
*yP = (sh - ih) / 2;
break;
case POS_CENTER_BOT:
*xP = (sw - iw) / 2;
*yP = (sh - ih);
break;
case POS_RIGHT_TOP:
*xP = (sw - iw);
*yP = 0;
break;
case POS_RIGHT_MID:
*xP = (sw - iw);
*yP = (sh - ih) / 2;
break;
case POS_RIGHT_BOT:
*xP = (sw - iw);
*yP = (sh - ih);
break;
};
}
#ifdef HAVE_LIBGD
SWITCH_DECLARE(switch_image_t *) switch_img_read_file(const char* file_name)
{
switch_image_t *img = switch_img_alloc(NULL, SWITCH_IMG_FMT_ARGB, 1, 1, 1);
gdImagePtr gd = NULL;
char *ext;
FILE *fp;
if (!img) return NULL;
// gd = gdImageCreateFromFile(file_name); // only available in 2.1.1
ext = strrchr(file_name, '.');
if (!ext) goto err;
fp = fopen(file_name, "rb");
if (!fp) goto err;
if (!strcmp(ext, ".png")) {
gd = gdImageCreateFromPng(fp);
} else if (!strcmp(ext, ".gif")) {
gd = gdImageCreateFromGif(fp);
} else if (!strcmp(ext, ".jpg") || !strcmp(ext, ".jpeg")) {
gd = gdImageCreateFromJpeg(fp);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Not supported file type: %s\n", ext);
}
fclose(fp);
if (!gd) goto err;
img->fmt = SWITCH_IMG_FMT_GD;
img->d_w = gd->sx;
img->d_h = gd->sy;
img->user_priv = gd;
return img;
err:
switch_img_free(&img);
return NULL;
}
#else
SWITCH_DECLARE(switch_image_t *) switch_img_read_file(const char* file_name)
{
return NULL;
}
#endif
SWITCH_DECLARE(switch_status_t) switch_I420_copy(const uint8_t *src_y, int src_stride_y,
const uint8_t *src_u, int src_stride_u,
const uint8_t *src_v, int src_stride_v,
uint8_t *dst_y, int dst_stride_y,
uint8_t *dst_u, int dst_stride_u,
uint8_t *dst_v, int dst_stride_v,
int width, int height)
{
#ifdef SWITCH_HAVE_YUV
int ret = I420Copy(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
dst_y, dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v,
width, height);
return ret == 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
#else
return SWITCH_STATUS_FALSE;
#endif
}
SWITCH_DECLARE(switch_status_t) switch_I420_copy2(uint8_t *src_planes[], int src_stride[],
uint8_t *dst_planes[], int dst_stride[],
int width, int height)
{
#ifdef SWITCH_HAVE_YUV
int ret = I420Copy(src_planes[SWITCH_PLANE_Y], src_stride[SWITCH_PLANE_Y],
src_planes[SWITCH_PLANE_U], src_stride[SWITCH_PLANE_U],
src_planes[SWITCH_PLANE_V], src_stride[SWITCH_PLANE_V],
dst_planes[SWITCH_PLANE_Y], dst_stride[SWITCH_PLANE_Y],
dst_planes[SWITCH_PLANE_U], dst_stride[SWITCH_PLANE_U],
dst_planes[SWITCH_PLANE_V], dst_stride[SWITCH_PLANE_V],
width, height);
return ret == 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
#else
return SWITCH_STATUS_FALSE;
#endif
}
SWITCH_DECLARE(switch_status_t) switch_I420ToARGB(const uint8_t *src_y, int src_stride_y,
const uint8_t *src_u, int src_stride_u,
const uint8_t *src_v, int src_stride_v,
uint8_t *dst_argb, int dst_stride_argb,
int width, int height)
{
#ifdef SWITCH_HAVE_YUV
int ret = I420ToARGB(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
dst_argb, dst_stride_argb, width, height);
return ret == 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
#else
return SWITCH_STATUS_FALSE;
#endif
}
SWITCH_DECLARE(switch_status_t) switch_RGBAToARGB(const uint8_t* src_frame, int src_stride_frame,
uint8_t* dst_argb, int dst_stride_argb,
int width, int height)
{
#ifdef SWITCH_HAVE_YUV
int ret = RGBAToARGB(src_frame, src_stride_frame, dst_argb, dst_stride_argb, width, height);
return ret == 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
#else
return SWITCH_STATUS_FALSE;
#endif
}
SWITCH_DECLARE(switch_status_t) switch_ABGRToARGB(const uint8_t* src_frame, int src_stride_frame,
uint8_t* dst_argb, int dst_stride_argb,
int width, int height)
{
#ifdef SWITCH_HAVE_YUV
int ret = ABGRToARGB(src_frame, src_stride_frame, dst_argb, dst_stride_argb, width, height);
return ret == 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
#else
return SWITCH_STATUS_FALSE;
#endif
}
SWITCH_DECLARE(switch_status_t) switch_ARGBToARGB(const uint8_t* src_frame, int src_stride_frame,
uint8_t* dst_argb, int dst_stride_argb,
int width, int height)
{
#ifdef SWITCH_HAVE_YUV
int ret = ARGBToARGB(src_frame, src_stride_frame, dst_argb, dst_stride_argb, width, height);
return ret == 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
#else
return SWITCH_STATUS_FALSE;
#endif
}
SWITCH_DECLARE(void) switch_core_video_parse_filter_string(switch_core_video_filter_t *filters, const char *filter_str)
{
*filters = 0;
if (!filter_str) return;
if (switch_stristr("fg-gray", filter_str)) {
*filters |= SCV_FILTER_GRAY_FG;
}
if (switch_stristr("bg-gray", filter_str)) {
*filters |= SCV_FILTER_GRAY_BG;
}
if (switch_stristr("fg-sepia", filter_str)) {
*filters |= SCV_FILTER_SEPIA_FG;
}
if (switch_stristr("bg-sepia", filter_str)) {
*filters |= SCV_FILTER_SEPIA_BG;
}
if (switch_stristr("fg-8bit", filter_str)) {
*filters |= SCV_FILTER_8BIT_FG;
}
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
*/