#include "evas_common_private.h" /* Also includes international specific stuff */
#include "evas_private.h"
#include "evas_blend_private.h"

#include "region.h"

#include <software/Ector_Software.h>
#include "evas_ector_software.h"
#include "draw.h"
#include "evas_filter_private.h"

#if defined HAVE_DLSYM && ! defined _WIN32
# include <dlfcn.h>      /* dlopen,dlclose,etc */
#endif

#ifdef _WIN32
# include <evil_private.h> /* dlsym */
#endif

#if defined HAVE_DLSYM
# define EVAS_GL 1
# define EVAS_GL_NO_GL_H_CHECK 1
# include "Evas_GL.h"

#else
# warning software_generic will not be able to have Evas_GL API.
#endif

#include "Evas_Engine_Software_Generic.h"
#include "evas_native_common.h"
#include "filters/evas_engine_filter.h"

#ifdef EVAS_GL
//----------------------------------//
// OSMesa...

#define OSMESA_MAJOR_VERSION 6
#define OSMESA_MINOR_VERSION 5
#define OSMESA_PATCH_VERSION 0

/*
 * Values for the format parameter of OSMesaCreateContext()
 * New in version 2.0.
 */
#define OSMESA_COLOR_INDEX	GL_COLOR_INDEX
#define OSMESA_RGBA		GL_RGBA
#define OSMESA_BGRA		0x1
#define OSMESA_ARGB		0x2
#define OSMESA_RGB		GL_RGB
#define OSMESA_BGR		0x4
#define OSMESA_RGB_565		0x5


/*
 * OSMesaPixelStore() parameters:
 * New in version 2.0.
 */
#define OSMESA_ROW_LENGTH	0x10
#define OSMESA_Y_UP		0x11


/*
 * Accepted by OSMesaGetIntegerv:
 */
#define OSMESA_WIDTH		0x20
#define OSMESA_HEIGHT		0x21
#define OSMESA_FORMAT		0x22
#define OSMESA_TYPE		0x23
#define OSMESA_MAX_WIDTH	0x24  /* new in 4.0 */
#define OSMESA_MAX_HEIGHT	0x25  /* new in 4.0 */

/* Required for orient */
#define TILE 32


typedef void (*OSMESAproc)();
typedef struct osmesa_context *OSMesaContext;
#endif

typedef struct _Render_Engine_GL_Surface    Render_Engine_GL_Surface;
typedef struct _Render_Engine_GL_Context    Render_Engine_GL_Context;

struct _Render_Engine_GL_Surface
{
   int     initialized;
   int     w, h;

#ifdef EVAS_GL
   GLenum  internal_fmt;
#endif
   int     internal_cpp;   // Component per pixel.  ie. RGB = 3

   int     depth_bits;
   int     stencil_bits;

   // Data
   void   *buffer;

   Render_Engine_GL_Context   *current_ctx;
};

#ifdef EVAS_GL
struct _Render_Engine_GL_Context
{
   int            initialized;

   OSMesaContext  context;

   Render_Engine_GL_Context   *share_ctx;

   Render_Engine_GL_Surface   *current_sfc;
};

//------------------------------------------------------//
typedef void                   (*_eng_fn) (void );
typedef _eng_fn                (*glsym_func_eng_fn) ();
typedef void                   (*glsym_func_void) ();
typedef unsigned int           (*glsym_func_uint) ();
typedef int                    (*glsym_func_int) ();
typedef unsigned char          (*glsym_func_uchar) ();
typedef unsigned char         *(*glsym_func_uchar_ptr) ();
typedef const unsigned char   *(*glsym_func_const_uchar_ptr) ();
typedef char const            *(*glsym_func_char_const_ptr) ();
typedef GLboolean              (*glsym_func_bool) ();
typedef OSMesaContext          (*glsym_func_osm_ctx) ();
//------------------------------------------------------//

/* Function table for GL APIs */
static Evas_GL_API gl_funcs;
static void *gl_lib_handle;
static int gl_lib_is_gles = 0;

static Eina_Bool _tls_init = EINA_FALSE;
static Eina_TLS gl_current_ctx_key = 0;
static Eina_TLS gl_current_sfc_key = 0;

//------------------------------------------------------//
// OSMesa APIS...
static OSMesaContext (*_sym_OSMesaCreateContextExt)             (GLenum format, GLint depthBits, GLint stencilBits, GLint accumBits, OSMesaContext sharelist) = NULL;
static void          (*_sym_OSMesaDestroyContext)               (OSMesaContext ctx) = NULL;
static GLboolean     (*_sym_OSMesaMakeCurrent)                  (OSMesaContext ctx, void *buffer, GLenum type, GLsizei width, GLsizei height) = NULL;
static void          (*_sym_OSMesaPixelStore)                   (GLint pname, GLint value) = NULL;
static OSMESAproc    (*_sym_OSMesaGetProcAddress)               (const char *funcName);


//------------------------------------------------------//
// GLES 2.0 APIs...
static void       (*_sym_glActiveTexture)                       (GLenum texture) = NULL;
static void       (*_sym_glAttachShader)                        (GLuint program, GLuint shader) = NULL;
static void       (*_sym_glBindAttribLocation)                  (GLuint program, GLuint index, const char* name) = NULL;
static void       (*_sym_glBindBuffer)                          (GLenum target, GLuint buffer) = NULL;
static void       (*_sym_glBindFramebuffer)                     (GLenum target, GLuint framebuffer) = NULL;
static void       (*_sym_glBindRenderbuffer)                    (GLenum target, GLuint renderbuffer) = NULL;
static void       (*_sym_glBindTexture)                         (GLenum target, GLuint texture) = NULL;
static void       (*_sym_glBlendColor)                          (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) = NULL;
static void       (*_sym_glBlendEquation)                       (GLenum mode) = NULL;
static void       (*_sym_glBlendEquationSeparate)               (GLenum modeRGB, GLenum modeAlpha) = NULL;
static void       (*_sym_glBlendFunc)                           (GLenum sfactor, GLenum dfactor) = NULL;
static void       (*_sym_glBlendFuncSeparate)                   (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) = NULL;
static void       (*_sym_glBufferData)                          (GLenum target, GLsizeiptr size, const void* data, GLenum usage) = NULL;
static void       (*_sym_glBufferSubData)                       (GLenum target, GLintptr offset, GLsizeiptr size, const void* data) = NULL;
static GLenum     (*_sym_glCheckFramebufferStatus)              (GLenum target) = NULL;
static void       (*_sym_glClear)                               (GLbitfield mask) = NULL;
static void       (*_sym_glClearColor)                          (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) = NULL;
static void       (*_sym_glClearDepthf)                         (GLclampf depth) = NULL;
static void       (*_sym_glClearStencil)                        (GLint s) = NULL;
static void       (*_sym_glColorMask)                           (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) = NULL;
static void       (*_sym_glCompileShader)                       (GLuint shader) = NULL;
static void       (*_sym_glCompressedTexImage2D)                (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data) = NULL;
static void       (*_sym_glCompressedTexSubImage2D)             (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) = NULL;
static void       (*_sym_glCopyTexImage2D)                      (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) = NULL;
static void       (*_sym_glCopyTexSubImage2D)                   (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) = NULL;
static GLuint     (*_sym_glCreateProgram)                       (void) = NULL;
static GLuint     (*_sym_glCreateShader)                        (GLenum type) = NULL;
static void       (*_sym_glCullFace)                            (GLenum mode) = NULL;
static void       (*_sym_glDeleteBuffers)                       (GLsizei n, const GLuint* buffers) = NULL;
static void       (*_sym_glDeleteFramebuffers)                  (GLsizei n, const GLuint* framebuffers) = NULL;
static void       (*_sym_glDeleteProgram)                       (GLuint program) = NULL;
static void       (*_sym_glDeleteRenderbuffers)                 (GLsizei n, const GLuint* renderbuffers) = NULL;
static void       (*_sym_glDeleteShader)                        (GLuint shader) = NULL;
static void       (*_sym_glDeleteTextures)                      (GLsizei n, const GLuint* textures) = NULL;
static void       (*_sym_glDepthFunc)                           (GLenum func) = NULL;
static void       (*_sym_glDepthMask)                           (GLboolean flag) = NULL;
static void       (*_sym_glDepthRangef)                         (GLclampf zNear, GLclampf zFar) = NULL;
static void       (*_sym_glDetachShader)                        (GLuint program, GLuint shader) = NULL;
static void       (*_sym_glDisable)                             (GLenum cap) = NULL;
static void       (*_sym_glDisableVertexAttribArray)            (GLuint index) = NULL;
static void       (*_sym_glDrawArrays)                          (GLenum mode, GLint first, GLsizei count) = NULL;
static void       (*_sym_glDrawElements)                        (GLenum mode, GLsizei count, GLenum type, const void* indices) = NULL;
static void       (*_sym_glEnable)                              (GLenum cap) = NULL;
static void       (*_sym_glEnableVertexAttribArray)             (GLuint index) = NULL;
static void       (*_sym_glFinish)                              (void) = NULL;
static void       (*_sym_glFlush)                               (void) = NULL;
static void       (*_sym_glFramebufferRenderbuffer)             (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) = NULL;
static void       (*_sym_glFramebufferTexture2D)                (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) = NULL;
static void       (*_sym_glFrontFace)                           (GLenum mode) = NULL;
static void       (*_sym_glGenBuffers)                          (GLsizei n, GLuint* buffers) = NULL;
static void       (*_sym_glGenerateMipmap)                      (GLenum target) = NULL;
static void       (*_sym_glGenFramebuffers)                     (GLsizei n, GLuint* framebuffers) = NULL;
static void       (*_sym_glGenRenderbuffers)                    (GLsizei n, GLuint* renderbuffers) = NULL;
static void       (*_sym_glGenTextures)                         (GLsizei n, GLuint* textures) = NULL;
static void       (*_sym_glGetActiveAttrib)                     (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) = NULL;
static void       (*_sym_glGetActiveUniform)                    (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) = NULL;
static void       (*_sym_glGetAttachedShaders)                  (GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) = NULL;
static int        (*_sym_glGetAttribLocation)                   (GLuint program, const char* name) = NULL;
static void       (*_sym_glGetBooleanv)                         (GLenum pname, GLboolean* params) = NULL;
static void       (*_sym_glGetBufferParameteriv)                (GLenum target, GLenum pname, GLint* params) = NULL;
static GLenum     (*_sym_glGetError)                            (void) = NULL;
static void       (*_sym_glGetFloatv)                           (GLenum pname, GLfloat* params) = NULL;
static void       (*_sym_glGetFramebufferAttachmentParameteriv) (GLenum target, GLenum attachment, GLenum pname, GLint* params) = NULL;
static void       (*_sym_glGetIntegerv)                         (GLenum pname, GLint* params) = NULL;
static void       (*_sym_glGetProgramiv)                        (GLuint program, GLenum pname, GLint* params) = NULL;
static void       (*_sym_glGetProgramInfoLog)                   (GLuint program, GLsizei bufsize, GLsizei* length, char* infolog) = NULL;
static void       (*_sym_glGetRenderbufferParameteriv)          (GLenum target, GLenum pname, GLint* params) = NULL;
static void       (*_sym_glGetShaderiv)                         (GLuint shader, GLenum pname, GLint* params) = NULL;
static void       (*_sym_glGetShaderInfoLog)                    (GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog) = NULL;
static void       (*_sym_glGetShaderPrecisionFormat)            (GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) = NULL;
static void       (*_sym_glGetShaderSource)                     (GLuint shader, GLsizei bufsize, GLsizei* length, char* source) = NULL;
static const GLubyte *(*_sym_glGetString)                       (GLenum name) = NULL;
static void       (*_sym_glGetTexParameterfv)                   (GLenum target, GLenum pname, GLfloat* params) = NULL;
static void       (*_sym_glGetTexParameteriv)                   (GLenum target, GLenum pname, GLint* params) = NULL;
static void       (*_sym_glGetUniformfv)                        (GLuint program, GLint location, GLfloat* params) = NULL;
static void       (*_sym_glGetUniformiv)                        (GLuint program, GLint location, GLint* params) = NULL;
static int        (*_sym_glGetUniformLocation)                  (GLuint program, const char* name) = NULL;
static void       (*_sym_glGetVertexAttribfv)                   (GLuint index, GLenum pname, GLfloat* params) = NULL;
static void       (*_sym_glGetVertexAttribiv)                   (GLuint index, GLenum pname, GLint* params) = NULL;
static void       (*_sym_glGetVertexAttribPointerv)             (GLuint index, GLenum pname, void** pointer) = NULL;
static void       (*_sym_glHint)                                (GLenum target, GLenum mode) = NULL;
static GLboolean  (*_sym_glIsBuffer)                            (GLuint buffer) = NULL;
static GLboolean  (*_sym_glIsEnabled)                           (GLenum cap) = NULL;
static GLboolean  (*_sym_glIsFramebuffer)                       (GLuint framebuffer) = NULL;
static GLboolean  (*_sym_glIsProgram)                           (GLuint program) = NULL;
static GLboolean  (*_sym_glIsRenderbuffer)                      (GLuint renderbuffer) = NULL;
static GLboolean  (*_sym_glIsShader)                            (GLuint shader) = NULL;
static GLboolean  (*_sym_glIsTexture)                           (GLuint texture) = NULL;
static void       (*_sym_glLineWidth)                           (GLfloat width) = NULL;
static void       (*_sym_glLinkProgram)                         (GLuint program) = NULL;
static void       (*_sym_glPixelStorei)                         (GLenum pname, GLint param) = NULL;
static void       (*_sym_glPolygonOffset)                       (GLfloat factor, GLfloat units) = NULL;
static void       (*_sym_glReadPixels)                          (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) = NULL;
static void       (*_sym_glReleaseShaderCompiler)               (void) = NULL;
static void       (*_sym_glRenderbufferStorage)                 (GLenum target, GLenum internalformat, GLsizei width, GLsizei height) = NULL;
static void       (*_sym_glSampleCoverage)                      (GLclampf value, GLboolean invert) = NULL;
static void       (*_sym_glScissor)                             (GLint x, GLint y, GLsizei width, GLsizei height) = NULL;
static void       (*_sym_glShaderBinary)                        (GLsizei n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLsizei length) = NULL;
static void       (*_sym_glShaderSource)                        (GLuint shader, GLsizei count, const char* const* string, const GLint* length) = NULL;
static void       (*_sym_glStencilFunc)                         (GLenum func, GLint ref, GLuint mask) = NULL;
static void       (*_sym_glStencilFuncSeparate)                 (GLenum face, GLenum func, GLint ref, GLuint mask) = NULL;
static void       (*_sym_glStencilMask)                         (GLuint mask) = NULL;
static void       (*_sym_glStencilMaskSeparate)                 (GLenum face, GLuint mask) = NULL;
static void       (*_sym_glStencilOp)                           (GLenum fail, GLenum zfail, GLenum zpass) = NULL;
static void       (*_sym_glStencilOpSeparate)                   (GLenum face, GLenum fail, GLenum zfail, GLenum zpass) = NULL;
static void       (*_sym_glTexImage2D)                          (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels) = NULL;
static void       (*_sym_glTexParameterf)                       (GLenum target, GLenum pname, GLfloat param) = NULL;
static void       (*_sym_glTexParameterfv)                      (GLenum target, GLenum pname, const GLfloat* params) = NULL;
static void       (*_sym_glTexParameteri)                       (GLenum target, GLenum pname, GLint param) = NULL;
static void       (*_sym_glTexParameteriv)                      (GLenum target, GLenum pname, const GLint* params) = NULL;
static void       (*_sym_glTexSubImage2D)                       (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels) = NULL;
static void       (*_sym_glUniform1f)                           (GLint location, GLfloat x) = NULL;
static void       (*_sym_glUniform1fv)                          (GLint location, GLsizei count, const GLfloat* v) = NULL;
static void       (*_sym_glUniform1i)                           (GLint location, GLint x) = NULL;
static void       (*_sym_glUniform1iv)                          (GLint location, GLsizei count, const GLint* v) = NULL;
static void       (*_sym_glUniform2f)                           (GLint location, GLfloat x, GLfloat y) = NULL;
static void       (*_sym_glUniform2fv)                          (GLint location, GLsizei count, const GLfloat* v) = NULL;
static void       (*_sym_glUniform2i)                           (GLint location, GLint x, GLint y) = NULL;
static void       (*_sym_glUniform2iv)                          (GLint location, GLsizei count, const GLint* v) = NULL;
static void       (*_sym_glUniform3f)                           (GLint location, GLfloat x, GLfloat y, GLfloat z) = NULL;
static void       (*_sym_glUniform3fv)                          (GLint location, GLsizei count, const GLfloat* v) = NULL;
static void       (*_sym_glUniform3i)                           (GLint location, GLint x, GLint y, GLint z) = NULL;
static void       (*_sym_glUniform3iv)                          (GLint location, GLsizei count, const GLint* v) = NULL;
static void       (*_sym_glUniform4f)                           (GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) = NULL;
static void       (*_sym_glUniform4fv)                          (GLint location, GLsizei count, const GLfloat* v) = NULL;
static void       (*_sym_glUniform4i)                           (GLint location, GLint x, GLint y, GLint z, GLint w) = NULL;
static void       (*_sym_glUniform4iv)                          (GLint location, GLsizei count, const GLint* v) = NULL;
static void       (*_sym_glUniformMatrix2fv)                    (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) = NULL;
static void       (*_sym_glUniformMatrix3fv)                    (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) = NULL;
static void       (*_sym_glUniformMatrix4fv)                    (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) = NULL;
static void       (*_sym_glUseProgram)                          (GLuint program) = NULL;
static void       (*_sym_glValidateProgram)                     (GLuint program) = NULL;
static void       (*_sym_glVertexAttrib1f)                      (GLuint indx, GLfloat x) = NULL;
static void       (*_sym_glVertexAttrib1fv)                     (GLuint indx, const GLfloat* values) = NULL;
static void       (*_sym_glVertexAttrib2f)                      (GLuint indx, GLfloat x, GLfloat y) = NULL;
static void       (*_sym_glVertexAttrib2fv)                     (GLuint indx, const GLfloat* values) = NULL;
static void       (*_sym_glVertexAttrib3f)                      (GLuint indx, GLfloat x, GLfloat y, GLfloat z) = NULL;
static void       (*_sym_glVertexAttrib3fv)                     (GLuint indx, const GLfloat* values) = NULL;
static void       (*_sym_glVertexAttrib4f)                      (GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w) = NULL;
static void       (*_sym_glVertexAttrib4fv)                     (GLuint indx, const GLfloat* values) = NULL;
static void       (*_sym_glVertexAttribPointer)                 (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr) = NULL;
static void       (*_sym_glViewport)                            (GLint x, GLint y, GLsizei width, GLsizei height) = NULL;

// GLES Extensions...
/* static void       (*_sym_glGetProgramBinary)                    (GLuint a, GLsizei b, GLsizei* c, GLenum* d, void* e) = NULL; */
/* static void       (*_sym_glProgramBinary)                       (GLuint a, GLenum b, const void* c, GLint d) = NULL; */
/* static void       (*_sym_glProgramParameteri)                   (GLuint a, GLuint b, GLint d) = NULL; */

static int gl_lib_init(void);
#endif

// Threaded Render

typedef struct _Evas_Thread_Command_Rect Evas_Thread_Command_Rect;
typedef struct _Evas_Thread_Command_Line Evas_Thread_Command_Line;
typedef struct _Evas_Thread_Command_Polygon Evas_Thread_Command_Polygon;
typedef struct _Evas_Thread_Command_Image Evas_Thread_Command_Image;
typedef struct _Evas_Thread_Command_Font Evas_Thread_Command_Font;
typedef struct _Evas_Thread_Command_Map Evas_Thread_Command_Map;
typedef struct _Evas_Thread_Command_Multi_Font Evas_Thread_Command_Multi_Font;
typedef struct _Evas_Thread_Command_Ector Evas_Thread_Command_Ector;
typedef struct _Evas_Thread_Command_Ector_Surface Evas_Thread_Command_Ector_Surface;

struct _Evas_Thread_Command_Rect
{
   void *surface;
   DATA32 color;
   int render_op;
   int x, y, w, h;
   void *mask;
   int mask_x, mask_y;
};

struct _Evas_Thread_Command_Line
{
   void *surface;
   Eina_Rectangle clip;
   DATA32 color;
   int render_op;
   Eina_Bool anti_alias;
   int x1, y1;
   int x2, y2;
   void *mask;
   int mask_x, mask_y;
};

struct _Evas_Thread_Command_Polygon
{
   Eina_Rectangle ext;
   DATA32 col;
   int render_op;
   void *surface;
   RGBA_Polygon_Point *points;
   int x, y;
   void *mask;
   int mask_x, mask_y;
};

struct _Evas_Thread_Command_Image
{
   void *surface;
   void *image;
   Eina_Rectangle src, dst, clip;
   DATA32 mul_col;
   int render_op;
   int smooth;
   void *mask;
   int mask_x, mask_y;
};

struct _Evas_Thread_Command_Font
{
   RGBA_Image *dst;
   int x;
   int y;
   Evas_Glyph_Array *glyphs;
   RGBA_Gfx_Func func;
   void *gl_new;
   void *gl_free;
   void *gl_draw;
   void *font_ext_data;
   DATA32 col;
   DATA32 mul_col;
   Eina_Rectangle clip_rect, ext;
   int im_w, im_h;
   void *mask;
   int mask_x, mask_y;
   Eina_Bool clip_use : 1;
};

struct _Evas_Thread_Command_Map
{
   void *image;
   RGBA_Draw_Context *image_ctx;
   void *surface;
   Eina_Rectangle clip;
   DATA32 mul_col;
   int render_op;
   RGBA_Map *map;
   int smooth, level, offset;
   Eina_Bool anti_alias;
   void *mask;
   int mask_x, mask_y;
};

struct _Evas_Thread_Command_Multi_Font
{
   RGBA_Draw_Context *context;
   void *surface;
   int x, y;
   Evas_Font_Array *texts;
};

struct _Evas_Thread_Command_Ector
{
   Ector_Renderer *r;
   Eina_Array *clips;

   DATA32 mul_col;
   Efl_Gfx_Render_Op render_op;

   Eina_Bool free_it;
};

struct _Evas_Thread_Command_Ector_Surface
{
   Ector_Surface *ector;
   void *pixels;
   int x, y;
};

// declare here as it is re-used
static void *eng_image_map_surface_new(void *data, int w, int h, int alpha);

Eina_Mempool *_mp_command_rect = NULL;
Eina_Mempool *_mp_command_line = NULL;
Eina_Mempool *_mp_command_polygon = NULL;
Eina_Mempool *_mp_command_image = NULL;
Eina_Mempool *_mp_command_font = NULL;
Eina_Mempool *_mp_command_map = NULL;
Eina_Mempool *_mp_command_multi_font = NULL;
Eina_Mempool *_mp_command_ector = NULL;
Eina_Mempool *_mp_command_ector_surface = NULL;
/*
 *****
 **
 ** ENGINE ROUTINES
 **
 *****
 */
static int cpunum = 0;
static int _evas_soft_gen_log_dom = -1;

//#define QCMD evas_thread_cmd_enqueue
#define QCMD evas_thread_queue_flush

static void
eng_output_dump(void *engine EINA_UNUSED, void *data EINA_UNUSED)
{
   evas_common_image_image_all_unload();
   evas_common_font_font_all_unload();
}

static void *
eng_context_new(void *data EINA_UNUSED)
{
   return evas_common_draw_context_new();
}

static void *
eng_context_dup(void *data EINA_UNUSED, void *context)
{
   RGBA_Draw_Context *ctx;

   ctx = evas_common_draw_context_dup(context);
   if (ctx->clip.mask)
     {
        Image_Entry *im = ctx->clip.mask;
        evas_cache_image_ref(im);
     }

   return ctx;
}

static void
eng_context_clip_set(void *data EINA_UNUSED, void *context, int x, int y, int w, int h)
{
   evas_common_draw_context_set_clip(context, x, y, w, h);
}

static void
eng_context_clip_image_unset(void *data EINA_UNUSED, void *context)
{
   RGBA_Draw_Context *ctx = context;

   if (ctx->clip.mask)
     {
        Image_Entry *ie = ctx->clip.mask;

        if (ctx->clip.async)
          evas_unref_queue_image_put(ctx->clip.evas, ie);
        else
          evas_cache_image_drop(ie);
        ctx->clip.mask = NULL;
     }
}

static void
eng_context_clip_image_set(void *data EINA_UNUSED, void *context, void *surface, int x, int y,
                           Evas_Public_Data *evas, Eina_Bool do_async)
{
   RGBA_Draw_Context *ctx = context;
   Eina_Bool noinc = EINA_FALSE;

   if (ctx->clip.mask)
     {
        if (ctx->clip.mask != surface)
          eng_context_clip_image_unset(data, context);
        else
          noinc = EINA_TRUE;
     }

   ctx->clip.mask = surface;
   ctx->clip.mask_x = x;
   ctx->clip.mask_y = y;
   ctx->clip.evas = evas;
   ctx->clip.async = do_async;

   if (surface)
     {
        Image_Entry *ie = surface;
        if (!noinc)
          evas_cache_image_ref(ie);
        RECTS_CLIP_TO_RECT(ctx->clip.x, ctx->clip.y, ctx->clip.w, ctx->clip.h,
                           x, y, ie->w, ie->h);
     }
}

static void
eng_context_clip_image_get(void *data EINA_UNUSED, void *context, void **ie, int *x, int *y)
{
   RGBA_Draw_Context *ctx = context;

   if (ie)
     {
        Image_Entry *im = ctx->clip.mask;

        *ie = im;
        if (im)
          evas_cache_image_ref(im);
     }
   if (x) *x = ctx->clip.mask_x;
   if (y) *y = ctx->clip.mask_y;
}

static void
eng_context_free(void *data, void *context)
{
   RGBA_Draw_Context *ctx = context;

   if (!ctx) return;
   if (ctx->clip.mask)
     eng_context_clip_image_unset(data, context);
   evas_common_draw_context_free(context);
}

static void
eng_context_clip_clip(void *data EINA_UNUSED, void *context, int x, int y, int w, int h)
{
   evas_common_draw_context_clip_clip(context, x, y, w, h);
}

static void
eng_context_clip_unset(void *data EINA_UNUSED, void *context)
{
   evas_common_draw_context_unset_clip(context);
}

static int
eng_context_clip_get(void *data EINA_UNUSED, void *context, int *x, int *y, int *w, int *h)
{
   if (x) *x = ((RGBA_Draw_Context *)context)->clip.x;
   if (y) *y = ((RGBA_Draw_Context *)context)->clip.y;
   if (w) *w = ((RGBA_Draw_Context *)context)->clip.w;
   if (h) *h = ((RGBA_Draw_Context *)context)->clip.h;
   return ((RGBA_Draw_Context *)context)->clip.use;
}

static void
eng_context_color_set(void *data EINA_UNUSED, void *context, int r, int g, int b, int a)
{
   evas_common_draw_context_set_color(context, r, g, b, a);
}

static int
eng_context_color_get(void *data EINA_UNUSED, void *context, int *r, int *g, int *b, int *a)
{
   *r = (int)(R_VAL(&((RGBA_Draw_Context *)context)->col.col));
   *g = (int)(G_VAL(&((RGBA_Draw_Context *)context)->col.col));
   *b = (int)(B_VAL(&((RGBA_Draw_Context *)context)->col.col));
   *a = (int)(A_VAL(&((RGBA_Draw_Context *)context)->col.col));
   return 1;
}

static void
eng_context_multiplier_set(void *data EINA_UNUSED, void *context, int r, int g, int b, int a)
{
   evas_common_draw_context_set_multiplier(context, r, g, b, a);
}

static void
eng_context_multiplier_unset(void *data EINA_UNUSED, void *context)
{
   evas_common_draw_context_unset_multiplier(context);
}

static int
eng_context_multiplier_get(void *data EINA_UNUSED, void *context, int *r, int *g, int *b, int *a)
{
   *r = (int)(R_VAL(&((RGBA_Draw_Context *)context)->mul.col));
   *g = (int)(G_VAL(&((RGBA_Draw_Context *)context)->mul.col));
   *b = (int)(B_VAL(&((RGBA_Draw_Context *)context)->mul.col));
   *a = (int)(A_VAL(&((RGBA_Draw_Context *)context)->mul.col));
   return ((RGBA_Draw_Context *)context)->mul.use;
}

static void
eng_context_cutout_add(void *data EINA_UNUSED, void *context, int x, int y, int w, int h)
{
   evas_common_draw_context_add_cutout(context, x, y, w, h);
}

static void
eng_context_cutout_clear(void *data EINA_UNUSED, void *context)
{
   evas_common_draw_context_target_set(context, 0, 0, 0, 0);
   evas_common_draw_context_clear_cutouts(context);
}

static void
eng_context_cutout_target(void *data EINA_UNUSED, void *context, int x, int y, int w, int h)
{
   evas_common_draw_context_target_set(context, x, y, w, h);
}

static void
eng_context_anti_alias_set(void *data EINA_UNUSED, void *context, unsigned char aa)
{
   evas_common_draw_context_set_anti_alias(context, aa);
}

static unsigned char
eng_context_anti_alias_get(void *data EINA_UNUSED, void *context)
{
   return ((RGBA_Draw_Context *)context)->anti_alias;
}

static void
eng_context_color_interpolation_set(void *data EINA_UNUSED, void *context, int color_space)
{
   evas_common_draw_context_set_color_interpolation(context, color_space);
}

static int
eng_context_color_interpolation_get(void *data EINA_UNUSED, void *context)
{
   return ((RGBA_Draw_Context *)context)->interpolation.color_space;
}

static void
eng_context_render_op_set(void *data EINA_UNUSED, void *context, int op)
{
   evas_common_draw_context_set_render_op(context, op);
}

static int
eng_context_render_op_get(void *data EINA_UNUSED, void *context)
{
   return ((RGBA_Draw_Context *)context)->render_op;
}

static void
_draw_thread_rectangle_draw(void *data)
{
    Evas_Thread_Command_Rect *rect = data;

    evas_common_rectangle_rgba_draw(rect->surface,
                                    rect->color, rect->render_op,
                                    rect->x, rect->y, rect->w, rect->h,
                                    rect->mask, rect->mask_x, rect->mask_y);

    eina_mempool_free(_mp_command_rect, rect);
}

static void
_draw_rectangle_thread_cmd(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, int w, int h)
{
   Evas_Thread_Command_Rect *cr;

   RECTS_CLIP_TO_RECT(x, y, w, h, dc->clip.x, dc->clip.y, dc->clip.w, dc->clip.h);
   if ((w <= 0) || (h <= 0)) return;

   cr = eina_mempool_malloc(_mp_command_rect, sizeof (Evas_Thread_Command_Rect));
   if (!cr) return;

   cr->surface = dst;
   cr->color = dc->col.col;
   cr->render_op = dc->render_op;
   cr->x = x;
   cr->y = y;
   cr->w = w;
   cr->h = h;
   cr->mask = dc->clip.mask;
   cr->mask_x = dc->clip.mask_x;
   cr->mask_y = dc->clip.mask_y;

   QCMD(_draw_thread_rectangle_draw, cr);
}

static void
eng_rectangle_draw(void *engine EINA_UNUSED, void *data EINA_UNUSED, void *context, void *surface, int x, int y, int w, int h, Eina_Bool do_async)
{
   if (do_async)
     evas_common_rectangle_draw_cb(surface, context, x, y, w, h,
                                   _draw_rectangle_thread_cmd);
#ifdef BUILD_PIPE_RENDER
   else if ((cpunum > 1))
     evas_common_pipe_rectangle_draw(surface, context, x, y, w, h);
#endif
   else
     {
        evas_common_rectangle_draw(surface, context, x, y, w, h);
        evas_common_cpu_end_opt();
     }
}

static void
_draw_thread_line_draw(void *data)
{
   Evas_Thread_Command_Line *line = data;
   int clip_x, clip_y, clip_w, clip_h;

   clip_x = line->clip.x;
   clip_y = line->clip.y;
   clip_w = line->clip.w;
   clip_h = line->clip.h;

   if ((line->x1 == line->x2) && (line->y1 == line->y2))
     {
        evas_common_line_point_draw(line->surface,
                                    clip_x, clip_y, clip_w, clip_h,
                                    line->color, line->render_op,
                                    line->x1, line->y1,
                                    line->mask, line->mask_x, line->mask_y);
        return;
     }

   if (line->anti_alias)
     evas_common_line_draw_line_aa
       (line->surface,
        clip_x, clip_y, clip_w, clip_h,
        line->color, line->render_op,
        line->x1, line->y1,
        line->x2, line->y2,
        line->mask, line->mask_x, line->mask_y);
   else
     evas_common_line_draw_line
       (line->surface,
        clip_x, clip_y, clip_w, clip_h,
        line->color, line->render_op,
        line->x1, line->y1,
        line->x2, line->y2,
        line->mask, line->mask_x, line->mask_y);

   eina_mempool_free(_mp_command_line, line);
}

static void
_line_draw_thread_cmd(RGBA_Image *dst, RGBA_Draw_Context *dc, int x1, int y1, int x2, int y2)
{
   Evas_Thread_Command_Line *cl;
   int clx, cly, clw, clh;
   int cx, cy, cw, ch;
   int x, y, w, h;

   cl = eina_mempool_malloc(_mp_command_line, sizeof (Evas_Thread_Command_Line));
   if (!cl) return;

   cl->surface = dst;

   if ((x1 == x2) && (y1 == y2))
     {
        EINA_RECTANGLE_SET(&cl->clip,
                           dc->clip.x, dc->clip.y, dc->clip.w, dc->clip.h);
        goto done;
     }

   clx = cly = 0;
   clw = dst->cache_entry.w;
   clh = dst->cache_entry.h;

   cx = dc->clip.x;
   cy = dc->clip.y;
   cw = dc->clip.w;
   ch = dc->clip.h;

   if (dc->clip.use)
     {
	RECTS_CLIP_TO_RECT(clx, cly, clw, clh, cx, cy, cw, ch);
	if ((clw < 1) || (clh < 1))
          {
             eina_mempool_free(_mp_command_line, cl);
             return;
          }
     }

   x = MIN(x1, x2);
   y = MIN(y1, y2);
   w = MAX(x1, x2) - x + 1;
   h = MAX(y1, y2) - y + 1;

   RECTS_CLIP_TO_RECT(clx, cly, clw, clh, x, y, w, h);
   if ((clw < 1) || (clh < 1))
     {
        eina_mempool_free(_mp_command_line, cl);
        return;
     }

   EINA_RECTANGLE_SET(&cl->clip, clx, cly, clw, clh);

 done:
   cl->color = dc->col.col;
   cl->render_op = dc->render_op;
   cl->anti_alias = dc->anti_alias;
   cl->x1 = x1;
   cl->y1 = y1;
   cl->x2 = x2;
   cl->y2 = y2;
   cl->mask = dc->clip.mask;
   cl->mask_x = dc->clip.mask_x;
   cl->mask_y = dc->clip.mask_y;

   QCMD(_draw_thread_line_draw, cl);
}

static void
eng_line_draw(void *engine EINA_UNUSED, void *data EINA_UNUSED, void *context, void *surface, int x1, int y1, int x2, int y2, Eina_Bool do_async)
{
   if (do_async) _line_draw_thread_cmd(surface, context, x1, y1, x2, y2);
#ifdef BUILD_PIPE_RENDER
   else if ((cpunum > 1))
     evas_common_pipe_line_draw(surface, context, x1, y1, x2, y2);
#endif
   else
     {
        evas_common_line_draw(surface, context, x1, y1, x2, y2);
        evas_common_cpu_end_opt();
     }
}

static void *
eng_polygon_point_add(void *data EINA_UNUSED, void *polygon, int x, int y)
{
   return evas_common_polygon_point_add(polygon, x, y);
}

static void *
eng_polygon_points_clear(void *data EINA_UNUSED, void *polygon)
{
   return evas_common_polygon_points_clear(polygon);
}

static void
_draw_thread_polygon_cleanup(Evas_Thread_Command_Polygon *poly)
{
   RGBA_Polygon_Point *points = poly->points;

   while (points)
     {
        RGBA_Polygon_Point *p;

        p = points;
        points =
          (RGBA_Polygon_Point *)eina_inlist_remove(EINA_INLIST_GET(points),
                                                   EINA_INLIST_GET(points));
        free(p);
     }

   poly->points = NULL;
}

static void
_draw_thread_polygon_draw(void *data)
{
   Evas_Thread_Command_Polygon *poly = data;

   evas_common_polygon_rgba_draw
     (poly->surface,
      poly->ext.x, poly->ext.y, poly->ext.w, poly->ext.h,
      poly->col, poly->render_op,
      poly->points, poly->x, poly->y,
      poly->mask, poly->mask_x, poly->mask_y);

   _draw_thread_polygon_cleanup(poly);
   eina_mempool_free(_mp_command_polygon, poly);
}

static void
_polygon_draw_thread_points_populate(Evas_Thread_Command_Polygon *cp, RGBA_Polygon_Point *points)
{
   RGBA_Polygon_Point *cur, *npoints = NULL;

   if (!points) return;

   EINA_INLIST_FOREACH(EINA_INLIST_GET(points), cur)
     {
        RGBA_Polygon_Point *point;

        point = malloc(sizeof *point);
        point->x = cur->x;
        point->y = cur->y;

        npoints =
          (RGBA_Polygon_Point *)eina_inlist_append(EINA_INLIST_GET(npoints),
                                                   EINA_INLIST_GET(point));
     }

   cp->points = npoints;
}

static void
_polygon_draw_thread_cmd(RGBA_Image *dst, RGBA_Draw_Context *dc, RGBA_Polygon_Point *points, int x, int y)
{
   int ext_x, ext_y, ext_w, ext_h;
   Evas_Thread_Command_Polygon *cp;

   ext_x = 0;
   ext_y = 0;
   ext_w = dst->cache_entry.w;
   ext_h = dst->cache_entry.h;

   if (dc->clip.use)
     {
	if (dc->clip.x > ext_x)
	  {
	     ext_w += ext_x - dc->clip.x;
	     ext_x = dc->clip.x;
	  }

	if ((ext_x + ext_w) > (dc->clip.x + dc->clip.w))
          ext_w = (dc->clip.x + dc->clip.w) - ext_x;

	if (dc->clip.y > ext_y)
	  {
	     ext_h += ext_y - dc->clip.y;
	     ext_y = dc->clip.y;
	  }

	if ((ext_y + ext_h) > (dc->clip.y + dc->clip.h))
          ext_h = (dc->clip.y + dc->clip.h) - ext_y;
     }

   cp = eina_mempool_malloc(_mp_command_polygon, sizeof (Evas_Thread_Command_Polygon));
   if (!cp) return;

   EINA_RECTANGLE_SET(&cp->ext, ext_x, ext_y, ext_w, ext_h);
   cp->col = dc->col.col;
   cp->render_op = dc->render_op;
   cp->surface = dst;

   _polygon_draw_thread_points_populate(cp, points);

   cp->x = x;
   cp->y = y;

   cp->mask = dc->clip.mask;
   cp->mask_x = dc->clip.mask_x;
   cp->mask_y = dc->clip.mask_y;

   QCMD(_draw_thread_polygon_draw, cp);
}

static void
eng_polygon_draw(void *engine EINA_UNUSED, void *data EINA_UNUSED, void *context, void *surface, void *polygon, int x, int y, Eina_Bool do_async)
{
   if (do_async) _polygon_draw_thread_cmd(surface, context, polygon, x, y);
#ifdef BUILD_PIPE_RENDER
   else if ((cpunum > 1))
     evas_common_pipe_poly_draw(surface, context, polygon, x, y);
#endif
   else
     {
	evas_common_polygon_draw(surface, context, polygon, x, y);
	evas_common_cpu_end_opt();
     }
}

static int
eng_image_alpha_get(void *data EINA_UNUSED, void *image)
{
   Image_Entry *im;

   if (!image) return 1;
   im = image;
   switch (im->space)
     {
      case EVAS_COLORSPACE_ARGB8888:
	if (im->flags.alpha) return 1;
      default:
	break;
     }
   return 0;
}

static Evas_Colorspace
eng_image_colorspace_get(void *data EINA_UNUSED, void *image)
{
   Image_Entry *im;

   if (!image) return EVAS_COLORSPACE_ARGB8888;
   im = image;
   return im->space;
}

static Eina_Bool
eng_image_can_region_get(void *data EINA_UNUSED, void *image)
{
   Image_Entry *im;
   if (!image) return EINA_FALSE;
   im = image;
   return ((Evas_Image_Load_Func*) im->info.loader)->do_region;
}

static void *
eng_image_alpha_set(void *data EINA_UNUSED, void *image, int has_alpha)
{
   RGBA_Image *im;

   if (!image) return NULL;
   im = image;
   if (im->cache_entry.space != EVAS_COLORSPACE_ARGB8888)
     {
	im->cache_entry.flags.alpha = 0;
	return im;
     }
   if (!im->image.data) evas_cache_image_load_data(&im->cache_entry);
   im = (RGBA_Image *) evas_cache_image_alone(&im->cache_entry);
   im->cache_entry.flags.alpha = has_alpha ? 1 : 0;
   evas_common_image_colorspace_dirty(im);
   return im;
}

static Evas_Colorspace
eng_image_file_colorspace_get(void *data EINA_UNUSED, void *image)
{
   RGBA_Image *im = image;

   if (!im) return EVAS_COLORSPACE_ARGB8888;
   if (im->cache_entry.cspaces)
     return im->cache_entry.cspaces[0];
   return im->cache_entry.space;
}

static Eina_Bool
eng_image_content_region_get(void *engine EINA_UNUSED, void *image, Eina_Rectangle *content)
{
   RGBA_Image *im = image;

   if (!im) return EINA_FALSE;

   if (!im->cache_entry.need_data) return EINA_FALSE;

   if (!im->image.data) evas_cache_image_load_data(&im->cache_entry);

   if (!im->cache_entry.content.w ||
       !im->cache_entry.content.h)
     return EINA_FALSE;

   if (!content) return EINA_FALSE;

   memcpy(content, &im->cache_entry.content, sizeof (Eina_Rectangle));
   return EINA_TRUE;
}

static Eina_Bool
eng_image_stretch_region_get(void *engine EINA_UNUSED, void *image,
                             uint8_t **horizontal, uint8_t **vertical)
{
   RGBA_Image *im = image;

   if (!im) return EINA_FALSE;

   if (!im->cache_entry.need_data) return EINA_FALSE;

   if (!im->image.data) evas_cache_image_load_data(&im->cache_entry);

   if (!im->cache_entry.stretch.horizontal.region ||
       !im->cache_entry.stretch.vertical.region)
     return EINA_FALSE;

   *horizontal = im->cache_entry.stretch.horizontal.region;
   *vertical = im->cache_entry.stretch.vertical.region;
   return EINA_TRUE;
}

static Eina_Bool
eng_image_data_direct_get(void *data EINA_UNUSED, void *image, int plane,
                          Eina_Slice *slice, Evas_Colorspace *cspace,
                          Eina_Bool load, Eina_Bool *tofree)
{
   RGBA_Image *im = image;
   if (tofree) *tofree = EINA_FALSE;

   if (!slice || !im)
     return EINA_FALSE;

   if (cspace) *cspace = im->cache_entry.space;
   if (load)
     {
        if (evas_cache_image_load_data(&im->cache_entry) != 0)
          return EINA_FALSE;
     }
   return _evas_common_rgba_image_plane_get(im, plane, slice);
}

static void
eng_image_colorspace_set(void *data EINA_UNUSED, void *image, Evas_Colorspace cspace)
{
   Image_Entry *im;

   if (!image) return;
   im = image;
   evas_cache_image_colorspace(im, cspace);
}

static int
eng_image_native_init(void *data EINA_UNUSED, Evas_Native_Surface_Type type)
{
   if (type == EVAS_NATIVE_SURFACE_TBM)
     return _evas_native_tbm_init();

   ERR("Native surface type %d not supported!", type);
   return 0;
}

static void
eng_image_native_shutdown(void *data EINA_UNUSED, Evas_Native_Surface_Type type)
{
   if (type == EVAS_NATIVE_SURFACE_TBM)
     _evas_native_tbm_shutdown();
   else
     ERR("Native surface type %d not supported!", type);
}

static void *
eng_image_native_set(void *data EINA_UNUSED, void *image, void *native)
{
   Evas_Native_Surface *ns = native;
   RGBA_Image *im = image;
   Image_Entry *ie = image, *ie2;

   if (!im)
     {
        /* This is a probe for wl_dmabuf viability */
        if (ns && ns->type == EVAS_NATIVE_SURFACE_WL_DMABUF &&
            !ns->data.wl_dmabuf.resource)
          return _evas_native_dmabuf_surface_image_set(image, native);

        return NULL;
     }
   if (!ns)
     {
        if (im->native.data && im->native.func.free)
          im->native.func.free(im);
        return NULL;
     }

   if ((ns->type == EVAS_NATIVE_SURFACE_EVASGL) &&
       (ns->version == EVAS_NATIVE_SURFACE_VERSION))
     {

        ie2 = evas_cache_image_data(evas_common_image_cache_get(),
                                    ie->w, ie->h,
                                    ns->data.evasgl.surface, 1,
                                    EVAS_COLORSPACE_ARGB8888);
     }
   else
     ie2 = evas_cache_image_data(evas_common_image_cache_get(),
                                 ie->w, ie->h,
                                 NULL, 1,
                                 EVAS_COLORSPACE_ARGB8888);
   if (ie->references > 1)
     ERR("Setting native with more than one references for im=%p", im);

   if (im->native.data)
     {
        if (im->native.func.free)
          im->native.func.free(im);
     }

   evas_cache_image_drop(ie);

   if (ns->type == EVAS_NATIVE_SURFACE_WL_DMABUF)
      return _evas_native_dmabuf_surface_image_set(ie2, ns);

   return ie2;
}

static void *
eng_image_native_get(void *data EINA_UNUSED, void *image)
{
   RGBA_Image *im = image;
   Evas_Native_Surface *n;

   if (!im) return NULL;
   n = im->native.data;
   return n;
}

static void *
eng_image_load(void *data EINA_UNUSED, const char *file, const char *key, int *error, Evas_Image_Load_Opts *lo)
{
   *error = EVAS_LOAD_ERROR_NONE;
#ifdef EVAS_CSERVE2
   if (evas_cserve2_use_get())
     {
        Image_Entry *ie;
        ie = evas_cache2_image_open(evas_common_image_cache2_get(),
                                    file, key, lo, error);
        if (ie)
          {
             *error = evas_cache2_image_open_wait(ie);
             if ((*error != EVAS_LOAD_ERROR_NONE) && ie->animated.animated)
               {
                  evas_cache2_image_close(ie);
                  goto use_local_cache;
               }
          }
        return ie;
     }
use_local_cache:
#endif

   return evas_common_load_image_from_file(file, key, lo, error);
}

static void *
eng_image_mmap(void *data EINA_UNUSED, Eina_File *f, const char *key, int *error, Evas_Image_Load_Opts *lo)
{
   *error = EVAS_LOAD_ERROR_NONE;

   return evas_common_load_image_from_mmap(f, key, lo, error);
}

static void *
eng_image_new_from_data(void *data EINA_UNUSED, int w, int h, DATA32 *image_data, int alpha, Evas_Colorspace cspace)
{
   return evas_cache_image_data(evas_common_image_cache_get(), w, h, image_data, alpha, cspace);
}

static void *
eng_image_new_from_copied_data(void *data EINA_UNUSED, int w, int h, DATA32 *image_data, int alpha, Evas_Colorspace cspace)
{
   return evas_cache_image_copied_data(evas_common_image_cache_get(), w, h, image_data, alpha, cspace);
}

static void
eng_image_free(void *data EINA_UNUSED, void *image)
{
   evas_cache_image_drop(image);
}

static void *
eng_image_ref(void *data EINA_UNUSED, void *image)
{
   if (!image) return NULL;
   evas_cache_image_ref(image);
   return image;
}

static void
eng_image_size_get(void *data EINA_UNUSED, void *image, int *w, int *h)
{
   Image_Entry *im;

   im = image;
   if (w) *w = im->w;
   if (h) *h = im->h;
}

static void *
eng_image_size_set(void *data EINA_UNUSED, void *image, int w, int h)
{
   RGBA_Image *rm;
   Image_Entry *im = image, *im2;
   if (!im) return NULL;

   /* sw engine im could be changed and removed in evas_cache_image_size_set.
      in this case, there is no chance to free its resource.  */
   evas_cache_image_ref(im);
   im2 = evas_cache_image_size_set(im, w, h);
   if (im != im2 && im->references == 1)
     {
        rm = (RGBA_Image *)im;
        if (rm->native.data)
          {
             if (rm->native.func.free)
               rm->native.func.free(im);
          }
     }
   evas_cache_image_drop(im);
   return im2;
}

static void *
eng_image_dirty_region(void *data EINA_UNUSED, void *image, int x, int y, int w, int h)
{
   Image_Entry *im = image;
   if (!im) return NULL;
   return evas_cache_image_dirty(im, x, y, w, h);
}

static void *
eng_image_data_get(void *data EINA_UNUSED, void *image, int to_write, DATA32 **image_data, int *err, Eina_Bool *tofree)
{
   RGBA_Image *im = image;
   int error = EVAS_LOAD_ERROR_NONE;

   *image_data = NULL;
   if (err) *err = EVAS_LOAD_ERROR_NONE;
   if (tofree) *tofree = EINA_FALSE;

   if (!im)
     {
        if (err) *err = EVAS_LOAD_ERROR_DOES_NOT_EXIST;
        return NULL;
     }

   error = evas_cache_image_load_data(&im->cache_entry);

   switch (im->cache_entry.space)
     {
      case EVAS_COLORSPACE_ARGB8888:
      case EVAS_COLORSPACE_AGRY88:
      case EVAS_COLORSPACE_GRY8:
	if (to_write)
          im = (RGBA_Image *)evas_cache_image_alone(&im->cache_entry);
	*image_data = im->image.data;
	break;
      case EVAS_COLORSPACE_YCBCR422P601_PL:
      case EVAS_COLORSPACE_YCBCR422P709_PL:
      case EVAS_COLORSPACE_YCBCR422601_PL:
      case EVAS_COLORSPACE_YCBCR420NV12601_PL:
      case EVAS_COLORSPACE_YCBCR420TM12601_PL:
        *image_data = im->cs.data;
        break;
      // unlikely formats, not supported for render by the sw engine
      case EVAS_COLORSPACE_ETC1:
      case EVAS_COLORSPACE_RGB8_ETC2:
      case EVAS_COLORSPACE_RGBA8_ETC2_EAC:
      case EVAS_COLORSPACE_ETC1_ALPHA:
      case EVAS_COLORSPACE_RGB_S3TC_DXT1:
      case EVAS_COLORSPACE_RGBA_S3TC_DXT1:
      case EVAS_COLORSPACE_RGBA_S3TC_DXT2:
      case EVAS_COLORSPACE_RGBA_S3TC_DXT3:
      case EVAS_COLORSPACE_RGBA_S3TC_DXT4:
      case EVAS_COLORSPACE_RGBA_S3TC_DXT5:
        if (to_write)
          {
             ERR("can not get ETC or S3TC data to write");
             *image_data = NULL;
             return NULL;
          }
        *image_data = im->image.data;
        break;
      default:
        CRI("unsupported format %d", im->cache_entry.space);
        if (err) *err = EVAS_LOAD_ERROR_UNKNOWN_FORMAT;
        *image_data = NULL;
        return NULL;
     }
   if (err) *err = error;
   return im;
}

static void *
eng_image_data_put(void *data, void *image, DATA32 *image_data)
{
   RGBA_Image *im, *im2;

   if (!image) return NULL;
   im = image;
   switch (im->cache_entry.space)
     {
      case EVAS_COLORSPACE_ARGB8888:
      case EVAS_COLORSPACE_AGRY88:
      case EVAS_COLORSPACE_GRY8:
      case EVAS_COLORSPACE_ETC1:
      case EVAS_COLORSPACE_RGB8_ETC2:
      case EVAS_COLORSPACE_RGBA8_ETC2_EAC:
      case EVAS_COLORSPACE_ETC1_ALPHA:
      case EVAS_COLORSPACE_RGB_S3TC_DXT1:
      case EVAS_COLORSPACE_RGBA_S3TC_DXT1:
      case EVAS_COLORSPACE_RGBA_S3TC_DXT2:
      case EVAS_COLORSPACE_RGBA_S3TC_DXT3:
      case EVAS_COLORSPACE_RGBA_S3TC_DXT4:
      case EVAS_COLORSPACE_RGBA_S3TC_DXT5:
	if (image_data != im->image.data)
	  {
	     int w, h;

	     w = im->cache_entry.w;
	     h = im->cache_entry.h;
	     im2 = eng_image_new_from_data(data, w, h, image_data,
					   eng_image_alpha_get(data, image),
					   eng_image_colorspace_get(data, image));
             evas_cache_image_drop(&im->cache_entry);
	     im = im2;
	  }
	break;
      case EVAS_COLORSPACE_YCBCR422P601_PL:
      case EVAS_COLORSPACE_YCBCR422P709_PL:
      case EVAS_COLORSPACE_YCBCR422601_PL:
      case EVAS_COLORSPACE_YCBCR420NV12601_PL:
      case EVAS_COLORSPACE_YCBCR420TM12601_PL:
	if (image_data != im->cs.data)
          {
	     if (im->cs.data)
	       {
		  if (!im->cs.no_free) free(im->cs.data);
	       }
	     im->cs.data = image_data;
	  }
        evas_common_image_colorspace_dirty(im);
        break;
      default:
        CRI("unsupported format %d", im->cache_entry.space);
        return NULL;
     }
   return im;
}

static Eina_Bool
eng_image_data_map(void *engdata EINA_UNUSED, void **image, Eina_Rw_Slice *slice,
                   int *stride, int x, int y, int w, int h,
                   Evas_Colorspace cspace, Efl_Gfx_Buffer_Access_Mode mode,
                   int plane)
{
   Eina_Bool cow = EINA_FALSE, to_write = EINA_FALSE;
   RGBA_Image_Data_Map *map;
   RGBA_Image *im;
   Image_Entry *ie;
   int src_stride, src_offset;
   void *data;

   EINA_SAFETY_ON_FALSE_RETURN_VAL(image && *image && slice, EINVAL);
   im = *image;
   ie = &im->cache_entry;

   slice->len = 0;
   slice->mem = NULL;

   // FIXME: implement planes support (YUV, RGB565, ETC1+Alpha)
   // FIXME: implement YUV support (im->cs.data)
   if (plane)
     {
        ERR("planar formats support not implemented yet!");
        return EINA_FALSE;
     }

   if (!im->image.data)
     {
        int error = evas_cache_image_load_data(ie);
        if (error != EVAS_LOAD_ERROR_NONE)
          return EINA_FALSE;
     }

   if (mode & EFL_GFX_BUFFER_ACCESS_MODE_COW)
     cow = EINA_TRUE;

   if (mode & EFL_GFX_BUFFER_ACCESS_MODE_WRITE)
     to_write = EINA_TRUE;

   // verify region is valid regarding special colorspaces (ETC, S3TC, YUV)
   src_offset = _evas_common_rgba_image_data_offset(x, y, w, h, 0, im);
   if ((src_offset < 0) || !w || !h)
     {
        ERR("invalid region for colorspace %d: %dx%d + %d,%d, image: %dx%d",
            cspace, w, h, x, y, ie->w, ie->h);
        return EINA_FALSE;
     }

   src_stride = _evas_common_rgba_image_data_offset(ie->w, 0, 0, 0, 0, im);

   // safety check for COW flag
   EINA_INLIST_FOREACH(im->maps, map)
     {
        if ((!(map->mode & EFL_GFX_BUFFER_ACCESS_MODE_COW)) != (!cow))
          {
             ERR("can't map shared image data multiple times with "
                 "different COW flag");
             return EINA_FALSE;
          }
     }

   // ensure that we are the sole owner of this image entry.
   if (cow)
     {
        ie = evas_cache_image_alone(ie);
        if (!ie) return EINA_FALSE;
        im = (RGBA_Image *) ie;
        *image = im;
     }
   else
     {
        if (to_write && (ie->references > 1))
          {
             ERR("write map requires COW flag for shared images");
             return EINA_FALSE;
          }
     }

   if (cspace != ie->space)
     {
        // using 4x4 "blocks" to support etc1/2, s3tc...
        // note: no actual stride support
        Cspace_Convert_Func cs_func;
        Eina_Bool can_region;
        RGBA_Image fake;
        int rx, ry, rw, rh;
        void *src_data;
        int dst_stride, dst_len, dst_offset = 0;

        cs_func = efl_draw_convert_func_get((Efl_Gfx_Colorspace)ie->space,
                                            (Efl_Gfx_Colorspace)cspace,
                                            &can_region);
        if (!cs_func) return EINA_FALSE;

        // make sure we can convert back, if map for writing
        if (to_write && !efl_draw_convert_func_get((Efl_Gfx_Colorspace)cspace,
                                                   (Efl_Gfx_Colorspace)ie->space,
                                                   NULL))
          return EINA_FALSE;

        if (can_region)
          {
             rx = x;
             ry = y;
             rw = w;
             rh = h;
             src_data = im->image.data8 + src_offset;
          }
        else
          {
             rx = 0;
             ry = 0;
             rw = ie->w;
             rh = ie->h;
             src_data = im->image.data8;
          }

        // a bit hacky, but avoids passing too many parameters to the function
        fake.cache_entry.w = rw;
        fake.cache_entry.h = rh;
        fake.cache_entry.space = cspace;
        dst_stride = _evas_common_rgba_image_data_offset(rw, 0, 0, 0, 0, &fake);
        if (!can_region)
          dst_offset = _evas_common_rgba_image_data_offset(rx, ry, 0, 0, 0, &fake);
        dst_len = rh * dst_stride;

        data = malloc(dst_len);
        if (!data) return EINA_FALSE;

        if (!cs_func(data, src_data, rw, rh, src_stride, dst_stride,
                     ie->flags.alpha,
                     (Efl_Gfx_Colorspace)ie->space,
                     (Efl_Gfx_Colorspace)cspace))
          {
             ERR("color conversion failed");
             free(data);
             return EINA_FALSE;
          }

        map = calloc(1, sizeof(*map));
        if (!map)
          {
             free(data);
             return EINA_FALSE;
          }
        map->allocated = EINA_TRUE;
        map->cspace = cspace;
        map->rx = rx;
        map->ry = ry;
        map->rh = rh;
        map->rw = rw;
        map->mode = mode;
        map->baseptr = data;
        map->slice.mem = map->baseptr + dst_offset;
        map->slice.len = dst_len;
        map->stride = dst_stride;
     }
   else
     {
        // same colorspace
        if (!to_write || !cow)
          {
             // no copy
             int end_offset = _evas_common_rgba_image_data_offset(x + w, y + h, 0, 0, 0, im) - src_stride;
             map = calloc(1, sizeof(*map));
             if (!map) return EINA_FALSE;

             map->baseptr = im->image.data8;
             map->slice.mem = im->image.data8 + src_offset;
             map->slice.len = end_offset - src_offset;
          }
        else
          {
             // copy
             int size = _evas_common_rgba_image_data_offset(w, h, 0, 0, 0, im);
             data = malloc(size);
             if (!data) return EINA_FALSE;
             map = calloc(1, sizeof(*map));
             if (!map)
               {
                  free(data);
                  return EINA_FALSE;
               }
             memcpy(data, im->image.data8 + src_offset, size);

             map->allocated = EINA_TRUE;
             map->baseptr = data;
             map->slice.mem = data;
             map->slice.len = size;
          }
        map->cspace = cspace;
        map->rx = x;
        map->ry = y;
        map->rw = w;
        map->rh = h;
        map->stride = src_stride;
     }

   im->maps = (RGBA_Image_Data_Map *)
     eina_inlist_prepend(EINA_INLIST_GET(im->maps), EINA_INLIST_GET(map));
   if (stride) *stride = map->stride;
   slice->mem = map->slice.mem;
   slice->len = map->slice.len;
   return EINA_TRUE;
}

static void
_image_data_commit(RGBA_Image *im, RGBA_Image_Data_Map *map)
{
   Image_Entry *ie = &im->cache_entry;

   int dst_offset = _evas_common_rgba_image_data_offset(map->rx, map->ry, 0, 0, map->plane, im);
   int dst_stride = _evas_common_rgba_image_data_offset(ie->w, 0, 0, 0, map->plane, im);
   unsigned char *dst = im->image.data8 + dst_offset;

   if (map->cspace == ie->space)
     {
        if (dst_stride == (int) map->stride)
          {
             DBG("unmap commit: single memcpy");
             memcpy(dst, map->slice.bytes, dst_stride * map->rh);
          }
        else
          {
             DBG("unmap commit: multiple memcpy");
             for (int k = 0; k < dst_stride; k++)
               memcpy(dst + k * dst_stride, map->slice.bytes + k * dst_stride, dst_stride);
          }
     }
   else
     {
        Cspace_Convert_Func cs_func;
        Eina_Bool can_region;

        cs_func = efl_draw_convert_func_get((Efl_Gfx_Colorspace)map->cspace,
                                            (Efl_Gfx_Colorspace)ie->space,
                                            &can_region);
        EINA_SAFETY_ON_NULL_RETURN(cs_func);

        DBG("unmap commit: convert func (%p)", cs_func);
        if (can_region)
          {
             cs_func(dst, map->slice.mem, map->rw, map->rh, map->stride, dst_stride,
                     ie->flags.alpha,
                     (Efl_Gfx_Colorspace)map->cspace,
                     (Efl_Gfx_Colorspace)ie->space);
          }
        else
          {
             cs_func(dst, map->baseptr, ie->w, ie->h, map->stride, dst_stride,
                     ie->flags.alpha,
                     (Efl_Gfx_Colorspace)map->cspace,
                     (Efl_Gfx_Colorspace)ie->space);
          }
     }
}

static Eina_Bool
eng_image_data_unmap(void *engdata EINA_UNUSED, void *image, const Eina_Rw_Slice *slice)
{
   RGBA_Image_Data_Map *map;
   RGBA_Image *im = image;

   if (!(image && slice))
     return EINA_FALSE;

   EINA_INLIST_FOREACH(EINA_INLIST_GET(im->maps), map)
     {
        if ((map->slice.len == slice->len) && (map->slice.mem == slice->mem))
          {
             if (map->allocated)
               {
                  if (map->mode & EFL_GFX_BUFFER_ACCESS_MODE_WRITE)
                    _image_data_commit(im, map);
                  free(map->baseptr);
               }
             im->maps = (RGBA_Image_Data_Map *)
                   eina_inlist_remove(EINA_INLIST_GET(im->maps), EINA_INLIST_GET(map));
             free(map);
             return EINA_TRUE;
          }
     }

   ERR("failed to unmap region %p (%zu bytes)", slice->mem, slice->len);
   return EINA_FALSE;
}

static int
eng_image_data_maps_get(void *engdata EINA_UNUSED, const void *image, const Eina_Rw_Slice **slices)
{
   RGBA_Image_Data_Map *map;
   const RGBA_Image *im = image;
   int k = 0;

   if (!im) return -1;

   if (!slices)
     return eina_inlist_count(EINA_INLIST_GET(im->maps));

   EINA_INLIST_FOREACH(EINA_INLIST_GET(im->maps), map)
     slices[k++] = &map->slice;

   return k;
}

static inline Eina_Bool
_is_yuv(Evas_Colorspace cspace)
{
   switch (cspace)
     {
      case EVAS_COLORSPACE_YCBCR422P601_PL:
      case EVAS_COLORSPACE_YCBCR422P709_PL:
      case EVAS_COLORSPACE_YCBCR422601_PL:
      case EVAS_COLORSPACE_YCBCR420NV12601_PL:
      case EVAS_COLORSPACE_YCBCR420TM12601_PL:
        return EINA_TRUE;

      default:
        return EINA_FALSE;
     }
}

static void *
eng_image_data_slice_add(void *engdata, void *image,
                         const Eina_Slice *slice, Eina_Bool copy,
                         int w, int h, int stride, Evas_Colorspace cspace,
                         int plane, Eina_Bool alpha)
{
   const Eina_Bool use_cs = _is_yuv(cspace);
   const unsigned char **cs_data;
   RGBA_Image *im = image;
   int bpp = 0;

   // Note: This code is not very robust by choice. It should NOT be used
   // in conjunction with data_put/data_get. Ever.
   // Assume w,h,cspace,alpha to be correct.
   // We still use cs.data for YUV.
   // 'image' may be NULL, in that case create a new one. Otherwise, it must
   // have been created by a previous call to this function.

   if ((plane < 0) || (plane >= RGBA_PLANE_MAX)) goto fail;
   if (!slice || !slice->mem) goto fail;
   copy = !!copy;

   // not implemented
   if (use_cs && copy)
     {
        // To implement this, we should switch the internals to slices first,
        // as this would give 3 planes rather than N rows of datas
        ERR("Evas can not copy YUV data (not implemented yet).");
        goto fail;
     }

   // alloc
   if (!im)
     {
        switch (cspace)
          {
           case EFL_GFX_COLORSPACE_ARGB8888:
           case EFL_GFX_COLORSPACE_AGRY88:
           case EFL_GFX_COLORSPACE_GRY8:
             if (plane != 0) goto fail;
             if (copy)
               im = eng_image_new_from_copied_data(engdata, w, h, NULL, alpha, cspace);
             else
               im = eng_image_new_from_data(engdata, w, h, NULL, alpha, cspace);
             break;

           case EFL_GFX_COLORSPACE_YCBCR422P601_PL:
           case EFL_GFX_COLORSPACE_YCBCR422P709_PL:
           case EFL_GFX_COLORSPACE_YCBCR422601_PL:
           case EFL_GFX_COLORSPACE_YCBCR420NV12601_PL:
             // Use 'copied' to allocate the RGBA buffer
             im = eng_image_new_from_copied_data(engdata, w, h, NULL, alpha, cspace);
             break;

           default:
             // TODO: ETC, S3TC, YCBCR420TM12 (aka ST12 or tiled NV12)
             goto fail;
          }
        if (!im) goto fail;
     }
   else
     {
        im = (RGBA_Image *) evas_cache_image_alone(&im->cache_entry);
        if (!im) goto fail;
     }

   if (use_cs && (!im->cs.data || im->cs.no_free))
     {
        im->cs.data = calloc(1, h * sizeof(void *) * 2);
        if (!im->cs.data) goto fail;
        im->cs.no_free = EINA_FALSE;
     }

   // assign
   switch (cspace)
     {
      case EFL_GFX_COLORSPACE_ARGB8888:
        bpp = 4;
        EINA_FALLTHROUGH;
      case EFL_GFX_COLORSPACE_AGRY88:
        if (!bpp) bpp = 2;
        EINA_FALLTHROUGH;
      case EFL_GFX_COLORSPACE_GRY8:
        if (!bpp) bpp = 1;
        if (plane != 0) goto fail;
        if (!stride) stride = w * bpp;
        if (copy)
          {
             for (int y = 0; y < h; y++)
               {
                  const unsigned char *src = slice->bytes + h * stride;
                  unsigned char *dst = im->image.data8 + bpp * w;
                  memcpy(dst, src, w * bpp);
               }
          }
        else
          {
             if (stride != (bpp * w))
               {
                  ERR("invalid stride for zero-copy data set");
                  goto fail;
               }
             im->image.data = (DATA32 *) slice->mem;
             im->image.no_free = EINA_TRUE;
          }
        break;

      case EFL_GFX_COLORSPACE_YCBCR422P601_PL:
      case EFL_GFX_COLORSPACE_YCBCR422P709_PL:
        /* YCbCr 4:2:2 Planar: Y rows, then the Cb, then Cr rows. */
        cs_data = im->cs.data;
        if (plane == 0)
          {
             if (!stride) stride = w;
             for (int y = 0; y < h; y++)
               cs_data[y] = slice->bytes + (y * stride);
          }
        else if (plane == 1)
          {
             if (!stride) stride = w / 2;
             for (int y = 0; y < (h / 2); y++)
               cs_data[h + y] = slice->bytes + (y * stride);
          }
        else if (plane == 2)
          {
             if (!stride) stride = w / 2;
             for (int y = 0; y < (h / 2); y++)
               cs_data[h + (h / 2) + y] = slice->bytes + (y * stride);
          }
        evas_common_image_colorspace_dirty(im);
        break;

      case EFL_GFX_COLORSPACE_YCBCR422601_PL:
        /* YCbCr 4:2:2: lines of Y,Cb,Y,Cr bytes. */
        if (plane != 0) goto fail;
        if (!stride) stride = w * 2;
        cs_data = im->cs.data;
        for (int y = 0; y < h; y++)
          cs_data[y] = slice->bytes + (y * stride);
        evas_common_image_colorspace_dirty(im);
        break;

      case EFL_GFX_COLORSPACE_YCBCR420NV12601_PL:
        /* YCbCr 4:2:0: Y rows, then the Cb,Cr rows. */
        if (!stride) stride = w;
        cs_data = im->cs.data;
        if (plane == 0)
          {
             for (int y = 0; y < h; y++)
               cs_data[y] = slice->bytes + (y * stride);
          }
        else if (plane == 1)
          {
             for (int y = 0; y < (h / 2); y++)
               cs_data[h + y] = slice->bytes + (y * stride);
          }
        evas_common_image_colorspace_dirty(im);
        break;

        // ETC, S3TC, YCBCR420TM12 (aka ST12 or tiled NV12)
      default:
        ERR("unsupported color space %d", cspace);
        goto fail;
     }

   return im;

fail:
   if (im) eng_image_free(engdata, im);
   return NULL;
}

static void
eng_image_prepare(void *engdata EINA_UNUSED, void *image EINA_UNUSED)
{
   // software rendering doesnt want/need to prepare at this point
   // XXX: though this could push along any loading threads or start
   // some thread jobs for loading in the bg.
}

static void *
eng_image_surface_noscale_new(void *engdata, int w, int h, int alpha)
{
   // simply call the map surface new as all we need is a basic buffer
   return eng_image_map_surface_new(engdata, w, h, alpha);
}

static void
_image_flip_horizontal(DATA32 *pixels_out, const DATA32 *pixels_in,
                       int iw, int ih)
{
   const unsigned int *pi1, *pi2;
   unsigned int *po1, *po2;
   int x, y;

   for (y = 0; y < ih; y++)
     {
        pi1 = pixels_in + (y * iw);
        pi2 = pixels_in + ((y + 1) * iw) - 1;
        po1 = pixels_out + (y * iw);
        po2 = pixels_out + ((y + 1) * iw) - 1;
        for (x = 0; x < (iw >> 1); x++)
          {
             *po2 = *pi1;
             *po1 = *pi2;
             pi1++; po1++;
             pi2--; po2--;
          }
     }
}

static void
_image_flip_vertical(DATA32 *pixels_out, const DATA32 *pixels_in,
                     int iw, int ih)
{
   const unsigned int *pi1, *pi2;
   unsigned int *po1, *po2;
   int x, y;

   for (y = 0; y < (ih >> 1); y++)
     {
        pi1 = pixels_in + (y * iw);
        pi2 = pixels_in + ((ih - 1 - y) * iw);
        po1 = pixels_out + (y * iw);
        po2 = pixels_out + ((ih - 1 - y) * iw);
        for (x = 0; x < iw; x++)
          {
             *po2 = *pi1;
             *po1 = *pi2;
             pi1++; po1++;
             pi2++; po2++;
          }
     }
}

static void
_image_rotate_180(DATA32 *pixels_out, const DATA32 *pixels_in,
                  int iw, int ih)
{
   const unsigned int *pi1, *pi2;
   unsigned int *po1, *po2;
   int hw;

   hw = iw * ih;
   pi1 = pixels_in;
   pi2 = pixels_in + hw - 1;
   po1 = pixels_out;
   po2 = pixels_out + hw - 1;
   for (; pi1 < pi2; )
     {
        *po2 = *pi1;
        *po1 = *pi2;
        pi1++; po1++;
        pi2--; po2--;
     }
}

static void
_image_rotate_90(DATA32 *pixels_out, const DATA32 *pixels_in, int iw, int ih)
{
   int x, y, xx, yy, xx2, yy2;

   for (y = 0; y < ih; y += TILE)
     {
        yy2 = y + TILE;
        if (yy2 > ih) yy2 = ih;
        for (x = 0; x < iw; x += TILE)
          {
             xx2 = x + TILE;
             if (xx2 > iw) xx2 = iw;
             for (yy = y; yy < yy2; yy++)
               {
                  const unsigned int *src;
                  unsigned int *dst;

                  src = pixels_in + (yy * iw) + x;
                  dst = pixels_out + (x * ih) + (ih - yy - 1);
                  for (xx = x; xx < xx2; xx++)
                    {
                       *dst = *src;
                       src++;
                       dst += ih;
                    }
               }
          }
     }
}

static void
_image_rotate_270(DATA32 *pixels_out, const DATA32 *pixels_in, int iw, int ih)
{
   int x, y, xx, yy, xx2, yy2;

   for (y = 0; y < ih; y += TILE)
     {
        yy2 = y + TILE;
        if (yy2 > ih) yy2 = ih;
        for (x = 0; x < iw; x += TILE)
          {
             xx2 = x + TILE;
             if (xx2 > iw) xx2 = iw;
             for (yy = y; yy < yy2; yy++)
               {
                  const unsigned int *src;
                  unsigned int *dst;

                  src = pixels_in + (yy * iw) + x;
                  dst = pixels_out + ((iw - x - 1) * ih) + yy;
                  for (xx = x; xx < xx2; xx++)
                    {
                       *dst = *src;
                       src++;
                       dst -= ih;
                    }
               }
          }
     }
}

static void
_image_flip_transpose(DATA32 *pixels_out, const DATA32 *pixels_in,
                      int iw, int ih)
{
   int x, y;
   const unsigned int *src;

   src = pixels_in;
   for (y = 0; y < ih; y++)
     {
        unsigned int *dst;

        dst = pixels_out + y;
        for (x = 0; x < iw; x++)
          {
             unsigned int tmp = *src;
             *dst = tmp;
             src++;
             dst += ih;
          }
     }
}

static void
_image_flip_transverse(DATA32 *pixels_out, const DATA32 *pixels_in,
                       int iw, int ih)
{
   int x, y;
   const unsigned int *src;

   src = pixels_in + (iw * ih) - 1;
   for (y = 0; y < ih; y++)
     {
        unsigned int *dst;

        dst = pixels_out + y;
        for (x = 0; x < iw; x++)
          {
             *dst = *src;
             src--;
             dst += ih;
          }
     }
}

static void *
eng_image_orient_set(void *data EINA_UNUSED, void *image, Evas_Image_Orient orient)
{
   Image_Entry *im;
   Image_Entry *im_new;
   void *pixels_in;
   void *pixels_out;
   int tw, th;
   int w, h;

   if (!image) return NULL;
   im = image;
   if (im->orient == orient) return im;

   if (im->orient == EVAS_IMAGE_ORIENT_90 ||
       im->orient == EVAS_IMAGE_ORIENT_270 ||
       im->orient == EVAS_IMAGE_FLIP_TRANSPOSE ||
       im->orient == EVAS_IMAGE_FLIP_TRANSVERSE)
     {
        tw = im->h;
        th = im->w;
     }
   else
     {
        th = im->h;
        tw = im->w;
     }

   if (orient == EVAS_IMAGE_ORIENT_90 ||
       orient == EVAS_IMAGE_ORIENT_270 ||
       orient == EVAS_IMAGE_FLIP_TRANSPOSE ||
       orient == EVAS_IMAGE_FLIP_TRANSVERSE)
     {
        w = th;
        h = tw;
     }
   else
     {
        h = th;
        w = tw;
     }

   im_new = evas_cache_image_copied_data(evas_common_image_cache_get(),
                                         w, h, NULL, im->flags.alpha,
                                         EVAS_COLORSPACE_ARGB8888);
   if (!im_new) return im;

   evas_cache_image_load_data(im);

   pixels_in = evas_cache_image_pixels(im);
   pixels_out = evas_cache_image_pixels(im_new);

   if (!pixels_out || !pixels_in) goto on_error;

   if ((im->orient <= EVAS_IMAGE_ORIENT_270) &&
       (orient <= EVAS_IMAGE_ORIENT_270))
     {
        // we are rotating from one anglee to another, so figure out delta
        // and apply that delta
        Evas_Image_Orient rot_delta = (4 + orient - im->orient) % 4;
        switch (rot_delta)
          {
           case EVAS_IMAGE_ORIENT_0:
              ERR("You shouldn't get this message, wrong orient value");
              goto on_error;
           case EVAS_IMAGE_ORIENT_90:
              _image_rotate_90(pixels_out, pixels_in, im->w, im->h);
              break;
           case EVAS_IMAGE_ORIENT_180:
              _image_rotate_180(pixels_out, pixels_in, im->w, im->h);
              break;
           case EVAS_IMAGE_ORIENT_270:
              _image_rotate_270(pixels_out, pixels_in, im->w, im->h);
              break;
           default:
              ERR("Wrong orient value");
              goto on_error;
          }
     }
   else if (((im->orient == EVAS_IMAGE_ORIENT_NONE) &&
             (orient == EVAS_IMAGE_FLIP_HORIZONTAL)) ||
            ((im->orient == EVAS_IMAGE_FLIP_HORIZONTAL) &&
             (orient == EVAS_IMAGE_ORIENT_NONE)))
     {
        // flip horizontally to get the new orientation
        _image_flip_horizontal(pixels_out, pixels_in, im->w, im->h);
     }
   else if (((im->orient == EVAS_IMAGE_ORIENT_NONE) &&
             (orient == EVAS_IMAGE_FLIP_VERTICAL)) ||
            ((im->orient == EVAS_IMAGE_FLIP_VERTICAL) &&
             (orient == EVAS_IMAGE_ORIENT_NONE)))
     {
        // flip vertically to get the new orientation
        _image_flip_vertical(pixels_out, pixels_in, im->w, im->h);
     }
   else
     {
        // generic solution - undo the previous orientation and then apply the
        // new one after that
        void *pixels_tmp;

        pixels_tmp = malloc(sizeof (unsigned int) * w * h);
        if (!pixels_tmp) goto on_error;

        // Undoing previous rotation
        switch (im->orient)
          {
           case EVAS_IMAGE_ORIENT_0:
              // FIXME: could be easily optimized away
              memcpy(pixels_tmp, pixels_in, sizeof (unsigned int) * w * h);
              break;
           case EVAS_IMAGE_ORIENT_90:
              _image_rotate_270(pixels_tmp, pixels_in, im->w, im->h);
              break;
           case EVAS_IMAGE_ORIENT_180:
              _image_rotate_180(pixels_tmp, pixels_in, im->w, im->h);
              break;
           case EVAS_IMAGE_ORIENT_270:
              _image_rotate_90(pixels_tmp, pixels_in, im->w, im->h);
              break;
           case EVAS_IMAGE_FLIP_HORIZONTAL:
              _image_flip_horizontal(pixels_tmp, pixels_in, im->w, im->h);
              break;
           case EVAS_IMAGE_FLIP_VERTICAL:
              _image_flip_vertical(pixels_tmp, pixels_in, im->w, im->h);
              break;
           case EVAS_IMAGE_FLIP_TRANSPOSE:
              _image_flip_transpose(pixels_tmp, pixels_in, im->w, im->h);
              break;
           case EVAS_IMAGE_FLIP_TRANSVERSE:
              _image_flip_transverse(pixels_tmp, pixels_in, im->w, im->h);
              break;
           default:
              ERR("Wrong orient value");
              free(pixels_tmp);
              goto on_error;
          }

        // Doing the new requested one
        switch (orient)
          {
           case EVAS_IMAGE_ORIENT_0:
              // FIXME: could be easily optimized away
              memcpy(pixels_out, pixels_tmp, sizeof (unsigned int) * w * h);
              break;
           case EVAS_IMAGE_ORIENT_90:
              _image_rotate_90(pixels_out, pixels_tmp, tw, th);
              break;
           case EVAS_IMAGE_ORIENT_180:
              _image_rotate_180(pixels_out, pixels_tmp, tw, th);
              break;
           case EVAS_IMAGE_ORIENT_270:
              _image_rotate_270(pixels_out, pixels_tmp, tw, th);
              break;
           case EVAS_IMAGE_FLIP_HORIZONTAL:
              _image_flip_horizontal(pixels_out, pixels_tmp, tw, th);
              break;
           case EVAS_IMAGE_FLIP_VERTICAL:
              _image_flip_vertical(pixels_out, pixels_tmp, tw, th);
              break;
           case EVAS_IMAGE_FLIP_TRANSPOSE:
              _image_flip_transpose(pixels_out, pixels_tmp, tw, th);
              break;
           case EVAS_IMAGE_FLIP_TRANSVERSE:
              _image_flip_transverse(pixels_out, pixels_tmp, tw, th);
              break;
          }

        free(pixels_tmp);
     }

   im_new->orient = orient;
   evas_cache_image_drop(im);

   return im_new;

 on_error:
   evas_cache_image_drop(im_new);
   return im;
}

static Evas_Image_Orient
eng_image_orient_get(void *data EINA_UNUSED, void *image)
{
   Image_Entry *im;

   if (!image) return EVAS_IMAGE_ORIENT_NONE;
   im = image;
   return im->orient;
}

static void
eng_image_data_preload_request(void *data EINA_UNUSED, void *image, const Eo *target)
{
   RGBA_Image *im = image;
   if (!im) return;

   evas_cache_image_preload_data(&im->cache_entry, target, NULL, NULL);
}

static void
eng_image_data_preload_cancel(void *data EINA_UNUSED, void *image, const Eo *target, Eina_Bool force)
{
   RGBA_Image *im = image;

   if (!im) return;

   evas_cache_image_preload_cancel(&im->cache_entry, target, force);
}

static void
_draw_thread_image_draw(void *data)
{
   Evas_Thread_Command_Image *image = data;

   if (image->smooth)
     evas_common_scale_rgba_smooth_draw
       (image->image, image->surface,
        image->clip.x, image->clip.y, image->clip.w, image->clip.h,
        image->mul_col, image->render_op,
        image->src.x, image->src.y, image->src.w, image->src.h,
        image->dst.x, image->dst.y, image->dst.w, image->dst.h,
        image->mask, image->mask_x, image->mask_y);
   else
     evas_common_scale_rgba_sample_draw
       (image->image, image->surface,
        image->clip.x, image->clip.y, image->clip.w, image->clip.h,
        image->mul_col, image->render_op,
        image->src.x, image->src.y, image->src.w, image->src.h,
        image->dst.x, image->dst.y, image->dst.w, image->dst.h,
        image->mask, image->mask_x, image->mask_y);

   eina_mempool_free(_mp_command_image, image);
}

static Eina_Bool
_image_draw_thread_cmd(RGBA_Image *src, RGBA_Image *dst, RGBA_Draw_Context *dc, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int dst_w, int dst_h, int smooth)
{
   Evas_Thread_Command_Image *cr;
   int clip_x, clip_y, clip_w, clip_h;

   if ((dst_w <= 0) || (dst_h <= 0)) return EINA_FALSE;
   if (!(RECTS_INTERSECT(dst_x, dst_y, dst_w, dst_h,
                         0, 0, dst->cache_entry.w, dst->cache_entry.h))) return EINA_FALSE;

   cr = eina_mempool_malloc(_mp_command_image, sizeof (Evas_Thread_Command_Image));
   if (!cr) return EINA_FALSE;

   cr->image = src;
   cr->surface = dst;
   EINA_RECTANGLE_SET(&cr->src, src_x, src_y, src_w, src_h);
   EINA_RECTANGLE_SET(&cr->dst, dst_x, dst_y, dst_w, dst_h);

   if (dc->clip.use)
     {
	clip_x = dc->clip.x;
	clip_y = dc->clip.y;
	clip_w = dc->clip.w;
	clip_h = dc->clip.h;
     }
   else
     {
	clip_x = 0;
	clip_y = 0;
	clip_w = dst->cache_entry.w;
	clip_h = dst->cache_entry.h;
     }

   /* Set image mask, if any */
   cr->mask = dc->clip.mask;
   cr->mask_x = dc->clip.mask_x;
   cr->mask_y = dc->clip.mask_y;
   if (cr->mask)
     {
        Image_Entry *im = cr->mask;
        RECTS_CLIP_TO_RECT(clip_x, clip_y, clip_w, clip_h,
                           cr->mask_x, cr->mask_y,
                           im->w, im->h);
     }

   EINA_RECTANGLE_SET(&cr->clip, clip_x, clip_y, clip_w, clip_h);

   cr->mul_col = dc->mul.use ? dc->mul.col : 0xffffffff;
   cr->render_op = dc->render_op;
   cr->smooth = smooth;

   QCMD(_draw_thread_image_draw, cr);

   return EINA_TRUE;
}

static Eina_Bool
_image_draw_thread_cmd_smooth(RGBA_Image *src, RGBA_Image *dst, RGBA_Draw_Context *dc, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int dst_w, int dst_h)
{
   return _image_draw_thread_cmd(src, dst, dc,
                                 src_x, src_y, src_w, src_h,
                                 dst_x, dst_y, dst_w, dst_h,
                                 1);
}

static Eina_Bool
_image_draw_thread_cmd_sample(RGBA_Image *src, RGBA_Image *dst, RGBA_Draw_Context *dc, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int dst_w, int dst_h)
{
   return _image_draw_thread_cmd(src, dst, dc,
                                 src_x, src_y, src_w, src_h,
                                 dst_x, dst_y, dst_w, dst_h,
                                 0);
}

static Eina_Bool
_image_thr_cb_smooth(RGBA_Image *src, RGBA_Image *dst, RGBA_Draw_Context *dc, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int dst_w, int dst_h)
{
   return evas_common_scale_rgba_in_to_out_clip_cb(src, dst, dc,
                                                   src_x, src_y, src_w, src_h,
                                                   dst_x, dst_y, dst_w, dst_h,
                                                   _image_draw_thread_cmd_smooth);
}

static Eina_Bool
_image_thr_cb_sample(RGBA_Image *src, RGBA_Image *dst, RGBA_Draw_Context *dc, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int dst_w, int dst_h)
{
   return evas_common_scale_rgba_in_to_out_clip_cb(src, dst, dc,
                                                   src_x, src_y, src_w, src_h,
                                                   dst_x, dst_y, dst_w, dst_h,
                                                   _image_draw_thread_cmd_sample);
}

static Eina_Bool
eng_image_draw(void *engine EINA_UNUSED, void *data EINA_UNUSED, void *context, void *surface, void *image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int dst_w, int dst_h, int smooth, Eina_Bool do_async)
{
   RGBA_Image *im;

   if (!image) return EINA_FALSE;
   im = image;
   if (im->native.func.bind)
      im->native.func.bind(image, src_x, src_y, src_w, src_h);

   if (do_async)
     {
        Eina_Bool ret;
        if (!evas_common_rgba_image_scalecache_prepare(image, surface, context, smooth,
                                                       src_x, src_y, src_w, src_h,
                                                       dst_x, dst_y, dst_w, dst_h))
          {
             if (im->cache_entry.space == EVAS_COLORSPACE_ARGB8888)
               {
                  evas_cache_image_load_data(&im->cache_entry);
                  if (!im->cache_entry.flags.loaded)
                    {
                       if (im->native.func.unbind)
                         im->native.func.unbind(image);
                       return EINA_FALSE;
                    }
               }
          }
        ret = evas_common_rgba_image_scalecache_do_cbs(image, surface,
                                                        context, smooth,
                                                        src_x, src_y, src_w, src_h,
                                                        dst_x, dst_y, dst_w, dst_h,
                                                        _image_thr_cb_sample,
                                                        _image_thr_cb_smooth);
        if (im->native.func.unbind)
           im->native.func.unbind(image);
        return ret;
     }
#ifdef BUILD_PIPE_RENDER
   else if ((cpunum > 1))
     {
        evas_common_rgba_image_scalecache_prepare((Image_Entry *)(im),
                                                  surface, context, smooth,
                                                  src_x, src_y, src_w, src_h,
                                                  dst_x, dst_y, dst_w, dst_h);

        evas_common_pipe_image_draw(im, surface, context, smooth,
                                    src_x, src_y, src_w, src_h,
                                    dst_x, dst_y, dst_w, dst_h);
     }
#endif
   else
     {
        evas_common_rgba_image_scalecache_prepare
          (&im->cache_entry, surface, context, smooth,
           src_x, src_y, src_w, src_h,
           dst_x, dst_y, dst_w, dst_h);
        evas_common_rgba_image_scalecache_do
          (&im->cache_entry, surface, context, smooth,
           src_x, src_y, src_w, src_h,
           dst_x, dst_y, dst_w, dst_h);

        evas_common_cpu_end_opt();
     }

   if (im->native.func.unbind)
      im->native.func.unbind(image);
   return EINA_FALSE;
}

static void
_map_image_draw(RGBA_Image *src, RGBA_Image *dst, RGBA_Draw_Context *dc, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int dst_w, int dst_h, int smooth)
{
   int clip_x, clip_y, clip_w, clip_h;
   DATA32 mul_col;

   if ((dst_w <= 0) || (dst_h <= 0)) return;
   if (!(RECTS_INTERSECT(dst_x, dst_y, dst_w, dst_h,
                         0, 0, dst->cache_entry.w, dst->cache_entry.h))) return;

   if (dc->clip.use)
     {
	clip_x = dc->clip.x;
	clip_y = dc->clip.y;
	clip_w = dc->clip.w;
	clip_h = dc->clip.h;
     }
   else
     {
	clip_x = clip_y = 0;
	clip_w = dst->cache_entry.w;
	clip_h = dst->cache_entry.h;
     }

   mul_col = dc->mul.use ? dc->mul.col : 0xffffffff;

   if (smooth)
     evas_common_scale_rgba_smooth_draw(src, dst,
                                        clip_x, clip_y, clip_w, clip_h,
                                        mul_col, dc->render_op,
                                        src_x, src_y, src_w, src_h,
                                        dst_x, dst_y, dst_w, dst_h,
                                        dc->clip.mask, dc->clip.mask_x, dc->clip.mask_y);
   else
     evas_common_scale_rgba_sample_draw(src, dst,
                                        clip_x, clip_y, clip_w, clip_h,
                                        mul_col, dc->render_op,
                                        src_x, src_y, src_w, src_h,
                                        dst_x, dst_y, dst_w, dst_h,
                                        dc->clip.mask, dc->clip.mask_x, dc->clip.mask_y);
}

static Eina_Bool
_map_image_sample_draw(RGBA_Image *src, RGBA_Image *dst, RGBA_Draw_Context *dc, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int dst_w, int dst_h)
{
   _map_image_draw(src, dst, dc,
                   src_x, src_y, src_w, src_h,
                   dst_x, dst_y, dst_w, dst_h, 0);
   return EINA_TRUE;
}

static Eina_Bool
_map_image_smooth_draw(RGBA_Image *src, RGBA_Image *dst, RGBA_Draw_Context *dc, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int dst_w, int dst_h)
{
   _map_image_draw(src, dst, dc,
                   src_x, src_y, src_w, src_h,
                   dst_x, dst_y, dst_w, dst_h, 1);
   return EINA_TRUE;
}

static void
_draw_thread_map_draw(void *data)
{
   Evas_Thread_Command_Map *map = data;
   int offset = map->offset;
   RGBA_Map *m = map->map;
   RGBA_Image *im = map->image;
   int dx, dy, dw, dh;

   do
     {
        if (m->count - offset < 4) goto free_out;

        //Fully Transparency. Skip this.
        if (!(m->pts[0 + offset].col & 0xff000000) &&
            !(m->pts[1 + offset].col & 0xff000000) &&
            !(m->pts[2 + offset].col & 0xff000000) &&
            !(m->pts[3 + offset].col & 0xff000000))
          {
             offset += 4;
             continue;
          }

        if (!(map->anti_alias && map->smooth) &&    //For sub-pixel rendering
            (m->pts[0 + offset].x == m->pts[3 + offset].x) &&
            (m->pts[1 + offset].x == m->pts[2 + offset].x) &&
            (m->pts[0 + offset].y == m->pts[1 + offset].y) &&
            (m->pts[3 + offset].y == m->pts[2 + offset].y) &&
            (m->pts[0 + offset].x <= m->pts[1 + offset].x) &&
            (m->pts[0 + offset].y <= m->pts[2 + offset].y) &&
            (m->pts[0 + offset].u == 0) &&
            (m->pts[0 + offset].v == 0) &&
            (m->pts[1 + offset].u == (int)(im->cache_entry.w << FP)) &&
            (m->pts[1 + offset].v == 0) &&
            (m->pts[2 + offset].u == (int)(im->cache_entry.w << FP)) &&
            (m->pts[2 + offset].v == (int)(im->cache_entry.h << FP)) &&
            (m->pts[3 + offset].u == 0) &&
            (m->pts[3 + offset].v == (int)(im->cache_entry.h << FP)) &&
            (m->pts[0].col == m->pts[1].col) &&
            (m->pts[1].col == m->pts[2].col) &&
            (m->pts[2].col == m->pts[3].col))
          {
             DATA32 col;
             Eina_Bool use;

             dx = m->pts[0 + offset].x >> FP;
             dy = m->pts[0 + offset].y >> FP;
             dw = (m->pts[2 + offset].x >> FP) - dx;
             dh = (m->pts[2 + offset].y >> FP) - dy;

             col = map->image_ctx->mul.col;
             use = map->image_ctx->mul.use;
             if (use) map->image_ctx->mul.col = MUL4_SYM(col, m->pts[0].col);
             else map->image_ctx->mul.col = m->pts[0].col;
             map->image_ctx->mul.use = 1;

             if (map->smooth)
               evas_common_scale_rgba_in_to_out_clip_cb
                 (im, map->surface, map->image_ctx,
                  0, 0, im->cache_entry.w, im->cache_entry.h,
                  dx, dy, dw, dh, _map_image_smooth_draw);
             else
               evas_common_scale_rgba_in_to_out_clip_cb
                 (im, map->surface, map->image_ctx,
                  0, 0, im->cache_entry.w, im->cache_entry.h,
                  dx, dy, dw, dh, _map_image_sample_draw);

             map->image_ctx->mul.col = col;
             map->image_ctx->mul.use = use;
          }
        else
          {
             evas_common_map_rgba_draw
               (im, map->surface,
                map->clip.x, map->clip.y, map->clip.w, map->clip.h,
                map->mul_col, map->render_op, m->count - offset, &m->pts[offset],
                map->smooth, map->anti_alias, map->level,
                map->mask, map->mask_x, map->mask_y);
          }

        evas_common_cpu_end_opt();

        offset += 4;
     }
   while ((m->count > 4) && (m->count - offset >= 4));

 free_out:
   free(m);
   evas_common_draw_context_free(map->image_ctx);
   eina_mempool_free(_mp_command_map, map);
}

static Eina_Bool
_map_draw_thread_cmd(RGBA_Image *src, RGBA_Image *dst, RGBA_Draw_Context *dc, RGBA_Map *map, int smooth, int level, int offset)
{
   Evas_Thread_Command_Map *cm;
   int clip_x, clip_y, clip_w, clip_h;

   cm = eina_mempool_malloc(_mp_command_map, sizeof (Evas_Thread_Command_Map));
   if (!cm) return EINA_FALSE;

   cm->image = src;
   cm->image_ctx = evas_common_draw_context_dup(dc);
   cm->surface = dst;

   if (dc->clip.use)
     {
	clip_x = dc->clip.x;
	clip_y = dc->clip.y;
	clip_w = dc->clip.w;
	clip_h = dc->clip.h;
     }
   else
     {
	clip_x = clip_y = 0;
	clip_w = dst->cache_entry.w;
	clip_h = dst->cache_entry.h;
     }

   EINA_RECTANGLE_SET(&cm->clip, clip_x, clip_y, clip_w, clip_h);

   cm->mul_col = dc->mul.use ? dc->mul.col : 0xffffffff;
   cm->render_op = dc->render_op;
   cm->anti_alias = dc->anti_alias;

   cm->map = calloc(1, sizeof(RGBA_Map) +
                    sizeof(RGBA_Map_Point) * map->count);
   cm->map->engine_data = map->engine_data;
   cm->map->image.w = map->image.w;
   cm->map->image.h = map->image.h;
   cm->map->uv.w = map->uv.w;
   cm->map->uv.h = map->uv.h;
   cm->map->x = map->x;
   cm->map->y = map->y;
   cm->map->count = map->count;
   memcpy(&cm->map->pts[0], &map->pts[0], sizeof(RGBA_Map_Point) * map->count);

   cm->smooth = smooth;
   cm->level = level;
   cm->offset = offset;

   cm->mask = dc->clip.mask;
   cm->mask_x = dc->clip.mask_x;
   cm->mask_y = dc->clip.mask_y;

   QCMD(_draw_thread_map_draw, cm);

   return EINA_TRUE;
}

static void
evas_software_image_map_draw(void *engine EINA_UNUSED, void *data, void *context, RGBA_Image *surface, RGBA_Image *im, RGBA_Map *m, int smooth, int level, int offset)
{
   if (m->count - offset < 4) return;

   if ((m->pts[0 + offset].x == m->pts[3 + offset].x) &&
       (m->pts[1 + offset].x == m->pts[2 + offset].x) &&
       (m->pts[0 + offset].y == m->pts[1 + offset].y) &&
       (m->pts[3 + offset].y == m->pts[2 + offset].y) &&
       (m->pts[0 + offset].x <= m->pts[1 + offset].x) &&
       (m->pts[0 + offset].y <= m->pts[2 + offset].y) &&
       (m->pts[0 + offset].u == 0) &&
       (m->pts[0 + offset].v == 0) &&
       (m->pts[1 + offset].u == (int)(im->cache_entry.w << FP)) &&
       (m->pts[1 + offset].v == 0) &&
       (m->pts[2 + offset].u == (int)(im->cache_entry.w << FP)) &&
       (m->pts[2 + offset].v == (int)(im->cache_entry.h << FP)) &&
       (m->pts[3 + offset].u == 0) &&
       (m->pts[3 + offset].v == (int)(im->cache_entry.h << FP)) &&
       (m->pts[0 + offset].col == m->pts[1 + offset].col) &&
       (m->pts[1 + offset].col == m->pts[2 + offset].col) &&
       (m->pts[2 + offset].col == m->pts[3 + offset].col))
     {
        DATA32 col;
        int a, r, g, b;
        int dx, dy, dw, dh;
        int mul;

        mul = eng_context_multiplier_get(data, context, &r, &g, &b, &a);
        if (mul) col = MUL4_256(a, r, g, b, m->pts[0 + offset].col);
        else col = m->pts[0 + offset].col;
        eng_context_multiplier_set(data, context, R_VAL(&col), G_VAL(&col), B_VAL(&col), A_VAL(&col));

        dx = m->pts[0 + offset].x >> FP;
        dy = m->pts[0 + offset].y >> FP;
        dw = (m->pts[2 + offset].x >> FP) - dx;
        dh = (m->pts[2 + offset].y >> FP) - dy;
        eng_image_draw
          (engine, data, context, surface, im,
           0, 0, im->cache_entry.w, im->cache_entry.h,
           dx, dy, dw, dh, smooth,
           EINA_FALSE);

        if (mul) eng_context_multiplier_set(data, context, r, g, b, a);
        else eng_context_multiplier_unset(data, context);
     }
   else
     {
#ifdef BUILD_PIPE_RENDER
        if ((cpunum > 1))
	  {
             evas_common_pipe_map_draw(im, surface, context, m, smooth, level);
             return;
          }
        else
#endif
          {
             evas_common_map_rgba(im, surface, context, m->count - offset, &m->pts[offset], smooth, level);
          }
     }
   evas_common_cpu_end_opt();

   if (m->count > 4)
     {
        evas_software_image_map_draw(engine, data, context, surface, im, m, smooth, level, offset + 4);
     }
}

static Eina_Bool
eng_image_map_draw(void *engine EINA_UNUSED, void *data, void *context, void *surface, void *image, RGBA_Map *m, int smooth, int level, Eina_Bool do_async)
{
   RGBA_Image *im = image;

   if (!im) return EINA_FALSE;
   if (m->count < 3) return EINA_FALSE;

   if (do_async)
     {
        /* Since the thread that'll draw the map won't call eng_image_draw()
         * (which sends the load request of source image to Cserve2) - we need
         * to send the load request here before enqueueing thread command.
         */
        if (im->cache_entry.space == EVAS_COLORSPACE_ARGB8888)
          {
             evas_cache_image_load_data(&im->cache_entry);

             if (!im->cache_entry.flags.loaded) return EINA_FALSE;
          }

        return evas_common_map_thread_rgba_cb(im, surface, context,
                                              m, smooth, level, 0,
                                              _map_draw_thread_cmd);
     }
   else
     evas_software_image_map_draw(engine, data, context, surface, im, m,
                                  smooth, level, 0);

   return EINA_FALSE;
}

static void
eng_image_map_clean(void *data EINA_UNUSED, RGBA_Map *m)
{
   evas_common_map_rgba_clean(m);
}

static void *
eng_image_map_surface_new(void *data EINA_UNUSED, int w, int h, int alpha)
{
   void *surface;

   surface = evas_cache_image_copied_data(evas_common_image_cache_get(),
                                          w, h, NULL, alpha,
                                          EVAS_COLORSPACE_ARGB8888);
   if (!surface) return NULL;
   evas_cache_image_pixels(surface);
   return surface;
}

static void
eng_image_scale_hint_set(void *data EINA_UNUSED, void *image, int hint)
{
   Image_Entry *im;

   if (!image) return;
   im = image;
   im->scale_hint = hint;
}

static int
eng_image_scale_hint_get(void *data EINA_UNUSED, void *image)
{
   Image_Entry *im;

   if (!image) return EVAS_IMAGE_SCALE_HINT_NONE;
   im = image;
   return im->scale_hint;
}

static Eina_Bool
eng_image_animated_get(void *data EINA_UNUSED, void *image)
{
   Image_Entry *im;

   if (!image) return EINA_FALSE;
   im = image;
   return im->animated.animated;
}

static int
eng_image_animated_frame_count_get(void *data EINA_UNUSED, void *image)
{
   Image_Entry *im;

   if (!image) return -1;
   im = image;
   if (!im->animated.animated) return -1;
   return im->animated.frame_count;
}

static Evas_Image_Animated_Loop_Hint
eng_image_animated_loop_type_get(void *data EINA_UNUSED, void *image)
{
   Image_Entry *im;

   if (!image) return EVAS_IMAGE_ANIMATED_HINT_NONE;
   im = image;
   if (!im->animated.animated) return EVAS_IMAGE_ANIMATED_HINT_NONE;
   return im->animated.loop_hint;
}

static int
eng_image_animated_loop_count_get(void *data EINA_UNUSED, void *image)
{
   Image_Entry *im;

   if (!image) return -1;
   im = image;
   if (!im->animated.animated) return -1;
   return im->animated.loop_count;
}

static double
eng_image_animated_frame_duration_get(void *data EINA_UNUSED, void *image, int start_frame, int frame_num)
{
   Image_Entry *im;

   if (!image) return -1;
   im = image;
   if (!im->animated.animated) return -1;
   return evas_common_load_rgba_image_frame_duration_from_file(im, start_frame, frame_num);
}

static Eina_Bool
eng_image_animated_frame_set(void *data EINA_UNUSED, void *image, int frame_index)
{
   Image_Entry *im;

   if (!image) return EINA_FALSE;
   im = image;
   if (!im->animated.animated) return EINA_FALSE;
   if (im->animated.cur_frame == frame_index) return EINA_FALSE;
   im->animated.cur_frame = frame_index;
   return EINA_TRUE;
}

static int
eng_image_animated_frame_get(void *data EINA_UNUSED, void *image)
{
   Image_Entry *im;

   if (!image) return EINA_FALSE;
   im = image;
   if (!im->animated.animated) return EINA_FALSE;

   return im->animated.cur_frame;
}

static void
_draw_thread_multi_font_draw(void *data)
{
   Evas_Thread_Command_Multi_Font *mf = data;
   Evas_Font_Array_Data           *itr;

   EINA_INARRAY_FOREACH(mf->texts->array, itr)
     {
        unsigned int r, g, b, a;
        int x = mf->x + itr->x, y = mf->y;

        r = itr->color.r;
        g = itr->color.g;
        b = itr->color.b;
        a = itr->color.a;

        eng_context_color_set(NULL, mf->context, r, g, b, a);
        evas_common_font_draw(mf->surface, mf->context, x, y, itr->glyphs);
        evas_common_cpu_end_opt();
     }

   evas_common_draw_context_free(mf->context);
   eina_mempool_free(_mp_command_multi_font, mf);
}

static Eina_Bool
_multi_font_draw_thread_cmd(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, Evas_Font_Array *texts)
{
   Evas_Thread_Command_Multi_Font *mf;

   mf = eina_mempool_malloc(_mp_command_multi_font,
                            sizeof(Evas_Thread_Command_Multi_Font));
   if (!mf)
     {
        ERR("Failed to allocate memory on mempool for multiple text_props "
            "commands.");
        return EINA_FALSE;
     }

   mf->context = evas_common_draw_context_dup(dc);
   mf->surface = dst;
   mf->x = x;
   mf->y = y;
   mf->texts = texts;

   QCMD(_draw_thread_multi_font_draw, mf);

   return EINA_TRUE;
}

static Eina_Bool
eng_multi_font_draw(void *engine EINA_UNUSED, void *data EINA_UNUSED, void *context, void *surface, Evas_Font_Set *font EINA_UNUSED, int x, int y, int w EINA_UNUSED, int h EINA_UNUSED, int ow EINA_UNUSED, int oh EINA_UNUSED, Evas_Font_Array *texts, Eina_Bool do_async)
{
   if (!texts) return EINA_FALSE;

   if (do_async)
     return _multi_font_draw_thread_cmd(surface, context, x, y, texts);

   return EINA_FALSE;
}

static Eina_Bool
eng_pixel_alpha_get(void *image, int x, int y, DATA8 *alpha, int src_region_x, int src_region_y, int src_region_w, int src_region_h, int dst_region_x, int dst_region_y, int dst_region_w, int dst_region_h)
{
   RGBA_Image *im = image;
   int px, py, dx, dy, sx, sy, src_w, src_h;
   double scale_w, scale_h;

   if (!im) return EINA_FALSE;

   if ((dst_region_x > x) || (x >= (dst_region_x + dst_region_w)) ||
       (dst_region_y > y) || (y >= (dst_region_y + dst_region_h)))
     {
        *alpha = 0;
        return EINA_FALSE;
     }

   src_w = im->cache_entry.w;
   src_h = im->cache_entry.h;
   if ((src_w == 0) || (src_h == 0))
     {
        *alpha = 0;
        return EINA_TRUE;
     }

   EINA_SAFETY_ON_TRUE_GOTO(src_region_x < 0, error_oob);
   EINA_SAFETY_ON_TRUE_GOTO(src_region_y < 0, error_oob);
   EINA_SAFETY_ON_TRUE_GOTO(src_region_x + src_region_w > src_w, error_oob);
   EINA_SAFETY_ON_TRUE_GOTO(src_region_y + src_region_h > src_h, error_oob);

   scale_w = (double)dst_region_w / (double)src_region_w;
   scale_h = (double)dst_region_h / (double)src_region_h;

   /* point at destination */
   dx = x - dst_region_x;
   dy = y - dst_region_y;

   /* point at source */
   sx = dx / scale_w;
   sy = dy / scale_h;

   /* pixel point (translated) */
   px = src_region_x + sx;
   py = src_region_y + sy;
   EINA_SAFETY_ON_TRUE_GOTO(px >= src_w, error_oob);
   EINA_SAFETY_ON_TRUE_GOTO(py >= src_h, error_oob);

   switch (im->cache_entry.space)
     {
     case EVAS_COLORSPACE_ARGB8888:
       {
          DATA32 *pixel;

          evas_cache_image_load_data(&im->cache_entry);

          if (!im->cache_entry.flags.loaded)
            {
               ERR("im %p has no pixels loaded yet", im);
               return EINA_FALSE;
            }

          pixel = im->image.data;
          pixel += ((py * src_w) + px);
          *alpha = ((*pixel) >> 24) & 0xff;
       }
       break;

     default:
        ERR("Colorspace %d not supported.", im->cache_entry.space);
        *alpha = 0;
     }

   return EINA_TRUE;

 error_oob:
   ERR("Invalid region src=(%d, %d, %d, %d), dst=(%d, %d, %d, %d), image=%dx%d",
       src_region_x, src_region_y, src_region_w, src_region_h,
       dst_region_x, dst_region_y, dst_region_w, dst_region_h,
       src_w, src_h);
   *alpha = 0;
   return EINA_TRUE;
}

static void
eng_image_cache_flush(void *data EINA_UNUSED)
{
   int tmp_size;

   tmp_size = evas_common_image_get_cache();
   evas_common_image_set_cache(0);
   evas_common_rgba_image_scalecache_flush();
   evas_common_image_set_cache(tmp_size);
}

static void
eng_image_cache_set(void *data EINA_UNUSED, int bytes)
{
   evas_common_image_set_cache(bytes);
   evas_common_rgba_image_scalecache_size_set(bytes);
}

static int
eng_image_cache_get(void *data EINA_UNUSED)
{
   return evas_common_image_get_cache();
}

static Evas_Font_Set *
eng_font_load(void *data EINA_UNUSED, const char *name, int size,
      Font_Rend_Flags wanted_rend, Efl_Text_Font_Bitmap_Scalable bitmap_scalable)
{
   return (Evas_Font_Set *) evas_common_font_load(name, size, wanted_rend, bitmap_scalable);
}

static Evas_Font_Set *
eng_font_memory_load(void *data EINA_UNUSED, const char *source, const char *name, int size, const void *fdata, int fdata_size,
                     Font_Rend_Flags wanted_rend, Efl_Text_Font_Bitmap_Scalable bitmap_scalable)
{
   return (Evas_Font_Set *) evas_common_font_memory_load(source, name, size,
         fdata, fdata_size, wanted_rend, bitmap_scalable);
}

static Evas_Font_Set *
eng_font_add(void *data EINA_UNUSED, Evas_Font_Set *font, const char *name, int size, Font_Rend_Flags wanted_rend,
             Efl_Text_Font_Bitmap_Scalable bitmap_scalable)
{
   return (Evas_Font_Set *) evas_common_font_add((RGBA_Font *) font, name,
         size, wanted_rend, bitmap_scalable);
}

static Evas_Font_Set *
eng_font_memory_add(void *data EINA_UNUSED, Evas_Font_Set *font, const char *source, const char *name, int size, const void *fdata, int fdata_size,
                    Font_Rend_Flags wanted_rend, Efl_Text_Font_Bitmap_Scalable bitmap_scalable)
{
   return (Evas_Font_Set *) evas_common_font_memory_add((RGBA_Font *) font,
         source, name, size, fdata, fdata_size, wanted_rend, bitmap_scalable);
}

static void
eng_font_free(void *data EINA_UNUSED, Evas_Font_Set *font)
{
   evas_common_font_free((RGBA_Font *) font);
}

static int
eng_font_ascent_get(void *data EINA_UNUSED, Evas_Font_Set *font)
{
   return evas_common_font_ascent_get((RGBA_Font *) font);
}

static int
eng_font_descent_get(void *data EINA_UNUSED, Evas_Font_Set *font)
{
   return evas_common_font_descent_get((RGBA_Font *) font);
}

static int
eng_font_max_ascent_get(void *data EINA_UNUSED, Evas_Font_Set *font)
{
   return evas_common_font_max_ascent_get((RGBA_Font *) font);
}

static int
eng_font_max_descent_get(void *data EINA_UNUSED, Evas_Font_Set *font)
{
   return evas_common_font_max_descent_get((RGBA_Font *) font);
}

static void
eng_font_string_size_get(void *data EINA_UNUSED, Evas_Font_Set *font, const Evas_Text_Props *text_props, int *w, int *h)
{
   evas_common_font_query_size((RGBA_Font *) font, text_props, w, h);
}

static int
eng_font_inset_get(void *data EINA_UNUSED, Evas_Font_Set *font, const Evas_Text_Props *text_props)
{
   return evas_common_font_query_inset((RGBA_Font *) font, text_props);
}

static int
eng_font_right_inset_get(void *data EINA_UNUSED, Evas_Font_Set *font, const Evas_Text_Props *text_props)
{
   return evas_common_font_query_right_inset((RGBA_Font *) font, text_props);
}

static int
eng_font_h_advance_get(void *data EINA_UNUSED, Evas_Font_Set *font, const Evas_Text_Props *text_props)
{
   int h, v;

   evas_common_font_query_advance((RGBA_Font *) font, text_props, &h, &v);
   return h;
}

static int
eng_font_v_advance_get(void *data EINA_UNUSED, Evas_Font_Set *font, const Evas_Text_Props *text_props)
{
   int h, v;

   evas_common_font_query_advance((RGBA_Font *) font, text_props, &h, &v);
   return v;
}

static int
eng_font_pen_coords_get(void *data EINA_UNUSED, Evas_Font_Set *font, const Evas_Text_Props *text_props, int pos, int *cpen_x, int *cy, int *cadv, int *ch)
{
   return evas_common_font_query_pen_coords((RGBA_Font *) font, text_props, pos, cpen_x, cy, cadv, ch);
}

static Eina_Bool
eng_font_text_props_info_create(void *data EINA_UNUSED, Evas_Font_Instance *fi, const Eina_Unicode *text, Evas_Text_Props *text_props, const Evas_BiDi_Paragraph_Props *par_props, size_t par_pos, size_t len, Evas_Text_Props_Mode mode, const char *lang)
{
   return evas_common_text_props_content_create((RGBA_Font_Int *) fi, text,
         text_props, par_props, par_pos, len, mode, lang);
}

static int
eng_font_char_coords_get(void *data EINA_UNUSED, Evas_Font_Set *font, const Evas_Text_Props *text_props, int pos, int *cx, int *cy, int *cw, int *ch)
{
   return evas_common_font_query_char_coords((RGBA_Font *) font, text_props, pos, cx, cy, cw, ch);
}

static int
eng_font_char_at_coords_get(void *data EINA_UNUSED, Evas_Font_Set *font, const Evas_Text_Props *text_props, int x, int y, int *cx, int *cy, int *cw, int *ch)
{
   return evas_common_font_query_char_at_coords((RGBA_Font *) font, text_props, x, y, cx, cy, cw, ch);
}

static int
eng_font_last_up_to_pos(void *data EINA_UNUSED, Evas_Font_Set *font, const Evas_Text_Props *text_props, int x, int y, int width_offset)
{
   return evas_common_font_query_last_up_to_pos((RGBA_Font *) font, text_props, x, y, width_offset);
}

static int
eng_font_run_font_end_get(void *data EINA_UNUSED, Evas_Font_Set *font, Evas_Font_Instance **script_fi, Evas_Font_Instance **cur_fi, Evas_Script_Type script, const Eina_Unicode *text, int run_len)
{
   return evas_common_font_query_run_font_end_get((RGBA_Font *) font,
         (RGBA_Font_Int **) script_fi, (RGBA_Font_Int **) cur_fi,
         script, text, run_len);
}

static void
_draw_thread_font_draw(void *data)
{
   Evas_Thread_Command_Font *font = data;
   RGBA_Draw_Context dc;
   memset(&dc, 0, sizeof(dc));

   dc.font_ext.data = font->font_ext_data;
   dc.font_ext.func.gl_new = font->gl_new;
   dc.font_ext.func.gl_free = font->gl_free;
   dc.font_ext.func.gl_draw = font->gl_draw;
   dc.col.col = font->col;
   dc.mul.col = font->mul_col;
   dc.mul.use = (font->mul_col == 0xffffffff) ? 0 : 1;
   dc.clip.use = font->clip_use;
   dc.clip.x = font->clip_rect.x;
   dc.clip.y = font->clip_rect.y;
   dc.clip.w = font->clip_rect.w;
   dc.clip.h = font->clip_rect.h;
   dc.clip.mask = font->mask;
   dc.clip.mask_x = font->mask_x;
   dc.clip.mask_y = font->mask_y;

   evas_common_font_rgba_draw
     (font->dst, &dc,
      font->x, font->y, font->glyphs, font->func,
      font->ext.x, font->ext.y, font->ext.w, font->ext.h,
      font->im_w, font->im_h);

   eina_mempool_free(_mp_command_font, font);
}

static Eina_Bool
_font_draw_thread_cmd(RGBA_Image *dst, RGBA_Draw_Context *dc, int x, int y, Evas_Glyph_Array *glyphs, RGBA_Gfx_Func func, int ext_x, int ext_y, int ext_w, int ext_h, int im_w, int im_h)
{
   Evas_Thread_Command_Font *cf;

   cf = eina_mempool_malloc(_mp_command_font, sizeof (Evas_Thread_Command_Font));
   if (!cf) return EINA_FALSE;

   cf->dst = dst;
   cf->x = x;
   cf->y = y;
   cf->gl_new = dc->font_ext.func.gl_new;
   cf->gl_free = dc->font_ext.func.gl_free;
   cf->gl_draw = dc->font_ext.func.gl_draw;
   cf->font_ext_data = dc->font_ext.data;
   cf->col = dc->col.col;
   cf->mul_col = dc->mul.use ? dc->mul.col : 0xffffffff;
   cf->clip_use = dc->clip.use;
   EINA_RECTANGLE_SET(&cf->clip_rect,
                      dc->clip.x, dc->clip.y, dc->clip.w, dc->clip.h);
   cf->glyphs = glyphs;
   cf->func = func;
   EINA_RECTANGLE_SET(&cf->ext, ext_x, ext_y, ext_w, ext_h);
   cf->im_w = im_w;
   cf->im_h = im_h;
   cf->mask = dc->clip.mask;
   cf->mask_x = dc->clip.mask_x;
   cf->mask_y = dc->clip.mask_y;

   QCMD(_draw_thread_font_draw, cf);

   return EINA_TRUE;
}

static Eina_Bool
eng_font_draw(void *engine EINA_UNUSED, void *data EINA_UNUSED, void *context, void *surface, Evas_Font_Set *font EINA_UNUSED, int x, int y, int w EINA_UNUSED, int h EINA_UNUSED, int ow EINA_UNUSED, int oh EINA_UNUSED, Evas_Text_Props *text_props, Eina_Bool do_async)
{
   if (do_async)
     {
        evas_common_font_draw_prepare(text_props);
        if (!text_props->glyphs) return EINA_FALSE;

        return evas_common_font_draw_cb(surface, context, x, y, text_props->glyphs,
                                        _font_draw_thread_cmd);
     }
#ifdef BUILD_PIPE_RENDER
   else if ((cpunum > 1))
     evas_common_pipe_text_draw(surface, context, x, y, text_props);
#endif
   else
     {
        evas_common_font_draw_prepare(text_props);
        evas_common_font_draw(surface, context, x, y, text_props->glyphs);
        evas_common_cpu_end_opt();
     }

   return EINA_FALSE;
}

static void
eng_font_cache_flush(void *data EINA_UNUSED)
{
   int tmp_size;

   tmp_size = evas_common_font_cache_get();
   evas_common_font_cache_set(0);
   evas_common_font_flush();
   evas_common_font_cache_set(tmp_size);
}

static void
eng_font_cache_set(void *data EINA_UNUSED, int bytes)
{
   evas_common_font_cache_set(bytes);
}

static int
eng_font_cache_get(void *data EINA_UNUSED)
{
   return evas_common_font_cache_get();
}

static void
eng_font_hinting_set(void *data EINA_UNUSED, Evas_Font_Set *font, int hinting)
{
   evas_common_font_hinting_set((RGBA_Font *) font, hinting);
}

static int
eng_font_hinting_can_hint(void *data EINA_UNUSED, int hinting)
{
   return evas_common_hinting_available(hinting);
}

static Eina_Bool
eng_canvas_alpha_get(void *data EINA_UNUSED)
{
   return EINA_TRUE;
}


static int
eng_image_load_error_get(void *data EINA_UNUSED, void *image)
{
   RGBA_Image *im;

   if (!image) return EVAS_LOAD_ERROR_NONE;
   im = image;
   return im->cache_entry.load_error;
}


//------------ Evas GL engine code ---------------//
#ifdef EVAS_GL
static inline int
_tls_check(void)
{
   // note: this is not thread safe...
   if (!_tls_init)
     {
        if (!eina_tls_new(&gl_current_ctx_key)) return 0;
        if (!eina_tls_new(&gl_current_sfc_key)) return 0;
        eina_tls_set(gl_current_ctx_key, NULL);
        eina_tls_set(gl_current_sfc_key, NULL);
     }
   _tls_init = EINA_TRUE;
   return 1;
}
#endif

static inline Eina_Bool
_check_gl(void)
{
   if (!gl_lib_init()) return 0;
   return 1;
}

static Eina_Bool
eng_gl_supports_evas_gl(void *data EINA_UNUSED)
{
   return _check_gl();
}

static void *
eng_gl_surface_create(void *data EINA_UNUSED, void *config, int w, int h)
{
#ifdef EVAS_GL
   Render_Engine_GL_Surface *sfc;
   Evas_GL_Config *cfg;

   if (!_check_gl()) return NULL;

   sfc = calloc(1, sizeof(Render_Engine_GL_Surface));
   if (!sfc) return NULL;

   cfg = (Evas_GL_Config *)config;

   sfc->initialized  = 0;
   sfc->w            = w;
   sfc->h            = h;

   // Color Format
   switch (cfg->color_format)
     {

      case EVAS_GL_RGB_888:
         sfc->internal_fmt = OSMESA_BGRA;
         sfc->internal_cpp = 4;
// FIXME: don't allow rgb buffers as evas doesn't understand them so pad out
// to 32bit bgra buffers anyway, so for now just pad out - one day do this
// nicely
//         sfc->internal_fmt = OSMESA_RGB;
//         sfc->internal_cpp = 3;
         break;
      case EVAS_GL_RGBA_8888:
         sfc->internal_fmt = OSMESA_BGRA;
         sfc->internal_cpp = 4;
         break;
      default:
         sfc->internal_fmt = OSMESA_RGBA;
         sfc->internal_cpp = 4;
         break;
     }

   // Depth Bits
   switch (cfg->depth_bits)
     {
      case EVAS_GL_DEPTH_BIT_8:
         sfc->depth_bits = 8;
         break;
      case EVAS_GL_DEPTH_BIT_16:
         sfc->depth_bits = 16;
         break;
      case EVAS_GL_DEPTH_BIT_24:
         sfc->depth_bits = 24;
         break;
      case EVAS_GL_DEPTH_BIT_32:
         sfc->depth_bits = 32;
         break;
      case EVAS_GL_DEPTH_NONE:
      default:
         sfc->depth_bits = 0;
         break;
     }

   // Stencil Bits
   switch (cfg->stencil_bits)
     {
      case EVAS_GL_STENCIL_BIT_1:
         sfc->stencil_bits = 1;
         break;
      case EVAS_GL_STENCIL_BIT_2:
         sfc->stencil_bits = 2;
         break;
      case EVAS_GL_STENCIL_BIT_4:
         sfc->stencil_bits = 4;
         break;
      case EVAS_GL_STENCIL_BIT_8:
         sfc->stencil_bits = 8;
         break;
      case EVAS_GL_STENCIL_BIT_16:
         sfc->stencil_bits = 16;
         break;
      case EVAS_GL_STENCIL_NONE:
      default:
         sfc->stencil_bits = 0;
         break;
     }

   sfc->buffer = malloc(sizeof(unsigned char)*sfc->internal_cpp*w*h);

   if (!sfc->buffer)
     {
        free(sfc);
        return NULL;
     }

   return sfc;
#else
   (void) config;
   (void) w;
   (void) h;
   return NULL;
#endif
}

static int
eng_gl_surface_destroy(void *data EINA_UNUSED, void *surface)
{
#ifdef EVAS_GL
   Render_Engine_GL_Surface *sfc;

   if (!_check_gl()) return 0;

   sfc = (Render_Engine_GL_Surface*)surface;

   if (!sfc) return 0;

   _tls_check();
   if (sfc == eina_tls_get(gl_current_sfc_key))
     eina_tls_set(gl_current_sfc_key, NULL);

   if (sfc->buffer) free(sfc->buffer);

   free(sfc);

   surface = NULL;

   return 1;
#else
   (void) surface;
   return 1;
#endif
}

static void *
eng_gl_context_create(void *data EINA_UNUSED, void *share_context, int version,
                      void *(*native_context_get)(void *) EINA_UNUSED,
                      void *(*engine_data_get)(void *) EINA_UNUSED)
{
#ifdef EVAS_GL
   Render_Engine_GL_Context *ctx;
   Render_Engine_GL_Context *share_ctx;

   if (!_check_gl()) return NULL;

   if (version != EVAS_GL_GLES_2_X)
     {
        ERR("This engine only supports OpenGL-ES 2.0 contexts for now!");
        return NULL;
     }

   ctx = calloc(1, sizeof(Render_Engine_GL_Context));

   if (!ctx) return NULL;

   share_ctx = (Render_Engine_GL_Context *)share_context;

   ctx->share_ctx = share_ctx;

#if 0
   if (share_ctx)
      ctx->context = _sym_OSMesaCreateContextExt( OSMESA_RGBA, 8, 0, 0, share_ctx->context );
   else
      ctx->context = _sym_OSMesaCreateContextExt( OSMESA_RGBA, 8, 0, 0, NULL );


   if (!ctx->context)
     {
        ERR("Error creating OSMesa Context.");
        free(ctx);
        return NULL;
     }
#endif

   ctx->initialized = 0;

   return ctx;
#else
   (void) share_context;
   return NULL;
#endif
}

static int
eng_gl_context_destroy(void *data EINA_UNUSED, void *context)
{
#ifdef EVAS_GL
   Render_Engine_GL_Context *ctx;

   if (!_check_gl()) return 0;

   ctx = (Render_Engine_GL_Context*)context;

   if (!ctx) return 0;

   _tls_check();
   if (ctx == eina_tls_get(gl_current_ctx_key))
     eina_tls_set(gl_current_ctx_key, NULL);

   _sym_OSMesaDestroyContext(ctx->context);

   free(ctx);
   context = NULL;

   return 1;
#else
   (void) context;
   return 0;
#endif
}

static int
eng_gl_make_current(void *data EINA_UNUSED, void *surface, void *context)
{
#ifdef EVAS_GL
   Render_Engine_GL_Surface *sfc;
   Render_Engine_GL_Context *ctx;
   OSMesaContext share_ctx;
   GLboolean ret;

   if (!_check_gl()) return 0;

   sfc = (Render_Engine_GL_Surface*)surface;
   ctx = (Render_Engine_GL_Context*)context;

   _tls_check();

   if ((!sfc) ^ (!ctx))
     {
        ERR("Evas GL on SW engine does not support surfaceless contexts.");
        return 0;
     }

   // Unset surface/context
   if ((!sfc) && (!ctx))
     {
        eina_tls_set(gl_current_ctx_key, NULL);
        eina_tls_set(gl_current_sfc_key, NULL);
        return 1;
     }

   // Initialize Context if it hasn't been.
   if (!ctx->initialized)
     {
        if (ctx->share_ctx)
          share_ctx = ctx->share_ctx->context;
        else
          share_ctx = NULL;

        ctx->context = _sym_OSMesaCreateContextExt(sfc->internal_fmt,
                                                   sfc->depth_bits,
                                                   sfc->stencil_bits,
                                                   0,
                                                   share_ctx);
        if (!ctx->context)
          {
             ERR("Error initializing context.");
             eina_tls_set(gl_current_ctx_key, NULL);
             eina_tls_set(gl_current_sfc_key, NULL);
             return 0;
          }

        ctx->initialized = 1;
     }


   // Call MakeCurrent
   ret = _sym_OSMesaMakeCurrent(ctx->context, sfc->buffer, GL_UNSIGNED_BYTE,
                                sfc->w, sfc->h);

   if (ret == GL_FALSE)
     {
        ERR("Error doing MakeCurrent.");
        eina_tls_set(gl_current_ctx_key, NULL);
        eina_tls_set(gl_current_sfc_key, NULL);
        return 0;
     }

   _sym_OSMesaPixelStore(OSMESA_Y_UP, 0);

   // Set the current surface/context
   ctx->current_sfc = sfc;
   sfc->current_ctx = ctx;
   eina_tls_set(gl_current_ctx_key, ctx);
   eina_tls_set(gl_current_sfc_key, sfc);

   return 1;
#else
   (void) surface;
   (void) context;
   return 1;
#endif
}

// FIXME!!! Implement later
static const char *
eng_gl_string_query(void *data EINA_UNUSED, int name EINA_UNUSED)
{
   return NULL;
}

static void *
eng_gl_proc_address_get(void *data EINA_UNUSED, const char *name)
{
#ifdef EVAS_GL
   if (!_check_gl()) return NULL;

   if (_sym_OSMesaGetProcAddress) return _sym_OSMesaGetProcAddress(name);
   return dlsym(RTLD_DEFAULT, name);
#else
   (void) name;
   return NULL;
#endif
}

static int
eng_gl_native_surface_get(void *data EINA_UNUSED, void *surface, void *native_surface)
{
#ifdef EVAS_GL
   Render_Engine_GL_Surface *sfc;
   Evas_Native_Surface *ns;

   if (!_check_gl()) return 0;

   sfc = (Render_Engine_GL_Surface*)surface;
   ns  = (Evas_Native_Surface*)native_surface;

   if (!sfc) return 0;

   ns->type = EVAS_NATIVE_SURFACE_EVASGL;
   ns->version = EVAS_NATIVE_SURFACE_VERSION;
   ns->data.evasgl.surface = sfc->buffer;

   return 1;
#else
   (void) surface;
   (void) native_surface;
   return 1;
#endif
}

static void *
eng_gl_api_get(void *data EINA_UNUSED, int version)
{
   if (version != EVAS_GL_GLES_2_X)
     return NULL;

#ifdef EVAS_GL
   if (!_check_gl()) return NULL;

   return &gl_funcs;
#else
   return NULL;
#endif
}

static int
eng_gl_error_get(void *data)
{
   Render_Output_Software_Generic *re = data;

   // TODO: Track EGL-like errors in the software engines

   if (!re->ob)
     return EVAS_GL_BAD_DISPLAY;

   return EVAS_GL_SUCCESS;
}

static void *
eng_gl_current_context_get(void *data EINA_UNUSED)
{
   _tls_check();
   return eina_tls_get(gl_current_ctx_key);
}

static void *
eng_gl_current_surface_get(void *data EINA_UNUSED)
{
   _tls_check();
   return eina_tls_get(gl_current_sfc_key);
}

static int
eng_gl_rotation_angle_get(void *data EINA_UNUSED)
{
   return 0;
}

//------------------------------------------------//

/* The following function require that any engine
   inheriting from software generic to have at the
   top of their render engine structure a
   Render_Output_Software_Generic structure that is
   initialized by evas_render_engine_software_generic_init().
 */

static void *
eng_engine_new(void)
{
   Render_Engine_Software_Generic *engine;

   engine = calloc(1, sizeof (Render_Engine_Software_Generic));
   if (!engine) return NULL;

   engine->surface_cache = generic_cache_new(engine, eng_image_free);

   return engine;
}

static void
eng_engine_free(void *engine)
{
   Render_Engine_Software_Generic *e = engine;
   Render_Output_Software_Generic *output;

   generic_cache_destroy(e->surface_cache);

   EINA_LIST_FREE(e->outputs, output)
     ERR("Output %p not properly cleaned before engine destruction.", output);

   free(e);
}

static void
eng_output_resize(void *engine EINA_UNUSED, void *data, int w, int h)
{
   Render_Output_Software_Generic *re;

   re = (Render_Output_Software_Generic *)data;
   re->outbuf_reconfigure(re->ob, w, h, re->outbuf_get_rot(re->ob),
                          OUTBUF_DEPTH_INHERIT);
   evas_common_tilebuf_free(re->tb);
   re->tb = evas_common_tilebuf_new(w, h);
   if (re->tb)
     {
        evas_common_tilebuf_set_tile_size(re->tb, TILESIZE, TILESIZE);
        evas_common_tilebuf_tile_strict_set(re->tb, re->tile_strict);
     }
   re->w = w;
   re->h = h;
}

static void
eng_output_redraws_rect_add(void *engine, int x, int y, int w, int h)
{
   Render_Engine_Software_Generic *backend = engine;
   Render_Output_Software_Generic *re;
   Eina_List *l;

   EINA_LIST_FOREACH(backend->outputs, l, re)
     evas_common_tilebuf_add_redraw(re->tb, x, y, w, h);
}

static void
eng_output_redraws_rect_del(void *engine, int x, int y, int w, int h)
{
   Render_Engine_Software_Generic *backend = engine;
   Render_Output_Software_Generic *re;
   Eina_List *l;

   EINA_LIST_FOREACH(backend->outputs, l, re)
     evas_common_tilebuf_del_redraw(re->tb, x, y, w, h);
}

static void
eng_output_redraws_clear(void *engine EINA_UNUSED, void *data)
{
   Render_Output_Software_Generic *re;

   re = (Render_Output_Software_Generic *)data;
   evas_common_tilebuf_clear(re->tb);
   if (re->outbuf_redraws_clear) re->outbuf_redraws_clear(re->ob);
}

static Tilebuf_Rect *
_smart_merge(Tilebuf *tb, Tilebuf_Rect *rects)
{
   Tilebuf_Rect *merged, *mergelist = NULL, *r;
   Box *box;
   Region *region;
   int i, j, k, n, n2;
   Eina_Bool did_merge;

   n = eina_inlist_count(EINA_INLIST_GET(rects));
   box = malloc(n * sizeof(Box));
   i = 0;
   EINA_INLIST_FOREACH(EINA_INLIST_GET(rects), r)
     {
        box[i].x1 = r->x;
        box[i].y1 = r->y;
        box[i].x2 = r->x + r->w;
        box[i].y2 = r->y + r->h;
        i++;
     }
   evas_common_tilebuf_free_render_rects(rects);

   n2 = n;
   for (;;)
     {
        Box *box2, bbox;
        int mergenum, area, minarea, area1, area2, perc;

        did_merge = EINA_FALSE;
        box2 = calloc(1, n2 * sizeof(Box));
        j = 0;
        for (i = 0; i < n2; i++)
          {
             if (box[i].x1 == box[i].x2) continue;

             mergenum = -1;
             minarea = 0x7fffffff;

             box2[j] = box[i];
             area1 = (box[i].x2 - box[i].x1) * (box[i].y2 - box[i].y1);
             box[i].x1 = 0;
             box[i].x2 = 0;
             for (k = i + 1; k < n; k++)
               {
                  if (box[k].x1 == box[k].x2) continue;
                  bbox = box2[j];
                  if (box[k].x1 < bbox.x1) bbox.x1 = box[k].x1;
                  if (box[k].y1 < bbox.y1) bbox.y1 = box[k].y1;
                  if (box[k].x2 > bbox.x2) bbox.x2 = box[k].x2;
                  if (box[k].y2 > bbox.y2) bbox.y2 = box[k].y2;
                  area = (bbox.x2 - bbox.x1) * (bbox.y2 - bbox.y1);
                  if (area < minarea)
                    {
                       mergenum = k;
                       minarea = area;
                    }
               }
             if (mergenum >= 0)
               {
                  k = mergenum;
                  area2 = (box[k].x2 - box[k].x1) * (box[k].y2 - box[k].y1);
                  perc = (minarea * 100) / (area1 + area2);
                  // if combined size of bounding box of rects is <= X% of
                  // the sum of the 2 src areas - then merge
                  if (perc <= 150)
                    {
                       bbox = box2[j];
                       if (box[k].x1 < bbox.x1) bbox.x1 = box[k].x1;
                       if (box[k].y1 < bbox.y1) bbox.y1 = box[k].y1;
                       if (box[k].x2 > bbox.x2) bbox.x2 = box[k].x2;
                       if (box[k].y2 > bbox.y2) bbox.y2 = box[k].y2;
                       box2[j] = bbox;
                       box[k].x1 = 0;
                       box[k].x2 = 0;
                       did_merge = EINA_TRUE;
                    }
               }
             j++;
          }
        free(box);
        box = box2;
        if (!did_merge) break;
     }
   region = region_new(tb->outbuf_w, tb->outbuf_h);
   for (i = 0; i < n2; i++)
     {
        if (box[i].x1 == box[i].x2) continue;
        region_rect_add(region,
                        box[i].x1, box[i].y1,
                        box[i].x2 - box[i].x1,
                        box[i].y2 - box[i].y1);
     }
   free(box);
   box = region_rects(region);
   n = region_rects_num(region);
   merged = calloc(1, n * sizeof(Tilebuf_Rect));
   if (merged) {
        j = 0;
#if 1
        // regions sometimes produce box sets like:
        // +---+
        // |   |
        // +---+    +-------+
        // |   |    |       |
        // +---+    +-------+---------+
        //          |                 |
        //          |                 |
        //          +-----------------+
        // so the upper-left 2 boxes can be merged into 1 and they have the same
        // x coords and are flush-aligned one above the other. that is what
        // this does - find these and merge them to have fewer rects
        for (i = 0; i < n; i++)
          {
             // skip empty boxes
             if (box[i].x1 == box[i].x2) continue;
             // walk all following boxes after this and see if they can be merged
             // into box i
             for (k = i + 1; k < n; k++)
               {
                  // skip empty boxes after i
                  if (box[k].x1 == box[k].x2) continue;
                  // match x coords
                  if ((box[i].x1 == box[k].x1) && // if aligned vertically
                      (box[i].x2 == box[k].x2))   // exactly above/below
                    {
                       // right below, or right above
                       if (box[i].y2 == box[k].y1) // this box flush below
                         {
                            box[i].y2 = box[k].y2; // merge below i
                            box[k].x2 = box[k].x1; // empty this box - merged
                         }
                       else if (box[i].y1 == box[k].y2) // this box flush above
                         {
                            box[i].y2 = box[k].y2; // merge above i
                            box[k].x2 = box[k].x1; // empty this box - merged
                         }
                    }
               }
             // i may have expanded but will not be empty. future boxes after
             // this may be empty though but handled at top of loop
             merged[j].x = box[i].x1;
             merged[j].y = box[i].y1;
             merged[j].w = box[i].x2 - box[i].x1;
             merged[j].h = box[i].y2 - box[i].y1;
             mergelist = (Tilebuf_Rect *)eina_inlist_append
                (EINA_INLIST_GET(mergelist), EINA_INLIST_GET(&(merged[j])));
             j++;
          }
#endif
   }
   region_free(region);
   rects = mergelist;

   return rects;
}

static Tilebuf_Rect *
_merge_rects(Render_Output_Merge_Mode merge_mode,
             Tilebuf *tb,
             Tilebuf_Rect *r1,
             Tilebuf_Rect *r2,
             Tilebuf_Rect *r3,
             Tilebuf_Rect *r4)
{
   Tilebuf_Rect *r, *rects;

   if (r1)
     {
        EINA_INLIST_FOREACH(EINA_INLIST_GET(r1), r)
          {
             evas_common_tilebuf_add_redraw(tb, r->x, r->y, r->w, r->h);
          }
     }
   if (r2)
     {
        EINA_INLIST_FOREACH(EINA_INLIST_GET(r2), r)
          {
             evas_common_tilebuf_add_redraw(tb, r->x, r->y, r->w, r->h);
          }
     }
   if (r3)
     {
        EINA_INLIST_FOREACH(EINA_INLIST_GET(r3), r)
          {
             evas_common_tilebuf_add_redraw(tb, r->x, r->y, r->w, r->h);
          }
     }

   if (r4)
     {
        EINA_INLIST_FOREACH(EINA_INLIST_GET(r4), r)
          {
             evas_common_tilebuf_add_redraw(tb, r->x, r->y, r->w, r->h);
          }
     }

   rects = evas_common_tilebuf_get_render_rects(tb);
   // bounding box -> make a bounding box single region update of all regions.
   // yes we could try and be smart and figure out size of regions, how far
   // apart etc. etc. to try and figure out an optimal "set". this is a tradeoff
   // between multiple update regions to render and total pixels to render.
   if (rects)
     {
        if (merge_mode == MERGE_BOUNDING
// disable smart updates for debugging
//            || (merge_mode == MERGE_SMART)
            )
          {
             int px1, py1, px2, py2;

             px1 = rects->x; py1 = rects->y;
             px2 = rects->x + rects->w; py2 = rects->y + rects->h;
             EINA_INLIST_FOREACH(EINA_INLIST_GET(rects), r)
               {
                  if (r->x < px1) px1 = r->x;
                  if (r->y < py1) py1 = r->y;
                  if ((r->x + r->w) > px2) px2 = r->x + r->w;
                  if ((r->y + r->h) > py2) py2 = r->y + r->h;
               }
             evas_common_tilebuf_free_render_rects(rects);
             rects = calloc(1, sizeof(Tilebuf_Rect));
             if (rects)
               {
                  rects->x = px1;
                  rects->y = py1;
                  rects->w = px2 - px1;
                  rects->h = py2 - py1;
               }
          }
        else if (merge_mode == MERGE_SMART)
          {
             rects = _smart_merge(tb, rects);
          }
     }
   evas_common_tilebuf_clear(tb);
   return rects;
}


static void *
eng_output_redraws_next_update_get(void *engine EINA_UNUSED, void *data, int *x, int *y, int *w, int *h, int *cx, int *cy, int *cw, int *ch)
{
   Render_Output_Software_Generic *re;
   void *surface;
   Tilebuf_Rect *rect;

#define CLEAR_PREV_RECTS(x) \
   do { \
      if (re->rects_prev[x]) \
        evas_common_tilebuf_free_render_rects(re->rects_prev[x]); \
      re->rects_prev[x] = NULL; \
   } while (0)

   re = (Render_Output_Software_Generic *)data;
   if (re->end)
     {
        re->end = 0;
        return NULL;
     }

   if (!re->rects)
     {
        int mode = MODE_COPY;

        re->rects = evas_common_tilebuf_get_render_rects(re->tb);
        if (re->rects)
          {
             // do anything needed for the first rect and update lost backbuffer if needed
             if (re->outbuf_region_first_rect)
               re->lost_back |= re->outbuf_region_first_rect(re->ob);

             if (re->outbuf_swap_mode_get) mode = re->outbuf_swap_mode_get(re->ob);
             re->swap_mode = mode;
             if ((re->lost_back) || (re->swap_mode == MODE_FULL) || (re->swap_mode == MODE_AUTO))
               {
                  /* if we lost our backbuffer since the last frame redraw all */
                  re->lost_back = 0;
                  evas_common_tilebuf_add_redraw(re->tb, 0, 0, re->w, re->h);
                  evas_common_tilebuf_free_render_rects(re->rects);
                  re->rects = evas_common_tilebuf_get_render_rects(re->tb);
               }
             /* ensure we get rid of previous rect lists we dont need if mode
              * changed/is appropriate */
             evas_common_tilebuf_clear(re->tb);
             CLEAR_PREV_RECTS(3);
             re->rects_prev[3] = re->rects_prev[2];
             re->rects_prev[2] = re->rects_prev[1];
             re->rects_prev[1] = re->rects_prev[0];
             re->rects_prev[0] = re->rects;
             re->rects = NULL;
             switch (re->swap_mode)
               {
                case MODE_AUTO:
                case MODE_FULL:
                case MODE_COPY: // no prev rects needed
                  re->rects = _merge_rects(re->merge_mode, re->tb, re->rects_prev[0], NULL, NULL, NULL);
                  break;
                case MODE_DOUBLE: // double mode - only 1 level of prev rect
                  re->rects = _merge_rects(re->merge_mode, re->tb, re->rects_prev[0], re->rects_prev[1], NULL, NULL);
                  break;
                case MODE_TRIPLE: // triple mode - 2 levels of prev rect
                  re->rects = _merge_rects(re->merge_mode, re->tb, re->rects_prev[0], re->rects_prev[1], re->rects_prev[2], NULL);
                  break;
                case MODE_QUADRUPLE: // keep all
                  re->rects = _merge_rects(re->merge_mode, re->tb, re->rects_prev[0], re->rects_prev[1], re->rects_prev[2], re->rects_prev[3]);
                  break;
                default:
                  break;
               }
          }
        evas_common_tilebuf_clear(re->tb);
        re->cur_rect = EINA_INLIST_GET(re->rects);
        if (re->cur_rect && re->outbuf_damage_region_set)
          re->outbuf_damage_region_set(re->ob, re->rects);
     }
   if (!re->cur_rect) return NULL;
   rect = (Tilebuf_Rect *)re->cur_rect;
   if (re->rects)
     {
        switch (re->swap_mode)
          {
           case MODE_COPY:
           case MODE_DOUBLE:
           case MODE_TRIPLE:
           case MODE_QUADRUPLE:
             rect = (Tilebuf_Rect *)re->cur_rect;
             *x = rect->x;
             *y = rect->y;
             *w = rect->w;
             *h = rect->h;
             *cx = rect->x;
             *cy = rect->y;
             *cw = rect->w;
             *ch = rect->h;
             re->cur_rect = re->cur_rect->next;
             break;
           case MODE_AUTO:
           case MODE_FULL:
             re->cur_rect = NULL;
             *x = 0;
             *y = 0;
             *w = re->w;
             *h = re->h;
             if (cx) *cx = 0;
             if (cy) *cy = 0;
             if (cw) *cw = re->w;
             if (ch) *ch = re->h;
             break;
           default:
             break;
          }
        surface = re->outbuf_new_region_for_update(re->ob,
                                                   *x, *y, *w, *h,
                                                   cx, cy, cw, ch);
        if ((re->swap_mode == MODE_AUTO) ||
            (re->swap_mode == MODE_FULL) ||
            (!surface))
          {
             evas_common_tilebuf_free_render_rects(re->rects);
             re->rects = NULL;
             re->end = 1;
          }
        return surface;
     }
   return NULL;
}

static void
eng_output_redraws_next_update_push(void *engine EINA_UNUSED, void *data, void *surface, int x, int y, int w, int h, Evas_Render_Mode render_mode)
{
   Render_Output_Software_Generic *re;

   if (render_mode == EVAS_RENDER_MODE_ASYNC_INIT) return;

   re = (Render_Output_Software_Generic *)data;
#if defined(BUILD_PIPE_RENDER)
   evas_common_pipe_map_begin(surface);
#endif /* BUILD_PIPE_RENDER */
   re->outbuf_push_updated_region(re->ob, surface, x, y, w, h);
   if (re->outbuf_free_region_for_update)
     re->outbuf_free_region_for_update(re->ob, surface);
   evas_common_cpu_end_opt();
}

static void
eng_output_flush(void *engine EINA_UNUSED, void *data, Evas_Render_Mode render_mode)
{
   Render_Output_Software_Generic *re;

   if (render_mode == EVAS_RENDER_MODE_ASYNC_INIT) return;

   re = (Render_Output_Software_Generic *)data;
   if (re->outbuf_flush) re->outbuf_flush(re->ob, re->rects_prev[0], re->rects, render_mode);
   if (re->rects && render_mode != EVAS_RENDER_MODE_ASYNC_INIT)
     {
        evas_common_tilebuf_free_render_rects(re->rects);
        re->rects = NULL;
     }
}

static void
eng_output_idle_flush(void *engine EINA_UNUSED, void *data)
{
   Render_Output_Software_Generic *re;

   re = (Render_Output_Software_Generic *)data;
   if (re->outbuf_idle_flush) re->outbuf_idle_flush(re->ob);
}

// Ector functions

static Ector_Surface *
eng_ector_create(void *engine EINA_UNUSED)
{
   Ector_Surface *ector;

   efl_domain_current_push(EFL_ID_DOMAIN_SHARED);
   ector = efl_add_ref(ECTOR_SOFTWARE_SURFACE_CLASS, NULL);
   efl_domain_current_pop();
   return ector;
}

static void*
eng_ector_surface_create(void *engine, int width, int height, int *error)
{
   void *surface;

   *error = EINA_FALSE;

   surface = eng_image_new_from_copied_data(engine, width, height, NULL, EINA_TRUE, EVAS_COLORSPACE_ARGB8888);
   if (!surface) *error = EINA_TRUE;

   return surface;
}

static void
eng_ector_surface_destroy(void *engine, void *surface)
{
   if (!surface) return;
   eng_image_free(engine, surface);
}

static void
eng_ector_surface_cache_set(void *engine, void *key , void *surface)
{
   Render_Engine_Software_Generic *e = engine;

   generic_cache_data_set(e->surface_cache, key, surface);

}

static void *
eng_ector_surface_cache_get(void *engine, void *key)
{
   Render_Engine_Software_Generic *e = engine;

   return generic_cache_data_get(e->surface_cache, key);
}

static void
eng_ector_surface_cache_drop(void *engine, void *key)
{
   Render_Engine_Software_Generic *e = engine;

   generic_cache_data_drop(e->surface_cache, key);
}

static void
eng_ector_destroy(void *data EINA_UNUSED, Ector_Surface *ector)
{
   if (ector) efl_unref(ector);
}

static Ector_Buffer *
eng_ector_buffer_wrap(void *data, Evas *e EINA_UNUSED, void *engine_image)
{
   Image_Entry *ie = engine_image;
   Ector_Buffer *buf = NULL;
   RGBA_Image *im = (RGBA_Image *)ie;

   if (!ie) return NULL;
   if (!im->image.data) return NULL;

   if (!efl_domain_current_push(EFL_ID_DOMAIN_SHARED))
     return NULL;
   buf = efl_add_ref(EVAS_ECTOR_SOFTWARE_BUFFER_CLASS, NULL,
                 evas_ector_buffer_engine_image_set(efl_added, data, ie));
   efl_domain_current_pop();

   return buf;
}

static Ector_Buffer *
eng_ector_buffer_new(void *data, Evas *evas, int width, int height,
                     Efl_Gfx_Colorspace cspace, Ector_Buffer_Flag flags EINA_UNUSED)
{
   Ector_Buffer *buf;
   Image_Entry *ie;
   void *pixels;
   int pxs;

   if (cspace == EFL_GFX_COLORSPACE_ARGB8888)
     pxs = 4;
   else if (cspace == EFL_GFX_COLORSPACE_GRY8)
     pxs = 1;
   else
     {
        ERR("Unsupported colorspace: %d", (int) cspace);
        return NULL;
     }

   // alloc buffer
   ie = evas_cache_image_copied_data(evas_common_image_cache_get(),
                                     width, height, NULL, EINA_TRUE,
                                     (Evas_Colorspace)cspace);
   if (!ie) return NULL;
   pixels = ((RGBA_Image *) ie)->image.data;
   memset(pixels, 0, width * height * pxs);

   buf = eng_ector_buffer_wrap(data, evas, ie);
   evas_cache_image_drop(ie);

   return buf;
}

static void
_draw_thread_ector_cleanup(Evas_Thread_Command_Ector *ector)
{
   Eina_Rectangle *r;

   while ((r = eina_array_pop(ector->clips)))
     eina_rectangle_free(r);
   eina_array_free(ector->clips);

   if (ector->free_it)
     eina_mempool_free(_mp_command_ector, ector);
}

static void
_draw_thread_ector_draw(void *data)
{
   Evas_Thread_Command_Ector *ector = data;

   ector_renderer_draw(ector->r, ector->render_op, ector->clips, ector->mul_col);

   _draw_thread_ector_cleanup(ector);
}

static void
eng_ector_renderer_draw(void *engine EINA_UNUSED, void *surface,
                        void *context, Ector_Renderer *renderer,
                        Eina_Array *clips, Eina_Bool do_async)
{
   RGBA_Image *dst = surface;
   RGBA_Draw_Context *dc = context;
   Evas_Thread_Command_Ector ector;
   Eina_Array *c;
   Eina_Rectangle *r;
   Eina_Rectangle clip;
   Eina_Array_Iterator it;
   unsigned int i;

   if (dc->clip.use)
     {
        clip.x = dc->clip.x;
        clip.y = dc->clip.y;
        clip.w = dc->clip.w;
        clip.h = dc->clip.h;
        // clip the clip rect to surface boundary.
        RECTS_CLIP_TO_RECT(clip.x, clip.y, clip.w, clip.h, 0, 0, dst->cache_entry.w, dst->cache_entry.h);
        if ((clip.w < 1) || (clip.h < 1)) return;
     }
   else
     {
        clip.x = 0;
        clip.y = 0;
        clip.w = dst->cache_entry.w;
        clip.h = dst->cache_entry.h;
     }

   c = eina_array_new(8);
   if (clips)
     {
        EINA_ARRAY_ITER_NEXT(clips, i, r, it)
          {
             Eina_Rectangle *rc;

             rc = eina_rectangle_new(r->x, r->y, r->w, r->h);
             if (!rc) continue;

             if (eina_rectangle_intersection(rc, &clip))
               eina_array_push(c, rc);
             else
               eina_rectangle_free(rc);
          }

        if (eina_array_count(c) == 0 &&
            eina_array_count(clips) > 0)
          {
             eina_array_free(c);
             return;
          }
     }

   if (eina_array_count(c) == 0)
     eina_array_push(c, eina_rectangle_new(clip.x, clip.y, clip.w, clip.h));

   ector.r = renderer; // This has already been refcounted by Evas_Object_VG
   ector.clips = c;
   ector.render_op = EFL_GFX_RENDER_OP_BLEND;
   ector.mul_col = 0xffffffff;
   ector.free_it = EINA_FALSE;

   if (do_async)
     {
        Evas_Thread_Command_Ector *ne;

        ne = eina_mempool_malloc(_mp_command_ector, sizeof (Evas_Thread_Command_Ector));
        if (!ne)
          {
             _draw_thread_ector_cleanup(&ector);
             return;
          }

        memcpy(ne, &ector, sizeof (Evas_Thread_Command_Ector));
        ne->free_it = EINA_TRUE;

        QCMD(_draw_thread_ector_draw, ne);
     }
   else
     {
        _draw_thread_ector_draw(&ector);
     }
}

static void
_draw_thread_ector_surface_set(void *data)
{
   Evas_Thread_Command_Ector_Surface *ector_surface = data;
   RGBA_Image *surface = ector_surface->pixels;
   void *pixels = NULL;
   unsigned int w = 0;
   unsigned int h = 0;
   unsigned int x = 0;
   unsigned int y = 0;

   // flush the cpu pipeline before ector drawing.
   evas_common_cpu_end_opt();

   if (surface)
     {
        pixels = evas_cache_image_pixels(&surface->cache_entry);
        if (pixels)
          {
             w = surface->cache_entry.w;
             h = surface->cache_entry.h;
             x = ector_surface->x;
             y = ector_surface->y;
             // clear the surface before giving to ector
             memset(pixels, 0, (w * h * 4));
          }
     }

   ector_buffer_pixels_set(ector_surface->ector, pixels, w, h, 0, EFL_GFX_COLORSPACE_ARGB8888, EINA_TRUE);
   ector_surface_reference_point_set(ector_surface->ector, x, y);

   eina_mempool_free(_mp_command_ector_surface, ector_surface);
}

static Eina_Bool
eng_ector_begin(void *engine EINA_UNUSED, void *surface,
                void *context EINA_UNUSED, Ector_Surface *ector,
                int x, int y, Eina_Bool do_async)
{
   if (do_async)
     {
        Evas_Thread_Command_Ector_Surface *nes;

        nes = eina_mempool_malloc(_mp_command_ector_surface, sizeof (Evas_Thread_Command_Ector_Surface));
        if (!nes) return EINA_FALSE;

        nes->ector = ector;
        nes->pixels = surface;
        nes->x = x;
        nes->y = y;

        QCMD(_draw_thread_ector_surface_set, nes);
     }
   else
     {
        RGBA_Image *sf = surface;
        void *pixels = NULL;
        unsigned int w = 0;
        unsigned int h = 0;

        pixels = evas_cache_image_pixels(&sf->cache_entry);
        if (!pixels) return EINA_FALSE;

        w = sf->cache_entry.w;
        h = sf->cache_entry.h;
        // clear the surface before giving to ector
        memset(pixels, 0, (w * h * 4));

        ector_buffer_pixels_set(ector, pixels, w, h, 0, EFL_GFX_COLORSPACE_ARGB8888, EINA_TRUE);
        ector_surface_reference_point_set(ector, x, y);
     }
   return EINA_TRUE;
}

static void
eng_ector_end(void *engine EINA_UNUSED,
              void *surface EINA_UNUSED,
              void *context EINA_UNUSED,
              Ector_Surface *ector,
              Eina_Bool do_async)
{
   if (do_async)
     {
        Evas_Thread_Command_Ector_Surface *nes;

        nes = eina_mempool_malloc(_mp_command_ector_surface, sizeof (Evas_Thread_Command_Ector_Surface));
        if (!nes) return ;

        nes->ector = ector;
        nes->pixels = NULL;

        QCMD(_draw_thread_ector_surface_set, nes);
     }
   else
     {
        ector_buffer_pixels_set(ector, NULL, 0, 0, 0, EFL_GFX_COLORSPACE_ARGB8888, EINA_TRUE);
        evas_common_cpu_end_opt();
     }
}

//------------------------------------------------//

static Software_Filter_Func
_gfx_filter_func_get(Evas_Filter_Command *cmd)
{
   Software_Filter_Func func = NULL;

   switch (cmd->mode)
     {
      case EVAS_FILTER_MODE_BLEND: func = eng_filter_blend_func_get(cmd); break;
      case EVAS_FILTER_MODE_BLUR: func = eng_filter_blur_func_get(cmd); break;
      case EVAS_FILTER_MODE_BUMP: func = eng_filter_bump_func_get(cmd); break;
      case EVAS_FILTER_MODE_CURVE: func = eng_filter_curve_func_get(cmd); break;
      case EVAS_FILTER_MODE_DISPLACE: func = eng_filter_displace_func_get(cmd); break;
      case EVAS_FILTER_MODE_FILL: func = eng_filter_fill_func_get(cmd); break;
      case EVAS_FILTER_MODE_MASK: func = eng_filter_mask_func_get(cmd); break;
      case EVAS_FILTER_MODE_TRANSFORM: func = eng_filter_transform_func_get(cmd); break;
      case EVAS_FILTER_MODE_GRAYSCALE: func = eng_filter_grayscale_func_get(cmd); break;
      case EVAS_FILTER_MODE_INVERSE_COLOR: func = eng_filter_inverse_color_func_get(cmd); break;
      default: return NULL;
     }

   return func;
}

static Evas_Filter_Support
eng_gfx_filter_supports(void *data EINA_UNUSED, Evas_Filter_Command *cmd)
{
   if (!_gfx_filter_func_get(cmd))
     return EVAS_FILTER_SUPPORT_NONE;

   return EVAS_FILTER_SUPPORT_CPU;
}

static void
eng_font_glyphs_gc_collect(void *data EINA_UNUSED, float ratio EINA_UNUSED, int *texture_size EINA_UNUSED, int *atlas_size EINA_UNUSED, Eina_Bool only_when_requested EINA_UNUSED)
{
   return;
}

static Eina_Bool
eng_gfx_filter_process(void *data EINA_UNUSED, Evas_Filter_Command *cmd)
{
   Software_Filter_Func func;

   func = _gfx_filter_func_get(cmd);
   EINA_SAFETY_ON_NULL_RETURN_VAL(func, EINA_FALSE);

   return func(cmd);
}

//------------------------------------------------//

/*
 *****
 **
 ** ENGINE API
 **
 *****
 */

static Evas_Func func =
{
     eng_engine_new,
     eng_engine_free,
     NULL, // eng_info_setup
     NULL, // eng_setup
     NULL, // eng_update
     NULL, // eng_output_free
     eng_output_resize,
     eng_output_redraws_rect_add,
     eng_output_redraws_rect_del,
     eng_output_redraws_clear,
     eng_output_redraws_next_update_get,
     eng_output_redraws_next_update_push,
     eng_output_flush,
     eng_output_idle_flush,
     eng_output_dump,
     /* draw context virtual methods */
     eng_context_new,
     eng_context_dup,
     eng_canvas_alpha_get,
     eng_context_free,
     eng_context_clip_set,
     eng_context_clip_image_set,
     eng_context_clip_image_unset,
     eng_context_clip_image_get,
     eng_context_clip_clip,
     eng_context_clip_unset,
     eng_context_clip_get,
     eng_context_color_set,
     eng_context_color_get,
     eng_context_multiplier_set,
     eng_context_multiplier_unset,
     eng_context_multiplier_get,
     eng_context_cutout_add,
     eng_context_cutout_clear,
     eng_context_cutout_target,
     eng_context_anti_alias_set,
     eng_context_anti_alias_get,
     eng_context_color_interpolation_set,
     eng_context_color_interpolation_get,
     eng_context_render_op_set,
     eng_context_render_op_get,
     /* rect draw funcs */
     eng_rectangle_draw,
     /* line draw funcs */
     eng_line_draw,
     /* polygon draw funcs */
     eng_polygon_point_add,
     eng_polygon_points_clear,
     eng_polygon_draw,
     /* image draw funcs */
     eng_image_load,
     eng_image_mmap,
     eng_image_new_from_data,
     eng_image_new_from_copied_data,
     eng_image_free,
     eng_image_ref,
     eng_image_size_get,
     eng_image_size_set,
     NULL, // eng_image_stride_get
     eng_image_dirty_region,
     eng_image_data_get,
     eng_image_data_put,
     eng_image_data_direct_get,
     eng_image_data_preload_request,
     eng_image_data_preload_cancel,
     eng_image_alpha_set,
     eng_image_alpha_get,
     eng_image_orient_set,
     eng_image_orient_get,
     eng_image_draw,
     eng_image_colorspace_set,
     eng_image_colorspace_get,
     eng_image_file_colorspace_get,
     eng_image_can_region_get,
     eng_image_data_map,
     eng_image_data_unmap,
     eng_image_data_maps_get,
     eng_image_content_region_get,
     eng_image_stretch_region_get,
     eng_image_data_slice_add,
     eng_image_prepare,
     eng_image_surface_noscale_new,
     eng_image_native_init,
     eng_image_native_shutdown,
     eng_image_native_set,
     eng_image_native_get,
     /* image cache funcs */
     eng_image_cache_flush,
     eng_image_cache_set,
     eng_image_cache_get,
     NULL, // image_plane_assign
     NULL, // image_plane_release
     /* font draw functions */
     eng_font_load,
     eng_font_memory_load,
     eng_font_add,
     eng_font_memory_add,
     eng_font_free,
     eng_font_ascent_get,
     eng_font_descent_get,
     eng_font_max_ascent_get,
     eng_font_max_descent_get,
     eng_font_string_size_get,
     eng_font_inset_get,
     eng_font_h_advance_get,
     eng_font_v_advance_get,
     eng_font_char_coords_get,
     eng_font_char_at_coords_get,
     eng_font_draw,
     /* font cache functions */
     eng_font_cache_flush,
     eng_font_cache_set,
     eng_font_cache_get,
     /* font hinting functions */
     eng_font_hinting_set,
     eng_font_hinting_can_hint,
     eng_image_scale_hint_set,
     eng_image_scale_hint_get,
     /* more font draw functions */
     eng_font_last_up_to_pos,
     eng_image_map_draw,
     eng_image_map_surface_new,
     eng_image_map_clean,
     NULL, // eng_image_scaled_get - used for live scaling in GL only (fastpath)
     NULL, // eng_image_content_hint_set - software doesn't use it
     eng_font_pen_coords_get,
     eng_font_text_props_info_create,
     eng_font_right_inset_get,
     eng_gl_supports_evas_gl, // returns true iif OSMesa is present
     NULL, // No need to set output for software engine
     eng_gl_surface_create, // need software mesa for gl rendering <- gl_surface_create
     NULL, // need software mesa for gl rendering <- gl_pbuffer_surface_create
     eng_gl_surface_destroy, // need software mesa for gl rendering <- gl_surface_destroy
     eng_gl_context_create, // need software mesa for gl rendering <- gl_context_create
     eng_gl_context_destroy, // need software mesa for gl rendering <- gl_context_destroy
     eng_gl_make_current, // need software mesa for gl rendering <- gl_make_current
     eng_gl_string_query, // need software mesa for gl rendering <- gl_string_query
     eng_gl_proc_address_get, // need software mesa for gl rendering <- gl_proc_address_get
     eng_gl_native_surface_get, // need software mesa for gl rendering <- gl_native_surface_get
       eng_gl_api_get, // need software mesa for gl rendering <- gl_api_get
     NULL, // need software mesa for gl rendering <- gl_direct_override
     NULL, // need software mesa for gl rendering <- gl_get_pixels_set
     NULL, // need software mesa for gl rendering <- gl_surface_lock
     NULL, // need software mesa for gl rendering <- gl_surface_read_pixels
     NULL, // need software mesa for gl rendering <- gl_surface_unlock
     eng_gl_error_get, // need software mesa for gl rendering <- gl_error_get
     eng_gl_current_context_get, // need software mesa for gl rendering <- gl_current_context_get
     eng_gl_current_surface_get, // need software mesa for gl rendering <- gl_current_surface_get
     eng_gl_rotation_angle_get, // need software mesa for gl rendering <- gl_rotation_angle_get
     NULL, // need software mesa for gl rendering <- gl_surface_query
     NULL, // need software mesa for gl rendering <- gl_surface_direct_renderable_get
     NULL, // need software mesa for gl rendering <- gl_image_direct_set
     NULL, // need software mesa for gl rendering <- gl_image_direct_get
     NULL, // need software mesa for gl rendering <- gl_get_pixels_pre
     NULL, // need software mesa for gl rendering <- gl_get_pixels_post
     eng_image_load_error_get,
     eng_font_run_font_end_get,
     eng_image_animated_get,
     eng_image_animated_frame_count_get,
     eng_image_animated_loop_type_get,
     eng_image_animated_loop_count_get,
     eng_image_animated_frame_duration_get,
     eng_image_animated_frame_set,
     eng_image_animated_frame_get,
     NULL, // image_max_size_get
     eng_multi_font_draw,
     eng_pixel_alpha_get,
     NULL, // eng_context_flush - software doesn't use it
     eng_ector_create,
     eng_ector_destroy,
     eng_ector_buffer_wrap,
     eng_ector_buffer_new,
     eng_ector_begin,
     eng_ector_renderer_draw,
     eng_ector_end,
     eng_ector_surface_create,
     eng_ector_surface_destroy,
     eng_ector_surface_cache_set,
     eng_ector_surface_cache_get,
     eng_ector_surface_cache_drop,
     eng_gfx_filter_supports,
     eng_gfx_filter_process,
   /* FUTURE software generic calls go here */
     eng_font_glyphs_gc_collect,
     0 // sizeof (Info)
};


//----------------------------------------------------------------//
//                                                                //
//                      Load Symbols                              //
//                                                                //
//----------------------------------------------------------------//
#ifdef EVAS_GL
static void
sym_missing(void)
{
   ERR("GL symbols missing!");
}

static int
glue_sym_init(void)
{
   //------------------------------------------------//
   // Use eglGetProcAddress
#define FINDSYM(dst, sym, typ) \
   if (!dst) dst = dlsym(gl_lib_handle, sym); \
   if (!dst)  \
     { \
        ERR("Symbol not found: %s", sym); \
        return 0; \
     }
#define FALLBAK(dst, typ) if (!dst) dst = (typeof(dst))sym_missing;

    //------------------------------------------------------//
   // OSMesa APIs...
   FINDSYM(_sym_OSMesaCreateContextExt, "OSMesaCreateContextExt", glsym_func_osm_ctx);
   FALLBAK(_sym_OSMesaCreateContextExt, glsym_func_void);

   FINDSYM(_sym_OSMesaDestroyContext, "OSMesaDestroyContext", glsym_func_void);
   FALLBAK(_sym_OSMesaDestroyContext, glsym_func_void);

   FINDSYM(_sym_OSMesaMakeCurrent, "OSMesaMakeCurrent", glsym_func_bool);
   FALLBAK(_sym_OSMesaMakeCurrent, glsym_func_void);

   FINDSYM(_sym_OSMesaPixelStore, "OSMesaPixelStore", glsym_func_void);
   FALLBAK(_sym_OSMesaPixelStore, glsym_func_void);

   FINDSYM(_sym_OSMesaGetProcAddress, "OSMesaGetProcAddress", glsym_func_eng_fn);
   FALLBAK(_sym_OSMesaGetProcAddress, glsym_func_void);

#undef FINDSYM
#undef FALLBAK

   return 1;
}

static Eina_Bool
gl_sym_init(void)
{
   Eina_Bool ok = EINA_TRUE;

   //------------------------------------------------//
#define FINDSYM(dst, sym, typ) do { \
   if (!dst) dst = dlsym(gl_lib_handle, sym); \
   if (!dst && _sym_OSMesaGetProcAddress) dst = (void *)_sym_OSMesaGetProcAddress(sym); \
   if (!dst) DBG("Symbol not found: %s", sym); \
   } while (0)
#define FALLBAK(dst, typ) do { \
   if (!dst) { dst = (void *)sym_missing; ok = EINA_FALSE; } \
   } while (0)

   //------------------------------------------------------//
   // GLES 2.0 APIs...
   FINDSYM(_sym_glActiveTexture, "glActiveTexture", glsym_func_void);
   FALLBAK(_sym_glActiveTexture, glsym_func_void);

   FINDSYM(_sym_glAttachShader, "glAttachShader", glsym_func_void);
   FALLBAK(_sym_glAttachShader, glsym_func_void);

   FINDSYM(_sym_glBindAttribLocation, "glBindAttribLocation", glsym_func_void);
   FALLBAK(_sym_glBindAttribLocation, glsym_func_void);

   FINDSYM(_sym_glBindBuffer, "glBindBuffer", glsym_func_void);
   FALLBAK(_sym_glBindBuffer, glsym_func_void);

   FINDSYM(_sym_glBindFramebuffer, "glBindFramebuffer", glsym_func_void);
   FALLBAK(_sym_glBindFramebuffer, glsym_func_void);

   FINDSYM(_sym_glBindRenderbuffer, "glBindRenderbuffer", glsym_func_void);
   FALLBAK(_sym_glBindRenderbuffer, glsym_func_void);

   FINDSYM(_sym_glBindTexture, "glBindTexture", glsym_func_void);
   FALLBAK(_sym_glBindTexture, glsym_func_void);

   FINDSYM(_sym_glBlendColor, "glBlendColor", glsym_func_void);
   FALLBAK(_sym_glBlendColor, glsym_func_void);

   FINDSYM(_sym_glBlendEquation, "glBlendEquation", glsym_func_void);
   FALLBAK(_sym_glBlendEquation, glsym_func_void);

   FINDSYM(_sym_glBlendEquationSeparate, "glBlendEquationSeparate", glsym_func_void);
   FALLBAK(_sym_glBlendEquationSeparate, glsym_func_void);

   FINDSYM(_sym_glBlendFunc, "glBlendFunc", glsym_func_void);
   FALLBAK(_sym_glBlendFunc, glsym_func_void);

   FINDSYM(_sym_glBlendFuncSeparate, "glBlendFuncSeparate", glsym_func_void);
   FALLBAK(_sym_glBlendFuncSeparate, glsym_func_void);

   FINDSYM(_sym_glBufferData, "glBufferData", glsym_func_void);
   FALLBAK(_sym_glBufferData, glsym_func_void);

   FINDSYM(_sym_glBufferSubData, "glBufferSubData", glsym_func_void);
   FALLBAK(_sym_glBufferSubData, glsym_func_void);

   FINDSYM(_sym_glCheckFramebufferStatus, "glCheckFramebufferStatus", glsym_func_uint);
   FALLBAK(_sym_glCheckFramebufferStatus, glsym_func_uint);

   FINDSYM(_sym_glClear, "glClear", glsym_func_void);
   FALLBAK(_sym_glClear, glsym_func_void);

   FINDSYM(_sym_glClearColor, "glClearColor", glsym_func_void);
   FALLBAK(_sym_glClearColor, glsym_func_void);

   FINDSYM(_sym_glClearDepthf, "glClearDepthf", glsym_func_void);
   FINDSYM(_sym_glClearDepthf, "glClearDepth", glsym_func_void);
   FALLBAK(_sym_glClearDepthf, glsym_func_void);

   FINDSYM(_sym_glClearStencil, "glClearStencil", glsym_func_void);
   FALLBAK(_sym_glClearStencil, glsym_func_void);

   FINDSYM(_sym_glColorMask, "glColorMask", glsym_func_void);
   FALLBAK(_sym_glColorMask, glsym_func_void);

   FINDSYM(_sym_glCompileShader, "glCompileShader", glsym_func_void);
   FALLBAK(_sym_glCompileShader, glsym_func_void);

   FINDSYM(_sym_glCompressedTexImage2D, "glCompressedTexImage2D", glsym_func_void);
   FALLBAK(_sym_glCompressedTexImage2D, glsym_func_void);

   FINDSYM(_sym_glCompressedTexSubImage2D, "glCompressedTexSubImage2D", glsym_func_void);
   FALLBAK(_sym_glCompressedTexSubImage2D, glsym_func_void);

   FINDSYM(_sym_glCopyTexImage2D, "glCopyTexImage2D", glsym_func_void);
   FALLBAK(_sym_glCopyTexImage2D, glsym_func_void);

   FINDSYM(_sym_glCopyTexSubImage2D, "glCopyTexSubImage2D", glsym_func_void);
   FALLBAK(_sym_glCopyTexSubImage2D, glsym_func_void);

   FINDSYM(_sym_glCreateProgram, "glCreateProgram", glsym_func_uint);
   FALLBAK(_sym_glCreateProgram, glsym_func_uint);

   FINDSYM(_sym_glCreateShader, "glCreateShader", glsym_func_uint);
   FALLBAK(_sym_glCreateShader, glsym_func_uint);

   FINDSYM(_sym_glCullFace, "glCullFace", glsym_func_void);
   FALLBAK(_sym_glCullFace, glsym_func_void);

   FINDSYM(_sym_glDeleteBuffers, "glDeleteBuffers", glsym_func_void);
   FALLBAK(_sym_glDeleteBuffers, glsym_func_void);

   FINDSYM(_sym_glDeleteFramebuffers, "glDeleteFramebuffers", glsym_func_void);
   FALLBAK(_sym_glDeleteFramebuffers, glsym_func_void);

   FINDSYM(_sym_glDeleteProgram, "glDeleteProgram", glsym_func_void);
   FALLBAK(_sym_glDeleteProgram, glsym_func_void);

   FINDSYM(_sym_glDeleteRenderbuffers, "glDeleteRenderbuffers", glsym_func_void);
   FALLBAK(_sym_glDeleteRenderbuffers, glsym_func_void);

   FINDSYM(_sym_glDeleteShader, "glDeleteShader", glsym_func_void);
   FALLBAK(_sym_glDeleteShader, glsym_func_void);

   FINDSYM(_sym_glDeleteTextures, "glDeleteTextures", glsym_func_void);
   FALLBAK(_sym_glDeleteTextures, glsym_func_void);

   FINDSYM(_sym_glDepthFunc, "glDepthFunc", glsym_func_void);
   FALLBAK(_sym_glDepthFunc, glsym_func_void);

   FINDSYM(_sym_glDepthMask, "glDepthMask", glsym_func_void);
   FALLBAK(_sym_glDepthMask, glsym_func_void);

   FINDSYM(_sym_glDepthRangef, "glDepthRangef", glsym_func_void);
   FINDSYM(_sym_glDepthRangef, "glDepthRange", glsym_func_void);
   FALLBAK(_sym_glDepthRangef, glsym_func_void);

   FINDSYM(_sym_glDetachShader, "glDetachShader", glsym_func_void);
   FALLBAK(_sym_glDetachShader, glsym_func_void);

   FINDSYM(_sym_glDisable, "glDisable", glsym_func_void);
   FALLBAK(_sym_glDisable, glsym_func_void);

   FINDSYM(_sym_glDisableVertexAttribArray, "glDisableVertexAttribArray", glsym_func_void);
   FALLBAK(_sym_glDisableVertexAttribArray, glsym_func_void);

   FINDSYM(_sym_glDrawArrays, "glDrawArrays", glsym_func_void);
   FALLBAK(_sym_glDrawArrays, glsym_func_void);

   FINDSYM(_sym_glDrawElements, "glDrawElements", glsym_func_void);
   FALLBAK(_sym_glDrawElements, glsym_func_void);

   FINDSYM(_sym_glEnable, "glEnable", glsym_func_void);
   FALLBAK(_sym_glEnable, glsym_func_void);

   FINDSYM(_sym_glEnableVertexAttribArray, "glEnableVertexAttribArray", glsym_func_void);
   FALLBAK(_sym_glEnableVertexAttribArray, glsym_func_void);

   FINDSYM(_sym_glFinish, "glFinish", glsym_func_void);
   FALLBAK(_sym_glFinish, glsym_func_void);

   FINDSYM(_sym_glFlush, "glFlush", glsym_func_void);
   FALLBAK(_sym_glFlush, glsym_func_void);

   FINDSYM(_sym_glFramebufferRenderbuffer, "glFramebufferRenderbuffer", glsym_func_void);
   FALLBAK(_sym_glFramebufferRenderbuffer, glsym_func_void);

   FINDSYM(_sym_glFramebufferTexture2D, "glFramebufferTexture2D", glsym_func_void);
   FALLBAK(_sym_glFramebufferTexture2D, glsym_func_void);

   FINDSYM(_sym_glFrontFace, "glFrontFace", glsym_func_void);
   FALLBAK(_sym_glFrontFace, glsym_func_void);

   FINDSYM(_sym_glGenBuffers, "glGenBuffers", glsym_func_void);
   FALLBAK(_sym_glGenBuffers, glsym_func_void);

   FINDSYM(_sym_glGenerateMipmap, "glGenerateMipmap", glsym_func_void);
   FALLBAK(_sym_glGenerateMipmap, glsym_func_void);

   FINDSYM(_sym_glGenFramebuffers, "glGenFramebuffers", glsym_func_void);
   FALLBAK(_sym_glGenFramebuffers, glsym_func_void);

   FINDSYM(_sym_glGenRenderbuffers, "glGenRenderbuffers", glsym_func_void);
   FALLBAK(_sym_glGenRenderbuffers, glsym_func_void);

   FINDSYM(_sym_glGenTextures, "glGenTextures", glsym_func_void);
   FALLBAK(_sym_glGenTextures, glsym_func_void);

   FINDSYM(_sym_glGetActiveAttrib, "glGetActiveAttrib", glsym_func_void);
   FALLBAK(_sym_glGetActiveAttrib, glsym_func_void);

   FINDSYM(_sym_glGetActiveUniform, "glGetActiveUniform", glsym_func_void);
   FALLBAK(_sym_glGetActiveUniform, glsym_func_void);

   FINDSYM(_sym_glGetAttachedShaders, "glGetAttachedShaders", glsym_func_void);
   FALLBAK(_sym_glGetAttachedShaders, glsym_func_void);

   FINDSYM(_sym_glGetAttribLocation, "glGetAttribLocation", glsym_func_int);
   FALLBAK(_sym_glGetAttribLocation, glsym_func_int);

   FINDSYM(_sym_glGetBooleanv, "glGetBooleanv", glsym_func_void);
   FALLBAK(_sym_glGetBooleanv, glsym_func_void);

   FINDSYM(_sym_glGetBufferParameteriv, "glGetBufferParameteriv", glsym_func_void);
   FALLBAK(_sym_glGetBufferParameteriv, glsym_func_void);

   FINDSYM(_sym_glGetError, "glGetError", glsym_func_uint);
   FALLBAK(_sym_glGetError, glsym_func_uint);

   FINDSYM(_sym_glGetFloatv, "glGetFloatv", glsym_func_void);
   FALLBAK(_sym_glGetFloatv, glsym_func_void);

   FINDSYM(_sym_glGetFramebufferAttachmentParameteriv, "glGetFramebufferAttachmentParameteriv", glsym_func_void);
   FALLBAK(_sym_glGetFramebufferAttachmentParameteriv, glsym_func_void);

   FINDSYM(_sym_glGetIntegerv, "glGetIntegerv", glsym_func_void);
   FALLBAK(_sym_glGetIntegerv, glsym_func_void);

   FINDSYM(_sym_glGetProgramiv, "glGetProgramiv", glsym_func_void);
   FALLBAK(_sym_glGetProgramiv, glsym_func_void);

   FINDSYM(_sym_glGetProgramInfoLog, "glGetProgramInfoLog", glsym_func_void);
   FALLBAK(_sym_glGetProgramInfoLog, glsym_func_void);

   FINDSYM(_sym_glGetRenderbufferParameteriv, "glGetRenderbufferParameteriv", glsym_func_void);
   FALLBAK(_sym_glGetRenderbufferParameteriv, glsym_func_void);

   FINDSYM(_sym_glGetShaderiv, "glGetShaderiv", glsym_func_void);
   FALLBAK(_sym_glGetShaderiv, glsym_func_void);

   FINDSYM(_sym_glGetShaderInfoLog, "glGetShaderInfoLog", glsym_func_void);
   FALLBAK(_sym_glGetShaderInfoLog, glsym_func_void);

   FINDSYM(_sym_glGetShaderPrecisionFormat, "glGetShaderPrecisionFormat", glsym_func_void);
   FALLBAK(_sym_glGetShaderPrecisionFormat, glsym_func_void);

   FINDSYM(_sym_glGetShaderSource, "glGetShaderSource", glsym_func_void);
   FALLBAK(_sym_glGetShaderSource, glsym_func_void);

   FINDSYM(_sym_glGetString, "glGetString", glsym_func_uchar_ptr);
   FALLBAK(_sym_glGetString, glsym_func_const_uchar_ptr);

   FINDSYM(_sym_glGetTexParameterfv, "glGetTexParameterfv", glsym_func_void);
   FALLBAK(_sym_glGetTexParameterfv, glsym_func_void);

   FINDSYM(_sym_glGetTexParameteriv, "glGetTexParameteriv", glsym_func_void);
   FALLBAK(_sym_glGetTexParameteriv, glsym_func_void);

   FINDSYM(_sym_glGetUniformfv, "glGetUniformfv", glsym_func_void);
   FALLBAK(_sym_glGetUniformfv, glsym_func_void);

   FINDSYM(_sym_glGetUniformiv, "glGetUniformiv", glsym_func_void);
   FALLBAK(_sym_glGetUniformiv, glsym_func_void);

   FINDSYM(_sym_glGetUniformLocation, "glGetUniformLocation", glsym_func_int);
   FALLBAK(_sym_glGetUniformLocation, glsym_func_int);

   FINDSYM(_sym_glGetVertexAttribfv, "glGetVertexAttribfv", glsym_func_void);
   FALLBAK(_sym_glGetVertexAttribfv, glsym_func_void);

   FINDSYM(_sym_glGetVertexAttribiv, "glGetVertexAttribiv", glsym_func_void);
   FALLBAK(_sym_glGetVertexAttribiv, glsym_func_void);

   FINDSYM(_sym_glGetVertexAttribPointerv, "glGetVertexAttribPointerv", glsym_func_void);
   FALLBAK(_sym_glGetVertexAttribPointerv, glsym_func_void);

   FINDSYM(_sym_glHint, "glHint", glsym_func_void);
   FALLBAK(_sym_glHint, glsym_func_void);

   FINDSYM(_sym_glIsBuffer, "glIsBuffer", glsym_func_uchar);
   FALLBAK(_sym_glIsBuffer, glsym_func_uchar);

   FINDSYM(_sym_glIsEnabled, "glIsEnabled", glsym_func_uchar);
   FALLBAK(_sym_glIsEnabled, glsym_func_uchar);

   FINDSYM(_sym_glIsFramebuffer, "glIsFramebuffer", glsym_func_uchar);
   FALLBAK(_sym_glIsFramebuffer, glsym_func_uchar);

   FINDSYM(_sym_glIsProgram, "glIsProgram", glsym_func_uchar);
   FALLBAK(_sym_glIsProgram, glsym_func_uchar);

   FINDSYM(_sym_glIsRenderbuffer, "glIsRenderbuffer", glsym_func_uchar);
   FALLBAK(_sym_glIsRenderbuffer, glsym_func_uchar);

   FINDSYM(_sym_glIsShader, "glIsShader", glsym_func_uchar);
   FALLBAK(_sym_glIsShader, glsym_func_uchar);

   FINDSYM(_sym_glIsTexture, "glIsTexture", glsym_func_uchar);
   FALLBAK(_sym_glIsTexture, glsym_func_uchar);

   FINDSYM(_sym_glLineWidth, "glLineWidth", glsym_func_void);
   FALLBAK(_sym_glLineWidth, glsym_func_void);

   FINDSYM(_sym_glLinkProgram, "glLinkProgram", glsym_func_void);
   FALLBAK(_sym_glLinkProgram, glsym_func_void);

   FINDSYM(_sym_glPixelStorei, "glPixelStorei", glsym_func_void);
   FALLBAK(_sym_glPixelStorei, glsym_func_void);

   FINDSYM(_sym_glPolygonOffset, "glPolygonOffset", glsym_func_void);
   FALLBAK(_sym_glPolygonOffset, glsym_func_void);

   FINDSYM(_sym_glReadPixels, "glReadPixels", glsym_func_void);
   FALLBAK(_sym_glReadPixels, glsym_func_void);

   FINDSYM(_sym_glReleaseShaderCompiler, "glReleaseShaderCompiler", glsym_func_void);
   FALLBAK(_sym_glReleaseShaderCompiler, glsym_func_void);

   FINDSYM(_sym_glRenderbufferStorage, "glRenderbufferStorage", glsym_func_void);
   FALLBAK(_sym_glRenderbufferStorage, glsym_func_void);

   FINDSYM(_sym_glSampleCoverage, "glSampleCoverage", glsym_func_void);
   FALLBAK(_sym_glSampleCoverage, glsym_func_void);

   FINDSYM(_sym_glScissor, "glScissor", glsym_func_void);
   FALLBAK(_sym_glScissor, glsym_func_void);

   FINDSYM(_sym_glShaderBinary, "glShaderBinary", glsym_func_void);
   FALLBAK(_sym_glShaderBinary, glsym_func_void);

   FINDSYM(_sym_glShaderSource, "glShaderSource", glsym_func_void);
   FALLBAK(_sym_glShaderSource, glsym_func_void);

   FINDSYM(_sym_glStencilFunc, "glStencilFunc", glsym_func_void);
   FALLBAK(_sym_glStencilFunc, glsym_func_void);

   FINDSYM(_sym_glStencilFuncSeparate, "glStencilFuncSeparate", glsym_func_void);
   FALLBAK(_sym_glStencilFuncSeparate, glsym_func_void);

   FINDSYM(_sym_glStencilMask, "glStencilMask", glsym_func_void);
   FALLBAK(_sym_glStencilMask, glsym_func_void);

   FINDSYM(_sym_glStencilMaskSeparate, "glStencilMaskSeparate", glsym_func_void);
   FALLBAK(_sym_glStencilMaskSeparate, glsym_func_void);

   FINDSYM(_sym_glStencilOp, "glStencilOp", glsym_func_void);
   FALLBAK(_sym_glStencilOp, glsym_func_void);

   FINDSYM(_sym_glStencilOpSeparate, "glStencilOpSeparate", glsym_func_void);
   FALLBAK(_sym_glStencilOpSeparate, glsym_func_void);

   FINDSYM(_sym_glTexImage2D, "glTexImage2D", glsym_func_void);
   FALLBAK(_sym_glTexImage2D, glsym_func_void);

   FINDSYM(_sym_glTexParameterf, "glTexParameterf", glsym_func_void);
   FALLBAK(_sym_glTexParameterf, glsym_func_void);

   FINDSYM(_sym_glTexParameterfv, "glTexParameterfv", glsym_func_void);
   FALLBAK(_sym_glTexParameterfv, glsym_func_void);

   FINDSYM(_sym_glTexParameteri, "glTexParameteri", glsym_func_void);
   FALLBAK(_sym_glTexParameteri, glsym_func_void);

   FINDSYM(_sym_glTexParameteriv, "glTexParameteriv", glsym_func_void);
   FALLBAK(_sym_glTexParameteriv, glsym_func_void);

   FINDSYM(_sym_glTexSubImage2D, "glTexSubImage2D", glsym_func_void);
   FALLBAK(_sym_glTexSubImage2D, glsym_func_void);

   FINDSYM(_sym_glUniform1f, "glUniform1f", glsym_func_void);
   FALLBAK(_sym_glUniform1f, glsym_func_void);

   FINDSYM(_sym_glUniform1fv, "glUniform1fv", glsym_func_void);
   FALLBAK(_sym_glUniform1fv, glsym_func_void);

   FINDSYM(_sym_glUniform1i, "glUniform1i", glsym_func_void);
   FALLBAK(_sym_glUniform1i, glsym_func_void);

   FINDSYM(_sym_glUniform1iv, "glUniform1iv", glsym_func_void);
   FALLBAK(_sym_glUniform1iv, glsym_func_void);

   FINDSYM(_sym_glUniform2f, "glUniform2f", glsym_func_void);
   FALLBAK(_sym_glUniform2f, glsym_func_void);

   FINDSYM(_sym_glUniform2fv, "glUniform2fv", glsym_func_void);
   FALLBAK(_sym_glUniform2fv, glsym_func_void);

   FINDSYM(_sym_glUniform2i, "glUniform2i", glsym_func_void);
   FALLBAK(_sym_glUniform2i, glsym_func_void);

   FINDSYM(_sym_glUniform2iv, "glUniform2iv", glsym_func_void);
   FALLBAK(_sym_glUniform2iv, glsym_func_void);

   FINDSYM(_sym_glUniform3f, "glUniform3f", glsym_func_void);
   FALLBAK(_sym_glUniform3f, glsym_func_void);

   FINDSYM(_sym_glUniform3fv, "glUniform3fv", glsym_func_void);
   FALLBAK(_sym_glUniform3fv, glsym_func_void);

   FINDSYM(_sym_glUniform3i, "glUniform3i", glsym_func_void);
   FALLBAK(_sym_glUniform3i, glsym_func_void);

   FINDSYM(_sym_glUniform3iv, "glUniform3iv", glsym_func_void);
   FALLBAK(_sym_glUniform3iv, glsym_func_void);

   FINDSYM(_sym_glUniform4f, "glUniform4f", glsym_func_void);
   FALLBAK(_sym_glUniform4f, glsym_func_void);

   FINDSYM(_sym_glUniform4fv, "glUniform4fv", glsym_func_void);
   FALLBAK(_sym_glUniform4fv, glsym_func_void);

   FINDSYM(_sym_glUniform4i, "glUniform4i", glsym_func_void);
   FALLBAK(_sym_glUniform4i, glsym_func_void);

   FINDSYM(_sym_glUniform4iv, "glUniform4iv", glsym_func_void);
   FALLBAK(_sym_glUniform4iv, glsym_func_void);

   FINDSYM(_sym_glUniformMatrix2fv, "glUniformMatrix2fv", glsym_func_void);
   FALLBAK(_sym_glUniformMatrix2fv, glsym_func_void);

   FINDSYM(_sym_glUniformMatrix3fv, "glUniformMatrix3fv", glsym_func_void);
   FALLBAK(_sym_glUniformMatrix3fv, glsym_func_void);

   FINDSYM(_sym_glUniformMatrix4fv, "glUniformMatrix4fv", glsym_func_void);
   FALLBAK(_sym_glUniformMatrix4fv, glsym_func_void);

   FINDSYM(_sym_glUseProgram, "glUseProgram", glsym_func_void);
   FALLBAK(_sym_glUseProgram, glsym_func_void);

   FINDSYM(_sym_glValidateProgram, "glValidateProgram", glsym_func_void);
   FALLBAK(_sym_glValidateProgram, glsym_func_void);

   FINDSYM(_sym_glVertexAttrib1f, "glVertexAttrib1f", glsym_func_void);
   FALLBAK(_sym_glVertexAttrib1f, glsym_func_void);

   FINDSYM(_sym_glVertexAttrib1fv, "glVertexAttrib1fv", glsym_func_void);
   FALLBAK(_sym_glVertexAttrib1fv, glsym_func_void);

   FINDSYM(_sym_glVertexAttrib2f, "glVertexAttrib2f", glsym_func_void);
   FALLBAK(_sym_glVertexAttrib2f, glsym_func_void);

   FINDSYM(_sym_glVertexAttrib2fv, "glVertexAttrib2fv", glsym_func_void);
   FALLBAK(_sym_glVertexAttrib2fv, glsym_func_void);

   FINDSYM(_sym_glVertexAttrib3f, "glVertexAttrib3f", glsym_func_void);
   FALLBAK(_sym_glVertexAttrib3f, glsym_func_void);

   FINDSYM(_sym_glVertexAttrib3fv, "glVertexAttrib3fv", glsym_func_void);
   FALLBAK(_sym_glVertexAttrib3fv, glsym_func_void);

   FINDSYM(_sym_glVertexAttrib4f, "glVertexAttrib4f", glsym_func_void);
   FALLBAK(_sym_glVertexAttrib4f, glsym_func_void);

   FINDSYM(_sym_glVertexAttrib4fv, "glVertexAttrib4fv", glsym_func_void);
   FALLBAK(_sym_glVertexAttrib4fv, glsym_func_void);

   FINDSYM(_sym_glVertexAttribPointer, "glVertexAttribPointer", glsym_func_void);
   FALLBAK(_sym_glVertexAttribPointer, glsym_func_void);

   FINDSYM(_sym_glViewport, "glViewport", glsym_func_void);
   FALLBAK(_sym_glViewport, glsym_func_void);

#undef FINDSYM
#undef FALLBAK

   /*
    * For desktop OpenGL we wrap these:
    * - glGetShaderPrecisionFormat
    * - glReleaseShaderCompiler
    * - glShaderBinary
    */
   if (_sym_glGetShaderPrecisionFormat != (typeof(_sym_glGetShaderPrecisionFormat))sym_missing )
     {
        DBG("The GL library is OpenGL ES or OpenGL 4.1+");
        gl_lib_is_gles = 1;
     }

   if (!ok)
     ERR("Evas failed to initialize OSMesa for OpenGL with the software engine!");

   return ok;
}

//--------------------------------------------------------------//
// Wrapped GL APIs to handle desktop compatibility

// Stripping precision code from GLES shader for desktop compatibility
// Code adopted from Meego GL code. Temporary Fix.
static const char *
opengl_strtok(const char *s, int *n, char **saveptr, char *prevbuf)
{
   char *start;
   char *ret;
   char *p;
   int retlen;
   static const char *delim = " \t\n\r/";

   if (prevbuf) free(prevbuf);

   if (s)
      *saveptr = (char *)s;
   else
     {
        if (!(*saveptr) || !(*n))
           return NULL;
        s = *saveptr;
     }

   for (; *n && strchr(delim, *s); s++, (*n)--)
     {
        if (*s == '/' && *n > 1)
          {
             if (s[1] == '/')
               {
                  do
                    {
                       s++, (*n)--;
                    }
                  while (*n > 1 && s[1] != '\n' && s[1] != '\r');
               }
             else if (s[1] == '*')
               {
                  do
                    {
                       s++, (*n)--;
                    }
                  while (*n > 2 && (s[1] != '*' || s[2] != '/'));
                  s++, (*n)--;
               }
          }
     }

   start = (char *)s;
   for (; *n && *s && !strchr(delim, *s); s++, (*n)--);
   if (*n > 0) s++, (*n)--;

   *saveptr = (char *)s;

   retlen = s - start;
   ret = malloc(retlen + 1);
   p = ret;

   while (retlen > 0)
     {
        if (*start == '/' && retlen > 1)
          {
             if (start[1] == '/')
               {
                  do
                    {
                       start++, retlen--;
                    }
                  while (retlen > 1 && start[1] != '\n' && start[1] != '\r');
                  start++, retlen--;
                  continue;
               }
             else if (start[1] == '*')
               {
                  do
                    {
                       start++, retlen--;
                    }
                  while (retlen > 2 && (start[1] != '*' || start[2] != '/'));
                  start += 3, retlen -= 3;
                  continue;
               }
          }
        *(p++) = *(start++), retlen--;
     }

   *p = 0;
   return ret;
}

static char *
patch_gles_shader(const char *source, int length, int *patched_len)
{
   char *saveptr = NULL;
   char *sp;
   char *p = NULL;

   if (!length) length = strlen(source);

   *patched_len = 0;
   int patched_size = length;
   char *patched = malloc(patched_size + 1);

   if (!patched) return NULL;

   p = (char *)opengl_strtok(source, &length, &saveptr, NULL);
   for (; p; p = (char *)opengl_strtok(0, &length, &saveptr, p))
     {
        if (!strncmp(p, "lowp", 4) || !strncmp(p, "mediump", 7) || !strncmp(p, "highp", 5))
          {
             continue;
          }
        else if (!strncmp(p, "precision", 9))
          {
             while ((p = (char *)opengl_strtok(0, &length, &saveptr, p)) && !strchr(p, ';'));
          }
        else
          {
             if (!strncmp(p, "gl_MaxVertexUniformVectors", 26))
               {
                  free(p);
                  p = strdup("(gl_MaxVertexUniformComponents / 4)");
               }
             else if (!strncmp(p, "gl_MaxFragmentUniformVectors", 28))
               {
                  free(p);
                  p = strdup("(gl_MaxFragmentUniformComponents / 4)");
               }
             else if (!strncmp(p, "gl_MaxVaryingVectors", 20))
               {
                  free(p);
                  p = strdup("(gl_MaxVaryingFloats / 4)");
               }

             int new_len = strlen(p);
             if (*patched_len + new_len > patched_size)
               {
                  char *tmp;

                  patched_size *= 2;
                  tmp = realloc(patched, patched_size + 1);
                  if (!tmp)
                    {
                       free(patched);
                       free(p);
                       return NULL;
                    }
                  patched = tmp;
               }

             memcpy(patched + *patched_len, p, new_len);
             *patched_len += new_len;
          }
     }

   patched[*patched_len] = 0;
   /* check that we don't leave dummy preprocessor lines */
   for (sp = patched; *sp;)
     {
        for (; *sp == ' ' || *sp == '\t'; sp++);
        if (!strncmp(sp, "#define", 7))
          {
             for (p = sp + 7; *p == ' ' || *p == '\t'; p++);
             if (*p == '\n' || *p == '\r' || *p == '/')
               {
                  memset(sp, 0x20, 7);
               }
          }
        for (; *sp && *sp != '\n' && *sp != '\r'; sp++);
        for (; *sp == '\n' || *sp == '\r'; sp++);
     }
   return patched;
}

static void
evgl_glShaderSource(GLuint shader, GLsizei count, const char* const* string, const GLint* length)
{
   int i = 0, len = 0;

   char **s = malloc(count * sizeof(char*));
   if (!s) goto err;
   GLint *l = malloc(count * sizeof(GLint));
   if (!l)
     {
        free(s);
        goto err;
     }

   memset(s, 0, count * sizeof(char*));
   memset(l, 0, count * sizeof(GLint));

   for (i = 0; i < count; ++i)
     {
        if (length)
          {
             len = length[i];
             if (len < 0)
                len = string[i] ? strlen(string[i]) : 0;
          }
        else
           len = string[i] ? strlen(string[i]) : 0;

        if (string[i])
          {
             s[i] = patch_gles_shader(string[i], len, &l[i]);
             if (!s[i])
               {
                  while(i)
                     free(s[--i]);
                  free(l);
                  free(s);
                  goto err;
               }
          }
        else
          {
             s[i] = NULL;
             l[i] = 0;
          }
     }

   _sym_glShaderSource(shader, count, (const char * const *)s, l);

   while(i)
      free(s[--i]);
   free(l);
   free(s);

err:
   ERR("Patching Shader Failed.");
}


static void
evgl_glGetShaderPrecisionFormat(GLenum shadertype EINA_UNUSED, GLenum precisiontype EINA_UNUSED, GLint* range, GLint* precision)
{
   if (range)
     {
        range[0] = -126; // floor(log2(FLT_MIN))
        range[1] = 127; // floor(log2(FLT_MAX))
     }
   if (precision)
     {
        precision[0] = 24; // floor(-log2((1.0/16777218.0)));
     }
   return;
}

static void
evgl_glReleaseShaderCompiler(void)
{
   DBG("Not supported in Desktop GL");
   return;
}

static void
evgl_glShaderBinary(GLsizei n EINA_UNUSED, const GLuint* shaders EINA_UNUSED, GLenum binaryformat EINA_UNUSED, const void* binary EINA_UNUSED, GLsizei length EINA_UNUSED)
{
   // FIXME: need to dlsym/getprocaddress for this
   DBG("Not supported in Desktop GL");
   return;
   //n = binaryformat = length = 0;
   //shaders = binary = 0;
}

static const GLubyte *
evgl_glGetString(GLenum name)
{
   static char _version[128] = {0};
   static char _glsl[128] = {0};
   const char *ret;

   /* NOTE: Please modify gl_common/evas_gl_api.c as well if you change
    *       this function!
    */

   switch (name)
     {
      case GL_VENDOR:
      case GL_RENDERER:
        // Keep these as-is.
        break;

      case GL_SHADING_LANGUAGE_VERSION:
        ret = (const char *) _sym_glGetString(GL_SHADING_LANGUAGE_VERSION);
        if (!ret) return NULL;
        snprintf(_glsl, sizeof(_glsl), "OpenGL ES GLSL ES 1.00 Evas GL (%s)", (char *) ret);
        _version[sizeof(_glsl) - 1] = '\0';
        return (const GLubyte *) _glsl;

      case GL_VERSION:
        ret = (const char *) _sym_glGetString(GL_VERSION);
        if (!ret) return NULL;
        snprintf(_version, sizeof(_version), "OpenGL ES 2.0 Evas GL (%s)", (char *) ret);
        _version[sizeof(_version) - 1] = '\0';
        return (const GLubyte *) _version;

      case GL_EXTENSIONS:
        // assume OSMesa's extensions are safe (no messing with GL context here)
        break;

      default:
        // GL_INVALID_ENUM is generated if name is not an accepted value.
        WRN("Unknown string requested: %x", (unsigned int) name);
        break;
     }

   return _sym_glGetString(name);
}


static void
override_gl_apis(Evas_GL_API *api)
{
   memset(api, 0, sizeof(*api));
   api->version = EVAS_GL_API_VERSION;

#define ORD(f) EVAS_API_OVERRIDE(f, api, _sym_)
   // GLES 2.0
   ORD(glActiveTexture);
   ORD(glAttachShader);
   ORD(glBindAttribLocation);
   ORD(glBindBuffer);
   ORD(glBindFramebuffer);
   ORD(glBindRenderbuffer);
   ORD(glBindTexture);
   ORD(glBlendColor);
   ORD(glBlendEquation);
   ORD(glBlendEquationSeparate);
   ORD(glBlendFunc);
   ORD(glBlendFuncSeparate);
   ORD(glBufferData);
   ORD(glBufferSubData);
   ORD(glCheckFramebufferStatus);
   ORD(glClear);
   ORD(glClearColor);
   ORD(glClearDepthf);
   ORD(glClearStencil);
   ORD(glColorMask);
   ORD(glCompileShader);
   ORD(glCompressedTexImage2D);
   ORD(glCompressedTexSubImage2D);
   ORD(glCopyTexImage2D);
   ORD(glCopyTexSubImage2D);
   ORD(glCreateProgram);
   ORD(glCreateShader);
   ORD(glCullFace);
   ORD(glDeleteBuffers);
   ORD(glDeleteFramebuffers);
   ORD(glDeleteProgram);
   ORD(glDeleteRenderbuffers);
   ORD(glDeleteShader);
   ORD(glDeleteTextures);
   ORD(glDepthFunc);
   ORD(glDepthMask);
   ORD(glDepthRangef);
   ORD(glDetachShader);
   ORD(glDisable);
   ORD(glDisableVertexAttribArray);
   ORD(glDrawArrays);
   ORD(glDrawElements);
   ORD(glEnable);
   ORD(glEnableVertexAttribArray);
   ORD(glFinish);
   ORD(glFlush);
   ORD(glFramebufferRenderbuffer);
   ORD(glFramebufferTexture2D);
   ORD(glFrontFace);
   ORD(glGenBuffers);
   ORD(glGenerateMipmap);
   ORD(glGenFramebuffers);
   ORD(glGenRenderbuffers);
   ORD(glGenTextures);
   ORD(glGetActiveAttrib);
   ORD(glGetActiveUniform);
   ORD(glGetAttachedShaders);
   ORD(glGetAttribLocation);
   ORD(glGetBooleanv);
   ORD(glGetBufferParameteriv);
   ORD(glGetError); // FIXME
   ORD(glGetFloatv);
   ORD(glGetFramebufferAttachmentParameteriv);
   ORD(glGetIntegerv);
   ORD(glGetProgramiv);
   ORD(glGetProgramInfoLog);
   ORD(glGetRenderbufferParameteriv);
   ORD(glGetShaderiv);
   ORD(glGetShaderInfoLog);
   ORD(glGetShaderPrecisionFormat);
   ORD(glGetShaderSource);
   ORD(glGetTexParameterfv);
   ORD(glGetTexParameteriv);
   ORD(glGetUniformfv);
   ORD(glGetUniformiv);
   ORD(glGetUniformLocation);
   ORD(glGetVertexAttribfv);
   ORD(glGetVertexAttribiv);
   ORD(glGetVertexAttribPointerv);
   ORD(glHint);
   ORD(glIsBuffer);
   ORD(glIsEnabled);
   ORD(glIsFramebuffer);
   ORD(glIsProgram);
   ORD(glIsRenderbuffer);
   ORD(glIsShader);
   ORD(glIsTexture);
   ORD(glLineWidth);
   ORD(glLinkProgram);
   ORD(glPixelStorei);
   ORD(glPolygonOffset);
   ORD(glReadPixels);
   ORD(glReleaseShaderCompiler);
   ORD(glRenderbufferStorage);
   ORD(glSampleCoverage);
   ORD(glScissor);
   ORD(glShaderBinary);
   ORD(glShaderSource);
   ORD(glStencilFunc);
   ORD(glStencilFuncSeparate);
   ORD(glStencilMask);
   ORD(glStencilMaskSeparate);
   ORD(glStencilOp);
   ORD(glStencilOpSeparate);
   ORD(glTexImage2D);
   ORD(glTexParameterf);
   ORD(glTexParameterfv);
   ORD(glTexParameteri);
   ORD(glTexParameteriv);
   ORD(glTexSubImage2D);
   ORD(glUniform1f);
   ORD(glUniform1fv);
   ORD(glUniform1i);
   ORD(glUniform1iv);
   ORD(glUniform2f);
   ORD(glUniform2fv);
   ORD(glUniform2i);
   ORD(glUniform2iv);
   ORD(glUniform3f);
   ORD(glUniform3fv);
   ORD(glUniform3i);
   ORD(glUniform3iv);
   ORD(glUniform4f);
   ORD(glUniform4fv);
   ORD(glUniform4i);
   ORD(glUniform4iv);
   ORD(glUniformMatrix2fv);
   ORD(glUniformMatrix3fv);
   ORD(glUniformMatrix4fv);
   ORD(glUseProgram);
   ORD(glValidateProgram);
   ORD(glVertexAttrib1f);
   ORD(glVertexAttrib1fv);
   ORD(glVertexAttrib2f);
   ORD(glVertexAttrib2fv);
   ORD(glVertexAttrib3f);
   ORD(glVertexAttrib3fv);
   ORD(glVertexAttrib4f);
   ORD(glVertexAttrib4fv);
   ORD(glVertexAttribPointer);
   ORD(glViewport);
#undef ORD

#define ORD(f) EVAS_API_OVERRIDE(f, &gl_funcs, evgl_)
   ORD(glGetString);

   if (!gl_lib_is_gles)
     {
        // Override functions wrapped by Evas_GL
        // GLES2.0 API compat on top of desktop gl
        // Note that Open GL 4.1+ provides these 3 functions as well
        ORD(glGetShaderPrecisionFormat);
        ORD(glReleaseShaderCompiler);
        ORD(glShaderBinary);
     }

   ORD(glShaderSource);    // Do precision stripping in both cases
#undef ORD
}
#endif

//-------------------------------------------//
static int
gl_lib_init(void)
{
#ifdef EVAS_GL
   // Current ctx & sfc stuff
   if (gl_lib_handle) return 1;
   if (!_tls_check()) return 0;

   // dlopen OSMesa
   gl_lib_handle = dlopen("libOSMesa.so.9", RTLD_NOW);
   if (!gl_lib_handle) gl_lib_handle = dlopen("libOSMesa.so.8", RTLD_NOW);
   if (!gl_lib_handle) gl_lib_handle = dlopen("libOSMesa.so.7", RTLD_NOW);
   if (!gl_lib_handle) gl_lib_handle = dlopen("libOSMesa.so.6", RTLD_NOW);
   if (!gl_lib_handle) gl_lib_handle = dlopen("libOSMesa.so.5", RTLD_NOW);
   if (!gl_lib_handle) gl_lib_handle = dlopen("libOSMesa.so.4", RTLD_NOW);
   if (!gl_lib_handle) gl_lib_handle = dlopen("libOSMesa.so.3", RTLD_NOW);
   if (!gl_lib_handle) gl_lib_handle = dlopen("libOSMesa.so.2", RTLD_NOW);
   if (!gl_lib_handle) gl_lib_handle = dlopen("libOSMesa.so.1", RTLD_NOW);
   if (!gl_lib_handle) gl_lib_handle = dlopen("libOSMesa.so", RTLD_NOW);
   if (!gl_lib_handle)
     {
        WRN("Unable to open libOSMesa:  %s", dlerror());
        DBG("Unable to support EvasGL in this engine module. Install OSMesa to get it running");
        return 0;
     }

   //------------------------------------------------//
   if (!glue_sym_init())
     {
        WRN("Unable to glue OSMesa syms");
        return 0;
     }
   if (!gl_sym_init())
     {
        WRN("Unable to init OSMesa syms");
        return 0;
     }

   override_gl_apis(&gl_funcs);

   return 1;
#else
   WRN("Evas GL not compiled in");
   return 0;
#endif
}

/*
static void
init_gl(void)
{
   DBG("Initializing Software OpenGL APIs...");

   if (!gl_lib_init())
      DBG("Unable to support EvasGL in this engine module. Install OSMesa to get it running");
   else
     {
#define ORD(f) EVAS_API_OVERRIDE(f, &func, eng_)
        ORD(gl_surface_create);
        ORD(gl_surface_destroy);
        ORD(gl_context_create);
        ORD(gl_context_destroy);
        ORD(gl_make_current);
        ORD(gl_string_query);           // FIXME: Need to implement
        ORD(gl_proc_address_get);       // FIXME: Need to implement
        ORD(gl_native_surface_get);
        ORD(gl_error_get);
        ORD(gl_current_context_get);
        ORD(gl_current_surface_get);
        ORD(gl_rotation_angle_get);
#undef ORD
     }
}
*/

/*
 *****
 **
 ** MODULE ACCESSIBLE API API
 **
 *****
 */

static int
module_open(Evas_Module *em)
{
   if (!em) return 0;
   _evas_soft_gen_log_dom = eina_log_domain_register
     ("evas-software_generic", EVAS_DEFAULT_LOG_COLOR);
   if(_evas_soft_gen_log_dom<0)
     {
        EINA_LOG_ERR("Can not create a module log domain.");
        return 0;
     }

   _mp_command_rect = eina_mempool_add("chained_mempool",
                                       "Evas_Thread_Command_Rect", NULL,
                                       sizeof (Evas_Thread_Command_Rect), 128);
   _mp_command_line = eina_mempool_add("chained_mempool",
                                       "Evas_Thread_Command_Line", NULL,
                                       sizeof (Evas_Thread_Command_Line), 32);
   _mp_command_polygon = eina_mempool_add("chained_mempool",
                                       "Evas_Thread_Command_Polygon", NULL,
                                       sizeof (Evas_Thread_Command_Polygon), 32);
   _mp_command_image = eina_mempool_add("chained_mempool",
                                       "Evas_Thread_Command_Image", NULL,
                                       sizeof (Evas_Thread_Command_Image), 128);
   _mp_command_font = eina_mempool_add("chained_mempool",
                                       "Evas_Thread_Command_Font", NULL,
                                       sizeof (Evas_Thread_Command_Font), 128);
   _mp_command_map = eina_mempool_add("chained_mempool",
                                       "Evas_Thread_Command_Map", NULL,
                                       sizeof (Evas_Thread_Command_Map), 64);
   _mp_command_multi_font =
     eina_mempool_add("chained_mempool", "Evas_Thread_Command_Multi_Font",
                      NULL, sizeof(Evas_Thread_Command_Multi_Font), 128);
   _mp_command_ector =
     eina_mempool_add("chained_mempool", "Evas_Thread_Command_Ector",
                      NULL, sizeof(Evas_Thread_Command_Ector), 128);
   _mp_command_ector_surface =
     eina_mempool_add("chained_mempool", "Evas_Thread_Command_Ector_Surface",
                      NULL, sizeof(Evas_Thread_Command_Ector_Surface), 128);

   ector_init();
// do on demand when first evas_gl_api_get is called...
//   init_gl();
   ector_glsym_set(dlsym, RTLD_DEFAULT);
   evas_common_pipe_init();

   em->functions = (void *)(&func);
   cpunum = eina_cpu_count();
   return 1;
}

static void
module_close(Evas_Module *em EINA_UNUSED)
{
   ector_shutdown();
   eina_mempool_del(_mp_command_rect);
   eina_mempool_del(_mp_command_line);
   eina_mempool_del(_mp_command_polygon);
   eina_mempool_del(_mp_command_image);
   eina_mempool_del(_mp_command_font);
   eina_mempool_del(_mp_command_map);
   eina_mempool_del(_mp_command_ector);
   if (_evas_soft_gen_log_dom >= 0)
     {
        eina_log_domain_unregister(_evas_soft_gen_log_dom);
        _evas_soft_gen_log_dom = -1;
     }
}

static Evas_Module_Api evas_modapi =
{
   EVAS_MODULE_API_VERSION,
   "software_generic",
   "none",
   {
     module_open,
     module_close
   }
};

Eina_Bool evas_engine_software_generic_init(void)
{
   return evas_module_register(&evas_modapi, EVAS_MODULE_TYPE_ENGINE);
}

// Time to destroy the ector context
void evas_engine_software_generic_shutdown(void)
{
   evas_module_unregister(&evas_modapi, EVAS_MODULE_TYPE_ENGINE);
}

#ifndef EVAS_STATIC_BUILD_SOFTWARE_GENERIC
EVAS_EINA_MODULE_DEFINE(engine, software_generic);
#endif
