首页 > 解决方案 > 以 VCL 形式在 TPaintBox 上绘制视频图像

问题描述

我有在 C++ 应用程序中的 VCL 表单上的 TPaintBox 上绘制 BMP 图像的代码。

只要我只有一个图像要在一种形式上绘制,一切都可以正常工作。当我创建第二个表单时,我会遇到零星的访问冲突。

该代码是从一个线程调用的,我正在使用 Synchronize 函数来与主 VCL 线程同步,因为

void TCameraForm::loadImage(FramePtr frame)
{
    syncing s;
    s.aFrame = frame;
    s.theForm = this;

    //Synchronize with UI thread
    TThread::Synchronize(0, &s.fn);
}

在代码中,FramePtr 是指向一个单独的“帧”的共享指针,其中包含一个设备相关位图。

同步变量是一个结构,保存实际绘画的代码:

//This is a trick to use VCL's TThread::Synchronize function "with parameters"
//Thanks to Mr. R. Lebeau for sharing this trick.
struct syncing
{
    FramePtr        aFrame;
    TCameraForm*    theForm;

    int tag;
    void __fastcall fn()
    {
        try
        {
            //Create a device dependent bitmap
            BitMap aBitMap(aFrame);

            //Get the bitmap memory into a TMemoryStream
            TMemoryStream* ms = new TMemoryStream();
            int bytes = ms->Write(aBitmap.getBuffer()->mMemoryBuffer, aBitmap.getBuffer()->mBufferSize);
            ms->Position = 0;

            //Create a TPicture object that will be used for drawing on the paintbox
            TBitmap* tbm = new TBitmap();

            tbm->LoadFromStream(ms);
            TRect stretchedRect(getStretchedDimensions(tbm->Width, tbm->Height, theForm->PaintBox1->Width, theForm->PaintBox1->Height));
            theForm->PaintBox1->Canvas->StretchDraw(stretchedRect, tbm);

            delete ms;
            delete tbm;
        }
        catch(...)
        {
            Log(lError) << "Exception occured in the CameraFrame sync function";
        }
    }
};

调试器主要在创建位图时停止。

我正在使用 BCC builder 10.3.2 和经典编译器。

位图类如下所示:

标题

class BitMap 
{
    public:
                                BitMap(FramePtr aFrame);
                                BitMap(unsigned long width, unsigned long height, ColorCode c, ImageMemoryBuffer& buf);
        ImageMemoryBuffer*      getBuffer();
                                ~BitMap();
        bool                    write(const string& file);

    protected:
        unsigned int            mWidth;
        unsigned int            mHeight;
        ColorCode               mColorCode;
        ImageMemoryBuffer       mImageMemoryBuffer;
        bool                    create();
        bool                    release();
};

和 CPP:

enum { THREE_CHANNEL    = 0xC,};
enum { BMP_HEADER_SIZE  = 54, };
enum { ALIGNMENT_SIZE   = 4, };

namespace ai
{

BitMap::BitMap(FramePtr aFrame)
:
mWidth(0),
mHeight(0),
mColorCode(ColorCodeMono8),
mImageMemoryBuffer()
{
    aFrame->GetImageSize(mImageMemoryBuffer.mBufferSize);
    aFrame->GetWidth(mWidth);
    aFrame->GetHeight(mHeight);

    VmbPixelFormatType ePixelFormat = VmbPixelFormatMono8;
    aFrame->GetPixelFormat(ePixelFormat);
    if((ePixelFormat != VmbPixelFormatMono8) && (ePixelFormat != VmbPixelFormatRgb8))
    {
        throw(MVRException("Invalid pixel format: " + toString(ePixelFormat)));
    }

    mColorCode = (ePixelFormat == VmbPixelFormatRgb8) ? ColorCodeRGB24 : ColorCodeMono8;

    VmbUchar_t *pImage = NULL;
    if (aFrame->GetImage(pImage) != VmbErrorSuccess)
    {
        throw(MVRException("Failed \"getting\" image"));
    }

    mImageMemoryBuffer.mMemoryBuffer = (unsigned char*) pImage;
    if(!create())
    {
        Log(lError) << "There was an error creating the bitmap";
        throw(MVRException("Failed creating Bitmap"));
    }
}

BitMap::BitMap(unsigned long width, unsigned long height, ColorCode c, ImageMemoryBuffer& buf)
:
mWidth(width),
mHeight(height),
mColorCode(c),
mImageMemoryBuffer(buf)
{
    if(!create())
    {
        Log(lError) << "There was an error creating the bitmap";
        throw(MVRException("Failed creating bitmap"));
    }
}

BitMap::~BitMap()
{
    if(!release())
    {
        Log(lError) << "There was an error releasing the bitmap";
    }
}

ImageMemoryBuffer* BitMap::getBuffer()
{
    return &mImageMemoryBuffer;
}

bool BitMap::create()
{
    try
    {
        unsigned char   nNumColors;                     // Number of colors of our image
        unsigned char   nPadLength;                     // The padding we need to align the bitmap ALIGNMENT_SIZE
        unsigned long   nPaletteSize = 0;               // The size of the bitmap's palette
        unsigned long   nHeaderSize;                    // The size of the bitmap's header
        unsigned long   nFileSize;                      // The size of the bitmap file
        unsigned char*  pBitmapBuffer;                  // A buffer we use for creating the bitmap
        unsigned char*  pCurBitmapBuf;                  // A cursor to move over "pBitmapBuffer"
        unsigned char*  pCurSrc;                        // A cursor to move over the given buffer "pBuffer"
        unsigned long   px;                             // A single pixel for storing transformed color information
        unsigned long   x;                              // The horizontal position within our image
        unsigned long   y;                              // The vertical position within our image
        unsigned long   i;                              // Counter for some iteration

        // The bitmap header
        char fileHeader[14] = { 'B','M',                // Default
                                0,0,0,0,                // File size
                                0,0,0,0,                // Reserved
                                0,0,0,0 };              // Offset to image content

        char infoHeader[40] = { 40,0,0,0,               // Size of info header
                                0,0,0,0,                // Width
                                0,0,0,0,                // Height
                                1,0,                    // Default
                                0, 0 };                 // bpp

        if ( 0 == mImageMemoryBuffer.mBufferSize || 0 == mWidth || 0 == mHeight )
        {
            Log(lError) << "Zero bitmap buffer, width ot height in Bitmap constructor";
            return false;
        }

        if ( mColorCode == (mColorCode & THREE_CHANNEL) )
        {
            nNumColors = 3;
        }
        else
        {
            nNumColors = 1;
        }

        // Bitmap padding always is a multiple of four Bytes. If data is not we need to pad with zeros.
        nPadLength = (mWidth * nNumColors) % ALIGNMENT_SIZE;
        if ( 0 != nPadLength )
        {
            nPadLength = ALIGNMENT_SIZE - nPadLength;
        }

        if ( ColorCodeRGB24 != mColorCode )
        {
            nPaletteSize = 256;
        }

        nHeaderSize     = BMP_HEADER_SIZE + nPaletteSize * 4;
        pBitmapBuffer   = (unsigned char*)malloc( nHeaderSize + mImageMemoryBuffer.mBufferSize + (nPadLength * mHeight) );
        nFileSize       = nHeaderSize + mImageMemoryBuffer.mBufferSize + (nPadLength * mHeight);

        // File size
        fileHeader[ 2]  = (char)(nFileSize);
        fileHeader[ 3]  = (char)(nFileSize >> 8);
        fileHeader[ 4]  = (char)(nFileSize >> 16);
        fileHeader[ 5]  = (char)(nFileSize >> 24);

        // Offset to image content
        fileHeader[10]  = (char)(nHeaderSize);
        fileHeader[11]  = (char)(nHeaderSize >> 8);
        fileHeader[12]  = (char)(nHeaderSize >> 16);
        fileHeader[13]  = (char)(nHeaderSize >> 24);

        // Width
        infoHeader[ 4]  = (char)(mWidth);
        infoHeader[ 5]  = (char)(mWidth >> 8);
        infoHeader[ 6]  = (char)(mWidth >> 16);
        infoHeader[ 7]  = (char)(mWidth >> 24);

        // Height (has to be negative for a top down image)
        infoHeader[ 8]  = (char)(-(long)mHeight);
        infoHeader[ 9]  = (char)(-(long)mHeight >> 8);
        infoHeader[10]  = (char)(-(long)mHeight >> 16);
        infoHeader[11]  = (char)(-(long)mHeight >> 24);

        // bpp
        infoHeader[14]  = 8 * nNumColors;

        // Image size
        infoHeader[20]  = (char)(mImageMemoryBuffer.mBufferSize);
        infoHeader[21]  = (char)(mImageMemoryBuffer.mBufferSize >> 8);
        infoHeader[22]  = (char)(mImageMemoryBuffer.mBufferSize >> 16);
        infoHeader[23]  = (char)(mImageMemoryBuffer.mBufferSize >> 24);

        // Palette size
        infoHeader[32]  = (char)(nPaletteSize);
        infoHeader[33]  = (char)(nPaletteSize >> 8);
        infoHeader[34]  = (char)(nPaletteSize >> 16);
        infoHeader[35]  = (char)(nPaletteSize >> 24);

        // Used colors
        infoHeader[36]  = (char)(nPaletteSize);
        infoHeader[37]  = (char)(nPaletteSize >> 8);
        infoHeader[38]  = (char)(nPaletteSize >> 16);
        infoHeader[39]  = (char)(nPaletteSize >> 24);

        // Write header
        pCurBitmapBuf   = pBitmapBuffer;
        memcpy(pCurBitmapBuf, fileHeader, 14);

        pCurBitmapBuf += 14;
        memcpy(pCurBitmapBuf, infoHeader, 40);

        pCurBitmapBuf += 40;
        for(i = 0; i < nPaletteSize; ++i)
        {
            pCurBitmapBuf[0] = (char)(i);
            pCurBitmapBuf[1] = (char)(i);
            pCurBitmapBuf[2] = (char)(i);
            pCurBitmapBuf[3] = 0;
            pCurBitmapBuf += 4;
        }

        // RGB -> BGR (a Windows bitmap is BGR)
        if(mColorCode == ColorCodeRGB24)
        {
            pCurSrc = (unsigned char*) mImageMemoryBuffer.mMemoryBuffer;
            for(y=0; y < mHeight; ++y, pCurBitmapBuf += nPadLength )
            {
                for (x = 0; x < mWidth; ++x, pCurSrc += 3, pCurBitmapBuf += 3)
                {
                    px = 0;
                    // Create a 4 Byte structure to store ARGB (we don't use A)
                    px = px | (pCurSrc[0] << 16) | (pCurSrc[1] << 8) | pCurSrc[2];
                    // Due to endianess ARGB is stored as BGRA
                    // and we only have to write the first three Bytes
                    memcpy( pCurBitmapBuf, &px, 3 );
                }

                // Add padding at the end of each row
                memset( pCurBitmapBuf, 0, nPadLength );
            }
            mColorCode = ColorCodeBGR24;
        }
        // Mono8
        else
        {
            if(nPadLength == 0)
            {
                memcpy( pCurBitmapBuf, mImageMemoryBuffer.mMemoryBuffer, mImageMemoryBuffer.mBufferSize );
            }
            else
            {
                pCurSrc = (unsigned char*)mImageMemoryBuffer.mMemoryBuffer;
                for (y=0; y < mHeight; ++y, pCurSrc += mWidth * nNumColors)
                {
                    // Write a single row of colored pixels
                    memcpy( pCurBitmapBuf, pCurSrc, mWidth * nNumColors );
                    pCurBitmapBuf += mWidth * nNumColors;

                    // Write padding pixels
                    memset(pCurBitmapBuf, 0, nPadLength);
                    pCurBitmapBuf += nPadLength;
                }
            }
        }

        mImageMemoryBuffer.mMemoryBuffer = pBitmapBuffer;
        mImageMemoryBuffer.mBufferSize = nFileSize;
        return true;
    }
    catch(...)
    {
        Log(lError) << "Exception in creation of bitmap create function";
        return false;
    }
}

bool BitMap::release()
{
    try
    {
        if (mImageMemoryBuffer.mMemoryBuffer != NULL && mImageMemoryBuffer.mBufferSize > 0)
        {
            free(mImageMemoryBuffer.mMemoryBuffer);
            mImageMemoryBuffer.mMemoryBuffer = NULL;
        }
        return true;
    }
    catch(...)
    {
        return false;
    }
}

bool BitMap::write(const string& fName)
{
    if (mImageMemoryBuffer.mMemoryBuffer == NULL)
    {
        return false;
    }

    FILE *file = fopen(fName.c_str(), "wb");
    if(!file)
    {
        Log(lError) << "Failed opening file: " << fName;
        return false;
    }

    fwrite(mImageMemoryBuffer.mMemoryBuffer, 1, mImageMemoryBuffer.mBufferSize, file );
    fclose(file);
    return true;
}

}

更新:上面的代码在由一个线程执行时工作正常。当涉及多个线程时会出现此问题。我已经找到了在创建位图(而不是 TBitmap)时调用 memcpy 函数时发生的问题,支持这一点。所以主要问题是相同的内存同时被两个或多个线程操作。

对于上面的代码,为了防止内存损坏,在哪里合并 Mutex 比较合适?或者可以使用另一种技术吗?

标签: c++c++buildervcl

解决方案


推荐阅读