# screenshot.cpp

    00001 /* $Id: screenshot.cpp 11828 2008-01-13 01:21:35Z rubidium $ */
    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 /* called by the ScreenShot proc to generate screenshot lines. */
    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 //*** SCREENSHOT CODE FOR WINDOWS BITMAP (.BMP)
    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 /* generic .BMP writer */
    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   /* only implemented for 8bit and 32bit images so far. */
    00084   if (pixelformat != 8 && pixelformat != 32) return false;
    00085
    00086   f = fopen(name, "wb");
    00087   if (f == NULL) return false;
    00088
    00089   /* each scanline must be aligned on a 32bit boundary */
    00090   padw = Align(w, 4);
    00091
    00092   if (pixelformat == 8) pal_size = sizeof(RgbQuad) * 256;
    00093
    00094   /* setup the file header */
    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   /* setup the info header */
    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     /* convert the palette to the windows format */
    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   /* write file header and info header and palette */
    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   /* use by default 64k temp memory */
    00129   maxlines = Clamp(65536 / padw, 16, 128);
    00130
    00131   /* now generate the bitmap bits */
    00132   void *buff = MallocT<uint8>(padw * maxlines * bpp); // by default generate 128 lines at a time.
    00133   if (buff == NULL) {
    00134     fclose(f);
    00135     return false;
    00136   }
    00137   memset(buff, 0, padw * maxlines); // zero the buffer to have the padding bytes set to 0
    00138
    00139   /* start at the bottom, since bitmaps are stored bottom up. */
    00140   do {
    00141     /* determine # lines */
    00142     n = min(h, maxlines);
    00143     h -= n;
    00144
    00145     /* render the pixels */
    00146     callb(userdata, buff, h, padw, n);
    00147
    00148     /* write each line */
    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 //*** SCREENSHOT CODE FOR PORTABLE NETWORK GRAPHICS (.PNG)
    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   /* only implemented for 8bit and 32bit images so far. */
    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     /* convert the palette to the .PNG format. */
    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     /* Save exact color/alpha resolution */
    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   /* use by default 64k temp memory */
    00257   maxlines = Clamp(65536 / w, 16, 128);
    00258
    00259   /* now generate the bitmap bits */
    00260   void *buff = MallocT<uint8>(w * maxlines * bpp); // by default generate 128 lines at a time.
    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     /* determine # lines to write */
    00271     n = min(h - y, maxlines);
    00272
    00273     /* render the pixels into the buffer */
    00274     callb(userdata, buff, y, w, n);
    00275     y += n;
    00276
    00277     /* write them to png */
    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 /* WITH_PNG */
    00290
    00291
    00292 //************************************************
    00293 //*** SCREENSHOT CODE FOR ZSOFT PAINTBRUSH (.PCX)
    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   /* setup pcx header */
    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   /* write pcx header */
    00351   if (fwrite(&pcx, sizeof(pcx), 1, f) != 1) {
    00352     fclose(f);
    00353     return false;
    00354   }
    00355
    00356   /* use by default 64k temp memory */
    00357   maxlines = Clamp(65536 / w, 16, 128);
    00358
    00359   /* now generate the bitmap bits */
    00360   uint8 *buff = MallocT<uint8>(w * maxlines); // by default generate 128 lines at a time.
    00361   if (buff == NULL) {
    00362     fclose(f);
    00363     return false;
    00364   }
    00365   memset(buff, 0, w * maxlines); // zero the buffer to have the padding bytes set to 0
    00366
    00367   y = 0;
    00368   do {
    00369     /* determine # lines to write */
    00370     uint n = min(h - y, maxlines);
    00371     uint i;
    00372
    00373     /* render the pixels into the buffer */
    00374     callb(userdata, buff, y, w, n);
    00375     y += n;
    00376
    00377     /* write them to pcx */
    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       /* for each pixel... */
    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       /* write remaining bytes.. */
    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   /* write 8-bit color palette */
    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     /* If the palette is word-aligned, copy it to a temporary byte array */
    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 //*** GENERIC SCREENSHOT CODE
    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 /* screenshot generator that dumps the current video buffer */
    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   /* We are no longer rendering to the screen */
    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   /* Render viewport in blocks of 1600 pixels width */
    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   /* Switch back to rendering to the screen */
    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   /* Add extension to screenshot file */
    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     /* If file exists try another one with same name, but just with a higher index */
    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