/* * mui_drawable.c * * Copyright (C) 2023 Michel Pollet * * SPDX-License-Identifier: MIT */ #include #include #include #include "mui.h" #include "cg.h" IMPLEMENT_C_ARRAY(mui_clip_stack); // create a new mui_draware of size w x h, bpp depth. // optionally allocate the pixels if pixels is NULL mui_drawable_t * mui_drawable_init( mui_drawable_t * d, c2_pt_t size, uint8_t bpp, void * pixels, // if NULL, will allocate uint32_t row_bytes) { static const mui_drawable_t zero = {}; *d = zero; d->pix.bpp = bpp; d->pix.size = size; d->pix.row_bytes = row_bytes == 0 ? (uint32_t)((size.x * (bpp / 8)) + 3) & ~3 : row_bytes; d->pix.pixels = pixels ? pixels : malloc(d->pix.row_bytes * size.y); d->dispose_pixels = pixels == NULL; return d; } // create a new mui_draware of size w x h, bpp depth. // optionally allocate the pixels if pixels is NULL mui_drawable_t * mui_drawable_new( c2_pt_t size, uint8_t bpp, void * pixels, // if NULL, will allocate uint32_t row_bytes) { mui_drawable_t *d = malloc(sizeof(mui_drawable_t)); mui_drawable_init(d, size, bpp, pixels, row_bytes); d->dispose_drawable = 1; return d; } void mui_drawable_clear( mui_drawable_t * dr) { if (!dr) return; if (dr->cg) cg_destroy(dr->cg); dr->cg = NULL; if (dr->cg_surface) cg_surface_destroy(dr->cg_surface); dr->cg_surface = NULL; if (dr->pixman) pixman_image_unref(dr->pixman); dr->pixman = NULL; for (int i = 0; i < (int)dr->clip.count; i++) pixman_region32_fini(&dr->clip.e[i]); mui_clip_stack_clear(&dr->clip); if (dr->pix.pixels && dr->dispose_pixels) free(dr->pix.pixels); dr->_pix_hash = NULL; } void mui_drawable_dispose( mui_drawable_t * dr) { if (!dr) return; mui_drawable_clear(dr); mui_clip_stack_free(&dr->clip); if (dr->dispose_drawable) free(dr); } static struct cg_ctx_t * _cg_updated_clip( mui_drawable_t * dr) { if (!dr->cg_clip_dirty) return dr->cg; dr->cg_clip_dirty = 0; cg_reset_clip(dr->cg); if (!dr->clip.count) return dr->cg; int cnt = 0; pixman_box32_t *r = pixman_region32_rectangles( &dr->clip.e[dr->clip.count-1], &cnt); for (int i = 0; i < cnt; i++) { cg_rectangle(dr->cg, r[i].x1, r[i].y1, r[i].x2 - r[i].x1, r[i].y2 - r[i].y1); } cg_clip(dr->cg); return dr->cg; } struct cg_ctx_t * mui_drawable_get_cg( mui_drawable_t * dr) { if (!dr) return NULL; if (dr->cg) { // in case the pix structure had changed... dr->cg_surface->stride = dr->pix.row_bytes; dr->cg_surface->pixels = dr->pix.pixels; dr->cg_surface->width = dr->pix.size.x; dr->cg_surface->height = dr->pix.size.y; return _cg_updated_clip(dr); } dr->cg_surface = cg_surface_create_for_data( dr->pix.size.x, dr->pix.size.y, dr->pix.pixels); dr->cg_surface->stride = dr->pix.row_bytes; dr->cg = cg_create(dr->cg_surface); return _cg_updated_clip(dr); } /* * Update clip to the 'latest' one from the stack of clips. */ static union pixman_image * _pixman_updated_clip( mui_drawable_t * dr) { if (!dr->pixman_clip_dirty) return dr->pixman; dr->pixman_clip_dirty = 0; if (dr->clip.count) pixman_image_set_clip_region32( dr->pixman, &dr->clip.e[dr->clip.count-1]); else pixman_image_set_clip_region32( dr->pixman, NULL); return dr->pixman; } union pixman_image * mui_pixmap_make_pixman( mui_pixmap_t * pix) { pixman_format_code_t k = PIXMAN_a8r8g8b8; switch (pix->bpp) { case 8: k = PIXMAN_a8; break; case 16: k = PIXMAN_r5g6b5; break; case 24: k = PIXMAN_r8g8b8; break; case 32: k = PIXMAN_a8r8g8b8; break; } union pixman_image * res = pixman_image_create_bits_no_clear( k, pix->size.x, pix->size.y, (uint32_t*)pix->pixels, pix->row_bytes); return res; } union pixman_image * mui_drawable_get_pixman( mui_drawable_t * dr) { if (!dr) return NULL; void * _hash = dr->pix.pixels + dr->pix.size.y * dr->pix.row_bytes; if (dr->_pix_hash != _hash) { dr->_pix_hash = _hash; dr->_pix_hash = dr->pix.pixels + dr->pix.size.y * dr->pix.row_bytes; if (dr->pixman) pixman_image_unref(dr->pixman); dr->pixman = NULL; } // return _pixman_updated_clip(dr); if (dr->pixman) return _pixman_updated_clip(dr); dr->pixman = mui_pixmap_make_pixman(&dr->pix); dr->pixman_clip_dirty = 1; return _pixman_updated_clip(dr); } pixman_region32_t * mui_drawable_clip_get( mui_drawable_t * dr) { if (!dr || !dr->clip.count) return NULL; return &dr->clip.e[dr->clip.count-1]; } void mui_drawable_set_clip( mui_drawable_t * dr, c2_rect_array_p clip ) { if (!dr) return; for (int i = 0; i < (int)dr->clip.count; i++) pixman_region32_fini(&dr->clip.e[i]); mui_clip_stack_clear(&dr->clip); if (clip && clip->count) { pixman_region32_t r = {}; pixman_region32_init_rects(&r, (pixman_box32_t*)clip->e, clip->count); // intersect the whole lot with the whole pixmap boundary pixman_region32_t rg = {}; pixman_region32_intersect_rect(&rg, &r, 0, 0, dr->pix.size.x, dr->pix.size.y); pixman_region32_fini(&r); mui_clip_stack_add(&dr->clip, rg); } dr->pixman_clip_dirty = 1; dr->cg_clip_dirty = 1; } int mui_drawable_clip_intersects( mui_drawable_t * dr, c2_rect_p r ) { if (!dr || !r) return 0; if (!dr->clip.count) return 1; // conveniently, c2_rect_t is the same as pixman_box_32_t pixman_region_overlap_t o = pixman_region32_contains_rectangle( &dr->clip.e[dr->clip.count-1], (pixman_box32_t*)r); return o == PIXMAN_REGION_OUT ? 0 : o == PIXMAN_REGION_IN ? 1 : -1; } int mui_drawable_clip_push( mui_drawable_t * dr, c2_rect_p r ) { if (!dr || !r) return 0; pixman_region32_t rg = {}; if (dr->clip.count == 0) { pixman_region32_init_rect(&rg, r->l, r->t, c2_rect_width(r), c2_rect_height(r)); } else { pixman_region32_intersect_rect(&rg, &dr->clip.e[dr->clip.count-1], r->l, r->t, c2_rect_width(r), c2_rect_height(r)); } mui_clip_stack_add(&dr->clip, rg); dr->pixman_clip_dirty = 1; dr->cg_clip_dirty = 1; return dr->clip.count; } int mui_drawable_clip_push_region( mui_drawable_t * dr, pixman_region32_t * rgn ) { if (!dr || !rgn) return 0; pixman_region32_t rg = {}; if (dr->clip.count == 0) { pixman_region32_copy(&rg, rgn); } else { pixman_region32_intersect(&rg, &dr->clip.e[dr->clip.count-1], rgn); } mui_clip_stack_add(&dr->clip, rg); dr->pixman_clip_dirty = 1; dr->cg_clip_dirty = 1; return dr->clip.count; } int mui_drawable_clip_substract_region( mui_drawable_t * dr, pixman_region32_t * rgn ) { if (!dr || !rgn) return 0; pixman_region32_t rg = {}; if (dr->clip.count != 0) { pixman_region32_subtract(&rg, &dr->clip.e[dr->clip.count-1], rgn); } mui_clip_stack_add(&dr->clip, rg); dr->pixman_clip_dirty = 1; dr->cg_clip_dirty = 1; return dr->clip.count; } void mui_drawable_clip_pop( mui_drawable_t * dr ) { if (!dr || !dr->clip.count) return; pixman_region32_fini(&dr->clip.e[dr->clip.count-1]); dr->clip.count--; dr->pixman_clip_dirty = 1; dr->cg_clip_dirty = 1; }