Chybné vykreslení pixelů   zodpovězená otázka

VB.NET, WinForms, Grafika

Zdravím,

[Visual Studio 2010 Profesional, Framework 3.5, Visual Basic]

podle postupu z http://msdn.microsoft.com/en-us/library/... pracuji s polem bajtů z bitmapy. Vše mi funguje bezproblémů do doby dokud nenastavím rozměry bitmapy na liché.

Potom se mi vykreslený obrázek rozpadne.

Jak to!?!

Obr1: http://imageshack.us/photo/my-images/28/...

Obr2: http://imageshack.us/photo/my-images/836...

Kód1: http://www.edisk.cz/stahni/62168/BMPRozb...

Option Strict On
Option Explicit On

Imports System.Runtime.InteropServices
Imports System.Drawing.Imaging
Imports System.Math

Public Class Form1

    Private BMP As New Bitmap(285, 291, PixelFormat.Format24bppRgb)

    Private REC As New Rectangle(0, 0, BMP.Width, BMP.Height)

    Private BMPData As BitmapData = BMP.LockBits(REC, ImageLockMode.ReadWrite, BMP.PixelFormat)

    Private Ptr As IntPtr = BMPData.Scan0

    Private BITY As Integer = Abs(BMPData.Stride) * BMP.Height

    Private RGB(BITY - 1) As Byte

    Private Sub Ctverec()
        Marshal.Copy(Ptr, RGB, 0, BITY)

        For X As Integer = 10 To 100
            For Y As Integer = 10 To 100
                'Převedeme dvojici souřadnic X a Y do podoby
                'jednorozměrného indexu pole(uff...)
                'tak že vynásobýme Y šířkou a ještě 3
                'protoře je každý pixel reprezentován třemi
                'hodnotami barev a přičteme X * 3
                Me.RGB(Y * BMP.Width * 3 + X * 3) = 255
                'výsledek by měl být modrý čtverec(10,10,100,100)
                'Co se ale stane když nastavím bitmapu na liché
                'rozlišení, třeba 285x291?
                'Něják se to rozjede...
            Next
        Next

        Marshal.Copy(RGB, 0, Ptr, BITY)

        BMP.UnlockBits(BMPData)
    End Sub
    Private Sub Vykreslit(ByVal S As Object, ByVal e As PaintEventArgs) Handles Me.Paint
        Ctverec()
        e.Graphics.DrawImage(BMP, 0, 0)
    End Sub
End Class

Protože v kódu nemohu najít chybu, usoudil jsme že mu naprosto nerozumím a nemůžu ho použít bez toho abych ho naplno pochopil.

Proto prosím někoho kdo má trpělivost s tak beznadějným případem jako jsem já aby mi vysvětlit následujíí:

Kód2: http://www.edisk.cz/stahni/85939/BMPVysv...

1) Proč se musí metodě LockBits() předávat obdelník?

2) Co za datový typ je IntPtr? Vidím ho poprvé.

3) Co je BitmapData.stride(). Proč je v kódu použita absolutní hodnota

4) Co přesně dělá Marshal.Copy()?

Option Strict On
Option Explicit On

Imports System.Runtime.InteropServices
Imports System.Drawing.Imaging
Imports System.Math

Public Class Form1
    'Vytvořím novou bitmapu 300x300 s 24 bitouvou barevnou hloubkou.
    Private BMP As New Bitmap(300, 300, PixelFormat.Format24bppRgb)

    'Obdélník :D začátek na 0x0 to znamená na začátku souřadného systému.(normálně vlevo nahoře)
    's velikostí 300x300(nebo to co se nastaví konstruktoru bitmapy)
    Private REC As New Rectangle(0, 0, BMP.Width, BMP.Height)

    'Uzamkuntí Bitů. Nevím proč to potřebuje obdélník. Asi proto aby se vědelo který bity to má zamknout?
    Private BMPData As BitmapData = BMP.LockBits(REC, ImageLockMode.ReadWrite, BMP.PixelFormat)

    'Tak tady je zvláštnost. Co je to datový týp IntPtr? Vím jenom že to je nějákej Integer :D.
    Private Ptr As IntPtr = BMPData.Scan0

    'Stride. Předpokládám že to vrátí počel pixelu na řádku vynásobeno počtem hodnot reprezentující barvu(doufám že 3)
    'Proč je to ale zabaleno ve funkci math.abs()? Vrací to snad zápornou hodnotu?
    Private BITY As Integer = Abs(BMPData.Stride) * BMP.Height

    'Pole do kterého budeme ukládat hodnoty barevných složek pixelů
    Private RGB(BITY - 1) As Byte

    Private Sub Ctverec()
        'Tak tady potřebuju vědět jak to fungule.
        'zdroj, cíl, start index, a délka
        'Jakto že můžu použít jako zdroj číslo?(IntPtr)
        Marshal.Copy(Ptr, RGB, 0, BITY)

        'Každá třetí hodnota v poli bude mít hodnotu 255
        'začínám od nuly takže by výsledek měl být červený(Red Green Blue)
        'ale je to modrý :( (asi to je BGR (blue green red))
        For i As Integer = 0 To BITY - 1 Step 3
            Me.RGB(i) = 255
        Next

        'Pff
        Marshal.Copy(RGB, 0, Ptr, BITY)

        'Nakonec musíme btity odemknout
        BMP.UnlockBits(BMPData)
    End Sub
    'No tak vykreslíme výsledek
    Private Sub Vykreslit(ByVal S As Object, ByVal e As PaintEventArgs) Handles Me.Paint
        Ctverec()
        e.Graphics.DrawImage(BMP, 0, 0)
    End Sub
End Class

nahlásit spamnahlásit spam 2 / 4 odpovědětodpovědět

Proč používáte kód, který nevíte co dělá a pointery + Marshal na vykreslení obyčejného čtverce?!

nahlásit spamnahlásit spam 1 / 3 odpovědětodpovědět

Tak,

nepoužívám to na vykreslení obyčejného čtverce. Ale na vykreslení orbitu dynamického systému Martin! viz: http://vbnet.cz/forum-tema--4962-jak_vyk... .

Jenže na tu chybu jsem přišel běhel vykreslování čtverce. A snažím se přijít na to co to způsobuje.

nahlásit spamnahlásit spam 2 / 4 odpovědětodpovědět

1) Obdélník se předává proto, že nemusíte chtít zamknout celou bitmapu, ale třeba její obdélníkovou část.

2) IntPtr je adresa v paměti, v samotném .NETu by byl prakticky k ničemu, jediné jeho použití je při spolupráci s Windows API nebo s unmanaged kódem. V tomto případě metoda LockBits vykopíruje kus bitmapy a místo pole bajtů vám vrátí pointer na místo v paměti, kde to začíná.

3) Stride je počet bajtů v řádku. Pokud je šířka obrázku lichá a na každý pixel používáte 3 bajty, tak je potíž v tom, že počet bajtů v řádku by byl lichý, ale grafické karty mají rády, když to je násobek 4. Takže ve skutečnosti se to v paměti dorovná nulami na násobek 4 a abyste to nemusel počítat, vlastnost Stride vám vrátí, kolik bajtů v řádku máte. Při přístupu do pole pak místo šířky řádku * 4 použijete Stride.

4) Marshal.Copy zkopíruje kus paměti někam jinam.

Celé se to dělá takhle zamotaně, protože tyto princiyp vychází z Windows API, kde se to dělalo podobně - zamknete bity, vykopírujete si data, změníte je, nakopírujete je zpátky a zase je odemknete.

nahlásit spamnahlásit spam 4 / 4 odpovědětodpovědět

Bomba!!! Kód funguje! A snad jsem pochopil celý kód :D.

Vy jste opravdu muž na správném místě.

nahlásit spamnahlásit spam 3 / 5 odpovědětodpovědět

Opravdu je nutné kopírovat, co takhle přez maršal jen zapisovat byty či integry? Nehamtá se tolik paměti.

nahlásit spamnahlásit spam 0 / 2 odpovědětodpovědět

Pokud needitujete opravdu velké bitmapy (třeba FullHD 1920x1200 má pouhých 6MB), tak spotřeba paměti nikoho netrápí.

Zapisovat přes Marshal jednotlivé bajty by šlo, ale to by bylo daleko pomalejší, zvlášť pokud jich chcete zapisovat hodně.

Daleko rychlejší je to vykopírovat ven a přistupovat k tomu normálně.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback