0

New GFX for CircuitPython
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

New GFX for CircuitPython

by johncblacker on Tue Oct 09, 2018 8:25 am

I have created a circuitpython version of the GFX library. With my module you are able to display text (using either the 5X8 default font or CUSTOM converted TTF fonts), draw triangles, rectangles, circles both open and filled. Also, you can rotate the display to any of 4 different positions, fill the screen, etc. The package is on GitHub as CircuitPython-GFX and is in it's beta stage, but is quite functional. I'm putting together a comprehensive "graphicstest" program and it will be ready in a day or two. Documentation is available, along with a new version of the fontconvert program that output's converted fonts in python format (.py) files. I've included the mpy-cross.exe program for use on Windows and a VS2010 project used to build the fontconvert program using visual studio 2010. Lastly, included is the freetype-2.3.5-1 package which is used when building the new fontconvert (much trouble finding the right binaries to do the build). I've completed quite a bit of testing, including using internal as well as custom fonts. Right now I'm doing my work using an Arial 12pt italic font on the ILI9341 display and am quite pleased with the results. CHECK IT OUT! Let me know of any issues and problems. This package replaces the use of the bitmapfont.py program as it extends that functionality significantly!

johncblacker
 
Posts: 75
Joined: Mon Aug 27, 2018 1:45 pm

Re: New GFX for CircuitPython

by jerryn on Tue Oct 09, 2018 9:16 pm

Thanks for posting this. I’m looking forward to giving it a try.

jerryn
 
Posts: 816
Joined: Sat Sep 14, 2013 9:05 am

Re: New GFX for CircuitPython

by johncblacker on Wed Oct 10, 2018 9:34 am

Just so you know...I've run into a few issues with behavior after rotating the screen and am tryinig to devise fixes. First issue which I had to solve was that after the screen was rotated, the "fill()" function wasn't working properly - wasn't reflecting the new height/width in landscape modes. Since the "fill()" function was provided by subclasses that I did not modify, the width and height weren't getting picked up by the subclass/module that actually performed the "fill()" and so I had to pull out ILI9341.py and rgb.py out of the Adafruit_rgb_display package and added a couple functions to rgb.py - "newWidth()" and "newHeight()" - that solved that problem at minimal cost. The only reason to pull out ILI9341 was to change it's import statement to load the stand-alone rgb.py instead of getting the one out of the Adafruit… library.
The second problem is a bit more complicated. It has to do with what happens when custom text wraps the bottom of the screen. Several things factor into the problem:
1.) the baseline for TTF/Freetype fonts is at the bottom left, not the top left of the characters boundary box;
2.) characters can extend lower than other characters and when you're wrapping back to the top, you really don't know what's up there, so you have to erase a larger area
than desireable - or - erase the entire screen. I'm leaning towards adding another setting, something like "wrapErase" which, if set, would cause the entire screen to be erased
following a wrap on "y." If "wrapErase" is not set to True, then I'll draw a filled rectangle (fill with background color) a size large enough to encompass any characters that happen
to be written at the top of the screen, and each subsequent "x" wrap would have to do the same thing. The effect wouldn't be ideal, since in the process of erasing it's entirely
possible to erase only part of a line of characters; but, hey, that'll be the user's option. I don't anticipate very many people trying to write screenfuls of text, but, you never know. The other option is to incorporate "paging" but that costs a lot of memory. Otherwise, I'm still getting the bugs out of my "graphicstest" example program, so what's in the repository at this time (graphicstest) doesn't work, but the demo program does work. I really did this whole thing for my own use but decided to put it out there for others to use as well.
Let me know what you uncover, problem-wise, and your thoughts overall.

johncblacker
 
Posts: 75
Joined: Mon Aug 27, 2018 1:45 pm

Re: New GFX for CircuitPython

by jerryn on Wed Oct 10, 2018 12:06 pm

Thanks for the warnings -- no worries. Off to a good start -- I have your demo (with all the extra parts uncommented) running on a feather M4 express an my 2.4inch TFT featherwing. So far so good. Now I'll read you API and play with it more as time allows.

jerryn
 
Posts: 816
Joined: Sat Sep 14, 2013 9:05 am

Re: New GFX for CircuitPython

by johncblacker on Thu Oct 11, 2018 9:24 am

That's good news...I've decided to go with an additional setting to control what happens when text overflows in the y direction: either erase the screen or just erase space at the top for the new line. Also, I've got only one small change to make to the "graphicstest.py" program and it will be put on GitHub along with all other changes I've made. I did change the fontconvert module so that I can pick up an additional freetype variable: "maxyadvance" and use that to increment the y value when the user issues the setCursor() function. That way, users can continue to put in something like (0,0) and the GFX interface will add the appropriate offset from the top to start outputting text. All in all, I'm pretty pleased with how it's all turned out...the only disappointing part is that since it's written in Python and using the slower SPI interface, it's slow to do things like draw large filled figures; but, that's nothing I can easily fix at this time.
Let me know how it goes and please pickup the new changes this afternoon, they'll be ready by then.
I appreciate your taking the time to review this.

johncblacker
 
Posts: 75
Joined: Mon Aug 27, 2018 1:45 pm

Re: New GFX for CircuitPython

by johncblacker on Thu Oct 11, 2018 9:49 am

I just had a (I think) good idea...one of the major slow parts of my API is drawing filled objects, especially large rectangles. I was thinking this morning that the filled rectangle is nothing more than a portion of a filled screen. The fill() function is quite fast, yet having to draw filled rectangles a pixel line at a time is painfully slow. So, what if there is a way to piggy back on what the fill() function does, except only for a portion of the screen? I know that fill() does use the _width, _length values, so those could be modified to do a partial fill with the color being the fill color of the rectangle. I'm going to take a look-see at this to determine if it's a feasible way to speed things up.

johncblacker
 
Posts: 75
Joined: Mon Aug 27, 2018 1:45 pm

Re: New GFX for CircuitPython

by johncblacker on Fri Oct 12, 2018 1:01 pm

Finished important changes to CPtGFX.py and removed the original demo.py program in favor of the graphicstest.py program. Also - THIS IS VERY IMPORTANT TO NOTE - changed the list of parameters used to instantiate the CPtGFX class...removed several and added 1 (dispobj=display which is the pointer to the instantiated ili9341 object). Took advantage of some low lever ili9341 routines to speed up many operations, particularly those that involve drawing horizontal and vertical lines (many of the fill functions use these). The result is that there's now a fully functional graphicstest.py which mimics the one found in the original Adafruit ili9341 example library (graphicstest.ino). Sorry for the parameter list changes, but things work much better the new way and require less code. Also, compiled the CPtGFX.py, ili9341.py and rgb.py modules for storage savings.
Please check it out! Let me know what y'all think about what I've put together. Thanks in advance!

johncblacker
 
Posts: 75
Joined: Mon Aug 27, 2018 1:45 pm

Re: New GFX for CircuitPython

by jerryn on Fri Oct 12, 2018 1:33 pm

Thanks for the updates -- I am getting an error with graphicstest:

Code: Select all | TOGGLE FULL SIZE
>>> import graphicstest
<bound_method>
Power Mode:             bytearray(b'')
Memory  access control: bytearray(b'')
Pixel format:           bytearray(b'')
Image format:           bytearray(b'')
Self diag flag:         bytearray(b'')
Let the benchmark begin at:        1025.814891
Screen fill                           
1.61963
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "graphicstest.py", line 336, in <module>
  File "graphicstest.py", line 74, in testText
  File "CPtGFX.py", line 356, in text
  File "CPtGFX.py", line 219, in draw_char
AttributeError: 'GFX' object has no attribute 'cd'
>>>


it appears to be at this line (219) in CPtGFX.py
Code: Select all | TOGGLE FULL SIZE
 
    218                     if (bitmapbyte & 0x80):
    219                         if (self.cd  == 1):
    220                             self._pixel( x + xo + xx , y + yo + yy , self.TextColor)
   

jerryn
 
Posts: 816
Joined: Sat Sep 14, 2013 9:05 am

Re: New GFX for CircuitPython

by johncblacker on Fri Oct 12, 2018 3:03 pm

Looking at it now...should have a resolution within the next 15 minutes.

johncblacker
 
Posts: 75
Joined: Mon Aug 27, 2018 1:45 pm

Re: New GFX for CircuitPython

by johncblacker on Fri Oct 12, 2018 3:14 pm

OK, is should read: "if self.TextSize == 1"
I must have been in a hurry to get some new mods out and somehow my cursor ended up in the wrong place.
Corrected and committed to github

johncblacker
 
Posts: 75
Joined: Mon Aug 27, 2018 1:45 pm

Re: New GFX for CircuitPython

by jerryn on Fri Oct 12, 2018 3:49 pm

Thanks - much better! Ran graphicstest on a Feather M4 Espress with 2.4 inch TFT Featherwing - ran to completion. Notable speed increase in the fills!
Code: Select all | TOGGLE FULL SIZE

Press any key to enter the REPL. Use CTRL-D to reload.
Adafruit CircuitPython 4.0.0-alpha.1-86-gdf80ad8e6 on 2018-10-10; Adafruit Feather M4 Express with samd51j19
>>>
>>>
>>>
>>> import graphicstest
<bound_method>
Power Mode:             bytearray(b'')
Memory  access control: bytearray(b'')
Pixel format:           bytearray(b'')
Image format:           bytearray(b'')
Self diag flag:         bytearray(b'')
Let the benchmark begin at:        337.576890
Screen fill
1.61401
Lines
1114.01
Horiz/Vert Lines
0.698975
Rectangles (outline)
0.803955
Rectangles (filled)
1.35303
Circles (filled )
33.7051
Circles (outline)
34.0869
Triangles (outline)
18.717
Triangles (filled)
0
Rounded rects (outline)
11.8071
Rounded rects (filled)
5.21606
Done!


jerryn
 
Posts: 816
Joined: Sat Sep 14, 2013 9:05 am

Re: New GFX for CircuitPython

by johncblacker on Fri Oct 12, 2018 4:18 pm

Great news...fixed another problem that caused a failure to change fonts after program started. For example, create GFX class with wingding12pt7b and then switch to scriptbl12pt7b.
Prior to fix that wouldn't work...after fix - WORKED! So you can switch fonts "on-the-fly" so-to-speak...
New version of CPtGFX.py and CPtGFX.py and a couple more converted fonts: scriptbl12pt7b and wingding12pt7b!

johncblacker
 
Posts: 75
Joined: Mon Aug 27, 2018 1:45 pm

Re: New GFX for CircuitPython

by johncblacker on Sun Oct 14, 2018 11:22 am

Please note: I tried today to create an ariali24pt7b.py font file and then use it as the default font when creating the GFX object and received a memory error. Here is some relevant information. I did a gc.collect() then a print(gc.mem_free()), following that I did a print(micropython.mem_info()) then tried to create the GFX object. Here's the output:
74784
stack: 548 out of 58412
GC: total: 128064, used: 53280, free: 74784
No. of 1-blocks: 331, 2-blocks: 102, max blk sz: 1124, max free sz: 3120
None
Traceback (most recent call last):
File "code.py", line 73, in <module>
File "CPtGFX.py", line 89, in __init__
File "CPtGFX.py", line 106, in setFont
File "ariali24pt7b.py", line 3, in __init__
MemoryError: memory allocation failed, allocating 34964 bytes

The ariali24pt7b module itself is about 9k. I see the max free sz:3120 above which is probably what's wrong. I changed to import ariali12pt7b instead, took out the import scriptbl12pt7b so that I'd get a failure issuing gfx.setFont(scriptbl12pt7b) and this is my output:
79984
stack: 548 out of 58412
GC: total: 128064, used: 48080, free: 79984
No. of 1-blocks: 332, 2-blocks: 197, max blk sz: 567, max free sz: 3128
None
Power Mode: bytearray(b'')
Memory access control: bytearray(b'')
Pixel format: bytearray(b'')
Image format: bytearray(b'')
Self diag flag: bytearray(b'')
Traceback (most recent call last):
File "code.py", line 347, in <module>
NameError: name 'scriptbl12pt7b' is not defined

So that time it loaded OK, ariali12pt7b.py is about 2900 bytes...max free sz: 3128 so I guess that's why it worked? One suggestion I see on the web is to issue a gc.collect() after each import...I'll try that but am not hopeful. At this point, looks like 24pt fonts aren't going to load... :-(

Put a gc.collect() after each import, the display= statement, the gfx = statement and still received the memory error. I don't know why it's trying to load 34964 since the arial24pt7b.mpy file is only about 19k.
If anyone has any words of wisdom...I'm all ears!
Here's a new output with gc.mem_free() and micropython.mem_info() following each of the statements up to trying to instantiate the GFX class:
stack: 548 out of 58412
GC: total: 128064, used: 8448, free: 119616
No. of 1-blocks: 52, 2-blocks: 16, max blk sz: 114, max free sz: 5637
None
119616
stack: 548 out of 58412
GC: total: 128064, used: 26896, free: 101168
No. of 1-blocks: 60, 2-blocks: 17, max blk sz: 1124, max free sz: 4484
None
101168
stack: 548 out of 58412
GC: total: 128064, used: 42144, free: 85920
No. of 1-blocks: 209, 2-blocks: 80, max blk sz: 1124, max free sz: 3543
None
85920
stack: 548 out of 58412
GC: total: 128064, used: 47152, free: 80912
No. of 1-blocks: 320, 2-blocks: 99, max blk sz: 1124, max free sz: 3322
None
80912
stack: 548 out of 58412
GC: total: 128064, used: 47312, free: 80752
No. of 1-blocks: 324, 2-blocks: 101, max blk sz: 1124, max free sz: 3317
None
80752
stack: 548 out of 58412
GC: total: 128064, used: 47296, free: 80768
No. of 1-blocks: 323, 2-blocks: 101, max blk sz: 1124, max free sz: 3317
None
80768
stack: 548 out of 58412
GC: total: 128064, used: 47312, free: 80752
No. of 1-blocks: 323, 2-blocks: 101, max blk sz: 1124, max free sz: 3317
None
80752
stack: 548 out of 58412
GC: total: 128064, used: 47312, free: 80752
No. of 1-blocks: 323, 2-blocks: 101, max blk sz: 1124, max free sz: 3317
None
80752
80736
stack: 548 out of 58412
GC: total: 128064, used: 47328, free: 80736
No. of 1-blocks: 323, 2-blocks: 101, max blk sz: 1124, max free sz: 3317
None
80528
stack: 548 out of 58412
GC: total: 128064, used: 47536, free: 80528
No. of 1-blocks: 323, 2-blocks: 101, max blk sz: 1124, max free sz: 3317
None
stack: 548 out of 58412
GC: total: 128064, used: 47728, free: 80336
No. of 1-blocks: 327, 2-blocks: 102, max blk sz: 1124, max free sz: 3317
None
80336
Traceback (most recent call last):
File "code.py", line 102, in <module>
File "CPtGFX.py", line 89, in __init__
File "CPtGFX.py", line 106, in setFont
File "ariali24pt7b.py", line 3, in __init__
MemoryError: memory allocation failed, allocating 34964 bytes

johncblacker
 
Posts: 75
Joined: Mon Aug 27, 2018 1:45 pm

Re: New GFX for CircuitPython

by johncblacker on Sun Oct 14, 2018 3:00 pm

Look for new changes coming within the next week or so. I've decided to change the structure of the fonts, since large fonts take up a bunch of memory which is in short supply. I've decided to take the bitmap portion of the font file and convert it to a .bin file and preserve the remainder of the font file (glyphs and several font related constants). The result is going to be a small font file with an accompanying .bin file which I'll open in the API and using the glyph information, seek to the correct bytes for the bitmap data for a particular character (I'll do a seek, etc.) This will get around the memory problem that occurs with large fonts or having multiple font files from which to write characters. The net result of this is a bunch of work on my part, but little on the users part - they'll have to import <fontfilename> which will be the .py file containing the glyphs and font constants and I'll take that name and append .bin which will be the name of the <fontbitmap>.bin containing bitmap data. Performance isn't going to be much different. I've got the conversion program written. For example, the ariali12pt7b.py file is 21KB, the ariali12pt7b.mpy file is 7KB, the ariali12pt7b.bin file is 3KB! Since there is a second step after the fontconvert program outputs the .py file to convert it to a smaller .mpy file, instead I'll have a small .py file and a .bin file. Code changes in the API module CPtGFX.py won't be rocket science, but I've said that before, that's why I'm giving myself a week target for putting it out on GitHub.

johncblacker
 
Posts: 75
Joined: Mon Aug 27, 2018 1:45 pm

Re: New GFX for CircuitPython

by jerryn on Sun Oct 14, 2018 3:13 pm

Thanks for all your work on this. I hav not had time to do more that download it and try you demos. I'll be happy to keep trying new versions and hope to find time to do more soon.
What board are you running on? It looks like an M4 from the available memory.

jerryn
 
Posts: 816
Joined: Sat Sep 14, 2013 9:05 am

Please be positive and constructive with your questions and comments.