Konverze z C# do VB   zodpovězená otázka

C#, VB.NET

Zdravím

Mohl by mi někdo pomoci s převodem tohoto kódu do VB?

System.IntPtr Scan0 = bmData.Scan0;
System.IntPtr SrcScan0 = bmSrc.Scan0;
unsafe
  {
    byte * p = (byte *)(void *)Scan0;
    byte * pSrc = (byte *)(void *)SrcScan0;
    /* .. */
  }

Sám to nezvládnu a konvertor mi vrátil toto:

Dim Scan0 As System.IntPtr = bmData.Scan0
Dim SrcScan0 As System.IntPtr = bmSrc.Scan0

Dim p As Byte = CByte(CType(Scan0, void))
Dim pSrc As Byte = CByte(CType(SrcScan0, void))

S tím, že VS mě seřve, že System.Void může být použito pouze ve výrazu GetType. Když ale 'void' nahradím za 'GetType(void)', dostanu se k další chybové hlášce. Tentokrát k tomu, že klíčové slovo nejmenuje datový typ.

Má to nějakou spojitost s větví unsafe? Konvertor mi vyhodil chybu, protože unsafe jím není podporováno. Mě je význam této konstrukce neznámý.

Nevím co s tím, předem díky za pomoc.

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

Zjistil jsem, že konstukce unsafe zapouzdřuje kód, který pracuje s ukazateli a fixes-size buffery, a že projekt musí mít při kompilaci parametr unsafe, aby mohl být sestaven a tato konstrukce používána. Nemám ale ideu, jak to přepsat do VB. Vlastnosti projektu ve VB neobsahují přepínač 'Allow unsafe code'. Celkově je karta Build oproti C# o hodně chudší.

Nevíte tedy, jak to vyřešit? Podporuje vůbec VB práci s ukazateli?

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

Přímou podporu práce s ukazateli VB nenabízí. Existuje sice typ IntPtr, což je jakási obdoba "řízeného" ukazatele, ale ten spravuje samotný VB a my s ním nemůžeme dělat totéž, co třeba v C++, či v unsafe C#. (alespoň "chytré knihy" to říkají)

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

Velice laicky - snad se strefím.

S tím, jak pracuje unsafe mód v C# nemám zkušenosti, ale předpokládám (i dle zápisu), že je to pupeční šňůra k C++.

No a pokud kouknu na ten původní kód, pak IntPtr je nějaká obdoba (spíše náhražka) ukazatelů z unmanaged v managed kódech.

Předpokládám, že bmData budou asi nějaká BitmapData, tj. reprezentace nějakého obrázku přímo v paměti a jejich metoda Scan0 vrací ukazatel na začátek této oblasti dat.

Dále v tom Unsafe pouze "přetypováváte" ten ukazatel. Ale ten překlad pro VB nedává smysl (pokud tedy pro typ inptr neexistuje nějaké přetížení metody CType), protože, alespoň v C++ při "přetypovávání" ukazatele de facto neměníte formát tohoto ukazatele (stále ukazuje na stejné místo v paměti), pouze mu říkáte, jak má data na tom místě kam ukazuje interpretovat (kolik bytů má vzít, o kolik bytů se má posunout, chce-li jít na následující prvek apod.)

Dalším problémem (a mnohem závažnějším) ale je, že VB nemá Unsafe bloky, takže stejně uvedený "doslovný převod" je Vám k ničemu a musíte to řešit nějak jinak.

Alespoň co jsem z různé literatury pochytil, tak problém je v tom, že v managed kódu se Vám o paměť stará GC a vy nikdy nevíte, kdy Vám vaše data přesune na jiné místo v paměti. Proto se ani nemůžete jednoduše na ta data odkazovat nějakým "ukazatelem".

Třeba u práce s bitmapami se to ale dá ale obejít tím, že si ta data v paměti "zamknete" (metodou lockBits), ale stejně pro práci s nimi (protože VB Vás ani tak nepustí přímo do unsafe paměti) si je musíte zkopírovat do řízeného pole ve kterém provedete úpravy a pak to celé vrátíte na původní místo a odemknete.

Takže by bylo spíše potřeba vědět, co ten Váš program provádí s daty v tom "Unsafe" bloku a zkusit to nějak přepsat do pro VB stravitelné formy.

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

V unsafe bloku je zapoudřena práce s transformační maticí (jedná se o grafický filtr), a jelikož nemám ani tušení, jak transformace pomocí matic probíhá, bylo by pošetilé snažit se to přepisovat.

Vyřeším to tedy tak, že si v C# vytvořím knihovnu, kam šoupnu tu metodu a k projektu jí připojím jako referenci. Zdá se mi to jako nejlepší řešení. Děkuji za snahu a vyčerpávající odpověď!

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

Já si to myslel :-)

Pro VB ale existují 2 cesty:

- buď grafické transformace využívající transformačních matic - na to ale nepotřebujete žádné unsafe módy ani ukazatele, protože ta transformační matice se aplikuje přímo na bázový grafický typ při jeho vykreslování, třeba "školský" příklad pro inverzi barev:

(můžou tam být překlepy - píšu to přímo do okna odpovědi):

' nejprve nadefinovat transformační matici
' různými hodnotami lze dosáhnout různých efektů
Dim TransformMatice as new ColorMatrix(New Single()() { _
New Single() {-1,0,0,0,0}, _
New Single() {0,-1,0,0,0}, _
New Single() {0,0,-1,0,0}, _
New Single() {0,0,0,1,0}, _
New Single() {1,1,1,0,1}})

' do vlastností obrázku vložíme naši matici jako barevnou transformaci
Dim AtribObr As new ImageAttributes()
AtribObr.SetColorMatrix(TransformMatice)

' a obrázek normálně vykreslíme, pouze pouřijeme jiné přetížení
GrafickyObjekt.DrawImage(PůvodníObrázek, _
ObdélníkKamVykreslit,0,0, _
PůvodníObrázek.Width,PůvodníObrázek.Height, _
GraphicsUnit.Pixel, AtribObr)

A obrázek se vykreslí včetně transformace.

Druhou možností je práce přímo s bitovou mapou (tzn. jako při užití těch ukazatelů). Vypadá to sice složitě jak dvoják žebř, ale je to velejednoduché a dají se s tím dělat jakékoliv nepřístojnosti:

Princip je asi takový:

' nejprve vytvoříme obdélník ohraničující plochu (obrázku), se kterou budeme pracovat

Dim Obdelnik as New rectangle (0,0,Obrázek.width, Obrázek.Height)

' teď si na chviličku zahraleme na "C-éčkaře a sáhneme si přímo do paměti
' nejprve data našeho obrázku uzamkneme v paměti, aby nám je GC někam nepřesunulo

Dim ObrazovaData as BitmapData = Obrázek.LockBits(Obdelnik, _
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb)

' a teď přichází ten "speciální" "řízený" ukazatel, který si nastavíme tak,
' aby nám ukazoval na začátek právě uzamknuté oblasti paměti

Dim DataZacatek As IntPtr = ObrazovaData.Scan0

' teď potřebujeme znát, jak "dlouhé" toto pole je.
' počet pixelů je jasný (šířka obrázku x výška) a počet
' byte pro každý pixel závisí na formátu, který jsme použili
' v příkaze LockBits (v našem případě 3 byty na 1 pixel)

Dim Bytes as integer = Obrázek.Width * Obrázek.Height * 3

' teď si vytvoříme pole (již v normální řízené oblasti paměti, tedy zcela obyčejné pole)

Dim HodnotyRGB(Bytes-1) As Byte

' a do takto vytvořeného pole překopírujeme tu naši zamknutou část paměti 

System.Runtime.InteropServices.Marshal.Copy(DataZacatek, HodnotyRGB, 0, Bytes)

' a to je celá příprava, teď máme celý obrázek nasáčkován v našem poli,
' se kterým můžeme docela obyčejně pracovat
' formát je jasný, je to jednorozměrné pole, kde první prvek představuje
' složku R prvního pixelu, druhý prvek složku G a třetí prvek
'  složku B, čtvrtý prvek pak složku R druhého pixelu atd...
' můžeme s tím udělat co se nám zlíbí, třeba ta minulá inverze
by vypadala třeba takto

For i As Integer = 0 To Bytes-1
   HodnotyRGB(i)=Cbyte(255-HodnotyRGB(i)
Next

' zkrátka projdeme celé pole a hodnoty barevných složek zinvertujeme

' a na závěr zbývá už jen projít tu děsnou "přípravnou část" pěkně opačným postupem zpátky:

' nejprve data vrátíme tam, kam patří
System.Runtime.InteropServices.Marshal.Copy(HodnotyRGB, _
0, DataZacatek, Bytes)

' a odemkneme bitmapu

Obrázek.UnlockBits(ObrazovaData)

' toť vše, už zbývá obrázek pouze běžným způsobem vykreslit
' (v proměnné obrázek teď již máme obrázek zinvertovaný)

GrafickýObjekt.DrawImageUnscaled(Obrázek, New Point(0,0))

Skutečně je to mnohem jednodušší, než to na první pohled vypadá a přestože by člověk řekl, že to musí být šíleně pomalé (jak se stále něco někam znovu a znovu kopíruje), opak je pravdou, a většinou je to ještě o poznání rychlejší, než u té transformace pomocí matic. (záleží ale na typu transformace).

P.S. neručím za funkčnost - datloval jsem to přímo, tak tam asi budou nějaké překlepy, ale metodika by měla fungovat)

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

V první řadě velmi děkuji za příklad na uzamknutí Bitmapy. Když sem tam zkouším něco s grafikou, používám SetPixel/GetPixel. Samozřejmě, najít to by byla otázka maximálně minuty, ale někteří lidé jsou prostě líní :-). Takže už ukládám Snippet.

V druhé řadě děkuji za vysvětlení, to má velkou hodnotu a ušetří spoustu času. Teď asi strávím nějakou chvíli nad převodem té matice z C# do VB, ale s tím se už poperu sám. Děkuji Ti za rady!

nahlásit spamnahlásit spam 0 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