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