DECLARE LIBRARY "QB64GuiTools\dev_framework\GuiAppFrame" 'Do not add .h here !! 'This is a replacement for the _RGB function. It works for upto 8-bit
'(256 colors) images only and needs a valid image. It can limit the
'number of pens to search and uses a better color matching algorithm.
ws
= _NEWIMAGE(640, 480, 32) 'A 32 bit screents
= _NEWIMAGE(640, 480, 256) 'A 256 bit screen to provide the default QB64 palette FOR y
= 0 TO 479 STEP 48 '10 steps = 100 total boxes LINE (x
, y
)-STEP(64, 48), &HFF000000 + RND * &HFFFFFF, BF
SLEEP 'pause to view original
nh = RemapImageFS&(ws, ts) 'Remap from the 32-bit to the 256 color screen
SCREEN nh
'show the remapped 256 color screen SLEEP 'pause so we can see it
nh2 = Image32To256(ws, nh) 'Remap from the 32-bit to the 256 color screen, using the palette chosen in RemapImageFS
SCREEN nh2
'show the remapped 256 color screen SLEEP 'pause so we can see it
'---------------------------------------------------------------------
'Function: Will remap the colors of any given image to use the palette
' of the given 256 colors (8-bit) destination screen, so that it
' can be displayed on that screen using _PUTIMAGE with as less
' than possible quality loss. For best results this function does
' also apply a Floyd-Steinberg error diffusion matrix on the
' given image to further improve its display quality.
' The original image is not changed, instead the new remapped
' image is returned in a new handle, also _SOURCE & _DEST handles
' are not changed by this function.
' The algorithm is optimized for speed to the fullest extent
' possible on the QB64 language level.
'
'Synopsis: rhan& = RemapImageFS& (ohan&, dhan&)
'
'Result: rhan& --> the handle of the new remapped image, check it for
' validity before use (rhan& < -1, as for any other
' image handles in QB64)
'
'Inputs: ohan& --> the handle of the original source image to remap,
' may be an image of any color depth (1-32 bits)
' dhan& --> the handle of the 8-bit destination image, usually
' the 256 colors screen the image shall be displayed
' on (for the GuiTools Framework this is "appScreen&"),
' note that the destinations color palette must already
' be setup prior calling this function
'
'Notes: For the use of this function a globally shared array must be
' DIMed in an appropriate place: REDIM SHARED fsNearCol%(&HFFFFFF).
' For the GuiTools Framework this is already done within its init
' procedure in file GuiAppFrame.bi.
'---------------------------------------------------------------------
RemapImageFS& = -1 'so far return invalid handle
shan& = ohan& 'avoid side effect on given argument
'--- check/adjust source image & get new 8-bit image ---
shan& = than&
than& = -1 'avoid freeing below
'--- Floyd-Steinberg error distribution arrays ---
rhan&
= _NEWIMAGE(swid%
, 2, 32) 'these are missused as LONG arrays, ghan&
= _NEWIMAGE(swid%
, 2, 32) 'with CHECKING:OFF this is much faster bhan&
= _NEWIMAGE(swid%
, 2, 32) 'than real QB64 arrays '--- curr/next row offsets (for distribution array access) ---
cro% = 0: nro% = swid% * 4 'will be swapped after each pixel row
'--- the matrix values are extended by 16384 to avoid slow floating ---
'--- point ops and to allow for integer storage in the above arrays ---
'--- also it's a power of 2, which may be optimized into a bitshift ---
seven% = (7 / 16) * 16384 'X+1,Y+0 error fraction
three% = (3 / 16) * 16384 'X-1,Y+1 error fraction
five% = (5 / 16) * 16384 'X+0,Y+1 error fraction
one% = (1 / 16) * 16384 'X+1,Y+1 error fraction
'--- if all is good, then start remapping ---
'--- for speed we do direct memory access ---
'--- iterate through pixels ---
'--- curr/prev/next pixel offsets ---
cpo% = x% * 4: ppo% = cpo% - 4: npo% = cpo% + 4
'--- get pixel ARGB value from source ---
'--- add distributed error, shrink by 16384, clear error ---
'--- current pixel X+0, Y+0 (= cro% (current row offset)) ---
poff% = cro% + cpo% 'pre-calc full pixel offset
sr%
= ((srgb~&
AND &HFF0000~&
) \
65536) + (_MEMGET(rbuf
, roff%&
+ poff%
, LONG) \
16384) 'red sg%
= ((srgb~&
AND &HFF00~&
) \
256) + (_MEMGET(gbuf
, goff%&
+ poff%
, LONG) \
16384) 'green sb%
= (srgb~&
AND &HFF~&
) + (_MEMGET(bbuf
, boff%&
+ poff%
, LONG) \
16384) 'blue _MEMPUT rbuf
, roff%&
+ poff%
, 0 AS LONG 'clearing each single pixel error using _MEMPUT _MEMPUT gbuf
, goff%&
+ poff%
, 0 AS LONG 'turns out even faster than clearing the entire _MEMPUT bbuf
, boff%&
+ poff%
, 0 AS LONG 'pixel row using _MEMFILL at the end of the loop '--- find nearest color ---
crgb~&
= _RGBA32(sr%
, sg%
, sb%
, 0) 'used for fast value clipping + channel merge IF fsNearCol%
(crgb~&
) > 0 THEN npen% = fsNearCol%(crgb~&) - 1 'already known
npen% = FindColor&(sr%, sg%, sb%, nhan&, 24, 255 - guiReservedPens%) 'not known, find one
fsNearCol%(crgb~&) = npen% + 1 'save for later use
'--- put colormapped pixel to dest ---
'------------------------------------------
'--- Floyd-Steinberg error distribution ---
'------------------------------------------
'--- You may comment this block out, to see the
'--- result without applied FS matrix.
'-----
'--- get dest palette RGB value, calc error to clipped source ---
er%
= ((crgb~&
AND &HFF0000~&
) - (nrgb~&
AND &HFF0000~&
)) \
65536 eg%
= ((crgb~&
AND &HFF00~&
) - (nrgb~&
AND &HFF00~&
)) \
256 eb%
= (crgb~&
AND &HFF~&
) - (nrgb~&
AND &HFF~&
) '--- distribute error according to FS matrix ---
'--- X-1, Y+1 (= nro% (next row offset)) ---
poff% = nro% + ppo% 'pre-calc full pixel offset
'--- X+0, Y+1 (= nro% (next row offset)) ---
poff% = nro% + cpo% 'pre-calc full pixel offset
'--- X+1, Y+0 (= cro% (current row offset)) ---
poff% = cro% + npo% 'pre-calc full pixel offset
'--- X+1, Y+1 (= nro% (next row offset)) ---
poff% = nro% + npo% 'pre-calc full pixel offset
'------------------------------------------
'--- End of FS ----------------------------
'------------------------------------------
noff%& = noff%& + 1 'next dest pixel
soff%& = soff%& + 4 'next source pixel
tmp% = cro%: cro% = nro%: nro% = tmp% 'exchange distribution array row offsets
'--- memory cleanup ---
'--- set result ---
RemapImageFS& = nhan&
nhan& = -1 'avoid freeing below
'--- remapping done or error, cleanup remains ---
_MEMGET m
(0), m
(0).OFFSET
+ o
+ 3, a
_MEMGET m
(0), m
(0).OFFSET
+ o
+ 2, r
_MEMGET m
(0), m
(0).OFFSET
+ o
+ 1, g
_MEMGET m
(0), m
(0).OFFSET
+ o
+ 0, b
o = o + 4