/* * Mosaic.c -- Implementation of Mosaic item. * * Authors : Patrick Lecoanet. * Creation date : * * $Id$ */ /* * Copyright (c) 1993 - 1999 CENA, Patrick Lecoanet -- * * This code is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this code; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* ********************************************************************************** * * Included files * ********************************************************************************** */ #include #include "Item.h" #include "Geo.h" /* PLC : à completer */ /* ********************************************************************************** * * Constants. * ********************************************************************************** */ static const char rcsid[] = "$Imagine: Mosaic.c,v 1.7 1997/08/11 12:29:18 lecoanet Exp $"; static const char compile_id[]="$Compile: " __FILE__ " " __DATE__ " " __TIME__ " $"; static const char invalid_attribute[] = "ZnMosaic : Invalid attribute."; static const char read_only_attribute[] = "This attribute is read only."; /* ********************************************************************************** * * Specific Mosaic item record * ********************************************************************************** */ typedef struct _MosaicItemStruct { ItemStruct header; /* Public data */ int x; /* Origin world coordinates */ int y; int tile_size; int row_count; /* Number of tiles per row */ int column_count; /* Number of tiles per column */ ZnColor *tile_palette; /* Color palette for tiles */ int *color_usage; /* Numbers of merged rectangles */ /* using the corresponding color */ /* in the palette. */ int palette_size; /* Number of colors in palette */ unsigned char *tile_colors; /* Color for each tile */ /* Private data */ } MosaicItemStruct, *MosaicItem; /* ********************************************************************************** * * Init -- * ********************************************************************************** */ static void Init(Item item) { MosaicItem mosaic = (MosaicItem) item; #ifdef XTOOLKIT ITEM_COMMON_INIT(item, NULL, wi->mosaic_res.visibility, wi->mosaic_res.sensitivity, wi->mosaic_res.priority); mosaic->tile_size = wi->mosaic_res.tile_size; mosaic->row_count = wi->mosaic_res.row_count; mosaic->column_count = wi->mosaic_res.column_count; #else /* PLC : à écrire */ #endif mosaic->x = 0; mosaic->y = 0; mosaic->tile_palette = NULL; mosaic->color_usage = NULL; mosaic->palette_size = 0; mosaic->tile_colors = NULL; } /* ********************************************************************************** * * Clone -- * ********************************************************************************** */ static void Clone(Item item) { MosaicItem mosaic = (MosaicItem) item; item->user_data = NULL; if (mosaic->tile_palette) { ZnColor *tile_palette; tile_palette = (ZnColor *) ZnMalloc(mosaic->palette_size*sizeof(ZnColor)); memcpy(tile_palette, mosaic->tile_palette, mosaic->palette_size*sizeof(ZnColor)); mosaic->tile_palette = tile_palette; } /* * The cached info is still up to date so keep it. */ if (mosaic->color_usage) { int *color_usage; color_usage = (int *) ZnMalloc(mosaic->palette_size*sizeof(int)); memcpy(color_usage, mosaic->color_usage, mosaic->palette_size*sizeof(int)); mosaic->color_usage = color_usage; } if (mosaic->tile_colors) { int size = mosaic->row_count*mosaic->column_count*sizeof(unsigned char); unsigned char *tile_colors; tile_colors = (unsigned char *) ZnMalloc(size); memcpy(tile_colors, mosaic->tile_colors, size); mosaic->tile_colors = tile_colors; } } /* ********************************************************************************** * * Remove -- * ********************************************************************************** */ static void Remove(Item item) { MosaicItem mosaic = (MosaicItem) item; if (mosaic->tile_palette) { ZnFree(mosaic->tile_palette); } if (mosaic->color_usage) { ZnFree(mosaic->color_usage); } if (mosaic->tile_colors) { ZnFree(mosaic->tile_colors); } } /* ********************************************************************************** * * Configure -- * ********************************************************************************** */ static void Configure(Item item, ZnList attrs, ZnBool *draw_item, ZnBool *draw_label, ZnBool *compute_coord, ZnBool init) { WidgetInfo *wi = item->wi; MosaicItem mosaic = (MosaicItem) item; ZnAttr *attr; unsigned int i, size, num_attrs; ZnBool b_value; unsigned char *tile_colors = NULL; ZnColor *tile_palette = NULL; ZnBool resize_palette = False; ZnBool resize_colors = False; num_attrs = ZnListSize(attrs); for (i = 0; i < num_attrs; i++) { attr = (ZnAttr *) ZnListAt(attrs, i); switch (attr->name) { ITEM_COMMON_CONFIGURE; case ZnItemX: if (mosaic->x != attr->value) { mosaic->x = attr->value; *draw_item = *compute_coord = True; } break; case ZnItemY: if (mosaic->y != attr->value) { mosaic->y = attr->value; *draw_item = *compute_coord = True; } break; case ZnItemTilePalette: tile_palette = (ZnColor *) attr->value; *draw_item = True; break; case ZnItemTileColors: tile_colors = (unsigned char *) attr->value; *draw_item = True; break; case ZnItemTileSize: if (attr->value >= 0 && mosaic->tile_size != attr->value) { mosaic->tile_size = attr->value; *draw_item = *compute_coord = True; } break; case ZnItemPaletteSize: if (attr->value >= 0) { mosaic->palette_size = attr->value; resize_palette = True; } break; case ZnItemRowCount: if (attr->value >= 0 && mosaic->row_count != attr->value) { mosaic->row_count = attr->value; resize_colors = True; *draw_item = *compute_coord = True; } break; case ZnItemColumnCount: if (attr->value >= 0 && mosaic->column_count != attr->value) { mosaic->column_count = attr->value; resize_colors = True; *draw_item = *compute_coord = True; } break; default: ZnWarning(invalid_attribute); break; } } if (tile_palette) { if (mosaic->tile_palette) { ZnFree(mosaic->tile_palette); } mosaic->tile_palette = (ZnColor *) ZnMalloc(mosaic->palette_size*sizeof(ZnColor)); memcpy(mosaic->tile_palette, tile_palette, mosaic->palette_size*sizeof(ZnColor)); } else if (resize_palette) { tile_palette = (ZnColor *) ZnMalloc(mosaic->palette_size*sizeof(ZnColor)); memcpy(tile_palette, mosaic->tile_palette, mosaic->palette_size*sizeof(ZnColor)); ZnFree(mosaic->tile_palette); mosaic->tile_palette = tile_palette; /* The palette size has changed, erase the cached info * and let the appropriate code fault on it to recreate. */ ZnFree(mosaic->color_usage); mosaic->color_usage = NULL; } if (tile_colors) { /* Here we could optimize the damaged area. No opt should be taken if init or compute_coord.*/ if (mosaic->tile_colors) { ZnFree(mosaic->tile_colors); } size = mosaic->row_count * mosaic->column_count; mosaic->tile_colors = (unsigned char *) ZnMalloc(size*sizeof(unsigned char)); memcpy(mosaic->tile_colors, tile_colors, size*sizeof(unsigned char)); /* * The color counts might need update, let the code fault on an * empty cache. */ ZnFree(mosaic->color_usage); mosaic->color_usage = NULL; } else if (resize_colors) { size = mosaic->row_count * mosaic->column_count; tile_colors = (unsigned char *) ZnMalloc(size*sizeof(unsigned char)); memcpy(tile_colors, mosaic->tile_colors, size*sizeof(unsigned char)); ZnFree(mosaic->tile_colors); mosaic->tile_colors = tile_colors; /* * The color counts might need update, let the code fault on an * empty cache. */ ZnFree(mosaic->color_usage); mosaic->color_usage = NULL; } } /* ********************************************************************************** * * Query -- * ********************************************************************************** */ static void Query(Item item, ZnList attrs) { MosaicItem mosaic = (MosaicItem) item; unsigned int i, num_attrs; ZnAttr *attr; num_attrs = ZnListSize(attrs); for (i = 0; i < num_attrs; i++) { attr = (ZnAttr *) ZnListAt(attrs, i); switch (attr->name) { ITEM_COMMON_QUERY; case ZnItemX: *((int *) attr->value) = mosaic->x; break; case ZnItemY: *((int *) attr->value) = mosaic->y; break; case ZnItemTilePalette: *((ZnColor **) attr->value) = mosaic->tile_palette; break; case ZnItemTileColors: *((unsigned char **) attr->value) = mosaic->tile_colors; break; case ZnItemTileSize: *((int *) attr->value) = mosaic->tile_size; break; case ZnItemPaletteSize: *((int *) attr->value) = mosaic->palette_size; break; case ZnItemRowCount: *((int *) attr->value) = mosaic->row_count; break; case ZnItemColumnCount: *((int *) attr->value) = mosaic->column_count; break; default: ZnWarning(invalid_attribute); break; } } } /* ********************************************************************************** * * ComputeCoordinates -- * ********************************************************************************** */ static void ComputeCoordinates(Item item) { WidgetInfo *wi = item->wi; MosaicItem mosaic = (MosaicItem) item; ZnPos x_dev, y_dev; ResetBBox(&item->item_bounding_box); /* Compute device coordinates */ x_dev = X_TO_DEVICE(wi, mosaic->x); y_dev = Y_TO_DEVICE(wi, mosaic->y); /* Compute bounding box */ AddPointToBBox(&item->item_bounding_box, x_dev, y_dev); AddPointToBBox(&item->item_bounding_box, x_dev + (ZnPos) LENGTH_TO_DEVICE(wi, mosaic->tile_size * mosaic->column_count), y_dev + (ZnPos) LENGTH_TO_DEVICE(wi, mosaic->tile_size * mosaic->row_count)); } /* ********************************************************************************** * * ToArea -- * Tell if the object is entirely outside (-1), * entirely inside (1) or in between (0). * ********************************************************************************** */ static int ToArea(Item item, XRectangle *area) { return -1; } /* ********************************************************************************** * * Draw -- * ********************************************************************************** */ static void Draw(Item item) { WidgetInfo *wi = item->wi; MosaicItem mosaic = (MosaicItem) item; ZnPos delta_x_min, delta_x_max; ZnPos delta_y_min, delta_y_max; ZnPos x_dev = X_TO_DEVICE(wi, mosaic->x); ZnPos y_dev = Y_TO_DEVICE(wi, mosaic->y); ZnDim delta_x_dev, delta_y_dev; ZnDim offset_x, offset_y; unsigned int start_row, stop_row; unsigned int start_col, stop_col; XGCValues values; unsigned int i, j; XRectangle *rect; XRectangle **run_cache; int *run_cache_index; int num_adj_tiles; unsigned char current_color; if (mosaic->palette_size == 0 || mosaic->tile_palette == NULL || mosaic->tile_colors == NULL) { return; } delta_x_min = wi->damaged_area.x - x_dev; delta_x_max = wi->damaged_area.x + wi->damaged_area.width - x_dev; delta_y_min = wi->damaged_area.y - y_dev; delta_y_max = wi->damaged_area.y + wi->damaged_area.height - y_dev; if (delta_x_min < 0) { start_col = 0; } else { start_col = MIN(mosaic->column_count, MAX(0, (LENGTH_FROM_DEVICE(wi, delta_x_min)/mosaic->tile_size)-1)); } if (delta_x_max < 0) { stop_col = 0; } else { stop_col = LENGTH_FROM_DEVICE(wi, delta_x_max)/mosaic->tile_size; stop_col += LENGTH_FROM_DEVICE(wi, delta_x_max) % mosaic->tile_size ? 1 : 0; stop_col = MIN(mosaic->column_count, stop_col+1); } if (delta_y_min < 0) { start_row = 0; } else { start_row = MIN(mosaic->row_count, MAX(0, (LENGTH_FROM_DEVICE(wi, delta_y_min)/mosaic->tile_size)-1)); } if (delta_y_max < 0) { stop_row = 0; } else { stop_row = LENGTH_FROM_DEVICE(wi, delta_y_max)/mosaic->tile_size; stop_row += LENGTH_FROM_DEVICE(wi, delta_y_max) % mosaic->tile_size ? 1 : 0; stop_row = MIN(mosaic->row_count, stop_row+1); } values.fill_style = FillSolid; XChangeGC(wi->dpy, wi->gc, GCFillStyle, &values); if (!mosaic->color_usage) { int *color_usage; /* * Build the color usage cache on the fly. */ color_usage = (int *) XtMalloc(mosaic->palette_size*sizeof(int)); memset(color_usage, 0, mosaic->palette_size*sizeof(int)); /* * First computes the max number of runs for each color. */ for (i = 0; i < mosaic->row_count; i++) { current_color = mosaic->tile_colors[i*mosaic->column_count]; for (j = 0; j < mosaic->column_count; j++) { /* * If at end of row or the next tile is not of the same color * as the current one, the current run is complete. */ if (j == mosaic->column_count - 1 || mosaic->tile_colors[i*mosaic->column_count + j + 1] != current_color) { /* * Update the cache if possible (not a transparent color). */ if (current_color < mosaic->palette_size) { color_usage[current_color]++; } /* * Look at the next tile color. */ if (j != mosaic->column_count - 1) { current_color = mosaic->tile_colors[i*mosaic->column_count + j + 1]; } } } } mosaic->color_usage = color_usage; } /* * Allocate memory to store the runs color by color. */ run_cache = (XRectangle **) alloca(mosaic->palette_size*sizeof(XRectangle *)); run_cache_index = (int *) alloca(mosaic->palette_size*sizeof(int)); memset(run_cache_index, 0, mosaic->palette_size*sizeof(int)); for (i = 0; i < mosaic->palette_size; i++) { if (mosaic->color_usage[i] != 0) { run_cache[i] = (XRectangle *) alloca(mosaic->color_usage[i]*sizeof(XRectangle)); } } /* * Now that the run cache is set up, fill it. */ /* printf("===>start_row:%d, stop_row:%d, start_col:%d, stop_col:%d\n", * start_row, stop_row, start_col, stop_col); */ for (i = start_row; i < stop_row; i++) { current_color = mosaic->tile_colors[i*mosaic->column_count + start_col]; num_adj_tiles = 0; for (j = start_col; j < stop_col; j++) { /* * If at end of row or the next tile is not of the same color * as the current one, the current run is complete. */ /* printf("lin:%d, col:%d, #tiles:%d, cur_color:%d, next_color:%d\n", * i, j, num_adj_tiles, current_color, * mosaic->tile_colors[i*mosaic->column_count + j + 1]); */ if (j == stop_col - 1 || mosaic->tile_colors[i*mosaic->column_count + j + 1] != current_color) { if (current_color < mosaic->palette_size) { /* * Record the current run. */ offset_x = LENGTH_TO_DEVICE(wi, mosaic->tile_size * (j - num_adj_tiles)); offset_y = LENGTH_TO_DEVICE(wi, mosaic->tile_size * i); delta_x_dev = LENGTH_TO_DEVICE(wi, mosaic->tile_size * (j+1)) - offset_x; delta_y_dev = LENGTH_TO_DEVICE(wi, mosaic->tile_size * (i+1)) - offset_y; rect = &run_cache[current_color][run_cache_index[current_color]]; rect->x = x_dev + (ZnPos) offset_x; rect->y = y_dev + (ZnPos) offset_y; rect->width = delta_x_dev; rect->height = delta_y_dev; /* printf("Adding x:%d, y:%d, with:%d, height:%d\n", * rect->x, rect->y, rect->width, rect->height); */ run_cache_index[current_color]++; } /* * Reset the current values and continue */ if (j != stop_col - 1) { current_color = mosaic->tile_colors[i*mosaic->column_count + j + 1]; num_adj_tiles = 0; } } else num_adj_tiles++; } } /* * And now the firework ;-) */ for (i = 0; i < mosaic->palette_size; i++) { if (mosaic->color_usage[i] != 0) { /* printf("color_usage:%d, run_cache_index:%d\n", * mosaic->color_usage[i], run_cache_index[i]); */ values.foreground = mosaic->tile_palette[i]; XChangeGC(wi->dpy, wi->gc, GCForeground, &values); XFillRectangles(wi->dpy, wi->draw_buffer, wi->gc, run_cache[i], run_cache_index[i]); } } } /* ********************************************************************************** * * Pick -- * Nothing to pick, we are almost transparent. * ********************************************************************************** */ static ZnBool Pick(Item item, XPoint *p, ZnDim aperture, ZnBool closest, ZnList items) { return False; } /* ********************************************************************************** * * PostScript -- * ********************************************************************************** */ static void PostScript(Item item, PostScriptInfo ps_info) { } /* ********************************************************************************** * * Exported functions struct -- * ********************************************************************************** */ static ItemClassStruct MOSAIC_ITEM_CLASS = { sizeof(MosaicItemStruct), False, 0, Init, Clone, Remove, Configure, Query, ComputeCoordinates, ToArea, Draw, Pick, PostScript }; ZnItemClassId ZnMosaic = (ZnItemClassId) &MOSAIC_ITEM_CLASS;