【问题标题】:Moving a Bitmap inside PictureBox在 PictureBox 中移动位图
【发布时间】:2014-10-21 19:51:59
【问题描述】:

我对 C++/CLI 中的位图有疑问。简而言之,我必须将位图块加载到 PictureBox,显示它并将其移动到下面几行。我做的步骤是:

1 - 从硬盘加载完整的位图,并获取它的参数(宽度、高度等)。

2 - 创建一个能够完全包含此位图的 PictureBox。

3 - 创建一个位图,我将用作绘图区域,并将其放在 PictureBox 内。

4 - 将初始位图的一个块(前 150 行)复制到我的绘图区域并显示它。

5 - 将此块移动到下面几行。 (这是我的问题)。

当我移动图像时,出现错误:图像似乎向左移动,而右侧是图像的左侧部分。我上传了图片:

这是移动之前的图像块,一切正常。 http://i58.tinypic.com/2duf48x.png

这是移动后的图像,如您所见,它似乎向左移动。 http://i61.tinypic.com/jz9ond.png

我用来“移动”图像的方法是:

1 - LockBits 并获得 anIntPtr,我将其转换为 unsigned char*。 2 - 使用 memcpy(或 memmove)移动块。

这里是代码,这些是类构造函数(Windows 窗体构造函数)和我用来显示和移动块的方法:

MyForm(){
    InitializeComponent();
    //Get a test srcIm and it's parameters.
    srcIm = gcnew Bitmap("./img/zhbackground.bmp");
    iWidth = srcIm->Width;
    iHeigth = srcIm->Height;
    pxF = srcIm->PixelFormat;
    Bpp = Image::GetPixelFormatSize(pxF) / 8;

    //Prepare a PictureBox, which will be given as parameter to Windows Form.
    pb = gcnew PictureBox();
    pb->SizeMode = PictureBoxSizeMode::StretchImage;
    pb->Size = Drawing::Size(iWidth, iHeigth);
    pb->Location = Drawing::Point(0, 0);

    //Create draw area and put it into the PictureBox.
    drawArea = gcnew Bitmap(pb->Width, pb->Height, pxF);
    pb->Image = drawArea;

    this->Controls->Add(this->pb);
}

void test(){
    //This is the number of lines of my image chunk.
    int nLines = 150;

    //First, load the first 150 lines of my image (that's on hard drive).
    BitmapData ^srcData = srcIm->LockBits(
        Drawing::Rectangle(0, 0, iWidth, iHeigth),
        ImageLockMode::ReadOnly,
        pxF
    );
    unsigned char *srcStream = (unsigned char*)srcData->Scan0.ToPointer();

    //Prepare the draw area for paint my image chunk.
    BitmapData ^dstData = drawArea->LockBits(
        Drawing::Rectangle(0, 0, iWidth, iHeigth),
        ImageLockMode::ReadWrite,
        pxF
    );
    unsigned char *dAhandler = (unsigned char*)dstData->Scan0.ToPointer();

    //Paint the chunk. (My image's 150 first lines)
    memcpy(dAhandler, srcStream, iWidth * Bpp * nLines);

    //Unlock and refresh to see the image.
    drawArea->UnlockBits(dstData);
    this->pb->Refresh();

    //Wait 1 second...
    Threading::Thread::Sleep(1000);

    //Unlock draw area to move the image.
    dstData = drawArea->LockBits(
        Drawing::Rectangle(0, 0, iWidth, iHeigth),
        ImageLockMode::ReadWrite,
        pxF
    );
    dAhandler = (unsigned char*)dstData->Scan0.ToPointer();

    //First, move the image from the beggining to 150 lines below.
    memcpy(&dAhandler[nLines * iWidth * Bpp], dAhandler, iWidth * nLines * Bpp);

    //Paint the "hole" with black.
    memset(dAhandler, 0, iWidth * Bpp * nLines);

    //Unlock and display the image.
    drawArea->UnlockBits(dstData);
    this->pb->Refresh();

    //Unlock the source image (image load from hard drive).
    srcIm->UnlockBits(srcData);
 }

我测试了很多方法:

1 - 我读到如果你使用 LockBits(),你可以获得的 IntPtr 是像素的字节数组,一个接着一个。这意味着我不会遇到内存中的位图组织等问题。

2 - 我已经测试了 memcpy 和 memmove。

3 - 我已经测试了一个简单的 for 循环。

没有用,我不知道该怎么办。

谢谢,问候。

【问题讨论】:

    标签: .net c++-cli


    【解决方案1】:

    我找到了一种方法,我将它发布在 C# 中(对于 C++/CLI 实际上是相同的) 我不知道这是否是更好的解决方案,但它确实有效。

    /**
     * Number of lines that will be displaced down. This match with the
     * Height that the Bitmap returned by {@code getNextLines} must have.
     */
    int displLines;
    
    /**
     * Destination coordinates of the image chunk that will be 'moved'
     */
    Point[] displaceCoords;
    
    /**
     * Source image chunk that will me 'moved'
     */
    Rectangle displaceArea;
    
    /**
     * Destination coordinates for the new lines.
     */
    Point[] newLineCoords;
    
    /**
     * Dimensions of the rectangle formed by the new lines.
     */
    Rectangle newLineArea;
    
    /**
     * Auxiliar buffers for paint the displaced image and the new lines.
     */
    Bitmap canvas1, canvas2;
    
    /**
     * Graphics for each buffer (canvas1 and canvas2).
     */
    Graphics canvas1Graphics, canvas2Grapchics;
    
    /**
     * Flag indicating which buffer have to be used in a determined time.
     */
    bool useCanvas1Graphics;
    
    /**
     * Timer used to refresh the image.
     */
    Timer timer;
    
    /**
     * Retrieves the next lines to be painted at the top of
     * the image.
     */
    abstract Bitmap getNextLines();
    
    /**
     * Initializes the parameters which will be used to displace
     * the current bitmap in the PictureBox 'displLines' down.
     * 
     */
    void InitDisplacingParameters() {
        displaceArea = new Rectangle(0, 0, Width, Height - displLines);
        displaceCoords = new Point[3];
        displaceCoords[0].X = 0;
        displaceCoords[0].Y = displLines;
    
        displaceCoords[1].X = Width;
        displaceCoords[1].Y = displLines;
    
        displaceCoords[2].X = 0;
        displaceCoords[2].Y = Height;
    }
    
    /**
     * Initializes the parameters use to insert the new lines
     * in the image.
     */
    void InitNewLinesParameters() {
        newLineCoords = new Point[3];
        newLineCoords[0].X = 0;
        newLineCoords[0].Y = 0;
    
        newLineCoords[1].X = Width;
        newLineCoords[1].Y = 0;
    
        newLineCoords[2].X = 0;
        newLineCoords[2].Y = displLines;   
    
        newLineArea = new Rectangle(0, 0, Width, displLines);
    }
    
    /**
     * WARNING: in .NET framework version 3.0 or less, there is no possible
     * to use Grapchis.DrawImage for paint in the same image that have been used to obtain
     * the Graphics object.
     * 
     * Example, this is not possible:
     * {@code
     *      Graphics g = Graphics.fromImage(image);
     *      f.DrawImage(image, ....);
     *      // ERROR! "Grpahics" can not paint in the same image which has been generated him (Graphics).
     * }
     */
    void DisplaceAndPaint(Object sender, EventArgs e) {
        try {
            // Stop paint.
            SuspendLayout();
    
            // Obtain the proper graphics object.
            Graphics g = useCanvas1Graphics ? canvas1Graphics : canvas2Grapchics;
    
            // Displace the current image 'desplLines' down.
            g.DrawImage(Image, displaceCoords, displaceArea, GraphicsUnit.Pixel);
    
            // Get the new lines.
            Bitmap bmp = getNextLines();
    
            // Draw the new lines in the top of the image.
            g.DrawImage(bmp, newLineCoords, newLineArea, GraphicsUnit.Pixel);
    
            // Set the just painted image as the display Image.
            Image = useCanvas1Graphics ? canvas1 : canvas2;
    
            // Next time, the other canvas and graphics will be used.
            useCanvas1Graphics = !useCanvas1Graphics;
    
            // Paint all.
            ResumeLayout();
    
            //NOTE: I don't know what is a better idea, do Suspend/Resume layout or 
            // Refresh() at the end.
        } catch(Exception) {
            if (IsRunning)
                Stop();
            MessageBox.Show("Error in LiveView");
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-01
      • 2018-06-30
      • 2020-06-29
      相关资源
      最近更新 更多