From 0697db4fb4f404eb1c20ba457ecc54cd1c715cc9 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 11 Feb 2015 14:07:06 -0600 Subject: [PATCH] FS-7506 FS-7513: set banner with video_banner_text variable set before calling in, NEEDS DOCS params are parsable inside a {} string --- Makefile.am | 7 +- configure.ac | 1 + src/include/switch_core_video.h | 38 +- src/include/switch_types.h | 6 +- src/include/switch_utf8.h | 119 ++++ src/include/switch_vpx.h | 84 +++ .../mod_conference/mod_conference.c | 295 +++++----- src/switch_core_video.c | 197 ++++++- src/switch_utf8.c | 516 ++++++++++++++++++ 9 files changed, 1067 insertions(+), 196 deletions(-) create mode 100644 src/include/switch_utf8.h create mode 100644 src/include/switch_vpx.h create mode 100644 src/switch_utf8.c diff --git a/Makefile.am b/Makefile.am index fa82c9fa9f..8f93c80317 100644 --- a/Makefile.am +++ b/Makefile.am @@ -153,9 +153,9 @@ libfreeswitch_spandsp_la_SOURCES = libs/spandsp/src/plc.c libs/spandsp/src/alloc libfreeswitch_spandsp_la_CFLAGS = -Ilibs/spandsp/src $(CORE_CFLAGS) $(AM_CFLAGS) CORE_LIBS+=libfreeswitch_spandsp.la lib_LTLIBRARIES = libfreeswitch.la -libfreeswitch_la_CFLAGS = $(CORE_CFLAGS) $(SQLITE_CFLAGS) $(CURL_CFLAGS) $(PCRE_CFLAGS) $(SPEEX_CFLAGS) $(LIBEDIT_CFLAGS) $(openssl_CFLAGS) $(AM_CFLAGS) +libfreeswitch_la_CFLAGS = $(CORE_CFLAGS) $(SQLITE_CFLAGS) $(FREETYPE_CFLAGS) $(CURL_CFLAGS) $(PCRE_CFLAGS) $(SPEEX_CFLAGS) $(LIBEDIT_CFLAGS) $(openssl_CFLAGS) $(AM_CFLAGS) libfreeswitch_la_LDFLAGS = -version-info 1:0:0 $(AM_LDFLAGS) $(PLATFORM_CORE_LDFLAGS) -no-undefined -libfreeswitch_la_LIBADD = $(CORE_LIBS) $(APR_LIBS) $(SQLITE_LIBS) $(CURL_LIBS) $(PCRE_LIBS) $(SPEEX_LIBS) $(LIBEDIT_LIBS) $(openssl_LIBS) $(VPX_LIBS) $(PLATFORM_CORE_LIBS) +libfreeswitch_la_LIBADD = $(CORE_LIBS) $(APR_LIBS) $(SQLITE_LIBS) $(FREETYPE_LIBS) $(CURL_LIBS) $(PCRE_LIBS) $(SPEEX_LIBS) $(LIBEDIT_LIBS) $(openssl_LIBS) $(VPX_LIBS) $(PLATFORM_CORE_LIBS) libfreeswitch_la_DEPENDENCIES = $(BUILT_SOURCES) if HAVE_ODBC @@ -215,6 +215,8 @@ library_include_HEADERS = \ src/include/switch_curl.h \ src/include/switch_json.h \ src/include/switch_stfu.h \ + src/include/switch_utf8.h \ + src/include/switch_vpx.h \ libs/libteletone/src/libteletone_detect.h \ libs/libteletone/src/libteletone_generate.h \ libs/libteletone/src/libteletone.h \ @@ -293,6 +295,7 @@ libfreeswitch_la_SOURCES = \ src/switch_curl.c \ src/switch_hashtable.c\ src/switch_stfu.c \ + src/switch_utf8.c \ libs/libtpl-1.5/src/tpl.c \ libs/libteletone/src/libteletone_detect.c \ libs/libteletone/src/libteletone_generate.c \ diff --git a/configure.ac b/configure.ac index c96d87af6e..965fc4f8cf 100644 --- a/configure.ac +++ b/configure.ac @@ -1192,6 +1192,7 @@ module_enabled() { grep -v -e "\#" -e "^\$" modules.conf | sed -e "s|^.*/||" | grep "^${1}\$" >/dev/null } +PKG_CHECK_MODULES([FREETYPE], [freetype2 >= 2.4.9]) PKG_CHECK_MODULES([SQLITE], [sqlite3 >= 3.6.20]) PKG_CHECK_MODULES([CURL], [libcurl >= 7.19]) PKG_CHECK_MODULES([PCRE], [libpcre >= 7.8]) diff --git a/src/include/switch_core_video.h b/src/include/switch_core_video.h index 92440a1e7b..43fd9bb747 100644 --- a/src/include/switch_core_video.h +++ b/src/include/switch_core_video.h @@ -22,6 +22,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Anthony Minessale II * * * switch_core_video.h -- Core Video header @@ -35,32 +36,14 @@ opportunity to thank libvpx for all the awesome stuff it does and for making my life much easier. */ + #ifndef SWITCH_VIDEO_H #define SWITCH_VIDEO_H -#include "vpx/vpx_image.h" -#include "vpx/vpx_integer.h" +#include SWITCH_BEGIN_EXTERN_C -#define SWITCH_IMG_FMT_PLANAR VPX_IMG_FMT_PLANAR -#define SWITCH_IMG_FMT_UV_FLIP VPX_IMG_FMT_UV_FLIP -#define SWITCH_IMG_FMT_HAS_ALPHA VPX_IMG_FMT_HAS_ALPHA - - -#define SWITCH_PLANE_PACKED VPX_PLANE_PACKED -#define SWITCH_PLANE_Y VPX_PLANE_Y -#define SWITCH_PLANE_U VPX_PLANE_U -#define SWITCH_PLANE_V VPX_PLANE_V -#define SWITCH_PLANE_ALPHA VPX_PLANE_ALPHA - -#ifndef VPX_IMG_FMT_HIGH /* not available in libvpx 1.3.0 (see commit hash e97aea28) */ -#define VPX_IMG_FMT_HIGH 0x800 /**< Image uses 16bit framebuffer */ -#endif - -#define SWITCH_IMG_FMT_HIGH VPX_IMG_FMT_HIGH -#define SWITCH_IMG_FMT_I420 VPX_IMG_FMT_I420 - typedef struct switch_yuv_color_s { uint8_t y; uint8_t u; @@ -68,9 +51,6 @@ typedef struct switch_yuv_color_s { } switch_yuv_color_t; -typedef vpx_img_fmt_t switch_img_fmt_t; - -typedef vpx_image_t switch_image_t; /**\brief Representation of a rectangle on a surface */ typedef struct switch_image_rect { @@ -80,6 +60,7 @@ typedef struct switch_image_rect { unsigned int h; /**< height */ } switch_image_rect_t; + /*!\brief Open a descriptor, allocating storage for the underlying image * * Returns a descriptor for storing an image of the given format. The @@ -193,7 +174,16 @@ SWITCH_DECLARE(void) switch_img_fill(switch_image_t *img, int x, int y, int w, i SWITCH_DECLARE(void) switch_img_draw_pixel(switch_image_t *img, int x, int y, switch_yuv_color_t color); -SWITCH_DECLARE(void) switch_color_set(switch_yuv_color_t *color, char *color_str); +SWITCH_DECLARE(void) switch_color_set(switch_yuv_color_t *color, const char *color_str); + +SWITCH_DECLARE(switch_status_t) switch_img_txt_handle_create(switch_img_txt_handle_t **handleP, const char *font_family, + const char *font_color, uint16_t font_size, double angle, switch_memory_pool_t *pool); + +SWITCH_DECLARE(void) switch_img_txt_handle_destroy(switch_img_txt_handle_t **handleP); + +SWITCH_DECLARE(switch_status_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, uint16_t font_size, double angle); /** @} */ diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 80f4809619..068e5d0849 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -40,7 +40,7 @@ #include #include -#include +#include SWITCH_BEGIN_EXTERN_C #define SWITCH_ENT_ORIGINATE_DELIM ":_:" @@ -2215,8 +2215,6 @@ typedef switch_status_t (*switch_core_codec_control_func_t) (switch_codec_t *cod void **ret_data); -typedef switch_status_t (*switch_image_write_callback_t) (switch_core_session_t *session, switch_frame_t *frame, switch_image_t *img, void *user_data); - typedef switch_status_t (*switch_core_codec_init_func_t) (switch_codec_t *, switch_codec_flag_t, const switch_codec_settings_t *codec_settings); typedef switch_status_t (*switch_core_codec_fmtp_parse_func_t) (const char *fmtp, switch_codec_fmtp_t *codec_fmtp); typedef switch_status_t (*switch_core_codec_destroy_func_t) (switch_codec_t *); @@ -2501,6 +2499,8 @@ typedef struct switch_waitlist_s { struct switch_vb_s; typedef struct switch_vb_s switch_vb_t; +struct switch_img_txt_handle_s; +typedef struct switch_img_txt_handle_s switch_img_txt_handle_t; SWITCH_END_EXTERN_C #endif diff --git a/src/include/switch_utf8.h b/src/include/switch_utf8.h new file mode 100644 index 0000000000..2089370b49 --- /dev/null +++ b/src/include/switch_utf8.h @@ -0,0 +1,119 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2014, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Anthony Minessale II + * + * + * switch_utf8.h UTf8 + * + */ + + +/* + Basic UTF-8 manipulation routines + by Jeff Bezanson + placed in the public domain Fall 2005 + + This code is designed to provide the utilities you need to manipulate + UTF-8 as an internal string encoding. These functions do not perform the + error checking normally needed when handling UTF-8 data, so if you happen + to be from the Unicode Consortium you will want to flay me alive. + I do this because error checking can be performed at the boundaries (I/O), + with these routines reserved for higher performance on data known to be + valid. +*/ + +#include + +/* is c the start of a utf8 sequence? */ +#define isutf(c) (((c)&0xC0)!=0x80) + +/* convert UTF-8 data to wide character */ +SWITCH_DECLARE(int) switch_u8_toucs(uint32_t *dest, int sz, char *src, int srcsz); + +/* the opposite conversion */ +SWITCH_DECLARE(int) switch_u8_toutf8(char *dest, int sz, uint32_t *src, int srcsz); + +/* single character to UTF-8 */ +SWITCH_DECLARE(int) switch_u8_wc_toutf8(char *dest, uint32_t ch); + +/* character number to byte offset */ +SWITCH_DECLARE(int) switch_u8_offset(char *str, int charnum); + +/* byte offset to character number */ +SWITCH_DECLARE(int) switch_u8_charnum(char *s, int offset); + +/* return next character, updating an index variable */ +SWITCH_DECLARE(uint32_t) switch_u8_nextchar(char *s, int *i); + +/* move to next character */ +SWITCH_DECLARE(void) switch_u8_inc(char *s, int *i); + +/* move to previous character */ +SWITCH_DECLARE(void) switch_u8_dec(char *s, int *i); + +/* returns length of next utf-8 sequence */ +SWITCH_DECLARE(int) switch_u8_seqlen(char *s); + +/* assuming src points to the character after a backslash, read an + escape sequence, storing the result in dest and returning the number of + input characters processed */ +SWITCH_DECLARE(int) switch_u8_read_escape_sequence(char *src, uint32_t *dest); + +/* given a wide character, convert it to an ASCII escape sequence stored in + buf, where buf is "sz" bytes. returns the number of characters output.*/ +SWITCH_DECLARE(int) switch_u8_escape_wchar(char *buf, int sz, uint32_t ch); + +/* convert a string "src" containing escape sequences to UTF-8 */ +SWITCH_DECLARE(int) switch_u8_unescape(char *buf, int sz, char *src); + +/* convert UTF-8 "src" to ASCII with escape sequences. + if escape_quotes is nonzero, quote characters will be preceded by + backslashes as well. */ +SWITCH_DECLARE(int) switch_u8_escape(char *buf, int sz, char *src, int escape_quotes); + +/* utility predicates used by the above */ +int octal_digit(char c); +int hex_digit(char c); + +/* return a pointer to the first occurrence of ch in s, or NULL if not + found. character index of found character returned in *charn. */ +SWITCH_DECLARE(char *) switch_u8_strchr(char *s, uint32_t ch, int *charn); + +/* same as the above, but searches a buffer of a given size instead of + a NUL-terminated string. */ +SWITCH_DECLARE(char *) switch_u8_memchr(char *s, uint32_t ch, size_t sz, int *charn); + +/* count the number of characters in a UTF-8 string */ +SWITCH_DECLARE(int) switch_u8_strlen(char *s); + +SWITCH_DECLARE(int) switch_u8_is_locale_utf8(char *locale); + +/* printf where the format string and arguments may be in UTF-8. + you can avoid this function and just use ordinary printf() if the current + locale is UTF-8. */ +SWITCH_DECLARE(int) switch_u8_vprintf(char *fmt, va_list ap); +SWITCH_DECLARE(int) switch_u8_printf(char *fmt, ...); + +SWITCH_DECLARE(uint32_t) switch_u8_get_char(char *s, int *i); diff --git a/src/include/switch_vpx.h b/src/include/switch_vpx.h new file mode 100644 index 0000000000..6a0f6de71b --- /dev/null +++ b/src/include/switch_vpx.h @@ -0,0 +1,84 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2014, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * + * switch_vpx.h -- vpx resources + * + */ +/*! \file switch_vpx.h + \brief vpx resources + + The things powered by libvpx are renamed into the switch_ namespace to provide a cleaner + look to things and helps me to document what parts of video I am using I'd like to take this + opportunity to thank libvpx for all the awesome stuff it does and for making my life much easier. + +*/ + +#ifndef SWITCH_VPX_H +#define SWITCH_VPX_H + +#include + +#include "vpx/vpx_image.h" +#include "vpx/vpx_integer.h" + +SWITCH_BEGIN_EXTERN_C + +#define SWITCH_IMG_FMT_PLANAR VPX_IMG_FMT_PLANAR +#define SWITCH_IMG_FMT_UV_FLIP VPX_IMG_FMT_UV_FLIP +#define SWITCH_IMG_FMT_HAS_ALPHA VPX_IMG_FMT_HAS_ALPHA + + +#define SWITCH_PLANE_PACKED VPX_PLANE_PACKED +#define SWITCH_PLANE_Y VPX_PLANE_Y +#define SWITCH_PLANE_U VPX_PLANE_U +#define SWITCH_PLANE_V VPX_PLANE_V +#define SWITCH_PLANE_ALPHA VPX_PLANE_ALPHA + +#ifndef VPX_IMG_FMT_HIGH /* not available in libvpx 1.3.0 (see commit hash e97aea28) */ +#define VPX_IMG_FMT_HIGH 0x800 /**< Image uses 16bit framebuffer */ +#endif + +#define SWITCH_IMG_FMT_HIGH VPX_IMG_FMT_HIGH +#define SWITCH_IMG_FMT_I420 VPX_IMG_FMT_I420 + +typedef vpx_img_fmt_t switch_img_fmt_t; + +typedef vpx_image_t switch_image_t; + + +SWITCH_END_EXTERN_C +#endif + +/* 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: + */ diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 44d8f97d74..abbf0d8731 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -358,8 +358,14 @@ typedef struct mcu_layer_s { int member_id; int idx; int tagged; + int screen_w; + int screen_h; + int x_pos; + int y_pos; switch_image_t *img; switch_image_t *cur_img; + switch_image_t *banner_img; + switch_img_txt_handle_t *txthandle; } mcu_layer_t; typedef struct mcu_canvas_s { @@ -702,132 +708,6 @@ typedef struct layout_group_s { } layout_group_t; -#include "utf8.h" - -static const u_int32_t offsetsFromUTF8[6] = { - 0x00000000UL, 0x00003080UL, 0x000E2080UL, - 0x03C82080UL, 0xFA082080UL, 0x82082080UL -}; - -/* reads the next utf-8 sequence out of a string, updating an index */ -uint32_t get_utf8_char(char *s, int *i) -{ - u_int32_t ch = 0; - int sz = 0; - - do { - ch <<= 6; - ch += (unsigned char)s[(*i)++]; - sz++; - } while (s[*i] && !isutf(s[*i])); - - ch -= offsetsFromUTF8[sz-1]; - - return ch; -} - -#include -#include FT_FREETYPE_H -#include FT_GLYPH_H - -static void draw_bitmap(switch_image_t *img, FT_Bitmap* bitmap, FT_Int x, FT_Int y, switch_yuv_color_t color) -{ - FT_Int i, j, p, q; - FT_Int x_max = x + bitmap->width; - FT_Int y_max = y + bitmap->rows; - - 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: - 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++ ) { - if ( i < 0 || j < 0 || i >= img->d_w || j >= img->d_h) continue; - - if (bitmap->buffer[q * bitmap->width + p] > 128) { - switch_img_draw_pixel(img, i, j, color); - } - } - } -} - -SWITCH_DECLARE(void) switch_img_draw_text(switch_image_t *img, int x, int y, switch_yuv_color_t color, uint16_t font_size, char *text) -{ - FT_Library library; - FT_Face face; - FT_GlyphSlot slot; - FT_Matrix matrix; /* transformation matrix */ - FT_Vector pen; /* untransformed origin */ - FT_Error error; - // char* font_family = "/usr/local/freeswitch/SimHei.ttf"; - char* font_family = "/usr/local/freeswitch/Arial.ttf"; - double angle; - int target_height; - int index = 0; - FT_ULong ch; - - if (zstr(text)) return; - - angle = 0; // (45.0 / 360 ) * 3.14159 * 2; - target_height = img->d_h; - - error = FT_Init_FreeType( &library ); /* initialize library */ - if (error) return; - - error = FT_New_Face(library, font_family, 0, &face); /* create face object */ - if (error) return; - - /* use 50pt at 100dpi */ - error = FT_Set_Char_Size(face, 64 * font_size, 0, 96, 96); /* set character size */ - if (error) return; - - slot = face->glyph; - - /* 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 * 64; - pen.y = (target_height - y) * 64; - - while(*(text + index)) { - ch = get_utf8_char(text, &index); - - if (ch == '\n') { - pen.x = x * 64; - pen.y -= (font_size + font_size / 4) * 64; - 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; - - /* now, draw to our target surface (convert position) */ - draw_bitmap(img, &slot->bitmap, slot->bitmap_left, target_height - slot->bitmap_top + font_size, color); - - /* increment pen position */ - pen.x += slot->advance.x; - pen.y += slot->advance.y; - } - - FT_Done_Face(face); - FT_Done_FreeType(library); -} static void conference_parse_layouts(conference_obj_t *conference) { @@ -1021,32 +901,22 @@ static void reset_image(switch_image_t *img, switch_yuv_color_t *color) static void reset_layer(mcu_canvas_t *canvas, mcu_layer_t *layer) { - int x = 0, y = 0; - int screen_w = 0, screen_h = 0; - layer->tagged = 0; - screen_w = canvas->img->d_w * layer->geometry.scale / SCALE_FACTOR; - screen_h = canvas->img->d_h * layer->geometry.scale / SCALE_FACTOR; + switch_img_free(&layer->banner_img); - if (screen_w % 2) screen_w++; // round to even - if (screen_h % 2) screen_h++; // round to even - - x = canvas->img->d_w * layer->geometry.x / SCALE_FACTOR; - y = canvas->img->d_h * layer->geometry.y / SCALE_FACTOR; - - if (layer->img && (layer->img->d_w != screen_w || layer->img->d_h != screen_h)) { + if (layer->img && (layer->img->d_w != layer->screen_w || layer->img->d_h != layer->screen_h)) { switch_img_free(&layer->img); } if (!layer->img) { - layer->img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, screen_w, screen_h, 1); + layer->img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, layer->screen_w, layer->screen_h, 1); } switch_assert(layer->img); reset_image(layer->img, &canvas->bgcolor); - switch_img_patch(canvas->img, layer->img, x, y); + switch_img_patch(canvas->img, layer->img, layer->x_pos, layer->y_pos); switch_img_free(&layer->cur_img); } @@ -1054,29 +924,27 @@ static void reset_layer(mcu_canvas_t *canvas, mcu_layer_t *layer) static void scale_and_patch(conference_obj_t *conference, mcu_layer_t *layer) { int ret; - int x = 0, y = 0; switch_image_t *IMG = conference->canvas->img, *img = layer->cur_img; - if (layer->geometry.scale) { - int screen_w = 0, screen_h = 0, img_w = 0, img_h = 0; + int img_w = 0, img_h = 0; double screen_aspect = 0, img_aspect = 0; + + img_w = layer->screen_w = IMG->d_w * layer->geometry.scale / SCALE_FACTOR; + img_h = layer->screen_h = IMG->d_h * layer->geometry.scale / SCALE_FACTOR; - img_w = screen_w = IMG->d_w * layer->geometry.scale / SCALE_FACTOR; - img_h = screen_h = IMG->d_h * layer->geometry.scale / SCALE_FACTOR; + layer->x_pos = IMG->d_w * layer->geometry.x / SCALE_FACTOR; + layer->y_pos = IMG->d_h * layer->geometry.y / SCALE_FACTOR; - x = IMG->d_w * layer->geometry.x / SCALE_FACTOR; - y = IMG->d_h * layer->geometry.y / SCALE_FACTOR; - - screen_aspect = (double) screen_w / screen_h; + screen_aspect = (double) layer->screen_w / layer->screen_h; img_aspect = (double) img->d_w / img->d_h; if (screen_aspect > img_aspect) { - img_w = img_aspect * screen_h; - x += (screen_w - img_w) / 2; + img_w = img_aspect * layer->screen_h; + layer->x_pos += (layer->screen_w - img_w) / 2; } else if (screen_aspect < img_aspect) { - img_h = screen_w / img_aspect; - y += (screen_h - img_h) / 2; + img_h = layer->screen_w / img_aspect; + layer->y_pos += (layer->screen_h - img_h) / 2; } @@ -1121,11 +989,15 @@ static void scale_and_patch(conference_obj_t *conference, mcu_layer_t *layer) // reserv the bottom room for text, e.g. caller id // switch_img_set_rect(layer->img, 0, 0, layer->img->d_w, layer->img->d_h - 20); } - switch_img_patch(IMG, layer->img, x, y); + switch_img_patch(IMG, layer->img, layer->x_pos, layer->y_pos); } } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, "insert at %d,%d\n", x, y); - switch_img_patch(IMG, img, x, y); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, "insert at %d,%d\n", 0, 0); + switch_img_patch(IMG, img, 0, 0); + } + + if (layer->banner_img) { + switch_img_patch(IMG, layer->banner_img, layer->x_pos, layer->y_pos + (layer->screen_h - layer->banner_img->d_h)); } } @@ -1165,6 +1037,10 @@ static void detach_video_layer(conference_member_t *member) conf_api_sub_position(member, NULL, "0:0:0"); } + if (layer->txthandle) { + switch_img_txt_handle_destroy(&layer->txthandle); + } + reset_layer(member->conference->canvas, layer); layer->member_id = 0; member->video_layer_id = -1; @@ -1172,13 +1048,74 @@ static void detach_video_layer(conference_member_t *member) switch_mutex_unlock(member->conference->canvas->mutex); } +static void layer_set_banner(mcu_layer_t *layer, const char *text) +{ + switch_yuv_color_t fgcolor, bgcolor; + int font_size = 24; + const char *fg = "#cccccc"; + const char *bg = "#142e55"; + char *parsed = NULL; + switch_event_t *params = NULL; + const char *font_face = "/usr/share/fonts/truetype/freefont/FreeSansOblique.ttf"; + const char *var; + + if (*text == '{') { + if (switch_event_create_brackets((char *)text, '{', '}', ',', ¶ms, &parsed, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS || !parsed) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error!\n"); + } else { + text = parsed; + } + } + + if (params) { + if ((var = switch_event_get_header(params, "fg"))) { + fg = var; + } + + if ((var = switch_event_get_header(params, "bg"))) { + bg = var; + } + + if ((var = switch_event_get_header(params, "font_face"))) { + font_face = var; + } + + if ((var = switch_event_get_header(params, "font_size"))) { + int tmp = atoi(var); + if (tmp >= 5 && tmp <= 50) { + font_size = tmp; + } + } + } + + switch_color_set(&fgcolor, fg); + switch_color_set(&bgcolor, bg); + + switch_img_free(&layer->banner_img); + layer->banner_img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, layer->screen_w, font_size * 2, 1); + + + if (layer->txthandle) { + switch_img_txt_handle_destroy(&layer->txthandle); + } + + switch_img_txt_handle_create(&layer->txthandle, font_face, fg, font_size, 0, NULL); + + reset_image(layer->banner_img, &bgcolor); + switch_img_txt_handle_render(layer->txthandle, layer->banner_img, font_size / 2, font_size / 2, text, NULL, NULL, 0, 0); + + switch_safe_free(parsed); + if (params) switch_event_destroy(¶ms); +} + static switch_status_t attach_video_layer(conference_member_t *member, int idx) { mcu_layer_t *layer = NULL; switch_channel_t *channel = NULL; const char *res_id = NULL; switch_status_t status = SWITCH_STATUS_SUCCESS; - + const char *banner = NULL; + if (!member->session) abort(); switch_mutex_lock(member->conference->canvas->mutex); @@ -1206,6 +1143,10 @@ static switch_status_t attach_video_layer(conference_member_t *member, int idx) } } + if ((banner = switch_channel_get_variable_dup(channel, "video_banner_text", SWITCH_FALSE, -1))) { + layer_set_banner(layer, banner); + } + layer->member_id = member->id; member->video_layer_id = idx; check_used_layers(member->conference); @@ -1239,6 +1180,17 @@ static void init_canvas_layers(conference_obj_t *conference, video_layout_t *vla layer->geometry.floor = vlayout->images[i].floor; layer->idx = i; + + layer->screen_w = conference->canvas->img->d_w * layer->geometry.scale / SCALE_FACTOR; + layer->screen_h = conference->canvas->img->d_h * layer->geometry.scale / SCALE_FACTOR; + + if (layer->screen_w % 2) layer->screen_w++; // round to even + if (layer->screen_h % 2) layer->screen_h++; // round to even + + layer->x_pos = conference->canvas->img->d_w * layer->geometry.x / SCALE_FACTOR; + layer->y_pos = conference->canvas->img->d_h * layer->geometry.y / SCALE_FACTOR; + + if (layer->geometry.floor) { conference->canvas->layout_floor_id = i; } @@ -1570,18 +1522,24 @@ static void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread } } - if (1) { + if (0) { + switch_img_txt_handle_t *txthandle = NULL; switch_yuv_color_t color; - switch_color_set(&color, "#FFFFFF"); - switch_img_draw_text(conference->canvas->img, 10, 10, color, 12, "AVA 123 你好 FreeSWITCH\nFreeSWITCH Rocks!"); - switch_img_draw_text(conference->canvas->img, 10, 40, color, 16, "AVA 123 你好 FreeSWITCH\nFreeSWITCH Rocks!"); - switch_img_draw_text(conference->canvas->img, 10, 80, color, 24, "AVA 123 你好 FreeSWITCH\nFreeSWITCH Rocks!"); - switch_img_draw_text(conference->canvas->img, 10, 160, color, 36, "AVA 123 你好 FreeSWITCH\nFreeSWITCH Rocks!"); - switch_img_draw_text(conference->canvas->img, 10, 300, color, 72, "AVA 123 你好 FreeSWITCH\nFreeSWITCH Rocks!"); - switch_img_fill(conference->canvas->img, 300, 10, 400, 40, color); + switch_img_txt_handle_create(&txthandle, "/usr/share/fonts/truetype/Microsoft/Verdana.ttf", + "#FFFFFF", 24, 0, NULL); + + switch_img_txt_handle_render(txthandle, conference->canvas->img, 10, 10, "W00t this works!", NULL, NULL, 0, 0); + switch_color_set(&color, "#FF0000"); - switch_img_draw_text(conference->canvas->img, 300, 10, color, 32, "FreeSWITCH"); + switch_img_fill(conference->canvas->img, 300, 10, 400, 40, color); + + switch_img_txt_handle_render(txthandle, conference->canvas->img, 300, 22, "W00t this works!", NULL, NULL, 0, 0); + + switch_img_txt_handle_destroy(&txthandle); + + + //switch_img_draw_text(conference->canvas->img, 300, 10, color, 32, "FreeSWITCH"); } if (used) { @@ -1654,6 +1612,11 @@ static void *SWITCH_THREAD_FUNC conference_video_muxing_thread_run(switch_thread switch_img_free(&layer->cur_img); switch_img_free(&layer->img); + switch_img_free(&layer->banner_img); + + if (layer->txthandle) { + switch_img_txt_handle_destroy(&layer->txthandle); + } } for (i = 0; write_codecs[i] && switch_core_codec_ready(&write_codecs[i]->codec) && i < MAX_MUX_CODECS; i++) { diff --git a/src/switch_core_video.c b/src/switch_core_video.c index 8a1396ccac..e089d7ecff 100644 --- a/src/switch_core_video.c +++ b/src/switch_core_video.c @@ -22,6 +22,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Anthony Minessale II * * * switch_core_video.c -- Core Video @@ -29,6 +30,7 @@ */ #include +#include SWITCH_DECLARE(switch_image_t *)switch_img_alloc(switch_image_t *img, @@ -247,7 +249,7 @@ SWITCH_DECLARE(void) switch_img_add_text(void *buffer, int w, int x, int y, char } } -SWITCH_DECLARE(void) switch_color_set(switch_yuv_color_t *color, char *color_str) +SWITCH_DECLARE(void) switch_color_set(switch_yuv_color_t *color, const char *color_str) { uint8_t y = 134; uint8_t u = 128; @@ -256,8 +258,13 @@ SWITCH_DECLARE(void) switch_color_set(switch_yuv_color_t *color, char *color_str if (color_str != NULL && strlen(color_str) == 7) { uint8_t red, green, blue; char str[7]; + int i; + color_str++; strncpy(str, color_str, 6); + for(i = 0; i < 6; i++) { + str[i] = switch_toupper(str[i]); + } red = (str[0] >= 'A' ? (str[0] - 'A' + 10) * 16 : (str[0] - '0') * 16) + (str[1] >= 'A' ? (str[1] - 'A' + 10) : (str[0] - '0')); green = (str[2] >= 'A' ? (str[2] - 'A' + 10) * 16 : (str[2] - '0') * 16) + (str[3] >= 'A' ? (str[3] - 'A' + 10) : (str[0] - '0')); blue = (str[4] >= 'A' ? (str[4] - 'A' + 10) * 16 : (str[4] - '0') * 16) + (str[5] >= 'A' ? (str[5] - 'A' + 10) : (str[0] - '0')); @@ -273,6 +280,194 @@ SWITCH_DECLARE(void) switch_color_set(switch_yuv_color_t *color, char *color_str color->v = v; } +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H + +struct switch_img_txt_handle_s { + FT_Library library; + FT_Face face; + char *font_family; + double angle; + uint16_t font_size; + switch_yuv_color_t color; + switch_image_t *img; + switch_memory_pool_t *pool; + int free_pool; +}; + + +SWITCH_DECLARE(switch_status_t) switch_img_txt_handle_create(switch_img_txt_handle_t **handleP, const char *font_family, + const char *font_color, 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 (FT_Init_FreeType(&new_handle->library)) { + return SWITCH_STATUS_FALSE; + } + + new_handle->pool = pool; + new_handle->free_pool = free_pool; + new_handle->font_family = switch_core_strdup(new_handle->pool, font_family); + new_handle->font_size = font_size; + new_handle->angle = angle; + + switch_color_set(&new_handle->color, font_color); + + *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 = *handleP; + switch_memory_pool_t *pool; + + *handleP = NULL; + + if (old_handle->library) { + FT_Done_FreeType(old_handle->library); + old_handle->library = NULL; + } + + pool = old_handle->pool; + + if (old_handle->free_pool) { + switch_core_destroy_memory_pool(&pool); + pool = NULL; + old_handle = NULL; + } + +} + +static void draw_bitmap(switch_image_t *img, FT_Bitmap* bitmap, FT_Int x, FT_Int y, switch_yuv_color_t color) +{ + FT_Int i, j, p, q; + FT_Int x_max = x + bitmap->width; + FT_Int y_max = y + bitmap->rows; + + 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: + 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++ ) { + if ( i < 0 || j < 0 || i >= img->d_w || j >= img->d_h) continue; + + if (bitmap->buffer[q * bitmap->width + p] > 128) { + switch_img_draw_pixel(img, i, j, color); + } + } + } +} + + +SWITCH_DECLARE(switch_status_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, uint16_t font_size, double angle) +{ + 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; + + if (zstr(text)) return SWITCH_STATUS_FALSE; + + 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(&handle->color, font_color); + } + + 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) {printf("WTF %s %d\n", font_family, __LINE__); return SWITCH_STATUS_FALSE;} + + /* use 50pt at 100dpi */ + error = FT_Set_Char_Size(face, 64 * font_size, 0, 96, 96); /* set character size */ + if (error) {printf("WTF %d\n", __LINE__); return SWITCH_STATUS_FALSE;} + + slot = face->glyph; + + /* 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 * 64; + pen.y = (target_height - y) * 64; + + while(*(text + index)) { + ch = switch_u8_get_char((char *)text, &index); + + if (ch == '\n') { + pen.x = x * 64; + pen.y -= (font_size + font_size / 4) * 64; + 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; + + /* now, draw to our target surface (convert position) */ + draw_bitmap(img, &slot->bitmap, slot->bitmap_left, target_height - slot->bitmap_top + font_size, handle->color); + + /* increment pen position */ + pen.x += slot->advance.x; + pen.y += slot->advance.y; + } + + FT_Done_Face(face); + + return SWITCH_STATUS_SUCCESS; +} + + + + /* For Emacs: * Local Variables: * mode:c diff --git a/src/switch_utf8.c b/src/switch_utf8.c new file mode 100644 index 0000000000..eaa59a5c9d --- /dev/null +++ b/src/switch_utf8.c @@ -0,0 +1,516 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2014, Anthony Minessale II + * + * 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 + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Anthony Minessale II + * + * + * switch_utf8.c UTf8 + * + */ + +#include "switch_utf8.h" + +/* + Basic UTF-8 manipulation routines + by Jeff Bezanson + placed in the public domain Fall 2005 + + This code is designed to provide the utilities you need to manipulate + UTF-8 as an internal string encoding. These functions do not perform the + error checking normally needed when handling UTF-8 data, so if you happen + to be from the Unicode Consortium you will want to flay me alive. + I do this because error checking can be performed at the boundaries (I/O), + with these routines reserved for higher performance on data known to be + valid. +*/ + +static const uint32_t offsetsFromUTF8[6] = { + 0x00000000UL, 0x00003080UL, 0x000E2080UL, + 0x03C82080UL, 0xFA082080UL, 0x82082080UL +}; + +static const char trailingBytesForUTF8[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 +}; + +/* returns length of next utf-8 sequence */ +SWITCH_DECLARE(int) switch_u8_seqlen(char *s) +{ + return trailingBytesForUTF8[(unsigned int)(unsigned char)s[0]] + 1; +} + +/* conversions without error checking + only works for valid UTF-8, i.e. no 5- or 6-byte sequences + srcsz = source size in bytes, or -1 if 0-terminated + sz = dest size in # of wide characters + + returns # characters converted + dest will always be L'\0'-terminated, even if there isn't enough room + for all the characters. + if sz = srcsz+1 (i.e. 4*srcsz+4 bytes), there will always be enough space. +*/ +SWITCH_DECLARE(int) switch_u8_toucs(uint32_t *dest, int sz, char *src, int srcsz) +{ + uint32_t ch; + char *src_end = src + srcsz; + int nb; + int i=0; + + while (i < sz-1) { + nb = trailingBytesForUTF8[(unsigned char)*src]; + if (srcsz == -1) { + if (*src == 0) + goto done_toucs; + } + else { + if (src + nb >= src_end) + goto done_toucs; + } + ch = 0; + switch (nb) { + /* these fall through deliberately */ + case 3: ch += (unsigned char)*src++; ch <<= 6; + case 2: ch += (unsigned char)*src++; ch <<= 6; + case 1: ch += (unsigned char)*src++; ch <<= 6; + case 0: ch += (unsigned char)*src++; + } + ch -= offsetsFromUTF8[nb]; + dest[i++] = ch; + } + done_toucs: + dest[i] = 0; + return i; +} + +/* srcsz = number of source characters, or -1 if 0-terminated + sz = size of dest buffer in bytes + + returns # characters converted + dest will only be '\0'-terminated if there is enough space. this is + for consistency; imagine there are 2 bytes of space left, but the next + character requires 3 bytes. in this case we could NUL-terminate, but in + general we can't when there's insufficient space. therefore this function + only NUL-terminates if all the characters fit, and there's space for + the NUL as well. + the destination string will never be bigger than the source string. +*/ +SWITCH_DECLARE(int) switch_u8_toutf8(char *dest, int sz, uint32_t *src, int srcsz) +{ + uint32_t ch; + int i = 0; + char *dest_end = dest + sz; + + while (srcsz<0 ? src[i]!=0 : i < srcsz) { + ch = src[i]; + if (ch < 0x80) { + if (dest >= dest_end) + return i; + *dest++ = (char)ch; + } + else if (ch < 0x800) { + if (dest >= dest_end-1) + return i; + *dest++ = (ch>>6) | 0xC0; + *dest++ = (ch & 0x3F) | 0x80; + } + else if (ch < 0x10000) { + if (dest >= dest_end-2) + return i; + *dest++ = (ch>>12) | 0xE0; + *dest++ = ((ch>>6) & 0x3F) | 0x80; + *dest++ = (ch & 0x3F) | 0x80; + } + else if (ch < 0x110000) { + if (dest >= dest_end-3) + return i; + *dest++ = (ch>>18) | 0xF0; + *dest++ = ((ch>>12) & 0x3F) | 0x80; + *dest++ = ((ch>>6) & 0x3F) | 0x80; + *dest++ = (ch & 0x3F) | 0x80; + } + i++; + } + if (dest < dest_end) + *dest = '\0'; + return i; +} + +SWITCH_DECLARE(int) switch_u8_wc_toutf8(char *dest, uint32_t ch) +{ + if (ch < 0x80) { + dest[0] = (char)ch; + return 1; + } + if (ch < 0x800) { + dest[0] = (ch>>6) | 0xC0; + dest[1] = (ch & 0x3F) | 0x80; + return 2; + } + if (ch < 0x10000) { + dest[0] = (ch>>12) | 0xE0; + dest[1] = ((ch>>6) & 0x3F) | 0x80; + dest[2] = (ch & 0x3F) | 0x80; + return 3; + } + if (ch < 0x110000) { + dest[0] = (ch>>18) | 0xF0; + dest[1] = ((ch>>12) & 0x3F) | 0x80; + dest[2] = ((ch>>6) & 0x3F) | 0x80; + dest[3] = (ch & 0x3F) | 0x80; + return 4; + } + return 0; +} + +/* charnum => byte offset */ +SWITCH_DECLARE(int) switch_u8_offset(char *str, int charnum) +{ + int offs=0; + + while (charnum > 0 && str[offs]) { + (void)(isutf(str[++offs]) || isutf(str[++offs]) || + isutf(str[++offs]) || ++offs); + charnum--; + } + return offs; +} + +/* byte offset => charnum */ +SWITCH_DECLARE(int) switch_u8_charnum(char *s, int offset) +{ + int charnum = 0, offs=0; + + while (offs < offset && s[offs]) { + (void)(isutf(s[++offs]) || isutf(s[++offs]) || + isutf(s[++offs]) || ++offs); + charnum++; + } + return charnum; +} + +/* number of characters */ +SWITCH_DECLARE(int) switch_u8_strlen(char *s) +{ + int count = 0; + int i = 0; + + while (switch_u8_nextchar(s, &i) != 0) + count++; + + return count; +} + +/* reads the next utf-8 sequence out of a string, updating an index */ +SWITCH_DECLARE(uint32_t) switch_u8_nextchar(char *s, int *i) +{ + uint32_t ch = 0; + int sz = 0; + + do { + ch <<= 6; + ch += (unsigned char)s[(*i)++]; + sz++; + } while (s[*i] && !isutf(s[*i])); + ch -= offsetsFromUTF8[sz-1]; + + return ch; +} + +SWITCH_DECLARE(void) switch_u8_inc(char *s, int *i) +{ + (void)(isutf(s[++(*i)]) || isutf(s[++(*i)]) || + isutf(s[++(*i)]) || ++(*i)); +} + +SWITCH_DECLARE(void) switch_u8_dec(char *s, int *i) +{ + (void)(isutf(s[--(*i)]) || isutf(s[--(*i)]) || + isutf(s[--(*i)]) || --(*i)); +} + +SWITCH_DECLARE(int) octal_digit(char c) +{ + return (c >= '0' && c <= '7'); +} + +SWITCH_DECLARE(int) hex_digit(char c) +{ + return ((c >= '0' && c <= '9') || + (c >= 'A' && c <= 'F') || + (c >= 'a' && c <= 'f')); +} + +/* assumes that src points to the character after a backslash + returns number of input characters processed */ +SWITCH_DECLARE(int) switch_u8_read_escape_sequence(char *str, uint32_t *dest) +{ + uint32_t ch; + char digs[9]="\0\0\0\0\0\0\0\0"; + int dno=0, i=1; + + ch = (uint32_t)str[0]; /* take literal character */ + if (str[0] == 'n') + ch = L'\n'; + else if (str[0] == 't') + ch = L'\t'; + else if (str[0] == 'r') + ch = L'\r'; + else if (str[0] == 'b') + ch = L'\b'; + else if (str[0] == 'f') + ch = L'\f'; + else if (str[0] == 'v') + ch = L'\v'; + else if (str[0] == 'a') + ch = L'\a'; + else if (octal_digit(str[0])) { + i = 0; + do { + digs[dno++] = str[i++]; + } while (octal_digit(str[i]) && dno < 3); + ch = strtol(digs, NULL, 8); + } + else if (str[0] == 'x') { + while (hex_digit(str[i]) && dno < 2) { + digs[dno++] = str[i++]; + } + if (dno > 0) + ch = strtol(digs, NULL, 16); + } + else if (str[0] == 'u') { + while (hex_digit(str[i]) && dno < 4) { + digs[dno++] = str[i++]; + } + if (dno > 0) + ch = strtol(digs, NULL, 16); + } + else if (str[0] == 'U') { + while (hex_digit(str[i]) && dno < 8) { + digs[dno++] = str[i++]; + } + if (dno > 0) + ch = strtol(digs, NULL, 16); + } + *dest = ch; + + return i; +} + +/* convert a string with literal \uxxxx or \Uxxxxxxxx characters to UTF-8 + example: u8_unescape(mybuf, 256, "hello\\u220e") + note the double backslash is needed if called on a C string literal */ +SWITCH_DECLARE(int) switch_u8_unescape(char *buf, int sz, char *src) +{ + int c=0, amt; + uint32_t ch; + char temp[4]; + + while (*src && c < sz) { + if (*src == '\\') { + src++; + amt = switch_u8_read_escape_sequence(src, &ch); + } + else { + ch = (uint32_t)*src; + amt = 1; + } + src += amt; + amt = switch_u8_wc_toutf8(temp, ch); + if (amt > sz-c) + break; + memcpy(&buf[c], temp, amt); + c += amt; + } + if (c < sz) + buf[c] = '\0'; + return c; +} + +SWITCH_DECLARE(int) switch_u8_escape_wchar(char *buf, int sz, uint32_t ch) +{ + if (ch == L'\n') + return snprintf(buf, sz, "\\n"); + else if (ch == L'\t') + return snprintf(buf, sz, "\\t"); + else if (ch == L'\r') + return snprintf(buf, sz, "\\r"); + else if (ch == L'\b') + return snprintf(buf, sz, "\\b"); + else if (ch == L'\f') + return snprintf(buf, sz, "\\f"); + else if (ch == L'\v') + return snprintf(buf, sz, "\\v"); + else if (ch == L'\a') + return snprintf(buf, sz, "\\a"); + else if (ch == L'\\') + return snprintf(buf, sz, "\\\\"); + else if (ch < 32 || ch == 0x7f) + return snprintf(buf, sz, "\\x%hhX", (unsigned char)ch); + else if (ch > 0xFFFF) + return snprintf(buf, sz, "\\U%.8X", (uint32_t)ch); + else if (ch >= 0x80 && ch <= 0xFFFF) + return snprintf(buf, sz, "\\u%.4hX", (unsigned short)ch); + + return snprintf(buf, sz, "%c", (char)ch); +} + +SWITCH_DECLARE(int) switch_u8_escape(char *buf, int sz, char *src, int escape_quotes) +{ + int c=0, i=0, amt; + + while (src[i] && c < sz) { + if (escape_quotes && src[i] == '"') { + amt = snprintf(buf, sz - c, "\\\""); + i++; + } + else { + amt = switch_u8_escape_wchar(buf, sz - c, switch_u8_nextchar(src, &i)); + } + c += amt; + buf += amt; + } + if (c < sz) + *buf = '\0'; + return c; +} + +SWITCH_DECLARE(char *) switch_u8_strchr(char *s, uint32_t ch, int *charn) +{ + int i = 0, lasti=0; + uint32_t c; + + *charn = 0; + while (s[i]) { + c = switch_u8_nextchar(s, &i); + if (c == ch) { + return &s[lasti]; + } + lasti = i; + (*charn)++; + } + return NULL; +} + +SWITCH_DECLARE(char *) switch_u8_memchr(char *s, uint32_t ch, size_t sz, int *charn) +{ + int i = 0, lasti=0; + uint32_t c; + int csz; + + *charn = 0; + while (i < sz) { + c = csz = 0; + do { + c <<= 6; + c += (unsigned char)s[i++]; + csz++; + } while (i < sz && !isutf(s[i])); + c -= offsetsFromUTF8[csz-1]; + + if (c == ch) { + return &s[lasti]; + } + lasti = i; + (*charn)++; + } + return NULL; +} + +SWITCH_DECLARE(int) switch_u8_is_locale_utf8(char *locale) +{ + /* this code based on libutf8 */ + const char* cp = locale; + + for (; *cp != '\0' && *cp != '@' && *cp != '+' && *cp != ','; cp++) { + if (*cp == '.') { + const char* encoding = ++cp; + for (; *cp != '\0' && *cp != '@' && *cp != '+' && *cp != ','; cp++) + ; + if ((cp-encoding == 5 && !strncmp(encoding, "UTF-8", 5)) + || (cp-encoding == 4 && !strncmp(encoding, "utf8", 4))) + return 1; /* it's UTF-8 */ + break; + } + } + return 0; +} + +SWITCH_DECLARE(int) switch_u8_vprintf(char *fmt, va_list ap) +{ + int cnt, sz=0; + char *buf; + uint32_t *wcs; + + sz = 512; + buf = (char*)alloca(sz); + try_print: + cnt = vsnprintf(buf, sz, fmt, ap); + if (cnt >= sz) { + buf = (char*)alloca(cnt - sz + 1); + sz = cnt + 1; + goto try_print; + } + wcs = (uint32_t*)alloca((cnt+1) * sizeof(uint32_t)); + cnt = switch_u8_toucs(wcs, cnt+1, buf, cnt); + printf("%ls", (wchar_t*)wcs); + return cnt; +} + +SWITCH_DECLARE(int) switch_u8_printf(char *fmt, ...) +{ + int cnt; + va_list args; + + va_start(args, fmt); + + cnt = switch_u8_vprintf(fmt, args); + + va_end(args); + return cnt; +} + + +/* reads the next utf-8 sequence out of a string, updating an index */ +SWITCH_DECLARE(uint32_t) switch_u8_get_char(char *s, int *i) +{ + u_int32_t ch = 0; + int sz = 0; + + do { + ch <<= 6; + ch += (unsigned char)s[(*i)++]; + sz++; + } while (s[*i] && !isutf(s[*i])); + + ch -= offsetsFromUTF8[sz-1]; + + return ch; +}