2014-10-21 2 views
0

У меня проблема с растровым изображением в C++/CLI. Короче говоря, мне нужно загрузить фрагмент Bitmap в PictureBox, отобразить его и переместить его на несколько строк ниже. Я делаю следующие шаги:Перемещение растрового изображения внутри PictureBox

1 - Загрузите полное растровое изображение с жесткого диска и получите его параметры (ширина, высота и т. Д.).

2 - Создайте PictureBox, который сможет полностью содержать этот растровый рисунок.

3 - Создайте растровое изображение, которое я использую в качестве области рисования, и поставлю его внутри PictureBox.

4 - Скопируйте фрагмент (первые 150 строк) исходного растрового изображения в мою область рисования и отобразите его.

5 - Переместите этот кусок на несколько строк ниже. (Вот моя проблема).

Когда я перемещаю изображение, у меня есть ошибка: изображение отображается влево, а справа - левая часть изображения. Я загрузил фотографии:

Это кусок изображения, прежде чем переместить его, все в порядке. http://i58.tinypic.com/2duf48x.png

Это изображение после перемещения, как вы видите, оно отображается влево. http://i61.tinypic.com/jz9ond.png

метод я использую, чтобы "двигаться" изображения:

1 - LockBits и получить anIntPtr, который я конвертировать в неподписанные символ *. 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, который вы можете получить, представляет собой массив байтов пикселей, за которым следует другой. Это означает, что у меня не может быть проблем с такими проблемами, как организация Bitmap в памяти и т. Д.

2 - Я протестировал memcpy и memmove.

3 - Я проверил, чтобы сделать простой цикл.

Ничего не работает, я не знаю, что делать.

Спасибо, привет.

ответ

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"); 
    } 
}