Skip to content

Commit 72d80d6

Browse files
authored
Merge pull request #4 from ladyada/master
Fix rectangle size, add font support
2 parents 4ce2168 + bb69581 commit 72d80d6

File tree

4 files changed

+392
-6
lines changed

4 files changed

+392
-6
lines changed

adafruit_framebuf.py

+84-6
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,13 @@
4444
__version__ = "0.0.0-auto.0"
4545
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_framebuf.git"
4646

47+
import struct
48+
4749
# Framebuf format constants:
4850
MVLSB = 0 # Single bit displays (like SSD1306 OLED)
4951
RGB565 = 1 # 16-bit color displays
5052
GS4_HMSB = 2 # Unimplemented!
5153

52-
5354
class MVLSBFormat:
5455
"""MVLSBFormat"""
5556
@staticmethod
@@ -134,6 +135,7 @@ def __init__(self, buf, width, height, buf_format=MVLSB, stride=None):
134135
self.width = width
135136
self.height = height
136137
self.stride = stride
138+
self._font = None
137139
if self.stride is None:
138140
self.stride = width
139141
if buf_format == MVLSB:
@@ -183,9 +185,9 @@ def rect(self, x, y, width, height, color):
183185
a 1 pixel outline."""
184186
# pylint: disable=too-many-arguments
185187
self.fill_rect(x, y, width, 1, color)
186-
self.fill_rect(x, y + height, width, 1, color)
188+
self.fill_rect(x, y + height-1, width, 1, color)
187189
self.fill_rect(x, y, 1, height, color)
188-
self.fill_rect(x + width, y, 1, height, color)
190+
self.fill_rect(x + width - 1, y, 1, height, color)
189191

190192
def line(self, x_0, y_0, x_1, y_1, color):
191193
# pylint: disable=too-many-arguments
@@ -223,9 +225,85 @@ def scroll(self):
223225
"""scroll is not yet implemented"""
224226
raise NotImplementedError()
225227

226-
def text(self):
227-
"""text is not yet implemented"""
228-
raise NotImplementedError()
228+
def text(self, string, x, y, color, *,
229+
font_name="font5x8.bin"):
230+
"""Write an ascii string to location (x, y) as the bottom left corner,
231+
in a given color, in left-to-right fashion.
232+
Does not handle text wrapping. You can also pass in a font_name, but
233+
this has to be generated as a special binary format and placed in the
234+
working directory of the main script (so, probably /)"""
235+
if not self._font or self._font.font_name != font_name:
236+
# load the font!
237+
self._font = BitmapFont()
238+
w = self._font.font_width
239+
for i, char in enumerate(string):
240+
self._font.draw_char(char,
241+
x + (i * (w + 1)),
242+
y, self, color)
243+
244+
# MicroPython basic bitmap font renderer.
245+
# Author: Tony DiCola
246+
# License: MIT License (https://opensource.org/licenses/MIT)
247+
class BitmapFont:
248+
"""A helper class to read binary font tiles and 'seek' through them as a
249+
file to display in a framebuffer. We use file access so we dont waste 1KB
250+
of RAM on a font!"""
251+
def __init__(self, font_name='font5x8.bin'):
252+
# Specify the drawing area width and height, and the pixel function to
253+
# call when drawing pixels (should take an x and y param at least).
254+
# Optionally specify font_name to override the font file to use (default
255+
# is font5x8.bin). The font format is a binary file with the following
256+
# format:
257+
# - 1 unsigned byte: font character width in pixels
258+
# - 1 unsigned byte: font character height in pixels
259+
# - x bytes: font data, in ASCII order covering all 255 characters.
260+
# Each character should have a byte for each pixel column of
261+
# data (i.e. a 5x8 font has 5 bytes per character).
262+
self.font_name = font_name
263+
264+
# Open the font file and grab the character width and height values.
265+
# Note that only fonts up to 8 pixels tall are currently supported.
266+
try:
267+
self._font = open(self.font_name, 'rb')
268+
except OSError:
269+
print("Could not find font file", font_name)
270+
raise
271+
self.font_width, self.font_height = struct.unpack('BB', self._font.read(2))
272+
273+
def deinit(self):
274+
"""Close the font file as cleanup."""
275+
self._font.close()
276+
277+
def __enter__(self):
278+
"""Initialize/open the font file"""
279+
self.__init__()
280+
return self
281+
282+
def __exit__(self, exception_type, exception_value, traceback):
283+
"""cleanup on exit"""
284+
self.deinit()
285+
286+
def draw_char(self, char, x, y, framebuffer, color):
287+
# pylint: disable=too-many-arguments
288+
"""Draw one character at position (x,y) to a framebuffer in a given color"""
289+
# Don't draw the character if it will be clipped off the visible area.
290+
if x < -self.font_width or x >= framebuffer.width or \
291+
y < -self.font_height or y >= framebuffer.height:
292+
return
293+
# Go through each column of the character.
294+
for char_x in range(self.font_width):
295+
# Grab the byte for the current column of font data.
296+
self._font.seek(2 + (ord(char) * self.font_width) + char_x)
297+
line = struct.unpack('B', self._font.read(1))[0]
298+
# Go through each row in the column byte.
299+
for char_y in range(self.font_height):
300+
# Draw a pixel for each bit that's flipped on.
301+
if (line >> char_y) & 0x1:
302+
framebuffer.pixel(x + char_x, y + char_y, color)
303+
304+
def width(self, text):
305+
"""Return the pixel width of the specified text message."""
306+
return len(text) * (self.font_width + 1)
229307

230308

231309
class FrameBuffer1(FrameBuffer): # pylint: disable=abstract-method

examples/font5x8.bin

1.25 KB
Binary file not shown.

examples/framebuf_simpletest.py

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import adafruit_framebuf
2+
3+
print("framebuf test will draw to the REPL")
4+
5+
WIDTH = 32
6+
HEIGHT = 8
7+
8+
buffer = bytearray(round(WIDTH * HEIGHT / 8))
9+
fb = adafruit_framebuf.FrameBuffer(buffer, WIDTH, HEIGHT,
10+
buf_format=adafruit_framebuf.MVLSB)
11+
12+
# Ascii printer for very small framebufs!
13+
def print_buffer(the_fb):
14+
print("." * (the_fb.width+2))
15+
for y in range(the_fb.height):
16+
print(".", end='')
17+
for x in range(the_fb.width):
18+
if fb.pixel(x, y):
19+
print("*", end='')
20+
else:
21+
print(" ", end='')
22+
print(".")
23+
print("." * (the_fb.width+2))
24+
25+
print("Shapes test: ")
26+
fb.pixel(3, 5, True)
27+
fb.rect(0, 0, fb.width, fb.height, True)
28+
fb.line(1, 1, fb.width-2, fb.height-2, True)
29+
fb.fill_rect(25, 2, 2, 2, True)
30+
print_buffer(fb)
31+
32+
print("Text test: ")
33+
# empty
34+
fb.fill_rect(0, 0, WIDTH, HEIGHT, False)
35+
# write some text
36+
fb.text("hello", 0, 0, True)
37+
print_buffer(fb)

0 commit comments

Comments
 (0)