Skip to content

Commit 2acf0cd

Browse files
authored
Use MTA screenshot function for photos taken by in-game camera weapon (#2765)
1 parent de3dc70 commit 2acf0cd

File tree

8 files changed

+149
-216
lines changed

8 files changed

+149
-216
lines changed

Client/core/CCommandFuncs.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ void CCommandFuncs::Ver(const char* szParameters)
6666

6767
void CCommandFuncs::ScreenShot(const char* szParameters)
6868
{
69-
g_pCore->TakeScreenShot();
69+
g_pCore->InitiateScreenShot(false);
7070
}
7171

7272
void CCommandFuncs::Vid(const char* szParameters)

Client/core/CCore.cpp

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,6 @@ CCore::CCore()
122122
// Setup our hooks.
123123
ApplyHooks();
124124

125-
// Reset the screenshot flag
126-
bScreenShot = false;
127-
128125
// No initial fps limit
129126
m_bDoneFrameRateLimit = false;
130127
m_uiFrameRateLimit = 0;
@@ -477,9 +474,9 @@ bool CCore::ClearChat()
477474
return false;
478475
}
479476

480-
void CCore::TakeScreenShot()
477+
void CCore::InitiateScreenShot(bool bIsCameraShot)
481478
{
482-
bScreenShot = true;
479+
CScreenShot::InitiateScreenShot(bIsCameraShot);
483480
}
484481

485482
void CCore::EnableChatInput(char* szCommand, DWORD dwColor)
@@ -975,11 +972,6 @@ void CCore::DeinitGUI()
975972
void CCore::InitGUI(IDirect3DDevice9* pDevice)
976973
{
977974
m_pGUI = InitModule<CGUI>(m_GUIModule, "GUI", "InitGUIInterface", pDevice);
978-
979-
// and set the screenshot path to this default library (screenshots shouldnt really be made outside mods)
980-
std::string strScreenShotPath = CalcMTASAPath("screenshots");
981-
CVARS_SET("screenshot_path", strScreenShotPath);
982-
CScreenShot::SetPath(strScreenShotPath.c_str());
983975
}
984976

985977
void CCore::CreateGUI()

Client/core/CCore.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ class CCore : public CCoreInterface, public CSingleton<CCore>
130130
int GetChatboxCharacterLimit();
131131
int GetChatboxMaxCharacterLimit();
132132

133-
// Screenshots
134-
void TakeScreenShot();
133+
// Screenshot
134+
void InitiateScreenShot(bool bIsCameraShot);
135135

136136
// GUI
137137
bool IsSettingsVisible();
@@ -232,7 +232,6 @@ class CCore : public CCoreInterface, public CSingleton<CCore>
232232

233233
SString GetConnectCommandFromURI(const char* szURI);
234234
void GetConnectParametersFromURI(const char* szURI, std::string& strHost, unsigned short& usPort, std::string& strNick, std::string& strPassword);
235-
bool bScreenShot;
236235
std::map<std::string, std::string>& GetCommandLineOptions() { return m_CommandLineOptions; }
237236
const char* GetCommandLineOption(const char* szOption);
238237
const char* GetCommandLineArgs() { return m_szCommandLineArgs; }

Client/core/CScreenShot.cpp

Lines changed: 98 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* PROJECT: Multi Theft Auto v1.0
44
* LICENSE: See LICENSE in the top level directory
55
* FILE: core/CScreenShot.cpp
6-
* PURPOSE: Screen capture file handling
6+
* PURPOSE: Screen capturing
77
*
88
* Multi Theft Auto is available from http://www.multitheftauto.com/
99
*
@@ -14,216 +14,178 @@
1414

1515
extern CCore* g_pCore;
1616

17-
static char szScreenShotPath[MAX_PATH] = {0};
18-
static bool bIsChatVisible = false;
19-
static bool bIsChatInputBlocked = false;
20-
static bool bIsDebugVisible = false;
17+
// If screenshot should be taken on current frame
18+
static bool ms_bScreenShot = false;
2119

22-
// Variables used for saving the screen shot file on a separate thread
23-
static bool ms_bIsSaving = false;
24-
static uint ms_uiWidth = 0;
25-
static uint ms_uiHeight = 0;
26-
static void* ms_pData = NULL;
27-
static uint ms_uiDataSize = 0;
28-
static bool ms_bHideChatBox = false; // TODO - Make setting
29-
static SString ms_strFileName;
20+
static bool ms_bIsCameraShot = false;
3021

31-
SString CScreenShot::PreScreenShot()
32-
{
33-
bIsChatVisible = g_pCore->IsChatVisible();
34-
bIsChatInputBlocked = g_pCore->IsChatInputBlocked();
35-
bIsDebugVisible = g_pCore->IsDebugVisible();
22+
// If screenshot should be taken before MTA draws GUI etc.
23+
static bool ms_bBeforeGUI = false;
3624

37-
// make the chat and debug windows invisible
38-
if (ms_bHideChatBox)
39-
{
40-
g_pCore->SetChatVisible(false, true);
41-
g_pCore->SetDebugVisible(false);
42-
}
25+
static SString ms_strScreenDirectoryPath;
4326

44-
SString strScreenShotName = GetValidScreenshotFilename();
27+
// Last save time, seperated per given type
28+
// (normal screenshot or camera weapon initiated)
29+
static long long ms_lLastSaveTime[2] = {0, 0};
4530

46-
return strScreenShotName;
47-
}
31+
// Variables which are also used in save thread
32+
static CBuffer ms_ScreenShotBuffer;
33+
static SString ms_strScreenShotPath;
34+
static bool ms_bIsSaving = false;
35+
static uint ms_uiWidth = 0;
36+
static uint ms_uiHeight = 0;
4837

49-
void CScreenShot::PostScreenShot(const SString& strFileName)
38+
void CScreenShot::InitiateScreenShot(bool bIsCameraShot)
5039
{
51-
// print a notice
52-
if (!strFileName.empty())
53-
g_pCore->GetConsole()->Printf(_("Screenshot taken: '%s'"), *strFileName);
54-
55-
// make the chat and debug windows visible again
56-
g_pCore->SetChatVisible(bIsChatVisible, bIsChatInputBlocked);
57-
g_pCore->SetDebugVisible(bIsDebugVisible);
58-
}
40+
if (ms_bScreenShot || ms_bIsSaving || IsRateLimited(bIsCameraShot))
41+
return;
5942

60-
void CScreenShot::SetPath(const char* szPath)
61-
{
62-
strncpy(szScreenShotPath, szPath, MAX_PATH - 1);
43+
ms_bScreenShot = true;
44+
ms_bIsCameraShot = bIsCameraShot;
6345

64-
// make sure the directory exists
65-
File::Mkdir(szPath);
66-
}
46+
// Camera shots shouldn't have GUI
47+
ms_bBeforeGUI = bIsCameraShot;
6748

68-
int CScreenShot::GetScreenShots()
69-
{
70-
int iNumberOfFiles = 0;
71-
HANDLE hFind;
72-
WIN32_FIND_DATAW fdFindData;
73-
// Create a search string
74-
SString strScreenShotName("%s\\mta-screen*.png", &szScreenShotPath[0]);
75-
// Find the first match
76-
hFind = FindFirstFileW(FromUTF8(strScreenShotName), &fdFindData);
77-
// Check if the first match failed
78-
if (hFind != INVALID_HANDLE_VALUE)
49+
if (bIsCameraShot)
7950
{
80-
iNumberOfFiles++;
81-
// Loop through and count the files
82-
while (FindNextFileW(hFind, &fdFindData))
83-
{
84-
// Keep going until we find the last file
85-
iNumberOfFiles++;
86-
}
51+
// Set the screenshot path to camera gallery path
52+
ms_strScreenDirectoryPath = PathJoin(GetSystemPersonalPath(), "GTA San Andreas User Files", "Gallery");
53+
}
54+
else
55+
{
56+
// Set the screenshot path to this default library (screenshots shouldn't really be made outside mods)
57+
ms_strScreenDirectoryPath = CalcMTASAPath("screenshots");
8758
}
88-
// Close the file handle
89-
FindClose(hFind);
90-
return iNumberOfFiles;
9159
}
9260

93-
SString CScreenShot::GetValidScreenshotFilename()
61+
SString CScreenShot::GetScreenshotPath()
9462
{
9563
// Get the system time
9664
SYSTEMTIME sysTime;
9765
GetLocalTime(&sysTime);
98-
return SString("%s\\mta-screen_%d-%02d-%02d_%02d-%02d-%02d.png", &szScreenShotPath[0], sysTime.wYear, sysTime.wMonth, sysTime.wDay, sysTime.wHour,
99-
sysTime.wMinute, sysTime.wSecond);
66+
return SString("%s\\mta-screen_%d-%02d-%02d_%02d-%02d-%02d.png", *ms_strScreenDirectoryPath, sysTime.wYear, sysTime.wMonth, sysTime.wDay,
67+
sysTime.wHour, sysTime.wMinute, sysTime.wSecond);
10068
}
10169

102-
SString CScreenShot::GetScreenShotPath(int iNumber)
70+
//
71+
// Take a screenshot if needed.
72+
// @ bool bBeforeGUI: whenever we try to capture screenshot before GUI gets drawn
73+
//
74+
void CScreenShot::CheckForScreenShot(bool bBeforeGUI)
10375
{
104-
// Create a search string
105-
SString strScreenShotName("%s\\mta-screen*.png", &szScreenShotPath[0]);
106-
HANDLE hFind;
107-
SString strReturn = "";
108-
WIN32_FIND_DATAW fdFindData;
109-
int i = 1;
110-
// Find the first match
111-
hFind = FindFirstFileW(FromUTF8(strScreenShotName), &fdFindData);
112-
// Check if the first match failed
113-
if (hFind != INVALID_HANDLE_VALUE)
76+
if (!ms_bScreenShot)
77+
return;
78+
79+
if (ms_bBeforeGUI != bBeforeGUI)
80+
return;
81+
82+
// Update last time of taken screenshot of given type
83+
ms_lLastSaveTime[ms_bIsCameraShot] = GetTickCount64_();
84+
85+
ms_strScreenShotPath = GetScreenshotPath();
86+
ms_uiWidth = CDirect3DData::GetSingleton().GetViewportWidth();
87+
ms_uiHeight = CDirect3DData::GetSingleton().GetViewportHeight();
88+
89+
// Try to get the screen data
90+
SString strError;
91+
if (CGraphics::GetSingleton().GetScreenGrabber()->GetBackBufferPixels(ms_uiWidth, ms_uiHeight, ms_ScreenShotBuffer, strError))
11492
{
115-
if (iNumber == 1)
93+
// Validate data size
94+
uint uiDataSize = ms_ScreenShotBuffer.GetSize();
95+
uint uiReqDataSize = ms_uiWidth * ms_uiHeight * 4;
96+
97+
if (uiDataSize == uiReqDataSize)
11698
{
117-
// We wanted the first file
118-
strReturn = SString("%s\\%s", &szScreenShotPath[0], fdFindData.cFileName);
99+
// Start the save thread
100+
StartSaveThread();
119101
}
120102
else
121103
{
122-
// Loop through and find all occurences of the file
123-
while (FindNextFileW(hFind, &fdFindData))
124-
{
125-
// Keep going until we find the last file
126-
i++;
127-
if (iNumber == i)
128-
{
129-
strReturn = SString("%s\\%s", &szScreenShotPath[0], fdFindData.cFileName);
130-
break;
131-
}
132-
}
104+
g_pCore->GetConsole()->Printf(_("Screenshot got %d bytes, but expected %d"), uiDataSize, uiReqDataSize);
105+
ClearBuffer();
133106
}
134107
}
135-
FindClose(hFind); // Close the file handle
136-
// Get Return the file directory
137-
return strReturn;
108+
else
109+
{
110+
g_pCore->GetConsole()->Print(_("Screenshot failed") + SString(" (%s)", *strError));
111+
ClearBuffer();
112+
}
113+
114+
ms_bScreenShot = false;
138115
}
139116

140117
// Callback for threaded save
141-
// Static function
142118
DWORD CScreenShot::ThreadProc(LPVOID lpdwThreadParam)
143119
{
144-
unsigned long ulScreenHeight = ms_uiHeight;
145-
unsigned long ulScreenWidth = ms_uiWidth;
146-
uint uiReqDataSize = ulScreenHeight * ulScreenWidth * 4;
147-
uint uiLinePitch = ulScreenWidth * 4;
148-
149-
if (uiReqDataSize != ms_uiDataSize)
150-
{
151-
ms_bIsSaving = false;
152-
return 0;
153-
}
120+
uint uiLinePitch = ms_uiWidth * 4;
121+
void* pData = ms_ScreenShotBuffer.GetData();
154122

155123
// Create the screen data buffer
156124
BYTE** ppScreenData = NULL;
157-
ppScreenData = new BYTE*[ulScreenHeight];
158-
for (unsigned short y = 0; y < ulScreenHeight; y++)
125+
ppScreenData = new BYTE*[ms_uiHeight];
126+
for (unsigned short y = 0; y < ms_uiHeight; y++)
159127
{
160-
ppScreenData[y] = new BYTE[ulScreenWidth * 4];
128+
ppScreenData[y] = new BYTE[ms_uiWidth * 4];
161129
}
162130

163131
// Copy the surface data into a row-based buffer for libpng
164132
#define BYTESPERPIXEL 4
165-
unsigned long ulLineWidth = ulScreenWidth * 4;
166-
for (unsigned int i = 0; i < ulScreenHeight; i++)
133+
unsigned long ulLineWidth = ms_uiWidth * 4;
134+
for (unsigned int i = 0; i < ms_uiHeight; i++)
167135
{
168-
memcpy(ppScreenData[i], (BYTE*)ms_pData + i * uiLinePitch, ulLineWidth);
136+
memcpy(ppScreenData[i], (BYTE*)pData + i * uiLinePitch, ulLineWidth);
169137
for (unsigned int j = 3; j < ulLineWidth; j += BYTESPERPIXEL)
170138
{
171139
ppScreenData[i][j] = 0xFF;
172140
}
173141
}
174142

175-
MakeSureDirExists(ms_strFileName);
176-
FILE* file = File::Fopen(ms_strFileName, "wb");
143+
MakeSureDirExists(ms_strScreenShotPath);
144+
FILE* file = File::Fopen(ms_strScreenShotPath, "wb");
177145
if (file)
178146
{
179147
png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
180148
png_info* info_ptr = png_create_info_struct(png_ptr);
181149
png_init_io(png_ptr, file);
182150
png_set_filter(png_ptr, 0, PNG_FILTER_NONE);
183151
png_set_compression_level(png_ptr, 1);
184-
png_set_IHDR(png_ptr, info_ptr, ulScreenWidth, ulScreenHeight, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
152+
png_set_IHDR(png_ptr, info_ptr, ms_uiWidth, ms_uiHeight, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
185153
PNG_FILTER_TYPE_DEFAULT);
186154
png_set_rows(png_ptr, info_ptr, ppScreenData);
187155
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_BGR | PNG_TRANSFORM_STRIP_ALPHA, NULL);
188156
png_write_end(png_ptr, info_ptr);
189157
png_destroy_write_struct(&png_ptr, &info_ptr);
190158
fclose(file);
159+
160+
CCore::GetSingleton().GetConsole()->Printf(_("Screenshot taken: '%s'"), *ms_strScreenShotPath);
191161
}
192162
else
193163
{
194-
CCore::GetSingleton().GetConsole()->Printf("Could not create screenshot file '%s'", *ms_strFileName);
164+
CCore::GetSingleton().GetConsole()->Printf("Could not create screenshot file '%s'", *ms_strScreenShotPath);
195165
}
196166

197167
// Clean up the screen data buffer
198168
if (ppScreenData)
199169
{
200-
for (unsigned short y = 0; y < ulScreenHeight; y++)
170+
for (unsigned short y = 0; y < ms_uiHeight; y++)
201171
{
202172
delete[] ppScreenData[y];
203173
}
204174
delete[] ppScreenData;
205175
}
206176

177+
ClearBuffer();
207178
ms_bIsSaving = false;
208179
return 0;
209180
}
210181

211-
// Static function
212-
void CScreenShot::BeginSave(const char* szFileName, void* pData, uint uiDataSize, uint uiWidth, uint uiHeight)
182+
void CScreenShot::StartSaveThread()
213183
{
214-
if (ms_bIsSaving)
215-
return;
216-
217-
ms_strFileName = szFileName;
218-
ms_pData = pData;
219-
ms_uiDataSize = uiDataSize;
220-
ms_uiWidth = uiWidth;
221-
ms_uiHeight = uiHeight;
222-
223-
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CScreenShot::ThreadProc, NULL, CREATE_SUSPENDED, NULL);
184+
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, NULL, CREATE_SUSPENDED, NULL);
224185
if (!hThread)
225186
{
226187
CCore::GetSingleton().GetConsole()->Printf("Could not create screenshot thread.");
188+
ClearBuffer();
227189
}
228190
else
229191
{
@@ -233,8 +195,13 @@ void CScreenShot::BeginSave(const char* szFileName, void* pData, uint uiDataSize
233195
}
234196
}
235197

236-
// Static function
237-
bool CScreenShot::IsSaving()
198+
// Max one screenshot per second
199+
bool CScreenShot::IsRateLimited(bool bIsCameraShot)
200+
{
201+
return GetTickCount64_() - ms_lLastSaveTime[bIsCameraShot] < 1000;
202+
}
203+
204+
void CScreenShot::ClearBuffer()
238205
{
239-
return ms_bIsSaving;
206+
ms_ScreenShotBuffer.Clear();
240207
}

0 commit comments

Comments
 (0)