diff options
Diffstat (limited to 'engines/sci/gfx/operations.cpp')
-rw-r--r-- | engines/sci/gfx/operations.cpp | 2490 |
1 files changed, 2490 insertions, 0 deletions
diff --git a/engines/sci/gfx/operations.cpp b/engines/sci/gfx/operations.cpp new file mode 100644 index 0000000000..110f52a52c --- /dev/null +++ b/engines/sci/gfx/operations.cpp @@ -0,0 +1,2490 @@ +/*************************************************************************** + gfx_operations Copyright (C) 2000 Christoph Reichenbach + + This program may be modified and copied freely according to the terms of + the GNU general public license (GPL), as long as the above copyright + notice and the licensing information contained herein are preserved. + + Please refer to www.gnu.org for licensing details. + + This work is provided AS IS, without warranty of any kind, expressed or + implied, including but not limited to the warranties of merchantibility, + noninfringement, and fitness for a specific purpose. The author will not + be held liable for any damage caused by this work or derivatives of it. + + By using this source code, you agree to the licensing terms as stated + above. + + + Please contact the maintainer for bug reports or inquiries. + + Current Maintainer: + + Christoph Reichenbach (CR) <jameson@linuxgames.com> + +***************************************************************************/ +/* Graphical operations, called from the widget state manager */ + +#include "sci/include/sci_memory.h" +#include "sci/include/gfx_operations.h" + +#include <ctype.h> + +#define PRECISE_PRIORITY_MAP /* Duplicate all operations on the local priority map as appropriate */ + +#undef GFXW_DEBUG_DIRTY /* Enable to debug stuff relevant for dirty rects + ** in widget management */ + +#ifdef GFXW_DEBUG_DIRTY +# define DDIRTY fprintf(stderr, "%s:%5d| ", __FILE__, __LINE__); fprintf +#else +# define DDIRTY if (0) fprintf +#endif + +/* Default color maps */ +#define DEFAULT_COLORS_NR 16 +gfx_pixmap_color_t default_colors[DEFAULT_COLORS_NR] = + {{GFX_COLOR_SYSTEM, 0x00, 0x00, 0x00}, {GFX_COLOR_SYSTEM, 0x00, 0x00, 0xaa}, + {GFX_COLOR_SYSTEM, 0x00, 0xaa, 0x00}, {GFX_COLOR_SYSTEM, 0x00, 0xaa, 0xaa}, + {GFX_COLOR_SYSTEM, 0xaa, 0x00, 0x00}, {GFX_COLOR_SYSTEM, 0xaa, 0x00, 0xaa}, + {GFX_COLOR_SYSTEM, 0xaa, 0x55, 0x00}, {GFX_COLOR_SYSTEM, 0xaa, 0xaa, 0xaa}, + {GFX_COLOR_SYSTEM, 0x55, 0x55, 0x55}, {GFX_COLOR_SYSTEM, 0x55, 0x55, 0xff}, + {GFX_COLOR_SYSTEM, 0x55, 0xff, 0x55}, {GFX_COLOR_SYSTEM, 0x55, 0xff, 0xff}, + {GFX_COLOR_SYSTEM, 0xff, 0x55, 0x55}, {GFX_COLOR_SYSTEM, 0xff, 0x55, 0xff}, + {GFX_COLOR_SYSTEM, 0xff, 0xff, 0x55}, {GFX_COLOR_SYSTEM, 0xff, 0xff, 0xff}}; /* "Normal" EGA */ + + +#define POINTER_VISIBLE_BUT_CLIPPED 2 + +/* Performs basic checks that apply to most functions */ +#define BASIC_CHECKS(error_retval) \ +if (!state) { \ + GFXERROR("Null state!\n"); \ + return error_retval; \ +} \ +if (!state->driver) { \ + GFXERROR("GFX driver invalid!\n"); \ + return error_retval; \ +} + +/* How to determine whether colors have to be allocated */ +#define PALETTE_MODE state->driver->mode->palette + +#define DRAW_POINTER { int __x = _gfxop_draw_pointer(state); if (__x) { GFXERROR("Drawing the mouse pointer failed!\n"); return __x;} } +#define REMOVE_POINTER { int __x = _gfxop_remove_pointer(state); if (__x) { GFXERROR("Removing the mouse pointer failed!\n"); return __x;} } + +/* #define GFXOP_DEBUG_DIRTY */ + +/* Internal operations */ + +static void +_gfxop_scale_rect(rect_t *rect, gfx_mode_t *mode) +{ + int xfact = mode->xfact; + int yfact = mode->yfact; + + rect->x *= xfact; + rect->y *= yfact; + rect->xl *= xfact; + rect->yl *= yfact; +} + +static void +_gfxop_scale_point(point_t *point, gfx_mode_t *mode) +{ + int xfact = mode->xfact; + int yfact = mode->yfact; + + point->x *= xfact; + point->y *= yfact; +} + +static void +_gfxop_alloc_colors(gfx_state_t *state, gfx_pixmap_color_t *colors, int colors_nr) +{ + int i; + + if (!PALETTE_MODE) + return; + + for (i = 0; i < colors_nr; i++) + gfx_alloc_color(state->driver->mode->palette, colors + i); +} + +static void +_gfxop_free_colors(gfx_state_t *state, gfx_pixmap_color_t *colors, int colors_nr) +{ + int i; + + if (!PALETTE_MODE) + return; + + for (i = 0; i < colors_nr; i++) + gfx_free_color(state->driver->mode->palette, colors + i); +} + + +int _gfxop_clip(rect_t *rect, rect_t clipzone) +/* Returns 1 if nothing is left */ +{ +#if 0 + printf ("Clipping (%d, %d) size (%d, %d) by (%d,%d)(%d,%d)\n", rect->x, rect->y, rect->xl, rect->yl, + clipzone.x, clipzone.y, clipzone.xl, clipzone.yl); +#endif + + if (rect->x < clipzone.x) { + rect->xl -= (clipzone.x - rect->x); + rect->x = clipzone.x; + } + + if (rect->y < clipzone.y) { + rect->yl -= (clipzone.y - rect->y); + rect->y = clipzone.y; + } + + if (rect->x + rect->xl > clipzone.x + clipzone.xl) + rect->xl = (clipzone.x + clipzone.xl) - rect->x; + + if (rect->y + rect->yl > clipzone.y + clipzone.yl) + rect->yl = (clipzone.y + clipzone.yl) - rect->y; + + if (rect->xl < 0) + rect->xl = 0; + if (rect->yl < 0) + rect->yl = 0; + +#if 0 + printf (" => (%d, %d) size (%d, %d)\n", rect->x, rect->y, rect->xl, rect->yl); +#endif + return (rect->xl <= 0 || rect->yl <= 0); +} + +static int +_gfxop_grab_pixmap(gfx_state_t *state, gfx_pixmap_t **pxmp, int x, int y, + int xl, int yl, int priority, rect_t *zone) + /* Returns 1 if the resulting data size was zero, GFX_OK or an error code otherwise */ +{ + int xfact = state->driver->mode->xfact; + int yfact = state->driver->mode->yfact; + int unscaled_xl = (xl + xfact - 1) / xfact; + int unscaled_yl = (yl + yfact - 1) / yfact; + *zone = gfx_rect(x, y, xl, yl); + + if (_gfxop_clip(zone, gfx_rect(0, 0, + 320 * state->driver->mode->xfact, + 200 * state->driver->mode->yfact))) + return GFX_ERROR; + + if (!*pxmp) + *pxmp = gfx_new_pixmap(unscaled_xl, unscaled_yl, GFX_RESID_NONE, 0, 0); + else + if (xl * yl > (*pxmp)->xl * (*pxmp)->yl) { + gfx_pixmap_free_data(*pxmp); + (*pxmp)->data = NULL; + } + + if (!(*pxmp)->data) { + (*pxmp)->index_xl = unscaled_xl + 1; + (*pxmp)->index_yl = unscaled_yl + 1; + gfx_pixmap_alloc_data(*pxmp, state->driver->mode); + } + return state->driver->grab_pixmap(state->driver, *zone, *pxmp, + priority? GFX_MASK_PRIORITY : GFX_MASK_VISUAL); +} + + +#define DRAW_LOOP(condition) \ +{ \ + rect_t drawrect = gfx_rect(pos.x, pos.y, pxm->index_xl, pxm->index_yl); \ + int offset, base_offset; \ + int read_offset, base_read_offset; \ + int x,y; \ + \ + if (!pxm->index_data) { \ + GFXERROR("Attempt to draw control color %d on pixmap %d/%d/%d without index data!\n", \ + color, pxm->ID, pxm->loop, pxm->cel); \ + return; \ + } \ + \ + if (_gfxop_clip(&drawrect, gfx_rect(0, 0, 320, 200))) \ + return; \ + \ + offset = base_offset = drawrect.x + drawrect.y * 320; \ + read_offset = base_read_offset = (drawrect.x - pos.x) + ((drawrect.y - pos.y) * pxm->index_xl); \ + \ + for (y = 0; y < drawrect.yl; y++) { \ + for (x = 0; x < drawrect.xl; x++) \ + if (pxm->index_data[read_offset++] != pxm->color_key) { \ + if (condition) \ + map->index_data[offset++] = color; \ + else \ + ++offset; \ + } else ++offset; \ + \ + offset = base_offset += 320; \ + read_offset = base_read_offset += pxm->index_xl; \ + } \ +} + +static void +_gfxop_draw_control(gfx_pixmap_t *map, gfx_pixmap_t *pxm, int color, point_t pos) +DRAW_LOOP(1) /* Always draw */ + +#ifdef PRECISE_PRIORITY_MAP +static void +_gfxop_draw_priority(gfx_pixmap_t *map, gfx_pixmap_t *pxm, int color, point_t pos) +DRAW_LOOP(map->index_data[offset] < color) /* Draw only lower priority */ +#endif + +#undef DRAW_LOOP + +static int +_gfxop_install_pixmap(gfx_driver_t *driver, gfx_pixmap_t *pxm) +{ + int error; + + if (driver->capabilities & GFX_CAPABILITY_PIXMAP_REGISTRY + && !(pxm->flags & GFX_PIXMAP_FLAG_INSTALLED)) { + error = driver->register_pixmap(driver, pxm); + + if (error) { + GFXERROR("driver->register_pixmap() returned error!\n"); + return error; + } + pxm->flags |= GFX_PIXMAP_FLAG_INSTALLED; + } + + if (driver->mode->palette && + (!(pxm->flags & GFX_PIXMAP_FLAG_PALETTE_SET))) { + int i; + int error; + + for (i = 0; i < pxm->colors_nr; i++) { + if ((error = driver->set_palette(driver, pxm->colors[i].global_index, + pxm->colors[i].r, + pxm->colors[i].g, + pxm->colors[i].b))) { + + GFXWARN("driver->set_palette(%d, %02x/%02x/%02x) failed!\n", + pxm->colors[i].global_index, + pxm->colors[i].r, + pxm->colors[i].g, + pxm->colors[i].b); + + if (error == GFX_FATAL) + return GFX_FATAL; + } + } + + pxm->flags |= GFX_PIXMAP_FLAG_PALETTE_SET; + } + return GFX_OK; +} + +static int +_gfxop_draw_pixmap(gfx_driver_t *driver, gfx_pixmap_t *pxm, int priority, int control, + rect_t src, rect_t dest, rect_t clip, int static_buf, gfx_pixmap_t *control_map, + gfx_pixmap_t *priority_map) +{ + int error; + rect_t clipped_dest = gfx_rect(dest.x, dest.y, dest.xl, dest.yl); + + if (control >= 0 || priority >= 0) { + point_t original_pos = gfx_point(dest.x / driver->mode->xfact, + dest.y / driver->mode->yfact); + + if (control >= 0) + _gfxop_draw_control(control_map, pxm, control, original_pos); + +#ifdef PRECISE_PRIORITY_MAP + if (priority >= 0) + _gfxop_draw_priority(priority_map, pxm, priority, original_pos); +#endif + } + + + if (_gfxop_clip(&clipped_dest, clip)) + return GFX_OK; + + src.x += clipped_dest.x - dest.x; + src.y += clipped_dest.y - dest.y; + src.xl = clipped_dest.xl; + src.yl = clipped_dest.yl; + + error = _gfxop_install_pixmap(driver, pxm); + if (error) return error; + + DDIRTY(stderr, "\\-> Drawing to actual %d %d %d %d\n", + clipped_dest.x / driver->mode->xfact, + clipped_dest.y / driver->mode->yfact, + clipped_dest.xl / driver->mode->xfact, + clipped_dest.yl / driver->mode->yfact); + + error = driver->draw_pixmap(driver, pxm, priority, src, clipped_dest, + static_buf? GFX_BUFFER_STATIC : GFX_BUFFER_BACK); + + if (error) { + GFXERROR("driver->draw_pixmap() returned error!\n"); + return error; + } + return GFX_OK; +} + +static int +_gfxop_remove_pointer(gfx_state_t *state) +{ + if (state->mouse_pointer_visible + && !state->mouse_pointer_in_hw + && state->mouse_pointer_bg) { + int retval; + + if (state->mouse_pointer_visible == POINTER_VISIBLE_BUT_CLIPPED) { + state->mouse_pointer_visible = 0; + state->pointer_pos.x = state->driver->pointer_x / state->driver->mode->xfact; + state->pointer_pos.y = state->driver->pointer_y / state->driver->mode->yfact; + return GFX_OK; + } + + state->mouse_pointer_visible = 0; + + retval = state->driver->draw_pixmap(state->driver, state->mouse_pointer_bg, GFX_NO_PRIORITY, + gfx_rect(0, 0, state->mouse_pointer_bg->xl, state->mouse_pointer_bg->yl), + state->pointer_bg_zone, + GFX_BUFFER_BACK); + + state->pointer_pos.x = state->driver->pointer_x / state->driver->mode->xfact; + state->pointer_pos.y = state->driver->pointer_y / state->driver->mode->yfact; + + return retval; + + } else { + state->pointer_pos.x = state->driver->pointer_x / state->driver->mode->xfact; + state->pointer_pos.y = state->driver->pointer_y / state->driver->mode->yfact; + return GFX_OK; + } +} + +static int /* returns 1 if there are no pointer bounds, 0 otherwise */ +_gfxop_get_pointer_bounds(gfx_state_t *state, rect_t *rect) +{ + gfx_pixmap_t *ppxm = state->mouse_pointer; + + if (!ppxm) + return 1; + + rect->x = state->driver->pointer_x - ppxm->xoffset * (state->driver->mode->xfact); + rect->y = state->driver->pointer_y - ppxm->yoffset * (state->driver->mode->yfact); + rect->xl = ppxm->xl; + rect->yl = ppxm->yl; + + return (_gfxop_clip(rect, gfx_rect(0, 0, 320 * state->driver->mode->xfact, + 200 * state->driver->mode->yfact))); +} + +static int +_gfxop_buffer_propagate_box(gfx_state_t *state, rect_t box, gfx_buffer_t buffer); + +static int +_gfxop_draw_pointer(gfx_state_t *state) +{ + if (state->mouse_pointer_visible || !state->mouse_pointer || state->mouse_pointer_in_hw) + return GFX_OK; + else { + int retval; + gfx_pixmap_t *ppxm = state->mouse_pointer; + int xfact, yfact; + int x = state->driver->pointer_x - ppxm->xoffset * (xfact = state->driver->mode->xfact); + int y = state->driver->pointer_y - ppxm->yoffset * (yfact = state->driver->mode->yfact); + int error; + + state->mouse_pointer_visible = 1; + + state->old_pointer_draw_pos.x = x; + state->old_pointer_draw_pos.y = y; + + /* FIXME: we are leaking the mouse_pointer_bg, but freeing it causes weirdness in jones + * we should reuse the buffer instead of malloc/free for better performance */ + + retval = _gfxop_grab_pixmap(state, &(state->mouse_pointer_bg), x, y, + ppxm->xl, ppxm->yl, 0, + &(state->pointer_bg_zone)); + + if (retval == GFX_ERROR) { + state->pointer_bg_zone = gfx_rect(0, 0, 320, 200); + state->mouse_pointer_visible = POINTER_VISIBLE_BUT_CLIPPED; + return GFX_OK; + } + + if (retval) + return retval; + + error = _gfxop_draw_pixmap(state->driver, ppxm, -1, -1, + gfx_rect(0, 0, ppxm->xl, ppxm->yl), + gfx_rect(x, y, ppxm->xl, ppxm->yl), + gfx_rect(0, 0, xfact * 320 , yfact * 200), + 0, state->control_map, state->priority_map); + + if (error) + return error; + + + return GFX_OK; + } +} + +gfx_pixmap_t * +_gfxr_get_cel(gfx_state_t *state, int nr, int *loop, int *cel, int palette) +{ + gfxr_view_t *view = gfxr_get_view(state->resstate, nr, loop, cel, palette); + gfxr_loop_t *indexed_loop; + + if (!view) + return NULL; + + if (*loop >= view->loops_nr + || *loop < 0) { + GFXWARN("Attempt to get cel from loop %d/%d inside view %d\n", *loop, + view->loops_nr, nr); + return NULL; + } + indexed_loop = view->loops + *loop; + + if (*cel >= indexed_loop->cels_nr + || *cel < 0) { + GFXWARN("Attempt to get cel %d/%d from view %d/%d\n", *cel, indexed_loop->cels_nr, + nr, *loop); + return NULL; + } + + return indexed_loop->cels[*cel]; /* Yes, view->cels uses a malloced pointer list. */ +} + +/*** Dirty rectangle operations ***/ + +static inline int +_gfxop_update_box(gfx_state_t *state, rect_t box) +{ + int retval; + _gfxop_scale_rect(&box, state->driver->mode); + + if ((retval = _gfxop_buffer_propagate_box(state, box, GFX_BUFFER_FRONT))) { + GFXERROR("Error occured while propagating box (%d,%d,%d,%d) to front buffer\n", + box.x, box.y, box.xl, box.yl); + return retval; + } + return GFX_OK; +} + + +static struct _dirty_rect * +_rect_create(rect_t box) +{ + struct _dirty_rect *rect; + + rect = (struct _dirty_rect*)sci_malloc(sizeof(struct _dirty_rect)); + rect->next = NULL; + rect->rect = box; + + return rect; +} + + +gfx_dirty_rect_t * +gfxdr_add_dirty(gfx_dirty_rect_t *base, rect_t box, int strategy) +{ + if (box.xl < 0) { + box.x += box.xl; + box.xl = - box.xl; + } + + if (box.yl < 0) { + box.y += box.yl; + box.yl = - box.yl; + } +#ifdef GFXOP_DEBUG_DIRTY + fprintf(stderr, "Adding new dirty (%d %d %d %d)\n", + GFX_PRINT_RECT(box)); +#endif + if (_gfxop_clip(&box, gfx_rect(0, 0, 320, 200))) + return base; + + switch (strategy) { + + case GFXOP_DIRTY_FRAMES_ONE: + if (base) + base->rect = gfx_rects_merge(box, base->rect); + else + base = _rect_create(box); + break; + + case GFXOP_DIRTY_FRAMES_CLUSTERS: { + struct _dirty_rect **rectp = &(base); + + while (*rectp) { + if (gfx_rects_overlap((*rectp)->rect, box)) { + struct _dirty_rect *next = (*rectp)->next; + box = gfx_rects_merge((*rectp)->rect, box); + free(*rectp); + *rectp = next; + } else + rectp = &((*rectp)->next); + } + *rectp = _rect_create(box); + + } break; + + default: + GFXERROR("Attempt to use invalid dirty frame mode %d!\nPlease refer to gfx_options.h.", strategy); + + } + + return base; +} + +static void +_gfxop_add_dirty(gfx_state_t *state, rect_t box) +{ + if (state->disable_dirty) + return; + + state->dirty_rects = gfxdr_add_dirty(state->dirty_rects, box, state->options->dirty_frames); +} + +static inline void +_gfxop_add_dirty_x(gfx_state_t *state, rect_t box) + /* Extends the box size by one before adding (used for lines) */ +{ + if (box.xl < 0) + box.xl--; + else + box.xl++; + + if (box.yl < 0) + box.yl--; + else + box.yl++; + + _gfxop_add_dirty(state, box); +} + +static int +_gfxop_clear_dirty_rec(gfx_state_t *state, struct _dirty_rect *rect) +{ + int retval; + + if (!rect) + return GFX_OK; + +#ifdef GFXOP_DEBUG_DIRTY + fprintf(stderr, "\tClearing dirty (%d %d %d %d)\n", + GFX_PRINT_RECT(rect->rect)); +#endif + if (!state->fullscreen_override) + retval = _gfxop_update_box(state, rect->rect); + else + retval = GFX_OK; + + retval |= _gfxop_clear_dirty_rec(state, rect->next); + + free(rect); + return retval; +} + + +/*** Exported operations ***/ + +static void +init_aux_pixmap(gfx_pixmap_t **pixmap) +{ + *pixmap = gfx_pixmap_alloc_index_data(gfx_new_pixmap(320, 200, GFX_RESID_NONE, 0, 0)); + (*pixmap)->flags |= GFX_PIXMAP_FLAG_EXTERNAL_PALETTE; + (*pixmap)->colors_nr = DEFAULT_COLORS_NR; + (*pixmap)->colors = default_colors; +} + +static int +_gfxop_init_common(gfx_state_t *state, gfx_options_t *options, void *misc_payload) +{ + state->options = options; + + if (!((state->resstate = gfxr_new_resource_manager(state->version, + state->options, + state->driver, + misc_payload)))) { + GFXERROR("Failed to initialize resource manager!\n"); + return GFX_FATAL; + } + + if ((state->static_palette = + gfxr_interpreter_get_static_palette(state->resstate, + state->version, + &(state->static_palette_entries), + misc_payload))) + _gfxop_alloc_colors(state, state->static_palette, state->static_palette_entries); + + state->visible_map = GFX_MASK_VISUAL; + state->fullscreen_override = NULL; /* No magical override */ + gfxop_set_clip_zone(state, gfx_rect(0, 0, 320, 200)); + + state->mouse_pointer = state->mouse_pointer_bg = NULL; + state->mouse_pointer_visible = 0; + + init_aux_pixmap(&(state->control_map)); + init_aux_pixmap(&(state->priority_map)); + init_aux_pixmap(&(state->static_priority_map)); + + state->options = options; + state->mouse_pointer_in_hw = 0; + state->disable_dirty = 0; + state->events = NULL; + + state->pic = state->pic_unscaled = NULL; + + state->pic_nr = -1; /* Set background pic number to an invalid value */ + + state->tag_mode = 0; + + state->dirty_rects = NULL; + + + return GFX_OK; +} + +int +gfxop_init_default(gfx_state_t *state, gfx_options_t *options, void *misc_info) +{ + BASIC_CHECKS(GFX_FATAL); + if (state->driver->init(state->driver)) + return GFX_FATAL; + + return _gfxop_init_common(state, options, misc_info); +} + + +int +gfxop_init(gfx_state_t *state, int xfact, int yfact, gfx_color_mode_t bpp, + gfx_options_t *options, void *misc_info) +{ + int color_depth = bpp? bpp : 1; + int initialized = 0; + + BASIC_CHECKS(GFX_FATAL); + + do { + if (!state->driver->init_specific(state->driver, xfact, yfact, color_depth)) + initialized = 1; + else + color_depth++; + } while (!initialized && color_depth < 9 && !bpp); + + if (!initialized) + return GFX_FATAL; + + return _gfxop_init_common(state, options, misc_info); +} + + +int +gfxop_set_parameter(gfx_state_t *state, char *attribute, char *value) +{ + BASIC_CHECKS(GFX_FATAL); + + return state->driver->set_parameter(state->driver, attribute, value); +} + + +int +gfxop_exit(gfx_state_t *state) +{ + BASIC_CHECKS(GFX_ERROR); + gfxr_free_resource_manager(state->driver, state->resstate); + + if (state->control_map) { + gfx_free_pixmap(state->driver, state->control_map); + state->control_map = NULL; + } + + if (state->priority_map) { + gfx_free_pixmap(state->driver, state->priority_map); + state->priority_map = NULL; + } + + if (state->static_priority_map) { + gfx_free_pixmap(state->driver, state->static_priority_map); + state->static_priority_map = NULL; + } + + if (state->mouse_pointer_bg) { + gfx_free_pixmap(state->driver, state->mouse_pointer_bg); + state->mouse_pointer_bg = NULL; + } + + state->driver->exit(state->driver); + return GFX_OK; +} + + +int +gfxop_have_mouse(gfx_state_t *state) +{ + return state->driver->capabilities & GFX_CAPABILITY_MOUSE_SUPPORT; +} + + +static int +_gfxop_scan_one_bitmask(gfx_pixmap_t *pixmap, rect_t zone) +{ + int retval = 0; + int pixmap_xscale = pixmap->index_xl / 320; + int pixmap_yscale = pixmap->index_yl / 200; + int line_width = pixmap_yscale * pixmap->index_xl; + int startindex = (line_width * zone.y) + (zone.x * pixmap_xscale); + + startindex += pixmap_xscale >> 1; /* Center on X */ + startindex += (pixmap_yscale >> 1) * pixmap->index_xl; /* Center on Y */ + + if (_gfxop_clip(&zone, gfx_rect(0, 0, pixmap->index_xl, pixmap->index_yl))) + return 0; + + while (zone.yl--) { + int i; + for (i = 0; i < (zone.xl * pixmap_xscale); i += pixmap_xscale) + retval |= (1 << ((pixmap->index_data[startindex + i]) & 0xf)); + + startindex += line_width; + } + + return retval; +} + +int +gfxop_scan_bitmask(gfx_state_t *state, rect_t area, gfx_map_mask_t map) +{ + gfxr_pic_t *pic = (state->pic_unscaled)? state->pic_unscaled : state->pic; + int retval = 0; + + _gfxop_clip(&area, gfx_rect(0, 10, 320, 200)); + + if (area.xl <= 0 + || area.yl <= 0) + return 0; + + if (map & GFX_MASK_VISUAL) + retval |= _gfxop_scan_one_bitmask(pic->visual_map, area); + + if (map & GFX_MASK_PRIORITY) + retval |= _gfxop_scan_one_bitmask(state->priority_map, area); + if (map & GFX_MASK_CONTROL) + retval |= _gfxop_scan_one_bitmask(state->control_map, area); + + return retval; +} + + +#define MIN_X 0 +#define MIN_Y 0 +#define MAX_X 319 +#define MAX_Y 199 + +int +gfxop_set_clip_zone(gfx_state_t *state, rect_t zone) +{ + int xfact, yfact; + BASIC_CHECKS(GFX_ERROR); + + DDIRTY(stderr, "-- Setting clip zone %d %d %d %d\n", GFX_PRINT_RECT(zone)); + + xfact = state->driver->mode->xfact; + yfact = state->driver->mode->yfact; + + if (zone.x < MIN_X) { + zone.xl -= (zone.x - MIN_X); + zone.x = MIN_X; + } + + if (zone.y < MIN_Y) { + zone.yl -= (zone.y - MIN_Y); + zone.y = MIN_Y; + } + + if (zone.x + zone.xl > MAX_X) + zone.xl = MAX_X + 1 - zone.x; + + if (zone.y + zone.yl > MAX_Y) + zone.yl = MAX_Y + 1 - zone.y; + + memcpy(&(state->clip_zone_unscaled), &zone, sizeof(rect_t)); + + state->clip_zone.x = state->clip_zone_unscaled.x * xfact; + state->clip_zone.y = state->clip_zone_unscaled.y * yfact; + state->clip_zone.xl = state->clip_zone_unscaled.xl * xfact; + state->clip_zone.yl = state->clip_zone_unscaled.yl * yfact; + + return GFX_OK; +} + +int +gfxop_set_color(gfx_state_t *state, gfx_color_t *color, int r, int g, int b, int a, + int priority, int control) +{ + gfx_pixmap_color_t pixmap_color = {0}; + int error_code; + int mask = ((r >= 0 && g >= 0 && b >= 0) ? GFX_MASK_VISUAL : 0) + | ((priority >= 0)? GFX_MASK_PRIORITY : 0) + | ((control >= 0)? GFX_MASK_CONTROL : 0); + + BASIC_CHECKS(GFX_FATAL); + + if (PALETTE_MODE && a >= GFXOP_ALPHA_THRESHOLD) + mask &= ~GFX_MASK_VISUAL; + + color->mask = mask; + + color->priority = priority; + color->control = control; + + if (mask & GFX_MASK_VISUAL) { + + color->visual.r = r; + color->visual.g = g; + color->visual.b = b; + color->alpha = a; + + if (PALETTE_MODE) { + pixmap_color.r = r; + pixmap_color.g = g; + pixmap_color.b = b; + pixmap_color.global_index = GFX_COLOR_INDEX_UNMAPPED; + if ((error_code = gfx_alloc_color(state->driver->mode->palette, &pixmap_color))) { + if (error_code < 0) { + GFXWARN("Could not get color entry for %02x/%02x/%02x\n", r, g, b); + return error_code; + } else if ((error_code = state->driver->set_palette(state->driver, pixmap_color.global_index, (byte) r, (byte) g, (byte) b))) { + GFXWARN("Graphics driver failed to set color index %d to (%02x/%02x/%02x)\n", + pixmap_color.global_index, r, g, b); + return error_code; + } + } + color->visual.global_index = pixmap_color.global_index; + } + } + return GFX_OK; +} + +int +gfxop_set_system_color(gfx_state_t *state, gfx_color_t *color) +{ + gfx_palette_color_t *palette_colors; + BASIC_CHECKS(GFX_FATAL); + + if (!PALETTE_MODE) + return GFX_OK; + + if (color->visual.global_index < 0 + || color->visual.global_index >= state->driver->mode->palette->max_colors_nr) { + GFXERROR("Attempt to set invalid color index %02x as system color\n", color->visual.global_index); + return GFX_ERROR; + } + + palette_colors = state->driver->mode->palette->colors; + palette_colors[color->visual.global_index].lockers = GFX_COLOR_SYSTEM; + + return GFX_OK; +} + +int +gfxop_free_color(gfx_state_t *state, gfx_color_t *color) +{ + gfx_palette_color_t *palette_color = {0}; + gfx_pixmap_color_t pixmap_color = {0}; + int error_code; + BASIC_CHECKS(GFX_FATAL); + + if (!PALETTE_MODE) + return GFX_OK; + + if (color->visual.global_index < 0 + || color->visual.global_index >= state->driver->mode->palette->max_colors_nr) { + GFXERROR("Attempt to free invalid color index %02x\n", color->visual.global_index); + return GFX_ERROR; + } + + pixmap_color.global_index = color->visual.global_index; + palette_color = state->driver->mode->palette->colors + pixmap_color.global_index; + pixmap_color.r = palette_color->r; + pixmap_color.g = palette_color->g; + pixmap_color.b = palette_color->b; + + if ((error_code = gfx_free_color(state->driver->mode->palette, &pixmap_color))) { + GFXWARN("Failed to free color with color index %02x\n", color->visual.global_index); + return error_code; + } + + return GFX_OK; +} + +/******************************/ +/* Generic drawing operations */ +/******************************/ + + +static int +line_check_bar(int *start, int *length, int clipstart, int cliplength) +{ + int overlength; + + if (*start < clipstart) { + *length -= (clipstart - *start); + *start = clipstart; + } + + overlength = 1 + (*start + *length) - (clipstart + cliplength); + + if (overlength > 0) + *length -= overlength; + + return (*length < 0); +} + +static void +clip_line_partial(float *start, float *end, float delta_val, float pos_val, float start_val, float end_val) +{ + float my_start = (start_val - pos_val) * delta_val; + float my_end = (end_val - pos_val) * delta_val; + + if (my_end < *end) + *end = my_end; + if (my_start > *start) + *start = my_start; +} + +static int +line_clip(rect_t *line, rect_t clip, int xfact, int yfact) +/* returns 1 if nothing is left, or 0 if part of the line is in the clip window */ +{ + /* Compensate for line thickness (should match precisely) */ + clip.xl -= xfact; + clip.yl -= yfact; + + if (!line->xl) {/* vbar */ + if (line->x < clip.x || line->x >= (clip.x + clip.xl)) + return 1; + + return line_check_bar(&(line->y), &(line->yl), clip.y, clip.yl); + + } else + + if (!line->yl) {/* hbar */ + if (line->y < clip.y || line->y >= (clip.y + clip.yl)) + return 1; + + return line_check_bar(&(line->x), &(line->xl), clip.x, clip.xl); + + } else { /* "normal" line */ + float start = 0.0, end = 1.0; + float xv = (float) line->xl; + float yv = (float) line->yl; + + if (line->xl < 0) + clip_line_partial(&start, &end, (float) (1.0 / xv), (float) line->x, (float) (clip.x + clip.xl), (float) clip.x); + else + clip_line_partial(&start, &end, (float) (1.0 / xv), (float) line->x, (float) clip.x, (float) (clip.x + clip.xl)); + + if (line->yl < 0) + clip_line_partial(&start, &end, (float) (1.0 / yv), (float) line->y, (float) (clip.y + clip.yl), (float) clip.y); + else + clip_line_partial(&start, &end, (float) (1.0 / yv), (float) line->y, (float) clip.y, (float) (clip.y + clip.yl)); + + line->x += (int) (xv * start); + line->y += (int) (yv * start); + + line->xl = (int) (xv * (end-start)); + line->yl = (int) (yv * (end-start)); + + return (start > 1.0 || end < 0.0); + } + return 0; +} + +static int +point_clip(point_t *start, point_t *end, rect_t clip, int xfact, int yfact) +{ + rect_t line = gfx_rect(start->x, start->y, end->x - start->x, end->y - start->y); + int retval = line_clip(&line, clip, xfact, yfact); + + start->x = line.x; + start->y = line.y; + + end->x = line.x + line.xl; + end->y = line.y + line.yl; + + return retval; +} + + + +static void +draw_line_to_control_map(gfx_state_t *state, point_t start, point_t end, gfx_color_t color) +{ + if (color.mask & GFX_MASK_CONTROL) + if (!point_clip(&start, &end, state->clip_zone_unscaled, 0, 0)) + gfx_draw_line_pixmap_i(state->control_map, start, end, color.control); +} + +static int +simulate_stippled_line_draw(gfx_driver_t *driver, int skipone, point_t start, point_t end, gfx_color_t color, gfx_line_mode_t line_mode) + /* Draws a stippled line if this isn't supported by the driver (skipone is ignored ATM) */ +{ + int xl = end.x - start.x; + int yl = end.y - start.y; + int stepwidth = (xl)? driver->mode->xfact : driver->mode->yfact; + int dbl_stepwidth = 2*stepwidth; + int linelength = (line_mode == GFX_LINE_MODE_FINE)? stepwidth - 1 : 0; + int *posvar; + int length; + int delta; + int length_left; + + if (!xl) { /* xl = 0, so we move along yl */ + posvar = &start.y; + length = yl; + delta = (yl < 0)? -dbl_stepwidth : dbl_stepwidth; + } else { + assert (!yl); /* We don't do diagonals; that's not needed ATM. */ + posvar = &start.x; + length = xl; + delta = (xl < 0)? -dbl_stepwidth : dbl_stepwidth; + } + + length_left = length; + + if (skipone) { + length_left -= stepwidth; + *posvar += stepwidth; + } + + length /= delta; + + length_left -= length * dbl_stepwidth; + + if (xl) + xl = linelength; + else + yl = linelength; + + while (length--) { + int retval; + point_t nextpos = gfx_point(start.x + xl, start.y + yl); + + if ((retval = driver->draw_line(driver, start, nextpos, + color, line_mode, GFX_LINE_STYLE_NORMAL))) { + GFXERROR("Failed to draw partial stippled line (%d,%d) -- (%d,%d)\n", + GFX_PRINT_POINT(start), GFX_PRINT_POINT(nextpos)); + return retval; + } + *posvar += delta; + } + + if (length_left) { + int retval; + point_t nextpos; + + if (length_left > stepwidth) + length_left = stepwidth; + + if (xl) + xl = length_left; + else + if (yl) + yl = length_left; + + nextpos = gfx_point(start.x + xl, start.y + yl); + + if ((retval = driver->draw_line(driver, start, nextpos, color, line_mode, GFX_LINE_STYLE_NORMAL))) { + GFXERROR("Failed to draw partial stippled line (%d,%d) -- (%d,%d)\n", + GFX_PRINT_POINT(start), GFX_PRINT_POINT(nextpos)); + return retval; + } + } + + return GFX_OK; +} + + +static int +_gfxop_draw_line_clipped(gfx_state_t *state, point_t start, point_t end, gfx_color_t color, gfx_line_mode_t line_mode, + gfx_line_style_t line_style) +{ + int retval; + int skipone = (start.x ^ end.y) & 1; /* Used for simulated line stippling */ + + BASIC_CHECKS(GFX_FATAL); + REMOVE_POINTER; + + /* First, make sure that the line is normalized */ + if (start.y > end.y) { + point_t swap = start; + start = end; + end = swap; + } + + if (start.x < state->clip_zone.x + || start.y < state->clip_zone.y + || end.x >= (state->clip_zone.x + state->clip_zone.xl) + || end.y >= (state->clip_zone.y + state->clip_zone.yl)) + if (point_clip(&start, &end, state->clip_zone, state->driver->mode->xfact - 1, + state->driver->mode->yfact - 1)) + return GFX_OK; /* Clipped off */ + + if (line_style == GFX_LINE_STYLE_STIPPLED) { + if (start.x != end.x && start.y != end.y) { + GFXWARN("Attempt to draw stippled line which is neither an hbar nor a vbar: (%d,%d) -- (%d,%d)\n", + GFX_PRINT_POINT(start), GFX_PRINT_POINT(end)); + return GFX_ERROR; + } + if (!(state->driver->capabilities & GFX_CAPABILITY_STIPPLED_LINES)) + return simulate_stippled_line_draw(state->driver, skipone, start, end, color, line_mode); + } + + if (line_mode == GFX_LINE_MODE_FINE + && !(state->driver->capabilities & GFX_CAPABILITY_FINE_LINES)) + line_mode = GFX_LINE_MODE_FAST; + + if ((retval = state->driver->draw_line(state->driver, start, end, color, line_mode, line_style))) { + GFXERROR("Failed to draw line (%d,%d) -- (%d,%d)\n", + GFX_PRINT_POINT(start), GFX_PRINT_POINT(end)); + return retval; + } + return GFX_OK; +} + +int +gfxop_draw_line(gfx_state_t *state, point_t start, point_t end, + gfx_color_t color, gfx_line_mode_t line_mode, + gfx_line_style_t line_style) +{ + int xfact, yfact; + + BASIC_CHECKS(GFX_FATAL); + _gfxop_add_dirty_x(state, gfx_rect(start.x, start.y, end.x - start.x, end.y - start.y)); + + xfact = state->driver->mode->xfact; + yfact = state->driver->mode->yfact; + + draw_line_to_control_map(state, start, end, color); + + _gfxop_scale_point(&start, state->driver->mode); + _gfxop_scale_point(&end, state->driver->mode); + + if (line_mode == GFX_LINE_MODE_FINE) { + start.x += xfact >> 1; + start.y += yfact >> 1; + + end.x += xfact >> 1; + end.y += yfact >> 1; + } + + return _gfxop_draw_line_clipped(state, start, end, color, line_mode, line_style); +} + +int +gfxop_draw_rectangle(gfx_state_t *state, rect_t rect, gfx_color_t color, gfx_line_mode_t line_mode, + gfx_line_style_t line_style) +{ + int retval = 0; + int xfact, yfact; + int xunit, yunit; + int x, y, xl, yl; + point_t upper_left_u, upper_right_u, lower_left_u, lower_right_u; + point_t upper_left, upper_right, lower_left, lower_right; + + BASIC_CHECKS(GFX_FATAL); + REMOVE_POINTER; + + xfact = state->driver->mode->xfact; + yfact = state->driver->mode->yfact; + + if (line_mode == GFX_LINE_MODE_FINE + && state->driver->capabilities & GFX_CAPABILITY_FINE_LINES) { + xunit = yunit = 1; + xl = 1 + (rect.xl - 1) * xfact; + yl = 1 + (rect.yl - 1) * yfact; + x = rect.x * xfact + (xfact - 1); + y = rect.y * yfact + (yfact - 1); + } else { + xunit = xfact; + yunit = yfact; + xl = rect.xl * xfact; + yl = rect.yl * yfact; + x = rect.x * xfact; + y = rect.y * yfact; + } + + upper_left_u = gfx_point(rect.x, rect.y); + upper_right_u = gfx_point(rect.x + rect.xl, rect.y); + lower_left_u = gfx_point(rect.x, rect.y + rect.yl); + lower_right_u = gfx_point(rect.x + rect.xl, rect.y + rect.yl); + + upper_left = gfx_point(x, y); + upper_right = gfx_point(x + xl, y); + lower_left = gfx_point(x, y + yl); + lower_right = gfx_point(x + xl, y + yl); + +#define PARTIAL_LINE(pt1, pt2) \ + retval |= _gfxop_draw_line_clipped(state, pt1, pt2, color, line_mode, line_style); \ + draw_line_to_control_map(state, pt1##_u, pt2##_u, color); \ + _gfxop_add_dirty_x(state, \ + gfx_rect(pt1##_u.x, pt1##_u.y, pt2##_u.x - pt1##_u.x, pt2##_u.y - pt1##_u.y)) + + PARTIAL_LINE (upper_left, upper_right); + PARTIAL_LINE (upper_right, lower_right); + PARTIAL_LINE (lower_right, lower_left); + PARTIAL_LINE (lower_left, upper_left); + +#undef PARTIAL_LINE + if (retval) { + GFXERROR("Failed to draw rectangle (%d,%d)+(%d,%d)\n", rect.x, rect.y, rect.xl, rect.yl); + return retval; + } + return GFX_OK; +} + + +#define COLOR_MIX(type, dist) ((color1.type * dist) + (color2.type * (1.0 - dist))) + + +int +gfxop_draw_box(gfx_state_t *state, rect_t box, gfx_color_t color1, gfx_color_t color2, + gfx_box_shade_t shade_type) +{ + gfx_driver_t *drv = state->driver; + int reverse = 0; /* switch color1 and color2 */ + float mod_offset = 0.0, mod_breadth = 1.0; /* 0.0 to 1.0: Color adjustment */ + gfx_rectangle_fill_t driver_shade_type; + rect_t new_box; + gfx_color_t draw_color1, draw_color2 = {{0, 0, 0}, 0, 0, 0, 0}; + + BASIC_CHECKS(GFX_FATAL); + REMOVE_POINTER; + + if (PALETTE_MODE || !(state->driver->capabilities & GFX_CAPABILITY_SHADING)) + shade_type = GFX_BOX_SHADE_FLAT; + + + _gfxop_add_dirty(state, box); + + if (color1.mask & GFX_MASK_CONTROL) { + /* Write control block, clipped by 320x200 */ + memcpy(&new_box, &box, sizeof(rect_t)); + _gfxop_clip(&new_box, gfx_rect(0, 0, 320, 200)); + + gfx_draw_box_pixmap_i(state->control_map, new_box, color1.control); + } + + _gfxop_scale_rect(&box, state->driver->mode); + + if (!(color1.mask & (GFX_MASK_VISUAL | GFX_MASK_PRIORITY))) + return GFX_OK; /* So long... */ + + if (box.xl <= 1 || box.yl <= 1) { + GFXDEBUG("Attempt to draw box with size %dx%d\n", box.xl, box.yl); + return GFX_OK; + } + + memcpy(&new_box, &box, sizeof(rect_t)); + if (_gfxop_clip(&new_box, state->clip_zone)) + return GFX_OK; + + switch (shade_type) { + + case GFX_BOX_SHADE_FLAT: + driver_shade_type = GFX_SHADE_FLAT; + break; + + case GFX_BOX_SHADE_LEFT: reverse = 1; + case GFX_BOX_SHADE_RIGHT: + driver_shade_type = GFX_SHADE_HORIZONTALLY; + mod_offset = (float) (((new_box.x - box.x) * 1.0) / (box.xl * 1.0)); + mod_breadth = (float) ((new_box.xl * 1.0) / (box.xl * 1.0)); + break; + + case GFX_BOX_SHADE_UP: reverse = 1; + case GFX_BOX_SHADE_DOWN: + driver_shade_type = GFX_SHADE_VERTICALLY; + mod_offset = (float) (((new_box.y - box.y) * 1.0) / (box.yl * 1.0)); + mod_breadth = (float) ((new_box.yl * 1.0) / (box.yl * 1.0)); + break; + + default: + GFXERROR("Invalid shade type: %d\n", shade_type); + return GFX_ERROR; + } + + + if (reverse) + mod_offset = (float) (1.0 - (mod_offset + mod_breadth)); + /* Reverse offset if we have to interpret colors inversely */ + + if (shade_type == GFX_BOX_SHADE_FLAT) + return drv->draw_filled_rect(drv, new_box, color1, color1, GFX_SHADE_FLAT); + else { + if (PALETTE_MODE) { + GFXWARN("Attempting to draw shaded box in palette mode!\n"); + return GFX_ERROR; + } + + draw_color1.mask = draw_color2.mask = color1.mask; + draw_color1.priority = draw_color2.priority = color1.priority; + + if (draw_color1.mask & GFX_MASK_VISUAL) { + draw_color1.visual.r = (guint8) COLOR_MIX(visual.r, mod_offset); + draw_color1.visual.g = (guint8) COLOR_MIX(visual.g, mod_offset); + draw_color1.visual.b = (guint8) COLOR_MIX(visual.b, mod_offset); + draw_color1.alpha = (guint8) COLOR_MIX(alpha, mod_offset); + + mod_offset += mod_breadth; + + draw_color2.visual.r = (guint8) COLOR_MIX(visual.r, mod_offset); + draw_color2.visual.g = (guint8) COLOR_MIX(visual.g, mod_offset); + draw_color2.visual.b = (guint8) COLOR_MIX(visual.b, mod_offset); + draw_color2.alpha = (guint8) COLOR_MIX(alpha, mod_offset); + } + if (reverse) + return drv->draw_filled_rect(drv, new_box, draw_color2, draw_color1, driver_shade_type); + else + return drv->draw_filled_rect(drv, new_box, draw_color1, draw_color2, driver_shade_type); + } +} +#undef COLOR_MIX + + +int +gfxop_fill_box(gfx_state_t *state, rect_t box, gfx_color_t color) +{ + return gfxop_draw_box(state, box, color, color, GFX_BOX_SHADE_FLAT); +} + + + +static int +_gfxop_buffer_propagate_box(gfx_state_t *state, rect_t box, gfx_buffer_t buffer) +{ + int error; + + if (_gfxop_clip(&box, gfx_rect(0, 0, 320 * state->driver->mode->xfact, 200 * state->driver->mode->yfact))) + return GFX_OK; + + if ((error = state->driver->update(state->driver, box, gfx_point(box.x, box.y), buffer))) { + GFXERROR("Error occured while updating region (%d,%d,%d,%d) in buffer %d\n", + box.x, box.y, box.xl, box.yl, buffer); + return error; + } + return GFX_OK; +} + +extern int sci0_palette; +int +gfxop_clear_box(gfx_state_t *state, rect_t box) +{ + BASIC_CHECKS(GFX_FATAL); + REMOVE_POINTER; + _gfxop_add_dirty(state, box); + DDIRTY(stderr, "[] clearing box %d %d %d %d\n", GFX_PRINT_RECT(box)); + if (box.x == 29 + && box.y == 77 + && (sci0_palette == 1)) { + BREAKPOINT(); + } + + _gfxop_clip(&box, gfx_rect(0, 0, 320, 200)); +#ifdef PRECISE_PRIORITY_MAP + if (state->pic_unscaled) + gfx_copy_pixmap_box_i(state->priority_map, state->static_priority_map, box); +#endif + + _gfxop_scale_rect(&box, state->driver->mode); + + return _gfxop_buffer_propagate_box(state, box, GFX_BUFFER_BACK); +} + +int +gfxop_set_visible_map(gfx_state_t *state, gfx_map_mask_t visible_map) +{ + switch (visible_map) { + + case GFX_MASK_VISUAL: + state->fullscreen_override = NULL; + if (visible_map != state->visible_map) { + rect_t rect = gfx_rect(0, 0, 320, 200); + gfxop_clear_box(state, rect); + gfxop_update_box(state, rect); + } + break; + + case GFX_MASK_PRIORITY: + state->fullscreen_override = state->priority_map; + break; + + case GFX_MASK_CONTROL: + state->fullscreen_override = state->control_map; + break; + + default: + fprintf(stderr, "Invalid display map %d selected!\n", visible_map); + return GFX_ERROR; + } + + state->visible_map = visible_map; + return GFX_OK; +} + +int +gfxop_update(gfx_state_t *state) +{ + int retval; + + BASIC_CHECKS(GFX_FATAL); + DRAW_POINTER; + + retval = _gfxop_clear_dirty_rec(state, state->dirty_rects); + + state->dirty_rects = NULL; + + if (state->fullscreen_override) { + /* We've been asked to re-draw the active full-screen image, essentially. */ + rect_t rect = gfx_rect(0, 0, 320, 200); + gfx_xlate_pixmap(state->fullscreen_override, state->driver->mode, GFX_XLATE_FILTER_NONE); + gfxop_draw_pixmap(state, state->fullscreen_override, rect, gfx_point(0,0)); + retval |= _gfxop_update_box(state, rect); + } + + if (retval) { + GFXERROR("Clearing the dirty rectangles failed!\n"); + } + + if (state->tag_mode) { + /* This usually happens after a pic and all resources have been drawn */ + gfxr_free_tagged_resources(state->driver, state->resstate); + state->tag_mode = 0; + } + + return retval; +} + + +int +gfxop_update_box(gfx_state_t *state, rect_t box) +{ + BASIC_CHECKS(GFX_FATAL); + DRAW_POINTER; + + if (state->disable_dirty) + _gfxop_update_box(state, box); + else + _gfxop_add_dirty(state, box); + + return gfxop_update(state); +} + +int +gfxop_enable_dirty_frames(gfx_state_t *state) +{ + BASIC_CHECKS(GFX_ERROR); + state->disable_dirty = 0; + + return GFX_OK; +} + +int +gfxop_disable_dirty_frames(gfx_state_t *state) +{ + BASIC_CHECKS(GFX_ERROR); + + state->disable_dirty = 1; + + return GFX_OK; +} + + +/**********************/ +/* Pointer and IO ops */ +/**********************/ + +#define SECONDS_OF_DAY (24*60*60) +#define MILLION 1000000 +/* Sure, this may seem silly, but it's too easy to miss a zero...) */ + + +#define GFXOP_FULL_POINTER_REFRESH if (_gfxop_full_pointer_refresh(state)) { GFXERROR("Failed to do full pointer refresh!\n"); return GFX_ERROR; } + +static int +_gfxop_full_pointer_refresh(gfx_state_t *state) +{ + rect_t pointer_bounds; + rect_t old_pointer_bounds = {0, 0, 0, 0}; + int new_x = state->driver->pointer_x; + int new_y = state->driver->pointer_y; + + if (new_x != state->old_pointer_draw_pos.x + || new_y != state->old_pointer_draw_pos.y) { + point_t pp_new = gfx_point(new_x / state->driver->mode->xfact, + new_y / state->driver->mode->yfact); + + if (!_gfxop_get_pointer_bounds(state, &pointer_bounds)) { + memcpy(&old_pointer_bounds, &(state->pointer_bg_zone), sizeof(rect_t)); + REMOVE_POINTER; + state->pointer_pos = pp_new; + + DRAW_POINTER; + if (_gfxop_buffer_propagate_box(state, pointer_bounds, GFX_BUFFER_FRONT)) return 1; + if (_gfxop_buffer_propagate_box(state, old_pointer_bounds, GFX_BUFFER_FRONT)) return 1; + + state->old_pointer_draw_pos = gfx_point(new_x, new_y); + } else + state->pointer_pos = pp_new; + } + return 0; +} + +int +gfxop_usleep(gfx_state_t *state, long usecs) +{ + long time, utime; + long wakeup_time, wakeup_utime; + long add_seconds; + int retval = GFX_OK; + + BASIC_CHECKS(GFX_FATAL); + + sci_gettime(&wakeup_time, &wakeup_utime); + wakeup_utime += usecs; + + add_seconds = (wakeup_utime / MILLION); + wakeup_time += add_seconds; + wakeup_utime -= (MILLION * add_seconds); + + do { + GFXOP_FULL_POINTER_REFRESH; + sci_gettime(&time, &utime); + usecs = (wakeup_time - time) * MILLION + wakeup_utime - utime; + } while ((usecs > 0) && !(retval = state->driver->usec_sleep(state->driver, usecs))); + + if (retval) { + GFXWARN("Waiting failed\n"); + } + + return retval; +} + + +int +_gfxop_set_pointer(gfx_state_t *state, gfx_pixmap_t *pxm) +{ + rect_t old_pointer_bounds = {0}; + rect_t pointer_bounds = {0}; + int retval = -1; + int draw_old; + int draw_new = 0; + + BASIC_CHECKS(GFX_FATAL); + + draw_old = state->mouse_pointer != NULL; + + if (state->driver->capabilities & GFX_CAPABILITY_MOUSE_POINTER) { + + if (draw_old && state->mouse_pointer->colors_nr > 2) + draw_old = state->driver->capabilities & GFX_CAPABILITY_COLOR_MOUSE_POINTER; + + if (!draw_old + && state->mouse_pointer + && (state->driver->capabilities & GFX_CAPABILITY_POINTER_PIXMAP_REGISTRY)) + if ((retval = state->driver->unregister_pixmap(state->driver, state->mouse_pointer))){ + GFXERROR("Pointer un-registration failed!\n"); + return retval; + } + + if (pxm == NULL + || (state->driver->capabilities & GFX_CAPABILITY_COLOR_MOUSE_POINTER) + || pxm->colors_nr <= 2) { + if (state->driver->capabilities & GFX_CAPABILITY_POINTER_PIXMAP_REGISTRY) { + if ((pxm) && (retval = state->driver->register_pixmap(state->driver, pxm))) { + GFXERROR("Pixmap-registering a new mouse pointer failed!\n"); + return retval; + } + } + draw_new = 0; + state->driver->set_pointer(state->driver, pxm); + state->mouse_pointer_in_hw = 1; + } else { + draw_new = 1; + state->mouse_pointer_in_hw = 0; + } + + } else draw_new = 1; + + if (!state->mouse_pointer_in_hw) + draw_old = state->mouse_pointer != NULL; + + + if (draw_old) { + _gfxop_get_pointer_bounds(state, &old_pointer_bounds); + REMOVE_POINTER; + } + + + if (draw_new) { + state->mouse_pointer = pxm; + DRAW_POINTER; + _gfxop_get_pointer_bounds(state, &pointer_bounds); + } + + if (draw_new && state->mouse_pointer) + _gfxop_buffer_propagate_box(state, pointer_bounds, GFX_BUFFER_FRONT); + + if (draw_old) + _gfxop_buffer_propagate_box(state, old_pointer_bounds, GFX_BUFFER_FRONT); + + if (state->mouse_pointer == NULL) + state->mouse_pointer_visible = 0; + else if (!state->mouse_pointer_visible) + state->mouse_pointer_visible = 1; + /* else don't touch it, as it might be VISIBLE_BUT_CLIPPED! */ + + return GFX_OK; +} + + +int +gfxop_set_pointer_cursor(gfx_state_t *state, int nr) +{ + gfx_pixmap_t *new_pointer = NULL; + + BASIC_CHECKS(GFX_FATAL); + + if (nr == GFXOP_NO_POINTER) + new_pointer = NULL; + else { + new_pointer = gfxr_get_cursor(state->resstate, nr); + + if (!new_pointer) { + GFXWARN("Attempt to set invalid pointer #%d\n", nr); + } + } + + return _gfxop_set_pointer(state, new_pointer); +} + + +int +gfxop_set_pointer_view(gfx_state_t *state, int nr, int loop, int cel, point_t *hotspot) +{ + int real_loop = loop; + int real_cel = cel; + gfx_pixmap_t *new_pointer = NULL; + + BASIC_CHECKS(GFX_FATAL); + + new_pointer = _gfxr_get_cel(state, nr, &real_loop, &real_cel, + 0); /* FIXME: For now, don't palettize pointers */ + + if (hotspot) + { + new_pointer->xoffset = hotspot->x; + new_pointer->yoffset = hotspot->y; + } + + if (!new_pointer) { + GFXWARN("Attempt to set invalid pointer #%d\n", nr); + return GFX_ERROR; + } else + { + if (real_loop != loop || real_cel != cel) { + GFXDEBUG("Changed loop/cel from %d/%d to %d/%d in view %d\n", + loop, cel, real_loop, real_cel, nr); + } + return _gfxop_set_pointer(state, new_pointer); + } +} + +int +gfxop_set_pointer_position(gfx_state_t *state, point_t pos) +{ + BASIC_CHECKS(GFX_ERROR); + + state->pointer_pos = pos; + + if (pos.x > 320 || pos.y > 200) + { + GFXWARN("Attempt to place pointer at invalid coordinates (%d, %d)\n", pos.x, pos.y); + return 0; /* Not fatal */ + } + + state->driver->pointer_x = pos.x * state->driver->mode->xfact; + state->driver->pointer_y = pos.y * state->driver->mode->yfact; + + GFXOP_FULL_POINTER_REFRESH; + return 0; +} + +#define SCANCODE_ROWS_NR 3 + +struct scancode_row { + int offset; + const char *keys; +} scancode_rows[SCANCODE_ROWS_NR] = { + {0x10, "QWERTYUIOP[]"}, + {0x1e, "ASDFGHJKL;'\\"}, + {0x2c, "ZXCVBNM,./"} +}; + +static int +_gfxop_scancode(int ch) + /* Calculates a PC keyboard scancode from a character */ +{ + int row; + int c = toupper((char)ch); + + for (row = 0; row < SCANCODE_ROWS_NR; row++) { + const char *keys = scancode_rows[row].keys; + int offset = scancode_rows[row].offset; + + while (*keys) { + if (*keys == c) + return offset << 8; + + offset++; + keys++; + } + } + + return ch; /* not found */ +} + +/* static */ int +_gfxop_shiftify(int c) +{ + char shifted_numbers[] = ")!@#$%^&*("; + + if (c < 256) + { + c = toupper((char)c); + + if (c >= 'A' && c <= 'Z') + return c; + + if (c >= '0' && c <= '9') + return shifted_numbers[c-'0']; + + switch (c) { + case SCI_K_TAB: return SCI_K_SHIFT_TAB; + case ']': return '}'; + case '[': return '{'; + case '`': return '~'; + case '-': return '_'; + case '=': return '+'; + case ';': return ':'; + case '\'': return '"'; + case '\\': return '|'; + case ',': return '<'; + case '.': return '>'; + case '/': return '?'; + default: return c; /* No match */ + } + } + + switch (c) + { + case SCI_K_F1 : return SCI_K_SHIFT_F1; + case SCI_K_F2 : return SCI_K_SHIFT_F2; + case SCI_K_F3 : return SCI_K_SHIFT_F3; + case SCI_K_F4 : return SCI_K_SHIFT_F4; + case SCI_K_F5 : return SCI_K_SHIFT_F5; + case SCI_K_F6 : return SCI_K_SHIFT_F6; + case SCI_K_F7 : return SCI_K_SHIFT_F7; + case SCI_K_F8 : return SCI_K_SHIFT_F8; + case SCI_K_F9 : return SCI_K_SHIFT_F9; + case SCI_K_F10 : return SCI_K_SHIFT_F10; + } + + return c; +} + +static int +_gfxop_numlockify(int c) +{ + switch (c) { + case SCI_K_DELETE: return '.'; + case SCI_K_INSERT: return '0'; + case SCI_K_END: return '1'; + case SCI_K_DOWN: return '2'; + case SCI_K_PGDOWN: return '3'; + case SCI_K_LEFT: return '4'; + case SCI_K_CENTER: return '5'; + case SCI_K_RIGHT: return '6'; + case SCI_K_HOME: return '7'; + case SCI_K_UP: return '8'; + case SCI_K_PGUP: return '9'; + default: return c; /* Unchanged */ + } +} + + +sci_event_t +gfxop_get_event(gfx_state_t *state, unsigned int mask) +{ + sci_event_t error_event = { SCI_EVT_ERROR, 0, 0 }; + sci_event_t event; + gfx_input_event_t **seekerp = &(state->events); + + BASIC_CHECKS(error_event); + if (_gfxop_remove_pointer(state)) { + GFXERROR("Failed to remove pointer before processing event!\n"); + } + + while (*seekerp && !((*seekerp)->event.type & mask)) + seekerp = &((*seekerp)->next); + + if (*seekerp) { + gfx_input_event_t *goner = *seekerp; + event = goner->event; + *seekerp = goner->next; + free(goner); + } else { + event.type = 0; + + if (!(mask & SCI_EVT_NONBLOCK)) + { + do { + if (event.type) { + *seekerp = (gfx_input_event_t*)sci_malloc(sizeof(gfx_input_event_t)); + (*seekerp)->next = NULL; + + event.data = (char) (event.data); + /* Clip illegal bits */ + + (*seekerp)->event = event; + seekerp = &((*seekerp)->next); + } + event = state->driver->get_event(state->driver); + + } while (event.type && !(event.type & mask)); + } + } + + if (_gfxop_full_pointer_refresh(state)) { + GFXERROR("Failed to update the mouse pointer!\n"); + return error_event; + } + + if (event.type == SCI_EVT_KEYBOARD + && !(state->driver->capabilities & GFX_CAPABILITY_KEYTRANSLATE)) { + /* Do we still have to translate the key? */ + + event.character = event.data; + + /* Scancodify if appropriate */ + if (event.buckybits & SCI_EVM_ALT) + event.character = _gfxop_scancode(event.character); + + /* Shift if appropriate */ + else if (((event.buckybits & (SCI_EVM_RSHIFT | SCI_EVM_LSHIFT)) + && !(event.buckybits & SCI_EVM_CAPSLOCK)) + || + (!(event.buckybits & (SCI_EVM_RSHIFT | SCI_EVM_LSHIFT)) + && (event.buckybits & SCI_EVM_CAPSLOCK))) + event.character = _gfxop_shiftify(event.character); + + /* Numlockify if appropriate */ + else if (event.buckybits & SCI_EVM_NUMLOCK) + event.data = _gfxop_numlockify(event.data); + } + + return event; +} + + +/*******************/ +/* View operations */ +/*******************/ + +int +gfxop_lookup_view_get_loops(gfx_state_t *state, int nr) +{ + int loop = 0, cel = 0; + gfxr_view_t *view = NULL; + + BASIC_CHECKS(GFX_ERROR); + + view = gfxr_get_view(state->resstate, nr, &loop, &cel, 0); + + if (!view) { + GFXWARN("Attempt to retreive number of loops from invalid view %d\n", nr); + return 0; + } + + return view->loops_nr; +} + + +int +gfxop_lookup_view_get_cels(gfx_state_t *state, int nr, int loop) +{ + int real_loop = loop, cel = 0; + gfxr_view_t *view = NULL; + + BASIC_CHECKS(GFX_ERROR); + + view = gfxr_get_view(state->resstate, nr, &real_loop, &cel, 0); + + if (!view) { + GFXWARN("Attempt to retreive number of cels from invalid/broken view %d\n", nr); + return 0; + } else if (real_loop != loop) { + GFXWARN("Loop number was corrected from %d to %d in view %d\n", loop, real_loop, nr); + } + + return view->loops[real_loop].cels_nr; +} + + +int +gfxop_check_cel(gfx_state_t *state, int nr, int *loop, int *cel) +{ + BASIC_CHECKS(GFX_ERROR); + + if (!gfxr_get_view(state->resstate, nr, loop, cel, 0)) { + GFXWARN("Attempt to verify loop/cel values for invalid view %d\n", nr); + return GFX_ERROR; + } + + return GFX_OK; +} + +int +gfxop_overflow_cel(gfx_state_t *state, int nr, int *loop, int *cel) +{ + int loop_v = *loop; + int cel_v = *cel; + BASIC_CHECKS(GFX_ERROR); + + if (!gfxr_get_view(state->resstate, nr, &loop_v, &cel_v, 0)) { + GFXWARN("Attempt to verify loop/cel values for invalid view %d\n", nr); + return GFX_ERROR; + } + + if (loop_v != *loop) + *loop = 0; + + if (loop_v != *loop + || cel_v != *cel) + *cel = 0; + + return GFX_OK; +} + + +int +gfxop_get_cel_parameters(gfx_state_t *state, int nr, int loop, int cel, + int *width, int *height, point_t *offset) +{ + gfxr_view_t *view = NULL; + gfx_pixmap_t *pxm = NULL; + BASIC_CHECKS(GFX_ERROR); + + if (!(view = gfxr_get_view(state->resstate, nr, &loop, &cel, 0))) { + GFXWARN("Attempt to get cel parameters for invalid view %d\n", nr); + return GFX_ERROR; + } + + pxm = view->loops[loop].cels[cel]; + *width = pxm->index_xl; + *height = pxm->index_yl; + offset->x = pxm->xoffset; + offset->y = pxm->yoffset; + + return GFX_OK; +} + + +static int +_gfxop_draw_cel_buffer(gfx_state_t *state, int nr, int loop, int cel, + point_t pos, gfx_color_t color, int static_buf, + int palette) +{ + int priority = (color.mask & GFX_MASK_PRIORITY)? color.priority : -1; + int control = (color.mask & GFX_MASK_CONTROL)? color.control : -1; + gfxr_view_t *view = NULL; + gfx_pixmap_t *pxm = NULL; + int old_x, old_y; + BASIC_CHECKS(GFX_FATAL); + + if (!(view = gfxr_get_view(state->resstate, nr, &loop, &cel, palette))) { + GFXWARN("Attempt to draw loop/cel %d/%d in invalid view %d\n", loop, cel, nr); + return GFX_ERROR; + } + pxm = view->loops[loop].cels[cel]; + + old_x = pos.x -= pxm->xoffset; + old_y = pos.y -= pxm->yoffset; + + pos.x *= state->driver->mode->xfact; + pos.y *= state->driver->mode->yfact; + + if (!static_buf) + _gfxop_add_dirty(state, gfx_rect(old_x, old_y, pxm->index_xl, pxm->index_yl)); + + return _gfxop_draw_pixmap(state->driver, pxm, priority, control, + gfx_rect(0, 0, pxm->xl, pxm->yl), + gfx_rect(pos.x, pos.y, pxm->xl, pxm->yl), + state->clip_zone, + static_buf , state->control_map, + static_buf + ? state->static_priority_map + : state->priority_map); +} + + +int +gfxop_draw_cel(gfx_state_t *state, int nr, int loop, int cel, point_t pos, + gfx_color_t color, int palette) +{ + return _gfxop_draw_cel_buffer(state, nr, loop, cel, pos, color, 0, palette); +} + + +int +gfxop_draw_cel_static(gfx_state_t *state, int nr, int loop, int cel, point_t pos, + gfx_color_t color, int palette) +{ + int retval; + rect_t oldclip = state->clip_zone; + + state->clip_zone = gfx_rect_fullscreen; + _gfxop_scale_rect(&(state->clip_zone), state->driver->mode); + retval = gfxop_draw_cel_static_clipped(state, nr, loop, cel, pos, color, + palette); + /* Except that the area it's clipped against is... unusual ;-) */ + state->clip_zone = oldclip; + + return retval; +} + + +int +gfxop_draw_cel_static_clipped(gfx_state_t *state, int nr, int loop, int cel, + point_t pos, gfx_color_t color, int palette) +{ + return _gfxop_draw_cel_buffer(state, nr, loop, cel, pos, color, 1, palette); +} + + +/******************/ +/* Pic operations */ +/******************/ + +static int +_gfxop_set_pic(gfx_state_t *state) +{ + gfx_copy_pixmap_box_i(state->control_map, state->pic->control_map, gfx_rect(0, 0, 320, 200)); + gfx_copy_pixmap_box_i(state->priority_map, state->pic_unscaled->priority_map, gfx_rect(0, 0, 320, 200)); + gfx_copy_pixmap_box_i(state->static_priority_map, state->pic_unscaled->priority_map, gfx_rect(0, 0, 320, 200)); + + _gfxop_install_pixmap(state->driver, state->pic->visual_map); + + if (state->options->pic0_unscaled) + state->pic->priority_map = gfx_pixmap_scale_index_data(state->pic->priority_map, state->driver->mode); + return state->driver->set_static_buffer(state->driver, state->pic->visual_map, state->pic->priority_map); +} + + +void * +gfxop_get_pic_metainfo(gfx_state_t *state) +{ + return (state->pic)? state->pic->internal : NULL; +} + + +int +gfxop_new_pic(gfx_state_t *state, int nr, int flags, int default_palette) +{ + BASIC_CHECKS(GFX_FATAL); + + gfxr_tag_resources(state->resstate); + state->tag_mode = 1; + state->palette_nr = default_palette; + + state->pic = gfxr_get_pic(state->resstate, nr, GFX_MASK_VISUAL, flags, default_palette, 1); + + if (state->driver->mode->xfact == 1 && state->driver->mode->yfact == 1) + state->pic_unscaled = state->pic; + else + state->pic_unscaled = gfxr_get_pic(state->resstate, nr, GFX_MASK_VISUAL, flags, default_palette, 0); + + if (!state->pic || !state->pic_unscaled) { + GFXERROR("Could not retreive background pic %d!\n", nr); + if (state->pic) { + GFXERROR(" -- Inconsistency: scaled pic _was_ retreived!\n"); + } + + if (state->pic_unscaled) { + GFXERROR(" -- Inconsistency: unscaled pic _was_ retreived!\n"); + } + + state->pic = state->pic_unscaled = NULL; + return GFX_ERROR; + } + + state->pic_nr = nr; + + return _gfxop_set_pic(state); +} + + +int +gfxop_add_to_pic(gfx_state_t *state, int nr, int flags, int default_palette) +{ + BASIC_CHECKS(GFX_FATAL); + + if (!state->pic) { + GFXERROR("Attempt to add to pic with no pic active!\n"); + return GFX_ERROR; + } + + if (!(state->pic = gfxr_add_to_pic(state->resstate, state->pic_nr, nr, + GFX_MASK_VISUAL, flags, state->palette_nr, default_palette, 1))) { + GFXERROR("Could not add pic #%d to pic #%d!\n", state->pic_nr, nr); + return GFX_ERROR; + } + state->pic_unscaled = gfxr_add_to_pic(state->resstate, state->pic_nr, nr, + GFX_MASK_VISUAL, flags, + state->palette_nr, + default_palette, 1); + + return _gfxop_set_pic(state); +} + + +/*******************/ +/* Text operations */ +/*******************/ + + +int +gfxop_get_font_height(gfx_state_t *state, int font_nr) +{ + gfx_bitmap_font_t *font; + BASIC_CHECKS(GFX_FATAL); + + font = gfxr_get_font(state->resstate, font_nr, 0); + if (!font) + return GFX_ERROR; + + return font->line_height; +} + +int +gfxop_get_text_params(gfx_state_t *state, int font_nr, const char *text, + int maxwidth, int *width, int *height, int text_flags, + int *lines_nr, int *lineheight, int *lastline_width) +{ + text_fragment_t *textsplits; + gfx_bitmap_font_t *font; + + BASIC_CHECKS(GFX_FATAL); + + font = gfxr_get_font(state->resstate, font_nr, 0); + + if (!font) { + GFXERROR("Attempt to calculate text size with invalid font #%d\n", font_nr); + *width = *height = 0; + return GFX_ERROR; + } + + textsplits = gfxr_font_calculate_size(font, maxwidth, text, width, + height, lines_nr, + lineheight, lastline_width, + (state->options->workarounds & GFX_WORKAROUND_WHITESPACE_COUNT) + | text_flags); + + + if (!textsplits) { + GFXERROR("Could not calculate text size!"); + *width = *height = 0; + return GFX_ERROR; + } + + free(textsplits); + return GFX_OK; +} + + +#define COL_XLATE(des,src) \ + des = src.visual; /* The new gfx_color_t structure makes things a lot easier :-) */ /* \ + if (gfxop_set_color(state, &src, \ + src.visual.r, \ + src.visual.g, \ + src.visual.b, \ + src.alpha, \ + src.priority, \ + src.control)) \ + { \ + GFXERROR("Unable to set up colors"); \ + return NULL; \ + } +*/ + +gfx_text_handle_t * +gfxop_new_text(gfx_state_t *state, int font_nr, char *text, int maxwidth, + gfx_alignment_t halign, gfx_alignment_t valign, + gfx_color_t color1, gfx_color_t color2, gfx_color_t bg_color, + int flags) +{ + gfx_text_handle_t *handle = {0}; + gfx_bitmap_font_t *font = {0}; + int i; + gfx_pixmap_color_t pxm_col1, pxm_col2, pxm_colbg= {0}; + BASIC_CHECKS(NULL); + + COL_XLATE(pxm_col1, color1); + COL_XLATE(pxm_col2, color2); + COL_XLATE(pxm_colbg, bg_color); + + font = gfxr_get_font(state->resstate, font_nr, 0); + + if (!font) { + GFXERROR("Attempt to draw text with invalid font #%d\n", font_nr); + return NULL; + } + + handle = (gfx_text_handle_t*)sci_malloc(sizeof(gfx_text_handle_t)); + + handle->text = (char*)sci_malloc(strlen(text) + 1); + strcpy(handle->text, text); + handle->halign = halign; + handle->valign = valign; + handle->line_height = font->line_height; + + handle->lines = + gfxr_font_calculate_size(font, maxwidth, handle->text, &(handle->width), &(handle->height), + &(handle->lines_nr), + NULL, NULL, + ((state->options->workarounds & GFX_WORKAROUND_WHITESPACE_COUNT)? + GFXR_FONT_FLAG_COUNT_WHITESPACE : 0) + | flags); + + if (!handle->lines) { + free(handle->text); + free(handle); + GFXERROR("Could not calculate text parameters in font #%d\n", font_nr); + return NULL; + } + + + if (flags & GFXR_FONT_FLAG_NO_NEWLINES) { + handle->lines_nr = 1; + handle->lines->length = strlen(text); + } + + handle->text_pixmaps = (gfx_pixmap_t**)sci_malloc(sizeof(gfx_pixmap_t *) * handle->lines_nr); + + for (i = 0; i < handle->lines_nr; i++) { + int chars_nr = handle->lines[i].length; + + handle->text_pixmaps[i] = gfxr_draw_font(font, handle->lines[i].offset, chars_nr, + (color1.mask & GFX_MASK_VISUAL)? &pxm_col1 : NULL, + (color2.mask & GFX_MASK_VISUAL)? &pxm_col2 : NULL, + (bg_color.mask & GFX_MASK_VISUAL)? &pxm_colbg : NULL); + + if (!handle->text_pixmaps[i]) { + int j; + + for (j = 0; j < i; j++) + gfx_free_pixmap(state->driver, handle->text_pixmaps[j]); + sci_free(handle->text_pixmaps); + sci_free(handle->text); + sci_free(handle->lines); + GFXERROR("Failed to draw text pixmap for line %d/%d\n", i, handle->lines_nr); + sci_free(handle); + return NULL; + } + } + + handle->font = font; + + handle->priority = (color1.mask & GFX_MASK_PRIORITY)? color1.priority : -1; + handle->control = (color1.mask & GFX_MASK_CONTROL)? color1.control : -1; + + return handle; +} + + +int +gfxop_free_text(gfx_state_t *state, gfx_text_handle_t *handle) +{ + int j; + + BASIC_CHECKS(GFX_ERROR); + + if (handle->text_pixmaps) { + for (j = 0; j < handle->lines_nr; j++) + gfx_free_pixmap(state->driver, handle->text_pixmaps[j]); + free(handle->text_pixmaps); + } + + free(handle->text); + free(handle->lines); + free(handle); + return GFX_OK; +} + + +int +gfxop_draw_text(gfx_state_t *state, gfx_text_handle_t *handle, rect_t zone) +{ + int line_height; + rect_t pos; + int i; + BASIC_CHECKS(GFX_FATAL); + REMOVE_POINTER; + + if (!handle) { + GFXERROR("Attempt to draw text with NULL handle!\n"); + return GFX_ERROR; + } + + if (!handle->lines_nr) { + GFXDEBUG("Skipping draw_text operation because number of lines is zero\n"); + return GFX_OK; + } + + _gfxop_scale_rect(&zone, state->driver->mode); + + line_height = handle->line_height * state->driver->mode->yfact; + + pos.y = zone.y; + + switch (handle->valign) { + + case ALIGN_TOP: + break; + + case ALIGN_CENTER: + pos.y += (zone.yl - (line_height * handle->lines_nr)) >> 1; + break; + + case ALIGN_BOTTOM: + pos.y += (zone.yl - (line_height * handle->lines_nr)); + break; + + default: + GFXERROR("Invalid vertical alignment %d!\n", handle->valign); + return GFX_FATAL; /* Internal error... */ + } + + for (i = 0; i < handle->lines_nr; i++) { + + gfx_pixmap_t *pxm = handle->text_pixmaps[i]; + + if (!pxm->data) { + gfx_xlate_pixmap(pxm, state->driver->mode, state->options->text_xlate_filter); + gfxr_endianness_adjust(pxm, state->driver->mode); /* FIXME: resmgr layer! */ + } + if (!pxm) { + GFXERROR("Could not find text pixmap %d/%d\n", i, handle->lines_nr); + return GFX_ERROR; + } + + pos.x = zone.x; + + switch (handle->halign) { + + case ALIGN_LEFT: + break; + + case ALIGN_CENTER: + pos.x += (zone.xl - pxm->xl) >> 1; + break; + + case ALIGN_RIGHT: + pos.x += (zone.xl - pxm->xl); + break; + + default: + GFXERROR("Invalid vertical alignment %d!\n", handle->valign); + return GFX_FATAL; /* Internal error... */ + } + + pos.xl = pxm->xl; + pos.yl = pxm->yl; + + _gfxop_add_dirty(state, pos); + + _gfxop_draw_pixmap(state->driver, pxm, handle->priority, handle->control, + gfx_rect(0, 0, pxm->xl, pxm->yl), pos, state->clip_zone, 0, + state->control_map, state->priority_map); + + pos.y += line_height; + } + + return GFX_OK; +} + + +gfx_pixmap_t * +gfxop_grab_pixmap(gfx_state_t *state, rect_t area) +{ + gfx_pixmap_t *pixmap = NULL; + rect_t resultzone; /* Ignored for this application */ + BASIC_CHECKS(NULL); + if (_gfxop_remove_pointer(state)) { + GFXERROR("Could not remove pointer!\n"); + return NULL; + } + + _gfxop_scale_rect(&area, state->driver->mode); + if (_gfxop_grab_pixmap(state, &pixmap, area.x, area.y, area.xl, area.yl, 0, &resultzone)) + return NULL; /* area CUT the visual screen had a null or negative size */ + + pixmap->flags |= GFX_PIXMAP_FLAG_PALETTE_SET | GFX_PIXMAP_FLAG_DONT_UNALLOCATE_PALETTE; + + return pixmap; +} + +int +gfxop_draw_pixmap(gfx_state_t *state, gfx_pixmap_t *pxm, rect_t zone, point_t pos) +{ + rect_t target; + BASIC_CHECKS(GFX_ERROR); + + if (!pxm) { + GFXERROR("Attempt to draw NULL pixmap!\n"); + return GFX_ERROR; + } + + REMOVE_POINTER; + + target = gfx_rect(pos.x, pos.y, zone.xl, zone.yl); + + _gfxop_add_dirty(state, target); + + if (!pxm) { + GFXERROR("Attempt to draw_pixmap with pxm=NULL\n"); + return GFX_ERROR; + } + + _gfxop_scale_rect(&zone, state->driver->mode); + _gfxop_scale_rect(&target, state->driver->mode); + + return _gfxop_draw_pixmap(state->driver, pxm, -1, -1, zone, target, + gfx_rect(0, 0, 320*state->driver->mode->xfact, + 200*state->driver->mode->yfact), 0, NULL, NULL); +} + +int +gfxop_free_pixmap(gfx_state_t *state, gfx_pixmap_t *pxm) +{ + BASIC_CHECKS(GFX_ERROR); + gfx_free_pixmap(state->driver, pxm); + return GFX_OK; +} + |