added optional button to restart game (door, panel and touch variants)
[rocksndiamonds.git] / src / libgame / image.c
1 // ============================================================================
2 // Artsoft Retro-Game Library
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // image.c
10 // ============================================================================
11
12 #include "image.h"
13 #include "misc.h"
14 #include "setup.h"
15
16
17 struct ImageInfo
18 {
19   char *source_filename;
20   int num_references;
21
22   Bitmap *bitmaps[NUM_IMG_BITMAP_POINTERS];
23
24   int original_width;                   // original image file width
25   int original_height;                  // original image file height
26
27   boolean contains_small_images;        // set after adding small images
28   boolean contains_textures;            // set after adding GPU textures
29   boolean scaled_up;                    // set after scaling up
30
31   int conf_tile_size;                   // tile size as defined in config
32   int game_tile_size;                   // tile size as resized for game
33
34   char *leveldir;                       // level set when image was loaded
35 };
36 typedef struct ImageInfo ImageInfo;
37
38 static struct ArtworkListInfo *image_info = NULL;
39
40 static void *Load_Image(char *filename)
41 {
42   ImageInfo *img_info = checked_calloc(sizeof(ImageInfo));
43
44   if ((img_info->bitmaps[IMG_BITMAP_STANDARD] = LoadImage(filename)) == NULL)
45   {
46     Warn("cannot load image file '%s': LoadImage() failed", filename);
47
48     free(img_info);
49
50     return NULL;
51   }
52
53   img_info->source_filename = getStringCopy(filename);
54
55   img_info->original_width  = img_info->bitmaps[IMG_BITMAP_STANDARD]->width;
56   img_info->original_height = img_info->bitmaps[IMG_BITMAP_STANDARD]->height;
57
58   img_info->contains_small_images = FALSE;
59   img_info->contains_textures = FALSE;
60   img_info->scaled_up = FALSE;
61
62   img_info->conf_tile_size = 0;         // will be set later
63   img_info->game_tile_size = 0;         // will be set later
64
65   img_info->leveldir = NULL;            // will be set later
66
67   return img_info;
68 }
69
70 static void FreeImage(void *ptr)
71 {
72   ImageInfo *image = (ImageInfo *)ptr;
73   int i;
74
75   if (image == NULL)
76     return;
77
78   for (i = 0; i < NUM_IMG_BITMAPS; i++)
79     if (image->bitmaps[i])
80       FreeBitmap(image->bitmaps[i]);
81
82   if (image->source_filename)
83     free(image->source_filename);
84
85   free(image);
86 }
87
88 int getImageListSize(void)
89 {
90   return (image_info->num_file_list_entries +
91           image_info->num_dynamic_file_list_entries);
92 }
93
94 struct FileInfo *getImageListEntryFromImageID(int pos)
95 {
96   int num_list_entries = image_info->num_file_list_entries;
97   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
98
99   return (pos < num_list_entries ? &image_info->file_list[list_pos] :
100           &image_info->dynamic_file_list[list_pos]);
101 }
102
103 static ImageInfo *getImageInfoEntryFromImageID(int pos)
104 {
105   int num_list_entries = image_info->num_file_list_entries;
106   int list_pos = (pos < num_list_entries ? pos : pos - num_list_entries);
107   ImageInfo **img_info =
108     (ImageInfo **)(pos < num_list_entries ? image_info->artwork_list :
109                    image_info->dynamic_artwork_list);
110
111   return img_info[list_pos];
112 }
113
114 Bitmap **getBitmapsFromImageID(int pos)
115 {
116   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
117
118   return (img_info != NULL ? img_info->bitmaps : NULL);
119 }
120
121 int getOriginalImageWidthFromImageID(int pos)
122 {
123   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
124
125   return (img_info != NULL ? img_info->original_width : 0);
126 }
127
128 int getOriginalImageHeightFromImageID(int pos)
129 {
130   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
131
132   return (img_info != NULL ? img_info->original_height : 0);
133 }
134
135 char *getTokenFromImageID(int graphic)
136 {
137   struct FileInfo *file_list = getImageListEntryFromImageID(graphic);
138
139   return (file_list != NULL ? file_list->token : NULL);
140 }
141
142 int getImageIDFromToken(char *token)
143 {
144   struct FileInfo *file_list = image_info->file_list;
145   int num_list_entries = image_info->num_file_list_entries;
146   int i;
147
148   for (i = 0; i < num_list_entries; i++)
149     if (strEqual(file_list[i].token, token))
150       return i;
151
152   return -1;
153 }
154
155 char *getImageConfigFilename(void)
156 {
157   return getCustomArtworkConfigFilename(image_info->type);
158 }
159
160 int getImageListPropertyMappingSize(void)
161 {
162   return image_info->num_property_mapping_entries;
163 }
164
165 struct PropertyMapping *getImageListPropertyMapping(void)
166 {
167   return image_info->property_mapping;
168 }
169
170 void InitImageList(struct ConfigInfo *config_list, int num_file_list_entries,
171                    struct ConfigTypeInfo *config_suffix_list,
172                    char **base_prefixes, char **ext1_suffixes,
173                    char **ext2_suffixes, char **ext3_suffixes,
174                    char **ignore_tokens)
175 {
176   int i;
177
178   image_info = checked_calloc(sizeof(struct ArtworkListInfo));
179   image_info->type = ARTWORK_TYPE_GRAPHICS;
180
181   // ---------- initialize file list and suffix lists ----------
182
183   image_info->num_file_list_entries = num_file_list_entries;
184   image_info->num_dynamic_file_list_entries = 0;
185
186   image_info->file_list =
187     getFileListFromConfigList(config_list, config_suffix_list, ignore_tokens,
188                               num_file_list_entries);
189   image_info->dynamic_file_list = NULL;
190
191   image_info->num_suffix_list_entries = 0;
192   for (i = 0; config_suffix_list[i].token != NULL; i++)
193     image_info->num_suffix_list_entries++;
194
195   image_info->suffix_list = config_suffix_list;
196
197   // ---------- initialize base prefix and suffixes lists ----------
198
199   image_info->num_base_prefixes = 0;
200   for (i = 0; base_prefixes[i] != NULL; i++)
201     image_info->num_base_prefixes++;
202
203   image_info->num_ext1_suffixes = 0;
204   for (i = 0; ext1_suffixes[i] != NULL; i++)
205     image_info->num_ext1_suffixes++;
206
207   image_info->num_ext2_suffixes = 0;
208   for (i = 0; ext2_suffixes[i] != NULL; i++)
209     image_info->num_ext2_suffixes++;
210
211   image_info->num_ext3_suffixes = 0;
212   for (i = 0; ext3_suffixes[i] != NULL; i++)
213     image_info->num_ext3_suffixes++;
214
215   image_info->num_ignore_tokens = 0;
216   for (i = 0; ignore_tokens[i] != NULL; i++)
217     image_info->num_ignore_tokens++;
218
219   image_info->base_prefixes = base_prefixes;
220   image_info->ext1_suffixes = ext1_suffixes;
221   image_info->ext2_suffixes = ext2_suffixes;
222   image_info->ext3_suffixes = ext3_suffixes;
223   image_info->ignore_tokens = ignore_tokens;
224
225   image_info->num_property_mapping_entries = 0;
226
227   image_info->property_mapping = NULL;
228
229   // ---------- initialize artwork reference and content lists ----------
230
231   image_info->sizeof_artwork_list_entry = sizeof(ImageInfo *);
232
233   image_info->artwork_list =
234     checked_calloc(num_file_list_entries * sizeof(ImageInfo *));
235   image_info->dynamic_artwork_list = NULL;
236
237   image_info->content_list = NULL;
238
239   // ---------- initialize artwork loading/freeing functions ----------
240
241   image_info->load_artwork = Load_Image;
242   image_info->free_artwork = FreeImage;
243 }
244
245 void ReloadCustomImages(void)
246 {
247   print_timestamp_init("ReloadCustomImages");
248
249   LoadArtworkConfig(image_info);
250   print_timestamp_time("LoadArtworkConfig");
251
252   ReloadCustomArtworkList(image_info);
253   print_timestamp_time("ReloadCustomArtworkList");
254
255   print_timestamp_done("ReloadCustomImages");
256 }
257
258 static boolean CheckIfImageContainsSmallImages(ImageInfo *img_info,
259                                                int tile_size)
260 {
261   if (!img_info->contains_small_images)
262     return FALSE;
263
264   // at this point, small images already exist for this image;
265   // now do some checks that may require re-creating small (or in-game) images
266
267   // special case 1:
268   //
269   // check if the configured tile size for an already loaded image has changed
270   // from one level set to another; this should usually not happen, but if a
271   // custom artwork set redefines classic (or default) graphics with wrong tile
272   // size (by mistake or by intention), it will be corrected to its original
273   // tile size here by forcing complete re-creation of all small images again
274
275   if (!strEqual(img_info->leveldir, leveldir_current->identifier) &&
276       img_info->conf_tile_size != tile_size)
277   {
278     int bitmap_nr = GET_BITMAP_ID_FROM_TILESIZE(img_info->conf_tile_size);
279     int i;
280
281     // free all calculated, resized bitmaps, but keep last configured size
282     for (i = 0; i < NUM_IMG_BITMAPS; i++)
283     {
284       if (i == bitmap_nr)
285         continue;
286
287       if (img_info->bitmaps[i])
288       {
289         FreeBitmap(img_info->bitmaps[i]);
290
291         img_info->bitmaps[i] = NULL;
292       }
293     }
294
295     // re-create small bitmaps from last configured size as new default size
296     if (bitmap_nr != IMG_BITMAP_STANDARD)
297     {
298       img_info->bitmaps[IMG_BITMAP_STANDARD] = img_info->bitmaps[bitmap_nr];
299       img_info->bitmaps[bitmap_nr] = NULL;
300     }
301
302     img_info->contains_small_images = FALSE;
303
304     return FALSE;
305   }
306
307   // special case 1 (continued):
308   //
309   // if different tile sizes are used in same image file (usually by mistake,
310   // like forgetting option ".tile_size" for one or more graphic definitions),
311   // make sure to use only the first tile size that is processed for this image
312   // (and ignore all subsequent, potentially different tile size definitions
313   // for this image within the current level set by disabling the above check)
314
315   setString(&img_info->leveldir, leveldir_current->identifier);
316
317   // special case 2:
318   //
319   // graphic config setting "game.tile_size" has changed since last level set;
320   // this may require resizing image to new size required for in-game graphics
321
322   if (img_info->game_tile_size != gfx.game_tile_size)
323   {
324     ReCreateGameTileSizeBitmap(img_info->bitmaps);
325
326     img_info->game_tile_size = gfx.game_tile_size;
327   }
328
329   return TRUE;
330 }
331
332 void CreateImageWithSmallImages(int pos, int zoom_factor, int tile_size)
333 {
334   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
335
336   if (img_info == NULL)
337     return;
338
339   if (CheckIfImageContainsSmallImages(img_info, tile_size))
340     return;
341
342   CreateBitmapWithSmallBitmaps(img_info->bitmaps, zoom_factor, tile_size);
343
344   img_info->contains_small_images = TRUE;
345   img_info->scaled_up = TRUE;                   // scaling was also done here
346
347   img_info->conf_tile_size = tile_size;
348   img_info->game_tile_size = gfx.game_tile_size;
349
350   setString(&img_info->leveldir, leveldir_current->identifier);
351 }
352
353 void CreateImageTextures(int pos)
354 {
355   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
356
357   if (img_info == NULL || img_info->contains_textures)
358     return;
359
360   CreateBitmapTextures(img_info->bitmaps);
361
362   img_info->contains_textures = TRUE;
363 }
364
365 static void FreeImageTextures(int pos)
366 {
367   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
368
369   if (img_info == NULL || !img_info->contains_textures)
370     return;
371
372   FreeBitmapTextures(img_info->bitmaps);
373
374   img_info->contains_textures = FALSE;
375 }
376
377 void FreeAllImageTextures(void)
378 {
379   int num_images = getImageListSize();
380   int i;
381
382   for (i = 0; i < num_images; i++)
383     FreeImageTextures(i);
384 }
385
386 void ScaleImage(int pos, int zoom_factor)
387 {
388   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
389
390   if (img_info == NULL || img_info->scaled_up)
391     return;
392
393   if (zoom_factor != 1)
394   {
395     ScaleBitmap(img_info->bitmaps, zoom_factor);
396
397     img_info->scaled_up = TRUE;
398   }
399 }
400
401 void FreeAllImages(void)
402 {
403   FreeCustomArtworkLists(image_info);
404 }