Yeah, I mean something like that The binary might be more intelligible to the like of me, so we still have to see.mk79 wrote:You mean, like in source code shared on GitHub or something like that? It's in ql_c function RGB8ToQLiFSpjw wrote:Spectacular! Any chance of you sharing your "proper on-the-fly Floyd-Steinberg dithering for MODE 8 graphics"?
Magnetic Scrolls Interpreter - Christmas Edition
Re: Magnetic Scrolls Interpreter - Christmas Edition
Per
dont be happy. worry
- ?
dont be happy. worry
- ?
Re: Magnetic Scrolls Interpreter - Christmas Edition
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.
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.
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));
}
ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
Re: Magnetic Scrolls Interpreter - Christmas Edition
Photon also used Floyd Steinberg 7/3/5/1 distribution : http://www.dilwyn.me.uk/graphics/photonsource.zip see process_asm (line 112 onwards).pjw wrote: The binary might be more intelligible to the like of me
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
Re: Magnetic Scrolls Interpreter - Christmas Edition
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.Silvester wrote: BTW tofro, are the original raw images available? I had a quick search but never found them.
ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
Re: Magnetic Scrolls Interpreter - Christmas Edition
Ah, I'd hoped that wasn't the case, thought the original images may have also been available somewhere as PNG/JPGtofro 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.
David
- 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
That looks amazing! Almost like the QL was designed for gamestofro wrote:I finally came around to
PawnGuru.pngPawnBridge.png
- Publish the source code on Github
- Fix some nasty bugs
- Implement proper on-the-fly Floyd-Steinberg dithering for MODE 8 graphics - looks much nicer now on a real QL. (activate with "-f")
(I'm pretty sure I meant "mid January 2022" in my previous post. Thus, I'm only 4 months late...)
Re: Magnetic Scrolls Interpreter - Christmas Edition
That gives you a very good reason to completely play through all the games (in high-colour, on QPC) and make some screenshotsSilvester wrote:Ah, I'd hoped that wasn't the case, thought the original images may have also been available somewhere as PNG/JPGtofro 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.
ʎɐqǝ ɯoɹɟ ǝq oʇ ƃuᴉoƃ ʇou sᴉ pɹɐoqʎǝʞ ʇxǝu ʎɯ 'ɹɐǝp ɥO
Re: Magnetic Scrolls Interpreter - Christmas Edition
Thanks for that excellent exposé, tofro. I think I got the gist of it.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.
<>
I'll put it by for later. I may make use of it some day.
Per
dont be happy. worry
- ?
dont be happy. worry
- ?
Re: Magnetic Scrolls Interpreter - Christmas Edition
Got it. Thanks for the tip!Silvester wrote:Photon also used Floyd Steinberg 7/3/5/1 distribution : http://www.dilwyn.me.uk/graphics/photonsource.zip see process_asm (line 112 onwards).<>pjw wrote: The binary might be more intelligible to the like of me
Per
dont be happy. worry
- ?
dont be happy. worry
- ?
- Sparrowhawk
- Super Gold Card
- Posts: 643
- Joined: Wed Dec 15, 2010 12:33 pm
- Location: @131072
- Contact:
Re: Magnetic Scrolls Interpreter - Christmas Edition
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.
And... fabulous work. Thank you.
a.k.a. Jean-Yves