Results 1 to 6 of 6

Thread: Take screenshot and save as jpg

  1. #1
    Administrator James's Avatar
    Join Date
    May 2010
    Location
    on the intraweb
    Posts
    3,173

    Default Take screenshot and save as jpg

    Would like some assistance.

    I'm writing an app that takes a screenshot of a users screen and outputs a jpg.
    I've written two seperate programs that I will share with you below.

    First application works, but when I save it as a jpg, it distorts the quality. It makes the text "blurry" even though I set the highest quality.
    The second application allows me to save it as an uncompressed image, and this image is a lot cleaner looking. However, the size is 7.91mb (which is BIG for a image). Then when I try converting it to jpg, I'm back with where I started and the newly created image is once again distorted\blurry.

    I will include screenshot to show you what I mean.

    App1:

    Code:
    // screenshot.cpp : Defines the entry point for the console application.
    //
    #include "stdafx.h"
    #include <windows.h>
    #include <GdiPlus.h>
    #include <stdio.h>
    #pragma comment( lib, "gdiplus" )
    
    
    using namespace Gdiplus;
    
    
    int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)   
    {   
        UINT num = 0;                     // number of image encoders   
        UINT size = 0;                   // size of the image encoder array in bytes   
        ImageCodecInfo* pImageCodecInfo = NULL;   
        GetImageEncodersSize(&num, &size);   
        if(size == 0)   
            return -1;     //   Failure   
        
        pImageCodecInfo = (ImageCodecInfo*)(malloc(size));   
        if(pImageCodecInfo == NULL)   
            return -1;     //   Failure   
        
        GetImageEncoders(num, size, pImageCodecInfo);   
        for(UINT j = 0; j < num; ++j)   
        {   
            if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )   
            {   
                *pClsid = pImageCodecInfo[j].Clsid;   
                free(pImageCodecInfo);   
                return j;     //   Success   
            }           
        }   
        free(pImageCodecInfo);   
        return -1;     //   Failure   
    }
    
    
    void gdiscreen()
    {
        using namespace Gdiplus;
        GdiplusStartupInput gdiplusStartupInput;
        ULONG_PTR gdiplusToken;
        EncoderParameters encoderParameters;
        ULONG quality;
    
    
        GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
        {
            HDC scrdc, memdc;
            HBITMAP membit;
            scrdc = ::GetDC(0);
            int Height = GetSystemMetrics(SM_CYSCREEN);
            int Width = GetSystemMetrics(SM_CXSCREEN);
            memdc = CreateCompatibleDC(scrdc);
            membit = CreateCompatibleBitmap(scrdc, Width, Height);
            HBITMAP hOldBitmap =(HBITMAP) SelectObject(memdc, membit);
            BitBlt(memdc, 0, 0, Width, Height, scrdc, 0, 0, SRCCOPY);
    
    
            Gdiplus::Bitmap bitmap(membit, NULL);
            CLSID clsid;
            GetEncoderClsid(L"image/jpeg", &clsid);
    
    
            //Let's optimize the picture quality
            encoderParameters.Count = 1;
            encoderParameters.Parameter[0].Guid = EncoderQuality;
            encoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
            encoderParameters.Parameter[0].NumberOfValues = 5;
            quality = 100;
            encoderParameters.Parameter[0].Value = &quality;
    
    
            bitmap.Save(L"ScreenShot.jpg", &clsid, &encoderParameters);
    
    
            SelectObject(memdc, hOldBitmap);
    
    
            DeleteObject(memdc);
    
    
            DeleteObject(membit);
    
    
            ReleaseDC(0,scrdc);
        }
    
    
        GdiplusShutdown(gdiplusToken);
    }
    
    
    bool isRunning() 
    { 
        //This checks if the process is already running so you don't have multiple instances running. 
        HANDLE handle = CreateMutex(NULL, true, "screenshot"); 
        if(GetLastError() != ERROR_SUCCESS) 
        { 
            MessageBox(0, "Process is already running", "Warning", MB_ICONWARNING); 
            return false; 
        } 
        else 
            return true; 
    } 
    
    
    int main()
    {    
        //Don't show the console window
        FreeConsole();
    
    
        //Check if this process is running 
        if(isRunning() == false) 
            return 0; 
    
    
        //Run this loop until we explicitly close the process
        while(1)
        {
            Sleep(5000);
            gdiscreen();
        }
    
    
        return 0;
    }

    App2:

    Code:
    // screenshot.cpp : Defines the entry point for the console application.
    //
    #include "stdafx.h"
    #include <windows.h>
    #include <stdio.h>
    #include <atlimage.h>
    ATL::CImage img;
    
    
    void ScreenShot(char*BmpName)
    {
        HWND DesktopHwnd = GetDesktopWindow();
        RECT DesktopParams;
        HDC DevC = GetDC(DesktopHwnd);
        GetWindowRect(DesktopHwnd,&DesktopParams);
        DWORD Width = GetSystemMetrics(SM_CXSCREEN);
        DWORD Height = GetSystemMetrics(SM_CYSCREEN);
    
    
        DWORD FileSize = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+(sizeof(RGBTRIPLE)+1*(Width*Height*4));
        char *BmpFileData = (char*)GlobalAlloc(0x0040,FileSize);
    
    
        PBITMAPFILEHEADER BFileHeader = (PBITMAPFILEHEADER)BmpFileData;
        PBITMAPINFOHEADER  BInfoHeader = (PBITMAPINFOHEADER)&BmpFileData[sizeof(BITMAPFILEHEADER)];
    
    
        BFileHeader->bfType = 0x4D42; // BM
        BFileHeader->bfSize = sizeof(BITMAPFILEHEADER);
        BFileHeader->bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
    
    
        BInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
        BInfoHeader->biPlanes = 1;
        BInfoHeader->biBitCount = 32;
        BInfoHeader->biCompression = BI_RGB;//BI_JPEG BI_RGB;
        BInfoHeader->biHeight = Height;
        BInfoHeader->biWidth = Width;
        BInfoHeader->biSizeImage = Width * Height+1;
    
    
    
    
        RGBTRIPLE *Image = (RGBTRIPLE*)&BmpFileData[sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)];
        RGBTRIPLE color;
        
        HDC CaptureDC = CreateCompatibleDC(DevC);
        HBITMAP CaptureBitmap = CreateCompatibleBitmap(DevC,Width,Height);
        SelectObject(CaptureDC,CaptureBitmap);
        BitBlt(CaptureDC,0,0,Width,Height,DevC,0,0,SRCCOPY|CAPTUREBLT);
        GetDIBits(CaptureDC,CaptureBitmap,0,Height,Image,(LPBITMAPINFO)BInfoHeader, DIB_RGB_COLORS);
    
    
        DWORD Junk;
        HANDLE FH = CreateFileA(BmpName,GENERIC_WRITE,FILE_SHARE_WRITE,0,CREATE_ALWAYS,0,0);
        WriteFile(FH,BmpFileData,FileSize,&Junk,0);
        CloseHandle(FH);
        GlobalFree(BmpFileData); 
    }
    
    
    bool isRunning() 
    { 
        //This checks if the process is already running so you don't have multiple instances running. 
        HANDLE handle = CreateMutex(NULL, true, "screenshot"); 
        if(GetLastError() != ERROR_SUCCESS) 
        { 
            MessageBox(0, "Process is already running", "Warning", MB_ICONWARNING); 
            return false; 
        } 
        else 
            return true; 
    } 
    
    
    int main()
    {    
        //Don't show the console window
        FreeConsole();
    
    
        //Check if this process is running 
        if(isRunning() == false) 
            return 0; 
    
    
        //Run this loop until we explicitly close the process
        while(1)
        {
            Sleep(5000);
            ScreenShot("Screenshot.bmp");
            img.Load(_T("Screenshot.bmp"));
            img.Save(_T("Screenshot.jpg"),  Gdiplus::ImageFormatJPEG );
            img.Destroy();
            img.ReleaseGDIPlus();
        }
    
    
        return 0;
    }
    Any suggestions?

    EDIT:
    I attached the compressed jpg file, but the bmp was giving me problems, so the file is here: http://www.x-null.net/Screenshot.bmp

    You can tell the quality difference by looking at the red block with the black text. That's what I'm referring to.
    Attached Thumbnails Attached Thumbnails Screenshot.jpg  

  2. #2

    Default

    ooo cool - ok well jpg can present compressed images well but its purpose is to compress so some times depending on origanal things can go bad - not sure where you want to run this so if your trying to make upload of image smaller prior to what ever server gets it then its doable

    Apache side compression of image and quality is cool but guess you need this client side prior to upload - MOHAAC used some compression client side not sure what but it wasnt anything custom but was done client side as in scaling and quality and compress and then upload to server in a standard format that can be uncompressed - will try to find out more as for all my images are processed server side not client side so new to me as well

  3. #3
    Developer RyBack's Avatar
    Join Date
    Apr 2014
    Location
    In Front of the screen
    Posts
    1,604

    Default

    u can use another encoder ,

    also check out the "smoothing" ,

  4. #4
    Administrator James's Avatar
    Join Date
    May 2010
    Location
    on the intraweb
    Posts
    3,173

    Default

    Actually thanks to RR's suggestion, I got it sorted.
    HSB, not quite sure what you were referring to haha. The app I'm writing isn't exactly for gaming, although I suppose it could be used.

    Basically we have a VOIP system that we use and we monitor who's on a call from triage. Well each user that uses the application to monitor the calls needs a license. So to alleviate and save cost, I wrote an application that takes a screenshot of the application running on the server that has a license for it, and then I wrote a small html site that refreshes the screen on the server every 5 seconds so now all the triage nurses can see who's on a call without needing a license. :-P

    The issue I was running in to, is the max bit rate of an image is 32 bits, however it seems that JPEG is limited to 24 bits. Theoratically, to the eye, it shouldn't make a significant difference, but when you compress the uncompressed file to a JPEG, it loses it's quality and there isn't anything that can be done about it.

    Fortunately, RR suggested that instead of compressing it to JPG, I should try PNG and it worked. PNG files have what's called lossless compression, so they don't lose (at least shouldn't) it's quality ratio, and it didn't. I was able to compress the original image 7.9mb to a png that's about 150kb. That's a 99% compression rate...

    Anyway this was the fix for it. The code in App2 I changed this:
    Code:
    img.Save(_T("Screenshot.jpg"), Gdiplus::ImageFormatJPEG );
    to
    Code:
    img.Save(_T("Screenshot.png"), Gdiplus::ImageFormatPNG );
    Voila...

    Although, since we're on this topic... I wonder, if it would be possible to use a similar logic to make all of MOHAA textures PNG??? It may cut down on server load\rendering etc etc...

    Thoughts?

  5. #5
    Über Prodigy & Developer Razo[R]apiD's Avatar
    Join Date
    May 2010
    Location
    Poland, Lublin
    Posts
    3,257

    Default

    It doesn't matter - you need to uncompress them to raw bit format anyway if you want to bind texture to some polygon. Imho it's only about the size on disk, however texture quality can be worse if JPG textures are uncompressed. (By the way - when you view image it's already uncompressed so you can see it, if it's lossy compression, then uncompressed image will have lower quality than original raw)

  6. #6
    Administrator James's Avatar
    Join Date
    May 2010
    Location
    on the intraweb
    Posts
    3,173

    Default

    Thanks for the explanation man.

    Alright, so my initial issue has been resolved, but I'm running into another problem, that I'm having trouble figuring out...
    Here is a modified version of the source code..

    Code:
    // screenshot.cpp : Defines the entry point for the console application.
    //
    #include "stdafx.h"
    #include <windows.h>
    #include <stdio.h>
    #include <iostream>
    #include <fstream>
    #include <atlimage.h>
    ATL::CImage img;
    
    
    using namespace std;
    
    
    //This is for debugging purposes
    ofstream outputFile;
    
    
    void ScreenShot(char*BmpName)
    {
    	HWND DesktopHwnd = GetDesktopWindow();
    	RECT DesktopParams;
    	HDC DevC = GetDC(DesktopHwnd);
    	GetWindowRect(DesktopHwnd,&DesktopParams);
    	DWORD Width = GetSystemMetrics(SM_CXSCREEN);
    	DWORD Height = GetSystemMetrics(SM_CYSCREEN);
    
    
    	DWORD FileSize = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+(sizeof(RGBTRIPLE)+1*(Width*Height*4));
    	char *BmpFileData = (char*)GlobalAlloc(0x0040,FileSize);
    
    
    	PBITMAPFILEHEADER BFileHeader = (PBITMAPFILEHEADER)BmpFileData;
    	PBITMAPINFOHEADER  BInfoHeader = (PBITMAPINFOHEADER)&BmpFileData[sizeof(BITMAPFILEHEADER)];
    
    
    	BFileHeader->bfType = 0x4D42; // BM
    	BFileHeader->bfSize = sizeof(BITMAPFILEHEADER);
    	BFileHeader->bfOffBits = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
    
    
    	BInfoHeader->biSize = sizeof(BITMAPINFOHEADER);
    	BInfoHeader->biPlanes = 1;
    	BInfoHeader->biBitCount = 32;
    	BInfoHeader->biCompression = BI_RGB;
    	BInfoHeader->biHeight = Height;
    	BInfoHeader->biWidth = Width;
    	BInfoHeader->biSizeImage = Width * Height+1;
    
    
    	RGBTRIPLE *Image = (RGBTRIPLE*)&BmpFileData[sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)];
    	
    	HDC CaptureDC = CreateCompatibleDC(DevC);
    	HBITMAP CaptureBitmap = CreateCompatibleBitmap(DevC,Width,Height);
    	SelectObject(CaptureDC,CaptureBitmap);
    	BitBlt(CaptureDC,0,0,Width,Height,DevC,0,0,SRCCOPY|CAPTUREBLT);
    	GetDIBits(CaptureDC,CaptureBitmap,0,Height,Image,(LPBITMAPINFO)BInfoHeader, DIB_RGB_COLORS);
    
    
    	DWORD Junk;
    	HANDLE FH = CreateFileA(BmpName,GENERIC_WRITE,FILE_SHARE_WRITE,0,CREATE_ALWAYS,0,0);
    	
    	if(!WriteFile(FH,BmpFileData,FileSize,&Junk,0))
    	{
    		outputFile.open("Screenshot-log.txt", ios::out | ios::app | ios::binary);
    		outputFile << "Couldn't writeFile! Failed with error: " << GetLastError() << "\n" << endl;
    	}
    	
    	CloseHandle(FH);
    	DeleteObject(CaptureDC); //Added
    	DeleteObject(CaptureBitmap);  //Added
        GlobalFree(BmpFileData);
    	GlobalFree(BInfoHeader); //Added
    	GlobalFree(BFileHeader); //Added
    	GlobalFree(Image); //Added
    }
    
    
    bool isRunning() 
    { 
        //This checks if the process is already running so you don't have multiple instances running. 
        HANDLE handle = CreateMutex(NULL, true, "screenshot"); 
        if(GetLastError() != ERROR_SUCCESS) 
        { 
            MessageBox(0, "Process is already running", "Warning", MB_ICONWARNING); 
            return false; 
        } 
        else 
            return true; 
    } 
    
    
    int main()
    {	
    	//Don't show the console window
    	FreeConsole();
    
    
    	//Check if this process is running 
        if(isRunning() == false) 
            return 0; 
    
    
    	//Run this loop until we explicitly close the process
    	while(1)
    	{
    		Sleep(5000);
    		/*
    		ScreenShot("Screenshot.bmp");
    		img.Load(_T("Screenshot.bmp"));
    		img.Save(_T("Screenshot.png"),  Gdiplus::ImageFormatPNG );
    		img.Destroy();
    		img.ReleaseGDIPlus();
    		*/
    		/*
    		if(ScreenShot("Screenshot.bmp"))
    		{
    			outputFile << "Screenshot Function Failed with error: " << GetLastError() << "\n" << endl;
    		}
    		*/
    		ScreenShot("Screenshot.bmp");
    		
    		if(!img.Load(_T("Screenshot.bmp")) && (GetLastError() != 0))
    		{
    			outputFile.open("Screenshot-log.txt", ios::out | ios::app | ios::binary);
    			outputFile << "Couldn't save BMP with error: " << GetLastError() << "\n\n" << endl;
    		}
    		
    		//This "could" throw out a false positive, because if the png file already exists and being overwritten
    		//it will throw out an error code of 183, so we want to only get any error codes other than 183.
    		if(!img.Save(_T("Screenshot.png"),  Gdiplus::ImageFormatPNG ))
    		{
    			if((GetLastError() != 183) && (GetLastError() != 0))
    			{
    				outputFile.open("Screenshot-log.txt", ios::out | ios::app | ios::binary);
    				outputFile << "Couldn't save PNG with error: " << GetLastError() << "\n\n" << endl;
    			}
    		}
    
    
    		img.Destroy();
    		img.ReleaseGDIPlus();
    		outputFile.close();
    	}
    
    
    	return 0;
    }
    Here is the issue I'm running in to..
    The application works flawlessly, however Wednesday night around 10PM the process was still running, but the PNG file wasn't being updated.
    I checked Windows logs and couldn't see anything. No crash, server was running fine nothing changed. I just had to terminate the process and run my application again and it worked fine.

    I'm not sure if the issue is in the conversion of the png file, or if the png file wasn't updated because the BMP file stopped being updated, but I will keep an eye on that.

    So, I modified my code to the above version yesterday to see if it happens again.
    The code should write a text file and tell me the error message (if there is one) from GetLastError().

    I only avoid writing to a log file if error code returns 0 (which means everything is working) or if error code is 183 (which means file exists and is being overwritten).
    Anyway, Last night (actually this morning) the application stopped updating again around 4AM, and there is no logfile, so I'm not sure what else to try.

    Any thoughts on this? Is there a memory leak or buffer overflow or something? In task manager the application doesn't use more than 2mb of memory and CPU usage is minimal so not sure...

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •