# screenshot.cpp

Go to the documentation of this file.

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