00001
00002
00003 #include "stdafx.h"
00004 #include "openttd.h"
00005 #include "debug.h"
00006 #include "fileio.h"
00007 #include "viewport_func.h"
00008 #include "gfx_func.h"
00009 #include "core/math_func.hpp"
00010 #include "screenshot.h"
00011 #include "variables.h"
00012 #include "blitter/factory.hpp"
00013 #include "fileio.h"
00014 #include "strings_func.h"
00015 #include "zoom_func.h"
00016 #include "core/alloc_func.hpp"
00017 #include "core/endian_func.hpp"
00018 #include "map_func.h"
00019 #include "date_func.h"
00020 #include "player_func.h"
00021
00022 #include "table/strings.h"
00023
00024 char _screenshot_format_name[8];
00025 uint _num_screenshot_formats;
00026 uint _cur_screenshot_format;
00027 ScreenshotType current_screenshot_type;
00028
00029
00030 typedef void ScreenshotCallback(void *userdata, void *buf, uint y, uint pitch, uint n);
00031 typedef bool ScreenshotHandlerProc(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette);
00032
00033 struct ScreenshotFormat {
00034 const char *name;
00035 const char *extension;
00036 ScreenshotHandlerProc *proc;
00037 };
00038
00039
00040
00041
00042 #if defined(_MSC_VER) || defined(__WATCOMC__)
00043 #pragma pack(push, 1)
00044 #endif
00045
00046 struct BitmapFileHeader {
00047 uint16 type;
00048 uint32 size;
00049 uint32 reserved;
00050 uint32 off_bits;
00051 } GCC_PACK;
00052 assert_compile(sizeof(BitmapFileHeader) == 14);
00053
00054 #if defined(_MSC_VER) || defined(__WATCOMC__)
00055 #pragma pack(pop)
00056 #endif
00057
00058 struct BitmapInfoHeader {
00059 uint32 size;
00060 int32 width, height;
00061 uint16 planes, bitcount;
00062 uint32 compression, sizeimage, xpels, ypels, clrused, clrimp;
00063 };
00064 assert_compile(sizeof(BitmapInfoHeader) == 40);
00065
00066 struct RgbQuad {
00067 byte blue, green, red, reserved;
00068 };
00069 assert_compile(sizeof(RgbQuad) == 4);
00070
00071
00072 static bool MakeBmpImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
00073 {
00074 BitmapFileHeader bfh;
00075 BitmapInfoHeader bih;
00076 RgbQuad rq[256];
00077 FILE *f;
00078 uint i, padw;
00079 uint n, maxlines;
00080 uint pal_size = 0;
00081 uint bpp = pixelformat / 8;
00082
00083
00084 if (pixelformat != 8 && pixelformat != 32) return false;
00085
00086 f = fopen(name, "wb");
00087 if (f == NULL) return false;
00088
00089
00090 padw = Align(w, 4);
00091
00092 if (pixelformat == 8) pal_size = sizeof(RgbQuad) * 256;
00093
00094
00095 bfh.type = TO_LE16('MB');
00096 bfh.size = TO_LE32(sizeof(bfh) + sizeof(bih) + pal_size + padw * h * bpp);
00097 bfh.reserved = 0;
00098 bfh.off_bits = TO_LE32(sizeof(bfh) + sizeof(bih) + pal_size);
00099
00100
00101 bih.size = TO_LE32(sizeof(BitmapInfoHeader));
00102 bih.width = TO_LE32(w);
00103 bih.height = TO_LE32(h);
00104 bih.planes = TO_LE16(1);
00105 bih.bitcount = TO_LE16(pixelformat);
00106 bih.compression = 0;
00107 bih.sizeimage = 0;
00108 bih.xpels = 0;
00109 bih.ypels = 0;
00110 bih.clrused = 0;
00111 bih.clrimp = 0;
00112
00113 if (pixelformat == 8) {
00114
00115 for (i = 0; i != 256; i++) {
00116 rq[i].red = palette[i].r;
00117 rq[i].green = palette[i].g;
00118 rq[i].blue = palette[i].b;
00119 rq[i].reserved = 0;
00120 }
00121 }
00122
00123
00124 if (fwrite(&bfh, sizeof(bfh), 1, f) != 1) return false;
00125 if (fwrite(&bih, sizeof(bih), 1, f) != 1) return false;
00126 if (pixelformat == 8) if (fwrite(rq, sizeof(rq), 1, f) != 1) return false;
00127
00128
00129 maxlines = Clamp(65536 / padw, 16, 128);
00130
00131
00132 void *buff = MallocT<uint8>(padw * maxlines * bpp);
00133 if (buff == NULL) {
00134 fclose(f);
00135 return false;
00136 }
00137 memset(buff, 0, padw * maxlines);
00138
00139
00140 do {
00141
00142 n = min(h, maxlines);
00143 h -= n;
00144
00145
00146 callb(userdata, buff, h, padw, n);
00147
00148
00149 while (n)
00150 if (fwrite((uint8 *)buff + (--n) * padw * bpp, padw * bpp, 1, f) != 1) {
00151 free(buff);
00152 fclose(f);
00153 return false;
00154 }
00155 } while (h != 0);
00156
00157 free(buff);
00158 fclose(f);
00159
00160 return true;
00161 }
00162
00163
00164
00165
00166 #if defined(WITH_PNG)
00167 #include <png.h>
00168
00169 static void PNGAPI png_my_error(png_structp png_ptr, png_const_charp message)
00170 {
00171 DEBUG(misc, 0, "[libpng] error: %s - %s", message, (char *)png_get_error_ptr(png_ptr));
00172 longjmp(png_ptr->jmpbuf, 1);
00173 }
00174
00175 static void PNGAPI png_my_warning(png_structp png_ptr, png_const_charp message)
00176 {
00177 DEBUG(misc, 1, "[libpng] warning: %s - %s", message, (char *)png_get_error_ptr(png_ptr));
00178 }
00179
00180 static bool MakePNGImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
00181 {
00182 png_color rq[256];
00183 FILE *f;
00184 uint i, y, n;
00185 uint maxlines;
00186 uint bpp = pixelformat / 8;
00187 png_structp png_ptr;
00188 png_infop info_ptr;
00189
00190
00191 if (pixelformat != 8 && pixelformat != 32) return false;
00192
00193 f = fopen(name, "wb");
00194 if (f == NULL) return false;
00195
00196 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (char *)name, png_my_error, png_my_warning);
00197
00198 if (png_ptr == NULL) {
00199 fclose(f);
00200 return false;
00201 }
00202
00203 info_ptr = png_create_info_struct(png_ptr);
00204 if (info_ptr == NULL) {
00205 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
00206 fclose(f);
00207 return false;
00208 }
00209
00210 if (setjmp(png_jmpbuf(png_ptr))) {
00211 png_destroy_write_struct(&png_ptr, &info_ptr);
00212 fclose(f);
00213 return false;
00214 }
00215
00216 png_init_io(png_ptr, f);
00217
00218 png_set_filter(png_ptr, 0, PNG_FILTER_NONE);
00219
00220 png_set_IHDR(png_ptr, info_ptr, w, h, 8, pixelformat == 8 ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_RGB,
00221 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00222
00223 if (pixelformat == 8) {
00224
00225 for (i = 0; i != 256; i++) {
00226 rq[i].red = palette[i].r;
00227 rq[i].green = palette[i].g;
00228 rq[i].blue = palette[i].b;
00229 }
00230
00231 png_set_PLTE(png_ptr, info_ptr, rq, 256);
00232 }
00233
00234 png_write_info(png_ptr, info_ptr);
00235 png_set_flush(png_ptr, 512);
00236
00237 if (pixelformat == 32) {
00238 png_color_8 sig_bit;
00239
00240
00241 sig_bit.alpha = 0;
00242 sig_bit.blue = 8;
00243 sig_bit.green = 8;
00244 sig_bit.red = 8;
00245 sig_bit.gray = 8;
00246 png_set_sBIT(png_ptr, info_ptr, &sig_bit);
00247
00248 #ifdef TTD_LITTLE_ENDIAN
00249 png_set_bgr(png_ptr);
00250 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
00251 #else
00252 png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
00253 #endif
00254 }
00255
00256
00257 maxlines = Clamp(65536 / w, 16, 128);
00258
00259
00260 void *buff = MallocT<uint8>(w * maxlines * bpp);
00261 if (buff == NULL) {
00262 png_destroy_write_struct(&png_ptr, &info_ptr);
00263 fclose(f);
00264 return false;
00265 }
00266 memset(buff, 0, w * maxlines * bpp);
00267
00268 y = 0;
00269 do {
00270
00271 n = min(h - y, maxlines);
00272
00273
00274 callb(userdata, buff, y, w, n);
00275 y += n;
00276
00277
00278 for (i = 0; i != n; i++)
00279 png_write_row(png_ptr, (png_bytep)buff + i * w * bpp);
00280 } while (y != h);
00281
00282 png_write_end(png_ptr, info_ptr);
00283 png_destroy_write_struct(&png_ptr, &info_ptr);
00284
00285 free(buff);
00286 fclose(f);
00287 return true;
00288 }
00289 #endif
00290
00291
00292
00293
00294
00295
00296 struct PcxHeader {
00297 byte manufacturer;
00298 byte version;
00299 byte rle;
00300 byte bpp;
00301 uint32 unused;
00302 uint16 xmax, ymax;
00303 uint16 hdpi, vdpi;
00304 byte pal_small[16 * 3];
00305 byte reserved;
00306 byte planes;
00307 uint16 pitch;
00308 uint16 cpal;
00309 uint16 width;
00310 uint16 height;
00311 byte filler[54];
00312 };
00313 assert_compile(sizeof(PcxHeader) == 128);
00314
00315 static bool MakePCXImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
00316 {
00317 FILE *f;
00318 uint maxlines;
00319 uint y;
00320 PcxHeader pcx;
00321 bool success;
00322
00323 if (pixelformat == 32) {
00324 DEBUG(misc, 0, "Can't convert a 32bpp screenshot to PCX format. Please pick an other format.");
00325 return false;
00326 }
00327 if (pixelformat != 8 || w == 0)
00328 return false;
00329
00330 f = fopen(name, "wb");
00331 if (f == NULL) return false;
00332
00333 memset(&pcx, 0, sizeof(pcx));
00334
00335
00336 pcx.manufacturer = 10;
00337 pcx.version = 5;
00338 pcx.rle = 1;
00339 pcx.bpp = 8;
00340 pcx.xmax = TO_LE16(w - 1);
00341 pcx.ymax = TO_LE16(h - 1);
00342 pcx.hdpi = TO_LE16(320);
00343 pcx.vdpi = TO_LE16(320);
00344
00345 pcx.planes = 1;
00346 pcx.cpal = TO_LE16(1);
00347 pcx.width = pcx.pitch = TO_LE16(w);
00348 pcx.height = TO_LE16(h);
00349
00350
00351 if (fwrite(&pcx, sizeof(pcx), 1, f) != 1) {
00352 fclose(f);
00353 return false;
00354 }
00355
00356
00357 maxlines = Clamp(65536 / w, 16, 128);
00358
00359
00360 uint8 *buff = MallocT<uint8>(w * maxlines);
00361 if (buff == NULL) {
00362 fclose(f);
00363 return false;
00364 }
00365 memset(buff, 0, w * maxlines);
00366
00367 y = 0;
00368 do {
00369
00370 uint n = min(h - y, maxlines);
00371 uint i;
00372
00373
00374 callb(userdata, buff, y, w, n);
00375 y += n;
00376
00377
00378 for (i = 0; i != n; i++) {
00379 const uint8 *bufp = buff + i * w;
00380 byte runchar = bufp[0];
00381 uint runcount = 1;
00382 uint j;
00383
00384
00385 for (j = 1; j < w; j++) {
00386 uint8 ch = bufp[j];
00387
00388 if (ch != runchar || runcount >= 0x3f) {
00389 if (runcount > 1 || (runchar & 0xC0) == 0xC0)
00390 if (fputc(0xC0 | runcount, f) == EOF) {
00391 free(buff);
00392 fclose(f);
00393 return false;
00394 }
00395 if (fputc(runchar, f) == EOF) {
00396 free(buff);
00397 fclose(f);
00398 return false;
00399 }
00400 runcount = 0;
00401 runchar = ch;
00402 }
00403 runcount++;
00404 }
00405
00406
00407 if (runcount > 1 || (runchar & 0xC0) == 0xC0)
00408 if (fputc(0xC0 | runcount, f) == EOF) {
00409 free(buff);
00410 fclose(f);
00411 return false;
00412 }
00413 if (fputc(runchar, f) == EOF) {
00414 free(buff);
00415 fclose(f);
00416 return false;
00417 }
00418 }
00419 } while (y != h);
00420
00421 free(buff);
00422
00423
00424 if (fputc(12, f) == EOF) {
00425 fclose(f);
00426 return false;
00427 }
00428
00429 if (sizeof(*palette) == 3) {
00430 success = fwrite(palette, 256 * sizeof(*palette), 1, f) == 1;
00431 } else {
00432
00433 byte tmp[256 * 3];
00434 uint i;
00435
00436 for (i = 0; i < 256; i++) {
00437 tmp[i * 3 + 0] = palette[i].r;
00438 tmp[i * 3 + 1] = palette[i].g;
00439 tmp[i * 3 + 2] = palette[i].b;
00440 }
00441 success = fwrite(tmp, sizeof(tmp), 1, f) == 1;
00442 }
00443
00444 fclose(f);
00445
00446 return success;
00447 }
00448
00449
00450
00451
00452
00453 static const ScreenshotFormat _screenshot_formats[] = {
00454 #if defined(WITH_PNG)
00455 {"PNG", "png", &MakePNGImage},
00456 #endif
00457 {"BMP", "bmp", &MakeBmpImage},
00458 {"PCX", "pcx", &MakePCXImage},
00459 };
00460
00461 void InitializeScreenshotFormats()
00462 {
00463 int i, j;
00464 for (i = 0, j = 0; i != lengthof(_screenshot_formats); i++)
00465 if (!strcmp(_screenshot_format_name, _screenshot_formats[i].extension)) {
00466 j = i;
00467 break;
00468 }
00469 _cur_screenshot_format = j;
00470 _num_screenshot_formats = lengthof(_screenshot_formats);
00471 current_screenshot_type = SC_NONE;
00472 }
00473
00474 const char *GetScreenshotFormatDesc(int i)
00475 {
00476 return _screenshot_formats[i].name;
00477 }
00478
00479 void SetScreenshotFormat(int i)
00480 {
00481 _cur_screenshot_format = i;
00482 strcpy(_screenshot_format_name, _screenshot_formats[i].extension);
00483 }
00484
00485
00486 static void CurrentScreenCallback(void *userdata, void *buf, uint y, uint pitch, uint n)
00487 {
00488 Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
00489 void *src = blitter->MoveTo(_screen.dst_ptr, 0, y);
00490 blitter->CopyImageToBuffer(src, buf, _screen.width, n, pitch);
00491 }
00492
00500 static void LargeWorldCallback(void *userdata, void *buf, uint y, uint pitch, uint n)
00501 {
00502 ViewPort *vp = (ViewPort *)userdata;
00503 DrawPixelInfo dpi, *old_dpi;
00504 int wx, left;
00505
00506
00507 DrawPixelInfo old_screen = _screen;
00508 bool old_disable_anim = _screen_disable_anim;
00509
00510 _screen.dst_ptr = buf;
00511 _screen.width = pitch;
00512 _screen.height = n;
00513 _screen.pitch = pitch;
00514 _screen_disable_anim = true;
00515
00516 old_dpi = _cur_dpi;
00517 _cur_dpi = &dpi;
00518
00519 dpi.dst_ptr = buf;
00520 dpi.height = n;
00521 dpi.width = vp->width;
00522 dpi.pitch = pitch;
00523 dpi.zoom = ZOOM_LVL_WORLD_SCREENSHOT;
00524 dpi.left = 0;
00525 dpi.top = y;
00526
00527
00528 left = 0;
00529 while (vp->width - left != 0) {
00530 wx = min(vp->width - left, 1600);
00531 left += wx;
00532
00533 ViewportDoDraw(vp,
00534 ScaleByZoom(left - wx - vp->left, vp->zoom) + vp->virtual_left,
00535 ScaleByZoom(y - vp->top, vp->zoom) + vp->virtual_top,
00536 ScaleByZoom(left - vp->left, vp->zoom) + vp->virtual_left,
00537 ScaleByZoom((y + n) - vp->top, vp->zoom) + vp->virtual_top
00538 );
00539 }
00540
00541 _cur_dpi = old_dpi;
00542
00543
00544 _screen = old_screen;
00545 _screen_disable_anim = old_disable_anim;
00546 }
00547
00548 static char *MakeScreenshotName(const char *ext)
00549 {
00550 static char filename[MAX_PATH];
00551 int serial;
00552 size_t len;
00553
00554 if (_game_mode == GM_EDITOR || _game_mode == GM_MENU || _local_player == PLAYER_SPECTATOR) {
00555 ttd_strlcpy(_screenshot_name, "screenshot", lengthof(_screenshot_name));
00556 } else {
00557 SetDParam(0, _local_player);
00558 SetDParam(1, _date);
00559 GetString(_screenshot_name, STR_4004, lastof(_screenshot_name));
00560 }
00561
00562
00563 SanitizeFilename(_screenshot_name);
00564 len = strlen(_screenshot_name);
00565 snprintf(&_screenshot_name[len], lengthof(_screenshot_name) - len, ".%s", ext);
00566
00567 for (serial = 1;; serial++) {
00568 snprintf(filename, lengthof(filename), "%s%s", _personal_dir, _screenshot_name);
00569 if (!FileExists(filename)) break;
00570
00571 snprintf(&_screenshot_name[len], lengthof(_screenshot_name) - len, "#%d.%s", serial, ext);
00572 }
00573
00574 return filename;
00575 }
00576
00577 void SetScreenshotType(ScreenshotType t)
00578 {
00579 current_screenshot_type = t;
00580 }
00581
00582 bool IsScreenshotRequested()
00583 {
00584 return (current_screenshot_type != SC_NONE);
00585 }
00586
00587 static bool MakeSmallScreenshot()
00588 {
00589 const ScreenshotFormat *sf = _screenshot_formats + _cur_screenshot_format;
00590 return sf->proc(MakeScreenshotName(sf->extension), CurrentScreenCallback, NULL, _screen.width, _screen.height, BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth(), _cur_palette);
00591 }
00592
00593 static bool MakeWorldScreenshot()
00594 {
00595 ViewPort vp;
00596 const ScreenshotFormat *sf;
00597
00598 vp.zoom = ZOOM_LVL_WORLD_SCREENSHOT;
00599 vp.left = 0;
00600 vp.top = 0;
00601 vp.virtual_left = -(int)MapMaxX() * TILE_PIXELS;
00602 vp.virtual_top = 0;
00603 vp.virtual_width = (MapMaxX() + MapMaxY()) * TILE_PIXELS;
00604 vp.width = vp.virtual_width;
00605 vp.virtual_height = (MapMaxX() + MapMaxY()) * TILE_PIXELS >> 1;
00606 vp.height = vp.virtual_height;
00607
00608 sf = _screenshot_formats + _cur_screenshot_format;
00609 return sf->proc(MakeScreenshotName(sf->extension), LargeWorldCallback, &vp, vp.width, vp.height, BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth(), _cur_palette);
00610 }
00611
00612 bool MakeScreenshot()
00613 {
00614 switch (current_screenshot_type) {
00615 case SC_VIEWPORT:
00616 UndrawMouseCursor();
00617 DrawDirtyBlocks();
00618 current_screenshot_type = SC_NONE;
00619 return MakeSmallScreenshot();
00620 case SC_WORLD:
00621 current_screenshot_type = SC_NONE;
00622 return MakeWorldScreenshot();
00623 default: return false;
00624 }
00625 }
00626
00627
00628