Page 1 of 1

Re: 128x192 with 4 levels compatible

Posted: Fri Apr 06, 2018 11:43 am
by hikoki
I cannot understad this. Is it about automatic dithering? If so it would be interesting to achieve any blending of colours which is a trick used to simulate oranges for example interleaving ditherings in red and yellow. Automatic dithering in colours, not only for greys, might Not be "ugly". I don't think dithering is ugly in general. Just plain flat colours don't help to tell bright and dimmed colours, besides a bit of noise is interesting to suggest volumes and shades.

Re: 128x192 with 4 levels compatible

Posted: Fri Apr 06, 2018 12:07 pm
by Joefish
No, the original idea was for an extra screen mode that still looks OK if you don't have the modified hardware to display it properly.
I don't think it's really worth trying, because the fallback image doesn't look good enough. There were a few games converted from the C64 and amstrad to the Spectrum that just used dithers in place of colours for lazy ports of the graphics, and they all looked terrible.

Image Image Image

Mind you, Gobots looked crap on the Amstrad in the first place...

Maybe if you could get the dithers to swap places on alternate rows so you got proper chessboard stipples, like Mr Heli there, but then it'd be an immense pain to program the 4-colour versions as the bit order would be reversed on alternate rows. Fine I suppose if you stick to 2-pixel movements of sprites as then the bits are always in the same positions...

Re: 128x192 with 4 levels compatible

Posted: Fri Apr 06, 2018 2:02 pm
by hikoki
what about something really subtle? just enough to make you believe there are more colours
something subtler than this
Image

Re: 128x192 with 4 levels compatible

Posted: Fri Apr 06, 2018 3:07 pm
by 4thRock
That works but you only get one intermediate tone (3 levels).
The advantage of using the pixel order is to have 2 intermediate tones (4 levels).

I agree that the fallback image is not nice.

So lets consider changing the pixel order on alternate lines 8-)
That way, we could still use the normal chessboard dithering....

I really need to write a converter (or try to mod an emulator), but if I'm not mistaken, we would get this:
Original
Image
Converted
Image

So it would be:

Even lines
00 = black
01 = grey 1
10 = grey 2
11 = white

Odd lines
00 = black
10 = grey 1
01 = grey 2
11 = white

Re: 128x192 with 4 levels compatible

Posted: Fri Apr 06, 2018 4:25 pm
by hikoki
Off-topic

This : https://commons.m.wikimedia.org/wiki/Fi ... hering.png
See? you'd make your graphics as usual and the engine would change small chameleon-esque ditherings for you on the fly! :shock:

Re: 128x192 with 4 levels compatible

Posted: Fri Apr 06, 2018 4:31 pm
by Joefish
You need large shaded or blended areas for all those different dithers to look any good. Even then, the pixels are pretty big and distinct, so it only really works for blending similar colours.

Within the usual size of Speccy game graphics, anything more than 01/10 dithering is too complicated. And you sometimes have to invert the stipple pattern (or move it by 1 pixel) when putting it next to the solid or clear parts of your design.

Re: 128x192 with 4 levels compatible

Posted: Fri Apr 06, 2018 4:47 pm
by hikoki
This is the idea! It was already invented around 2009 by Gasman ?
It's called Chunkypaint 53c :

http://chunkypaint.zxdemo.org

http://events.retroscene.org/53c

http://53c.verve.space

There are some works from parties on zx-art, pouet, demozoo, etc

https://demozoo.org/parties/1779/

http://www.pouet.net/prod.php?which=61392

Dunno if this would be possible to make games or it's just useful for lower resolution loading screens.

Re: 128x192 with 4 levels compatible

Posted: Fri Apr 06, 2018 6:18 pm
by 4thRock
hikoki, those examples are different. They use dithering to simulate a 50% mix between paper and ink.

The point here it to try to use dithering in a way that:
- still looks decent on a normal spectrum
- encodes two values, so you can have 33% and 66% mixing (on compatible "hardware")

Re: 128x192 with 4 levels compatible

Posted: Fri Apr 06, 2018 6:34 pm
by hikoki
Good luck 4thRock. Hope someone will help you mod some emulator.
Anyways I like the idea of painting with a virtual extended palette like and the paint editor makes all the dithering for you. This might be a nice feature suggestion for ZXPaintBrush. According to Joefish this is not feasible to make games? Please let me know if there is some game using this technique intensively.

Re: 128x192 with 4 levels compatible

Posted: Sat Apr 07, 2018 1:47 am
by 4thRock
OK, wrote a simple Javascript image converter and got some results

Standard image
Image

Decoded
Image

Not horrible on a standard display.

I'll share the code latter.

Re: 128x192 with 4 levels compatible

Posted: Sat Apr 07, 2018 12:05 pm
by AndyC
Using a different encoding on different scanlines would be pretty much unworkable for moving images, so you'd really be limiting the use to static screens.

Re: 128x192 with 4 levels compatible

Posted: Sat Apr 07, 2018 1:48 pm
by Joefish
Not really. Because of the drop in resolution, things would only move horizontally in 2-pixel steps, so it would be reasonable for things to move vertically in 2-pixel steps too, thus preserving the stipple alignment.

Re: 128x192 with 4 levels compatible

Posted: Tue Apr 10, 2018 6:00 pm
by 4thRock
Copy this into a HTML file and you have an encoder / decoder.
The JS code is quite basic and not optimized, so it's very simple to understand.

You can see it working here:
http://4throckworld.com/dev/128x192/

Of course, for better results it's better to start with images already converted to 4 levels.
The converter will only do straight brightness level mapping.

Attributes are completely ignored.

Code: Select all

<!DOCTYPE html>
<html>
<head>
<script>

function draw(img) {
  var canvas = document.getElementById('canvas');
  var ctx = canvas.getContext('2d');
  ctx.drawImage(img, 0, 0);
  img.style.display = 'none';
  var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  var data = imageData.data;
    
  var Encode = function() {
     for (var i = 0; i < data.length; i += 8) {
	
	//get average value
    var avg = (data[i] + data[i + 1] + data[i + 2] + data[i + 4] + data[i + 5] + data[i + 6]) /  6;

	var lineNumber =  Math.round( (i/4)/256 );
	var oddLine = lineNumber % 2 
	
	
	// 00
	if  ( avg <	64) {var avg_a = 0 ;  var avg_b = 0};
	// 01
	if  ( (avg >	64)  && (avg < 127) ) { if ( oddLine == 0 ) { var avg_a = 0 ;  var avg_b = 255}  else { avg_a = 255 ; avg_b = 0}  };
	// 10
	if  ( (avg >	127) && (avg < 191) ) { if ( oddLine == 0 ) { var avg_a = 255 ; var avg_b = 0}  else { avg_a = 0 ; avg_b = 255}  };
	// 11
	if  ( avg >	191 ) { var avg_a = 255 ;  var avg_b = 255};
	  
	  
		  
      data[i]     = avg_a; // red
      data[i + 1] = avg_a; // green
      data[i + 2] = avg_a; // blue
	  //data[i + 3 ]     = 255; // red
	  
      data[i + 4] = avg_b; // green
      data[i + 5] = avg_b; // blue
	  data[i + 6] = avg_b; // green
      //data[i + 7] = 255; // blue
	  
    }
    ctx.putImageData(imageData, 0, 0);
  };

  var Decode = function() {
    for (var i = 0; i < data.length; i += 8) {
	
	//get average values of two consecutive pixels
    var avg_a = (data[i] + data[i + 1] + data[i + 2] ) /  3;
	var avg_b = (data[i + 4] + data[i + 5] + data[i + 6] ) /3;

	var lineNumber =  Math.round( (i/4)/256 );
	var oddLine = lineNumber % 2 
	
	
	// 00
	if  ( (avg_a <	85) && (avg_b < 85) ) {avg = 0};
	// 01
	if  ( (avg_a <	85) && (avg_b > 170) ) { if ( oddLine == 0 ) { avg = 85}  else { avg = 170}  };
	// 10
	if  ( (avg_a >	170) && (avg_b < 85) ) { if ( oddLine == 0 ) { avg = 170}  else { avg = 85}  };
	// 11
	if  ( (avg_a >	170) && (avg_b > 170) ) {avg = 255};
	  
	  
		  
      data[i]     = avg; // red
      data[i + 1] = avg; // green
      data[i + 2] = avg; // blue
	  //data[i + 3 ]     = 255; // red
	  
      data[i + 4] = avg; // green
      data[i + 5] = avg; // blue
	  data[i + 6] = avg; // green
      //data[i + 7] = 255; // blue

    }
    ctx.putImageData(imageData, 0, 0);
  };

  var Encodebtn = document.getElementById('Encodebtn');
  Encodebtn.addEventListener('click', Encode);
  var Decodebtn = document.getElementById('Decodebtn');
  Decodebtn.addEventListener('click', Decode);
}

function handleImage(e){
    var reader = new FileReader();
		
	var img = new Image();
//img.src = 'heli.png';

	
    reader.onload = function(event){
		
				img.src = event.target.result;
				img.onload = function() {
  draw(this);
};
			}
    reader.readAsDataURL(e.target.files[0]);     
}

function readImage() {
    if ( this.files && this.files[0] ) {
        var FR= new FileReader();
        FR.onload = function(e) {
           //var img = new Image();
           img.addEventListener("load", function() {
             ctx.drawImage(img, 0, 0);
           });
           img.src = e.target.result;
        };       
        FR.readAsDataURL( this.files[0] );
    }
}



function pageLoaded() {
var imageLoader = document.getElementById('fileUpload');
imageLoader.addEventListener('change', handleImage);



var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');


}
</script>
</head>
<body onload = "pageLoaded();">
<label>Loads a 256x192 image.<br> Encodes / decodes a 192x192 4 bit level image using 1 bit dithering. <br>Bit order is reversed on each line for better visual quality on the undecoded image.</label><br/>
<hr>
<input type='file' id="fileUpload" />
<canvas id="canvas" width="256" height="192"></canvas>
<div>
  <input id="Decodebtn" value="Decode" type="button">
  <input id="Encodebtn" value="Encode" type="button">
</div>
</body>
</html>

Re: 128x192 with 4 levels compatible

Posted: Tue Apr 10, 2018 9:46 pm
by chernandezba
Seems some of the video modes of machine ZX Prism from Jeff Braine
https://youtu.be/Jrge4HtnqPI
Minute 5.37
You can test it on ZEsarUX ;)

Re: 128x192 with 4 levels compatible

Posted: Mon Apr 30, 2018 3:57 pm
by hikoki
I've found this demo in Basic, somewhat related with 53c, which tried to emulate 128 different colours, Rainbird