1 (*
2 * Hedgewars, a free turn based strategy game
3 * Copyright (c) 2004-2015 Andrey Korotaev <unC0Rr@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 2 of the License
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *)
18
19 {$INCLUDE "options.inc"}
20
21 unit uTextures;
22 interface
23 uses SDLh, uTypes;
24
NewTexturenull25 function NewTexture(width, height: Longword; buf: Pointer): PTexture;
26 procedure Surface2GrayScale(surf: PSDL_Surface);
Surface2Texnull27 function Surface2Tex(surf: PSDL_Surface; enableClamp: boolean): PTexture;
28 procedure PrettifySurfaceAlpha(surf: PSDL_Surface; pixels: PLongwordArray);
29 procedure PrettifyAlpha2D(pixels: TLandArray; height, width: LongWord);
30 procedure FreeAndNilTexture(var tex: PTexture);
31
32 procedure initModule;
33 procedure freeModule;
34
35 implementation
36 uses GLunit, uUtils, uVariables, uConsts, uDebug, uConsole;
37
38 var TextureList: PTexture;
39
40
41 procedure SetTextureParameters(enableClamp: Boolean);
42 begin
43 if enableClamp and ((cReducedQuality and rqClampLess) = 0) then
44 begin
45 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
46 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)
47 end;
48 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
49 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
50 end;
51
52 procedure ResetVertexArrays(texture: PTexture);
53 begin
54 with texture^ do
55 begin
56 vb[0].X:= 0;
57 vb[0].Y:= 0;
58 vb[1].X:= w;
59 vb[1].Y:= 0;
60 vb[2].X:= w;
61 vb[2].Y:= h;
62 vb[3].X:= 0;
63 vb[3].Y:= h;
64
65 tb[0].X:= 0;
66 tb[0].Y:= 0;
67 tb[1].X:= rx;
68 tb[1].Y:= 0;
69 tb[2].X:= rx;
70 tb[2].Y:= ry;
71 tb[3].X:= 0;
72 tb[3].Y:= ry
73 end;
74 end;
75
NewTexturenull76 function NewTexture(width, height: Longword; buf: Pointer): PTexture;
77 begin
78 new(NewTexture);
79 NewTexture^.PrevTexture:= nil;
80 NewTexture^.NextTexture:= nil;
81 if TextureList <> nil then
82 begin
83 TextureList^.PrevTexture:= NewTexture;
84 NewTexture^.NextTexture:= TextureList
85 end;
86 TextureList:= NewTexture;
87
88 NewTexture^.Scale:= 1;
89 NewTexture^.Priority:= 0;
90 NewTexture^.w:= width;
91 NewTexture^.h:= height;
92 NewTexture^.rx:= 1.0;
93 NewTexture^.ry:= 1.0;
94
95 ResetVertexArrays(NewTexture);
96
97 glGenTextures(1, @NewTexture^.id);
98
99 glBindTexture(GL_TEXTURE_2D, NewTexture^.id);
100 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf);
101
102 SetTextureParameters(true);
103 end;
104
105 procedure Surface2GrayScale(surf: PSDL_Surface);
106 var tw, x, y: Longword;
107 fromP4: PLongWordArray;
108 begin
109 fromP4:= Surf^.pixels;
110 for y:= 0 to Pred(Surf^.h) do
111 begin
112 for x:= 0 to Pred(Surf^.w) do
113 begin
114 tw:= fromP4^[x];
115 tw:= round((tw shr RShift and $FF) * RGB_LUMINANCE_RED +
116 (tw shr GShift and $FF) * RGB_LUMINANCE_GREEN +
117 (tw shr BShift and $FF) * RGB_LUMINANCE_BLUE);
118 if tw > 255 then tw:= 255;
119 tw:= (tw and $FF shl RShift) or (tw and $FF shl BShift) or (tw and $FF shl GShift) or (fromP4^[x] and AMask);
120 fromP4^[x]:= tw;
121 end;
122 fromP4:= PLongWordArray(@(fromP4^[Surf^.pitch div 4]))
123 end;
124 end;
125
126 { this will make invisible pixels that have a visible neighbor have the
127 same color as their visible neighbor, so that bilinear filtering won't
128 display a "wrongly" colored border when zoomed in }
129 procedure PrettifyAlpha(row1, row2: PLongwordArray; firsti, lasti, ioffset: LongWord);
130 var
131 i: Longword;
132 lpi, cpi, bpi: boolean; // was last/current/bottom neighbor pixel invisible?
133 begin
134 // suppress incorrect warning
135 lpi:= true;
136 for i:=firsti to lasti do
137 begin
138 // use first pixel in row1 as starting point
139 if i = firsti then
140 cpi:= ((row1^[i] and AMask) = 0)
141 else
142 begin
143 cpi:= ((row1^[i] and AMask) = 0);
144 if cpi <> lpi then
145 begin
146 // invisible pixels get colors from visible neighbors
147 if cpi then
148 begin
149 row1^[i]:= row1^[i-1] and (not AMask);
150 // as this pixel is invisible and already colored correctly now, no point in further comparing it
151 lpi:= cpi;
152 continue;
153 end
154 else
155 row1^[i-1]:= row1^[i] and (not AMask);
156 end;
157 end;
158 lpi:= cpi;
159 // also check bottom neighbor
160 if row2 <> nil then
161 begin
162 bpi:= ((row2^[i+ioffset] and AMask) = 0);
163 if cpi <> bpi then
164 begin
165 if cpi then
166 row1^[i]:= row2^[i+ioffset] and (not AMask)
167 else
168 row2^[i+ioffset]:= row1^[i] and (not AMask);
169 end;
170 end;
171 end;
172 end;
173
174 procedure PrettifySurfaceAlpha(surf: PSDL_Surface; pixels: PLongwordArray);
175 var
176 // current row index, second last row index of array, width and first/last i of row
177 r, slr, w, si, li: LongWord;
178 begin
179 w:= surf^.w;
180 // just a single pixel, nothing to do here
181 if (w < 2) and (surf^.h < 2) then
182 exit;
183 slr:= surf^.h - 2;
184 si:= 0;
185 li:= w - 1;
186 for r:= 0 to slr do
187 begin
188 PrettifyAlpha(pixels, pixels, si, li, w);
189 // move indices to next row
190 si:= si + w;
191 li:= li + w;
192 end;
193 // don't forget last row
194 PrettifyAlpha(pixels, nil, si, li, w);
195 end;
196
197 procedure PrettifyAlpha2D(pixels: TLandArray; height, width: LongWord);
198 var
199 // current y; last x, second last y of array;
200 y, lx, sly: LongWord;
201 begin
202 sly:= height - 2;
203 lx:= width - 1;
204 for y:= 0 to sly do
205 begin
206 PrettifyAlpha(PLongWordArray(pixels[y]), PLongWordArray(pixels[y+1]), 0, lx, 0);
207 end;
208 // don't forget last row
209 PrettifyAlpha(PLongWordArray(pixels[sly+1]), nil, 0, lx, 0);
210 end;
211
Surface2Texnull212 function Surface2Tex(surf: PSDL_Surface; enableClamp: boolean): PTexture;
213 var tw, th, x, y: Longword;
214 tmpp: pointer;
215 fromP4, toP4: PLongWordArray;
216 begin
217 if cOnlyStats then exit(nil);
218 new(Surface2Tex);
219 Surface2Tex^.PrevTexture:= nil;
220 Surface2Tex^.NextTexture:= nil;
221 if TextureList <> nil then
222 begin
223 TextureList^.PrevTexture:= Surface2Tex;
224 Surface2Tex^.NextTexture:= TextureList
225 end;
226 TextureList:= Surface2Tex;
227
228 Surface2Tex^.Scale:= 1;
229 Surface2Tex^.Priority:= 0;
230 Surface2Tex^.w:= surf^.w;
231 Surface2Tex^.h:= surf^.h;
232
233 if (surf^.format^.BytesPerPixel <> 4) then
234 begin
235 checkFails(false, 'Surface2Tex failed, expecting 32 bit surface', true);
236 Surface2Tex^.id:= 0;
237 exit
238 end;
239
240 glGenTextures(1, @Surface2Tex^.id);
241
242 glBindTexture(GL_TEXTURE_2D, Surface2Tex^.id);
243
244 if SDL_MustLock(surf) then
245 if SDLCheck(SDL_LockSurface(surf) >= 0, 'Lock surface', true) then
246 exit(nil);
247
248 fromP4:= Surf^.pixels;
249
250 // FIXME move out of surface2tex
251 if GrayScale then
252 Surface2GrayScale(Surf);
253
254 // FIXME move out of surface2tex
255 PrettifySurfaceAlpha(surf, fromP4);
256
257 if (not SupportNPOTT) and (not (isPowerOf2(Surf^.w) and isPowerOf2(Surf^.h))) then
258 begin
259 tw:= toPowerOf2(Surf^.w);
260 th:= toPowerOf2(Surf^.h);
261
262 Surface2Tex^.rx:= Surf^.w / tw;
263 Surface2Tex^.ry:= Surf^.h / th;
264
265 tmpp:= GetMem(tw * th * surf^.format^.BytesPerPixel);
266
267 fromP4:= Surf^.pixels;
268 toP4:= tmpp;
269
270 for y:= 0 to Pred(Surf^.h) do
271 begin
272 for x:= 0 to Pred(Surf^.w) do
273 toP4^[x]:= fromP4^[x];
274 for x:= Surf^.w to Pred(tw) do
275 toP4^[x]:= fromP4^[0];
276 toP4:= PLongWordArray(@(toP4^[tw]));
277 fromP4:= PLongWordArray(@(fromP4^[Surf^.pitch div 4]))
278 end;
279
280 for y:= Surf^.h to Pred(th) do
281 begin
282 for x:= 0 to Pred(tw) do
283 toP4^[x]:= 0;
284 toP4:= PLongWordArray(@(toP4^[tw]))
285 end;
286
287 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, GL_UNSIGNED_BYTE, tmpp);
288
289 FreeMem(tmpp, tw * th * surf^.format^.BytesPerPixel)
290 end
291 else
292 begin
293 Surface2Tex^.rx:= 1.0;
294 Surface2Tex^.ry:= 1.0;
295 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf^.w, surf^.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf^.pixels);
296 end;
297
298 ResetVertexArrays(Surface2Tex);
299
300 if SDL_MustLock(surf) then
301 SDL_UnlockSurface(surf);
302
303 SetTextureParameters(enableClamp);
304 end;
305
306 // deletes texture and frees the memory allocated for it.
307 // if nil is passed nothing is done
308 procedure FreeAndNilTexture(var tex: PTexture);
309 begin
310 if tex <> nil then
311 begin
312 if tex^.NextTexture <> nil then
313 tex^.NextTexture^.PrevTexture:= tex^.PrevTexture;
314 if tex^.PrevTexture <> nil then
315 tex^.PrevTexture^.NextTexture:= tex^.NextTexture
316 else
317 TextureList:= tex^.NextTexture;
318 glDeleteTextures(1, @tex^.id);
319 Dispose(tex);
320 tex:= nil;
321 end;
322 end;
323
324 procedure initModule;
325 begin
326 TextureList:= nil;
327 end;
328
329 procedure freeModule;
330 var tex: PTexture;
331 begin
332 if TextureList <> nil then
333 WriteToConsole('FIXME FIXME FIXME. App shutdown without full cleanup of texture list; read game0.log and please report this problem');
334 while TextureList <> nil do
335 begin
336 tex:= TextureList;
337 AddFileLog('Texture not freed: width='+inttostr(LongInt(tex^.w))+' height='+inttostr(LongInt(tex^.h))+' priority='+inttostr(round(tex^.priority*1000)));
338 FreeAndNilTexture(tex);
339 end
340 end;
341
342 end.
343