[Tutorial & Source] GDiPlus no Visual C++ 6.0, tirando Screenshot em JPG.

Iniciado por keenlanas, 21 de Setembro , 2009, 05:34:44 PM

tópico anterior - próximo tópico

0 Membros e 1 Visitante estão vendo este tópico.

keenlanas

Bom, esse tempos (uns 3 ou 4 meses) eu tava procurando um jeito simples e tranquilo de converter uma imagem BMP para uma imagem JPG.

Acho que nunca usei tanto o google pra uma coisa. Com direito a perguntar aos professores da faculdade. Todos indicavam bibliotecas prontas e tudo mais, como OpenCV e ImageMagik, mas todas elas precisam do acompanhamento da DLL. Ou seja, se você quiser simplesmente converter uma BMP pra uma JPG você precisa daquela DLL incomoda de 150kb com mais 20 funções que você não vai usar.

Pesquisando mais e mais, fiquei sabendo que o Windows tem uma DLL (gdiplus.dll) que já tem funções que você pode usar pra converter uma imagem de BMP pra JPG, entre outros formatos.

Exatamente o que eu estava precisando. Assim eu não precisaria passar uma DLL junto com o arquivo EXE que eu queria passar.

Para isso usa-se o SDK da Microsoft, um conjunto de ferramentas para desenvolvedores windows. Além de poder trabalhar com imagens, tem também manipulação de webcams, de videos e outras coisas interessantes...

Só que feito para Visual C++ e ainda cheio de erros em alguns includes. Totalmente fora dos padrões. É triste, mas é um fato. EXTREMAMENTE dificil de fazer funcionar no Dev C++ (tentei, sem sucesso)

Então resta usar o Visual C++, mas é por uma boa (???) causa.  ::)

Como isso deve ser uma questão comum para aplicativos que tiram print, gravam imagens de web cams e tudo mais, resolvi compartilhar como fazer pra tudo funcionar direitinho, com direito a exemplo e tudo mais.   ;)

Primeiramente, é preciso baixar e instalar o MS VC++ 6.0 (É o que eu recomendo por não usar frameworks .net e ser levim) OU o .Net, e nesse caso você nem precisa ler esse tutorial, praticamente.

Depois, baixar e instalar o SDK da Microsoft (Sim, é muito pesado. Deve ter TODAS as dlls do windows ae, pelamor de deos)

OU, se você quiser SÓ trablhar com imagens, você pode só baxar os includes Gdiplus*.h e GdiPlus.Lib daqui.

Pra quem pegar o MS VC++, é preciso baxar esse mesmo arquivo, pq aqui tem a gdiplus.lib compilada como queremos, sem stress. Provavelmente, se você usar a que está no SDK, você terá um erro de que a biblioteca (.lib, nesse caso) precisa ser recompilada.

Agora que já temos tudo o que precisamos, iremos ao trabalho principal.

Abra o VC++ vá em Tools -> Options -> Directories

Adicione as pastas de Lib, Bin e Include do SDK em Library Files, Executable Files e Include Files, para que o VC++ encontre os arquivos.

Crie um novo arquivo cpp em File->New->Files->C++ Source File

Copie e cole o código a seguir, que é um exemplo de printscreen que é salvo em .JPG, e o arquivo ficando com 100kb, já prints em .BMP os printscreens ficam com 3.01MB!!


#include <stdio.h>
#include <windows.h>
#include <winuser.h>
#include <shlwapi.h>
#include <time.h>
#include <sys/types.h>
#include <Gdiplus.h>
#include <GdiPlusEnums.h>


#define MAX_BUF_SIZE 1024



#pragma comment(lib, "Gdi32.lib")
#pragma comment(lib, "User32.lib")
#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "shell32.lib")


using namespace Gdiplus;


DWORD sendScreenCaptureInfos();
int CreateBMPFile(HWND hwnd, LPTSTR pOutputFileName, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC);
int convertBMP2JPG(char *pBMPFile, char *pJPGFile);
int GetCodecClsid(const WCHAR* pFormat, CLSID* pClsid);
PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp);

int main(void)
{
 int lRetVal = 0;
 char *lBMPFileFullPath = "PrintScreen.bmp";
 char *lJPGFileFullPath = "PrintScreen.jpg";
 PBITMAPINFO lPBI;
 SIZE lSize;
 HWND hWND;
 HDC hDC;
 HDC hMemDC;
 HBITMAP hBitmap;
 HBITMAP hOld;



 
 hDC = GetDC(NULL);
 
 hMemDC = CreateCompatibleDC(hDC);
 
 hWND = GetDesktopWindow();

 lSize.cx = GetSystemMetrics(SM_CXSCREEN);
 lSize.cy = GetSystemMetrics(SM_CYSCREEN);


 

 if (hBitmap = CreateCompatibleBitmap(hDC, lSize.cx, lSize.cy))
 {
   
   hOld = (HBITMAP) SelectObject(hMemDC, hBitmap);

   
   BitBlt(hMemDC, 0, 0, lSize.cx, lSize.cy, hDC, 0, 0, SRCCOPY);

   lPBI = CreateBitmapInfoStruct(hWND, hBitmap);
   
   if (lPBI != NULL)
   {
     if (CreateBMPFile(hWND, (LPTSTR) lBMPFileFullPath, lPBI, hBitmap, hDC) == 0)
     {

       convertBMP2JPG(lBMPFileFullPath, lJPGFileFullPath);
       ShellExecute(NULL, "open", lJPGFileFullPath, NULL, NULL, 0);
       DeleteFile(lBMPFileFullPath);
     }
   }

   SelectObject(hMemDC, hOld);
   DeleteDC(hMemDC);
   ReleaseDC(NULL, hDC);
   DeleteObject(hBitmap);
   LocalFree(lPBI);

 }


 return(lRetVal);
}



int CreateBMPFile(HWND hwnd, LPTSTR pOutputFileName, PBITMAPINFO pPBI, HBITMAP hBMP, HDC hDC)
{
 BITMAPFILEHEADER lBFH;
 PBITMAPINFOHEADER lBIH = (PBITMAPINFOHEADER) pPBI;
 LPBYTE lBits = NULL;
 int lRetVal = 0;
 HANDLE lFileHandle = INVALID_HANDLE_VALUE;
 DWORD lBytesWritten = 0;


 if (!(lBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, lBIH->biSizeImage)))
 {
   lBits = NULL;
   lRetVal = 1;
   goto END;
 }

 
 if (!GetDIBits(hDC, hBMP, 0, (WORD) lBIH->biHeight, lBits, pPBI, DIB_RGB_COLORS))
 {
   lRetVal = 2;
   goto END;
 }

 if ((lFileHandle = CreateFile(pOutputFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
 {
   lRetVal = 3;
   goto END;
 }


 lBFH.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M"

 lBFH.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + lBIH->biSize + lBIH->biClrUsed * sizeof(RGBQUAD) + lBIH->biSizeImage);
 lBFH.bfReserved1 = 0;
 lBFH.bfReserved2 = 0;

 
 lBFH.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + lBIH->biSize + lBIH->biClrUsed  * sizeof (RGBQUAD);

 
 WriteFile(lFileHandle, &lBFH, sizeof(BITMAPFILEHEADER), &lBytesWritten, NULL);
 
 WriteFile(lFileHandle, lBIH, sizeof(BITMAPINFOHEADER) + lBIH->biClrUsed * sizeof (RGBQUAD), &lBytesWritten, NULL);
 
 WriteFile(lFileHandle, lBits, lBIH->biSizeImage, &lBytesWritten, NULL);

END:

 

 if (lBits != NULL)
   GlobalFree((HGLOBAL) lBits);

 if (lFileHandle != INVALID_HANDLE_VALUE)
   CloseHandle(lFileHandle);

 return(lRetVal);
}


PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp)
{
 BITMAP lBitMap;
 PBITMAPINFO lBMI = NULL;
 WORD lCtrlBits;


 
 if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&lBitMap))
   goto END;


 
 lCtrlBits = (WORD)(lBitMap.bmPlanes * lBitMap.bmBitsPixel);
 if (lCtrlBits == 1)
   lCtrlBits = 1;
 else if (lCtrlBits <= 4)
   lCtrlBits = 4;
 else if (lCtrlBits <= 8)
   lCtrlBits = 8;
 else if (lCtrlBits <= 16)
   lCtrlBits = 16;
 else if (lCtrlBits <= 24)
   lCtrlBits = 24;
 else lCtrlBits = 32;


 if (lCtrlBits != 24)
   lBMI = (PBITMAPINFO) LocalAlloc(LPTR,sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1<< lCtrlBits));
 
 else
   lBMI = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));

 

 lBMI->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
 lBMI->bmiHeader.biWidth = lBitMap.bmWidth;
 lBMI->bmiHeader.biHeight = lBitMap.bmHeight;
 lBMI->bmiHeader.biPlanes = lBitMap.bmPlanes;
 lBMI->bmiHeader.biBitCount = lBitMap.bmBitsPixel;

 if (lCtrlBits < 24)
   lBMI->bmiHeader.biClrUsed = (1<<lCtrlBits);

 lBMI->bmiHeader.biCompression = BI_RGB;


 lBMI->bmiHeader.biSizeImage = ((lBMI->bmiHeader.biWidth * lCtrlBits +31) & ~31) /8 * lBMI->bmiHeader.biHeight;
 
 lBMI->bmiHeader.biClrImportant = 0;

END:

 return(lBMI);
}

int convertBMP2JPG(char *pBMPFile, char *pJPGFile)
{
 CLSID lCodecClsid;
 EncoderParameters lEncoderParameters;
 long lQuality = 80;
 GdiplusStartupInput lGdiplusStartupInput;
 ULONG_PTR lGdiplusToken;
 WCHAR lWCBMPFile[MAX_BUF_SIZE + 1];
 WCHAR lWCJPGFile[MAX_BUF_SIZE + 1];
 int lRetVal = 0;


 ZeroMemory(lWCBMPFile, sizeof(lWCBMPFile));
 ZeroMemory(lWCBMPFile, sizeof(lWCJPGFile));

 mbstowcs(lWCBMPFile, pBMPFile, sizeof(lWCBMPFile) / sizeof(WCHAR));
 mbstowcs(lWCJPGFile, pJPGFile, sizeof(lWCJPGFile) / sizeof(WCHAR));

 GdiplusStartup(&lGdiplusToken, &lGdiplusStartupInput, NULL);
 {
   Image image(lWCBMPFile);

   
   if (GetCodecClsid(L"image/jpeg", &lCodecClsid) >= 0)
   {
     lEncoderParameters.Count = 1;
     lEncoderParameters.Parameter[0].Guid = EncoderQuality;
     lEncoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
     lEncoderParameters.Parameter[0].NumberOfValues = 1;
     lEncoderParameters.Parameter[0].Value = &lQuality;

     if (image.Save(lWCJPGFile, &lCodecClsid, &lEncoderParameters) != Ok)
       lRetVal = 1;
   }
 }

 GdiplusShutdown(lGdiplusToken);

 return(lRetVal);
}


int GetCodecClsid(const WCHAR* pFormat, CLSID* pClsid)
{
 UINT lNumberOfEncoders = 0;
 UINT lArraySize = 0;
 ImageCodecInfo* lImageCodecInfo = NULL;
 int lRetVal = 0;
 int lCounter = 0;

 GetImageEncodersSize(&lNumberOfEncoders, &lArraySize);
 if(lArraySize == 0)
 {
   lRetVal = -1;
   goto END;
 }


 if ((lImageCodecInfo = (ImageCodecInfo*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lArraySize)) == NULL)
 {
   lRetVal = -2;
   goto END;
 }

 GetImageEncoders(lNumberOfEncoders, lArraySize, lImageCodecInfo);

 for(lCounter = 0; lCounter < lNumberOfEncoders; ++lCounter)
 {
   if(wcscmp(lImageCodecInfo[lCounter].MimeType, pFormat) == 0 )
   {
     *pClsid = lImageCodecInfo[lCounter].Clsid;
     lRetVal = lCounter;
     goto END;
   }
 }

END:
 if (lImageCodecInfo != NULL)
   HeapFree(GetProcessHeap(), 0, lImageCodecInfo);


 return(lRetVal);
}


Se você tentar compilar o código agora, ele pedirá para que você adicione a um Workspace. Faça isso e coloque "Sim". É bem mais pratico do que criar um Workspace primeiro.

Até aqui, tudo bem, tudo certinho... Agora vem a parte MacGyver.

Quem foi apressadinho e já tentou compilar viu que deu vários erros, um dos primeiro dizendo que ULONG_PTR não foi defino.

Pois bem, nós vamos defini-lo.

Vá até o diretório onde você instalou o SDK e abra a pasta Includes. Procure pelo arquivo "GDIPlusInit.h".

Abra-o e coloque essa linha depois de #define _GDIPLUSINIT_H


typedef unsigned __int32 ULONG_PTR, *PULONG_PTR;


Você verá que vai ficar somente um ou dois Warnings... Dos 19 Erros que davam.  :D

Porém, quando você for rodar o programa vai ter um erro... De linker. Dizendo que a gdiplus.lib foi corrompida ou coisa do tipo. Isso foi pq nós demos um novo tipo de dado pra o include que a usa.

O correto seria recompilarmos. Mas, somos preguiçosos, claro... Eu vi que a GdiPlus.lib que tem naquele arquivo .zip que falei lá em cima já está com esse erro consertado, então baixem esses Kbs a mais, extraiam e substituam a GdiPlus.Lib que tem na pasta Lib do SDK pela que tem no arquivo .zip

Compilem e rodem e vocês teram um print em JPG.

Chega de prints com 3mb! :D

Ps.1 Não tem garantias que de a Lib do arquivo Zip tenha TODAS as funções necessarias, no caso, ela tem as que servem à esse tutorial... Se você quiser usar mais do GDIPLus, recomendo que recompile a Lib.

Ps. 2 Se alguém souber uma maneira mais fácil, por favor, poste!