Path: blob/master/libmupen64plus/mupen64plus-core/src/osd/screenshot.cpp
2 views
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *1* Mupen64plus - screenshot.c *2* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *3* Copyright (C) 2008 Richard42 *4* *5* This program is free software; you can redistribute it and/or modify *6* it under the terms of the GNU General Public License as published by *7* the Free Software Foundation; either version 2 of the License, or *8* (at your option) any later version. *9* *10* This program is distributed in the hope that it will be useful, *11* but WITHOUT ANY WARRANTY; without even the implied warranty of *12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *13* GNU General Public License for more details. *14* *15* You should have received a copy of the GNU General Public License *16* along with this program; if not, write to the *17* Free Software Foundation, Inc., *18* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *19* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */2021#include <stdlib.h>22#include <string.h>23#include <stdio.h>24#include <ctype.h>2526#include <SDL_opengl.h>27#include <SDL.h>28#include <png.h>2930#include "osd.h"3132extern "C" {33#define M64P_CORE_PROTOTYPES 134#include "api/m64p_types.h"35#include "api/callbacks.h"36#include "api/m64p_config.h"37#include "api/config.h"38#include "main/main.h"39#include "main/util.h"40#include "main/rom.h"41#include "osal/files.h"42#include "osal/preproc.h"43#include "plugin/plugin.h"44}4546/*********************************************************************************************************47* PNG support functions for writing screenshot files48*/4950static void mupen_png_error(png_structp png_write, const char *message)51{52DebugMessage(M64MSG_ERROR, "PNG Error: %s", message);53}5455static void mupen_png_warn(png_structp png_write, const char *message)56{57DebugMessage(M64MSG_WARNING, "PNG Warning: %s", message);58}5960static void user_write_data(png_structp png_write, png_bytep data, png_size_t length)61{62FILE *fPtr = (FILE *) png_get_io_ptr(png_write);63if (fwrite(data, 1, length, fPtr) != length)64DebugMessage(M64MSG_ERROR, "Failed to write %zi bytes to screenshot file.", length);65}6667static void user_flush_data(png_structp png_write)68{69FILE *fPtr = (FILE *) png_get_io_ptr(png_write);70fflush(fPtr);71}7273/*********************************************************************************************************74* Other Local (static) functions75*/7677static int SaveRGBBufferToFile(const char *filename, const unsigned char *buf, int width, int height, int pitch)78{79int i;8081// allocate PNG structures82png_structp png_write = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, mupen_png_error, mupen_png_warn);83if (!png_write)84{85DebugMessage(M64MSG_ERROR, "Error creating PNG write struct.");86return 1;87}88png_infop png_info = png_create_info_struct(png_write);89if (!png_info)90{91png_destroy_write_struct(&png_write, (png_infopp)NULL);92DebugMessage(M64MSG_ERROR, "Error creating PNG info struct.");93return 2;94}95// Set the jumpback96if (setjmp(png_jmpbuf(png_write)))97{98png_destroy_write_struct(&png_write, &png_info);99DebugMessage(M64MSG_ERROR, "Error calling setjmp()");100return 3;101}102// open the file to write103FILE *savefile = fopen(filename, "wb");104if (savefile == NULL)105{106DebugMessage(M64MSG_ERROR, "Error opening '%s' to save screenshot.", filename);107return 4;108}109// set function pointers in the PNG library, for write callbacks110png_set_write_fn(png_write, (png_voidp) savefile, user_write_data, user_flush_data);111// set the info112png_set_IHDR(png_write, png_info, width, height, 8, PNG_COLOR_TYPE_RGB,113PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);114// allocate row pointers and scale each row to 24-bit color115png_byte **row_pointers;116row_pointers = (png_byte **) malloc(height * sizeof(png_bytep));117for (i = 0; i < height; i++)118{119row_pointers[i] = (png_byte *) (buf + (height - 1 - i) * pitch);120}121// set the row pointers122png_set_rows(png_write, png_info, row_pointers);123// write the picture to disk124png_write_png(png_write, png_info, 0, NULL);125// free memory126free(row_pointers);127png_destroy_write_struct(&png_write, &png_info);128// close file129fclose(savefile);130// all done131return 0;132}133134static int CurrentShotIndex;135136static char *GetNextScreenshotPath(void)137{138char *ScreenshotPath;139char ScreenshotFileName[20 + 8 + 1];140141// generate the base name of the screenshot142// add the ROM name, convert to lowercase, convert spaces to underscores143strcpy(ScreenshotFileName, ROM_PARAMS.headername);144for (char *pch = ScreenshotFileName; *pch != '\0'; pch++)145*pch = (*pch == ' ') ? '_' : tolower(*pch);146strcat(ScreenshotFileName, "-###.png");147148// add the base path to the screenshot file name149const char *SshotDir = ConfigGetParamString(g_CoreConfig, "ScreenshotPath");150if (SshotDir == NULL || *SshotDir == '\0')151{152// note the trick to avoid an allocation. we add a NUL character153// instead of the separator, call mkdir, then add the separator154ScreenshotPath = formatstr("%sscreenshot%c%s", ConfigGetUserDataPath(), '\0', ScreenshotFileName);155if (ScreenshotPath == NULL)156return NULL;157osal_mkdirp(ScreenshotPath, 0700);158ScreenshotPath[strlen(ScreenshotPath)] = OSAL_DIR_SEPARATORS[0];159}160else161{162ScreenshotPath = combinepath(SshotDir, ScreenshotFileName);163if (ScreenshotPath == NULL)164return NULL;165}166167// patch the number part of the name (the '###' part) until we find a free spot168char *NumberPtr = ScreenshotPath + strlen(ScreenshotPath) - 7;169for (; CurrentShotIndex < 1000; CurrentShotIndex++)170{171sprintf(NumberPtr, "%03i.png", CurrentShotIndex);172FILE *pFile = fopen(ScreenshotPath, "r");173if (pFile == NULL)174break;175fclose(pFile);176}177178if (CurrentShotIndex >= 1000)179{180DebugMessage(M64MSG_ERROR, "Can't save screenshot; folder already contains 1000 screenshots for this ROM");181return NULL;182}183CurrentShotIndex++;184185return ScreenshotPath;186}187188/*********************************************************************************************************189* Global screenshot functions190*/191192extern "C" void ScreenshotRomOpen(void)193{194CurrentShotIndex = 0;195}196197extern "C" void TakeScreenshot(int iFrameNumber)198{199char *filename;200201// look for an unused screenshot filename202filename = GetNextScreenshotPath();203if (filename == NULL)204return;205206// get the width and height207int width = 640;208int height = 480;209gfx.readScreen(NULL, &width, &height, 0);210211// allocate memory for the image212unsigned char *pucFrame = (unsigned char *) malloc(width * height * 3);213if (pucFrame == NULL)214{215free(filename);216return;217}218219// grab the back image from OpenGL by calling the video plugin220gfx.readScreen(pucFrame, &width, &height, 0);221222// write the image to a PNG223SaveRGBBufferToFile(filename, pucFrame, width, height, width * 3);224// free the memory225free(pucFrame);226free(filename);227// print message -- this allows developers to capture frames and use them in the regression test228main_message(M64MSG_INFO, OSD_BOTTOM_LEFT, "Captured screenshot for frame %i.", iFrameNumber);229}230231232233