changeset 14059:76db067696bb draft

(svn r18602) -Codechange: unify the viewport sign and text effect drawing -Fix [FS#3394]: signs could occasionally glitch
author rubidium <rubidium@openttd.org>
date Tue, 22 Dec 2009 12:50:41 +0000
parents e72e69fcf01b
children 61587e09b9c1
files src/texteff.cpp src/viewport.cpp src/viewport_func.h src/viewport_type.h
diffstat 4 files changed, 102 insertions(+), 172 deletions(-) [+]
line wrap: on
line diff
--- a/src/texteff.cpp
+++ b/src/texteff.cpp
@@ -24,15 +24,20 @@
 	INIT_NUM_TEXT_EFFECTS  =  20,
 };
 
-struct TextEffect {
-	StringID string_id;
-	int32 x;
-	int32 y;
-	int32 right;
-	int32 bottom;
-	uint16 duration;
-	uint64 params_1;
-	TextEffectMode mode;
+/** Container for all information about a text effect */
+struct TextEffect : public ViewportSign{
+	StringID string_id;  ///< String to draw for the text effect, if INVALID_STRING_ID then it's not valid
+	uint16 duration;     ///< How long the text effect should stay
+	uint64 params_1;     ///< DParam parameter
+	TextEffectMode mode; ///< Type of text effect
+
+	/** Reset the text effect */
+	void Reset()
+	{
+		this->MarkDirty();
+		this->width_normal = 0;
+		this->string_id = INVALID_STRING_ID;
+	}
 };
 
 /* used for text effects */
@@ -40,30 +45,8 @@
 static uint16 _num_text_effects = INIT_NUM_TEXT_EFFECTS;
 
 /* Text Effects */
-/**
- * Mark the area of the text effect as dirty.
- *
- * This function marks the area of a text effect as dirty for repaint.
- *
- * @param te The TextEffect to mark the area dirty
- * @ingroup dirty
- */
-static void MarkTextEffectAreaDirty(TextEffect *te)
+TextEffectID AddTextEffect(StringID msg, int center, int y, uint16 duration, TextEffectMode mode)
 {
-	/* Width and height of the text effect are doubled, so they are correct in both zoom out levels 1x and 2x. */
-	MarkAllViewportsDirty(
-		te->x,
-		te->y - 1,
-		(te->right - te->x)*2 + te->x + 1,
-		(te->bottom - (te->y - 1)) * 2 + (te->y - 1) + 1
-	);
-}
-
-TextEffectID AddTextEffect(StringID msg, int x, int y, uint16 duration, TextEffectMode mode)
-{
-	TextEffect *te;
-	int w;
-	char buffer[100];
 	TextEffectID i;
 
 	if (_game_mode == GM_MENU) return INVALID_TE_ID;
@@ -81,22 +64,17 @@
 		i = _num_text_effects - 1;
 	}
 
-	te = &_text_effect_list[i];
+	TextEffect *te = &_text_effect_list[i];
 
 	/* Start defining this object */
 	te->string_id = msg;
 	te->duration = duration;
-	te->y = y - FONT_HEIGHT_NORMAL / 2;
-	te->bottom = y + FONT_HEIGHT_NORMAL / 2;
 	te->params_1 = GetDParam(0);
 	te->mode = mode;
 
-	GetString(buffer, msg, lastof(buffer));
-	w = GetStringBoundingBox(buffer).width;
-
-	te->x = x - (w >> 1);
-	te->right = x + (w >> 1) - 1;
-	MarkTextEffectAreaDirty(te);
+	/* Make sure we only dirty the new area */
+	te->width_normal = 0;
+	te->UpdatePosition(center, y, msg);
 
 	return i;
 }
@@ -104,33 +82,19 @@
 void UpdateTextEffect(TextEffectID te_id, StringID msg)
 {
 	assert(te_id < _num_text_effects);
-	TextEffect *te;
 
 	/* Update details */
-	te = &_text_effect_list[te_id];
+	TextEffect *te = &_text_effect_list[te_id];
 	te->string_id = msg;
 	te->params_1 = GetDParam(0);
 
-	/* Update width of text effect */
-	char buffer[100];
-	GetString(buffer, msg, lastof(buffer));
-	int w = GetStringBoundingBox(buffer).width;
-
-	/* Only allow to make it broader, so it completely covers the old text. That avoids remnants of the old text. */
-	int right_new = te->x + w;
-	if (te->right < right_new) te->right = right_new;
-
-	MarkTextEffectAreaDirty(te);
+	te->UpdatePosition(te->center, te->top, msg);
 }
 
 void RemoveTextEffect(TextEffectID te_id)
 {
 	assert(te_id < _num_text_effects);
-	TextEffect *te;
-
-	te = &_text_effect_list[te_id];
-	MarkTextEffectAreaDirty(te);
-	te->string_id = INVALID_STRING_ID;
+	_text_effect_list[te_id].Reset();
 }
 
 static void MoveTextEffect(TextEffect *te)
@@ -138,13 +102,13 @@
 	/* Never expire for duration of 0xFFFF */
 	if (te->duration == 0xFFFF) return;
 	if (te->duration < 8) {
-		te->string_id = INVALID_STRING_ID;
+		te->Reset();
 	} else {
 		te->duration -= 8;
-		te->y--;
-		te->bottom--;
+		te->MarkDirty();
+		te->top--;
+		te->MarkDirty();
 	}
-	MarkTextEffectAreaDirty(te);
 }
 
 void MoveAllTextEffects()
@@ -164,41 +128,15 @@
 
 void DrawTextEffects(DrawPixelInfo *dpi)
 {
-	switch (dpi->zoom) {
-		case ZOOM_LVL_NORMAL:
-			for (TextEffectID i = 0; i < _num_text_effects; i++) {
-				TextEffect *te = &_text_effect_list[i];
-				if (te->string_id != INVALID_STRING_ID &&
-						dpi->left <= te->right &&
-						dpi->top  <= te->bottom &&
-						dpi->left + dpi->width  > te->x &&
-						dpi->top  + dpi->height > te->y) {
-					if (te->mode == TE_RISING || (_settings_client.gui.loading_indicators && !IsTransparencySet(TO_LOADING))) {
-						AddStringToDraw(te->x, te->y, te->string_id, te->params_1, INVALID_STRING_ID);
-					}
-				}
-			}
-			break;
+	/* Don't draw the text effects when zoomed out a lot */
+	if (dpi->zoom > ZOOM_LVL_OUT_2X) return;
 
-		case ZOOM_LVL_OUT_2X:
-			for (TextEffectID i = 0; i < _num_text_effects; i++) {
-				TextEffect *te = &_text_effect_list[i];
-				if (te->string_id != INVALID_STRING_ID &&
-						dpi->left <= te->right  * 2 - te->x &&
-						dpi->top  <= te->bottom * 2 - te->y &&
-						dpi->left + dpi->width  > te->x &&
-						dpi->top  + dpi->height > te->y) {
-					if (te->mode == TE_RISING || (_settings_client.gui.loading_indicators && !IsTransparencySet(TO_LOADING))) {
-						AddStringToDraw(te->x, te->y, (StringID)(te->string_id - 1), te->params_1, INVALID_STRING_ID);
-					}
-				}
-			}
-			break;
+	for (TextEffectID i = 0; i < _num_text_effects; i++) {
+		const TextEffect *te = &_text_effect_list[i];
+		if (te->string_id == INVALID_STRING_ID) continue;
 
-		case ZOOM_LVL_OUT_4X:
-		case ZOOM_LVL_OUT_8X:
-			break;
-
-		default: NOT_REACHED();
+		if (te->mode == TE_RISING || (_settings_client.gui.loading_indicators && !IsTransparencySet(TO_LOADING))) {
+			ViewportAddString(dpi, ZOOM_LVL_OUT_2X, te, te->string_id, te->string_id - 1, 0, te->params_1);
+		}
 	}
 }
--- a/src/viewport.cpp
+++ b/src/viewport.cpp
@@ -775,9 +775,9 @@
 	_vd.last_child = &cs->next;
 }
 
-/* Returns a StringSpriteToDraw */
-void AddStringToDraw(int x, int y, StringID string, uint64 params_1, uint64 params_2, Colours colour, uint16 width)
+static void AddStringToDraw(int x, int y, StringID string, uint64 params_1, uint64 params_2, Colours colour, uint16 width)
 {
+	assert(width != 0);
 	StringSpriteToDraw *ss = _vd.string_sprites_to_draw.Append();
 	ss->string = string;
 	ss->x = x;
@@ -1066,55 +1066,42 @@
 /**
  * Add a string to draw in the viewport
  * @param dpi current viewport area
+ * @param small_from Zoomlevel from when the small font should be used
  * @param sign sign position and dimension
  * @param string_normal String for normal and 2x zoom level
  * @param string_small String for 4x and 8x zoom level
  * @param string_small_shadow Shadow string for 4x and 8x zoom level; or STR_NULL if no shadow
  * @param colour colour of the sign background; or 0 if transparent
  */
-static void ViewportAddString(const DrawPixelInfo *dpi, const ViewportSign *sign, StringID string_normal, StringID string_small, StringID string_small_shadow, uint64 params_1, uint64 params_2, Colours colour = INVALID_COLOUR)
+void ViewportAddString(const DrawPixelInfo *dpi, ZoomLevel small_from, const ViewportSign *sign, StringID string_normal, StringID string_small, StringID string_small_shadow, uint64 params_1, uint64 params_2, Colours colour)
 {
-	int left = dpi->left;
-	int top = dpi->top;
-	int right = left + dpi->width;
+	bool small = dpi->zoom >= small_from;
+
+	int left   = dpi->left;
+	int top    = dpi->top;
+	int right  = left + dpi->width;
 	int bottom = top + dpi->height;
 
-	int sign_height = FONT_HEIGHT_NORMAL + 2;
-
-	switch (dpi->zoom) {
-		case ZOOM_LVL_OUT_2X:
-			right += 2;
-			bottom += 2;
-		/* FALL THROUGH */
-		case ZOOM_LVL_NORMAL:
-			if (bottom > sign->top &&
-					top    < sign->top + ScaleByZoom(sign_height, dpi->zoom) &&
-					right  > sign->left &&
-					left   < sign->left + ScaleByZoom(sign->width_normal, dpi->zoom)) {
-				AddStringToDraw(sign->left + 1, sign->top + 1, string_normal, params_1, params_2, colour, colour != INVALID_COLOUR ? sign->width_normal : 0);
-			}
-			break;
-
-		case ZOOM_LVL_OUT_4X:
-		case ZOOM_LVL_OUT_8X:
-			right += ScaleByZoom(1, dpi->zoom);
-			bottom += ScaleByZoom(1, dpi->zoom) + 1;
-
-			if (bottom > sign->top &&
-					top    < sign->top + ScaleByZoom(sign_height, dpi->zoom) &&
-					right  > sign->left &&
-					left   < sign->left + ScaleByZoom(sign->width_small, dpi->zoom)) {
-				int shadow_offset = 0;
-				if (string_small_shadow != STR_NULL) {
-					shadow_offset = 4;
-					AddStringToDraw(sign->left + 1 + shadow_offset, sign->top + 1, string_small_shadow, params_1, params_2);
-				}
-				AddStringToDraw(sign->left + 1, sign->top + 1 - shadow_offset, string_small, params_1, params_2,
-						colour, colour != INVALID_COLOUR ? sign->width_small | 0x8000 : 0);
-			}
-			break;
-
-		default: NOT_REACHED();
+	int sign_height     = ScaleByZoom(VPSM_TOP + FONT_HEIGHT_NORMAL + VPSM_BOTTOM, dpi->zoom);
+	int sign_half_width = ScaleByZoom((small ? sign->width_small : sign->width_normal) / 2, dpi->zoom);
+
+	if (bottom < sign->top &&
+			top    > sign->top + sign_height &&
+			right  < sign->center - sign_half_width &&
+			left   > sign->center + sign_half_width) {
+		return;
+	}
+
+	if (!small) {
+		AddStringToDraw(sign->center - sign_half_width, sign->top, string_normal, params_1, params_2, colour, sign->width_normal);
+	} else {
+		int shadow_offset = 0;
+		if (string_small_shadow != STR_NULL) {
+			shadow_offset = 4;
+			AddStringToDraw(sign->center - sign_half_width + shadow_offset, sign->top, string_small_shadow, params_1, params_2, INVALID_COLOUR, sign->width_small);
+		}
+		AddStringToDraw(sign->center - sign_half_width, sign->top - shadow_offset, string_small, params_1, params_2,
+				colour, sign->width_small | 0x8000);
 	}
 }
 
@@ -1124,7 +1111,7 @@
 
 	const Town *t;
 	FOR_ALL_TOWNS(t) {
-		ViewportAddString(dpi, &t->sign,
+		ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &t->sign,
 				_settings_client.gui.population_in_label ? STR_VIEWPORT_TOWN_POP : STR_VIEWPORT_TOWN,
 				STR_VIEWPORT_TOWN_TINY_WHITE, STR_VIEWPORT_TOWN_TINY_BLACK,
 				t->index, t->population);
@@ -1144,7 +1131,7 @@
 		/* Don't draw if the display options are disabled */
 		if (!HasBit(_display_opt, is_station ? DO_SHOW_STATION_NAMES : DO_SHOW_WAYPOINT_NAMES)) continue;
 
-		ViewportAddString(dpi, &st->sign,
+		ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &st->sign,
 				is_station ? STR_VIEWPORT_STATION : STR_VIEWPORT_WAYPOINT,
 				(is_station ? STR_VIEWPORT_STATION : STR_VIEWPORT_WAYPOINT) + 1, STR_NULL,
 				st->index, st->facilities, (st->owner == OWNER_NONE || !st->IsInUse()) ? COLOUR_GREY : _company_colours[st->owner]);
@@ -1159,7 +1146,7 @@
 
 	const Sign *si;
 	FOR_ALL_SIGNS(si) {
-		ViewportAddString(dpi, &si->sign,
+		ViewportAddString(dpi, ZOOM_LVL_OUT_4X, &si->sign,
 				STR_WHITE_SIGN,
 				IsTransparencySet(TO_SIGNS) ? STR_VIEWPORT_SIGN_SMALL_WHITE : STR_VIEWPORT_SIGN_SMALL_BLACK, STR_NULL,
 				si->index, 0, (si->owner == OWNER_NONE) ? COLOUR_GREY : _company_colours[si->owner]);
@@ -1181,12 +1168,12 @@
 	char buffer[DRAW_STRING_BUFFER];
 
 	GetString(buffer, str, lastof(buffer));
-	this->width_normal = GetStringBoundingBox(buffer).width;
-	this->left = center - this->width_normal / 2;
+	this->width_normal = VPSM_LEFT + Align(GetStringBoundingBox(buffer).width, 2) + VPSM_RIGHT;
+	this->center = center;
 
 	/* zoomed out version */
 	_cur_fontsize = FS_SMALL;
-	this->width_small = GetStringBoundingBox(buffer).width;
+	this->width_small = VPSM_LEFT + Align(GetStringBoundingBox(buffer).width, 2) + VPSM_RIGHT;
 	_cur_fontsize = FS_NORMAL;
 
 	this->MarkDirty();
@@ -1201,12 +1188,13 @@
 {
 	/* We use ZOOM_LVL_MAX here, as every viewport can have an other zoom,
 	 *  and there is no way for us to know which is the biggest. So make the
-	 *  biggest area dirty, and we are safe for sure. */
+	 *  biggest area dirty, and we are safe for sure.
+	 * We also add 1 to make sure the whole thing is redrawn. */
 	MarkAllViewportsDirty(
-		this->left - 6,
-		this->top  - 3,
-		this->left + ScaleByZoom(this->width_normal + 12, ZOOM_LVL_MAX),
-		this->top  + ScaleByZoom(FONT_HEIGHT_NORMAL + 2, ZOOM_LVL_MAX));
+		this->center - ScaleByZoom(this->width_normal / 2 + 1, ZOOM_LVL_MAX),
+		this->top    - ScaleByZoom(1, ZOOM_LVL_MAX),
+		this->center + ScaleByZoom(this->width_normal / 2 + 1, ZOOM_LVL_MAX),
+		this->top    + ScaleByZoom(VPSM_TOP + FONT_HEIGHT_NORMAL + VPSM_BOTTOM + 1, ZOOM_LVL_MAX));
 }
 
 static void ViewportDrawTileSprites(const TileSpriteToDrawVector *tstdv)
@@ -1335,12 +1323,12 @@
 		int w = GB(ss->width, 0, 15);
 		int x = UnScaleByZoom(ss->x, zoom);
 		int y = UnScaleByZoom(ss->y, zoom);
-		int bottom = y + (small ? FONT_HEIGHT_SMALL : FONT_HEIGHT_NORMAL);
+		int h = VPSM_TOP + (small ? FONT_HEIGHT_SMALL : FONT_HEIGHT_NORMAL) + VPSM_BOTTOM;
 
 		SetDParam(0, ss->params[0]);
 		SetDParam(1, ss->params[1]);
 
-		if (w != 0) {
+		if (ss->colour != INVALID_COLOUR) {
 			/* Do not draw signs nor station names if they are set invisible */
 			if (IsInvisibilitySet(TO_SIGNS) && ss->string != STR_WHITE_SIGN) continue;
 
@@ -1356,19 +1344,13 @@
 			 * or if we are drawing a general text sign (STR_WHITE_SIGN) */
 			if (!IsTransparencySet(TO_SIGNS) || ss->string == STR_WHITE_SIGN) {
 				DrawFrameRect(
-					x - 1, y - 1, x + 1 + w, bottom, ss->colour,
+					x, y, x + w, y + h, ss->colour,
 					IsTransparencySet(TO_SIGNS) ? FR_TRANSPARENT : FR_NONE
 				);
 			}
-
-			if (small) y -= 1;
-		} else {
-			char buffer[DRAW_STRING_BUFFER];
-			GetString(buffer, ss->string, lastof(buffer));
-			w = GetStringBoundingBox(buffer).width;
 		}
 
-		DrawString(x, x + w - 1, y, ss->string, colour, SA_CENTER);
+		DrawString(x + VPSM_LEFT, x + w - 1 - VPSM_RIGHT, y + VPSM_TOP, ss->string, colour, SA_CENTER);
 	}
 }
 
@@ -1721,16 +1703,18 @@
  */
 static bool CheckClickOnViewportSign(const ViewPort *vp, int x, int y, const ViewportSign *sign)
 {
-	int sign_width = (vp->zoom >= ZOOM_LVL_OUT_4X ? sign->width_small : sign->width_normal);
-	int sign_height = FONT_HEIGHT_NORMAL + 2;
-
-	x = ScaleByZoom(x - vp->left + ScaleByZoom(1, vp->zoom) - 1, vp->zoom) + vp->virtual_left;
-	y = ScaleByZoom(y - vp->top  + ScaleByZoom(1, vp->zoom) - 1, vp->zoom) + vp->virtual_top;
-
-	return (y >= sign->top &&
-			y < sign->top + ScaleByZoom(sign_height, vp->zoom) &&
-			x >= sign->left &&
-			x < sign->left + ScaleByZoom(sign_width, vp->zoom));
+	bool small = (vp->zoom >= ZOOM_LVL_OUT_4X);
+	int sign_half_width = ScaleByZoom((small ? sign->width_small : sign->width_normal) / 2, vp->zoom);
+	int sign_height = ScaleByZoom(VPSM_TOP + (small ? FONT_HEIGHT_SMALL : FONT_HEIGHT_NORMAL) + VPSM_BOTTOM, vp->zoom);
+
+	x = ScaleByZoom(x - vp->left, vp->zoom) + vp->virtual_left;
+	y = ScaleByZoom(y - vp->top, vp->zoom) + vp->virtual_top;
+
+	return
+			y >= sign->top &&
+			y <  sign->top + sign_height &&
+			x >= sign->center - sign_half_width &&
+			x <  sign->center + sign_half_width;
 }
 
 static bool CheckClickOnTown(const ViewPort *vp, int x, int y)
--- a/src/viewport_func.h
+++ b/src/viewport_func.h
@@ -48,8 +48,8 @@
 void DrawGroundSprite(SpriteID image, SpriteID pal, const SubSprite *sub = NULL, int extra_offs_x = 0, int extra_offs_y = 0);
 void DrawGroundSpriteAt(SpriteID image, SpriteID pal, int32 x, int32 y, byte z, const SubSprite *sub = NULL, int extra_offs_x = 0, int extra_offs_y = 0);
 void AddSortableSpriteToDraw(SpriteID image, SpriteID pal, int x, int y, int w, int h, int dz, int z, bool transparent = false, int bb_offset_x = 0, int bb_offset_y = 0, int bb_offset_z = 0, const SubSprite *sub = NULL);
-void AddStringToDraw(int x, int y, StringID string, uint64 params_1, uint64 params_2, Colours colour = INVALID_COLOUR, uint16 width = 0);
 void AddChildSpriteScreen(SpriteID image, SpriteID pal, int x, int y, bool transparent = false, const SubSprite *sub = NULL);
+void ViewportAddString(const DrawPixelInfo *dpi, ZoomLevel small_from, const ViewportSign *sign, StringID string_normal, StringID string_small, StringID string_small_shadow, uint64 params_1, uint64 params_2 = 0, Colours colour = INVALID_COLOUR);
 
 
 void StartSpriteCombine();
--- a/src/viewport_type.h
+++ b/src/viewport_type.h
@@ -32,10 +32,18 @@
 	ZoomLevel zoom;
 };
 
+/** Margings for the viewport sign */
+enum ViewportSignMargin {
+	VPSM_LEFT   = 1, ///< Left margin
+	VPSM_RIGHT  = 1, ///< Right margin
+	VPSM_TOP    = 1, ///< Top margin
+	VPSM_BOTTOM = 1, ///< Bottom margin
+};
+
 /** Location information about a sign as seen on the viewport */
 struct ViewportSign {
-	int32 left; ///< The left most position of the sign
-	int32 top;  ///< THe top of the sign
+	int32 center;        ///< The center position of the sign
+	int32 top;           ///< The top of the sign
 	uint16 width_normal; ///< The width when not zoomed out (normal font)
 	uint16 width_small;  ///< The width when zoomed out (small font)