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