Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | pmbaty | 1 | /* |
| 2 | * Portions of this file are copyright Rebirth contributors and licensed as |
||
| 3 | * described in COPYING.txt. |
||
| 4 | * Portions of this file are copyright Parallax Software and licensed |
||
| 5 | * according to the Parallax license below. |
||
| 6 | * See COPYING.txt for license details. |
||
| 7 | |||
| 8 | THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX |
||
| 9 | SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO |
||
| 10 | END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A |
||
| 11 | ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS |
||
| 12 | IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS |
||
| 13 | SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE |
||
| 14 | FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE |
||
| 15 | CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS |
||
| 16 | AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. |
||
| 17 | COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. |
||
| 18 | */ |
||
| 19 | |||
| 20 | /* |
||
| 21 | * |
||
| 22 | * Routines to cache merged textures. |
||
| 23 | * |
||
| 24 | */ |
||
| 25 | |||
| 26 | |||
| 27 | #include "gr.h" |
||
| 28 | #include "dxxerror.h" |
||
| 29 | #include "game.h" |
||
| 30 | #include "textures.h" |
||
| 31 | #include "rle.h" |
||
| 32 | #include "timer.h" |
||
| 33 | #include "piggy.h" |
||
| 34 | #include "texmerge.h" |
||
| 35 | #include "piggy.h" |
||
| 36 | |||
| 37 | #include "compiler-range_for.h" |
||
| 38 | #include "partial_range.h" |
||
| 39 | |||
| 40 | #if DXX_USE_OGL |
||
| 41 | #include "ogl_init.h" |
||
| 42 | #endif |
||
| 43 | #define MAX_NUM_CACHE_BITMAPS 10 |
||
| 44 | |||
| 45 | //static grs_bitmap * cache_bitmaps[MAX_NUM_CACHE_BITMAPS]; |
||
| 46 | |||
| 47 | namespace { |
||
| 48 | |||
| 49 | struct TEXTURE_CACHE { |
||
| 50 | grs_bitmap_ptr bitmap; |
||
| 51 | grs_bitmap * bottom_bmp; |
||
| 52 | grs_bitmap * top_bmp; |
||
| 53 | int orient; |
||
| 54 | fix64 last_time_used; |
||
| 55 | }; |
||
| 56 | |||
| 57 | /* Helper classes merge_texture_0 through merge_texture_3 correspond to |
||
| 58 | * the four values of `orient` used by texmerge_get_cached_bitmap. |
||
| 59 | */ |
||
| 60 | struct merge_texture_0 |
||
| 61 | { |
||
| 62 | static size_t get_top_data_index(const unsigned wh, const unsigned y, const unsigned x) |
||
| 63 | { |
||
| 64 | return wh * y + x; |
||
| 65 | } |
||
| 66 | }; |
||
| 67 | |||
| 68 | struct merge_texture_1 |
||
| 69 | { |
||
| 70 | static size_t get_top_data_index(const unsigned wh, const unsigned y, const unsigned x) |
||
| 71 | { |
||
| 72 | return wh * x + ((wh - 1) - y); |
||
| 73 | } |
||
| 74 | }; |
||
| 75 | |||
| 76 | struct merge_texture_2 |
||
| 77 | { |
||
| 78 | static size_t get_top_data_index(const unsigned wh, const unsigned y, const unsigned x) |
||
| 79 | { |
||
| 80 | return wh * ((wh - 1) - y) + ((wh - 1) - x); |
||
| 81 | } |
||
| 82 | }; |
||
| 83 | |||
| 84 | struct merge_texture_3 |
||
| 85 | { |
||
| 86 | static size_t get_top_data_index(const unsigned wh, const unsigned y, const unsigned x) |
||
| 87 | { |
||
| 88 | return wh * ((wh - 1) - x) + y; |
||
| 89 | } |
||
| 90 | }; |
||
| 91 | |||
| 92 | /* For supertransparent colors, remap 254. |
||
| 93 | * For regular transparent colors, do nothing. |
||
| 94 | * |
||
| 95 | * In both cases, the caller remaps TRANSPARENCY_COLOR to the bottom |
||
| 96 | * bitmap. |
||
| 97 | */ |
||
| 98 | struct merge_transform_super_xparent |
||
| 99 | { |
||
| 100 | static uint8_t transform_color(uint8_t c) |
||
| 101 | { |
||
| 102 | return c == 254 ? TRANSPARENCY_COLOR : c; |
||
| 103 | } |
||
| 104 | }; |
||
| 105 | |||
| 106 | struct merge_transform_new |
||
| 107 | { |
||
| 108 | static uint8_t transform_color(uint8_t c) |
||
| 109 | { |
||
| 110 | return c; |
||
| 111 | } |
||
| 112 | }; |
||
| 113 | |||
| 114 | } |
||
| 115 | |||
| 116 | /* Run the transform for one texture merge case. Different values of |
||
| 117 | * `orient` in texmerge_get_cached_bitmap lead to different types for |
||
| 118 | * `get_index`. |
||
| 119 | */ |
||
| 120 | template <typename texture_transform, typename get_index> |
||
| 121 | static void merge_textures_case(const unsigned wh, const uint8_t *const top_data, const uint8_t *const bottom_data, uint8_t *dest_data) |
||
| 122 | { |
||
| 123 | for (unsigned y = 0; y < wh; ++y) |
||
| 124 | for (unsigned x = 0; x < wh; ++x) |
||
| 125 | { |
||
| 126 | const auto c = top_data[get_index::get_top_data_index(wh, y, x)]; |
||
| 127 | /* All merged textures support TRANSPARENCY_COLOR, so handle |
||
| 128 | * it here. Supertransparency is delegated down to |
||
| 129 | * `texture_transform`, since not all textures want |
||
| 130 | * supertransparency. |
||
| 131 | */ |
||
| 132 | *dest_data++ = (c == TRANSPARENCY_COLOR) |
||
| 133 | ? bottom_data[wh * y + x] |
||
| 134 | : texture_transform::transform_color(c); |
||
| 135 | } |
||
| 136 | } |
||
| 137 | |||
| 138 | /* Dispatch a texture transformation based on the value of `orient`. |
||
| 139 | * The loops are duplicated in each case so that `orient` is not reread |
||
| 140 | * for each byte processed. |
||
| 141 | */ |
||
| 142 | template <typename texture_transform> |
||
| 143 | static void merge_textures(unsigned orient, const grs_bitmap &expanded_bottom_bmp, const grs_bitmap &expanded_top_bmp, uint8_t *const dest_data) |
||
| 144 | { |
||
| 145 | const auto &top_data = expanded_top_bmp.bm_data; |
||
| 146 | const auto &bottom_data = expanded_bottom_bmp.bm_data; |
||
| 147 | const auto wh = expanded_bottom_bmp.bm_w; |
||
| 148 | switch (orient) |
||
| 149 | { |
||
| 150 | case 0: |
||
| 151 | merge_textures_case<texture_transform, merge_texture_0>(wh, top_data, bottom_data, dest_data); |
||
| 152 | break; |
||
| 153 | case 1: |
||
| 154 | merge_textures_case<texture_transform, merge_texture_1>(wh, top_data, bottom_data, dest_data); |
||
| 155 | break; |
||
| 156 | case 2: |
||
| 157 | merge_textures_case<texture_transform, merge_texture_2>(wh, top_data, bottom_data, dest_data); |
||
| 158 | break; |
||
| 159 | case 3: |
||
| 160 | merge_textures_case<texture_transform, merge_texture_3>(wh, top_data, bottom_data, dest_data); |
||
| 161 | break; |
||
| 162 | } |
||
| 163 | } |
||
| 164 | |||
| 165 | static std::array<TEXTURE_CACHE, MAX_NUM_CACHE_BITMAPS> Cache; |
||
| 166 | |||
| 167 | static int cache_hits = 0; |
||
| 168 | static int cache_misses = 0; |
||
| 169 | |||
| 170 | //---------------------------------------------------------------------- |
||
| 171 | |||
| 172 | int texmerge_init() |
||
| 173 | { |
||
| 174 | range_for (auto &i, Cache) |
||
| 175 | { |
||
| 176 | i.bitmap = NULL; |
||
| 177 | i.last_time_used = -1; |
||
| 178 | i.top_bmp = NULL; |
||
| 179 | i.bottom_bmp = NULL; |
||
| 180 | i.orient = -1; |
||
| 181 | } |
||
| 182 | |||
| 183 | return 1; |
||
| 184 | } |
||
| 185 | |||
| 186 | void texmerge_flush() |
||
| 187 | { |
||
| 188 | range_for (auto &i, Cache) |
||
| 189 | { |
||
| 190 | i.last_time_used = -1; |
||
| 191 | i.top_bmp = NULL; |
||
| 192 | i.bottom_bmp = NULL; |
||
| 193 | i.orient = -1; |
||
| 194 | } |
||
| 195 | } |
||
| 196 | |||
| 197 | |||
| 198 | //------------------------------------------------------------------------- |
||
| 199 | void texmerge_close() |
||
| 200 | { |
||
| 201 | range_for (auto &i, Cache) |
||
| 202 | { |
||
| 203 | i.bitmap.reset(); |
||
| 204 | } |
||
| 205 | } |
||
| 206 | |||
| 207 | //--unused-- int info_printed = 0; |
||
| 208 | |||
| 209 | grs_bitmap &texmerge_get_cached_bitmap(unsigned tmap_bottom, unsigned tmap_top) |
||
| 210 | { |
||
| 211 | grs_bitmap *bitmap_top, *bitmap_bottom; |
||
| 212 | int orient; |
||
| 213 | int lowest_time_used; |
||
| 214 | |||
| 215 | bitmap_top = &GameBitmaps[Textures[tmap_top&0x3FFF].index]; |
||
| 216 | bitmap_bottom = &GameBitmaps[Textures[tmap_bottom].index]; |
||
| 217 | |||
| 218 | orient = ((tmap_top&0xC000)>>14) & 3; |
||
| 219 | |||
| 220 | lowest_time_used = Cache[0].last_time_used; |
||
| 221 | auto least_recently_used = &Cache.front(); |
||
| 222 | range_for (auto &i, Cache) |
||
| 223 | { |
||
| 224 | if ( (i.last_time_used > -1) && (i.top_bmp==bitmap_top) && (i.bottom_bmp==bitmap_bottom) && (i.orient==orient )) { |
||
| 225 | cache_hits++; |
||
| 226 | i.last_time_used = timer_query(); |
||
| 227 | return *i.bitmap.get(); |
||
| 228 | } |
||
| 229 | if ( i.last_time_used < lowest_time_used ) { |
||
| 230 | lowest_time_used = i.last_time_used; |
||
| 231 | least_recently_used = &i; |
||
| 232 | } |
||
| 233 | } |
||
| 234 | |||
| 235 | //---- Page out the LRU bitmap; |
||
| 236 | cache_misses++; |
||
| 237 | |||
| 238 | // Make sure the bitmaps are paged in... |
||
| 239 | |||
| 240 | PIGGY_PAGE_IN(Textures[tmap_top&0x3FFF]); |
||
| 241 | PIGGY_PAGE_IN(Textures[tmap_bottom]); |
||
| 242 | if (bitmap_bottom->bm_w != bitmap_bottom->bm_h || bitmap_top->bm_w != bitmap_top->bm_h) |
||
| 243 | Error("Texture width != texture height!\nbottom tmap = %u; bottom bitmap = %u; bottom width = %u; bottom height = %u\ntop tmap = %u; top bitmap = %u; top width=%u; top height=%u", tmap_bottom, Textures[tmap_bottom].index, bitmap_bottom->bm_w, bitmap_bottom->bm_h, tmap_top, Textures[tmap_top & 0x3fff].index, bitmap_top->bm_w, bitmap_top->bm_h); |
||
| 244 | if (bitmap_bottom->bm_w != bitmap_top->bm_w || bitmap_bottom->bm_h != bitmap_top->bm_h) |
||
| 245 | Error("Top and Bottom textures have different size!\nbottom tmap = %u; bottom bitmap = %u; bottom width = %u; bottom height = %u\ntop tmap = %u; top bitmap = %u; top width=%u; top height=%u", tmap_bottom, Textures[tmap_bottom].index, bitmap_bottom->bm_w, bitmap_bottom->bm_h, tmap_top, Textures[tmap_top & 0x3fff].index, bitmap_top->bm_w, bitmap_top->bm_h); |
||
| 246 | |||
| 247 | least_recently_used->bitmap = gr_create_bitmap(bitmap_bottom->bm_w, bitmap_bottom->bm_h); |
||
| 248 | #if DXX_USE_OGL |
||
| 249 | ogl_freebmtexture(*least_recently_used->bitmap.get()); |
||
| 250 | #endif |
||
| 251 | |||
| 252 | auto &expanded_top_bmp = *rle_expand_texture(*bitmap_top); |
||
| 253 | auto &expanded_bottom_bmp = *rle_expand_texture(*bitmap_bottom); |
||
| 254 | if (bitmap_top->get_flag_mask(BM_FLAG_SUPER_TRANSPARENT)) |
||
| 255 | { |
||
| 256 | merge_textures<merge_transform_super_xparent>(orient, expanded_bottom_bmp, expanded_top_bmp, least_recently_used->bitmap->get_bitmap_data()); |
||
| 257 | gr_set_bitmap_flags(*least_recently_used->bitmap.get(), BM_FLAG_TRANSPARENT); |
||
| 258 | #if !DXX_USE_OGL |
||
| 259 | least_recently_used->bitmap->avg_color = bitmap_top->avg_color; |
||
| 260 | #endif |
||
| 261 | } else { |
||
| 262 | merge_textures<merge_transform_new>(orient, expanded_bottom_bmp, expanded_top_bmp, least_recently_used->bitmap->get_bitmap_data()); |
||
| 263 | least_recently_used->bitmap->set_flags(bitmap_bottom->get_flag_mask(~BM_FLAG_RLE)); |
||
| 264 | #if !DXX_USE_OGL |
||
| 265 | least_recently_used->bitmap->avg_color = bitmap_bottom->avg_color; |
||
| 266 | #endif |
||
| 267 | } |
||
| 268 | |||
| 269 | least_recently_used->top_bmp = bitmap_top; |
||
| 270 | least_recently_used->bottom_bmp = bitmap_bottom; |
||
| 271 | least_recently_used->last_time_used = timer_query(); |
||
| 272 | least_recently_used->orient = orient; |
||
| 273 | return *least_recently_used->bitmap.get(); |
||
| 274 | } |