diff --git a/Images/VBMP.dsk b/dsk/VBMP.dsk similarity index 100% rename from Images/VBMP.dsk rename to dsk/VBMP.dsk diff --git a/Images/VBMP.po b/dsk/VBMP.po similarity index 100% rename from Images/VBMP.po rename to dsk/VBMP.po diff --git a/Samples/A2C_1984.bmp b/img/A2C_1984.bmp similarity index 100% rename from Samples/A2C_1984.bmp rename to img/A2C_1984.bmp diff --git a/Samples/ADVTIME2.bmp b/img/ADVTIME2.bmp similarity index 100% rename from Samples/ADVTIME2.bmp rename to img/ADVTIME2.bmp diff --git a/Samples/Donald313.bmp b/img/Donald313.bmp similarity index 100% rename from Samples/Donald313.bmp rename to img/Donald313.bmp diff --git a/Samples/IMAGE.BMP b/img/IMAGE.BMP similarity index 100% rename from Samples/IMAGE.BMP rename to img/IMAGE.BMP diff --git a/Samples/SJOBS.bmp b/img/SJOBS.bmp similarity index 100% rename from Samples/SJOBS.bmp rename to img/SJOBS.bmp diff --git a/Samples/STEVES.BMP b/img/STEVES.BMP similarity index 100% rename from Samples/STEVES.BMP rename to img/STEVES.BMP diff --git a/Samples/Zim_A2.bmp b/img/Zim_A2.bmp similarity index 100% rename from Samples/Zim_A2.bmp rename to img/Zim_A2.bmp diff --git a/Samples/ironman.bmp b/img/ironman.bmp similarity index 100% rename from Samples/ironman.bmp rename to img/ironman.bmp diff --git a/Samples/simpsons.bmp b/img/simpsons.bmp similarity index 100% rename from Samples/simpsons.bmp rename to img/simpsons.bmp diff --git a/Samples/spongebob.bmp b/img/spongebob.bmp similarity index 100% rename from Samples/spongebob.bmp rename to img/spongebob.bmp diff --git a/Utils/Apple-II-NTSC.gpl b/palettes/Apple-II-NTSC.gpl similarity index 100% rename from Utils/Apple-II-NTSC.gpl rename to palettes/Apple-II-NTSC.gpl diff --git a/Utils/JACE-DHGR.gpl b/palettes/JACE-DHGR.gpl similarity index 100% rename from Utils/JACE-DHGR.gpl rename to palettes/JACE-DHGR.gpl diff --git a/palettes/JACE-NTSC.gpl b/palettes/JACE-NTSC.gpl new file mode 100644 index 0000000..def1aba --- /dev/null +++ b/palettes/JACE-NTSC.gpl @@ -0,0 +1,20 @@ +GIMP Palette +Name: JACE NTSC +Columns: 16 +# + 0 1 0 #0 +178 0 95 #1 + 92 87 2 #2 +255 84 0 #3 + 0 127 34 #4 + 80 82 79 #5 + 27 214 0 #6 +221 214 0 #7 + 24 42 255 #8 +207 43 255 #9 +126 128 125 #10 +255 126 218 #11 + 17 167 255 #12 +164 167 254 #13 + 77 255 159 #14 +253 255 252 #15 diff --git a/plug-ins/README.md b/plug-ins/README.md new file mode 100644 index 0000000..28bd222 --- /dev/null +++ b/plug-ins/README.md @@ -0,0 +1,36 @@ +Retro dither Plug-In for the GIMP +================================= +Python plug-in for color reduction of low resolution images for displaying on retro computers. + +Introduction +============ +This plug-in adds some dithering algorithms intended for converting images into a format that can be displayed on the low resolution graphic modes of old computers like the Apple II. + +Installation +============ +* Copy the file retro-dither.py to the GIMP plug-ins directory. This directory can be changed so check first what is configured under the folders category of the GIMP settings dialog. + +* On mac or linux the plug in must be made executable. For this use the context menu of your favorite file browser or from the console use following command: +chmod +x retro-dither.py + +* Copy your preferred palettes into the GIMP palettes directory. For converting color images you should choose a palette that matches the system that will be used to display them. Some sample palettes have been included in the palettes directory. + +* Restart GIMP. After this the plug-in will appear under the Colors menu as "Dither..." + + +Usage +===== +* Load the picture you would like to convert. Depending on the mode you want to use it would make sense to resize and/or crop the picture to match the resolution you intend to use. + +* Select the mode. In monochrome mode, the plug-in will automatically convert a color image into a BW image. In color mode you should select a palette. Which palette is the best will depend if your system is NTSC/PAL and the color calibration of your TV set/ monitor so you should experiment with different palettes to see which works best for your setup. + +* Select the dithering algorithm. Which one to choose depends on the image you are converting and your personal taste so make sure to try which one works best. + +Dither algorithms +================= +The algorithms implemented here are based on the excellent explanation from Tanner Helland: + + +The performance of this plug-in is limited by the fact that is written in python and surely some optimizations could still be done. For the low resolutions of the retro computers this is not critical so expandability and simplicity was preferred over a compiled plug-in. + + diff --git a/plug-ins/retro_dither.py b/plug-ins/retro_dither.py new file mode 100755 index 0000000..0d1a817 --- /dev/null +++ b/plug-ins/retro_dither.py @@ -0,0 +1,217 @@ +#!/usr/bin/env python + +# Error Diffusion Dither script for the GIMP +# Version 1.0 +# Author: Mario Patino - cybernesto@gmail.com +# based on the dither script of Andy Hefner - ahefner@gmail.com +# dithering algorithms based on the information contained here: +# http://www.tannerhelland.com/4660/dithering-eleven-algorithms-source-code/ +# Copyright Mario Patino, 2014 + +import math +from gimpfu import * +from array import array + + +def change_pixel(dim, src, x, y, px_d): + w, h, p = dim + if x > w: + return + if y > h: + return + i = (x + y * w)*p + src[i:i+p] = array("B",[max(min((a+b),255),0) for a,b in zip(px_d,src[i:i+p])]) + +def floyd_stein(dim, src_pixels, x, y, px_d): + px_delta = [a*7/16 for a in px_d] + change_pixel(dim, src_pixels, x+1, y , px_delta) + px_delta = [a*5/16 for a in px_d] + change_pixel(dim, src_pixels, x , y+1, px_delta) + px_delta = [a*3/16 for a in px_d] + change_pixel(dim, src_pixels, x-1, y+1, px_delta) + px_delta = [a*1/16 for a in px_d] + change_pixel(dim, src_pixels, x+1, y+1, px_delta) + +def jarvis(dim, src_pixels, x, y, px_d): + px_delta = [a*7/48 for a in px_d] + change_pixel(dim, src_pixels,x+1 ,y, px_delta) + change_pixel(dim, src_pixels,x ,y+1, px_delta) + px_delta = [a*5/48 for a in px_d] + change_pixel(dim, src_pixels,x+2 ,y, px_delta) + change_pixel(dim, src_pixels,x-1 ,y+1, px_delta) + change_pixel(dim, src_pixels,x+1 ,y+1, px_delta) + change_pixel(dim, src_pixels,x ,y+2, px_delta) + px_delta = [a*3/48 for a in px_d] + change_pixel(dim, src_pixels,x-2 ,y+1, px_delta) + change_pixel(dim, src_pixels,x+2 ,y+1, px_delta) + change_pixel(dim, src_pixels,x-1 ,y+2, px_delta) + change_pixel(dim, src_pixels,x+1 ,y+2, px_delta) + px_delta = [a*1/48 for a in px_d] + change_pixel(dim, src_pixels,x-2 ,y+2, px_delta) + change_pixel(dim, src_pixels,x+2 ,y+2, px_delta) + +def stucki(dim, src_pixels, x, y, px_d): + px_delta = [a*8/42 for a in px_d] + change_pixel(dim, src_pixels,x+1 ,y, px_delta) + change_pixel(dim, src_pixels,x ,y+1, px_delta) + px_delta = [a*4/42 for a in px_d] + change_pixel(dim, src_pixels,x+2 ,y, px_delta) + change_pixel(dim, src_pixels,x-1 ,y+1, px_delta) + change_pixel(dim, src_pixels,x+1 ,y+1, px_delta) + change_pixel(dim, src_pixels,x ,y+2, px_delta) + px_delta = [a*2/42 for a in px_d] + change_pixel(dim, src_pixels,x-2 ,y+1, px_delta) + change_pixel(dim, src_pixels,x+2 ,y+1, px_delta) + change_pixel(dim, src_pixels,x-1 ,y+2, px_delta) + change_pixel(dim, src_pixels,x+1 ,y+2, px_delta) + px_delta = [a*1/42 for a in px_d] + change_pixel(dim, src_pixels,x-2 ,y+2, px_delta) + change_pixel(dim, src_pixels,x+2 ,y+2, px_delta) + +def atkinson(dim, src_pixels, x, y, px_d): + px_delta = [a*1/8 for a in px_d] + change_pixel(dim, src_pixels,x+1 ,y, px_delta) + change_pixel(dim, src_pixels,x+2 ,y, px_delta) + change_pixel(dim, src_pixels,x-1 ,y+1, px_delta) + change_pixel(dim, src_pixels,x ,y+1, px_delta) + change_pixel(dim, src_pixels,x+1 ,y+1, px_delta) + change_pixel(dim, src_pixels,x ,y+2, px_delta) + +def burkes(dim, src_pixels, x, y, px_d): + px_delta = [a*8/32 for a in px_d] + change_pixel(dim, src_pixels,x+1 ,y, px_delta) + change_pixel(dim, src_pixels,x ,y+1, px_delta) + px_delta = [a*4/32 for a in px_d] + change_pixel(dim, src_pixels,x+2 ,y, px_delta) + change_pixel(dim, src_pixels,x-1 ,y+1, px_delta) + change_pixel(dim, src_pixels,x+1 ,y+1, px_delta) + px_delta = [a*2/32 for a in px_d] + change_pixel(dim, src_pixels,x-2 ,y+1, px_delta) + change_pixel(dim, src_pixels,x+2 ,y+1, px_delta) + +def sierra3(dim, src_pixels, x, y, px_d): + px_delta = [a*5/32 for a in px_d] + change_pixel(dim, src_pixels,x+1 ,y, px_delta) + change_pixel(dim, src_pixels,x ,y+1, px_delta) + px_delta = [a*4/32 for a in px_d] + change_pixel(dim, src_pixels,x-1 ,y+1, px_delta) + change_pixel(dim, src_pixels,x+1 ,y+1, px_delta) + px_delta = [a*3/32 for a in px_d] + change_pixel(dim, src_pixels,x+2 ,y, px_delta) + change_pixel(dim, src_pixels,x ,y+2, px_delta) + px_delta = [a*2/42 for a in px_d] + change_pixel(dim, src_pixels,x-2 ,y+1, px_delta) + change_pixel(dim, src_pixels,x-1 ,y+2, px_delta) + change_pixel(dim, src_pixels,x+1 ,y+2, px_delta) + change_pixel(dim, src_pixels,x+2 ,y+1, px_delta) + + +def sierra2(dim, src_pixels, x, y, px_d): + px_delta = [a*4/16 for a in px_d] + change_pixel(dim, src_pixels,x+1 ,y, px_delta) + px_delta = [a*3/16 for a in px_d] + change_pixel(dim, src_pixels,x+2 ,y, px_delta) + change_pixel(dim, src_pixels,x ,y+1, px_delta) + px_delta = [a*2/16 for a in px_d] + change_pixel(dim, src_pixels,x-1 ,y+1, px_delta) + change_pixel(dim, src_pixels,x+1 ,y+1, px_delta) + px_delta = [a*1/16 for a in px_d] + change_pixel(dim, src_pixels,x-2 ,y+1, px_delta) + change_pixel(dim, src_pixels,x+2 ,y+1, px_delta) + +def sierra1(dim, src_pixels, x, y, px_d): + px_delta = [a*2/4 for a in px_d] + change_pixel(dim, src_pixels, x+1, y , px_delta) + px_delta = [a*1/4 for a in px_d] + change_pixel(dim, src_pixels, x , y+1, px_delta) + change_pixel(dim, src_pixels, x-1, y+1, px_delta) + +def px_dist(p1,p2): + return sum([(a-b)**2 for a,b in zip(p1,p2)]) + +def retro_dither(timg, tdrawable, mode, palette, alg): + dither = {0 : floyd_stein, + 1 : jarvis, + 2 : stucki, + 3 : atkinson, + 4 : burkes, + 5 : sierra3, + 6 : sierra2, + 7 : sierra1, + } + pdb.gimp_image_undo_group_start(timg) + try: + layer = tdrawable.copy() + timg.add_layer(layer, 0) + width = layer.width + height = layer.height + px_size = pdb.gimp_drawable_bpp(tdrawable) + dim = width, height, px_size + rgn = layer.get_pixel_rgn(0, 0, width, height, TRUE, FALSE) + src_pixels = array("B", rgn[0:width, 0:height]) + + i=0 + total=px_size*width*height + if mode == 1: + num_colors, colors = pdb.gimp_palette_get_colors(palette) + for y in range(height): + for x in range(width): + pixel = src_pixels[i:i+px_size] + dist = [px_dist(colors[c],pixel) for c in range(num_colors)] + nearest = colors[dist.index(min(dist))][0:px_size] + src_pixels[i:i+px_size] = array("B",nearest) + px_delta = [(a-b) for a,b in zip(pixel,src_pixels[i:i+px_size])] + dither[alg](dim, src_pixels, x, y, px_delta) + gimp.progress_update(i*1.0/total) + i = i + px_size + + rgn[0:width, 0:height] = src_pixels.tostring() + layer.flush() + layer.update(0,0,width,height) + pdb.gimp_image_convert_indexed(timg, NO_DITHER, CUSTOM_PALETTE, 0, FALSE, FALSE, palette) + else: + white = [255] * px_size + black = [0] * px_size + for y in range(height): + for x in range(width): + pixel = src_pixels[i:i+px_size] + dist = sum(pixel)/px_size + if dist > 127: + nearest = white + else: + nearest = black + src_pixels[i:i+px_size] = array("B",nearest) + px_delta = [(a-b) for a,b in zip(pixel,src_pixels[i:i+px_size])] + dither[alg](dim, src_pixels, x, y, px_delta) + gimp.progress_update(i*1.0/total) + i = i + px_size + + rgn[0:width, 0:height] = src_pixels.tostring() + layer.flush() + layer.update(0,0,width,height) + pdb.gimp_image_convert_indexed(timg, NO_DITHER, MONO_PALETTE, 0, FALSE, FALSE, palette) + + pdb.gimp_image_undo_group_end(timg) + + except Exception, err: + pdb.gimp_message("ERR: " + str(err)) + pdb.gimp_image_undo_group_end(timg) + +register( + "retro_dither", + "Error-diffusion dithering algorithms to create low resolution pictures in B&W or with a limited palette", + "Error-diffusion dithering algorithms", + "Mario Patino", + "Mario Patino", + "2014", + "/Colors/Dither...", + "RGB*, GRAY*", + [ + (PF_RADIO, "mode", "Dithering mode:", 0, (("Monochrome",0),("Color", 1))), + (PF_PALETTE, "palette", "Palette:", "Default"), + (PF_OPTION,"alg", "Dithering algorithm:", 0, ["Floyd-Steinberg","Jarvis, Judice, and Ninke","Stucki","Atkinson", "Burkes", "Sierra", "Two-Row Sierra", "Sierra Lite"]) + ], + [], + retro_dither) + +main() \ No newline at end of file diff --git a/Sources/DISPLAY.S b/src/DISPLAY.S similarity index 100% rename from Sources/DISPLAY.S rename to src/DISPLAY.S diff --git a/Sources/SLIDESHOW.bas b/src/SLIDESHOW.bas similarity index 100% rename from Sources/SLIDESHOW.bas rename to src/SLIDESHOW.bas diff --git a/Sources/VBMP.bas b/src/VBMP.bas similarity index 100% rename from Sources/VBMP.bas rename to src/VBMP.bas