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