Bitmap per Pointer kopieren, AccessViolation

03/02/2008 - 23:34 von Michael Stum | Report spam
Hallo,

ich habe gerade irgendwie ein Brett vor dem Kopf oder etwas übersehen.
Ich möchte ein Bitmap kopieren, und zwar über Pointer. (Das alleine
klingt und ist Schwachsinnig, aber das dient spàter zur portierung eines
Rescale-Algorhytmus und vor allem als Lernsubjekt).

Dazu habe ich folgenden Code, welcher sogar funktioniert:

unsafe private void button2_Click(object sender, EventArgs e)
{
Bitmap srcBmp = new Bitmap(pictureBox1.Image);
BitmapData srcBits = srcBmp.LockBits(new Rectangle(0, 0, 32, 32),
ImageLockMode.ReadWrite, srcBmp.PixelFormat);
Bitmap dstBmp = new Bitmap(32, 32, srcBmp.PixelFormat);
BitmapData dstBits = dstBmp.LockBits(new Rectangle(0, 0, 32, 32),
ImageLockMode.WriteOnly, dstBmp.PixelFormat);
for (int y = 0; y < srcBits.Height; y++)
{

byte* row = (byte*)srcBits.Scan0 + (y * srcBits.Stride);
byte* rowDst = (byte*)dstBits.Scan0 + (y * dstBits.Stride);

for (int x = 0; x < srcBits.Width; x++)
{
rowDst[x*4] = row[x*4];
rowDst[(x * 4)+1] = row[(x * 4)+1];
rowDst[(x * 4) + 2] = row[(x * 4) + 2];
rowDst[(x * 4) + 3] = row[(x * 4) + 3];
}
}
srcBmp.UnlockBits(srcBits);
dstBmp.UnlockBits(dstBits);
pictureBox2.Image = dstBmp;
}


Das ganze ist also ein 32x32 Pixel Bitmap, und aus irgendeinem Grund
mache "new Bitmap(Image)" immer ein Format32BppArgb, egal was
Image.PixelFormat ist, aber das nur am Rande.

Wichtig ist hier:
byte* row bzw. byte* rowDst sind Pointer auf die Quell und Ziel Bitmaps.
In der Mitte ist dann die "rowDst[x*4] = row[x*4]" zuweisung.

Soweit, so gut - das funktioniert. Jetzt habe ich aber in der
Zuweisungsschleife 4 Zuweisungen. Warum also nicht den Pointer von byte*
auf uint* umstellen, dann hàtte ich einen 32-Bit statt eines 8-Bit wertes:
uint* row = (uint*)srcBits.Scan0 + (y * srcBits.Stride);
uint* rowDst = (uint*)dstBits.Scan0 + (y * dstBits.Stride);

Die Zuweisung ist dann nur noch "rowDst[x] = row[x]" und alles ist gut -
bis zur letzten Zeile. Da crasht das ganze mit einer AccessViolation (im
geschützten Speicher zu lesen oder zu schreiben).
Wenn ich einen breakpoint auf die row-Zeile setze, dann sehe ich das y
am Ende 30 ist, also 1 weniger als srcBits.Height.

Um noch mehr verwirrung zu erstellen:
Eine Änderung auf
for (int y = 0; y < srcBits.Height-1; y++)
sorgt dafür, das der Crash schon bei y) auftritt. Wenn ich auf
srcBits.Height-2 gehe dann crasht es bei y( und so weiter.

Und jetzt verstehe ich nur Bahnhof.
Warum funktioniert es mit byte* und nicht mit uint* oder int*?

Wie würde ich am besten ein Format32BppArgb Bitmap kopieren - Pixel für
Pixel, mittels eines Pointers? Geht das nur per byte* und 4 Zuweisungen
oder kann ich da irgendwie einen 32-Bit wert direkt reinbauen?

Ist der Cast von IntPtr auf (byte*) oder (uint*) evtl. generell
problematisch? Sollte ich irgendwie mit IntPtr arbeiten (aber dann kann
er y*dstBits.Stride nicht addieren)?
 

Lesen sie die antworten

#1 Thomas Scheidegger
04/02/2008 - 00:29 | Warnen spam
Hallo Michael

Stride



lies dazu alles in der MSDN, kann zB auch negativ sein!

rowDst[x*4] = row[x*4];
rowDst[(x * 4)+1] = row[(x * 4)+1];
rowDst[(x * 4) + 2] = row[(x * 4) + 2];
rowDst[(x * 4) + 3] = row[(x * 4) + 3];
Jetzt habe ich aber in der Zuweisungsschleife 4 Zuweisungen



dies scheint mir eh schon recht falsch (viel zu viel).

Simple Kopie:
Auf Basis Byte-Pointer sind keine solche 4-fach Operationen nötig,
(weder x*4+... noch 4 Zuweisungen)
sondern minimal wohl _eine_ simple 1:1 byte-copy Schleife,
über (Stride*Height) Bytes.
(siehe aber obiger Vorbehalt zu Stride)

Für mit Pointer auf ~Int32, wird es wohl ((Stride / 4)*Height).

Falls mit Farb-OPs, ggf etwas weitere Logik rund um Width & Stride,
spàtestens falls non-32Bit PixelFormate!

Alle Details/Irrtum vorbehalten.





Thomas Scheidegger - 'NETMaster'
http://www.cetus-links.org/oo_dotnet.html - http://dnetmaster.net/

Ähnliche fragen