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 }

Generated on Mon Feb 16 23:12:10 2009 for openttd by  doxygen 1.5.6