Barcode generation and recognition in-memory with GDI+, STD and Aspose.Barcode for C++ libraries
Tags: cpp, aspose, barcode, tutorial
Published: 2020-11-18 04:12:56

[Download source](https://github.com/AsposeAG/CodeNotes/tree/main/cpp/ABGateway)

#Introduction

Articles describes communication and conversion between [Aspose.Barcode for C++](https://products.aspose.com/barcode/cpp) internal in-memory objects, [**GDI+ bitmaps**](https://docs.microsoft.com/en-us/windows/win32/api/gdiplusheaders/nl-gdiplusheaders-bitmap) 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**](https://docs.microsoft.com/ru-ru/dotnet/api/system.io.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](https://apireference.aspose.com/barcode/cpp), 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](https://en.wikipedia.org/wiki/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](https://docs.aspose.com/barcode/cpp/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](https://dev.to/alifesoft/aspose-barcode-for-c-beginners-guide-1mj0) 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.

```cpp
//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] (https://apireference.aspose.com/barcode/cpp/class/system.smart_ptr) 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.
```cpp
//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] (https://docs.microsoft.com/uk-ua/dotnet/api/system.io.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] (https://docs.microsoft.com/en-us/dotnet/api/system.drawing.bitmap).

* **IStream** - is an [interface] (https://docs.microsoft.com/en-us/windows/win32/api/objidl/nn-objidl-istream) 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] (https://docs.microsoft.com/en-us/windows/win32/api/gdiplusheaders/nl-gdiplusheaders-image) 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.
```cpp
//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.
```cpp
//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.
```cpp
//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.
```cpp
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
```cpp
SHCreateMemStream(arr.data(), (UINT)arr.size());
```

* Read data from **IStream** into **ByteArray** is slightly more complicated and provided with this function
```cpp
//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.
```cpp
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](https://docs.microsoft.com/en-us/dotnet/api/system.drawing.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.
```cpp
//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.
```cpp
//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.
```cpp
//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.
```cpp
//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.
```cpp
//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.
```cpp
//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.
```cpp
//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.
```cpp
//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**] (https://docs.microsoft.com/en-us/windows/win32/gdi/bitmaps). The same way the barcode can be drawn or captured from any DC context.
```cpp
//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. 
```cpp
//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.