Drawing the 16-bit colour space and working with it.

Started by Scavenger, Thu 26/11/2015 15:37:42

Previous topic - Next topic

Scavenger

I'm trying to draw this colour space:

with AGS using Game.GetColorFromRGB and DrawPixel. it's the standard 565 16 bit colour space, but I'm having trouble coming up with the formula just looking at it. I tried a couple of things but it looks really off. Can someone point me in the right direction? My current code is:

Code: AGS

DynamicSprite *CLUT256;
int x = 0;
int y = 0;
int r = 0;
int g = 0;
int b = 0;
int maxr = 0;
int maxb = 31;
int maxg = 0;

function room_AfterFadeIn()
{
CLUT256 = DynamicSprite.Create (256, 256);
SetGameSpeed (60);
}
function room_RepExec()
{
  DrawingSurface *surf = CLUT256.GetDrawingSurface ();
  if (y < 256)
  {
    while (x < 256)
      {
        surf.DrawingColor = Game.GetColorFromRGB (r*8, g*4, b*8);
        //surf.DrawingColor = 21;
        surf.DrawPixel (x, y);
        /* //Clearly wrong colour code.
        if (b < maxb) b++;
        else
          {
            if (maxb > 0) maxb --;
            b = 0;
            if (r < maxr) r++;
            else 
            {
              if (maxr < 31) maxr++;
              r = 0;
              if (g < maxg) g++;
              else
              {
                g=0;
                if (maxg < 63) maxg++;
              }
            }
          }
           */
          x++;
      }
      x=0;
      y++;
  }
  surf.Release ();
  oClut.Graphic = CLUT256.Graphic;


And on a related note, colour mixing in the 16bit colour space is done simply by doing math on the index number, right? EG the colour that is 50% between color 3000 and color 6000 is (6000+3000)/2? I tested it out with AGS' 16bit colour mixer tab, it seemed to check out, but I need some confirmation.

I'm not too familiar with pure RGB mixing. I'm just trying to create something more memory efficient than a 256*2048 array for my colour mixing, and something more extensible.

Snarky

In standard 16-bit, the bits are just split among the RGB color channels (usually 5-6-5 bits). AGS, for some totally idiotic reason, messes with this mapping. Refer to these previous threads:

http://www.adventuregamestudio.co.uk/forums/index.php?topic=43966
http://www.adventuregamestudio.co.uk/forums/index.php?topic=43311
http://www.adventuregamestudio.co.uk/forums/index.php?topic=34391.msg451781#msg451781

That should be enough to work it all out. If it weren't for AGS's stupid remapping, it would just be a matter of:

Code: ags

  while (y < 256)
  {
    while (x < 256)
    {
        surf.DrawingColor = x + 256*y;
        surf.DrawPixel(x,y);
        x++;
    }
    x=0;
    y++;
  }


One reason why your current code isn't working is that you're never actually updating the r, g or b variables. Here's how I would do it, given AGS's limitations:

Code: ags

  while (y < 256)
  {
    while (x < 256)
    {
        int c = x + 256*y;
        b = (c % 32);    // The lower 5 bits of c
        c = c/32;
        g = (c % 64);    // The next 6 bits of c
        b = (c/64);      // The remaining (upper) 5 bits of c

        surf.DrawingColor = Game.GetColorFromRGB (r*8, g*4, b*8);
        surf.DrawPixel(x,y);
        x++;
    }
    x=0;
    y++;
  }


Also, if you consider the split between the color channels, it's clear that you can't mix colors just treating the 16-bit numbers as a whole, because values will underflow. Specifically, if either the R or G channel is an odd value when you add the two numbers together, when you divide by two (a right-shift), this bit will move into the next lower channel (G or B).

For example, if you try to mix black (0,0,0) (in binary: 00000-000000-00000 = 0) and very slight red (1,0,0) (in binary: 00001-000000-00000 = 2048), you'll get (2048+0) / 2 = 1024. Which in binary is 00000-100000-00000, or (0,128,0) in 8-bit RGB. In other words, a deep blue. The AGS remapping just makes this problem worse, since the results will be even more unpredictable.

Scavenger

#2
Ah, I'm not actually using 16-bit colour mode, I'm trying to fill out the colour space using Game.GetColorFromRGB in 8-bit mode. It's too slow for realtime stuff, but it seems fast enough to populate a lookup table. I won't actually be running afoul of AGS' remapping problems. I just need to know how to draw it using RGB values. Then, using C++, I'll read the values from it and use it to draw colour effects to the screen. It seemed like a better idea than having static lookup tables that took up a load of memory.


Hmm, if I can't do simple math with the resulting number, that means I'll need to just use the palette entry's values instead:

Code: AGS

function GetClosestMixedColour (char x,char y)
{
//average of two colours, add additional /2 to the red and blue channels, because 256 colour mode uses 666 and 16bit uses 565
int result;
int r = (palette[x].r + palette[y].r)>>2;
int b = (palette[x].b + palette[y].b)>>2;
int g = (palette[x].g + palette[x].g)>>1;
result = (r<<11) + (g<<5) + b; //shift bits into place. Not sure if these are the right numbers for shifting, will check later
return result;
}

Though I'm not sure how I'd model 10% transparent, 20% transparent and so on from this.

EDIT:
Looking good! It isn't losing too much in the way of precision, except at the darker end where it doesn't matter too much.

Snarky

Goddammit! I tried to look up whether AGS had bitwise right-shift and left-shift, but none of the searches gave me anything. Of course you should use that, then.

So your approach is basically to do all the logic in pseudo-16-bit (or 32-bit) in your plugin, and then just do a color reduction in order to display on the screen? It doesn't seem very much in the 8-bit spirit. Personally, if you insist on sticking with this outmoded color mode, I think you should embrace its limitations. If you want to fade something, either use a pixel dissolve (erode by dithering), or restrict the foreground and background so you can just do a straight palette shift.

Scavenger

#4
Oh no, this is just to save space and allow me to use one lookup table instead of nine when I do translucency. After all, 65 kilobytes is smaller than 512k, and the less overhead I have, the better. Originally I had 256x256 LUTs that manually blended every possible pixel combination at different translucencies, but that approach is clunky and restrictive. I didn't like it, and it took half an hour to generate the LUTs.

I'm fully aware of what else I can do in 8-bit colour mode, like palette cycling and stuff, and I fully intend to keep doing all I can do with it. This is just a way of not having to deal with three billion different tables for different effects. My plugin also takes palette cycling into account anyway, so there's no problem there.

Snarky

OK, I understand what you're doing (give or take some minor details, like why you need to generate this lookup table in AGS in the first place; can't you do it yourself from the palette? In fact, if you have control over the palette layout, you could arrange it to be much more efficient by e.g. sorting it by one of the channels), but not why you're doing it. To me it would not be a satisfying solution.

That's nothing new, though; I've never really understood your reasons for implementing the game the way you have chosen. But it takes all kinds, and clearly you have some vision for this that I'm not on the right wavelength for. So I'll stop arguing over it, and just wish you good luck.

Oh, one tip: Instead of hacking all the image processing code yourself, have you considered using a library? I'm pretty sure ImageMagick, for example, should support everything you're talking about.

Monsieur OUXX

You can get inspiration from the BMP 2.0 module which allows some reading and writing from several AGS color spaces (just throwing my two cents, please ignore if it doesn't contain any helpful function)
 

SMF spam blocked by CleanTalk