New port numbers
[electrum-nvc.git] / lib / bmp.py
1 # -*- coding: utf-8 -*-\r
2 """\r
3 bmp.py - module for constructing simple BMP graphics files\r
4 \r
5  Permission is hereby granted, free of charge, to any person obtaining\r
6  a copy of this software and associated documentation files (the\r
7  "Software"), to deal in the Software without restriction, including\r
8  without limitation the rights to use, copy, modify, merge, publish,\r
9  distribute, sublicense, and/or sell copies of the Software, and to\r
10  permit persons to whom the Software is furnished to do so, subject to\r
11  the following conditions:\r
12 \r
13  The above copyright notice and this permission notice shall be\r
14  included in all copies or substantial portions of the Software.\r
15 \r
16  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
17  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
18  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
19  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\r
20  CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\r
21  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r
22  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
23 \r
24 """\r
25 __version__ = "0.3"\r
26 __about =  "bmp module, version %s, written by Paul McGuire, October, 2003, updated by Margus Laak, September, 2009" % __version__ \r
27 \r
28 from math import ceil, hypot\r
29 \r
30 \r
31 def shortToString(i):\r
32   hi = (i & 0xff00) >> 8\r
33   lo = i & 0x00ff\r
34   return chr(lo) + chr(hi)\r
35 \r
36 def longToString(i):\r
37   hi = (long(i) & 0x7fff0000) >> 16\r
38   lo = long(i) & 0x0000ffff\r
39   return shortToString(lo) + shortToString(hi)\r
40 \r
41 def long24ToString(i):\r
42   return chr(i & 0xff) + chr(i >> 8 & 0xff) + chr(i >> 16 & 0xff)\r
43 \r
44 def stringToLong(input_string, offset):\r
45   return ord(input_string[offset+3]) << 24 | ord(input_string[offset+2]) << 16 | ord(input_string[offset+1]) << 8 | ord(input_string[offset])\r
46 \r
47 def stringToLong24(input_string, offset):\r
48   return ord(input_string[offset+2]) << 16 | ord(input_string[offset+1]) << 8 | ord(input_string[offset])\r
49 \r
50 class Color(object):\r
51   """class for specifying colors while drawing BitMap elements"""\r
52   __slots__ = [ 'red', 'grn', 'blu' ]\r
53   __shade = 32\r
54   \r
55   def __init__( self, r=0, g=0, b=0 ):\r
56     self.red = r\r
57     self.grn = g\r
58     self.blu = b\r
59 \r
60   def __setattr__(self, name, value):\r
61     if hasattr(self, name):\r
62       raise AttributeError, "Color is immutable"\r
63     else:\r
64       object.__setattr__(self, name, value)\r
65 \r
66   def __str__( self ):\r
67     return "R:%d G:%d B:%d" % (self.red, self.grn, self.blu )\r
68     \r
69   def __hash__( self ):\r
70     return ( ( long(self.blu) ) + \r
71               ( long(self.grn) <<  8 ) + \r
72               ( long(self.red) << 16 ) )\r
73   \r
74   def __eq__( self, other ):\r
75     return (self is other) or (self.toLong == other.toLong)\r
76 \r
77   def lighten( self ):\r
78     return Color( \r
79       min( self.red + Color.__shade, 255), \r
80       min( self.grn + Color.__shade, 255), \r
81       min( self.blu + Color.__shade, 255)  \r
82       )\r
83   \r
84   def darken( self ):\r
85     return Color( \r
86       max( self.red - Color.__shade, 0), \r
87       max( self.grn - Color.__shade, 0), \r
88       max( self.blu - Color.__shade, 0)  \r
89       )\r
90        \r
91   def toLong( self ):\r
92     return self.__hash__()\r
93     \r
94   def fromLong( l ):\r
95     b = l & 0xff\r
96     l = l >> 8\r
97     g = l & 0xff\r
98     l = l >> 8\r
99     r = l & 0xff\r
100     return Color( r, g, b )\r
101   fromLong = staticmethod(fromLong)\r
102 \r
103 # define class constants for common colors\r
104 Color.BLACK    = Color(   0,   0,   0 )\r
105 Color.RED      = Color( 255,   0,   0 )\r
106 Color.GREEN    = Color(   0, 255,   0 )\r
107 Color.BLUE     = Color(   0,   0, 255 )\r
108 Color.CYAN     = Color(   0, 255, 255 )\r
109 Color.MAGENTA  = Color( 255,   0, 255 )\r
110 Color.YELLOW   = Color( 255, 255,   0 )\r
111 Color.WHITE    = Color( 255, 255, 255 )\r
112 Color.DKRED    = Color( 128,   0,   0 )\r
113 Color.DKGREEN  = Color(   0, 128,   0 )\r
114 Color.DKBLUE   = Color(   0,   0, 128 )\r
115 Color.TEAL     = Color(   0, 128, 128 )\r
116 Color.PURPLE   = Color( 128,   0, 128 )\r
117 Color.BROWN    = Color( 128, 128,   0 )\r
118 Color.GRAY     = Color( 128, 128, 128 )\r
119 \r
120 \r
121 class BitMap(object):\r
122   """class for drawing and saving simple Windows bitmap files"""\r
123   \r
124   LINE_SOLID  = 0\r
125   LINE_DASHED = 1\r
126   LINE_DOTTED = 2\r
127   LINE_DOT_DASH=3\r
128   _DASH_LEN = 12.0\r
129   _DOT_LEN = 6.0\r
130   _DOT_DASH_LEN = _DOT_LEN + _DASH_LEN\r
131   \r
132   def __init__( self, width, height, \r
133                  bkgd = Color.WHITE, frgd = Color.BLACK ):\r
134     self.wd = int( ceil(width) )\r
135     self.ht = int( ceil(height) )\r
136     self.bgcolor = 0\r
137     self.fgcolor = 1\r
138     self.palette = []\r
139     self.palette.append( bkgd.toLong() )\r
140     self.palette.append( frgd.toLong() )\r
141     self.currentPen = self.fgcolor\r
142 \r
143     tmparray = [ self.bgcolor ] * self.wd\r
144     self.bitarray = [ tmparray[:] for i in range( self.ht ) ]\r
145     self.currentPen = 1\r
146     \r
147 \r
148   def plotPoint( self, x, y ):\r
149     if ( 0 <= x < self.wd and 0 <= y < self.ht ):\r
150       x = int(x)\r
151       y = int(y)\r
152       self.bitarray[y][x] = self.currentPen\r
153       \r
154 \r
155   def _saveBitMapNoCompression( self ):\r
156     line_padding = (4 - (self.wd % 4)) % 4\r
157     \r
158     # write bitmap header\r
159     _bitmap = "BM"\r
160     _bitmap += longToString( 54 + self.ht*(self.wd*3 + line_padding) )   # DWORD size in bytes of the file\r
161     _bitmap += longToString( 0 )    # DWORD 0\r
162     _bitmap += longToString( 54  )\r
163     _bitmap += longToString( 40 )    # DWORD header size = 40\r
164     _bitmap += longToString( self.wd )    # DWORD image width\r
165     _bitmap += longToString( self.ht )    # DWORD image height\r
166     _bitmap += shortToString( 1 )    # WORD planes = 1\r
167     _bitmap += shortToString( 24 )    # WORD bits per pixel = 8\r
168     _bitmap += longToString( 0 )    # DWORD compression = 0\r
169     _bitmap += longToString( self.ht * (self.wd * 3 + line_padding) )    # DWORD sizeimage = size in bytes of the bitmap = width * height\r
170     _bitmap += longToString( 0 )    # DWORD horiz pixels per meter (?)\r
171     _bitmap += longToString( 0 )    # DWORD ver pixels per meter (?)\r
172     _bitmap += longToString( 0 )    # DWORD number of colors used = 256\r
173     _bitmap += longToString( 0 )    # DWORD number of "import colors = len( self.palette )\r
174 \r
175     # write pixels\r
176     self.bitarray.reverse()\r
177     for row in self.bitarray:\r
178       for pixel in row:\r
179         c = self.palette[pixel]\r
180         _bitmap += long24ToString(c)\r
181       for i in range(line_padding):\r
182         _bitmap += chr( 0 )\r
183 \r
184     return _bitmap\r
185 \r
186     \r
187     \r
188   def saveFile( self, filename):\r
189     _b = self._saveBitMapNoCompression( )\r
190     \r
191     f = file(filename, 'wb')\r
192     f.write(_b)\r
193     f.close()\r
194   \r
195 \r
196 def save_qrcode(qr, filename):\r
197     matrix = qr.get_matrix()\r
198     k = len(matrix)\r
199     bitmap = BitMap( (k+2)*8, (k+2)*8 )\r
200     bitmap.bitarray = []\r
201     for r in range(k+2):\r
202         tmparray = [ 0 ] * (k+2)*8\r
203 \r
204         if 0 < r < k+1:\r
205             for c in range(k):\r
206                 if matrix[r-1][c]:\r
207                     tmparray[ (1+c)*8:(2+c)*8] = [1]*8\r
208 \r
209         for i in range(8):\r
210             bitmap.bitarray.append( tmparray[:] )\r
211 \r
212     bitmap.saveFile( filename )\r
213 \r
214   \r
215     \r
216 if __name__ == "__main__":\r
217   \r
218   bmp = BitMap( 10, 10 )\r
219   bmp.plotPoint( 5, 5 )\r
220   bmp.plotPoint( 0, 0 )\r
221   bmp.saveFile( "test.bmp" )\r
222 \r