• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Brak pomysłu na algorytm zapalający pojedynczy piksel na ekranie

Object Storage Arubacloud
0 głosów
420 wizyt
pytanie zadane 25 listopada 2020 w C i C++ przez jpacanowski VIP (101,940 p.)
edycja 25 listopada 2020 przez jpacanowski

Witam,

Od paru godzin męczę się z napisaniem procedury wyświetlającej piksel.

Tryb graficzny to 640 × 400 x 2 kolory (czarny i biały).

Piksel w pamięci ekranu to jeden bit (1 - kolor czarny, 0 - kolor biały). Bity są obok siebie, liniowo.

Nie mam pojęcia jak zapalić pojedynczy bit, gdyż najmniejsza porcja danych to bajt.

Obecnie mam taki kod:

void put_pixel(u16 x, u16 y, u8 *screen)
{
	u16 bitOffset = (y * 640) + x;
	screen[bitOffset] |= (1 >> 7);
}

put_pixel(0, 0, screen);
put_pixel(1, 0, screen);
put_pixel(2, 0, screen);

Ale wyświetla mi 3 piksele oddalone od siebie o 8 punktów, co jest rzeczą oczywistą, bo offset to 1 bajt.

Nie mam pojęcia jak się dobrać do pojedynczego bitu, tak aby kolejny offset był kolejnym sąsiednim bitem.

C++ nie wchodzi w grę.

Nie mam pomysłu...

komentarz 25 listopada 2020 przez tkz Nałogowiec (42,000 p.)
A nie możesz zapalić dwóch bitów w jednym bajcie?
komentarz 25 listopada 2020 przez jpacanowski VIP (101,940 p.)
Funkcja ma wyświetlać pojedynczy piksel.
komentarz 25 listopada 2020 przez tkz Nałogowiec (42,000 p.)
No tak, z tego co rozumiem 8 bitów, to 8 pikseli. Czyli zapaleni dwóch pierwszych bitów 1 bajtowego słowa, powinno zapalić dwa piksele.
komentarz 25 listopada 2020 przez jpacanowski VIP (101,940 p.)
Tak się dzieje.
1
komentarz 25 listopada 2020 przez jankustosz1 Nałogowiec (35,880 p.)

@tkz, To zależy od interpretacji i tego jak jest napisana funkcja wyświetlająca, bo może lecieć dla każdego bajta od najmniej znaczącego bitu. 

Jeżeli jednak chcemy przyjąć odwrotne założenie to można zamienić

u8 maska = 1 << bitIndex;

na

u8 maska = 1 << (7-bitIndex);

 

komentarz 25 listopada 2020 przez jpacanowski VIP (101,940 p.)
edycja 25 listopada 2020 przez jpacanowski
u8 maska = 1 << (7-bitIndex);
for(int x=0; x<100; x++)
  put_pixel(x, 0, screen);

for(int x=20; x<150; x++)
  put_pixel(x, 20, screen);

Zdaje się działać...

Z tym, że draw_rect() nie działa.

DZIĘKI!!!

komentarz 25 listopada 2020 przez tkz Nałogowiec (42,000 p.)
A czym jest draw_rect()?
komentarz 25 listopada 2020 przez jpacanowski VIP (101,940 p.)

Rysuje wypełniony prostokąt...

void high_draw_rect(u16 x1, u16 y1, u16 x2, u16 y2, u8 *screen)
{
	for(int y=y1; y<y2; y++) {
		for(int x=x1; x<x2; x++) {
			high_put_pixel(x, y, screen);
		}
	}
}
high_draw_rect(30, 30, 80, 80, screen);

1
komentarz 26 listopada 2020 przez tangarr Mędrzec (154,860 p.)
void high_draw_rect(u16 x1, u16 y1, u16 x2, u16 y2, u8 *screen)
{
    for (int x=x1; x<x2; x++) {
        high_put_pixel(x, y1, screen);
        high_put_pixel(x, y2, screen);
    }
    for (int y=y1; y<y2; y++) {
        high_put_pixel(x1, y1 screen);
        high_put_pixel(x2, y, screen);
    }
}
komentarz 26 listopada 2020 przez jpacanowski VIP (101,940 p.)
Nie wiem jak wszystkim dziękować... :D

4 odpowiedzi

+1 głos
odpowiedź 29 listopada 2020 przez tangarr Mędrzec (154,860 p.)
wybrane 30 listopada 2020 przez jpacanowski
 
Najlepsza

(Edit: Ta odpowiedź dotyczy drugiego pytania zadanego w tym temacie)

Dla ułatwienia zadania stworzyłem strukturę zawierającą cztery słowa 16 bitowe reprezentującą jeden blok pikseli.

typedef struct pixelBlock {
    uint16_t w1;
    uint16_t w2;
    uint16_t w3;
    uint16_t w4;
} pixelBlock_t;

oraz funkcję ustawiającą piksel w bloku

int setBlockPixel(pixelBlock_t *block, int pixelIndex, uint8_t color) {
    if (block == NULL)
        return 1;
    if (pixelIndex < 0 || pixelIndex >= 16)
        return 1;
    if (color >= 16)
        return 1;
    uint16_t pixelMask = 1 << (15-pixelIndex);
    block->w1 = (block->w1 & (~pixelMask)) | (color & (1 << 0) ? pixelMask : 0);
    block->w2 = (block->w2 & (~pixelMask)) | (color & (1 << 1) ? pixelMask : 0);
    block->w3 = (block->w3 & (~pixelMask)) | (color & (1 << 2) ? pixelMask : 0);
    block->w4 = (block->w4 & (~pixelMask)) | (color & (1 << 3) ? pixelMask : 0);
    return 0;
}

Przy ich pomocy napisałem funkcję ustawiającą dowolny piksel

int setPixel(pixelBlock_t *screen, int x, int y, uint8_t color) {
    if (screen == NULL)
        return 1;
    if (x < 0 || x >= 320)
        return 1;
    if (y < 0 || y >= 200)
        return 1;
    if (color >= 16)
        return 1;
    int blockNumber = x / 16;
    int pixelIndex = x % 16;
    int index = y*20 + blockNumber;

    pixelBlock_t *block = screen + index;
    return setBlockPixel(block, pixelIndex, color);
}

Procesor Motorola 68000 pracuje w trybie big endian, więc raczej nie powinno być problemów z kolejnością bajtów.

komentarz 30 listopada 2020 przez jpacanowski VIP (101,940 p.)

Miałem stresa odpalając kod, bo jakby znowu nie zadziałał, to już nie zamierzałem Ciebie dalej męczyć, ale z drugiej strony zależało mi na działającej funkcji setpixel(), bo bez niej bym raczej porzucił dalszy rozwój swojej biblioteki, w której docelowo chcę napisać grę. Bez funkcji setpixel() nie byłoby dalszego rozwoju tej biblioteki, bo na tej funkcji się wszystko opiera.

No więc... działa... :)

/* ustaw paletę kolorów */
setcolor(0x700, 11); /* red */
setcolor(0x070, 12); /* green */
setcolor(0x007, 13); /* blue */

setPixel(screen, 0, 0, 11);
setPixel(screen, 1, 0, 11);

setPixel(screen, 2, 0, 12);
setPixel(screen, 3, 0, 12);

setPixel(screen, 4, 0, 13);
setPixel(screen, 5, 0, 13);

komentarz 30 listopada 2020 przez tangarr Mędrzec (154,860 p.)
Dla pewności, czy działa poprawnie narysuj prostokąt o rozmiarach 320X200. Zobaczymy, czy funkcja setPixel działa poprawnie dla całej przestrzeni.
komentarz 30 listopada 2020 przez jpacanowski VIP (101,940 p.)
Boję się... :D

Za jakiś czas sprawdzę :D

Okomentuję twój kod w swojej bibliotece, że to twoja robota ;)
+1 głos
odpowiedź 25 listopada 2020 przez tangarr Mędrzec (154,860 p.)

Najpierw musisz znaleźć komórkę pamięci, którą chcesz zmodyfikować

u16 offset = ((y * 640) + x)/8;

Potem musisz znaleźć indeks bitu wewnątrz komórki

u8 bitIndex = ((y*640)+x)%8;

Następnie tworzysz maskę bitową z ustawionym odpowiednim bitem

u8 maska = 1 << bitIndex;

I na koniec modyfikujesz odpowiednią komórkę

screen[offset] |= maska;

 

komentarz 25 listopada 2020 przez jpacanowski VIP (101,940 p.)

Wyświetla tak...

komentarz 25 listopada 2020 przez tangarr Mędrzec (154,860 p.)
Jesteś pewien, że kod wyświetlający działa prawidłowo?

Zrób eksperyment. Zobacz jak to wygląda bez ustawionych pikseli. A potem osobno dla każdego piksela.
komentarz 25 listopada 2020 przez jpacanowski VIP (101,940 p.)

Czyści cały ekran na biało

void clrscr(u32 *screen)
{
	for(u32 i=0; i<8000; i++) {
		screen[i] = 0;
	}
}

 

...i czarno.

void clrscr(u32 *screen)
{
	for(u32 i=0; i<8000; i++) {
		screen[i] = 0xffffffff;
	}
}

 

komentarz 25 listopada 2020 przez tangarr Mędrzec (154,860 p.)

Testowanie tylko tych dwóch kombinacji to troszkę mało.
A jaki efekt dostaniesz dla

void test(u32 *screen, u8 offset) {
    u32 value = 0x01010101 << offset;
    for(u32 i=0; i<8000; i++) {
        screen[i] = value
    }
}

przetestuj offset w granicach 0-7

komentarz 25 listopada 2020 przez jpacanowski VIP (101,940 p.)

Taki...

Przesuwa się ładnie...

for(int i=0; i<8; i++)
  test(screen, i);

 

komentarz 26 listopada 2020 przez Oscar Nałogowiec (29,290 p.)
W lewo czy w prawo?
komentarz 26 listopada 2020 przez jpacanowski VIP (101,940 p.)
W lewo.
+1 głos
odpowiedź 25 listopada 2020 przez jankustosz1 Nałogowiec (35,880 p.)

@jpacanowski, Możesz użyć bitset'a, bez tego będzie ciężej, ewentualnie możesz sam napisać podobną strukturę. Jeżeli piszesz w c możesz poszukać w internecie jakieś gotowej implementacji.

Btw. kod który napisałeś jest bez sensu, powinno być coś tego typu:

u8 screen[640*400/8];

void put_pixel(u16 x, u16 y, u8 *screen)
{
    u16 arrayIndex = (y * 640 + x )/8;
    u16 bitOffset = x%8;
    screen[arrayIndex ] |= (1 << bitOffset);
}

Nie testowałem tego ale coś tego rodzaju powinno być.

komentarz 25 listopada 2020 przez jankustosz1 Nałogowiec (35,880 p.)

@jpacanowski, i bezpieczniej jest trzymać u8 *screen niż u32*, bo w zdefiniowałeś jako tablicę u8 i można się pomylić jak argumentem jest u32* i potem wysyłasz do funkcji z u8*, ale w tym przypadku to akurat nie robi różnicy w działaniu

komentarz 25 listopada 2020 przez jpacanowski VIP (101,940 p.)
void put_pixel(u16 x, u16 y, u8 *screen)
{
	//u16 bitOffset = (y * 640) + x;
	//screen[bitOffset] |= (1 >> 7);

	//bitptr p = create_bitptr(screen, bitOffset);
	//set_bit(p);

	//const int wholeBytes = bitOffset / 8;
	//const int remainingBits = bitOffset % 8;

	u16 offset = ((y * 640) + x) / 8;
	u8 bitIndex = ((y * 640) + x) % 8;
	screen[offset] |= (1 << bitIndex);
}

 

komentarz 25 listopada 2020 przez jankustosz1 Nałogowiec (35,880 p.)
no to co już pisaliśmy, i to działa, ale problem jest z wyświetlaniem na ekran
komentarz 25 listopada 2020 przez jpacanowski VIP (101,940 p.)
komentarz 25 listopada 2020 przez jankustosz1 Nałogowiec (35,880 p.)
Dodałem komentarz pod pytaniem on naprawi problem z tym pustym odcinkiem, ale te ukośne kreski mogły się pojawić tylko z powodu błędnego wyświetlania.
0 głosów
odpowiedź 28 listopada 2020 przez jpacanowski VIP (101,940 p.)

Mam znowu podobny problem w implementacji algorytmu putpixel(), ale tym razem dotyczący trybu graficznego LOW (najważniejszego w Atari ST).

To ostatnia rzecz z którą mam największy kłopot, więc nie będę Was już męczył więcej w przyszłości.. ;)

A więc...

Nie radę sobie od dłuższego czasu z implementacją algorytmu rysującego pojedynczy piksel w trybie 320x200x16 kolorów.

Sprawa jest tym skomplikowana, że Atari ST używa bitplanów...

Rozdzielczość ta ma 16 kolorów, każdy kolor jest opisany 4 bitami (kod koloru od 0 do 15).

Ok, ustawmy sobie paletę kolorów dla np. trzech kolorów...

setcolor(0x700, 11); /* red */
setcolor(0x070, 12); /* green */
setcolor(0x007, 13); /* blue */

Jeśli chcę wyświetlić teraz piksel o kolorze niebieskim, to muszę zmodyfikować pierwszy bit w następnych czterech słowach...

Kod wyświetlający piksel (przykładowy kod w asemblerze Motorola 68000):

; rejestr a0 wskazuje na początek pamięci ekranu
move.w  #%1000000000000000, (a0)
move.w  #%0000000000000000, 2(a0)
move.w  #%1000000000000000, 4(a0)
move.w  #%1000000000000000, 6(a0)

1011 = 13 (kolor niebieski)

W asemblerze wiem jak to zrobić, bo kod rysuje piksel na sztywno, ale nie radzę sobie z napisaniem funkcji putpixel().

Kod w C jaki napisałem (ale nie działa poprawnie):

void low_put_pixel(u16 x, u16 y, u16 color, u16 *screen)
{
	int c, k;
	u8 color_bin[4];

	/* tu zamieniam liczbę dziesiętną na binarną */
    for (c = 0; c <= 3; c++) {
		k = color >> c;
		color_bin[c] = (k & 1) ? 1 : 0;
	}

	u16 offset = ((y * 320) + x) / 16;
	u8 bitIndex = ((y * 320) + x) % 16;

	if(color_bin[0]) {
		screen[offset++] |= (1 << (15-bitIndex));
	} else {
		screen[offset++] &= ~(1 << (15-bitIndex));
	}

	if(color_bin[1]) {
		screen[offset++] |= (1 << (15-bitIndex));
	} else {
		screen[offset++] &= ~(1 << (15-bitIndex));
	}

	if(color_bin[2]) {
		screen[offset++] |= (1 << (15-bitIndex));
	} else {
		screen[offset++] &= ~(1 << (15-bitIndex));
	}

	if(color_bin[3]) {
		screen[offset] |= (1 << (15-bitIndex));
	} else {
		screen[offset] &= ~(1 << (15-bitIndex));
	}
}

 

komentarz 28 listopada 2020 przez jankustosz1 Nałogowiec (35,880 p.)

Można chyba w c używać wstawki z assemblera ale nwm, ja nigdy w assemblerze nie pisałem.

ciężko mi coś powiedzieć, spróbuj na sztywno ustawić tak:

screen[0]=screen[2]=screen[3] = 1<<15;
screen[0]=screen[2]=screen[3] = 1<<13;
screen[0]=screen[2]=screen[3] = 1<<11;
screen[0]=screen[2]=screen[3] = 1<<9;
screen[0]=screen[2]=screen[3] = 1<<7;
screen[0]=screen[2]=screen[3] = 1<<5;
screen[0]=screen[2]=screen[3] = 1<<3;
screen[0]=screen[2]=screen[3] = 1<<1;

I pokaż co to wyśietla

komentarz 28 listopada 2020 przez jpacanowski VIP (101,940 p.)
edycja 28 listopada 2020 przez jpacanowski
setcolor(0x700, 11); /* red */
setcolor(0x070, 12); /* green */
setcolor(0x007, 13); /* blue */

screen[0]=screen[2]=screen[3] = 1<<15;
screen[0]=screen[2]=screen[3] = 1<<13;
screen[0]=screen[2]=screen[3] = 1<<11;
screen[0]=screen[2]=screen[3] = 1<<9;
screen[0]=screen[2]=screen[3] = 1<<7;
screen[0]=screen[2]=screen[3] = 1<<5;
screen[0]=screen[2]=screen[3] = 1<<3;
screen[0]=screen[2]=screen[3] = 1<<1;

W tym trybie graficznym właśnie od tego miejsca jest początek ekranu (emulator wtedy centruje ekran), więc ten pierwszy zielony piksel jest na pozycji 0,0 - wygląda na to, że twój kod działa.

komentarz 28 listopada 2020 przez jankustosz1 Nałogowiec (35,880 p.)
edycja 28 listopada 2020 przez jankustosz1

Hmmm, nie uważasz że powinny być odstępy między każdym pikselem?

Do tego wydawało mi się że piksele będą niebieskie. To weźmy inny przykład:

for(int i =0;i<=15; i++){
 if(i%2)
  screen[0]=screen[2]=screen[3] = 1<<i;
 else
  screen[2]=screen[3] = 1<<i;
}

for(int i =0;i<=15; i++){
 if(i%2)
  screen[0+4]=screen[1+4]=screen[3+4] = 1<<i;
 else
  screen[2+4]=screen[3+4] = 1<<i;
}

 

komentarz 28 listopada 2020 przez jpacanowski VIP (101,940 p.)

To samo wyświetla. Sprawdzałem 3 razy...

Bitplany działają tak (pewnie źle wytłumaczyłem):
https://nguillaumin.github.io/perihelion-m68k-tutorials/tutorial-05.html
(patrz dla rozdzielczości Low resolution)

#%1000000000000000 (pierwsze 16 bitów - słowo)
#%0000000000000000 (drugie 16 bitów - słowo)
#%1000000000000000 (trzecie 16 bitów - słowo)
#%1000000000000000 (czwarte 16 bitów - słowo)

i to wyświetli jeden piksel o kolorze kodu 13 (1011)
W moim kodzie w asemblerze to działa, i wiem jak łączyć ASM + C, ale jak widzisz, mój kod w asemblerze działa na sztywno, i resztę bitów zeruje.

Np. ten kod wyświetla 16 pikseli obok siebie, w kolorze kodu 3 (1100):

move.w  #%1111111111111111, (a0)+
move.w  #%1111111111111111, (a0)+
move.w  #%0000000000000000, (a0)+
move.w  #%0000000000000000, (a0)+

 

komentarz 29 listopada 2020 przez tangarr Mędrzec (154,860 p.)

Możesz pokazać jak są wyświetlone piksele przez ten kod

move.w  #%1111111111111111, (a0)+
move.w  #%1111111111111111, (a0)+
move.w  #%0000000000000000, (a0)+
move.w  #%0000000000000000, (a0)+

Piksele są wyświetlane od lewej do prawej czy od góry do dołu a może jeszcze inaczej?
 

komentarz 29 listopada 2020 przez jpacanowski VIP (101,940 p.)

16 pikseli od pozycji 0,0

Więc chyba spoko...

Kolor się zgadza, bo taką mam ustawioną paletę.

komentarz 29 listopada 2020 przez jankustosz1 Nałogowiec (35,880 p.)
dlaczego 16, a nie 8?

Skoro 4 bity przypadają na jeden piksel, a bitów jest 32 to powinno być 8 pikseli. Mylę się?
komentarz 29 listopada 2020 przez tangarr Mędrzec (154,860 p.)
Na tej platformie piksele są przechowywane w 64 bitowych grupach (cztery słowa szesnastobitowe). Jedna grupa reprezentuje 16 pikseli. Dlatego takie przesunięcie.

Ciekawe dlaczego twórcy konsoli (a może procesora) zdecydowali się na taki sposób obsługi pikseli.
komentarz 30 listopada 2020 przez jpacanowski VIP (101,940 p.)

Ciekawe dlaczego twórcy konsoli (a może procesora) zdecydowali się na taki sposób obsługi pikseli.

To mikrokomputer, a nie konsola. Atari ST
https://pl.wikipedia.org/wiki/Atari_ST

To konkurent Amigi 500 z tych samych czasów (też na tym samym procesorze)
https://pl.wikipedia.org/wiki/Amiga_500

To raczej decyzja projektantów komputera, niż procesora.

https://pl.wikipedia.org/wiki/MC68000
Sam procesor jest ciekawy, bo ma 8 32-bitowych rejestrów danych ogólnego przeznaczenia (D0 - D7) oraz 8 32-bitowych rejestrów adresowych (A0 - A7).

Tu jeden z wielozadaniowych OSów na Atari ST:
https://www.youtube.com/watch?v=f389fxMi1Lw
(tu akurat odpalony na Atari Falcon, ale to bez większego znaczenia - procesor ten sam, ciutkę szybszy. Używałem, wiem)

W tamtych czasach w tym samym czasie, Mac OSy oferowały jeszcze tylko jednozadaniowość...

komentarz 17 grudnia 2020 przez jpacanowski VIP (101,940 p.)

Ciekawe dlaczego twórcy konsoli (a może procesora) zdecydowali się na taki sposób obsługi pikseli.

The main reasons are i) it is essentially a form of compression (handy for early low-memory, slow CPU computers and consoles), and ii) it simplifies video output especially when you have to support multiple palette sizes. (It can also be used to reduce memory bandwidth requirements for video chips, allowing the use of cheaper memory, but I'm pretty sure that is not required on the ST.)

Bitplanes allow the same character/icon set to be used in all graphics modes, but the same would be true with simple LUTs to convert 1bpp bitmaps to 2 or 4-bit packed pixels. (a simple 256x4 byte table would unpack 1 byte, 8 1-bit pixels, into 8 4-bit pixels packed into 4 bytes, same for a 256x2 table for 2bpp modes, either would fit into the 32-bit data registers of the 68k)

Podobne pytania

+1 głos
1 odpowiedź 279 wizyt
0 głosów
1 odpowiedź 199 wizyt
0 głosów
2 odpowiedzi 202 wizyt
pytanie zadane 15 czerwca 2022 w C i C++ przez MichaelM Bywalec (2,520 p.)

92,568 zapytań

141,422 odpowiedzi

319,629 komentarzy

61,956 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...