2017-06-13 6 views
0

Le but de mon programme est de faire une capture d'écran toutes les 1/16 seconde, et de l'envoyer par socket au serveur distant.puis-je convertir les données en mémoire bmp en des données png in-momory beaucoup plus petites?

ce programme peut fonctionner avec la capture d'écran au format BMP, cependant, les données au format BMP ont trop d'octets à envoyer, cela ralentit évidemment le processus d'envoi et de recv. Mon idée est la suivante: si je peux convertir le BMP en PNG, et le compresser avant d'envoyer, peut-être le programme peut fonctionner plus lisse.

ici est mon code, sélectionnez projet gh0st

LPVOID m_lpvFullBits = NULL; 
HDC m_hFullDC, m_hFullMemDC; 
LPBITMAPINFO m_lpbmi_full; 
m_hFullDC = GetDC(NULL); 
int m_nFullWidth = ::GetSystemMetrics(SM_CXSCREEN); 
int m_nFullHeight = ::GetSystemMetrics(SM_CYSCREEN); 
m_hFullMemDC = ::CreateCompatibleDC(m_hFullDC); 

m_lpbmi_full = (BITMAPINFO *) new BYTE[40]; 
BITMAPINFOHEADER *lpbmih = &(m_lpbmi_full ->bmiHeader); 
lpbmih->biSize = sizeof(BITMAPINFOHEADER); 
lpbmih->biWidth = m_nFullWidth ; 
lpbmih->biHeight = m_nFullHeight ; 
lpbmih->biPlanes = 1; 
lpbmih->biBitCount = 32;  // 32 bit per pixel 
lpbmih->biCompression = BI_RGB; 
lpbmih->biXPelsPerMeter = 0; 
lpbmih->biYPelsPerMeter = 0; 
lpbmih->biClrUsed = 0; 
lpbmih->biClrImportant = 0; 
lpbmih->biSizeImage = (((lpbmih->biWidth * lpbmih->biBitCount + 31) & ~31) >> 3) * lpbmih->biHeight; 

HBITMAP m_hFullBitmap = ::CreateDIBSection(m_hFullDC, m_lpbmi_full, DIB_RGB_COLORS, &m_lpvFullBits, NULL, NULL); 

::SelectObject(m_hFullMemDC, m_hFullBitmap); 

::BitBlt(m_hFullMemDC, 0, 0, m_nFullWidth, m_nFullHeight, m_hFullDC, 0, 0, SRCCOPY); 

jusqu'à cette déclaration:

::BitBlt(m_hFullMemDC, 0, 0, m_nFullWidth, m_nFullHeight, m_hFullDC, 0, 0, SRCCOPY); 

données de pixels BMP est enregistré à l'adresse de commencer m_lpvFullBits, non? Maintenant, ce que je veux savoir, est-ce que je peux utiliser les informations connues actuelles telles que la largeur, la hauteur, les données de pixel BMP .., reconstruire un nouveau format png en mémoire qui est beaucoup plus petit que les données BMP actuelles, et envoyer les nouvelles données de format png par socket au serveur distant?

merci pour toute aide

+1

Oui, c'est possible. Vous devrez utiliser un encodeur PNG à partir du [Composant Windows Imaging] (https://msdn.microsoft.com/fr-fr/library/windows/desktop/ee719902.aspx). Cela fonctionne sur une interface 'IStream', donc vous pouvez passer une implémentation de flux de mémoire. – IInspectable

+3

_convertir le BMP en PNG, et le compresser avant send_ ... pas besoin de ZIP après la conversion en PNG car le format PNG "zippe" déjà les données et il le fait encore plus efficacement que le ZIP. – zett42

+0

Gdiplus ne serait pas plus facile à utiliser pour enregistrer au png? Ou peut-il seulement enregistrer des PNG sur disque, pas de mémoire? –

Répondre

2

Oui, vous pouvez. À mon avis, GDI + est assez bon pour cette tâche. Tout d'abord, créez un Gdiplus::Bitmap à partir de HBITMAP, puis enregistrez-le dans un IStream. A partir du IStream, vous pouvez obtenir le nombre d'octets via IStream::Stat(). Enfin, lisez-le dans un tableau d'octets avec IStream::Read(). Maintenant, vous avez votre PNG en mémoire.

Voici un exemple minimal que vous pouvez copier et coller dans un fichier CPP et compiler. Notez que le code de gestion des erreurs et de nettoyage est omis.

#include <Windows.h> 
#include <gdiplus.h> 
#include <iostream> 
using namespace Gdiplus; 

#pragma comment(lib, "gdiplus.lib") // or you specify it in linker option 

IStream * PngMemStreamFrom(HBITMAP hbm) 
{ 
    // image/png : {557cf406-1a04-11d3-9a73-0000f81ef32e} 
    const CLSID clsidPngEncoder = 
     { 0x557cf406, 0x1a04, 0x11d3, 
     { 0x9a,0x73,0x00,0x00,0xf8,0x1e,0xf3,0x2e } }; 
    IStream *stream = NULL; 
    Bitmap *bmp = Bitmap::FromHBITMAP(hbm, NULL); 
    CreateStreamOnHGlobal(NULL, TRUE, &stream); 
    bmp->Save(stream, &clsidPngEncoder);  
    delete bmp; 
    return stream; 
} 

void ScreenshotTest(LPCWSTR szPath) 
{ 
    LPVOID m_lpvFullBits = NULL; 
    HDC m_hFullDC, m_hFullMemDC; 
    LPBITMAPINFO m_lpbmi_full; 
    m_hFullDC = GetDC(NULL); 
    int m_nFullWidth = ::GetSystemMetrics(SM_CXSCREEN); 
    int m_nFullHeight = ::GetSystemMetrics(SM_CYSCREEN); 
    m_hFullMemDC = ::CreateCompatibleDC(m_hFullDC); 

    m_lpbmi_full = (BITMAPINFO *) new BYTE[40]; 
    BITMAPINFOHEADER *lpbmih = &(m_lpbmi_full ->bmiHeader); 
    lpbmih->biSize = sizeof(BITMAPINFOHEADER); 
    lpbmih->biWidth = m_nFullWidth ; 
    lpbmih->biHeight = m_nFullHeight ; 
    lpbmih->biPlanes = 1; 
    lpbmih->biBitCount = 32;  // 32 bit per pixel 
    lpbmih->biCompression = BI_RGB; 
    lpbmih->biXPelsPerMeter = 0; 
    lpbmih->biYPelsPerMeter = 0; 
    lpbmih->biClrUsed = 0; 
    lpbmih->biClrImportant = 0; 
    lpbmih->biSizeImage = (((lpbmih->biWidth * lpbmih->biBitCount + 31) & ~31) >> 3) * lpbmih->biHeight; 

    HBITMAP m_hFullBitmap = ::CreateDIBSection(m_hFullDC, m_lpbmi_full, DIB_RGB_COLORS, &m_lpvFullBits, NULL, NULL); 
    ::SelectObject(m_hFullMemDC, m_hFullBitmap); 
    ::BitBlt(m_hFullMemDC, 0, 0, m_nFullWidth, m_nFullHeight, m_hFullDC, 0, 0, SRCCOPY); 

    IStream *stream = PngMemStreamFrom(m_hFullBitmap); 

    STATSTG stat = {0}; 
    stream->Stat(&stat, STATFLAG_NONAME); 
    UINT64 cbSize = stat.cbSize.QuadPart; 
    std::cout << "mem stream byte count = " << cbSize << "\n"; 
    LPBYTE buffer = new BYTE[cbSize]; 
    // IMPORTANT! must seek to offset zero before read it 
    LARGE_INTEGER offZero = {0}; 
    stream->Seek(offZero, STREAM_SEEK_SET, NULL); 
    stream->Read(buffer, cbSize, NULL); 
    // do something with buffer, such as save to disk 
    HANDLE hfile = CreateFile(szPath, GENERIC_WRITE, 0, NULL, 
     CREATE_ALWAYS, 0, NULL); 
    DWORD cbWritten = 0; 
    WriteFile(hfile, buffer, cbSize, &cbWritten, NULL); 
    CloseHandle(hfile); 
    // TODO: release m_hFullDC, m_hFullMemDC... here 
} 

int main() 
{ 
    ULONG_PTR token = NULL; 
    GdiplusStartupInput gdipIn; 
    GdiplusStartupOutput gdipOut; 
    GdiplusStartup(&token, &gdipIn, &gdipOut); 
    ScreenshotTest(L"D:\\Test.png"); 
    GdiplusShutdown(token); 
    return 0; 
} 
+0

merci pour votre réponse, laissez-moi essayer et comparer avant après la taille – yangl

+0

cela peut fonctionner, après convertir, la taille du tampon est seulement original taille du fichier BMP 1/10, btw, après avoir envoyé le tampon au serveur , comment dessiner sur Windows, utilisez toujours bitblt? – yangl

+0

@yangl 'BitBlt' ne peut pas dessiner de PNG directement. Si vous voulez vraiment le 'BitBlt', vous devez écrire le tampon dans un' IStream * ', chercher à zéro, créer un bitmap à partir du flux avec' bmp = Gdiplus :: Bitmap :: FromStream (...) ', puis' bmp-> GetHBITMAP() 'et' BitBlt' le 'HBITMAP'. – raymai97