Author Topic: Need a cheaper way to do clearcolor  (Read 6622 times)

0 Members and 1 Guest are viewing this topic.

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #15 on: June 26, 2021, 06:35:24 pm »
@Galleon

The C++ code is very interesting.  What does *lp&0xFFFFFF do?

&0xFFFFFF = 16,777,215 decimal.  Is this the size of a memory block? and why is it fixed size of &0xFFFFFF?

Is variable c a color value?.

 I have read that CLEARCOLOR and PUTIMAGE work together. Do they use the same &0xFFFFFF block of memory?

Oh . . . .could lp&0xFFFFFF be a variable name?, assembler directive?

I know lp stands for long pointer and * is some type of memory operator. I have no clue what the C++ code is doing.

Quote
FYI - This is the core loop of _CLEARCOLOR in C++:
------------------------------------------
for (lp=im->offset32;lp<last;lp++){
     if ((*lp&0xFFFFFF)==c) *lp=c;
}
------------------------------------------
« Last Edit: June 26, 2021, 07:13:24 pm by NOVARSEG »

Offline johannhowitzer

  • Forum Regular
  • Posts: 118
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #16 on: June 26, 2021, 09:01:46 pm »
Quote
I hear that, I usually start with 2 programs on a project, the MAIN code source and my DATA program that outputs all the data into an external file, I rarely(but sometimes do) attempt to start putting data into the MAIN code cause it quickly becomes messy, It can be a little tricky keeping both in sync but make does it make the main code cleaner.

Main game code currently contains dialogue data, the demo stage's scripting, the keybinding raw data, movement scripting, sprite references, and all the ships' and shots' preset information for spawning.

Already using your MFI for the images and sounds, I was the guy who asked about that a couple weeks ago - https://www.qb64.org/forum/index.php?topic=3968.0  Much appreciated!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Need a cheaper way to do clearcolor
« Reply #17 on: June 26, 2021, 11:34:59 pm »
@Galleon

The C++ code is very interesting.  What does *lp&0xFFFFFF do?

&0xFFFFFF = 16,777,215 decimal.  Is this the size of a memory block? and why is it fixed size of &0xFFFFFF?

Is variable c a color value?.

 I have read that CLEARCOLOR and PUTIMAGE work together. Do they use the same &0xFFFFFF block of memory?

Oh . . . .could lp&0xFFFFFF be a variable name?, assembler directive?

I know lp stands for long pointer and * is some type of memory operator. I have no clue what the C++ code is doing.

*lp is basically the _OFFSET of variable lp.
&0xFFFFFF is basically the same as AND &HFFFFFF.

So what you have is a routine that looks at where your image is in memory, goes through it quickly pixel by pixel, and if the _RGB value matches what you specified, it then changes that pixel.

The only way you could possibly do what it’s doing any faster, is to do exactly what it’s doing, except in a limited scope.  (Say from x/y point 100,100 to x/y point 200,200, rather than a whole 1280x720 image.)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #18 on: June 27, 2021, 01:25:50 am »
@SMcNeill

 So 0xFFFFFF is the RGB color but why is  AND logic used?

As far as fast goes there is an IF statement in the C++ CLEARCOLOR code which checks each pixel in the image.  If a match is found the alpha is changed to 0.  It is still fast but if the code did not have an IF (whatever) then it would go faster.  That is why I think MEM would be faster.

Each sprite could be saved in a data format that stores pixel locations. I was thinking of using READ statements in the BAS file to actually animate or apply effects to a sprite - avoiding IF statements.  Look up table oriented = fast.

A few IFs here and there might not make a difference.  Processing unnecessary pixels is where code might be slower.


« Last Edit: June 27, 2021, 01:58:11 am by NOVARSEG »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Need a cheaper way to do clearcolor
« Reply #19 on: June 27, 2021, 02:34:34 am »
The AND is there to swap all instances of alpha to the clear color.

RGB(any alpha)
So clear color _RGB(255,255,255) will clear color _RGBA(255,255,255,0) to _RGBA(255,255,255,255).

How would you code it so that it works without an IF statement with _MEM??  You still have to compare to see if each pixel is a match to the one which you want to replace, or not.  It's not like you're simply replacing EVERY pixel on the screen -- just the ones which match the RGB value you specified.

for (lp=im->offset32;lp<last;lp++){
     if ((*lp&0xFFFFFF)==c) *lp=c;
}

The above code would basically translate to basic as:

DIM lp AS _MEM: lp = _MEMIMAGE(0)
DIM c AS _UNSIGNED LONG: c = _RGB32(r, g,b) 'whatever we want
DO UNTIL counter > lp.SIZE
    IF _MEMGET(lp, lp.OFFSET + counter, _UNSIGNED LONG) AND &HFFFFFF = c THEN
       _MEMPUT lp, lp.OFFSET + counter, c 'Notice we put the value WITHOUT any alpha present?
    END IF
    counter = counter +4
LOOP
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #20 on: June 27, 2021, 02:24:15 pm »
@SMcNeill

4 byte unsigned long is ANDed with 0xFFFFFF.  I thought 0xFFFFFF was a color value but looks to be a 3 byte mask for the RGB color values.

So the conditional statement
  if ((*lp&0xFFFFFF)==c) 
 is comparing the alpha value of the image pixel to the alpha value of the the RGB specified  in
clear color _RGB(255,255,255)   where I think the default alpha value is 255?

If there is a match then the new alpha value is still 255 but I thought a transparent pixel was alpha = 0 .

This line of code does not seem to set alpha to zero?
 if ((*lp&0xFFFFFF)==c) *lp=c;

Another thing if say

clear color _RGB(100,100,200)
then how does   if ((*lp&0xFFFFFF)==c) work with that. The0xFFFFFF is masking out the RGB color values anyway so it looks like there is some code missing or Im just not getting this.

So I'm still confused here.

« Last Edit: June 27, 2021, 02:35:51 pm by NOVARSEG »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #21 on: June 27, 2021, 06:13:53 pm »
double post
« Last Edit: June 27, 2021, 06:15:02 pm by NOVARSEG »

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #22 on: June 27, 2021, 06:14:24 pm »
I got it backwards

0xFFFFFF masks out the alpha byte but does not mask the RGB bytes.

but how does

if ((*lp&0xFFFFFF)==c) *lp=c;

set the alpha byte to zero?

Quote
DIM c AS _UNSIGNED LONG: c = _RGB32(r, g,b)

Looks like I'm answering my own questions?
« Last Edit: June 27, 2021, 06:21:08 pm by NOVARSEG »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Need a cheaper way to do clearcolor
« Reply #23 on: June 27, 2021, 06:48:45 pm »
What we’re doing is taking a 4 byte unsigned long, and using it for a 32-bit color value.

ARGB — One byte for Alpha, Red, Green, Blue.

Now in hex, those 4 bytes are represented by 8 hex-characters, from 00 to FF (0 to 255)

_RGB(r,g,b) gives us a 3-byte value for color, without an alpha channel.  Since there’s no such thing as a 3-byte long, the value is actually 0RGB — One byte for zero alpha, then a byte for Red, Green, Blue.

Now, when you take a 4-byte ARGB value AND it with a 0RGB value, the result will always be a 0RGB value.  (You stripped out the alpha with the 0 you ANDed it against.).  IF the result is the same 0RGB value that you ANDed against, then you have a perfect color match (minus alpha, of course).  This means that anything from 0 alpha to 255 alpha would be a match, as long as the RGB values match.

IF they do match, we simply set that pixel’s value to the 0RGB value (without any alpha value), and then we move on to the next pixel.

And that’s clearcolor in a nutshell.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #24 on: June 27, 2021, 06:56:53 pm »
@SMcNeill

Without your explanation I would have never figure that out. Thar 0xFFFFFF really confused me for a while.

Here is another thing.   Clear color changes an old alpha value to a new one.  Now  not sure, but I think clear color can return the  old value and if so does that mean clear color might be keeping copies of the alpha channel?

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Need a cheaper way to do clearcolor
« Reply #25 on: June 27, 2021, 07:06:25 pm »
What you have to remember is that that FFFFFF value is actually a 00FFFFFF value.

And with AND

    AARRGGBB (alpha, red, green, blue)
AND 00FFFFFF
    ========
    00AARRGG


That 0 strips out the alpha, while the FF leaves the red, green, blue values intact.



As for restoring those lost alpha values?  That’s impossible, as far as I know.  There’s no hidden second page in memory, storing those values for us.  We just strip off the alpha channel and then we’re done with it.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline NOVARSEG

  • Forum Resident
  • Posts: 509
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #26 on: June 28, 2021, 01:26:43 am »
The thing is why did johannhowitzer notice a speed increase when clearcolor was commented out?

Petr said that
Quote
Used POINT function after CLEARCOLOR is the most speed problem

This might be true but POINT should be fast as well.   The C++ code posted by Galleon showed that clearcolor is very fast code.

The C++ code for POINT should be even faster because POINT returns a pixel value at a certain location but CLEARCOLOR must look at every pixel in an image.




Offline Galleon

  • QB64 Developer
  • Newbie
  • Posts: 25
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #27 on: June 28, 2021, 06:34:39 am »
Quote
The thing is why did johannhowitzer notice a speed increase when clearcolor was commented out?
Exactly. As the OP is not interested in pursuing this neither am I. But I'm inclined to believe that _CLEARCOLOR may have been the proverbial "straw that broke the camel's back" in this case. Also for in-game usage I assume some sort of temporary copy of the original was made, then the operations were performed (adding coloured shading then using _CLEARCOLOR), then the temporary copy was rendered to the screen and probably that temporary image was disposed of at some point (or re-used?). I suspect that entire process is responsible somehow for the slowness. Also given the example of pixel-art provided I wonder if the re-colouring could not have been done before the up-scaling.

Here's an alternative I did code just for fun but didn't share because I'm very sure it provides no speed benefit over _CLEARCOLOR...
Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(1280, 768, 32)
  2. ship = _LOADIMAGE("ship.png")
  3.  
  4.  
  5. shipmask = getMask(ship)
  6. tempShip = _COPYIMAGE(ship)
  7.  
  8.  
  9. x = 0
  10. t = TIMER - 0.01
  11.     CLS , _RGB(0, 0, 128)
  12.     f = f + 1
  13.     PRINT CLNG(f / (TIMER - t))
  14.     FOR y = 10 TO 700 STEP 20
  15.         FOR z = 10 TO 1000 STEP 40
  16.             _DEST tempShip
  17.             _DONTBLEND
  18.             _PUTIMAGE (0, 0), ship
  19.             _BLEND
  20.             LINE (0, 0)-(100, 100), _RGBA(255, 0, 0, ABS(SIN((x + y + z) / 10)) * 255), BF
  21.             _PUTIMAGE (0, 0), shipmask
  22.             _DEST 0
  23.  
  24.  
  25.  
  26.             _PUTIMAGE (x + y + z, y), tempShip
  27.         NEXT
  28.     NEXT
  29.  
  30.     _DISPLAY
  31.     _LIMIT 30
  32.     x = x + 1
  33.  
  34.  
  35. FUNCTION getMask (img)
  36.     mask = _NEWIMAGE(_WIDTH(img), _HEIGHT(img), 256)
  37.     _SOURCE img
  38.     _DEST mask
  39.     FOR y = 0 TO _HEIGHT(mask) - 1
  40.         FOR x = 0 TO _WIDTH(mask) - 1
  41.             c& = POINT(x, y)
  42.             IF _ALPHA32(c&) = 0 THEN c& = 0 ELSE c& = 1
  43.             PSET (x, y), c&
  44.         NEXT
  45.     NEXT
  46.     PALETTE 0, _RGBA(0, 0, 0, 0)
  47.     _CLEARCOLOR 1, mask
  48.     _DONTBLEND mask
  49.     _SOURCE 0
  50.     _DEST 0
  51.     getMask = mask
  52.  


Offline johannhowitzer

  • Forum Regular
  • Posts: 118
    • View Profile
Re: Need a cheaper way to do clearcolor
« Reply #28 on: June 28, 2021, 11:03:38 pm »
Quote
Also for in-game usage I assume some sort of temporary copy of the original was made, then the operations were performed (adding coloured shading then using _CLEARCOLOR), then the temporary copy was rendered to the screen and probably that temporary image was disposed of at some point (or re-used?).
That's right, I've been using a dedicated image surface for pre-processing effects.

Quote
Also given the example of pixel-art provided I wonder if the re-colouring could not have been done before the up-scaling.
The upscale was only for visibility on the forum, my game's code does not use putimage to stretch anything - except the laser, which is going to be in the sprite sheet pre-stretched soon.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Need a cheaper way to do clearcolor
« Reply #29 on: June 29, 2021, 10:21:58 am »
I still don't see why a simple sprite sheet wouldn't work for this.

 [ You are not allowed to view this attachment ]  

With the above, we have our 25  instances of our base ship blended with red alpha, in increasing levels of 10 alpha.  The whole sheet takes up about 1MB of memory and we don't have to clearcolor anything -- just pop the image we need directly onto the screen where we need it.

The code to create this sheet is very simple, coming in at less than 40 lines, and it'd be easy enough to create another sheet for blue or white scale as well.

Code: QB64: [Select]
  1. '$INCLUDE:'SaveImage.BI'
  2. im = _LoadImage("ship.png", 32)
  3. AlphaSheet = _NewImage(143, 104, 32)
  4. _Dest AlphaSheet
  5.  
  6. 'Strip out the grayscale from the QB64 forum's image so we're left with the bare ship.
  7. For y = 0 To 103
  8.     For x = 0 To 142
  9.         p&& = Point(x, y)
  10.         If _Red32(p&&) = _Green32(p&&) And _Red32(p&&) = _Blue32(p&&) Then
  11.         Else
  12.             PSet (x, y), p&&
  13.         End If
  14.     Next
  15. Screen AlphaSheet
  16. ni = _NewImage(143 * 5, 104 * 5, 32)
  17. For x = 0 To 4
  18.     For y = 0 To 4
  19.         If c Then _Dest 0: _Source 0: _FreeImage c
  20.         c = _CopyImage(AlphaSheet)
  21.         _Dest c: _Source c
  22.         Line (0, 0)-(142, 103), _RGBA32(255, 0, 0, 50 * x + 10 * y), BF
  23.         _DontBlend
  24.         p&& = Point(0, 0)
  25.         For y1 = 0 To 103
  26.             For x1 = 0 To 142
  27.                 If Point(x1, y1) = p&& Then PSet (x1, y1), 0 'Strip out raw alpha
  28.         Next x1, y1
  29.         _Blend
  30.         _PutImage (143 * x, 104 * y)-Step(143, 104), c, ni
  31. Next y, x
  32.  
  33. SaveFullImage ("AlphaShip.png")
  34.  
  35. '$INCLUDE:'SaveImage.BM'
  36.  
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!