tools/un-fuzzy.py |
| 1 | #!/usr/bin/env python |
| 2 | # |
| 3 | # This tool depends on the "pygame" package. |
| 4 | # |
| 5 | # The Ben NanoNote has a "delta" arrangement of the pixels on its LCD: |
| 6 | # |
| 7 | # even rows: R G B|R G B|.. |
| 8 | # odd rows: G B R|G B R|.. |
| 9 | # |
| 10 | # Note that the odd-numbered rows are shifted 1/6th of pixel width left. |
| 11 | # |
| 12 | # A white pixel at (0,1) will appear shifted 1/6th of a pixel left relative to |
| 13 | # a white pixel at (0,0) resulting in jaggies on vertical lines. |
| 14 | # |
| 15 | # This tool processes a sheet of 8x8 font glyphs into a sheet of 4x8 font |
| 16 | # glyphs using individual green and magenta "pixels" to achieve twice the |
| 17 | # horizontal resolution. It also compensates for the unusual screen on the |
| 18 | # NanoNote. |
| 19 | # |
| 20 | # NOTE: On the even rows, a single pixel is discarded that would have been made |
| 21 | # up of the red component of the first column along with the blue component of |
| 22 | # the last column. On odds rows, the green component of the first column is |
| 23 | # discarded. This means that last column of the source image is ignored. |
| 24 | # |
| 25 | # Example usage: |
| 26 | # |
| 27 | # un-fuzzy.py 8x8-glyphs.png /tmp/4x8-glyphs.tga |
| 28 | # |
| 29 | # Then use GIMP to convert the .tga file to a .pnm file. Imagemagick seems to |
| 30 | # create P3 .pnm files yet setfont2 can only load P6 .pnm files presently. |
| 31 | # |
| 32 | |
| 33 | import sys |
| 34 | import pygame |
| 35 | |
| 36 | |
| 37 | # Check and grab the command line arguments |
| 38 | if len( sys.argv) < 3: |
| 39 | print "Use: %s source-image-file target-image-file(BMP|TGA) [grid]" % sys.argv[0] |
| 40 | sys.exit( 1) |
| 41 | |
| 42 | path_to_source_file = sys.argv[ 1] |
| 43 | path_to_target_file = sys.argv[ 2] |
| 44 | grid = ( 4 == len( sys.argv)) |
| 45 | |
| 46 | # Load the source image and create the target image with the same number of |
| 47 | # rows but half the number of columns |
| 48 | source_image = pygame.image.load( path_to_source_file) |
| 49 | target_image = pygame.Surface( (source_image.get_width()/2, source_image.get_height())) |
| 50 | |
| 51 | |
| 52 | class Colour: |
| 53 | def __init__( self): |
| 54 | self.red = 0 |
| 55 | self.green = 0 |
| 56 | self.blue = 0 |
| 57 | |
| 58 | def isnt_black( self): |
| 59 | return self.red != 0 or self.green != 0 or self.blue != 0 |
| 60 | |
| 61 | def __repr__( self): |
| 62 | return "(%i,%i,%i)" % ( self.red, self.green, self.blue) |
| 63 | |
| 64 | |
| 65 | def update_pixel( self, location, colour): |
| 66 | current_state = list( self.get_at( location)) |
| 67 | # NOTE that the new luminances are logical-ORed with the current state of |
| 68 | # the pixels since no component should be added to twice, which means that |
| 69 | # no overflow of component values should be possible |
| 70 | current_state[ 0] |= colour.red |
| 71 | current_state[ 1] |= colour.green |
| 72 | current_state[ 2] |= colour.blue |
| 73 | self.set_at( location, current_state) |
| 74 | |
| 75 | |
| 76 | for row in range( source_image.get_height()): |
| 77 | for column in range( source_image.get_width() - 1): |
| 78 | pixel = source_image.get_at( (column, row)) |
| 79 | # "pixel" should be monochrome |
| 80 | red = pixel[ 0] |
| 81 | green = pixel[ 1] |
| 82 | blue = pixel[ 2] |
| 83 | if red != green or green != blue: |
| 84 | raise Exception("not monochrome at (%i,%i)"%( column, row)) |
| 85 | |
| 86 | luminance = red |
| 87 | |
| 88 | # The pixel at ( x, y) in the target image encodes two virtual pixels: one |
| 89 | # from the green component and one from the magenta component made by |
| 90 | # combining the blue component and the red component of the pixel |
| 91 | # immediately to the right of this pixel |
| 92 | # For odd-numbered rows the virtual pixels are: |
| 93 | # 1) magenta made from blue and red |
| 94 | # 2) the green component of the pixel to the right |
| 95 | |
| 96 | # The luminance of the pixel at ( x, y) in the source image is encoded in |
| 97 | # different ways depending whether the row is odd or even-numbered and |
| 98 | # whether the column is odd or even-numbered |
| 99 | |
| 100 | left = Colour() |
| 101 | right = Colour() |
| 102 | |
| 103 | # If preparing SubLCD for a grid-based LCD rather than a Delta LCD.. |
| 104 | if grid: |
| 105 | if 0 == column & 1: |
| 106 | # The luminance of this pixel is provided by the green component of the |
| 107 | # pixel at ( x, y) |
| 108 | left.green = luminance |
| 109 | else: |
| 110 | # The luminance of this pixel is provided by the magenta component that |
| 111 | # is distributed across two pixels ( the blue component of the pixel at |
| 112 | # ( x, y) and the red component of the pixel at ( x+1, y)) |
| 113 | left.blue = luminance |
| 114 | right.red = luminance |
| 115 | else: |
| 116 | # If this row is even-numbered.. |
| 117 | if 0 == row & 1: |
| 118 | # If this column is even-numbered.. |
| 119 | if 0 == column & 1: |
| 120 | # The luminance of this pixel is provided by the green component of the |
| 121 | # pixel at ( x, y) |
| 122 | left.green = luminance |
| 123 | else: |
| 124 | # The luminance of this pixel is provided by the magenta component that |
| 125 | # is distributed across two pixels ( the blue component of the pixel at |
| 126 | # ( x, y) and the red component of the pixel at ( x+1, y)) |
| 127 | left.blue = luminance |
| 128 | right.red = luminance |
| 129 | else: |
| 130 | if 0 == column & 1: |
| 131 | # The luminance of this pixel is provided by the magenta component ( |
| 132 | # the sum of the red and blue components) of the pixel at ( x, y) |
| 133 | left.red = luminance |
| 134 | left.blue = luminance |
| 135 | else: |
| 136 | # The luminance of this pixel is provided by the green component of the |
| 137 | # pixel at ( x+1, y) |
| 138 | right.green = luminance |
| 139 | update_pixel( target_image, (column/2,row), left) |
| 140 | #print repr((column,row)) |
| 141 | #print "L"+repr(left) |
| 142 | #print "R"+repr(right) |
| 143 | if right.isnt_black(): |
| 144 | update_pixel( target_image, (column/2+1,row), right) |
| 145 | |
| 146 | pygame.image.save( target_image, path_to_target_file) |
| 147 | |