# screenshot.cpp

Go to the documentation of this file.

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