re-enabled storing selection from "last played level set" menu
[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 char *getFilenameFromImageID(int graphic)
143 {
144   struct FileInfo *file_list = getImageListEntryFromImageID(graphic);
145
146   return (file_list != NULL ? file_list->filename : NULL);
147 }
148
149 int getImageIDFromToken(char *token)
150 {
151   struct FileInfo *file_list = image_info->file_list;
152   int num_list_entries = image_info->num_file_list_entries;
153   int i;
154
155   for (i = 0; i < num_list_entries; i++)
156     if (strEqual(file_list[i].token, token))
157       return i;
158
159   return -1;
160 }
161
162 char *getImageConfigFilename(void)
163 {
164   return getCustomArtworkConfigFilename(image_info->type);
165 }
166
167 int getImageListPropertyMappingSize(void)
168 {
169   return image_info->num_property_mapping_entries;
170 }
171
172 struct PropertyMapping *getImageListPropertyMapping(void)
173 {
174   return image_info->property_mapping;
175 }
176
177 void InitImageList(struct ConfigInfo *config_list, int num_file_list_entries,
178                    struct ConfigTypeInfo *config_suffix_list,
179                    char **base_prefixes, char **ext1_suffixes,
180                    char **ext2_suffixes, char **ext3_suffixes,
181                    char **ignore_tokens)
182 {
183   int i;
184
185   image_info = checked_calloc(sizeof(struct ArtworkListInfo));
186   image_info->type = ARTWORK_TYPE_GRAPHICS;
187
188   // ---------- initialize file list and suffix lists ----------
189
190   image_info->num_file_list_entries = num_file_list_entries;
191   image_info->num_dynamic_file_list_entries = 0;
192
193   image_info->file_list =
194     getFileListFromConfigList(config_list, config_suffix_list, ignore_tokens,
195                               num_file_list_entries);
196   image_info->dynamic_file_list = NULL;
197
198   image_info->num_suffix_list_entries = 0;
199   for (i = 0; config_suffix_list[i].token != NULL; i++)
200     image_info->num_suffix_list_entries++;
201
202   image_info->suffix_list = config_suffix_list;
203
204   // ---------- initialize base prefix and suffixes lists ----------
205
206   image_info->num_base_prefixes = 0;
207   for (i = 0; base_prefixes[i] != NULL; i++)
208     image_info->num_base_prefixes++;
209
210   image_info->num_ext1_suffixes = 0;
211   for (i = 0; ext1_suffixes[i] != NULL; i++)
212     image_info->num_ext1_suffixes++;
213
214   image_info->num_ext2_suffixes = 0;
215   for (i = 0; ext2_suffixes[i] != NULL; i++)
216     image_info->num_ext2_suffixes++;
217
218   image_info->num_ext3_suffixes = 0;
219   for (i = 0; ext3_suffixes[i] != NULL; i++)
220     image_info->num_ext3_suffixes++;
221
222   image_info->num_ignore_tokens = 0;
223   for (i = 0; ignore_tokens[i] != NULL; i++)
224     image_info->num_ignore_tokens++;
225
226   image_info->base_prefixes = base_prefixes;
227   image_info->ext1_suffixes = ext1_suffixes;
228   image_info->ext2_suffixes = ext2_suffixes;
229   image_info->ext3_suffixes = ext3_suffixes;
230   image_info->ignore_tokens = ignore_tokens;
231
232   image_info->num_property_mapping_entries = 0;
233
234   image_info->property_mapping = NULL;
235
236   // ---------- initialize artwork reference and content lists ----------
237
238   image_info->sizeof_artwork_list_entry = sizeof(ImageInfo *);
239
240   image_info->artwork_list =
241     checked_calloc(num_file_list_entries * sizeof(ImageInfo *));
242   image_info->dynamic_artwork_list = NULL;
243
244   image_info->content_list = NULL;
245
246   // ---------- initialize artwork loading/freeing functions ----------
247
248   image_info->load_artwork = Load_Image;
249   image_info->free_artwork = FreeImage;
250 }
251
252 void ReloadCustomImages(void)
253 {
254   print_timestamp_init("ReloadCustomImages");
255
256   LoadArtworkConfig(image_info);
257   print_timestamp_time("LoadArtworkConfig");
258
259   ReloadCustomArtworkList(image_info);
260   print_timestamp_time("ReloadCustomArtworkList");
261
262   print_timestamp_done("ReloadCustomImages");
263 }
264
265 static boolean CheckIfImageContainsSmallImages(ImageInfo *img_info,
266                                                int tile_size)
267 {
268   if (!img_info->contains_small_images)
269     return FALSE;
270
271   // at this point, small images already exist for this image;
272   // now do some checks that may require re-creating small (or in-game) images
273
274   // special case 1:
275   //
276   // check if the configured tile size for an already loaded image has changed
277   // from one level set to another; this should usually not happen, but if a
278   // custom artwork set redefines classic (or default) graphics with wrong tile
279   // size (by mistake or by intention), it will be corrected to its original
280   // tile size here by forcing complete re-creation of all small images again
281
282   if (!strEqual(img_info->leveldir, leveldir_current->identifier) &&
283       img_info->conf_tile_size != tile_size)
284   {
285     int bitmap_nr = GET_BITMAP_ID_FROM_TILESIZE(img_info->conf_tile_size);
286     int i;
287
288     // free all calculated, resized bitmaps, but keep last configured size
289     for (i = 0; i < NUM_IMG_BITMAPS; i++)
290     {
291       if (i == bitmap_nr)
292         continue;
293
294       if (img_info->bitmaps[i])
295       {
296         FreeBitmap(img_info->bitmaps[i]);
297
298         img_info->bitmaps[i] = NULL;
299       }
300     }
301
302     // re-create small bitmaps from last configured size as new default size
303     if (bitmap_nr != IMG_BITMAP_STANDARD)
304     {
305       img_info->bitmaps[IMG_BITMAP_STANDARD] = img_info->bitmaps[bitmap_nr];
306       img_info->bitmaps[bitmap_nr] = NULL;
307     }
308
309     img_info->contains_small_images = FALSE;
310
311     return FALSE;
312   }
313
314   // special case 1 (continued):
315   //
316   // if different tile sizes are used in same image file (usually by mistake,
317   // like forgetting option ".tile_size" for one or more graphic definitions),
318   // make sure to use only the first tile size that is processed for this image
319   // (and ignore all subsequent, potentially different tile size definitions
320   // for this image within the current level set by disabling the above check)
321
322   setString(&img_info->leveldir, leveldir_current->identifier);
323
324   // special case 2:
325   //
326   // graphic config setting "game.tile_size" has changed since last level set;
327   // this may require resizing image to new size required for in-game graphics
328
329   if (img_info->game_tile_size != gfx.game_tile_size)
330   {
331     ReCreateGameTileSizeBitmap(img_info->bitmaps);
332
333     img_info->game_tile_size = gfx.game_tile_size;
334   }
335
336   return TRUE;
337 }
338
339 void CreateImageWithSmallImages(int pos, int zoom_factor, int tile_size)
340 {
341   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
342
343   if (img_info == NULL)
344     return;
345
346   if (CheckIfImageContainsSmallImages(img_info, tile_size))
347     return;
348
349   CreateBitmapWithSmallBitmaps(img_info->bitmaps, zoom_factor, tile_size);
350
351   img_info->contains_small_images = TRUE;
352   img_info->scaled_up = TRUE;                   // scaling was also done here
353
354   img_info->conf_tile_size = tile_size;
355   img_info->game_tile_size = gfx.game_tile_size;
356
357   setString(&img_info->leveldir, leveldir_current->identifier);
358 }
359
360 void CreateImageTextures(int pos)
361 {
362   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
363
364   if (img_info == NULL || img_info->contains_textures)
365     return;
366
367   CreateBitmapTextures(img_info->bitmaps);
368
369   img_info->contains_textures = TRUE;
370 }
371
372 static void FreeImageTextures(int pos)
373 {
374   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
375
376   if (img_info == NULL || !img_info->contains_textures)
377     return;
378
379   FreeBitmapTextures(img_info->bitmaps);
380
381   img_info->contains_textures = FALSE;
382 }
383
384 void FreeAllImageTextures(void)
385 {
386   int num_images = getImageListSize();
387   int i;
388
389   for (i = 0; i < num_images; i++)
390     FreeImageTextures(i);
391 }
392
393 void ScaleImage(int pos, int zoom_factor)
394 {
395   ImageInfo *img_info = getImageInfoEntryFromImageID(pos);
396
397   if (img_info == NULL || img_info->scaled_up)
398     return;
399
400   if (zoom_factor != 1)
401     ScaleBitmap(img_info->bitmaps, zoom_factor);
402
403   img_info->scaled_up = TRUE;
404 }
405
406 void FreeAllImages(void)
407 {
408   FreeCustomArtworkLists(image_info);
409 }