2009-08-21 22:21:05 +02:00
/*
* This file is part of OpenTTD .
* OpenTTD is free software ; you can redistribute it and / or modify it under the terms of the GNU General Public License as published by the Free Software Foundation , version 2.
* OpenTTD is distributed in the hope that it will be useful , but WITHOUT ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
* See the GNU General Public License for more details . You should have received a copy of the GNU General Public License along with OpenTTD . If not , see < http : //www.gnu.org/licenses/>.
*/
2008-05-06 17:11:33 +02:00
/** @file screenshot.cpp The creation of screenshots! */
2004-08-09 19:04:08 +02:00
# include "stdafx.h"
2023-01-26 19:21:36 +01:00
# include "core/backup_type.hpp"
2012-01-10 00:04:03 +01:00
# include "fileio_func.h"
2008-01-09 10:57:48 +01:00
# include "viewport_func.h"
# include "gfx_func.h"
2004-11-15 20:25:59 +01:00
# include "screenshot.h"
2021-11-07 17:41:24 +01:00
# include "screenshot_gui.h"
2007-06-17 22:30:28 +02:00
# include "blitter/factory.hpp"
2007-12-23 11:56:02 +01:00
# include "zoom_func.h"
2007-12-25 14:28:09 +01:00
# include "core/endian_func.hpp"
2009-01-04 16:32:25 +01:00
# include "saveload/saveload.h"
2020-01-04 17:47:37 +01:00
# include "company_base.h"
2008-09-30 22:51:04 +02:00
# include "company_func.h"
2009-12-19 20:21:37 +01:00
# include "strings_func.h"
2011-12-10 14:54:10 +01:00
# include "error.h"
2020-06-17 23:50:22 +02:00
# include "textbuf_gui.h"
2010-08-16 01:32:36 +02:00
# include "window_gui.h"
# include "window_func.h"
2011-01-01 22:15:50 +01:00
# include "tile_map.h"
2012-12-09 17:53:01 +01:00
# include "landscape.h"
2021-01-16 16:43:11 +01:00
# include "video/video_driver.hpp"
2022-10-01 00:20:25 +02:00
# include "smallmap_gui.h"
2009-12-19 20:21:37 +01:00
# include "table/strings.h"
2004-08-09 19:04:08 +02:00
2014-04-23 22:13:33 +02:00
# include "safeguards.h"
2011-05-28 15:53:52 +02:00
static const char * const SCREENSHOT_NAME = " screenshot " ; ///< Default filename of a saved screenshot.
2011-05-28 15:54:18 +02:00
static const char * const HEIGHTMAP_NAME = " heightmap " ; ///< Default filename of a saved heightmap.
2008-01-13 02:21:35 +01:00
2021-04-28 17:32:33 +02:00
std : : string _screenshot_format_name ; ///< Extension of the current screenshot format (corresponds with #_cur_screenshot_format).
2023-05-25 18:23:26 +02:00
static std : : string _screenshot_name ; ///< Filename of the screenshot file.
std : : string _full_screenshot_path ; ///< Pathname of the screenshot file.
2021-03-25 23:51:37 +01:00
uint _heightmap_highest_peak ; ///< When saving a heightmap, this contains the highest peak on the map.
2005-07-19 08:47:07 +02:00
2011-05-22 22:18:38 +02:00
/**
* Callback function signature for generating lines of pixel data to be written to the screenshot file .
* @ param userdata Pointer to user data .
* @ param buf Destination buffer .
* @ param y Line number of the first line to write .
* @ param pitch Number of pixels to write ( 1 byte for 8 bpp , 4 bytes for 32 bpp ) . @ see Colour
* @ param n Number of lines to write .
*/
2007-06-12 22:24:12 +02:00
typedef void ScreenshotCallback ( void * userdata , void * buf , uint y , uint pitch , uint n ) ;
2011-05-22 22:18:38 +02:00
/**
* Function signature for a screenshot generation routine for one of the available formats .
* @ param name Filename , including extension .
* @ param callb Callback function for generating lines of pixels .
2012-07-10 20:37:54 +02:00
* @ param userdata User data , passed on to \ a callb .
2011-05-22 22:18:38 +02:00
* @ param w Width of the image in pixels .
* @ param h Height of the image in pixels .
* @ param pixelformat Bits per pixel ( bpp ) , either 8 or 32.
* @ param palette % Colour palette ( for 8 bpp images ) .
* @ return File was written successfully .
*/
2009-11-14 16:28:57 +01:00
typedef bool ScreenshotHandlerProc ( const char * name , ScreenshotCallback * callb , void * userdata , uint w , uint h , int pixelformat , const Colour * palette ) ;
2004-08-09 19:04:08 +02:00
2011-05-22 22:18:38 +02:00
/** Screenshot format information. */
2007-03-07 13:11:48 +01:00
struct ScreenshotFormat {
2011-05-22 22:18:38 +02:00
const char * extension ; ///< File extension.
ScreenshotHandlerProc * proc ; ///< Function for writing the screenshot.
2007-03-07 13:11:48 +01:00
} ;
2004-08-09 19:04:08 +02:00
2020-01-04 17:47:37 +01:00
# define MKCOLOUR(x) TO_LE32X(x)
2009-03-15 01:32:18 +01:00
/*************************************************
* * * * SCREENSHOT CODE FOR WINDOWS BITMAP ( . BMP )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-08-09 19:04:08 +02:00
2009-11-01 21:54:21 +01:00
/** BMP File Header (stored in little endian) */
2018-06-18 22:21:45 +02:00
PACK ( struct BitmapFileHeader {
2023-05-08 19:01:06 +02:00
uint16_t type ;
uint32_t size ;
uint32_t reserved ;
uint32_t off_bits ;
2018-06-18 22:21:45 +02:00
} ) ;
2020-12-27 11:44:22 +01:00
static_assert ( sizeof ( BitmapFileHeader ) = = 14 ) ;
2004-08-09 19:04:08 +02:00
2009-11-01 21:54:21 +01:00
/** BMP Info Header (stored in little endian) */
2007-03-07 13:11:48 +01:00
struct BitmapInfoHeader {
2023-05-08 19:01:06 +02:00
uint32_t size ;
int32_t width , height ;
uint16_t planes , bitcount ;
uint32_t compression , sizeimage , xpels , ypels , clrused , clrimp ;
2007-03-07 13:11:48 +01:00
} ;
2020-12-27 11:44:22 +01:00
static_assert ( sizeof ( BitmapInfoHeader ) = = 40 ) ;
2004-08-09 19:04:08 +02:00
2009-11-01 21:54:21 +01:00
/** Format of palette data in BMP header */
2007-03-07 13:11:48 +01:00
struct RgbQuad {
2024-03-16 23:59:32 +01:00
uint8_t blue , green , red , reserved ;
2007-03-07 13:11:48 +01:00
} ;
2020-12-27 11:44:22 +01:00
static_assert ( sizeof ( RgbQuad ) = = 4 ) ;
2004-08-09 19:04:08 +02:00
2009-11-01 21:54:21 +01:00
/**
* Generic . BMP writer
* @ param name file name including extension
* @ param callb callback used for gathering rendered image
2012-07-10 20:37:54 +02:00
* @ param userdata parameters forwarded to \ a callb
2009-11-01 21:54:21 +01:00
* @ param w width in pixels
* @ param h height in pixels
* @ param pixelformat bits per pixel
2009-11-13 18:40:21 +01:00
* @ param palette colour palette ( for 8 bpp mode )
2009-11-01 21:54:21 +01:00
* @ return was everything ok ?
2011-05-22 22:18:38 +02:00
* @ see ScreenshotHandlerProc
2009-11-01 21:54:21 +01:00
*/
2009-11-14 16:31:06 +01:00
static bool MakeBMPImage ( const char * name , ScreenshotCallback * callb , void * userdata , uint w , uint h , int pixelformat , const Colour * palette )
2004-08-09 19:04:08 +02:00
{
2009-11-01 21:54:21 +01:00
uint bpp ; // bytes per pixel
switch ( pixelformat ) {
case 8 : bpp = 1 ; break ;
/* 32bpp mode is saved as 24bpp BMP */
case 32 : bpp = 3 ; break ;
/* Only implemented for 8bit and 32bit images so far */
default : return false ;
}
2004-08-09 19:04:08 +02:00
2009-11-01 21:54:21 +01:00
FILE * f = fopen ( name , " wb " ) ;
2019-04-10 23:07:06 +02:00
if ( f = = nullptr ) return false ;
2004-08-09 19:04:08 +02:00
2009-11-01 21:54:21 +01:00
/* Each scanline must be aligned on a 32bit boundary */
uint bytewidth = Align ( w * bpp , 4 ) ; // bytes per line in file
2004-08-09 19:04:08 +02:00
2009-11-01 21:54:21 +01:00
/* Size of palette. Only present for 8bpp mode */
uint pal_size = pixelformat = = 8 ? sizeof ( RgbQuad ) * 256 : 0 ;
2007-06-12 22:24:12 +02:00
2009-11-01 21:54:21 +01:00
/* Setup the file header */
BitmapFileHeader bfh ;
2004-08-09 19:04:08 +02:00
bfh . type = TO_LE16 ( ' MB ' ) ;
2023-01-02 21:30:02 +01:00
bfh . size = TO_LE32 ( sizeof ( BitmapFileHeader ) + sizeof ( BitmapInfoHeader ) + pal_size + static_cast < size_t > ( bytewidth ) * h ) ;
2004-08-09 19:04:08 +02:00
bfh . reserved = 0 ;
2009-11-01 21:54:21 +01:00
bfh . off_bits = TO_LE32 ( sizeof ( BitmapFileHeader ) + sizeof ( BitmapInfoHeader ) + pal_size ) ;
2004-08-09 19:04:08 +02:00
2009-11-01 21:54:21 +01:00
/* Setup the info header */
BitmapInfoHeader bih ;
2004-08-09 19:04:08 +02:00
bih . size = TO_LE32 ( sizeof ( BitmapInfoHeader ) ) ;
bih . width = TO_LE32 ( w ) ;
bih . height = TO_LE32 ( h ) ;
bih . planes = TO_LE16 ( 1 ) ;
2009-11-01 21:54:21 +01:00
bih . bitcount = TO_LE16 ( bpp * 8 ) ;
2004-08-09 19:04:08 +02:00
bih . compression = 0 ;
bih . sizeimage = 0 ;
bih . xpels = 0 ;
bih . ypels = 0 ;
bih . clrused = 0 ;
bih . clrimp = 0 ;
2009-11-01 21:54:21 +01:00
/* Write file header and info header */
if ( fwrite ( & bfh , sizeof ( bfh ) , 1 , f ) ! = 1 | | fwrite ( & bih , sizeof ( bih ) , 1 , f ) ! = 1 ) {
fclose ( f ) ;
return false ;
}
2007-06-12 22:24:12 +02:00
if ( pixelformat = = 8 ) {
2009-11-01 21:54:21 +01:00
/* Convert the palette to the windows format */
RgbQuad rq [ 256 ] ;
for ( uint i = 0 ; i < 256 ; i + + ) {
2007-06-12 22:24:12 +02:00
rq [ i ] . red = palette [ i ] . r ;
rq [ i ] . green = palette [ i ] . g ;
rq [ i ] . blue = palette [ i ] . b ;
rq [ i ] . reserved = 0 ;
}
2009-11-01 21:54:21 +01:00
/* Write the palette */
if ( fwrite ( rq , sizeof ( rq ) , 1 , f ) ! = 1 ) {
fclose ( f ) ;
return false ;
}
2004-08-09 19:04:08 +02:00
}
2009-11-01 21:54:21 +01:00
/* Try to use 64k of memory, store between 16 and 128 lines */
uint maxlines = Clamp ( 65536 / ( w * pixelformat / 8 ) , 16 , 128 ) ; // number of lines per iteration
2004-08-09 19:04:08 +02:00
2023-05-08 19:01:06 +02:00
uint8_t * buff = MallocT < uint8_t > ( maxlines * w * pixelformat / 8 ) ; // buffer which is rendered to
uint8_t * line = CallocT < uint8_t > ( bytewidth ) ; // one line, stored to file
2004-08-09 19:04:08 +02:00
2009-11-01 21:54:21 +01:00
/* Start at the bottom, since bitmaps are stored bottom up */
2004-08-09 19:04:08 +02:00
do {
2021-01-08 11:16:18 +01:00
uint n = std : : min ( h , maxlines ) ;
2004-08-09 19:04:08 +02:00
h - = n ;
2004-09-10 21:02:27 +02:00
2009-11-01 21:54:21 +01:00
/* Render the pixels */
callb ( userdata , buff , h , w , n ) ;
/* Write each line */
while ( n - - ! = 0 ) {
if ( pixelformat = = 8 ) {
/* Move to 'line', leave last few pixels in line zeroed */
memcpy ( line , buff + n * w , w ) ;
} else {
/* Convert from 'native' 32bpp to BMP-like 24bpp.
* Works for both big and little endian machines */
Colour * src = ( ( Colour * ) buff ) + n * w ;
2024-03-16 23:59:32 +01:00
uint8_t * dst = line ;
2009-11-01 21:54:21 +01:00
for ( uint i = 0 ; i < w ; i + + ) {
2009-12-31 08:39:05 +01:00
dst [ i * 3 ] = src [ i ] . b ;
dst [ i * 3 + 1 ] = src [ i ] . g ;
dst [ i * 3 + 2 ] = src [ i ] . r ;
2009-11-01 21:54:21 +01:00
}
}
/* Write to file */
if ( fwrite ( line , bytewidth , 1 , f ) ! = 1 ) {
2021-05-01 22:06:17 +02:00
free ( line ) ;
2004-11-15 20:25:59 +01:00
free ( buff ) ;
fclose ( f ) ;
return false ;
}
2009-11-01 21:54:21 +01:00
}
2004-11-15 20:25:59 +01:00
} while ( h ! = 0 ) ;
2004-08-09 19:04:08 +02:00
2021-05-01 22:06:17 +02:00
free ( line ) ;
2004-11-15 20:25:59 +01:00
free ( buff ) ;
2004-08-09 19:04:08 +02:00
fclose ( f ) ;
return true ;
}
2009-03-15 01:32:18 +01:00
/*********************************************************
* * * * SCREENSHOT CODE FOR PORTABLE NETWORK GRAPHICS ( . PNG )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-08-09 19:04:08 +02:00
# if defined(WITH_PNG)
# include <png.h>
2010-12-20 23:54:15 +01:00
# ifdef PNG_TEXT_SUPPORTED
# include "rev.h"
# include "newgrf_config.h"
# include "ai/ai_info.hpp"
# include "company_base.h"
2010-12-21 11:11:11 +01:00
# include "base_media_base.h"
2010-12-20 23:54:15 +01:00
# endif /* PNG_TEXT_SUPPORTED */
2004-08-09 19:04:08 +02:00
static void PNGAPI png_my_error ( png_structp png_ptr , png_const_charp message )
{
2021-06-12 09:10:17 +02:00
Debug ( misc , 0 , " [libpng] error: {} - {} " , message , ( const char * ) png_get_error_ptr ( png_ptr ) ) ;
2010-01-08 04:17:12 +01:00
longjmp ( png_jmpbuf ( png_ptr ) , 1 ) ;
2004-08-09 19:04:08 +02:00
}
static void PNGAPI png_my_warning ( png_structp png_ptr , png_const_charp message )
{
2021-06-12 09:10:17 +02:00
Debug ( misc , 1 , " [libpng] warning: {} - {} " , message , ( const char * ) png_get_error_ptr ( png_ptr ) ) ;
2004-08-09 19:04:08 +02:00
}
2011-05-22 22:18:38 +02:00
/**
* Generic . PNG file image writer .
* @ param name Filename , including extension .
* @ param callb Callback function for generating lines of pixels .
2012-07-10 20:37:54 +02:00
* @ param userdata User data , passed on to \ a callb .
2011-05-22 22:18:38 +02:00
* @ param w Width of the image in pixels .
* @ param h Height of the image in pixels .
* @ param pixelformat Bits per pixel ( bpp ) , either 8 or 32.
* @ param palette % Colour palette ( for 8 bpp images ) .
* @ return File was written successfully .
* @ see ScreenshotHandlerProc
*/
2009-11-14 16:28:57 +01:00
static bool MakePNGImage ( const char * name , ScreenshotCallback * callb , void * userdata , uint w , uint h , int pixelformat , const Colour * palette )
2004-08-09 19:04:08 +02:00
{
2004-09-27 14:36:59 +02:00
png_color rq [ 256 ] ;
2004-08-09 19:04:08 +02:00
FILE * f ;
uint i , y , n ;
uint maxlines ;
2007-06-12 22:24:12 +02:00
uint bpp = pixelformat / 8 ;
2004-08-09 19:04:08 +02:00
png_structp png_ptr ;
png_infop info_ptr ;
2007-06-12 22:24:12 +02:00
/* only implemented for 8bit and 32bit images so far. */
if ( pixelformat ! = 8 & & pixelformat ! = 32 ) return false ;
2004-08-09 19:04:08 +02:00
2006-11-28 15:42:31 +01:00
f = fopen ( name , " wb " ) ;
2019-04-10 23:07:06 +02:00
if ( f = = nullptr ) return false ;
2004-08-09 19:04:08 +02:00
2011-11-12 09:37:12 +01:00
png_ptr = png_create_write_struct ( PNG_LIBPNG_VER_STRING , const_cast < char * > ( name ) , png_my_error , png_my_warning ) ;
2004-08-09 19:04:08 +02:00
2019-04-10 23:07:06 +02:00
if ( png_ptr = = nullptr ) {
2004-08-09 19:04:08 +02:00
fclose ( f ) ;
return false ;
}
info_ptr = png_create_info_struct ( png_ptr ) ;
2019-04-10 23:07:06 +02:00
if ( info_ptr = = nullptr ) {
png_destroy_write_struct ( & png_ptr , ( png_infopp ) nullptr ) ;
2004-08-09 19:04:08 +02:00
fclose ( f ) ;
return false ;
}
if ( setjmp ( png_jmpbuf ( png_ptr ) ) ) {
png_destroy_write_struct ( & png_ptr , & info_ptr ) ;
fclose ( f ) ;
return false ;
}
png_init_io ( png_ptr , f ) ;
2004-09-10 21:02:27 +02:00
2004-08-09 19:04:08 +02:00
png_set_filter ( png_ptr , 0 , PNG_FILTER_NONE ) ;
2007-06-12 22:24:12 +02:00
png_set_IHDR ( png_ptr , info_ptr , w , h , 8 , pixelformat = = 8 ? PNG_COLOR_TYPE_PALETTE : PNG_COLOR_TYPE_RGB ,
2004-11-15 20:25:59 +01:00
PNG_INTERLACE_NONE , PNG_COMPRESSION_TYPE_DEFAULT , PNG_FILTER_TYPE_DEFAULT ) ;
2004-08-09 19:04:08 +02:00
2010-12-20 23:54:15 +01:00
# ifdef PNG_TEXT_SUPPORTED
/* Try to add some game metadata to the PNG screenshot so
* it ' s more useful for debugging and archival purposes . */
png_text_struct text [ 2 ] ;
memset ( text , 0 , sizeof ( text ) ) ;
text [ 0 ] . key = const_cast < char * > ( " Software " ) ;
text [ 0 ] . text = const_cast < char * > ( _openttd_revision ) ;
text [ 0 ] . text_length = strlen ( _openttd_revision ) ;
text [ 0 ] . compression = PNG_TEXT_COMPRESSION_NONE ;
2023-04-27 20:59:34 +02:00
std : : string message ;
message . reserve ( 1024 ) ;
fmt : : format_to ( std : : back_inserter ( message ) , " Graphics set: {} ({}) \n " , BaseGraphics : : GetUsedSet ( ) - > name , BaseGraphics : : GetUsedSet ( ) - > version ) ;
message + = " NewGRFs: \n " ;
2019-04-10 23:07:06 +02:00
for ( const GRFConfig * c = _game_mode = = GM_MENU ? nullptr : _grfconfig ; c ! = nullptr ; c = c - > next ) {
2023-05-18 22:41:42 +02:00
fmt : : format_to ( std : : back_inserter ( message ) , " {:08X} {} {} \n " , BSWAP32 ( c - > ident . grfid ) , FormatArrayAsHex ( c - > ident . md5sum ) , c - > filename ) ;
2010-12-20 23:54:15 +01:00
}
2023-04-27 20:59:34 +02:00
message + = " \n Companies: \n " ;
2019-12-14 17:22:38 +01:00
for ( const Company * c : Company : : Iterate ( ) ) {
2019-04-10 23:07:06 +02:00
if ( c - > ai_info = = nullptr ) {
2023-04-27 20:59:34 +02:00
fmt : : format_to ( std : : back_inserter ( message ) , " {:2d}: Human \n " , ( int ) c - > index ) ;
2010-12-20 23:54:15 +01:00
} else {
2023-04-27 20:59:34 +02:00
fmt : : format_to ( std : : back_inserter ( message ) , " {:2d}: {} (v{}) \n " , ( int ) c - > index , c - > ai_info - > GetName ( ) , c - > ai_info - > GetVersion ( ) ) ;
2010-12-20 23:54:15 +01:00
}
}
text [ 1 ] . key = const_cast < char * > ( " Description " ) ;
2023-04-27 20:59:34 +02:00
text [ 1 ] . text = const_cast < char * > ( message . c_str ( ) ) ;
text [ 1 ] . text_length = message . size ( ) ;
2010-12-20 23:54:15 +01:00
text [ 1 ] . compression = PNG_TEXT_COMPRESSION_zTXt ;
png_set_text ( png_ptr , info_ptr , text , 2 ) ;
# endif /* PNG_TEXT_SUPPORTED */
2007-06-12 22:24:12 +02:00
if ( pixelformat = = 8 ) {
/* convert the palette to the .PNG format. */
for ( i = 0 ; i ! = 256 ; i + + ) {
rq [ i ] . red = palette [ i ] . r ;
rq [ i ] . green = palette [ i ] . g ;
rq [ i ] . blue = palette [ i ] . b ;
}
png_set_PLTE ( png_ptr , info_ptr , rq , 256 ) ;
2004-08-09 19:04:08 +02:00
}
png_write_info ( png_ptr , info_ptr ) ;
png_set_flush ( png_ptr , 512 ) ;
2007-06-12 22:24:12 +02:00
if ( pixelformat = = 32 ) {
png_color_8 sig_bit ;
2009-02-09 03:57:15 +01:00
/* Save exact colour/alpha resolution */
2007-06-12 22:24:12 +02:00
sig_bit . alpha = 0 ;
sig_bit . blue = 8 ;
sig_bit . green = 8 ;
sig_bit . red = 8 ;
sig_bit . gray = 8 ;
png_set_sBIT ( png_ptr , info_ptr , & sig_bit ) ;
2008-06-17 21:38:00 +02:00
# if TTD_ENDIAN == TTD_LITTLE_ENDIAN
2007-06-12 22:24:12 +02:00
png_set_bgr ( png_ptr ) ;
png_set_filler ( png_ptr , 0 , PNG_FILLER_AFTER ) ;
# else
png_set_filler ( png_ptr , 0 , PNG_FILLER_BEFORE ) ;
2008-06-17 21:38:00 +02:00
# endif /* TTD_ENDIAN == TTD_LITTLE_ENDIAN */
2007-06-12 22:24:12 +02:00
}
2007-04-04 03:35:16 +02:00
/* use by default 64k temp memory */
2007-11-19 19:38:10 +01:00
maxlines = Clamp ( 65536 / w , 16 , 128 ) ;
2004-08-09 19:04:08 +02:00
2007-04-04 03:35:16 +02:00
/* now generate the bitmap bits */
2023-05-08 19:01:06 +02:00
void * buff = CallocT < uint8_t > ( static_cast < size_t > ( w ) * maxlines * bpp ) ; // by default generate 128 lines at a time.
2004-08-09 19:04:08 +02:00
y = 0 ;
do {
2007-04-04 03:35:16 +02:00
/* determine # lines to write */
2021-01-08 11:16:18 +01:00
n = std : : min ( h - y , maxlines ) ;
2004-09-10 21:02:27 +02:00
2007-04-04 03:35:16 +02:00
/* render the pixels into the buffer */
2004-08-09 19:04:08 +02:00
callb ( userdata , buff , y , w , n ) ;
y + = n ;
2007-04-04 03:35:16 +02:00
/* write them to png */
2010-07-24 12:14:39 +02:00
for ( i = 0 ; i ! = n ; i + + ) {
2007-06-12 22:24:12 +02:00
png_write_row ( png_ptr , ( png_bytep ) buff + i * w * bpp ) ;
2010-07-24 12:14:39 +02:00
}
2004-08-09 19:04:08 +02:00
} while ( y ! = h ) ;
png_write_end ( png_ptr , info_ptr ) ;
png_destroy_write_struct ( & png_ptr , & info_ptr ) ;
2004-11-15 20:25:59 +01:00
free ( buff ) ;
2004-08-09 19:04:08 +02:00
fclose ( f ) ;
return true ;
}
(svn r5946) -Add: merged the TGP branch to mainline. TGP adds:
- New optional landscape generator (TerraGenesis Perlin)
- Load heightmaps (either BMP or PNG)
- Progress dialog while generating worlds (no longer a 'hanging' screen)
- New dialogs for NewGame, Create Scenario and Play Heightmap
- Easier to configure your landscape
- More things to configure (tree-placer, ..)
- Speedup of world generation
- New console command 'restart': restart the map EXACTLY as it was when you
first started it (needs a game made after or with this commit)
- New console command 'getseed': get the seed of your map and share it with
others (of course only works with generated maps)
- Many new, world generation related, things
- Many internal cleanups and rewrites
Many tnx to those people who helped making this:
Belugas, DaleStan, glx, KUDr, RichK67, Rubidium, and TrueLight (alfabetic)
Many tnx to those who helped testing:
Arnau, Bjarni, and tokai (alfabetic)
And to all other people who helped testing and sending comments / bugs
Stats: 673 lines changed, 3534 new lines, 79 new strings
2006-08-19 12:00:30 +02:00
# endif /* WITH_PNG */
2004-08-09 19:04:08 +02:00
2009-03-15 01:32:18 +01:00
/*************************************************
* * * * SCREENSHOT CODE FOR ZSOFT PAINTBRUSH ( . PCX )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-08-09 19:04:08 +02:00
2011-05-22 22:18:38 +02:00
/** Definition of a PCX file header. */
2007-03-07 13:11:48 +01:00
struct PcxHeader {
2024-03-16 23:59:32 +01:00
uint8_t manufacturer ;
uint8_t version ;
uint8_t rle ;
uint8_t bpp ;
2023-05-08 19:01:06 +02:00
uint32_t unused ;
uint16_t xmax , ymax ;
uint16_t hdpi , vdpi ;
2024-03-16 23:59:32 +01:00
uint8_t pal_small [ 16 * 3 ] ;
uint8_t reserved ;
uint8_t planes ;
2023-05-08 19:01:06 +02:00
uint16_t pitch ;
uint16_t cpal ;
uint16_t width ;
uint16_t height ;
2024-03-16 23:59:32 +01:00
uint8_t filler [ 54 ] ;
2007-03-07 13:11:48 +01:00
} ;
2020-12-27 11:44:22 +01:00
static_assert ( sizeof ( PcxHeader ) = = 128 ) ;
2004-08-09 19:04:08 +02:00
2011-05-22 22:18:38 +02:00
/**
* Generic . PCX file image writer .
* @ param name Filename , including extension .
* @ param callb Callback function for generating lines of pixels .
2012-07-10 20:37:54 +02:00
* @ param userdata User data , passed on to \ a callb .
2011-05-22 22:18:38 +02:00
* @ param w Width of the image in pixels .
* @ param h Height of the image in pixels .
* @ param pixelformat Bits per pixel ( bpp ) , either 8 or 32.
* @ param palette % Colour palette ( for 8 bpp images ) .
* @ return File was written successfully .
* @ see ScreenshotHandlerProc
*/
2009-11-14 16:28:57 +01:00
static bool MakePCXImage ( const char * name , ScreenshotCallback * callb , void * userdata , uint w , uint h , int pixelformat , const Colour * palette )
2004-08-09 19:04:08 +02:00
{
FILE * f ;
uint maxlines ;
uint y ;
PcxHeader pcx ;
2006-06-13 19:13:46 +02:00
bool success ;
2004-08-09 19:04:08 +02:00
2007-06-12 22:24:12 +02:00
if ( pixelformat = = 32 ) {
2021-06-12 09:10:17 +02:00
Debug ( misc , 0 , " Can't convert a 32bpp screenshot to PCX format. Please pick another format. " ) ;
2007-06-12 22:24:12 +02:00
return false ;
}
2010-07-24 12:14:39 +02:00
if ( pixelformat ! = 8 | | w = = 0 ) return false ;
2004-08-09 19:04:08 +02:00
2006-11-28 15:42:31 +01:00
f = fopen ( name , " wb " ) ;
2019-04-10 23:07:06 +02:00
if ( f = = nullptr ) return false ;
2004-08-09 19:04:08 +02:00
memset ( & pcx , 0 , sizeof ( pcx ) ) ;
2004-09-10 21:02:27 +02:00
2007-04-04 03:35:16 +02:00
/* setup pcx header */
2004-08-09 19:04:08 +02:00
pcx . manufacturer = 10 ;
pcx . version = 5 ;
pcx . rle = 1 ;
pcx . bpp = 8 ;
2004-11-15 20:25:59 +01:00
pcx . xmax = TO_LE16 ( w - 1 ) ;
pcx . ymax = TO_LE16 ( h - 1 ) ;
2004-08-09 19:04:08 +02:00
pcx . hdpi = TO_LE16 ( 320 ) ;
pcx . vdpi = TO_LE16 ( 320 ) ;
pcx . planes = 1 ;
pcx . cpal = TO_LE16 ( 1 ) ;
pcx . width = pcx . pitch = TO_LE16 ( w ) ;
pcx . height = TO_LE16 ( h ) ;
2004-09-10 21:02:27 +02:00
2007-04-04 03:35:16 +02:00
/* write pcx header */
2004-11-15 20:25:59 +01:00
if ( fwrite ( & pcx , sizeof ( pcx ) , 1 , f ) ! = 1 ) {
fclose ( f ) ;
return false ;
}
2004-08-09 19:04:08 +02:00
2007-04-04 03:35:16 +02:00
/* use by default 64k temp memory */
2007-11-19 19:38:10 +01:00
maxlines = Clamp ( 65536 / w , 16 , 128 ) ;
2004-08-09 19:04:08 +02:00
2007-04-04 03:35:16 +02:00
/* now generate the bitmap bits */
2023-05-08 19:01:06 +02:00
uint8_t * buff = CallocT < uint8_t > ( static_cast < size_t > ( w ) * maxlines ) ; // by default generate 128 lines at a time.
2004-08-09 19:04:08 +02:00
y = 0 ;
do {
2007-04-04 03:35:16 +02:00
/* determine # lines to write */
2021-01-08 11:16:18 +01:00
uint n = std : : min ( h - y , maxlines ) ;
2004-11-15 20:25:59 +01:00
uint i ;
2004-09-10 21:02:27 +02:00
2007-04-04 03:35:16 +02:00
/* render the pixels into the buffer */
2004-08-09 19:04:08 +02:00
callb ( userdata , buff , y , w , n ) ;
y + = n ;
2007-04-04 03:35:16 +02:00
/* write them to pcx */
2004-11-15 20:25:59 +01:00
for ( i = 0 ; i ! = n ; i + + ) {
2023-05-08 19:01:06 +02:00
const uint8_t * bufp = buff + i * w ;
2024-03-16 23:59:32 +01:00
uint8_t runchar = bufp [ 0 ] ;
2005-07-03 12:22:20 +02:00
uint runcount = 1 ;
uint j ;
2004-08-09 19:04:08 +02:00
2007-04-04 03:35:16 +02:00
/* for each pixel... */
2005-07-03 12:22:20 +02:00
for ( j = 1 ; j < w ; j + + ) {
2023-05-08 19:01:06 +02:00
uint8_t ch = bufp [ j ] ;
2005-07-03 12:22:20 +02:00
2004-08-09 19:04:08 +02:00
if ( ch ! = runchar | | runcount > = 0x3f ) {
2010-07-24 12:14:39 +02:00
if ( runcount > 1 | | ( runchar & 0xC0 ) = = 0xC0 ) {
2004-11-15 20:25:59 +01:00
if ( fputc ( 0xC0 | runcount , f ) = = EOF ) {
free ( buff ) ;
fclose ( f ) ;
return false ;
}
2010-07-24 12:14:39 +02:00
}
2004-11-15 20:25:59 +01:00
if ( fputc ( runchar , f ) = = EOF ) {
free ( buff ) ;
fclose ( f ) ;
return false ;
}
2004-08-09 19:04:08 +02:00
runcount = 0 ;
runchar = ch ;
}
runcount + + ;
}
2007-04-04 03:35:16 +02:00
/* write remaining bytes.. */
2010-07-24 12:14:39 +02:00
if ( runcount > 1 | | ( runchar & 0xC0 ) = = 0xC0 ) {
2004-11-15 20:25:59 +01:00
if ( fputc ( 0xC0 | runcount , f ) = = EOF ) {
free ( buff ) ;
fclose ( f ) ;
return false ;
}
2010-07-24 12:14:39 +02:00
}
2004-11-15 20:25:59 +01:00
if ( fputc ( runchar , f ) = = EOF ) {
free ( buff ) ;
fclose ( f ) ;
return false ;
}
2004-08-09 19:04:08 +02:00
}
} while ( y ! = h ) ;
2004-11-15 20:25:59 +01:00
free ( buff ) ;
2009-02-09 03:57:15 +01:00
/* write 8-bit colour palette */
2004-11-15 20:25:59 +01:00
if ( fputc ( 12 , f ) = = EOF ) {
fclose ( f ) ;
return false ;
}
2005-06-30 07:27:32 +02:00
2008-06-18 23:19:04 +02:00
/* Palette is word-aligned, copy it to a temporary byte array */
2024-03-16 23:59:32 +01:00
uint8_t tmp [ 256 * 3 ] ;
2006-06-28 07:47:55 +02:00
2008-06-18 23:19:04 +02:00
for ( uint i = 0 ; i < 256 ; i + + ) {
tmp [ i * 3 + 0 ] = palette [ i ] . r ;
tmp [ i * 3 + 1 ] = palette [ i ] . g ;
tmp [ i * 3 + 2 ] = palette [ i ] . b ;
2004-11-15 20:25:59 +01:00
}
2008-06-18 23:19:04 +02:00
success = fwrite ( tmp , sizeof ( tmp ) , 1 , f ) = = 1 ;
2006-06-13 19:13:46 +02:00
2004-08-09 19:04:08 +02:00
fclose ( f ) ;
2006-06-13 19:13:46 +02:00
return success ;
2004-08-09 19:04:08 +02:00
}
2009-03-15 01:32:18 +01:00
/*************************************************
* * * * GENERIC SCREENSHOT CODE
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-08-09 19:04:08 +02:00
2011-05-22 22:18:38 +02:00
/** Available screenshot formats. */
2004-08-09 19:04:08 +02:00
static const ScreenshotFormat _screenshot_formats [ ] = {
# if defined(WITH_PNG)
2014-04-27 17:13:46 +02:00
{ " png " , & MakePNGImage } ,
2004-08-09 19:04:08 +02:00
# endif
2014-04-27 17:13:46 +02:00
{ " bmp " , & MakeBMPImage } ,
{ " pcx " , & MakePCXImage } ,
2004-08-09 19:04:08 +02:00
} ;
2024-04-20 15:34:30 +02:00
/* The currently loaded screenshot format. Set to a valid value as it might be used in early crash logs, when InitializeScreenshotFormats has not been called yet. */
static const ScreenshotFormat * _cur_screenshot_format = std : : begin ( _screenshot_formats ) ;
2011-05-28 15:55:05 +02:00
/** Get filename extension of current screenshot file format. */
const char * GetCurrentScreenshotExtension ( )
{
2024-04-20 15:34:30 +02:00
return _cur_screenshot_format - > extension ;
2011-05-28 15:55:05 +02:00
}
2011-05-22 22:18:38 +02:00
/** Initialize screenshot format information on startup, with #_screenshot_format_name filled from the loadsave code. */
2007-03-07 12:47:46 +01:00
void InitializeScreenshotFormats ( )
2004-08-09 19:04:08 +02:00
{
2024-04-20 15:34:30 +02:00
for ( auto & format : _screenshot_formats ) {
if ( _screenshot_format_name = = format . extension ) {
_cur_screenshot_format = & format ;
return ;
2004-11-15 20:25:59 +01:00
}
2010-07-01 00:17:53 +02:00
}
2024-04-20 15:34:30 +02:00
_cur_screenshot_format = std : : begin ( _screenshot_formats ) ;
2004-08-09 19:04:08 +02:00
}
2011-05-22 22:18:38 +02:00
/**
* Callback of the screenshot generator that dumps the current video buffer .
* @ see ScreenshotCallback
*/
2023-09-16 22:20:53 +02:00
static void CurrentScreenCallback ( void * , void * buf , uint y , uint pitch , uint n )
2004-08-09 19:04:08 +02:00
{
2014-01-02 23:41:58 +01:00
Blitter * blitter = BlitterFactory : : GetCurrentBlitter ( ) ;
2007-06-17 22:30:28 +02:00
void * src = blitter - > MoveTo ( _screen . dst_ptr , 0 , y ) ;
2007-06-21 14:36:46 +02:00
blitter - > CopyImageToBuffer ( src , buf , _screen . width , n , pitch ) ;
2004-08-09 19:04:08 +02:00
}
2010-08-01 21:22:34 +02:00
/**
* generate a large piece of the world
2008-01-11 18:12:41 +01:00
* @ param userdata Viewport area to draw
* @ param buf Videobuffer with same bitdepth as current blitter
* @ param y First line to render
* @ param pitch Pitch of the videobuffer
* @ param n Number of lines to render
*/
2007-06-12 22:24:12 +02:00
static void LargeWorldCallback ( void * userdata , void * buf , uint y , uint pitch , uint n )
2004-08-09 19:04:08 +02:00
{
2020-06-29 03:38:29 +02:00
Viewport * vp = ( Viewport * ) userdata ;
2023-01-26 19:21:36 +01:00
DrawPixelInfo dpi ;
2004-08-09 19:04:08 +02:00
int wx , left ;
2008-01-11 18:12:41 +01:00
/* We are no longer rendering to the screen */
DrawPixelInfo old_screen = _screen ;
bool old_disable_anim = _screen_disable_anim ;
_screen . dst_ptr = buf ;
_screen . width = pitch ;
_screen . height = n ;
_screen . pitch = pitch ;
_screen_disable_anim = true ;
2023-01-26 19:21:36 +01:00
AutoRestoreBackup dpi_backup ( _cur_dpi , & dpi ) ;
2004-08-09 19:04:08 +02:00
dpi . dst_ptr = buf ;
dpi . height = n ;
dpi . width = vp - > width ;
dpi . pitch = pitch ;
2007-05-15 16:08:39 +02:00
dpi . zoom = ZOOM_LVL_WORLD_SCREENSHOT ;
2004-08-09 19:04:08 +02:00
dpi . left = 0 ;
dpi . top = y ;
2008-01-11 18:12:41 +01:00
/* Render viewport in blocks of 1600 pixels width */
2004-08-09 19:04:08 +02:00
left = 0 ;
while ( vp - > width - left ! = 0 ) {
2021-01-08 11:16:18 +01:00
wx = std : : min ( vp - > width - left , 1600 ) ;
2004-08-09 19:04:08 +02:00
left + = wx ;
2004-09-10 21:02:27 +02:00
ViewportDoDraw ( vp ,
2007-05-15 18:08:46 +02:00
ScaleByZoom ( left - wx - vp - > left , vp - > zoom ) + vp - > virtual_left ,
ScaleByZoom ( y - vp - > top , vp - > zoom ) + vp - > virtual_top ,
ScaleByZoom ( left - vp - > left , vp - > zoom ) + vp - > virtual_left ,
ScaleByZoom ( ( y + n ) - vp - > top , vp - > zoom ) + vp - > virtual_top
2004-08-09 19:04:08 +02:00
) ;
}
2008-01-11 18:12:41 +01:00
/* Switch back to rendering to the screen */
_screen = old_screen ;
_screen_disable_anim = old_disable_anim ;
2004-08-09 19:04:08 +02:00
}
2011-05-22 22:18:38 +02:00
/**
* Construct a pathname for a screenshot file .
2011-05-28 15:53:52 +02:00
* @ param default_fn Default filename .
* @ param ext Extension to use .
2012-12-09 17:52:43 +01:00
* @ param crashlog Create path for crash . png
2011-05-22 22:18:38 +02:00
* @ return Pathname for a screenshot file .
*/
2012-12-09 17:52:43 +01:00
static const char * MakeScreenshotName ( const char * default_fn , const char * ext , bool crashlog = false )
2004-08-09 19:04:08 +02:00
{
2023-05-25 18:23:26 +02:00
bool generate = _screenshot_name . empty ( ) ;
2009-11-14 16:50:38 +01:00
if ( generate ) {
2009-11-01 19:15:35 +01:00
if ( _game_mode = = GM_EDITOR | | _game_mode = = GM_MENU | | _local_company = = COMPANY_SPECTATOR ) {
2023-05-25 18:23:26 +02:00
_screenshot_name = default_fn ;
2009-11-01 19:15:35 +01:00
} else {
2023-05-25 18:23:26 +02:00
_screenshot_name = GenerateDefaultSaveName ( ) ;
2009-11-01 19:15:35 +01:00
}
2004-08-09 19:04:08 +02:00
}
2022-11-08 12:13:16 +01:00
/* Handle user-specified filenames ending in # with automatic numbering */
2024-01-17 06:30:44 +01:00
if ( _screenshot_name . ends_with ( " # " ) ) {
2022-11-08 12:13:16 +01:00
generate = true ;
2023-05-25 18:23:26 +02:00
_screenshot_name . pop_back ( ) ;
2022-11-08 12:13:16 +01:00
}
2023-05-25 18:23:26 +02:00
size_t len = _screenshot_name . size ( ) ;
2022-11-08 12:13:16 +01:00
/* Add extension to screenshot file */
2023-05-25 18:23:26 +02:00
_screenshot_name + = fmt : : format ( " .{} " , ext ) ;
2007-06-17 17:48:57 +02:00
2020-12-06 21:11:42 +01:00
const char * screenshot_dir = crashlog ? _personal_dir . c_str ( ) : FiosGetScreenshotDir ( ) ;
2012-12-09 17:52:43 +01:00
2009-11-01 19:15:35 +01:00
for ( uint serial = 1 ; ; serial + + ) {
2023-05-25 18:23:26 +02:00
_full_screenshot_path = fmt : : format ( " {}{} " , screenshot_dir , _screenshot_name ) ;
2009-11-14 16:50:38 +01:00
if ( ! generate ) break ; // allow overwriting of non-automatic filenames
2023-05-25 18:23:26 +02:00
if ( ! FileExists ( _full_screenshot_path ) ) break ;
2007-06-17 17:48:57 +02:00
/* If file exists try another one with same name, but just with a higher index */
2023-05-25 18:23:26 +02:00
_screenshot_name . erase ( len ) ;
_screenshot_name + = fmt : : format ( " #{}.{} " , serial , ext ) ;
2004-08-09 19:04:08 +02:00
}
2023-05-25 18:23:26 +02:00
return _full_screenshot_path . c_str ( ) ;
2004-08-09 19:04:08 +02:00
}
2010-08-16 01:32:36 +02:00
/** Make a screenshot of the current screen. */
2012-12-09 17:52:43 +01:00
static bool MakeSmallScreenshot ( bool crashlog )
2004-08-09 19:04:08 +02:00
{
2024-04-20 15:34:30 +02:00
return _cur_screenshot_format - > proc ( MakeScreenshotName ( SCREENSHOT_NAME , _cur_screenshot_format - > extension , crashlog ) , CurrentScreenCallback , nullptr , _screen . width , _screen . height ,
2014-01-02 23:41:58 +01:00
BlitterFactory : : GetCurrentBlitter ( ) - > GetScreenDepth ( ) , _cur_palette . palette ) ;
2004-08-09 19:04:08 +02:00
}
2012-12-09 17:53:01 +01:00
/**
2020-06-29 03:38:29 +02:00
* Configure a Viewport for rendering ( a part of ) the map into a screenshot .
2012-12-09 17:53:01 +01:00
* @ param t Screenshot type
2021-01-22 16:16:33 +01:00
* @ param width the width of the screenshot , or 0 for current viewport width ( needs to be 0 with SC_VIEWPORT , SC_CRASHLOG , and SC_WORLD ) .
* @ param height the height of the screenshot , or 0 for current viewport height ( needs to be 0 with SC_VIEWPORT , SC_CRASHLOG , and SC_WORLD ) .
2018-10-28 03:17:36 +01:00
* @ param [ out ] vp Result viewport
2012-12-09 17:53:01 +01:00
*/
2023-05-08 19:01:06 +02:00
void SetupScreenshotViewport ( ScreenshotType t , Viewport * vp , uint32_t width , uint32_t height )
2010-08-16 01:32:36 +02:00
{
2020-01-26 15:48:35 +01:00
switch ( t ) {
case SC_VIEWPORT :
case SC_CRASHLOG : {
2021-01-22 16:16:33 +01:00
assert ( width = = 0 & & height = = 0 ) ;
2023-01-06 23:24:38 +01:00
Window * w = GetMainWindow ( ) ;
2020-01-26 15:48:35 +01:00
vp - > virtual_left = w - > viewport - > virtual_left ;
vp - > virtual_top = w - > viewport - > virtual_top ;
vp - > virtual_width = w - > viewport - > virtual_width ;
vp - > virtual_height = w - > viewport - > virtual_height ;
/* Compute pixel coordinates */
vp - > left = 0 ;
vp - > top = 0 ;
2021-01-22 16:16:33 +01:00
vp - > width = _screen . width ;
2020-01-26 15:48:35 +01:00
vp - > height = _screen . height ;
vp - > overlay = w - > viewport - > overlay ;
break ;
}
case SC_WORLD : {
2021-01-22 16:16:33 +01:00
assert ( width = = 0 & & height = = 0 ) ;
2020-01-26 15:48:35 +01:00
/* Determine world coordinates of screenshot */
vp - > zoom = ZOOM_LVL_WORLD_SCREENSHOT ;
TileIndex north_tile = _settings_game . construction . freeform_edges ? TileXY ( 1 , 1 ) : TileXY ( 0 , 0 ) ;
2023-01-21 10:43:03 +01:00
TileIndex south_tile = Map : : Size ( ) - 1 ;
2020-01-26 15:48:35 +01:00
/* We need to account for a hill or high building at tile 0,0. */
int extra_height_top = TilePixelHeight ( north_tile ) + 150 ;
/* If there is a hill at the bottom don't create a large black area. */
int reclaim_height_bottom = TilePixelHeight ( south_tile ) ;
vp - > virtual_left = RemapCoords ( TileX ( south_tile ) * TILE_SIZE , TileY ( north_tile ) * TILE_SIZE , 0 ) . x ;
vp - > virtual_top = RemapCoords ( TileX ( north_tile ) * TILE_SIZE , TileY ( north_tile ) * TILE_SIZE , extra_height_top ) . y ;
vp - > virtual_width = RemapCoords ( TileX ( north_tile ) * TILE_SIZE , TileY ( south_tile ) * TILE_SIZE , 0 ) . x - vp - > virtual_left + 1 ;
vp - > virtual_height = RemapCoords ( TileX ( south_tile ) * TILE_SIZE , TileY ( south_tile ) * TILE_SIZE , reclaim_height_bottom ) . y - vp - > virtual_top + 1 ;
/* Compute pixel coordinates */
vp - > left = 0 ;
vp - > top = 0 ;
vp - > width = UnScaleByZoom ( vp - > virtual_width , vp - > zoom ) ;
vp - > height = UnScaleByZoom ( vp - > virtual_height , vp - > zoom ) ;
vp - > overlay = nullptr ;
break ;
}
default : {
vp - > zoom = ( t = = SC_ZOOMEDIN ) ? _settings_client . gui . zoom_min : ZOOM_LVL_VIEWPORT ;
2023-01-06 23:24:38 +01:00
Window * w = GetMainWindow ( ) ;
2020-01-26 15:48:35 +01:00
vp - > virtual_left = w - > viewport - > virtual_left ;
vp - > virtual_top = w - > viewport - > virtual_top ;
2021-01-22 16:16:33 +01:00
if ( width = = 0 | | height = = 0 ) {
vp - > virtual_width = w - > viewport - > virtual_width ;
vp - > virtual_height = w - > viewport - > virtual_height ;
} else {
vp - > virtual_width = width < < vp - > zoom ;
vp - > virtual_height = height < < vp - > zoom ;
}
2020-01-26 15:48:35 +01:00
/* Compute pixel coordinates */
vp - > left = 0 ;
vp - > top = 0 ;
vp - > width = UnScaleByZoom ( vp - > virtual_width , vp - > zoom ) ;
vp - > height = UnScaleByZoom ( vp - > virtual_height , vp - > zoom ) ;
vp - > overlay = nullptr ;
break ;
}
2012-12-09 17:53:01 +01:00
}
2010-08-16 01:32:36 +02:00
}
2012-12-09 17:53:01 +01:00
/**
* Make a screenshot of the map .
* @ param t Screenshot type : World or viewport screenshot
2021-01-22 16:16:33 +01:00
* @ param width the width of the screenshot of , or 0 for current viewport width .
* @ param height the height of the screenshot of , or 0 for current viewport height .
2012-12-09 17:53:01 +01:00
* @ return true on success
*/
2023-05-08 19:01:06 +02:00
static bool MakeLargeWorldScreenshot ( ScreenshotType t , uint32_t width = 0 , uint32_t height = 0 )
2004-08-09 19:04:08 +02:00
{
2020-06-29 03:38:29 +02:00
Viewport vp ;
2021-01-22 16:16:33 +01:00
SetupScreenshotViewport ( t , & vp , width , height ) ;
2012-12-09 17:53:01 +01:00
2024-04-20 15:34:30 +02:00
return _cur_screenshot_format - > proc ( MakeScreenshotName ( SCREENSHOT_NAME , _cur_screenshot_format - > extension ) , LargeWorldCallback , & vp , vp . width , vp . height ,
2014-01-02 23:41:58 +01:00
BlitterFactory : : GetCurrentBlitter ( ) - > GetScreenDepth ( ) , _cur_palette . palette ) ;
2004-08-09 19:04:08 +02:00
}
2006-07-28 23:51:00 +02:00
2011-05-28 15:54:18 +02:00
/**
* Callback for generating a heightmap . Supports 8 bpp grayscale only .
2018-10-28 03:17:36 +01:00
* @ param buffer Destination buffer .
2011-05-28 15:54:18 +02:00
* @ param y Line number of the first line to write .
* @ param n Number of lines to write .
* @ see ScreenshotCallback
*/
2023-09-16 22:20:53 +02:00
static void HeightmapCallback ( void * , void * buffer , uint y , uint , uint n )
2011-05-28 15:54:18 +02:00
{
2024-03-16 23:59:32 +01:00
uint8_t * buf = ( uint8_t * ) buffer ;
2011-05-28 15:54:18 +02:00
while ( n > 0 ) {
2023-01-21 10:43:03 +01:00
TileIndex ti = TileXY ( Map : : MaxX ( ) , y ) ;
for ( uint x = Map : : MaxX ( ) ; true ; x - - ) {
2021-03-25 23:51:37 +01:00
* buf = 256 * TileHeight ( ti ) / ( 1 + _heightmap_highest_peak ) ;
2011-05-28 15:54:18 +02:00
buf + + ;
if ( x = = 0 ) break ;
2024-03-10 14:51:08 +01:00
ti = TileAddXY ( ti , - 1 , 0 ) ;
2011-05-28 15:54:18 +02:00
}
y + + ;
n - - ;
}
}
/**
* Make a heightmap of the current map .
* @ param filename Filename to use for saving .
*/
bool MakeHeightmapScreenshot ( const char * filename )
{
Colour palette [ 256 ] ;
for ( uint i = 0 ; i < lengthof ( palette ) ; i + + ) {
palette [ i ] . a = 0xff ;
palette [ i ] . r = i ;
palette [ i ] . g = i ;
palette [ i ] . b = i ;
}
2021-03-25 23:51:37 +01:00
_heightmap_highest_peak = 0 ;
2023-01-21 10:43:03 +01:00
for ( TileIndex tile = 0 ; tile < Map : : Size ( ) ; tile + + ) {
2021-03-25 23:51:37 +01:00
uint h = TileHeight ( tile ) ;
_heightmap_highest_peak = std : : max ( h , _heightmap_highest_peak ) ;
}
2024-04-20 15:34:30 +02:00
return _cur_screenshot_format - > proc ( filename , HeightmapCallback , nullptr , Map : : SizeX ( ) , Map : : SizeY ( ) , 8 , palette ) ;
2011-05-28 15:54:18 +02:00
}
2020-06-17 23:50:22 +02:00
static ScreenshotType _confirmed_screenshot_type ; ///< Screenshot type the current query is about to confirm.
/**
* Callback on the confirmation window for huge screenshots .
* @ param confirmed true on confirmation
*/
2023-09-16 22:20:53 +02:00
static void ScreenshotConfirmationCallback ( Window * , bool confirmed )
2020-06-17 23:50:22 +02:00
{
2021-05-01 19:55:46 +02:00
if ( confirmed ) MakeScreenshot ( _confirmed_screenshot_type , { } ) ;
2020-06-17 23:50:22 +02:00
}
/**
2020-06-20 07:12:31 +02:00
* Make a screenshot .
* Ask for confirmation first if the screenshot will be huge .
2020-06-17 23:50:22 +02:00
* @ param t Screenshot type : World , defaultzoom , heightmap or viewport screenshot
* @ see MakeScreenshot
*/
2020-06-20 07:12:31 +02:00
void MakeScreenshotWithConfirm ( ScreenshotType t )
2020-06-17 23:50:22 +02:00
{
2020-06-29 03:38:29 +02:00
Viewport vp ;
2020-06-17 23:50:22 +02:00
SetupScreenshotViewport ( t , & vp ) ;
2020-06-23 16:20:02 +02:00
bool heightmap_or_minimap = t = = SC_HEIGHTMAP | | t = = SC_MINIMAP ;
2023-01-21 10:43:03 +01:00
uint64_t width = ( heightmap_or_minimap ? Map : : SizeX ( ) : vp . width ) ;
uint64_t height = ( heightmap_or_minimap ? Map : : SizeY ( ) : vp . height ) ;
2020-06-23 16:20:02 +02:00
if ( width * height > 8192 * 8192 ) {
2020-06-17 23:50:22 +02:00
/* Ask for confirmation */
_confirmed_screenshot_type = t ;
2020-06-23 16:20:02 +02:00
SetDParam ( 0 , width ) ;
SetDParam ( 1 , height ) ;
2020-06-17 23:50:22 +02:00
ShowQuery ( STR_WARNING_SCREENSHOT_SIZE_CAPTION , STR_WARNING_SCREENSHOT_SIZE_MESSAGE , nullptr , ScreenshotConfirmationCallback ) ;
} else {
/* Less than 64M pixels, just do it */
2021-05-01 19:55:46 +02:00
MakeScreenshot ( t , { } ) ;
2020-06-17 23:50:22 +02:00
}
}
2009-12-19 20:21:37 +01:00
/**
2020-06-17 23:50:22 +02:00
* Make a screenshot .
2009-12-19 20:21:37 +01:00
* @ param t the type of screenshot to make .
* @ param name the name to give to the screenshot .
2021-01-22 16:16:33 +01:00
* @ param width the width of the screenshot of , or 0 for current viewport width ( only works for SC_ZOOMEDIN and SC_DEFAULTZOOM ) .
* @ param height the height of the screenshot of , or 0 for current viewport height ( only works for SC_ZOOMEDIN and SC_DEFAULTZOOM ) .
2013-01-08 23:46:42 +01:00
* @ return true iff the screenshot was made successfully
2009-12-19 20:21:37 +01:00
*/
2023-05-08 19:01:06 +02:00
static bool RealMakeScreenshot ( ScreenshotType t , std : : string name , uint32_t width , uint32_t height )
2006-07-28 23:51:00 +02:00
{
2009-12-25 20:16:58 +01:00
if ( t = = SC_VIEWPORT ) {
/* First draw the dirty parts of the screen and only then change the name
* of the screenshot . This way the screenshot will always show the name
2010-07-31 23:02:56 +02:00
* of the previous screenshot in the ' successful ' message instead of the
2009-12-25 20:16:58 +01:00
* name of the new screenshot ( or an empty name ) . */
2021-11-07 17:41:24 +01:00
SetScreenshotWindowVisibility ( true ) ;
2009-12-25 20:16:58 +01:00
UndrawMouseCursor ( ) ;
DrawDirtyBlocks ( ) ;
2021-11-07 17:41:24 +01:00
SetScreenshotWindowVisibility ( false ) ;
2009-12-25 20:16:58 +01:00
}
2023-05-25 18:23:26 +02:00
_screenshot_name = name ;
2009-12-19 20:21:37 +01:00
bool ret ;
switch ( t ) {
2006-07-28 23:51:00 +02:00
case SC_VIEWPORT :
2012-12-09 17:52:43 +01:00
ret = MakeSmallScreenshot ( false ) ;
break ;
case SC_CRASHLOG :
ret = MakeSmallScreenshot ( true ) ;
2009-12-19 20:21:37 +01:00
break ;
2010-08-16 01:32:36 +02:00
case SC_ZOOMEDIN :
2011-12-31 15:56:39 +01:00
case SC_DEFAULTZOOM :
2021-01-22 16:16:33 +01:00
ret = MakeLargeWorldScreenshot ( t , width , height ) ;
break ;
2006-07-28 23:51:00 +02:00
case SC_WORLD :
2012-12-09 17:53:01 +01:00
ret = MakeLargeWorldScreenshot ( t ) ;
2009-12-19 20:21:37 +01:00
break ;
2011-05-28 15:54:18 +02:00
case SC_HEIGHTMAP : {
2024-04-20 15:34:30 +02:00
ret = MakeHeightmapScreenshot ( MakeScreenshotName ( HEIGHTMAP_NAME , _cur_screenshot_format - > extension ) ) ;
2011-05-28 15:54:18 +02:00
break ;
}
2020-01-04 17:47:37 +01:00
case SC_MINIMAP :
ret = MakeMinimapWorldScreenshot ( ) ;
break ;
2009-12-19 20:21:37 +01:00
default :
NOT_REACHED ( ) ;
2006-07-28 23:51:00 +02:00
}
2009-12-19 20:21:37 +01:00
if ( ret ) {
2021-03-25 23:51:37 +01:00
if ( t = = SC_HEIGHTMAP ) {
SetDParamStr ( 0 , _screenshot_name ) ;
SetDParam ( 1 , _heightmap_highest_peak ) ;
2021-05-01 10:46:47 +02:00
ShowErrorMessage ( STR_MESSAGE_HEIGHTMAP_SUCCESSFULLY , INVALID_STRING_ID , WL_WARNING ) ;
2021-03-25 23:51:37 +01:00
} else {
SetDParamStr ( 0 , _screenshot_name ) ;
ShowErrorMessage ( STR_MESSAGE_SCREENSHOT_SUCCESSFULLY , INVALID_STRING_ID , WL_WARNING ) ;
}
2009-12-19 20:21:37 +01:00
} else {
2010-02-24 15:46:15 +01:00
ShowErrorMessage ( STR_ERROR_SCREENSHOT_FAILED , INVALID_STRING_ID , WL_ERROR ) ;
2009-12-19 20:21:37 +01:00
}
return ret ;
2006-07-28 23:51:00 +02:00
}
2020-01-04 17:47:37 +01:00
2021-05-01 19:55:46 +02:00
/**
* Schedule making a screenshot .
* Unconditionally take a screenshot of the requested type .
* @ param t the type of screenshot to make .
* @ param name the name to give to the screenshot .
* @ param width the width of the screenshot of , or 0 for current viewport width ( only works for SC_ZOOMEDIN and SC_DEFAULTZOOM ) .
* @ param height the height of the screenshot of , or 0 for current viewport height ( only works for SC_ZOOMEDIN and SC_DEFAULTZOOM ) .
* @ return true iff the screenshot was successfully made .
* @ see MakeScreenshotWithConfirm
*/
2023-05-08 19:01:06 +02:00
bool MakeScreenshot ( ScreenshotType t , std : : string name , uint32_t width , uint32_t height )
2021-05-01 19:55:46 +02:00
{
if ( t = = SC_CRASHLOG ) {
/* Video buffer might or might not be locked. */
VideoDriver : : VideoBufferLocker lock ;
return RealMakeScreenshot ( t , name , width , height ) ;
}
VideoDriver : : GetInstance ( ) - > QueueOnMainThread ( [ = ] { // Capture by value to not break scope.
RealMakeScreenshot ( t , name , width , height ) ;
} ) ;
return true ;
}
2020-01-04 17:47:37 +01:00
2023-09-16 22:20:53 +02:00
static void MinimapScreenCallback ( void * , void * buf , uint y , uint pitch , uint n )
2020-01-04 17:47:37 +01:00
{
2023-05-08 19:01:06 +02:00
uint32_t * ubuf = ( uint32_t * ) buf ;
2020-01-06 00:05:28 +01:00
uint num = ( pitch * n ) ;
2020-01-04 17:47:37 +01:00
for ( uint i = 0 ; i < num ; i + + ) {
2020-01-06 00:05:28 +01:00
uint row = y + ( int ) ( i / pitch ) ;
2023-01-21 10:43:03 +01:00
uint col = ( Map : : SizeX ( ) - 1 ) - ( i % pitch ) ;
2020-01-04 17:47:37 +01:00
TileIndex tile = TileXY ( col , row ) ;
2024-03-16 23:59:32 +01:00
uint8_t val = GetSmallMapOwnerPixels ( tile , GetTileType ( tile ) , IncludeHeightmap : : Never ) & 0xFF ;
2020-01-04 17:47:37 +01:00
2023-05-08 19:01:06 +02:00
uint32_t colour_buf = 0 ;
2020-01-04 17:47:37 +01:00
colour_buf = ( _cur_palette . palette [ val ] . b < < 0 ) ;
colour_buf | = ( _cur_palette . palette [ val ] . g < < 8 ) ;
colour_buf | = ( _cur_palette . palette [ val ] . r < < 16 ) ;
* ubuf = colour_buf ;
2020-01-06 00:05:28 +01:00
ubuf + + ; // Skip alpha
2020-01-04 17:47:37 +01:00
}
}
/**
* Make a minimap screenshot .
*/
bool MakeMinimapWorldScreenshot ( )
{
2024-04-20 15:34:30 +02:00
return _cur_screenshot_format - > proc ( MakeScreenshotName ( SCREENSHOT_NAME , _cur_screenshot_format - > extension ) , MinimapScreenCallback , nullptr , Map : : SizeX ( ) , Map : : SizeY ( ) , 32 , _cur_palette . palette ) ;
2020-01-04 17:47:37 +01:00
}