BIts Per Sample / Pixel libtiff vs WIC - c++

TIFF *TiffImage;
uint16 photo, bps, spp, fillorder;
uint32 width,height;
unsigned long stripSize;
unsigned long imageOffset, result;
int stripMax, stripCount;
unsigned char *buffer, tempbyte;
unsigned short *buffer16;
unsigned int *buffer32;
unsigned long bufferSize, count;
bool success = true;
int shiftCount = 0;
//read image to InData
const char *InFileName = fileName.c_str();
if((TiffImage = TIFFOpen(InFileName, "r")) == NULL){
ErrMsg("Could not open incoming image\n");
return false;
}
// Check that it is of a type that we support
if(TIFFGetField(TiffImage, TIFFTAG_BITSPERSAMPLE, &bps) == 0) {
ErrMsg("Either undefined or unsupported number of bits per sample\n");
return false;
}
TBitPrecision bitPrecision = (TBitPrecision)bps;
char* imageDesc = NULL;
TIFFGetField(TiffImage, TIFFTAG_IMAGEDESCRIPTION, &imageDesc);
// Get actual bit precision for CP Images
if (bps > 8 && bps <= 16)
{
if (GetCpTiffTag(imageDesc, CP_TIFFTAG_BITPRECISION, (uint32*)&bitPrecision) == true)
{
shiftCount = 16 - bitPrecision;
}
}
In my libtiff implementation I have used 12 bit per pixel image and also 10 bpp
it is very easy to set this info in libtiff
I can't find a similar way to do so in WIC
uint16_t photo, bps, spp, fillorder;
uint32_t width,height;
unsigned long stripSize;
unsigned long imageOffset, result;
int stripMax, stripCount;
unsigned char *buffer, tempbyte;
unsigned short *buffer16;
unsigned int *buffer32;
unsigned long bufferSize, count;
bool success = true;
int shiftCount = 0;
//read image to InData
const char *InFileName = fileName.c_str();
IWICImagingFactory* piFactory = NULL;
// Create WIC factory
HRESULT hr = CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&piFactory)
);
// Create a decoder
IWICBitmapDecoder *pIDecoder = NULL;
IWICBitmapFrameDecode *pIDecoderFrame = NULL;
std::wstring ws;
ws.assign(fileName.begin(), fileName.end());
// get temporary LPCWSTR (pretty safe)
LPCWSTR pcwstr = ws.c_str();
hr = piFactory->CreateDecoderFromFilename(
pcwstr, // Image to be decoded
NULL, // Do not prefer a particular vendor
GENERIC_READ, // Desired read access to the file
WICDecodeMetadataCacheOnDemand, // Cache metadata when needed
&pIDecoder // Pointer to the decoder
);
// Retrieve the first bitmap frame.
if (SUCCEEDED(hr))
{
hr = pIDecoder->GetFrame(0, &pIDecoderFrame);
}
else
{
ErrMsg("Could not open incoming image\n");
}
return true;
Am I suppose to use EXIF or XMP? how do I find the TIFFTAG's for WIC?

The DirectXTex library has lots of examples of using WIC from C++.
You need something like:
using Microsoft::WRL::ComPtr;
ComPtr<IWICMetadataQueryReader> metareader;
hr = pIDecoderFrame->GetMetadataQueryReader(metareader.GetAddressOf());
if (SUCCEEDED(hr))
{
PROPVARIANT value;
PropVariantInit(&value);
if (SUCCEEDED(metareader->GetMetadataByName(L"/ifd/{ushort=258}", &value))
&& value.vt == VT_UI2)
{
// BitsPerSample is in value.uiVal
}
PropVariantClear(&value);
}
You should get in the habit of using a smart-pointer like ComPtr rather than using raw interface pointers to keep the ref counts correct.

Related

No Sound When Playing Back PCM-Decoded Audio

I am reading AAC audio frames which I then decode to PCM with Media Foundation and am trying to play back through WASAPI. Particularly 48000khz 2 channels, 16 bit. I am able to decode the frames, write them to a file full.pcm, and then open and play that PCM file successfully in Audacity. However, my code to play back through the device speakers gives me nothing. The source I am trying to play through is the default source, which is my DAC. I am not getting any bad HRESULTS from any of the WASAPI-related code, so I'm confused. WASAPI is new to me though, so maybe there is something obvious I am missing.
#include "AudioDecoder.h"
#include <vector>
#include <__msvc_chrono.hpp>
#include <string>
#include <fstream>
#include <cassert>
#include <filesystem>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <functiondiscoverykeys.h>
#include <audioclient.h>
int fps_counter = 0;
int frame_index = 0;
IAudioClient* audio_client;
IAudioRenderClient* render_client = nullptr;
int setup_audio_playback()
{
HRESULT hr = S_OK;
IMMDeviceEnumerator* pEnumerator = nullptr;
IMMDevice* pDevice = nullptr;
ATLENSURE_SUCCEEDED(CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator));
ATLENSURE_SUCCEEDED(pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice));
IPropertyStore* ips;
ATLENSURE_SUCCEEDED(pDevice->OpenPropertyStore(STGM_READ, &ips));
PROPVARIANT varName;
// Initialize container for property value.
PropVariantInit(&varName);
ATLENSURE_SUCCEEDED(ips->GetValue(PKEY_Device_FriendlyName, &varName));
std::wcout << L"Device name: " << varName.pwszVal << std::endl;
ATLENSURE_SUCCEEDED(pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&audio_client));
WAVEFORMATEX* format;
ATLENSURE_SUCCEEDED(audio_client->GetMixFormat(&format));
ATLENSURE_SUCCEEDED(audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, 10000000, 0, format, NULL));
uint32_t bufferFrameCount;
ATLENSURE_SUCCEEDED(audio_client->GetBufferSize(&bufferFrameCount));
ATLENSURE_SUCCEEDED(audio_client->GetService(__uuidof(IAudioRenderClient), (void**)&render_client));
ATLENSURE_SUCCEEDED(audio_client->Start());
return hr;
}
int main()
{
HRESULT hr = S_OK;
std::ofstream fout_all_frames_pcm;
std::filesystem::remove(std::filesystem::current_path() / "full.pcm");
fout_all_frames_pcm.open("full.pcm", std::ios::binary | std::ios::out);
if (FAILED(hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED)))
return hr;
if (FAILED(hr = MFStartup(MF_VERSION)))
return hr;
setup_audio_playback();
AudioDecoder* ad = new AudioDecoder();
std::vector<uint8_t> data;
while (true)
{
std::chrono::time_point<std::chrono::steady_clock> iteration_time = std::chrono::high_resolution_clock::now();
// Read frame data
std::ifstream fin("Encoded Audio Frames\\frame" + std::to_string(frame_index) + ".aac", std::ios::binary | std::ios::in);
if (fin.fail())
{
//throw std::runtime_error("Invalid file path specified");
break;
}
// Get file length
fin.seekg(0, std::ios::end);
size_t const length = fin.tellg();
fin.seekg(0, std::ios::beg);
if (length > data.size())
{
static size_t constexpr const granularity = 64 << 10;
data.resize((length + (granularity - 1)) & ~(granularity - 1));
assert(length <= data.size());
}
// Copy frame data from file to array;
fin.read(reinterpret_cast<char*>(data.data()), length);
fin.close();
CComPtr<IMFSample> pcm_sample;
while (!ad->decode_sync(data.data(), length, &pcm_sample))
{
if (pcm_sample == nullptr) // This will happen if the color converter isn't able to produce output, so we will continue in that case
continue;
CComPtr<IMFMediaBuffer> buffer;
if (FAILED(hr = pcm_sample->ConvertToContiguousBuffer(&buffer)))
return hr;
unsigned char* datas;
DWORD length;
if (FAILED(hr = buffer->GetCurrentLength(&length)))
return hr;
if (FAILED(hr = buffer->Lock(&datas, nullptr, &length)))
return hr;
fout_all_frames_pcm.write((char*)datas, length);
// Does nothing
//Sleep(120);
// Grab all the available space in the shared buffer.
uint8_t* pData;
ATLENSURE_SUCCEEDED(render_client->GetBuffer(1, &pData));
memcpy(pData, datas, length);
DWORD flags = 0;
ATLENSURE_SUCCEEDED(render_client->ReleaseBuffer(1, flags));
pcm_sample.Release();
}
frame_index++;
}
audio_client->Stop();
return 0;
}
Doing
render_client->GetBuffer(1, ...
will not give you any stable behavior because you are trying to submit data sample by sample. Literally, one PCM sample of your 48000 samples per second. Of course, the code is likely to be broken more than this because you seem to be simply losing most of the data getting much more from decoder and feeding just one sample to the device.
You would want to check this article in the part where the code identifies how many samples the GetBuffer will carry and then loop with filling those buffers accurately until you consume your IMFsample data.
How large those buffers are, those you obtain with GetBuffer? For 10 ms buffers which are pretty typical and 48 kHz sampling rate, you would have 480 samples per buffer. With stereo and 16-bit PCM you have four bytes per sample and so you would be delivering around 2K bytes every GetBuffer/ReleaseBuffer iteration.

How to write an image to STDOUT with Wincodec.h (WIC)

I came across this example and couldn't figure out how to change stream->InitializeFromFilename to something that outputs to STDOUT, inside the HRESULT SavePixelsToFile32bppPBGRA(UINT width, UINT height, UINT stride, LPBYTE pixels, LPWSTR filePath, const GUID &format) function. Tried using stream->InitializeFromMemory but hit a wall due to minimal C++ experience.
Reasons behind this is that i'm building something and would like to pipe the stdout of the example using something else.
Regards in advance
Thanks to #SimonMourier, I was able to update his code to achieve what was needed.
SavePixelsToFile32bppPBGRA now writes the data to an IStream pointer, streamOut. It also invokes a new function at the end, to output data.
HRESULT SavePixelsToFile32bppPBGRA(UINT width, UINT height, UINT stride, LPBYTE pixels, const GUID& format)
{
if (!pixels)
return E_INVALIDARG;
HRESULT hr = S_OK;
IWICImagingFactory* factory = nullptr;
IWICBitmapEncoder* encoder = nullptr;
IWICBitmapFrameEncode* frame = nullptr;
IWICStream* streamOut = nullptr;
IStream * streamIn = nullptr;
GUID pf = GUID_WICPixelFormat32bppPBGRA;
BOOL coInit = CoInitialize(nullptr);
HRCHECK(CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory)));
HRCHECK(factory->CreateStream(&streamOut));
HRCHECK(CreateStreamOnHGlobal(NULL, true, &streamIn));
HRCHECK(streamOut->InitializeFromIStream(streamIn));
HRCHECK(factory->CreateEncoder(format, nullptr, &encoder));
HRCHECK(encoder->Initialize(streamOut, WICBitmapEncoderNoCache));
HRCHECK(encoder->CreateNewFrame(&frame, nullptr)); // we don't use options here
HRCHECK(frame->Initialize(nullptr)); // we dont' use any options here
HRCHECK(frame->SetSize(width, height));
HRCHECK(frame->SetPixelFormat(&pf));
HRCHECK(frame->WritePixels(height, stride, stride * height, pixels));
HRCHECK(frame->Commit());
HRCHECK(encoder->Commit());
sendIStreamToOutput(streamOut);
cleanup:
RELEASE(streamIn);
RELEASE(streamOut);
RELEASE(frame);
RELEASE(encoder);
RELEASE(factory);
if (coInit) CoUninitialize();
return hr;
}
sendIStreamToOutput reads the data from the IStream and writes it to std::cout (with binary mode active).
void sendIStreamToOutput (IStream * pIStream) {
STATSTG sts;
pIStream->Stat(&sts, STATFLAG_DEFAULT);
ULARGE_INTEGER uli = sts.cbSize;
LARGE_INTEGER zero;
zero.QuadPart = 0;
ULONG size = (ULONG)uli.QuadPart;
char* bits = new char[size];
ULONG written;
pIStream->Seek(zero, STREAM_SEEK_SET, NULL);
pIStream->Read(bits, size, &written);
std::ostream& lhs = std::cout;
_setmode(_fileno(stdout), _O_BINARY);
lhs.write(bits, size);
fflush(stdout);
delete[] bits;
}

How to convert IStream to Base64 string in c++

I have IStream and I want to convert it to base 64 string in c++ so that I can use it in C++/cli to convert it to .net stream.
Similar way, I want to know how to convert base 64 string back to IStream.
My code uses ATL CString...
template<class T>
HRESULT StreamToBase64(IStream* pStream, T &strOut)
{
// Clear buffer
strOut.ReleaseBuffer(0);
if (!pStream)
return S_OK;
// Get size
STATSTG stat;
pStream->Stat(&stat, STATFLAG_NONAME);
unsigned iLen = stat.cbSize.LowPart;
// Seek to start
LARGE_INTEGER lPos;
lPos.QuadPart = 0;
HRESULT hr = pStream->Seek(lPos, SEEK_SET, nullptr);
if (FAILED(hr))
return hr;
// Reserve the memory
strOut.GetBuffer(Mfx::BinToBase64GetRequiredLength(iLen));
strOut.ReleaseBuffer(0);
// Use one line to decode a time. On line of Base64 code has usually 39 chars. We
// use a nearly 1kb buffer
const int iBase64LIneLen = 76/4*3;
BYTE bLine[(1024/iBase64LIneLen)*iBase64LIneLen];
ULONG bRead;
while (SUCCEEDED(hr = pStream->Read(bLine, sizeof(bLine), &bRead)) && bRead!=0)
strOut += T(Mfx::BinToBase64A(bLine, bRead));
// And seek back to start
pStream->Seek(lPos, SEEK_SET, nullptr);
return SUCCEEDED(hr) ? S_OK : hr;
}
HRESULT StreamToBase64(IStream* pStream, CStringA &strOut)
{
return StreamToBase64<CStringA>(pStream, strOut);
}
HRESULT StreamToBase64(IStream* pStream, CStringW &strOut)
{
return StreamToBase64<CStringW>(pStream, strOut);
}

Serialize: CArchive a CImage

I am currently trying to figure out how to properly store a CImage file within a CArchive (JPEG). My current approach to this is the following (pseudo) code:
BOOL CPicture::Serialize(CArchive &ar)
{
IStream *pStream = NULL;
HRESULT hr;
CImage *img = GetImage();
if (ar.IsLoading())
{
HGLOBAL hMem = GlobalAlloc(GMEM_FIXED, 54262);
hr = CreateStreamOnHGlobal(hMem, FALSE, &pStream);
if(SUCCEEDED(hr))
{
ar.Read(pStream, 54262);
img->Load(pStream);
pStream->Release();
GlobalUnlock(hMem);
GlobalFree(hMem);
}
}
else
{
hr = CreateStreamOnHGlobal(0, TRUE, &pStream);
if (SUCCEEDED(hr))
{
hr = img->Save(pStream, Gdiplus::ImageFormatJPEG);
if (SUCCEEDED(hr))
ar.Write(pStream, 54262);
}
}
...
I am just now getting back into C++ and have only done a little with it in the past. Any help would be very much appreciated.
Thank you very much in advance.
I'm not an expert on IStream, but I think you may not be using it correctly. The following code seems to work. It currently archives in png format, but it can easily be changed through Gdiplus::ImageFormatPNG.
It owes a lot to the tutorial "Embracing IStream as just a stream of bytes" by S. Arman on CodeProject.
void ImageArchive(CImage *pImage, CArchive &ar)
{
HRESULT hr;
if (ar.IsStoring())
{
// create a stream
IStream *pStream = SHCreateMemStream(NULL, 0);
ASSERT(pStream != NULL);
if (pStream == NULL)
return;
// write the image to a stream rather than file (the stream in this case is just a chunk of memory automatically allocated by the stream itself)
pImage->Save(pStream, Gdiplus::ImageFormatPNG); // Note: IStream will automatically grow up as necessary.
// find the size of memory written (i.e. the image file size)
STATSTG statsg;
hr = pStream->Stat(&statsg, STATFLAG_NONAME);
ASSERT(hr == S_OK);
ASSERT(statsg.cbSize.QuadPart < ULONG_MAX);
ULONG nImgBytes = ULONG(statsg.cbSize.QuadPart); // any image that can be displayed had better not have more than MAX_ULONG bytes
// go to the start of the stream
LARGE_INTEGER seekPos;
seekPos.QuadPart = 0;
hr = pStream->Seek(seekPos, STREAM_SEEK_SET, NULL);
ASSERT(hr == S_OK);
// get data in stream into a standard byte array
char *bytes = new char[nImgBytes];
ULONG nRead;
hr = pStream->Read(bytes, nImgBytes, &nRead); // read the data from the stream into normal memory. nRead should be equal to statsg.cbSize.QuadPart.
ASSERT(hr == S_OK);
ASSERT(nImgBytes == nRead);
// write data to archive and finish
ar << nRead; // need to save the amount of memory needed for the file, since we will need to read this amount later
ar.Write(bytes, nRead); // write the data to the archive file from the stream memory
pStream->Release();
delete[] bytes;
}
else
{
// get the data from the archive
ULONG nBytes;
ar >> nBytes;
char *bytes = new char[nBytes]; // ordinary memory to hold data from archive file
UINT nBytesRead = ar.Read(bytes, nBytes); // read the data from the archive file
ASSERT(nBytesRead == UINT(nBytes));
// make the stream
IStream *pStream = SHCreateMemStream(NULL, 0);
ASSERT(pStream != NULL);
if (pStream == NULL)
return;
// put the archive data into the stream
ULONG nBytesWritten;
pStream->Write(bytes, nBytes, &nBytesWritten);
ASSERT(nBytes == nBytesWritten);
if (nBytes != nBytesWritten)
return;
// go to the start of the stream
LARGE_INTEGER seekPos;
seekPos.QuadPart = 0;
hr = pStream->Seek(seekPos, STREAM_SEEK_SET, NULL);
ASSERT(hr == S_OK);
// load the stream into CImage and finish
pImage->Load(pStream); // pass the archive data to CImage
pStream->Release();
delete[] bytes;
}
}

C++ bitmap to base64

I have the following code to create a bitmap:
//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
And I save it as follows:
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);
I think the above is the standard way of saving bitmap images. However, instead of saving the image, I want it to be available as BASE64 and pass it in a HTTP Post. Therefore, this question relates to this one, but I am having a lot of difficulties converting the bmp structure to BASE64. I have taken the BASE64 encoder from here, but I have no idea how to pass the BMPFILEHEADER, BMPINFOHEADER, RGBQUAD, and raw data structure as a parameter to the BASE64 encoder.
Any thoughts or pointers on how to combine the information I gathered?
UPDATE
Thanks to Roman Pustylnikov, I have gotten a bit farther already:
I'm creating a struct like this:
struct ImageBuffer
{
BITMAPFILEHEADER bfheader;
BITMAPINFOHEADER infobmp;
RGBQUAD rgb[256];
PBYTE bitmap;
};
Fill it as follows:
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;
And convert it as follows:
int totalSize = sizeof(capture.bfheader) + sizeof(capture.infobmp) + sizeof(capture.rgb) + imageSize;
std::string encodedImage = base64_encode(reinterpret_cast<const unsigned char*>(&capture), totalSize);
However, it gives me an invalid bitmap. Also, when I load the bitmap from disk (the one that is generated with writefile), I get a different base64 string. I use C# code to compare the two Base64 strings:
// 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);
UPDATE TWO: solution
The solution can be found in the latest update of Roman Pustylnikov's answer.
You can do it without file in the middle, using the following example as encoder/decoder:
The first approach is to create the contiguous memory and put it as an input:
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);
The cons of this approach is that you need to duplicate the memory.
Another approach is to handle the "lost triplet". For this we'll need to define a structure:
struct ScreenShotBuffer
{
BITMAPFILEHEADER bfheader;
BITMAPINFO infobmp;
RGBQUAD rgb[256];
};
Now here comes the tricky part. Since the encoding handles the triples of bytes, you need to handle the border between two buffers (I haven't tested this so it might contain bugs, just a general approach).
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;
A little bit messy but should work on the same memory.