RTS API Documentation  1.10.11
switch_core_video.c
Go to the documentation of this file.
1 /*
2  * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3  * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
4  *
5  * Version: MPL 1.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18  *
19  * The Initial Developer of the Original Code is
20  * Seven Du <dujinfang@gmail.com>
21  * Portions created by the Initial Developer are Copyright (C)
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  * Anthony Minessale II <anthm@freeswitch.org>
26  *
27  *
28  * switch_core_video.c -- Core Video
29  *
30  */
31 
32 #ifdef SWITCH_HAVE_VPX
33 #include "vpx/vpx_image.h"
34 #if VPX_IMAGE_ABI_VERSION != (4)
35 #error VPX_IMAGE_ABI_VERSION is not (4)
36 #endif
37 #endif
38 
39 #include <switch.h>
40 #include <switch_utf8.h>
41 
42 #ifdef SWITCH_HAVE_YUV
43 #include <libyuv.h>
44 #endif
45 
46 // #define HAVE_LIBGD
47 #ifdef HAVE_LIBGD
48 #include <gd.h>
49 #endif
50 
51 #define STB_IMAGE_IMPLEMENTATION
52 #include "../libs/stb/stb_image.h"
53 
54 #define STB_IMAGE_WRITE_IMPLEMENTATION
55 #include "../libs/stb/stb_image_write.h"
56 
57 #ifdef SWITCH_HAVE_YUV
58 static inline void switch_img_get_yuv_pixel(switch_image_t *img, switch_yuv_color_t *yuv, int x, int y);
59 #endif
60 
61 static inline void switch_img_get_rgb_pixel(switch_image_t *img, switch_rgb_color_t *rgb, int x, int y);
62 
63 
64 /*!\brief Convert RGB color to YUV
65 *
66 * \param[in] rgb RGB color pointer
67 * \param[out] yuv YUV color pointer
68 */
69 #ifdef SWITCH_HAVE_YUV
70 static inline void switch_color_rgb2yuv(switch_rgb_color_t *rgb, switch_yuv_color_t *yuv);
71 #endif
72 
73 /*!\brief Convert YUV color to RGB
74 *
75 * \param[in] yuv YUV color pointer
76 * \param[out] rgb RGB color pointer
77 */
78 #ifdef SWITCH_HAVE_YUV
79 static inline void switch_color_yuv2rgb(switch_yuv_color_t *yuv, switch_rgb_color_t *rgb);
80 #endif
81 
82 /*!\brief compute distance between two colors
83 *
84 * \param[in] c1 RGB color1
85 * \param[in] c2 RGB color2
86 */
88 
89 /*!\brief compute distance between a color and a list of colors
90 *
91 * \param[in] c1 RGB color1
92 * \param[in] clist RGB color list
93 * \param[in] count number of colors in list
94 * \param[in] threshold hint of target threshold to stop processing list
95 */
96 static inline int switch_color_distance_multi(switch_rgb_color_t *c1, switch_rgb_color_t *clist, int count, uint32_t *thresholds);
97 
98 /*!\brief Draw a pixel on an image
99 *
100 * \param[in] img Image descriptor
101 * \param[in] x leftmost pos
102 * \param[in] y topmost pos
103 * \param[in] color RGB color
104 */
105 static inline void switch_img_draw_pixel(switch_image_t *img, int x, int y, switch_rgb_color_t *color);
106 
107 
108 struct pos_el {
110  const char *name;
111 };
112 
113 
114 static struct pos_el POS_TABLE[] = {
115  {POS_LEFT_TOP, "left-top"},
116  {POS_LEFT_MID, "left-mid"},
117  {POS_LEFT_BOT, "left-bot"},
118  {POS_CENTER_TOP, "center-top"},
119  {POS_CENTER_MID, "center-mid"},
120  {POS_CENTER_BOT, "center-bot"},
121  {POS_RIGHT_TOP, "right-top"},
122  {POS_RIGHT_MID, "right-mid"},
123  {POS_RIGHT_BOT, "right-bot"},
124  {POS_NONE, "none"},
125  {POS_NONE, NULL}
126 };
127 
128 
130 {
132  int i;
133 
134  switch_assert(name);
135 
136  for(i = 0; POS_TABLE[i].name; i++) {
137  if (!strcasecmp(POS_TABLE[i].name, name)) {
138  r = POS_TABLE[i].pos;
139  break;
140  }
141  }
142 
143  return r;
144 }
145 
146 
147 struct fit_el {
149  const char *name;
150 };
151 
152 
153 static struct fit_el IMG_FIT_TABLE[] = {
154  {SWITCH_FIT_SIZE, "fit-size"},
155  {SWITCH_FIT_SCALE, "fit-scale"},
156  {SWITCH_FIT_SIZE_AND_SCALE, "fit-size-and-scale"},
157  {SWITCH_FIT_NECESSARY, "fit-necessary"},
158  {SWITCH_FIT_NONE, NULL}
159 };
160 
161 
163 {
165  int i;
166 
167  switch_assert(name);
168 
169  for(i = 0; IMG_FIT_TABLE[i].name; i++) {
170  if (!strcasecmp(IMG_FIT_TABLE[i].name, name)) {
171  r = IMG_FIT_TABLE[i].fit;
172  break;
173  }
174  }
175 
176  return r;
177 }
178 
180 {
181 #ifdef SWITCH_HAVE_VPX
182 #ifdef SWITCH_HAVE_YUV
183  return SWITCH_TRUE;
184 #else
185  return SWITCH_FALSE;
186 #endif
187 #else
188  return SWITCH_FALSE;
189 #endif
190 }
191 
194  unsigned int d_w,
195  unsigned int d_h,
196  unsigned int align)
197 {
198 #ifdef SWITCH_HAVE_VPX
199  switch_image_t *r = NULL;
200 #ifdef HAVE_LIBGD
201  if (fmt == SWITCH_IMG_FMT_GD) {
202  gdImagePtr gd = gdImageCreateTrueColor(d_w, d_h);
203 
204  if (!gd) return NULL;
205 
206  switch_img_free(&img);
207  img = (switch_image_t *)vpx_img_alloc(NULL, SWITCH_IMG_FMT_ARGB, 1, 1, 1);
208 
209  if (!img) {
210  gdImageDestroy(gd);
211  return NULL;
212  }
213 
214  img->user_priv = gd;
215  img->d_w = d_w;
216  img->d_h = d_h;
217  img->fmt = SWITCH_IMG_FMT_GD;
218  return img;
219  }
220 #endif
221 
222  switch_assert(d_w > 0);
223  switch_assert(d_h > 0);
224  r = (switch_image_t *)vpx_img_alloc((vpx_image_t *)img, (vpx_img_fmt_t)fmt, d_w, d_h, align);
225  switch_assert(r);
226  switch_assert(r->d_w == d_w);
227  switch_assert(r->d_h == d_h);
228 
229  return r;
230 #else
231  return NULL;
232 #endif
233 }
234 
237  unsigned int d_w,
238  unsigned int d_h,
239  unsigned int align,
240  unsigned char *img_data)
241 {
242 #ifdef SWITCH_HAVE_VPX
243  return (switch_image_t *)vpx_img_wrap((vpx_image_t *)img, (vpx_img_fmt_t)fmt, d_w, d_h, align, img_data);
244 #else
245  return NULL;
246 #endif
247 }
248 
250  unsigned int x,
251  unsigned int y,
252  unsigned int w,
253  unsigned int h)
254 {
255 #ifdef SWITCH_HAVE_VPX
256  return vpx_img_set_rect((vpx_image_t *)img, x, y, w, h);
257 #else
258  return 0;
259 #endif
260 }
261 
263 {
264 #ifdef SWITCH_HAVE_YUV
265  switch_image_t *tmp_img;
266 
267  switch_assert(img);
268 
269 
270  if ((*img)->fmt != SWITCH_IMG_FMT_I420) return;
271 
272  if (mode == SRM_90 || mode == SRM_270) {
273  tmp_img = switch_img_alloc(NULL, (*img)->fmt, (*img)->d_h, (*img)->d_w, 1);
274  } else {
275  tmp_img = switch_img_alloc(NULL, (*img)->fmt, (*img)->d_w, (*img)->d_h, 1);
276  }
277 
278  switch_assert(tmp_img);
279 
280  I420Rotate((*img)->planes[SWITCH_PLANE_Y], (*img)->stride[SWITCH_PLANE_Y],
281  (*img)->planes[SWITCH_PLANE_U], (*img)->stride[SWITCH_PLANE_U],
282  (*img)->planes[SWITCH_PLANE_V], (*img)->stride[SWITCH_PLANE_V],
283  tmp_img->planes[SWITCH_PLANE_Y], tmp_img->stride[SWITCH_PLANE_Y],
284  tmp_img->planes[SWITCH_PLANE_U], tmp_img->stride[SWITCH_PLANE_U],
285  tmp_img->planes[SWITCH_PLANE_V], tmp_img->stride[SWITCH_PLANE_V],
286  (*img)->d_w, (*img)->d_h, (int)mode);
287 
288 
289  switch_img_free(img);
290  *img = tmp_img;
291 
292 #endif
293 }
294 
296 {
297 #ifdef SWITCH_HAVE_VPX
298  if (img && *img) {
299  if ((*img)->fmt == SWITCH_IMG_FMT_GD) {
300 #ifdef HAVE_LIBGD
301  gdImageDestroy((gdImagePtr)(*img)->user_priv);
302 #endif
303  } else {
304  if ((int)(intptr_t)(*img)->user_priv != 1) {
305  switch_safe_free((*img)->user_priv);
306  }
307  }
308  switch_assert((*img)->fmt <= SWITCH_IMG_FMT_I44016);
309  switch_assert((*img)->d_w <= 7860 && (*img)->d_w > 0);
310  switch_assert((*img)->d_h <= 4320 && (*img)->d_h > 0);
311  vpx_img_free((vpx_image_t *)*img);
312  *img = NULL;
313  }
314 #endif
315 }
316 
317 #ifndef MIN
318 #define MIN(a,b) ((a) < (b) ? (a) : (b))
319 #endif
320 
321 #ifndef MAX
322 #define MAX(a,b) ((a) > (b) ? (a) : (b))
323 #endif
324 
325 #ifdef SWITCH_HAVE_YUV
326 static void switch_img_patch_rgb_noalpha(switch_image_t *IMG, switch_image_t *img, int x, int y)
327 {
328  int i;
329 
330  if (img->fmt == SWITCH_IMG_FMT_ARGB && IMG->fmt == SWITCH_IMG_FMT_ARGB) {
331  int max_w = MIN(img->d_w, IMG->d_w - abs(x));
332  int max_h = MIN(img->d_h, IMG->d_h - abs(y));
333  int j;
334  uint8_t alpha;
335  switch_rgb_color_t *rgb, *RGB;
336 
337  for (i = 0; i < max_h; i++) {
338  for (j = 0; j < max_w; j++) {
339  rgb = (switch_rgb_color_t *)(img->planes[SWITCH_PLANE_PACKED] + i * img->stride[SWITCH_PLANE_PACKED] + j * 4);
340  RGB = (switch_rgb_color_t *)(IMG->planes[SWITCH_PLANE_PACKED] + (y + i) * IMG->stride[SWITCH_PLANE_PACKED] + (x + j) * 4);
341 
342  alpha = rgb->a;
343 
344  if (RGB->a == 0) {
345  *RGB = *rgb;
346  continue;
347  }
348 
349  if (alpha == 255) {
350  *RGB = *rgb;
351  continue;
352  }
353 
354  if (alpha > 0) {
355  uint8_t delta1, delta2, delta;
356 
357  delta1 = 255 - RGB->a;
358  delta2 = 255 - rgb->a;
359  delta = (delta1 * delta2) >> 8;
360  RGB->r = ((RGB->r * RGB->a) + (rgb->r * rgb->a)) / (RGB->a + rgb->a);
361  RGB->g = ((RGB->g * RGB->a) + (rgb->g * rgb->a)) / (RGB->a + rgb->a);
362  RGB->b = ((RGB->b * RGB->a) + (rgb->b * rgb->a)) / (RGB->a + rgb->a);
363  RGB->a = 255 - delta;
364  }
365  }
366  }
367  }
368 }
369 #endif
370 
372 {
373 #ifdef SWITCH_HAVE_YUV
374  if (img->fmt != SWITCH_IMG_FMT_ARGB) {
375  return;
376  }
377 
378  if (img->user_priv) return;
379 
380  img->user_priv = (void *)(intptr_t)1;
381 
382  ARGBAttenuate(img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED],
383  img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED], img->d_w, img->d_h);
384 #else
385  return;
386 #endif
387 
388 }
389 
391 {
392 #ifdef SWITCH_HAVE_YUV
393  int i;
394 
395  if (noalpha) {
396  switch_img_patch_rgb_noalpha(IMG, img, x, y);
397  return;
398  }
399 
400  if (img->fmt == SWITCH_IMG_FMT_ARGB && IMG->fmt == SWITCH_IMG_FMT_ARGB) {
401  uint8_t* src_argb0 = img->planes[SWITCH_PLANE_PACKED];
402  int src_stride_argb0 = img->stride[SWITCH_PLANE_PACKED];
403  uint8_t* src_argb1 = IMG->planes[SWITCH_PLANE_PACKED];
404  int src_stride_argb1 = IMG->stride[SWITCH_PLANE_PACKED];
405  uint8_t* dst_argb = IMG->planes[SWITCH_PLANE_PACKED];
406  int dst_stride_argb = IMG->stride[SWITCH_PLANE_PACKED];
407  int width = MIN(img->d_w, IMG->d_w - abs(x));
408  int height = MIN(img->d_h, IMG->d_h - abs(y));
409  void (*ARGBBlendRow)(const uint8_t* src_argb, const uint8_t* src_argb1, uint8_t* dst_argb, int width) = GetARGBBlend();
410 
411  // switch_img_attenuate(img);
412 
413  // Coalesce rows. we have same size images, treat as a single row
414  if (src_stride_argb0 == width * 4 &&
415  src_stride_argb1 == width * 4 &&
416  x == 0 && y == 0) {
417  width *= height;
418  height = 1;
419  src_stride_argb0 = src_stride_argb1 = dst_stride_argb = 0;
420  }
421 
422  if (y) {
423  src_argb1 += (y * IMG->d_w * 4);
424  dst_argb += (y * IMG->d_w * 4);
425  }
426  if (x) {
427  src_argb1 += (x * 4);
428  dst_argb += (x * 4);
429  }
430 
431  for (i = 0; i < height; ++i) {
432  ARGBBlendRow(src_argb0, src_argb1, dst_argb, width);
433  src_argb0 += src_stride_argb0;
434  src_argb1 += src_stride_argb1;
435  dst_argb += dst_stride_argb;
436  }
437  }
438 #endif
439 }
440 
442 {
443  int i, len, max_h;
444  int xoff = 0, yoff = 0;
445 
446  if (img->fmt == SWITCH_IMG_FMT_ARGB && IMG->fmt == SWITCH_IMG_FMT_ARGB) {
447  switch_img_patch_rgb(IMG, img, x, y, SWITCH_FALSE);
448  return;
449  }
450 
452 
453  if (img->fmt == SWITCH_IMG_FMT_ARGB) {
454  int max_w = MIN(img->d_w, IMG->d_w - abs(x));
455  int max_h = MIN(img->d_h, IMG->d_h - abs(y));
456  int j;
457  uint8_t alpha;
458  switch_rgb_color_t *rgb;
459 
460  for (i = 0; i < max_h; i++) {
461  for (j = 0; j < max_w; j++) {
462  rgb = (switch_rgb_color_t *)(img->planes[SWITCH_PLANE_PACKED] + i * img->stride[SWITCH_PLANE_PACKED] + j * 4);
463  alpha = rgb->a;
464 
465  if (alpha == 255) {
466  switch_img_draw_pixel(IMG, x + j, y + i, rgb);
467  } else if (alpha != 0) {
468  switch_rgb_color_t RGB = { 0 };
469 
470  switch_img_get_rgb_pixel(IMG, &RGB, x + j, y + i);
471  RGB.a = 255;
472  RGB.r = ((RGB.r * (255 - alpha)) >> 8) + ((rgb->r * alpha) >> 8);
473  RGB.g = ((RGB.g * (255 - alpha)) >> 8) + ((rgb->g * alpha) >> 8);
474  RGB.b = ((RGB.b * (255 - alpha)) >> 8) + ((rgb->b * alpha) >> 8);
475 
476  switch_img_draw_pixel(IMG, x + j, y + i, &RGB);
477  }
478  }
479  }
480 
481  return;
482 
483 #ifdef HAVE_LIBGD
484  } else if (img->fmt == SWITCH_IMG_FMT_GD) {
485  gdImagePtr gd = (gdImagePtr)img->user_priv;
486  switch_rgb_color_t rgb_color;
487  int pixel;
488  int i, j;
489 
490  switch_assert(gd);
491 
492  if (!gd->trueColor) {
493  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "GD is experimental, only true color image is supported\n");
494  return;
495  }
496  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "truecolor: %d alpha: %d, transparent? %d\n", gd->trueColor, gd->saveAlphaFlag, gd->transparent);
497 
498  for(i = 0; i < img->d_h; i++) {
499  for(j = 0; j < img->d_w; j++) {
500  pixel = gd->tpixels[i][j];
501  rgb_color.a = 255; // TODO: handle transparent
502  rgb_color.r = gdTrueColorGetRed(pixel);
503  rgb_color.g = gdTrueColorGetGreen(pixel);
504  rgb_color.b = gdTrueColorGetBlue(pixel);
505  switch_img_draw_pixel(IMG, x + j, y + i, &rgb_color);
506  }
507  }
508 
509  return;
510 #endif
511 
512  }
513 
514  if (x < 0) {
515  xoff = -x;
516  x = 0;
517  }
518 
519  if (y < 0) {
520  yoff = -y;
521  y = 0;
522  }
523 
524  max_h = MIN(y + img->d_h - yoff, IMG->d_h);
525  len = MIN(img->d_w - xoff, IMG->d_w - x);
526 
527 
528  if (x & 0x1) { x++; len--; }
529  if (y & 0x1) y++;
530  if (len <= 0) return;
531 
532  for (i = y; i < max_h; i++) {
533  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);
534  }
535 
536  if ((len & 1) && (x + len) < img->d_w - 1) len++;
537 
538  len /= 2;
539 
540  for (i = y; i < max_h; i += 2) {
541  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);
542  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);
543  }
544 }
545 
546 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)
547 {
548 #ifdef SWITCH_HAVE_VPX
549  switch_image_t *tmp = NULL;
550  uint8_t *data;
551 
552  if (x >= img->d_w || y >= img->d_h) return;
553 
554  if (w == img->d_w && h == img->d_h) {
555  switch_img_patch(IMG, img, X, Y);
556  return;
557  }
558 
559  if (!(img->fmt & SWITCH_IMG_FMT_PLANAR)) {
560  data = img->planes[SWITCH_PLANE_PACKED];
561  } else {
562  data = img->planes[SWITCH_PLANE_Y];
563  }
564 
565  if (img->d_w == img->stride[0]) {
566  tmp = (switch_image_t *)vpx_img_wrap(NULL, img->fmt, img->d_w, img->d_h, 1, data);
567  } else {
568  switch_img_copy(img, &tmp);
569  }
570 
571  if (!tmp) return;
572 
573  w = MIN(img->d_w - x, w);
574  h = MIN(img->d_h - y, h);
575 
576  if (!switch_img_set_rect(tmp, x, y, w, h)) {
577  switch_img_patch(IMG, tmp, X, Y);
578  }
579 
580  switch_img_free(&tmp);
581 #endif
582 }
583 
585 {
586 #ifdef SWITCH_HAVE_YUV
587  switch_img_fmt_t new_fmt = img->fmt;
588 
589  switch_assert(img);
590  switch_assert(new_img);
591 
592  if (img->fmt != SWITCH_IMG_FMT_I420 && img->fmt != SWITCH_IMG_FMT_ARGB) return;
593 
594  if (*new_img) {
595  new_fmt = (*new_img)->fmt;
596  if ((*new_img)->fmt != SWITCH_IMG_FMT_I420 && (*new_img)->fmt != SWITCH_IMG_FMT_ARGB && (*new_img)->fmt != SWITCH_IMG_FMT_ARGB_LE) return;
597  if (img->d_w != (*new_img)->d_w || img->d_h != (*new_img)->d_h ) {
598  new_fmt = (*new_img)->fmt;
599  switch_img_free(new_img);
600  }
601  }
602 
603  if (*new_img == NULL) {
604  *new_img = switch_img_alloc(NULL, new_fmt, img->d_w, img->d_h, 1);
605  }
606 
607  switch_assert(*new_img);
608 
609  if (img->fmt == SWITCH_IMG_FMT_I420) {
610  if (new_fmt == SWITCH_IMG_FMT_I420) {
611  I420Copy(img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
614  (*new_img)->planes[SWITCH_PLANE_Y], (*new_img)->stride[SWITCH_PLANE_Y],
615  (*new_img)->planes[SWITCH_PLANE_U], (*new_img)->stride[SWITCH_PLANE_U],
616  (*new_img)->planes[SWITCH_PLANE_V], (*new_img)->stride[SWITCH_PLANE_V],
617  img->d_w, img->d_h);
618  } else if (new_fmt == SWITCH_IMG_FMT_ARGB) {
619  I420ToARGB(img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
622  (*new_img)->planes[SWITCH_PLANE_PACKED], (*new_img)->stride[SWITCH_PLANE_PACKED],
623  img->d_w, img->d_h);
624  } else if (new_fmt == SWITCH_IMG_FMT_ARGB_LE) {
625  I420ToABGR(img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
628  (*new_img)->planes[SWITCH_PLANE_PACKED], (*new_img)->stride[SWITCH_PLANE_PACKED],
629  img->d_w, img->d_h);
630  }
631  } else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
632  if (new_fmt == SWITCH_IMG_FMT_ARGB) {
634  (*new_img)->planes[SWITCH_PLANE_PACKED], (*new_img)->stride[SWITCH_PLANE_PACKED],
635  img->d_w, img->d_h);
636  } else if (new_fmt == SWITCH_IMG_FMT_I420) {
637  ARGBToI420(img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED],
638  (*new_img)->planes[SWITCH_PLANE_Y], (*new_img)->stride[SWITCH_PLANE_Y],
639  (*new_img)->planes[SWITCH_PLANE_U], (*new_img)->stride[SWITCH_PLANE_U],
640  (*new_img)->planes[SWITCH_PLANE_V], (*new_img)->stride[SWITCH_PLANE_V],
641  img->d_w, img->d_h);
642  }
643  }
644 #else
645  return;
646 #endif
647 }
648 
649 
651 {
652  switch_assert(img);
653  switch_assert(new_img);
654 
655 #ifdef SWITCH_HAVE_YUV
656  if (img->fmt != SWITCH_IMG_FMT_I420) abort();
657 
658  if (*new_img != NULL) {
659  if (img->fmt != (*new_img)->fmt || img->d_w != (*new_img)->d_w || img->d_h != (*new_img)->d_h) {
660  switch_img_free(new_img);
661  }
662  }
663 
664  if (*new_img == NULL) {
665  if (mode == SRM_90 || mode == SRM_270) {
666  *new_img = switch_img_alloc(NULL, img->fmt, img->d_h, img->d_w, 1);
667  } else {
668  *new_img = switch_img_alloc(NULL, img->fmt, img->d_w, img->d_h, 1);
669  }
670  }
671 
672  switch_assert(*new_img);
673 
674 
675  I420Rotate(img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
678  (*new_img)->planes[SWITCH_PLANE_Y], (*new_img)->stride[SWITCH_PLANE_Y],
679  (*new_img)->planes[SWITCH_PLANE_U], (*new_img)->stride[SWITCH_PLANE_U],
680  (*new_img)->planes[SWITCH_PLANE_V], (*new_img)->stride[SWITCH_PLANE_V],
681  img->d_w, img->d_h, (int)mode);
682 #else
683  return;
684 #endif
685 }
686 
687 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)
688 {
689 #ifdef SWITCH_HAVE_VPX
690  switch_image_t *new_img = NULL, *tmp;
691  uint8_t *data;
692 
693  switch_assert(img);
694 
695  if (x >= img->d_w || y >= img->d_h) return NULL;
696 
697  if (!(img->fmt & SWITCH_IMG_FMT_PLANAR)) {
698  data = img->planes[SWITCH_PLANE_PACKED];
699  } else {
700  data = img->planes[SWITCH_PLANE_Y];
701  }
702 
703  tmp = (switch_image_t *)vpx_img_wrap(NULL, img->fmt, img->d_w, img->d_h, 1, data);
704  if (!tmp) return NULL;
705 
706  w = MIN(img->d_w - x, w);
707  h = MIN(img->d_h - y, h);
708 
709  if (!switch_img_set_rect(tmp, x, y, w, h)) {
710  switch_img_copy(tmp, &new_img);
711  }
712 
713  switch_img_free(&tmp);
714 
715  return new_img;
716 #else
717  return NULL;
718 #endif
719 }
720 
721 #if 0
722 static inline void switch_core_rgb2xyz(switch_rgb_color_t *rgb, switch_xyz_color_t *xyz)
723 {
724  double r, g, b;
725 
726  r = (double)rgb->r / 255;
727  g = (double)rgb->g / 255;
728  b = (double)rgb->b / 255;
729 
730  if ( r > 0.04045 ) {
731  r = ( ( r + 0.055 ) / 1.055 );
732  r = pow(r, 2.4);
733  } else {
734  r = r / 12.92;
735  }
736 
737  if ( g > 0.04045 ) {
738  g = ( ( g + 0.055 ) / 1.055 );
739  g = pow(g, 2.4);
740  } else {
741  g = g / 12.92;
742  }
743 
744  if ( b > 0.04045 ) {
745  b = ( ( b + 0.055 ) / 1.055 );
746  b = pow(b, 2.4);
747  } else {
748  b = b / 12.92;
749  }
750 
751  r = r * 100;
752  g = g * 100;
753  b = b * 100;
754 
755  //Observer. = 2degrees, Illuminant = D65
756  xyz->x = r * 0.4124 + g * 0.3576 + b * 0.1805;
757  xyz->y = r * 0.2126 + g * 0.7152 + b * 0.0722;
758  xyz->z = r * 0.0193 + g * 0.1192 + b * 0.9505;
759 
760 }
761 
762 #define SVMAX(a,b) ((a) > (b) ? (a) : (b))
763 #define SVMAX3(a,b,c) (SVMAX((a), SVMAX((b),(c))))
764 #define SVMIN(a,b) ((a) < (b) ? (a) : (b))
765 #define SVMIN3(a,b,c) (SVMIN((a), SVMIN((b),(c))))
766 
767 
768 static inline void switch_core_rgb2hsl(switch_rgb_color_t *rgb, switch_hsl_color_t *hsl)
769 {
770  double r, g, b, max, min;
771 
772  r = (double)rgb->r / 255;
773  g = (double)rgb->g / 255;
774  b = (double)rgb->b / 255;
775 
776  max = SVMAX3(r, g, b);
777  min = SVMIN3(r, g, b);
778 
779  hsl->l = (max + min) / 2;
780 
781  if (max != min) {
782  double d = max - min;
783 
784  hsl->s = hsl->l > 0.5f ? d / (2 - max - min) : d / (max + min);
785 
786  if (max == r) {
787  hsl->h = (g - b) / (max - min);
788  } else if(max == g) {
789  hsl->h = 2.0 + ((b - r) / (max - min));
790  } else {
791  hsl->h = 4.0 + ((r - g) / (max - min));
792  }
793  } else {
794  hsl->h = hsl->s = 0;
795  }
796 
797  hsl->h = round(hsl->h * 60);
798  if (hsl->h < 0) hsl->h += 360;
799 
800  hsl->s *= 100;
801  hsl->l *= 100;
802 
803 }
804 
805 static inline void switch_core_rgb2lab(switch_rgb_color_t *rgb, switch_lab_color_t *lab)
806 {
807  double x,y,z;
808  double r = rgb->r;
809  double g = rgb->g;
810  double b = rgb->b;
811 
812  r=r>10.31475 ? 1.474000611989649e-6 * pow(r+14.025 , 2.4) : r * 0.0003035269835488375;
813  g=g>10.31475 ? 1.474000611989649e-6 * pow(g+14.025 , 2.4) : g * 0.0003035269835488375;
814  b=b>10.31475 ? 1.474000611989649e-6 * pow(b+14.025 , 2.4) : b * 0.0003035269835488375;
815  x=r * 0.43394994055572506 + g * 0.3762097699033109 + b * 0.18984028954096394;
816  y=r * 0.2126729 + g * 0.7151522 + b * 0.0721750;
817  z=r * 0.017756582753965265 + g * 0.10946796102238182 + b * 0.8727754562236529;
818 
819 
820  x = x > 0.008856452 ? pow(x , 0.3333333333333333) : 7.787037037037037 * x + 0.13793103448275862;
821  y = y > 0.008856452 ? pow(y , 0.3333333333333333) : 7.787037037037037 * y + 0.13793103448275862;
822  z = z > 0.008856452 ? pow(z , 0.3333333333333333) : 7.787037037037037 * z + 0.13793103448275862;
823 
824  lab->l = 116 * y - 16;
825  lab->a = 500 * (x - y);
826  lab->b = 200 * (y - z);
827 }
828 
829 
830 
831 /// Computes the CIEDE2000 color-difference between two Lab colors
832 /// Based on the article:
833 /// The CIEDE2000 Color-Difference Formula: Implementation Notes,
834 /// Supplementary Test Data, and Mathematical Observations,", G. Sharma,
835 /// W. Wu, E. N. Dalal, submitted to Color Research and Application,
836 /// January 2004.
837 /// Available at http://www.ece.rochester.edu/~/gsharma/ciede2000/
838 /// Based on the C++ implementation by Ofir Pele, The Hebrew University of Jerusalem 2010.
839 //
840 static inline double switch_CIEDE2000(switch_lab_color_t *lab1, switch_lab_color_t *lab2)
841 {
842  double Lstd = lab1->l;
843  double astd = lab1->a;
844  double bstd = lab1->b;
845  double pi = M_PI;
846 
847  double Lsample = lab2->l;
848  double asample = lab2->a;
849  double bsample = lab2->b;
850 
851  //double _kL = 1.0;
852  //double _kC = 1.0;
853  //double _kH = 1.0;
854 
855  double Cabstd= sqrt(astd*astd+bstd*bstd);
856  double Cabsample= sqrt(asample*asample+bsample*bsample);
857 
858  double Cabarithmean= (Cabstd + Cabsample)/2.0;
859 
860  double G= 0.5*( 1.0 - sqrt( pow(Cabarithmean,7.0)/(pow(Cabarithmean,7.0) + pow(25.0,7.0))));
861 
862  double apstd= (1.0+G)*astd; // aprime in paper
863  double apsample= (1.0+G)*asample; // aprime in paper
864  double Cpsample= sqrt(apsample*apsample+bsample*bsample);
865 
866  double Cpstd= sqrt(apstd*apstd+bstd*bstd);
867  // Compute product of chromas
868  double Cpprod= (Cpsample*Cpstd);
869 
870 
871  double hpsample, dL, dC, dhp, dH, Lp, Cp;
872  double hp, Lpm502, Sl, Sc, T, Sh, delthetarad, Rc, RT;
873 
874  // Ensure hue is between 0 and 2pi
875  double hpstd= atan2(bstd,apstd);
876  if (hpstd<0) hpstd+= 2.0*pi; // rollover ones that come -ve
877 
878  hpsample= atan2(bsample,apsample);
879  if (hpsample<0) hpsample+= 2.0*pi;
880  if ( (fabs(apsample)+fabs(bsample))==0.0) hpsample= 0.0;
881 
882  dL= (Lsample-Lstd);
883  dC= (Cpsample-Cpstd);
884 
885  // Computation of hue difference
886  dhp= (hpsample-hpstd);
887  if (dhp>pi) dhp-= 2.0*pi;
888  if (dhp<-pi) dhp+= 2.0*pi;
889  // set chroma difference to zero if the product of chromas is zero
890  if (Cpprod == 0.0) dhp= 0.0;
891 
892  // Note that the defining equations actually need
893  // signed Hue and chroma differences which is different
894  // from prior color difference formulae
895 
896  dH= 2.0*sqrt(Cpprod)*sin(dhp/2.0);
897  //%dH2 = 4*Cpprod.*(sin(dhp/2)).^2;
898 
899  // weighting functions
900  Lp= (Lsample+Lstd)/2.0;
901  Cp= (Cpstd+Cpsample)/2.0;
902 
903  // Average Hue Computation
904  // This is equivalent to that in the paper but simpler programmatically.
905  // Note average hue is computed in radians and converted to degrees only
906  // where needed
907  hp= (hpstd+hpsample)/2.0;
908  // Identify positions for which abs hue diff exceeds 180 degrees
909  if ( fabs(hpstd-hpsample) > pi ) hp-= pi;
910  // rollover ones that come -ve
911  if (hp<0) hp+= 2.0*pi;
912 
913  // Check if one of the chroma values is zero, in which case set
914  // mean hue to the sum which is equivalent to other value
915  if (Cpprod==0.0) hp= hpsample+hpstd;
916 
917  Lpm502= (Lp-50.0)*(Lp-50.0);;
918  Sl= 1.0+0.015*Lpm502/sqrt(20.0+Lpm502);
919  Sc= 1.0+0.045*Cp;
920  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);
921  Sh= 1.0 + 0.015*Cp*T;
922  delthetarad= (30.0*pi/180.0)*exp(- pow(( (180.0/pi*hp-275.0)/25.0),2.0));
923  Rc= 2.0*sqrt(pow(Cp,7.0)/(pow(Cp,7.0) + pow(25.0,7.0)));
924  RT= -sin(2.0*delthetarad)*Rc;
925 
926  // The CIE 00 color difference
927  return sqrt( pow((dL/Sl),2.0) + pow((dC/Sc),2.0) + pow((dH/Sh),2.0) + RT*(dC/Sc)*(dH/Sh) );
928 }
929 #endif
930 
932 {
933  int cr, cg, cb;
934  int cr2, cg2, cb2;
935  double a, b;
936  int aa, bb, r;
937 
938 
939  cr = c1->r - c2->r;
940  cg = c1->g - c2->g;
941  cb = c1->b - c2->b;
942 
943  if (!cr && !cg && !cb) return 0;
944 
945  cr2 = c1->r/2 - c2->r/2;
946  cg2 = c1->g/2 - c2->g/2;
947  cb2 = c1->b/2 - c2->b/2;
948 
949  a = ((2*cr*cr) + (4*cg*cg) + (3*cb*cb));
950  b = ((2*cr2*cr2) + (4*cg2*cg2) + (3*cb2*cb2));
951 
952  aa = (int)a;
953  bb = (int)b*25;
954 
955  r = (((bb*4)+(aa))/9)/100;
956 
957  return r;
958 
959 }
960 
961 static inline int switch_color_distance_multi(switch_rgb_color_t *c1, switch_rgb_color_t *clist, int count, uint32_t *thresholds)
962 {
963  int x = 0, hits = 0;
964 
965  for (x = 0; x < count; x++) {
966  int distance = switch_color_distance(c1, &clist[x]);
967 
968  if (distance <= thresholds[x]) {
969  hits++;
970  break;
971  }
972  }
973 
974  return hits;
975 }
976 
977 
981  uint32_t thresholds[CHROMAKEY_MAX_MASK];
982  int mask_len;
984  uint32_t dft_thresh;
986 
987  uint32_t rr;
988  uint32_t gg;
989  uint32_t bb;
990  uint32_t color_count;
991 
993  int no_cache;
995 };
996 
998 {
1000 
1001  if (!strcasecmp(shade_name, "red")) {
1002  shade = SWITCH_SHADE_RED;
1003  } else if (!strcasecmp(shade_name, "green")) {
1004  shade = SWITCH_SHADE_GREEN;
1005  } else if (!strcasecmp(shade_name, "blue")) {
1006  shade = SWITCH_SHADE_BLUE;
1007  } else if (!strcasecmp(shade_name, "auto")) {
1008  shade = SWITCH_SHADE_AUTO;
1009  }
1010 
1011  return shade;
1012 }
1013 
1015 {
1016  int i;
1017 
1018  ck->dft_thresh = threshold;
1019  ck->dft_thresh_squared = threshold * threshold;
1020 
1021  for (i = 0; i < ck->mask_len; i++) {
1022  if (!ck->thresholds[i]) ck->thresholds[i] = ck->dft_thresh_squared;
1023  }
1024 }
1025 
1027 {
1028  switch_assert(ck);
1029 
1031  ck->mask_len = 0;
1032  memset(ck->mask, 0, sizeof(ck->mask[0]) * CHROMAKEY_MAX_MASK);
1033  memset(ck->thresholds, 0, sizeof(ck->thresholds[0]) * CHROMAKEY_MAX_MASK);
1034  ck->no_cache = 1;
1035 
1036  return SWITCH_STATUS_SUCCESS;
1037 }
1038 
1040 {
1041  switch_assert(ck);
1042 
1044  ck->autocolor = autocolor;
1045  ck->dft_thresh = threshold;
1046  ck->dft_thresh_squared = threshold * threshold;
1047  switch_img_free(&ck->cache_img);
1048  ck->no_cache = 90;
1049  memset(&ck->auto_color, 0, sizeof(ck->auto_color));
1050 
1051  return SWITCH_STATUS_SUCCESS;
1052 }
1053 
1055 {
1056  switch_assert(ck);
1057 
1058  if (ck->mask_len == CHROMAKEY_MAX_MASK) {
1059  return SWITCH_STATUS_FALSE;
1060  }
1061 
1062  ck->mask[ck->mask_len] = *color;
1063  ck->thresholds[ck->mask_len] = threshold * threshold;
1064  ck->mask_len++;
1065 
1066  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Adding color %d:%d:%d #%.2x%.2x%.2x\n",
1067  ck->auto_color.r, ck->auto_color.g, ck->auto_color.b, ck->auto_color.r, ck->auto_color.g, ck->auto_color.b);
1068 
1069  return SWITCH_STATUS_SUCCESS;
1070 }
1071 
1073 {
1074  switch_chromakey_t *ck;
1075 
1076  switch_assert(ckP);
1077 
1078  ck = *ckP;
1079  *ckP = NULL;
1080 
1081  if (ck) {
1082  switch_img_free(&ck->cache_img);
1083  free(ck);
1084  }
1085 
1086  return SWITCH_STATUS_SUCCESS;
1087 }
1088 
1090 {
1091  switch_chromakey_t *ck;
1092 
1093  switch_assert(ckP);
1094 
1095  switch_zmalloc(ck, sizeof(*ck));
1096 
1097  *ckP = ck;
1098 
1099  return SWITCH_STATUS_SUCCESS;
1100 
1101 }
1102 
1104 {
1105  switch_assert(ck);
1106 
1107  return ck->cache_img;
1108 }
1109 
1110 static inline int get_max(switch_rgb_color_t *c1)
1111 {
1112  if (c1->r > c1->g && c1->r > c1->b) {
1113  return 1;
1114  }
1115 
1116  if (c1->g > c1->r && c1->g > c1->b) {
1117  return 2;
1118  }
1119 
1120  if (c1->b > c1->r && c1->b > c1->g) {
1121  return 3;
1122  }
1123 
1124  return 0;
1125 }
1126 
1128 {
1129 
1130  int c1_max = get_max(c1);
1131  int c2_max = get_max(c2);
1132 
1133  if (c1_max && c1_max == c2_max) return 1;
1134 
1135  return 0;
1136 
1137 }
1138 
1140 {
1141  int r = abs(c1->r - c2->r);
1142  int g = abs(c1->g - c2->g);
1143  int b = abs(c1->b - c2->b);
1144 
1145  if (r < distance && g < distance && b < distance) return 1;
1146 
1147  return 0;
1148 }
1149 
1151 {
1152  int r = abs(c1->r - c2->r);
1153  int g = abs(c1->g - c2->g);
1154  int b = abs(c1->b - c2->b);
1155 
1156  if (r < 5 && g < 5 && b < 5) return 0;
1157 
1158  return (3*r) + (4*g) + (3*b);
1159 }
1160 
1161 static inline void get_dom(switch_shade_t autocolor, switch_rgb_color_t *color, int *domP, int *aP, int *bP)
1162 {
1163  int dom, a, b;
1164 
1165  switch(autocolor) {
1166  case SWITCH_SHADE_RED:
1167  dom = color->r;
1168  a = color->g;
1169  b = color->b;
1170  break;
1171  case SWITCH_SHADE_GREEN:
1172  dom = color->g;
1173  a = color->r;
1174  b = color->b;
1175  break;
1176  case SWITCH_SHADE_BLUE:
1177  dom = color->b;
1178  a = color->r;
1179  b = color->g;
1180  break;
1181  default:
1182  dom = 0;
1183  a = 0;
1184  b = 0;
1185  break;
1186  }
1187 
1188  *domP = dom;
1189  *aP = a;
1190  *bP = b;
1191 
1192 }
1193 
1195 {
1196  uint8_t *pixel, *last_pixel = NULL, *cache_pixel = NULL, *end_pixel = NULL;
1197  int last_hits = 0;
1198  switch_image_t *cache_img;
1199  int same = 0;
1200  int same_same = 0;
1201 
1202 #ifdef DEBUG_CHROMA
1203  int other_img_cached = 0, color_cached = 0, checked = 0, hit_total = 0, total_pixel = 0, delta_hits = 0;
1204 #endif
1205 
1206  switch_assert(ck);
1207  switch_assert(img);
1208 
1209  if (img->fmt != SWITCH_IMG_FMT_ARGB) return;
1210 
1211  pixel = img->planes[SWITCH_PLANE_PACKED];
1212 
1213  cache_img = ck->cache_img;
1214  ck->cache_img = NULL;
1215 
1216  ck->frames_read++;
1217 
1218  if ((ck->frames_read % 300) == 0) {
1219  ck->no_cache = 2;
1220  }
1221 
1222  if (cache_img && (cache_img->d_w != img->d_w || cache_img->d_h != img->d_h)) {
1223  switch_img_free(&cache_img);
1224  }
1225 
1226  if (cache_img) {
1227  cache_pixel = cache_img->planes[SWITCH_PLANE_PACKED];
1228  }
1229 
1230  end_pixel = (img->planes[SWITCH_PLANE_PACKED] + img->d_w * img->d_h * 4);
1231 
1232  if (ck->autocolor) {
1233  ck->color_count = 0;
1234  ck->rr = ck->gg = ck->bb = 0;
1235  }
1236 
1237  for (; pixel < end_pixel; pixel += 4) {
1238  switch_rgb_color_t *color = (switch_rgb_color_t *)pixel;
1239  switch_rgb_color_t *last_color = (switch_rgb_color_t *)last_pixel;
1240  int hits = 0;
1241 
1242 
1243 
1244 #ifdef DEBUG_CHROMA
1245  total_pixel++;
1246 #endif
1247 
1248  if (!ck->no_cache && cache_img && cache_pixel) {
1249  switch_rgb_color_t *cache_color = (switch_rgb_color_t *)cache_pixel;
1250 
1251  if (switch_color_distance_cheap(color, cache_color) < 5) {
1252 #ifdef DEBUG_CHROMA
1253  other_img_cached++;
1254 #endif
1255  color->a = cache_color->a;
1256  goto end;
1257  }
1258  }
1259 
1260 
1261  if (last_color) {
1262  if (switch_color_distance_cheap(color, last_color) < 5) {
1263 
1264  hits = last_hits;
1265 #ifdef DEBUG_CHROMA
1266  color_cached++;
1267 #endif
1268 
1269  same++;
1270  } else {
1271  same = 0;
1272  }
1273  }
1274 
1275  if (!hits) {
1276 
1277  if (ck->autocolor) {
1278  int dom, a, b;
1279 
1280  get_dom(ck->autocolor, color, &dom, &a, &b);
1281 
1282  if (ck->autocolor != SWITCH_SHADE_AUTO) {
1283  //printf("WTF %d\n", ck->dft_thresh);
1284 
1285  int tol = ck->dft_thresh;
1286  int a_tol = tol/6;
1287  int b_tol = tol/6;
1288 
1289  if (dom > a && dom > b && dom > tol) {
1290  if (dom - a > a_tol && dom - b > b_tol) {
1291  hits = 1;
1292  }
1293  }
1294  }
1295  }
1296 
1297  if (!hits && ck->mask_len) {
1298  hits = switch_color_distance_multi(color, ck->mask, ck->mask_len, ck->thresholds);
1299  }
1300 
1301 
1302 #ifdef DEBUG_CHROMA
1303  checked++;
1304 #endif
1305  }
1306 
1307  end:
1308 
1309  if (same > 100 && last_color && switch_color_dom_cmp(color, last_color)) {
1310  same_same++;
1311  } else {
1312  same_same = 0;
1313  }
1314 
1315  if (!hits && ck->autocolor == SWITCH_SHADE_AUTO && (same > 300 || same_same > 50) && ck->no_cache) {
1316  ck->color_count++;
1317  ck->rr += color->r;
1318  ck->gg += color->g;
1319  ck->bb += color->b;
1320  }
1321 
1322  if (cache_pixel) {
1323  cache_pixel += 4;
1324  }
1325 
1326  if (hits) {
1327 #ifdef DEBUG_CHROMA
1328  hit_total++;
1329 #endif
1330  color->a = 0;
1331  }
1332 
1333  last_pixel = pixel;
1334  last_hits = hits;
1335  }
1336 
1337  if (ck->color_count > 1000) {
1338  switch_rgb_color_t *last_color = NULL;
1339  int skip = 0;
1340  int dom, a, b;
1341 
1342  ck->auto_color.r = ck->rr / ck->color_count;
1343  ck->auto_color.g = ck->gg / ck->color_count;
1344  ck->auto_color.b = ck->bb / ck->color_count;
1345 
1346  if (ck->mask_len) {
1347  int i = 0;
1348 
1349  for (i = 0; i < ck->mask_len; i++) {
1350  last_color = &ck->mask[i];
1351 
1352  get_dom(ck->autocolor, &ck->auto_color, &dom, &a, &b);
1353 
1354  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)) {
1355  skip = 1;
1356  break;
1357  }
1358  }
1359  }
1360 
1361  if (!ck->mask_len || !skip) {
1363  }
1364 
1365  }
1366 
1367  if (ck->no_cache > 0 && ck->mask_len) {
1368  ck->no_cache--;
1369  }
1370 
1371 
1372 #ifdef DEBUG_CHROMA
1373  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);
1374 #endif
1375 
1376  if (!ck->no_cache) {
1377  switch_img_copy(img, &ck->cache_img);
1378  }
1379 
1380  switch_img_free(&cache_img);
1381 
1382  return;
1383 }
1384 
1386 {
1387  switch_rgb_color_t *pixel, *last_pixel = NULL;
1388  int last_threshold = 0;
1389  switch_assert(img);
1390 
1391  if (img->fmt != SWITCH_IMG_FMT_ARGB) return;
1392 
1394 
1395  for (; pixel < ((switch_rgb_color_t *)img->planes[SWITCH_PLANE_PACKED] + img->d_w * img->d_h); pixel++) {
1396  int threshold = 0;
1397 
1398  if (last_pixel && (*(uint32_t *)pixel & 0xFFFFFF) == (*(uint32_t *)last_pixel & 0xFFFFFF)) {
1399  threshold = last_threshold;
1400  } else {
1401  threshold = switch_color_distance(pixel, mask);
1402  }
1403 
1404  last_threshold = threshold;
1405  last_pixel = pixel;
1406 
1407  if (threshold) {
1408  pixel->a = 0;
1409  }
1410  }
1411 
1412  return;
1413 }
1414 
1415 static inline void switch_img_draw_pixel(switch_image_t *img, int x, int y, switch_rgb_color_t *color)
1416 {
1417 #ifdef SWITCH_HAVE_YUV
1418  switch_yuv_color_t yuv = {0};
1419 
1420  if (x < 0 || y < 0 || x >= img->d_w || y >= img->d_h) return;
1421 
1422  if (img->fmt == SWITCH_IMG_FMT_I420) {
1423  switch_color_rgb2yuv(color, &yuv);
1424 
1425  img->planes[SWITCH_PLANE_Y][y * img->stride[SWITCH_PLANE_Y] + x] = yuv.y;
1426 
1427  if (((x & 0x1) == 0) && ((y & 0x1) == 0)) {// only draw on even position
1428  img->planes[SWITCH_PLANE_U][y / 2 * img->stride[SWITCH_PLANE_U] + x / 2] = yuv.u;
1429  img->planes[SWITCH_PLANE_V][y / 2 * img->stride[SWITCH_PLANE_V] + x / 2] = yuv.v;
1430  }
1431  } else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
1432  *((switch_rgb_color_t *)img->planes[SWITCH_PLANE_PACKED] + img->d_w * y + x) = *color;
1433  }
1434 #endif
1435 }
1436 
1437 SWITCH_DECLARE(void) switch_img_fill_noalpha(switch_image_t *img, int x, int y, int w, int h, switch_rgb_color_t *color)
1438 {
1439 #ifdef SWITCH_HAVE_YUV
1440  int i;
1441 
1442  if (img->fmt == SWITCH_IMG_FMT_ARGB) {
1443  int max_w = img->d_w;
1444  int max_h = img->d_h;
1445  int j;
1446  switch_rgb_color_t *rgb;
1447 
1448  for (i = 0; i < max_h; i++) {
1449  for (j = 0; j < max_w; j++) {
1450  rgb = (switch_rgb_color_t *)(img->planes[SWITCH_PLANE_PACKED] + i * img->stride[SWITCH_PLANE_PACKED] + j * 4);
1451 
1452  if (rgb->a != 0) {
1453  continue;
1454  }
1455 
1456  *rgb = *color;
1457  }
1458  }
1459  }
1460 
1461 #endif
1462 }
1463 
1465 {
1466 #ifdef SWITCH_HAVE_YUV
1467  int i;
1468 
1469  if (img->fmt == SWITCH_IMG_FMT_ARGB) {
1470  int max_w = img->d_w;
1471  int max_h = img->d_h;
1472  int j;
1473  switch_rgb_color_t *rgb;
1474  uint32_t *bytes;
1475 
1476  for (i = 0; i < max_h; i++) {
1477  for (j = 0; j < max_w; j++) {
1478  rgb = (switch_rgb_color_t *)(img->planes[SWITCH_PLANE_PACKED] + i * img->stride[SWITCH_PLANE_PACKED] + j * 4);
1479  //if (rgb);
1480 
1481 
1482  if (!rgb->a) continue;;
1483 
1484  //rgb->r = rgb->r & 0xE0, rgb->g = rgb->g & 0xE0, rgb->b = rgb->b & 0xC0;
1485  bytes = (uint32_t *) rgb;
1486 
1487 #if SWITCH_BYTE_ORDER == __BIG_ENDIAN
1488  *bytes = *bytes & 0xE0E0C0FF;
1489 #else
1490  *bytes = *bytes & 0xFFC0E0E0;
1491 #endif
1492 
1493  }
1494  }
1495  } else if (img->fmt == SWITCH_IMG_FMT_I420) {
1496  switch_image_t *tmp_img = switch_img_alloc(NULL, SWITCH_IMG_FMT_ARGB, img->d_w, img->d_h, 1);
1497 
1498  I420ToARGB(img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
1502  img->d_w, img->d_h);
1503 
1504  switch_img_8bit(tmp_img);
1505 
1506  ARGBToI420(tmp_img->planes[SWITCH_PLANE_PACKED], tmp_img->stride[SWITCH_PLANE_PACKED],
1510  tmp_img->d_w, tmp_img->d_h);
1511 
1512  switch_img_free(&tmp_img);
1513  }
1514 #endif
1515 }
1516 
1517 SWITCH_DECLARE(void) switch_img_sepia(switch_image_t *img, int x, int y, int w, int h)
1518 {
1519 #ifdef SWITCH_HAVE_YUV
1520  if (x < 0 || y < 0 || x >= img->d_w || y >= img->d_h) return;
1521 
1522  if (img->fmt == SWITCH_IMG_FMT_ARGB) {
1523  ARGBSepia(img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED], x, y, w, h);
1524  } else if (img->fmt == SWITCH_IMG_FMT_I420) {
1525  int len, i, max_h;
1526 
1527  max_h = MIN(y + h, img->d_h);
1528  len = MIN(w, img->d_w - x);
1529 
1530  if (x & 1) { x++; len--; }
1531  if (y & 1) y++;
1532  if (len <= 0) return;
1533 
1534  if ((len & 1) && (x + len) < img->d_w - 1) len++;
1535 
1536  len /= 2;
1537 
1538  for (i = y; i < max_h; i += 2) {
1539  memset(img->planes[SWITCH_PLANE_U] + img->stride[SWITCH_PLANE_U] * (i / 2) + x / 2, 108, len);
1540  memset(img->planes[SWITCH_PLANE_V] + img->stride[SWITCH_PLANE_V] * (i / 2) + x / 2, 137, len);
1541  }
1542  }
1543 #endif
1544 }
1545 
1546 SWITCH_DECLARE(void) switch_img_gray(switch_image_t *img, int x, int y, int w, int h)
1547 {
1548 #ifdef SWITCH_HAVE_YUV
1549 
1550  if (x < 0 || y < 0 || x >= img->d_w || y >= img->d_h) return;
1551 
1552  if (img->fmt == SWITCH_IMG_FMT_ARGB) {
1553  ARGBGray(img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED], x, y, w, h);
1554  } else if (img->fmt == SWITCH_IMG_FMT_I420) {
1555  int len, i, max_h;
1556 
1557  max_h = MIN(y + h, img->d_h);
1558  len = MIN(w, img->d_w - x);
1559 
1560  if (x & 1) { x++; len--; }
1561  if (y & 1) y++;
1562  if (len <= 0) return;
1563 
1564  if ((len & 1) && (x + len) < img->d_w - 1) len++;
1565 
1566  len /= 2;
1567 
1568  for (i = y; i < max_h; i += 2) {
1569  memset(img->planes[SWITCH_PLANE_U] + img->stride[SWITCH_PLANE_U] * (i / 2) + x / 2, 128, len);
1570  memset(img->planes[SWITCH_PLANE_V] + img->stride[SWITCH_PLANE_V] * (i / 2) + x / 2, 128, len);
1571  }
1572  }
1573 #endif
1574 }
1575 
1576 SWITCH_DECLARE(void) switch_img_fill(switch_image_t *img, int x, int y, int w, int h, switch_rgb_color_t *color)
1577 {
1578 #ifdef SWITCH_HAVE_YUV
1579  int len, i, max_h;
1580  switch_yuv_color_t yuv_color;
1581 
1582  if (x < 0 || y < 0 || x >= img->d_w || y >= img->d_h) return;
1583 
1584  if (img->fmt == SWITCH_IMG_FMT_I420) {
1585  switch_color_rgb2yuv(color, &yuv_color);
1586 
1587  max_h = MIN(y + h, img->d_h);
1588  len = MIN(w, img->d_w - x);
1589 
1590  if (x & 1) { x++; len--; }
1591  if (y & 1) y++;
1592  if (len <= 0) return;
1593 
1594  for (i = y; i < max_h; i++) {
1595  memset(img->planes[SWITCH_PLANE_Y] + img->stride[SWITCH_PLANE_Y] * i + x, yuv_color.y, len);
1596  }
1597 
1598  if ((len & 1) && (x + len) < img->d_w - 1) len++;
1599 
1600  len /= 2;
1601 
1602  for (i = y; i < max_h; i += 2) {
1603  memset(img->planes[SWITCH_PLANE_U] + img->stride[SWITCH_PLANE_U] * (i / 2) + x / 2, yuv_color.u, len);
1604  memset(img->planes[SWITCH_PLANE_V] + img->stride[SWITCH_PLANE_V] * (i / 2) + x / 2, yuv_color.v, len);
1605  }
1606  } else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
1607  for (i = 0; i < img->d_w; i++) {
1608  *((switch_rgb_color_t *)img->planes[SWITCH_PLANE_PACKED] + i) = *color;
1609  }
1610 
1611  for (i = 1; i < img->d_h; i++) {
1612  memcpy( img->planes[SWITCH_PLANE_PACKED] + i * img->d_w * 4,
1613  img->planes[SWITCH_PLANE_PACKED], img->d_w * 4);
1614  }
1615  }
1616 #endif
1617 }
1618 
1619 #ifdef SWITCH_HAVE_YUV
1620 static inline void switch_img_get_yuv_pixel(switch_image_t *img, switch_yuv_color_t *yuv, int x, int y)
1621 {
1622  // switch_assert(img->fmt == SWITCH_IMG_FMT_I420);
1623  if (x < 0 || y < 0 || x >= img->d_w || y >= img->d_h) return;
1624 
1625  yuv->y = *(img->planes[SWITCH_PLANE_Y] + img->stride[SWITCH_PLANE_Y] * y + x);
1626  yuv->u = *(img->planes[SWITCH_PLANE_U] + img->stride[SWITCH_PLANE_U] * (y / 2) + x / 2);
1627  yuv->v = *(img->planes[SWITCH_PLANE_V] + img->stride[SWITCH_PLANE_V] * (y / 2) + x / 2);
1628 }
1629 #endif
1630 
1631 static inline void switch_img_get_rgb_pixel(switch_image_t *img, switch_rgb_color_t *rgb, int x, int y)
1632 {
1633 #ifdef SWITCH_HAVE_YUV
1634  if (x < 0 || y < 0 || x >= img->d_w || y >= img->d_h) return;
1635 
1636  if (img->fmt == SWITCH_IMG_FMT_I420) {
1637  switch_yuv_color_t yuv = {0};
1638 
1639  switch_img_get_yuv_pixel(img, &yuv, x, y);
1640  switch_color_yuv2rgb(&yuv, rgb);
1641  } else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
1642  *rgb = *((switch_rgb_color_t *)img->planes[SWITCH_PLANE_PACKED] + img->d_w * y + x);
1643  }
1644 #endif
1645 }
1646 
1647 SWITCH_DECLARE(void) switch_img_overlay(switch_image_t *IMG, switch_image_t *img, int x, int y, uint8_t percent)
1648 {
1649  int i, j, len, max_h;
1650  switch_rgb_color_t RGB = {0}, rgb = {0}, c = {0};
1651  int xoff = 0, yoff = 0;
1652  uint8_t alpha = (int8_t)((255 * percent) / 100);
1653 
1654 
1656 
1657  if (x < 0) {
1658  xoff = -x;
1659  x = 0;
1660  }
1661 
1662  if (y < 0) {
1663  yoff = -y;
1664  y = 0;
1665  }
1666 
1667  max_h = MIN(y + img->d_h - yoff, IMG->d_h);
1668  len = MIN(img->d_w - xoff, IMG->d_w - x);
1669 
1670  if (x & 1) { x++; len--; }
1671  if (y & 1) y++;
1672  if (len <= 0) return;
1673 
1674  for (i = y; i < max_h; i++) {
1675  for (j = 0; j < len; j++) {
1676  switch_img_get_rgb_pixel(IMG, &RGB, x + j, i);
1677  switch_img_get_rgb_pixel(img, &rgb, j + xoff, i - y + yoff);
1678 
1679  if (rgb.a > 0) {
1680  c.r = ((RGB.r * (255 - alpha)) >> 8) + ((rgb.r * alpha) >> 8);
1681  c.g = ((RGB.g * (255 - alpha)) >> 8) + ((rgb.g * alpha) >> 8);
1682  c.b = ((RGB.b * (255 - alpha)) >> 8) + ((rgb.b * alpha) >> 8);
1683  } else {
1684  c.r = RGB.r;
1685  c.g = RGB.g;
1686  c.b = RGB.b;
1687  }
1688 
1689  switch_img_draw_pixel(IMG, x + j, i, &c);
1690  }
1691  }
1692 }
1693 
1694 static uint8_t scv_art[14][16] = {
1695  {0x00, 0x7E, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x00},
1696  {0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00},
1697  {0x00, 0x7E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x7E, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7E, 0x00},
1698  {0x00, 0x7E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x7E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x7E, 0x00},
1699  {0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00},
1700  {0x00, 0x7E, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x7E, 0x00},
1701  {0x00, 0x7E, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7E, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x00},
1702  {0x00, 0x7E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00},
1703  {0x00, 0x7E, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x00},
1704  {0x00, 0x7E, 0x42, 0x42, 0x42, 0x42, 0x42, 0x7E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x7E, 0x00},
1705  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00}, /*.*/
1706  {0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*:*/
1707  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /*-*/
1708  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* */
1709 };
1710 
1711 static void scv_tag(void *buffer, int w, int x, int y, uint8_t n)
1712 {
1713  int i = 0, j=0;
1714  uint8_t *p = buffer;
1715 
1716  if (n > 13) return;
1717 
1718  for(i=0; i<8; i++) {
1719  for (j=0; j<16; j++) {
1720  *( p + (y + j) * w + (x + i)) = (scv_art[n][j] & 0x80 >> i) ? 0xFF : 0x00;
1721  }
1722  }
1723 }
1724 
1725 SWITCH_DECLARE(void) switch_img_add_text(void *buffer, int w, int x, int y, char *s)
1726 {
1727  while (*s) {
1728  int index;
1729 
1730  if (x > w - 8) break;
1731 
1732  switch (*s) {
1733  case '.': index = 10; break;
1734  case ':': index = 11; break;
1735  case '-': index = 12; break;
1736  case ' ': index = 13; break;
1737  default:
1738  index = *s - 0x30;
1739  }
1740 
1741  scv_tag(buffer, w, x, y, index);
1742  x += 8;
1743  s++;
1744  }
1745 }
1746 
1748 {
1749  if (zstr(str)) return;
1750 
1751  if ((*str) == '#' && strlen(str) == 7) {
1752  unsigned int r, g, b;
1753  sscanf(str, "#%02x%02x%02x", &r, &g, &b);
1754  color->r = r;
1755  color->g = g;
1756  color->b = b;
1757  } else {
1758  if (!strcmp(str, "red")) {
1759  color->r = 255;
1760  color->g = 0;
1761  color->b = 0;
1762  } else if (!strcmp(str, "green")) {
1763  color->r = 0;
1764  color->g = 255;
1765  color->b = 0;
1766  } else if (!strcmp(str, "blue")) {
1767  color->r = 0;
1768  color->g = 0;
1769  color->b = 255;
1770  }
1771  }
1772 
1773  color->a = 255;
1774 }
1775 
1776 #ifdef SWITCH_HAVE_YUV
1777 static inline void switch_color_rgb2yuv(switch_rgb_color_t *rgb, switch_yuv_color_t *yuv)
1778 {
1779 
1780  yuv->y = ( ( 66 * rgb->r + 129 * rgb->g + 25 * rgb->b + 128) >> 8) + 16;
1781  yuv->u = ( ( -38 * rgb->r - 74 * rgb->g + 112 * rgb->b + 128) >> 8) + 128;
1782  yuv->v = ( ( 112 * rgb->r - 94 * rgb->g - 18 * rgb->b + 128) >> 8) + 128;
1783 
1784  //yuv->y = (uint8_t)(((rgb->r * 4897) >> 14) + ((rgb->g * 9611) >> 14) + ((rgb->b * 1876) >> 14));
1785  //yuv->u = (uint8_t)(- ((rgb->r * 2766) >> 14) - ((5426 * rgb->g) >> 14) + rgb->b / 2 + 128);
1786  //yuv->v = (uint8_t)(rgb->r / 2 -((6855 * rgb->g) >> 14) - ((rgb->b * 1337) >> 14) + 128);
1787 }
1788 #endif
1789 
1790 #define CLAMP(val) MAX(0, MIN(val, 255))
1791 
1792 #ifdef SWITCH_HAVE_YUV
1793 static inline void switch_color_yuv2rgb(switch_yuv_color_t *yuv, switch_rgb_color_t *rgb)
1794 {
1795 #if 0
1796  int C = yuv->y - 16;
1797  int D = yuv->u - 128;
1798  int E = yuv->v - 128;
1799 
1800  rgb->r = CLAMP((298 * C + 409 * E + 128) >> 8);
1801  rgb->g = CLAMP((298 * C - 100 * D - 208 * E + 128) >> 8);
1802  rgb->b = CLAMP((298 * C + 516 * D + 128) >> 8);
1803 #endif
1804 
1805  rgb->a = 255;
1806  rgb->r = CLAMP( yuv->y + ((22457 * (yuv->v-128)) >> 14));
1807  rgb->g = CLAMP((yuv->y - ((715 * (yuv->v-128)) >> 10) - ((5532 * (yuv->u-128)) >> 14)));
1808  rgb->b = CLAMP((yuv->y + ((28384 * (yuv->u-128)) >> 14)));
1809  }
1810 #endif
1811 
1813 {
1814 #ifdef SWITCH_HAVE_YUV
1815  switch_rgb_color_t rgb = { 0 };
1816 
1817  switch_color_set_rgb(&rgb, str);
1818  switch_color_rgb2yuv(&rgb, color);
1819 #endif
1820 }
1821 
1822 #if SWITCH_HAVE_FREETYPE
1823 #include <ft2build.h>
1824 #include FT_FREETYPE_H
1825 #include FT_GLYPH_H
1826 #endif
1827 
1828 #define MAX_GRADIENT 8
1829 
1831 #if SWITCH_HAVE_FREETYPE
1832  FT_Library library;
1833  FT_Face face;
1834 #endif
1836  double angle;
1837  uint16_t font_size;
1845 };
1846 
1848 {
1849  int i;
1850  switch_rgb_color_t *color;
1851 
1852  switch_rgb_color_t *c1 = &handle->bgcolor;
1853  switch_rgb_color_t *c2 = &handle->color;
1854 
1855  for (i = 0; i < MAX_GRADIENT; i++) {
1856  color = &handle->gradient_table[i];
1857  color->r = c1->r + (c2->r - c1->r) * i / MAX_GRADIENT;
1858  color->g = c1->g + (c2->g - c1->g) * i / MAX_GRADIENT;
1859  color->b = c1->b + (c2->b - c1->b) * i / MAX_GRADIENT;
1860  color->a = 255;
1861  }
1862 }
1863 
1865  const char *font_color, const char *bgcolor, uint16_t font_size, double angle, switch_memory_pool_t *pool)
1866 {
1867  int free_pool = 0;
1868  switch_img_txt_handle_t *new_handle;
1869 
1870  if (!pool) {
1871  free_pool = 1;
1873  }
1874 
1875  new_handle = switch_core_alloc(pool, sizeof(*new_handle));
1876 
1877 #if SWITCH_HAVE_FREETYPE
1878  if (FT_Init_FreeType(&new_handle->library)) {
1879  return SWITCH_STATUS_FALSE;
1880  }
1881 /*#else
1882  return SWITCH_STATUS_FALSE; */
1883 #endif
1884 
1885  new_handle->pool = pool;
1886  new_handle->free_pool = free_pool;
1887 
1888  if (zstr(font_family)) {
1889  font_family = switch_core_sprintf(new_handle->pool, "%s%s%s",SWITCH_GLOBAL_dirs.fonts_dir, SWITCH_PATH_SEPARATOR, "FreeSans.ttf");
1890  }
1891 
1892  if (!switch_is_file_path(font_family)) {
1893  new_handle->font_family = switch_core_sprintf(new_handle->pool, "%s%s%s",SWITCH_GLOBAL_dirs.fonts_dir, SWITCH_PATH_SEPARATOR, font_family);
1894  } else {
1895  new_handle->font_family = switch_core_strdup(new_handle->pool, font_family);
1896  }
1897 
1898  if (switch_file_exists(new_handle->font_family, new_handle->pool) != SWITCH_STATUS_SUCCESS) {
1899  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Font %s does not exist\n", new_handle->font_family);
1900 #if SWITCH_HAVE_FREETYPE
1901  if (new_handle->library) {
1902  FT_Done_FreeType(new_handle->library);
1903  new_handle->library = NULL;
1904  }
1905 #endif
1906  if (free_pool) {
1908  }
1909  *handleP = NULL;
1910  return SWITCH_STATUS_FALSE;
1911  }
1912 
1913  new_handle->font_size = font_size;
1914  new_handle->angle = angle;
1915 
1916  switch_color_set_rgb(&new_handle->color, font_color);
1917  switch_color_set_rgb(&new_handle->bgcolor, bgcolor);
1918 
1919  init_gradient_table(new_handle);
1920 
1921  *handleP = new_handle;
1922 
1923  return SWITCH_STATUS_SUCCESS;
1924 }
1925 
1926 
1928 {
1929  switch_img_txt_handle_t *old_handle;
1931 
1932  switch_assert(handleP);
1933 
1934  old_handle = *handleP;
1935  *handleP = NULL;
1936  if (!old_handle) return;
1937 
1938 
1939 #if SWITCH_HAVE_FREETYPE
1940  if (old_handle->library) {
1941  FT_Done_FreeType(old_handle->library);
1942  old_handle->library = NULL;
1943  }
1944 #endif
1945  pool = old_handle->pool;
1946 
1947  if (old_handle->free_pool) {
1949  pool = NULL;
1950  old_handle = NULL;
1951  }
1952 
1953 }
1954 
1955 #if SWITCH_HAVE_FREETYPE
1956 static void draw_bitmap(switch_img_txt_handle_t *handle, switch_image_t *img, FT_Bitmap* bitmap, FT_Int x, FT_Int y)
1957 {
1958  FT_Int i, j, p, q;
1959  FT_Int x_max = x + bitmap->width;
1960  FT_Int y_max = y + bitmap->rows;
1961 
1962  if (bitmap->width == 0) return;
1963 
1964  switch (bitmap->pixel_mode) {
1965  case FT_PIXEL_MODE_GRAY: // it should always be GRAY since we use FT_LOAD_RENDER?
1966  break;
1967  case FT_PIXEL_MODE_NONE:
1968  case FT_PIXEL_MODE_MONO:
1969  {
1970  for ( j = y, q = 0; j < y_max; j++, q++ ) {
1971  for ( i = x, p = 0; i < x_max; i++, p++ ) {
1972  uint8_t byte;
1973  int linesize = ((bitmap->width - 1) / 8 + 1) * 8;
1974 
1975  if ( i < 0 || j < 0 || i >= img->d_w || j >= img->d_h) continue;
1976 
1977  byte = bitmap->buffer[(q * linesize + p) / 8];
1978  if ((byte >> (7 - (p % 8))) & 0x1) {
1979  switch_img_draw_pixel(img, i, j, &handle->color);
1980  }
1981  }
1982  }
1983  return;
1984  }
1985  case FT_PIXEL_MODE_GRAY2:
1986  case FT_PIXEL_MODE_GRAY4:
1987  case FT_PIXEL_MODE_LCD:
1988  case FT_PIXEL_MODE_LCD_V:
1989  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "unsupported pixel mode %d\n", bitmap->pixel_mode);
1990  return;
1991  }
1992 
1993  for ( i = x, p = 0; i < x_max; i++, p++ ) {
1994  for ( j = y, q = 0; j < y_max; j++, q++ ) {
1995  int gradient = bitmap->buffer[q * bitmap->width + p];
1996  if ( i < 0 || j < 0 || i >= img->d_w || j >= img->d_h) continue;
1997 
1998  if (handle->use_bgcolor) {
1999  switch_img_draw_pixel(img, i, j, &handle->gradient_table[gradient * MAX_GRADIENT / 256]);
2000  } else {
2001  switch_rgb_color_t rgb_color = {0};
2003  switch_img_get_rgb_pixel(img, &rgb_color, i, j);
2004 
2005  if (rgb_color.a > 0) {
2006  c.a = rgb_color.a * gradient / 255;
2007  c.r = ((rgb_color.r * (255 - gradient)) >> 8) + ((handle->color.r * gradient) >> 8);
2008  c.g = ((rgb_color.g * (255 - gradient)) >> 8) + ((handle->color.g * gradient) >> 8);
2009  c.b = ((rgb_color.b * (255 - gradient)) >> 8) + ((handle->color.b * gradient) >> 8);
2010  } else {
2011  c.a = gradient;
2012  c.r = handle->color.r;
2013  c.g = handle->color.g;
2014  c.b = handle->color.b;
2015  }
2016 
2017  switch_img_draw_pixel(img, i, j, &c);
2018  }
2019  }
2020  }
2021 }
2022 #endif
2023 
2024 
2026  int x, int y, const char *text,
2027  const char *font_family, const char *font_color,
2028  const char *bgcolor, uint16_t font_size, double angle)
2029 {
2030 #if SWITCH_HAVE_FREETYPE
2031  FT_GlyphSlot slot;
2032  FT_Matrix matrix; /* transformation matrix */
2033  FT_Vector pen; /* untransformed origin */
2034  FT_Error error;
2035  //int target_height;
2036  int index = 0;
2037  FT_ULong ch;
2038  FT_Face face;
2039  uint32_t width = 0;
2040  int this_x = 0, last_x = 0, space = 0;
2041  uint32_t ret;
2042 
2043  if (zstr(text)) return 0;
2044 
2045  if (!handle) return 0;
2046 
2047  switch_assert(!img || img->fmt == SWITCH_IMG_FMT_I420 || img->fmt == SWITCH_IMG_FMT_ARGB);
2048 
2049  if (font_family) {
2050  handle->font_family = switch_core_strdup(handle->pool, font_family);
2051  } else {
2052  font_family = handle->font_family;
2053  }
2054 
2055  if (font_size) {
2056  handle->font_size = font_size;
2057  } else {
2058  font_size = handle->font_size;
2059  }
2060 
2061  if (font_color) {
2062  switch_color_set_rgb(&handle->color, font_color);
2063  }
2064 
2065  if (bgcolor) {
2066  switch_color_set_rgb(&handle->bgcolor, bgcolor);
2067  handle->use_bgcolor = SWITCH_TRUE;
2068  } else {
2069  handle->use_bgcolor = SWITCH_FALSE;
2070  }
2071 
2072  handle->angle = angle;
2073 
2074  //angle = 0; (45.0 / 360 ) * 3.14159 * 2;
2075 
2076  //target_height = img->d_h;
2077 
2078  error = FT_New_Face(handle->library, font_family, 0, &face); /* create face object */
2079  if (error) {
2080  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to open font %s\n", font_family);
2081  return 0;
2082  }
2083 
2084  error = FT_Set_Char_Size(face, 64 * font_size, 0, 96, 0); /* set character size */
2085  if (error) return 0;
2086 
2087  slot = face->glyph;
2088 
2089  if (handle->use_bgcolor && slot->bitmap.pixel_mode != FT_PIXEL_MODE_MONO) {
2090  init_gradient_table(handle);
2091  }
2092 
2093  /* set up matrix */
2094  matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
2095  matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
2096  matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
2097  matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );
2098 
2099  pen.x = x;
2100  pen.y = y;
2101 
2102  while(*(text + index)) {
2103  ch = switch_u8_get_char((char *)text, &index);
2104 
2105  if (ch == '\n') {
2106  pen.x = x;
2107  pen.y += (font_size + font_size / 4);
2108  continue;
2109  }
2110 
2111  /* set transformation */
2112  FT_Set_Transform(face, &matrix, &pen);
2113 
2114  /* load glyph image into the slot (erase previous one) */
2115  error = FT_Load_Char(face, ch, FT_LOAD_RENDER);
2116 
2117  if (error) continue;
2118 
2119  this_x = pen.x + slot->bitmap_left;
2120 
2121  if (img) {
2122  /* now, draw to our target surface (convert position) */
2123  draw_bitmap(handle, img, &slot->bitmap, this_x, pen.y - slot->bitmap_top + font_size);
2124  }
2125 
2126  if (last_x) {
2127  space = this_x - last_x;
2128  } else {
2129  space = 0;
2130  }
2131 
2132  last_x = this_x;
2133 
2134  width += space;
2135 
2136  /* increment pen position */
2137  pen.x += slot->advance.x >> 6;
2138  pen.y += slot->advance.y >> 6;
2139  }
2140 
2141  ret = width + slot->bitmap.width * 5;
2142 
2143  FT_Done_Face(face);
2144 
2145  return ret;
2146 #else
2147  return 0;
2148 #endif
2149 }
2150 
2152 {
2153  const char *fg ="#cccccc";
2154  const char *bg = "#142e55";
2155  // const char *bg = NULL; // use a NULL bg for transparent
2156  const char *font_face = NULL;
2157  const char *fontsz = "4%";
2158  char *txt = "Value Optimized Out!";
2159  int argc = 0;
2160  char *argv[6] = { 0 };
2161  switch_rgb_color_t bgcolor = { 0 };
2162  int pre_width = 0, width = 0, font_size = 0, height = 0;
2163  char *duptxt = strdup(text);
2164  switch_img_txt_handle_t *txthandle = NULL;
2165  switch_image_t *txtimg = NULL;
2166  int x = 0, y = 0;
2167 
2168  if (!duptxt) {
2169  return NULL;
2170  }
2171 
2172  if (strchr(text, ':')) {
2173  argc = switch_split(duptxt, ':', argv);
2174 
2175  if (argc > 0 && !zstr(argv[0])) {
2176  fg = argv[0];
2177  }
2178 
2179  if (argc > 1 && !zstr(argv[1])) {
2180  bg = argv[1];
2181  if (!strcasecmp(bg, "transparent")) {
2182  bg = NULL;
2183  }
2184  }
2185 
2186  if (argc > 2 && !zstr(argv[2])) {
2187  font_face = argv[2];
2188  }
2189 
2190  if (argc > 3 && !zstr(argv[3])) {
2191  fontsz = argv[3];
2192  }
2193 
2194  if (argc > 4) {
2195  txt = argv[4];
2196  }
2197  } else txt = duptxt;
2198 
2199  if (!txt) txt = duptxt;
2200 
2201  if (strrchr(fontsz, '%')) {
2202  font_size = 1 + ((int) (float)h * (atof(fontsz) / 100.0f));
2203  } else {
2204  font_size = atoi(fontsz);
2205  }
2206 
2207  switch_url_decode(txt);
2208 
2209  while (*txt == ' ') txt++;
2210  while (end_of(txt) == ' ') end_of(txt) = '\0';
2211 
2212  switch_img_txt_handle_create(&txthandle, font_face, fg, bg, font_size, 0, NULL);
2213 
2214  pre_width = switch_img_txt_handle_render(txthandle,
2215  NULL,
2216  font_size / 2, font_size / 2,
2217  txt, NULL, fg, bg, 0, 0);
2218 
2219  height = font_size * 2;
2220 
2221  if (full && w > width) {
2222  width = w;
2223  } else {
2224  width = pre_width;
2225  }
2226 
2227  if (width == 0 || height == 0) {
2228  txtimg = NULL;
2229  goto done;
2230  }
2231 
2232  //if (bg) {
2233  // txtimg = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, width, height, 1);
2234  // switch_assert(txtimg);
2235  // switch_img_fill(txtimg, 0, 0, txtimg->d_w, txtimg->d_h, &bgcolor);
2236  //} else {
2237  txtimg = switch_img_alloc(NULL, SWITCH_IMG_FMT_ARGB, width, height, 1);
2238  switch_assert(txtimg);
2239  //memset(txtimg->planes[SWITCH_PLANE_PACKED], 0, width * height * 4);
2240  if (bg) {
2241  switch_color_set_rgb(&bgcolor, bg);
2242  switch_img_fill(txtimg, 0, 0, txtimg->d_w, txtimg->d_h, &bgcolor);
2243  } else {
2244  memset(txtimg->planes[SWITCH_PLANE_PACKED], 0, width * height * 4);
2245  }
2246  //}
2247 
2248  x = font_size / 2;
2249  y = font_size / 2;
2250 
2251  if (full) {
2252  x = (txtimg->d_w / 2) - (pre_width / 2);
2253  }
2254 
2255  switch_img_txt_handle_render(txthandle,
2256  txtimg,
2257  x, y,
2258  txt, NULL, fg, bg, 0, 0);
2259 
2260  done:
2261 
2262  switch_img_txt_handle_destroy(&txthandle);
2263 
2264  switch_safe_free(duptxt);
2265 
2266  return txtimg;
2267 }
2268 
2269 /* WARNING:
2270  patch a big IMG with a rect hole, note this function is WIP ......
2271  It ONLY works when the hole is INSIDE the big IMG and the place the small img will patch to,
2272  more sanity checks need to be decided
2273 */
2275 {
2276  int i, len;
2277 
2280 
2281  len = MIN(img->d_w, IMG->d_w - x);
2282  if (len <= 0) return;
2283 
2284  for (i = y; i < (y + img->d_h) && i < IMG->d_h; i++) {
2285  if (rect && i >= rect->y && i < (rect->y + rect->h)) {
2286  int size = rect->x > x ? rect->x - x : 0;
2287  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);
2288  size = MIN(img->d_w - rect->w - size, IMG->d_w - (rect->x + rect->w));
2289  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);
2290  } else {
2291  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);
2292  }
2293  }
2294 
2295  len /= 2;
2296 
2297  for (i = y; i < (y + img->d_h) && i < IMG->d_h; i += 2) {
2298  if (rect && i > rect->y && i < (rect->y + rect->h)) {
2299  int size = rect->x > x ? rect->x - x : 0;
2300 
2301  size /= 2;
2302  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);
2303  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);
2304  size = MIN(img->d_w - rect->w - size, IMG->d_w - (rect->x + rect->w)) / 2;
2305  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);
2306  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);
2307  } else {
2308  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);
2309  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);
2310  }
2311  }
2312 }
2313 
2314 #define SWITCH_IMG_MAX_WIDTH 1920 * 4
2315 #define SWITCH_IMG_MAX_HEIGHT 1080 * 4
2316 
2317 #if !defined(SWITCH_HAVE_YUV)
2318 #undef SWITCH_HAVE_PNG
2319 #endif
2320 
2321 #ifdef SWITCH_HAVE_PNG
2322 // WIP png functions, need furthur tweak/check to make sure it works on all png files and errors are properly detected and reported
2323 // #define PNG_DEBUG 3
2324 #define PNG_SKIP_SETJMP_CHECK
2325 #include <png.h>
2326 
2327 
2328 #ifdef PNG_SIMPLIFIED_READ_SUPPORTED /* available from libpng 1.6.0 */
2329 
2330 struct switch_png_opaque_s {
2331  png_image png;
2332  png_bytep buffer;
2333 };
2334 
2335 
2336 
2337 SWITCH_DECLARE(switch_status_t) switch_png_open(switch_png_t **pngP, const char *file_name)
2338 {
2339  switch_png_t *use_png;
2341 
2342  switch_zmalloc(use_png, sizeof(*use_png));
2343  switch_zmalloc(use_png->pvt, sizeof(struct switch_png_opaque_s));
2344  use_png->pvt->png.version = PNG_IMAGE_VERSION;
2345 
2346  if (!png_image_begin_read_from_file(&use_png->pvt->png, file_name)) {
2347  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error read PNG %s\n", file_name);
2349  }
2350 
2351  use_png->pvt->png.format = PNG_FORMAT_ARGB;
2352 
2353  use_png->pvt->buffer = malloc(PNG_IMAGE_SIZE(use_png->pvt->png));
2354  switch_assert(use_png->pvt->buffer);
2355 
2356  if (!png_image_finish_read(&use_png->pvt->png, NULL/*background*/, use_png->pvt->buffer, 0, NULL)) {
2357  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error read PNG %s\n", file_name);
2359  }
2360 
2361 
2362  use_png->w = use_png->pvt->png.width;
2363  use_png->h = use_png->pvt->png.height;
2364 
2365 end:
2366 
2367  if (status == SWITCH_STATUS_SUCCESS) {
2368  *pngP = use_png;
2369  } else {
2370  switch_png_free(&use_png);
2371  *pngP = NULL;
2372  }
2373 
2374  return status;
2375 }
2376 
2378 {
2379  switch_png_t *use_png;
2380 
2381  if (pngP) {
2382  use_png = *pngP;
2383  *pngP = NULL;
2384  png_image_free(&use_png->pvt->png);
2385  switch_safe_free(use_png->pvt->buffer);
2386  switch_safe_free(use_png->pvt);
2387  switch_safe_free(use_png);
2388  }
2389 }
2390 
2391 
2393 {
2395  switch_rgb_color_t *rgb_color;
2396  int i, j;
2397 
2398  switch_assert(use_png);
2399 
2400  for (i = 0; i < use_png->pvt->png.height; i++) {
2401  for (j = 0; j < use_png->pvt->png.width; j++) {
2402  rgb_color = (switch_rgb_color_t *)use_png->pvt->buffer + i * use_png->pvt->png.width + j;
2403 
2404  if (rgb_color->a) { // todo, mux alpha with the underlying pixel
2405  switch_img_draw_pixel(img, x + j, y + i, rgb_color);
2406  }
2407  }
2408  }
2409 
2410  return status;
2411 }
2412 
2413 #else /* libpng < 1.6.0 */
2414 
2415 SWITCH_DECLARE(switch_status_t) switch_png_open(switch_png_t **pngP, const char *file_name)
2416 {
2417  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NOT IMPLEMENTED\n");
2418  return SWITCH_STATUS_FALSE;
2419 }
2420 
2422 {
2423  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NOT IMPLEMENTED\n");
2424 }
2425 
2427 {
2428  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NOT IMPLEMENTED\n");
2429  return SWITCH_STATUS_FALSE;
2430 }
2431 
2432 #endif
2433 
2434 
2435 #ifdef PNG_SIMPLIFIED_READ_SUPPORTED /* available from libpng 1.6.0 */
2436 
2437 static switch_image_t *png2img(png_image *png, switch_img_fmt_t img_fmt)
2438 {
2439  png_bytep buffer = NULL;
2440  switch_image_t *img = NULL;
2441 
2442  png->version = PNG_IMAGE_VERSION;
2443 
2444  if (img_fmt == SWITCH_IMG_FMT_I420) {
2445  png->format = PNG_FORMAT_RGB;
2446  } else if (img_fmt == SWITCH_IMG_FMT_ARGB) {
2447 #if SWITCH_BYTE_ORDER == __BIG_ENDIAN
2448  png->format = PNG_FORMAT_ARGB;
2449 #else
2450  png->format = PNG_FORMAT_BGRA;
2451 #endif
2452  } else {
2453  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unsupported image format: %x\n", img_fmt);
2454  goto err;
2455  }
2456 
2457  buffer = malloc(PNG_IMAGE_SIZE(*png));
2458  switch_assert(buffer);
2459 
2460  if (!png_image_finish_read(png, NULL/*background*/, buffer, 0, NULL)) {
2462  goto err;
2463  }
2464 
2465  if (png->width > SWITCH_IMG_MAX_WIDTH || png->height > SWITCH_IMG_MAX_HEIGHT) {
2466  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PNG is too large! %dx%d\n", png->width, png->height);
2467  goto err;
2468  }
2469 
2470  img = switch_img_alloc(NULL, img_fmt, png->width, png->height, 1);
2471  switch_assert(img);
2472 
2473  if (img_fmt == SWITCH_IMG_FMT_I420) {
2474  RAWToI420(buffer, png->width * 3,
2478  png->width, png->height);
2479  } else if (img_fmt == SWITCH_IMG_FMT_ARGB){
2480  ARGBCopy(buffer, png->width * 4,
2481  img->planes[SWITCH_PLANE_PACKED], png->width * 4,
2482  png->width, png->height);
2483  }
2484 
2485 err:
2486  png_image_free(png);
2487  switch_safe_free(buffer);
2488  return img;
2489 }
2490 
2491 SWITCH_DECLARE(switch_image_t *) switch_img_read_png(const char* file_name, switch_img_fmt_t img_fmt)
2492 {
2493  png_image png = { 0 };
2494 
2495  png.version = PNG_IMAGE_VERSION;
2496 
2497  if (!png_image_begin_read_from_file(&png, file_name)) {
2498  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error open png: %s\n", file_name);
2499  return NULL;
2500  }
2501 
2502  return png2img(&png, img_fmt);
2503 }
2504 
2506 {
2507  png_image png = { 0 };
2508 
2509  png.version = PNG_IMAGE_VERSION;
2510 
2511  if (!png_image_begin_read_from_memory(&png, mem, size)) {
2512  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error open png from memory\n");
2513  return NULL;
2514  }
2515 
2516  return png2img(&png, img_fmt);
2517 }
2518 
2519 #else /* libpng < 1.6.0 */
2520 
2521 // ref: most are out-dated, man libpng :)
2522 // http://zarb.org/~gc/html/libpng.html
2523 // http://www.libpng.org/pub/png/book/toc.html
2524 // http://www.vias.org/pngguide/chapter01_03_02.html
2525 // http://www.libpng.org/pub/png/libpng-1.2.5-manual.html
2526 // ftp://ftp.oreilly.com/examples/9781565920583/CDROM/SOFTWARE/SOURCE/LIBPNG/EXAMPLE.C
2527 
2528 SWITCH_DECLARE(switch_image_t *) switch_img_read_png(const char* file_name, switch_img_fmt_t img_fmt)
2529 {
2530  png_byte header[8]; // 8 is the maximum size that can be checked
2531  png_bytep *row_pointers = NULL;
2532  int y;
2533 
2534  int width, height;
2535  png_byte color_type;
2536  png_byte bit_depth;
2537 
2538  png_structp png_ptr = NULL;
2539  png_infop info_ptr = NULL;
2540  //int number_of_passes;
2541  int row_bytes;
2542  png_color_8p sig_bit;
2543 
2544  png_byte *buffer = NULL;
2545  switch_image_t *img = NULL;
2546 
2547  FILE *fp;
2548 
2549  if (img_fmt != SWITCH_IMG_FMT_I420 && img_fmt != SWITCH_IMG_FMT_ARGB) {
2550  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Only ARGB and I420 are supported, you want 0x%x\n", img_fmt);
2551  return NULL;
2552  }
2553 
2554  /* open file and test for it being a png */
2555  fp = fopen(file_name, "rb");
2556  if (!fp) {
2557  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "File %s could not be opened for reading\n", file_name);
2558  goto end;
2559  }
2560 
2561  fread(header, 1, 8, fp);
2562  if (png_sig_cmp(header, 0, 8)) {
2563  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "File %s is not recognized as a PNG file\n", file_name);
2564  goto end;
2565  }
2566 
2567  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
2568  if (!png_ptr) {
2569  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "png_create_read_struct failed\n");
2570  goto end;
2571  }
2572 
2573  info_ptr = png_create_info_struct(png_ptr);
2574  if (!info_ptr) {
2575  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "png_create_info_struct failed\n");
2576  goto end;
2577  }
2578 
2579  if (setjmp(png_jmpbuf(png_ptr))) {
2580  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during init_io\n");
2581  goto end;
2582  }
2583 
2584  png_init_io(png_ptr, fp);
2585  png_set_sig_bytes(png_ptr, 8);
2586  png_read_info(png_ptr, info_ptr);
2587 
2588  width = png_get_image_width(png_ptr, info_ptr);
2589  height = png_get_image_height(png_ptr, info_ptr);
2590  color_type = png_get_color_type(png_ptr, info_ptr);
2591  bit_depth = png_get_bit_depth(png_ptr, info_ptr);
2592  //number_of_passes = png_set_interlace_handling(png_ptr);
2593 
2594  /* set up the transformations you want. Note that these are
2595  all optional. Only call them if you want them */
2596 
2597  /* expand paletted colors into true rgb */
2598  if (color_type == PNG_COLOR_TYPE_PALETTE) {
2599  png_set_expand(png_ptr);
2600  }
2601 
2602  /* expand grayscale images to the full 8 bits */
2603  if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
2604  png_set_expand(png_ptr);
2605  }
2606 
2607  /* expand images with transparency to full alpha channels */
2608  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
2609  png_set_expand(png_ptr);
2610  }
2611 
2612  /* Set the background color to draw transparent and alpha images over */
2613  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) {
2614  // png_get_bKGD(png_ptr, info_ptr, &my_background);
2615  // png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
2616  } else {
2617  // png_color_16 my_background = { 0 }; //{index,r, g, b, grey}
2618  // png_color_16 my_background = {0, 99, 99, 99, 0};
2619  // png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
2620  }
2621 
2622  /* tell libpng to handle the gamma conversion for you */
2623  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) {
2624  // png_set_gamma(png_ptr, screen_gamma, info_ptr->gamma);
2625  } else {
2626  // png_set_gamma(png_ptr, screen_gamma, 0.45);
2627  }
2628 
2629  /* tell libpng to strip 16 bit depth files down to 8 bits */
2630  if (bit_depth == 16) {
2631  png_set_strip_16(png_ptr);
2632  }
2633 
2634 #if 0
2635  /* dither rgb files down to 8 bit palettes & reduce palettes
2636  to the number of colors available on your screen */
2637  if (0 && color_type & PNG_COLOR_MASK_COLOR) {
2638  if (png_get_valid(png_ptr, info_ptr, & PNG_INFO_PLTE)) {
2639  png_set_dither(png_ptr, info_ptr->palette,
2640  info_ptr->num_palette, max_screen_colors,
2641  info_ptr->histogram);
2642  } else {
2643  png_color std_color_cube[MAX_SCREEN_COLORS] =
2644  {/* ... colors ... */};
2645 
2646  png_set_dither(png_ptr, std_color_cube, MAX_SCREEN_COLORS,
2647  MAX_SCREEN_COLORS, NULL);
2648  }
2649  }
2650 #endif
2651 
2652  /* invert monocrome files */
2653  if (bit_depth == 1 && color_type == PNG_COLOR_TYPE_GRAY) {
2654  // png_set_invert(png_ptr);
2655  }
2656 
2657  png_get_sBIT(png_ptr, info_ptr, &sig_bit);
2658 
2659  /* shift the pixels down to their true bit depth */
2660  // if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT) && (bit_depth > (*sig_bit).red)) {
2661  // png_set_shift(png_ptr, sig_bit);
2662  // }
2663 
2664  /* pack pixels into bytes */
2665  if (bit_depth < 8) {
2666  png_set_packing(png_ptr);
2667  }
2668 
2669  /* flip the rgb pixels to bgr */
2670  if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
2671  // png_set_bgr(png_ptr);
2672  }
2673 
2674  /* swap bytes of 16 bit files to least significant bit first */
2675  if (bit_depth == 16) {
2676  png_set_swap(png_ptr);
2677  }
2678 
2679  if (0 && color_type & PNG_COLOR_MASK_ALPHA) {
2680  if (setjmp(png_jmpbuf(png_ptr))) {
2682  goto end;
2683  }
2684 
2685  png_set_strip_alpha(png_ptr);
2686  }
2687 
2688  if (setjmp(png_jmpbuf(png_ptr))) {
2689  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Error during read_updated_info\n");
2690  goto end;
2691  }
2692 
2693  if (color_type == PNG_COLOR_TYPE_PALETTE) {
2694  png_set_palette_to_rgb(png_ptr);
2695  }
2696 
2697  png_read_update_info(png_ptr, info_ptr);
2698 
2699  color_type = png_get_color_type(png_ptr, info_ptr);
2700  // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "color_type: 0x%x\n", color_type);
2701 
2702  if (width > SWITCH_IMG_MAX_WIDTH || height > SWITCH_IMG_MAX_HEIGHT) {
2703  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "PNG is too large! %dx%d\n", width, height);
2704  }
2705 
2706  row_bytes = png_get_rowbytes(png_ptr, info_ptr);
2707  //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);
2708 
2709  row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
2710  switch_assert(row_pointers);
2711 
2712  buffer = (png_byte *)malloc(row_bytes * height);
2713  switch_assert(buffer);
2714 
2715  for (y = 0; y < height; y++) {
2716  row_pointers[y] = buffer + row_bytes * y;
2717  }
2718 
2719  /* read file */
2720  if (setjmp(png_jmpbuf(png_ptr))) {
2721  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during read_image");
2722  goto end;
2723  }
2724 
2725  png_read_image(png_ptr, row_pointers);
2726 
2727  if (color_type == PNG_COLOR_TYPE_RGBA) {
2728  if (row_bytes > width * 4) {
2729  for(y = 1; y < height; y++) {
2730  memcpy(buffer + y * width * 4, row_pointers[y], width * 4);
2731  }
2732  }
2733 
2734  img = switch_img_alloc(NULL, img_fmt, width, height, 1);
2735  switch_assert(img);
2736 
2737  if (img_fmt == SWITCH_IMG_FMT_I420) {
2738  ABGRToI420(buffer, width * 4,
2742  width, height);
2743  } else if (img_fmt == SWITCH_IMG_FMT_ARGB) {
2744  BGRAToARGB(buffer, width * 4,
2745  img->planes[SWITCH_PLANE_PACKED], width * 4,
2746  width, height);
2747  }
2748  } else if (color_type == PNG_COLOR_TYPE_RGB) {
2749  if (row_bytes > width * 3) {
2750  for(y = 1; y < height; y++) {
2751  memcpy(buffer + y * width * 3, row_pointers[y], width * 3);
2752  }
2753  }
2754 
2755  if (img_fmt == SWITCH_IMG_FMT_ARGB) {
2756  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No alpha channel in image [%s], fallback to I420\n", file_name);
2757  img_fmt = SWITCH_IMG_FMT_I420;
2758  }
2759 
2760  img = switch_img_alloc(NULL, img_fmt, width, height, 1);
2761  switch_assert(img);
2762 
2763  RAWToI420(buffer, width * 3,
2767  width, height);
2768  } else {
2769  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unsupported color type: %d\n", png_get_color_type(png_ptr, info_ptr));
2770  }
2771 
2772 end:
2773  switch_safe_free(buffer);
2774  switch_safe_free(row_pointers);
2775  if (fp) fclose(fp);
2776  if (info_ptr) png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
2777 
2778  return img;
2779 }
2780 
2782 {
2783  return NULL;
2784 }
2785 
2786 #endif
2787 
2788 static void my_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
2789 {
2790  switch_buffer_t *data_buffer = (switch_buffer_t *)png_get_io_ptr(png_ptr);
2791  switch_buffer_write(data_buffer, data, length);
2792 }
2793 
2794 static void my_png_flush(png_structp png_ptr)
2795 {
2796 }
2797 
2799 {
2800  int width, height;
2801  png_byte color_type;
2802  png_byte bit_depth;
2803  png_structp png_ptr;
2804  png_infop info_ptr;
2805  png_bytep *row_pointers = NULL;
2806  int row_bytes;
2807  int y;
2808  png_byte *buffer = NULL;
2810  switch_buffer_t *data_buffer = NULL;
2811  unsigned char *head;
2812 
2813  width = img->d_w;
2814  height = img->d_h;
2815  bit_depth = 8;
2816  color_type = PNG_COLOR_TYPE_RGB;
2817 
2818  if (img->fmt == SWITCH_IMG_FMT_ARGB) {
2819  color_type = PNG_COLOR_TYPE_RGBA;
2820  }
2821 
2822 
2823  png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
2824 
2825  if (!png_ptr) {
2826  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "png_create_write_struct failed");
2827  goto end;
2828  }
2829 
2830  info_ptr = png_create_info_struct(png_ptr);
2831  if (!info_ptr) {
2832  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "png_create_info_struct failed");
2833  goto end;
2834  }
2835 
2836  if (setjmp(png_jmpbuf(png_ptr))) {
2837  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during init_io");
2838  goto end;
2839  }
2840 
2841  switch_buffer_create_dynamic(&data_buffer, 1024, 1024, 0);
2842  png_set_write_fn(png_ptr, data_buffer, my_png_write_data, my_png_flush);
2843  //png_init_io(png_ptr, fp);
2844 
2845  if (setjmp(png_jmpbuf(png_ptr))) {
2846  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during writing header");
2847  goto end;
2848  }
2849 
2850  png_set_IHDR(png_ptr, info_ptr, width, height,
2851  bit_depth, color_type, PNG_INTERLACE_NONE,
2852  PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
2853 
2854  png_write_info(png_ptr, info_ptr);
2855 
2856  row_bytes = png_get_rowbytes(png_ptr, info_ptr);
2857  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);
2858 
2859  buffer = (png_byte *)malloc(row_bytes * height);
2860  switch_assert(buffer);
2861 
2862  row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
2863  switch_assert(row_pointers);
2864 
2865  for (y = 0; y < height; y++) {
2866  row_pointers[y] = buffer + row_bytes * y;
2867  }
2868 
2869  if (img->fmt == SWITCH_IMG_FMT_I420) {
2870  I420ToRAW( img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
2873  buffer, width * 3,
2874  width, height);
2875  } else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
2876  ARGBToABGR(img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED],
2877  buffer, row_bytes, width, height);
2878  }
2879 
2880  if (setjmp(png_jmpbuf(png_ptr))) {
2881  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during writing bytes");
2882  goto end;
2883  }
2884 
2885  //png_set_rows(png_ptr, info_ptr, row_pointers);
2886  //png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
2887  png_write_image(png_ptr, row_pointers);
2888 
2889  if (setjmp(png_jmpbuf(png_ptr))) {
2890  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during end of write");
2891  goto end;
2892  }
2893 
2894  png_write_end(png_ptr, info_ptr);
2895 
2896  if ((head = (unsigned char *) switch_buffer_get_head_pointer(data_buffer))) {
2897  switch_size_t olen = 0, blen = 0;
2898  unsigned char *out = NULL;
2899  const char *header = "data:image/png;base64,";
2900 
2901  blen = switch_buffer_len(data_buffer);
2902  olen = blen * 4;
2903 
2904  if (olen > strlen(header) + 1) {
2905  switch_zmalloc(out, olen);
2906 
2907  switch_snprintf((char *)out, strlen(header) + 1, header);
2908  switch_b64_encode(head, blen, out + strlen(header), olen - strlen(header));
2909  *urlP = (char *)out;
2910  } else {
2911  status = SWITCH_STATUS_MEMERR;
2912  goto end;
2913  }
2914  }
2915 
2916  status = SWITCH_STATUS_SUCCESS;
2917 
2918 end:
2919 
2920  if (status != SWITCH_STATUS_SUCCESS) {
2921  *urlP = NULL;
2922  }
2923 
2924  switch_buffer_destroy(&data_buffer);
2925  switch_safe_free(buffer);
2926  switch_safe_free(row_pointers);
2927  png_destroy_write_struct(&png_ptr, &info_ptr);
2928 
2929  return status;
2930 }
2931 
2932 #ifdef PNG_SIMPLIFIED_WRITE_SUPPORTED /* available from libpng 1.6.0 */
2933 
2935 {
2936  png_image png = { 0 };
2937  png_bytep buffer = NULL;
2939 
2940  if (img->fmt == SWITCH_IMG_FMT_I420) {
2941  png.format = PNG_FORMAT_RGB;
2942  buffer = malloc(img->d_w * img->d_h * 3);
2943  switch_assert(buffer);
2944 
2945  I420ToRAW(img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
2948  buffer, img->d_w * 3,
2949  img->d_w, img->d_h);
2950  } else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
2951 #if SWITCH_BYTE_ORDER == __BIG_ENDIAN
2952  png.format = PNG_FORMAT_ARGB;
2953 #else
2954  png.format = PNG_FORMAT_BGRA;
2955 #endif
2956  buffer = img->planes[SWITCH_PLANE_PACKED];
2957  }
2958 
2959  png.version = PNG_IMAGE_VERSION;
2960  png.width = img->d_w;
2961  png.height = img->d_h;
2962 
2963  if (!png_image_write_to_file(&png, file_name, 0, buffer, 0, NULL)) {
2964  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error write PNG %s\n", file_name);
2965  status = SWITCH_STATUS_FALSE;
2966  }
2967 
2968  if (img->fmt == SWITCH_IMG_FMT_I420) {
2969  free(buffer);
2970  }
2971 
2972  return status;
2973 }
2974 
2975 #else
2976 
2977 
2979 {
2980  int width, height;
2981  png_byte color_type;
2982  png_byte bit_depth;
2983  png_structp png_ptr;
2984  png_infop info_ptr;
2985  png_bytep *row_pointers = NULL;
2986  int row_bytes;
2987  int y;
2988  png_byte *buffer = NULL;
2989  FILE *fp = NULL;
2991 
2992  width = img->d_w;
2993  height = img->d_h;
2994  bit_depth = 8;
2995  color_type = PNG_COLOR_TYPE_RGB;
2996 
2997  if (img->fmt == SWITCH_IMG_FMT_ARGB) {
2998  color_type = PNG_COLOR_TYPE_RGBA;
2999  }
3000 
3001  fp = fopen(file_name, "wb");
3002  if (!fp) {
3003  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "File %s could not be opened for writing", file_name);
3004  goto end;
3005  }
3006 
3007  png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
3008  if (!png_ptr) {
3009  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "png_create_write_struct failed");
3010  goto end;
3011  }
3012 
3013  info_ptr = png_create_info_struct(png_ptr);
3014  if (!info_ptr) {
3015  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "png_create_info_struct failed");
3016  goto end;
3017  }
3018 
3019  if (setjmp(png_jmpbuf(png_ptr))) {
3020  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during init_io");
3021  goto end;
3022  }
3023 
3024  png_init_io(png_ptr, fp);
3025 
3026  if (setjmp(png_jmpbuf(png_ptr))) {
3027  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during writing header");
3028  goto end;
3029  }
3030 
3031  png_set_IHDR(png_ptr, info_ptr, width, height,
3032  bit_depth, color_type, PNG_INTERLACE_NONE,
3033  PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
3034 
3035  png_write_info(png_ptr, info_ptr);
3036 
3037  row_bytes = png_get_rowbytes(png_ptr, info_ptr);
3038  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);
3039 
3040  row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * height);
3041  switch_assert(row_pointers);
3042 
3043  buffer = (png_byte *)malloc(row_bytes * height);
3044  switch_assert(buffer);
3045 
3046  for (y = 0; y < height; y++) {
3047  row_pointers[y] = buffer + row_bytes * y;
3048  }
3049 
3050  if (img->fmt == SWITCH_IMG_FMT_I420) {
3051  I420ToRAW( img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
3054  buffer, width * 3,
3055  width, height);
3056  } else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
3057  ARGBToABGR(img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED],
3058  buffer, row_bytes, width, height);
3059  }
3060 
3061  if (setjmp(png_jmpbuf(png_ptr))) {
3062  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during writing bytes");
3063  goto end;
3064  }
3065 
3066  png_write_image(png_ptr, row_pointers);
3067 
3068  if (setjmp(png_jmpbuf(png_ptr))) {
3069  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error during end of write");
3070  goto end;
3071  }
3072 
3073  png_write_end(png_ptr, NULL);
3074 
3075  status = SWITCH_STATUS_SUCCESS;
3076 
3077 end:
3078 
3079  switch_safe_free(buffer);
3080  switch_safe_free(row_pointers);
3081  fclose(fp);
3082  png_destroy_write_struct(&png_ptr, &info_ptr);
3083 
3084  return status;
3085 }
3086 
3087 #endif
3088 
3089 #else
3090 
3091 SWITCH_DECLARE(switch_status_t) switch_img_patch_png(switch_image_t *img, int x, int y, const char *file_name)
3092 {
3093  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This function is not available, libpng not installed\n");
3094  return SWITCH_STATUS_FALSE;
3095 }
3096 
3098 {
3099  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This function is not available, libpng not installed\n");
3100  return NULL;
3101 }
3102 
3104 {
3105  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This function is not available, libpng not installed\n");
3106  return SWITCH_STATUS_FALSE;
3107 }
3108 
3110 {
3111  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This function is not available, libpng not installed\n");
3112  return SWITCH_STATUS_FALSE;
3113 }
3114 
3115 #endif
3116 
3118 {
3119 #ifdef SWITCH_HAVE_YUV
3120  int width = 0, height = 0, channels = 0;
3121  int comp = STBI_rgb;
3122  unsigned char *data = NULL;
3123 
3124  if (img_fmt == SWITCH_IMG_FMT_I420) {
3125  comp = STBI_rgb;
3126  } else if (img_fmt == SWITCH_IMG_FMT_ARGB) {
3127  comp = STBI_rgb_alpha;
3128  } else {
3129  return NULL;
3130  }
3131 
3132  data = stbi_load(file_name, &width, &height, &channels, comp);
3133  // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%dx%d channels=%d\n", width, height, channels);
3134 
3135  if (data && width > 0 && height > 0) {
3136  switch_image_t *img = switch_img_alloc(NULL, img_fmt, width, height, 1);
3137  switch_assert(img);
3138 
3139  if (img_fmt == SWITCH_IMG_FMT_I420) {
3140  RAWToI420(data, width * 3,
3144  width, height);
3145  } else if (img_fmt == SWITCH_IMG_FMT_ARGB) {
3146 #if SWITCH_BYTE_ORDER == __BIG_ENDIAN
3147  RGBAToARGB(data, width * 4, img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED], width, height);
3148 #else
3149  ABGRToARGB(data, width * 4, img->planes[SWITCH_PLANE_PACKED], img->stride[SWITCH_PLANE_PACKED], width, height);
3150 #endif
3151  }
3152 
3153  stbi_image_free(data);
3154 
3155  return img;
3156  } else if (data) {
3157  stbi_image_free(data);
3158  }
3159 #endif
3160 
3161  return NULL;
3162 }
3163 
3164 SWITCH_DECLARE(switch_status_t) switch_img_write_to_file(switch_image_t *img, const char* file_name, int quality)
3165 {
3166 #ifndef SWITCH_HAVE_YUV
3167  return SWITCH_STATUS_FALSE;
3168 #else
3169  int comp = STBI_rgb;
3170  unsigned char *data = NULL;
3171  const char *ext = strrchr(file_name, '.');
3172  int stride_in_bytes = 0;
3173  int ret = 0;
3174 
3175  if (!ext) return SWITCH_STATUS_FALSE;
3176 
3177  ext++;
3178 
3179  if (img->fmt == SWITCH_IMG_FMT_I420) {
3180  comp = STBI_rgb;
3181  stride_in_bytes = img->d_w * 3;
3182 
3183  data = malloc(stride_in_bytes * img->d_h);
3184  switch_assert(data);
3185 
3186  I420ToRAW(img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
3189  data, stride_in_bytes,
3190  img->d_w, img->d_h);
3191  } else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
3192  comp = STBI_rgb_alpha;
3193  stride_in_bytes = img->d_w * 4;
3194 
3195  data = malloc(stride_in_bytes * img->d_h);
3196  switch_assert(data);
3197 
3198 #if SWITCH_BYTE_ORDER == __BIG_ENDIAN
3199  ARGBToRGBA(img->planes[SWITCH_PLANE_PACKED], stride_in_bytes, data, stride_in_bytes, img->d_w, img->d_h);
3200 #else
3201  ARGBToABGR(img->planes[SWITCH_PLANE_PACKED], stride_in_bytes, data, stride_in_bytes, img->d_w, img->d_h);
3202 #endif
3203  } else {
3204  return SWITCH_STATUS_FALSE;
3205  }
3206 
3207  if (!strcasecmp(ext, "png")) {
3208  ret = stbi_write_png(file_name, img->d_w, img->d_h, comp, (const void *)data, stride_in_bytes);
3209  } else if (!strcasecmp(ext, "jpg") || !strcasecmp(ext, "jpeg")) {
3210  ret = stbi_write_jpg(file_name, img->d_w, img->d_h, comp, (const void *)data, quality);
3211  } else if (!strcasecmp(ext, "bmp")) {
3212  ret = stbi_write_bmp(file_name, img->d_w, img->d_h, comp, (const void *)data);
3213  } else if (!strcasecmp(ext, "tga")) {
3214  ret = stbi_write_tga(file_name, img->d_w, img->d_h, comp, (const void *)data);
3215  } else if (!strcasecmp(ext, "hdr")) {
3216  ret = stbi_write_hdr(file_name, img->d_w, img->d_h, comp, (const float *)data);
3217  } else {
3218  ret = 0;
3219  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unsupported file format [%s]", ext);
3220  }
3221 
3222  free(data);
3223 
3225 #endif
3226 }
3227 
3228 typedef struct data_url_context_s {
3229  const char *type;
3230  char **urlP;
3232 
3233 #ifdef SWITCH_HAVE_YUV
3234 static void data_url_write_func(void *context, void *data, int size)
3235 {
3236  switch_buffer_t *buffer = (switch_buffer_t *)context;
3237  switch_buffer_write(buffer, data, size);
3238 }
3239 #endif
3240 
3241 SWITCH_DECLARE(switch_status_t) switch_img_data_url(switch_image_t *img, char **urlP, const char *type, int quality)
3242 {
3243 #ifndef SWITCH_HAVE_YUV
3244  return SWITCH_STATUS_FALSE;
3245 #else
3246  int comp = STBI_rgb;
3247  unsigned char *data = NULL;
3248  int stride_in_bytes = 0;
3249  int ret = 0;
3250  switch_buffer_t *buffer = NULL;
3251  const char *header = NULL;
3252  int header_len = 0;
3253 
3254  if (!type) return SWITCH_STATUS_FALSE;
3255 
3256  if (img->fmt == SWITCH_IMG_FMT_I420) {
3257  comp = STBI_rgb;
3258  stride_in_bytes = img->d_w * 3;
3259 
3260  data = malloc(stride_in_bytes * img->d_h);
3261  switch_assert(data);
3262 
3263  I420ToRAW(img->planes[SWITCH_PLANE_Y], img->stride[SWITCH_PLANE_Y],
3266  data, stride_in_bytes,
3267  img->d_w, img->d_h);
3268  } else if (img->fmt == SWITCH_IMG_FMT_ARGB) {
3269  comp = STBI_rgb_alpha;
3270  stride_in_bytes = img->d_w * 4;
3271 
3272  data = malloc(stride_in_bytes * img->d_h);
3273  switch_assert(data);
3274 
3275 #if SWITCH_BYTE_ORDER == __BIG_ENDIAN
3276  ARGBToRGBA(img->planes[SWITCH_PLANE_PACKED], stride_in_bytes, data, stride_in_bytes, img->d_w, img->d_h);
3277 #else
3278  ARGBToABGR(img->planes[SWITCH_PLANE_PACKED], stride_in_bytes, data, stride_in_bytes, img->d_w, img->d_h);
3279 #endif
3280  } else {
3281  return SWITCH_STATUS_FALSE;
3282  }
3283 
3284  switch_buffer_create_dynamic(&buffer, 1024, 1024, 0);
3285 
3286  if (!strcmp(type, "png")) {
3287  header = "data:image/png;base64,";
3288  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);
3289  } else if (!strcmp(type, "jpeg") || !strcmp(type, "jpeg")) {
3290  header = "data:image/jpeg;base64,";
3291  ret = stbi_write_jpg_to_func(data_url_write_func, (void *)buffer, img->d_w, img->d_h, comp, (const void *)data, quality);
3292  } else {
3293  ret = 0;
3294  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unsupported file format [%s]\n", type);
3295  }
3296 
3297  if (ret && switch_buffer_inuse(buffer) > 0) {
3298  switch_size_t blen = switch_buffer_inuse(buffer);
3299  switch_size_t olen = blen * 4 + strlen(header) + 1;
3300  uint8_t *data = switch_buffer_get_head_pointer(buffer);
3301  unsigned char *out = NULL;
3302 
3303  switch_zmalloc(out, olen);
3304  header_len = strlen(header);
3305  memcpy(out, header, header_len);
3306  switch_b64_encode(data, blen, out + header_len, olen - header_len);
3307  *urlP = (char *)out;
3308  }
3309 
3310  free(data);
3311  switch_buffer_destroy(&buffer);
3312 
3314 #endif /* SWITCH_HAVE_YUV */
3315 }
3316 
3317 
3318 SWITCH_DECLARE(switch_status_t) switch_img_letterbox(switch_image_t *img, switch_image_t **imgP, int width, int height, const char *color)
3319 {
3320  int img_w = 0, img_h = 0;
3321  double screen_aspect = 0, img_aspect = 0;
3322  int x_pos = 0;
3323  int y_pos = 0;
3324  switch_image_t *IMG = NULL, *scale_img = NULL;
3325  switch_rgb_color_t bgcolor = { 0 };
3326 
3327  switch_assert(imgP);
3328  *imgP = NULL;
3329 
3330  if (img->d_w == width && img->d_h == height) {
3331  switch_img_copy(img, imgP);
3332  return SWITCH_STATUS_SUCCESS;
3333  }
3334 
3335  IMG = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, width, height, 1);
3336  switch_color_set_rgb(&bgcolor, color);
3337  switch_img_fill(IMG, 0, 0, IMG->d_w, IMG->d_h, &bgcolor);
3338 
3339  img_w = IMG->d_w;
3340  img_h = IMG->d_h;
3341 
3342  screen_aspect = (double) IMG->d_w / IMG->d_h;
3343  img_aspect = (double) img->d_w / img->d_h;
3344 
3345 
3346  if (screen_aspect > img_aspect) {
3347  img_w = img_aspect * IMG->d_h;
3348  x_pos = (IMG->d_w - img_w) / 2;
3349  } else if (screen_aspect < img_aspect) {
3350  img_h = IMG->d_w / img_aspect;
3351  y_pos = (IMG->d_h - img_h) / 2;
3352  }
3353 
3354  switch_img_scale(img, &scale_img, img_w, img_h);
3355  switch_img_patch(IMG, scale_img, x_pos, y_pos);
3356  switch_img_free(&scale_img);
3357 
3358  *imgP = IMG;
3359 
3360  return SWITCH_STATUS_SUCCESS;
3361 }
3362 
3363 SWITCH_DECLARE(void) switch_img_calc_fit(switch_image_t *src, int width, int height, int *new_wP, int *new_hP)
3364 {
3365  int new_w, new_h;
3366 
3367  switch_assert(src);
3368 
3369  new_w = src->d_w;
3370  new_h = src->d_h;
3371 
3372  if (src->d_w < width && src->d_h < height) {
3373  float rw = (float)new_w / width;
3374  float rh = (float)new_h / height;
3375 
3376  if (rw > rh) {
3377  new_h = (int)((float)new_h / rw);
3378  new_w = width;
3379  } else {
3380  new_w = (int)((float)new_w / rh);
3381  new_h = height;
3382  }
3383  } else {
3384  while(new_w > width || new_h > height) {
3385  if (new_w > width) {
3386  double m = (double) width / new_w;
3387  new_w = width;
3388  new_h = (int) (new_h * m);
3389  } else {
3390  double m = (double) height / new_h;
3391  new_h = height;
3392  new_w = (int) (new_w * m);
3393  }
3394  }
3395  }
3396 
3397  *new_wP = new_w;
3398  *new_hP = new_h;
3399 }
3400 
3402 {
3403  switch_image_t *src, *tmp = NULL;
3404  int new_w = 0, new_h = 0;
3405 
3406  switch_assert(srcP);
3407  switch_assert(width && height);
3408 
3409  src = *srcP;
3410 
3411  if (!src || (src->d_w == width && src->d_h == height)) {
3412  return SWITCH_STATUS_SUCCESS;
3413  }
3414 
3415  if (fit == SWITCH_FIT_NECESSARY && src->d_w <= width && src->d_h < height) {
3416  return SWITCH_STATUS_SUCCESS;
3417  }
3418 
3419  if (fit == SWITCH_FIT_SCALE) {
3420  switch_img_scale(src, &tmp, width, height);
3421  switch_img_free(&src);
3422  *srcP = tmp;
3423  return SWITCH_STATUS_SUCCESS;
3424  }
3425 
3426  switch_img_calc_fit(src, width, height, &new_w, &new_h);
3427 
3428  if (new_w && new_h) {
3429  if (switch_img_scale(src, &tmp, new_w, new_h) == SWITCH_STATUS_SUCCESS) {
3430  switch_img_free(&src);
3431  *srcP = tmp;
3432 
3433  if (fit == SWITCH_FIT_SIZE_AND_SCALE) {
3434  src = *srcP;
3435  tmp = NULL;
3436  switch_img_scale(src, &tmp, width, height);
3437  switch_img_free(&src);
3438  *srcP = tmp;
3439  }
3440 
3441  return SWITCH_STATUS_SUCCESS;
3442  }
3443  }
3444 
3445  return SWITCH_STATUS_FALSE;
3446 }
3447 
3448 #ifdef SWITCH_HAVE_YUV
3449 static inline uint32_t switch_img_fmt2fourcc(switch_img_fmt_t fmt)
3450 {
3451  uint32_t fourcc;
3452 
3453  switch(fmt) {
3454  case SWITCH_IMG_FMT_NONE: fourcc = (uint32_t)FOURCC_ANY ; break;
3455  case SWITCH_IMG_FMT_RGB24: fourcc = (uint32_t)FOURCC_24BG; break;
3456  case SWITCH_IMG_FMT_RGB32: fourcc = (uint32_t)FOURCC_ANY ; break;
3457  case SWITCH_IMG_FMT_RGB565: fourcc = (uint32_t)FOURCC_ANY ; break;
3458  case SWITCH_IMG_FMT_RGB555: fourcc = (uint32_t)FOURCC_ANY ; break;
3459  case SWITCH_IMG_FMT_UYVY: fourcc = (uint32_t)FOURCC_ANY ; break;
3460  case SWITCH_IMG_FMT_YUY2: fourcc = (uint32_t)FOURCC_YUY2; break;
3461  case SWITCH_IMG_FMT_YVYU: fourcc = (uint32_t)FOURCC_ANY ; break;
3462  case SWITCH_IMG_FMT_BGR24: fourcc = (uint32_t)FOURCC_RAW ; break;
3463  case SWITCH_IMG_FMT_RGB32_LE: fourcc = (uint32_t)FOURCC_ANY ; break;
3464  case SWITCH_IMG_FMT_ARGB: fourcc = (uint32_t)FOURCC_ARGB; break;
3465  case SWITCH_IMG_FMT_ARGB_LE: fourcc = (uint32_t)FOURCC_ANY ; break;
3466  case SWITCH_IMG_FMT_RGB565_LE: fourcc = (uint32_t)FOURCC_ANY ; break;
3467  case SWITCH_IMG_FMT_RGB555_LE: fourcc = (uint32_t)FOURCC_ANY ; break;
3468  case SWITCH_IMG_FMT_YV12: fourcc = (uint32_t)FOURCC_ANY ; break;
3469  case SWITCH_IMG_FMT_I420: fourcc = (uint32_t)FOURCC_I420; break;
3470  case SWITCH_IMG_FMT_VPXYV12: fourcc = (uint32_t)FOURCC_ANY ; break;
3471  case SWITCH_IMG_FMT_VPXI420: fourcc = (uint32_t)FOURCC_ANY ; break;
3472  case SWITCH_IMG_FMT_I422: fourcc = (uint32_t)FOURCC_ANY ; break;
3473  case SWITCH_IMG_FMT_I444: fourcc = (uint32_t)FOURCC_ANY ; break;
3474  case SWITCH_IMG_FMT_I440: fourcc = (uint32_t)FOURCC_ANY ; break;
3475  case SWITCH_IMG_FMT_444A: fourcc = (uint32_t)FOURCC_ANY ; break;
3476  case SWITCH_IMG_FMT_I42016: fourcc = (uint32_t)FOURCC_ANY ; break;
3477  case SWITCH_IMG_FMT_I42216: fourcc = (uint32_t)FOURCC_ANY ; break;
3478  case SWITCH_IMG_FMT_I44416: fourcc = (uint32_t)FOURCC_ANY ; break;
3479  case SWITCH_IMG_FMT_I44016: fourcc = (uint32_t)FOURCC_ANY ; break;
3480  default: fourcc = (uint32_t)FOURCC_ANY;
3481  }
3482 
3483  return fourcc;
3484 }
3485 #endif
3486 
3488 {
3489 #ifdef SWITCH_HAVE_YUV
3490  uint32_t fourcc;
3491  int ret;
3492 
3493  switch_assert(dest);
3494 
3495  fourcc = switch_img_fmt2fourcc(fmt);
3496 
3497  if (fourcc == FOURCC_ANY) {
3498  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unsupported format: %d\n", fmt);
3499  return SWITCH_STATUS_FALSE;
3500  }
3501 
3502  if (src->fmt == SWITCH_IMG_FMT_I420) {
3503  ret = ConvertFromI420(src->planes[0], src->stride[0],
3504  src->planes[1], src->stride[1],
3505  src->planes[2], src->stride[2],
3506  dest, stride,
3507  src->d_w, src->d_h,
3508  fourcc);
3509  } else if (src->fmt == SWITCH_IMG_FMT_ARGB && fmt == src->fmt) {
3510  ret = ARGBCopy(src->planes[SWITCH_PLANE_PACKED], src->stride[SWITCH_PLANE_PACKED],
3511  dest, stride,
3512  src->d_w, src->d_h);
3513  } else {
3514  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Convertion not supported %d -> %d\n", src->fmt, fmt);
3515  return SWITCH_STATUS_FALSE;
3516  }
3517 
3518  return ret == 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
3519 #else
3520  return SWITCH_STATUS_FALSE;
3521 #endif
3522 }
3523 
3525 {
3526 #ifdef SWITCH_HAVE_YUV
3527  uint32_t fourcc;
3528  int ret = -1;
3529  switch_image_t *dest = NULL;
3530 
3531  if (!destP) {
3532  return SWITCH_STATUS_FALSE;
3533  }
3534 
3535  dest = *destP;
3536 
3537  fourcc = switch_img_fmt2fourcc(fmt);
3538 
3539  if (fourcc == FOURCC_ANY) {
3540  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "unsupported format: %d\n", fmt);
3541  return SWITCH_STATUS_FALSE;
3542  }
3543 
3544  if (!dest && width > 0 && height > 0) dest = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, width, height, 1);
3545  if (!dest) return SWITCH_STATUS_FALSE;
3546 
3547  if (width == 0 || height == 0) {
3548  width = dest->d_w;
3549  height = dest->d_h;
3550  }
3551 
3552 /*
3553  int ConvertToI420(const uint8_t* src_frame, size_t src_size,
3554  uint8_t* dst_y, int dst_stride_y,
3555  uint8_t* dst_u, int dst_stride_u,
3556  uint8_t* dst_v, int dst_stride_v,
3557  int crop_x, int crop_y,
3558  int src_width, int src_height,
3559  int crop_width, int crop_height,
3560  enum RotationMode rotation,
3561  uint32 format);
3562 
3563  src_size is only used when FOURCC_MJPG which we don't support so always 0
3564 */
3565 
3566  if (dest->fmt == SWITCH_IMG_FMT_I420) {
3567  ret = ConvertToI420(src, 0,
3568  dest->planes[0], dest->stride[0],
3569  dest->planes[1], dest->stride[1],
3570  dest->planes[2], dest->stride[2],
3571  0, 0,
3572  width, height,
3573  width, height,
3574  0, fourcc);
3575  } else if (dest->fmt == SWITCH_IMG_FMT_ARGB) {
3576  ConvertToARGB(src, 0,
3577  dest->planes[0], width * 4,
3578  0, 0,
3579  width, height,
3580  width, height,
3581  0, fourcc);
3582  }
3583 
3584  *destP = dest;
3585 
3586  return ret == 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
3587 #else
3588  return SWITCH_STATUS_FALSE;
3589 #endif
3590 }
3591 
3593 {
3594 #ifdef SWITCH_HAVE_YUV
3595  switch_image_t *dest = NULL;
3596  int ret = 0;
3597 
3598  if (!destP) {
3599  return SWITCH_STATUS_FALSE;
3600  }
3601 
3602  dest = *destP;
3603 
3604  switch_assert(width > 0);
3605  switch_assert(height > 0);
3606 
3607  if (dest && src->fmt != dest->fmt) switch_img_free(&dest);
3608 
3609  if (!dest) dest = switch_img_alloc(NULL, src->fmt, width, height, 1);
3610 
3611  if (src->fmt == SWITCH_IMG_FMT_I420) {
3612  ret = I420Scale(src->planes[0], src->stride[0],
3613  src->planes[1], src->stride[1],
3614  src->planes[2], src->stride[2],
3615  src->d_w, src->d_h,
3616  dest->planes[0], dest->stride[0],
3617  dest->planes[1], dest->stride[1],
3618  dest->planes[2], dest->stride[2],
3619  width, height,
3620  kFilterBox);
3621  } else if (src->fmt == SWITCH_IMG_FMT_ARGB) {
3622  ret = ARGBScale(src->planes[SWITCH_PLANE_PACKED], src->d_w * 4,
3623  src->d_w, src->d_h,
3624  dest->planes[SWITCH_PLANE_PACKED], width * 4,
3625  width, height,
3626  kFilterBox);
3627  }
3628 
3629  *destP = dest;
3630 
3631  if (ret != 0) {
3632  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Scaling Error: ret: %d\n", ret);
3633  return SWITCH_STATUS_FALSE;
3634  }
3635 
3636  return SWITCH_STATUS_SUCCESS;
3637 #else
3638  return SWITCH_STATUS_FALSE;
3639 #endif
3640 }
3641 
3642 
3644 {
3645 #ifdef SWITCH_HAVE_YUV
3646  switch_image_t *dest = NULL;
3647  int ret = 0;
3648 
3649  if (!destP) {
3650  return SWITCH_STATUS_FALSE;
3651  }
3652 
3653  dest = *destP;
3654 
3655  if (dest && src->fmt != dest->fmt) switch_img_free(&dest);
3656 
3657  if (!dest) dest = switch_img_alloc(NULL, src->fmt, src->d_w, src->d_h, 1);
3658 
3659  if (src->fmt == SWITCH_IMG_FMT_I420) {
3660  ret = I420Mirror(src->planes[0], src->stride[0],
3661  src->planes[1], src->stride[1],
3662  src->planes[2], src->stride[2],
3663  dest->planes[0], dest->stride[0],
3664  dest->planes[1], dest->stride[1],
3665  dest->planes[2], dest->stride[2],
3666  src->d_w, src->d_h);
3667  } else if (src->fmt == SWITCH_IMG_FMT_ARGB) {
3668  ret = ARGBMirror(src->planes[SWITCH_PLANE_PACKED], src->d_w * 4,
3669  dest->planes[SWITCH_PLANE_PACKED], src->d_w * 4,
3670  src->d_w, src->d_h);
3671 
3672  }
3673 
3674  *destP = dest;
3675 
3676  if (ret != 0) {
3677  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Mirror Error: ret: %d\n", ret);
3678  return SWITCH_STATUS_FALSE;
3679  }
3680 
3681  return SWITCH_STATUS_SUCCESS;
3682 #else
3683  return SWITCH_STATUS_FALSE;
3684 #endif
3685 }
3686 
3687 SWITCH_DECLARE(void) switch_img_find_position(switch_img_position_t pos, int sw, int sh, int iw, int ih, int *xP, int *yP)
3688 {
3689  switch(pos) {
3690  case POS_NONE:
3691  case POS_LEFT_TOP:
3692  *xP = 0;
3693  *yP = 0;
3694  break;
3695  case POS_LEFT_MID:
3696  *xP = 0;
3697  *yP = (sh - ih) / 2;
3698  break;
3699  case POS_LEFT_BOT:
3700  *xP = 0;
3701  *yP = (sh - ih);
3702  break;
3703  case POS_CENTER_TOP:
3704  *xP = (sw - iw) / 2;
3705  *yP = 0;
3706  break;
3707  case POS_CENTER_MID:
3708  *xP = (sw - iw) / 2;
3709  *yP = (sh - ih) / 2;
3710  break;
3711  case POS_CENTER_BOT:
3712  *xP = (sw - iw) / 2;
3713  *yP = (sh - ih);
3714  break;
3715  case POS_RIGHT_TOP:
3716  *xP = (sw - iw);
3717  *yP = 0;
3718  break;
3719  case POS_RIGHT_MID:
3720  *xP = (sw - iw);
3721  *yP = (sh - ih) / 2;
3722  break;
3723  case POS_RIGHT_BOT:
3724  *xP = (sw - iw);
3725  *yP = (sh - ih);
3726  break;
3727  };
3728 
3729 }
3730 
3731 #ifdef HAVE_LIBGD
3732 SWITCH_DECLARE(switch_image_t *) switch_img_read_file(const char* file_name)
3733 {
3734  switch_image_t *img = switch_img_alloc(NULL, SWITCH_IMG_FMT_ARGB, 1, 1, 1);
3735  gdImagePtr gd = NULL;
3736  char *ext;
3737  FILE *fp;
3738 
3739  if (!img) return NULL;
3740 
3741  // gd = gdImageCreateFromFile(file_name); // only available in 2.1.1
3742 
3743  ext = strrchr(file_name, '.');
3744  if (!ext) goto err;
3745 
3746  fp = fopen(file_name, "rb");
3747  if (!fp) goto err;
3748 
3749  if (!strcmp(ext, ".png")) {
3750  gd = gdImageCreateFromPng(fp);
3751  } else if (!strcmp(ext, ".gif")) {
3752  gd = gdImageCreateFromGif(fp);
3753  } else if (!strcmp(ext, ".jpg") || !strcmp(ext, ".jpeg")) {
3754  gd = gdImageCreateFromJpeg(fp);
3755  } else {
3756  switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Not supported file type: %s\n", ext);
3757  }
3758 
3759  fclose(fp);
3760  if (!gd) goto err;
3761 
3762  img->fmt = SWITCH_IMG_FMT_GD;
3763  img->d_w = gd->sx;
3764  img->d_h = gd->sy;
3765  img->user_priv = gd;
3766  return img;
3767 
3768 err:
3769  switch_img_free(&img);
3770  return NULL;
3771 }
3772 #else
3774 {
3775  return NULL;
3776 }
3777 #endif
3778 
3779 SWITCH_DECLARE(switch_status_t) switch_I420_copy(const uint8_t *src_y, int src_stride_y,
3780  const uint8_t *src_u, int src_stride_u,
3781  const uint8_t *src_v, int src_stride_v,
3782  uint8_t *dst_y, int dst_stride_y,
3783  uint8_t *dst_u, int dst_stride_u,
3784  uint8_t *dst_v, int dst_stride_v,
3785  int width, int height)
3786 {
3787 #ifdef SWITCH_HAVE_YUV
3788  int ret = I420Copy(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
3789  dst_y, dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v,
3790  width, height);
3791  return ret == 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
3792 #else
3793  return SWITCH_STATUS_FALSE;
3794 #endif
3795 }
3796 
3797 SWITCH_DECLARE(switch_status_t) switch_I420_copy2(uint8_t *src_planes[], int src_stride[],
3798  uint8_t *dst_planes[], int dst_stride[],
3799  int width, int height)
3800 {
3801 #ifdef SWITCH_HAVE_YUV
3802  int ret = I420Copy(src_planes[SWITCH_PLANE_Y], src_stride[SWITCH_PLANE_Y],
3803  src_planes[SWITCH_PLANE_U], src_stride[SWITCH_PLANE_U],
3804  src_planes[SWITCH_PLANE_V], src_stride[SWITCH_PLANE_V],
3805  dst_planes[SWITCH_PLANE_Y], dst_stride[SWITCH_PLANE_Y],
3806  dst_planes[SWITCH_PLANE_U], dst_stride[SWITCH_PLANE_U],
3807  dst_planes[SWITCH_PLANE_V], dst_stride[SWITCH_PLANE_V],
3808  width, height);
3809  return ret == 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
3810 #else
3811  return SWITCH_STATUS_FALSE;
3812 #endif
3813 }
3814 
3815 SWITCH_DECLARE(switch_status_t) switch_I420ToARGB(const uint8_t *src_y, int src_stride_y,
3816  const uint8_t *src_u, int src_stride_u,
3817  const uint8_t *src_v, int src_stride_v,
3818  uint8_t *dst_argb, int dst_stride_argb,
3819  int width, int height)
3820 {
3821 
3822 #ifdef SWITCH_HAVE_YUV
3823  int ret = I420ToARGB(src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v,
3824  dst_argb, dst_stride_argb, width, height);
3825 
3826  return ret == 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
3827 #else
3828  return SWITCH_STATUS_FALSE;
3829 #endif
3830 }
3831 
3832 
3833 SWITCH_DECLARE(switch_status_t) switch_RGBAToARGB(const uint8_t* src_frame, int src_stride_frame,
3834  uint8_t* dst_argb, int dst_stride_argb,
3835  int width, int height)
3836 {
3837 #ifdef SWITCH_HAVE_YUV
3838  int ret = RGBAToARGB(src_frame, src_stride_frame, dst_argb, dst_stride_argb, width, height);
3839 
3840  return ret == 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
3841 #else
3842  return SWITCH_STATUS_FALSE;
3843 #endif
3844 }
3845 
3846 
3847 SWITCH_DECLARE(switch_status_t) switch_ABGRToARGB(const uint8_t* src_frame, int src_stride_frame,
3848  uint8_t* dst_argb, int dst_stride_argb,
3849  int width, int height)
3850 {
3851 #ifdef SWITCH_HAVE_YUV
3852  int ret = ABGRToARGB(src_frame, src_stride_frame, dst_argb, dst_stride_argb, width, height);
3853 
3854  return ret == 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
3855 #else
3856  return SWITCH_STATUS_FALSE;
3857 #endif
3858 }
3859 
3860 SWITCH_DECLARE(switch_status_t) switch_ARGBToARGB(const uint8_t* src_frame, int src_stride_frame,
3861  uint8_t* dst_argb, int dst_stride_argb,
3862  int width, int height)
3863 {
3864 #ifdef SWITCH_HAVE_YUV
3865  int ret = ARGBToARGB(src_frame, src_stride_frame, dst_argb, dst_stride_argb, width, height);
3866 
3867  return ret == 0 ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
3868 #else
3869  return SWITCH_STATUS_FALSE;
3870 #endif
3871 }
3872 
3873 
3875 {
3876  *filters = 0;
3877 
3878  if (!filter_str) return;
3879 
3880  if (switch_stristr("fg-gray", filter_str)) {
3881  *filters |= SCV_FILTER_GRAY_FG;
3882  }
3883 
3884  if (switch_stristr("bg-gray", filter_str)) {
3885  *filters |= SCV_FILTER_GRAY_BG;
3886  }
3887 
3888  if (switch_stristr("fg-sepia", filter_str)) {
3889  *filters |= SCV_FILTER_SEPIA_FG;
3890  }
3891 
3892  if (switch_stristr("bg-sepia", filter_str)) {
3893  *filters |= SCV_FILTER_SEPIA_BG;
3894  }
3895 
3896  if (switch_stristr("fg-8bit", filter_str)) {
3897  *filters |= SCV_FILTER_8BIT_FG;
3898  }
3899 }
3900 
3901 
3902 
3903 
3904 /* For Emacs:
3905  * Local Variables:
3906  * mode:c
3907  * indent-tabs-mode:t
3908  * tab-width:4
3909  * c-basic-offset:4
3910  * End:
3911  * For VIM:
3912  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
3913  */
#define switch_core_new_memory_pool(p)
Create a new sub memory pool from the core&#39;s master pool.
Definition: switch_core.h:633
switch_status_t switch_chromakey_create(switch_chromakey_t **ckP)
void vpx_img_free(vpx_image_t *img)
Close an image descriptor.
switch_image_t * cache_img
Image Descriptor.
Definition: switch_image.h:88
switch_size_t switch_buffer_len(_In_ switch_buffer_t *buffer)
Get the length of a switch_buffer_t.
switch_rgb_color_t color
switch_status_t switch_chromakey_autocolor(switch_chromakey_t *ck, switch_shade_t autocolor, uint32_t threshold)
#define SWITCH_CHANNEL_LOG
vpx_image_t * vpx_img_alloc(vpx_image_t *img, vpx_img_fmt_t fmt, unsigned int d_w, unsigned int d_h, unsigned int align)
Open a descriptor, allocating storage for the underlying image.
switch_image_t * switch_img_read_from_file(const char *file_name, switch_img_fmt_t img_fmt)
Read an image file to switch_image_t.
static struct pos_el POS_TABLE[]
switch_image_t * switch_img_read_file(const char *file_name)
switch_img_position_t pos
#define SWITCH_IMG_FMT_I444
Definition: switch_vpx.h:81
switch_rgb_color_t auto_color
switch_image_t * switch_chromakey_cache_image(switch_chromakey_t *ck)
switch_status_t switch_chromakey_add_color(switch_chromakey_t *ck, switch_rgb_color_t *color, uint32_t threshold)
switch_status_t switch_buffer_create_dynamic(_Out_ switch_buffer_t **buffer, _In_ switch_size_t blocksize, _In_ switch_size_t start_len, _In_ switch_size_t max_len)
Allocate a new dynamic switch_buffer.
#define SWITCH_IMG_FMT_RGB24
Definition: switch_vpx.h:63
struct data_url_context_s data_url_context_t
const char * name
switch_image_t * switch_img_write_text_img(int w, int h, switch_bool_t full, const char *text)
switch_rgb_color_t mask[CHROMAKEY_MAX_MASK]
int vpx_img_set_rect(vpx_image_t *img, unsigned int x, unsigned int y, unsigned int w, unsigned int h)
Set the rectangle identifying the displayed portion of the image.
#define SWITCH_IMG_MAX_HEIGHT
switch_status_t switch_chromakey_clear_colors(switch_chromakey_t *ck)
void switch_img_patch_rgb(switch_image_t *IMG, switch_image_t *img, int x, int y, switch_bool_t noalpha)
patch a small img to a big IMG at position x,y
void switch_img_8bit(switch_image_t *img)
switch_bool_t
Definition: switch_types.h:437
switch_status_t switch_chromakey_destroy(switch_chromakey_t **ckP)
#define SWITCH_IMG_FMT_VPXYV12
Definition: switch_vpx.h:78
#define switch_core_strdup(_pool, _todup)
Copy a string using memory allocation from a given pool.
Definition: switch_core.h:733
const cJSON *const b
Definition: switch_cJSON.h:243
#define switch_split(_data, _delim, _array)
Definition: switch_utils.h:375
#define SWITCH_IMG_FMT_I440
Definition: switch_vpx.h:82
#define SWITCH_IMG_FMT_RGB555
Definition: switch_vpx.h:66
#define switch_core_destroy_memory_pool(p)
Returns a subpool back to the main pool.
Definition: switch_core.h:642
switch_memory_pool_t * pool
void switch_img_rotate_copy(switch_image_t *img, switch_image_t **new_img, switch_image_rotation_mode_t mode)
Representation of a rectangle on a surface.
#define SWITCH_IMG_FMT_YVYU
Definition: switch_vpx.h:69
switch_status_t switch_img_data_url_png(switch_image_t *img, char **urlP)
switch_status_t switch_img_from_raw(switch_image_t **destP, void *src, switch_img_fmt_t fmt, int width, int height)
convert raw memory to switch_img_t
switch_status_t switch_img_write_png(switch_image_t *img, char *file_name)
void switch_img_gray(switch_image_t *img, int x, int y, int w, int h)
#define SWITCH_PLANE_V
Definition: switch_vpx.h:55
#define end_of(_s)
Definition: switch_utils.h:685
#define SWITCH_PLANE_PACKED
Definition: switch_vpx.h:52
void switch_img_copy(switch_image_t *img, switch_image_t **new_img)
Copy image to a new image.
#define SWITCH_IMG_FMT_ARGB
Definition: switch_vpx.h:72
switch_status_t switch_img_fit(switch_image_t **srcP, int width, int height, switch_img_fit_t fit)
#define SWITCH_IMG_FMT_RGB32
Definition: switch_vpx.h:64
void switch_core_video_parse_filter_string(switch_core_video_filter_t *filters, const char *filter_str)
switch_status_t switch_img_data_url(switch_image_t *img, char **urlP, const char *type, int quality)
int switch_snprintf(_Out_z_cap_(len) char *buf, _In_ switch_size_t len, _In_z_ _Printf_format_string_ const char *format,...)
switch_size_t switch_buffer_write(_In_ switch_buffer_t *buffer, _In_bytecount_(datalen) const void *data, _In_ switch_size_t datalen)
Write data into a switch_buffer_t up to the length of datalen.
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)
switch_image_t * switch_img_read_png(const char *file_name, switch_img_fmt_t img_fmt)
static void scv_tag(void *buffer, int w, int x, int y, uint8_t n)
#define SWITCH_IMG_FMT_I44416
Definition: switch_vpx.h:86
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)
patch part of a small img (x,y,w,h) to a big IMG at position X,Y
switch_img_position_t parse_img_position(const char *name)
void switch_img_free(switch_image_t **img)
Close an image descriptor.
#define SWITCH_IMG_FMT_NONE
Definition: switch_vpx.h:62
switch_shade_t switch_chromakey_str2shade(switch_chromakey_t *ck, const char *shade_name)
void switch_img_txt_handle_destroy(switch_img_txt_handle_t **handleP)
Free a text handle.
void switch_png_free(switch_png_t **pngP)
#define SWITCH_IMG_FMT_YV12
Definition: switch_vpx.h:76
#define zstr(x)
Definition: switch_utils.h:314
int cJSON_bool fmt
Definition: switch_cJSON.h:150
unsigned int d_w
Definition: switch_image.h:99
switch_status_t switch_img_letterbox(switch_image_t *img, switch_image_t **imgP, int width, int height, const char *color)
#define SWITCH_IMG_FMT_VPXI420
Definition: switch_vpx.h:79
void switch_img_attenuate(switch_image_t *img)
switch_png_opaque_t * pvt
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)
#define SWITCH_IMG_FMT_RGB565_LE
Definition: switch_vpx.h:74
#define SWITCH_IMG_FMT_GD
Definition: switch_vpx.h:89
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)
switch_memory_pool_t * pool
#define SWITCH_PATH_SEPARATOR
Definition: switch_types.h:124
static int switch_color_dom_cmp(switch_rgb_color_t *c1, switch_rgb_color_t *c2)
Definition: cJSON.c:68
if((uint32_t)(unpack->cur - unpack->buf) > unpack->buflen)
switch_status_t switch_img_patch_png(switch_image_t *img, int x, int y, const char *file_name)
#define CHROMAKEY_MAX_MASK
int stride[4]
Definition: switch_image.h:117
uint32_t thresholds[CHROMAKEY_MAX_MASK]
switch_img_fit_t
switch_status_t switch_b64_encode(unsigned char *in, switch_size_t ilen, unsigned char *out, switch_size_t olen)
#define switch_core_alloc(_pool, _mem)
Allocate memory directly from a memory pool.
Definition: switch_core.h:684
switch_img_position_t
int index
Definition: switch_cJSON.h:160
static void get_dom(switch_shade_t autocolor, switch_rgb_color_t *color, int *domP, int *aP, int *bP)
vpx_img_fmt_t fmt
Definition: switch_image.h:89
#define switch_zmalloc(ptr, len)
unsigned char * planes[4]
Definition: switch_image.h:116
static int switch_color_distance_multi(switch_rgb_color_t *c1, switch_rgb_color_t *clist, int count, uint32_t *thresholds)
compute distance between a color and a list of colors
void switch_img_rotate(switch_image_t **img, switch_image_rotation_mode_t mode)
Flip the image vertically (top for bottom)
#define switch_safe_free(it)
Free a pointer and set it to NULL unless it already is NULL.
Definition: switch_utils.h:885
switch_shade_t autocolor
switch_byte_t switch_byte_t uint32_t switch_bitpack_mode_t mode
#define SWITCH_IMG_FMT_RGB32_LE
Definition: switch_vpx.h:71
switch_img_fit_t fit
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)
Open a descriptor, using existing storage for the underlying image.
#define SWITCH_IMG_FMT_PLANAR
Definition: switch_vpx.h:47
char const int length
Definition: switch_cJSON.h:153
switch_bool_t switch_core_has_video(void)
#define SWITCH_PLANE_Y
Definition: switch_vpx.h:53
static struct fit_el IMG_FIT_TABLE[]
static void switch_img_draw_pixel(switch_image_t *img, int x, int y, switch_rgb_color_t *color)
Draw a pixel on an image.
switch_status_t switch_png_patch_img(switch_png_t *use_png, switch_image_t *img, int x, int y)
uintptr_t switch_size_t
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)
Render text to an img.
switch_status_t switch_img_write_to_file(switch_image_t *img, const char *file_name, int quality)
Write an image file, supported formats png,jpg,bmp,tga,hdr.
int switch_img_set_rect(switch_image_t *img, unsigned int x, unsigned int y, unsigned int w, unsigned int h)
Set the rectangle identifying the displayed portion of the image.
char * switch_url_decode(char *s)
#define CLAMP(val)
vpx_img_fmt_t switch_img_fmt_t
Definition: switch_vpx.h:91
#define SWITCH_IMG_FMT_I42216
Definition: switch_vpx.h:85
static void switch_img_get_rgb_pixel(switch_image_t *img, switch_rgb_color_t *rgb, int x, int y)
#define SWITCH_PLANE_U
Definition: switch_vpx.h:54
switch_directories SWITCH_GLOBAL_dirs
Definition: switch_core.c:82
void switch_img_overlay(switch_image_t *IMG, switch_image_t *img, int x, int y, uint8_t percent)
put a small img over a big IMG at position x,y, with alpha transparency
switch_status_t switch_I420_copy2(uint8_t *src_planes[], int src_stride[], uint8_t *dst_planes[], int dst_stride[], int width, int height)
void switch_img_chromakey(switch_image_t *img, switch_rgb_color_t *mask, int threshold)
chromakey an img, img must be RGBA and return modified img
switch_status_t switch_img_mirror(switch_image_t *src, switch_image_t **destP)
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)
Created a text handle.
static int switch_color_distance_literal(switch_rgb_color_t *c1, switch_rgb_color_t *c2, int distance)
switch_status_t switch_file_exists(const char *filename, switch_memory_pool_t *pool)
Definition: switch_apr.c:519
void switch_img_add_text(void *buffer, int w, int x, int y, char *s)
void switch_img_fill_noalpha(switch_image_t *img, int x, int y, int w, int h, switch_rgb_color_t *color)
switch_status_t
Common return values.
#define switch_goto_status(_status, _label)
Definition: switch_utils.h:287
static void init_gradient_table(switch_img_txt_handle_t *handle)
#define M_PI
Definition: libteletone.h:100
vpx_image_t * vpx_img_wrap(vpx_image_t *img, vpx_img_fmt_t fmt, unsigned int d_w, unsigned int d_h, unsigned int stride_align, unsigned char *img_data)
Open a descriptor, using existing storage for the underlying image.
#define SWITCH_IMG_FMT_ARGB_LE
Definition: switch_vpx.h:73
static uint8_t scv_art[14][16]
switch_status_t switch_png_open(switch_png_t **pngP, const char *file_name)
void switch_chromakey_set_default_threshold(switch_chromakey_t *ck, uint32_t threshold)
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)
Open a descriptor, allocating storage for the underlying image.
Main Library Header.
switch_status_t switch_img_to_raw(switch_image_t *src, void *dest, int stride, switch_img_fmt_t fmt)
convert img to raw format
static switch_bool_t switch_is_file_path(const char *file)
#define SWITCH_DECLARE(type)
static int get_max(switch_rgb_color_t *c1)
#define SWITCH_IMG_FMT_UYVY
Definition: switch_vpx.h:67
#define SWITCH_IMG_FMT_I420
Definition: switch_vpx.h:77
#define SWITCH_IMG_FMT_I44016
Definition: switch_vpx.h:87
switch_img_fit_t parse_img_fit(const char *name)
#define SWITCH_IMG_FMT_BGR24
Definition: switch_vpx.h:70
char * buffer
Definition: switch_cJSON.h:153
switch_status_t switch_img_scale(switch_image_t *src, switch_image_t **destP, int width, int height)
void switch_img_patch_hole(switch_image_t *IMG, switch_image_t *img, int x, int y, switch_image_rect_t *rect)
#define SWITCH_IMG_FMT_RGB555_LE
Definition: switch_vpx.h:75
void switch_img_patch(switch_image_t *IMG, switch_image_t *img, int x, int y)
patch a small img to a big IMG at position x,y
uint32_t switch_u8_get_char(char *s, int *i)
Definition: switch_utf8.c:469
#define SWITCH_IMG_FMT_444A
Definition: switch_vpx.h:83
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)
I420 to I420 Copy.
void switch_log_printf(_In_ switch_text_channel_t channel, _In_z_ const char *file, _In_z_ const char *func, _In_ int line, _In_opt_z_ const char *userdata, _In_ switch_log_level_t level, _In_z_ _Printf_format_string_ const char *fmt,...) PRINTF_FUNCTION(7
Write log data to the logging engine.
const char * switch_stristr(const char *instr, const char *str)
#define SWITCH_IMG_FMT_I42016
Definition: switch_vpx.h:84
void switch_color_set_yuv(switch_yuv_color_t *color, const char *str)
Set YUV color with a string.
switch_image_rotation_mode_t
int count
Definition: switch_cJSON.h:204
struct fspr_pool_t switch_memory_pool_t
void switch_chromakey_process(switch_chromakey_t *ck, switch_image_t *img)
void * user_priv
The following member may be set by the application to associate data with this image.
Definition: switch_image.h:124
void switch_img_sepia(switch_image_t *img, int x, int y, int w, int h)
unsigned int d_h
Definition: switch_image.h:100
char * switch_core_sprintf(_In_ switch_memory_pool_t *pool, _In_z_ _Printf_format_string_ const char *fmt,...)
printf-style style printing routine. The data is output to a string allocated from the pool ...
#define switch_assert(expr)
static int switch_color_distance(switch_rgb_color_t *c1, switch_rgb_color_t *c2)
Convert RGB color to YUV.
const char * name
#define SWITCH_IMG_FMT_RGB565
Definition: switch_vpx.h:65
void switch_img_find_position(switch_img_position_t pos, int sw, int sh, int iw, int ih, int *xP, int *yP)
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)
I420 to ARGB Convertion.
static int switch_color_distance_cheap(switch_rgb_color_t *c1, switch_rgb_color_t *c2)
#define MAX_GRADIENT
void switch_color_set_rgb(switch_rgb_color_t *color, const char *str)
Set RGB color with a string.
switch_size_t switch_buffer_inuse(_In_ switch_buffer_t *buffer)
Get the in use amount of a switch_buffer_t.
enum vpx_img_fmt vpx_img_fmt_t
List of supported image formats.
void switch_buffer_destroy(switch_buffer_t **buffer)
Destroy the buffer.
memset(buf, 0, buflen)
void * switch_buffer_get_head_pointer(switch_buffer_t *buffer)
Definition: switch_buffer.c:57
#define SWITCH_IMG_FMT_I422
Definition: switch_vpx.h:80
#define MIN(a, b)
switch_image_t * switch_img_read_png_from_memory(void *mem, size_t size, switch_img_fmt_t img_fmt)
void switch_img_fill(switch_image_t *img, int x, int y, int w, int h, switch_rgb_color_t *color)
Fill image with color.
#define SWITCH_IMG_MAX_WIDTH
switch_shade_t
switch_rgb_color_t bgcolor
switch_image_t * switch_img_copy_rect(switch_image_t *img, uint32_t x, uint32_t y, uint32_t w, uint32_t h)
Copy part of an image to a new image.
#define SWITCH_IMG_FMT_YUY2
Definition: switch_vpx.h:68
void switch_img_calc_fit(switch_image_t *src, int width, int height, int *new_wP, int *new_hP)
switch_rgb_color_t gradient_table[MAX_GRADIENT]
switch_core_video_filter_t