1 #!/usr/bin/env python 2 3 """ 4 Convert and optimise images for display in an Acorn Electron MODE 1 variant 5 with four colours per line but eight colours available for selection on each 6 line. 7 8 Copyright (C) 2015 Paul Boddie <paul@boddie.org.uk> 9 10 This program is free software; you can redistribute it and/or modify it under 11 the terms of the GNU General Public License as published by the Free Software 12 Foundation; either version 3 of the License, or (at your option) any later 13 version. 14 15 This program is distributed in the hope that it will be useful, but WITHOUT ANY 16 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 17 PARTICULAR PURPOSE. See the GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License along 20 with this program. If not, see <http://www.gnu.org/licenses/>. 21 """ 22 23 from optimiser import * 24 from os.path import split, splitext 25 import EXIF 26 import PIL.Image 27 import sys 28 29 def rotate_and_scale(exif, im, width, height, rotate): 30 31 """ 32 Using the given 'exif' information, rotate and scale image 'im' given the 33 indicated 'width' and 'height' constraints and any explicit 'rotate' 34 indication. The returned image will be within the given 'width' and 35 'height', filling either or both, and preserve its original aspect ratio. 36 """ 37 38 if rotate or exif and exif["Image Orientation"].values == [6L]: 39 im = im.rotate(270) 40 41 w, h = im.size 42 if w > h: 43 height = (width * h) / w 44 else: 45 width = (height * w) / h 46 47 return im.resize((width, height)) 48 49 def test(): 50 51 "Generate slices of the colour cube." 52 53 size = 64 54 for r in (0, 63, 127, 191, 255): 55 pim = PIL.Image.new("RGB", (size, size)) 56 im = SimpleImage(list(pim.getdata()), pim.size) 57 test_slice(im, size, r) 58 pim.putdata(im.getdata()) 59 pim.save("rgb%d.png" % r) 60 61 def test_flat(rgb): 62 63 "Generate a flat image for the colour 'rgb'." 64 65 size = 64 66 pim = PIL.Image.new("RGB", (size, size)) 67 im = SimpleImage(list(pim.getdata()), pim.size) 68 test_flat_slice(im, size, rgb) 69 pim.putdata(im.getdata()) 70 pim.save("rgb%02d%02d%02d.png" % rgb) 71 72 def get_float(options, flag): 73 try: 74 i = options.index(flag) 75 if i+1 < len(options) and options[i+1].isdigit(): 76 return float(options[i+1]) 77 else: 78 return 1.0 79 except ValueError: 80 return 0.0 81 82 # Main program. 83 84 if __name__ == "__main__": 85 86 # Test options. 87 88 if "--test" in sys.argv: 89 test() 90 sys.exit(0) 91 elif "--test-flat" in sys.argv: 92 test_flat((120, 40, 60)) 93 sys.exit(0) 94 elif "--help" in sys.argv: 95 print >>sys.stderr, """\ 96 Usage: %s <input filename> <output filename> [ <options> ] 97 98 Options are... 99 100 -s - Saturate the input image (can be followed by a float, default 1.0) 101 -d - Desaturate the input image (can be followed by a float, default 1.0) 102 -D - Darken the input image (can be followed by a float, default 1.0) 103 -B - Brighten the input image (can be followed by a float, default 1.0) 104 105 -r - Rotate the input image clockwise 106 -p - Generate a separate preview image 107 -h - Make the preview image with half horizontal resolution (MODE 2) 108 -v - Verify the output image (loaded if -n is given) 109 -n - Generate no output image 110 """ % split(sys.argv[0])[1] 111 sys.exit(1) 112 113 width = 320 114 height = 256 115 116 input_filename, output_filename = sys.argv[1:3] 117 basename, ext = splitext(output_filename) 118 preview_filename = "".join([basename + "_preview", ext]) 119 120 options = sys.argv[3:] 121 122 # Preprocessing options that can be repeated for extra effect. 123 124 saturate = get_float(options, "-s") 125 desaturate = get_float(options, "-d") 126 darken = get_float(options, "-D") 127 brighten = get_float(options, "-B") 128 129 # General output options. 130 131 rotate = "-r" in options 132 preview = "-p" in options 133 half_resolution_preview = "-h" in options 134 verify = "-v" in options 135 no_normal_output = "-n" in options 136 make_image = not no_normal_output 137 138 # Load the input image if requested. 139 140 if make_image or preview: 141 exif = EXIF.process_file(open(input_filename)) 142 pim = PIL.Image.open(input_filename).convert("RGB") 143 pim = rotate_and_scale(exif, pim, width, height, rotate) 144 im = SimpleImage(list(pim.getdata()), pim.size) 145 process_image(im, saturate, desaturate, darken, brighten) 146 147 # Generate a preview if requested. 148 149 if preview: 150 imp = preview_image(im, half_resolution_preview) 151 pimp = pim.copy() 152 pimp.putdata(imp.getdata()) 153 pimp.save(preview_filename) 154 155 # Generate an output image if requested. 156 157 if make_image: 158 convert_image(im) 159 pim.putdata(im.getdata()) 160 pim.save(output_filename) 161 162 # Verify the output image (which may be loaded) if requested. 163 164 if verify: 165 if no_normal_output: 166 pim = PIL.Image.open(output_filename).convert("RGB") 167 im = SimpleImage(list(pim.getdata()), pim.size) 168 169 result = count_colours(im, 4) 170 if result is not None: 171 y, colours = result 172 print "Image %s: row %d has the following colours: %s" % (output_filename, y, "; ".join([repr(c) for c in colours])) 173 174 # vim: tabstop=4 expandtab shiftwidth=4