Page 1 of 1

Generating MODE 2 screens on a PC

Posted: Sat Jul 18, 2020 3:26 pm
by julie_m
I'm not sure this qualifies as a full-on development tool; but it worked first time when I tried it and somebody might find it useful, so I thought I would tidy it up a little and share it.

It uses the PerlMagick interface to the ImageMagick library to read in an image file, up to 320 pixels by 256, and build up an array of the nearest colour values using a simple threshhold based system. if the red channel is above a certain threshhold, its corresponding array entry will have bit 0 set. If the green channel is above a certain threshhold, the array entry will have bit 1 set; and if the blue channel is above a certain threshhold, the array entry will have bit 2 set. This will result in the closest matching colour chosen from the BBC's MODE 2 palette.

Having created this array, we then work through it in the same order as the bytes are arranged in the frame buffer RAM. As our array is 320*256 but the MODE 2 screen is only 160*256, we need to discard half the data. Each byte in the BBC frame buffer data corresponds to two physical pixels; we will use the first and third logical pixels as our left and right pixels on an even row, and the second and fourth logical pixels on an odd row.

Code: Select all

#!/usr/bin/perl -w

# NO RIGHTS RESERVED
# GIFTED TO THE PUBLIC DOMAIN BY JULIE MONTOYA 2020

use strict;
use Image::Magick;
use Data::Dumper;
use Getopt::Std;

my %OPTIONS;
getopts "b:g:i:o:r:", \%OPTIONS;
my $R_THRESH    = $OPTIONS{"r"} // .5;
my $G_THRESH    = $OPTIONS{"g"} // .5;
my $B_THRESH    = $OPTIONS{"b"} // .5;
my $INPUT_FILE  = $OPTIONS{"i"} // "-";
my $OUTPUT_FILE = $OPTIONS{"o"};

my $image = new Image::Magick;
my ($width, $height, $x, $y, $r, $g, $b, $c, $c1, $c2, $char_row, $byte,
    $scanline);
my @beeb_screen;
my $screen_bytes = "";

$image->Read($INPUT_FILE);

$width = $image->Get("width");
$height = $image->Get("height");

$x = $y = 0;

#  Build up an image of the screen on the Beeb

for ( $y = 0; $y < 256; ++$y ) {
    for ( $x = 0; $x < 320; ++$x ) {
        $c = 0;
        if ($x < $width && $y < $height) {
            ($r, $g, $b) = $image->GetPixel("x" => $x, "y" => $y);
            $c |= 1 if ($r > $R_THRESH);
            $c |= 2 if ($g > $G_THRESH);
            $c |= 4 if ($b > $B_THRESH);
        };
        $beeb_screen[$y]->[$x] = $c;
    };
};

#  Now @beeb_screen is a 320 by 256 array of pixel colours in range 0-7
#  We need to de-linearise this into BBC order

for ( $char_row = 0; $char_row < 32; ++$char_row ) {
    for ( $byte = 0; $byte < 80; ++$byte ) {
        for ( $scanline = 0; $scanline < 8; ++$scanline ) {
            $y = 8 * $char_row + $scanline;
            $x = 4 * $byte;
            
            #  use pixel columns 0 and 2 on even rows, 1 and 3 on odd
            $c1 = $beeb_screen[$y][$x + 2 + $y % 2];
            $c2 = $beeb_screen[$y][$x + $y % 2];

            #  interleave the bits for the BBC's frame buffer
            $c = 0;
            $c |= 1 if ($c1 & 1);
            $c |= 2 if ($c2 & 1);
            $c |= 4 if ($c1 & 2);
            $c |= 8 if ($c2 & 2);
            $c |= 16 if ($c1 & 4);
            $c |= 32 if ($c2 & 4);

            $screen_bytes .= pack "C", $c;
        };
    };
};

if ($OUTPUT_FILE) {
    open OFH, ">", $OUTPUT_FILE or die "Could not open $OUTPUT_FILE: $!";
    print OFH $screen_bytes;
    close OFH;
}
else {
    print $screen_bytes;
}

exit;
Command line options are -r, -g, -b for the colour threshholds (between 0 and 1, default 0.5); -i for the input file; and -o for the output file. If no output file is specified, the output will be sent to STDOUT.

Here's what the output looks like:
brighton2_beeb.png
Corrected program output
brighton2_beeb.png (8.21 KiB) Viewed 510 times
And here's the original picture:
brighton1.png
Original image
brighton1.png (194.99 KiB) Viewed 521 times
This is the output from an earlier version with a mistake in it. Can you spot what I did wrong?
brighton1_beeb.png
Program output displayed on BeebEm
brighton1_beeb.png (8.8 KiB) Viewed 521 times

Re: Generating MODE 2 screens on a PC

Posted: Sat Jul 25, 2020 9:52 pm
by scruss
Neat! I just found out about Dithertron, which I tried out on your image in BBC Mode 2. Here's an image uncorrected for aspect ratio:
brighton1-bbcmicro.mode2.png
brighton from Dithertron
brighton1-bbcmicro.mode2.png (16.58 KiB) Viewed 380 times
I wrote about Dithertron and its associated IDE here: viewtopic.php?f=55&t=20047

Re: Generating MODE 2 screens on a PC

Posted: Sat Jul 25, 2020 10:32 pm
by julie_m
That's pretty good!

I actually had a go at dithering colours myself. My first attempt suggested "you'll have to try harder than that", but then the day job got in the way of my adventures with the ImageMagick documentation (no point importing a dog from a library and barking yourself, and all that; it has methods to replicate anything you can do with the command line).

Re: Generating MODE 2 screens on a PC

Posted: Sun Jul 26, 2020 2:22 am
by scruss
I've always found ImageMagick to be unnecessarily hard to use. Far too many options, and sometimes not great output. Netpbm is what I use to dither to a limited palette. It's best to build it from source so you're not stuck with the 20+ year old version that comes packaged with most Linux distributions.