@@ -39,10 +39,7 @@ static void drawFramerateBar(void);
3939#include < windows.h>
4040#include < io.h>
4141#include < time.h>
42- #include < thread>
43- #include < memory>
4442
45- // TheSuperHackers @bobtista 02/11/2025 STB for image encoding
4643#define STB_IMAGE_WRITE_IMPLEMENTATION
4744#include < stb_image_write.h>
4845
@@ -3028,12 +3025,33 @@ void W3DDisplay::takeScreenShot(void)
30283025 TheInGameUI->message (TheGameText->fetch (" GUI:ScreenCapture" ), ufileName.str ());
30293026}
30303027
3031- // / TheSuperHackers @bobtista 02/11/2025 Save compressed screenshot (JPEG/PNG) to file without stalling the game
3032- // / This implementation captures the frame buffer on the main thread, then spawns a background thread
3033- // / to compress and save the image, allowing the game to continue running smoothly.
3028+ struct ScreenshotThreadData
3029+ {
3030+ unsigned char * imageData;
3031+ unsigned int width;
3032+ unsigned int height;
3033+ char pathname[1024 ];
3034+ char leafname[256 ];
3035+ };
3036+
3037+ static DWORD WINAPI screenshotThreadFunc (LPVOID param)
3038+ {
3039+ ScreenshotThreadData* data = (ScreenshotThreadData*)param;
3040+
3041+ int result = stbi_write_jpg (data->pathname , data->width , data->height , 3 , data->imageData , 90 );
3042+
3043+ if (!result) {
3044+ OutputDebugStringA (" Failed to write screenshot JPEG\n " );
3045+ }
3046+
3047+ delete [] data->imageData ;
3048+ delete data;
3049+
3050+ return 0 ;
3051+ }
3052+
30343053void W3DDisplay::takeScreenShotCompressed (void )
30353054{
3036- // TheSuperHackers @bobtista 02/11/2025 Find next available filename
30373055 char leafname[256 ];
30383056 char pathname[1024 ];
30393057 static int frame_number = 1 ;
@@ -3047,7 +3065,6 @@ void W3DDisplay::takeScreenShotCompressed(void)
30473065 done = true ;
30483066 }
30493067
3050- // TheSuperHackers @bobtista 02/11/2025 Get the back buffer and create a copy
30513068 SurfaceClass* surface = DX8Wrapper::_Get_DX8_Back_Buffer ();
30523069 SurfaceClass::SurfaceDescription surfaceDesc;
30533070 surface->Get_Description (surfaceDesc);
@@ -3075,53 +3092,40 @@ void W3DDisplay::takeScreenShotCompressed(void)
30753092 unsigned int width = surfaceDesc.Width ;
30763093 unsigned int height = surfaceDesc.Height ;
30773094
3078- // TheSuperHackers @bobtista 02/11/2025 Allocate buffer for RGB image data
3079- // Using shared_ptr for automatic cleanup in the background thread
3080- std::shared_ptr<unsigned char > imageData (new unsigned char [3 * width * height],
3081- std::default_delete<unsigned char []>());
3082- unsigned char * image = imageData.get ();
3095+ unsigned char * image = new unsigned char [3 * width * height];
30833096
3084- // TheSuperHackers @bobtista 02/11/2025 Copy and convert BGRA to RGB
30853097 for (y = 0 ; y < height; y++)
30863098 {
30873099 for (x = 0 ; x < width; x++)
30883100 {
30893101 index = 3 * (x + y * width);
30903102 index2 = y * lrect.Pitch + 4 * x;
30913103
3092- image[index] = *((unsigned char *)lrect.pBits + index2 + 2 ); // R
3093- image[index + 1 ] = *((unsigned char *)lrect.pBits + index2 + 1 ); // G
3094- image[index + 2 ] = *((unsigned char *)lrect.pBits + index2 + 0 ); // B
3104+ image[index] = *((unsigned char *)lrect.pBits + index2 + 2 );
3105+ image[index + 1 ] = *((unsigned char *)lrect.pBits + index2 + 1 );
3106+ image[index + 2 ] = *((unsigned char *)lrect.pBits + index2 + 0 );
30953107 }
30963108 }
30973109
30983110 surfaceCopy->Unlock ();
30993111 surfaceCopy->Release_Ref ();
31003112 surfaceCopy = NULL ;
31013113
3102- // TheSuperHackers @bobtista 02/11/2025 Make a copy of the pathname for the background thread
3103- std::string pathnameCopy (pathname);
3104- std::string leafnameCopy (leafname);
3105-
3106- // TheSuperHackers @bobtista 02/11/2025 Spawn background thread to compress and save the image
3107- // This allows the game to continue running without freezing
3108- std::thread ([imageData, width, height, pathnameCopy, leafnameCopy]() {
3109- // TheSuperHackers @bobtista 02/11/2025 Write JPEG with quality 90 (range: 1-100)
3110- // stbi_write_jpg expects image data with Y-axis going down, which matches our data
3111- int result = stbi_write_jpg (pathnameCopy.c_str (), width, height, 3 , imageData.get (), 90 );
3112-
3113- if (!result) {
3114- // TheSuperHackers @bobtista 02/11/2025 Log error if write failed
3115- // Note: Can't show UI message from background thread
3116- OutputDebugStringA (" Failed to write screenshot JPEG\n " );
3117- }
3118-
3119- // TheSuperHackers @bobtista 02/11/2025 imageData will be automatically cleaned up when shared_ptr goes out of scope
3120- }).detach (); // TheSuperHackers @bobtista 02/11/2025 Detach thread to run independently
3121-
3122- // TheSuperHackers @bobtista 02/11/2025 Show message to user immediately (file is being saved in background)
3114+ ScreenshotThreadData* threadData = new ScreenshotThreadData ();
3115+ threadData->imageData = image;
3116+ threadData->width = width;
3117+ threadData->height = height;
3118+ strcpy (threadData->pathname , pathname);
3119+ strcpy (threadData->leafname , leafname);
3120+
3121+ DWORD threadId;
3122+ HANDLE hThread = CreateThread (NULL , 0 , screenshotThreadFunc, threadData, 0 , &threadId);
3123+ if (hThread) {
3124+ CloseHandle (hThread);
3125+ }
3126+
31233127 UnicodeString ufileName;
3124- ufileName.translate (leafnameCopy. c_str () );
3128+ ufileName.translate (leafname );
31253129 TheInGameUI->message (TheGameText->fetch (" GUI:ScreenCapture" ), ufileName.str ());
31263130}
31273131
0 commit comments