Barcode generation and recognition in-memory with GDI+, STD and Aspose.Barcode for C++ libraries
AsposeAG
Posted on November 18, 2020
Introduction
Articles describes communication and conversion between Aspose.Barcode for C++ internal in-memory objects, GDI+ bitmaps and common C++ objects from STD library. The conversion functions could be used with other Aspose C++ libraries because they have the same Aspose System Library for C++. The library types are look like standard .Net objects and can be easily used by anyone who is common with C#.
The article describes how to write and read data from Aspose MemoryStream which copies .Net MemoryStream functions and behavior. Also, it describes how to convert Aspose Bitmap between standard in Windows GDI+ Bitmap. Conversion functions has additional layer which is conversion between Aspose Bitmap and abstract in-memory 32-bit ARGB bitmap, which can be used to connect Aspose Bitmap with other objects from different graphic libraries.
All of these helper functions allow to use Aspose System Library for C++ with well-known types and functions from STD and GDI+ libraries.
Background
Aspose.Barcode for C++ uses custom system library which is similar to .Net standard types. All of library API are documented, but have low amount of examples: how to work with all of these graphics or system objects like files, streams or strings. Some users are confused how to use these Aspose types and link them with standard C++ objects or standard Windows objects. To solve this problem, we have developed helper functions which allow to make bridge between Aspose System Library for C++ and commonly used STD library objects, GDI+ graphics objects.
In most cases, only two functions are required from the barcode library : barcode generation and recognition. Other functions are unnecessary or even excessively. Aspose.Barcode for C++ uses Aspose System Library for C++ for own purposes, but learning new methods to manipulate data objects instead of just obtaining generated barcode image or providing one for the recognition is not the best idea. Most of users have own software libraries which already have solved their problems with manipulating system or graphics objects. The problem solution is conversion between Aspose system and graphic objects and user’s libraries objects.
Developer Guide describes communication mostly by file system which is not appropriate for some projects or applications. Helper functions, developed in this project, solve the problem and allow to communicate with other system libraries as common graphic library GDI+ and C++ standard library.
Prerequisites
At first you need to read Aspose.BarCode for C++ beginners guide how to create first project with the library and obtain the library with NuGet.
The next, you need to add to you project standard Windows GDI+ library which is common for any 2D graphic usage. You need to add GDI+ library as and pragma and setup startup and shutdown GDI+ token. You need to startup GDI+ library once on process startup or dll initialization and shutdown on process exit or dll unloading. However, you can avoid GDI+ shutdown on process exit because all library variables automatically will be cleared on process exit.
//adds library to the project, can be added in linker
#pragma comment (lib,"Gdiplus.lib")
//initalize GDI+ (must be inialized in process before any call)
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
//shutdown GDI+ (can be avoided if we terminate the process)
//but we must be attentive in dll creation
Gdiplus::GdiplusShutdown(gdiplusToken);
Used types
Aspose System Library for C++ types are based on the own implementation of smart pointers and allows to use all features of heap memory management and thread automatic safe memory management. User doesn’t need to care about object destruction or memory leaks. However, to correctly exit from the process or unload Dll, you have to correctly shutdown the library with PrepareForUnload function. It is required in low number of cases but sometimes it is required.
//unload Aspose.Barcode.Cpp library variables and threads
System::AsposeCppLibrary::PrepareForUnload();
System::IO::MemoryStream – is an Aspose memory stream class which is similar to .Net MemoryStream and used to store data into the memory.
System::Drawing::Bitmap - is an Aspose Bitmap class which is used to store pixel data for a graphics image and its attributes. It is similar to .Net Bitmap.
IStream - is an interface of streaming objects which is used in COM Automation. We use it to Save and Load GDI+ bitmaps.
Gdiplus::Bitmap – is an Microsoft GDI+ Bitmap class which is used to store pixel data of an images and their attributes.
Byte – is unsigned char C++ alias type.
ByteArray – is a vector of Byte variables. We use it to transfer data between MemoryStream, IStream and other types.
MemBmpARGB – is a memory structure in ARGB(alpha, red, green, blue: 8 bits) format, which represents color matrix and can be used to copy data into non Aspose and non GDI+ graphic libraries.
//memory structure in ARGB(alpha, red, green, blue: 8 bits) format
//which represents color matrix and can be used to copy data
//into non Aspose and non GDI+ graphic libraries
struct MemBmpARGB
{
//bitmap width in ARGB pixels
int Width = 0;
//bitmap height in ARGB pixels
int Height = 0;
//bitmap array in ARGB pixels
//size is Width * Height * 4
//format:
//row0(width)
//...
//row_height-1(width)
ByteArray Data;
//ARGB memory bitmap constructor
MemBmpARGB(int _width, int _height)
{
Width = _width;
Height = _height;
Data.resize(_width * _height * 4);//ARGB = 8888 4 bytes
}
};
Streams conversions
Aspose.Barcode for C++ uses FileStream and MemoryStream to save and load image data. Because to save or load data from a file we need just provide a string with file path we do not describe working with FileStream. MemoryStream allows to save and load data into the memory which could be used for network processing or with other communication methods.
- We can convert data from MemoryStream to ByteArray with this code. As we see we extract MemoryStream size, initialize vector capacity and copy the data.
//function converts memory stream to byte array
//memoryStream - is a memory steam
//returns byte array
ByteArray MemoryStreamToByteArray(System::SharedPtr<System::IO::MemoryStream> memoryStream)
{
//initialize array capacity
System::ByteArrayPtr lMemAdr = memoryStream->GetBuffer();
ByteArray arr(lMemAdr->Count());
//copy data
memcpy(arr.data(), lMemAdr->data_ptr(), lMemAdr->Count());
return arr;
}
- The same thing we can convert ByteArray to MemoryStream. We initialize memory stream length, receive addresses of our buffers and copy data from vector to memory stream.
//function converts byte array to memory stream
//arr - is byte array
//returns memory stream
System::SharedPtr<System::IO::MemoryStream> ByteArrayToMemoryStream(ByteArray &arr)
{
//intialize memory stream capacity
System::SharedPtr<System::IO::MemoryStream> memoryStream = System::MakeObject<System::IO::MemoryStream>();
memoryStream->SetLength(arr.size());
System::ByteArrayPtr lMemAdr = memoryStream->GetBuffer();
//copy data and set position to 0
memcpy(lMemAdr->data_ptr(), arr.data(), arr.size());
memoryStream->set_Position(0);
return memoryStream;
}
- The library also have code to copy data from/to std::string and creation MemoryStream from memory pointer with size.
System::SharedPtr<System::IO::MemoryStream> MemoryStreamFromBuffer(void* buffer, int size);
std::string MemoryStreamToString(System::SharedPtr<System::IO::MemoryStream> memoryStream);
System::SharedPtr<System::IO::MemoryStream> StringToToMemoryStream(std::string &str);
GDI+ uses IStream to load and saves bitmaps into non file stream, as example memory stream, in various formats.
- We can create in-memory IStream from ByteArray with the function SHCreateMemStream
SHCreateMemStream(arr.data(), (UINT)arr.size());
- Read data from IStream into ByteArray is slightly more complicated and provided with this function
//function coverts COM IStream implementation to byte array
//https://docs.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-istream
//istr - is COM IStream implementation
//returns byte array
ByteArray IStreamToByteArray(IStream *istr)
{
//set position to zero
LARGE_INTEGER SeekPos;
SeekPos.QuadPart = 0;
ULARGE_INTEGER actualPos;
ULARGE_INTEGER zeroPos;
//remember position
istr->Seek(SeekPos, STREAM_SEEK_CUR, &actualPos);
//set to zero
istr->Seek(SeekPos, STREAM_SEEK_SET, &zeroPos);
//get size
tagSTATSTG istat;
istr->Stat(&istat, STATFLAG_NONAME);
//read to buffer
int size = istat.cbSize.LowPart;
ULONG actualBytes = 0;
ByteArray arr(size);
istr->Read(arr.data(), size, &actualBytes);
//return position
SeekPos.QuadPart = actualPos.QuadPart;
istr->Seek(SeekPos, STREAM_SEEK_CUR, &actualPos);
return arr;
}
- It is simple to convert directly IStream from/to MemoryStream with usage of previous code.
IStream* MemoryStreamToIStream(System::SharedPtr<System::IO::MemoryStream> memoryStream);
System::SharedPtr<System::IO::MemoryStream> IStreamToMemoryStream(IStream *istr);
Bitmaps conversions
Aspose.Barcode for C++ can write and read data not only from the file and streams but also from Aspose Bitmap which is similar to .Net Bitmap. In this way, understanding how to manipulate with Bitmap data helps to use it for reports, screen drawing or printing.
We implemented structure MemBmpARGB which represents pixels array, representing ARGB(alpha, red, green, blue: 8 bits) most common format. This helps to convert data from other graphic libraries.
- We can convert Aspose Bitmap to MemBmpARGB with this code. In this code we extract raw pixels in 32-bit ARGB format from Aspose Bitmap and copy to our in-memory structure.
//function converts Aspose Bitmap to ARGB memory bitmap
//bitmap - is Aspose Bitmap
//returns ARGB memory bitmap
MemBmpARGB AsposeBitmapToMemBitmap(System::SharedPtr<System::Drawing::Bitmap> bitmap)
{
MemBmpARGB memBitmap(bitmap->get_Width(), bitmap->get_Height());
//lock and extract bitmap data
System::Drawing::Rectangle rect(0, 0, bitmap->get_Width(), bitmap->get_Height());
System::SharedPtr<System::Drawing::Imaging::BitmapData> lBmpData =
bitmap->LockBits(rect, System::Drawing::Imaging::ImageLockMode::ReadOnly, System::Drawing::Imaging::PixelFormat::Format32bppArgb);
int lBuffSize = std::min(lBmpData->get_Stride() * lBmpData->get_Height(), (int)memBitmap.Data.size());
//copy data
memcpy(memBitmap.Data.data(), (void*)lBmpData->get_Scan0(), lBuffSize);
bitmap->UnlockBits(lBmpData);
return memBitmap;
}
- The same way we can copy data from MemBmpARGB structure to Aspose Bitmap. We receive buffer information from Aspose Bitmap and copy pixels array data directly to the buffer.
//function converts ARGB memory bitmap to Aspose Bitmap
//memBitmap - is ARGB memory bitmap
//returns Aspose Bitmap
System::SharedPtr<System::Drawing::Bitmap> MemBitmapToAsposeBitmap(MemBmpARGB &memBitmap)
{
System::SharedPtr<System::Drawing::Bitmap> bitmap =
System::MakeObject<System::Drawing::Bitmap>(memBitmap.Width, memBitmap.Height, System::Drawing::Imaging::PixelFormat::Format32bppArgb);
//lock and extract bitmap data
System::Drawing::Rectangle rect(0, 0, memBitmap.Width, memBitmap.Height);
System::SharedPtr<System::Drawing::Imaging::BitmapData> lBmpData =
bitmap->LockBits(rect, System::Drawing::Imaging::ImageLockMode::WriteOnly, System::Drawing::Imaging::PixelFormat::Format32bppArgb);
int lBuffSize = std::min(lBmpData->get_Stride() * lBmpData->get_Height(), (int)memBitmap.Data.size());
//copy data
memcpy((void*)lBmpData->get_Scan0(), memBitmap.Data.data(), lBuffSize);
bitmap->UnlockBits(lBmpData);
return bitmap;
}
- On the Windows, in most cases, we use GDI+ graphic library because it fast and has enough functions for any 2D graphic requirements. Converting from Aspose Bitmap to GDI+ Bitmap we can use following code. As we see we just need to unlock data buffers both from Aspose Bitmap and GDI+ Bitmap and copy pixels array one from other.
//function converts Aspose Bitmap to GDI+ Bitmap
//abitmap - is Aspose Bitmap
//returns GDI+ Bitmap
std::shared_ptr<Gdiplus::Bitmap> AsposeBitmapToGdiPlusBitmap(System::SharedPtr<System::Drawing::Bitmap> abitmap)
{
std::shared_ptr<Gdiplus::Bitmap> gbitmap = std::make_shared<Gdiplus::Bitmap>(abitmap->get_Width(), abitmap->get_Height(), PixelFormat32bppARGB);
//lock gbitmap
Gdiplus::BitmapData gbitmapData;
Gdiplus::Rect grect(0, 0, abitmap->get_Width(), abitmap->get_Height());
gbitmap->LockBits(&grect, Gdiplus::ImageLockModeWrite, PixelFormat32bppARGB, &gbitmapData);
//lock abitmap
System::Drawing::Rectangle arect(0, 0, abitmap->get_Width(), abitmap->get_Height());
System::SharedPtr<System::Drawing::Imaging::BitmapData> abitmapData =
abitmap->LockBits(arect, System::Drawing::Imaging::ImageLockMode::ReadOnly, System::Drawing::Imaging::PixelFormat::Format32bppArgb);
//copy
int lBuffSize = std::min((int)(gbitmapData.Stride * gbitmapData.Height), abitmapData->get_Stride() * abitmapData->get_Height());
memcpy(gbitmapData.Scan0, (void*)abitmapData->get_Scan0(), lBuffSize);
//unlock
abitmap->UnlockBits(abitmapData);
gbitmap->UnlockBits(&gbitmapData);
return gbitmap;
}
- The same way is conversion between GDI+ Bitmap and Aspose Bitmap, we extract data buffers in 32-bit ARGB formats and copy pixels from GDI+ buffer to Aspose buffer.
//function converts GDI+ Bitmap to Aspose Bitmap
//gbitmap - is GDI+ Bitmap
//returns Aspose Bitmap
System::SharedPtr<System::Drawing::Bitmap> GdiPlusBitmapToAsposeBitmap(Gdiplus::Bitmap &gbitmap)
{
System::SharedPtr<System::Drawing::Bitmap> abitmap =
System::MakeObject<System::Drawing::Bitmap>(gbitmap.GetWidth(), gbitmap.GetHeight(), System::Drawing::Imaging::PixelFormat::Format32bppArgb);
//lock abitmap
System::Drawing::Rectangle arect(0, 0, abitmap->get_Width(), abitmap->get_Height());
System::SharedPtr<System::Drawing::Imaging::BitmapData> abitmapData =
abitmap->LockBits(arect, System::Drawing::Imaging::ImageLockMode::WriteOnly, System::Drawing::Imaging::PixelFormat::Format32bppArgb);
//lock gbitmap
Gdiplus::BitmapData gbitmapData;
Gdiplus::Rect grect(0, 0, abitmap->get_Width(), abitmap->get_Height());
gbitmap.LockBits(&grect, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, &gbitmapData);
//copy
int lBuffSize = std::min((int)(gbitmapData.Stride * gbitmapData.Height), abitmapData->get_Stride() * abitmapData->get_Height());
memcpy((void*)abitmapData->get_Scan0(), gbitmapData.Scan0, lBuffSize);
//unlock
abitmap->UnlockBits(abitmapData);
gbitmap.UnlockBits(&gbitmapData);
return abitmap;
}
- Sometimes we need to store or load data GDI+ Bitmap to or from file or stream. GDI+ Bitmap can use IStream for this. IStream of system memory stream can be created with function SHCreateMemStream. How to convert IStream from and to MemoryStream** we have reviewed in previous section. But GDI+ need not only image format identifier to save image in special format it needs encoder GUID. To find encoder GUID we can use the following code.
//function searches image encoders/decoder for GDI+
//imageFormat - is image format (bmp, png, jpg...)
//isEncoder - is is we are searching Encoders(true) or decoders(false)
//returns encoder/decoders ID
CLSID FindCodecByFormat(const GUID &imageFormat, bool isEncoder)
{
//get buffer size
UINT num, size;
Gdiplus::Status stat = Gdiplus::Status::Ok;
if (isEncoder)
stat = Gdiplus::GetImageEncodersSize(&num, &size);
else
stat = Gdiplus::GetImageDecodersSize(&num, &size);
if ((Gdiplus::Status::Ok!= stat) || (0 == size))
return CLSID_NULL;
//get encoders/decoders
Gdiplus::ImageCodecInfo* pArray = (Gdiplus::ImageCodecInfo*)(malloc(size));
if (isEncoder)
Gdiplus::GetImageEncoders(num, size, pArray);
else
Gdiplus::GetImageDecoders(num, size, pArray);
CLSID codec = CLSID_NULL;
for (UINT j = 0; j < num; ++j)
if (imageFormat == pArray[j].FormatID)
{
codec = pArray[j].Clsid;
break;
}
free(pArray);
return codec;
}
- How to save GDI+ Bitmap to file or memory stream is shown in these functions.
//function load GDI+ Bitmap from memory stream
//memoryStream - is memory stream
//returns GDI+ Bitmap
std::shared_ptr<Gdiplus::Bitmap> GdiPlusBitmapFromMemoryStream(System::SharedPtr<System::IO::MemoryStream> memoryStream);
//function saves GDI+ Bitmap to memory stream
//gbitmap - is GDI+ Bitmap
//imageFormat - is image format (bmp, png, jpg...)
//returns memory stream
System::SharedPtr<System::IO::MemoryStream> GdiPlusBitmapToMemoryStream(Gdiplus::Bitmap &gbitmap, const GUID &imageFormat = Gdiplus::ImageFormatPNG);
//function saves GDI+ Bitmap to file
//gbitmap - is GDI+ Bitmap
//fileName - is file name
//imageFormat - is image format (bmp, png, jpg...)
void SaveGdiPlusBitmapToFile(Gdiplus::Bitmap &gbitmap, const wchar_t* fileName, const GUID &imageFormat = Gdiplus::ImageFormatPNG);
//function loads GDI+ Bitmap from file
//fileName - is file name
//returns GDI+ Bitmap
std::shared_ptr<Gdiplus::Bitmap> LoadGdiPlusBitmapFromFile(const wchar_t* fileName);
Examples
Project contains a lot of examples which demonstrate usage of described upper functions. Al of these examples are provided in example.cpp. There are not only Streams and Bitmap conversions, but barcode generation and recognition examples from GDI+ or system in-memory objects and examples in generation barcodes with drawing them on device surfaces.
- These two functions demonstrate how to convert MemoryStream, IStream, ByteArray, memory buffer or std::string each in other.
//test examples
//functions demonstrates how to extract data from and to memory stream
void HowToConvertAndSaveMemoryStream(const wchar_t* pathName);
//functions demonstrates how to extract data from and to memory stream
//with special data types like memory buffer or std::string
void HowToConvertAndSaveMemoryStreamExt(const wchar_t* pathName);
- In this section usage of different conversion between bitmaps are shown.
//functions demonstrates how to extract from and to ARGB memory bitmap
void HowToWorkWithMemBmpARGB(const wchar_t* pathName);
//functions demonstrates how to convert data between GDI+ and Aspose bitmap objects
void HowToWorkWithGdiPlusAndAsposeBitmapConversion(const wchar_t* pathName);
//functions demonstrates how to load and save GDI+ bitmaps to memory stream
void HowToWorkWithGdiPlusAndMemoryStream(const wchar_t* pathName);
- This section shows how to draw barcodes with GDI+ graphic library function on GDI+ Bitmap, HBITMAP. The same way the barcode can be drawn or captured from any DC context.
//functions demonstrates how to draw barcodes on GDI+ bitmap
void HowToDrawBarcodeOnBitmap(const wchar_t* pathName);
//functions demonstrates how to draw barcodes on HBITMAP object
void HowToDrawBarcodeOnHBITMAP(const wchar_t* pathName);
- And this section describes how we can recognize barcodes not only from file on the disk but from GDI+ image acquired from different contexts.
//functions demonstrates how to recognize barcode from GDI+ Bitmap
void HowToRecognize(const wchar_t* pathName);
Conclusion
In this article and project, we provide helper library which allows Aspose.Barcode for C++ easily communicating with standard system graphics library GDI+ or standard system objects. This can help to use Aspose barcode library in different C++ projects without storing or loading data directly from disk subsystem.
Posted on November 18, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.