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