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 | } |