3
3
* PROJECT: Multi Theft Auto v1.0
4
4
* LICENSE: See LICENSE in the top level directory
5
5
* FILE: core/CScreenShot.cpp
6
- * PURPOSE: Screen capture file handling
6
+ * PURPOSE: Screen capturing
7
7
*
8
8
* Multi Theft Auto is available from http://www.multitheftauto.com/
9
9
*
14
14
15
15
extern CCore* g_pCore;
16
16
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 ;
21
19
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 ;
30
21
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 ;
36
24
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;
43
26
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 };
45
30
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 ;
48
37
49
- void CScreenShot::PostScreenShot ( const SString& strFileName )
38
+ void CScreenShot::InitiateScreenShot ( bool bIsCameraShot )
50
39
{
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 ;
59
42
60
- void CScreenShot::SetPath (const char * szPath)
61
- {
62
- strncpy (szScreenShotPath, szPath, MAX_PATH - 1 );
43
+ ms_bScreenShot = true ;
44
+ ms_bIsCameraShot = bIsCameraShot;
63
45
64
- // make sure the directory exists
65
- File::Mkdir (szPath);
66
- }
46
+ // Camera shots shouldn't have GUI
47
+ ms_bBeforeGUI = bIsCameraShot;
67
48
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)
79
50
{
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 " );
87
58
}
88
- // Close the file handle
89
- FindClose (hFind);
90
- return iNumberOfFiles;
91
59
}
92
60
93
- SString CScreenShot::GetValidScreenshotFilename ()
61
+ SString CScreenShot::GetScreenshotPath ()
94
62
{
95
63
// Get the system time
96
64
SYSTEMTIME sysTime;
97
65
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 );
100
68
}
101
69
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)
103
75
{
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))
114
92
{
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)
116
98
{
117
- // We wanted the first file
118
- strReturn = SString ( " %s \\ %s " , &szScreenShotPath[ 0 ], fdFindData. cFileName );
99
+ // Start the save thread
100
+ StartSaveThread ( );
119
101
}
120
102
else
121
103
{
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 ();
133
106
}
134
107
}
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 ;
138
115
}
139
116
140
117
// Callback for threaded save
141
- // Static function
142
118
DWORD CScreenShot::ThreadProc (LPVOID lpdwThreadParam)
143
119
{
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 ();
154
122
155
123
// Create the screen data buffer
156
124
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++)
159
127
{
160
- ppScreenData[y] = new BYTE[ulScreenWidth * 4 ];
128
+ ppScreenData[y] = new BYTE[ms_uiWidth * 4 ];
161
129
}
162
130
163
131
// Copy the surface data into a row-based buffer for libpng
164
132
#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++)
167
135
{
168
- memcpy (ppScreenData[i], (BYTE*)ms_pData + i * uiLinePitch, ulLineWidth);
136
+ memcpy (ppScreenData[i], (BYTE*)pData + i * uiLinePitch, ulLineWidth);
169
137
for (unsigned int j = 3 ; j < ulLineWidth; j += BYTESPERPIXEL)
170
138
{
171
139
ppScreenData[i][j] = 0xFF ;
172
140
}
173
141
}
174
142
175
- MakeSureDirExists (ms_strFileName );
176
- FILE* file = File::Fopen (ms_strFileName , " wb" );
143
+ MakeSureDirExists (ms_strScreenShotPath );
144
+ FILE* file = File::Fopen (ms_strScreenShotPath , " wb" );
177
145
if (file)
178
146
{
179
147
png_struct* png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL , NULL , NULL );
180
148
png_info* info_ptr = png_create_info_struct (png_ptr);
181
149
png_init_io (png_ptr, file);
182
150
png_set_filter (png_ptr, 0 , PNG_FILTER_NONE);
183
151
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,
185
153
PNG_FILTER_TYPE_DEFAULT);
186
154
png_set_rows (png_ptr, info_ptr, ppScreenData);
187
155
png_write_png (png_ptr, info_ptr, PNG_TRANSFORM_BGR | PNG_TRANSFORM_STRIP_ALPHA, NULL );
188
156
png_write_end (png_ptr, info_ptr);
189
157
png_destroy_write_struct (&png_ptr, &info_ptr);
190
158
fclose (file);
159
+
160
+ CCore::GetSingleton ().GetConsole ()->Printf (_ (" Screenshot taken: '%s'" ), *ms_strScreenShotPath);
191
161
}
192
162
else
193
163
{
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 );
195
165
}
196
166
197
167
// Clean up the screen data buffer
198
168
if (ppScreenData)
199
169
{
200
- for (unsigned short y = 0 ; y < ulScreenHeight ; y++)
170
+ for (unsigned short y = 0 ; y < ms_uiHeight ; y++)
201
171
{
202
172
delete[] ppScreenData[y];
203
173
}
204
174
delete[] ppScreenData;
205
175
}
206
176
177
+ ClearBuffer ();
207
178
ms_bIsSaving = false ;
208
179
return 0 ;
209
180
}
210
181
211
- // Static function
212
- void CScreenShot::BeginSave (const char * szFileName, void * pData, uint uiDataSize, uint uiWidth, uint uiHeight)
182
+ void CScreenShot::StartSaveThread ()
213
183
{
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 );
224
185
if (!hThread)
225
186
{
226
187
CCore::GetSingleton ().GetConsole ()->Printf (" Could not create screenshot thread." );
188
+ ClearBuffer ();
227
189
}
228
190
else
229
191
{
@@ -233,8 +195,13 @@ void CScreenShot::BeginSave(const char* szFileName, void* pData, uint uiDataSize
233
195
}
234
196
}
235
197
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 ()
238
205
{
239
- return ms_bIsSaving ;
206
+ ms_ScreenShotBuffer. Clear () ;
240
207
}
0 commit comments