DEV Community

Cover image for Barcode generation and recognition in-memory with GDI+, STD and Aspose.Barcode for C++ libraries
AsposeAG
AsposeAG

Posted on

Barcode generation and recognition in-memory with GDI+, STD and Aspose.Barcode for C++ libraries

Download source

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);
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode
  • 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
    }
};
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode
  • 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;
}
Enter fullscreen mode Exit fullscreen mode
  • 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);
Enter fullscreen mode Exit fullscreen mode

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());
Enter fullscreen mode Exit fullscreen mode
  • 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;
}
Enter fullscreen mode Exit fullscreen mode
  • 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);
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode
  • 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;
}
Enter fullscreen mode Exit fullscreen mode
  • 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;
}
Enter fullscreen mode Exit fullscreen mode
  • 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;
}
Enter fullscreen mode Exit fullscreen mode
  • 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;
}
Enter fullscreen mode Exit fullscreen mode
  • 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);
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode
  • 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);
Enter fullscreen mode Exit fullscreen mode
  • 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);
Enter fullscreen mode Exit fullscreen mode
  • 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);
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)