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 uMisc;
22 interface
23
24 uses SDLh, uConsts, GLunit, uTypes;
25
26 procedure initModule;
27 procedure freeModule;
28
doSurfaceConversionnull29 function doSurfaceConversion(tmpsurf: PSDL_Surface): PSDL_Surface;
MakeScreenshotnull30 function MakeScreenshot(filename: shortstring; k: LongInt; dump: LongWord): boolean;
GetTeamStatStringnull31 function GetTeamStatString(p: PTeam): shortstring;
SDL_RectMakenull32 function SDL_RectMake(x, y, width, height: LongInt): TSDL_Rect; inline;
33
34 implementation
35 uses uVariables, uUtils
36 {$IFDEF PNG_SCREENSHOTS}, PNGh, png {$ENDIF};
37
38 type PScreenshot = ^TScreenshot;
39 TScreenshot = record
40 buffer: PByte;
41 filename: shortstring;
42 width, height: LongInt;
43 size: QWord;
44 end;
45
46 var conversionFormat : PSDL_PixelFormat;
47
48 {$IFDEF PNG_SCREENSHOTS}
49 // this funtion will be executed in separate thread
SaveScreenshotnull50 function SaveScreenshot(screenshot: pointer): LongInt; cdecl; export;
51 var i: LongInt;
52 png_ptr: ^png_struct;
53 info_ptr: ^png_info;
54 f: File;
55 image: PScreenshot;
56 begin
57 image:= PScreenshot(screenshot);
58
59 png_ptr := png_create_write_struct(png_get_libpng_ver(nil), nil, nil, nil);
60 if png_ptr = nil then
61 begin
62 // AddFileLog('Error: Could not create png write struct.');
63 SaveScreenshot:= 0;
64 exit;
65 end;
66
67 info_ptr := png_create_info_struct(png_ptr);
68 if info_ptr = nil then
69 begin
70 png_destroy_write_struct(@png_ptr, nil);
71 // AddFileLog('Error: Could not create png info struct.');
72 SaveScreenshot:= 0;
73 exit;
74 end;
75
76 {$IOCHECKS OFF}
77 Assign(f, image^.filename);
78 Rewrite(f, 1);
79 if IOResult = 0 then
80 begin
81 png_init_pascal_io(png_ptr,@f);
82 png_set_IHDR(png_ptr, info_ptr, image^.width, image^.height,
83 8, // bit depth
84 PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
85 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
86 png_write_info(png_ptr, info_ptr);
87 // glReadPixels and libpng number rows in different order
88 for i:= image^.height-1 downto 0 do
89 png_write_row(png_ptr, image^.buffer + i*4*image^.width);
90 png_write_end(png_ptr, info_ptr);
91 Close(f);
92 end;
93 {$IOCHECKS ON}
94
95 // free everything
96 png_destroy_write_struct(@png_ptr, @info_ptr);
97 FreeMem(image^.buffer, image^.size);
98 Dispose(image);
99 SaveScreenshot:= 0;
100 end;
101
102 {$ELSE} //PNG_SCREENSHOTS
103 {$IFDEF WINDOWS}
SaveScreenshotnull104 function SaveScreenshot(screenshot: pointer): LongInt; cdecl; export;
105 var
106 surface: PSDL_Surface;
107 image: PScreenshot;
108 rowBuffer: PByte;
109 row, stride: LongInt;
110 begin
111 image:= PScreenshot(screenshot);
112 stride:= image^.width * 4;
113 rowBuffer:= PByte(GetMem(stride));
114
115 for row:= 0 to (image^.height div 2) - 1 do
116 begin
117 Move((image^.buffer + (image^.height - row - 1) * stride)^,
118 rowBuffer^,
119 stride);
120 Move((image^.buffer + row * stride)^,
121 (image^.buffer + (image^.height - row - 1) * stride)^,
122 stride);
123 Move(rowBuffer^,
124 (image^.buffer + row * stride)^,
125 stride);
126 end;
127
128 surface:= SDL_CreateRGBSurfaceFrom(
129 image^.buffer,
130 image^.width, image^.height, 32, stride,
131 $000000FF, $0000FF00, $00FF0000, $FF000000);
132
133 if surface <> nil then
134 begin
135 IMG_SavePNG(surface, Str2PChar(image^.filename));
136 SDL_FreeSurface(surface);
137 end;
138
139 FreeMem(rowBuffer, stride);
140 FreeMem(image^.buffer, image^.size);
141 Dispose(image);
142 SaveScreenshot:= 0;
143 end;
144 {$ELSE} //WINDOWS
145 // this funtion will be executed in separate thread
SaveScreenshotnull146 function SaveScreenshot(screenshot: pointer): LongInt; cdecl; export;
147 var f: file;
148 // Windows Bitmap Header
149 head: array[0..53] of Byte = (
150 $42, $4D, // identifier ("BM")
151 0, 0, 0, 0, // file size
152 0, 0, 0, 0, // reserved
153 54, 0, 0, 0, // starting offset
154 40, 0, 0, 0, // header size
155 0, 0, 0, 0, // width
156 0, 0, 0, 0, // height
157 1, 0, // color planes
158 32, 0, // bit depth
159 0, 0, 0, 0, // compression method (uncompressed)
160 0, 0, 0, 0, // image size
161 96, 0, 0, 0, // horizontal resolution
162 96, 0, 0, 0, // vertical resolution
163 0, 0, 0, 0, // number of colors (all)
164 0, 0, 0, 0 // number of important colors
165 );
166 image: PScreenshot;
167 size: QWord;
168 writeResult:LongInt;
169 begin
170 image:= PScreenshot(screenshot);
171
172 size:= image^.Width*image^.Height*4;
173
174 head[$02]:= (size + 54) and $ff;
175 head[$03]:= ((size + 54) shr 8) and $ff;
176 head[$04]:= ((size + 54) shr 16) and $ff;
177 head[$05]:= ((size + 54) shr 24) and $ff;
178 head[$12]:= image^.Width and $ff;
179 head[$13]:= (image^.Width shr 8) and $ff;
180 head[$14]:= (image^.Width shr 16) and $ff;
181 head[$15]:= (image^.Width shr 24) and $ff;
182 head[$16]:= image^.Height and $ff;
183 head[$17]:= (image^.Height shr 8) and $ff;
184 head[$18]:= (image^.Height shr 16) and $ff;
185 head[$19]:= (image^.Height shr 24) and $ff;
186 head[$22]:= size and $ff;
187 head[$23]:= (size shr 8) and $ff;
188 head[$24]:= (size shr 16) and $ff;
189 head[$25]:= (size shr 24) and $ff;
190
191 {$IOCHECKS OFF}
192 Assign(f, image^.filename);
193 Rewrite(f, 1);
194 if IOResult = 0 then
195 begin
196 writeResult:= 0; // suppress fpc hint
197 BlockWrite(f, head, sizeof(head), writeResult);
198 BlockWrite(f, image^.buffer^, size, writeResult);
199 Close(f);
200 end
201 else
202 begin
203 //AddFileLog('Error: Could not write to ' + filename);
204 end;
205 {$IOCHECKS ON}
206
207 // free everything
208 FreeMem(image^.buffer, image^.size);
209 Dispose(image);
210 SaveScreenshot:= 0;
211 end;
212
213 {$ENDIF} // WINDOWS
214 {$ENDIF} // PNG_SCREENSHOTS
215
216 {$IFDEF USE_VIDEO_RECORDING}
217 // make image k times smaller (useful for saving thumbnails)
218 procedure ReduceImage(img: PByteArray; width, height, k: LongInt);
219 var i, j, i0, j0, w, h, r, g, b: LongInt;
220 begin
221 w:= width div k;
222 h:= height div k;
223
224 // rescale inplace
225 if k <> 1 then
226 begin
227 for i:= 0 to h-1 do
228 for j:= 0 to w-1 do
229 begin
230 r:= 0;
231 g:= 0;
232 b:= 0;
233 for i0:= 0 to k-1 do
234 for j0:= 0 to k-1 do
235 begin
236 inc(r, img^[4*(width*(i*k+i0) + j*k + j0)+0]);
237 inc(g, img^[4*(width*(i*k+i0) + j*k + j0)+1]);
238 inc(b, img^[4*(width*(i*k+i0) + j*k + j0)+2]);
239 end;
240 img^[4*(w*i + j)+0]:= r div (k*k);
241 img^[4*(w*i + j)+1]:= g div (k*k);
242 img^[4*(w*i + j)+2]:= b div (k*k);
243 img^[4*(w*i + j)+3]:= 255;
244 end;
245 end;
246 end;
247 {$ENDIF}
248
249 // captures and saves the screen. returns true on success.
250 // saved image will be k times smaller than original (useful for saving thumbnails).
MakeScreenshotnull251 function MakeScreenshot(filename: shortstring; k: LongInt; dump: LongWord): boolean;
252 var p: Pointer;
253 size: QWord;
254 image: PScreenshot;
255 format: GLenum;
256 ext: string[4];
257 x,y: LongWord;
258 begin
259 {$IFDEF PNG_SCREENSHOTS}
260 format:= GL_RGBA;
261 ext:= '.png';
262 {$ELSE}
263 {$IFDEF WINDOWS}
264 format:= GL_RGBA;
265 ext:= '.png';
266 {$ELSE}
267 format:= GL_BGRA;
268 ext:= '.bmp';
269 {$ENDIF}
270 {$ENDIF}
271
272 if dump > 0 then
273 size:= LAND_WIDTH*LAND_HEIGHT*4
274 else size:= toPowerOf2(cScreenWidth) * toPowerOf2(cScreenHeight) * 4;
275 p:= GetMem(size); // will be freed in SaveScreenshot()
276
277 // memory could not be allocated
278 if p = nil then
279 begin
280 AddFileLog('Error: Could not allocate memory for screenshot.');
281 MakeScreenshot:= false;
282 exit;
283 end;
284
285 // read pixels from land array
286 if dump > 0 then
287 begin
288 for y:= 0 to LAND_HEIGHT-1 do
289 for x:= 0 to LAND_WIDTH-1 do
290 if dump = 2 then
291 PLongWordArray(p)^[y*LAND_WIDTH+x]:= LandPixels[LAND_HEIGHT-1-y, x]
292 else
293 begin
294 if Land[LAND_HEIGHT-1-y, x] and lfIndestructible = lfIndestructible then
295 PLongWordArray(p)^[y*LAND_WIDTH+x]:= (AMask or RMask)
296 else if Land[LAND_HEIGHT-1-y, x] and lfIce = lfIce then
297 PLongWordArray(p)^[y*LAND_WIDTH+x]:= (AMask or BMask)
298 else if Land[LAND_HEIGHT-1-y, x] and lfBouncy = lfBouncy then
299 PLongWordArray(p)^[y*LAND_WIDTH+x]:= (AMask or GMask)
300 else if Land[LAND_HEIGHT-1-y, x] and lfObject = lfObject then
301 PLongWordArray(p)^[y*LAND_WIDTH+x]:= $FFFFFFFF
302 else if Land[LAND_HEIGHT-1-y, x] and lfBasic = lfBasic then
303 PLongWordArray(p)^[y*LAND_WIDTH+x]:= AMask
304 else
305 PLongWordArray(p)^[y*LAND_WIDTH+x]:= 0
306 end
307 end
308 else
309 // read pixels from the front buffer
310 begin
311 glReadPixels(0, 0, cScreenWidth, cScreenHeight, format, GL_UNSIGNED_BYTE, p);
312 {$IFDEF USE_VIDEO_RECORDING}
313 ReduceImage(p, cScreenWidth, cScreenHeight, k)
314 {$ENDIF}
315 end;
316
317 // allocate and fill structure that will be passed to new thread
318 New(image); // will be disposed in SaveScreenshot()
319 if dump = 2 then
320 image^.filename:= shortstring(UserPathPrefix) + filename + '_landpixels' + ext
321 else if dump = 1 then
322 image^.filename:= shortstring(UserPathPrefix) + filename + '_land' + ext
323 else image^.filename:= shortstring(UserPathPrefix) + filename + ext;
324
325 if dump <> 0 then
326 begin
327 image^.width:= LAND_WIDTH;
328 image^.height:= LAND_HEIGHT
329 end
330 else
331 begin
332 image^.width:= cScreenWidth div k;
333 image^.height:= cScreenHeight div k
334 end;
335 image^.size:= size;
336 image^.buffer:= p;
337
338 SDL_CreateThread(@SaveScreenshot, PChar('snapshot'), image);
339 MakeScreenshot:= true; // possibly it is not true but we will not wait for thread to terminate
340 end;
341
342 // http://www.idevgames.com/forums/thread-5602-post-21860.html#pid21860
doSurfaceConversionnull343 function doSurfaceConversion(tmpsurf: PSDL_Surface): PSDL_Surface;
344 var convertedSurf: PSDL_Surface;
345 begin
346 doSurfaceConversion:= tmpsurf;
347 if ((tmpsurf^.format^.bitsperpixel = 32) and (tmpsurf^.format^.rshift > tmpsurf^.format^.bshift)) or
348 (tmpsurf^.format^.bitsperpixel = 24) then
349 begin
350 convertedSurf:= SDL_ConvertSurface(tmpsurf, conversionFormat, SDL_SWSURFACE);
351 SDL_FreeSurface(tmpsurf);
352 doSurfaceConversion:= convertedSurf;
353 end;
354 end;
355
SDL_RectMakenull356 function SDL_RectMake(x, y, width, height: LongInt): TSDL_Rect; inline;
357 begin
358 SDL_RectMake.x:= x;
359 SDL_RectMake.y:= y;
360 SDL_RectMake.w:= width;
361 SDL_RectMake.h:= height;
362 end;
363
GetTeamStatStringnull364 function GetTeamStatString(p: PTeam): shortstring;
365 var s: shortstring;
366 begin
367 s:= p^.TeamName + ':' + IntToStr(p^.TeamHealth) + ':';
368 GetTeamStatString:= s;
369 end;
370
371 procedure initModule;
372 const SDL_PIXELFORMAT_ABGR8888 = (1 shl 28) or (6 shl 24) or (7 shl 20) or (6 shl 16) or (32 shl 8) or 4;
373 begin
374 conversionFormat:= SDL_AllocFormat(SDL_PIXELFORMAT_ABGR8888);
375 end;
376
377 procedure freeModule;
378 begin
379 SDL_FreeFormat(conversionFormat);
380 end;
381
382 end.
383