Suorat muistiosoitukset ja kuvien pikselidata

Oletko tehnyt jotain, mistä muut voisivat hyötyä. Postita vinkit tänne.
Post Reply
User avatar
CCE
Artist
Artist
Posts: 650
Joined: Mon Aug 27, 2007 9:53 pm

Suorat muistiosoitukset ja kuvien pikselidata

Post by CCE »

Nyt kun Blitz3D:n lähdekoodit ovat vapaata riistaa, olen hieman tutkiskellut sen runtimen sielunelämää. Koodi on tyyliltään aika kivaa C-henkistä C++:ssaa, eli varsin helppoa luettavaa.

Halusin päästä käsiksi kuvien pikselidataan suoraan CoolBasicin puolelta, ihan ilman PutPixelin käyttöä. Tämä vaatisi kuitenkin kirjoittelun mielivaltaisiin muistiosoitteisiin, jota CB ei varsinaisesti tue.

Ongelma kuitenkin helpottuu, jos huomaamme että CB:n muistipalat ovatkin oikeastaan vain bbBank-tietueita, joiden määritelmä on seuraava:

Code: Select all

struct bbBank {
	int paske; // Only in CoolBasic implementation, extra info for VM?
	char *data; // MemBlock data, this is visible to the programmer.
	int size, capacity;
};
Tietueen ensimmäinen jäsen on jotain ihme paddingia, koska sitä ei näy Blitzin koodissa mutta CB:n MemBlockeissa kylläkin. Ehkä se on jotain lisädataa CB:n tulkkia varten. data-kenttä on muistipalan sisältö joka näkyy innokkaalle CB-koodarille, ja kaikki Peek ja Poke -komentojen operaatiot tehdään tämän osoittimen päässä olevalle muistialueelle.

Nyt voimme vielä huomata, että CoolBasicin MEMBlockSize-komento kutsuu Blitzin runtimen bbBankSize-funktiota, joka on koodissa määritelty seuraavasti:

Code: Select all

int bbBankSize( bbBank *b ){
	return b->size;
}
Olin tässä vaiheessa jumissa aika pitkään, kunnes hoksasin, että mehän voimme vain muuttaa parametrina annettavan b-osoittimen arvoa niin että funktio palauttaakin suoraan data-kentän osoitteen!

Code: Select all

blok = MakeMemBlock(8)
addr = MemBlockSize(blok - 4) // Returns m->data pointer!
Nyt voimme käyttää osoitetta addr muistipalana CB:n Peek ja Poke -komennoille. Sujautamme muistipalan kohtaan 4 mielivaltaisen osoitteen A johon haluamme kirjoittaa, ja nyt runtime tulkitsee sen bbBank-tietueen data-jäseneksi.

Nyt voimme kirjoittaa arvon X osoiteeseen A.

Code: Select all

pokeInt blok, 4, A
addr = MEMBlockSize(blok - 4)
PokeInt addr, 0, X
Tehdään tästä vielä nätti funktio.

Code: Select all

global proxyblock
proxyblock = makeMEMBlock(8) // this acts as a bbBank*

function write_int(addr, ofs, value)
	pokeInt proxyblock, 4, addr // ((bbBank*)proxyblock)->data = addr;
	pokeInt MEMBlockSize(proxyblock-4), ofs , value // ((bbBank*)proxyblock->data)[ofs] = value
endFunction
Tiivistettynä, luomme muistipalan jonka sisältöä käytämme bbBank-tietueen ilmentymänä, ja asetamme tietueen data-kentän osoittamaan osoitteeseen johon haluamme kirjoittaa.

Kuvat

Pitkän räpellyksen oman DLL:n ja Visual Studion debuggerin kanssa sain vihdoin selviteltyä miten kuvadata on tallennettu. Eipä siinä mitään kovin erikoista sitten ollutkaan. Tarkoituksena on siis löytää osoitin, johon kirjoittamalla saamme piirreltyä pikseleitä ruudulle.

CoolBasicin kuvat ovat seuraavaa muotoa:

Code: Select all

struct CBImage {
    int useless_data;
    bbImage img;
};
Jos katsomme bbImagen määritelmän, voimme muuttaa koodin seuraavaksi:

Code: Select all

struct CBImage {
    int useless_data;
    std::vector<gxCanvas*> frames;
};
Onneksi C++:n standardikirjaston std::vectorin ensimmäinen jäsen on suoraan osoitin varattuun dataan, ja olemme kiinnostuneita vaan listan ensimmäisestä alkiosta. Koodi yksinkertaistuu seuraavaksi:

Code: Select all

struct CBImage {
    int useless_data;
    gxCanvas** canvas;
};
Voimme alkuosan muistintökkimisfunktioiden avulla nyt lukea ja kirjoittaa kuvadatan sisältävän gxCanvasin kenttiä, mutta mikä niistä on oikea? Katsotaan gxCanvaksen setPixelFast-komennon sisältö.

Code: Select all

void setPixelFast( int x,int y,unsigned argb ){
    format.setPixel( locked_surf+y*locked_pitch+x*format.getPitch(),argb );
    ++mod_cnt;
}
Ilmeisesti locked_surf ja locked_pitch ovat tarpeelliset kentät. gxCanvaksen määritelmästä voimme laskea käsin oikeat osoitelisäykset. Siis locked_pitch on osoitteessa kuvan_osoite + 18*4 ja locked_pitch vastaavasti osoitteessa kuvan_osoite + 14*3.

Alla on listattu gxCanvas-tietueen määritelmä.

Code: Select all

class gxCanvas{
private:
	int flags,cube_mode;
	gxGraphics *graphics;

	ddSurf *main_surf,*surf,*z_surf,*cube_surfs[6];

	mutable int mod_cnt;
	mutable ddSurf *t_surf;

	mutable int locked_pitch,locked_cnt,lock_mod_cnt,remip_cnt;
	mutable unsigned char *locked_surf;

	mutable int cm_pitch;
	mutable unsigned *cm_mask;

	RECT clip_rect;

	PixelFormat format;

	gxFont *font;
	RECT viewport;
	int origin_x,origin_y,handle_x,handle_y;
	unsigned mask_surf,color_surf,color_argb,clsColor_surf;
}
Lopuksi vielä koodiesimerkki jossa luetaan kuvan koko, ja sen jälkeen täytetään se suorilla muistiinkirjoituksilla. Tämä tuskin on nopeampaa kuin PutPixel2:n käyttö, mutta testini oli varsin minimaalinen ja suosittelen muita tekemään omat profilointinsa.

Image
Generoitu kuva näyttää tältä.

Code: Select all

clsColor 64,64,64
cls

global proxyblock
proxyblock = makeMEMBlock(8) // this acts as a bbBank*

function write_int(addr, ofs, value)
	pokeInt proxyblock, 4, addr // ((int*)proxyblock->data)[1] = addr;
	pokeInt MEMBlockSize(proxyblock-4), ofs , value // ((bbBank*)proxyblock->data)[ofs] = value
endFunction

function read_int(addr, ofs)
	pokeInt proxyblock, 4, addr // ((int*)proxyblock->data)[1] = addr;
	return peekInt (MEMBlockSize(proxyblock-4), ofs) // return ((bbBank*)proxyblock->data)->data[ofs];
endFunction

m = makeMEMBlock(8)
write_int(m,8,3) // we can overwrite memblock->size, for example
addText ""+read_int(m, 8)

img = makeImage(200, 200)
maskImage img, 255,0,255

// struct CBImage {
// 	int useless_data;
// 	gxCanvas** canvas;
// };

//struct gxCanvas {
//	int flags, cube_mode;
//	gxGraphics *graphics;
//
//	ddSurf *main_surf, *surf, *z_surf, *cube_surfs[6];
//
//	int mod_cnt;
//	ddSurf *t_surf;
//
//	int locked_pitch, locked_cnt, lock_mod_cnt, remip_cnt;
//	unsigned char *locked_surf; <-- we want this!
//
//	int cm_pitch;
//	unsigned *cm_mask;
//
//	RECT clip_rect;
//
//	PixelFormat format;
//
//	gxFont *font;
//	RECT viewport;
//	int origin_x, origin_y, handle_x, handle_y;
//	unsigned mask_surf, color_surf, color_argb, clsColor_surf;
//};

lock image(img)
canvas_pp = read_int(img, 4)
canvas_p = read_int(canvas_pp, 0)
locked_surf = read_int(canvas_p, 18*4)
// typedef struct tagRECT { 
//   LONG left;   LONG top;   LONG right;   LONG bottom;} RECT;
w = read_int(canvas_p, (21 + 2)*4)
h = read_int(canvas_p, (21 + 3)*4)
locked_pitch = read_int(canvas_p, (14)*4)
addText "pitch: " + locked_pitch

for y = 0 to h-1
for x = 0 to w-1
	write_int(locked_surf, y*locked_pitch + x*4, x + (y shl 8))
next x
next y

unlock image(img)

addText "width: " + w
addText "height: " + h

drawImage img, 100, 50
drawScreen

waitKey
:ugeek:
Post Reply