From 799dbf35aa29d154921aa27ecf4b55a2088cae79 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Fri, 19 Jul 2019 17:46:40 -0400 Subject: [PATCH 01/16] Move code over --- .gitignore | 12 + .pylintrc | 433 +++++++++++++++++++++++++++++ .readthedocs.yml | 3 + .travis.yml | 48 ++++ CODE_OF_CONDUCT.md | 127 +++++++++ LICENSE | 21 ++ README.rst | 163 +++++++++++ adafruit_bitmapsaver.py | 117 ++++++++ docs/_static/favicon.ico | Bin 0 -> 4414 bytes docs/api.rst | 8 + docs/conf.py | 160 +++++++++++ docs/examples.rst | 8 + docs/index.rst | 45 +++ examples/bitmapsaver_simpletest.py | 50 ++++ requirements.txt | 1 + setup.py | 63 +++++ 16 files changed, 1259 insertions(+) create mode 100644 .gitignore create mode 100644 .pylintrc create mode 100644 .readthedocs.yml create mode 100644 .travis.yml create mode 100644 CODE_OF_CONDUCT.md create mode 100644 LICENSE create mode 100644 README.rst create mode 100644 adafruit_bitmapsaver.py create mode 100644 docs/_static/favicon.ico create mode 100644 docs/api.rst create mode 100644 docs/conf.py create mode 100644 docs/examples.rst create mode 100644 docs/index.rst create mode 100644 examples/bitmapsaver_simpletest.py create mode 100644 requirements.txt create mode 100644 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..55f127b --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +*.mpy +.idea +__pycache__ +_build +*.pyc +.env +build* +bundles +*.DS_Store +.eggs +dist +**/*.egg-info \ No newline at end of file diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..039eaec --- /dev/null +++ b/.pylintrc @@ -0,0 +1,433 @@ +[MASTER] + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Add files or directories matching the regex patterns to the blacklist. The +# regex matches against base names, not paths. +ignore-patterns= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. +# jobs=1 +jobs=2 + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Specify a configuration file. +#rcfile= + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +# disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call +disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable= + + +[REPORTS] + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio).You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Tells whether to display a full report or only the messages +reports=no + +# Activate the evaluation score. +score=yes + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +# notes=FIXME,XXX,TODO +notes=FIXME,XXX + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules=board + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,future.builtins + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +# expected-line-ending-format= +expected-line-ending-format=LF + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module +max-module-lines=1000 + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma,dict-separator + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[SIMILARITIES] + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[BASIC] + +# Naming hint for argument names +argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct argument names +argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Naming hint for attribute names +attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct attribute names +attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class names +# class-name-hint=[A-Z_][a-zA-Z0-9]+$ +class-name-hint=[A-Z_][a-zA-Z0-9_]+$ + +# Regular expression matching correct class names +# class-rgx=[A-Z_][a-zA-Z0-9]+$ +class-rgx=[A-Z_][a-zA-Z0-9_]+$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming hint for function names +function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct function names +function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Good variable names which should always be accepted, separated by a comma +# good-names=i,j,k,ex,Run,_ +good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_ + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for method names +method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct method names +method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +property-classes=abc.abstractproperty + +# Naming hint for variable names +variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct variable names +variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + + +[IMPORTS] + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=optparse,tkinter.tix + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Maximum number of attributes for a class (see R0902). +# max-attributes=7 +max-attributes=11 + +# Maximum number of boolean expressions in a if statement +max-bool-expr=5 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of statements in function / method body +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=1 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..f4243ad --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,3 @@ +python: + version: 3 +requirements_file: requirements.txt diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..8e12837 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,48 @@ +# This is a common .travis.yml for generating library release zip files for +# CircuitPython library releases using circuitpython-build-tools. +# See https://github.com/adafruit/circuitpython-build-tools for detailed setup +# instructions. + +dist: xenial +language: python +python: + - "3.6" + +cache: + pip: true + +# TODO: if deployment to PyPi is desired, change 'DEPLOY_PYPI' to "true", +# or remove the env block entirely and remove the condition in the +# deploy block. +env: + - DEPLOY_PYPI="false" + +deploy: + - provider: releases + api_key: "$GITHUB_TOKEN" + file_glob: true + file: "$TRAVIS_BUILD_DIR/bundles/*" + skip_cleanup: true + overwrite: true + on: + tags: true + # TODO: Use 'travis encrypt --com -r adafruit/' to generate + # the encrypted password for adafruit-travis. Paste result below. + - provider: pypi + user: adafruit-travis + password: + secure: #-- PASTE ENCRYPTED PASSWORD HERE --# + on: + tags: true + condition: $DEPLOY_PYPI = "true" + +install: + - pip install -r requirements.txt + - pip install circuitpython-build-tools Sphinx sphinx-rtd-theme + - pip install --force-reinstall pylint==1.9.2 + +script: + - pylint adafruit_bitmapsaver.py + - ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace examples/*.py) + - circuitpython-build-bundles --filename_prefix adafruit-circuitpython-bitmapsaver --library_location . + - cd docs && sphinx-build -E -W -b html . _build/html && cd .. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..7ca3a1d --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,127 @@ +# Adafruit Community Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and leaders pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level or type of +experience, education, socio-economic status, nationality, personal appearance, +race, religion, or sexual identity and orientation. + +## Our Standards + +We are committed to providing a friendly, safe and welcoming environment for +all. + +Examples of behavior that contributes to creating a positive environment +include: + +* Be kind and courteous to others +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Collaborating with other community members +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and sexual attention or advances +* The use of inappropriate images, including in a community member's avatar +* The use of inappropriate language, including in a community member's nickname +* Any spamming, flaming, baiting or other attention-stealing behavior +* Excessive or unwelcome helping; answering outside the scope of the question + asked +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate + +The goal of the standards and moderation guidelines outlined here is to build +and maintain a respectful community. We ask that you don’t just aim to be +"technically unimpeachable", but rather try to be your best self. + +We value many things beyond technical expertise, including collaboration and +supporting others within our community. Providing a positive experience for +other community members can have a much more significant impact than simply +providing the correct answer. + +## Our Responsibilities + +Project leaders are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project leaders have the right and responsibility to remove, edit, or +reject messages, comments, commits, code, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any community member for other behaviors that they deem +inappropriate, threatening, offensive, or harmful. + +## Moderation + +Instances of behaviors that violate the Adafruit Community Code of Conduct +may be reported by any member of the community. Community members are +encouraged to report these situations, including situations they witness +involving other community members. + +You may report in the following ways: + +In any situation, you may send an email to . + +On the Adafruit Discord, you may send an open message from any channel +to all Community Helpers by tagging @community moderators. You may also send an +open message from any channel, or a direct message to @kattni#1507, +@tannewt#4653, @Dan Halbert#1614, @cater#2442, @sommersoft#0222, or +@Andon#8175. + +Email and direct message reports will be kept confidential. + +In situations on Discord where the issue is particularly egregious, possibly +illegal, requires immediate action, or violates the Discord terms of service, +you should also report the message directly to Discord. + +These are the steps for upholding our community’s standards of conduct. + +1. Any member of the community may report any situation that violates the +Adafruit Community Code of Conduct. All reports will be reviewed and +investigated. +2. If the behavior is an egregious violation, the community member who +committed the violation may be banned immediately, without warning. +3. Otherwise, moderators will first respond to such behavior with a warning. +4. Moderators follow a soft "three strikes" policy - the community member may +be given another chance, if they are receptive to the warning and change their +behavior. +5. If the community member is unreceptive or unreasonable when warned by a +moderator, or the warning goes unheeded, they may be banned for a first or +second offense. Repeated offenses will result in the community member being +banned. + +## Scope + +This Code of Conduct and the enforcement policies listed above apply to all +Adafruit Community venues. This includes but is not limited to any community +spaces (both public and private), the entire Adafruit Discord server, and +Adafruit GitHub repositories. Examples of Adafruit Community spaces include +but are not limited to meet-ups, audio chats on the Adafruit Discord, or +interaction at a conference. + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. As a community +member, you are representing our community, and are expected to behave +accordingly. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at +, +and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). + +For other projects adopting the Adafruit Community Code of +Conduct, please contact the maintainers of those projects for enforcement. +If you wish to use this code of conduct for your own project, consider +explicitly mentioning your moderation policy or making a copy with your +own moderation policy so as to avoid confusion. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d69195f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Dave Astels for Adafruit Industries + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..2bf382c --- /dev/null +++ b/README.rst @@ -0,0 +1,163 @@ +Introduction +============ + +.. image:: https://readthedocs.org/projects/adafruit-circuitpython-bitmapsaver/badge/?version=latest + :target: https://circuitpython.readthedocs.io/projects/bitmapsaver/en/latest/ + :alt: Documentation Status + +.. image:: https://img.shields.io/discord/327254708534116352.svg + :target: https://discord.gg/nBQh6qu + :alt: Discord + +.. image:: https://travis-ci.com/adafruit/Adafruit_CircuitPython_BitmapSaver.svg?branch=master + :target: https://travis-ci.com/adafruit/Adafruit_CircuitPython_BitmapSaver + :alt: Build Status + +Save a displayio.Bitmap (and associated displayio.Palette) into a BMP file. + + +Dependencies +============= +This driver depends on: + +* `Adafruit CircuitPython `_ + +Please ensure all dependencies are available on the CircuitPython filesystem. +This is easily achieved by downloading +`the Adafruit library and driver bundle `_. + +Installing from PyPI +===================== +.. note:: This library is not available on PyPI yet. Install documentation is included + as a standard element. Stay tuned for PyPI availability! + +On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from +PyPI `_. To install for current user: + +.. code-block:: shell + + pip3 install adafruit-circuitpython-bitmapsaver + +To install system-wide (this may be required in some cases): + +.. code-block:: shell + + sudo pip3 install adafruit-circuitpython-bitmapsaver + +To install in a virtual environment in your current project: + +.. code-block:: shell + + mkdir project-name && cd project-name + python3 -m venv .env + source .env/bin/activate + pip3 install adafruit-circuitpython-bitmapsaver + +Usage Example +============= + +.. code-block:: python + + import board + import busio + import digitalio + from displayio import Bitmap, Palette + import adafruit_sdcard + import storage + from adafruit_bitmap_saver import save_bitmap + + print('Setting up SD card') + spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + cs = digitalio.DigitalInOut(board.SD_CS) + sdcard = adafruit_sdcard.SDCard(spi, cs) + vfs = storage.VfsFat(sdcard) + storage.mount(vfs, "/sd") + + WHITE = 0xFFFFFF + BLACK = 0x000000 + RED = 0xFF0000 + ORANGE = 0xFFA500 + YELLOW = 0xFFFF00 + GREEN = 0x00FF00 + BLUE = 0x0000FF + PURPLE = 0x800080 + PINK = 0xFFC0CB + + colors = (BLACK, RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE, WHITE) + + print('Building sample bitmap and palette') + bitmap = Bitmap(16, 16, 9) + palette = Palette(len(colors)) + for i, c in enumerate(colors): + palette[i] = c + + for x in range(16): + for y in range(16): + if x == 0 or y == 0 or x == 15 or y == 15: + bitmap[x, y] = 1 + elif x == y: + bitmap[x, y] = 4 + elif x == 15 - y: + bitmap[x, y] = 5 + else: + bitmap[x, y] = 0 + + print('Saving bitmap') + save_bitmap(bitmap, palette, '/sd/test.bmp') + +Contributing +============ + +Contributions are welcome! Please read our `Code of Conduct +`_ +before contributing to help this project stay welcoming. + +Building locally +================ + +Zip release files +----------------- + +To build this library locally you'll need to install the +`circuitpython-build-tools `_ package. + +.. code-block:: shell + + python3 -m venv .env + source .env/bin/activate + pip install circuitpython-build-tools + +Once installed, make sure you are in the virtual environment: + +.. code-block:: shell + + source .env/bin/activate + +Then run the build: + +.. code-block:: shell + + circuitpython-build-bundles --filename_prefix adafruit-circuitpython-bitmapsaver --library_location . + +Sphinx documentation +----------------------- + +Sphinx is used to build the documentation based on rST files and comments in the code. First, +install dependencies (feel free to reuse the virtual environment from above): + +.. code-block:: shell + + python3 -m venv .env + source .env/bin/activate + pip install Sphinx sphinx-rtd-theme + +Now, once you have the virtual environment activated: + +.. code-block:: shell + + cd docs + sphinx-build -E -W -b html . _build/html + +This will output the documentation to ``docs/_build/html``. Open the index.html in your browser to +view them. It will also (due to -W) error out on any warning like Travis will. This is a good way to +locally verify it will pass. diff --git a/adafruit_bitmapsaver.py b/adafruit_bitmapsaver.py new file mode 100644 index 0000000..37a528b --- /dev/null +++ b/adafruit_bitmapsaver.py @@ -0,0 +1,117 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 Dave Astels for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_bitmapsaver` +================================================================================ + +Save a displayio.Bitmap (and associated displayio.Palette) into a BMP file. + + +* Author(s): Dave Astels + +Implementation Notes +-------------------- + +**Hardware:** + + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases + +""" + +# imports + +import struct +from displayio import Bitmap, Palette + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BitmapSaver.git" + +#pylint:disable=line-too-long,broad-except,redefined-outer-name + +def _write_bmp_header(output_file, filesize): + output_file.write(bytes('BM', 'ascii')) + output_file.write(struct.pack('> 8) | ((value & 0x00FF) << 8) + +def _bytes_per_row(bitmap): + pixel_bytes = 3 * bitmap.width + padding_bytes = (4 - (pixel_bytes % 4)) % 4 + return pixel_bytes + padding_bytes + +def _write_pixels(output_file, bitmap, palette): + row_buffer = bytearray(_bytes_per_row(bitmap)) + + for y in range(bitmap.height, 0, -1): + buffer_index = 0 + for x in range(bitmap.width): + pixel = bitmap[x, y-1] # this is an index into the palette + color = _swap_bytes(palette[pixel]) # This is a 16 bit value in r5g6b5 format + blue = (color << 3) & 0x00F8 # extract each of the RGB tripple into it's own byte + green = (color >> 3) & 0x00FC + red = (color >> 8) & 0x00F8 + for value in (blue, green, red): + row_buffer[buffer_index] = value + buffer_index += 1 + output_file.write(row_buffer) + +def save_bitmap(bitmap, palette, file_or_filename): + """Save a bitmap (using an associated palette) to a 24 bit per pixel BMP file. + + :param bitmap: the displayio.Bitmap to save + :param palette: the displayio.Palette to use for looking up colors in the bitmap + """ + if not isinstance(bitmap, Bitmap): + raise ValueError('bitmap') + if not isinstance(palette, Palette): + raise ValueError('palette') + try: + if isinstance(file_or_filename, str): + output_file = open(file_or_filename, 'wb') + else: + output_file = file_or_filename + + filesize = 54 + bitmap.height * _bytes_per_row(bitmap) + _write_bmp_header(output_file, filesize) + _write_dib_header(output_file, bitmap) + _write_pixels(output_file, bitmap, palette) + except Exception: + print('Error saving bitmap') + raise + else: + output_file.close() diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..5aca98376a1f7e593ebd9cf41a808512c2135635 GIT binary patch literal 4414 zcmd^BX;4#F6n=SG-XmlONeGrD5E6J{RVh+e928U#MG!$jWvO+UsvWh`x&VqGNx*en zx=qox7Dqv{kPwo%fZC$dDwVpRtz{HzTkSs8QhG0)%Y=-3@Kt!4ag|JcIo?$-F|?bXVS9UDUyev>MVZQ(H8K4#;BQW-t2CPorj8^KJrMX}QK zp+e<;4ldpXz~=)2GxNy811&)gt-}Q*yVQpsxr@VMoA##{)$1~=bZ1MmjeFw?uT(`8 z^g=09<=zW%r%buwN%iHtuKSg|+r7HkT0PYN*_u9k1;^Ss-Z!RBfJ?Un4w(awqp2b3 z%+myoFis_lTlCrGx2z$0BQdh+7?!JK#9K9@Z!VrG zNj6gK5r(b4?YDOLw|DPRoN7bdP{(>GEG41YcN~4r_SUHU2hgVtUwZG@s%edC;k7Sn zC)RvEnlq~raE2mY2ko64^m1KQL}3riixh?#J{o)IT+K-RdHae2eRX91-+g!y`8^># z-zI0ir>P%Xon)!@xp-BK2bDYUB9k613NRrY6%lVjbFcQc*pRqiK~8xtkNPLxt}e?&QsTB}^!39t_%Qb)~Ukn0O%iC;zt z<&A-y;3h++)>c1br`5VFM~5(83!HKx$L+my8sW_c#@x*|*vB1yU)_dt3vH;2hqPWx zAl^6@?ipx&U7pf`a*>Yq6C85nb+B=Fnn+(id$W#WB^uHAcZVG`qg;rWB}ubvi(Y>D z$ei>REw$#xp0SHAd^|1hq&9HJ=jKK8^zTH~nk)G?yUcmTh9vUM6Y0LMw4(gYVY$D$ zGl&WY&H<)BbJ&3sYbKjx1j^=3-0Q#f^}(aP1?8^`&FUWMp|rmtpK)bLQ1Zo?^s4jqK=Lfg*9&geMGVQ z#^-*!V`fG@;H&{M9S8%+;|h&Qrxym0Ar>WT4BCVLR8cGXF=JmEYN(sNT(9vl+S|%g z8r7nXQ(95i^`=+XHo|){$vf2$?=`F$^&wFlYXyXg$B{a>$-Fp+V}+D;9k=~Xl~?C4 zAB-;RKXdUzBJE{V&d&%R>aEfFe;vxqI$0@hwVM}gFeQR@j}a>DDxR+n+-*6|_)k%% z*mSpDV|=5I9!&VC&9tD%fcVygWZV!iIo2qFtm#!*(s|@ZT33*Ad;+<|3^+yrp*;oH zBSYLV(H1zTU?2WjrCQoQW)Z>J2a=dTriuvezBmu16`tM2fm7Q@d4^iqII-xFpwHGI zn9CL}QE*1vdj2PX{PIuqOe5dracsciH6OlAZATvE8rj6ykqdIjal2 z0S0S~PwHb-5?OQ-tU-^KTG@XNrEVSvo|HIP?H;7ZhYeZkhSqh-{reE!5di;1zk$#Y zCe7rOnlzFYJ6Z#Hm$GoidKB=2HBCwm`BbZVeZY4ukmG%1uz7p2URs6c9j-Gjj^oQV zsdDb3@k2e`C$1I5ML5U0Qs0C1GAp^?!*`=|Nm(vWz3j*j*8ucum2;r0^-6Aca=Gv) zc%}&;!+_*S2tlnnJnz0EKeRmw-Y!@9ob!XQBwiv}^u9MkaXHvM=!<3YX;+2#5Cj5pp?FEK750S3BgeSDtaE^ zXUM@xoV6yBFKfzvY20V&Lr0yC + CircuitPython Reference Documentation + CircuitPython Support Forum + Discord Chat + Adafruit Learning System + Adafruit Blog + Adafruit Store + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/examples/bitmapsaver_simpletest.py b/examples/bitmapsaver_simpletest.py new file mode 100644 index 0000000..dd20257 --- /dev/null +++ b/examples/bitmapsaver_simpletest.py @@ -0,0 +1,50 @@ +"""Example of using save_bitmap""" + +import board +import busio +import digitalio +from displayio import Bitmap, Palette +import adafruit_sdcard +import storage +from adafruit_bitmapsaver import save_bitmap + +#pylint:disable=invalid-name + +print('Setting up SD card') +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) +cs = digitalio.DigitalInOut(board.SD_CS) +sdcard = adafruit_sdcard.SDCard(spi, cs) +vfs = storage.VfsFat(sdcard) +storage.mount(vfs, "/sd") + +WHITE = 0xFFFFFF +BLACK = 0x000000 +RED = 0xFF0000 +ORANGE = 0xFFA500 +YELLOW = 0xFFFF00 +GREEN = 0x00FF00 +BLUE = 0x0000FF +PURPLE = 0x800080 +PINK = 0xFFC0CB + +colors = (BLACK, RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE, WHITE) + +print('Building sample bitmap and palette') +bitmap = Bitmap(16, 16, 9) +palette = Palette(len(colors)) +for i, c in enumerate(colors): + palette[i] = c + +for x in range(16): + for y in range(16): + if x == 0 or y == 0 or x == 15 or y == 15: + bitmap[x, y] = 1 + elif x == y: + bitmap[x, y] = 4 + elif x == 15 - y: + bitmap[x, y] = 5 + else: + bitmap[x, y] = 0 + +print('Saving bitmap') +save_bitmap(bitmap, palette, '/sd/test.bmp') diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..edf9394 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +Adafruit-Blinka diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..07f056b --- /dev/null +++ b/setup.py @@ -0,0 +1,63 @@ +"""A setuptools based setup module. + +See: +https://packaging.python.org/en/latest/distributing.html +https://github.com/pypa/sampleproject +""" + +from setuptools import setup, find_packages +# To use a consistent encoding +from codecs import open +from os import path + +here = path.abspath(path.dirname(__file__)) + +# Get the long description from the README file +with open(path.join(here, 'README.rst'), encoding='utf-8') as f: + long_description = f.read() + +setup( + name='adafruit-circuitpython-bitmapsaver', + + use_scm_version=True, + setup_requires=['setuptools_scm'], + + description='Save a displayio.Bitmap (and associated displayio.Palette) into a BMP file.', + long_description=long_description, + long_description_content_type='text/x-rst', + + # The project's main homepage. + url='https://github.com/adafruit/Adafruit_CircuitPython_BitmapSaver', + + # Author details + author='Adafruit Industries', + author_email='circuitpython@adafruit.com', + + install_requires=[ + 'Adafruit-Blinka' + ], + + # Choose your license + license='MIT', + + # See https://pypi.python.org/pypi?%3Aaction=list_classifiers + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'Topic :: Software Development :: Libraries', + 'Topic :: System :: Hardware', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + ], + + # What does your project relate to? + keywords='adafruit blinka circuitpython micropython bitmapsaver displayio', + + # You can just specify the packages manually here if your project is + # simple. Or you can use find_packages(). + # TODO: IF LIBRARY FILES ARE A PACKAGE FOLDER, + # CHANGE `py_modules=['...']` TO `packages=['...']` + py_modules=['adafruit_bitmapsaver'], +) From b186fdb07b5f14bf75895546cd4f965c0e3efb5a Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Fri, 19 Jul 2019 17:46:40 -0400 Subject: [PATCH 02/16] Revert "Move code over" This reverts commit 799dbf35aa29d154921aa27ecf4b55a2088cae79. --- .gitignore | 12 - .pylintrc | 433 ----------------------------- .readthedocs.yml | 3 - .travis.yml | 48 ---- CODE_OF_CONDUCT.md | 127 --------- LICENSE | 21 -- README.rst | 163 ----------- adafruit_bitmapsaver.py | 117 -------- docs/_static/favicon.ico | Bin 4414 -> 0 bytes docs/api.rst | 8 - docs/conf.py | 160 ----------- docs/examples.rst | 8 - docs/index.rst | 45 --- examples/bitmapsaver_simpletest.py | 50 ---- requirements.txt | 1 - setup.py | 63 ----- 16 files changed, 1259 deletions(-) delete mode 100644 .gitignore delete mode 100644 .pylintrc delete mode 100644 .readthedocs.yml delete mode 100644 .travis.yml delete mode 100644 CODE_OF_CONDUCT.md delete mode 100644 LICENSE delete mode 100644 README.rst delete mode 100644 adafruit_bitmapsaver.py delete mode 100644 docs/_static/favicon.ico delete mode 100644 docs/api.rst delete mode 100644 docs/conf.py delete mode 100644 docs/examples.rst delete mode 100644 docs/index.rst delete mode 100644 examples/bitmapsaver_simpletest.py delete mode 100644 requirements.txt delete mode 100644 setup.py diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 55f127b..0000000 --- a/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -*.mpy -.idea -__pycache__ -_build -*.pyc -.env -build* -bundles -*.DS_Store -.eggs -dist -**/*.egg-info \ No newline at end of file diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index 039eaec..0000000 --- a/.pylintrc +++ /dev/null @@ -1,433 +0,0 @@ -[MASTER] - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code -extension-pkg-whitelist= - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=CVS - -# Add files or directories matching the regex patterns to the blacklist. The -# regex matches against base names, not paths. -ignore-patterns= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Use multiple processes to speed up Pylint. -# jobs=1 -jobs=2 - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - -# Pickle collected data for later comparisons. -persistent=yes - -# Specify a configuration file. -#rcfile= - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" -# disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call -disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -enable= - - -[REPORTS] - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio).You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages -reports=no - -# Activate the evaluation score. -score=yes - - -[REFACTORING] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging - - -[SPELLING] - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -# notes=FIXME,XXX,TODO -notes=FIXME,XXX - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# This flag controls whether pylint should warn about no-member and similar -# checks whenever an opaque object is returned when inferring. The inference -# can return multiple potential results while evaluating a Python object, but -# some branches might not be evaluated, which results in partial inference. In -# that case, it might be useful to still emit no-member and other checks for -# the rest of the inferred objects. -ignore-on-opaque-inference=yes - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules=board - -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes - -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 - -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_,_cb - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.*|^ignored_|^unused_ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,future.builtins - - -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -# expected-line-ending-format= -expected-line-ending-format=LF - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=100 - -# Maximum number of lines in a module -max-module-lines=1000 - -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -no-space-check=trailing-comma,dict-separator - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - - -[SIMILARITIES] - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - -# Minimum lines number of a similarity. -min-similarity-lines=4 - - -[BASIC] - -# Naming hint for argument names -argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct argument names -argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Naming hint for attribute names -attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct attribute names -attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# Naming hint for class attribute names -class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Regular expression matching correct class attribute names -class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Naming hint for class names -# class-name-hint=[A-Z_][a-zA-Z0-9]+$ -class-name-hint=[A-Z_][a-zA-Z0-9_]+$ - -# Regular expression matching correct class names -# class-rgx=[A-Z_][a-zA-Z0-9]+$ -class-rgx=[A-Z_][a-zA-Z0-9_]+$ - -# Naming hint for constant names -const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Regular expression matching correct constant names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# Naming hint for function names -function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct function names -function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Good variable names which should always be accepted, separated by a comma -# good-names=i,j,k,ex,Run,_ -good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_ - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=no - -# Naming hint for inline iteration names -inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ - -# Regular expression matching correct inline iteration names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Naming hint for method names -method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct method names -method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Naming hint for module names -module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Regular expression matching correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -property-classes=abc.abstractproperty - -# Naming hint for variable names -variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct variable names -variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - - -[IMPORTS] - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=optparse,tkinter.tix - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict,_fields,_replace,_source,_make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - - -[DESIGN] - -# Maximum number of arguments for function / method -max-args=5 - -# Maximum number of attributes for a class (see R0902). -# max-attributes=7 -max-attributes=11 - -# Maximum number of boolean expressions in a if statement -max-bool-expr=5 - -# Maximum number of branch for function / method body -max-branches=12 - -# Maximum number of locals for function / method body -max-locals=15 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of statements in function / method body -max-statements=50 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=1 - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=Exception diff --git a/.readthedocs.yml b/.readthedocs.yml deleted file mode 100644 index f4243ad..0000000 --- a/.readthedocs.yml +++ /dev/null @@ -1,3 +0,0 @@ -python: - version: 3 -requirements_file: requirements.txt diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 8e12837..0000000 --- a/.travis.yml +++ /dev/null @@ -1,48 +0,0 @@ -# This is a common .travis.yml for generating library release zip files for -# CircuitPython library releases using circuitpython-build-tools. -# See https://github.com/adafruit/circuitpython-build-tools for detailed setup -# instructions. - -dist: xenial -language: python -python: - - "3.6" - -cache: - pip: true - -# TODO: if deployment to PyPi is desired, change 'DEPLOY_PYPI' to "true", -# or remove the env block entirely and remove the condition in the -# deploy block. -env: - - DEPLOY_PYPI="false" - -deploy: - - provider: releases - api_key: "$GITHUB_TOKEN" - file_glob: true - file: "$TRAVIS_BUILD_DIR/bundles/*" - skip_cleanup: true - overwrite: true - on: - tags: true - # TODO: Use 'travis encrypt --com -r adafruit/' to generate - # the encrypted password for adafruit-travis. Paste result below. - - provider: pypi - user: adafruit-travis - password: - secure: #-- PASTE ENCRYPTED PASSWORD HERE --# - on: - tags: true - condition: $DEPLOY_PYPI = "true" - -install: - - pip install -r requirements.txt - - pip install circuitpython-build-tools Sphinx sphinx-rtd-theme - - pip install --force-reinstall pylint==1.9.2 - -script: - - pylint adafruit_bitmapsaver.py - - ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace examples/*.py) - - circuitpython-build-bundles --filename_prefix adafruit-circuitpython-bitmapsaver --library_location . - - cd docs && sphinx-build -E -W -b html . _build/html && cd .. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 7ca3a1d..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,127 +0,0 @@ -# Adafruit Community Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and leaders pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level or type of -experience, education, socio-economic status, nationality, personal appearance, -race, religion, or sexual identity and orientation. - -## Our Standards - -We are committed to providing a friendly, safe and welcoming environment for -all. - -Examples of behavior that contributes to creating a positive environment -include: - -* Be kind and courteous to others -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Collaborating with other community members -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and sexual attention or advances -* The use of inappropriate images, including in a community member's avatar -* The use of inappropriate language, including in a community member's nickname -* Any spamming, flaming, baiting or other attention-stealing behavior -* Excessive or unwelcome helping; answering outside the scope of the question - asked -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate - -The goal of the standards and moderation guidelines outlined here is to build -and maintain a respectful community. We ask that you don’t just aim to be -"technically unimpeachable", but rather try to be your best self. - -We value many things beyond technical expertise, including collaboration and -supporting others within our community. Providing a positive experience for -other community members can have a much more significant impact than simply -providing the correct answer. - -## Our Responsibilities - -Project leaders are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project leaders have the right and responsibility to remove, edit, or -reject messages, comments, commits, code, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any community member for other behaviors that they deem -inappropriate, threatening, offensive, or harmful. - -## Moderation - -Instances of behaviors that violate the Adafruit Community Code of Conduct -may be reported by any member of the community. Community members are -encouraged to report these situations, including situations they witness -involving other community members. - -You may report in the following ways: - -In any situation, you may send an email to . - -On the Adafruit Discord, you may send an open message from any channel -to all Community Helpers by tagging @community moderators. You may also send an -open message from any channel, or a direct message to @kattni#1507, -@tannewt#4653, @Dan Halbert#1614, @cater#2442, @sommersoft#0222, or -@Andon#8175. - -Email and direct message reports will be kept confidential. - -In situations on Discord where the issue is particularly egregious, possibly -illegal, requires immediate action, or violates the Discord terms of service, -you should also report the message directly to Discord. - -These are the steps for upholding our community’s standards of conduct. - -1. Any member of the community may report any situation that violates the -Adafruit Community Code of Conduct. All reports will be reviewed and -investigated. -2. If the behavior is an egregious violation, the community member who -committed the violation may be banned immediately, without warning. -3. Otherwise, moderators will first respond to such behavior with a warning. -4. Moderators follow a soft "three strikes" policy - the community member may -be given another chance, if they are receptive to the warning and change their -behavior. -5. If the community member is unreceptive or unreasonable when warned by a -moderator, or the warning goes unheeded, they may be banned for a first or -second offense. Repeated offenses will result in the community member being -banned. - -## Scope - -This Code of Conduct and the enforcement policies listed above apply to all -Adafruit Community venues. This includes but is not limited to any community -spaces (both public and private), the entire Adafruit Discord server, and -Adafruit GitHub repositories. Examples of Adafruit Community spaces include -but are not limited to meet-ups, audio chats on the Adafruit Discord, or -interaction at a conference. - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. As a community -member, you are representing our community, and are expected to behave -accordingly. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 1.4, available at -, -and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). - -For other projects adopting the Adafruit Community Code of -Conduct, please contact the maintainers of those projects for enforcement. -If you wish to use this code of conduct for your own project, consider -explicitly mentioning your moderation policy or making a copy with your -own moderation policy so as to avoid confusion. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d69195f..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2019 Dave Astels for Adafruit Industries - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.rst b/README.rst deleted file mode 100644 index 2bf382c..0000000 --- a/README.rst +++ /dev/null @@ -1,163 +0,0 @@ -Introduction -============ - -.. image:: https://readthedocs.org/projects/adafruit-circuitpython-bitmapsaver/badge/?version=latest - :target: https://circuitpython.readthedocs.io/projects/bitmapsaver/en/latest/ - :alt: Documentation Status - -.. image:: https://img.shields.io/discord/327254708534116352.svg - :target: https://discord.gg/nBQh6qu - :alt: Discord - -.. image:: https://travis-ci.com/adafruit/Adafruit_CircuitPython_BitmapSaver.svg?branch=master - :target: https://travis-ci.com/adafruit/Adafruit_CircuitPython_BitmapSaver - :alt: Build Status - -Save a displayio.Bitmap (and associated displayio.Palette) into a BMP file. - - -Dependencies -============= -This driver depends on: - -* `Adafruit CircuitPython `_ - -Please ensure all dependencies are available on the CircuitPython filesystem. -This is easily achieved by downloading -`the Adafruit library and driver bundle `_. - -Installing from PyPI -===================== -.. note:: This library is not available on PyPI yet. Install documentation is included - as a standard element. Stay tuned for PyPI availability! - -On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from -PyPI `_. To install for current user: - -.. code-block:: shell - - pip3 install adafruit-circuitpython-bitmapsaver - -To install system-wide (this may be required in some cases): - -.. code-block:: shell - - sudo pip3 install adafruit-circuitpython-bitmapsaver - -To install in a virtual environment in your current project: - -.. code-block:: shell - - mkdir project-name && cd project-name - python3 -m venv .env - source .env/bin/activate - pip3 install adafruit-circuitpython-bitmapsaver - -Usage Example -============= - -.. code-block:: python - - import board - import busio - import digitalio - from displayio import Bitmap, Palette - import adafruit_sdcard - import storage - from adafruit_bitmap_saver import save_bitmap - - print('Setting up SD card') - spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) - cs = digitalio.DigitalInOut(board.SD_CS) - sdcard = adafruit_sdcard.SDCard(spi, cs) - vfs = storage.VfsFat(sdcard) - storage.mount(vfs, "/sd") - - WHITE = 0xFFFFFF - BLACK = 0x000000 - RED = 0xFF0000 - ORANGE = 0xFFA500 - YELLOW = 0xFFFF00 - GREEN = 0x00FF00 - BLUE = 0x0000FF - PURPLE = 0x800080 - PINK = 0xFFC0CB - - colors = (BLACK, RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE, WHITE) - - print('Building sample bitmap and palette') - bitmap = Bitmap(16, 16, 9) - palette = Palette(len(colors)) - for i, c in enumerate(colors): - palette[i] = c - - for x in range(16): - for y in range(16): - if x == 0 or y == 0 or x == 15 or y == 15: - bitmap[x, y] = 1 - elif x == y: - bitmap[x, y] = 4 - elif x == 15 - y: - bitmap[x, y] = 5 - else: - bitmap[x, y] = 0 - - print('Saving bitmap') - save_bitmap(bitmap, palette, '/sd/test.bmp') - -Contributing -============ - -Contributions are welcome! Please read our `Code of Conduct -`_ -before contributing to help this project stay welcoming. - -Building locally -================ - -Zip release files ------------------ - -To build this library locally you'll need to install the -`circuitpython-build-tools `_ package. - -.. code-block:: shell - - python3 -m venv .env - source .env/bin/activate - pip install circuitpython-build-tools - -Once installed, make sure you are in the virtual environment: - -.. code-block:: shell - - source .env/bin/activate - -Then run the build: - -.. code-block:: shell - - circuitpython-build-bundles --filename_prefix adafruit-circuitpython-bitmapsaver --library_location . - -Sphinx documentation ------------------------ - -Sphinx is used to build the documentation based on rST files and comments in the code. First, -install dependencies (feel free to reuse the virtual environment from above): - -.. code-block:: shell - - python3 -m venv .env - source .env/bin/activate - pip install Sphinx sphinx-rtd-theme - -Now, once you have the virtual environment activated: - -.. code-block:: shell - - cd docs - sphinx-build -E -W -b html . _build/html - -This will output the documentation to ``docs/_build/html``. Open the index.html in your browser to -view them. It will also (due to -W) error out on any warning like Travis will. This is a good way to -locally verify it will pass. diff --git a/adafruit_bitmapsaver.py b/adafruit_bitmapsaver.py deleted file mode 100644 index 37a528b..0000000 --- a/adafruit_bitmapsaver.py +++ /dev/null @@ -1,117 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2019 Dave Astels for Adafruit Industries -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -""" -`adafruit_bitmapsaver` -================================================================================ - -Save a displayio.Bitmap (and associated displayio.Palette) into a BMP file. - - -* Author(s): Dave Astels - -Implementation Notes --------------------- - -**Hardware:** - - -**Software and Dependencies:** - -* Adafruit CircuitPython firmware for the supported boards: - https://github.com/adafruit/circuitpython/releases - -""" - -# imports - -import struct -from displayio import Bitmap, Palette - -__version__ = "0.0.0-auto.0" -__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BitmapSaver.git" - -#pylint:disable=line-too-long,broad-except,redefined-outer-name - -def _write_bmp_header(output_file, filesize): - output_file.write(bytes('BM', 'ascii')) - output_file.write(struct.pack('> 8) | ((value & 0x00FF) << 8) - -def _bytes_per_row(bitmap): - pixel_bytes = 3 * bitmap.width - padding_bytes = (4 - (pixel_bytes % 4)) % 4 - return pixel_bytes + padding_bytes - -def _write_pixels(output_file, bitmap, palette): - row_buffer = bytearray(_bytes_per_row(bitmap)) - - for y in range(bitmap.height, 0, -1): - buffer_index = 0 - for x in range(bitmap.width): - pixel = bitmap[x, y-1] # this is an index into the palette - color = _swap_bytes(palette[pixel]) # This is a 16 bit value in r5g6b5 format - blue = (color << 3) & 0x00F8 # extract each of the RGB tripple into it's own byte - green = (color >> 3) & 0x00FC - red = (color >> 8) & 0x00F8 - for value in (blue, green, red): - row_buffer[buffer_index] = value - buffer_index += 1 - output_file.write(row_buffer) - -def save_bitmap(bitmap, palette, file_or_filename): - """Save a bitmap (using an associated palette) to a 24 bit per pixel BMP file. - - :param bitmap: the displayio.Bitmap to save - :param palette: the displayio.Palette to use for looking up colors in the bitmap - """ - if not isinstance(bitmap, Bitmap): - raise ValueError('bitmap') - if not isinstance(palette, Palette): - raise ValueError('palette') - try: - if isinstance(file_or_filename, str): - output_file = open(file_or_filename, 'wb') - else: - output_file = file_or_filename - - filesize = 54 + bitmap.height * _bytes_per_row(bitmap) - _write_bmp_header(output_file, filesize) - _write_dib_header(output_file, bitmap) - _write_pixels(output_file, bitmap, palette) - except Exception: - print('Error saving bitmap') - raise - else: - output_file.close() diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico deleted file mode 100644 index 5aca98376a1f7e593ebd9cf41a808512c2135635..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4414 zcmd^BX;4#F6n=SG-XmlONeGrD5E6J{RVh+e928U#MG!$jWvO+UsvWh`x&VqGNx*en zx=qox7Dqv{kPwo%fZC$dDwVpRtz{HzTkSs8QhG0)%Y=-3@Kt!4ag|JcIo?$-F|?bXVS9UDUyev>MVZQ(H8K4#;BQW-t2CPorj8^KJrMX}QK zp+e<;4ldpXz~=)2GxNy811&)gt-}Q*yVQpsxr@VMoA##{)$1~=bZ1MmjeFw?uT(`8 z^g=09<=zW%r%buwN%iHtuKSg|+r7HkT0PYN*_u9k1;^Ss-Z!RBfJ?Un4w(awqp2b3 z%+myoFis_lTlCrGx2z$0BQdh+7?!JK#9K9@Z!VrG zNj6gK5r(b4?YDOLw|DPRoN7bdP{(>GEG41YcN~4r_SUHU2hgVtUwZG@s%edC;k7Sn zC)RvEnlq~raE2mY2ko64^m1KQL}3riixh?#J{o)IT+K-RdHae2eRX91-+g!y`8^># z-zI0ir>P%Xon)!@xp-BK2bDYUB9k613NRrY6%lVjbFcQc*pRqiK~8xtkNPLxt}e?&QsTB}^!39t_%Qb)~Ukn0O%iC;zt z<&A-y;3h++)>c1br`5VFM~5(83!HKx$L+my8sW_c#@x*|*vB1yU)_dt3vH;2hqPWx zAl^6@?ipx&U7pf`a*>Yq6C85nb+B=Fnn+(id$W#WB^uHAcZVG`qg;rWB}ubvi(Y>D z$ei>REw$#xp0SHAd^|1hq&9HJ=jKK8^zTH~nk)G?yUcmTh9vUM6Y0LMw4(gYVY$D$ zGl&WY&H<)BbJ&3sYbKjx1j^=3-0Q#f^}(aP1?8^`&FUWMp|rmtpK)bLQ1Zo?^s4jqK=Lfg*9&geMGVQ z#^-*!V`fG@;H&{M9S8%+;|h&Qrxym0Ar>WT4BCVLR8cGXF=JmEYN(sNT(9vl+S|%g z8r7nXQ(95i^`=+XHo|){$vf2$?=`F$^&wFlYXyXg$B{a>$-Fp+V}+D;9k=~Xl~?C4 zAB-;RKXdUzBJE{V&d&%R>aEfFe;vxqI$0@hwVM}gFeQR@j}a>DDxR+n+-*6|_)k%% z*mSpDV|=5I9!&VC&9tD%fcVygWZV!iIo2qFtm#!*(s|@ZT33*Ad;+<|3^+yrp*;oH zBSYLV(H1zTU?2WjrCQoQW)Z>J2a=dTriuvezBmu16`tM2fm7Q@d4^iqII-xFpwHGI zn9CL}QE*1vdj2PX{PIuqOe5dracsciH6OlAZATvE8rj6ykqdIjal2 z0S0S~PwHb-5?OQ-tU-^KTG@XNrEVSvo|HIP?H;7ZhYeZkhSqh-{reE!5di;1zk$#Y zCe7rOnlzFYJ6Z#Hm$GoidKB=2HBCwm`BbZVeZY4ukmG%1uz7p2URs6c9j-Gjj^oQV zsdDb3@k2e`C$1I5ML5U0Qs0C1GAp^?!*`=|Nm(vWz3j*j*8ucum2;r0^-6Aca=Gv) zc%}&;!+_*S2tlnnJnz0EKeRmw-Y!@9ob!XQBwiv}^u9MkaXHvM=!<3YX;+2#5Cj5pp?FEK750S3BgeSDtaE^ zXUM@xoV6yBFKfzvY20V&Lr0yC - CircuitPython Reference Documentation - CircuitPython Support Forum - Discord Chat - Adafruit Learning System - Adafruit Blog - Adafruit Store - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/examples/bitmapsaver_simpletest.py b/examples/bitmapsaver_simpletest.py deleted file mode 100644 index dd20257..0000000 --- a/examples/bitmapsaver_simpletest.py +++ /dev/null @@ -1,50 +0,0 @@ -"""Example of using save_bitmap""" - -import board -import busio -import digitalio -from displayio import Bitmap, Palette -import adafruit_sdcard -import storage -from adafruit_bitmapsaver import save_bitmap - -#pylint:disable=invalid-name - -print('Setting up SD card') -spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) -cs = digitalio.DigitalInOut(board.SD_CS) -sdcard = adafruit_sdcard.SDCard(spi, cs) -vfs = storage.VfsFat(sdcard) -storage.mount(vfs, "/sd") - -WHITE = 0xFFFFFF -BLACK = 0x000000 -RED = 0xFF0000 -ORANGE = 0xFFA500 -YELLOW = 0xFFFF00 -GREEN = 0x00FF00 -BLUE = 0x0000FF -PURPLE = 0x800080 -PINK = 0xFFC0CB - -colors = (BLACK, RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE, WHITE) - -print('Building sample bitmap and palette') -bitmap = Bitmap(16, 16, 9) -palette = Palette(len(colors)) -for i, c in enumerate(colors): - palette[i] = c - -for x in range(16): - for y in range(16): - if x == 0 or y == 0 or x == 15 or y == 15: - bitmap[x, y] = 1 - elif x == y: - bitmap[x, y] = 4 - elif x == 15 - y: - bitmap[x, y] = 5 - else: - bitmap[x, y] = 0 - -print('Saving bitmap') -save_bitmap(bitmap, palette, '/sd/test.bmp') diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index edf9394..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -Adafruit-Blinka diff --git a/setup.py b/setup.py deleted file mode 100644 index 07f056b..0000000 --- a/setup.py +++ /dev/null @@ -1,63 +0,0 @@ -"""A setuptools based setup module. - -See: -https://packaging.python.org/en/latest/distributing.html -https://github.com/pypa/sampleproject -""" - -from setuptools import setup, find_packages -# To use a consistent encoding -from codecs import open -from os import path - -here = path.abspath(path.dirname(__file__)) - -# Get the long description from the README file -with open(path.join(here, 'README.rst'), encoding='utf-8') as f: - long_description = f.read() - -setup( - name='adafruit-circuitpython-bitmapsaver', - - use_scm_version=True, - setup_requires=['setuptools_scm'], - - description='Save a displayio.Bitmap (and associated displayio.Palette) into a BMP file.', - long_description=long_description, - long_description_content_type='text/x-rst', - - # The project's main homepage. - url='https://github.com/adafruit/Adafruit_CircuitPython_BitmapSaver', - - # Author details - author='Adafruit Industries', - author_email='circuitpython@adafruit.com', - - install_requires=[ - 'Adafruit-Blinka' - ], - - # Choose your license - license='MIT', - - # See https://pypi.python.org/pypi?%3Aaction=list_classifiers - classifiers=[ - 'Development Status :: 3 - Alpha', - 'Intended Audience :: Developers', - 'Topic :: Software Development :: Libraries', - 'Topic :: System :: Hardware', - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - ], - - # What does your project relate to? - keywords='adafruit blinka circuitpython micropython bitmapsaver displayio', - - # You can just specify the packages manually here if your project is - # simple. Or you can use find_packages(). - # TODO: IF LIBRARY FILES ARE A PACKAGE FOLDER, - # CHANGE `py_modules=['...']` TO `packages=['...']` - py_modules=['adafruit_bitmapsaver'], -) From 7cb0c6164745ea7e9f3ed0b478807a2ddfef4e2e Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Fri, 19 Jul 2019 17:46:40 -0400 Subject: [PATCH 03/16] Revert "Revert "Move code over"" This reverts commit b186fdb07b5f14bf75895546cd4f965c0e3efb5a. --- .gitignore | 12 + .pylintrc | 433 +++++++++++++++++++++++++++++ .readthedocs.yml | 3 + .travis.yml | 48 ++++ CODE_OF_CONDUCT.md | 127 +++++++++ LICENSE | 21 ++ README.rst | 163 +++++++++++ adafruit_bitmapsaver.py | 117 ++++++++ docs/_static/favicon.ico | Bin 0 -> 4414 bytes docs/api.rst | 8 + docs/conf.py | 160 +++++++++++ docs/examples.rst | 8 + docs/index.rst | 45 +++ examples/bitmapsaver_simpletest.py | 50 ++++ requirements.txt | 1 + setup.py | 63 +++++ 16 files changed, 1259 insertions(+) create mode 100644 .gitignore create mode 100644 .pylintrc create mode 100644 .readthedocs.yml create mode 100644 .travis.yml create mode 100644 CODE_OF_CONDUCT.md create mode 100644 LICENSE create mode 100644 README.rst create mode 100644 adafruit_bitmapsaver.py create mode 100644 docs/_static/favicon.ico create mode 100644 docs/api.rst create mode 100644 docs/conf.py create mode 100644 docs/examples.rst create mode 100644 docs/index.rst create mode 100644 examples/bitmapsaver_simpletest.py create mode 100644 requirements.txt create mode 100644 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..55f127b --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +*.mpy +.idea +__pycache__ +_build +*.pyc +.env +build* +bundles +*.DS_Store +.eggs +dist +**/*.egg-info \ No newline at end of file diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..039eaec --- /dev/null +++ b/.pylintrc @@ -0,0 +1,433 @@ +[MASTER] + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Add files or directories matching the regex patterns to the blacklist. The +# regex matches against base names, not paths. +ignore-patterns= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. +# jobs=1 +jobs=2 + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Specify a configuration file. +#rcfile= + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +# disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call +disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable= + + +[REPORTS] + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio).You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Tells whether to display a full report or only the messages +reports=no + +# Activate the evaluation score. +score=yes + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +# notes=FIXME,XXX,TODO +notes=FIXME,XXX + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules=board + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,future.builtins + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +# expected-line-ending-format= +expected-line-ending-format=LF + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module +max-module-lines=1000 + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma,dict-separator + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[SIMILARITIES] + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[BASIC] + +# Naming hint for argument names +argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct argument names +argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Naming hint for attribute names +attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct attribute names +attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class names +# class-name-hint=[A-Z_][a-zA-Z0-9]+$ +class-name-hint=[A-Z_][a-zA-Z0-9_]+$ + +# Regular expression matching correct class names +# class-rgx=[A-Z_][a-zA-Z0-9]+$ +class-rgx=[A-Z_][a-zA-Z0-9_]+$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming hint for function names +function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct function names +function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Good variable names which should always be accepted, separated by a comma +# good-names=i,j,k,ex,Run,_ +good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_ + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for method names +method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct method names +method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +property-classes=abc.abstractproperty + +# Naming hint for variable names +variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct variable names +variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + + +[IMPORTS] + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=optparse,tkinter.tix + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Maximum number of attributes for a class (see R0902). +# max-attributes=7 +max-attributes=11 + +# Maximum number of boolean expressions in a if statement +max-bool-expr=5 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of statements in function / method body +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=1 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..f4243ad --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,3 @@ +python: + version: 3 +requirements_file: requirements.txt diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..8e12837 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,48 @@ +# This is a common .travis.yml for generating library release zip files for +# CircuitPython library releases using circuitpython-build-tools. +# See https://github.com/adafruit/circuitpython-build-tools for detailed setup +# instructions. + +dist: xenial +language: python +python: + - "3.6" + +cache: + pip: true + +# TODO: if deployment to PyPi is desired, change 'DEPLOY_PYPI' to "true", +# or remove the env block entirely and remove the condition in the +# deploy block. +env: + - DEPLOY_PYPI="false" + +deploy: + - provider: releases + api_key: "$GITHUB_TOKEN" + file_glob: true + file: "$TRAVIS_BUILD_DIR/bundles/*" + skip_cleanup: true + overwrite: true + on: + tags: true + # TODO: Use 'travis encrypt --com -r adafruit/' to generate + # the encrypted password for adafruit-travis. Paste result below. + - provider: pypi + user: adafruit-travis + password: + secure: #-- PASTE ENCRYPTED PASSWORD HERE --# + on: + tags: true + condition: $DEPLOY_PYPI = "true" + +install: + - pip install -r requirements.txt + - pip install circuitpython-build-tools Sphinx sphinx-rtd-theme + - pip install --force-reinstall pylint==1.9.2 + +script: + - pylint adafruit_bitmapsaver.py + - ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace examples/*.py) + - circuitpython-build-bundles --filename_prefix adafruit-circuitpython-bitmapsaver --library_location . + - cd docs && sphinx-build -E -W -b html . _build/html && cd .. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..7ca3a1d --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,127 @@ +# Adafruit Community Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and leaders pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level or type of +experience, education, socio-economic status, nationality, personal appearance, +race, religion, or sexual identity and orientation. + +## Our Standards + +We are committed to providing a friendly, safe and welcoming environment for +all. + +Examples of behavior that contributes to creating a positive environment +include: + +* Be kind and courteous to others +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Collaborating with other community members +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and sexual attention or advances +* The use of inappropriate images, including in a community member's avatar +* The use of inappropriate language, including in a community member's nickname +* Any spamming, flaming, baiting or other attention-stealing behavior +* Excessive or unwelcome helping; answering outside the scope of the question + asked +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate + +The goal of the standards and moderation guidelines outlined here is to build +and maintain a respectful community. We ask that you don’t just aim to be +"technically unimpeachable", but rather try to be your best self. + +We value many things beyond technical expertise, including collaboration and +supporting others within our community. Providing a positive experience for +other community members can have a much more significant impact than simply +providing the correct answer. + +## Our Responsibilities + +Project leaders are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project leaders have the right and responsibility to remove, edit, or +reject messages, comments, commits, code, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any community member for other behaviors that they deem +inappropriate, threatening, offensive, or harmful. + +## Moderation + +Instances of behaviors that violate the Adafruit Community Code of Conduct +may be reported by any member of the community. Community members are +encouraged to report these situations, including situations they witness +involving other community members. + +You may report in the following ways: + +In any situation, you may send an email to . + +On the Adafruit Discord, you may send an open message from any channel +to all Community Helpers by tagging @community moderators. You may also send an +open message from any channel, or a direct message to @kattni#1507, +@tannewt#4653, @Dan Halbert#1614, @cater#2442, @sommersoft#0222, or +@Andon#8175. + +Email and direct message reports will be kept confidential. + +In situations on Discord where the issue is particularly egregious, possibly +illegal, requires immediate action, or violates the Discord terms of service, +you should also report the message directly to Discord. + +These are the steps for upholding our community’s standards of conduct. + +1. Any member of the community may report any situation that violates the +Adafruit Community Code of Conduct. All reports will be reviewed and +investigated. +2. If the behavior is an egregious violation, the community member who +committed the violation may be banned immediately, without warning. +3. Otherwise, moderators will first respond to such behavior with a warning. +4. Moderators follow a soft "three strikes" policy - the community member may +be given another chance, if they are receptive to the warning and change their +behavior. +5. If the community member is unreceptive or unreasonable when warned by a +moderator, or the warning goes unheeded, they may be banned for a first or +second offense. Repeated offenses will result in the community member being +banned. + +## Scope + +This Code of Conduct and the enforcement policies listed above apply to all +Adafruit Community venues. This includes but is not limited to any community +spaces (both public and private), the entire Adafruit Discord server, and +Adafruit GitHub repositories. Examples of Adafruit Community spaces include +but are not limited to meet-ups, audio chats on the Adafruit Discord, or +interaction at a conference. + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. As a community +member, you are representing our community, and are expected to behave +accordingly. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at +, +and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). + +For other projects adopting the Adafruit Community Code of +Conduct, please contact the maintainers of those projects for enforcement. +If you wish to use this code of conduct for your own project, consider +explicitly mentioning your moderation policy or making a copy with your +own moderation policy so as to avoid confusion. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d69195f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Dave Astels for Adafruit Industries + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..2bf382c --- /dev/null +++ b/README.rst @@ -0,0 +1,163 @@ +Introduction +============ + +.. image:: https://readthedocs.org/projects/adafruit-circuitpython-bitmapsaver/badge/?version=latest + :target: https://circuitpython.readthedocs.io/projects/bitmapsaver/en/latest/ + :alt: Documentation Status + +.. image:: https://img.shields.io/discord/327254708534116352.svg + :target: https://discord.gg/nBQh6qu + :alt: Discord + +.. image:: https://travis-ci.com/adafruit/Adafruit_CircuitPython_BitmapSaver.svg?branch=master + :target: https://travis-ci.com/adafruit/Adafruit_CircuitPython_BitmapSaver + :alt: Build Status + +Save a displayio.Bitmap (and associated displayio.Palette) into a BMP file. + + +Dependencies +============= +This driver depends on: + +* `Adafruit CircuitPython `_ + +Please ensure all dependencies are available on the CircuitPython filesystem. +This is easily achieved by downloading +`the Adafruit library and driver bundle `_. + +Installing from PyPI +===================== +.. note:: This library is not available on PyPI yet. Install documentation is included + as a standard element. Stay tuned for PyPI availability! + +On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from +PyPI `_. To install for current user: + +.. code-block:: shell + + pip3 install adafruit-circuitpython-bitmapsaver + +To install system-wide (this may be required in some cases): + +.. code-block:: shell + + sudo pip3 install adafruit-circuitpython-bitmapsaver + +To install in a virtual environment in your current project: + +.. code-block:: shell + + mkdir project-name && cd project-name + python3 -m venv .env + source .env/bin/activate + pip3 install adafruit-circuitpython-bitmapsaver + +Usage Example +============= + +.. code-block:: python + + import board + import busio + import digitalio + from displayio import Bitmap, Palette + import adafruit_sdcard + import storage + from adafruit_bitmap_saver import save_bitmap + + print('Setting up SD card') + spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + cs = digitalio.DigitalInOut(board.SD_CS) + sdcard = adafruit_sdcard.SDCard(spi, cs) + vfs = storage.VfsFat(sdcard) + storage.mount(vfs, "/sd") + + WHITE = 0xFFFFFF + BLACK = 0x000000 + RED = 0xFF0000 + ORANGE = 0xFFA500 + YELLOW = 0xFFFF00 + GREEN = 0x00FF00 + BLUE = 0x0000FF + PURPLE = 0x800080 + PINK = 0xFFC0CB + + colors = (BLACK, RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE, WHITE) + + print('Building sample bitmap and palette') + bitmap = Bitmap(16, 16, 9) + palette = Palette(len(colors)) + for i, c in enumerate(colors): + palette[i] = c + + for x in range(16): + for y in range(16): + if x == 0 or y == 0 or x == 15 or y == 15: + bitmap[x, y] = 1 + elif x == y: + bitmap[x, y] = 4 + elif x == 15 - y: + bitmap[x, y] = 5 + else: + bitmap[x, y] = 0 + + print('Saving bitmap') + save_bitmap(bitmap, palette, '/sd/test.bmp') + +Contributing +============ + +Contributions are welcome! Please read our `Code of Conduct +`_ +before contributing to help this project stay welcoming. + +Building locally +================ + +Zip release files +----------------- + +To build this library locally you'll need to install the +`circuitpython-build-tools `_ package. + +.. code-block:: shell + + python3 -m venv .env + source .env/bin/activate + pip install circuitpython-build-tools + +Once installed, make sure you are in the virtual environment: + +.. code-block:: shell + + source .env/bin/activate + +Then run the build: + +.. code-block:: shell + + circuitpython-build-bundles --filename_prefix adafruit-circuitpython-bitmapsaver --library_location . + +Sphinx documentation +----------------------- + +Sphinx is used to build the documentation based on rST files and comments in the code. First, +install dependencies (feel free to reuse the virtual environment from above): + +.. code-block:: shell + + python3 -m venv .env + source .env/bin/activate + pip install Sphinx sphinx-rtd-theme + +Now, once you have the virtual environment activated: + +.. code-block:: shell + + cd docs + sphinx-build -E -W -b html . _build/html + +This will output the documentation to ``docs/_build/html``. Open the index.html in your browser to +view them. It will also (due to -W) error out on any warning like Travis will. This is a good way to +locally verify it will pass. diff --git a/adafruit_bitmapsaver.py b/adafruit_bitmapsaver.py new file mode 100644 index 0000000..37a528b --- /dev/null +++ b/adafruit_bitmapsaver.py @@ -0,0 +1,117 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 Dave Astels for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_bitmapsaver` +================================================================================ + +Save a displayio.Bitmap (and associated displayio.Palette) into a BMP file. + + +* Author(s): Dave Astels + +Implementation Notes +-------------------- + +**Hardware:** + + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://github.com/adafruit/circuitpython/releases + +""" + +# imports + +import struct +from displayio import Bitmap, Palette + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BitmapSaver.git" + +#pylint:disable=line-too-long,broad-except,redefined-outer-name + +def _write_bmp_header(output_file, filesize): + output_file.write(bytes('BM', 'ascii')) + output_file.write(struct.pack('> 8) | ((value & 0x00FF) << 8) + +def _bytes_per_row(bitmap): + pixel_bytes = 3 * bitmap.width + padding_bytes = (4 - (pixel_bytes % 4)) % 4 + return pixel_bytes + padding_bytes + +def _write_pixels(output_file, bitmap, palette): + row_buffer = bytearray(_bytes_per_row(bitmap)) + + for y in range(bitmap.height, 0, -1): + buffer_index = 0 + for x in range(bitmap.width): + pixel = bitmap[x, y-1] # this is an index into the palette + color = _swap_bytes(palette[pixel]) # This is a 16 bit value in r5g6b5 format + blue = (color << 3) & 0x00F8 # extract each of the RGB tripple into it's own byte + green = (color >> 3) & 0x00FC + red = (color >> 8) & 0x00F8 + for value in (blue, green, red): + row_buffer[buffer_index] = value + buffer_index += 1 + output_file.write(row_buffer) + +def save_bitmap(bitmap, palette, file_or_filename): + """Save a bitmap (using an associated palette) to a 24 bit per pixel BMP file. + + :param bitmap: the displayio.Bitmap to save + :param palette: the displayio.Palette to use for looking up colors in the bitmap + """ + if not isinstance(bitmap, Bitmap): + raise ValueError('bitmap') + if not isinstance(palette, Palette): + raise ValueError('palette') + try: + if isinstance(file_or_filename, str): + output_file = open(file_or_filename, 'wb') + else: + output_file = file_or_filename + + filesize = 54 + bitmap.height * _bytes_per_row(bitmap) + _write_bmp_header(output_file, filesize) + _write_dib_header(output_file, bitmap) + _write_pixels(output_file, bitmap, palette) + except Exception: + print('Error saving bitmap') + raise + else: + output_file.close() diff --git a/docs/_static/favicon.ico b/docs/_static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..5aca98376a1f7e593ebd9cf41a808512c2135635 GIT binary patch literal 4414 zcmd^BX;4#F6n=SG-XmlONeGrD5E6J{RVh+e928U#MG!$jWvO+UsvWh`x&VqGNx*en zx=qox7Dqv{kPwo%fZC$dDwVpRtz{HzTkSs8QhG0)%Y=-3@Kt!4ag|JcIo?$-F|?bXVS9UDUyev>MVZQ(H8K4#;BQW-t2CPorj8^KJrMX}QK zp+e<;4ldpXz~=)2GxNy811&)gt-}Q*yVQpsxr@VMoA##{)$1~=bZ1MmjeFw?uT(`8 z^g=09<=zW%r%buwN%iHtuKSg|+r7HkT0PYN*_u9k1;^Ss-Z!RBfJ?Un4w(awqp2b3 z%+myoFis_lTlCrGx2z$0BQdh+7?!JK#9K9@Z!VrG zNj6gK5r(b4?YDOLw|DPRoN7bdP{(>GEG41YcN~4r_SUHU2hgVtUwZG@s%edC;k7Sn zC)RvEnlq~raE2mY2ko64^m1KQL}3riixh?#J{o)IT+K-RdHae2eRX91-+g!y`8^># z-zI0ir>P%Xon)!@xp-BK2bDYUB9k613NRrY6%lVjbFcQc*pRqiK~8xtkNPLxt}e?&QsTB}^!39t_%Qb)~Ukn0O%iC;zt z<&A-y;3h++)>c1br`5VFM~5(83!HKx$L+my8sW_c#@x*|*vB1yU)_dt3vH;2hqPWx zAl^6@?ipx&U7pf`a*>Yq6C85nb+B=Fnn+(id$W#WB^uHAcZVG`qg;rWB}ubvi(Y>D z$ei>REw$#xp0SHAd^|1hq&9HJ=jKK8^zTH~nk)G?yUcmTh9vUM6Y0LMw4(gYVY$D$ zGl&WY&H<)BbJ&3sYbKjx1j^=3-0Q#f^}(aP1?8^`&FUWMp|rmtpK)bLQ1Zo?^s4jqK=Lfg*9&geMGVQ z#^-*!V`fG@;H&{M9S8%+;|h&Qrxym0Ar>WT4BCVLR8cGXF=JmEYN(sNT(9vl+S|%g z8r7nXQ(95i^`=+XHo|){$vf2$?=`F$^&wFlYXyXg$B{a>$-Fp+V}+D;9k=~Xl~?C4 zAB-;RKXdUzBJE{V&d&%R>aEfFe;vxqI$0@hwVM}gFeQR@j}a>DDxR+n+-*6|_)k%% z*mSpDV|=5I9!&VC&9tD%fcVygWZV!iIo2qFtm#!*(s|@ZT33*Ad;+<|3^+yrp*;oH zBSYLV(H1zTU?2WjrCQoQW)Z>J2a=dTriuvezBmu16`tM2fm7Q@d4^iqII-xFpwHGI zn9CL}QE*1vdj2PX{PIuqOe5dracsciH6OlAZATvE8rj6ykqdIjal2 z0S0S~PwHb-5?OQ-tU-^KTG@XNrEVSvo|HIP?H;7ZhYeZkhSqh-{reE!5di;1zk$#Y zCe7rOnlzFYJ6Z#Hm$GoidKB=2HBCwm`BbZVeZY4ukmG%1uz7p2URs6c9j-Gjj^oQV zsdDb3@k2e`C$1I5ML5U0Qs0C1GAp^?!*`=|Nm(vWz3j*j*8ucum2;r0^-6Aca=Gv) zc%}&;!+_*S2tlnnJnz0EKeRmw-Y!@9ob!XQBwiv}^u9MkaXHvM=!<3YX;+2#5Cj5pp?FEK750S3BgeSDtaE^ zXUM@xoV6yBFKfzvY20V&Lr0yC + CircuitPython Reference Documentation + CircuitPython Support Forum + Discord Chat + Adafruit Learning System + Adafruit Blog + Adafruit Store + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/examples/bitmapsaver_simpletest.py b/examples/bitmapsaver_simpletest.py new file mode 100644 index 0000000..dd20257 --- /dev/null +++ b/examples/bitmapsaver_simpletest.py @@ -0,0 +1,50 @@ +"""Example of using save_bitmap""" + +import board +import busio +import digitalio +from displayio import Bitmap, Palette +import adafruit_sdcard +import storage +from adafruit_bitmapsaver import save_bitmap + +#pylint:disable=invalid-name + +print('Setting up SD card') +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) +cs = digitalio.DigitalInOut(board.SD_CS) +sdcard = adafruit_sdcard.SDCard(spi, cs) +vfs = storage.VfsFat(sdcard) +storage.mount(vfs, "/sd") + +WHITE = 0xFFFFFF +BLACK = 0x000000 +RED = 0xFF0000 +ORANGE = 0xFFA500 +YELLOW = 0xFFFF00 +GREEN = 0x00FF00 +BLUE = 0x0000FF +PURPLE = 0x800080 +PINK = 0xFFC0CB + +colors = (BLACK, RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE, WHITE) + +print('Building sample bitmap and palette') +bitmap = Bitmap(16, 16, 9) +palette = Palette(len(colors)) +for i, c in enumerate(colors): + palette[i] = c + +for x in range(16): + for y in range(16): + if x == 0 or y == 0 or x == 15 or y == 15: + bitmap[x, y] = 1 + elif x == y: + bitmap[x, y] = 4 + elif x == 15 - y: + bitmap[x, y] = 5 + else: + bitmap[x, y] = 0 + +print('Saving bitmap') +save_bitmap(bitmap, palette, '/sd/test.bmp') diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..edf9394 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +Adafruit-Blinka diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..07f056b --- /dev/null +++ b/setup.py @@ -0,0 +1,63 @@ +"""A setuptools based setup module. + +See: +https://packaging.python.org/en/latest/distributing.html +https://github.com/pypa/sampleproject +""" + +from setuptools import setup, find_packages +# To use a consistent encoding +from codecs import open +from os import path + +here = path.abspath(path.dirname(__file__)) + +# Get the long description from the README file +with open(path.join(here, 'README.rst'), encoding='utf-8') as f: + long_description = f.read() + +setup( + name='adafruit-circuitpython-bitmapsaver', + + use_scm_version=True, + setup_requires=['setuptools_scm'], + + description='Save a displayio.Bitmap (and associated displayio.Palette) into a BMP file.', + long_description=long_description, + long_description_content_type='text/x-rst', + + # The project's main homepage. + url='https://github.com/adafruit/Adafruit_CircuitPython_BitmapSaver', + + # Author details + author='Adafruit Industries', + author_email='circuitpython@adafruit.com', + + install_requires=[ + 'Adafruit-Blinka' + ], + + # Choose your license + license='MIT', + + # See https://pypi.python.org/pypi?%3Aaction=list_classifiers + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'Topic :: Software Development :: Libraries', + 'Topic :: System :: Hardware', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + ], + + # What does your project relate to? + keywords='adafruit blinka circuitpython micropython bitmapsaver displayio', + + # You can just specify the packages manually here if your project is + # simple. Or you can use find_packages(). + # TODO: IF LIBRARY FILES ARE A PACKAGE FOLDER, + # CHANGE `py_modules=['...']` TO `packages=['...']` + py_modules=['adafruit_bitmapsaver'], +) From 93d57cdcfbac3df21d1d4ef3ec1e52a54d266996 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Fri, 19 Jul 2019 18:09:49 -0400 Subject: [PATCH 04/16] Add mock imports --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index e73dabf..528d734 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,7 +20,7 @@ # Uncomment the below if you use native CircuitPython modules such as # digitalio, micropython and busio. List the modules you use. Without it, the # autodoc module docs will fail to generate with a warning. -# autodoc_mock_imports = ["digitalio", "busio"] +autodoc_mock_imports = ["digitalio", "busio"] intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None),'CircuitPython': ('https://circuitpython.readthedocs.io/en/latest/', None)} From b20f61622654f6c0c07e1f0d72037528d009b230 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Fri, 19 Jul 2019 18:16:24 -0400 Subject: [PATCH 05/16] Actually add the required module --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 528d734..07ac589 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,7 +20,7 @@ # Uncomment the below if you use native CircuitPython modules such as # digitalio, micropython and busio. List the modules you use. Without it, the # autodoc module docs will fail to generate with a warning. -autodoc_mock_imports = ["digitalio", "busio"] +autodoc_mock_imports = ["displayio", "digitalio", "busio"] intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None),'CircuitPython': ('https://circuitpython.readthedocs.io/en/latest/', None)} From 45c652cb6e55d7370b4cd4c0864d99aa3f34b34f Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Thu, 25 Jul 2019 12:34:13 -0400 Subject: [PATCH 06/16] Some cleanup --- adafruit_bitmapsaver.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/adafruit_bitmapsaver.py b/adafruit_bitmapsaver.py index 37a528b..3bfc399 100644 --- a/adafruit_bitmapsaver.py +++ b/adafruit_bitmapsaver.py @@ -52,6 +52,7 @@ #pylint:disable=line-too-long,broad-except,redefined-outer-name def _write_bmp_header(output_file, filesize): + # print('writing bmp header') output_file.write(bytes('BM', 'ascii')) output_file.write(struct.pack('> 8) | ((value & 0x00FF) << 8) - def _bytes_per_row(bitmap): pixel_bytes = 3 * bitmap.width padding_bytes = (4 - (pixel_bytes % 4)) % 4 return pixel_bytes + padding_bytes def _write_pixels(output_file, bitmap, palette): + # print('writing pixels') row_buffer = bytearray(_bytes_per_row(bitmap)) for y in range(bitmap.height, 0, -1): buffer_index = 0 for x in range(bitmap.width): - pixel = bitmap[x, y-1] # this is an index into the palette - color = _swap_bytes(palette[pixel]) # This is a 16 bit value in r5g6b5 format - blue = (color << 3) & 0x00F8 # extract each of the RGB tripple into it's own byte - green = (color >> 3) & 0x00FC - red = (color >> 8) & 0x00F8 - for value in (blue, green, red): - row_buffer[buffer_index] = value + pixel = bitmap[x, y-1] + color = palette[pixel] + for _ in range(3): + row_buffer[buffer_index] = color & 0xFF + color >>= 8 buffer_index += 1 output_file.write(row_buffer) @@ -96,6 +94,7 @@ def save_bitmap(bitmap, palette, file_or_filename): :param bitmap: the displayio.Bitmap to save :param palette: the displayio.Palette to use for looking up colors in the bitmap """ + print("Saving bitmap") if not isinstance(bitmap, Bitmap): raise ValueError('bitmap') if not isinstance(palette, Palette): @@ -107,6 +106,9 @@ def save_bitmap(bitmap, palette, file_or_filename): output_file = file_or_filename filesize = 54 + bitmap.height * _bytes_per_row(bitmap) + # print('Bitmap height: ', bitmap.height) + # print('Bitmap width: ', bitmap.width) + # print('Filesize: ', filesize) _write_bmp_header(output_file, filesize) _write_dib_header(output_file, bitmap) _write_pixels(output_file, bitmap, palette) @@ -115,3 +117,4 @@ def save_bitmap(bitmap, palette, file_or_filename): raise else: output_file.close() + print("Finished saving bitmap") From 54418624ee0ddfcee28996b9229891b7952776c4 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Thu, 25 Jul 2019 12:35:26 -0400 Subject: [PATCH 07/16] Add note about needing 5.0 --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index 2bf382c..875b127 100644 --- a/README.rst +++ b/README.rst @@ -22,6 +22,8 @@ This driver depends on: * `Adafruit CircuitPython `_ +CircuitPython 5.0 or later is required. + Please ensure all dependencies are available on the CircuitPython filesystem. This is easily achieved by downloading `the Adafruit library and driver bundle `_. From 8f5d91054ca792e1941c968ab73156aaa55f5957 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Thu, 25 Jul 2019 12:36:56 -0400 Subject: [PATCH 08/16] Get rid of unnecessary file --- README.md | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index 8f9722f..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Adafruit_CircuitPython_BitmapSaver -Save displayio bitmaps to BMP disk From 8578d2ce86782dbeb51e647102cac52117b5d8e6 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Thu, 25 Jul 2019 14:09:07 -0400 Subject: [PATCH 09/16] Remove debugging prints --- adafruit_bitmapsaver.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/adafruit_bitmapsaver.py b/adafruit_bitmapsaver.py index 3bfc399..a6f772d 100644 --- a/adafruit_bitmapsaver.py +++ b/adafruit_bitmapsaver.py @@ -52,7 +52,6 @@ #pylint:disable=line-too-long,broad-except,redefined-outer-name def _write_bmp_header(output_file, filesize): - # print('writing bmp header') output_file.write(bytes('BM', 'ascii')) output_file.write(struct.pack(' Date: Thu, 25 Jul 2019 14:09:25 -0400 Subject: [PATCH 10/16] Make exceptions more expressive --- adafruit_bitmapsaver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_bitmapsaver.py b/adafruit_bitmapsaver.py index a6f772d..c7baaa7 100644 --- a/adafruit_bitmapsaver.py +++ b/adafruit_bitmapsaver.py @@ -92,9 +92,9 @@ def save_bitmap(bitmap, palette, file_or_filename): :param palette: the displayio.Palette to use for looking up colors in the bitmap """ if not isinstance(bitmap, Bitmap): - raise ValueError('bitmap') + raise ValueError('First argument must be a Bitmap') if not isinstance(palette, Palette): - raise ValueError('palette') + raise ValueError('Second argument must be a Palette') try: if isinstance(file_or_filename, str): output_file = open(file_or_filename, 'wb') From 782f54f16e43dd7eeb8db3b6dba1dcd7359791df Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Wed, 31 Jul 2019 09:56:29 -0400 Subject: [PATCH 11/16] Integrate saving bitmaps and taking screenshots --- adafruit_bitmapsaver.py | 78 +++++++++++++++++++----------- examples/bitmapsaver_simpletest.py | 26 +++++++++- examples/screenshot_simpletest.py | 41 ++++++++++++++++ 3 files changed, 115 insertions(+), 30 deletions(-) create mode 100644 examples/screenshot_simpletest.py diff --git a/adafruit_bitmapsaver.py b/adafruit_bitmapsaver.py index c7baaa7..1612c11 100644 --- a/adafruit_bitmapsaver.py +++ b/adafruit_bitmapsaver.py @@ -23,7 +23,8 @@ `adafruit_bitmapsaver` ================================================================================ -Save a displayio.Bitmap (and associated displayio.Palette) into a BMP file. +Save a displayio.Bitmap (and associated displayio.Palette) in a BMP file. +Make a screenshot (the contents of a displayio.Display) and save in a BMP file. * Author(s): Dave Astels @@ -43,8 +44,10 @@ # imports +import gc import struct -from displayio import Bitmap, Palette +import board +from displayio import Bitmap, Palette, Display __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BitmapSaver.git" @@ -58,53 +61,72 @@ def _write_bmp_header(output_file, filesize): output_file.write(b'\00\x00') output_file.write(struct.pack('> 3) & 0x00FC + red = (color >> 8) & 0x00F8 + return (blue, green, red) - for y in range(bitmap.height, 0, -1): +def _write_pixels(output_file, pixel_source, palette): + row_buffer = bytearray(_bytes_per_row(pixel_source)) + saving_bitmap = isinstance(pixel_source, Bitmap) + for y in range(pixel_source.height, 0, -1): buffer_index = 0 - for x in range(bitmap.width): - pixel = bitmap[x, y-1] - color = palette[pixel] - for _ in range(3): - row_buffer[buffer_index] = color & 0xFF - color >>= 8 - buffer_index += 1 + if saving_bitmap: + for x in range(pixel_source.width): + pixel = pixel_source[x, y-1] + color = palette[pixel] + for _ in range(3): + row_buffer[buffer_index] = color & 0xFF + color >>= 8 + buffer_index += 1 + else: + data = pixel_source.fill_area(x=0, y=y-1, width=pixel_source.width, height=1) + for i in range(pixel_source.width): + pixel565 = (data[i * 2] << 8) + data[i * 2 + 1] + for b in rgb565_to_bgr_tuple(pixel565): + row_buffer[buffer_index] = b & 0xFF + buffer_index += 1 output_file.write(row_buffer) + gc.collect() -def save_bitmap(bitmap, palette, file_or_filename): - """Save a bitmap (using an associated palette) to a 24 bit per pixel BMP file. +def save_pixels(file_or_filename, pixel_source=board.DISPLAY, palette=None): + """Save pixels to a 24 bit per pixel BMP file. + If pixel_source if a displayio.Bitmap, save it's pixels through palette. + If it's a displayio.Display, a palette isn't required. - :param bitmap: the displayio.Bitmap to save - :param palette: the displayio.Palette to use for looking up colors in the bitmap + :param file_or_filename: either the file to save to, or it's absolute name + :param pixel_source: the Bitmap or Display to save + :param palette: the Palette to use for looking up colors in the bitmap """ - if not isinstance(bitmap, Bitmap): - raise ValueError('First argument must be a Bitmap') - if not isinstance(palette, Palette): - raise ValueError('Second argument must be a Palette') + if isinstance(pixel_source, Bitmap): + if not isinstance(palette, Palette): + raise ValueError('Third argument must be a Palette for a Bitmap save') + elif not isinstance(pixel_source, Display): + raise ValueError('Second argument must be a Bitmap or Display') try: if isinstance(file_or_filename, str): output_file = open(file_or_filename, 'wb') else: output_file = file_or_filename - filesize = 54 + bitmap.height * _bytes_per_row(bitmap) + filesize = 54 + pixel_source.height * _bytes_per_row(pixel_source) _write_bmp_header(output_file, filesize) - _write_dib_header(output_file, bitmap) - _write_pixels(output_file, bitmap, palette) + _write_dib_header(output_file, pixel_source) + _write_pixels(output_file, pixel_source, palette) except Exception: raise else: diff --git a/examples/bitmapsaver_simpletest.py b/examples/bitmapsaver_simpletest.py index dd20257..fb5822d 100644 --- a/examples/bitmapsaver_simpletest.py +++ b/examples/bitmapsaver_simpletest.py @@ -1,3 +1,25 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 Dave Astels for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + """Example of using save_bitmap""" import board @@ -6,7 +28,7 @@ from displayio import Bitmap, Palette import adafruit_sdcard import storage -from adafruit_bitmapsaver import save_bitmap +from adafruit_bitmapsaver import save_pixels #pylint:disable=invalid-name @@ -47,4 +69,4 @@ bitmap[x, y] = 0 print('Saving bitmap') -save_bitmap(bitmap, palette, '/sd/test.bmp') +save_pixels('/sd/test.bmp', bitmap, palette) diff --git a/examples/screenshot_simpletest.py b/examples/screenshot_simpletest.py new file mode 100644 index 0000000..ea223f7 --- /dev/null +++ b/examples/screenshot_simpletest.py @@ -0,0 +1,41 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019 Dave Astels for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +"""Example of taking a screenshot.""" + +import board +import digitalio +import busio +import adafruit_sdcard +import storage +from adafruit_bitmapsaver import save_pixels + + +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) +cs = digitalio.DigitalInOut(board.SD_CS) +sdcard = adafruit_sdcard.SDCard(spi, cs) +vfs = storage.VfsFat(sdcard) +storage.mount(vfs, "/sd") + +print('Taking Screenshot...') +save_pixels('/sd/screenshot.bmp') +print('Screenshot taken') From 92421c07ec8daa4bbc0dbe7f14cf963e4b151592 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Wed, 31 Jul 2019 15:45:06 -0400 Subject: [PATCH 12/16] Handle buffer rotation for various boards --- adafruit_bitmapsaver.py | 38 +++++++++++++++++++++------------- guide/pygamer_screenshot.bmp | Bin 0 -> 61494 bytes guide/pyportal_screenshot.bmp | Bin 0 -> 230454 bytes 3 files changed, 24 insertions(+), 14 deletions(-) create mode 100644 guide/pygamer_screenshot.bmp create mode 100644 guide/pyportal_screenshot.bmp diff --git a/adafruit_bitmapsaver.py b/adafruit_bitmapsaver.py index 1612c11..60061a7 100644 --- a/adafruit_bitmapsaver.py +++ b/adafruit_bitmapsaver.py @@ -61,32 +61,41 @@ def _write_bmp_header(output_file, filesize): output_file.write(b'\00\x00') output_file.write(struct.pack('> 3) & 0x00FC red = (color >> 8) & 0x00F8 return (blue, green, red) def _write_pixels(output_file, pixel_source, palette): - row_buffer = bytearray(_bytes_per_row(pixel_source)) saving_bitmap = isinstance(pixel_source, Bitmap) - for y in range(pixel_source.height, 0, -1): + w, h = _rotated_height_and_width(pixel_source) + row_buffer = bytearray(_bytes_per_row(w)) + for y in range(h, 0, -1): buffer_index = 0 if saving_bitmap: - for x in range(pixel_source.width): + for x in range(w): pixel = pixel_source[x, y-1] color = palette[pixel] for _ in range(3): @@ -94,10 +103,10 @@ def _write_pixels(output_file, pixel_source, palette): color >>= 8 buffer_index += 1 else: - data = pixel_source.fill_area(x=0, y=y-1, width=pixel_source.width, height=1) - for i in range(pixel_source.width): + data = pixel_source.fill_area(x=0, y=y-1, width=w, height=1) + for i in range(w): pixel565 = (data[i * 2] << 8) + data[i * 2 + 1] - for b in rgb565_to_bgr_tuple(pixel565): + for b in _rgb565_to_bgr_tuple(pixel565): row_buffer[buffer_index] = b & 0xFF buffer_index += 1 output_file.write(row_buffer) @@ -123,9 +132,10 @@ def save_pixels(file_or_filename, pixel_source=board.DISPLAY, palette=None): else: output_file = file_or_filename - filesize = 54 + pixel_source.height * _bytes_per_row(pixel_source) + w, h = _rotated_height_and_width(pixel_source) + filesize = 54 + h * _bytes_per_row(w) _write_bmp_header(output_file, filesize) - _write_dib_header(output_file, pixel_source) + _write_dib_header(output_file, w, h) _write_pixels(output_file, pixel_source, palette) except Exception: raise diff --git a/guide/pygamer_screenshot.bmp b/guide/pygamer_screenshot.bmp new file mode 100644 index 0000000000000000000000000000000000000000..158d47eebd1c33188d829e76cd03e25d6ecabf6f GIT binary patch literal 61494 zcmeHKJF+B23|wx(5iqdV!XWVRIReMP+&{?7jXW`F;%zu$cN*4{&Z{`&s+*XPU2kjifE-GJ}^ z|5*EiPozWFaN`~HtDvZwoX=Wm7MuBmbZUlxykbT z{_{dsU|zmsxV(m6@Bgnq{^B938%)_ixqr*JvH`pQubcn!;`_I7is=@?240u^?~6UJ zAH>&xXZ<6<=HEYsHv$?rn6d%CuV%LWYK%XqPGft{`OGl&YTVweI)?y@Kpjl-GMnTD? zAP5k&1SM`~9JFxBTRIv`5o7{2DM{!G)g;8H0JDG#K)151$@@r6XdWJs`|_q4G@=S1 zVt<(aN?Y8~5g)ucFtewiH1RE;_`lR83^kbR{j zVaWsmfH|$$M5<4#&MAM^w5LW+5IYH3L`o8tOb`H=GX<|uo}!r-5vGK^B`HZ*GC=@f z&i!B;PoDAFnvTrwv2`D_X_$C`UA_4R@^@gxsC<_gix&?W(4^}$WZ6^l3WA(P!am2m zh%>JmTeFE90l9)=^BxU5*QQZ!v0H?lrW*{XZ5|)idIOUXGaYh6s zjC+r&A|fGoc?kpn=2UT>S#9^Eh)5`nr~(L~J2XK&-6BC_Ffd9!KrDhGPsxFfmqZHT zQKJ#KBq5hJe$Wm09hfydZdCHBoVv4M6FEK+%f`$_RU5#R{mo#+4r(qUAuH$I0}&Q9 zEKCWR-0dD{vVI2VXUM2IN5+$ob@J|k2n!k(ri4sBbvFVyj`3!l5e$77@o+@YdPPJ+ z7SFo}A}qMbJ+_c)YiLK~r?tJx&kHisa8=Edr%WLrJk^ALz}%&ex&ho10~61fQFH5w zri-L_MRRACElD_XoV?WipD2Hp){Tj>gsp1KZ zA|dh}ATJf4y~CPY@?&t9;3*=drXO71LhBAQGrM)S#g&bVwshqYw%0WqkgF1Z5tuw{ zJa%L(`fSZuXda#ts$I8A2B+qcoCY!ga~uaHA$MyA7E%%m8K65fSuyI9%8STBs*RQ` zpJ1?zrnifoglr)tv0&ysV3{@;Ne&V;N=ZVtSf>dWu|WP#K(L&0LL-9@P|_q5Fi|o9 zEbR!LbIN2IFhn+Z=ndEhiuoBM*#U4##syi5@#$1f4rr`qIAE?Txi8!SMjY2^W~S

l5M~ zsDb0)h$*y~d?d2-kG=uE2KRiH(>a;Ic<@jz1rZB@$APFx*s5P`uO?kND2cT*!3PkU zg;J8Rq|+(fXOA`YI`;5EmD#YvIe9iAv2q> z>6A~CLX|AzNoXx@3cxc|1(QtHVH{|3$yzvMEF}q zf)nwC+)~aKh?L}=*&E=i6>nn&Gj{B&=I89wpV%eeRtee2OCSI+$FYx@;qJw0f0GjPT7FLM%HY5>(>}<7y8+;;W?ZedWRmWC>TXiZSu{*$ z;LVx7f&3kqZhc!pK6rekg&C8Z4&_o11ZWZwvWQ41i$ zJJhak@!_k!L8;2mCtVr(0C|N|<_gVI(+)x~XHe=ek`@z(K7epOWatOycW_LmJ=KOaX$HWJ#nXOo|B2MIZ$rGz+C9 zA@jWi0swPbv4}{>YF+{XfH~dR)bMnRa3jWZSuT|r*61oZo?*39@{vf84>VEY83L(Y zdY}#XXL^%QHgC#9=DWr}h0=GfyQ%Sz5^`xB&Yb8bgW<^Q+(lVJ3~&K3>%cuIbwA1QYg^noN04!+nn3UAJ}b9(y;QZh$}8 zQ*x4!*UU4M){AJ1<}_IzSVAu4v4}{>I$i<+fH{tXQc0ek5qAkk>_ke!K4lS+kTtyo z0)TKVFAozgz%rPGBLj$(gnh~)A|Y#f2?PM?9oYGBrGdQfP8Q^1 z##1uu178F3C%R^y?Q*vp;H$#dyBs*rv|c8cQV*K3LrtH&Ni#`y8pr_Chp!5*urtl_ zsk*l&^V7C6!3Suat(9*^<@Z}&928}@}FVe~cA0QZGVoB^ITxZm+Y!mWxq|6Ti@})`Hk&pl$QHh{;U(p0q;D#pZ(wY zq85#}_rGYdTlOX!@Ly4R-(-?)iVf@^8LZ+a;34+!-`Sh&XSL}=?0@wVuF>~fB=2ji zx69tZx(&$BWAKxbWXg90;kqL{t}?a_U}s^Bqag`!$~+=Ka|0|B;Am+e0Gu+N!2Fvm z+y=P$3`YmYU;w9E%`+jzEk~0)Z63=p;Lp)2nwTPm-Xrmy-y68+4fuU7-{s6D$1^2! zXM6*;A6|uo!r>}oC?E-7CB|SrIk27hS{(4Z+>A42lSncjNdW0$^2X%`cyFj831B70 z9Za-rklBE3+ZXPjx_r~A=Bvq!>E`SWtlj|bGr))=LfqA2o$&`-+Qj3`-4d%Yo;dKwld?teGk8Eq4eIRny@3lhfa@xt&sszf_NhHXm#v78 zDPgk2v=RrJCHdJhvK$G08^l;udsXP1(M%oLnwO5>A+wjVtCBj>l8 zXXf2Eb8q1K4d83Cuk`rxt2?YF-AhUmj?eRK3bw%NbF>FpxP(VtJKwZ{V5?G+u*qeU S`!--Z$J3{c7H}D_jf3}AJ literal 0 HcmV?d00001 diff --git a/guide/pyportal_screenshot.bmp b/guide/pyportal_screenshot.bmp new file mode 100644 index 0000000000000000000000000000000000000000..865af900f93aedd645dee6fa9bd9a9ae7c15c489 GIT binary patch literal 230454 zcmeHPyRmHBaeFK!p%7%yrX_<#pT0s+7&K%k00yBj7y?6~c?}xsS0F)DWA!>~zGr4G zUTqGs&}ejT%-(h0xtGO1{PB1H^KZZW`p@s`KmSqx`TcKx`Q>l*-@p0g-|I8~B!MK5 z1d>1!NCHV92_%6ekOY!I5=a6`APFRaB#;D>KoUp-NgxR%fh3Rwl0Xtj0!bhVB!MK5 z1d>1!NCHV92_%6ekOY!I5=a6`APFRaB#;E&IDsF3|AFg`?;)!vfuBBsdW(Ph3=%*R zcvJ%I{o>C}@=E|2cA}t;#1N>=Z-5$>8dFD>2&iGTG32WSxCzL^lX}WHivns|F9qJx zm>iS9BNCXNO!5FcVh24scE&e%&7SeOCyaWkUU^_=R1bU$qYlAI;9d!Q`P`O3HJGc6 zV`ui;La$vuz<|8W#5H%AEVS zl7j-0z>6f%o^8A`N)js|uDdAAi#J6^m8_ymkHGBW5lZkR^|%q}XgY&z$3thx@|gr) zCjouPms@=)Say9$s;%NL8}A}GpLnjk%VIG}0*49Ild0F#wS0h=KoUp-NgxTlV*<4P)@6>xyot(8-m5TSCs#JW^rF!6eCV}@#K%aALt_<*=neKPrrB!uM z9@v>_PvuX#bZ&4Gc*g|vImiCW056#$c`a{z*+r#RNfuBZ*cs^e{JyFagOkAfCg9Kc zb&Mx);&V%v`@;?VzN&LoseEt}c#8z;0mj8`Hw>S4*RUOc0&Ss&BpCuW(nm`qA!|q4 z5|n@#Ej+RpAfHL#;}d8P|2N+6b(>t*^^Lcg(MjNr3B1npf88e6b=@(*SS5iZkOY!I z5=a6`APFRaB#;D>KoUp-NgxR%fh3Rwl0Xtj0!iTY6R5vZ;Z@t}->@iN-Uu3}F51G! zKR2EbH|M1u2E+6W~}Y%GRDRo}2`faw6R^l?qI)gygirAL(!t2szA?O6QO#g?UJ z>Y_gd0nbdJeOWcynQ5O%30|vkP~NMmQ9LPiE7vGdaGN|QAJx03 zGOC>L?Rs~1#_vTnxU3_Y7>dGyiFcq28Kg$0PApEQ)Y zfQ>qlG9`oGF#&${wY=h2zL(qgR0m*XaPP`Ynkgmioir2wh~8T~r#-{JLIV2DDt?9C zrX6nIvmAI~3XcFqDnre*Qsb+)G3Q6^Rea*G)EBwrPYk0;0!iTN1oGZp9bFoeKoUp- zNgxR%fh3Rwl0Xtj0!iSZ2^{{$ia%326hCy~m?wcGzyvORB`?);P{79|pw9qt0bY8i zAi*pJO{~Y(Rqc(T*qzCE7a6t_u&WK)w3O1j)NqMODri1cY7EwBpH1Uk%Ai+=0@FkV z6RGV>jSA?mB)dp%m@1Ww0vy@c0AO3ixpY+OKhjj|DGjKlPbqCeE!=%(M;1fpO6l?FL{4`yCPO$Fa zSf_Xu{wMsjiTXFUR?;bxTXUeydzDel7J{j&JTX8!(y9PUN;a$-#gKtfUU9RkqgE%Z zU8+(3I$Umg9qyw$0ILa^wvuZgfPRCuakF6?*OWNI%9UVz6KAI0iJu(6`{%UDo^R93 z78iLkm{>f39$mwB1#BU?XVjKTk~C?6%HoLu^jMgeDw~@g*}l}Hy4Rezsl%jK{yMBv z_M5vB6P4E;if<)7UW#?r)T`4~@7R^D!Ibk^&9fSDDerDz^mE!|®7SBGg6)p+kz zRX5jY)$jCVig#@o9k(=B0dWCb+`lnPt0L*J!P=`+l)n!9bZ4+wn~S50+X5&xSv#Vo znYd=dv|il-UCuba)NnVSuKFcjZq??}mSShP1E)}2jA9DdLbt~7$QJsv777%&28?P7 z(-ZvEwd|!;>rzkRrKXWINL7(4rs%;fx~U}_BiWdg63?W^tc zWq^l*Pra|h+m9>>B!MK51d>1!NCHV92_%6ekOY!I5=a6`APFRaB#;D>KoUp-NgxR% zfh3Rwl0Xtj0!bhVB!MK51d>1!NCHV92_%6ekOY!I5=a6`APFRaB#;D>KoUp-NgxR% zfh3Rwl0Xtj0!bhVB!MK51d>1!xHN(KKQF??Ml$^WHFBoJtO8TxK4e-KdGzwAK&O!B zEL7VOGLqg!MwN}sF$uhW0(kzhC*K9GJu#}MnBdYb-GL?qUfna(bnTijhXy2pw@ARB zVKkF4e$wbGF>+bP6Dr%fIsK8VUfIWTE_ZN7oxRFI z0Lxk01?&vh)^Wi};EfWPo@(5}{>j(dR3Gi#W3Q?wtIb@G(}pJ+`uor@N#I=)s7D8odC4nT61d>1!NCHV92_%6ekOY!I5=a6`APKy6 z0`&(eBM%t6W&zZoz{$%)GK0oKTkh^~61pL`{ruHwAmminR@~&xuDfonv zxM?PzFS|~BXyVhCb5#%jV%Xa-j;unz?1Js)bJ;aHyAPFE37_y>I>SK)?%-3%u+e>7 zqt2|hM&(uSn4@7Y)T;NkSrikn<%DT&>{XyXinZ5ybx`FYrXhhFlA(rx zr3O?mVQo|N^bc<-rI z@6}nVRID0xaE(?ctX+zAiu3L^X_NOkF-}dE8Wj{) zO)D*roW<(IP@tfRb$TmRmsZsTSgRA(F4s77;#^(Ys3RIynPX>KdmUX<3@_-H8n^nA zIq`9g+p0#Z$~#s@yA-QNv1)*|I$`Z9jmoN|#|f*$rPw{e)F&<&wj&m6B##BAHK=K} zY$3pijSn3NV9q0Z)srM;+EpZun-vU}Zr-OC-VS~B-?Rv*P}+~!zayddKdph#t?*?3N+@?l9a$#cM!(841?k;+i> zr9Zn)lwWGD5ODu1def2wl0Xtj0!bhVB!MK51d>1!NCHV92_%6ekOY!I5=a6`APFRa zB#;D>KoUp-NgxR%fh3Rwl0Xtj0!bhVB!MK51d>1!NCHV92_%6ekOY!I5=a6`APFRa zB#;D>KoUp-NgxR%fh3Rwl0Xtj0!bhVB!MK51d>1!NCHV92_%6ekOY!I5=a6`APFRa zB#;D>KoUp-NgxR%fh3Rwl0Xtj0!bhVB!MK51d>1!NCHV92_%6ekOY!I5=a6`APFRa zB#;D>KoUp-NgxR%fh3Rwl0Xtj0!bhVB!MK51d>1!NCHV92_%6ekOY!I5=a6`APFRa zB#;D>KoUp-NgxR%fh3Rwl0Xtj0!bhVB!MK51d_mOCh+6$KX5&Ny_);{p_!NjJ~Dyl zzfI4d_>o^+ZtC?Dz>i3M;K?*n?}ZwWOC>d0U7DzXU8*b$9-w`)s+!5C#Q7%)d`1HG zkn=`>oq<4kXSBNa;kW|c<<8EizhYHR&d}sD34C4x)4Nc2>LrrM!QU5WJh4fm{N5i* z)lZC150eBwDFMC(1$f~G{iP_L`X!#Kn%JaKUKdad4V49WE%(v6!Aam_641xK-4xyq ze4=!YC#8;1!NCHV92_%6ekOY!I5_nny^{Xzf`%ZlQ-=Lql;WKC5_j`=nyCiVm zXY9UikI3+u8$NT^BeII;lt6v6yr?0=@12ZmoSC}FR|_$n$32LbraPq@4dRoNtbKX!TtBsFLj&zeTOiQ zNWi}@XZ$;I=A_Ft&Mfb-WVl!*1BdDJ%RQS9{R!Z__nE4?RcYF+$(f^R%SM<`(EMAK zvc_{RZOORuOAV$phh>-U6MU}(JnHscaFFsKmAMX2T^w3KHOlJ_(D=@Xuu zbszWKyQyCNy~~-YSAA47Ih^qBId$p8;Cm(DX**N1ic}uc8v~6h71QK|YI-mN|)|-u0K~k?q+Y_;l#l;&OGiOU}*k!^}zoNgxR%fh3Rw zl0Xtj0!bhVd~gEwYa_1nd(3Z|=UGLW1fG+?`CECee9q>Xk_28V0sKJJ*My9cwKI5K zUKw^lpuNP@kWn`_QdSJ{rbT7V1jp%<<`j^@xaH8MVQ)YZcya>zd~TlD+DesJa?fbh zowcgBto&WN)KCYtJDO&ByE2NKd5%fo$qD!~tPA+Fx@WZN&RU(OoOii{`YU$j#Q1b5 z)lISb1Sf&#C%`AR057iL&+4AhDzGy&Cvc536fePbvuIWAyo%K6&?uvr4gpEvnF;9Q zT66W8oG{kYsxw*zcE)ehHO_cfF(t#b-$Z-&&Zw&AxwB3QP6CfhK%Zew!9%O1bH+`U zK1#Bsfbzig$xD@rwF|s5inXh-J2wWsPBy05=9mPYlK`Gi%u?*rz3Pm2J%`avfXNU% zVv~oOKVso{eqI9g5aQb3nm?;2%--{k)pi%{c*j9%-);KNgxR% zfh3Rwl0Xtj0!bhVB!MJwdjiYfg&*|CO3PWd-$ERcz)zdN{+-%aFAeR!ZkJAvJ_&qd z0`>6Y;tm^zZwfVJs=Z-q+*y^Z0-Se2iMdyFaa>y<$0YDB3FvzSd?}-M>bh|V}E6U zmrRkomN&laqEf3Q3n&ll4D@?`U)71hN#K1G@aOzG#uGU4xuwhf;Rb$R)w!xvJ~#=y zMFRBz1!NCHV92_%6ekOY!I z5=a6`APFRaB=GtP)L*Ias%`aeSQIaB1dUS{ZQ;c zZ&)?JbO2cTxTe|akv_1}qsoZY93+``EPm=@%ThCS(Vv2VXC~0TtQzgiw9ljjuT?lG z?^V?(o|L+kYm_;`I#Hb+)6D_JENvK;8qnSeYs;ij-t}4qb_QJ&@1Dv8KPLhIcAS|u znUvtQ3J2xAsv5fKWrRZjSJy*oSO_o5ozA^5uz&~F6T znQ6C4NuL6|%7i?zJ3|9r^{!NgQvj;uKI%!VjjanB|7>?AlaOT6QT(2L&qNtOB?;&T6v3 z?(d%3exg^6_NudLF2(K{<~a%YH&qO;%??e&V*z%N1jD0hC?=qcT?HCRF+mgXSYXn) zEyV0)qk?7#6BRUuo=vkn`escy8p>S2Mx98RlELqo0KfWLUhym6%k6ur z1F$lCdGD@{E{#bb2_%6ekOY!I5=a6`APKxh0)P4Qj~{>i z)AI8#zy05Ky~Q2~vIu!pr>3znXwl~y||NZ;_!d3TM)z7%=&$x?AU&%}L zpOMmM-^J#I<(peSpzA^o`V0^k;H7baWTPHcM!}M7&h1z&8>UJ$cUc?P1U%BD>6TBrfv zs@kZST_hXB|H*~HB&zODe62a zm7ny=D{eo9`I8s?{^zgGy%g&X+6&$p=T!rI!suMZ+IuNKO;?{2tUEZ?DPD#D2|sP3 z{>`nGbjsw`94PZ%WfZf8V5%xl4A73WD!`JG4XZ{mWMGt6+^p)T)d_2tYLsu-{}gNb z`CEp(4)@U=faw6xe6X8q$OE)4ID=&Gq@jZXI)GFYl@Y5R*cpls!Q`O+iebC&a!p&N z+HsL5LuUXzx`yov*g|s8s4bNwY0?0d#S;VQu`n-HHa9)8eW^!vuQ_p3he@xzWBn%|tmavbxRiG{F#0)dvgfBM z%B#b)iE6xes;Zl7wCZooN<1s0m;#uLE6r)C8;Lv?2NlL2cNp5UjhWiPE-mwFN}HI1Ypmmvcmx;5VQ6tIL@3*Na^X27V_||B} zPunDel0Xu;G=YD;zyJInKi3~bUuqJ45=a8yk^tY|`Zt{a`_Eth2hVkV%O)9`1d_m| k3F!O#>;Ix7FYl!ZMxO+}H37cDd3nF}d&ux4@O1+J4<{R7wg3PC literal 0 HcmV?d00001 From 82ce3abf99ade06122988050c8c5f5c83b7d5aab Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Wed, 31 Jul 2019 17:54:18 -0400 Subject: [PATCH 13/16] Ignore the guide directory that is a temp image storage for the guide --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 55f127b..43389f1 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ bundles *.DS_Store .eggs dist -**/*.egg-info \ No newline at end of file +**/*.egg-info +guide/ From 85380a2a0e72efbb69ad8652de94a9e8881545f5 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Wed, 31 Jul 2019 18:37:25 -0400 Subject: [PATCH 14/16] Address pylint issues --- adafruit_bitmapsaver.py | 4 +++- examples/screenshot_simpletest.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/adafruit_bitmapsaver.py b/adafruit_bitmapsaver.py index 60061a7..887bfd4 100644 --- a/adafruit_bitmapsaver.py +++ b/adafruit_bitmapsaver.py @@ -52,7 +52,7 @@ __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BitmapSaver.git" -#pylint:disable=line-too-long,broad-except,redefined-outer-name +#pylint:disable=line-too-long,broad-except,redefined-outer-name,invalid-name def _write_bmp_header(output_file, filesize): output_file.write(bytes('BM', 'ascii')) @@ -88,6 +88,7 @@ def _rgb565_to_bgr_tuple(color): red = (color >> 8) & 0x00F8 return (blue, green, red) +#pylint:disable=too-many-locals def _write_pixels(output_file, pixel_source, palette): saving_bitmap = isinstance(pixel_source, Bitmap) w, h = _rotated_height_and_width(pixel_source) @@ -111,6 +112,7 @@ def _write_pixels(output_file, pixel_source, palette): buffer_index += 1 output_file.write(row_buffer) gc.collect() +#pylint:enable=too-many-locals def save_pixels(file_or_filename, pixel_source=board.DISPLAY, palette=None): """Save pixels to a 24 bit per pixel BMP file. diff --git a/examples/screenshot_simpletest.py b/examples/screenshot_simpletest.py index ea223f7..f6ef016 100644 --- a/examples/screenshot_simpletest.py +++ b/examples/screenshot_simpletest.py @@ -22,6 +22,7 @@ """Example of taking a screenshot.""" +#pylint:disable=invalid-name import board import digitalio import busio From 51d8efa23223b00381cf2fb1cd576ca8663c446b Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Wed, 31 Jul 2019 19:02:51 -0400 Subject: [PATCH 15/16] Add mocking for board --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 07ac589..c37ef70 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,7 +20,7 @@ # Uncomment the below if you use native CircuitPython modules such as # digitalio, micropython and busio. List the modules you use. Without it, the # autodoc module docs will fail to generate with a warning. -autodoc_mock_imports = ["displayio", "digitalio", "busio"] +autodoc_mock_imports = ["displayio", "digitalio", "busio", "board"] intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None),'CircuitPython': ('https://circuitpython.readthedocs.io/en/latest/', None)} From d84e967886ff2c8de48c1a5f5a525950132df8f8 Mon Sep 17 00:00:00 2001 From: Dave Astels Date: Thu, 1 Aug 2019 14:25:53 -0400 Subject: [PATCH 16/16] Fix lint complaints --- adafruit_bitmapsaver.py | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/adafruit_bitmapsaver.py b/adafruit_bitmapsaver.py index 887bfd4..8bc8edd 100644 --- a/adafruit_bitmapsaver.py +++ b/adafruit_bitmapsaver.py @@ -52,7 +52,6 @@ __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_BitmapSaver.git" -#pylint:disable=line-too-long,broad-except,redefined-outer-name,invalid-name def _write_bmp_header(output_file, filesize): output_file.write(bytes('BM', 'ascii')) @@ -61,13 +60,14 @@ def _write_bmp_header(output_file, filesize): output_file.write(b'\00\x00') output_file.write(struct.pack('>= 8 buffer_index += 1 else: - data = pixel_source.fill_area(x=0, y=y-1, width=w, height=1) - for i in range(w): + data = pixel_source.fill_area(x=0, y=y-1, width=width, height=1) + for i in range(width): pixel565 = (data[i * 2] << 8) + data[i * 2 + 1] for b in _rgb565_to_bgr_tuple(pixel565): row_buffer[buffer_index] = b & 0xFF @@ -134,10 +132,10 @@ def save_pixels(file_or_filename, pixel_source=board.DISPLAY, palette=None): else: output_file = file_or_filename - w, h = _rotated_height_and_width(pixel_source) - filesize = 54 + h * _bytes_per_row(w) + width, height = _rotated_height_and_width(pixel_source) + filesize = 54 + height * _bytes_per_row(width) _write_bmp_header(output_file, filesize) - _write_dib_header(output_file, w, h) + _write_dib_header(output_file, width, height) _write_pixels(output_file, pixel_source, palette) except Exception: raise