diff options
Diffstat (limited to 'src/cairo-tg-surface.c')
-rwxr-xr-x | src/cairo-tg-surface.c | 1372 |
1 files changed, 1372 insertions, 0 deletions
diff --git a/src/cairo-tg-surface.c b/src/cairo-tg-surface.c new file mode 100755 index 000000000..c32799fa4 --- /dev/null +++ b/src/cairo-tg-surface.c @@ -0,0 +1,1372 @@ +/* + * Copyright © 2012 SCore Corporation + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * Author: Taekyun Kim (podain77@gmail.com) + */ + +#include "cairoint.h" +#include "cairo-surface-fallback-private.h" +#include "cairo-tg.h" +#include "cairo-tg-private.h" +#include "cairo-image-surface-inline.h" +#include "cairo-surface-subsurface-inline.h" +#include "cairo-compositor-private.h" +#include "cairo-clip-inline.h" +#include "cairo-recording-surface-inline.h" + +#if CAIRO_HAS_OPENMP +#include <omp.h> +#endif + +#define CAIRO_TG_THREAD_POOL_BUSY_WAIT +#define CAIRO_TG_NUM_MIN_ENTRIES_FOR_PARALLEL_FLUSH 2 + +static inline int +get_num_cpu_cores (void) +{ + static int num_cpu_cores = 0; + + if (num_cpu_cores == 0) + { +#if CAIRO_HAS_OPENMP + num_cpu_cores = omp_get_num_procs (); +#elif defined (__WIN32) + SYSTEM_INFO sysinfo; + + GetSystemInfo (&sysinfo); + num_cpu_cores = sysinfo.dwNumberOfProcessors; +#elif defined (__linux__) + cpu_set_t cs; + int i; + + CPU_ZERO (&cs); + + if (sched_getaffinity (0, sizeof (cs), &cs) != 0) + num_cpu_cores = 1; + + for (i = 0; i < 8; i++) + { + if (CPU_ISSET (i, &cs)) + num_cpu_cores++; + } +#else + num_cpu_cores = 1; +#endif + } + + return num_cpu_cores; +} + +static inline int +get_bpp_for_format (cairo_format_t format) +{ + switch (format) + { + case CAIRO_FORMAT_ARGB32: + case CAIRO_FORMAT_RGB24: + case CAIRO_FORMAT_RGB30: + return 32; + case CAIRO_FORMAT_RGB16_565: + return 16; + case CAIRO_FORMAT_A8: + return 8; + case CAIRO_FORMAT_A1: + return 1; + case CAIRO_FORMAT_INVALID: + default: + ASSERT_NOT_REACHED; + return 0; + } +} + +static inline cairo_bool_t +_cairo_surface_is_tg(const cairo_surface_t *surface) +{ + return surface->backend && surface->backend->type == CAIRO_SURFACE_TYPE_TG; +} + +static inline cairo_bool_t +_cairo_tg_surface_is_size_valid (int width, int height) +{ + if (width < 0 || height < 0) + return FALSE; + + /* TODO: Check for upper limit of surface size. */ + + return TRUE; +} + +static inline cairo_bool_t +_cairo_pattern_is_self_copy (cairo_surface_t *surface, + const cairo_pattern_t *pattern) +{ + if (unlikely (surface == NULL)) + return FALSE; + + if (unlikely (pattern == NULL)) + return FALSE; + + if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE ) + { + cairo_surface_t *pattern_surface = + ((cairo_surface_pattern_t *) pattern)->surface; + + while (_cairo_surface_is_subsurface (pattern_surface)) + { + pattern_surface = + _cairo_surface_subsurface_get_target (pattern_surface); + } + + return pattern_surface == surface; + } + + return FALSE; +} + +static inline cairo_bool_t +_cairo_pattern_is_recording (const cairo_pattern_t *pattern) +{ + cairo_surface_t *surface; + + if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) + return FALSE; + + surface = ((const cairo_surface_pattern_t *) pattern)->surface; + return _cairo_surface_is_recording (surface); +} + +static inline cairo_bool_t +_cairo_tg_surface_owns_data (cairo_tg_surface_t *surface) +{ + return ((cairo_image_surface_t *) surface->image_surface)->owns_data; +} + +static inline cairo_int_status_t +_cairo_tg_image_surface_paint (void *closure, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_image_surface_t *surface = (cairo_image_surface_t *) closure; + cairo_int_status_t status; + + status = _cairo_surface_begin_modification (&surface->base); + + if (unlikely (status)) + return status; + + status = _cairo_compositor_paint (surface->compositor, &surface->base, + op, source, clip); + + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) + { + surface->base.is_clear = op == CAIRO_OPERATOR_CLEAR && clip == NULL; + surface->base.serial++; + } + + return status; +} + +static inline cairo_int_status_t +_cairo_tg_image_surface_mask (void *closure, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + cairo_image_surface_t *surface = (cairo_image_surface_t *) closure; + cairo_int_status_t status; + + status = _cairo_surface_begin_modification (&surface->base); + + if (unlikely (status)) + return status; + + status = _cairo_compositor_mask (surface->compositor, &surface->base, + op, source, mask, clip); + + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) + { + surface->base.is_clear = FALSE; + surface->base.serial++; + } + + return status; +} + +static inline cairo_int_status_t +_cairo_tg_image_surface_stroke (void *closure, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + cairo_image_surface_t *surface = (cairo_image_surface_t *) closure; + cairo_int_status_t status; + + status = _cairo_surface_begin_modification (&surface->base); + + if (unlikely (status)) + return status; + + status = _cairo_compositor_stroke (surface->compositor, &surface->base, + op, source, path, + style, ctm, ctm_inverse, + tolerance, antialias, clip); + + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) + { + surface->base.is_clear = FALSE; + surface->base.serial++; + } + + return status; +} + + +static inline cairo_int_status_t +_cairo_tg_image_surface_fill (void *closure, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + cairo_image_surface_t *surface = (cairo_image_surface_t *) closure; + cairo_int_status_t status; + + status = _cairo_surface_begin_modification (&surface->base); + + if (unlikely (status)) + return status; + + status = _cairo_compositor_fill (surface->compositor, &surface->base, + op, source, path, + fill_rule, tolerance, antialias, clip); + + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) + { + surface->base.is_clear = FALSE; + surface->base.serial++; + } + + return status; +} + +static inline cairo_int_status_t +_cairo_tg_image_surface_glyphs (void *closure, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + const cairo_clip_t *clip) +{ + cairo_image_surface_t *surface = (cairo_image_surface_t *) closure; + cairo_int_status_t status; + + status = _cairo_surface_begin_modification (&surface->base); + + if (unlikely (status)) + return status; + + status = _cairo_compositor_glyphs (surface->compositor, &surface->base, + op, source, + glyphs, num_glyphs, scaled_font, + clip); + + if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) + { + surface->base.is_clear = FALSE; + surface->base.serial++; + } + + return status; +} + +const cairo_tg_journal_replay_funcs_t replay_funcs_image_fallback = +{ + _cairo_tg_image_surface_paint, + _cairo_tg_image_surface_mask, + _cairo_tg_image_surface_stroke, + _cairo_tg_image_surface_fill, + _cairo_tg_image_surface_glyphs, +}; + +typedef struct _cairo_tg_surface_tile +{ + cairo_surface_t *surface; + cairo_rectangle_int_t tile_rect; +} cairo_tg_surface_tile_t; + +static inline int +_cairo_tg_surface_tiles_init (cairo_tg_surface_t *surface, + const cairo_rectangle_int_t *extents, + int num_tiles, + cairo_tg_surface_tile_t *tiles) +{ + int tile_height; + int i; + + if (extents->height <= 0) + return 0; + + if (extents->height <= num_tiles) + num_tiles = extents->height; + + tile_height = extents->height / num_tiles; + + for (i = 0; i < num_tiles; i++) + { + tiles[i].surface = surface->tile_surfaces[i]; + tiles[i].tile_rect.x = extents->x; + tiles[i].tile_rect.y = extents->y + i * tile_height; + tiles[i].tile_rect.width = extents->width; + tiles[i].tile_rect.height = tile_height; + } + + tiles[num_tiles - 1].tile_rect.height = extents->height - i * (num_tiles - 1); + + return num_tiles; +} + +static cairo_int_status_t +_cairo_tg_surface_tile_paint (void *closure, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_tg_surface_tile_t *tile = (cairo_tg_surface_tile_t *) closure; + cairo_clip_t *tile_clip; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + + tile_clip = _cairo_clip_copy_intersect_rectangle (clip, &tile->tile_rect); + + if (! _cairo_clip_is_all_clipped (tile_clip)) + status = _cairo_tg_image_surface_paint (tile->surface, op, source, tile_clip); + + _cairo_clip_destroy (tile_clip); + + return status; +} + +static cairo_int_status_t +_cairo_tg_surface_tile_mask (void *closure, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + cairo_tg_surface_tile_t *tile = (cairo_tg_surface_tile_t *) closure; + cairo_clip_t *tile_clip; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + + tile_clip = _cairo_clip_copy_intersect_rectangle (clip, &tile->tile_rect); + + if (! _cairo_clip_is_all_clipped (tile_clip)) + { + status = _cairo_tg_image_surface_mask (tile->surface, op, source, + mask, tile_clip); + } + + _cairo_clip_destroy (tile_clip); + + return status; +} + +static cairo_int_status_t +_cairo_tg_surface_tile_stroke (void *closure, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + cairo_tg_surface_tile_t *tile = (cairo_tg_surface_tile_t *) closure; + cairo_clip_t *tile_clip; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + + tile_clip = _cairo_clip_copy_intersect_rectangle (clip, &tile->tile_rect); + + if (! _cairo_clip_is_all_clipped (tile_clip)) + { + status = _cairo_tg_image_surface_stroke (tile->surface, op, source, + path, style, ctm, ctm_inverse, + tolerance, antialias, tile_clip); + } + + _cairo_clip_destroy (tile_clip); + + return status; +} + +static cairo_int_status_t +_cairo_tg_surface_tile_fill (void *closure, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + cairo_tg_surface_tile_t *tile = (cairo_tg_surface_tile_t *) closure; + cairo_clip_t *tile_clip; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + + tile_clip = _cairo_clip_copy_intersect_rectangle (clip, &tile->tile_rect); + + if (! _cairo_clip_is_all_clipped (tile_clip)) + { + status = _cairo_tg_image_surface_fill (tile->surface, op, source, + path, fill_rule, tolerance, + antialias, tile_clip); + } + + _cairo_clip_destroy (tile_clip); + + return status; +} + +static cairo_int_status_t +_cairo_tg_surface_tile_glyphs (void *closure, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + const cairo_clip_t *clip) +{ + cairo_tg_surface_tile_t *tile = (cairo_tg_surface_tile_t *) closure; + cairo_clip_t *tile_clip; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + + tile_clip = _cairo_clip_copy_intersect_rectangle (clip, &tile->tile_rect); + + if (! _cairo_clip_is_all_clipped (tile_clip)) + { + status = _cairo_tg_image_surface_glyphs (tile->surface, op, source, + glyphs, num_glyphs, scaled_font, + tile_clip); + } + + _cairo_clip_destroy (tile_clip); + + return status; +} + +const cairo_tg_journal_replay_funcs_t replay_funcs_tile = +{ + _cairo_tg_surface_tile_paint, + _cairo_tg_surface_tile_mask, + _cairo_tg_surface_tile_stroke, + _cairo_tg_surface_tile_fill, + _cairo_tg_surface_tile_glyphs, +}; + +#if ! CAIRO_HAS_OPENMP +#define CAIRO_TG_NUM_MAX_WORKERS CAIRO_TG_NUM_MAX_TILES + +typedef enum _cairo_tg_worker_status +{ + CAIRO_TG_WORKER_STATUS_IDLE, /* can transit to either OCCUPIED or KILLED */ + CAIRO_TG_WORKER_STATUS_TO_DO, /* only can transit to IDLE state */ + CAIRO_TG_WORKER_STATUS_KILLED, /* worker will be no longer valid */ +} cairo_tg_worker_status_t; + +typedef struct _cairo_tg_worker +{ + cairo_tg_journal_t *journal; + cairo_tg_surface_tile_t *tile; + + pthread_t thread; + pthread_mutex_t lock; + pthread_cond_t cond_wake_up; + cairo_tg_worker_status_t status; + +#ifdef CAIRO_TG_THREAD_POOL_BUSY_WAIT + pthread_spinlock_t spinlock; +#else + pthread_cond_t cond_done; +#endif +} cairo_tg_worker_t; + +cairo_tg_worker_t workers[CAIRO_TG_NUM_MAX_WORKERS]; + +pthread_mutex_t workers_lock; +cairo_bool_t workers_occupied; + +static void * +_cairo_tg_worker_mainloop (void *arg) +{ + cairo_tg_worker_t *worker = (cairo_tg_worker_t *) arg; + + while (1) + { + pthread_mutex_lock (&worker->lock); + + while (worker->status == CAIRO_TG_WORKER_STATUS_IDLE) + pthread_cond_wait (&worker->cond_wake_up, &worker->lock); + + /* Here, worker is kicked off to do some action. */ + + if (worker->status == CAIRO_TG_WORKER_STATUS_KILLED) + { + /* Worker is killed, so release mutex and exit. */ + pthread_mutex_unlock (&worker->lock); + pthread_exit (NULL); + } + + assert (worker->status == CAIRO_TG_WORKER_STATUS_TO_DO); + + _cairo_tg_journal_replay (worker->journal, (void *)worker->tile, + &worker->tile->tile_rect, &replay_funcs_tile); + + worker->status = CAIRO_TG_WORKER_STATUS_IDLE; + +#ifndef CAIRO_TG_THREAD_POOL_BUSY_WAIT + pthread_cond_signal (&worker->cond_done); +#endif + + pthread_mutex_unlock (&worker->lock); + } + + return NULL; +} + +static void +_cairo_tg_workers_init (void) +{ + int i; + + for (i = 0; i < CAIRO_TG_NUM_MAX_WORKERS; i++) + { + workers[i].status = CAIRO_TG_WORKER_STATUS_IDLE; + + pthread_mutex_init (&workers[i].lock, NULL); + pthread_cond_init (&workers[i].cond_wake_up, NULL); + +#ifdef CAIRO_TG_THREAD_POOL_BUSY_WAIT + pthread_spin_init (&workers[i].spinlock, 0); +#else + pthread_cond_init (&workers[i].cond_done, NULL); +#endif + + pthread_create (&workers[i].thread, NULL, _cairo_tg_worker_mainloop, (void *) &workers[i]); + } + + pthread_mutex_init (&workers_lock, NULL); + workers_occupied = FALSE; +} + +static void +_cairo_tg_workers_fini (void) +{ + int i; + + for (i = 0; i < CAIRO_TG_NUM_MAX_WORKERS; i++) + { + pthread_mutex_lock (&workers[i].lock); + + workers[i].status = CAIRO_TG_WORKER_STATUS_KILLED; + pthread_cond_signal (&workers[i].cond_wake_up); + pthread_mutex_unlock (&workers[i].lock); + } + + for (i = 0; i < CAIRO_TG_NUM_MAX_WORKERS; i++) + pthread_join (workers[i].thread, NULL); + + for (i = 0; i < CAIRO_TG_NUM_MAX_WORKERS; i++) + { + pthread_mutex_destroy (&workers[i].lock); + pthread_cond_destroy (&workers[i].cond_wake_up); + +#ifdef CAIRO_TG_THREAD_POOL_BUSY_WAIT + pthread_spin_destroy (&workers[i].spinlock); +#else + pthread_cond_destroy (&workers[i].cond_done); +#endif + } +} + +static void __attribute__((constructor)) +_cairo_tg_constructor (void) +{ + pthread_atfork (NULL, NULL, _cairo_tg_workers_init); + _cairo_tg_workers_init (); +} + +static void __attribute__((destructor)) +_cairo_tg_destructor (void) +{ + _cairo_tg_workers_fini (); +} + +#endif /* ! CAIRO_HAS_OPENMP */ + +static void +_cairo_tg_surface_prepare_flush_parallel (cairo_tg_surface_t *surface) +{ + const cairo_tg_journal_entry_t *entry; + const cairo_tg_journal_entry_t *next; + + cairo_list_foreach_entry_safe (entry, next, cairo_tg_journal_entry_t, + &surface->journal.entry_list, link) + { + if (entry->source.base.type == CAIRO_PATTERN_TYPE_SURFACE) + { + cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) (&entry->source.base); + cairo_surface_flush (pattern->surface); + } + + if (entry->type == CAIRO_TG_JOURNAL_ENTRY_MASK) + { + cairo_tg_journal_entry_mask_t *e = + (cairo_tg_journal_entry_mask_t *) entry; + + if (e->mask.base.type == CAIRO_PATTERN_TYPE_SURFACE) + { + cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) (&e->mask.base); + cairo_surface_flush (pattern->surface); + } + } + } +} + +static cairo_int_status_t +_cairo_tg_surface_flush_parallel (cairo_tg_surface_t *surface) +{ + int num_tiles, i; + cairo_tg_surface_tile_t tiles[CAIRO_TG_NUM_MAX_TILES]; + cairo_rectangle_int_t extents; + + if (surface->journal.num_entries < CAIRO_TG_NUM_MIN_ENTRIES_FOR_PARALLEL_FLUSH) + return CAIRO_INT_STATUS_UNSUPPORTED; + + _cairo_tg_surface_prepare_flush_parallel (surface); + + extents.x = 0; + extents.y = 0; + extents.width = surface->width; + extents.height = surface->height; + + _cairo_rectangle_intersect (&extents, &surface->journal.extents); + + num_tiles = get_num_cpu_cores (); + +#if ! CAIRO_HAS_OPENMP + if (num_tiles > CAIRO_TG_NUM_MAX_WORKERS) + num_tiles = CAIRO_TG_NUM_MAX_WORKERS; +#endif + + num_tiles = _cairo_tg_surface_tiles_init (surface, &extents, num_tiles, &tiles[0]); + +#if CAIRO_HAS_OPENMP + #pragma omp parallel for + for (i = 0; i < num_tiles; i++) + { + _cairo_tg_journal_replay (&surface->journal, (void *) &tiles[i], + &tiles[i].tile_rect, &replay_funcs_tile); + } +#else + pthread_mutex_lock (&workers_lock); + + if (workers_occupied) + { + pthread_mutex_unlock (&workers_lock); + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + workers_occupied = TRUE; + pthread_mutex_unlock (&workers_lock); + + /* Kick workers to start. */ + for (i = 0; i < num_tiles - 1; i++) + { + pthread_mutex_lock (&workers[i].lock); + + workers[i].status = CAIRO_TG_WORKER_STATUS_TO_DO; + workers[i].journal = &surface->journal; + workers[i].tile = &tiles[i]; + + pthread_cond_signal (&workers[i].cond_wake_up); + pthread_mutex_unlock (&workers[i].lock); + } + + _cairo_tg_journal_replay (&surface->journal, &tiles[num_tiles - 1], + &tiles[num_tiles - 1].tile_rect, &replay_funcs_tile); + + /* Wait for workers to finish. */ + for (i = 0; i < num_tiles - 1; i++) + { +#ifdef CAIRO_TG_THREAD_POOL_BUSY_WAIT + pthread_spin_lock (&workers[i].spinlock); + + while (workers[i].status == CAIRO_TG_WORKER_STATUS_TO_DO) + { + pthread_spin_unlock (&workers[i].spinlock); + pthread_spin_lock (&workers[i].spinlock); + } + + pthread_spin_unlock (&workers[i].spinlock); +#else + pthread_mutex_lock (&workers[i].lock); + + while (workers[i].status == CAIRO_TG_WORKER_STATUS_TO_DO) + pthread_cond_wait (&workers[i].cond_done, &workers[i].lock); + + pthread_mutex_unlock (&workers[i].lock); +#endif + } + + /* Release thread pool. */ + pthread_mutex_lock (&workers_lock); + workers_occupied = FALSE; + pthread_mutex_unlock (&workers_lock); +#endif + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_status_t +_cairo_tg_surface_flush (void *abstract_surface, + unsigned flags) +{ + cairo_tg_surface_t *surface = abstract_surface; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + + if (flags) + return CAIRO_STATUS_SUCCESS; + + _cairo_tg_journal_lock (&surface->journal); + + if (surface->journal.num_entries) + { + status = _cairo_tg_surface_flush_parallel (surface); + + if (status) + { + status = _cairo_tg_journal_replay (&surface->journal, + (void *) surface->image_surface, + NULL, &replay_funcs_image_fallback); + } + + _cairo_tg_journal_clear (&surface->journal); + } + + _cairo_tg_journal_unlock (&surface->journal); + + return status; +} + +static cairo_image_surface_t * +_cairo_tg_surface_map_to_image (void *abstract_surface, + const cairo_rectangle_int_t *extents) +{ + cairo_tg_surface_t *other = abstract_surface; + cairo_surface_t *surface; + uint8_t *buffer; + + _cairo_tg_surface_flush (other, 0); + + buffer = other->data; + buffer += extents->y * other->stride; + buffer += extents->x * other->bpp / 8; + + surface = + _cairo_image_surface_create_with_pixman_format (buffer, + other->pixman_format, + extents->width, + extents->height, + other->stride); + + if (unlikely (surface == NULL)) + return NULL; + + cairo_surface_set_device_offset (surface, -extents->x, extents->y); + + return (cairo_image_surface_t *) surface; +} + +static cairo_int_status_t +_cairo_tg_surface_unmap_image (void *abstract_surface, + cairo_image_surface_t *image) +{ + cairo_surface_finish (&image->base); + cairo_surface_destroy (&image->base); + + return CAIRO_INT_STATUS_SUCCESS; +} + +static cairo_bool_t +_cairo_tg_surface_get_extents (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_tg_surface_t *surface = abstract_surface; + + extents->x = 0; + extents->y = 0; + extents->width = surface->width; + extents->height = surface->height; + + return TRUE; +} + +static cairo_int_status_t +_cairo_tg_surface_paint (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_clip_t *clip) +{ + cairo_tg_surface_t *surface = abstract_surface; + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_pattern_is_self_copy (&surface->base, source) && + ! _cairo_pattern_is_recording (source)) + status = _cairo_tg_journal_log_paint (&surface->journal, op, source, clip); + + if (status) + { + status = _cairo_tg_surface_flush (surface, 0); + + if (unlikely (status)) + return status; + + status = _cairo_tg_image_surface_paint (surface->image_surface, op, source, clip); + } + + return status; +} + +static cairo_int_status_t +_cairo_tg_surface_mask (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_pattern_t *mask, + const cairo_clip_t *clip) +{ + cairo_tg_surface_t *surface = abstract_surface; + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_pattern_is_self_copy (&surface->base, source) && + ! _cairo_pattern_is_self_copy (&surface->base, mask) && + ! _cairo_pattern_is_recording (source)) + status = _cairo_tg_journal_log_mask (&surface->journal, op, source, mask, clip); + + if (status) + { + status = _cairo_tg_surface_flush (surface, 0); + + if (unlikely (status)) + return status; + + status = _cairo_tg_image_surface_mask (surface->image_surface, op, source, + mask, clip); + } + + return status; +} + +static cairo_int_status_t +_cairo_tg_surface_stroke (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + const cairo_stroke_style_t *style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + cairo_tg_surface_t *surface = abstract_surface; + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_pattern_is_self_copy (&surface->base, source) && + ! _cairo_pattern_is_recording (source)) + { + status = _cairo_tg_journal_log_stroke (&surface->journal, op, source, + path, style, ctm, ctm_inverse, + tolerance, antialias, clip); + } + + if (status) + { + status = _cairo_tg_surface_flush (surface, 0); + + if (unlikely (status)) + return status; + + status = _cairo_tg_image_surface_stroke (surface->image_surface, op, source, + path, style, ctm, ctm_inverse, + tolerance, antialias, clip); + } + + return status; +} + +static cairo_int_status_t +_cairo_tg_surface_fill (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + const cairo_path_fixed_t *path, + cairo_fill_rule_t fill_rule, + double tolerance, + cairo_antialias_t antialias, + const cairo_clip_t *clip) +{ + cairo_tg_surface_t *surface = abstract_surface; + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_pattern_is_self_copy (&surface->base, source) && + ! _cairo_pattern_is_recording (source)) + { + status = _cairo_tg_journal_log_fill (&surface->journal, op, source, + path, fill_rule, tolerance, antialias, clip); + } + + if (status) + { + status = _cairo_tg_surface_flush (surface, 0); + + if (unlikely (status)) + return status; + + status = _cairo_tg_image_surface_fill (surface->image_surface, op, source, + path, fill_rule, tolerance, antialias, clip); + } + + return status; +} + +static cairo_int_status_t +_cairo_tg_surface_glyphs (void *abstract_surface, + cairo_operator_t op, + const cairo_pattern_t *source, + cairo_glyph_t *glyphs, + int num_glyphs, + cairo_scaled_font_t *scaled_font, + const cairo_clip_t *clip) +{ + cairo_tg_surface_t *surface = abstract_surface; + cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; + + if (! _cairo_pattern_is_self_copy (&surface->base, source) && + ! _cairo_pattern_is_recording (source)) + { + status = _cairo_tg_journal_log_glyphs (&surface->journal, op, source, + glyphs, num_glyphs, scaled_font, clip); + } + + if (status) + { + status = _cairo_tg_surface_flush (surface, 0); + + if (unlikely (status)) + return status; + + status = _cairo_tg_image_surface_glyphs (surface->image_surface, op, source, + glyphs, num_glyphs, scaled_font, clip); + } + + return status; +} + +static cairo_surface_t * +_cairo_tg_surface_create_similar (void *abstract_other, + cairo_content_t content, + int width, + int height) +{ + cairo_tg_surface_t *other = abstract_other; + + if (! _cairo_tg_surface_is_size_valid (width, height)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE)); + + if (content == other->base.content) + return cairo_tg_surface_create (other->format, width, height); + + return cairo_tg_surface_create (_cairo_format_from_content (content), width, height); +} + +static cairo_surface_t * +_cairo_tg_surface_source (void *abstract_surface, + cairo_rectangle_int_t *extents) +{ + cairo_tg_surface_t *surface = abstract_surface; + + if (extents) + { + extents->x = extents->y = 0; + extents->width = surface->width; + extents->height = surface->height; + } + + return &surface->base; +} + +static cairo_status_t +_cairo_tg_surface_acquire_source_image (void *abstract_surface, + cairo_image_surface_t **image_out, + void **image_extra) +{ + cairo_tg_surface_t *surface = abstract_surface; + + _cairo_tg_surface_flush (surface, 0); + + *image_out = (cairo_image_surface_t *) surface->image_surface; + *image_extra = NULL; + + return CAIRO_STATUS_SUCCESS; +} + +static void +_cairo_tg_surface_release_source_image (void *abstract_surface, + cairo_image_surface_t *image, + void *image_extra) +{ + /* Do nothing */ +} + +static cairo_surface_t * +_cairo_tg_surface_snapshot (void *abstract_surface) +{ + cairo_tg_surface_t *surface = abstract_surface; + cairo_tg_surface_t *clone; + + _cairo_tg_surface_flush (surface, 0); + + if (_cairo_tg_surface_owns_data (surface) && surface->base._finishing) + { + return cairo_tg_surface_create_for_data (surface->data, surface->format, + surface->width, surface->height, + surface->stride); + } + + clone = (cairo_tg_surface_t *) + cairo_tg_surface_create (surface->format, surface->width, surface->height); + + if (unlikely (clone->base.status)) + return &clone->base; + + if (surface->stride == clone->stride) + { + memcpy (clone->data, surface->data, clone->stride * clone->height); + } + else + { + unsigned char *dst = clone->data; + unsigned char *src = surface->data; + int i; + int stride = clone->stride < surface->stride ? clone->stride : surface->stride; + + for (i = 0; i < clone->height; i++) + { + memcpy (dst, src, stride); + dst += clone->stride; + src += surface->stride; + } + } + + clone->base.is_clear = FALSE; + + return &clone->base; +} + +static cairo_int_status_t +_cairo_tg_surface_init_tile_surfaces (cairo_tg_surface_t *surface) +{ + int i; + cairo_int_status_t status = CAIRO_INT_STATUS_SUCCESS; + + memset (&surface->tile_surfaces[0], 0x00, + sizeof (cairo_surface_t *) * CAIRO_TG_NUM_MAX_TILES); + + for (i = 0; i < CAIRO_TG_NUM_MAX_TILES; i++) + { + surface->tile_surfaces[i] = cairo_image_surface_create_for_data (surface->data, + surface->format, + surface->width, + surface->height, + surface->stride); + + if (surface->tile_surfaces[i] == NULL) + { + status = CAIRO_INT_STATUS_NO_MEMORY; + break; + } + } + + if (unlikely (status)) + { + for (i = 0; i < CAIRO_TG_NUM_MAX_TILES; i++) + { + if (surface->tile_surfaces[i]) + cairo_surface_destroy (surface->tile_surfaces[i]); + else + break; + } + } + + return status; +} + +static void +_cairo_tg_surface_fini_tile_surfaces (cairo_tg_surface_t *surface) +{ + int i; + + for (i = 0; i < CAIRO_TG_NUM_MAX_TILES; i++) + { + if (surface->tile_surfaces[i]) + cairo_surface_destroy (surface->tile_surfaces[i]); + else + break; + } +} + +static cairo_status_t +_cairo_tg_surface_finish (void *abstract_surface) +{ + cairo_tg_surface_t *surface = abstract_surface; + + _cairo_tg_surface_flush (surface, 0); + _cairo_tg_journal_fini (&surface->journal); + _cairo_tg_surface_fini_tile_surfaces (surface); + cairo_surface_destroy (surface->image_surface); + + return CAIRO_STATUS_SUCCESS; +} + +static const cairo_surface_backend_t _cairo_tg_surface_backend = +{ + CAIRO_SURFACE_TYPE_TG, + _cairo_tg_surface_finish, + + _cairo_default_context_create, + + _cairo_tg_surface_create_similar, + NULL, /* create_similar image */ + _cairo_tg_surface_map_to_image, + _cairo_tg_surface_unmap_image, + + _cairo_tg_surface_source, + _cairo_tg_surface_acquire_source_image, + _cairo_tg_surface_release_source_image, + _cairo_tg_surface_snapshot, + + NULL, /* copy_page */ + NULL, /* show_page */ + + _cairo_tg_surface_get_extents, + NULL, /* get_font_options */ + + _cairo_tg_surface_flush, + NULL, /* mark_dirty_rectangle */ + + _cairo_tg_surface_paint, + _cairo_tg_surface_mask, + _cairo_tg_surface_stroke, + _cairo_tg_surface_fill, + NULL, /* fill_stroke */ + _cairo_tg_surface_glyphs, +}; + +cairo_surface_t * +cairo_tg_surface_create (cairo_format_t format, + int width, + int height) +{ + cairo_tg_surface_t *surface; + cairo_surface_t *image_surface; + + image_surface = cairo_image_surface_create (format, width, height); + + if (unlikely (image_surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface = malloc (sizeof (cairo_tg_surface_t)); + + if (unlikely (surface == NULL)) + { + cairo_surface_destroy (image_surface); + + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + _cairo_surface_init (&surface->base, + &_cairo_tg_surface_backend, + NULL, image_surface->content); + + surface->format = format; + surface->pixman_format = ((cairo_image_surface_t *) image_surface)->pixman_format; + surface->data = (unsigned char *) cairo_image_surface_get_data (image_surface); + surface->width = width; + surface->height = height; + surface->stride = cairo_image_surface_get_stride (image_surface); + surface->bpp = get_bpp_for_format (format); + surface->image_surface = image_surface; + surface->base.is_clear = image_surface->is_clear; + + _cairo_tg_journal_init (&surface->journal); + + if (_cairo_tg_surface_init_tile_surfaces (surface)) + { + cairo_surface_destroy (image_surface); + _cairo_tg_journal_fini (&surface->journal); + + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + return &surface->base; +} + +cairo_surface_t * +cairo_tg_surface_create_for_data (unsigned char *data, + cairo_format_t format, + int width, + int height, + int stride) +{ + cairo_tg_surface_t *surface; + cairo_surface_t *image_surface; + + image_surface = cairo_image_surface_create_for_data (data, format, width, height, stride); + + if (unlikely (image_surface == NULL)) + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + + surface = malloc (sizeof (cairo_tg_surface_t)); + + if (unlikely (surface == NULL)) + { + cairo_surface_destroy (image_surface); + + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + _cairo_surface_init (&surface->base, + &_cairo_tg_surface_backend, + NULL, image_surface->content); + + surface->format = format; + surface->pixman_format = ((cairo_image_surface_t *) image_surface)->pixman_format; + surface->data = (unsigned char *) cairo_image_surface_get_data (image_surface); + surface->width = width; + surface->height = height; + surface->stride = cairo_image_surface_get_stride (image_surface); + surface->bpp = get_bpp_for_format (format); + surface->image_surface = image_surface; + surface->base.is_clear = image_surface->is_clear; + + _cairo_tg_journal_init (&surface->journal); + + if (_cairo_tg_surface_init_tile_surfaces (surface)) + { + cairo_surface_destroy (image_surface); + _cairo_tg_journal_fini (&surface->journal); + + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); + } + + return &surface->base; +} + +unsigned char * +cairo_tg_surface_get_data (cairo_surface_t *surface) +{ + cairo_tg_surface_t *tg_surface = (cairo_tg_surface_t *) surface; + + if (! _cairo_surface_is_tg (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return NULL; + } + + return tg_surface->data; +} + +cairo_format_t +cairo_tg_surface_get_format (cairo_surface_t *surface) +{ + cairo_tg_surface_t *tg_surface = (cairo_tg_surface_t *) surface; + + if (! _cairo_surface_is_tg (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return CAIRO_FORMAT_INVALID; + } + + return tg_surface->format; +} + +int +cairo_tg_surface_get_width (cairo_surface_t *surface) +{ + cairo_tg_surface_t *tg_surface = (cairo_tg_surface_t *) surface; + + if (! _cairo_surface_is_tg (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return tg_surface->width; +} + +int +cairo_tg_surface_get_height (cairo_surface_t *surface) +{ + cairo_tg_surface_t *tg_surface = (cairo_tg_surface_t *) surface; + + if (! _cairo_surface_is_tg (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return tg_surface->height; +} + +int +cairo_tg_surface_get_stride (cairo_surface_t *surface) +{ + cairo_tg_surface_t *tg_surface = (cairo_tg_surface_t *) surface; + + if (! _cairo_surface_is_tg (surface)) { + _cairo_error_throw (CAIRO_STATUS_SURFACE_TYPE_MISMATCH); + return 0; + } + + return tg_surface->stride; +} |