1 /**
2 * @file
3 */
4
5 /*
6 Copyright (C) 2002-2013 UFO: Alien Invasion.
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
17 See the GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23 */
24
25 #include "ui_main.h"
26 #include "ui_internal.h"
27 #include "ui_parse.h"
28 #include "ui_sprite.h"
29 #include "ui_render.h"
30
31 const value_t ui_spriteProperties[] = {
32 {"size", V_POS, offsetof(uiSprite_t, size), MEMBER_SIZEOF(uiSprite_t, size)},
33 {"single", V_BOOL, offsetof(uiSprite_t, single), 0},
34 {"blend", V_BOOL, offsetof(uiSprite_t, blend), 0},
35 {"pack64", V_BOOL, offsetof(uiSprite_t, pack64), 0},
36 {"tiled_17_1_3", V_BOOL, offsetof(uiSprite_t, tiled_17_1_3), 0},
37 {"tiled_25_1_3", V_BOOL, offsetof(uiSprite_t, tiled_25_1_3), 0},
38 {"tiled_popup", V_BOOL, offsetof(uiSprite_t, tiled_popup), 0},
39 {"border", V_INT, offsetof(uiSprite_t, border), MEMBER_SIZEOF(uiSprite_t, border)},
40
41 {"texl", V_POS, offsetof(uiSprite_t, pos[SPRITE_STATUS_NORMAL]), MEMBER_SIZEOF(uiSprite_t, pos[SPRITE_STATUS_NORMAL])},
42 {"hoveredtexl", V_POS, offsetof(uiSprite_t, pos[SPRITE_STATUS_HOVER]), MEMBER_SIZEOF(uiSprite_t, pos[SPRITE_STATUS_HOVER])},
43 {"disabledtexl", V_POS, offsetof(uiSprite_t, pos[SPRITE_STATUS_DISABLED]), MEMBER_SIZEOF(uiSprite_t, pos[SPRITE_STATUS_DISABLED])},
44 {"clickedtexl", V_POS, offsetof(uiSprite_t, pos[SPRITE_STATUS_CLICKED]), MEMBER_SIZEOF(uiSprite_t, pos[SPRITE_STATUS_CLICKED])},
45
46 {"image", (valueTypes_t)V_REF_OF_STRING, offsetof(uiSprite_t, image[SPRITE_STATUS_NORMAL]), 0},
47 {"hoveredimage", (valueTypes_t)V_REF_OF_STRING, offsetof(uiSprite_t, image[SPRITE_STATUS_HOVER]), 0},
48 {"disabledimage", (valueTypes_t)V_REF_OF_STRING, offsetof(uiSprite_t, image[SPRITE_STATUS_DISABLED]), 0},
49 {"clickedimage", (valueTypes_t)V_REF_OF_STRING, offsetof(uiSprite_t, image[SPRITE_STATUS_CLICKED]), 0},
50
51 {"color", V_COLOR, offsetof(uiSprite_t, color[SPRITE_STATUS_NORMAL]), MEMBER_SIZEOF(uiSprite_t, color[SPRITE_STATUS_NORMAL])},
52 {"hoveredcolor", V_COLOR, offsetof(uiSprite_t, color[SPRITE_STATUS_HOVER]), MEMBER_SIZEOF(uiSprite_t, color[SPRITE_STATUS_HOVER])},
53 {"disabledcolor", V_COLOR, offsetof(uiSprite_t, color[SPRITE_STATUS_DISABLED]), MEMBER_SIZEOF(uiSprite_t, color[SPRITE_STATUS_DISABLED])},
54 {"clickedcolor", V_COLOR, offsetof(uiSprite_t, color[SPRITE_STATUS_CLICKED]), MEMBER_SIZEOF(uiSprite_t, color[SPRITE_STATUS_CLICKED])},
55
56 {nullptr, V_NULL, 0, 0}
57 };
58
59 /**
60 * @brief Search a file name inside pics/ according to the sprite name
61 * If it exists, generate a "single" sprite using the size of the image
62 * @param name Name of the sprite
63 * @return A sprite, else nullptr
64 */
UI_AutoGenerateSprite(const char * name)65 static uiSprite_t* UI_AutoGenerateSprite (const char* name)
66 {
67 uiSprite_t* sprite = nullptr;
68 const char* suffix[SPRITE_STATUS_MAX] = {"", "_hovered", "_disabled", "_clicked"};
69 char basePicNameBuf[MAX_QPATH];
70 const image_t* pic;
71 int i;
72
73 Q_strncpyz(basePicNameBuf, name, sizeof(basePicNameBuf));
74
75 pic = UI_LoadImage(basePicNameBuf);
76 if (pic == nullptr)
77 return nullptr;
78
79 sprite = UI_AllocStaticSprite(basePicNameBuf);
80 sprite->image[SPRITE_STATUS_NORMAL] = UI_AllocStaticString(basePicNameBuf, 0);
81 sprite->size[0] = pic->width;
82 sprite->size[1] = pic->height;
83 for (i = 1; i < SPRITE_STATUS_MAX; i++) {
84 char picNameBuf[MAX_QPATH];
85 Com_sprintf(picNameBuf, sizeof(picNameBuf), "%s%s", basePicNameBuf, suffix[i]);
86 pic = UI_LoadImage(picNameBuf);
87 if (pic != nullptr)
88 sprite->image[i] = UI_AllocStaticString(picNameBuf, 0);
89 }
90 return sprite;
91 }
92
93 /**
94 * @brief Check if an sprite name exists
95 * @param[in] name Name of the sprite
96 * @return True if the sprite exists
97 * @note not very fast; if we use it often we should improve the search
98 */
UI_SpriteExists(const char * name)99 static bool UI_SpriteExists (const char* name)
100 {
101 int i;
102 for (i = 0; i < ui_global.numSprites; i++) {
103 if (strncmp(name, ui_global.sprites[i].name, MEMBER_SIZEOF(uiSprite_t, name)) != 0)
104 continue;
105 return true;
106 }
107 return false;
108 }
109
110 /**
111 * @brief Return an sprite by is name
112 * @param[in] name Name of the sprite
113 * @return The requested sprite, else nullptr
114 * @note not very fast; if we use it often we should improve the search
115 */
UI_GetSpriteByName(const char * name)116 uiSprite_t* UI_GetSpriteByName (const char* name)
117 {
118 int i;
119 for (i = 0; i < ui_global.numSprites; i++) {
120 if (Q_streq(name, ui_global.sprites[i].name))
121 return &ui_global.sprites[i];
122 }
123 return UI_AutoGenerateSprite(name);
124 }
125
126 /**
127 * @brief Allocate an sprite to the UI static memory
128 * @note Its not a dynamic memory allocation. Please only use it at the loading time
129 * @param[in] name Name of the sprite
130 * @todo Assert out when we are not in parsing/loading stage
131 */
UI_AllocStaticSprite(const char * name)132 uiSprite_t* UI_AllocStaticSprite (const char* name)
133 {
134 uiSprite_t* result;
135
136 if (UI_SpriteExists(name))
137 Com_Error(ERR_FATAL, "UI_AllocStaticSprite: \"%s\" sprite already allocated. Check your scripts.", name);
138
139 if (ui_global.numSprites >= UI_MAX_SPRITES)
140 Com_Error(ERR_FATAL, "UI_AllocStaticSprite: UI_MAX_SPRITES hit");
141
142 result = &ui_global.sprites[ui_global.numSprites];
143 ui_global.numSprites++;
144
145 OBJZERO(*result);
146 Q_strncpyz(result->name, name, sizeof(result->name));
147 return result;
148 }
149
150 /**
151 * Template to draw a tiled texture with
152 * corner size of 17 pixels, middle size of 1 pixel,
153 * margin between tiles of 3 pixels
154 */
155 static const int tile_template_17_1_3[] = {
156 17, 1, 17,
157 17, 1, 17,
158 3
159 };
160
161 /**
162 * Template to draw a tiled texture with
163 * corner size of 17 pixels, middle size of 1 pixel,
164 * margin between tiles of 3 pixels
165 */
166 static const int tile_template_25_1_3[] = {
167 25, 1, 25,
168 25, 1, 25,
169 3
170 };
171
172 /**
173 * Template to draw a tiled texture with
174 * used for popup windows
175 */
176 static const int tile_template_popup[] = {
177 20, 1, 19,
178 46, 1, 19,
179 3
180 };
181
182 /**
183 * @param[in] status The state of the sprite node
184 * @param[in] sprite Context sprite
185 * @param[in] posX Absolute X position of the top-left corner
186 * @param[in] posY Absolute Y position of the top-left corner
187 * @param[in] sizeX Width of the bounded box
188 * @param[in] sizeY Height of the bounded box
189 * @todo use named const for status
190 */
UI_DrawSpriteInBox(bool flip,const uiSprite_t * sprite,uiSpriteStatus_t status,int posX,int posY,int sizeX,int sizeY)191 void UI_DrawSpriteInBox (bool flip, const uiSprite_t* sprite, uiSpriteStatus_t status, int posX, int posY, int sizeX, int sizeY)
192 {
193 float texX;
194 float texY;
195 const char* image;
196
197 /** @todo Add warning */
198 if (status >= SPRITE_STATUS_MAX)
199 return;
200
201 /** @todo merge all this cases */
202 if (sprite->single || sprite->blend) {
203 texX = sprite->pos[SPRITE_STATUS_NORMAL][0];
204 texY = sprite->pos[SPRITE_STATUS_NORMAL][1];
205 image = sprite->image[SPRITE_STATUS_NORMAL];
206 } else if (sprite->pack64) {
207 texX = sprite->pos[SPRITE_STATUS_NORMAL][0];
208 texY = sprite->pos[SPRITE_STATUS_NORMAL][1] + (64 * status);
209 image = sprite->image[SPRITE_STATUS_NORMAL];
210 } else {
211 texX = sprite->pos[status][0];
212 texY = sprite->pos[status][1];
213 image = sprite->image[status];
214 if (!image) {
215 if (texX == 0 && texY == 0) {
216 texX = sprite->pos[SPRITE_STATUS_NORMAL][0];
217 texY = sprite->pos[SPRITE_STATUS_NORMAL][1];
218 image = sprite->image[SPRITE_STATUS_NORMAL];
219 } else {
220 image = sprite->image[SPRITE_STATUS_NORMAL];
221 }
222 }
223 }
224 if (!image)
225 return;
226
227 if (sprite->blend) {
228 const vec_t* color = sprite->color[status];
229 R_Color(color);
230 }
231
232 if (sprite->tiled_17_1_3) {
233 const vec2_t pos = Vector2FromInt(posX, posY);
234 const vec2_t size = Vector2FromInt(sizeX, sizeY);
235 UI_DrawPanel(pos, size, image, texX, texY, tile_template_17_1_3);
236 } else if (sprite->tiled_25_1_3) {
237 const vec2_t pos = Vector2FromInt(posX, posY);
238 const vec2_t size = Vector2FromInt(sizeX, sizeY);
239 UI_DrawPanel(pos, size, image, texX, texY, tile_template_25_1_3);
240 } else if (sprite->tiled_popup) {
241 const vec2_t pos = Vector2FromInt(posX, posY);
242 const vec2_t size = Vector2FromInt(sizeX, sizeY);
243 UI_DrawPanel(pos, size, image, texX, texY, tile_template_popup);
244 } else if (sprite->border != 0) {
245 const vec2_t pos = Vector2FromInt(posX, posY);
246 const vec2_t size = Vector2FromInt(sizeX, sizeY);
247 UI_DrawBorderedPanel(pos, size, image, texX, texY, sprite->size[0], sprite->size[1], sprite->border);
248 } else {
249 posX += (sizeX - sprite->size[0]) / 2;
250 posY += (sizeY - sprite->size[1]) / 2;
251 UI_DrawNormImageByName(flip, posX, posY, sprite->size[0], sprite->size[1],
252 texX + sprite->size[0], texY + sprite->size[1], texX, texY, image);
253 }
254
255 if (sprite->blend)
256 R_Color(nullptr);
257 }
258