kopia lustrzana https://github.com/peterhinch/micropython-font-to-py
Update WRITER.md remove most debug print statements.
rodzic
103a6bae85
commit
081ef12d90
155
writer/WRITER.md
155
writer/WRITER.md
|
@ -48,30 +48,31 @@ Labels and Fields (from nanogui.py).
|
|||
# Contents
|
||||
|
||||
1. [Introduction](./WRITER.md#1-introduction)
|
||||
1.1 [Hardware](./WRITER.md#11-hardware)
|
||||
1.2 [Files](./WRITER.md#11-files)
|
||||
1.3 [Fonts](./WRITER.md#11-fonts)
|
||||
1.1 [Release notes](./WRITER.md#11-release-notes)
|
||||
1.2 [Hardware](./WRITER.md#12-hardware)
|
||||
1.3 [Files](./WRITER.md#13-files)
|
||||
1.4 [Fonts](./WRITER.md#14-fonts)
|
||||
2. [Writer and CWriter classes](./WRITER.md#2-writer-and-cwriter-classes)
|
||||
2.1 [The Writer class](./WRITER.md#21-the-writer-class) For monochrome displays.
|
||||
2.1.1 [Static Method](./WRITER.md#211-static-method)
|
||||
2.1.2.[Constructor](./WRITER.md#212-constructor)
|
||||
2.1.3 [Methods](./WRITER.md#213-methods)
|
||||
2.2 [The CWriter class](./WRITER.md#22-the-cwriter-class) For colour displays
|
||||
and for upside-down rendering.
|
||||
2.2.1 [Static Method](./WRITER.md#221-static-method)
|
||||
2.2.2 [Constructor](./WRITER.md#222-constructor)
|
||||
2.2.3 [Methods](./WRITER.md#223-methods)
|
||||
2.2.4 [A performance boost](./WRITER.md#224-a-performance-boost)
|
||||
3. [Notes](./WRITER.md#4-notes)
|
||||
2.2 [The CWriter class](./WRITER.md#22-the-cwriter-class) For colour displays.
|
||||
2.2.1 [Constructor](./WRITER.md#221-constructor)
|
||||
2.2.2 [Methods](./WRITER.md#222-methods)
|
||||
2.2.3 [A performance boost](./WRITER.md#223-a-performance-boost)
|
||||
3. [Notes](./WRITER.md#3-notes)
|
||||
|
||||
###### [Main README](../README.md)
|
||||
|
||||
# 1. Introduction
|
||||
|
||||
The original `Writer` class was a proof of concept intended to demonstrate
|
||||
rendering, on an SSD1306 OLED display, fonts created by`font_to_py.py`.
|
||||
The module provides a `Writer` class for rendering bitmapped monochrome fonts
|
||||
created by `font_to_py.py`. The `CWriter` class extends this to support color
|
||||
rendering. Rendering is to a `FrameBuffer` instance, e.g. to a display whose
|
||||
driver is subclassed from a `FrameBuffer`.
|
||||
|
||||
This update for practical applications has the following features:
|
||||
The module has the following features:
|
||||
* Genarality: capable of working with any `framebuf` derived driver.
|
||||
* Multiple display operation.
|
||||
* Text display of fixed and variable pitch fonts with wrapping and vertical
|
||||
|
@ -80,29 +81,42 @@ This update for practical applications has the following features:
|
|||
* Tab support.
|
||||
* String metrics to enable right or centre justification.
|
||||
* Inverse (background color on foreground color) display.
|
||||
* Inverted display option.
|
||||
|
||||
Note that these changes have significantly increased code size. On the ESP8266
|
||||
it is likely that `writer.py` will need to be frozen as bytecode. The original
|
||||
very simple version still exists as `writer_minimal.py`.
|
||||
|
||||
## 1.1 Hardware
|
||||
## 1.1 Release Notes
|
||||
|
||||
V0.4.0 Jan 2021
|
||||
Improved handling of the `col_clip` and `wrap` options. Improved accuracy
|
||||
avoids needless word wrapping. The clip option now displays as much of the last
|
||||
visible glyph as possible: formerly a glyph which would not fit in its entirety
|
||||
was discarded.
|
||||
|
||||
The inverted display option has been withdrawn. It added significant code size
|
||||
and was not an optimal solution. Display inversion should be done at the device
|
||||
driver level. Such a solution works for graphics objects and GUI widgets, while
|
||||
the old option only affected rendered text.
|
||||
|
||||
## 1.2 Hardware
|
||||
|
||||
Tests and demos assume a 128*64 SSD1306 OLED display connected via I2C or SPI.
|
||||
Wiring is specified in `ssd1306_setup.py`. Edit this to use a different bus or
|
||||
for a non-Pyboard target.
|
||||
|
||||
## 1.2 Files
|
||||
## 1.3 Files
|
||||
|
||||
1. `writer.py` Supports `Writer` and `CWriter` classes.
|
||||
2. `writer_gui.py` Provides optional GUI objects.
|
||||
3. `ssd1306_setup.py` Hardware initialisation for SSD1306. Requires the
|
||||
2. `ssd1306_setup.py` Hardware initialisation for SSD1306. Requires the
|
||||
official [SSD1306 driver](https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py).
|
||||
4. `writer_demo.py` Demo using a 128*64 SSD1306 OLED display. Import to see
|
||||
3. `writer_demo.py` Demo using a 128*64 SSD1306 OLED display. Import to see
|
||||
usage information.
|
||||
5. `writer_tests.py` Test/demo scripts. Import to see usage information.
|
||||
6. `writer_minimal.py` A minimal version for highly resource constrained
|
||||
4. `writer_tests.py` Test/demo scripts. Import to see usage information.
|
||||
5. `writer_minimal.py` A minimal version for highly resource constrained
|
||||
devices.
|
||||
6. `framebuf_utils.framebuf_utils.mpy` A means of improving rendering speed
|
||||
on color displays. Discussed [in 2.2.3](./WRITER.md#223-a-performance-boost)
|
||||
|
||||
Sample fonts:
|
||||
1. `freesans20.py` Variable pitch font file.
|
||||
|
@ -110,25 +124,24 @@ Sample fonts:
|
|||
3. `font10.py` Smaller variable pitch fonts.
|
||||
4. `font6.py`
|
||||
|
||||
## 1.3 Fonts
|
||||
## 1.4 Fonts
|
||||
|
||||
Python font files should be created using `font-to-py.py` using horizontal
|
||||
mapping (`-x` option). The `-r` option is not required. If RAM is critical
|
||||
fonts may be frozen as bytecode reducing the RAM impact of each font to about
|
||||
340 bytes.
|
||||
340 bytes. This is highly recommended.
|
||||
|
||||
###### [Contents](./WRITER.md#contents)
|
||||
|
||||
# 2. Writer and CWriter classes
|
||||
|
||||
The `Writer` class provides fast rendering to monochrome displays using bit
|
||||
blitting. Most applications will use this class.
|
||||
blitting.
|
||||
|
||||
The `CWriter` class is a subclass of `Writer`. It can optionally support color
|
||||
displays. It provides additional functionality in the form of an upside-down
|
||||
display option. Owing to limitations in the `frmebuf.blit` method the
|
||||
`CWriter` class renders glyphs one pixel at a time; rendering is therefore
|
||||
slower than the `Writer` class.
|
||||
The `CWriter` class is a subclass of `Writer` to support color displays. Owing
|
||||
to limitations in the `frmebuf.blit` method the `CWriter` class renders glyphs
|
||||
one pixel at a time; rendering is therefore slower than the `Writer` class. A
|
||||
substantial improvement is possible. See [2.2.3](./WRITER.md#223-a-performance-boost).
|
||||
|
||||
Multiple screens are supported. On any screen multiple `Writer` or `CWriter`
|
||||
instances may be used, each using a different font. A class variable holds the
|
||||
|
@ -176,10 +189,10 @@ SSD1306 OLED display and the official
|
|||
|
||||
The `Writer` class exposes the following static method:
|
||||
|
||||
1. `set_textpos` Args: `device`,`row=None`, `col=None`. The `device` is the
|
||||
display instance. This method determines where on screen subsequent text is to
|
||||
be rendered. The initial value is (0, 0) - the top left corner. Arguments are
|
||||
in pixels with positive values representing down and right respectively. The
|
||||
1. `set_textpos(device, row=None, col=None)`. The `device` is the display
|
||||
instance. This method determines where on screen subsequent text is to be
|
||||
rendered. The initial value is (0, 0) - the top left corner. Arguments are in
|
||||
pixels with positive values representing down and right respectively. The
|
||||
insertion point defines the top left hand corner of the next character to be
|
||||
output.
|
||||
|
||||
|
@ -199,45 +212,37 @@ This takes the following args:
|
|||
|
||||
### 2.1.3 Methods
|
||||
|
||||
1. `printstring` Args: `string`, `invert=False`. Outputs a text string at the
|
||||
current insertion point. Newline and Tab characters are honoured. If `invert`
|
||||
is `True` the text is output as black on white.
|
||||
2. `height` No args. Returns the font height in pixels.
|
||||
3. `stringlen` Arg: `string`. Returns the length of a string in pixels. Used
|
||||
for right or centre justification.
|
||||
4. `set_clip` Args: `row_clip=None`, `col_clip=None`, `wrap=None`. If
|
||||
`row_clip` and/or `col_clip` are `True`, characters will be clipped if they
|
||||
extend beyond the boundaries of the physical display. If `col_clip` is
|
||||
`False` characters will wrap onto the next line. If `row_clip` is `False` the
|
||||
display will, where necessary, scroll up to ensure the line is rendered. If
|
||||
`wrap` is `True` word-wrapping will be performed, assuming words are separated
|
||||
by spaces.
|
||||
1. `printstring(string, invert=False)`. Renders the string at the current
|
||||
insertion point. Newline and Tab characters are honoured. If `invert` is
|
||||
`True` the text is output with foreground and background colors transposed.
|
||||
2. `height()` Returns the font height in pixels.
|
||||
3. `stringlen(string, oh=False)` Returns the length of a string in pixels.
|
||||
Appications can use this for right or centre justification.
|
||||
The `oh` arg is for internal use. If set, the method returns a `bool`, `True`
|
||||
if the string would overhang the display edge if rendered at the current
|
||||
insertion point.
|
||||
4. `set_clip(row_clip=None, col_clip=None, wrap=None)`. If `row_clip` and/or
|
||||
`col_clip` are `True`, characters will be clipped if they extend beyond the
|
||||
boundaries of the physical display. If `col_clip` is `False` characters will
|
||||
wrap onto the next line. If `row_clip` is `False` the display will, where
|
||||
necessary, scroll up to ensure the line is rendered. If `wrap` is `True`
|
||||
word-wrapping will be performed, assuming words are separated by spaces.
|
||||
If any arg is `None`, that value will be left unchanged.
|
||||
Returns the current values of `row_clip`, `col_clip` and `wrap`.
|
||||
5. `tabsize` Arg `value=None`. If `value` is an integer sets the tab size.
|
||||
Returns the current tab size (initial default is 4). Tabs only work properly
|
||||
with fixed pitch fonts.
|
||||
5. `tabsize(value=None)`. If `value` is an integer sets the tab size. Returns
|
||||
the current tab size (initial default is 4). Tabs only work properly with
|
||||
fixed pitch fonts.
|
||||
|
||||
###### [Contents](./WRITER.md#contents)
|
||||
|
||||
## 2.2 The CWriter class
|
||||
|
||||
This extends the `Writer` class by adding support for upside-down and/or color
|
||||
displays. A color value is an integer whose interpretation is dependent on the
|
||||
display hardware and device driver.
|
||||
This extends the `Writer` class by adding support for color displays. A color
|
||||
value is an integer whose interpretation is dependent on the display hardware
|
||||
and device driver. The Python font file uses single bit pixels. On a color
|
||||
screen these are rendered using foreground and background colors.
|
||||
|
||||
### 2.2.1 Static method
|
||||
|
||||
The following static method is added:
|
||||
1. `invert_display` Args `device`, `value=True`. The `device` is the display
|
||||
instance. If `value` is set, causes text to be rendered upside down. The
|
||||
`set_textpos` method should be called to ensure that text is rendered from the
|
||||
bottom right hand corner (viewing the display in its normal orientation).
|
||||
|
||||
If a display is to be run inverted, this method must be called prior to
|
||||
instantiating a `Writer` for this display.
|
||||
|
||||
### 2.2.2 Constructor
|
||||
### 2.2.1 Constructor
|
||||
|
||||
This takes the following args:
|
||||
1. `device` The hardware device driver instance for the screen in use.
|
||||
|
@ -246,20 +251,20 @@ This takes the following args:
|
|||
4. `bgcolor=None` Background color. If `None` a monochrome display is assumed.
|
||||
5. `verbose=True` If `True` the constructor emits console printout.
|
||||
|
||||
### 2.2.3 Methods
|
||||
### 2.2.2 Methods
|
||||
|
||||
All methods of the base class are supported. Additional method:
|
||||
1. `setcolor` Args: `fgcolor=None`, `bgcolor=None`. Sets the foreground and
|
||||
background colors. If one is `None` that value is left unchanged. If both
|
||||
are `None` the constructor defaults are restored. Constructor defaults are
|
||||
1 and 0 for monochrome displays (`Writer`). Returns foreground
|
||||
and background color values.
|
||||
1. `setcolor(fgcolor=None, bgcolor=None)`. Sets the foreground and background
|
||||
colors. If one is `None` that value is left unchanged. If both are `None` the
|
||||
constructor defaults are restored. Constructor defaults are 1 and 0
|
||||
for monochrome displays (`Writer`). Returns foreground and background color
|
||||
values.
|
||||
|
||||
The `printstring` method works as per the base class except that the string is
|
||||
rendered in foreground color on background color (or reversed if `invert` is
|
||||
`True`).
|
||||
|
||||
### 2.2.4 A performance boost
|
||||
### 2.2.3 A performance boost
|
||||
|
||||
Rendering performance of the `Cwriter` class is slow: owing to limitations in
|
||||
the `framebuf.blit` method the class renders glyphs one pixel at a time. There
|
||||
|
@ -269,8 +274,8 @@ consists of a native C module.
|
|||
On import, `writer.py` attempts to import a module `framebuf_utils`. If this
|
||||
succeeds, glyph rendering will be substantially faster. If the file is not
|
||||
present the class will work using normal rendering. If the file exists but was
|
||||
compiled for a different architecture a warning message will be printed but the
|
||||
class will work using normal rendering.
|
||||
compiled for a different architecture a warning message will be printed. This
|
||||
is a harmless advisory - the code will run using normal rendering.
|
||||
|
||||
The directory `framebuf_utils` contains the source file, the makefile and a
|
||||
version of `framebuf_utils.mpy` for `armv7m` architecture (e.g. Pyboards).
|
||||
|
@ -280,10 +285,6 @@ to specify the `xtensawin` arch and rebuild.
|
|||
It is suggested that moving the appropriate `framebuf_utils.mpy` to the target
|
||||
is only done once the basic operation of an application has been verified.
|
||||
|
||||
The native module does not support the `CWriter.invert_display` option. If this
|
||||
is used, the presence of the native module will have no effect. The module has
|
||||
no effect on the `Writer` class which uses fast rendering by default.
|
||||
|
||||
The module has a `fast_mode` variable which is set `True` on import if the mode
|
||||
was successfully engaged. User code should treat this as read-only.
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
# writer.py Implements the Writer class.
|
||||
# V0.35 Peter Hinch Sept 2020 Fast rendering option for color displays
|
||||
# Handles colour, upside down diplays, word wrap and tab stops
|
||||
# Handles colour, word wrap and tab stops
|
||||
|
||||
# V0.40 Jan 2021 Improved handling of word wrap and line clip. Upside-down
|
||||
# rendering no longer supported: delegate to device driver.
|
||||
# V0.35 Sept 2020 Fast rendering option for color displays
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2019-2020 Peter Hinch
|
||||
|
@ -21,6 +24,8 @@
|
|||
import framebuf
|
||||
from uctypes import bytearray_at, addressof
|
||||
|
||||
__version__ = (0, 4, 0)
|
||||
|
||||
fast_mode = True
|
||||
try:
|
||||
try:
|
||||
|
@ -39,7 +44,6 @@ class DisplayState():
|
|||
def __init__(self):
|
||||
self.text_row = 0
|
||||
self.text_col = 0
|
||||
self.usd = False
|
||||
|
||||
def _get_id(device):
|
||||
if not isinstance(device, framebuf.FrameBuffer):
|
||||
|
@ -60,11 +64,11 @@ class Writer():
|
|||
if row is not None:
|
||||
if row < 0 or row >= device.height:
|
||||
raise ValueError('row is out of range')
|
||||
s.text_row = device.height - 1 - row if s.usd else row
|
||||
s.text_row = row
|
||||
if col is not None:
|
||||
if col < 0 or col >= device.width:
|
||||
raise ValueError('col is out of range')
|
||||
s.text_col = device.width -1 - col if s.usd else col
|
||||
s.text_col = col
|
||||
return s.text_row, s.text_col
|
||||
|
||||
def __init__(self, device, font, verbose=True):
|
||||
|
@ -73,7 +77,6 @@ class Writer():
|
|||
if self.devid not in Writer.state:
|
||||
Writer.state[self.devid] = DisplayState()
|
||||
self.font = font
|
||||
self.usd = Writer.state[self.devid].usd
|
||||
|
||||
# Allow to work with reverse or normal font mapping
|
||||
if font.hmap():
|
||||
|
@ -105,20 +108,12 @@ class Writer():
|
|||
def _newline(self):
|
||||
s = self._getstate()
|
||||
height = self.font.height()
|
||||
if self.usd:
|
||||
s.text_row -= height
|
||||
s.text_col = self.screenwidth - 1
|
||||
margin = s.text_row - height
|
||||
y = 0
|
||||
else:
|
||||
s.text_row += height
|
||||
s.text_col = 0
|
||||
margin = self.screenheight - (s.text_row + height)
|
||||
y = self.screenheight + margin
|
||||
if margin < 0:
|
||||
if not self.row_clip:
|
||||
if self.usd:
|
||||
margin = -margin
|
||||
self.device.scroll(0, margin)
|
||||
self.device.fill_rect(0, y, self.screenwidth, abs(margin), self.bgcolor)
|
||||
s.text_row += margin
|
||||
|
@ -157,7 +152,6 @@ class Writer():
|
|||
if pos > 0:
|
||||
rstr = string[pos + 1:]
|
||||
string = lstr
|
||||
#print("[", string, "] [", lstr, "] [", rstr, "]", pos)
|
||||
|
||||
for char in string:
|
||||
self._printchar(char, invert)
|
||||
|
@ -167,14 +161,12 @@ class Writer():
|
|||
|
||||
def stringlen(self, string, oh=False):
|
||||
sc = self._getstate().text_col # Start column
|
||||
#print('stringlen sc =', sc)
|
||||
wd = self.screenwidth
|
||||
l = 0
|
||||
for char in string[:-1]:
|
||||
_, _, char_width = self.font.get_ch(char)
|
||||
l += char_width
|
||||
if oh and l + sc > wd:
|
||||
print('sl1', string, l + sc, wd)
|
||||
return True # All done. Save time.
|
||||
char = string[-1]
|
||||
_, _, char_width = self.font.get_ch(char)
|
||||
|
@ -182,7 +174,6 @@ class Writer():
|
|||
l += self._truelen(char) # Last char might have blank cols on RHS
|
||||
else:
|
||||
l += char_width # Public method. Return same value as old code.
|
||||
print('sl2', string, l + sc, wd, char, char_width)
|
||||
return l + sc > wd if oh else l
|
||||
|
||||
# Return the printable width of a glyph less any blank columns on RHS
|
||||
|
@ -204,7 +195,7 @@ class Writer():
|
|||
break
|
||||
if mc + 1 == wd:
|
||||
break # All done: no trailing space
|
||||
print('Truelen', char, wd, mc + 1)
|
||||
print('Truelen', char, wd, mc + 1) # TEST
|
||||
return mc + 1
|
||||
|
||||
def _get_char(self, char, recurse):
|
||||
|
@ -228,20 +219,6 @@ class Writer():
|
|||
glyph, char_height, char_width = self.font.get_ch(char)
|
||||
s = self._getstate()
|
||||
np = None # Allow restriction on printable columns
|
||||
if self.usd:
|
||||
if s.text_row - char_height < 0:
|
||||
if self.row_clip:
|
||||
return
|
||||
self._newline()
|
||||
oh = s.text_col - char_width # Amount glyph would overhang edge (-ve)
|
||||
if oh < 0:
|
||||
if self.col_clip or self.wrap:
|
||||
np = char_width + oh # No of printable columns
|
||||
if np <= 0:
|
||||
return
|
||||
else:
|
||||
self._newline()
|
||||
else:
|
||||
if s.text_row + char_height > self.screenheight:
|
||||
if self.row_clip:
|
||||
return
|
||||
|
@ -286,12 +263,6 @@ class Writer():
|
|||
# Writer for colour displays or upside down rendering
|
||||
class CWriter(Writer):
|
||||
|
||||
@staticmethod
|
||||
def invert_display(device, value=True):
|
||||
devid = id(device)
|
||||
if devid not in Writer.state:
|
||||
Writer.state[devid] = DisplayState()
|
||||
Writer.state[devid].usd = value
|
||||
|
||||
def __init__(self, device, font, fgcolor=None, bgcolor=None, verbose=True):
|
||||
super().__init__(device, font, verbose)
|
||||
|
@ -301,9 +272,8 @@ class CWriter(Writer):
|
|||
self.fgcolor = fgcolor
|
||||
self.def_bgcolor = self.bgcolor
|
||||
self.def_fgcolor = self.fgcolor
|
||||
fm = fast_mode and not self.usd
|
||||
self._printchar = self._pchfast if fm else self._pchslow
|
||||
verbose and print('Render {} using fast mode'.format('is' if fm else 'not'))
|
||||
self._printchar = self._pchfast if fast_mode else self._pchslow
|
||||
verbose and print('Render {} using fast mode'.format('is' if fast_mode else 'not'))
|
||||
|
||||
def _pchfast(self, char, invert=False, recurse=False):
|
||||
s = self._getstate()
|
||||
|
@ -333,25 +303,21 @@ class CWriter(Writer):
|
|||
device = self.device
|
||||
fgcolor = self.bgcolor if invert else self.fgcolor
|
||||
bgcolor = self.fgcolor if invert else self.bgcolor
|
||||
usd = self.usd
|
||||
drow = s.text_row # Destination row
|
||||
wcol = s.text_col # Destination column of character start
|
||||
for srow in range(char_height): # Source row
|
||||
for scol in range(clip_width): # Source column
|
||||
# Destination column: add/subtract writer column
|
||||
if usd:
|
||||
dcol = wcol - scol
|
||||
else:
|
||||
# Destination column: add writer column
|
||||
dcol = wcol + scol
|
||||
gbyte, gbit = divmod(scol, 8)
|
||||
if gbit == 0: # Next glyph byte
|
||||
data = self.glyph[srow * gbytes + gbyte]
|
||||
pixel = fgcolor if data & (1 << (7 - gbit)) else bgcolor
|
||||
device.pixel(dcol, drow, pixel)
|
||||
drow += -1 if usd else 1
|
||||
drow += 1
|
||||
if drow >= self.screenheight or drow < 0:
|
||||
break
|
||||
s.text_col += -char_width if usd else char_width
|
||||
s.text_col += char_width
|
||||
self.cpos += 1
|
||||
|
||||
def setcolor(self, fgcolor=None, bgcolor=None):
|
||||
|
|
Ładowanie…
Reference in New Issue