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 

Generated on Wed Oct 1 17:03:23 2008 for openttd by  doxygen 1.5.6