How can I get a pixel color?

Anything QL Software or Programming Related.
User avatar
dilwyn
Mr QL
Posts: 3087
Joined: Wed Dec 01, 2010 10:39 pm

Re: How can I get a pixel color?

Post by dilwyn »

Here's a little routine written in BASIC to get pixel colours. I wrote it a long time ago and am not sure if I ever properly tested it, so use with care, there are almost certainly bugs in it. Plenty of room for improvement to speed it up, etc (e.g. use integer variables if compiling, or string individual calculations together).

If nothing else, it'll make an interesting programming exercise for you debugging it and improving it. (I'm sure by introducing a deliberate little error here or there I'll have provoked people who don't normally reply to questions to "correct me" - Cunningham's Law)

They work with coordinates which cover the whole screen, not individual windows. To get pixels from a window is much more complex as you need to work out where the window starts. However, if you've defined a window in your program, you'll know where it is and just add the coordinates, e.g. WINDOW 256,128,128,64 means that to get the colour of pixel 0,0 in that window you'd add the origin, i.e. PRINT PIXEL4%(128+0,64+0).
The procedure called ScreenDetails seeks to set the screen base address (screenbase) and line length in bytes (screenllen). This allows the routines to work in high resolution screens, not just on 512x256. This works by using VER$ to check if we are running on an SBASIC system which has the SCR_BASE and SCR_LLEN functions. The use of VER$ prevents QDOS systems trying to use non-existent functions. Where this falls over is on uQLx/sQLux systems which have some high resolution modes which work under QDOS, and may have these extensions, but I don't know how to specifically test for those emulators.

There is a separate function for each mode.

PRINT PIXEL4%(x,y) prints the pixel colour at the top left of the screen in 4 colour mode.

PRINT PIXEL8%(x,y) prints the pixel colour at the top left fo the screen in 8 colour mode (note: accepts x values 0 to 511 across a mode 8 QL screen, not 0 to 255 - pixels are double width in mode 8)

PRINT PIXEL16%(x,y) gives the colour byte value at x,y pixels across and down the screen in 256 colour modes on Aurora, QPC2 and SMSQmulator. Note, this is the native colour value, no attempt to read palettes etc.

PRINT PIXEL32%(x,y) does the same for 16-bit colour modes on QPC2, Q40, Q60 etc. Note, this is the native colour value as stored in the screen RAM, no attempt at looking up the colour palettes etc. e.g. in mode 32 16-bit colour, the first 256 colours may be paletted to look like the QL mode 4/mode 8 colours.

If nothing else, these routines show how to handle the screen and an easy way to ensure your programs work in non-QL modes and high resolution screens.

Code: Select all

200 DEFine PROCedure ScreenDetails
210   REMark QDOS ROM defaults
220   screenbase = 131072 : REMark base address of screen
230   screenllen = 128    : REMark screen line width in bytes
240   IF VER$ = 'HBA' THEN
250     REMark use SBASIC functions on hires systems
260     screenbase = SCR_BASE
270     screenllen = SCR_LLEN
280   END IF
290 END DEFine ScreenDetails
300 :
310 DEFine FuNction PIXEL4%(x,y)
320   LOCal screenbase,screenllen,pixel,word,green,red,addr
330   ScreenDetails
340   pixel = 7-(x MOD 8) : REMark pixel number across colour byte
350   word  = 2*(x DIV 8)
360   addr  = screenbase+(y*screenllen)+word
370   green = 4*((PEEK(addr)&&(2^pixel))<>0)
380   red   = 2*((PEEK(addr+1)&&(2^pixel))<>0)
390   RETurn green+red+(green<>0 AND red<>0) : REMark 4+2->7 for white
400 END DEFine PIXEL4%
410 :
420 DEFine FuNction PIXEL8%(x,y)
430   LOCal screenbase,screenllen,pixel,word,green,red,blue,addr
440   ScreenDetails
450   pixel = 7-((x&&254) MOD 8) : REMark pixel number across colour byte
460   word  = 2*(x DIV 8)
470   addr  = screenbase+(y*screenllen)+word
480   green = 4*((PEEK(addr)&&(2^pixel))<>0)
490   red   = 2*((PEEK(addr+1)&&(2^pixel))<>0)
500   blue  = (PEEK(addr+1)&&(2^(pixel-1)))<>0
510   RETurn green+red+blue
520 END DEFine PIXEL8%
530 :
540 DEFine FuNction PIXEL16%(x,y)
550   ScreenDetails
560   RETurn PEEK(screenbase+(y*screenllen)+x)
570 END DEFine PIXEL16%
580 :
590 DEFine PROCedure PIXEL32%
600   ScreenDetails
610   RETurn PEEK_W(screenbase+(y*screenllen)+(2*x))
620 END DEFine PIXEL32%


User avatar
pjw
QL Wafer Drive
Posts: 1622
Joined: Fri Jul 11, 2014 8:44 am
Location: Norway
Contact:

Re: How can I get a pixel color?

Post by pjw »

Some gremlins must have snuck in, Dilwyn. I didnt check them all, but PIXEL32% didnt look right. That method should work for mode 33, though.

No, mode 32 has its internal colours rewired to little-endian, so it needs to have its bytes switched:

Code: Select all

590 DEFine FuNction PIXEL32%(x, y)
600 LOCal c$(4), c
610   ScreenDetails
620   c$ = HEX$(PEEK_W(screenbase+(y*screenllen)+(2*x)), 16)
630   c = HEX(c$(3 TO 4) & c$(1 TO 2))
640   IF c > 32767: RETurn c - 65536: ELSE : RETurn c
650 END DEFine PIXEL32%
660 :
670 DEFine FuNction PIXEL33%(x, y)
680   ScreenDetails
690   RETurn PEEK_W(screenbase+(y*screenllen)+(2*x))
700 END DEFine PIXEL33%
Perhaps there are better ways of switching bytes around? (I always use machine code for this as in PCBO% and IIMM_W% (from Knoware.no, of course!)) but the punter dint want toolkits, so..


Per
I love long walks, especially when they are taken by people who annoy me.
- Fred Allen
User avatar
dilwyn
Mr QL
Posts: 3087
Joined: Wed Dec 01, 2010 10:39 pm

Re: How can I get a pixel color?

Post by dilwyn »

Could probably take that a step further to even eliminate HEX$ in the byte swap if no-toolkits is the requirement (untested!)

byte1 = peek(addr)
byte2 = peek (addr+1)
word = 256*byte2 + byte1
IF word > 32767 then word = word-65536


User avatar
pjw
QL Wafer Drive
Posts: 1622
Joined: Fri Jul 11, 2014 8:44 am
Location: Norway
Contact:

Re: How can I get a pixel color?

Post by pjw »

dilwyn wrote: Sat Nov 05, 2022 9:09 pm Could probably take that a step further to even eliminate HEX$ in the byte swap if no-toolkits is the requirement (untested!)

byte1 = peek(addr)
byte2 = peek (addr+1)
word = 256*byte2 + byte1
IF word > 32767 then word = word-65536
Of course! Much better :)


Per
I love long walks, especially when they are taken by people who annoy me.
- Fred Allen
User avatar
pjw
QL Wafer Drive
Posts: 1622
Joined: Fri Jul 11, 2014 8:44 am
Location: Norway
Contact:

Re: How can I get a pixel color?

Post by pjw »

or while we're at it:

Code: Select all

word = peek(addr + 1) * 256 + peek(addr)
if word > 32767: ret word - 65536
ret word


Per
I love long walks, especially when they are taken by people who annoy me.
- Fred Allen
User avatar
dilwyn
Mr QL
Posts: 3087
Joined: Wed Dec 01, 2010 10:39 pm

Re: How can I get a pixel color?

Post by dilwyn »

There we are, nice and compact byte reversal!


User avatar
dilwyn
Mr QL
Posts: 3087
Joined: Wed Dec 01, 2010 10:39 pm

Re: How can I get a pixel color?

Post by dilwyn »

So, with that bit of help from Per, here's an amended PIXELx% set of functions, now with separate mode 32 and mode 33 functions to tinker with. Get debugging everyone!

Code: Select all

200 DEFine PROCedure ScreenDetails
210   REMark QDOS ROM defaults
220   screenbase = 131072 : REMark base address of screen
230   screenllen = 128    : REMark screen line width in bytes
240   IF VER$ = 'HBA' THEN
250     REMark use SBASIC functions on hires systems
260     screenbase = SCR_BASE
270     screenllen = SCR_LLEN
280   END IF
290 END DEFine ScreenDetails
300 :
310 DEFine FuNction PIXEL4%(x,y)
320   LOCal screenbase,screenllen,pixel,word,green,red,addr
330   ScreenDetails
340   pixel = 7-(x MOD 8) : REMark pixel number across colour byte
350   word  = 2*(x DIV 8)
360   addr  = screenbase+(y*screenllen)+word
370   green = 4*((PEEK(addr)&&(2^pixel))<>0)
380   red   = 2*((PEEK(addr+1)&&(2^pixel))<>0)
390   RETurn green+red+(green<>0 AND red<>0) : REMark 4+2->7 for white
400 END DEFine PIXEL4%
410 :
420 DEFine FuNction PIXEL8%(x,y)
430   LOCal screenbase,screenllen,pixel,word,green,red,blue,addr
440   ScreenDetails
450   pixel = 7-((x&&254) MOD 8) : REMark pixel number across colour byte
460   word  = 2*(x DIV 8)
470   addr  = screenbase+(y*screenllen)+word
480   green = 4*((PEEK(addr)&&(2^pixel))<>0)
490   red   = 2*((PEEK(addr+1)&&(2^pixel))<>0)
500   blue  = (PEEK(addr+1)&&(2^(pixel-1)))<>0
510   RETurn green+red+blue
520 END DEFine PIXEL8%
530 :
540 DEFine FuNction PIXEL16%(x,y)
550   ScreenDetails
560   RETurn PEEK(screenbase+(y*screenllen)+x)
570 END DEFine PIXEL16%
580 :
590 DEFine FuNction PIXEL32%(x,y)
600   LOCal addr,word
610   ScreenDetails
620   addr = screenbase+(y*screenllen)+(2*x)
630   word = PEEK(addr+1)*256 + PEEK(addr) : REMark byte reverse colour words on mode 32 systems
640   IF word > 32767 THEN word = word-65536
650   RETurn word
660 END DEFine PIXEL32%
670 :
680 DEFine FuNction PIXEL33%
690   ScreenDetails
700   RETurn PEEK_W(screenbase+(y*screenllen)+(2*x))
710 END DEFine PIXEL33%


User avatar
pjw
QL Wafer Drive
Posts: 1622
Joined: Fri Jul 11, 2014 8:44 am
Location: Norway
Contact:

Re: How can I get a pixel color?

Post by pjw »

Aaargh! I missed this one:

680 DEFine PROCedure PIXEL33%

should be

680 DEFine FuNction PIXEL33%


Per
I love long walks, especially when they are taken by people who annoy me.
- Fred Allen
User avatar
dilwyn
Mr QL
Posts: 3087
Joined: Wed Dec 01, 2010 10:39 pm

Re: How can I get a pixel color?

Post by dilwyn »

Thanks, not having a mode 33 system I didn't even test this one. Amended the code listing above.

How the hell can such a small listing generate so many issues :oops: :D


User avatar
BSJR
Trump Card
Posts: 222
Joined: Sun Oct 18, 2015 12:53 pm
Location: Amsterdam
Contact:

Re: How can I get a pixel color?

Post by BSJR »

pjw wrote: Sat Nov 05, 2022 7:26 pm Some gremlins must have snuck in, Dilwyn. I didnt check them all, but PIXEL32% didnt look right. That method should work for mode 33, though.

No, mode 32 has its internal colours rewired to little-endian, so it needs to have its bytes switched:
This is not entirely true.
When in mode 32: COLOUR_NATIVE: PAPER $1F: CLS: gives bleu, :PAPER $F800: then gives red as in rrrrrggg.gggbbbbb.
Only in PIC and SPR files are the bytes reversed as in a 16-bit BMP.

BSJR


Post Reply