Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
5310 lines (4623 sloc) 151 KB
/*
* Copyright 2010 Simutrans contributors
* Available under the Artistic License (see license.txt)
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include "../macros.h"
#include "../simtypes.h"
#include "font.h"
#include "../pathes.h"
#include "../simconst.h"
#include "../simsys.h"
#include "../simmem.h"
#include "../simdebug.h"
#include "../descriptor/image.h"
#include "../dataobj/environment.h"
#include "../dataobj/translator.h"
#include "../unicode.h"
#include "../simticker.h"
#include "../utils/simstring.h"
#include "simgraph.h"
#ifdef _MSC_VER
# include <io.h>
# define W_OK 2
#else
# include <unistd.h>
#endif
#ifdef MULTI_THREAD
#include "../utils/simthread.h"
// currently just redrawing/rezooming
static pthread_mutex_t rezoom_img_mutex[MAX_THREADS];
static pthread_mutex_t recode_img_mutex;
#endif
// to pass the extra clipnum when not needed use this
#ifdef MULTI_THREAD
#define CLIPNUM_IGNORE , 0
#else
#define CLIPNUM_IGNORE
#endif
#include "simgraph.h"
// undefine for debugging the update routines
//#define DEBUG_FLUSH_BUFFER
#ifdef USE_SOFTPOINTER
static int softpointer = -1;
#endif
static int standard_pointer = -1;
#ifdef USE_SOFTPOINTER
/*
* Icon bar needs to redrawn on mouse hover
*/
int old_my = -1;
#endif
/*
* struct to hold the information about visible area
* at screen line y
* associated to some clipline
*/
struct xrange {
int sx, sy;
KOORD_VAL y;
bool non_convex_active;
};
class clip_line_t {
private:
// line from (x0,y0) to (x1 y1)
// clip (do not draw) everything right from the ray (x0,y0)->(x1,y1)
// pixels on the ray are not drawn
// (not_convex) if y0>=y1 then clip along the path (x0,-inf)->(x0,y0)->(x1,y1)
// (not_convex) if y0<y1 then clip along the path (x0,y0)->(x1,y1)->(x1,+inf)
int x0, y0;
int dx, dy, sdy, sdx;
int inc;
bool non_convex;
public:
void clip_from_to(int x0_, int y0_, int x1, int y1, bool non_convex_) {
x0 = x0_;
dx = x1 - x0;
y0 = y0_;
dy = y1 - y0;
non_convex = non_convex_;
int steps = (abs(dx) > abs(dy) ? abs(dx) : abs(dy));
if( steps == 0 ) {
return;
}
sdx = (dx << 16) / steps;
sdy = (dy << 16) / steps;
// to stay right from the line
// left border: xmin <= x
// right border: x < xmax
if( dy > 0 ) {
if( dy > dx ) {
inc = 1 << 16;
}
else {
inc = (dx << 16) / dy - (1 << 16);
}
}
else if( dy < 0 ) {
if( dy < dx ) {
inc = 0; // (+1)-1 << 16;
}
else {
inc = 0;
}
}
}
// clip if
// ( x-x0) . ( y1-y0 )
// ( y-y0) . (-(x1-x0)) < 0
// -- initialize the clipping
// has to be called before image will be drawn
// return interval for x coordinate
inline void get_x_range(KOORD_VAL y, xrange &r, bool use_non_convex) const {
// do everything for the previous row
y--;
r.y = y;
r.non_convex_active = false;
if( non_convex && use_non_convex && y < y0 && y < (y0 + dy) ) {
r.non_convex_active = true;
y = min(y0, y0+dy) - 1;
}
if( dy != 0 ) {
// init Bresenham algorithm
const int t = ((y - y0) << 16) / sdy;
// sx >> 16 = x
// sy >> 16 = y
r.sx = t * sdx + inc + (x0 << 16);
r.sy = t * sdy + (y0 << 16);
}
}
// -- step one line down, return interval for x coordinate
inline void inc_y(xrange &r, int &xmin, int &xmax) const {
r.y++;
// switch between clip vertical and along ray
if( r.non_convex_active ) {
if( r.y == min( y0, y0 + dy ) ) {
r.non_convex_active = false;
}
else {
if( dy < 0 ) {
const int r_xmax = x0 + dx;
if( xmax > r_xmax ) {
xmax = r_xmax;
}
}
else {
const int r_xmin = x0 + 1;
if( xmin < r_xmin ) {
xmin = r_xmin;
}
}
return;
}
}
// go along the ray, Bresenham
if( dy != 0 ) {
if( dy > 0 ) {
do {
r.sx += sdx;
r.sy += sdy;
} while( (r.sy >> 16) < r.y );
const int r_xmin = r.sx >> 16;
if( xmin < r_xmin ) {
xmin = r_xmin;
}
}
else {
do {
r.sx -= sdx;
r.sy -= sdy;
} while( (r.sy >> 16) < r.y );
const int r_xmax = r.sx >> 16;
if( xmax > r_xmax ) {
xmax = r_xmax;
}
}
}
// horizontal clip
else {
const bool clip = dx * (r.y - y0) > 0;
if( clip ) {
// invisible row
xmin = +1;
xmax = -1;
}
}
}
};
#define MAX_POLY_CLIPS 6
MSVC_ALIGN(64) struct clipping_info_t {
// current clipping rectangle
clip_dimension clip_rect;
// clipping rectangle to be swapped by display_clip_wh_toggle
clip_dimension clip_rect_swap;
bool swap_active;
// poly clipping variables
int number_of_clips;
uint8 active_ribi;
uint8 clip_ribi[MAX_POLY_CLIPS];
clip_line_t poly_clips[MAX_POLY_CLIPS];
xrange xranges[MAX_POLY_CLIPS];
} GCC_ALIGN(64); // aligned to separate cachelines
#ifdef MULTI_THREAD
clipping_info_t clips[MAX_THREADS];
#define CR0 clips[0]
#else
clipping_info_t clips;
#define CR0 clips
#endif
#define CR clips CLIP_NUM_INDEX
static font_t large_font;
// needed for resizing gui
int large_font_ascent = 9;
int large_font_total_height = 11;
#define MAX_PLAYER_COUNT (16)
#define RGBMAPSIZE (0x8000+LIGHT_COUNT+MAX_PLAYER_COUNT)
/*
* Hajo: mapping tables for RGB 555 to actual output format
* plus the special (player, day&night) colors appended
*
* 0x0000 - 0x7FFF: RGB colors
* 0x8000 - 0x800F: Player colors
* 0x8010 - 0x001F: Day&Night special colors
* The following transparent colors are not in the colortable
* 0x8020 - 0xFFE1: 3 4 3 RGB transparent colors in 31 transparency levels
*/
static PIXVAL rgbmap_day_night[RGBMAPSIZE];
/*
* Hajo: same as rgbmap_day_night, but always daytime colors
*/
static PIXVAL rgbmap_all_day[RGBMAPSIZE];
/*
* Hajo:used by pixel copy functions, is one of rgbmap_day_night
* rgbmap_all_day
*/
static PIXVAL *rgbmap_current = 0;
/*
* Hajo: mapping table for special-colors (AI player colors)
* to actual output format - day&night mode
* 16 sets of 16 colors
*/
static PIXVAL specialcolormap_day_night[256];
/*
* Hajo: mapping table for special-colors (AI player colors)
* to actual output format - all day mode
* 16 sets of 16 colors
*/
PIXVAL specialcolormap_all_day[256];
/*
* contains all color conversions for transparency
* 16 player colors, 15 special colors and 1024 3 4 3 encoded colors for transparent base
*/
static PIXVAL transparent_map_day_night[MAX_PLAYER_COUNT+LIGHT_COUNT+1024];
//static PIXVAL transparent_map_all_day[MAX_PLAYER_COUNT+LIGHT_COUNT+1024];
//static PIXVAL *transparent_map_current;
/*
* contains all color conversions for transparency
* 16 player colors, 15 special colors and 1024 3 4 3 encoded colors for transparent base
*/
static uint8 transparent_map_day_night_rgb[(MAX_PLAYER_COUNT+LIGHT_COUNT+1024)*4];
//static uint8 transparent_map_all_day_rgb[(MAX_PLAYER_COUNT+LIGHT_COUNT+1024)*4];
//static uint8 *transparent_map_current_rgb;
// offsets of first and second company color
static uint8 player_offsets[MAX_PLAYER_COUNT][2];
/*
* Hajo: Image map descriptor structure
*/
struct imd {
sint16 x; // current (zoomed) min x offset
sint16 y; // current (zoomed) min y offset
sint16 w; // current (zoomed) width
sint16 h; // current (zoomed) height
uint8 recode_flags;
uint16 player_flags; // bit # is player number, ==1 cache image needs recoding
PIXVAL* data[MAX_PLAYER_COUNT]; // current data - zoomed and recolored (player + daynight)
PIXVAL* zoom_data; // zoomed original data
uint32 len; // current zoom image data size (or base if not zoomed) (used for allocation purposes only)
sint16 base_x; // min x offset
sint16 base_y; // min y offset
sint16 base_w; // width
sint16 base_h; // height
PIXVAL* base_data; // original image data
};
// Flags for recoding
#define FLAG_HAS_PLAYER_COLOR (1)
#define FLAG_HAS_TRANSPARENT_COLOR (2)
#define FLAG_ZOOMABLE (4)
#define FLAG_REZOOM (8)
//#define FLAG_POSITION_CHANGED (16)
#define TRANSPARENT_RUN (0x8000u)
// different masks needed for RGB 555 and RGB 565 for blending
#define ONE_OUT_16 (0x7bef)
#define TWO_OUT_16 (0x39E7)
#define ONE_OUT_15 (0x3DEF)
#define TWO_OUT_15 (0x1CE7)
static int bitdepth = 16;
static KOORD_VAL disp_width = 640;
static KOORD_VAL disp_actual_width = 640;
static KOORD_VAL disp_height = 480;
/*
* Static buffers for rezoom_img()
*/
static uint8 *rezoom_baseimage[MAX_THREADS];
static PIXVAL *rezoom_baseimage2[MAX_THREADS];
static size_t rezoom_size[MAX_THREADS];
/*
* Image table
*/
static struct imd* images = NULL;
/*
* Number of loaded images
*/
static image_id anz_images = 0;
/*
* Number of allocated entries for images
* (>= anz_images)
*/
static image_id alloc_images = 0;
/*
* Output framebuffer
*/
static PIXVAL* textur = NULL;
/*
* Hajo: dirty tile management structures
*/
#define DIRTY_TILE_SIZE 16
#define DIRTY_TILE_SHIFT 4
static uint32 *tile_dirty = NULL;
static uint32 *tile_dirty_old = NULL;
static int tiles_per_line = 0;
static int tile_buffer_per_line = 0; // number of tiles that fit the allocated buffer per line - maintain alignment - x=0 is always first bit in a word for each row
static int tile_lines = 0;
static int tile_buffer_length = 0;
static int light_level = 0;
static int night_shift = -1;
/*
* Hajo: special colors during daytime
*/
uint8 display_day_lights[LIGHT_COUNT*3] = {
0x57, 0x65, 0x6F, // Dark windows, lit yellowish at night
0x7F, 0x9B, 0xF1, // Lighter windows, lit blueish at night
0xFF, 0xFF, 0x53, // Yellow light
0xFF, 0x21, 0x1D, // Red light
0x01, 0xDD, 0x01, // Green light
0x6B, 0x6B, 0x6B, // Non-darkening grey 1 (menus)
0x9B, 0x9B, 0x9B, // Non-darkening grey 2 (menus)
0xB3, 0xB3, 0xB3, // non-darkening grey 3 (menus)
0xC9, 0xC9, 0xC9, // Non-darkening grey 4 (menus)
0xDF, 0xDF, 0xDF, // Non-darkening grey 5 (menus)
0xE3, 0xE3, 0xFF, // Nearly white light at day, yellowish light at night
0xC1, 0xB1, 0xD1, // Windows, lit yellow
0x4D, 0x4D, 0x4D, // Windows, lit yellow
0xE1, 0x00, 0xE1, // purple light for signals
0x01, 0x01, 0xFF, // blue light
};
/*
* Hajo: special colors during nighttime
*/
uint8 display_night_lights[LIGHT_COUNT*3] = {
0xD3, 0xC3, 0x80, // Dark windows, lit yellowish at night
0x80, 0xC3, 0xD3, // Lighter windows, lit blueish at night
0xFF, 0xFF, 0x53, // Yellow light
0xFF, 0x21, 0x1D, // Red light
0x01, 0xDD, 0x01, // Green light
0x6B, 0x6B, 0x6B, // Non-darkening grey 1 (menus)
0x9B, 0x9B, 0x9B, // Non-darkening grey 2 (menus)
0xB3, 0xB3, 0xB3, // non-darkening grey 3 (menus)
0xC9, 0xC9, 0xC9, // Non-darkening grey 4 (menus)
0xDF, 0xDF, 0xDF, // Non-darkening grey 5 (menus)
0xFF, 0xFF, 0xE3, // Nearly white light at day, yellowish light at night
0xD3, 0xC3, 0x80, // Windows, lit yellow
0xD3, 0xC3, 0x80, // Windows, lit yellow
0xE1, 0x00, 0xE1, // purple light for signals
0x01, 0x01, 0xFF, // blue light
};
// the players colors and colors for simple drawing operations
// each eight colors are corresponding to a player color
static const uint8 special_pal[224*3]=
{
36, 75, 103,
57, 94, 124,
76, 113, 145,
96, 132, 167,
116, 151, 189,
136, 171, 211,
156, 190, 233,
176, 210, 255,
88, 88, 88,
107, 107, 107,
125, 125, 125,
144, 144, 144,
162, 162, 162,
181, 181, 181,
200, 200, 200,
219, 219, 219,
17, 55, 133,
27, 71, 150,
37, 86, 167,
48, 102, 185,
58, 117, 202,
69, 133, 220,
79, 149, 237,
90, 165, 255,
123, 88, 3,
142, 111, 4,
161, 134, 5,
180, 157, 7,
198, 180, 8,
217, 203, 10,
236, 226, 11,
255, 249, 13,
86, 32, 14,
110, 40, 16,
134, 48, 18,
158, 57, 20,
182, 65, 22,
206, 74, 24,
230, 82, 26,
255, 91, 28,
34, 59, 10,
44, 80, 14,
53, 101, 18,
63, 122, 22,
77, 143, 29,
92, 164, 37,
106, 185, 44,
121, 207, 52,
0, 86, 78,
0, 108, 98,
0, 130, 118,
0, 152, 138,
0, 174, 158,
0, 196, 178,
0, 218, 198,
0, 241, 219,
74, 7, 122,
95, 21, 139,
116, 37, 156,
138, 53, 173,
160, 69, 191,
181, 85, 208,
203, 101, 225,
225, 117, 243,
59, 41, 0,
83, 55, 0,
107, 69, 0,
131, 84, 0,
155, 98, 0,
179, 113, 0,
203, 128, 0,
227, 143, 0,
87, 0, 43,
111, 11, 69,
135, 28, 92,
159, 45, 115,
183, 62, 138,
230, 74, 174,
245, 121, 194,
255, 156, 209,
20, 48, 10,
44, 74, 28,
68, 99, 45,
93, 124, 62,
118, 149, 79,
143, 174, 96,
168, 199, 113,
193, 225, 130,
54, 19, 29,
82, 44, 44,
110, 69, 58,
139, 95, 72,
168, 121, 86,
197, 147, 101,
226, 173, 115,
255, 199, 130,
8, 11, 100,
14, 22, 116,
20, 33, 139,
26, 44, 162,
41, 74, 185,
57, 104, 208,
76, 132, 231,
96, 160, 255,
43, 30, 46,
68, 50, 85,
93, 70, 110,
118, 91, 130,
143, 111, 170,
168, 132, 190,
193, 153, 210,
219, 174, 230,
63, 18, 12,
90, 38, 30,
117, 58, 42,
145, 78, 55,
172, 98, 67,
200, 118, 80,
227, 138, 92,
255, 159, 105,
11, 68, 30,
33, 94, 56,
54, 120, 81,
76, 147, 106,
98, 174, 131,
120, 201, 156,
142, 228, 181,
164, 255, 207,
64, 0, 0,
96, 0, 0,
128, 0, 0,
192, 0, 0,
255, 0, 0,
255, 64, 64,
255, 96, 96,
255, 128, 128,
0, 128, 0,
0, 196, 0,
0, 225, 0,
0, 240, 0,
0, 255, 0,
64, 255, 64,
94, 255, 94,
128, 255, 128,
0, 0, 128,
0, 0, 192,
0, 0, 224,
0, 0, 255,
0, 64, 255,
0, 94, 255,
0, 106, 255,
0, 128, 255,
128, 64, 0,
193, 97, 0,
215, 107, 0,
255, 128, 0,
255, 128, 0,
255, 149, 43,
255, 170, 85,
255, 193, 132,
8, 52, 0,
16, 64, 0,
32, 80, 4,
48, 96, 4,
64, 112, 12,
84, 132, 20,
104, 148, 28,
128, 168, 44,
164, 164, 0,
193, 193, 0,
215, 215, 0,
255, 255, 0,
255, 255, 32,
255, 255, 64,
255, 255, 128,
255, 255, 172,
32, 4, 0,
64, 20, 8,
84, 28, 16,
108, 44, 28,
128, 56, 40,
148, 72, 56,
168, 92, 76,
184, 108, 88,
64, 0, 0,
96, 8, 0,
112, 16, 0,
120, 32, 8,
138, 64, 16,
156, 72, 32,
174, 96, 48,
192, 128, 64,
32, 32, 0,
64, 64, 0,
96, 96, 0,
128, 128, 0,
144, 144, 0,
172, 172, 0,
192, 192, 0,
224, 224, 0,
64, 96, 8,
80, 108, 32,
96, 120, 48,
112, 144, 56,
128, 172, 64,
150, 210, 68,
172, 238, 80,
192, 255, 96,
32, 32, 32,
48, 48, 48,
64, 64, 64,
80, 80, 80,
96, 96, 96,
172, 172, 172,
236, 236, 236,
255, 255, 255,
41, 41, 54,
60, 45, 70,
75, 62, 108,
95, 77, 136,
113, 105, 150,
135, 120, 176,
165, 145, 218,
198, 191, 232,
};
/*
* Hajo: tile raster width
*/
KOORD_VAL tile_raster_width = 16; // zoomed
KOORD_VAL base_tile_raster_width = 16; // original
// Knightly : variables for storing currently used image procedure set and tile raster width
display_image_proc display_normal = NULL;
display_image_proc display_color = NULL;
display_blend_proc display_blend = NULL;
display_alpha_proc display_alpha = NULL;
signed short current_tile_raster_width = 0;
/*
* Hajo: Zoom factor
*/
#define MAX_ZOOM_FACTOR (9)
#define ZOOM_NEUTRAL (3)
static uint32 zoom_factor = ZOOM_NEUTRAL;
static sint32 zoom_num[MAX_ZOOM_FACTOR+1] = { 2, 3, 4, 1, 3, 5, 1, 3, 1, 1 };
static sint32 zoom_den[MAX_ZOOM_FACTOR+1] = { 1, 2, 3, 1, 4, 8, 2, 8, 4, 8 };
/*
* Gets a colour index and returns RGB888
*/
uint32 get_color_rgb(uint8 idx)
{
// special_pal has 225 values
if (idx <= 224) {
return special_pal[idx*3 + 0]<<16 | special_pal[idx*3 + 1]<<8 | special_pal[idx*3 + 2];
}
// if it uses one of the special colours it's under display_day_lights
if (idx <= 224 + LIGHT_COUNT) {
uint8 lidx = idx - 224;
return display_day_lights[lidx*3 + 0]<<16 | display_day_lights[lidx*3 + 1]<<8 | display_day_lights[lidx*3 + 2];
}
// Return black for anything else
return 0;
}
/**
* Convert indexed colors to rgb and back
*/
PIXVAL color_idx_to_rgb(PIXVAL idx)
{
return (specialcolormap_all_day[(idx)&0x00FF]);
}
PIXVAL color_rgb_to_idx(PIXVAL color)
{
for(PIXVAL i=0; i<=0xff; i++) {
if (specialcolormap_all_day[i] == color) {
return i;
}
}
return 0;
}
/*
* Convert env_t colours from RGB888 to the system format
*/
void env_t_rgb_to_system_colors()
{
// get system colours for the default colours or settings.xml
uint32 rgb = env_t::default_window_title_color_rgb;
env_t::default_window_title_color = get_system_color( rgb>>16, (rgb>>8)&0xFF, rgb&0xFF );
rgb = env_t::front_window_text_color_rgb;
env_t::front_window_text_color = get_system_color( rgb>>16, (rgb>>8)&0xFF, rgb&0xFF );
rgb = env_t::bottom_window_text_color_rgb;
env_t::bottom_window_text_color = get_system_color( rgb>>16, (rgb>>8)&0xFF, rgb&0xFF );
rgb = env_t::tooltip_color_rgb;
env_t::tooltip_color = get_system_color( rgb>>16, (rgb>>8)&0xFF, rgb&0xFF );
rgb = env_t::tooltip_textcolor_rgb;
env_t::tooltip_textcolor = get_system_color( rgb>>16, (rgb>>8)&0xFF, rgb&0xFF );
rgb = env_t::cursor_overlay_color_rgb;
env_t::cursor_overlay_color = get_system_color( rgb>>16, (rgb>>8)&0xFF, rgb&0xFF );
rgb = env_t::background_color_rgb;
env_t::background_color = get_system_color( rgb>>16, (rgb>>8)&0xFF, rgb&0xFF );
}
/* changes the raster width after loading */
KOORD_VAL display_set_base_raster_width(KOORD_VAL new_raster)
{
KOORD_VAL old = base_tile_raster_width;
base_tile_raster_width = new_raster;
tile_raster_width = (new_raster * zoom_num[zoom_factor]) / zoom_den[zoom_factor];
return old;
}
// ----------------------------------- clipping routines ------------------------------------------
sint16 display_get_width()
{
return disp_actual_width;
}
// only use, if you are really really sure!
void display_set_actual_width(KOORD_VAL w)
{
disp_actual_width = w;
}
sint16 display_get_height()
{
return disp_height;
}
void display_set_height(KOORD_VAL const h)
{
disp_height = h;
}
/**
* Clips intervall [x,x+w] such that left <= x and x+w <= right.
* If @p w is negative, it stays negative.
* @returns difference between old and new value of @p x.
*/
inline int clip_intv(KOORD_VAL &x, KOORD_VAL &w, const KOORD_VAL left, const KOORD_VAL right)
{
KOORD_VAL xx = min(x+w, right);
KOORD_VAL xoff = left - x;
if (xoff > 0) { // equivalent to x < left
x = left;
}
else {
xoff = 0;
}
w = xx - x;
return xoff;
}
/// wrapper for clip_intv
static int clip_wh(KOORD_VAL *x, KOORD_VAL *w, const KOORD_VAL left, const KOORD_VAL right)
{
return clip_intv(*x, *w, left, right);
}
/// wrapper for clip_intv, @returns whether @p w is positive
static bool clip_lr(KOORD_VAL *x, KOORD_VAL *w, const KOORD_VAL left, const KOORD_VAL right)
{
clip_intv(*x, *w, left, right);
return *w > 0;
}
/**
* Get the clipping rectangle dimensions
* @author Hj. Malthaner
*/
clip_dimension display_get_clip_wh(CLIP_NUM_DEF0)
{
return CR.clip_rect;
}
/**
* Set the clipping rectangle dimensions
* @author Hj. Malthaner
*
* here, a pixel at coordinate xp is displayed if
* clip. x <= xp < clip.xx
* the right-most pixel of an image located at xp with width w is displayed if
* clip.x < xp+w <= clip.xx
* analogously for the y coordinate
*/
void display_set_clip_wh(KOORD_VAL x, KOORD_VAL y, KOORD_VAL w, KOORD_VAL h CLIP_NUM_DEF, bool fit)
{
if (!fit) {
clip_wh( &x, &w, 0, disp_width);
clip_wh( &y, &h, 0, disp_height);
}
else {
clip_wh( &x, &w, CR.clip_rect.x, CR.clip_rect.xx);
clip_wh( &y, &h, CR.clip_rect.y, CR.clip_rect.yy);
}
CR.clip_rect.x = x;
CR.clip_rect.y = y;
CR.clip_rect.w = w;
CR.clip_rect.h = h;
CR.clip_rect.xx = x + w; // watch out, clips to KOORD_VAL max
CR.clip_rect.yy = y + h; // watch out, clips to KOORD_VAL max
}
void display_push_clip_wh(KOORD_VAL x, KOORD_VAL y, KOORD_VAL w, KOORD_VAL h CLIP_NUM_DEF)
{
assert(!CR.swap_active);
// save active clipping rectangle
CR.clip_rect_swap = CR.clip_rect;
// active rectangle provided by parameters
display_set_clip_wh(x, y, w, h CLIP_NUM_PAR);
CR.swap_active = true;
}
void display_swap_clip_wh(CLIP_NUM_DEF0)
{
if (CR.swap_active) {
// swap clipping rectangles
clip_dimension save = CR.clip_rect;
CR.clip_rect = CR.clip_rect_swap;
CR.clip_rect_swap = save;
}
}
void display_pop_clip_wh(CLIP_NUM_DEF0)
{
if (CR.swap_active) {
// swap original clipping rectangle back
CR.clip_rect = CR.clip_rect_swap;
CR.swap_active = false;
}
}
/*
* Add clipping line through (x0,y0) and (x1,y1)
* with associated ribi
* if ribi & 16 then non-convex clipping.
*/
void add_poly_clip(int x0,int y0, int x1, int y1, int ribi CLIP_NUM_DEF)
{
if( CR.number_of_clips < MAX_POLY_CLIPS ) {
CR.poly_clips[CR.number_of_clips].clip_from_to( x0, y0, x1, y1, ribi&16 );
CR.clip_ribi[CR.number_of_clips] = ribi&15;
CR.number_of_clips++;
}
}
/*
* Clears all clipping lines
*/
void clear_all_poly_clip(CLIP_NUM_DEF0)
{
CR.number_of_clips = 0;
CR.active_ribi = 15; // set all to active
}
/*
* Activates clipping lines associated with ribi
* ie if clip_ribi[i] & active_ribi
*/
void activate_ribi_clip(int ribi CLIP_NUM_DEF)
{
CR.active_ribi = ribi;
}
/*
* Initialize clipping region for image starting at screen line y
*/
static inline void init_ranges(int y CLIP_NUM_DEF)
{
for( uint8 i = 0; i < CR.number_of_clips; i++ ) {
if( (CR.clip_ribi[i] & CR.active_ribi) ) {
CR.poly_clips[i].get_x_range( y, CR.xranges[i], CR.active_ribi & 16 );
}
}
}
/*
* Returns left/right border of visible area
* Computes l/r border for the next line (ie y+1)
* takes also clipping rectangle into account
*/
inline void get_xrange_and_step_y(int &xmin, int &xmax CLIP_NUM_DEF)
{
xmin = CR.clip_rect.x;
xmax = CR.clip_rect.xx;
for( uint8 i = 0; i < CR.number_of_clips; i++ ) {
if( (CR.clip_ribi[i] & CR.active_ribi) ) {
CR.poly_clips[i].inc_y( CR.xranges[i], xmin, xmax );
}
}
}
// ------------------------------ dirty tile stuff --------------------------------
/*
* Simutrans keeps a list of dirty areas, i.e. places that received new graphics
* and must be copied to the screen after an update
*/
static inline void mark_tile_dirty(const int x, const int y)
{
const int bit = x + y * tile_buffer_per_line;
#if 0
assert(bit / 8 < tile_buffer_length);
#endif
tile_dirty[bit >> 5] |= 1 << (bit & 31);
}
/**
* Mark tile as dirty, with _NO_ clipping
* @author Hj. Malthaner
*/
static void mark_rect_dirty_nc(KOORD_VAL x1, KOORD_VAL y1, KOORD_VAL x2, KOORD_VAL y2)
{
// floor to tile size
x1 >>= DIRTY_TILE_SHIFT;
y1 >>= DIRTY_TILE_SHIFT;
x2 >>= DIRTY_TILE_SHIFT;
y2 >>= DIRTY_TILE_SHIFT;
#if 0
assert(x1 >= 0);
assert(x1 < tiles_per_line);
assert(y1 >= 0);
assert(y1 < tile_lines);
assert(x2 >= 0);
assert(x2 < tiles_per_line);
assert(y2 >= 0);
assert(y2 < tile_lines);
#endif
for( ; y1 <= y2; y1++ ) {
int bit = y1 * tile_buffer_per_line + x1;
const int end = bit + x2 - x1;
do {
tile_dirty[bit >> 5] |= 1 << (bit & 31);
} while( ++bit <= end );
}
}
/**
* Mark tile as dirty, with clipping
* @author Hj. Malthaner
*/
void mark_rect_dirty_wc(KOORD_VAL x1, KOORD_VAL y1, KOORD_VAL x2, KOORD_VAL y2)
{
// inside display?
if( x2 >= 0 && y2 >= 0 && x1 < disp_width && y1 < disp_height ) {
if( x1 < 0 ) {
x1 = 0;
}
if( y1 < 0 ) {
y1 = 0;
}
if( x2 >= disp_width ) {
x2 = disp_width - 1;
}
if( y2 >= disp_height ) {
y2 = disp_height - 1;
}
mark_rect_dirty_nc( x1, y1, x2, y2 );
}
}
void mark_rect_dirty_clip(KOORD_VAL x1, KOORD_VAL y1, KOORD_VAL x2, KOORD_VAL y2 CLIP_NUM_DEF)
{
// inside clip_rect?
if( x2 >= CR.clip_rect.x && y2 >= CR.clip_rect.y && x1 < CR.clip_rect.xx && y1 < CR.clip_rect.yy ) {
if( x1 < CR.clip_rect.x ) {
x1 = CR.clip_rect.x;
}
if( y1 < CR.clip_rect.y ) {
y1 = CR.clip_rect.y;
}
if( x2 >= CR.clip_rect.xx ) {
x2 = CR.clip_rect.xx-1;
}
if( y2 >= CR.clip_rect.yy ) {
y2 = CR.clip_rect.yy-1;
}
mark_rect_dirty_nc( x1, y1, x2, y2 );
}
}
/**
* Mark the whole screen as dirty.
*
*/
void mark_screen_dirty()
{
memset( tile_dirty, 0xFFFFFFFF, sizeof(uint32) * tile_buffer_length );
}
/**
* the area of this image need update
* @author Hj. Malthaner
*/
void display_mark_img_dirty(image_id image, KOORD_VAL xp, KOORD_VAL yp)
{
if( image < anz_images ) {
mark_rect_dirty_wc(
xp + images[image].x,
yp + images[image].y,
xp + images[image].x + images[image].w - 1,
yp + images[image].y + images[image].h - 1
);
}
}
// ------------------------- rendering images for display --------------------------------
/*
* Simutrans caches player colored images, to allow faster drawing of them
* They are derived from a base image, which may need zooming too
*/
/**
* Flag all images for rezoom on next draw
* @author Hj. Malthaner
*/
static void rezoom()
{
for( image_id n = 0; n < anz_images; n++ ) {
if( (images[n].recode_flags & FLAG_ZOOMABLE) != 0 && images[n].base_h > 0 ) {
images[n].recode_flags |= FLAG_REZOOM;
}
}
}
void set_zoom_factor(int z)
{
// do not zoom beyond 4 pixels
if( (base_tile_raster_width * zoom_num[z]) / zoom_den[z] > 4 ) {
zoom_factor = z;
tile_raster_width = (base_tile_raster_width * zoom_num[zoom_factor]) / zoom_den[zoom_factor];
fprintf(stderr, "set_zoom_factor() : set %d (%i/%i)\n", zoom_factor, zoom_num[zoom_factor], zoom_den[zoom_factor] );
rezoom();
}
}
int zoom_factor_up()
{
// zoom out, if size permits
if( zoom_factor > 0 ) {
set_zoom_factor( zoom_factor-1 );
return true;
}
return false;
}
int zoom_factor_down()
{
if( zoom_factor < MAX_ZOOM_FACTOR ) {
set_zoom_factor( zoom_factor+1 );
return true;
}
return false;
}
static uint8 player_night=0xFF;
static uint8 player_day=0xFF;
static void activate_player_color(sint8 player_nr, bool daynight)
{
// caches the last settings
if(!daynight) {
if(player_day!=player_nr) {
int i;
player_day = player_nr;
for(i=0; i<8; i++ ) {
rgbmap_all_day[0x8000+i] = specialcolormap_all_day[player_offsets[player_day][0]+i];
rgbmap_all_day[0x8008+i] = specialcolormap_all_day[player_offsets[player_day][1]+i];
}
}
rgbmap_current = rgbmap_all_day;
}
else {
// changing color table
if(player_night!=player_nr) {
int i;
player_night = player_nr;
for(i=0; i<8; i++ ) {
rgbmap_day_night[0x8000+i] = specialcolormap_day_night[player_offsets[player_night][0]+i];
rgbmap_day_night[0x8008+i] = specialcolormap_day_night[player_offsets[player_night][1]+i];
if( bitdepth==16 ) {
transparent_map_day_night[i] = (specialcolormap_day_night[player_offsets[player_day][0]+i] >> 2) & TWO_OUT_16;
transparent_map_day_night[i+8] = (specialcolormap_day_night[player_offsets[player_day][1]+i] >> 2) & TWO_OUT_16;
// those save RGB components
transparent_map_day_night_rgb[i*4+0] = specialcolormap_day_night[player_offsets[player_day][0]+i] >> 11;
transparent_map_day_night_rgb[i*4+1] = (specialcolormap_day_night[player_offsets[player_day][0]+i] >> 5) & 0x3F;
transparent_map_day_night_rgb[i*4+2] = specialcolormap_day_night[player_offsets[player_day][0]+i] & 0x1F;
transparent_map_day_night_rgb[i*4+0+32] = specialcolormap_day_night[player_offsets[player_day][1]+i] >> 11;
transparent_map_day_night_rgb[i*4+1+32] = (specialcolormap_day_night[player_offsets[player_day][1]+i] >> 5) & 0x3F;
transparent_map_day_night_rgb[i*4+2+32] = specialcolormap_day_night[player_offsets[player_day][1]+i] & 0x1F;
}
else {
transparent_map_day_night[i] = (specialcolormap_day_night[player_offsets[player_day][0]+i] >> 2) & TWO_OUT_15;
transparent_map_day_night[i+8] = (specialcolormap_day_night[player_offsets[player_day][1]+i] >> 2) & TWO_OUT_15;
// those save RGB components
transparent_map_day_night_rgb[i*4+0] = specialcolormap_day_night[player_offsets[player_day][0]+i] >> 10;
transparent_map_day_night_rgb[i*4+1] = (specialcolormap_day_night[player_offsets[player_day][0]+i] >> 5) & 0x31;
transparent_map_day_night_rgb[i*4+2] = specialcolormap_day_night[player_offsets[player_day][0]+i] & 0x1F;
transparent_map_day_night_rgb[i*4+0+32] = specialcolormap_day_night[player_offsets[player_day][1]+i] >> 10;
transparent_map_day_night_rgb[i*4+1+32] = (specialcolormap_day_night[player_offsets[player_day][1]+i] >> 5) & 0x1F;
transparent_map_day_night_rgb[i*4+2+32] = specialcolormap_day_night[player_offsets[player_day][1]+i] & 0x1F;
}
}
}
rgbmap_current = rgbmap_day_night;
}
}
/**
* Flag all images to recode colors on next draw
* @author Hj. Malthaner
*/
static void recode()
{
for( image_id n = 0; n < anz_images; n++ ) {
images[n].player_flags = 0xFFFF; // recode all player colors
}
}
// to switch between 15 bit and 16 bit recoding ...
typedef void (*display_recode_img_src_target_proc)(KOORD_VAL h, PIXVAL *src, PIXVAL *target);
static display_recode_img_src_target_proc recode_img_src_target = NULL;
/**
* Convert a certain image data to actual output data
* @author prissi
*/
static void recode_img_src_target_15(KOORD_VAL h, PIXVAL *src, PIXVAL *target)
{
if( h > 0 ) {
do {
uint16 runlen = *target++ = *src++;
// decode rows
do {
// clear run is always ok
runlen = *target++ = *src++;
if( runlen & TRANSPARENT_RUN ) {
runlen &= ~TRANSPARENT_RUN;
while( runlen-- ) {
if( *src < 0x8020+(31*16) ) {
// expand transparent player color
PIXVAL rgb555 = rgbmap_day_night[(*src-0x8020)/31+0x8000];
PIXVAL alpha = (*src-0x8020) % 31;
PIXVAL pix = ((rgb555 >> 6) & 0x0380) | ((rgb555 >> 4) & 0x0038) | ((rgb555 >> 2) & 0x07);
*target++ = 0x8020 + 31*31 + pix*31 + alpha;
src ++;
}
else {
*target++ = *src++;
}
}
}
else {
// now just convert the color pixels
while( runlen-- ) {
*target++ = rgbmap_day_night[*src++];
}
}
// next clear run or zero = end
} while( (runlen = *target++ = *src++) );
} while( --h );
}
}
static void recode_img_src_target_16(KOORD_VAL h, PIXVAL *src, PIXVAL *target)
{
if( h > 0 ) {
do {
uint16 runlen = *target++ = *src++;
// decode rows
do {
// clear run is always ok
runlen = *target++ = *src++;
if( runlen & TRANSPARENT_RUN ) {
runlen &= ~TRANSPARENT_RUN;
while( runlen-- ) {
if( *src < 0x8020+(31*16) ) {
// expand transparent player color
PIXVAL rgb565 = rgbmap_day_night[(*src-0x8020)/31+0x8000];
PIXVAL alpha = (*src-0x8020) % 31;
PIXVAL pix = ((rgb565 >> 6) & 0x0380) | ((rgb565 >> 3) & 0x0078) | ((rgb565 >> 2) & 0x07);
*target++ = 0x8020 + 31*31 + pix*31 + alpha;
src ++;
}
else {
*target++ = *src++;
}
}
}
else {
// now just convert the color pixels
while( runlen-- ) {
*target++ = rgbmap_day_night[*src++];
}
}
// next clear run or zero = end
} while( (runlen = *target++ = *src++) );
} while( --h );
}
}
image_id get_image_count()
{
return anz_images;
}
/**
* Handles the conversion of an image to the output color
* @author prissi
*/
static void recode_img(const image_id n, const sint8 player_nr)
{
// Hajo: may this image be zoomed
#ifdef MULTI_THREAD
pthread_mutex_lock( &recode_img_mutex );
if( (images[n].player_flags & (1<<player_nr)) == 0 ) {
// other thread did already the re-code...
pthread_mutex_unlock( &recode_img_mutex );
return;
}
#endif
PIXVAL *src = images[n].zoom_data != NULL ? images[n].zoom_data : images[n].base_data;
if( images[n].data[player_nr] == NULL ) {
images[n].data[player_nr] = MALLOCN( PIXVAL, images[n].len );
}
// contains now the player color ...
activate_player_color( player_nr, true );
recode_img_src_target( images[n].h, src, images[n].data[player_nr] );
images[n].player_flags &= ~(1<<player_nr);
#ifdef MULTI_THREAD
pthread_mutex_unlock( &recode_img_mutex );
#endif
}
// for zoom out
#define SumSubpixel(p) \
if(*(p)<255 && valid<255) { \
if(*(p)==1) { valid = 255; r = g = b = 0; } else { valid ++; } /* mark special colors */\
r += (p)[1]; \
g += (p)[2]; \
b += (p)[3]; \
}
// recode 4*bytes into PIXVAL
PIXVAL inline compress_pixel(uint8* p)
{
return (((p[0]==1 ? 0x8000 : 0 ) | p[1]) + (((uint16)p[2])<<5) + (((uint16)p[3])<<10));
}
// recode 4*bytes into PIXVAL, respect transparency
PIXVAL inline compress_pixel_transparent(uint8 *p)
{
return p[0]==255 ? 0x73FE : compress_pixel(p);
}
// zoom-in pixel taking color of above/below and transparency of diagonal neighbor into account
PIXVAL inline zoomin_pixel(uint8 *p, uint8* pab, uint8 *prl, uint8* pdia)
{
if (p[0] == 255) {
if ( (pab[0] | prl[0] | pdia[0])==255) {
return 0x73FE; // pixel and one neighbor transparent -> return transparent
}
// pixel transparent but all three neighbors not -> interpolate
uint8 valid=0;
uint8 r=0, g=0, b=0;
SumSubpixel(pab);
SumSubpixel(prl);
if(valid==0) {
return 0x73FE;
}
else if(valid==255) {
return (0x8000 | r) + (((uint16)g)<<5) + (((uint16)b)<<10);
}
else {
return (r/valid) + (((uint16)(g/valid))<<5) + (((uint16)(b/valid))<<10);
}
}
else {
if ( (pab[0] & prl[0] & pdia[0])!=255) {
// pixel and one neighbor not transparent
return compress_pixel(p);
}
return 0x73FE;
}
}
/**
* Convert base image data to actual image size
* Uses averages of all sampled points to get the "real" value
* Blurs a bit
* @author prissi
*/
static void rezoom_img(const image_id n)
{
// Hajo: may this image be zoomed
if( n < anz_images && images[n].base_h > 0 ) {
#ifdef MULTI_THREAD
pthread_mutex_lock( &rezoom_img_mutex[n % env_t::num_threads] );
if( (images[n].recode_flags & FLAG_REZOOM) == 0 ) {
// other routine did already the re-zooming ...
pthread_mutex_unlock( &rezoom_img_mutex[n % env_t::num_threads] );
return;
}
#endif
// we may need night conversion afterwards
images[n].player_flags = 0xFFFF; // recode all player colors
// we recalculate the len (since it may be larger than before)
// thus we have to free the old caches
if( images[n].zoom_data != NULL ) {
guarded_free( images[n].zoom_data );
images[n].zoom_data = NULL;
}
for( uint8 i = 0; i < MAX_PLAYER_COUNT; i++ ) {
if( images[n].data[i] != NULL ) {
guarded_free( images[n].data[i] );
images[n].data[i] = NULL;
}
}
// just restore original size?
if( zoom_factor == ZOOM_NEUTRAL || (images[n].recode_flags&FLAG_ZOOMABLE) == 0 ) {
// this we can do be a simple copy ...
images[n].x = images[n].base_x;
images[n].w = images[n].base_w;
images[n].y = images[n].base_y;
images[n].h = images[n].base_h;
// recalculate length
sint16 h = images[n].base_h;
PIXVAL *sp = images[n].base_data;
while( h-- > 0 ) {
do {
// clear run + colored run + next clear run
sp++;
sp += (*sp)&(~TRANSPARENT_RUN); // MSVC crashes on (*sp)&(~TRANSPARENT_RUN) + 1 !!!
sp ++;
} while( *sp );
sp++;
}
images[n].len = (uint32)(size_t)(sp - images[n].base_data);
images[n].recode_flags &= ~FLAG_REZOOM;
#ifdef MULTI_THREAD
pthread_mutex_unlock( &rezoom_img_mutex[n % env_t::num_threads] );
#endif
return;
}
// now we want to downsize the image
// just divide the sizes
images[n].x = (images[n].base_x * zoom_num[zoom_factor]) / zoom_den[zoom_factor];
images[n].y = (images[n].base_y * zoom_num[zoom_factor]) / zoom_den[zoom_factor];
images[n].w = (images[n].base_w * zoom_num[zoom_factor]) / zoom_den[zoom_factor];
images[n].h = (images[n].base_h * zoom_num[zoom_factor]) / zoom_den[zoom_factor];
if( images[n].h > 0 && images[n].w > 0 ) {
// just recalculate the image in the new size
PIXVAL *src = images[n].base_data;
PIXVAL *dest = NULL;
// embed the baseimage in an image with margin ~ remainder
const sint16 x_rem = (images[n].base_x * zoom_num[zoom_factor]) % zoom_den[zoom_factor];
const sint16 y_rem = (images[n].base_y * zoom_num[zoom_factor]) % zoom_den[zoom_factor];
const sint16 xl_margin = max( x_rem, 0);
const sint16 xr_margin = max(-x_rem, 0);
const sint16 yl_margin = max( y_rem, 0);
const sint16 yr_margin = max(-y_rem, 0);
// baseimage top-left corner is at (xl_margin, yl_margin)
// ... low-right corner is at (xr_margin, yr_margin)
sint32 orgzoomwidth = ((images[n].base_w + zoom_den[zoom_factor] - 1 ) / zoom_den[zoom_factor]) * zoom_den[zoom_factor];
sint32 newzoomwidth = (orgzoomwidth*zoom_num[zoom_factor])/zoom_den[zoom_factor];
sint32 orgzoomheight = ((images[n].base_h + zoom_den[zoom_factor] - 1 ) / zoom_den[zoom_factor]) * zoom_den[zoom_factor];
sint32 newzoomheight = (orgzoomheight * zoom_num[zoom_factor]) / zoom_den[zoom_factor];
// we will unpack, re-sample, pack it
// thus the unpack buffer must at least fit the window => find out maximum size
// Note: This value is certainly way bigger than the average size we'll get,
// but it's the worst scenario possible, a succession of solid - transparent - solid - transparent
// pattern.
// This would encode EACH LINE as:
// 0x0000 (0 transparent) 0x0001 PIXWORD 0x0001 (every 2 pixels, 3 words) 0x0000 (EOL)
// The extra +1 is to make sure we cover divisions with module != 0
// We end with an over sized buffer for the normal usage, but since it's re-used for all re-zooms,
// it's not performance critical and we are safe from all possible inputs.
size_t new_size = ( ( (newzoomwidth * 3) / 2 ) + 1 + 2) * newzoomheight * sizeof(PIXVAL);
size_t unpack_size = (xl_margin + orgzoomwidth + xr_margin) * (yl_margin + orgzoomheight + yr_margin) * 4;
if( unpack_size > new_size ) {
new_size = unpack_size;
}
new_size = ((new_size * 128) + 127) / 128; // enlarge slightly to try and keep buffers on their own cacheline for multithreaded access. A portable aligned_alloc would be better.
if( rezoom_size[n % env_t::num_threads] < new_size ) {
free( rezoom_baseimage2[n % env_t::num_threads] );
free( rezoom_baseimage[n % env_t::num_threads] );
rezoom_size[n % env_t::num_threads] = new_size;
rezoom_baseimage[n % env_t::num_threads] = MALLOCN( uint8, new_size );
rezoom_baseimage2[n % env_t::num_threads] = (PIXVAL *)MALLOCN( uint8, new_size );
}
memset( rezoom_baseimage[n % env_t::num_threads], 255, new_size ); // fill with invalid data to mark transparent regions
// index of top-left corner
uint32 baseoff = 4 * (yl_margin * (xl_margin + orgzoomwidth + xr_margin) + xl_margin);
sint32 basewidth = xl_margin + orgzoomwidth + xr_margin;
// now: unpack the image
for( sint32 y = 0; y < images[n].base_h; ++y ) {
uint16 runlen;
uint8 *p = rezoom_baseimage[n % env_t::num_threads] + baseoff + y * (basewidth * 4);
// decode line
runlen = *src++;
do {
// clear run
p += (runlen & ~TRANSPARENT_RUN) * 4;
// color pixel
runlen = (*src++) & ~TRANSPARENT_RUN;
while( runlen-- ) {
// get rgb components
PIXVAL s = *src++;
*p++ = (s>>15);
*p++ = (s & 31);
s >>= 5;
*p++ = (s & 31);
s >>= 5;
*p++ = (s & 31);
}
runlen = *src++;
} while( runlen != 0 );
}
// now we have the image, we do a repack then
dest = rezoom_baseimage2[n % env_t::num_threads];
switch( zoom_den[zoom_factor] ) {
case 1: {
assert(zoom_num[zoom_factor]==2);
// first half row - just copy values, do not fiddle with neighbor colors
uint8 *p1 = rezoom_baseimage[n % env_t::num_threads] + baseoff;
for( sint16 x = 0; x < orgzoomwidth; x++ ) {
PIXVAL c1 = compress_pixel_transparent( p1 + (x * 4) );
// now set the pixel ...
dest[x * 2] = c1;
dest[x * 2 + 1] = c1;
}
// skip one line
dest += newzoomwidth;
for( sint16 y = 0; y < orgzoomheight - 1; y++ ) {
uint8 *p1 = rezoom_baseimage[n % env_t::num_threads] + baseoff + y * (basewidth * 4);
// copy leftmost pixels
dest[0] = compress_pixel_transparent( p1 );
dest[newzoomwidth] = compress_pixel_transparent( p1 + basewidth * 4 );
for( sint16 x = 0; x < orgzoomwidth - 1; x++ ) {
uint8 *px1 = p1 + (x * 4);
// pixel at 2,2 in 2x2 superpixel
dest[x * 2 + 1] = zoomin_pixel( px1, px1 + 4, px1 + basewidth * 4, px1 + basewidth * 4 + 4 );
// 2x2 superpixel is transparent but original pixel was not
// preserve one pixel
if( dest[x * 2 + 1] == 0x73FE && px1[0] != 255 && dest[x * 2] == 0x73FE && dest[x * 2 - newzoomwidth] == 0x73FE && dest[x * 2 - newzoomwidth - 1] == 0x73FE ) {
// preserve one pixel
dest[x * 2 + 1] = compress_pixel( px1 );
}
// pixel at 2,1 in next 2x2 superpixel
dest[x * 2 + 2] = zoomin_pixel( px1 + 4, px1, px1 + basewidth * 4 + 4, px1 + basewidth * 4 );
// pixel at 1,2 in next row 2x2 superpixel
dest[x * 2 + newzoomwidth + 1] = zoomin_pixel( px1 + basewidth * 4, px1 + basewidth * 4 + 4, px1, px1 + 4 );
// pixel at 1,1 in next row next 2x2 superpixel
dest[x * 2 + newzoomwidth + 2] = zoomin_pixel( px1 + basewidth * 4 + 4, px1 + basewidth * 4, px1 + 4, px1 );
}
// copy rightmost pixels
dest[2 * orgzoomwidth - 1] = compress_pixel_transparent( p1 + 4 * (orgzoomwidth - 1) );
dest[2 * orgzoomwidth + newzoomwidth - 1] = compress_pixel_transparent( p1 + 4 * (orgzoomwidth - 1) + basewidth * 4 );
// skip two lines
dest += 2 * newzoomwidth;
}
// last half row - just copy values, do not fiddle with neighbor colors
p1 = rezoom_baseimage[n % env_t::num_threads] + baseoff + (orgzoomheight - 1) * (basewidth * 4);
for( sint16 x = 0; x < orgzoomwidth; x++ ) {
PIXVAL c1 = compress_pixel_transparent( p1 + (x * 4) );
// now set the pixel ...
dest[x * 2] = c1;
dest[x * 2 + 1] = c1;
}
break;
}
case 2:
for( sint16 y = 0; y < newzoomheight; y++ ) {
uint8 *p1 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * zoom_den[zoom_factor] + 0 - y_rem) / zoom_num[zoom_factor]) * (basewidth * 4);
uint8 *p2 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * zoom_den[zoom_factor] + 1 - y_rem) / zoom_num[zoom_factor]) * (basewidth * 4);
for( sint16 x = 0; x < newzoomwidth; x++ ) {
uint8 valid = 0;
uint8 r = 0, g = 0, b = 0;
sint16 xreal1 = ((x * zoom_den[zoom_factor] + 0 - x_rem) / zoom_num[zoom_factor]) * 4;
sint16 xreal2 = ((x * zoom_den[zoom_factor] + 1 - x_rem) / zoom_num[zoom_factor]) * 4;
SumSubpixel( p1 + xreal1 );
SumSubpixel( p1 + xreal2 );
SumSubpixel( p2 + xreal1 );
SumSubpixel( p2 + xreal2 );
if( valid == 0 ) {
*dest++ = 0x73FE;
}
else if( valid == 255 ) {
*dest++ = (0x8000 | r) + (((uint16)g)<<5) + (((uint16)b)<<10);
}
else {
*dest++ = (r/valid) + (((uint16)(g/valid))<<5) + (((uint16)(b/valid))<<10);
}
}
}
break;
case 3:
for( sint16 y = 0; y < newzoomheight; y++ ) {
uint8 *p1 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * zoom_den[zoom_factor] + 0 - y_rem) / zoom_num[zoom_factor]) * (basewidth * 4);
uint8 *p2 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * zoom_den[zoom_factor] + 1 - y_rem) / zoom_num[zoom_factor]) * (basewidth * 4);
uint8 *p3 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * zoom_den[zoom_factor] + 2 - y_rem) / zoom_num[zoom_factor]) * (basewidth * 4);
for( sint16 x = 0; x < newzoomwidth; x++ ) {
uint8 valid = 0;
uint16 r = 0, g = 0, b = 0;
sint16 xreal1 = ((x * zoom_den[zoom_factor] + 0 - x_rem) / zoom_num[zoom_factor]) * 4;
sint16 xreal2 = ((x * zoom_den[zoom_factor] + 1 - x_rem) / zoom_num[zoom_factor]) * 4;
sint16 xreal3 = ((x * zoom_den[zoom_factor] + 2 - x_rem) / zoom_num[zoom_factor]) * 4;
SumSubpixel( p1 + xreal1 );
SumSubpixel( p1 + xreal2 );
SumSubpixel( p1 + xreal3 );
SumSubpixel( p2 + xreal1 );
SumSubpixel( p2 + xreal2 );
SumSubpixel( p2 + xreal3 );
SumSubpixel( p3 + xreal1 );
SumSubpixel( p3 + xreal2 );
SumSubpixel( p3 + xreal3 );
if( valid == 0 ) {
*dest++ = 0x73FE;
}
else if( valid == 255 ) {
*dest++ = (0x8000 | r) + (((uint16)g)<<5) + (((uint16)b)<<10);
}
else {
*dest++ = (r/valid) | (((uint16)(g/valid))<<5) | (((uint16)(b/valid))<<10);
}
}
}
break;
case 4:
for( sint16 y = 0; y < newzoomheight; y++ ) {
uint8 *p1 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * zoom_den[zoom_factor] + 0 - y_rem) / zoom_num[zoom_factor]) * (basewidth * 4);
uint8 *p2 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * zoom_den[zoom_factor] + 1 - y_rem) / zoom_num[zoom_factor]) * (basewidth * 4);
uint8 *p3 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * zoom_den[zoom_factor] + 2 - y_rem) / zoom_num[zoom_factor]) * (basewidth * 4);
uint8 *p4 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * zoom_den[zoom_factor] + 3 - y_rem) / zoom_num[zoom_factor]) * (basewidth * 4);
for( sint16 x = 0; x < newzoomwidth; x++ ) {
uint8 valid = 0;
uint16 r = 0, g = 0, b = 0;
sint16 xreal1 = ((x * zoom_den[zoom_factor] + 0 - x_rem) / zoom_num[zoom_factor]) * 4;
sint16 xreal2 = ((x * zoom_den[zoom_factor] + 1 - x_rem) / zoom_num[zoom_factor]) * 4;
sint16 xreal3 = ((x * zoom_den[zoom_factor] + 2 - x_rem) / zoom_num[zoom_factor]) * 4;
sint16 xreal4 = ((x * zoom_den[zoom_factor] + 3 - x_rem) / zoom_num[zoom_factor]) * 4;
SumSubpixel( p1 + xreal1 );
SumSubpixel( p1 + xreal2 );
SumSubpixel( p1 + xreal3 );
SumSubpixel( p1 + xreal4 );
SumSubpixel( p2 + xreal1 );
SumSubpixel( p2 + xreal2 );
SumSubpixel( p2 + xreal3 );
SumSubpixel( p2 + xreal4 );
SumSubpixel( p3 + xreal1 );
SumSubpixel( p3 + xreal2 );
SumSubpixel( p3 + xreal3 );
SumSubpixel( p3 + xreal4 );
SumSubpixel( p4 + xreal1 );
SumSubpixel( p4 + xreal2 );
SumSubpixel( p4 + xreal3 );
SumSubpixel( p4 + xreal4 );
if( valid == 0 ) {
*dest++ = 0x73FE;
}
else if( valid == 255 ) {
*dest++ = (0x8000 | r) + (((uint16)g)<<5) + (((uint16)b)<<10);
}
else {
*dest++ = (r/valid) | (((uint16)(g/valid))<<5) | (((uint16)(b/valid))<<10);
}
}
}
break;
case 8:
for( sint16 y = 0; y < newzoomheight; y++ ) {
uint8 *p1 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * zoom_den[zoom_factor] + 0 - y_rem) / zoom_num[zoom_factor]) * (basewidth * 4);
uint8 *p2 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * zoom_den[zoom_factor] + 1 - y_rem) / zoom_num[zoom_factor]) * (basewidth * 4);
uint8 *p3 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * zoom_den[zoom_factor] + 2 - y_rem) / zoom_num[zoom_factor]) * (basewidth * 4);
uint8 *p4 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * zoom_den[zoom_factor] + 3 - y_rem) / zoom_num[zoom_factor]) * (basewidth * 4);
uint8 *p5 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * zoom_den[zoom_factor] + 4 - y_rem) / zoom_num[zoom_factor]) * (basewidth * 4);
uint8 *p6 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * zoom_den[zoom_factor] + 5 - y_rem) / zoom_num[zoom_factor]) * (basewidth * 4);
uint8 *p7 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * zoom_den[zoom_factor] + 6 - y_rem) / zoom_num[zoom_factor]) * (basewidth * 4);
uint8 *p8 = rezoom_baseimage[n % env_t::num_threads] + baseoff + ((y * zoom_den[zoom_factor] + 7 - y_rem) / zoom_num[zoom_factor]) * (basewidth * 4);
for( sint16 x = 0; x < newzoomwidth; x++ ) {
uint8 valid = 0;
uint16 r = 0, g = 0, b = 0;
sint16 xreal1 = ((x * zoom_den[zoom_factor] + 0 - x_rem) / zoom_num[zoom_factor]) * 4;
sint16 xreal2 = ((x * zoom_den[zoom_factor] + 1 - x_rem) / zoom_num[zoom_factor]) * 4;
sint16 xreal3 = ((x * zoom_den[zoom_factor] + 2 - x_rem) / zoom_num[zoom_factor]) * 4;
sint16 xreal4 = ((x * zoom_den[zoom_factor] + 3 - x_rem) / zoom_num[zoom_factor]) * 4;
sint16 xreal5 = ((x * zoom_den[zoom_factor] + 4 - x_rem) / zoom_num[zoom_factor]) * 4;
sint16 xreal6 = ((x * zoom_den[zoom_factor] + 5 - x_rem) / zoom_num[zoom_factor]) * 4;
sint16 xreal7 = ((x * zoom_den[zoom_factor] + 6 - x_rem) / zoom_num[zoom_factor]) * 4;
sint16 xreal8 = ((x * zoom_den[zoom_factor] + 7 - x_rem) / zoom_num[zoom_factor]) * 4;
SumSubpixel( p1 + xreal1 );
SumSubpixel( p1 + xreal2 );
SumSubpixel( p1 + xreal3 );
SumSubpixel( p1 + xreal4 );
SumSubpixel( p1 + xreal5 );
SumSubpixel( p1 + xreal6 );
SumSubpixel( p1 + xreal7 );
SumSubpixel( p1 + xreal8 );
SumSubpixel( p2 + xreal1 );
SumSubpixel( p2 + xreal2 );
SumSubpixel( p2 + xreal3 );
SumSubpixel( p2 + xreal4 );
SumSubpixel( p2 + xreal5 );
SumSubpixel( p2 + xreal6 );
SumSubpixel( p2 + xreal7 );
SumSubpixel( p2 + xreal8 );
SumSubpixel( p3 + xreal1 );
SumSubpixel( p3 + xreal2 );
SumSubpixel( p3 + xreal3 );
SumSubpixel( p3 + xreal4 );
SumSubpixel( p3 + xreal5 );
SumSubpixel( p3 + xreal6 );
SumSubpixel( p3 + xreal7 );
SumSubpixel( p3 + xreal8 );
SumSubpixel( p4 + xreal1 );
SumSubpixel( p4 + xreal2 );
SumSubpixel( p4 + xreal3 );
SumSubpixel( p4 + xreal4 );
SumSubpixel( p4 + xreal5 );
SumSubpixel( p4 + xreal6 );
SumSubpixel( p4 + xreal7 );
SumSubpixel( p4 + xreal8 );
SumSubpixel( p5 + xreal1 );
SumSubpixel( p5 + xreal2 );
SumSubpixel( p5 + xreal3 );
SumSubpixel( p5 + xreal4 );
SumSubpixel( p5 + xreal5 );
SumSubpixel( p5 + xreal6 );
SumSubpixel( p5 + xreal7 );
SumSubpixel( p5 + xreal8 );
SumSubpixel( p6 + xreal1 );
SumSubpixel( p6 + xreal2 );
SumSubpixel( p6 + xreal3 );
SumSubpixel( p6 + xreal4 );
SumSubpixel( p6 + xreal5 );
SumSubpixel( p6 + xreal6 );
SumSubpixel( p6 + xreal7 );
SumSubpixel( p6 + xreal8 );
SumSubpixel( p7 + xreal1 );
SumSubpixel( p7 + xreal2 );
SumSubpixel( p7 + xreal3 );
SumSubpixel( p7 + xreal4 );
SumSubpixel( p7 + xreal5 );
SumSubpixel( p7 + xreal6 );
SumSubpixel( p7 + xreal7 );
SumSubpixel( p7 + xreal8 );
SumSubpixel( p8 + xreal1 );
SumSubpixel( p8 + xreal2 );
SumSubpixel( p8 + xreal3 );
SumSubpixel( p8 + xreal4 );
SumSubpixel( p8 + xreal5 );
SumSubpixel( p8 + xreal6 );
SumSubpixel( p8 + xreal7 );
SumSubpixel( p8 + xreal8 );
if( valid == 0 ) {
*dest++ = 0x73FE;
}
else if( valid == 255 ) {
*dest++ = (0x8000 | r) + (((uint16)g)<<5) + (((uint16)b)<<10);
}
else {
*dest++ = (r/valid) | (((uint16)(g/valid))<<5) | (((uint16)(b/valid))<<10);
}
}
}
break;
default: assert(0);
}
// now encode the image again
dest = (PIXVAL*)rezoom_baseimage[n % env_t::num_threads];
for( sint16 y = 0; y < newzoomheight; y++ ) {
PIXVAL *line = ((PIXVAL *)rezoom_baseimage2[n % env_t::num_threads]) + (y * newzoomwidth);
PIXVAL count;
sint16 x = 0;
uint16 clear_colored_run_pair_count = 0;
do {
// check length of transparent pixels
for( count = 0; x < newzoomwidth && line[x] == 0x73FE; count++, x++ )
{}
// first runlength: transparent pixels
*dest++ = count;
uint16 has_alpha = 0;
// copy for non-transparent
count = 0;
while( x < newzoomwidth && line[x] != 0x73FE ) {
PIXVAL pixval = line[x++];
if( pixval >= 0x8020 && !has_alpha ) {
if( count ) {
*dest++ = count;
dest += count;
count = 0;
*dest++ = TRANSPARENT_RUN;
}
has_alpha = TRANSPARENT_RUN;
}
else if( pixval < 0x8020 && has_alpha ) {
if( count ) {
*dest++ = count+TRANSPARENT_RUN;
dest += count;
count = 0;
*dest++ = TRANSPARENT_RUN;
}
has_alpha = 0;
}
count++;
dest[count] = pixval;
}
/* Knightly:
* If it is not the first clear-colored-run pair and its colored run is empty
* --> it is superfluous and can be removed by rolling back the pointer
*/
if( clear_colored_run_pair_count > 0 && count == 0 ) {
dest--;
// this only happens at the end of a line, so no need to increment clear_colored_run_pair_count
}
else {
*dest++ = count+has_alpha; // number of colored pixel
dest += count; // skip them
clear_colored_run_pair_count++;
}
} while( x < newzoomwidth );
*dest++ = 0; // mark line end
}
// something left?
images[n].w = newzoomwidth;
images[n].h = newzoomheight;
if( newzoomheight > 0 ) {
const size_t zoom_len = (size_t)(((uint8 *)dest) - ((uint8 *)rezoom_baseimage[n % env_t::num_threads]));
images[n].len = (uint32)(zoom_len / sizeof(PIXVAL));
images[n].zoom_data = MALLOCN(PIXVAL, images[n].len);
assert( images[n].zoom_data );
memcpy( images[n].zoom_data, rezoom_baseimage[n % env_t::num_threads], zoom_len );
}
}
else {
// if (images[n].w <= 0) {
// // h=0 will be ignored, with w=0 there was an error!
// printf("WARNING: image%d w=0!\n", n);
// }
images[n].h = 0;
}
images[n].recode_flags &= ~FLAG_REZOOM;
#ifdef MULTI_THREAD
pthread_mutex_unlock( &rezoom_img_mutex[n % env_t::num_threads] );
#endif
}
}
// force a certain size on a image (for rescaling tool images)
void display_fit_img_to_width( const image_id n, sint16 new_w )
{
if( n < anz_images && images[n].base_h > 0 && images[n].w != new_w ) {
int old_zoom_factor = zoom_factor;
for( int i=0; i<=MAX_ZOOM_FACTOR; i++ ) {
int zoom_w = (images[n].base_w * zoom_num[i]) / zoom_den[i];
if( zoom_w <= new_w ) {
uint8 old_zoom_flag = images[n].recode_flags & FLAG_ZOOMABLE;
images[n].recode_flags |= FLAG_REZOOM | FLAG_ZOOMABLE;
zoom_factor = i;
rezoom_img(n);
images[n].recode_flags &= ~FLAG_ZOOMABLE;
images[n].recode_flags |= old_zoom_flag;
zoom_factor = old_zoom_factor;
return;
}
}
}
}
/* Tomas variant */
static void calc_base_pal_from_night_shift(const int night)
{
const int night2 = min(night, 4);
const int day = 4 - night2;
unsigned int i;
// constant multiplier 0,66 - dark night 255 will drop to 49, 55 to 10
// 0,7 - dark, but all is visible 61 13
// 0,73 72 15
// 0,75 - quite bright 80 17
// 0,8 bright 104 22
const double RG_night_multiplier = pow(0.75, night) * ((light_level + 8.0) / 8.0);
const double B_night_multiplier = pow(0.83, night) * ((light_level + 8.0) / 8.0);
for (i = 0; i < 0x8000; i++) {
// (1<<15) this is total no of all possible colors in RGB555)
// RGB 555 input
int R = (i & 0x7C00) >> 7;
int G = (i & 0x03E0) >> 2;
int B = (i & 0x001F) << 3;
// lines generate all possible colors in 555RGB code - input
// however the result is in 888RGB - 8bit per channel
R = (int)(R * RG_night_multiplier);
G = (int)(G * RG_night_multiplier);
B = (int)(B * B_night_multiplier);
rgbmap_day_night[i] = get_system_color(R, G, B);
}
// again the same but for transparent colors
for (i = 0; i < 0x0400; i++) {
// RGB 343 input
int R = (i & 0x0380) >> 2;
int G = (i & 0x0078) << 1;
int B = (i & 0x0007) << 5;
// lines generate all possible colors in 343RGB code - input
// however the result is in 888RGB - 8bit per channel
R = (int)(R * RG_night_multiplier);
G = (int)(G * RG_night_multiplier);
B = (int)(B * B_night_multiplier);
if( bitdepth==16 ) {
// 16 bit colors form here!
PIXVAL color = get_system_color(R, G, B);
transparent_map_day_night[MAX_PLAYER_COUNT+LIGHT_COUNT+i] = (color >> 2) & TWO_OUT_16;
transparent_map_day_night_rgb[(MAX_PLAYER_COUNT+LIGHT_COUNT+i)*4+0] = color >> 11;
transparent_map_day_night_rgb[(MAX_PLAYER_COUNT+LIGHT_COUNT+i)*4+1] = (color >> 5) & 0x3F;
transparent_map_day_night_rgb[(MAX_PLAYER_COUNT+LIGHT_COUNT+i)*4+2] = color & 0x1F;
}
else {
// 15 bit colors form here!
PIXVAL color = get_system_color(R, G, B);
transparent_map_day_night[MAX_PLAYER_COUNT+LIGHT_COUNT+i] = (color >> 2) & TWO_OUT_15;
transparent_map_day_night_rgb[(MAX_PLAYER_COUNT+LIGHT_COUNT+i)*4+0] = color >> 10;
transparent_map_day_night_rgb[(MAX_PLAYER_COUNT+LIGHT_COUNT+i)*4+1] = (color >> 5) & 0x1F;
transparent_map_day_night_rgb[(MAX_PLAYER_COUNT+LIGHT_COUNT+i)*4+2] = color & 0x1F;
}
}
// player color map (and used for map display etc.)
for (i = 0; i < 224; i++) {
const int R = (int)(special_pal[i*3 + 0] * RG_night_multiplier);
const int G = (int)(special_pal[i*3 + 1] * RG_night_multiplier);
const int B = (int)(special_pal[i*3 + 2] * B_night_multiplier);
specialcolormap_day_night[i] = get_system_color(R, G, B);
}
// special light colors (actually, only non-darkening greys should be used
for(i=0; i<LIGHT_COUNT; i++ ) {
specialcolormap_day_night[i+224] = get_system_color( display_day_lights[i*3 + 0], display_day_lights[i*3 + 1], display_day_lights[i*3 + 2] );
}
// init with black for forbidden colors
for(i=224+LIGHT_COUNT; i<256; i++ ) {
specialcolormap_day_night[i] = 0;
}
// default player colors
for(i=0; i<8; i++ ) {
rgbmap_day_night[0x8000+i] = specialcolormap_day_night[player_offsets[0][0]+i];
rgbmap_day_night[0x8008+i] = specialcolormap_day_night[player_offsets[0][1]+i];
if( bitdepth==16 ) {
// 16 bit colors from here!
transparent_map_day_night[i] = (specialcolormap_day_night[player_offsets[player_day][0]+i] >> 2) & TWO_OUT_16;
transparent_map_day_night[i+8] = (specialcolormap_day_night[player_offsets[player_day][1]+i] >> 2) & TWO_OUT_16;
// save RGB components
transparent_map_day_night_rgb[i*4+0] = specialcolormap_day_night[player_offsets[player_day][0]+i] >> 11;
transparent_map_day_night_rgb[i*4+1] = (specialcolormap_day_night[player_offsets[player_day][0]+i] >> 5) & 0x3F;
transparent_map_day_night_rgb[i*4+2] = specialcolormap_day_night[player_offsets[player_day][0]+i] & 0x1F;
transparent_map_day_night_rgb[i*4+0+32] = specialcolormap_day_night[player_offsets[player_day][1]+i] >> 11;
transparent_map_day_night_rgb[i*4+1+32] = (specialcolormap_day_night[player_offsets[player_day][1]+i] >> 5) & 0x3F;
transparent_map_day_night_rgb[i*4+2+32] = specialcolormap_day_night[player_offsets[player_day][1]+i] & 0x1F;
}
else {
// 15 bit colors from here!
transparent_map_day_night[i] = (specialcolormap_day_night[player_offsets[player_day][0]+i] >> 2) & TWO_OUT_15;
transparent_map_day_night[i+8] = (specialcolormap_day_night[player_offsets[player_day][1]+i] >> 2) & TWO_OUT_15;
transparent_map_day_night_rgb[i*4+0] = specialcolormap_day_night[player_offsets[player_day][0]+i] >> 10;
transparent_map_day_night_rgb[i*4+1] = (specialcolormap_day_night[player_offsets[player_day][0]+i] >> 5) & 0x1F;
transparent_map_day_night_rgb[i*4+2] = specialcolormap_day_night[player_offsets[player_day][0]+i] & 0x1F;
transparent_map_day_night_rgb[i*4+0+32] = specialcolormap_day_night[player_offsets[player_day][1]+i] >> 10;
transparent_map_day_night_rgb[i*4+1+32] = (specialcolormap_day_night[player_offsets[player_day][1]+i] >> 5) & 0x1F;
transparent_map_day_night_rgb[i*4+2+32] = specialcolormap_day_night[player_offsets[player_day][1]+i] & 0x1F;
}
}
player_night = 0;
// Lights
for (i = 0; i < LIGHT_COUNT; i++) {
const int day_R = display_day_lights[i*3+0];
const int day_G = display_day_lights[i*3+1];
const int day_B = display_day_lights[i*3+2];
const int night_R = display_night_lights[i*3+0];
const int night_G = display_night_lights[i*3+1];
const int night_B = display_night_lights[i*3+2];
const int R = (day_R * day + night_R * night2) >> 2;
const int G = (day_G * day + night_G * night2) >> 2;
const int B = (day_B * day + night_B * night2) >> 2;
PIXVAL color = get_system_color(R > 0 ? R : 0, G > 0 ? G : 0, B > 0 ? B : 0);
rgbmap_day_night[0x8000 + MAX_PLAYER_COUNT + i] = color;
if( bitdepth==16 ) {
// 16 bit colors from here!
transparent_map_day_night[i+MAX_PLAYER_COUNT] = (color >> 2) & TWO_OUT_16;
transparent_map_day_night_rgb[(i+MAX_PLAYER_COUNT)*4+0] = color >> 11;
transparent_map_day_night_rgb[(i+MAX_PLAYER_COUNT)*4+1] = (color >> 5) & 0x3F;
transparent_map_day_night_rgb[(i+MAX_PLAYER_COUNT)*4+2] = color & 0x1F;
}
else {
// 15 bit colors from here!
transparent_map_day_night[i+MAX_PLAYER_COUNT] = (color >> 2) & TWO_OUT_15;
transparent_map_day_night_rgb[(i+MAX_PLAYER_COUNT)*4+0] = color >> 10;
transparent_map_day_night_rgb[(i+MAX_PLAYER_COUNT)*4+1] = (color >> 5) & 0x1F;
transparent_map_day_night_rgb[(i+MAX_PLAYER_COUNT)*4+2] = color & 0x1F;
}
}
// convert to RGB xxx
recode();
}
void display_day_night_shift(int night)
{
if( night != night_shift ) {
night_shift = night;
calc_base_pal_from_night_shift(night);
mark_screen_dirty();
}
}
// set first and second company color for player
void display_set_player_color_scheme(const int player, const uint8 col1, const uint8 col2 )
{
if(player_offsets[player][0]!=col1 || player_offsets[player][1]!=col2) {
// set new player colors
player_offsets[player][0] = col1;
player_offsets[player][1] = col2;
if(player==player_day || player==player_night) {
// and recalculate map (and save it)
calc_base_pal_from_night_shift(0);
memcpy(rgbmap_all_day, rgbmap_day_night, RGBMAPSIZE * sizeof(PIXVAL));
// memcpy(transparent_map_all_day, transparent_map_day_night, lengthof(transparent_map_day_night) * sizeof(PIXVAL));
if(night_shift!=0) {
calc_base_pal_from_night_shift(night_shift);
}
// calc_base_pal_from_night_shift resets player_night to 0
player_day = player_night;
}
recode();
mark_screen_dirty();
}
}
void register_image(image_t *image_in)
{
struct imd *image;
/* valid image? */
if( image_in->len == 0 || image_in->h == 0 ) {
fprintf(stderr, "Warning: ignoring image %d because of missing data\n", anz_images);
image_in->imageid = IMG_EMPTY;
return;
}
if( anz_images == alloc_images ) {
if( images==NULL ) {
alloc_images = 510;
}
else {
alloc_images += 512;
}
if( anz_images > alloc_images ) {
// overflow
dbg->fatal( "register_image", "*** Out of images (more than %li!) ***", anz_images );
}
images = REALLOC(images, imd, alloc_images);
}
image_in->imageid = anz_images;
image = &images[anz_images];
anz_images++;
image->x = image_in->x;
image->w = image_in->w;
image->y = image_in->y;
image->h = image_in->h;
image->recode_flags = FLAG_REZOOM;
if( image_in->zoomable ) {
image->recode_flags |= FLAG_ZOOMABLE;
}
image->player_flags = 0xFFFF; // recode all player colors
// find out if there are really player colors
for( PIXVAL *src = image_in->data, y = 0; y < image_in->h; ++y ) {
uint16 runlen;
// decode line
runlen = *src++;
do {
// clear run .. nothing to do
runlen = *src++;
if( runlen & TRANSPARENT_RUN ) {
image->recode_flags |= FLAG_HAS_TRANSPARENT_COLOR;
runlen &= ~TRANSPARENT_RUN;
}
// no this many color pixel
while( runlen-- ) {
// get rgb components
PIXVAL s = *src++;
if( s>=0x8000 && s<0x8010 ) {
image->recode_flags |= FLAG_HAS_PLAYER_COLOR;
}
}
runlen = *src++;
} while( runlen!=0 ); // end of row: runlen == 0
}
for( uint8 i = 0; i < MAX_PLAYER_COUNT; i++ ) {
image->data[i] = NULL;
}
image->zoom_data = NULL;
image->len = image_in->len;
image->base_x = image_in->x;
image->base_w = image_in->w;
image->base_y = image_in->y;
image->base_h = image_in->h;
// since we do not recode them, we can work with the original data
image->base_data = image_in->data;
// now find out, it contains player colors
}
// delete all images above a certain number ...
// (mostly needed when changing climate zones)
void display_free_all_images_above( image_id above )
{
while( above < anz_images ) {
anz_images--;
if( images[anz_images].zoom_data != NULL ) {
guarded_free( images[anz_images].zoom_data );
}
for( uint8 i = 0; i < MAX_PLAYER_COUNT; i++ ) {
if( images[anz_images].data[i] != NULL ) {
guarded_free( images[anz_images].data[i] );
}
}
}
}
// prissi: query offsets
void display_get_image_offset(image_id image, KOORD_VAL *xoff, KOORD_VAL *yoff, KOORD_VAL *xw, KOORD_VAL *yw)
{
if( image < anz_images ) {
*xoff = images[image].x;
*yoff = images[image].y;
*xw = images[image].w;
*yw = images[image].h;
}
}
// prissi: query un-zoomed offsets
void display_get_base_image_offset(image_id image, scr_coord_val *xoff, scr_coord_val *yoff, scr_coord_val *xw, scr_coord_val *yw)
{
if( image < anz_images ) {
*xoff = images[image].base_x;
*yoff = images[image].base_y;
*xw = images[image].base_w;
*yw = images[image].base_h;
}
}
// ------------------ display all kind of images from here on ------------------------------
/**
* Copy Pixel from src to dest
* @author Hj. Malthaner
*/
static inline void pixcopy(PIXVAL *dest, const PIXVAL *src, const PIXVAL * const end)
{
// for gcc this seems to produce the optimal code ...
while (src < end) {
*dest++ = *src++;
}
}
#ifdef RGB555
/**
* Copy pixel, replace player color
* @author Hj. Malthaner
*/
static inline void colorpixcopy(PIXVAL *dest, const PIXVAL *src, const PIXVAL* const end)
{
if( *src < 0x8020 ) {
while (src < end) {
*dest++ = rgbmap_current[*src++];
}
}
else {
while (src < end) {
// a semi-transparent pixel
uint16 alpha = ((*src-0x8020) % 31)+1;
if( (alpha & 0x07)==0 ) {
const PIXVAL colval = transparent_map_day_night[(*src++-0x8020)/31];
alpha /= 8;
*dest = alpha*colval + (4-alpha)*(((*dest)>>2) & TWO_OUT_15);
dest++;
}
else {
uint8 *trans_rgb = transparent_map_day_night_rgb+((*src++-0x8020)/31)*4;
const PIXVAL r_src = *trans_rgb++;
const PIXVAL g_src = *trans_rgb++;
const PIXVAL b_src = *trans_rgb++;
const PIXVAL r_dest = (*dest >> 10);
const PIXVAL g_dest = (*dest >> 5) & 0x1F;
const PIXVAL b_dest = (*dest & 0x1F);
const PIXVAL r = r_dest + ( ( (r_src - r_dest) * alpha ) >> 5 );
const PIXVAL g = g_dest + ( ( (g_src - g_dest) * alpha ) >> 5 );
const PIXVAL b = b_dest + ( ( (b_src - b_dest) * alpha ) >> 5 );
*dest++ = (r << 10) | (g << 5) | b;
}
}
}
}
#else
/**
* Copy pixel, replace player color
* @author Hj. Malthaner
*/
static inline void colorpixcopy(PIXVAL *dest, const PIXVAL *src, const PIXVAL* const end)
{
if( *src < 0x8020 ) {
while (src < end) {
*dest++ = rgbmap_current[*src++];
}
}
else {
while (src < end) {
// a semi-transparent pixel
uint16 alpha = ((*src-0x8020) % 31)+1;
//assert( *src>=0x8020+16*31 );
if( (alpha & 0x07)==0 ) {
const PIXVAL colval = transparent_map_day_night[(*src++-0x8020)/31];
alpha /= 8;
*dest = alpha*colval + (4-alpha)*(((*dest)>>2) & TWO_OUT_16);
dest++;
}
else {
uint8 *trans_rgb = transparent_map_day_night_rgb+((*src++-0x8020)/31)*4;
const PIXVAL r_src = *trans_rgb++;
const PIXVAL g_src = *trans_rgb++;
const PIXVAL b_src = *trans_rgb++;
// const PIXVAL colval = transparent_map_day_night[(*src++-0x8020)/31];
// const PIXVAL r_src = (colval >> 11);
// const PIXVAL g_src = (colval >> 5) & 0x3F;
// const PIXVAL b_src = colval & 0x1F;
// all other alphas
const PIXVAL r_dest = (*dest >> 11);
const PIXVAL g_dest = (*dest >> 5) & 0x3F;
const PIXVAL b_dest = (*dest & 0x1F);
const PIXVAL r = r_dest + ( ( (r_src - r_dest) * alpha ) >> 5 );
const PIXVAL g = g_dest + ( ( (g_src - g_dest) * alpha ) >> 5 );
const PIXVAL b = b_dest + ( ( (b_src - b_dest) * alpha ) >> 5 );
*dest++ = (r << 11) | (g << 5) | b;
}
}
}
}
#endif
/**
* templated pixel copy routines
* to be used in display_img_pc
*/
enum pixcopy_routines {
plain = 0, /// simply copies the pixels
colored = 1 /// replaces player colors
};
template<pixcopy_routines copyroutine> void templated_pixcopy(PIXVAL *dest, const PIXVAL *src, const PIXVAL * const end);
template<> void templated_pixcopy<plain>(PIXVAL *dest, const PIXVAL *src, const PIXVAL * const end)
{
pixcopy(dest, src, end);
}
template<> void templated_pixcopy<colored>(PIXVAL *dest, const PIXVAL *src, const PIXVAL * const end)
{
colorpixcopy(dest, src, end);
}
/**
* draws image with clipping along arbitrary lines
* @author Dwachs
*/
template<pixcopy_routines copyroutine>
static void display_img_pc(KOORD_VAL h, const KOORD_VAL xp, const KOORD_VAL yp, const PIXVAL *sp CLIP_NUM_DEF)
{
if( h > 0 ) {
PIXVAL *tp = textur + yp * disp_width;
// initialize clipping
init_ranges( yp CLIP_NUM_PAR);
do { // line decoder
int xpos = xp;
// display image
int runlen = *sp++;
// get left/right boundary, step
int xmin, xmax;
get_xrange_and_step_y( xmin, xmax CLIP_NUM_PAR );
do {
// we start with a clear run (which may be 0 pixels)
xpos += (runlen & ~TRANSPARENT_RUN);
// now get colored pixels
runlen = *sp++;
uint16 has_alpha = runlen & TRANSPARENT_RUN;
runlen &= ~TRANSPARENT_RUN;
// Hajo: something to display?
if (xmin < xmax && xpos + runlen > xmin && xpos < xmax) {
const int left = (xpos >= xmin ? 0 : xmin - xpos);
const int len = (xmax - xpos >= runlen ? runlen : xmax - xpos);
if( !has_alpha ) {
templated_pixcopy<copyroutine>(tp + xpos + left, sp + left, sp + len);
}
else {
colorpixcopy(tp + xpos + left, sp + left, sp + len);
}
}
sp += runlen;
xpos += runlen;
} while ((runlen = *sp++));
tp += disp_width;
} while (--h);
}
}
/**
* Draw image with horizontal clipping
* @author Hj. Malthaner
*/
static void display_img_wc(KOORD_VAL h, const KOORD_VAL xp, const KOORD_VAL yp, const PIXVAL *sp CLIP_NUM_DEF)
{
if( h > 0 ) {
PIXVAL *tp = textur + yp * disp_width;
do { // line decoder
int xpos = xp;
// display image
uint16 runlen = *sp++;
do {
// we start with a clear run
xpos += (runlen & ~TRANSPARENT_RUN);
// now get colored pixels
runlen = *sp++;
uint16 has_alpha = runlen & TRANSPARENT_RUN;
runlen &= ~TRANSPARENT_RUN;
// Hajo: something to display?
if( xpos + runlen > CR.clip_rect.x && xpos < CR.clip_rect.xx ) {
const int left = (xpos >= CR.clip_rect.x ? 0 : CR.clip_rect.x - xpos);
const int len = (CR.clip_rect.xx - xpos >= runlen ? runlen : CR.clip_rect.xx - xpos);
if( !has_alpha ) {
pixcopy(tp + xpos + left, sp + left, sp + len);
}
else {
colorpixcopy(tp + xpos + left, sp + left, sp + len);
}
}
sp += runlen;
xpos += runlen;
} while ((runlen = *sp++));
tp += disp_width;
} while (--h);
}
}
/**
* Draw each image without clipping
*/
static void display_img_nc(KOORD_VAL h, const KOORD_VAL xp, const KOORD_VAL yp, const PIXVAL *sp)
{
if (h > 0) {
PIXVAL *tp = textur + xp + yp * disp_width;
do { // line decoder
uint16 runlen = *sp++;
PIXVAL *p = tp;
// one line decoder
do {
// we start with a clear run
p += (runlen & ~TRANSPARENT_RUN);
// now get colored pixels
runlen = *sp++;
if( runlen & TRANSPARENT_RUN ) {
runlen &= ~TRANSPARENT_RUN;
colorpixcopy( p, sp, sp+runlen );
p += runlen;
sp += runlen;
}
else {
#ifdef LOW_LEVEL
#ifdef SIM_BIG_ENDIAN
// low level c++ without any unrolling
while( runlen-- ) {
*p++ = *sp++;
}
#else
// trying to merge reads and writes
if( runlen ) {
// align to 4 bytes, should use uintptr_t but not available
if( reinterpret_cast<size_t>(p) & 0x2 ) {
*p++ = *sp++;
runlen--;
}
// aligned fast copy loop
bool const postalign = runlen & 1;
runlen >>= 1;
uint32 *ld = (uint32 *)p;
while (runlen--) {
#if defined _MSC_VER // MSVC can read unaligned
*ld++ = *(uint32 const *const)sp;
#else
// little endian order, assumed by default
*ld++ = (uint32(sp[1]) << 16) | uint32(sp[0]);
#endif
sp += 2;
}
p = (PIXVAL*)ld;
// finish unaligned remainder
if( postalign ) {
*p++ = *sp++;
}
}
#endif
#else
// high level c++
const PIXVAL *const splast = sp + runlen;
p = std::copy(sp, splast, p);
sp = splast;
#endif
}
runlen = *sp++;
} while (runlen != 0);
tp += disp_width;
} while (--h > 0);
}
}
// only used for GUI
void display_img_aligned( const image_id n, scr_rect area, int align, const int dirty)
{
if( n < anz_images ) {
scr_coord_val x,y;
// align the image horizontally
x = area.x;
if( (align & ALIGN_RIGHT) == ALIGN_CENTER_H ) {
x -= images[n].x;
x += (area.w-images[n].w)/2;
}
else if( (align & ALIGN_RIGHT) == ALIGN_RIGHT ) {
x = area.get_right() - images[n].x - images[n].w;
}
// align the image vertically
y = area.y;
if( (align & ALIGN_BOTTOM) == ALIGN_CENTER_V ) {
y -= images[n].y;
y += (area.h-images[n].h)/2;
}
else if( (align & ALIGN_BOTTOM) == ALIGN_BOTTOM ) {
y = area.get_bottom() - images[n].y - images[n].h;
}
display_color_img( n, x, y, 0, false, dirty CLIP_NUM_DEFAULT);
}
}
/**
* Draw image with vertical clipping (quickly) and horizontal (slowly)
* @author prissi
*/
void display_img_aux(const image_id n, KOORD_VAL xp, KOORD_VAL yp, const sint8 player_nr_raw, const int /*daynight*/, const int dirty CLIP_NUM_DEF)
{
if( n < anz_images ) {
// only use player images if needed
const sint8 use_player = (images[n].recode_flags & FLAG_HAS_PLAYER_COLOR) * player_nr_raw;
// need to go to nightmode and or re-zoomed?
PIXVAL *sp;
if( use_player > 0 ) {
// player colour images are rezoomed/recoloured in display_color_img
sp = images[n].data[use_player];
if( sp == NULL ) {
printf("CImg[%i] %u failed!\n", use_player, n);
return;
}
}
else {
if( (images[n].recode_flags & FLAG_REZOOM) ) {
rezoom_img( n );
recode_img( n, 0 );
}
else if( (images[n].player_flags & 1) ) {
recode_img( n, 0 );
}
sp = images[n].data[0];
if( sp == NULL ) {
printf("Img %u failed!\n", n);
return;
}
}
// now, since zooming may have change this image
yp += images[n].y;
KOORD_VAL h = images[n].h; // may change due to vertical clipping
// in the next line the vertical clipping will be handled
// by that way the drawing routines must only take into account the horizontal clipping
// this should be much faster in most cases
// must the height be reduced?
KOORD_VAL reduce_h = yp + h - CR.clip_rect.yy;
if( reduce_h > 0 ) {
h -= reduce_h;
}
// still something to draw
if( h <= 0 ) {
return;
}
// vertically lines to skip (only bottom is visible
KOORD_VAL skip_lines = CR.clip_rect.y - (int)yp;
if( skip_lines > 0 ) {
if( skip_lines >= h ) {
// not visible at all
return;
}
h -= skip_lines;
yp += skip_lines;
// now skip them
while (skip_lines--) {
do {
// clear run + colored run + next clear run
sp++;
sp += (*sp) & (~TRANSPARENT_RUN);
sp ++;
} while (*sp);
sp++;
}
// now sp is the new start of an image with height h
}
// new block for new variables
{
// needed now ...
const KOORD_VAL w = images[n].w;
xp += images[n].x;
// clipping at poly lines?
if( CR.number_of_clips > 0 ) {
display_img_pc<plain>( h, xp, yp, sp CLIP_NUM_PAR );
// since height may be reduced, start marking here
if( dirty ) {
mark_rect_dirty_clip( xp, yp, xp + w - 1, yp + h - 1 CLIP_NUM_PAR );
}
}
else {
// use horizontal clipping or skip it?
if( xp >= CR.clip_rect.x && xp + w <= CR.clip_rect.xx ) {
// marking change?
if( dirty ) {
mark_rect_dirty_nc( xp, yp, xp + w - 1, yp + h - 1 );
}
display_img_nc( h, xp, yp, sp );
}
else if( xp < CR.clip_rect.xx && xp + w > CR.clip_rect.x ) {
display_img_wc( h, xp, yp, sp CLIP_NUM_PAR);
// since height may be reduced, start marking here
if( dirty ) {
mark_rect_dirty_clip( xp, yp, xp + w - 1, yp + h - 1 CLIP_NUM_PAR );
}
}
}
}
}
}
// local helper function for tiles buttons
static void display_three_image_row( image_id i1, image_id i2, image_id i3, scr_rect row )
{
if( i1!=IMG_EMPTY ) {
scr_coord_val w = images[i1].w;
display_color_img( i1, row.x, row.y, 0, false, true CLIP_NUM_DEFAULT);
row.x += w;
row.w -= w;
}
// right
if( i3!=IMG_EMPTY ) {
scr_coord_val w = images[i3].w;
display_color_img( i3, row.get_right()-w, row.y, 0, false, true CLIP_NUM_DEFAULT);
row.w -= w;
}
// middle
if( i2!=IMG_EMPTY ) {
scr_coord_val w = images[i2].w;
// tile it wide
while( w <= row.w ) {
display_color_img( i2, row.x, row.y, 0, false, true CLIP_NUM_DEFAULT);
row.x += w;
row.w -= w;
}
// for the rest we have to clip the rectangle
if( row.w > 0 ) {
clip_dimension const cl = display_get_clip_wh();
display_set_clip_wh( cl.x, cl.y, max(0,min(row.get_right(),cl.xx)-cl.x), cl.h );
display_color_img( i2, row.x, row.y, 0, false, true CLIP_NUM_DEFAULT);
display_set_clip_wh(cl.x, cl.y, cl.w, cl.h );
}
}
}
// this displays a 3x3 array of images to fit the scr_rect
void display_img_stretch( const stretch_map_t &imag, scr_rect area )
{
scr_coord_val h_top = 0, h_bottom = 0;
scr_coord_val w_left = 0;
if( imag[0][0]!=IMG_EMPTY ) {
h_top = images[ imag[0][0] ].h;
w_left = images[ imag[0][0] ].w;
}
if( imag[0][2]!=IMG_EMPTY ) {
h_bottom = images[ imag[0][2] ].h;
}
// center vertically?
if( imag[0][1] == IMG_EMPTY && imag[2][1] == IMG_EMPTY ) {
scr_coord_val h = h_top;
if( imag[1][0]!=IMG_EMPTY ) {
h = max( h, images[ imag[1][0] ].h );
}
// center vertically
area.y += (area.h-h)/2;
}
// center horizontcally?
if( imag[1][0] == IMG_EMPTY && imag[1][2] == IMG_EMPTY ) {
scr_coord_val w = w_left;
if( imag[0][1]!=IMG_EMPTY ) {
w = max( w, images[ imag[0][1] ].w );
}
// center vertically
area.x += (area.w-w)/2;
}
// top row
display_three_image_row( imag[0][0], imag[1][0], imag[2][0], area );
// bottom row
if( imag[0][2]!=IMG_EMPTY ) {
scr_rect row( area.x, area.y+area.h-h_bottom, area.w, h_bottom );
display_three_image_row( imag[0][2], imag[1][2], imag[2][2], row );
}
// now stretch the middle
if( imag[0][1]!=IMG_EMPTY ) {
scr_rect row( area.x, area.y+h_top, area.w, area.h-h_top-h_bottom );
// tile it wide
scr_coord_val h = images[imag[0][1]].h;
while( h <= row.h ) {
display_three_image_row( imag[0][1], imag[1][1], imag[2][1], row );
row.y += h;
row.h -= h;
}
// for the rest we have to clip the rectangle
if( row.h > 0 ) {
clip_dimension const cl = display_get_clip_wh();
display_set_clip_wh( cl.x, cl.y, cl.w, max(0,min(row.get_bottom(),cl.yy)-cl.y) );
display_three_image_row( imag[0][1], imag[1][1], imag[2][1], row );
display_set_clip_wh(cl.x, cl.y, cl.w, cl.h );
}
}
}
// local helper function for tiles buttons
static void display_three_blend_row( image_id i1, image_id i2, image_id i3, scr_rect row, FLAGGED_PIXVAL color )
{
if( i1!=IMG_EMPTY ) {
scr_coord_val w = images[i1].w;
display_rezoomed_img_blend( i1, row.x, row.y, 0, color, false, true CLIPNUM_IGNORE );
row.x += w;
row.w -= w;
}
// right
if( i3!=IMG_EMPTY ) {
scr_coord_val w = images[i3].w;
display_rezoomed_img_blend( i3, row.get_right()-w, row.y, 0, color, false, true CLIPNUM_IGNORE );
row.w -= w;
}
// middle
if( i2!=IMG_EMPTY ) {
scr_coord_val w = images[i2].w;
// tile it wide
while( w <= row.w ) {
display_rezoomed_img_blend( i2, row.x, row.y, 0, color, false, true CLIPNUM_IGNORE );
row.x += w;
row.w -= w;
}
// for the rest we have to clip the rectangle
if( row.w > 0 ) {
clip_dimension const cl = display_get_clip_wh();
display_set_clip_wh( cl.x, cl.y, max(0,min(row.get_right(),cl.xx)-cl.x), cl.h );
display_rezoomed_img_blend( i2, row.x, row.y, 0, color, false, true CLIPNUM_IGNORE );
display_set_clip_wh(cl.x, cl.y, cl.w, cl.h );
}
}
}
// this displays a 3x3 array of images to fit the scr_rect like above, but blend the color
void display_img_stretch_blend( const stretch_map_t &imag, scr_rect area, FLAGGED_PIXVAL color )
{
scr_coord_val h_top = 0, h_bottom = 0;
if( imag[0][0]!=IMG_EMPTY ) {
h_top = images[ imag[0][0] ].h;
}
if( imag[0][2]!=IMG_EMPTY ) {
h_bottom = images[ imag[0][2] ].h;
}
// center vertically?
if( imag[0][1] == IMG_EMPTY ) {
scr_coord_val h = h_top;
if( imag[1][0]!=IMG_EMPTY ) {
h = max( h, images[ imag[1][0] ].h );
}
// center vertically
area.y += (area.h-h)/2;
}
// top row
display_three_blend_row( imag[0][0], imag[1][0], imag[2][0], area, color );
// bottom row
if( imag[0][2]!=IMG_EMPTY ) {
scr_rect row( area.x, area.y+area.h-h_bottom, area.w, h_bottom );
display_three_blend_row( imag[0][2], imag[1][2], imag[2][2], row, color );
}
// now stretch the middle
if( imag[0][1]!=IMG_EMPTY ) {
scr_rect row( area.x, area.y+h_top, area.w, area.h-h_top-h_bottom );
// tile it wide
scr_coord_val h = images[imag[0][1]].h;
while( h <= row.h ) {
display_three_blend_row( imag[0][1], imag[1][1], imag[2][1], row, color );
row.y += h;
row.h -= h;
}
// for the rest we have to clip the rectangle
if( row.h > 0 ) {
clip_dimension const cl = display_get_clip_wh();
display_set_clip_wh( cl.x, cl.y, cl.w, max(0,min(row.get_bottom(),cl.yy)-cl.y) );
display_three_blend_row( imag[0][1], imag[1][1], imag[2][1], row, color );
display_set_clip_wh(cl.x, cl.y, cl.w, cl.h );
}
}
}
/**
* Draw Image, replace player color,
* assumes height is ok and valid data are calculated.
* color replacement needs the original data => sp points to non-cached data
* @author hajo/prissi
*/
static void display_color_img_wc(const PIXVAL *sp, KOORD_VAL x, KOORD_VAL y, KOORD_VAL h CLIP_NUM_DEF)
{
PIXVAL *tp = textur + y * disp_width;
do { // line decoder
int xpos = x;
// Display image
uint16 runlen = *sp++;
do {
// we start with a clear run
xpos += (runlen & ~TRANSPARENT_RUN);
// now get colored pixels
runlen = (*sp++) & ~TRANSPARENT_RUN; // we recode anyway, so no need to do it explicitely
// Hajo: something to display?
if( xpos + runlen > CR.clip_rect.x && xpos < CR.clip_rect.xx ) {
const int left = (xpos >= CR.clip_rect.x ? 0 : CR.clip_rect.x - xpos);
const int len = (CR.clip_rect.xx - xpos > runlen ? runlen : CR.clip_rect.xx - xpos);
colorpixcopy(tp + xpos + left, sp + left, sp + len);
}
sp += runlen;
xpos += runlen;
} while ((runlen = *sp++));
tp += disp_width;
} while (--h);
}
/**
* Draw Image, replaced player color
* @author Hj. Malthaner
*/
void display_color_img(const image_id n, KOORD_VAL xp, KOORD_VAL yp, sint8 player_nr_raw, const int daynight, const int dirty CLIP_NUM_DEF)
{
if( n < anz_images ) {
// do we have to use a player nr?
const sint8 player_nr = (images[n].recode_flags & FLAG_HAS_PLAYER_COLOR) * player_nr_raw;
// first: size check
if( (images[n].recode_flags & FLAG_REZOOM) ) {
rezoom_img( n );
}
if( daynight || night_shift == 0 ) {
// ok, now we could use the same faster code as for the normal images
if( (images[n].player_flags & (1<<player_nr)) ) {
recode_img( n, player_nr );
}
display_img_aux( n, xp, yp, player_nr, true, dirty CLIP_NUM_PAR);
return;
}
else {
// do player colour substitution but not daynight - can't use cached images. Do NOT call multithreaded.
// prissi: now test if visible and clipping needed
const KOORD_VAL x = images[n].x + xp;
KOORD_VAL y = images[n].y + yp;
const KOORD_VAL w = images[n].w;
KOORD_VAL h = images[n].h;
if( h <= 0 || x >= CR.clip_rect.xx || y >= CR.clip_rect.yy || x + w <= CR.clip_rect.x || y + h <= CR.clip_rect.y ) {
// not visible => we are done
// happens quite often ...
return;
}
if( dirty ) {
mark_rect_dirty_wc( x, y, x + w - 1, y + h - 1 );
}
activate_player_color( player_nr, daynight );
// color replacement needs the original data => sp points to non-cached data
const PIXVAL *sp = images[n].zoom_data != NULL ? images[n].zoom_data : images[n].base_data;
// clip top/bottom
KOORD_VAL yoff = clip_wh( &y, &h, CR.clip_rect.y, CR.clip_rect.yy );
if( h > 0 ) { // clipping may have reduced it
// clip top
while( yoff ) {
yoff--;
do {
// clear run + colored run + next clear run
++sp;
sp += (*sp) & (~TRANSPARENT_RUN);
sp++;
} while (*sp);
sp++;
}
// clipping at poly lines?
if( CR.number_of_clips > 0 ) {
display_img_pc<colored>( h, x, y, sp CLIP_NUM_PAR );
}
else {
display_color_img_wc( sp, x, y, h CLIP_NUM_PAR );
}
}
}
} // number ok
}
/**
* draw unscaled images, replaces base color
* @author prissi
*/
void display_base_img(const image_id n, KOORD_VAL xp, KOORD_VAL yp, const sint8 player_nr, const int daynight, const int dirty CLIP_NUM_DEF)
{
if( base_tile_raster_width==tile_raster_width ) {
// same size => use standard routine
display_color_img( n, xp, yp, player_nr, daynight, dirty CLIP_NUM_PAR);
}
else if( n < anz_images ) {
// prissi: now test if visible and clipping needed
const KOORD_VAL x = images[n].base_x + xp;
KOORD_VAL y = images[n].base_y + yp;
const KOORD_VAL w = images[n].base_w;
KOORD_VAL h = images[n].base_h;
if( h <= 0 || x >= CR.clip_rect.xx || y >= CR.clip_rect.yy || x + w <= CR.clip_rect.x || y + h <= CR.clip_rect.y ) {
// not visible => we are done
// happens quite often ...
return;
}
if (dirty) {
mark_rect_dirty_wc(x, y, x + w - 1, y + h - 1);
}
// colors for 2nd company color
if(player_nr>=0) {
activate_player_color( player_nr, daynight );
}
else {
// no player
activate_player_color( 0, daynight );
}
// color replacement needs the original data => sp points to non-cached data
const PIXVAL *sp = images[n].base_data;
// clip top/bottom
KOORD_VAL yoff = clip_wh( &y, &h, CR.clip_rect.y, CR.clip_rect.yy );
if( h > 0 ) { // clipping may have reduced it
// clip top
while( yoff ) {
yoff--;
do {
// clear run + colored run + next clear run
sp++;
sp += (*sp) & (~TRANSPARENT_RUN);
sp++;
} while (*sp);
sp++;
}
// clipping at poly lines?
if( CR.number_of_clips > 0 ) {
display_img_pc<colored>( h, x, y, sp CLIP_NUM_PAR );
}
else {
display_color_img_wc( sp, x, y, h CLIP_NUM_PAR);
}
}
} // number ok
}
// Blends two colors
PIXVAL display_blend_colors(PIXVAL background, PIXVAL foreground, int percent_blend)
{
const PIXVAL alpha = (percent_blend*64)/100;
switch( alpha ) {
case 0: // nothing to do ...
return background;
case 16:
{
const PIXVAL two = (bitdepth==16) ? TWO_OUT_16 : TWO_OUT_15;
return (3*(((background)>>2) & two)) + (((foreground)>>2) & two);
}
case 32:
{
const PIXVAL one = (bitdepth==16) ? ONE_OUT_16 : ONE_OUT_15;
return ((((background)>>1) & one)) + (((foreground)>>1) & one);
}
case 48:
{
const PIXVAL two = (bitdepth==16) ? TWO_OUT_16 : TWO_OUT_15;
return ((((background)>>2) & two)) + (3*((foreground)>>2) & two);
}
case 64:
return foreground;
default:
// any percentage blending: SLOW!
if( bitdepth == 15 ) {
// 555 BITMAPS
const PIXVAL r_src = (background >> 10) & 0x1F;
const PIXVAL g_src = (background >> 5) & 0x1F;
const PIXVAL b_src = background & 0x1F;
const PIXVAL r_dest = (foreground >> 10) & 0x1F;
const PIXVAL g_dest = (foreground >> 5) & 0x1F;
const PIXVAL b_dest = (foreground & 0x1F);
const PIXVAL r = (r_dest * alpha + r_src * (64-alpha) + 32) >> 6;
const PIXVAL g = (g_dest * alpha + g_src * (64-alpha) + 32) >> 6;
const PIXVAL b = (b_dest * alpha + b_src * (64-alpha) + 32) >> 6;
return (r << 10) | (g << 5) | b;
}
else {
// 565 colors
const PIXVAL r_src = (background >> 11);
const PIXVAL g_src = (background >> 5) & 0x3F;
const PIXVAL b_src = background & 0x1F;
const PIXVAL r_dest = (foreground >> 11);
const PIXVAL g_dest = (foreground >> 5) & 0x3F;
const PIXVAL b_dest = (foreground & 0x1F);
const PIXVAL r = (r_dest * alpha + r_src * (64-alpha) + 32) >> 6;
const PIXVAL g = (g_dest * alpha + g_src * (64-alpha) + 32) >> 6;
const PIXVAL b = (b_dest * alpha + b_src * (64-alpha) + 32) >> 6;
return (r << 11) | (g << 5) | b;
}
break;
}
}
/* from here code for transparent images */
typedef void (*blend_proc)(PIXVAL *dest, const PIXVAL *src, const PIXVAL colour, const PIXVAL len);
static void pix_blend75_15(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = (3*(((*src)>>2) & TWO_OUT_15)) + (((*dest)>>2) & TWO_OUT_15);
dest++;
src++;
}
}
static void pix_blend75_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = (3*(((*src)>>2) & TWO_OUT_16)) + (((*dest)>>2) & TWO_OUT_16);
dest++;
src++;
}
}
static void pix_blend50_15(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = (((*src)>>1) & ONE_OUT_15) + (((*dest)>>1) & ONE_OUT_15);
dest++;
src++;
}
}
static void pix_blend50_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = (((*src)>>1) & ONE_OUT_16) + (((*dest)>>1) & ONE_OUT_16);
dest++;
src++;
}
}
static void pix_blend25_15(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = (((*src)>>2) & TWO_OUT_15) + (3*(((*dest)>>2) & TWO_OUT_15));
dest++;
src++;
}
}
static void pix_blend25_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = (((*src)>>2) & TWO_OUT_16) + (3*(((*dest)>>2) & TWO_OUT_16));
dest++;
src++;
}
}
// Knightly : the following 6 functions are for display_base_img_blend()
static void pix_blend_recode75_15(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = (3*(((rgbmap_current[*src])>>2) & TWO_OUT_15)) + (((*dest)>>2) & TWO_OUT_15);
dest++;
src++;
}
}
static void pix_blend_recode75_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = (3*(((rgbmap_current[*src])>>2) & TWO_OUT_16)) + (((*dest)>>2) & TWO_OUT_16);
dest++;
src++;
}
}
static void pix_blend_recode50_15(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = (((rgbmap_current[*src])>>1) & ONE_OUT_15) + (((*dest)>>1) & ONE_OUT_15);
dest++;
src++;
}
}
static void pix_blend_recode50_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = (((rgbmap_current[*src])>>1) & ONE_OUT_16) + (((*dest)>>1) & ONE_OUT_16);
dest++;
src++;
}
}
static void pix_blend_recode25_15(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = (((rgbmap_current[*src])>>2) & TWO_OUT_15) + (3*(((*dest)>>2) & TWO_OUT_15));
dest++;
src++;
}
}
static void pix_blend_recode25_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL , const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = (((rgbmap_current[*src])>>2) & TWO_OUT_16) + (3*(((*dest)>>2) & TWO_OUT_16));
dest++;
src++;
}
}
static void pix_outline75_15(PIXVAL *dest, const PIXVAL *, const PIXVAL colour, const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = (3*((colour>>2) & TWO_OUT_15)) + (((*dest)>>2) & TWO_OUT_15);
dest++;
}
}
static void pix_outline75_16(PIXVAL *dest, const PIXVAL *, const PIXVAL colour, const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = (3*((colour>>2) & TWO_OUT_16)) + (((*dest)>>2) & TWO_OUT_16);
dest++;
}
}
static void pix_outline50_15(PIXVAL *dest, const PIXVAL *, const PIXVAL colour, const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = ((colour>>1) & ONE_OUT_15) + (((*dest)>>1) & ONE_OUT_15);
dest++;
}
}
static void pix_outline50_16(PIXVAL *dest, const PIXVAL *, const PIXVAL colour, const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = ((colour>>1) & ONE_OUT_16) + (((*dest)>>1) & ONE_OUT_16);
dest++;
}
}
static void pix_outline25_15(PIXVAL *dest, const PIXVAL *, const PIXVAL colour, const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = ((colour>>2) & TWO_OUT_15) + (3*(((*dest)>>2) & TWO_OUT_15));
dest++;
}
}
static void pix_outline25_16(PIXVAL *dest, const PIXVAL *, const PIXVAL colour, const PIXVAL len)
{
const PIXVAL *const end = dest + len;
while (dest < end) {
*dest = ((colour>>2) & TWO_OUT_16) + (3*(((*dest)>>2) & TWO_OUT_16));
dest++;
}
}
// will kept the actual values
static blend_proc blend[3];
static blend_proc blend_recode[3];
static blend_proc outline[3];
/**
* Blends a rectangular region with a color
*/
void display_blend_wh_rgb(KOORD_VAL xp, KOORD_VAL yp, KOORD_VAL w, KOORD_VAL h, PIXVAL colval, int percent_blend )
{
if( clip_lr( &xp, &w, CR0.clip_rect.x, CR0.clip_rect.xx ) && clip_lr( &yp, &h, CR0.clip_rect.y, CR0.clip_rect.yy ) ) {
const PIXVAL alpha = (percent_blend*64)/100;
switch( alpha ) {
case 0: // nothing to do ...
break;
case 16:
case 32:
case 48:
{
// fast blending with 1/4 | 1/2 | 3/4 percentage
blend_proc blend = outline[ (alpha>>4) - 1 ];
for( KOORD_VAL y=0; y<h; y++ ) {
blend( textur + xp + (yp+y) * disp_width, NULL, colval, w );
}
}
break;
case 64:
// opaque ...
display_fillbox_wh_rgb( xp, yp, w, h, colval, false );
break;
default:
// any percentage blending: SLOW!
if( blend[0] == pix_blend25_15 ) {
// 555 BITMAPS
const PIXVAL r_src = (colval >> 10) & 0x1F;
const PIXVAL g_src = (colval >> 5) & 0x1F;
const PIXVAL b_src = colval & 0x1F;
for( ; h>0; yp++, h-- ) {
PIXVAL *dest = textur + yp*disp_width + xp;
const PIXVAL *const end = dest + w;
while (dest < end) {
const PIXVAL r_dest = (*dest >> 10) & 0x1F;
const PIXVAL g_dest = (*dest >> 5) & 0x1F;
const PIXVAL b_dest = (*dest & 0x1F);
const PIXVAL r = r_dest + ( ( (r_src - r_dest) * alpha ) >> 6 );
const PIXVAL g = g_dest + ( ( (g_src - g_dest) * alpha ) >> 6 );
const PIXVAL b = b_dest + ( ( (b_src - b_dest) * alpha ) >> 6 );
*dest++ = (r << 10) | (g << 5) | b;
}
}
}
else {
// 565 BITMAPS
const PIXVAL r_src = (colval >> 11);
const PIXVAL g_src = (colval >> 5) & 0x3F;
const PIXVAL b_src = colval & 0x1F;
for( ; h>0; yp++, h-- ) {
PIXVAL *dest = textur + yp*disp_width + xp;
const PIXVAL *const end = dest + w;
while (dest < end) {
const PIXVAL r_dest = (*dest >> 11);
const PIXVAL g_dest = (*dest >> 5) & 0x3F;
const PIXVAL b_dest = (*dest & 0x1F);
const PIXVAL r = r_dest + ( ( (r_src - r_dest) * alpha ) >> 6 );
const PIXVAL g = g_dest + ( ( (g_src - g_dest) * alpha ) >> 6 );
const PIXVAL b = b_dest + ( ( (b_src - b_dest) * alpha ) >> 6 );
*dest++ = (r << 11) | (g << 5) | b;
}
}
}
break;
}
}
}
static void display_img_blend_wc(KOORD_VAL h, const KOORD_VAL xp, const KOORD_VAL yp, const PIXVAL *sp, int colour, blend_proc p CLIP_NUM_DEF )
{
if( h > 0 ) {
PIXVAL *tp = textur + yp * disp_width;
do { // line decoder
int xpos = xp;
// display image
uint16 runlen = *sp++;
do {
// we start with a clear run
xpos += (runlen & ~TRANSPARENT_RUN);
// now get colored pixels
runlen = (*sp++) & (~TRANSPARENT_RUN);
// Hajo: something to display?
if( xpos + runlen > CR.clip_rect.x && xpos < CR.clip_rect.xx ) {
const int left = (xpos >= CR.clip_rect.x ? 0 : CR.clip_rect.x - xpos);
const int len = (CR.clip_rect.xx - xpos >= runlen ? runlen : CR.clip_rect.xx - xpos);
p(tp + xpos + left, sp + left, colour, len - left);
}
sp += runlen;
xpos += runlen;
} while ((runlen = *sp++));
tp += disp_width;
} while (--h);
}
}
/* from here code for transparent images */
typedef void (*alpha_proc)(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap, const unsigned alpha_flags, const PIXVAL colour, const PIXVAL len);
static alpha_proc alpha;
static alpha_proc alpha_recode;
static void pix_alpha_15(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap, const unsigned alpha_flags, const PIXVAL , const PIXVAL len)
{
const PIXVAL *const end = dest + len;
const uint16 rmask = alpha_flags & ALPHA_RED ? 0x7c00 : 0;
const uint16 gmask = alpha_flags & ALPHA_GREEN ? 0x03e0 : 0;
const uint16 bmask = alpha_flags & ALPHA_BLUE ? 0x001f : 0;
while( dest < end ) {
// read mask components - always 15bpp
uint16 alpha_value = ((*alphamap) & bmask) + (((*alphamap) & gmask) >> 5) + (((*alphamap) & rmask) >> 10);
if( alpha_value > 30 ) {
// opaque, just copy source
*dest = *src;
}
else if( alpha_value > 0 ) {
alpha_value = alpha_value > 15 ? alpha_value + 1 : alpha_value;
//read screen components - 15bpp
const uint16 rbs = (*dest) & 0x7c1f;
const uint16 gs = (*dest) & 0x03e0;
// read image components - 15bpp
const uint16 rbi = (*src) & 0x7c1f;
const uint16 gi = (*src) & 0x03e0;
// calculate and write destination components - 16bpp
const uint16 rbd = ((rbi * alpha_value) + (rbs * (32 - alpha_value))) >> 5;
const uint16 gd = ((gi * alpha_value) + (gs * (32 - alpha_value))) >> 5;
*dest = (rbd & 0x7c1f) | (gd & 0x03e0);
}
dest++;
src++;
alphamap++;
}
}
static void pix_alpha_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap, const unsigned alpha_flags, const PIXVAL , const PIXVAL len)
{
const PIXVAL *const end = dest + len;
const uint16 rmask = alpha_flags & ALPHA_RED ? 0x7c00 : 0;
const uint16 gmask = alpha_flags & ALPHA_GREEN ? 0x03e0 : 0;
const uint16 bmask = alpha_flags & ALPHA_BLUE ? 0x001f : 0;
while( dest < end ) {
// read mask components - always 15bpp
uint16 alpha_value = ((*alphamap) & bmask) + (((*alphamap) & gmask) >> 5) + (((*alphamap) & rmask) >> 10);
if( alpha_value > 30 ) {
// opaque, just copy source
*dest = *src;
}
else if( alpha_value > 0 ) {
alpha_value = alpha_value > 15 ? alpha_value + 1 : alpha_value;
//read screen components - 16bpp
const uint16 rbs = (*dest) & 0xf81f;
const uint16 gs = (*dest) & 0x07e0;
// read image components 16bpp
const uint16 rbi = (*src) & 0xf81f;
const uint16 gi = (*src) & 0x07e0;
// calculate and write destination components - 16bpp
const uint16 rbd = ((rbi * alpha_value) + (rbs * (32 - alpha_value))) >> 5;
const uint16 gd = ((gi * alpha_value) + (gs * (32 - alpha_value))) >> 5;
*dest = (rbd & 0xf81f) | (gd & 0x07e0);
}
dest++;
src++;
alphamap++;
}
}
static void pix_alpha_recode_15(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap, const unsigned alpha_flags, const PIXVAL , const PIXVAL len)
{
const PIXVAL *const end = dest + len;
const uint16 rmask = alpha_flags & ALPHA_RED ? 0x7c00 : 0;
const uint16 gmask = alpha_flags & ALPHA_GREEN ? 0x03e0 : 0;
const uint16 bmask = alpha_flags & ALPHA_BLUE ? 0x001f : 0;
while( dest < end ) {
// read mask components - always 15bpp
uint16 alpha_value = ((*alphamap) & bmask) + (((*alphamap) & gmask) >> 5) + (((*alphamap) & rmask) >> 10);
if( alpha_value > 30 ) {
// opaque, just copy source
*dest = rgbmap_current[*src];
}
else if( alpha_value > 0 ) {
alpha_value = alpha_value > 15 ? alpha_value + 1 : alpha_value;
//read screen components - 15bpp
const uint16 rbs = (*dest) & 0x7c1f;
const uint16 gs = (*dest) & 0x03e0;
// read image components - 15bpp
const uint16 rbi = (rgbmap_current[*src]) & 0x7c1f;
const uint16 gi = (rgbmap_current[*src]) & 0x03e0;
// calculate and write destination components - 16bpp
const uint16 rbd = ((rbi * alpha_value) + (rbs * (32 - alpha_value))) >> 5;
const uint16 gd = ((gi * alpha_value) + (gs * (32 - alpha_value))) >> 5;
*dest = (rbd & 0x7c1f) | (gd & 0x03e0);
}
dest++;
src++;
alphamap++;
}
}
static void pix_alpha_recode_16(PIXVAL *dest, const PIXVAL *src, const PIXVAL *alphamap, const unsigned alpha_flags, const PIXVAL , const PIXVAL len)
{
const PIXVAL *const end = dest + len;
const uint16 rmask = alpha_flags & ALPHA_RED ? 0x7c00 : 0;
const uint16 gmask = alpha_flags & ALPHA_GREEN ? 0x03e0 : 0;
const uint16 bmask = alpha_flags & ALPHA_BLUE ? 0x001f : 0;
while( dest < end ) {
// read mask components - always 15bpp
uint16 alpha_value = ((*alphamap) & bmask) + (((*alphamap) & gmask) >> 5) + (((*alphamap) & rmask) >> 10);
if( alpha_value > 30 ) {
// opaque, just copy source
*dest = rgbmap_current[*src];
}
else if( alpha_value > 0 ) {
alpha_value = alpha_value> 15 ? alpha_value + 1 : alpha_value;
//read screen components - 16bpp
const uint16 rbs = (*dest) & 0xf81f;
const uint16 gs = (*dest) & 0x07e0;
// read image components 16bpp
const uint16 rbi = (rgbmap_current[*src]) & 0xf81f;
const uint16 gi = (rgbmap_current[*src]) & 0x07e0;
// calculate and write destination components - 16bpp
const uint16 rbd = ((rbi * alpha_value) + (rbs * (32 - alpha_value))) >> 5;
const uint16 gd = ((gi * alpha_value) + (gs * (32 - alpha_value))) >> 5;
*dest = (rbd & 0xf81f) | (gd & 0x07e0);
}
dest++;
src++;
alphamap++;
}
}
static void display_img_alpha_wc(KOORD_VAL h, const KOORD_VAL xp, const KOORD_VAL yp, const PIXVAL *sp, const PIXVAL *alphamap, const uint8 alpha_flags, int colour, alpha_proc p CLIP_NUM_DEF )
{
if( h > 0 ) {
PIXVAL *tp = textur + yp * disp_width;
do { // line decoder
int xpos = xp;
// display image
uint16 runlen = *sp++;
alphamap++;
do {
// we start with a clear run
xpos += runlen;
// now get colored pixels
runlen = ((*sp++) & ~TRANSPARENT_RUN);
alphamap++;
// Hajo: something to display?
if( xpos + runlen > CR.clip_rect.x && xpos < CR.clip_rect.xx ) {
const int left = (xpos >= CR.clip_rect.x ? 0 : CR.clip_rect.x - xpos);
const int len = (CR.clip_rect.xx - xpos >= runlen ? runlen : CR.clip_rect.xx - xpos);
p( tp + xpos + left, sp + left, alphamap + left, alpha_flags, colour, len - left );
}
sp += runlen;
alphamap += runlen;
xpos += runlen;
alphamap++;
} while( (runlen = *sp++) );
tp += disp_width;
} while( --h );
}
}
/**
* draws the transparent outline of an image
* @author kierongreen
*/
void display_rezoomed_img_blend(const image_id n, KOORD_VAL xp, KOORD_VAL yp, const signed char /*player_nr*/, const FLAGGED_PIXVAL color_index, const int /*daynight*/, const int dirty CLIP_NUM_DEF)
{
if( n < anz_images ) {
// need to go to nightmode and or rezoomed?
if( (images[n].recode_flags & FLAG_REZOOM) ) {
rezoom_img( n );
recode_img( n, 0 );
}
else if( (images[n].player_flags & 1) ) {
recode_img( n, 0 );
}
PIXVAL *sp = images[n].data[0];
// now, since zooming may have change this image
xp += images[n].x;
yp += images[n].y;
KOORD_VAL h = images[n].h; // may change due to vertical clipping
// in the next line the vertical clipping will be handled
// by that way the drawing routines must only take into account the horizontal clipping
// this should be much faster in most cases
// must the height be reduced?
KOORD_VAL reduce_h = yp + h - CR.clip_rect.yy;
if( reduce_h > 0 ) {
h -= reduce_h;
}
// still something to draw
if( h <= 0 ) return;
// vertically lines to skip (only bottom is visible)
KOORD_VAL skip_lines = CR.clip_rect.y - (int)yp;
if( skip_lines > 0 ) {
if( skip_lines >= h ) {
// not visible at all
return;
}
h -= skip_lines;
yp += skip_lines;
// now skip them
while (skip_lines--) {
do {
// clear run + colored run + next clear run
sp++;
sp += (*sp) & (~TRANSPARENT_RUN);
sp++;
} while (*sp);
sp++;
}
// now sp is the new start of an image with height h
}
// new block for new variables
{
// needed now ...
const KOORD_VAL w = images[n].w;
// get the real color
const PIXVAL color = color_index & 0xFFFF;
// we use function pointer for the blend runs for the moment ...
blend_proc pix_blend = (color_index&OUTLINE_FLAG) ? outline[ (color_index&TRANSPARENT_FLAGS)/TRANSPARENT25_FLAG - 1 ] : blend[ (color_index&TRANSPARENT_FLAGS)/TRANSPARENT25_FLAG - 1 ];
// use horizontal clipping or skip it?
if( xp >= CR.clip_rect.x && xp + w <= CR.clip_rect.xx ) {
// marking change?
if( dirty ) {
mark_rect_dirty_nc( xp, yp, xp + w - 1, yp + h - 1 );
}
display_img_blend_wc( h, xp, yp, sp, color, pix_blend CLIP_NUM_PAR );
}
else if( xp < CR.clip_rect.xx && xp + w > CR.clip_rect.x ) {
display_img_blend_wc( h, xp, yp, sp, color, pix_blend CLIP_NUM_PAR );
// since height may be reduced, start marking here
if( dirty ) {
mark_rect_dirty_wc( xp, yp, xp + w - 1, yp + h - 1 );
}
}
}
}
}
void display_rezoomed_img_alpha(const image_id n, const image_id alpha_n, const unsigned alpha_flags, KOORD_VAL xp, KOORD_VAL yp, const signed char /*player_nr*/, const FLAGGED_PIXVAL color_index, const int /*daynight*/, const int dirty CLIP_NUM_DEF)
{
if( n < anz_images && alpha_n < anz_images ) {
// need to go to nightmode and or rezoomed?
if( (images[n].recode_flags & FLAG_REZOOM) ) {
rezoom_img( n );
recode_img( n, 0 );
}
else if( (images[n].player_flags & 1) ) {
recode_img( n, 0 );
}
if( (images[alpha_n].recode_flags & FLAG_REZOOM) ) {
rezoom_img( alpha_n );
}
PIXVAL *sp = images[n].data[0];
// alphamap image uses base data as we don't want to recode
PIXVAL *alphamap = images[alpha_n].zoom_data != NULL ? images[alpha_n].zoom_data : images[alpha_n].base_data;
// now, since zooming may have change this image
xp += images[n].x;
yp += images[n].y;
KOORD_VAL h = images[n].h; // may change due to vertical clipping
// in the next line the vertical clipping will be handled
// by that way the drawing routines must only take into account the horizontal clipping
// this should be much faster in most cases
// must the height be reduced?
KOORD_VAL reduce_h = yp + h - CR.clip_rect.yy;
if( reduce_h > 0 ) {
h -= reduce_h;
}
// still something to draw
if( h <= 0 ) {
return;
}
// vertically lines to skip (only bottom is visible
KOORD_VAL skip_lines = CR.clip_rect.y - (int)yp;
if( skip_lines > 0 ) {
if( skip_lines >= h ) {
// not visible at all
return;
}
h -= skip_lines;
yp += skip_lines;
// now skip them
while( skip_lines-- ) {
do {
// clear run + colored run + next clear run
sp++;
sp += (*sp) & (~TRANSPARENT_RUN);
sp++;
alphamap++;
alphamap += (*alphamap) & (~TRANSPARENT_RUN);
alphamap++;
} while( *sp );
sp++;
alphamap++;
}
// now sp is the new start of an image with height h (same for alphamap)
}
// new block for new variables
{
// needed now ...
const KOORD_VAL w = images[n].w;
// get the real color
const PIXVAL color = color_index & 0xFFFF;
// use horizontal clipping or skip it?
if( xp >= CR.clip_rect.x && xp + w <= CR.clip_rect.xx ) {
// marking change?
if( dirty ) {
mark_rect_dirty_nc( xp, yp, xp + w - 1, yp + h - 1 );
}
display_img_alpha_wc( h, xp, yp, sp, alphamap, alpha_flags, color, alpha CLIP_NUM_PAR );
}
else if( xp < CR.clip_rect.xx && xp + w > CR.clip_rect.x ) {
display_img_alpha_wc( h, xp, yp, sp, alphamap, alpha_flags, color, alpha CLIP_NUM_PAR );
// since height may be reduced, start marking here
if( dirty ) {
mark_rect_dirty_wc( xp, yp, xp + w - 1, yp + h - 1 );
}
}
}
}
}
// Knightly : For blending or outlining unzoomed image. Adapted from display_base_img() and display_unzoomed_img_blend()
void display_base_img_blend(const image_id n, KOORD_VAL xp, KOORD_VAL yp, const signed char player_nr, const FLAGGED_PIXVAL color_index, const int daynight, const int dirty CLIP_NUM_DEF)
{
if( base_tile_raster_width == tile_raster_width ) {
// same size => use standard routine
display_rezoomed_img_blend( n, xp, yp, player_nr, color_index, daynight, dirty CLIP_NUM_PAR );
}
else if( n < anz_images ) {
// prissi: now test if visible and clipping needed
KOORD_VAL x = images[n].base_x + xp;
KOORD_VAL y = images[n].base_y + yp;
KOORD_VAL w = images[n].base_w;
KOORD_VAL h = images[n].base_h;
if( h == 0 || x >= CR.clip_rect.xx || y >= CR.clip_rect.yy || x + w <= CR.clip_rect.x || y + h <= CR.clip_rect.y ) {
// not visible => we are done
// happens quite often ...
return;
}
PIXVAL *sp = images[n].base_data;
// must the height be reduced?
KOORD_VAL reduce_h = y + h - CR.clip_rect.yy;
if( reduce_h > 0 ) {
h -= reduce_h;
}
// vertical lines to skip (only bottom is visible)
KOORD_VAL skip_lines = CR.clip_rect.y - (int)y;
if( skip_lines > 0 ) {
h -= skip_lines;
y += skip_lines;
// now skip them
while (skip_lines--) {
do {
// clear run + colored run + next clear run
sp++;
sp += (*sp) & (~TRANSPARENT_RUN);
sp++;
} while (*sp);
sp++;
}
// now sp is the new start of an image with height h
}
// new block for new variables
{
const PIXVAL color = color_index & 0xFFFF;
blend_proc pix_blend = (color_index&OUTLINE_FLAG) ? outline[ (color_index&TRANSPARENT_FLAGS)/TRANSPARENT25_FLAG - 1 ] : blend_recode[ (color_index&TRANSPARENT_FLAGS)/TRANSPARENT25_FLAG - 1 ];
// recode is needed only for blending
if( !(color_index&OUTLINE_FLAG) ) {
// colors for 2nd company color
if(player_nr>=0) {
activate_player_color( player_nr, daynight );
}
else {
// no player
activate_player_color( 0, daynight );
}
}
// use horizontal clipping or skip it?
if( x >= CR.clip_rect.x && x + w <= CR.clip_rect.xx ) {
if( dirty ) {
mark_rect_dirty_nc( x, y, x + w - 1, y + h - 1 );
}
display_img_blend_wc( h, x, y, sp, color, pix_blend CLIP_NUM_PAR );
}
else {
if( dirty ) {
mark_rect_dirty_wc( x, y, x + w - 1, y + h - 1 );
}
display_img_blend_wc( h, x, y, sp, color, pix_blend CLIP_NUM_PAR );
}
}
} // number ok
}
void display_base_img_alpha(const image_id n, const image_id alpha_n, const unsigned alpha_flags, KOORD_VAL xp, KOORD_VAL yp, const signed char player_nr, const FLAGGED_PIXVAL color_index, const int daynight, const int dirty CLIP_NUM_DEF)
{
if( base_tile_raster_width == tile_raster_width ) {
// same size => use standard routine
display_rezoomed_img_alpha( n, alpha_n, alpha_flags, xp, yp, player_nr, color_index, daynight, dirty CLIP_NUM_PAR );
}
else if( n < anz_images ) {
// prissi: now test if visible and clipping needed
KOORD_VAL x = images[n].base_x + xp;
KOORD_VAL y = images[n].base_y + yp;
KOORD_VAL w = images[n].base_w;
KOORD_VAL h = images[n].base_h;
if( h == 0 || x >= CR.clip_rect.xx || y >= CR.clip_rect.yy || x + w <= CR.clip_rect.x || y + h <= CR.clip_rect.y ) {
// not visible => we are done
// happens quite often ...
return;
}
PIXVAL *sp = images[n].base_data;
PIXVAL *alphamap = images[alpha_n].base_data;
// must the height be reduced?
KOORD_VAL reduce_h = y + h - CR.clip_rect.yy;
if( reduce_h > 0 ) {
h -= reduce_h;
}
// vertical lines to skip (only bottom is visible)
KOORD_VAL skip_lines = CR.clip_rect.y - (int)y;
if( skip_lines > 0 ) {
h -= skip_lines;
y += skip_lines;
// now skip them
while( skip_lines-- ) {
do {
// clear run + colored run + next clear run
sp++;
sp += (*sp) & (~TRANSPARENT_RUN);
sp++;
} while( *sp );
do {
// clear run + colored run + next clear run
alphamap++;
alphamap += (*alphamap) & (~TRANSPARENT_RUN);
alphamap++;
} while( *alphamap );
sp++;
alphamap++;
}
// now sp is the new start of an image with height h