tarina

git clone https://git.tarina.org/tarina
Log | Files | Refs | README | LICENSE

graphics.c (45390B)


      1 /*
      2 Copyright (c) 2012, Broadcom Europe Ltd
      3 All rights reserved.
      4 
      5 Redistribution and use in source and binary forms, with or without
      6 modification, are permitted provided that the following conditions are met:
      7     * Redistributions of source code must retain the above copyright
      8       notice, this list of conditions and the following disclaimer.
      9     * Redistributions in binary form must reproduce the above copyright
     10       notice, this list of conditions and the following disclaimer in the
     11       documentation and/or other materials provided with the distribution.
     12     * Neither the name of the copyright holder nor the
     13       names of its contributors may be used to endorse or promote products
     14       derived from this software without specific prior written permission.
     15 
     16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
     17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
     20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26 */
     27 
     28 // Graphics library for VG
     29 
     30 #include <stdio.h>
     31 #include <stdlib.h>
     32 #include <string.h>
     33 #include <assert.h>
     34 #include "vgfont.h"
     35 #include "graphics_x_private.h"
     36 
     37 /******************************************************************************
     38 Defines.
     39 ******************************************************************************/
     40 #define ATEXT_FONT_SIZE 12 /*< Default font size (font size can be set with *_ext functions). */
     41 
     42 /******************************************************************************
     43 Local data
     44 ******************************************************************************/
     45 static GX_DISPLAY_T display; /*< Our one and only EGL display. */
     46 
     47 /**
     48  * We create one eglContext for each of the possible graphics_x resource types 
     49  * that are supported.
     50  ***********************************************************/
     51 static EGLContext gx_contexts[GRAPHICS_RESOURCE_HANDLE_TYPE_MAX]; 
     52 
     53 /** Note: we have to share all our contexts, because otherwise it seems
     54  * to be not valid to blit from one image to another if the images
     55  * have different contexts.
     56  *
     57  * That means we have to use a single global lock to serialise all accesses
     58  * to any contexts.
     59  ***********************************************************/
     60 static VCOS_MUTEX_T lock;
     61 
     62 static EGLConfig gx_configs[GRAPHICS_RESOURCE_HANDLE_TYPE_MAX];
     63 
     64 static int inited;
     65 
     66 /******************************************************************************
     67 Local Functions
     68 ******************************************************************************/
     69 
     70 /** Convert graphics_x colour formats into EGL format. */
     71 static int gx_egl_attrib_colours(EGLint *attribs, GRAPHICS_RESOURCE_TYPE_T res_type)
     72 {
     73    int i, n;
     74    static EGLint rgba[] = {EGL_RED_SIZE, EGL_GREEN_SIZE, EGL_BLUE_SIZE, EGL_ALPHA_SIZE};
     75    static uint8_t rgb565[] = {5,6,5,0};
     76    static uint8_t rgb888[] = {8,8,8,0};
     77    static uint8_t rgb32a[] = {8,8,8,8};
     78 
     79    uint8_t *sizes = NULL;
     80 
     81    switch (res_type)
     82    {
     83       case GRAPHICS_RESOURCE_RGB565:
     84          sizes = rgb565;
     85          break;
     86       case GRAPHICS_RESOURCE_RGB888:
     87          sizes = rgb888;
     88          break;
     89       case GRAPHICS_RESOURCE_RGBA32:
     90          sizes = rgb32a;
     91          break;
     92       default:
     93          vcos_assert(0);
     94          return -1;
     95    }
     96    for (n=0, i=0; i<countof(rgba); i++)
     97    {
     98       attribs[n++] = rgba[i];
     99       attribs[n++] = sizes[i];
    100    }
    101    return n;
    102 }
    103 
    104 /* Create an EGLContext for a given GRAPHICS_RESOURCE_TYPE */
    105 static VCOS_STATUS_T create_context(EGLDisplay disp, 
    106                                     GRAPHICS_RESOURCE_TYPE_T image_type,
    107                                     EGLContext *shared_with)
    108 {
    109    int n;
    110    EGLConfig configs[1];
    111    EGLint nconfigs, attribs[32];
    112    n = gx_egl_attrib_colours(attribs, image_type);
    113 
    114    // we want to be able to do OpenVG on this surface...
    115    attribs[n++] = EGL_RENDERABLE_TYPE; attribs[n++] = EGL_OPENVG_BIT;
    116    attribs[n++] = EGL_SURFACE_TYPE;    attribs[n++] = EGL_WINDOW_BIT;
    117 
    118    attribs[n] = EGL_NONE;
    119 
    120    EGLBoolean egl_ret = eglChooseConfig(disp,
    121                                         attribs, configs,
    122                                         countof(configs), &nconfigs);
    123 
    124    if (!egl_ret || !nconfigs)
    125    {
    126       GX_LOG("%s: no suitable configurations for res type %d",
    127              __FUNCTION__, image_type);
    128       return VCOS_EINVAL;
    129    }
    130 
    131    EGLContext cxt = eglCreateContext(disp, configs[0], *shared_with, 0);
    132    if (!cxt)
    133    {
    134       GX_LOG("Could not create context for image type %d: 0x%x",
    135              image_type, eglGetError());
    136       return VCOS_ENOSPC;
    137    }
    138    
    139    gx_contexts[image_type] = cxt;
    140    gx_configs[image_type] = configs[0];
    141    *shared_with = cxt;
    142 
    143    return VCOS_SUCCESS;
    144 }
    145 
    146 /******************************************************************************
    147 Functions private to code inside GraphicsX
    148 ******************************************************************************/
    149 
    150 static VCOS_STATUS_T gx_priv_initialise( void )
    151 {
    152    int i;
    153    EGLDisplay disp;
    154    EGLint egl_maj, egl_min;
    155    int32_t ret = VCOS_EINVAL;
    156    EGLBoolean result;
    157 
    158    vcos_demand(inited == 0);
    159 
    160    vcos_log_set_level(&gx_log_cat, VCOS_LOG_WARN);
    161    vcos_log_register("graphics", &gx_log_cat);
    162 
    163    memset(&display,0,sizeof(display));
    164 
    165    gx_priv_init();
    166 
    167    disp = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    168 
    169    if (disp == EGL_NO_DISPLAY)
    170    {
    171       GX_LOG("Could not open display: 0x%x", eglGetError());
    172       vcos_assert(0);
    173       goto fail_disp;
    174    }
    175 
    176    result = eglInitialize(disp, &egl_maj, &egl_min);
    177    if (!result)
    178    {
    179       GX_LOG("Could not init display :0x%x", eglGetError());
    180       vcos_assert(0); // really can't continue
    181       goto fail_egl_init;
    182    }
    183 
    184    result = eglBindAPI(EGL_OPENVG_API);
    185    vcos_assert(result); // really should succeed
    186 
    187    display.disp = disp;
    188 
    189    GX_TRACE("Supported client APIS: %s", eglQueryString(disp, EGL_CLIENT_APIS));
    190 
    191    // create the available contexts
    192    EGLContext shared_context = EGL_NO_CONTEXT;
    193    ret =  create_context(disp,GRAPHICS_RESOURCE_RGB565, &shared_context);
    194    ret |= create_context(disp,GRAPHICS_RESOURCE_RGB888, &shared_context);
    195    ret |= create_context(disp,GRAPHICS_RESOURCE_RGBA32, &shared_context);
    196 
    197    if (ret != VCOS_SUCCESS)
    198       goto fail_cxt;
    199 
    200    eglSwapInterval(disp, 1);
    201 
    202    inited = 1;
    203 
    204    return ret;
    205 
    206 fail_cxt:
    207    for (i=0; i<GRAPHICS_RESOURCE_HANDLE_TYPE_MAX; i++)
    208    {
    209       if (gx_contexts[i])
    210       {
    211          eglDestroyContext(display.disp,gx_contexts[i]);
    212          vcos_mutex_delete(&lock);
    213       }
    214    }
    215    eglTerminate(display.disp);
    216 fail_egl_init:
    217 fail_disp:
    218    return ret;
    219 }
    220 
    221 /*****************************************************************************/
    222 void gx_priv_save(GX_CLIENT_STATE_T *state, GRAPHICS_RESOURCE_HANDLE res)
    223 {
    224    EGLBoolean egl_result;
    225    vcos_assert(res == NULL || (res->magic == RES_MAGIC));
    226    vcos_assert(res == NULL || !res->context_bound);
    227 
    228    state->context      = eglGetCurrentContext();
    229    state->api          = eglQueryAPI();
    230    state->read_surface = eglGetCurrentSurface(EGL_READ);
    231    state->draw_surface = eglGetCurrentSurface(EGL_DRAW);
    232    state->res = res;
    233 
    234    vcos_assert(state->api); // should never be anything other than VG or GL
    235    
    236    vcos_mutex_lock(&lock);
    237 
    238    egl_result = eglBindAPI(EGL_OPENVG_API);
    239    vcos_assert(egl_result);
    240    
    241    if (res)
    242    {
    243       GX_TRACE("gx_priv_save: eglMakeCurrent: %s, res %x surface %x, cxt %x", vcos_thread_get_name(vcos_thread_current()), 
    244          (uint32_t)res, (uint32_t)res->surface, (uint32_t)res->context);
    245 
    246       egl_result = eglMakeCurrent(display.disp, res->surface, 
    247                                   res->surface, res->context);
    248       vcos_assert(egl_result);
    249       
    250       res->context_bound = 1;
    251    }
    252 }
    253 
    254 /*****************************************************************************/
    255 void gx_priv_restore(GX_CLIENT_STATE_T *state)
    256 {
    257    EGLBoolean egl_result;
    258 
    259    GX_TRACE("gx_priv_restore: eglMakeCurrent: %s, res %x draw_surface %x, surface %x, cxt %x", vcos_thread_get_name(vcos_thread_current()),
    260       (uint32_t)state->res, (uint32_t)state->draw_surface, (uint32_t)state->read_surface, (uint32_t)state->context);
    261 
    262    // disconnect our thread from this context, so we other threads can use it via
    263    // this API
    264    egl_result = eglMakeCurrent(display.disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    265    vcos_assert(egl_result);
    266 
    267    // now return to the client's API binding
    268    egl_result = eglBindAPI(state->api);
    269    vcos_assert(egl_result);
    270 
    271    egl_result = eglMakeCurrent(display.disp, state->draw_surface, state->read_surface, state->context);
    272    vcos_assert(egl_result);
    273 
    274    if (state->res) state->res->context_bound = 0;
    275    
    276    vcos_mutex_unlock(&lock);
    277 }
    278 
    279 /******************************************************************************
    280 Functions and data exported as part of the public GraphicsX API
    281 ******************************************************************************/
    282 
    283 VCOS_LOG_CAT_T gx_log_cat; /*< Logging category for GraphicsX. */
    284 
    285 int32_t graphics_initialise( void )
    286 {
    287    // dummy initialisation function. This is typically called
    288    // early in the day before VLLs are available, and so cannot
    289    // do anything useful.
    290    return 0;
    291 }
    292 
    293 /*****************************************************************************/
    294 int32_t graphics_uninitialise( void )
    295 {
    296    int i;
    297    vcos_assert(inited);
    298 
    299    gx_priv_font_term();
    300 
    301    for (i=0; i<GRAPHICS_RESOURCE_HANDLE_TYPE_MAX; i++)
    302       if (gx_contexts[i])
    303          eglDestroyContext(display.disp,gx_contexts[i]);
    304 
    305    eglTerminate(display.disp);
    306    gx_priv_destroy();
    307    vcos_log_unregister(&gx_log_cat);
    308    inited = 0;
    309    return 0;
    310 }
    311 
    312 /*****************************************************************************/
    313 VCOS_STATUS_T gx_create_window( uint32_t screen_id,
    314                                 uint32_t width,
    315                                 uint32_t height,
    316                                 GRAPHICS_RESOURCE_TYPE_T image_type,
    317                                 GRAPHICS_RESOURCE_HANDLE *resource_handle )
    318 {
    319    int rc;
    320    VCOS_STATUS_T status = VCOS_SUCCESS;
    321    GRAPHICS_RESOURCE_HANDLE h;
    322    EGLBoolean egl_result;
    323    void *cookie;
    324    GX_CLIENT_STATE_T save;
    325 
    326    if (!gx_contexts[image_type])
    327    {
    328       GX_LOG("Invalid image type %d", image_type);
    329       return VCOS_EINVAL;
    330    }
    331 
    332    h = vcos_calloc(1,sizeof(*h), "graphics_x_resource");
    333    if (!h)
    334    {
    335       GX_LOG("%s: no memory for resource", __FUNCTION__);
    336       return VCOS_ENOMEM;
    337    }
    338 
    339    // now need to get the native window
    340    rc = gx_priv_create_native_window(screen_id, 
    341                                      width, height, image_type,
    342                                      &h->u.native_window,
    343                                      &cookie);
    344    if (rc < 0)
    345    {
    346       GX_LOG("%s: could not create native window", __FUNCTION__);
    347       status = VCOS_ENOMEM;
    348       goto fail_create_native_win;
    349    }
    350 
    351    h->magic = RES_MAGIC;
    352    h->type  = GX_WINDOW;
    353    h->alpha = 1.0;
    354 
    355    h->surface = eglCreateWindowSurface(display.disp, gx_configs[image_type], &h->u.native_window.egl_win, NULL);
    356    if (!h->surface)
    357    {
    358       GX_LOG("Could not create window surface: 0x%x", eglGetError());
    359       status = VCOS_ENOMEM;
    360       goto fail_win;
    361    }
    362 
    363    egl_result = eglSurfaceAttrib(display.disp, h->surface,
    364       EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
    365    vcos_assert(egl_result);
    366 
    367    h->context   = gx_contexts[image_type];
    368    h->screen_id = screen_id;
    369    h->width     = width;
    370    h->height    = height;
    371    h->restype   = image_type;
    372 
    373    gx_priv_save(&save, h);
    374 
    375    // fill it with black
    376    status = gx_priv_resource_fill(h, 0, 0, width, height, GRAPHICS_RGBA32(0,0,0,0xff));
    377    vcos_assert(status == VCOS_SUCCESS);
    378 
    379    gx_priv_finish_native_window(h, cookie);
    380    gx_priv_flush(h);
    381 
    382    *resource_handle = h;
    383    gx_priv_restore(&save);
    384    return status;
    385 
    386 fail_win:
    387    gx_priv_destroy_native_window(h);
    388 fail_create_native_win:
    389    vcos_free(h);
    390    return status;
    391 }
    392 
    393 /*****************************************************************************/
    394 int32_t graphics_delete_resource( GRAPHICS_RESOURCE_HANDLE res )
    395 {
    396    EGLBoolean result;
    397 
    398    if (!res)
    399    {
    400       // let it slide - mimics old behaviour
    401       return 0;
    402    }
    403    GX_TRACE("delete resource @%p", res);
    404 
    405    vcos_assert(res->magic == RES_MAGIC);
    406 
    407    if (res->type == GX_PBUFFER)
    408    {
    409       GX_CLIENT_STATE_T save;
    410       gx_priv_save(&save, res);
    411       vgDestroyImage(res->u.pixmap);
    412       vcos_assert(vgGetError() == 0);
    413       gx_priv_restore(&save);
    414    }
    415 
    416    GX_TRACE("graphics_delete_resource: calling eglDestroySurface...");
    417    result = eglDestroySurface(display.disp, res->surface);
    418    vcos_assert(result);
    419 
    420    GX_TRACE("graphics_delete_resource: calling eglWaitClient...");
    421    eglWaitClient(); // wait for EGL to finish sorting out its surfaces
    422 
    423    if (res->type == GX_WINDOW)
    424    {
    425       GX_TRACE("graphics_delete_resource: calling gx_priv_destroy_native_window...");
    426       gx_priv_destroy_native_window(res);
    427    }
    428 
    429    res->magic = ~RES_MAGIC;
    430    vcos_free(res);
    431    GX_TRACE("graphics_delete_resource: done");
    432 
    433    return 0;
    434 }
    435 
    436 /*****************************************************************************/
    437 int32_t graphics_update_displayed_resource(GRAPHICS_RESOURCE_HANDLE res,
    438                                            const uint32_t x_offset,
    439                                            const uint32_t y_offset,
    440                                            const uint32_t width,
    441                                            const uint32_t height )
    442 {
    443    GX_CLIENT_STATE_T save;
    444    gx_priv_save(&save, res);
    445       
    446    gx_priv_flush(res);
    447 
    448    gx_priv_restore(&save);
    449 
    450    return 0;
    451 }
    452 
    453 /*****************************************************************************/
    454 int32_t graphics_resource_fill(GRAPHICS_RESOURCE_HANDLE res,
    455                                uint32_t x,
    456                                uint32_t y,
    457                                uint32_t width,
    458                                uint32_t height,
    459                                uint32_t fill_colour )
    460 {
    461    GX_CLIENT_STATE_T save;
    462    gx_priv_save(&save, res);
    463    
    464    VCOS_STATUS_T st = gx_priv_resource_fill(
    465       res, 
    466       x, res->height-y-height, 
    467       width, height, 
    468       fill_colour);
    469 
    470    gx_priv_restore(&save);
    471       
    472    return st == VCOS_SUCCESS ? 0 : -1;
    473 }
    474 
    475 /*****************************************************************************/
    476 int32_t graphics_resource_render_text_ext( GRAPHICS_RESOURCE_HANDLE res,
    477                                            const int32_t x,
    478                                            const int32_t y,
    479                                            const uint32_t width,
    480                                            const uint32_t height,
    481                                            const uint32_t fg_colour,
    482                                            const uint32_t bg_colour,
    483                                            const char *text,
    484                                            const uint32_t text_length,
    485                                            const uint32_t text_size )
    486 {
    487 
    488    /*
    489    * FIXME: Not at all optimal - re-renders each time.
    490    * FIXME: Not UTF-8 safe
    491    * FIXME: much better caching (or any caching)
    492    */
    493    VCOS_STATUS_T rc = gx_priv_render_text(
    494       &display, res, 
    495       x, res->height-y-text_size, width, height, fg_colour, bg_colour,
    496       text, text_length, text_size);
    497 
    498    return (rc == VCOS_SUCCESS) ? 0 : -1;
    499 }
    500 
    501 /*****************************************************************************/
    502 int32_t graphics_resource_render_text(  GRAPHICS_RESOURCE_HANDLE res,
    503                                         const int32_t x,
    504                                         const int32_t y,
    505                                         const uint32_t width, /* this can be GRAPHICS_RESOURCE_WIDTH for no clipping */
    506                                         const uint32_t height, /* this can be GRAPHICS_RESOURCE_HEIGHT for no clipping */
    507                                         const uint32_t fg_colour,
    508                                         const uint32_t bg_colour,
    509                                         const char *text,
    510                                         const uint32_t text_length)
    511 {
    512    return graphics_resource_render_text_ext(res, x, y, width, height,
    513                                             fg_colour, bg_colour,
    514                                             text, text_length,
    515                                             ATEXT_FONT_SIZE);
    516 }
    517 
    518 /*****************************************************************************/
    519 int32_t graphics_get_resource_size(
    520    const GRAPHICS_RESOURCE_HANDLE res,
    521    uint32_t *w,
    522    uint32_t *h)
    523 {
    524    if (w) *w = res->width;
    525    if (h) *h = res->height;
    526    return 0;
    527 }
    528 
    529 /*****************************************************************************/
    530 int32_t graphics_get_resource_type(const GRAPHICS_RESOURCE_HANDLE res, GRAPHICS_RESOURCE_TYPE_T *type)
    531 {
    532    if (type) *type = res->restype;
    533    return 0;
    534 }
    535 
    536 /*****************************************************************************/
    537 int32_t graphics_bitblt( const GRAPHICS_RESOURCE_HANDLE src,
    538                          const uint32_t x, // offset within source
    539                          const uint32_t y, // offset within source
    540                          const uint32_t width,
    541                          const uint32_t height,
    542                          GRAPHICS_RESOURCE_HANDLE dest,
    543                          const uint32_t x_pos,
    544                          const uint32_t y_pos )
    545 {
    546    int rc = -1;
    547    VGfloat old[9];
    548    uint32_t w, h;
    549    VGPaint paint = VG_INVALID_HANDLE;
    550    GX_CLIENT_STATE_T save;
    551    int is_child = 0;
    552    VGImage img = VG_INVALID_HANDLE;
    553 
    554    gx_priv_save(&save, dest);
    555 
    556    if (src->type != GX_PBUFFER)
    557    {
    558       vcos_assert(0);
    559       goto finish;
    560    }
    561 
    562    // create a child image that contains just the part wanted
    563    w = width == GRAPHICS_RESOURCE_WIDTH ? src->width : width;
    564    h = height == GRAPHICS_RESOURCE_HEIGHT ? src->height : height;
    565 
    566    if (x==0 && y==0 && 
    567        w == src->width &&
    568        h == src->height)
    569    {
    570       img = src->u.pixmap;
    571    }
    572    else
    573    {
    574       is_child = 1;
    575       img = vgChildImage(src->u.pixmap, x, y, w, h);
    576       if (img == VG_INVALID_HANDLE)
    577       {
    578          vcos_assert(0);
    579          goto finish;
    580       }
    581    }
    582 
    583    vcos_assert(vgGetError()==0);
    584 
    585    vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
    586    vgGetMatrix(old);
    587    vgLoadIdentity();
    588    vgTranslate((VGfloat)x_pos, (VGfloat)(dest->height-y_pos));
    589    vgScale(1.0, -1.0);
    590 
    591    // Do we have a translucency going on?
    592    if (src->alpha != 1.0)
    593    {
    594       VGfloat colour[4] = {1.0,1.0,1.0,src->alpha};
    595       paint = vgCreatePaint();
    596 
    597       vgSetParameterfv(paint, VG_PAINT_COLOR, 4, colour);
    598       vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_MULTIPLY);
    599       vgSetPaint(paint, VG_STROKE_PATH | VG_FILL_PATH);
    600    }
    601    vcos_assert(vgGetError()==0);
    602 
    603    vgDrawImage(img);
    604    vcos_assert(vgGetError()==0);
    605    vgLoadMatrix(old);
    606 
    607    int err = vgGetError();
    608 
    609    if (err)
    610    {
    611       GX_LOG("vg error %x blitting area", err);
    612       vcos_assert(0);
    613       rc = -1;
    614    }
    615    else
    616    {
    617       rc = 0;
    618    }
    619 finish:
    620    if (paint != VG_INVALID_HANDLE)
    621       vgDestroyPaint(paint);
    622 
    623    if (is_child)
    624       vgDestroyImage(img);
    625 
    626    gx_priv_restore(&save);
    627    return rc;
    628 }
    629 
    630 void gx_priv_flush(GRAPHICS_RESOURCE_HANDLE res)
    631 {
    632    EGLBoolean result;
    633    result = eglSwapBuffers(display.disp, res->surface);
    634    vcos_assert(result);
    635 }
    636 
    637 
    638 /** Map a colour, which the client will have supplied in RGB888.
    639  */
    640 
    641 void gx_priv_colour_to_paint(uint32_t col, VGfloat *rgba)
    642 {
    643    // with OpenVG we use RGB order.
    644    rgba[0] = ((VGfloat)((col & R_888_MASK) >> 16 )) / 0xff;
    645    rgba[1] = ((VGfloat)((col & G_888_MASK) >> 8 )) / 0xff;
    646    rgba[2] = ((VGfloat)((col & B_888_MASK) >> 0 )) / 0xff;
    647    rgba[3] = ((VGfloat)((col & ALPHA_888_MASK) >> 24)) / 0xff;
    648 }
    649 
    650 /** Fill an area of a surface with a fixed colour.
    651   */
    652 VCOS_STATUS_T gx_priv_resource_fill(GRAPHICS_RESOURCE_HANDLE res,
    653                                uint32_t x,
    654                                uint32_t y,
    655                                uint32_t width,
    656                                uint32_t height,
    657                                uint32_t fill_colour )
    658 {
    659    VGfloat vg_clear_colour[4];
    660 
    661    gx_priv_colour_to_paint(fill_colour, vg_clear_colour);
    662    vgSeti(VG_SCISSORING, VG_FALSE);
    663 
    664    vgSetfv(VG_CLEAR_COLOR, 4, vg_clear_colour);
    665    vgClear(x, y, width, height);
    666 
    667    int err = vgGetError();
    668    if (err)
    669    {
    670       GX_LOG("vg error %x filling area", err);
    671       vcos_assert(0);
    672    }
    673 
    674    return VCOS_SUCCESS;
    675 }
    676 
    677 VCOS_STATUS_T gx_priv_get_pixels(const GRAPHICS_RESOURCE_HANDLE res, void **p_pixels, GX_RASTER_ORDER_T raster_order)
    678 {
    679    VCOS_STATUS_T status = VCOS_SUCCESS;
    680    void *pixels, *dest;
    681    uint32_t width, height;
    682    int data_size, pitch;
    683    VGImageFormat image_format;
    684  
    685    if (!p_pixels)
    686    {
    687       status = VCOS_EINVAL;
    688       goto finish;
    689    }
    690 
    691    GX_TRACE("%s: res %p", __FUNCTION__, res);
    692 
    693    graphics_get_resource_size(res, &width, &height);
    694 
    695    /* FIXME: implement e.g. gx_get_pitch */
    696    switch (res->restype)
    697    {
    698       case GRAPHICS_RESOURCE_RGB565:
    699          pitch = ((width + 31)&(~31)) << 1;
    700          break;
    701       case GRAPHICS_RESOURCE_RGB888:
    702       case GRAPHICS_RESOURCE_RGBA32:
    703          pitch = ((width + 31)&(~31)) << 2;
    704          break;
    705       default:
    706       {
    707          GX_LOG("Unsupported pixel format");
    708          status = VCOS_EINVAL;
    709          goto finish;
    710       }
    711    }
    712    
    713    data_size = pitch * height;
    714 
    715    /* NB: vgReadPixels requires that the data pointer is aligned, but does not 
    716       require the stride to be aligned. Most implementations probably will 
    717       require that as well though... */
    718    pixels = vcos_malloc(data_size, "gx_get_pixels data");
    719    if (!pixels)
    720    {
    721       GX_LOG("Could not allocate %d bytes for vgReadPixels", data_size);
    722       status = VCOS_ENOMEM;
    723       goto finish;
    724    }
    725    /* FIXME: introduce e.g. GX_COLOR_FORMAT and mapping to VGImageFormat... */
    726 
    727    /* Hand out image data formatted to match OpenGL RGBA format.
    728     */
    729    switch (res->restype)
    730    {
    731       case GRAPHICS_RESOURCE_RGB565:
    732          image_format = VG_sBGR_565;
    733          break;
    734       case GRAPHICS_RESOURCE_RGB888:
    735          image_format = VG_sXBGR_8888;
    736          break;
    737       case GRAPHICS_RESOURCE_RGBA32:
    738          image_format = VG_sABGR_8888;
    739          break;
    740       default:
    741       {
    742          GX_LOG("Unsupported pixel format");
    743          status = VCOS_EINVAL;
    744          goto finish;
    745       }
    746    }   
    747 
    748    /* VG raster order is bottom-to-top */
    749    if (raster_order == GX_TOP_BOTTOM)
    750    {
    751       dest = ((uint8_t*)pixels)+(pitch*(height-1));
    752       pitch = -pitch;
    753    }
    754    else
    755    {
    756       dest = pixels;
    757    }
    758 
    759    vgReadPixels(dest, pitch, image_format, 0, 0, width, height);
    760 
    761    vcos_assert(vgGetError() == 0);
    762    
    763    *p_pixels = pixels;
    764 
    765 finish:
    766    return status;
    767 }
    768 
    769 static VCOS_STATUS_T convert_image_type(GRAPHICS_RESOURCE_TYPE_T image_type,
    770                                         VGImageFormat *vg_image_type,
    771                                         int *pbytes_per_pixel)
    772 {
    773    int bytes_per_pixel;
    774 
    775    switch (image_type)
    776    {
    777    case GRAPHICS_RESOURCE_RGB565:
    778       *vg_image_type = VG_sRGB_565;
    779       bytes_per_pixel = 2;
    780       break;
    781    case GRAPHICS_RESOURCE_RGB888:
    782       *vg_image_type = VG_sRGBX_8888;
    783       bytes_per_pixel = 3; // 24 bpp
    784       break;
    785    case GRAPHICS_RESOURCE_RGBA32:
    786       *vg_image_type = VG_sARGB_8888;
    787       bytes_per_pixel = 4;
    788       break;
    789    default:
    790       vcos_assert(0);
    791       *vg_image_type = 0;
    792       return VCOS_EINVAL;
    793    }
    794    if (pbytes_per_pixel)
    795       *pbytes_per_pixel = bytes_per_pixel;
    796 
    797    return VCOS_SUCCESS;
    798 }
    799 
    800 
    801 /*****************************************************************************/
    802 VCOS_STATUS_T gx_create_pbuffer( uint32_t width,
    803                                 uint32_t height,
    804                                 GRAPHICS_RESOURCE_TYPE_T image_type,
    805                                 GRAPHICS_RESOURCE_HANDLE *resource_handle )
    806 {
    807    VCOS_STATUS_T status = VCOS_SUCCESS;
    808    GRAPHICS_RESOURCE_HANDLE h;
    809    VGImage image;
    810    VGImageFormat vg_image_type;
    811    GX_CLIENT_STATE_T save;
    812 
    813    h = vcos_calloc(1,sizeof(*h), "graphics_x_resource");
    814    if (!h)
    815    {
    816       GX_LOG("%s: no memory for resource", __FUNCTION__);
    817       return VCOS_ENOMEM;
    818    }
    819 
    820    status = convert_image_type(image_type, &vg_image_type, NULL);
    821    if (status != VCOS_SUCCESS)
    822    {
    823       vcos_free(h);
    824       return status;
    825    }
    826 
    827    h->magic     = RES_MAGIC;
    828    h->context   = gx_contexts[image_type];
    829    h->config    = gx_configs[image_type];
    830    h->alpha     = 1.0;
    831    h->type      = GX_PBUFFER;
    832    h->width     = width;
    833    h->height    = height;
    834    h->restype   = image_type;
    835 
    836    GX_TRACE("Creating pbuffer surface");
    837 
    838    EGLint attribs[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE};
    839    h->surface = eglCreatePbufferSurface(display.disp, h->config, 
    840                                         attribs);
    841    if (!h->surface)
    842    {
    843       GX_LOG("Could not create EGL pbuffer surface: 0x%x", eglGetError());
    844       vcos_free(h);
    845       return VCOS_EINVAL;
    846    }
    847 
    848    gx_priv_save(&save, h);
    849 
    850    image = vgCreateImage(vg_image_type, width, height, VG_IMAGE_QUALITY_BETTER);
    851    if (image == VG_INVALID_HANDLE)
    852    {
    853       GX_LOG("Could not create vg image type %d: vg error 0x%x",
    854              vg_image_type, vgGetError());
    855       eglDestroySurface(display.disp, h->surface);
    856       vcos_free(h);
    857       status = VCOS_ENOMEM;
    858       goto finish;
    859    }
    860  
    861    h->u.pixmap  = image;
    862  
    863    // fill it with black
    864    status = gx_priv_resource_fill(h, 0, 0, width, height, GRAPHICS_RGBA32(0,0,0,0xff));
    865    vcos_assert(status == VCOS_SUCCESS);
    866 
    867    *resource_handle = h;
    868 finish:
    869    gx_priv_restore(&save);
    870    return status;
    871 }
    872 
    873 /*****************************************************************************/
    874 GX_PAINT_T *gx_create_gradient(GRAPHICS_RESOURCE_HANDLE res,
    875                                uint32_t start_colour,
    876                                uint32_t end_colour)
    877 {
    878    // holds  the two colour stops (offset,r,g,b,a).
    879    VGfloat fill_stops[10];
    880    GX_CLIENT_STATE_T save;
    881    VGPaint paint = VG_INVALID_HANDLE;
    882 
    883    gx_priv_save(&save, res);
    884 
    885    paint = vgCreatePaint();
    886    if (!paint)
    887    {
    888       gx_priv_restore(&save);
    889       vcos_log("Could not create paint: vg %d\n", vgGetError());
    890       vcos_assert(0);
    891       goto finish;
    892    }
    893 
    894    fill_stops[0] = 0.0;
    895    gx_priv_colour_to_paint(start_colour, fill_stops+1);
    896 
    897    fill_stops[5] = 1.0;
    898    gx_priv_colour_to_paint(end_colour, fill_stops+6);
    899 
    900    vgSetParameteri(paint, VG_PAINT_TYPE, VG_PAINT_TYPE_LINEAR_GRADIENT);
    901    vgSetParameterfv(paint, VG_PAINT_COLOR_RAMP_STOPS, 5*2, fill_stops);
    902 
    903 finish:
    904    gx_priv_restore(&save);
    905    return (GX_PAINT_T*)paint;
    906 }
    907 
    908 /*****************************************************************************/
    909 void gx_destroy_paint(GRAPHICS_RESOURCE_HANDLE res, GX_PAINT_T *p)
    910 {
    911    GX_CLIENT_STATE_T save;
    912    VGPaint paint = (VGPaint)p;
    913    gx_priv_save(&save, res);
    914    vgDestroyPaint(paint);
    915    gx_priv_restore(&save);
    916 }
    917 
    918 /*****************************************************************************/
    919 VCOS_STATUS_T gx_fill_gradient(GRAPHICS_RESOURCE_HANDLE dest,
    920                                uint32_t x, uint32_t y,
    921                                uint32_t width, uint32_t height,
    922                                uint32_t radius,
    923                                GX_PAINT_T *p)
    924 {
    925    /* Define start and end points of gradient, see OpenVG specification, 
    926       section 9.3.3. */
    927    VGfloat gradient[4] = {0.0, 0.0, 0.0, 0.0};
    928    VGPaint paint = (VGPaint)p;
    929    VGPath path;
    930    GX_CLIENT_STATE_T save;
    931    VCOS_STATUS_T status = VCOS_SUCCESS;
    932 
    933    if (!paint)
    934       return VCOS_EINVAL;
    935 
    936    gx_priv_save(&save, dest);
    937 
    938    if (width == GRAPHICS_RESOURCE_WIDTH)
    939       width = dest->width;
    940 
    941    if (height == GRAPHICS_RESOURCE_HEIGHT)
    942       height = dest->height;
    943 
    944    gradient[2] = width;
    945 
    946    vgSetParameterfv(paint, VG_PAINT_LINEAR_GRADIENT, 4, gradient);
    947    vgSetPaint(paint, VG_FILL_PATH);
    948 
    949    path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_S_32,
    950                        1.0, 0.0, 8, 8, VG_PATH_CAPABILITY_ALL);
    951    if (!path)
    952    {
    953       status = VCOS_ENOMEM;
    954       goto finish;
    955    }
    956 
    957    vguRoundRect(path, (VGfloat)x, (VGfloat)y, (VGfloat)width, (VGfloat)height,
    958                 (VGfloat)radius, (VGfloat)radius);
    959    vgDrawPath(path, VG_FILL_PATH);
    960    vgDestroyPath(path);
    961 
    962    vcos_assert(vgGetError() == 0);
    963    
    964 finish:
    965    gx_priv_restore(&save);
    966 
    967    return status;
    968 }
    969 
    970 /*****************************************************************************/
    971 VCOS_STATUS_T gx_graphics_init(const char *font_dir)
    972 {
    973    GX_CLIENT_STATE_T save;
    974    VCOS_STATUS_T rc;
    975    
    976    gx_priv_save(&save, NULL);
    977    
    978    rc = gx_priv_initialise();
    979    if (rc == VCOS_SUCCESS)
    980       rc = gx_priv_font_init(font_dir);
    981 
    982    gx_priv_restore(&save);
    983    
    984    return rc;
    985 }
    986 
    987 /*****************************************************************************/
    988 int gx_is_double_buffered(void)
    989 {
    990    return 1;
    991 }
    992 
    993 /*****************************************************************************/
    994 int32_t graphics_userblt(GRAPHICS_RESOURCE_TYPE_T src_type,
    995                          const void *src_data,
    996                          const uint32_t src_x,
    997                          const uint32_t src_y,
    998                          const uint32_t width,
    999                          const uint32_t height,
   1000                          const uint32_t pitch,
   1001                          GRAPHICS_RESOURCE_HANDLE dest,
   1002                          const uint32_t x_pos,
   1003                          const uint32_t y_pos )
   1004 {
   1005    VCOS_STATUS_T status;
   1006    VGImageFormat vg_src_type;
   1007    int bytes_per_pixel;
   1008    GX_CLIENT_STATE_T save;
   1009    
   1010    status = convert_image_type(src_type, &vg_src_type, &bytes_per_pixel);
   1011    if (status != VCOS_SUCCESS)
   1012       return status;
   1013 
   1014    gx_priv_save(&save, dest);
   1015 
   1016    if (dest->type == GX_PBUFFER)
   1017    {
   1018       vgImageSubData(dest->u.pixmap,
   1019                      src_data,
   1020                      pitch,
   1021                      vg_src_type,
   1022                      x_pos, y_pos, width, height);
   1023    }
   1024    else if (dest->type == GX_WINDOW)
   1025    {
   1026       // need to invert this as VG thinks zero is at the bottom
   1027       // while graphics_x thinks it is at the top.
   1028       vgWritePixels((uint8_t*)src_data + pitch*(height-1),
   1029                     -pitch,
   1030                     vg_src_type,
   1031                     x_pos, dest->height-y_pos-height, width, height);
   1032    }
   1033    else
   1034    {
   1035       vcos_assert(0);
   1036    }
   1037 
   1038    if (vgGetError() == 0)
   1039       status = VCOS_SUCCESS;
   1040    else
   1041    {
   1042       vcos_assert(0);
   1043       status = VCOS_EINVAL;
   1044    }
   1045 
   1046    gx_priv_restore(&save);
   1047    return status;
   1048 }
   1049 
   1050 /*****************************************************************************/
   1051 int32_t graphics_resource_text_dimensions( GRAPHICS_RESOURCE_HANDLE resource_handle,
   1052                                            const char *text,
   1053                                            const uint32_t text_length,
   1054                                            uint32_t *width,
   1055                                            uint32_t *height )
   1056 {
   1057    return graphics_resource_text_dimensions_ext(resource_handle, text, text_length, width, height, ATEXT_FONT_SIZE);
   1058 }
   1059 
   1060 /*****************************************************************************/
   1061 VCOS_STATUS_T gx_render_arrowhead(GRAPHICS_RESOURCE_HANDLE res,
   1062                                   uint32_t tip_x, uint32_t tip_y, 
   1063                                   int32_t w, int32_t h,
   1064                                   GX_PAINT_T *p)
   1065 {
   1066    VGfloat gradient[4];
   1067    VGPaint paint = (VGPaint)p;
   1068    VGPath path;
   1069    VCOS_STATUS_T status = VCOS_SUCCESS;
   1070 
   1071    GX_CLIENT_STATE_T save;
   1072    gx_priv_save(&save, res);
   1073 
   1074    if (!paint)
   1075    {
   1076       vcos_assert(0);
   1077       status = VCOS_EINVAL;
   1078       goto finish;
   1079    }
   1080 
   1081    gradient[0] = 0.0; gradient[1] = 0.0;
   1082    gradient[2] = w; gradient[2] = 0.0;
   1083 
   1084    vgSetParameterfv(paint, VG_PAINT_LINEAR_GRADIENT, 4, gradient);
   1085    vgSetPaint(paint, VG_FILL_PATH);
   1086 
   1087    path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_S_32,
   1088                        1.0, 0.0, 8, 8, VG_PATH_CAPABILITY_ALL);
   1089    if (!path)
   1090    {
   1091       status = VCOS_ENOMEM;
   1092       goto finish;
   1093    }
   1094    VGfloat points[] = {
   1095       (VGfloat)tip_x, (VGfloat)tip_y,
   1096       (VGfloat)tip_x + w, (VGfloat)tip_y + h/2,
   1097       (VGfloat)tip_x + w, (VGfloat)tip_y - h/2,
   1098    };
   1099 
   1100    vguPolygon(path, points, 3, 1);
   1101 
   1102    vgDrawPath(path, VG_FILL_PATH);
   1103    vgDestroyPath(path);
   1104 
   1105    vcos_assert(vgGetError()==0);
   1106 
   1107 finish:
   1108    gx_priv_restore(&save);
   1109    return status;
   1110 }
   1111 
   1112 /*****************************************************************************/
   1113 int32_t gx_apply_alpha( GRAPHICS_RESOURCE_HANDLE resource_handle,
   1114                         const uint8_t alpha )
   1115 {
   1116    vcos_assert(resource_handle);
   1117    if (resource_handle->type != GX_PBUFFER)
   1118    {
   1119       vcos_assert(0);
   1120       return -1;
   1121    }
   1122    resource_handle->alpha = 1.0*alpha/255;
   1123    return 0;
   1124 }
   1125 
   1126 /*****************************************************************************/
   1127 int32_t graphics_resource_set_alpha_per_colour( GRAPHICS_RESOURCE_HANDLE res,
   1128                                                 const uint32_t colour,
   1129                                                 const uint8_t alpha )
   1130 {
   1131    GX_ERROR("Not implemented yet!");
   1132    return 0;
   1133 }
   1134 
   1135 /*****************************************************************************/
   1136 VCOS_STATUS_T gx_get_pixels(const GRAPHICS_RESOURCE_HANDLE res, void **pixels)
   1137 {
   1138    VCOS_STATUS_T status = VCOS_SUCCESS;
   1139    GX_CLIENT_STATE_T save;
   1140    gx_priv_save(&save, res);
   1141    
   1142    /* Default to top-top-bottom raster scan order */
   1143    status = gx_priv_get_pixels(res, pixels, GX_TOP_BOTTOM);
   1144    
   1145    gx_priv_restore(&save);
   1146    return status;
   1147 }
   1148 
   1149 /*****************************************************************************/
   1150 VCOS_STATUS_T gx_get_pixels_in_raster_order(const GRAPHICS_RESOURCE_HANDLE res, 
   1151                                             void **pixels, 
   1152                                             GX_RASTER_ORDER_T raster_order)
   1153 {
   1154    VCOS_STATUS_T status = VCOS_SUCCESS;
   1155    GX_CLIENT_STATE_T save;
   1156    gx_priv_save(&save, res);
   1157    
   1158    status = gx_priv_get_pixels(res, pixels, raster_order);
   1159    
   1160    gx_priv_restore(&save);
   1161    return status;
   1162 }
   1163 
   1164 /*****************************************************************************/
   1165 void gx_free_pixels(const GRAPHICS_RESOURCE_HANDLE res, void *pixels)
   1166 {
   1167    vcos_free(pixels);
   1168 }
   1169 
   1170 VCOS_STATUS_T gx_bind_vg( GX_CLIENT_STATE_T *save, GRAPHICS_RESOURCE_HANDLE res )
   1171 {
   1172    gx_priv_save(save, res);
   1173    vcos_assert(vgGetError()==0);
   1174    return VCOS_SUCCESS;
   1175 }
   1176 
   1177 /** Unbind VG */
   1178 void gx_unbind_vg(GX_CLIENT_STATE_T *restore)
   1179 {
   1180    gx_priv_restore(restore);
   1181 }
   1182 
   1183 
   1184 GX_CLIENT_STATE_T *gx_alloc_context(void)
   1185 {
   1186    GX_CLIENT_STATE_T *ret = vcos_calloc(1,sizeof(*ret), "gx_client_state");
   1187    return ret;
   1188 }
   1189 
   1190 void gx_free_context(GX_CLIENT_STATE_T *state)
   1191 {
   1192    vcos_free(state);
   1193 }
   1194 
   1195 void gx_convert_colour(uint32_t colour, float *dest)
   1196 {
   1197    gx_priv_colour_to_paint(colour, dest);
   1198 }
   1199 
   1200 
   1201 #define MAX_DISPLAY_HANDLES  4
   1202 
   1203 #define CHANGE_LAYER    (1<<0)
   1204 #define CHANGE_OPACITY  (1<<1)
   1205 #define CHANGE_DEST     (1<<2)
   1206 #define CHANGE_SRC      (1<<3)
   1207 #define CHANGE_MASK     (1<<4)
   1208 #define CHANGE_XFORM    (1<<5)
   1209 
   1210 typedef struct
   1211 {
   1212    /** Keep a display handle going for each connected screen (LCD, HDMI). */
   1213    DISPMANX_DISPLAY_HANDLE_T screens[MAX_DISPLAY_HANDLES];
   1214    int refcounts[MAX_DISPLAY_HANDLES];
   1215 
   1216    //a flag to count the number of dispman starts that have been invoked
   1217 
   1218    uint32_t dispman_start_count;
   1219    // maintain the single global handle to the update in progress
   1220    DISPMANX_UPDATE_HANDLE_T current_update;
   1221 
   1222    VCOS_MUTEX_T lock;
   1223 } gx_priv_state_t;
   1224 
   1225 static gx_priv_state_t gx;
   1226 
   1227 void gx_priv_init(void)
   1228 {
   1229    vcos_mutex_create(&gx.lock,NULL);
   1230 }
   1231 
   1232 void gx_priv_destroy(void)
   1233 {
   1234    vcos_mutex_delete(&gx.lock);
   1235 }
   1236 
   1237 
   1238 static
   1239 int32_t gx_priv_open_screen(uint32_t index, DISPMANX_DISPLAY_HANDLE_T *pscreen)
   1240 {
   1241    int ret = -1;
   1242    vcos_mutex_lock(&gx.lock);
   1243 
   1244    if (gx.refcounts[index] != 0)
   1245    {
   1246       *pscreen = gx.screens[index];
   1247       gx.refcounts[index]++;
   1248       ret = 0;
   1249    }
   1250    else
   1251    {
   1252       DISPMANX_DISPLAY_HANDLE_T h = vc_dispmanx_display_open(index);
   1253       if (h == DISPMANX_NO_HANDLE)
   1254       {
   1255          GX_LOG("Could not open dispmanx display %d", index);
   1256          ret = -1;
   1257          goto finish;
   1258       }
   1259       gx.screens[index] = h;
   1260       gx.refcounts[index] = 1;
   1261       *pscreen = h;
   1262       ret = 0;
   1263    }
   1264 finish:
   1265    vcos_mutex_unlock(&gx.lock);
   1266    return ret;
   1267 }
   1268 
   1269 static
   1270 int32_t gx_priv_release_screen(uint32_t index)
   1271 {
   1272    vcos_mutex_lock(&gx.lock);
   1273    gx.refcounts[index]--;
   1274    if (gx.refcounts[index] == 0)
   1275    {
   1276       vc_dispmanx_display_close(gx.screens[index]);
   1277       gx.screens[index] = DISPMANX_NO_HANDLE;
   1278    }
   1279    vcos_mutex_unlock(&gx.lock);
   1280    return 0;
   1281 }
   1282 
   1283 
   1284 
   1285 
   1286 int gx_priv_create_native_window(uint32_t screen_id,
   1287                                  uint32_t w, uint32_t h,
   1288                                  GRAPHICS_RESOURCE_TYPE_T type,
   1289                                  GX_NATIVE_WINDOW_T *win,
   1290                                  void **cookie)
   1291 {
   1292    int rc;
   1293    DISPMANX_DISPLAY_HANDLE_T dispmanx_display;
   1294    VC_RECT_T dst_rect;
   1295    VC_RECT_T src_rect;
   1296    DISPMANX_UPDATE_HANDLE_T current_update;
   1297    *cookie = NULL;
   1298 
   1299    rc = gx_priv_open_screen(screen_id, &dispmanx_display);
   1300    if (rc < 0)
   1301    {
   1302       GX_LOG("Could not open display %d", screen_id);
   1303       goto fail_screen;
   1304    }
   1305 
   1306    current_update = vc_dispmanx_update_start(0);
   1307    if (!current_update)
   1308    {
   1309       GX_LOG("Could not start update on screen %d", screen_id);
   1310       goto fail_update;
   1311    }
   1312    
   1313    src_rect.x = src_rect.y = 0;
   1314    src_rect.width = w << 16;
   1315    src_rect.height = h << 16;
   1316 
   1317    dst_rect.x = dst_rect.y = 0;
   1318    dst_rect.width = dst_rect.height = 1;
   1319    
   1320    win->egl_win.width = w;
   1321    win->egl_win.height = h;
   1322    VC_DISPMANX_ALPHA_T alpha;
   1323    memset(&alpha, 0x0, sizeof(VC_DISPMANX_ALPHA_T));
   1324    alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE;
   1325 
   1326    DISPMANX_CLAMP_T clamp;
   1327    memset(&clamp, 0x0, sizeof(DISPMANX_CLAMP_T));
   1328 
   1329    win->egl_win.element = vc_dispmanx_element_add(current_update, dispmanx_display,
   1330       0 /* layer */, &dst_rect, 
   1331       0 /* src */, &src_rect, 
   1332       DISPMANX_PROTECTION_NONE,
   1333       &alpha /* alpha */,
   1334       &clamp /* clamp */,
   1335       0 /* transform */);
   1336 
   1337    if ( !win->egl_win.element )
   1338    {
   1339       GX_LOG("Could not add element %dx%d",w,h);
   1340       vc_dispmanx_update_submit_sync(current_update);
   1341       rc = -1;
   1342    }
   1343 
   1344    // have to pass back the update so it can be completed *After* the
   1345    // window has been initialised (filled with background colour).
   1346    *cookie = (void*)current_update;
   1347 
   1348    return 0;
   1349 
   1350 fail_update:
   1351    gx_priv_release_screen(screen_id);
   1352 fail_screen:
   1353    return rc;
   1354 }
   1355 
   1356 void gx_priv_finish_native_window(GRAPHICS_RESOURCE_HANDLE_TABLE_T *res,
   1357                                   void *current_update)
   1358 {
   1359    vc_dispmanx_update_submit_sync((DISPMANX_UPDATE_HANDLE_T)current_update);
   1360 }
   1361 
   1362 void
   1363 gx_priv_destroy_native_window(GRAPHICS_RESOURCE_HANDLE_TABLE_T *res)
   1364 {
   1365    DISPMANX_UPDATE_HANDLE_T current_update;
   1366 
   1367    if((current_update = vc_dispmanx_update_start(0)) != 0)
   1368    {
   1369       int ret = vc_dispmanx_element_remove(current_update, res->u.native_window.egl_win.element);
   1370       vcos_assert(ret == 0);
   1371       ret = vc_dispmanx_update_submit_sync(current_update);
   1372       vcos_assert(ret == 0);
   1373    }
   1374 
   1375    gx_priv_release_screen(res->screen_id);
   1376 }
   1377 
   1378 
   1379 /***********************************************************
   1380  * Name: graphics_get_display_size
   1381  *
   1382  * Arguments:
   1383  *       void
   1384  *
   1385  * Description: Return size of display
   1386  *
   1387  * Returns: int32_t:
   1388  *               >=0 if it succeeded
   1389  *
   1390  ***********************************************************/
   1391 int32_t graphics_get_display_size( const uint16_t display_number,
   1392                                    uint32_t *width,
   1393                                    uint32_t *height)
   1394 {
   1395    DISPMANX_MODEINFO_T mode_info;
   1396    int32_t success = -1;
   1397    DISPMANX_DISPLAY_HANDLE_T disp;
   1398    vcos_assert(width && height);
   1399    *width = *height = 0;
   1400 
   1401    if(vcos_verify(display_number < MAX_DISPLAY_HANDLES))
   1402    {
   1403       // TODO Shouldn't this close the display if it wasn't previously open?
   1404       if (gx_priv_open_screen(display_number, &disp) < 0)
   1405       {
   1406          vcos_assert(0);
   1407          return -1;
   1408       }
   1409       success = vc_dispmanx_display_get_info(disp, &mode_info);
   1410 
   1411       if( success >= 0 )
   1412       {
   1413          *width = mode_info.width;
   1414          *height = mode_info.height;
   1415          vcos_assert(*height > 64);
   1416       }
   1417       else
   1418       {
   1419          vcos_assert(0);
   1420       }
   1421    }
   1422 
   1423    return success;
   1424 }
   1425 
   1426 static inline uint16_t auto_size(uint16_t arg, uint16_t actual_size)
   1427 {
   1428    return arg == GRAPHICS_RESOURCE_WIDTH ? actual_size : arg;
   1429 }
   1430 
   1431 int32_t graphics_display_resource( GRAPHICS_RESOURCE_HANDLE res,
   1432                                    const uint16_t screen_number,
   1433                                    const int16_t z_order,
   1434                                    const uint16_t offset_x,
   1435                                    const uint16_t offset_y,
   1436                                    const uint16_t dest_width,
   1437                                    const uint16_t dest_height,
   1438                                    const VC_DISPMAN_TRANSFORM_T transform,
   1439                                    const uint8_t display )
   1440 {
   1441    DISPMANX_UPDATE_HANDLE_T update;
   1442    int32_t rc;
   1443    int xform_changed;
   1444 
   1445    if (!res)
   1446    {
   1447       // mimics old behaviour.
   1448       (void)vcos_verify(0);
   1449       return 0;
   1450    }
   1451    vcos_assert(res->magic == RES_MAGIC);
   1452 
   1453    xform_changed = transform != res->transform;
   1454    res->transform = transform;
   1455 
   1456    rc = graphics_update_start();
   1457    update = gx.current_update;
   1458    vcos_assert(rc == 0);
   1459 
   1460    if (display)
   1461    {
   1462       VC_RECT_T src_rect, dest_rect;
   1463 
   1464       int32_t src_width = res->width;
   1465       int32_t src_height = res->height;
   1466 
   1467       uint32_t change_flags = CHANGE_LAYER;
   1468 
   1469       // has the destination position changed?
   1470       uint32_t w = auto_size(dest_width, res->width);
   1471       uint32_t h = auto_size(dest_height, res->height);
   1472 
   1473       vcos_assert(screen_number == res->screen_id);
   1474 
   1475       if (gx.screens[screen_number] == 0)
   1476       {
   1477          vcos_assert(0);
   1478          DISPMANX_DISPLAY_HANDLE_T display_handle;
   1479          gx_priv_open_screen(screen_number, &display_handle);
   1480       }
   1481 
   1482       if ((offset_x != res->dest.x) ||
   1483           (offset_y != res->dest.y) ||
   1484           (h != res->dest.height) ||
   1485           (w != res->dest.width))
   1486       {
   1487          change_flags |= CHANGE_DEST;
   1488          res->dest.x      = offset_x;
   1489          res->dest.y      = offset_y;
   1490          res->dest.height = h;
   1491          res->dest.width  = w;
   1492       }
   1493 
   1494       if (xform_changed)
   1495          change_flags |= CHANGE_XFORM;
   1496 
   1497       vc_dispmanx_rect_set( &src_rect, 0, 0, ((uint32_t)src_width)<<16, ((uint32_t)src_height)<<16 );
   1498       vc_dispmanx_rect_set( &dest_rect, offset_x, offset_y, w, h);
   1499 
   1500       rc = vc_dispmanx_element_change_attributes(update,
   1501          res->u.native_window.egl_win.element,
   1502          change_flags,
   1503          z_order, /* layer */
   1504          0xff, /* opacity */
   1505          &dest_rect,
   1506          &src_rect,
   1507          0, transform);
   1508 
   1509       vcos_assert(rc==0);
   1510       gx_priv_flush(res);
   1511 
   1512    }
   1513    else
   1514    {
   1515       vgFinish();
   1516       eglWaitClient();
   1517       rc = vc_dispmanx_element_change_source(update, res->u.native_window.egl_win.element, 0);
   1518       vcos_assert(rc==0);
   1519    }
   1520 
   1521    rc = graphics_update_end();
   1522    vcos_assert(rc==0);
   1523 
   1524    return rc;
   1525 }
   1526 
   1527 /***********************************************************
   1528  * Name: graphics_update_start
   1529  *
   1530  * Arguments:
   1531  *       void
   1532  *
   1533  * Description: Starts an update UNLESS and update is already in progress
   1534  *
   1535  * Returns: int32_t:
   1536  *               >=0 if it succeeded
   1537  *
   1538  ***********************************************************/
   1539 int32_t graphics_update_start(void)
   1540 {
   1541    int32_t success = 0;
   1542 
   1543    //check we are not already in an update
   1544    if ( 0 == gx.dispman_start_count )
   1545    {
   1546       gx.current_update = vc_dispmanx_update_start( 10 );
   1547       if( gx.current_update == DISPMANX_NO_HANDLE )
   1548       {
   1549          //error
   1550          success = -1;
   1551          vc_assert( 0 );
   1552       }
   1553    }
   1554 
   1555    if( success == 0 )
   1556    {
   1557       //inc the counter
   1558       gx.dispman_start_count++;
   1559    }
   1560 
   1561    return success;
   1562 }
   1563 
   1564 
   1565 /***********************************************************
   1566  * Name: graphics_update_end
   1567  *
   1568  * Arguments:
   1569  *       void
   1570  *
   1571  * Description: Ends an update UNLESS more than one update is in progress
   1572  *
   1573  * Returns: int32_t:
   1574  *               >=0 if it succeeded
   1575  *
   1576  ***********************************************************/
   1577 int32_t graphics_update_end( void )
   1578 {
   1579    int32_t success = -1;
   1580 
   1581    // make sure you are checking the return value of graphics_update_start
   1582    if(vcos_verify(gx.current_update != DISPMANX_NO_HANDLE))
   1583    {
   1584       //check we are in an update
   1585       if(vcos_verify(gx.dispman_start_count > 0))
   1586       {
   1587          //dec the counter
   1588          gx.dispman_start_count--;
   1589 
   1590          success = 0;
   1591 
   1592          //is the counter now 0?
   1593          if( 0 == gx.dispman_start_count )
   1594          {
   1595             eglWaitClient();
   1596             if( vc_dispmanx_update_submit_sync( gx.current_update ) != 0 )
   1597             {
   1598                //error
   1599                success = -1;
   1600                vc_assert( 0 );
   1601             }
   1602          }
   1603       }
   1604    }
   1605 
   1606    return success;
   1607 }
   1608