Magnetic Scrolls Interpreter - Christmas Edition

Anything QL Software or Programming Related.
User avatar
pjw
QL Wafer Drive
Posts: 1297
Joined: Fri Jul 11, 2014 8:44 am
Location: Norway
Contact:

Re: Magnetic Scrolls Interpreter - Christmas Edition

Post by pjw »

mk79 wrote:
pjw wrote:Spectacular! Any chance of you sharing your "proper on-the-fly Floyd-Steinberg dithering for MODE 8 graphics"?
You mean, like in source code shared on GitHub or something like that? :-P It's in ql_c function RGB8ToQLiFS ;-)
Yeah, I mean something like that :? The binary might be more intelligible to the like of me, so we still have to see.


Per
dont be happy. worry
- ?
User avatar
tofro
Font of All Knowledge
Posts: 2700
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: Magnetic Scrolls Interpreter - Christmas Edition

Post by tofro »

Floyd-Steinberg is actually easy to understand - It will be useful if you try to display a higher-quality picture on a lower-quality (in terms of colour depth) display.

It starts by comparing what it wants to display with what it's capable to display. In the code, it scales down R3G3B3 Amiga (00000RRR0GGG0BBB) pictures to R1G1B1 (number == bit depth/colour) QL pictures - The original has 7 shades of each base colour, the QL only one (on or off). Let's assume the original pixel has "not quite full" red (0000010000000000 == 100R). The QL can only display "full red" - So, when we display a "full red" (111b) pixel for this one on the QL, we have accumulated 011b "too much red". This is compensated by deducting this "too much red" from neighboring pixels. (That also works the other way round, when we display "not enough" of a colour because of rounding down at 011b). The "too much" or "not enough" of a base colour is distributed in 1/16 of the difference to neighbours: 7/16 of the difference goes to the pixel to the right, 1/16 to the pixel one down and one right, 5/16 goes to the pixel below us, and 3/16to the one diagonally left and down (in the code, the division by 16 is delayed to long after the final sum of the applied differences is calculated to keep rounding errors small - we're doing integer arithmetic only). This is done for all 3 base colour components (red, green, blue) separately - Basically a "wave of colour differences" runs from the top left to bottom right and evens out the picture.

The following code converts one Amiga pixel into one QL pixel and sets the correction arrays (oldErrors and newErrors). Note these arrays (even if they "look" local) are static and survive until the next (and all following) function call. So, while we run horizontally through the line, we collect an array of colour correction values for the next line in the nextLinCorr array - which will be applied when we run through the next line.

Code: Select all

unsigned short RGB8ToQLiFS(unsigned int RGB8, int x, int y) {
    char r, b, g, newR, newG, newB;
    short rCorr, bCorr, gCorr;
    int newCol;

    static colourCorr nextLinCorr [259], thisLinCorr [259];
    //static colourCorr nextPixCorr;
    static colourCorr *oldErrors;
    static colourCorr *newErrors;

    if ((x > 250) || (x < 0)) return 0;
    if ((y > 250) || (y < 0)) return 0;

    // Initialize if we start a new picture
    if ((x == 0) && (y == 0)) {
        memset (nextLinCorr, 0, sizeof (nextLinCorr));
        memset (thisLinCorr, 0, sizeof (thisLinCorr));
        oldErrors = &(nextLinCorr [1]); // Catches out of bounds at start and end of a line
        newErrors = &(thisLinCorr [1]);
    } else if (x == 0) // Initialize if we start a new line (alternate the two arrays)
    {
        colourCorr *tmp = oldErrors;
        oldErrors = newErrors;
        newErrors = tmp;
        memset(newErrors - sizeof (colourCorr), 0, sizeof(nextLinCorr)); // clear new
        //memset(&nextPixCorr, 0, sizeof (colourCorr));
    }

    // Convert color parts to 0-7
    r = (((RGB8 & red_mask)) >> red_shift);
    g = (((RGB8 & green_mask)) >> green_shift);
    b = (RGB8 & blue_mask);

    // Add the correction values
    r += (oldErrors [x + 1].RCorr / 16);
    g += (oldErrors [x + 1].GCorr / 16);
    b += (oldErrors [x + 1].BCorr / 16);

    // Now find the colour errors (in original colour space) that occur when we map the colour values
    newR = (r > 3) ? 7 : 0; 
    newG = (g > 3) ? 7 : 0; 
    newB = (b > 3) ? 7 : 0; 

    rCorr = r - newR; 
    gCorr = g - newG;
    bCorr = b - newB;

    //printf ()

    // And apply the correction Values to the correction array
    // (note correction array is shifted one to the right!)
    oldErrors [x + 2].RCorr += 7 * rCorr;
    newErrors [x + 2].RCorr += 1 * rCorr;
    newErrors [x + 1].RCorr += 5 * rCorr;
    newErrors [x + 0].RCorr += 3 * rCorr;
    oldErrors [x + 2].GCorr += 7 * gCorr;
    newErrors [x + 2].GCorr += 1 * gCorr;
    newErrors [x + 1].GCorr += 5 * gCorr;
    newErrors [x + 0].GCorr += 3 * gCorr;
    oldErrors [x + 2].BCorr += 7 * bCorr;
    newErrors [x + 2].BCorr += 1 * bCorr;
    newErrors [x + 1].BCorr += 5 * bCorr;
    newErrors [x + 0].BCorr += 3 * bCorr;

    // simply fumble the three bits into one number 0-7
    return (((newB != 0)) | ((newR != 0) << 1) | ((newG != 0) << 2));
}
Normally, you would simply apply the correction values for the "next right" and "next row down" pixels back to the original picture - The code above uses a two separate arrays that are alternated between rows for the correction values, because the original picture is read-only, which makes the code a bit harder to understand.


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
Silvester
Gold Card
Posts: 436
Joined: Thu Dec 12, 2013 10:14 am
Location: UK

Re: Magnetic Scrolls Interpreter - Christmas Edition

Post by Silvester »

pjw wrote: :? The binary might be more intelligible to the like of me
Photon also used Floyd Steinberg 7/3/5/1 distribution : http://www.dilwyn.me.uk/graphics/photonsource.zip see process_asm (line 112 onwards).

BTW tofro, are the original raw images available? I had a quick search but never found them.
Last edited by Silvester on Tue May 10, 2022 2:06 pm, edited 1 time in total.


David
User avatar
tofro
Font of All Knowledge
Posts: 2700
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: Magnetic Scrolls Interpreter - Christmas Edition

Post by tofro »

Silvester wrote: BTW tofro, are the original raw images available? I had a quick search but never found them.
Well, they're packed (rather: concatenated with a small header) in the .gfx files. The format of those is (somewhat loosely) described in the "tech_txt" file on GitHub. The truth is (as always) in the code.


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
Silvester
Gold Card
Posts: 436
Joined: Thu Dec 12, 2013 10:14 am
Location: UK

Re: Magnetic Scrolls Interpreter - Christmas Edition

Post by Silvester »

tofro wrote:Well, they're packed (rather: concatenated with a small header) in the .gfx files. The format of those is (somewhat loosely) described in the "tech_txt" file on GitHub. The truth is (as always) in the code.
Ah, I'd hoped that wasn't the case, thought the original images may have also been available somewhere as PNG/JPG :)


David
User avatar
XorA
Site Admin
Posts: 1365
Joined: Thu Jun 02, 2011 11:31 am
Location: Shotts, North Lanarkshire, Scotland, UK

Re: Magnetic Scrolls Interpreter - Christmas Edition

Post by XorA »

tofro wrote:I finally came around to
  1. Publish the source code on Github
  2. Fix some nasty bugs
  3. Implement proper on-the-fly Floyd-Steinberg dithering for MODE 8 graphics - looks much nicer now on a real QL. (activate with "-f")
PawnGuru.pngPawnBridge.png

(I'm pretty sure I meant "mid January 2022" in my previous post. Thus, I'm only 4 months late...)
That looks amazing! Almost like the QL was designed for games :-D


User avatar
tofro
Font of All Knowledge
Posts: 2700
Joined: Sun Feb 13, 2011 10:53 pm
Location: SW Germany

Re: Magnetic Scrolls Interpreter - Christmas Edition

Post by tofro »

Silvester wrote:
tofro wrote:Well, they're packed (rather: concatenated with a small header) in the .gfx files. The format of those is (somewhat loosely) described in the "tech_txt" file on GitHub. The truth is (as always) in the code.
Ah, I'd hoped that wasn't the case, thought the original images may have also been available somewhere as PNG/JPG :)
That gives you a very good reason to completely play through all the games (in high-colour, on QPC) and make some screenshots ;)


ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
User avatar
pjw
QL Wafer Drive
Posts: 1297
Joined: Fri Jul 11, 2014 8:44 am
Location: Norway
Contact:

Re: Magnetic Scrolls Interpreter - Christmas Edition

Post by pjw »

tofro wrote:Floyd-Steinberg is actually easy to understand - It will be useful if you try to display a higher-quality picture on a lower-quality (in terms of colour depth) display.
<>
Thanks for that excellent exposé, tofro. I think I got the gist of it.

I'll put it by for later. I may make use of it some day.


Per
dont be happy. worry
- ?
User avatar
pjw
QL Wafer Drive
Posts: 1297
Joined: Fri Jul 11, 2014 8:44 am
Location: Norway
Contact:

Re: Magnetic Scrolls Interpreter - Christmas Edition

Post by pjw »

Silvester wrote:
pjw wrote: :? The binary might be more intelligible to the like of me
Photon also used Floyd Steinberg 7/3/5/1 distribution : http://www.dilwyn.me.uk/graphics/photonsource.zip see process_asm (line 112 onwards).<>
Got it. Thanks for the tip!


Per
dont be happy. worry
- ?
User avatar
Sparrowhawk
Super Gold Card
Posts: 641
Joined: Wed Dec 15, 2010 12:33 pm
Location: @131072
Contact:

Re: Magnetic Scrolls Interpreter - Christmas Edition

Post by Sparrowhawk »

Any chance you could add the release zips to the github Releases section of the repo's pages? Saves lazy people like me having to trawl through forum posts to find the latest ;)

And... fabulous work. Thank you.


a.k.a. Jean-Yves
Post Reply