2015-10-23 2 views
3

je le code suivant pour créer une image bitmap:C++ bitmap à base64

//raw data 
PBYTE firstPixel = (PBYTE)((PBYTE)AnsiBdbRecord) + sizeof(WINBIO_BDB_ANSI_381_RECORD); 

// declare other bmp structures 
BITMAPFILEHEADER bmfh; 
BITMAPINFOHEADER info; 
RGBQUAD rq[256]; 
// create the grayscale palette 
for (int i = 0; i<256; i++) 
{ 
    rq[i].rgbBlue = i; 
    rq[i].rgbGreen = i; 
    rq[i].rgbRed = i; 
    rq[i].rgbReserved = 0; 
} 
//RGBQUAD bl = { 0,0,0,0 }; //black color 
//RGBQUAD wh = { 0xff,0xff,0xff,0xff }; // white color 

// andinitialize them to zero 
memset(&bmfh, 0, sizeof(BITMAPFILEHEADER)); 
memset(&info, 0, sizeof(BITMAPINFOHEADER)); 

// fill the fileheader with data 
bmfh.bfType = 0x4d42; // 0x4d42 = 'BM' 
bmfh.bfReserved1 = 0; 
bmfh.bfReserved2 = 0; 
bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); // + padding; 
bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD); 

// fill the infoheader 
info.biSize = sizeof(BITMAPINFOHEADER); 
info.biWidth = Width; 
info.biHeight = Height; 
info.biPlanes = 1; // we only have one bitplane 
info.biBitCount = PixelDepth; // RGB mode is 24 bits 
info.biCompression = BI_RGB; 
info.biSizeImage = 0; // can be 0 for 24 bit images 
info.biXPelsPerMeter = 0x0ec4; // paint and PSP use this values 
info.biYPelsPerMeter = 0x0ec4; 
info.biClrUsed = 0; // we are in RGB mode and have no palette 
info.biClrImportant = 0; // all colors are importantenter code here 

Et je l'enregistre comme suit:

HANDLE file = CreateFile(bmpfile, GENERIC_WRITE, FILE_SHARE_READ, 
    NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 
if (file == NULL) 
{ 
    DWORD dw = GetLastError(); 
    CloseHandle(file); 
} 

// write file header 
if (WriteFile(file, &bmfh, sizeof(BITMAPFILEHEADER), &bwritten, NULL) == false) 
{ 
    DWORD dw = GetLastError(); 
    CloseHandle(file); 
} 
// write infoheader 
if (WriteFile(file, &info, sizeof(BITMAPINFOHEADER), &bwritten, NULL) == false) 
{ 
    DWORD dw = GetLastError(); 
    CloseHandle(file); 
} 
//write rgbquad for black 
if (WriteFile(file, &rq, sizeof(rq), &bwritten, NULL) == false) 
{ 
    DWORD dw = GetLastError(); 
    CloseHandle(file); 
} 
// write image data 
if (WriteFile(file, &firstPixel[0], imageSize, &bwritten, NULL) == false) 
{ 
    DWORD dw = GetLastError(); 
    CloseHandle(file); 
} 

// and clean up 
CloseHandle(file); 

Je pense que ce qui précède est le moyen standard d'enregistrement d'images bitmap . Cependant, au lieu de sauvegarder l'image, je veux qu'elle soit disponible en tant que BASE64 et transmettez-la dans un message HTTP. Par conséquent, cette question concerne this one, mais j'ai beaucoup de difficultés à convertir la structure bmp en BASE64. J'ai pris l'encodeur BASE64 de here, mais je n'ai aucune idée comment passer la structure de données BMPFILEHEADER, BMPINFOHEADER, RGBQUAD, et de données brutes en tant que paramètre à l'encodeur BASE64.

Des idées ou des conseils sur la façon de combiner les informations que j'ai recueillies?

MISE À JOUR

Merci à Roman Pustylnikov, j'ai obtenu un peu plus loin déjà: Je crée une structure comme ceci:

struct ImageBuffer 
{ 
    BITMAPFILEHEADER bfheader; 
    BITMAPINFOHEADER infobmp; 
    RGBQUAD rgb[256]; 
    PBYTE bitmap; 
}; 

Remplissez comme suit:

ImageBuffer capture; 
capture.bfheader = bmfh; 
capture.infobmp = info; 
// create the grayscale palette 
for (int i = 0; i<256; i++) 
{ 
    capture.rgb[i].rgbBlue = i; 
    capture.rgb[i].rgbGreen = i; 
    capture.rgb[i].rgbRed = i; 
    capture.rgb[i].rgbReserved = 0; 
} 
capture.bitmap = firstPixel; 

Et le convertir comme suit:

int totalSize = sizeof(capture.bfheader) + sizeof(capture.infobmp) + sizeof(capture.rgb) + imageSize; 
std::string encodedImage = base64_encode(reinterpret_cast<const unsigned char*>(&capture), totalSize); 

Cependant, il me donne un bitmap invalide. De plus, lorsque je charge le bitmap à partir du disque (celui qui est généré avec writefile), j'obtiens une chaîne base64 différente. J'utilise le code C# pour comparer les deux chaînes Base64:

  // generated base64 string 
     string test = "Put base64string generated from C++ here"; 
     byte[] imageBytes = Convert.FromBase64String(test); 

     // generate the same string based on the actual bmp 
     byte[] data = File.ReadAllBytes(@"c:\successtest.bmp"); 
     string original = Convert.ToBase64String(data); 

MISE À JOUR DEUX: solution

La solution peut être trouvée dans la dernière mise à jour de la réponse de Roman Pustylnikov.

Répondre

1

Vous pouvez le faire sans file in the middle, en utilisant les éléments suivants example en tant que codeur/décodeur:

La première approche est de créer la mémoire contiguë et le mettre comme entrée:

int size=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256 + imageSize ; 

    unsigned char * bmpBuff = new char[size]; 
    int i = 0; 
    memcpy((void *)(&(bmpBuff[i])), (void *) &bfheader,sizeof(BITMAPFILEHEADER)); 
    i+=sizeof(BITMAPFILEHEADER); 
    memcpy((void *)(&(bmpBuff[i])), (void *) &infobmp,sizeof(BITMAPINFOHEADER)); 
    i+=sizeof(BITMAPINFOHEADER); 
    memcpy((void *)(&(bmpBuff[i])), (void *) &rq,sizeof(RGBQUAD)*256); 
    i+=sizeof(RGBQUAD)*256; 
    memcpy((void *)(&(bmpBuff[i])), (void *) firstPixel, imageSize); 
    std::string encodedImage = base64_encode(bmpBuff, size); 

Les inconvénients de cette approche est que vous devez dupliquer la mémoire.

Une autre approche consiste à gérer le "triplet perdu". Pour cela, nous aurons besoin de définir une structure:

struct ScreenShotBuffer 
{ 
    BITMAPFILEHEADER bfheader; 
    BITMAPINFO infobmp; 
    RGBQUAD rgb[256];  
}; 

Maintenant, voici la partie délicate. Comme le codage gère les triplets d'octets, vous devez gérer la frontière entre deux tampons (je n'ai pas testé cela pour qu'il contienne des bugs, juste une approche générale).

int size=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*256; 


unsigned char *info = einterpret_cast<const unsigned char*> &screenshotInfo; 
unsigned char *img = einterpret_cast<const unsigned char*> firstPixel; 

std::string encodedInfo = base64_encode(info , size); 
std::string encodedLostTriplet = ""; 

int offset = size%3; 

unsigned char lostTriplet[3]; 
    if (offset) { 
    lostTriplet[0]=info[size-offset]; 
    if (offset==2) 
     lostTriplet[1] = info[size-offset+1]; 
    else 
     lostTriplet[1] = img[0]; 
    lostTriplet[2] = img[2-offset]; 
    encodedLostTriplet = base64_encode(lostTriplet, 3); 
    } 
    else { 
     offset=3; 
    } 

std::string encodedData = base64_encode(reinterpret_cast<const unsigned char*> &img[3-offset], imageSize - (3 - offset)); 

std::string encodedImage = encodedInfo + lostTriplet + encodedData; 

Un peu brouillon mais devrait fonctionner sur la même mémoire.

+0

Cela semble aller dans la bonne direction. Je devrais concaténer le bitmapfileheader, infoheader, RGBQuad et rawdata ensemble d'abord alors? – Michael

+0

Je crois que vous pouvez utiliser la structure et concaténer les en-têtes avec l'image comme résultat. Puisque l'en-tête doit toujours être de la longueur constante (comme la longueur des en-têtes combinés et encodés), le reste sera la donnée d'image. Devrait être facile à décoder. J'ai mis à jour la réponse. –

+0

Je pense que vous êtes proche, même si cela ne fonctionne toujours pas. Voir la mise à jour de ma question pour plus de détails. – Michael