--- /dev/null
+#include "evas_common_private.h"
+#include "evas_xlib_image.h"
+#include "evas_private.h"
+
+#include "Evas_Engine_Software_X11.h"
+#include "evas_engine.h"
+
+#ifdef HAVE_DLSYM
+# include <dlfcn.h> /* dlopen,dlclose,etc */
+#else
+# warning native_tbm should not get compiled if dlsym is not found on the system!
+#endif
+
+#define EVAS_ROUND_UP_4(num) (((num)+3) & ~3)
+#define EVAS_ROUND_UP_8(num) (((num)+7) & ~7)
+
+#define TBM_SURF_PLANE_MAX 4 /**< maximum number of the planes */
+
+/* option to map the tbm_surface */
+#define TBM_SURF_OPTION_READ (1 << 0) /**< access option to read */
+#define TBM_SURF_OPTION_WRITE (1 << 1) /**< access option to write */
+
+#define __tbm_fourcc_code(a,b,c,d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \
+ ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
+
+#define TBM_FORMAT_RGBX8888 __tbm_fourcc_code('R', 'X', '2', '4') /* [31:0] R:G:B:x 8:8:8:8 little endian */
+#define TBM_FORMAT_RGBA8888 __tbm_fourcc_code('R', 'A', '2', '4') /* [31:0] R:G:B:A 8:8:8:8 little endian */
+#define TBM_FORMAT_BGRA8888 __tbm_fourcc_code('B', 'A', '2', '4') /* [31:0] B:G:R:A 8:8:8:8 little endian */
+#define TBM_FORMAT_NV12 __tbm_fourcc_code('N', 'V', '1', '2') /* 2x2 subsampled Cr:Cb plane */
+#define TBM_FORMAT_YUV420 __tbm_fourcc_code('Y', 'U', '1', '2') /* 2x2 subsampled Cb (1) and Cr (2) planes */
+#define TBM_FORMAT_YVU420 __tbm_fourcc_code('Y', 'V', '1', '2') /* 2x2 subsampled Cr (1) and Cb (2) planes */
+
+static void *tbm_lib = NULL;
+static int tbm_ref = 0;
+
+typedef struct _tbm_surface * tbm_surface_h;
+typedef uint32_t tbm_format;
+
+typedef struct _tbm_surface_plane
+{
+ unsigned char *ptr; /**< Plane pointer */
+ uint32_t size; /**< Plane size */
+ uint32_t offset; /**< Plane offset */
+ uint32_t stride; /**< Plane stride */
+
+ void *reserved1; /**< Reserved pointer1 */
+ void *reserved2; /**< Reserved pointer2 */
+ void *reserved3; /**< Reserved pointer3 */
+} tbm_surface_plane_s;
+
+typedef struct _tbm_surface_info
+{
+ uint32_t width; /**< TBM surface width */
+ uint32_t height; /**< TBM surface height */
+ tbm_format format; /**< TBM surface format*/
+ uint32_t bpp; /**< TBM surface bbp */
+ uint32_t size; /**< TBM surface size */
+
+ uint32_t num_planes; /**< The number of planes */
+ tbm_surface_plane_s planes[TBM_SURF_PLANE_MAX]; /**< Array of planes */
+
+ void *reserved4; /**< Reserved pointer4 */
+ void *reserved5; /**< Reserved pointer5 */
+ void *reserved6; /**< Reserved pointer6 */
+} tbm_surface_info_s;
+
+
+/* returns 0 on success */
+static int (*sym_tbm_surface_map) (tbm_surface_h surface, int opt, tbm_surface_info_s *info) = NULL;
+static int (*sym_tbm_surface_unmap) (tbm_surface_h surface) = NULL;
+
+static Eina_Bool
+tbm_init(void)
+{
+ if (tbm_lib)
+ {
+ tbm_ref++;
+ return EINA_TRUE;
+ }
+
+ const char *tbm_libs[] =
+ {
+ "libtbm.so.1",
+ "libtbm.so.0",
+ NULL,
+ };
+ int i, fail;
+#define SYM(lib, xx) \
+ do { \
+ sym_ ## xx = dlsym(lib, #xx); \
+ if (!(sym_ ## xx)) { \
+ ERR("%s", dlerror()); \
+ fail = 1; \
+ } \
+ } while (0)
+
+ for (i = 0; tbm_libs[i]; i++)
+ {
+ tbm_lib = dlopen(tbm_libs[i], RTLD_LOCAL | RTLD_LAZY);
+ if (tbm_lib)
+ {
+ fail = 0;
+ SYM(tbm_lib, tbm_surface_map);
+ SYM(tbm_lib, tbm_surface_unmap);
+ if (fail)
+ {
+ dlclose(tbm_lib);
+ tbm_lib = NULL;
+ }
+ else break;
+ }
+ }
+ if (!tbm_lib) return EINA_FALSE;
+
+ tbm_ref++;
+ return EINA_TRUE;
+}
+
+static void
+tbm_shutdown(void)
+{
+ if (tbm_ref > 0)
+ {
+ tbm_ref--;
+
+ if (tbm_ref == 0)
+ {
+ if (tbm_lib)
+ {
+ dlclose(tbm_lib);
+ tbm_lib = NULL;
+ }
+ }
+ }
+}
+
+static void
+_evas_video_yv12(unsigned char *evas_data, const unsigned char *source_data, unsigned int w, unsigned int h, unsigned int output_height)
+{
+ const unsigned char **rows;
+ unsigned int i, j;
+ unsigned int rh;
+ unsigned int stride_y, stride_uv;
+
+ rh = output_height;
+
+ rows = (const unsigned char **)evas_data;
+
+ stride_y = EVAS_ROUND_UP_4(w);
+ stride_uv = EVAS_ROUND_UP_8(w) / 2;
+
+ for (i = 0; i < rh; i++)
+ rows[i] = &source_data[i * stride_y];
+
+ for (j = 0; j < (rh / 2); j++, i++)
+ rows[i] = &source_data[h * stride_y +
+ (rh / 2) * stride_uv +
+ j * stride_uv];
+
+ for (j = 0; j < (rh / 2); j++, i++)
+ rows[i] = &source_data[h * stride_y + j * stride_uv];
+}
+
+static void
+_evas_video_i420(unsigned char *evas_data, const unsigned char *source_data, unsigned int w, unsigned int h, unsigned int output_height)
+{
+ const unsigned char **rows;
+ unsigned int i, j;
+ unsigned int rh;
+ unsigned int stride_y, stride_uv;
+
+ rh = output_height;
+
+ rows = (const unsigned char **)evas_data;
+
+ stride_y = EVAS_ROUND_UP_4(w);
+ stride_uv = EVAS_ROUND_UP_8(w) / 2;
+
+ for (i = 0; i < rh; i++)
+ rows[i] = &source_data[i * stride_y];
+
+ for (j = 0; j < (rh / 2); j++, i++)
+ rows[i] = &source_data[h * stride_y + j * stride_uv];
+
+ for (j = 0; j < (rh / 2); j++, i++)
+ rows[i] = &source_data[h * stride_y +
+ (rh / 2) * stride_uv +
+ j * stride_uv];
+}
+
+static void
+_evas_video_nv12(unsigned char *evas_data, const unsigned char *source_data, unsigned int w, unsigned int h EINA_UNUSED, unsigned int output_height)
+{
+ const unsigned char **rows;
+ unsigned int i, j;
+ unsigned int rh;
+
+ rh = output_height;
+
+ rows = (const unsigned char **)evas_data;
+
+ for (i = 0; i < rh; i++)
+ rows[i] = &source_data[i * w];
+
+ for (j = 0; j < (rh / 2); j++, i++)
+ rows[i] = &source_data[rh * w + j * w];
+}
+
+static void
+_native_bind_cb(void *data EINA_UNUSED, void *image, int x EINA_UNUSED, int y EINA_UNUSED, int w EINA_UNUSED, int h EINA_UNUSED)
+{
+ RGBA_Image *im = image;
+ Native *n = im->native.data;
+
+ if (!im) return;
+ if ((n) && (n->ns.type == EVAS_NATIVE_SURFACE_TBM))
+ {
+ tbm_surface_info_s info;
+
+ if (sym_tbm_surface_map(n->ns.data.tbm.buffer, TBM_SURF_OPTION_READ|TBM_SURF_OPTION_WRITE, &info)) return;
+
+ im->image.data = (DATA32 *)info.planes[0].ptr;
+ }
+}
+
+static void
+_native_unbind_cb(void *data EINA_UNUSED, void *image)
+{
+ RGBA_Image *im = image;
+ Native *n = im->native.data;
+
+ if (!im) return;
+ if ((n) && (n->ns.type == EVAS_NATIVE_SURFACE_TBM))
+ {
+ sym_tbm_surface_unmap(n->ns.data.tbm.buffer);
+ }
+}
+
+static void
+_native_free_cb(void *data EINA_UNUSED, void *image)
+{
+ RGBA_Image *im = image;
+ Native *n = im->native.data;
+
+ if (!im) return;
+ im->native.data = NULL;
+ im->native.func.bind = NULL;
+ im->native.func.unbind = NULL;
+ im->native.func.free = NULL;
+ im->native.func.data = NULL;
+ im->image.data = NULL;
+
+ free(n);
+
+ tbm_shutdown();
+}
+
+void *
+evas_native_tbm_image_set(void *data EINA_UNUSED, void *image, void *native)
+{
+ Evas_Native_Surface *ns = native;
+ RGBA_Image *im = image;
+
+ if (!im) return NULL;
+ if ((ns) && (ns->type == EVAS_NATIVE_SURFACE_TBM))
+ {
+ void *pixels_data;
+ int w, h, stride;
+ tbm_format format;
+ tbm_surface_info_s info;
+ Native *n;
+
+ if (!tbm_init())
+ {
+ ERR("Could not initialize TBM!");
+ return NULL;
+ }
+
+ n = calloc(1, sizeof(Native));
+ if (!n) return NULL;
+
+ if (sym_tbm_surface_map(ns->data.tbm.buffer, TBM_SURF_OPTION_READ|TBM_SURF_OPTION_WRITE, &info))
+ {
+ free(n);
+ return im;
+ }
+
+ w = info.width;
+ h = info.height;
+ stride = info.planes[0].stride;
+ format = info.format;
+ pixels_data = info.planes[0].ptr;
+ im->cache_entry.w = stride;
+ im->cache_entry.h = h;
+
+ // Handle all possible format here :"(
+ switch (format)
+ {
+ case TBM_FORMAT_RGBA8888:
+ case TBM_FORMAT_RGBX8888:
+ case TBM_FORMAT_BGRA8888:
+ im->cache_entry.w = stride / 4;
+ evas_cache_image_colorspace(&im->cache_entry, EVAS_COLORSPACE_ARGB8888);
+ im->cache_entry.flags.alpha = (format == TBM_FORMAT_RGBX8888 ? 0 : 1);
+ im->image.data = pixels_data;
+ im->image.no_free = 1;
+ break;
+ /* borrowing code from emotion here */
+ case TBM_FORMAT_YVU420: /* EVAS_COLORSPACE_YCBCR422P601_PL */
+ evas_cache_image_colorspace(&im->cache_entry, EVAS_COLORSPACE_YCBCR422P601_PL);
+ _evas_video_yv12(im->cs.data, pixels_data, w, h, h);
+ evas_common_image_colorspace_dirty(im);
+ break;
+ case TBM_FORMAT_YUV420: /* EVAS_COLORSPACE_YCBCR422P601_PL */
+ evas_cache_image_colorspace(&im->cache_entry, EVAS_COLORSPACE_YCBCR422P601_PL);
+ _evas_video_i420(im->cs.data, pixels_data, w, h, h);
+ evas_common_image_colorspace_dirty(im);
+ break;
+ case TBM_FORMAT_NV12: /* EVAS_COLORSPACE_YCBCR420NV12601_PL */
+ evas_cache_image_colorspace(&im->cache_entry, EVAS_COLORSPACE_YCBCR420NV12601_PL);
+ _evas_video_nv12(im->cs.data, pixels_data, w, h, h);
+ evas_common_image_colorspace_dirty(im);
+ break;
+ /* Not planning to handle those in software */
+ default:
+ sym_tbm_surface_unmap(ns->data.tbm.buffer);
+ free(n);
+ return im;
+ }
+
+ memcpy(n, ns, sizeof(Evas_Native_Surface));
+ im->native.data = n;
+ im->native.func.bind = _native_bind_cb;
+ im->native.func.unbind = _native_unbind_cb;
+ im->native.func.free = _native_free_cb;
+
+ sym_tbm_surface_unmap(ns->data.tbm.buffer);
+ }
+ return im;
+}
+