It uses good old StretchDIBits() for the color translation and drawing.
I originally wrote this as exercise, maybe its useful for someone
Note: In PB Import "mpix256.obj" or Import "mpix256.lib"
Updated 25.01.2023
Updated 14.08.2023 - bug fix (drawing with the c backend)
Commented source (fasm v.1.73.30):
Alpha 2:
Code: Select all
;mpix256 - 256 colors @ 256 x 256 pixels
;basic example on 8 bit - 256 color palette gfx
;author: mijikai
;version alpha 02
;changes/improvements:
;- fixed a bug in mpixDevice() & mpixBuffer() / hwnd & buffersize was not returned
;- removed the need to link to msvcrt / LocalAlloc() LocalFree() are perfectly fine and cooler :)
;- mpixPixelDraw() now uses a struct instead of several variables
;- replaced the locals with the local macro (no need to zero the variable if it is not needed)
;- the frame macro is now used to improve the stack reservation
format MS64 COFF;<- 64 bit!
include 'win64w.inc';<- use the win x64 wchar macros provided by fasm
extrn GetDC;<- all the win api functions used
extrn ReleaseDC
extrn StretchDIBits
extrn LocalAlloc
extrn LocalFree
public mpixCreate;<- all the exported functions
public mpixDevice
public mpixBuffer
public mpixPalette
public mpixPaletteSet
public mpixPaletteGet
public mpixPixelClear
public mpixPixelSet
public mpixPixelGet
public mpixPixelDraw
public mpixRelease
public mpixVersion
section '.code' code readable executable;<- where code lives
proc mpixCreate;(hwnd)
local .hwnd :QWORD
frame ;<- reserve space on the stack once
mov [.hwnd],rcx ;<- store the supplied window handle into [hwnd]
fastcall mpixRelease ;<- reset and release the old device!
fastcall LocalAlloc,LPTR,0x10000 ;<- allocate the pixel buffer (256 x 256)
test rax,rax ;<- was the buffer allocated?
jz mpixCreate_error ;<- if there was an error return
mov [surface],rax ;<- store the pointer to the pixel buffer into [surface]
fastcall GetDC,qword[.hwnd] ;<- get a device handle from the window (note: better check the device capabilities!)
test rax,rax ;<- check if the device handle was obtained
jz mpixCreate_error ;<- return on error
mov rcx,[.hwnd] ;<- load hwnd from [hwnd]
mov [window],rcx ;<- store the window handle into [window]
mov [device],rax ;<- store the device handle into [device]
xor rcx,rcx ;<- set the clear color $0 = black
fastcall mpixPixelClear,rcx ;<- clear all pixels
mov rax,0x1 ;<- mov 1 = true into the return register
jmp mpixCreate_return ;
mpixCreate_error: ;<- something went wrong continue execution here
fastcall mpixRelease ;<- clean up if anything went wrong
mpixCreate_return: ;
endf ;
ret ;<- return
endp
proc mpixDevice;(*Hwnd)
test rcx,rcx ;<- if the first parameter is set (pointer to a variable) return the window handle
jz @f ;<- no pointer just return the device handle
mov rax,[window] ;<- grab the window handle
mov [rcx],rax ;<- write the windo handle into the variable pointed to by *Hwnd
@@: ;<- only return the device handle
mov rax,[device] ;<- mov the device handle into the return register
ret ;<- return
endp
proc mpixBuffer;(*Size)
test rcx,rcx ;<- if the first parameter is set (pointer to a variable) return the pixel buffer size
jz @f ;<- no parameter just return a pointer to the pixel buffer
mov qword[rcx],0x10000 ;<- write the buffer size into the variable pointed to by *Size (hardcoded - it was just a exercise...)
@@: ;<- only return a pointer to the pixel buffer
mov rax,[surface] ;<- move the buffer handle into the return register
ret ;<- return
endp
proc mpixPalette;(*Palette)
test rcx,rcx ;<- if *Palette is true load it (copy it)
jz @f ;<- if *Palette is false use the default palette (reset to default)
mov rsi,rcx ;<- pointer to the palette to be loaded
mov rdi,palette ;<- pointer to the palette
mov rcx,0x100 ;<- how many colors (256)
rep movsd ;<- copy all colors
xor rax,rax ;<- clear the return register
ret ;<- return
@@: ;<- create the default palette
mov rcx,0xFF ;<- how many colors (0 - 255)
mov rax,0x400 ;<- palette size (1024)
lea rdx,qword[palette] ;<- load the pointer to the palette
@@: ;<- color creation loop
sub rax,0x4 ;<- advance to the next palette color
mov dword[rdx + rax],0x0 ;<- clear the palette color
mov byte[rdx + rax + 1],cl ;<- make a green color
dec cl ;<- next green color
test rax,rax ;<- are all color there
jnz @b ;<- repeat until the whole palette is filled
ret ;<- return
endp
proc mpixPaletteSet;(Index.a,Color.a)
lea rax,[palette] ;<- get a pointer to the palette
lea rax,[rax + rcx * 0x4] ;<- where is the color
mov dword[rax],edx ;<- set the color
xor rax,rax ;<- clear the return register
ret ;<- return
endp
proc mpixPaletteGet;(Index.a,*Color.a)
lea rax,[palette] ;<- get a pointer to the palette
lea rax,[rax + rcx * 0x4] ;<- where is the color
mov ecx,dword[rax] ;<- read the color
mov dword[rdx],ecx ;<- store the color into the variable pointed to by *Color
xor rax,rax ;<- clear the return register
ret ;<- return
endp
proc mpixPixelClear;(Index.a)
mov rdi,qword[surface] ;<- get a pointer to the pixel buffer
mov rax,rcx ;<- clear with color index
mov rcx,0x10000 ;<- how many bytes
repe stosb ;<- clear the buffer
xor rax,rax ;<- clear the return register
ret ;<- return
endp
proc mpixPixelSet;(X.i,Y.i,Index.a)
cmp rcx,0x0 ;<- check if the pixel is inside the screen
jl @f ;
cmp rdx,0x0 ;
jl @f ;
cmp rcx,0xFF ;
jg @f ;
cmp rdx,0xFF ;
jg @f ;
shl rdx,0x8 ;<- calculate the y offset (y * span)
mov rax,[surface] ;<- get a pointer to the pixel buffer
lea rcx,[rcx + rdx] ;<- calculate the x + y offset
mov byte[rax + rcx],r8b ;<- write the color index into the pixel buffer
mov rax,0x1 ;<- return 1 if all went well
ret ;
@@: ;
xor rax,rax ;<- return 0 if something went wrong
ret ;<- return
endp
proc mpixPixelGet;(X.i,Y.i,*Index.Ascii)
cmp rcx,0x0 ;<- check if the pixel is inside the screen
jl @f ;
cmp rdx,0x0 ;
jl @f ;
cmp rcx,0xFF ;
jg @f ;
cmp rdx,0xFF ;
jg @f ;
shl rdx,0x8 ;<- calculate the y offset (y * span)
mov rax,[surface] ;<- get a pointer to the pixel buffer
lea rcx,[rcx + rdx] ;<- calculate the x + y offset
mov dl,byte[rax + rcx] ;<- read the color index from the pixel buffer
mov byte[r8],dl ;<- return the color index to the variable pointed to by *Index
mov rax,0x1 ;<- return 1 if all went well
ret ;
@@: ;
xor rax,rax ;<- return 0 if something went wrong
ret ;<- return
endp
proc mpixPixelDraw;(X.i,X.i,W.i,H.i)
local .rect :RECT ;<- rect to draw to
mov dword[.rect.left],ecx
mov dword[.rect.top],edx
mov dword[.rect.right],r8d
mov dword[.rect.bottom],r9d
fastcall StretchDIBits,qword[device],[.rect.left],[.rect.top],[.rect.right],[.rect.bottom],0x0,0x0,0x100,0x100,qword[surface],addr layout,0x0,0xCC0020
ret
endp
proc mpixRelease;()
frame
cmp qword[surface],0x0
je mpixRelease_surface
fastcall LocalFree,qword[surface] ;<- clean up the surface (pixel buffer)
mpixRelease_surface:
cmp qword[device],0x0
je mpixRelease_device
fastcall ReleaseDC,qword[window],qword[device] ;<- clean up the device handle
mpixRelease_device:
mov qword[surface],0x0 ;<- set everthing to zero
mov qword[device],0x0
mov qword[window],0x0
xor rcx,rcx
fastcall mpixPalette,rcx ;<- clear the palette
xor rax,rax
endf
ret
endp
proc mpixVersion;()
mov eax,0x000002;<- return a hardcoded version number
ret
endp
section '.data' data readable writeable;<- where data lives
window:
dq 0x00;<- stores the window handle
device:
dq 0x00;<- stores the device handle
layout:
db 0x28,0x00,0x00,0x00,0x00,0x01,0x00,0x00;<- stores the pixel layout (note: a bitmap info header)
db 0x00,0xFF,0xFF,0xFF,0x01,0x00,0x08,0x00;<- black magic (note: not really check out StretchDIBits on msdn) :>
db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00;<- takes care of the 8 -> 32 bit color conversion!
db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
db 0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00
palette:
rb 0x400;<- palette buffer
surface:
dq 0x00;<- stores a pointer to the pixel buffer
Code: Select all
;mpix256 - 256 colors @ 256 x 256 pixels
;basic example on 8 bit - 256 color palette gfx
;author: mijikai
format MS64 COFF;<- 64 bit!
include 'win64w.inc';<- use the win x64 wchar macros provided by fasm
extrn GetDC;<- all the win api functions used
extrn ReleaseDC
extrn StretchDIBits
extrn malloc
extrn free
public mpixCreate;<- all the exported functions
public mpixDevice
public mpixBuffer
public mpixPalette
public mpixPaletteSet
public mpixPaletteGet
public mpixPixelClear
public mpixPixelSet
public mpixPixelGet
public mpixPixelDraw
public mpixRelease
public mpixVersion
section '.code' code readable executable;<- where code lives
proc mpixCreate;(hwnd)
locals
hwnd dq 0x0;<- local variable
endl
mov qword[hwnd],rcx;<- store the supplied window handle into [hwnd]
fastcall mpixRelease;<- reset and release the old device!
mov rcx,0x10000;<- pixel buffer size (256 x 256)
fastcall malloc,rcx;<- allocate the pixel buffer
test rax,rax;<- was the buffer allocated?
jz @f;<- if there was an error return
mov qword[surface],rax;<- store the pointer to the pixel buffer into [surface]
fastcall GetDC,qword[hwnd];<- get a device handle from the window (note: better check the device capabilities!)
test rax,rax;<- check if the device handle was obtained
jz @f;<- return on error
mov rcx,qword[hwnd];<- load hwnd from [hwnd]
mov qword[window],rcx;<- store the window handle into [window]
mov qword[device],rax;<- store the device handle into [device]
xor rcx,rcx;<- set the clear color $0 = black
fastcall mpixPixelClear,rcx;<- clear all pixels
mov rax,0x1;<- mov 1 = true into the return register
ret;<- return
@@:;<- something went wrong continue execution here
fastcall mpixRelease;<- clean up if anything went wrong
ret;<- return
endp
proc mpixDevice;(*Hwnd)
cmp rcx,rcx;<- if the first parameter is set (pointer to a variable) return the window handle
jz @f;<- no pointer just return the device handle
mov rax,[window];<- grab the window handle
mov [rcx],rax;<- write the windo handle into the variable pointed to by *Hwnd
@@:;<- only return the device handle
mov rax,qword[device];<- mov the device handle into the return register
ret;<- return
endp
proc mpixBuffer;(*Size)
cmp rcx,rcx;<- if the first parameter is set (pointer to a variable) return the pixel buffer size
jz @f;<- no parameter just return a pointer to the pixel buffer
mov qword[rcx],0x10000;<- write the buffer size into the variable pointed to by *Size (hardcoded - it was just a exercise...)
@@:;<- only return a pointer to the pixel buffer
mov rax,qword[surface];<- move the buffer handle into the return register
ret;<- return
endp
proc mpixPalette;(*Palette)
test rcx,rcx;<- if *Palette is true load it (copy it)
jz @f;<- if *Palette is false use the default palette (reset to default)
mov rsi,rcx;<- pointer to the palette to be loaded
mov rdi,palette;<- pointer to the palette
mov rcx,0x100;<- how many colors (256)
rep movsd;<- copy all colors
xor rax,rax;<- clear the return register
ret;<- return
@@:;<- create the default palette
mov rcx,0xFF;<- how many colors (0 - 255)
mov rax,0x400;<- palette size (1024)
lea rdx,qword[palette];<- load the pointer to the palette
@@:;<- color creation loop
sub rax,0x4;<- advance to the next palette color
mov dword[rdx + rax],0x0;<- clear the palette color
mov byte[rdx + rax + 1],cl;<- make a green color
dec cl;<- next green color
test rax,rax;<- are all color there
jnz @b;<- repeat until the whole palette is filled
ret;<- return
endp
proc mpixPaletteSet;(Index.a,Color.a)
lea rax,[palette];<- get a pointer to the palette
lea rax,[rax + rcx * 0x4];<- where is the color
mov dword[rax],edx;<- set the color
xor rax,rax;<- clear the return register
ret;<- return
endp
proc mpixPaletteGet;(Index.a,*Color.a)
lea rax,[palette];<- get a pointer to the palette
lea rax,[rax + rcx * 0x4];<- where is the color
mov ecx,dword[rax];<- read the color
mov dword[rdx],ecx;<- store the color into the variable pointed to by *Color
xor rax,rax;<- clear the return register
ret;<- return
endp
proc mpixPixelClear;(Index.a)
mov rdi,qword[surface];<- get a pointer to the pixel buffer
mov rax,rcx;<- clear with color index
mov rcx,0x10000;<- how many bytes
repe stosb;<- clear the buffer
xor rax,rax;<- clear the return register
ret;<- return
endp
proc mpixPixelSet;(X.i,Y.i,Index.a)
cmp rcx,0x0;<- check if the pixel is inside the screen
jl @f
cmp rdx,0x0
jl @f
cmp rcx,0xFF
jg @f
cmp rdx,0xFF
jg @f
shl rdx,0x8;<- calculate the y offset (y * span)
mov rax,qword[surface];<- get a pointer to the pixel buffer
lea rcx,[rcx + rdx];<- calculate the x + y offset
mov byte[rax + rcx],r8b;<- write the color index into the pixel buffer
mov rax,0x1;<- return 1 if all went well
ret
@@:
xor rax,rax;<- return 0 if something went wrong
ret
endp
proc mpixPixelGet;(X.i,Y.i,*Index.Ascii)
cmp rcx,0x0;<- check if the pixel is inside the screen
jl @f
cmp rdx,0x0
jl @f
cmp rcx,0xFF
jg @f
cmp rdx,0xFF
jg @f
shl rdx,0x8;<- calculate the y offset (y * span)
mov rax,qword[surface];<- get a pointer to the pixel buffer
lea rcx,[rcx + rdx];<- calculate the x + y offset
mov dl,byte[rax + rcx];<- read the color index from the pixel buffer
mov byte[r8],dl;<- return the color index to the variable pointed to by *Index
mov rax,0x1;<- return 1 if all went well
ret
@@:
xor rax,rax;<- return 0 if something went wrong
ret
endp
proc mpixPixelDraw;(X.i,X.i,W.i,H.i)
locals
x dq 0x0;<- the function draws the pixel buffer (the palette is used to translate the colors)
y dq 0x0;(note: check out msdn for details)
w dq 0x0
h dq 0x0
endl
mov qword[x],rcx
mov qword[y],rdx
mov qword[w],r8
mov qword[h],r9
fastcall StretchDIBits,qword[device],qword[x],qword[y],qword[w],qword[h],0x0,0x0,0x100,0x100,qword[surface],addr layout,0x0,0xCC0020
ret
endp
proc mpixRelease;()
cmp qword[surface],0x0
je @f
fastcall free,qword[surface];<- clean up the surface (pixel buffer)
@@:
cmp qword[device],0x0
je @f
fastcall ReleaseDC,qword[window],qword[device];<- clean up the device handle
@@:
mov qword[surface],0x0;<- set everthing to zero
mov qword[device],0x0
mov qword[window],0x0
xor rcx,rcx
fastcall mpixPalette,rcx;<- clear the palette
xor rax,rax
ret
endp
proc mpixVersion;()
mov eax,0x000001;<- return a hardcoded version number
ret
endp
section '.data' data readable writeable;<- where data lives
window:
dq 0x00;<- stores the window handle
device:
dq 0x00;<- stores the device handle
layout:
db 0x28,0x00,0x00,0x00,0x00,0x01,0x00,0x00;<- stores the pixel layout (note: a bitmap info header)
db 0x00,0xFF,0xFF,0xFF,0x01,0x00,0x08,0x00;<- black magic (note: not really check out StretchDIBits on msdn) :>
db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00;<- takes care of the 8 -> 32 bit color conversion!
db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
db 0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00
palette:
rb 0x400;<- palette buffer
surface:
dq 0x00;<- stores a pointer to the pixel buffer
Code: Select all
EnableExplicit
Import "mpix256.lib"
mpixCreate.i(WindowHandle.i = #Null)
mpixDevice.i(*WindowHandle.Integer = #Null)
mpixBuffer.i(*BufferSize.Integer = #Null)
mpixPalette.i(*Palette = #Null)
mpixPaletteSet.i(Index.a,Color.l)
mpixPaletteGet.i(Index.a,*Color.Long)
mpixPixelClear.i(Index.a)
mpixPixelSet.i(X.i,Y.i,Index.a)
mpixPixelGet.i(X.i,Y.i,*Index.Ascii)
mpixPixelDraw.i(X.i,Y.i,Width.i,Height.i)
mpixRelease.i()
mpixVersion.i()
EndImport
#MPIX_VERSION = $000001
Procedure.i Main()
Protected exit.i
If OpenWindow(0,#Null,#Null,640,400,#Null$,#PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_MinimizeGadget)
If mpixCreate(WindowID(0))
mpixPaletteSet(250,$FF00FF)
Repeat
Repeat
Select WindowEvent()
Case #Null
Break
Case #PB_Event_CloseWindow
exit = #True
EndSelect
ForEver
mpixPixelClear(255)
mpixPixelSet(100,100,250)
mpixPixelDraw(0,0,640,400)
Until exit
mpixRelease()
EndIf
CloseWindow(0)
EndIf
ProcedureReturn #Null
EndProcedure
Main()
End