added Group5 to bb_eink

This commit is contained in:
Laurence Bank
2024-09-22 11:44:10 +01:00
parent 445102897c
commit a2cf04a76a
22 changed files with 2772 additions and 158 deletions
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+92 -53
View File
@@ -1,64 +1,103 @@
License text copyright © 2024 MariaDB plc, All Rights Reserved.
“Business Source License” is a trademark of MariaDB plc.
Business Source License 1.1
Terms
=====
The Licensor hereby grants you the right to copy, modify, create derivative works, redistribute,
and make non-production use of the Licensed Work. The Licensor may make an Additional Use Grant,
above, permitting limited production use.
Parameters
Effective on the Change Date, or the fourth anniversary of the first publicly available
distribution of a specific version of the Licensed Work under this License, whichever comes first,
the Licensor hereby grants you rights under the terms of the Change License, and the rights granted
in the paragraph above terminate.
Licensor: BitBank Software, Inc.
Licensed Works: bb_eink and Group5 1-bit imaging codec
The Licensed Work is (c) 2024 BitBank Software, Inc.
Additional Use Grant: You may make use of the Licensed Work, provided that
you may not use the Licensed Work for a Commercial
Product or Service.
If your use of the Licensed Work does not comply with the requirements currently in effect as
described in this License, you must purchase a commercial license from the Licensor, its affiliated
entities, or authorized resellers, or you must refrain from using the Licensed Work.
A “Commercial Product or Service” is a commercial offering that
allows third parties (other than your employees and
contractors) to access the functionality of the
Licensed Work without first having paid for a
license.
All copies of the original and modified Licensed Work, and derivative works of the Licensed Work,
are subject to this License. This License applies separately for each version of the Licensed Work
and the Change Date may vary for each version of the Licensed Work released by Licensor.
Change Date: 2028-10-01
You must conspicuously display this License on each original or modified copy of the Licensed Work.
If you receive the Licensed Work in original or modified form from a third party, the terms and
conditions set forth in this License apply to your use of that work.
Change License: Apache License, Version 2.0
Any use of the Licensed Work in violation of this License will automatically terminate your rights
under this License for the current and all other versions of the Licensed Work.
This License does not grant you any right in any trademark or logo of Licensor or its affiliates
(provided that you may use a trademark or logo of Licensor as expressly required by this License).
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN “AS IS” BASIS.
LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS OR IMPLIED, INCLUDING
(WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
NON-INFRINGEMENT, AND TITLE. MariaDB hereby grants you permission to use this Licenses text to
license your works, and to refer to it using the trademark “Business Source License”, as long as
you comply with the Covenants of Licensor below.
Covenants of Licensor
=====================
In consideration of the right to use this Licenses text and the “Business Source License”
name and trademark, Licensor covenants to BitBank Software, Inc., and to all other recipients
of the licensed work to be provided by Licensor:
To specify as the Change License the GPL Version 2.0 or any later version, or a license that is
compatible with GPL Version 2.0 or a later version, where “compatible” means that software
provided under the Change License can be included in a program with software provided under GPL
Version 2.0 or a later version. Licensor may specify additional Change Licenses without limitation.
To either: (a) specify an additional grant of rights to use that does not impose any additional
restriction on the right granted in this License, as the Additional Use Grant; or (b) insert the
text “None” to specify a Change Date. Not to modify this License in any other way.
For information about alternative licensing arrangements for the Software,
please visit: https://bitbanksoftware.com/
Notice
======
The Business Source License (this document, or the “License”) is not an Open Source license.
However, the Licensed Work will eventually be made available under an Open Source License,
as stated in this License.
For more information on the use of the Business Source License for BitBank Software, Inc. products,
please visit the site: https://bitbanksoftware.com
The Business Source License (this document, or the “License”) is not an Open
Source license. However, the Licensed Work will eventually be made available
under an Open Source License, as stated in this License.
For more information on the use of the Business Source License generally, please visit
the FAQ at https://mariadb.com/bsl-faq-adopting/
License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.
“Business Source License” is a trademark of MariaDB Corporation Ab.
-----------------------------------------------------------------------------
Business Source License 1.1
Terms
The Licensor hereby grants you the right to copy, modify, create derivative
works, redistribute, and make non-production use of the Licensed Work. The
Licensor may make an Additional Use Grant, above, permitting limited
production use.
Effective on the Change Date, or the fourth anniversary of the first publicly
available distribution of a specific version of the Licensed Work under this
License, whichever comes first, the Licensor hereby grants you rights under
the terms of the Change License, and the rights granted in the paragraph
above terminate.
If your use of the Licensed Work does not comply with the requirements
currently in effect as described in this License, you must purchase a
commercial license from the Licensor, its affiliated entities, or authorized
resellers, or you must refrain from using the Licensed Work.
All copies of the original and modified Licensed Work, and derivative works
of the Licensed Work, are subject to this License. This License applies
separately for each version of the Licensed Work and the Change Date may vary
for each version of the Licensed Work released by Licensor.
You must conspicuously display this License on each original or modified copy
of the Licensed Work. If you receive the Licensed Work in original or
modified form from a third party, the terms and conditions set forth in this
License apply to your use of that work.
Any use of the Licensed Work in violation of this License will automatically
terminate your rights under this License for the current and all other
versions of the Licensed Work.
This License does not grant you any right in any trademark or logo of
Licensor or its affiliates (provided that you may use a trademark or logo of
Licensor as expressly required by this License).
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
TITLE.
MariaDB hereby grants you permission to use this Licenses text to license
your works, and to refer to it using the trademark “Business Source License”,
as long as you comply with the Covenants of Licensor below.
Covenants of Licensor
In consideration of the right to use this Licenses text and the “Business
Source License” name and trademark, Licensor covenants to MariaDB, and to all
other recipients of the licensed work to be provided by Licensor:
1. To specify as the Change License the GPL Version 2.0 or any later version,
or a license that is compatible with GPL Version 2.0 or a later version,
where “compatible” means that software provided under the Change License can
be included in a program with software provided under GPL Version 2.0 or a
later version. Licensor may specify additional Change Licenses without
limitation.
2. To either: (a) specify an additional grant of rights to use that does not
impose any additional restriction on the right granted in this License, as
the Additional Use Grant; or (b) insert the text “None”.
3. To specify a Change Date.
4. Not to modify this License in any other way.
+343
View File
@@ -0,0 +1,343 @@
//
// I/O wrapper functions for the ESP-IDF
//
#ifndef __ESP_IDF_IO__
#define __ESP_IDF_IO__
#define INPUT 0
#define INPUT_PULLUP 1
#define OUTPUT 2
#define DISABLED 3
#define PROGMEM
#define HIGH 1
#define LOW 0
#define PIN_SCK 4
#define PIN_MOSI 6
void digitalWrite(int iPin, int iState) {
gpio_set_level((gpio_num_t)iPin, (uint32_t)iState);
}
void pinMode(int iPin, int iMode)
{
gpio_config_t io_conf = {};
gpio_reset_pin(iPin);
if (iMode == DISABLED) return;
io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt
//bit mask of the pins that you want to set,e.g.GPIO18/19
io_conf.pin_bit_mask = (1ULL << iPin);
io_conf.pull_down_en = 0; //disable pull-down mode
io_conf.pull_up_en = (iMode == INPUT_PULLUP); // pull-up mode
if (iMode == INPUT || iMode == INPUT_PULLUP) {
io_conf.mode = GPIO_MODE_INPUT;
} else { // must be output
io_conf.mode = GPIO_MODE_OUTPUT;
}
gpio_config(&io_conf); //configure GPIO with the given settings
} /* pinMode() */
int digitalRead(int iPin)
{
return (int)gpio_get_level((gpio_num_t)iPin);
} /* digitalRead() */
void delayMicroseconds(long l)
{
l *= 40;
while (l) {
__asm__ __volatile__ ("nop");
l--;
}
}
void _delay(long l)
{
delayMicroseconds(l*1000);
}
void mymemset(void *d, uint8_t u8, int len)
{
uint8_t *d8;
if (((intptr_t)d & 3) == 0) { // dword aligned
uint32_t u32, *d32 = (uint32_t *)d;
u32 = u8 | (u8 << 8);
u32 |= (u32 << 16);
while (len >= 4) {
*d32++ = u32;
len -= 4;
}
d = d32;
}
d8 = (uint8_t *)d;
while (len) {
*d8++ = u8;
len--;
}
} /* mymemset() */
void mymemcpy(void *d, void *s, size_t iLen)
{
uint8_t *d8 = (uint8_t *)d;
uint8_t *s8 = (uint8_t *)s;
while (iLen) {
*d8++ = *s8++;
iLen--;
}
} /* mymemcpy() */
//
// Convert a number into a fixed length, zero-terminated string
//
void i2strf(char *pDest, int iVal, int iDigits)
{
char *d = pDest;
int i;
pDest[iDigits] = 0;
while (iDigits) {
iDigits--;
i = iVal % 10;
d[iDigits] = '0' + (char)i;
iVal /= 10;
}
} /* i2strf() */
//
// Convert a number into a zero-terminated string
//
int i2str(char *pDest, int iVal)
{
char *d = pDest;
int i, iPlaceVal = 10000;
int iDigits = 0;
if (iVal < 0) {
iDigits++;
*d++ = '-';
iVal = -iVal;
}
while (iPlaceVal) {
if (iVal >= iPlaceVal) {
i = iVal / iPlaceVal;
*d++ = '0' + (char)i;
iVal -= (i*iPlaceVal);
iDigits++;
} else if (iDigits != 0) {
*d++ = '0'; // non-zeros were already displayed
}
iPlaceVal /= 10;
}
if (d == pDest) // must be zero
*d++ = '0';
*d++ = 0; // terminator
return (int)(d - pDest - 1); // string length
} /* i2str() */
void delayCycles(int i)
{
while (i > 3) {
__asm__ __volatile__ ("nop\n\t");
i -= 3;
}
}
//
// Bit Bang SPI
//
void SPIWriteByte(uint8_t uc)
{
if (uc == 0 || uc == 0xff) { // special case
digitalWrite(PIN_MOSI, (uc & 1));
delayCycles(5);
for (int i=0; i<8; i++) {
digitalWrite(PIN_SCK, 1); // no delays needed since it runs at a slow clock
delayCycles(5);
digitalWrite(PIN_SCK, 0);
delayCycles(5);
}
} else {
for (int i=0; i<8; i++) {
digitalWrite(PIN_MOSI, (uc & 0x80) != 0); // MSB first
delayCycles(5);
digitalWrite(PIN_SCK, 1);
uc <<= 1;
delayCycles(5);
digitalWrite(PIN_SCK, 0);
delayCycles(5);
}
}
} /* SPIWriteByte() */
//
// Need to bit-bang I2C
//
uint8_t SDA_READ(void)
{
return digitalRead(PIN_SDA);
}
void SDA_HIGH(void)
{
pinMode(PIN_SDA, INPUT_PULLUP);
}
void SDA_LOW(void)
{
pinMode(PIN_SDA, OUTPUT);
digitalWrite(PIN_SDA, LOW);
}
void SCL_HIGH(void)
{
pinMode(PIN_SCL, INPUT_PULLUP);
}
void SCL_LOW(void)
{
pinMode(PIN_SCL, OUTPUT);
digitalWrite(PIN_SCL, LOW);
}
// Transmit a byte and read the ack bit
// if we get a NACK (negative acknowledge) return 0
// otherwise return 1 for success
//
int i2cByteOut(uint8_t b)
{
uint8_t i, ack;
for (i=0; i<8; i++) {
// my_sleep_us(iDelay);
if (b & 0x80)
SDA_HIGH(); // set data line to 1
else
SDA_LOW(); // set data line to 0
b <<= 1;
// my_sleep_us(iDelay);
SCL_HIGH(); // clock high (slave latches data)
delayMicroseconds(1);
SCL_LOW(); // clock low
delayMicroseconds(1);
} // for i
//my_sleep_us(iDelay);
// read ack bit
SDA_HIGH(); // set data line for reading
//my_sleep_us(iDelay);
SCL_HIGH(); // clock line high
delayMicroseconds(1);
ack = SDA_READ();
//my_sleep_us(iDelay);
SCL_LOW(); // clock low
delayMicroseconds(1);
SDA_LOW(); // data low
return (ack == 0); // a low ACK bit means success
} /* i2cByteOut() */
//
// Receive a byte and read the ack bit
// if we get a NACK (negative acknowledge) return 0
// otherwise return 1 for success
//
uint8_t i2cByteIn(uint8_t bLast)
{
uint8_t i;
uint8_t b = 0;
SDA_HIGH(); // set data line as input
for (i=0; i<8; i++)
{
delayMicroseconds(1);
SCL_HIGH(); // clock high (slave latches data)
delayMicroseconds(1);
b <<= 1;
if (SDA_READ() != 0) // read the data bit
b |= 1; // set data bit
SCL_LOW(); // clock low
} // for i
if (bLast)
SDA_HIGH(); // last byte sends a NACK
else
SDA_LOW();
// my_sleep_us(iDelay);
SCL_HIGH(); // clock high
delayMicroseconds(1);
SCL_LOW(); // clock low to send ack
delayMicroseconds(1);
// SDA_HIGH();
SDA_LOW(); // data low
return b;
} /* i2cByteIn() */
//
// Send I2C STOP condition
//
void i2cEnd(void)
{
SDA_LOW(); // data line low
delayMicroseconds(1);
SCL_HIGH(); // clock high
delayMicroseconds(1);
SDA_HIGH(); // data high
delayMicroseconds(1);
} /* i2cEnd() */
int i2cBegin(uint8_t addr, uint8_t bRead)
{
int rc;
// SCL_HIGH();
// my_sleep_us(iDelay);
SDA_LOW(); // data line low first
delayMicroseconds(1);
SCL_LOW(); // then clock line low is a START signal
addr <<= 1;
if (bRead)
addr++; // set read bit
rc = i2cByteOut(addr); // send the slave address and R/W bit
return rc;
} /* i2cBegin() */
void I2CWrite(uint8_t addr, uint8_t *pData, int iLen)
{
uint8_t b;
int rc;
i2cBegin(addr, 0);
rc = 1;
while (iLen && rc == 1)
{
b = *pData++;
rc = i2cByteOut(b);
if (rc == 1) // success
{
iLen--;
}
} // for each byte
i2cEnd();
//return (rc == 1) ? (iOldLen - iLen) : 0; // 0 indicates bad ack from sending a byte
} /* I2CWrite() */
int I2CRead(uint8_t addr, uint8_t *pData, int iLen)
{
i2cBegin(addr, 1);
while (iLen--)
{
*pData++ = i2cByteIn(iLen == 0);
} // for each byte
i2cEnd();
return 1;
} /* I2CRead() */
int I2CTest(uint8_t addr)
{
int response = 0;
if (i2cBegin(addr, 0)) // try to write to the given address
{
response = 1;
}
i2cEnd();
return response;
} /* I2CTest() */
void I2CInit(void)
{
pinMode(PIN_SDA, INPUT_PULLUP);
pinMode(PIN_SCL, INPUT_PULLUP);
}
void I2CDeInit(void)
{
pinMode(PIN_SDA, DISABLED);
pinMode(PIN_SCL, DISABLED);
}
//
// Read N bytes starting at a specific I2C internal register
// returns 1 for success, 0 for error
//
void I2CReadRegister(uint8_t iAddr, uint8_t u8Register, uint8_t *pData, int iLen)
{
I2CWrite(iAddr, &u8Register, 1);
I2CRead(iAddr, pData, iLen);
} /* I2CReadRegister() */
#endif // __ESP_IDF_IO__
+331
View File
@@ -0,0 +1,331 @@
// Created with image_to_c
// https://github.com/bitbank2/image_to_c
//
// Roboto_Black_40
// Data size = 5105 bytes
//
// for non-Arduino builds...
#ifndef PROGMEM
#define PROGMEM
#endif
const uint8_t Roboto_Black_40[] PROGMEM = {
0xff,0xbb,0x20,0x00,0x7e,0x00,0x5c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x13,
0x01,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x0e,0x16,0x38,0x00,0x04,0x00,0xca,0xff,
0x21,0x00,0x16,0x19,0x16,0x00,0x01,0x00,0xc6,0xff,0x2d,0x00,0x2b,0x2d,0x37,0x00,
0x01,0x00,0xca,0xff,0x68,0x00,0x27,0x2d,0x49,0x00,0x03,0x00,0xc1,0xff,0xba,0x00,
0x34,0x3a,0x39,0x00,0x03,0x00,0xc9,0xff,0x18,0x01,0x31,0x35,0x39,0x00,0x02,0x00,
0xc9,0xff,0x68,0x01,0x09,0x0c,0x16,0x00,0x02,0x00,0xc6,0xff,0x6d,0x01,0x15,0x1c,
0x4e,0x00,0x05,0x00,0xc4,0xff,0xa2,0x01,0x17,0x1b,0x4e,0x00,0x01,0x00,0xc4,0xff,
0xde,0x01,0x23,0x24,0x22,0x00,0x01,0x00,0xca,0xff,0x14,0x02,0x26,0x2a,0x27,0x00,
0x02,0x00,0xd4,0xff,0x2d,0x02,0x0e,0x15,0x19,0x00,0x02,0x00,0xf7,0xff,0x3f,0x02,
0x17,0x23,0x0a,0x00,0x06,0x00,0xe4,0xff,0x41,0x02,0x0f,0x18,0x0d,0x00,0x04,0x00,
0xf4,0xff,0x50,0x02,0x1c,0x1b,0x3c,0x00,0xff,0xff,0xca,0xff,0x79,0x02,0x27,0x2d,
0x39,0x00,0x03,0x00,0xc9,0xff,0xb1,0x02,0x1a,0x2d,0x37,0x00,0x06,0x00,0xca,0xff,
0xd1,0x02,0x28,0x2d,0x38,0x00,0x02,0x00,0xc9,0xff,0x0a,0x03,0x28,0x2d,0x39,0x00,
0x02,0x00,0xc9,0xff,0x4f,0x03,0x29,0x2d,0x37,0x00,0x02,0x00,0xca,0xff,0x7e,0x03,
0x27,0x2d,0x38,0x00,0x03,0x00,0xca,0xff,0xbb,0x03,0x28,0x2d,0x39,0x00,0x03,0x00,
0xc9,0xff,0x00,0x04,0x28,0x2d,0x37,0x00,0x02,0x00,0xca,0xff,0x24,0x04,0x27,0x2d,
0x39,0x00,0x03,0x00,0xc9,0xff,0x6b,0x04,0x27,0x2d,0x39,0x00,0x03,0x00,0xc9,0xff,
0xae,0x04,0x0f,0x17,0x2b,0x00,0x04,0x00,0xd6,0xff,0xcf,0x04,0x10,0x16,0x3a,0x00,
0x03,0x00,0xd6,0xff,0xf6,0x04,0x21,0x28,0x26,0x00,0x02,0x00,0xd7,0xff,0x23,0x05,
0x23,0x2e,0x1b,0x00,0x05,0x00,0xdb,0xff,0x2b,0x05,0x22,0x28,0x26,0x00,0x04,0x00,
0xd7,0xff,0x56,0x05,0x24,0x28,0x38,0x00,0x01,0x00,0xc9,0xff,0x8d,0x05,0x41,0x46,
0x47,0x00,0x02,0x00,0xcb,0xff,0x16,0x06,0x35,0x35,0x37,0x00,0x00,0x00,0xca,0xff,
0x4e,0x06,0x2b,0x32,0x37,0x00,0x04,0x00,0xca,0xff,0x7a,0x06,0x2e,0x33,0x39,0x00,
0x03,0x00,0xc9,0xff,0xb8,0x06,0x2c,0x32,0x37,0x00,0x04,0x00,0xca,0xff,0xe1,0x06,
0x26,0x2c,0x37,0x00,0x04,0x00,0xca,0xff,0xf4,0x06,0x25,0x2b,0x37,0x00,0x04,0x00,
0xca,0xff,0x06,0x07,0x2f,0x35,0x39,0x00,0x03,0x00,0xc9,0xff,0x47,0x07,0x2f,0x37,
0x37,0x00,0x04,0x00,0xca,0xff,0x5e,0x07,0x0d,0x18,0x37,0x00,0x05,0x00,0xca,0xff,
0x65,0x07,0x27,0x2c,0x38,0x00,0x01,0x00,0xca,0xff,0x8c,0x07,0x2f,0x32,0x37,0x00,
0x04,0x00,0xca,0xff,0xba,0x07,0x25,0x2a,0x37,0x00,0x04,0x00,0xca,0xff,0xcb,0x07,
0x3c,0x44,0x37,0x00,0x04,0x00,0xca,0xff,0x01,0x08,0x2f,0x37,0x37,0x00,0x04,0x00,
0xca,0xff,0x26,0x08,0x30,0x36,0x39,0x00,0x03,0x00,0xc9,0xff,0x69,0x08,0x2c,0x33,
0x37,0x00,0x04,0x00,0xca,0xff,0x8d,0x08,0x30,0x36,0x42,0x00,0x03,0x00,0xc9,0xff,
0xdc,0x08,0x2d,0x33,0x37,0x00,0x04,0x00,0xca,0xff,0x07,0x09,0x2b,0x31,0x39,0x00,
0x03,0x00,0xc9,0xff,0x52,0x09,0x2f,0x31,0x37,0x00,0x01,0x00,0xca,0xff,0x6e,0x09,
0x2c,0x34,0x38,0x00,0x04,0x00,0xca,0xff,0x95,0x09,0x34,0x34,0x37,0x00,0x00,0x00,
0xca,0xff,0xcc,0x09,0x42,0x44,0x37,0x00,0x01,0x00,0xca,0xff,0x17,0x0a,0x32,0x32,
0x37,0x00,0x00,0x00,0xca,0xff,0x5a,0x0a,0x31,0x31,0x37,0x00,0x00,0x00,0xca,0xff,
0x8d,0x0a,0x2c,0x30,0x37,0x00,0x02,0x00,0xca,0xff,0xb6,0x0a,0x11,0x16,0x4e,0x00,
0x04,0x00,0xc0,0xff,0xcb,0x0a,0x25,0x21,0x3c,0x00,0xff,0xff,0xca,0xff,0xf7,0x0a,
0x11,0x16,0x4e,0x00,0x01,0x00,0xc0,0xff,0x11,0x0b,0x21,0x23,0x1b,0x00,0x01,0x00,
0xca,0xff,0x31,0x0b,0x22,0x22,0x0a,0x00,0x00,0x00,0x01,0x00,0x33,0x0b,0x15,0x1a,
0x0c,0x00,0x02,0x00,0xc6,0xff,0x40,0x0b,0x26,0x29,0x2b,0x00,0x02,0x00,0xd7,0xff,
0x78,0x0b,0x26,0x2c,0x3c,0x00,0x04,0x00,0xc6,0xff,0xa3,0x0b,0x25,0x29,0x2b,0x00,
0x02,0x00,0xd7,0xff,0xd4,0x0b,0x26,0x2c,0x3c,0x00,0x02,0x00,0xc6,0xff,0x05,0x0c,
0x27,0x2a,0x2b,0x00,0x02,0x00,0xd7,0xff,0x3c,0x0c,0x1c,0x1c,0x3b,0x00,0x00,0x00,
0xc6,0xff,0x63,0x0c,0x27,0x2d,0x3a,0x00,0x02,0x00,0xd7,0xff,0xa8,0x0c,0x25,0x2c,
0x3b,0x00,0x03,0x00,0xc6,0xff,0xc7,0x0c,0x0e,0x15,0x3a,0x00,0x03,0x00,0xc7,0xff,
0xe6,0x0c,0x16,0x15,0x4b,0x00,0xfc,0xff,0xc7,0xff,0x15,0x0d,0x28,0x2b,0x3b,0x00,
0x04,0x00,0xc6,0xff,0x3d,0x0d,0x0d,0x15,0x3b,0x00,0x04,0x00,0xc6,0xff,0x45,0x0d,
0x3b,0x43,0x2a,0x00,0x04,0x00,0xd7,0xff,0x74,0x0d,0x26,0x2c,0x2a,0x00,0x03,0x00,
0xd7,0xff,0x91,0x0d,0x28,0x2c,0x2b,0x00,0x02,0x00,0xd7,0xff,0xc3,0x0d,0x26,0x2c,
0x3a,0x00,0x04,0x00,0xd7,0xff,0xee,0x0d,0x26,0x2c,0x3a,0x00,0x02,0x00,0xd7,0xff,
0x20,0x0e,0x19,0x1e,0x2a,0x00,0x04,0x00,0xd7,0xff,0x32,0x0e,0x25,0x28,0x2b,0x00,
0x01,0x00,0xd7,0xff,0x72,0x0e,0x19,0x1b,0x34,0x00,0x01,0x00,0xce,0xff,0x95,0x0e,
0x26,0x2c,0x2a,0x00,0x03,0x00,0xd8,0xff,0xb2,0x0e,0x28,0x28,0x29,0x00,0x00,0x00,
0xd8,0xff,0xd9,0x0e,0x37,0x39,0x29,0x00,0x01,0x00,0xd8,0xff,0x13,0x0f,0x28,0x28,
0x29,0x00,0x00,0x00,0xd8,0xff,0x43,0x0f,0x28,0x28,0x3a,0x00,0x00,0x00,0xd8,0xff,
0x78,0x0f,0x23,0x28,0x29,0x00,0x03,0x00,0xd8,0xff,0x94,0x0f,0x17,0x19,0x4b,0x00,
0x01,0x00,0xc4,0xff,0xcc,0x0f,0x07,0x14,0x41,0x00,0x07,0x00,0xca,0xff,0xd5,0x0f,
0x17,0x19,0x4b,0x00,0x01,0x00,0xc4,0xff,0x0f,0x10,0x2a,0x31,0x12,0x00,0x03,0x00,
0xe1,0xff,0x42,0x90,0x1f,0xff,0xff,0xff,0xff,0xff,0xaf,0xff,0xff,0xff,0xdf,0xff,
0xff,0xff,0xff,0xff,0xff,0xf1,0xff,0xc9,0x36,0x83,0xc2,0x7d,0x8f,0xc8,0x0f,0x5b,
0x58,0x61,0x40,0x32,0x6f,0xff,0xff,0xfd,0x7f,0xfd,0x7f,0xff,0xfd,0x7f,0xf0,0x28,
0x3c,0xc4,0x62,0xf3,0x34,0xbf,0xeb,0xff,0xf5,0xff,0x5f,0xae,0xbf,0xff,0x5f,0xf5,
0xfa,0xeb,0xe5,0x74,0x7f,0xff,0xf3,0x96,0x41,0x37,0xff,0xaf,0xd7,0xfd,0x7f,0xaf,
0xfa,0xfd,0x62,0x27,0x44,0x8f,0xff,0x28,0x20,0xc4,0x63,0x90,0x97,0xfe,0xba,0xfd,
0x75,0xff,0xfa,0xff,0xaf,0xd7,0x5f,0xff,0xaf,0xe0,0x28,0x48,0xb9,0x0f,0xff,0xff,
0xff,0x82,0x0f,0x04,0x0f,0x08,0x3c,0x27,0xa0,0xf4,0xf5,0xd3,0xef,0x5e,0x63,0xeb,
0xe1,0x3e,0xaf,0xd5,0xf7,0xff,0xff,0xc7,0x7b,0x7d,0xf7,0xb0,0xf6,0x0f,0x87,0xb0,
0xf0,0xc3,0xdb,0xd8,0x78,0x6f,0x0d,0xe0,0xde,0x1f,0x0d,0xe1,0xbd,0xf7,0x9c,0xd3,
0x3b,0x7f,0xf2,0x02,0xfd,0xa5,0xda,0xf6,0xbb,0x1f,0x5b,0xed,0x6d,0x6d,0x6d,0x61,
0xac,0x30,0xb0,0x67,0x30,0xe8,0x30,0x5f,0xff,0xff,0xff,0x00,0x28,0x24,0xe4,0x4f,
0x04,0x0f,0x4f,0x09,0xf7,0xa6,0x63,0x0e,0xa8,0x3e,0x62,0xa6,0x1f,0x4d,0xa7,0xd7,
0x51,0xba,0xfa,0x5f,0xfa,0x5f,0x5e,0xb2,0x03,0x54,0xbb,0xfe,0xd5,0x2e,0x29,0x2d,
0xfd,0xa4,0xba,0xac,0x34,0xbb,0x09,0x2c,0x18,0x5e,0x29,0x69,0x7e,0x97,0xe9,0x17,
0x91,0xa4,0x08,0x1f,0xa7,0xa4,0x83,0xe9,0x75,0x4f,0x4a,0xfd,0x18,0xa9,0xd2,0xa7,
0xd2,0xa7,0xff,0xd2,0xff,0xee,0x97,0xd2,0xff,0xd6,0x97,0xf5,0x7e,0xb6,0x17,0x06,
0x98,0xf0,0xf5,0x8b,0x5b,0xec,0x2d,0xac,0x18,0x28,0x28,0x40,0xe4,0xbf,0x04,0x0f,
0x09,0xe8,0x3d,0x3d,0x3d,0x7b,0xd7,0xbe,0x63,0x2f,0x49,0xfd,0xfa,0xff,0xff,0xfe,
0xd7,0xd2,0xdb,0x5f,0x09,0x6c,0x57,0x5b,0x5d,0x6d,0x75,0xc2,0xd4,0xe5,0x17,0xa7,
0xe1,0x3f,0x4f,0xd3,0xff,0xd1,0x8d,0x2d,0x74,0x9b,0xaf,0x6f,0xea,0x38,0xdf,0x6b,
0x7d,0xf6,0xad,0xf6,0xa4,0x05,0xd7,0x0c,0x2f,0x17,0xb7,0xde,0xfb,0x7b,0x7b,0x33,
0x45,0xe1,0x85,0x78,0x61,0x37,0x06,0x72,0xe0,0x00,0xff,0xd7,0xff,0xff,0xf0,0x28,
0x90,0x43,0x0b,0x84,0xf5,0xd3,0xad,0x69,0x75,0xa5,0xa5,0xd6,0xba,0x5f,0xa5,0xfd,
0x6b,0xfd,0x6b,0xff,0xa5,0xff,0xff,0x1d,0x7f,0xff,0xff,0xff,0xf7,0x20,0x3f,0xff,
0xfd,0xbf,0xfd,0xfb,0xfd,0xfb,0xfd,0xbf,0xdb,0xdf,0xbd,0xbd,0xbe,0xf6,0xef,0x7b,
0x5b,0xe1,0xac,0x3c,0x20,0x65,0x34,0xa0,0xfb,0xd0,0x7d,0xf7,0x17,0x20,0x5d,0xbd,
0xbe,0xf7,0xdb,0xef,0x7e,0xf7,0xfd,0xbf,0xef,0xf7,0xef,0xff,0x7e,0xff,0xff,0xff,
0xff,0xbf,0xff,0xff,0xeb,0xff,0xff,0xff,0xfa,0xd7,0xff,0xad,0x7f,0xad,0x7a,0xfd,
0x7a,0xd7,0xad,0x2d,0x7a,0xd2,0xd7,0x4a,0x2a,0x40,0x57,0x5b,0x0b,0xad,0x85,0x00,
0x28,0x38,0xe4,0x25,0xff,0xff,0xaf,0xff,0xc8,0x9e,0x62,0x97,0x0f,0x05,0xd0,0x78,
0x4f,0x83,0x70,0xbc,0x38,0xe2,0x2e,0x40,0x96,0x0c,0xec,0x95,0x39,0x28,0xe0,0xc1,
0x7e,0x9e,0x9e,0x8c,0x41,0x7a,0x57,0xab,0x7a,0x4f,0xa5,0x7e,0xde,0x95,0xed,0x35,
0x87,0x61,0x6d,0x56,0xd3,0x0a,0x28,0x34,0xe6,0x1f,0xff,0xff,0xff,0xff,0xff,0xff,
0xe2,0x66,0x57,0xfc,0xa0,0xd3,0x98,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xf8,0x20,
0x9f,0xff,0xff,0xff,0xfd,0x2f,0xff,0xd2,0xfd,0x2f,0x8a,0xa9,0x01,0xe1,0xac,0x35,
0x00,0xff,0xc0,0x20,0xa4,0xba,0x10,0x7a,0x7a,0x7f,0x17,0x20,0x3d,0x7e,0xd6,0xd6,
0x18,0x50,0x28,0x9e,0xba,0xfd,0x7a,0xfd,0x7a,0xfd,0x7a,0xff,0xd2,0xff,0xd2,0xff,
0xd7,0xaf,0xd7,0xaf,0xd7,0xaf,0xfd,0x2f,0xfd,0x2f,0xfd,0x2f,0xfd,0x2f,0xfd,0x7a,
0xfd,0x7a,0xff,0xd2,0xff,0xd2,0xff,0xd2,0xff,0x8a,0xe0,0x28,0x3c,0xe4,0xa1,0x39,
0x68,0xe1,0x07,0xa7,0xa7,0xa7,0xa7,0xa7,0xaf,0x7a,0x31,0xf7,0xd3,0x7d,0x3e,0x93,
0x7f,0xff,0xd3,0xff,0x1d,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf9,0x01,0xd7,0xfd,
0xaf,0xff,0xed,0xa5,0xda,0xf6,0x96,0xc7,0xb5,0xfb,0x5b,0x5b,0x5b,0x5b,0x58,0x61,
0x60,0xc1,0x40,0x28,0xbe,0x0b,0x05,0x82,0xc1,0x60,0xb0,0x58,0x28,0xfe,0x66,0x18,
0x2c,0x16,0x54,0xb8,0x5f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0x80,0x28,0x3c,0xe5,0x9d,0x04,0x0f,0x08,0x3c,0x20,0xf4,0xf4,0xf4,0xf4,
0xf5,0xef,0x46,0x3e,0xfa,0x0f,0xa4,0xfd,0x7f,0x71,0x4f,0xff,0xd4,0xa1,0x9f,0xeb,
0xd6,0xbd,0x6b,0xa5,0xaf,0x5a,0x5a,0x5a,0x5a,0x5a,0xe9,0x69,0x69,0x69,0x69,0x69,
0x69,0x69,0x69,0x69,0x6b,0xa5,0xa3,0x4c,0x2b,0xff,0xff,0xfe,0x28,0x38,0xe5,0x9f,
0x04,0x74,0x8b,0x84,0x1e,0x10,0x7a,0x7a,0x7a,0x7a,0x7a,0xf7,0xa3,0x21,0xbc,0x26,
0xfa,0x7f,0x7e,0xbc,0x7f,0xff,0xfd,0x2d,0x70,0x96,0x73,0x57,0xeb,0xae,0xb8,0x5d,
0xf0,0xfb,0xef,0xbc,0xeb,0x9f,0x86,0xf7,0xfd,0xbf,0xf9,0xcd,0x3b,0xea,0xf6,0x94,
0x80,0x86,0x17,0x8a,0xdf,0xad,0xf6,0x16,0xd6,0xd6,0x18,0x58,0x30,0xb0,0x67,0x2c,
0xf0,0x28,0x58,0xe6,0x9b,0x5f,0xd7,0xf5,0xd7,0xf5,0xd7,0xf5,0xd7,0xf5,0xfd,0x75,
0xe6,0x28,0xf5,0xf4,0xbe,0xbd,0x7d,0x2f,0xfd,0x2f,0xaf,0x5f,0x4b,0xff,0x4b,0xd2,
0xff,0xd0,0x99,0x3f,0xc7,0xc8,0x0f,0xff,0x3a,0xc6,0xff,0xff,0xff,0xff,0xff,0xfc,
0x20,0x81,0x0f,0xff,0xff,0xff,0xd7,0xf9,0xcc,0x67,0xff,0xf5,0xfa,0xff,0xf9,0x61,
0x1e,0x08,0xe7,0x95,0x8b,0xe1,0xf7,0xde,0x9f,0xf7,0xcc,0x76,0xe4,0xdc,0x37,0x98,
0xa4,0xf8,0xbf,0xef,0xdf,0xff,0xf3,0x9a,0x6e,0x95,0xfa,0xc8,0x0b,0xf8,0x61,0x2d,
0x8f,0xad,0xad,0xf6,0xb6,0xb6,0xb0,0xc2,0xc3,0x0b,0x39,0xe4,0xc0,0x28,0x60,0xbc,
0x99,0xd4,0x2f,0x05,0xc2,0xe1,0x75,0xc2,0xeb,0xae,0xba,0x3a,0x87,0x67,0x41,0x2a,
0x0b,0x4b,0xad,0x2e,0xb4,0xbf,0xa2,0xe2,0x35,0x04,0x0f,0xc2,0x0f,0xa4,0xf8,0xb8,
0xbb,0xef,0x99,0x1a,0xe1,0x07,0xa7,0xa7,0xee,0xff,0xff,0xc8,0x0f,0xfe,0xd2,0xff,
0x6d,0x7b,0x4b,0x6c,0x2f,0x1e,0xd6,0xd7,0x5b,0xec,0x2c,0x35,0xb0,0xb0,0xc2,0xce,
0x81,0x30,0xff,0x5c,0xa1,0xa5,0xfa,0xf5,0xaf,0x5a,0xf5,0xaf,0xf5,0xaf,0x5a,0xf5,
0xaf,0xe9,0x7e,0x97,0xe9,0x7f,0x5a,0xfe,0x97,0xe9,0x7e,0x97,0xeb,0xd6,0xbd,0x7e,
0x97,0xeb,0xd6,0xbd,0x6b,0xd4,0x28,0x38,0xe5,0x9f,0x04,0x0f,0x04,0x0f,0x4f,0x4f,
0x4f,0x4f,0x4f,0xf4,0xf9,0x8f,0xbe,0x9f,0x49,0xbe,0x9f,0xff,0xff,0xff,0x6d,0x2f,
0xfb,0x5d,0xb4,0xb6,0x2b,0xf6,0xb0,0xc2,0xda,0xfc,0x20,0xf4,0xf4,0xf4,0xf4,0x63,
0xeb,0xe1,0x07,0xd2,0x6f,0xfe,0x9e,0x3b,0xff,0xfe,0xd4,0x80,0xeb,0xb5,0xe1,0x85,
0xe2,0xb7,0xda,0xfd,0xad,0xac,0x30,0xb6,0xb0,0xc2,0xce,0x71,0x70,0x28,0x3c,0xe4,
0xa1,0x04,0x0f,0x08,0x3c,0x27,0xa0,0xf4,0xf4,0xf5,0xd3,0xef,0x46,0x3e,0xfa,0x6f,
0x49,0xfa,0x7f,0xb8,0xaf,0x7f,0xfe,0xff,0xfd,0xfc,0x80,0xb5,0xda,0xed,0x6c,0x7e,
0xd7,0xef,0xbe,0xcc,0x99,0xed,0x2e,0x1a,0xac,0x18,0x2f,0x15,0xeb,0x5e,0xb0,0xba,
0x58,0x5c,0x12,0xce,0x43,0x1d,0x75,0xd7,0x5c,0x2e,0xb8,0x2e,0x17,0x39,0x0c,0x00,
0x20,0xa4,0xba,0x10,0x7a,0x7a,0x7f,0x17,0x20,0x3d,0x7e,0xd6,0xd6,0x18,0x58,0xff,
0xff,0xff,0xff,0xe4,0xb7,0x08,0x3d,0x3d,0x3f,0x8b,0x90,0x1e,0xbf,0x6b,0x6b,0x0c,
0x28,0x20,0xc4,0xb6,0x10,0x7a,0x7a,0x7f,0xa7,0xef,0xaf,0xda,0xda,0xc3,0x0b,0x1f,
0xff,0xff,0xff,0xff,0xf2,0xc5,0x7f,0xff,0xff,0xff,0xff,0xfe,0x97,0xff,0xe9,0x7e,
0x97,0xc5,0x54,0x80,0xf0,0xd6,0x1a,0x80,0x28,0x83,0x0b,0x05,0x85,0x82,0xc2,0xc2,
0xc1,0x61,0x60,0xb0,0xb0,0x58,0x41,0x61,0x05,0x10,0x50,0x50,0x50,0x50,0x5c,0x1c,
0x1c,0x1c,0x1c,0x80,0x83,0xc3,0x0f,0x0c,0x38,0x3c,0x3c,0x1e,0x1e,0x0f,0x0f,0x0f,
0x07,0x87,0x83,0xc3,0x80,0xff,0xca,0x28,0xff,0xf3,0x47,0x7f,0xe0,0x29,0x9c,0x1c,
0x38,0x38,0x70,0xe0,0xe1,0xc1,0xc3,0x83,0x87,0x20,0x41,0xe0,0xc1,0xe0,0xdc,0x1e,
0x0f,0x07,0x83,0xd6,0x0b,0x0b,0x05,0x82,0xc1,0x2c,0x10,0x28,0x85,0x0a,0x0a,0x14,
0x14,0x28,0x50,0x50,0xa0,0xa1,0x41,0x40,0x28,0x34,0xe5,0xa1,0x04,0x74,0x8c,0x82,
0x0f,0x4f,0x4f,0x4f,0x4f,0x4f,0xf5,0xe6,0x3e,0xbd,0x3e,0x9c,0x7a,0xf9,0x42,0xff,
0xe9,0x6b,0xa5,0xae,0x96,0x96,0x96,0x96,0x97,0x5a,0x5d,0x75,0xd6,0xbf,0xd7,0xfe,
0x3f,0xf9,0x8f,0xd8,0x4f,0x41,0xeb,0xdf,0xff,0xff,0x5b,0xed,0x61,0x85,0x00,0x28,
0x3a,0x71,0x87,0xe7,0x30,0x57,0x08,0x1e,0x08,0x3d,0x07,0x84,0x1e,0x9e,0x11,0xc8,
0x8a,0x7a,0x38,0xe2,0x4f,0x41,0x06,0xf4,0x10,0x6f,0x49,0xf8,0x4d,0xe9,0x37,0xa4,
0xfd,0x37,0xa9,0xc8,0x08,0x7d,0x20,0x40,0xef,0xa0,0x83,0x7d,0x50,0x7f,0x49,0xb7,
0xeb,0xe9,0x2f,0xe8,0xc2,0xf2,0xfa,0x4b,0x7d,0x6b,0xbe,0x92,0xfa,0xaf,0xeb,0xf5,
0xaf,0xff,0xf5,0xfd,0x7f,0xeb,0xff,0x5f,0xff,0xaf,0xff,0xaf,0xd7,0xff,0xff,0xd7,
0xff,0xff,0xaf,0xd5,0x7d,0xff,0xed,0x52,0xef,0xff,0xb0,0xa9,0x6e,0xc4,0x30,0x97,
0xc7,0xdb,0x31,0x22,0xbd,0xa6,0xb6,0xda,0xaf,0x61,0x06,0x17,0x61,0x84,0x18,0x5b,
0x88,0xf7,0xb7,0xde,0xde,0xde,0xc3,0xd8,0x66,0x28,0xf6,0x72,0x03,0x3b,0x1e,0xde,
0xf8,0x7c,0x35,0x83,0x05,0x9c,0xc8,0xd8,0x28,0x50,0xe6,0xaf,0xf4,0xff,0xf4,0xff,
0x4f,0xff,0x4f,0xff,0x4f,0xf4,0x63,0x0b,0xff,0xfd,0x5b,0xeb,0xff,0x5b,0xe9,0xfe,
0xf5,0xfa,0x7d,0x6f,0xfe,0x9f,0x5b,0xff,0xa6,0xf5,0xff,0xd0,0xbf,0xfd,0x3f,0xef,
0x5f,0xd3,0xfe,0x73,0x53,0xeb,0x7f,0xf4,0xfa,0xdf,0xfe,0xde,0x97,0xfc,0x76,0x9c,
0x3b,0x6a,0x9d,0xf4,0x41,0xc3,0xbb,0xbb,0xee,0x73,0x4b,0xe1,0xf6,0xf7,0xff,0xff,
0xfe,0xb5,0xd2,0xc2,0xc5,0x55,0x55,0x43,0x87,0x77,0x39,0xa6,0xf0,0xde,0xfd,0xff,
0x7f,0x5f,0xe9,0x61,0x63,0xae,0xaa,0xaa,0x14,0x29,0xdd,0x4c,0x28,0x48,0xe5,0xa7,
0x39,0xc9,0x61,0x03,0xc2,0x0f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x99,0x0f,0xd0,0x41,
0xbd,0x27,0xe9,0xbe,0x9f,0x5f,0xab,0xee,0x3f,0xc5,0x7f,0xff,0xff,0xff,0x67,0x4c,
0xde,0x40,0x6b,0xfd,0xd7,0xfb,0x6b,0xeb,0xb0,0xd2,0xe1,0x85,0xd8,0xad,0xae,0xb7,
0xda,0xc3,0x0b,0x6b,0x0c,0x2c,0x30,0x58,0x30,0x50,0x3b,0x0c,0x03,0x83,0x87,0x70,
0xee,0xef,0x9c,0xe2,0x1e,0x1b,0xc3,0xed,0xfe,0xdf,0xef,0xdf,0xef,0xfd,0xff,0xff,
0xff,0xff,0xaf,0xd7,0xff,0x4b,0xf4,0xbf,0x4b,0x0b,0x84,0xa2,0xba,0xa8,0x55,0x50,
0xa0,0xa0,0xa0,0xff,0xce,0x73,0x5f,0xff,0xff,0x9a,0x3b,0xff,0xff,0x9c,0xe6,0xbf,
0xff,0xff,0xcd,0x32,0xff,0x80,0xff,0xce,0x73,0x5f,0xff,0xff,0xe6,0x8e,0xff,0xff,
0xe7,0x39,0xaf,0xff,0xff,0xff,0xff,0xfc,0x28,0x4c,0xe5,0xa5,0x39,0xe9,0xe1,0x07,
0x84,0x1e,0x13,0xd0,0x7a,0x7a,0xe9,0xf7,0xa3,0x23,0xfc,0x20,0xde,0x93,0xf4,0xfa,
0x57,0xef,0xa5,0xff,0xff,0x43,0xf1,0xff,0xf9,0xca,0x5f,0xff,0xff,0xfe,0x74,0xce,
0x20,0x3f,0x7f,0xff,0x6f,0xfe,0xfb,0x7e,0xd6,0xc1,0x85,0xb1,0xfb,0x5b,0x5b,0x5b,
0x58,0x61,0x61,0x85,0x86,0x0b,0x3a,0x66,0x40,0x39,0xc9,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x8f,0xfe,0x73,0x93,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe0,
0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0x28,0x67,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x9c,0xe2,0xfc,0x80,0xeb,0xfe,0xd7,0xd7,0x86,
0x96,0xc7,0xd6,0xd6,0xfd,0x6d,0x61,0x85,0xb5,0x86,0x16,0x73,0x8a,0x80,0x39,0xc8,
0x69,0x69,0x75,0xa5,0xaf,0x5a,0x5a,0x5d,0x6b,0xa5,0xd6,0x96,0xba,0x5d,0x69,0x69,
0x7e,0x96,0x97,0x51,0xd5,0xf7,0x7d,0xf7,0x7c,0xc9,0x27,0x5d,0x37,0x57,0xbd,0x37,
0xde,0xfd,0xef,0xde,0xde,0xfd,0xef,0xde,0xdf,0xed,0xed,0x80,0x39,0xcd,0x7f,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x9a,0x57,0xff,0x00,0x3a,0x4c,0x7b,
0xeb,0xdf,0x5f,0xda,0xfe,0xd7,0xf6,0xbc,0xc6,0x1c,0xc5,0x1e,0xd7,0xbf,0xfa,0xdd,
0xfd,0xd7,0xf5,0xef,0xee,0xab,0xeb,0xdf,0xdd,0x7f,0x5e,0xfe,0xeb,0xfa,0xf7,0xf7,
0x5f,0xd7,0xbf,0xb1,0xfa,0xff,0x7d,0xd7,0xfb,0xfa,0xff,0x6b,0xff,0xed,0x7f,0xfd,
0xaf,0xf7,0xc0,0x39,0xc9,0xdf,0xdf,0xdf,0xde,0xfe,0xfe,0xfe,0xf7,0xf7,0xf7,0xf7,
0x98,0xe2,0xff,0xdb,0xff,0x6f,0xbf,0xbe,0xdf,0xfb,0x7f,0xec,0x77,0xf7,0xf7,0xf7,
0xbf,0xbf,0xbf,0xbd,0xfd,0xfd,0xef,0xc0,0x28,0x4c,0xe5,0x27,0x39,0xe9,0x61,0x07,
0x84,0x1e,0x9e,0x13,0xd0,0x7a,0x7a,0xf7,0xa3,0x23,0x9e,0x82,0x0f,0xc2,0x6f,0x57,
0xe9,0xbe,0xbd,0x5b,0xff,0xa7,0x8f,0xef,0xf5,0xdf,0xff,0xff,0xf5,0xef,0xff,0x52,
0x03,0xfb,0x5f,0xf7,0x4b,0xbf,0xb4,0xb6,0xd7,0xb4,0xb6,0x18,0x5d,0x8a,0xeb,0x7d,
0xad,0xac,0x30,0xb6,0xb0,0xc2,0xc3,0x0b,0x3a,0x65,0x40,0x3b,0x8a,0x1e,0x04,0x03,
0x87,0x77,0x77,0x77,0x39,0xc5,0xf0,0xde,0xfb,0xed,0xff,0xff,0xff,0xd2,0xd7,0x0b,
0x15,0xd5,0x55,0x42,0xa8,0x29,0xdc,0x50,0xe7,0x45,0xff,0xff,0xff,0xff,0xe0,0x28,
0x4c,0xe5,0x27,0x39,0xe9,0x61,0x07,0x84,0x1e,0x9e,0x10,0x7a,0x7a,0x7f,0xa7,0xa3,
0x23,0x9f,0x08,0x3e,0x82,0x6f,0xdf,0x49,0xbe,0x9f,0x5b,0xff,0xa7,0xfe,0x3b,0xfe,
0xeb,0xff,0xff,0xff,0x7d,0x7f,0x20,0x3f,0xd7,0x6b,0xfe,0xf5,0xda,0xed,0xaf,0x69,
0x6d,0xa5,0xc3,0x0b,0xb1,0x5b,0x5f,0xb5,0xb5,0x86,0xb6,0xf0,0xc3,0xc3,0x79,0xd3,
0x67,0x3b,0x88,0xe1,0x87,0xb7,0x61,0x6d,0x61,0xad,0xad,0xad,0x85,0x00,0x3b,0x8a,
0x1e,0x04,0x01,0xdc,0x3b,0xbb,0xee,0x73,0x8b,0xe1,0xf6,0xf7,0xfd,0xff,0xff,0xf5,
0xeb,0x5d,0x70,0x94,0x75,0x55,0x55,0x0b,0xee,0x63,0xbf,0x6f,0xf6,0xff,0x6f,0xf6,
0xff,0xbd,0xfb,0xdf,0xbd,0xfb,0xdf,0xbd,0xbe,0x28,0x44,0xe5,0xa9,0x39,0xa9,0xe0,
0x83,0xc2,0x0f,0x4f,0x4f,0x4f,0x4f,0x4f,0xf4,0x64,0x73,0xe1,0x07,0xd2,0x6f,0x4f,
0xfd,0x3e,0xfe,0x3b,0xdb,0xef,0x87,0xb0,0xf6,0x0f,0x83,0xd8,0x7b,0x07,0x86,0xf6,
0x1e,0x1b,0xc3,0x0f,0x0f,0x86,0xf0,0x6f,0x06,0xf0,0xf8,0x7d,0xb3,0x9a,0x86,0xff,
0xbf,0x90,0x16,0xbb,0x5c,0x30,0x96,0xc7,0xbf,0x5b,0x5b,0x58,0x6b,0x6b,0x0c,0x2c,
0x18,0x59,0xd1,0x32,0xff,0xca,0x11,0x39,0xa9,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x80,
0x39,0xc8,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xa9,0x01,0xfd,0xaf,0x6b,0xb6,0x97,0x0c,0x2e,0xc5,0x7e,0xd6,0xd6,0xd6,0xd6,
0xd6,0x18,0x58,0x60,0xb3,0xa0,0x64,0x39,0xeb,0x48,0x0e,0xbb,0x5f,0xf7,0xaf,0x5e,
0xfd,0xeb,0xfe,0xd7,0x7a,0xff,0xfd,0xb4,0xbf,0xff,0x74,0xbb,0xff,0xde,0xbb,0x5f,
0xf7,0xaf,0x5e,0xfd,0xeb,0xff,0x5d,0xba,0xff,0xfd,0xb4,0xbf,0xff,0x74,0xbb,0xff,
0xde,0xb8,0xff,0x6b,0xff,0x6b,0xfa,0xdf,0xfa,0xdf,0xfa,0xdf,0xfa,0x80,0x38,0xd1,
0xc7,0x18,0x3b,0xd5,0xf5,0x20,0x35,0xfe,0xff,0xff,0xd2,0xdb,0xff,0x5f,0xef,0xff,
0xfd,0x2d,0xb5,0xfe,0xff,0xff,0xff,0xeb,0x75,0x5e,0xef,0xff,0xff,0xdd,0x6b,0xed,
0x7b,0xff,0xff,0x98,0x51,0xd6,0xeb,0xff,0xb7,0xff,0xaf,0x75,0xfe,0xbd,0x6f,0xb7,
0xff,0xff,0xc7,0xc5,0x7b,0xb5,0xff,0xfd,0x7e,0xd6,0xff,0xfd,0x7e,0xff,0x5b,0xff,
0xf5,0xfb,0xef,0x5f,0xf5,0xfb,0xfd,0x6f,0xc0,0x20,0x27,0x3d,0x24,0x3b,0x4b,0x7f,
0x6d,0x2f,0xf6,0xd2,0xff,0x6d,0x2f,0xad,0xb5,0xde,0xbb,0x5d,0xeb,0xb5,0xde,0xbb,
0x4b,0x7f,0xd2,0xd8,0xf6,0xbf,0x6b,0xad,0xfa,0xdf,0x6b,0xf5,0xef,0x4f,0xf4,0xf5,
0xef,0x5e,0xf4,0x63,0x8b,0xf7,0xd2,0xbd,0x5f,0xab,0xd5,0xfa,0xbd,0x5b,0xeb,0xd5,
0xbd,0x2f,0xdb,0xd2,0xfd,0xbd,0x2b,0xf7,0xd2,0xb8,0xdb,0x00,0x39,0xca,0x48,0x0b,
0xaf,0xf6,0xd2,0xff,0x6d,0x2f,0xf7,0x4b,0xbf,0x7a,0xed,0x77,0xae,0xd7,0x7a,0xf5,
0xdb,0xaf,0xf6,0xd2,0xff,0x6d,0x2f,0xf7,0x4b,0xbf,0x7a,0xe3,0xda,0xfd,0xaf,0xda,
0xfd,0xaf,0xda,0xfd,0xaf,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x80,0x43,
0xff,0xfa,0xa9,0x43,0x7d,0x2d,0x2f,0xd2,0xd2,0xd7,0xad,0x2d,0x7a,0xd2,0xd7,0xad,
0x2d,0x74,0xba,0xd2,0xd7,0xad,0x2d,0x7a,0xd2,0xd7,0xad,0x2d,0x74,0xba,0xd7,0x4b,
0xad,0x74,0x6a,0xa7,0xaf,0xff,0xff,0xc0,0xff,0xce,0xd5,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe6,0x8f,0xff,0x80,0x39,0xcd,0x10,
0x1f,0x7b,0xff,0x7b,0xff,0x7b,0xf7,0xbf,0xf7,0xbf,0x7b,0xff,0x7b,0xf7,0xfb,0xf7,
0xbf,0xf7,0xbf,0x7b,0xff,0x7b,0xf7,0xfb,0xf7,0xbf,0xf7,0xbf,0x7b,0xff,0x7b,0xf7,
0xfb,0xf7,0xbf,0xf7,0xbf,0x7b,0xff,0x7b,0xf6,0xff,0xc8,0x27,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0x8f,0xfe,0x28,0x30,0xe5,0x25,0x5f,0xd3,0xfe,0xf5,0xfd,0x3f,0xd3,0xe6,0x24,
0xfe,0xf4,0x9f,0xfd,0x5b,0xeb,0xd6,0xfa,0x7f,0xbd,0x5f,0xaf,0x5b,0xe9,0xf5,0xbf,
0xe2,0x9b,0x00,0xff,0xc0,0x3b,0x94,0x40,0x5e,0xfb,0x7b,0x7b,0xed,0xed,0xef,0xb7,
0xdd,0xc0,0x28,0x34,0xe5,0xa1,0x04,0x0f,0x08,0x3c,0x20,0xf4,0xf4,0xf4,0xf5,0xef,
0x46,0x3e,0x78,0x41,0xfe,0xfd,0xff,0xc7,0xff,0xce,0x6a,0xf9,0xc9,0x6f,0x05,0xd7,
0x0b,0xaf,0xe8,0xc7,0xff,0x0b,0x8a,0xff,0xff,0xf5,0xd6,0x1a,0xc7,0x20,0x3f,0xdf,
0x6f,0x66,0x4c,0xf6,0xbc,0x30,0x9b,0x86,0x10,0x80,0x39,0xad,0xff,0xff,0xff,0xff,
0xcb,0x48,0xc2,0x07,0x84,0x1c,0x5d,0xdf,0x7d,0xcc,0x85,0xc2,0x0d,0xd7,0xbf,0xed,
0xff,0xff,0xff,0xff,0xfd,0x2f,0xfb,0x58,0x61,0x28,0xfa,0xae,0xa6,0x30,0xaa,0x6b,
0x0c,0x29,0x42,0x40,0xa0,0x28,0x3c,0xe4,0xa1,0x39,0x68,0xe1,0x07,0xa7,0xa7,0xa7,
0xa7,0xa7,0xaf,0x7a,0x31,0xf7,0xc2,0x7d,0x5b,0xd7,0xdf,0x5f,0xc6,0x3f,0xff,0xe4,
0x07,0xe7,0x30,0xcd,0xff,0xda,0xdd,0x2e,0x1a,0xec,0x7d,0x6f,0xb5,0xb5,0xb5,0xb5,
0xb5,0x86,0x16,0x73,0xc9,0x80,0x28,0x67,0xff,0xff,0xff,0xff,0xff,0xff,0x39,0x84,
0x70,0x41,0xf0,0x9f,0x43,0xad,0x7d,0x7d,0x73,0x1f,0x74,0x10,0x7e,0xfa,0xfe,0x2b,
0xff,0xff,0xff,0xff,0xf2,0x02,0xff,0xef,0xeb,0x61,0x85,0xc7,0x7f,0x7f,0x66,0x50,
0xed,0x70,0xd7,0x69,0xe0,0xc1,0x08,0x28,0x3c,0xe5,0x1f,0x04,0x0f,0x08,0x3c,0x20,
0xf4,0xf4,0xf4,0xf4,0xff,0x4f,0x98,0xed,0xd2,0x0d,0xf5,0xe9,0x3f,0xff,0x7a,0xf1,
0x8f,0xf3,0x9a,0xd9,0x01,0x7f,0xd9,0x90,0x3f,0x7b,0x69,0xf6,0x17,0x61,0x84,0xf8,
0xbd,0xbe,0xf6,0xb6,0xb6,0xb0,0xd6,0x18,0x58,0x61,0x60,0xce,0x51,0xc0,0x28,0x8a,
0x18,0x27,0x0b,0x5a,0xd6,0xbe,0xbf,0x3b,0xd5,0x85,0xaf,0x5f,0xff,0xf1,0x35,0x6f,
0xff,0xf9,0x06,0x3b,0x5b,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xe0,0x28,0x34,0xe4,0x27,0x04,0x0c,0xb0,0xb8,0x4d,0x69,0xf4,
0x3a,0xd7,0xd6,0xb9,0x90,0xbc,0x20,0xf4,0x9f,0xfa,0xfe,0x3d,0x7f,0xff,0xff,0xfc,
0x80,0xbf,0xfb,0xff,0x6d,0x70,0xc2,0xe3,0xbd,0xfd,0xef,0x7b,0x32,0x27,0x0c,0x2e,
0x0c,0x2e,0x3c,0x9c,0xad,0x95,0x1f,0xa6,0x96,0x81,0x85,0xd0,0xaf,0xd2,0xd2,0xfc,
0x30,0xb6,0xb0,0xc2,0xc3,0x0b,0x06,0x72,0xd0,0x00,0x39,0xad,0xff,0xff,0xff,0xff,
0xcb,0xc8,0xc1,0x03,0xd3,0xd3,0xd3,0x8b,0xbf,0xb3,0x21,0xa1,0x3a,0x7f,0xef,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf8,0x20,0x84,0xda,0x83,0xd3,0xd3,0x1f,
0x90,0x1d,0xad,0xad,0x85,0x8f,0xf9,0x4e,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x80,0x28,0x61,0x36,0x84,0xf4,0xf4,0xfb,0xff,
0xfd,0x6d,0x6d,0x61,0xac,0x7f,0xce,0x86,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xba,
0xc7,0x5d,0x75,0x50,0xa1,0x42,0x80,0x39,0xad,0xff,0xff,0xff,0xff,0xf3,0x94,0x86,
0x96,0x97,0x5a,0x5a,0x5a,0x5a,0x5f,0xa5,0xa5,0xa5,0x15,0x55,0xd7,0x7d,0xf7,0x7d,
0xcc,0x7a,0x75,0xd3,0x7d,0xef,0xb7,0xfb,0x7d,0xef,0xde,0xde,0xfd,0xed,0x80,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xe0,0x28,0x50,0xe4,0x1c,0x72,0x4d,0x31,0x94,0x30,
0x83,0xc2,0x0c,0x20,0xf4,0xd3,0xd5,0x38,0xb5,0x8b,0xfb,0x32,0x14,0xc9,0x58,0x4c,
0x27,0x4d,0x3a,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0x00,0x28,0x54,0xe4,0x16,0x63,0x30,0x76,0x10,0x7a,0x7a,
0x71,0xdf,0x7c,0xc8,0x6c,0x27,0xde,0xbd,0xfb,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xfc,0x28,0x3c,0xe5,0x1f,0x04,0x0f,0x04,0x1e,0x83,0xd3,0xd3,0xd3,0xd3,
0xd7,0xbd,0x18,0xfa,0xfa,0x0f,0xa4,0xfd,0x37,0xff,0x5c,0x7e,0xfd,0xff,0xff,0xfe,
0x97,0xc8,0x0b,0xff,0xed,0x2d,0xb5,0xed,0x76,0x2b,0xf6,0xb6,0xb6,0xb6,0xb6,0xb6,
0x16,0x18,0x59,0xcf,0x2a,0x28,0x48,0xe4,0x1c,0x63,0x20,0x3c,0x27,0xa0,0xe2,0xef,
0xbe,0xe6,0x42,0xe1,0x37,0x4f,0xbf,0xfe,0xdf,0xff,0xff,0xff,0xf4,0xbf,0xf5,0xb5,
0x86,0x94,0x75,0xd5,0x70,0xa6,0x34,0xac,0x35,0x86,0x0b,0x1f,0xff,0xff,0xff,0x80,
0x28,0x30,0xe4,0x29,0x04,0x19,0x69,0x74,0x1f,0x09,0xf4,0x3a,0xfa,0xfa,0xe6,0x3e,
0xe8,0x20,0xfd,0xf5,0xfc,0x57,0xff,0xff,0xff,0xff,0x20,0x3f,0x7f,0xf7,0xf5,0xb0,
0xc2,0xe3,0xbf,0xbd,0xfd,0xe1,0x99,0x03,0xb0,0xb8,0x30,0xb8,0xff,0xff,0xff,0xff,
0xff,0xf8,0x28,0x94,0xcc,0x9a,0xd6,0xbc,0x7f,0x3c,0x5e,0x15,0x57,0xff,0xff,0xff,
0xff,0xff,0xff,0xc0,0x28,0x34,0xe5,0xa1,0x04,0x74,0x8c,0x84,0x1e,0x9e,0x13,0xd3,
0xef,0x4f,0xf4,0x63,0xab,0xe8,0x3f,0x5f,0xb7,0xfe,0x38,0x7c,0x3d,0x9d,0x44,0x73,
0xb0,0x6d,0x87,0xb0,0xf6,0x1e,0xde,0x1b,0xc3,0xe0,0xde,0x74,0xd1,0xc1,0xf7,0x9c,
0xc3,0x19,0x01,0x7f,0xae,0xd7,0x86,0xbb,0x1e,0xd7,0xed,0x6d,0x6c,0x2c,0x35,0x86,
0x16,0x0c,0xe5,0xa0,0x20,0xa7,0x6b,0xbf,0xff,0xff,0xff,0xfc,0x4f,0x34,0x7f,0xff,
0x90,0x53,0xb5,0xdf,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xbe,0x6a,
0x7f,0xef,0xef,0x7b,0xde,0x0c,0x28,0x39,0xa6,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xfc,0x80,0xb5,0xeb,0x86,0x17,0x1f,0xbf,0xbf,0xb3,0x28,0x76,0x17,
0x0d,0x70,0x60,0x84,0x39,0xc6,0x75,0x20,0x3f,0xfe,0xd2,0xdf,0xff,0xd2,0xdb,0xff,
0xff,0x74,0xbb,0xff,0xde,0xbd,0x7b,0xf7,0xaf,0xfd,0x76,0xeb,0xff,0xf7,0x4b,0x8f,
0xf6,0xbf,0xf6,0xbf,0xf6,0xbf,0xf6,0xbf,0xf6,0xbf,0xe0,0x39,0x85,0x9c,0x92,0xaf,
0xa9,0x01,0xa7,0xff,0xff,0xfa,0xfb,0xda,0x5d,0xff,0xff,0xaf,0xf7,0x5b,0xff,0xeb,
0xda,0x7f,0xf5,0xbf,0xff,0xfa,0x31,0x25,0xaf,0xfd,0x6d,0xff,0xe9,0x3f,0xfd,0xff,
0xf5,0xbf,0x1f,0x14,0xff,0xff,0xb5,0x5e,0xff,0xff,0xd3,0x5b,0xff,0xf5,0xfb,0x5b,
0xff,0xf4,0xff,0x5b,0xf0,0x20,0x27,0x38,0xbd,0xd2,0xee,0xb7,0x5e,0xeb,0x75,0xdb,
0xaf,0x5d,0xba,0xf4,0xb6,0xfe,0x2b,0x7e,0xb7,0xeb,0x7d,0xaf,0xda,0xfd,0x3d,0x7b,
0xd7,0xbd,0x7b,0xd7,0x98,0xd2,0xf4,0xbf,0x6f,0x4a,0xf5,0x7e,0xaf,0x57,0xea,0xf5,
0x7e,0xaf,0x57,0x8a,0xb0,0x39,0xc6,0xeb,0xc8,0x0e,0xbb,0x5d,0xff,0xaf,0xf6,0xd7,
0xeb,0xfd,0xb4,0xbf,0xff,0x74,0xbb,0xff,0xde,0xbb,0x5f,0xf7,0xaf,0x5e,0x3d,0xaf,
0xfd,0xaf,0xfd,0xaf,0xfd,0xaf,0xeb,0x7f,0xda,0xff,0xda,0xff,0xd2,0xfd,0x7a,0xc2,
0xe5,0xa9,0xf5,0xfd,0x7f,0x5d,0x75,0xc2,0xd8,0x50,0x43,0xff,0xfa,0xa9,0x42,0x2b,
0xf4,0xb4,0xb4,0xbf,0x4b,0x4b,0x4b,0x5e,0xb4,0xb4,0xb4,0xbf,0x4b,0x4b,0x4b,0xf4,
0xb4,0xb4,0x68,0xa8,0xff,0xc0,0x28,0x98,0x21,0x82,0x78,0x5d,0x74,0xf5,0xd3,0xa0,
0xba,0xd2,0xfd,0x2f,0xff,0xff,0xff,0xff,0xff,0xff,0xfd,0x7f,0xaf,0xd7,0xac,0x2c,
0x57,0x50,0xaa,0xe1,0xdf,0x20,0x6f,0x0f,0xde,0xff,0xdf,0xef,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xb7,0xfb,0x7d,0xec,0x3b,0x5b,0xef,0xb5,0x87,0xc1,0xa8,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0x80,0x20,0x65,0x34,0xa0,0x7c,0x3e,0xf4,0xfb,0x8b,
0x90,0x27,0xbe,0xdf,0xed,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf7,0xfb,0xff,0x7f,
0xb0,0xf6,0x0f,0xde,0x1e,0xf5,0x85,0xaf,0xa0,0x5a,0x0b,0xfa,0xfd,0x7f,0xaf,0xff,
0xff,0xff,0xff,0xff,0xff,0xd7,0xaf,0xd2,0xd6,0x2a,0x40,0x57,0x5b,0x5d,0x70,0xb6,
0x0a,0x28,0x24,0xe4,0x3f,0x08,0x33,0x9c,0x4d,0x07,0xd3,0xe9,0xf4,0xd6,0x9f,0x86,
0xb8,0xad,0x79,0x8b,0x9e,0x83,0x5d,0x3f,0xb5,0x1b,0x5b,0x52,0x0c,0x83,0x58,0x86,
0x0a};
+91
View File
@@ -0,0 +1,91 @@
//
// Compressed font example
// written by Larry Bank
//
// This sketch demonstrates how to work with BBF (BitBank Font) files
// and render the characters to a color LCD display
// This should definitely be considered an advanced topic. Some of this functionality will be baked into
// my display libraries eventually, but the paid vs free divide makes it necessary to demonstrate how to
// work with the compressed font files created by the Group5 fontconvert tool
//
#include <Group5.h>
#include "bb_font.h"
#include <bb_spi_lcd.h>
#include "Roboto_Black_40.h" // BBF (BitBank Font file included as hex data to compile with the sketch)
BB_SPI_LCD lcd; // class instance of bb_spi_lcd display library
G5DECODER g5dec; // class instance of Group5 image decoder
uint16_t u16Temp[256]; // holds one line of a character being displayed
//
// Draw a string of characters in the given font at the given position and color
// The y coordinate is relative to the baseline of the character, not the top
// This means that drawing at coordinate 0,0 on the display will usually get clipped 100%
// For this example, a 40pt font is about 64 pixels tall, so the baseline should be top+64
//
void DrawString(const char *szMsg, const BB_FONT *pFont, int x, int y, uint16_t u16FG, uint16_t u16BG)
{
int i, rc, iBitOff, dx, dy, cx, cy, tx, ty;
char c;
BB_GLYPH *pGlyph;
uint8_t *s, u8, u8Mask, *pG5, u8Temp[32]; // 32 bytes can hold 256 1-bit pixels (of each character)
uint16_t *d;
u16FG = __builtin_bswap16(u16FG); // swap for big endian (the SPI LCD data format)
u16BG = __builtin_bswap16(u16BG);
i = 0;
cx = (pFont->last - pFont->first + 1); // Glyph count
pG5 = (uint8_t *)&pFont->glyphs[cx]; // compressed data starts after the last glyph
while (szMsg[i] != 0 && x < lcd.width()) {
c = szMsg[i++];
if (c < pFont->first || c > pFont->last) // undefined character
continue; // skip it
c -= pFont->first; // subtract first char of font defined to start from index 0
pGlyph = (BB_GLYPH *)&pFont->glyphs[c];
// set up the destination window (rectangle) on the display
dx = x + pGlyph->xOffset; // offset from character UL to start drawing
dy = y + pGlyph->yOffset;
cx = pGlyph->width;
cy = pGlyph->height;
if (dy + cy > lcd.height())
cy = lcd.height() - dy; // clip bottom edge
if (dx + cx > lcd.width())
cx = lcd.width() - dx; // clip right edge
lcd.setAddrWindow(dx, dy, cx, cy); // tell the LCD the memory window in advance of writing the pixels
s = pG5 + pGlyph->bitmapOffset; // start of bitmap data
ty = (pGlyph[1].bitmapOffset - pGlyph[0].bitmapOffset); // compressed size
if (ty < 0 || ty > 4096) ty = 4096; // DEBUG
rc = g5dec.init(pGlyph->width, cy, s, ty);
if (rc != G5_SUCCESS) return;
for (ty=0; ty<cy && rc == G5_SUCCESS; ty++) { // loop over the lines (all equal width)
rc = g5dec.decodeLine(u8Temp); // decode the 1-bit image one line at a time
s = u8Temp;
d = u16Temp; // point to start of output buffer
u8Mask = 0x80; // MSB first
u8 = *s++; // preload first byte
for (tx=0; tx<cx; tx++) { // loop over the pixels on this line and convert from 1 to 16-bits
*d++ = (u8 & u8Mask) ? u16FG : u16BG; // 1/0 foreground or background color is written
u8Mask >>= 1;
if (u8Mask == 0) { // next source byte
u8 = *s++;
u8Mask = 0x80;
}
} // for x
lcd.pushPixels(u16Temp, cx); // write to the memory window one line at a time
} // for ty
x += pGlyph->xAdvance;
} // while characters
} /* DrawString() */
void setup()
{
lcd.begin(DISPLAY_CYD_2USB); // easy initialization of a named display type (ESP32 w/ 2.8" 240x320 ST7789)
lcd.fillScreen(TFT_BLACK);
DrawString("Hello", (BB_FONT *)Roboto_Black_40, 0, 70, TFT_GREEN, TFT_BLACK);
DrawString("World!", (BB_FONT *)Roboto_Black_40, 0, 140, TFT_BLUE, TFT_BLACK);
} /* setup() */
void loop()
{
} /* loop() */
+33
View File
@@ -0,0 +1,33 @@
// Created with image_to_c
// https://github.com/bitbank2/image_to_c
//
// bart
// Data size = 349 bytes
//
// for non-Arduino builds...
#ifndef PROGMEM
#define PROGMEM
#endif
const uint8_t bart[] PROGMEM = {
0xcc,0xa8,0xe9,0xcc,0x9f,0x09,0x1c,0x2c,0x3c,0x14,0x11,0x0a,0xa0,0xb6,0xe6,0x3e,
0xe1,0x04,0xec,0xbc,0x74,0x21,0xf6,0x61,0x35,0x91,0xc5,0x98,0x25,0x74,0x14,0x1e,
0xe0,0x91,0x09,0x98,0x29,0xa5,0x22,0xe6,0x0a,0x32,0x23,0x84,0x08,0x17,0xce,0x10,
0x4e,0xa9,0x88,0xf0,0x49,0x8b,0xb2,0x13,0xac,0xc2,0x3f,0x85,0x08,0x84,0xec,0x96,
0x18,0x51,0x79,0x85,0xcc,0x7f,0x59,0xc2,0x43,0x43,0x5f,0xfe,0xa0,0xcc,0x1b,0x61,
0x75,0x94,0x85,0x84,0x42,0x5f,0xa0,0x47,0x10,0x09,0x91,0x10,0x88,0x4a,0xff,0xff,
0x6c,0xc2,0xfa,0xa6,0x08,0x5e,0xbd,0x90,0xdf,0xbb,0xfb,0xff,0xfe,0xa6,0x1c,0x3c,
0xa4,0x2b,0xff,0xc7,0x08,0x2c,0x76,0x88,0x55,0xa5,0xeb,0x44,0x26,0x1a,0x11,0x61,
0xcc,0x21,0xbf,0x4f,0xe1,0xc8,0x48,0x8c,0x90,0xc7,0xfd,0xf0,0xcb,0xc3,0x57,0x1f,
0xc2,0x46,0x28,0x39,0x82,0x09,0x84,0xd2,0xca,0xc2,0x3f,0x48,0xc2,0xfb,0xc6,0x2b,
0x58,0xf4,0x83,0xee,0xa3,0x5b,0xc8,0xa7,0x79,0x48,0x58,0x44,0x26,0x08,0x84,0xd9,
0x50,0x98,0x41,0xbf,0xc1,0x16,0x04,0x84,0x13,0x21,0x3f,0x0b,0xdd,0xff,0xf7,0xf4,
0x92,0x77,0xf4,0x08,0x84,0xf6,0x42,0xbf,0x0a,0x9f,0xfa,0xf7,0xfe,0xde,0x4a,0xbb,
0x5f,0xfb,0x05,0xf7,0xdd,0xeb,0xaf,0xac,0x77,0x5f,0x75,0xff,0x82,0x6a,0x96,0xd3,
0xe8,0x11,0x0b,0xeb,0x4b,0xe9,0x38,0xfd,0x06,0x0f,0x58,0xfa,0xde,0xba,0xce,0x10,
0x27,0x54,0x38,0x44,0x26,0x1f,0xe2,0x99,0x0a,0xf5,0x2a,0x09,0x5f,0xbe,0xb6,0x44,
0xce,0x10,0x19,0xaa,0x7f,0xa5,0x7d,0x37,0x24,0x2f,0x09,0x61,0x90,0xef,0x5d,0x78,
0x8b,0x55,0x5b,0xfd,0x3d,0x61,0x91,0xad,0x56,0xff,0x61,0xb2,0x2c,0x9c,0x49,0xac,
0x45,0x32,0x19,0xfa,0x1e,0xc3,0xab,0x4f,0xa5,0x83,0x12,0xa1,0xa1,0x08,0x84,0xea,
0x45,0xd6,0x0d,0x3f,0x4a,0xb4,0x85,0x8f,0x07,0x82,0x05,0x92,0x1f,0x87,0xd3,0xfc,
0x6d,0x7f,0xf5,0xff,0xe8,0xe1,0x61,0x7f,0xff,0x8f,0xf3,0x88,0x05,0x7e,0x94,0xc3,
0x52,0xc2,0xfb,0x89,0x82,0x16,0xfd,0x4c,0x3f,0xc2,0x56,0x86,0x38};
+76
View File
@@ -0,0 +1,76 @@
//
// Getting_started with the Group5 image compression library
// Written by Larry Bank, September 20, 2024
//
// This sketch will demonstrate how to decode a 128x64 1-bit image
// and display it on an OLED display
//
#include <Group5.h>
#include <OneBitDisplay.h>
#include "bart.h" // a pre-compressed image made with the Linux CLI tool in this repo
ONE_BIT_DISPLAY oled;
G5DECODER g5dec;
uint8_t u8Buffer[1024]; // big enough to hold the OLED contents
#define IMAGE_WIDTH 128
#define IMAGE_HEIGHT 64
//
// Image files are normally organized as horizontal bytes with the MSB on the left
// The SSD1306 OLED is vertical bytes with the LSB as the top pixel of each set of 8 rows
// So we need to rotate the pixels to the display's memory layout
//
void DrawLine(int y, uint8_t *u8Line)
{
uint8_t u8, u8SrcMask, u8DestMask, *s, *d;
int x;
u8DestMask = 1 << (y & 7);
d = &u8Buffer[(y>>3) * IMAGE_WIDTH];
s = u8Line;
u8SrcMask = 0x80; // image encoding is MSB first (left pixel of each byte)
u8 = *s++;
for (x=0; x<IMAGE_WIDTH; x++) {
if (u8 & u8SrcMask)
d[x] |= u8DestMask;
else
d[x] &= ~u8DestMask;
u8SrcMask >>= 1;
if (u8SrcMask == 0) { // next byte
u8SrcMask = 0x80;
u8 = *s++;
}
} // for x
} /* DrawLine() */
void setup()
{
oled.I2Cbegin(OLED_128x64); // use default I2C interface for target board
oled.fillScreen(OBD_WHITE); // really black on OLEDs
oled.setFont(FONT_12x16);
oled.println("Group5 Demo");
oled.setBuffer(u8Buffer);
delay(2000);
} /* setup() */
void loop()
{
int rc, y;
long lTime;
uint8_t u8Line[16]; // current decoded line
rc = g5dec.init(IMAGE_WIDTH, IMAGE_HEIGHT, (uint8_t *)bart, sizeof(bart));
if (rc == G5_SUCCESS) {
lTime = millis();
for (y=0; y<IMAGE_HEIGHT && rc == G5_SUCCESS; y++) {
rc = g5dec.decodeLine(u8Line);
DrawLine(y, u8Line); // draw this into the correct bit direction for the OLED
}
lTime = millis() - lTime;
oled.display();
delay(2000);
oled.fillScreen(OBD_WHITE);
oled.println("Decoded in");
oled.print((int)lTime, DEC);
oled.println(" msec");
delay(3000);
}
} /* loop() */
+12
View File
@@ -0,0 +1,12 @@
all: fontconvert
CC = gcc
CFLAGS = -Wall -I/usr/local/include/freetype2 -I/usr/include/freetype2 -I/usr/include -I/opt/homebrew/include/freetype2
LIBS = -L/opt/homebrew/lib -lfreetype
fontconvert: fontconvert.c
$(CC) $(CFLAGS) $< $(LIBS) -o $@
strip $@
clean:
rm -f fontconvert
+263
View File
@@ -0,0 +1,263 @@
//
// TrueType to bb_font converter
// Written by Larry Bank
// August 31, 2024
// The bb_font format is a losslessly compressed bitmap font of a single point size
// The data is compressed with a compression scheme based on CCITT T.6
// The font structure includes overall size, per-character glyph info and then the
// compressed image data at the end.
// The font file format is designed to allow both dynamic loading of font data from
// external memory/disk or compiling the data as const into a progarm.
//
// Example usage:
// ./fontconvert <my_font.ttf> <out.bbf> <pt size> <start char> <end char>
// This code requires the freetype library
// found here: www.freetype.org
//
#include <ctype.h>
#include <ft2build.h>
#include <stdint.h>
#include <stdio.h>
#include FT_GLYPH_H
#include FT_MODULE_H
#include FT_TRUETYPE_DRIVER_H
#include "../src/bb_font.h" // BitBank font structures
#include "../src/g5enc.h" // Group5 image compression library
G5ENCIMAGE g5enc; // Group5 encoder state
#define DPI 141 // Approximate resolution of common displays
#define OUTBUF_SIZE 65536
//
// Rotate a character 90/180/270 degrees
//
int RotateBitmap(int iAngle, uint8_t *pSrc, int iWidth, int iHeight, int iSrcPitch, uint8_t *pDst)
{
int x, y, tx, ty, iDstPitch;
uint8_t *s, *d, uc, ucMask;
switch (iAngle) {
case 90:
iDstPitch = (iHeight+7)/8;
for (y=0; y<iWidth; y++) {
ucMask = 0x80 >> (y & 7);
s = &pSrc[(y>>3) + (iHeight-1) * iSrcPitch]; // work from bottom up
d = &pDst[y * iDstPitch];
memset(d, 0, iDstPitch);
for (x=0; x<iHeight; x++) {
if (s[0] & ucMask) {
d[x >> 3] |= (0x80 >> (x & 7));
}
s -= iSrcPitch;
} // for x
} // for y
break;
case 180:
iDstPitch = iSrcPitch;
for (y=0; y<iHeight; y++) {
s = &pSrc[(iHeight-y-1) * iSrcPitch]; // work from bottom up
d = &pDst[y * iDstPitch];
memset(d, 0, iDstPitch);
for (x=0; x<iWidth; x++) {
tx = (iWidth-x-1); // reverse x direction
// This code doesn't need to be efficient
uc = s[tx>>3] & (0x80 >> (tx & 7)); // source pixel
if (uc) { // set
d[x >> 3] |= (0x80 >> (x & 7));
}
} // for x
} // for y
break;
case 270:
iDstPitch = (iHeight+7)/8;
for (y=0; y<iWidth; y++) {
ty = (iWidth-1-y);
ucMask = 0x80 >> (ty & 7);
s = &pSrc[ty>>3]; // work from bottom up
d = &pDst[y * iDstPitch];
memset(d, 0, iDstPitch);
for (x=0; x<iHeight; x++) {
if (s[0] & ucMask) {
d[x >> 3] |= (0x80 >> (x & 7));
}
s += iSrcPitch;
} // for x
} // for y
break;
default:
return -1; // invalid
}
return iDstPitch;
} /* RotateBitmap() */
int main(int argc, char *argv[])
{
int i, err, size, first = ' ', last = '~', y;
int iLen ,iOffset = 0;
int iRotation = 0;
uint8_t *pBitmap, *pTemp;
FILE *fOut;
// TrueType library structures
FT_Library library;
FT_Face face;
FT_Glyph glyph;
FT_Bitmap *bitmap;
FT_BitmapGlyphRec *g;
// BitBank Font structures
BB_GLYPH *pGlyphs;
BB_FONT bbff;
if (argc < 4) {
printf("Usage: %s <in.ttf> <out.bbf> point_size [first_char] [last_char] [rotation (90/180/270)]\n", argv[0]);
return 1;
}
size = atoi(argv[3]);
if (argc == 5) {
last = atoi(argv[4]); // only ending character was provided
} else if (argc >= 6) {
first = atoi(argv[4]); // start + end character codes were provided
last = atoi(argv[5]);
}
if (argc == 7) { // rotation angle provided
iRotation = atoi(argv[6]);
if (iRotation != 90 && iRotation != 180 && iRotation != 270) {
printf("Rotation angle can only be 90/180/270\n");
return 1;
}
}
if (last < first) {
printf("Something went wrong - the starting character comes after the ending character. Try again...\n");
return 1;
}
pGlyphs = (BB_GLYPH *)malloc((last - first + 1) * sizeof(BB_GLYPH));
if (!pGlyphs) {
printf("Error allocating memory for glyph data\n");
return 1;
}
pBitmap = (uint8_t *)malloc(OUTBUF_SIZE); // Enough to hold the output
pTemp = (uint8_t *)malloc(OUTBUF_SIZE); // for rotated bitmaps
if (!pBitmap || ! pTemp) {
printf("Error allocating memory for bitmap data\n");
return 1;
}
// Init FreeType lib, load font
if ((err = FT_Init_FreeType(&library))) {
printf("FreeType init error: %d", err);
return err;
}
// Print parameters
printf("fontconvert %s to %s, size: %dpt, first: %d, last: %d, rotation: %d\n", argv[1], argv[2], size, first, last, iRotation);
// Use TrueType engine version 35, without subpixel rendering.
// This improves clarity of fonts since this library does not
// support rendering multiple levels of gray in a glyph.
// See https://github.com/adafruit/Adafruit-GFX-Library/issues/103
FT_UInt interpreter_version = TT_INTERPRETER_VERSION_35;
FT_Property_Set(library, "truetype", "interpreter-version",
&interpreter_version);
if ((err = FT_New_Face(library, argv[1], 0, &face))) {
printf("Font load error: %d", err);
FT_Done_FreeType(library);
return err;
}
// Shift the size left by 6 because the library uses '26dot6' fixed-point format
FT_Set_Char_Size(face, size << 6, 0, DPI, 0);
// Currently all symbols from 'first' to 'last' are processed.
// Fonts may contain WAY more glyphs than that, but this code
// will need to handle encoding stuff to deal with extracting
// the right symbols, and that's not done yet.
// Process glyphs and output huge bitmap data array
for (i = first; i <= last; i++) {
int index = i - first;
uint8_t *s;
int iPitch;
// MONO renderer provides clean image with perfect crop
// (no wasted pixels) via bitmap struct.
if ((err = FT_Load_Char(face, i, FT_LOAD_TARGET_MONO))) {
printf("Error %d loading char '%c'\n", err, i);
continue;
}
if ((err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO))) {
printf("Error %d rendering char '%c'\n", err, i);
continue;
}
if ((err = FT_Get_Glyph(face->glyph, &glyph))) {
printf("Error %d getting glyph '%c'\n", err, i);
continue;
}
bitmap = &face->glyph->bitmap;
g = (FT_BitmapGlyphRec *)glyph;
// Minimal font and per-glyph information is stored to
// reduce flash space requirements. Glyph bitmaps are
// fully bit-packed; no per-scanline pad, though end of
// each character may be padded to next byte boundary
// when needed. 16-bit offset means 64K max for bitmaps,
// code currently doesn't check for overflow. (Doesn't
// check that size & offsets are within bounds either for
// that matter...please convert fonts responsibly.)
pGlyphs[index].bitmapOffset = iOffset;
pGlyphs[index].width = bitmap->width;
pGlyphs[index].height = bitmap->rows;
pGlyphs[index].xAdvance = face->glyph->advance.x >> 6;
pGlyphs[index].xOffset = g->left;
pGlyphs[index].yOffset = 1 - g->top;
s = bitmap->buffer;
iPitch = bitmap->pitch;
if (iRotation != 0) {
iPitch = RotateBitmap(iRotation, bitmap->buffer, bitmap->width, bitmap->rows, bitmap->pitch, pTemp);
s = pTemp;
g5_encode_init(&g5enc, bitmap->rows, bitmap->width, &pBitmap[iOffset], OUTBUF_SIZE-iOffset);
for (y = 0; y < bitmap->width; y++) {
g5_encode_encodeLine(&g5enc, &s[y * iPitch]);
} // for y
} else { // not rotated
g5_encode_init(&g5enc, bitmap->width, bitmap->rows, &pBitmap[iOffset], OUTBUF_SIZE-iOffset);
for (y = 0; y < bitmap->rows; y++) {
g5_encode_encodeLine(&g5enc, &s[y * iPitch]);
} // for y
}
iLen = g5_encode_getOutSize(&g5enc);
iOffset += iLen;
FT_Done_Glyph(glyph);
} // for each glyph
// Try to create the output file
fOut = fopen(argv[2], "w+b");
if (!fOut) {
printf("Error creating output file: %s\n", argv[2]);
return 1;
}
// Write the file header
bbff.u16Marker = BB_MARKER;
bbff.first = first;
bbff.last = last;
bbff.rotation = iRotation; // save rotation angle
if (face->size->metrics.height == 0) {
// No face height info, assume fixed width and get from a glyph.
bbff.height = pGlyphs[0].height;
} else {
bbff.height = (face->size->metrics.height >> 6);
}
fwrite(&bbff, 1, sizeof(bbff), fOut);
// Write the glyph table
fwrite(pGlyphs, (last-first+1), sizeof(BB_GLYPH), fOut);
// Write the compressed bitmap data
fwrite(pBitmap, 1, iOffset, fOut);
fflush(fOut);
iLen = ftell(fOut);
fclose(fOut); // done!
FT_Done_FreeType(library);
printf("Success!\nFont file size: %d bytes\n", iLen);
return 0;
} /* main() */
+2 -3
View File
@@ -3,9 +3,8 @@ version=1.0.0
author=Larry Bank
maintainer=Larry Bank
sentence=A frustration-free eink library.
paragraph=Supports most common 24-pin SPI eink panels with a rich API. Both buffered and bufferless graphics supported. This means you can do interesting things on very constrained MCUs.
paragraph=Supports most common 24-pin SPI eink panels with a rich API. Includes an efficient image compression scheme. Both buffered and bufferless graphics supported; this means you can do interesting things on very constrained MCUs.
category=Display
url=https://github.com/bitbank2/bb_eink
architectures=*
includes=bb_eink.h
depends=Group5
includes=bb_eink.h,Group5.h
+13
View File
@@ -0,0 +1,13 @@
CFLAGS=-D__LINUX__ -Wall -O2
LIBS =
all: g5_demo
g5_demo: main.o
$(CC) main.o -o g5_demo
main.o: main.c ../src/Group5.h ../src/g5dec.h ../src/g5enc.h
$(CC) $(CFLAGS) -c main.c
clean:
rm -rf *.o g5_demo
+138
View File
@@ -0,0 +1,138 @@
//
// main.c
// Group5
//
// Created by Larry Bank on 8/19/24.
//
#include <stdio.h>
#include "../src/g5dec.h"
#include "../src/g5enc.h"
//
// Read a Windows BMP file into memory
//
uint8_t * ReadBMP(const char *fname, int *width, int *height, int *bpp, unsigned char *pPal)
{
int y, w, h, bits, offset;
uint8_t *s, *d, *pTemp, *pBitmap;
int pitch, bytewidth;
int iSize, iDelta;
FILE *infile;
infile = fopen(fname, "r+b");
if (infile == NULL) {
printf("Error opening input file %s\n", fname);
return NULL;
}
// Read the bitmap into RAM
fseek(infile, 0, SEEK_END);
iSize = (int)ftell(infile);
fseek(infile, 0, SEEK_SET);
pBitmap = (uint8_t *)malloc(iSize);
pTemp = (uint8_t *)malloc(iSize);
fread(pTemp, 1, iSize, infile);
fclose(infile);
if (pTemp[0] != 'B' || pTemp[1] != 'M' || pTemp[14] < 0x28) {
free(pBitmap);
free(pTemp);
printf("Not a Windows BMP file!\n");
return NULL;
}
w = *(int32_t *)&pTemp[18];
h = *(int32_t *)&pTemp[22];
bits = *(int16_t *)&pTemp[26] * *(int16_t *)&pTemp[28];
if (bits <= 8 && pPal != NULL) { // it has a palette, copy it
uint8_t *p = pPal;
for (int i=0; i<(1<<bits); i++)
{
*p++ = pTemp[54+i*4];
*p++ = pTemp[55+i*4];
*p++ = pTemp[56+i*4];
}
}
offset = *(int32_t *)&pTemp[10]; // offset to bits
if (bits == 1) {
bytewidth = (w+7) >> 3;
} else {
bytewidth = (w * bits) >> 3;
}
pitch = (bytewidth + 3) & 0xfffc; // DWORD aligned
// move up the pixels
d = pBitmap;
s = &pTemp[offset];
iDelta = pitch;
if (h > 0) {
iDelta = -pitch;
s = &pTemp[offset + (h-1) * pitch];
} else {
h = -h;
}
for (y=0; y<h; y++) {
if (bits == 32) {// need to swap red and blue
for (int i=0; i<bytewidth; i+=4) {
d[i] = s[i+2];
d[i+1] = s[i+1];
d[i+2] = s[i];
d[i+3] = s[i+3];
}
} else {
memcpy(d, s, bytewidth);
}
d += bytewidth;
s += iDelta;
}
*width = w;
*height = h;
*bpp = bits;
free(pTemp);
return pBitmap;
} /* ReadBMP() */
#define OUTSIZE 65536
int main(int argc, const char * argv[]) {
uint8_t *pBMP, *pOut = NULL;
int rc, w, h, y, bpp;
int iOutSize, iPitch;
G5ENCIMAGE g5enc;
printf("G5 file compressor\n");
if (argc != 3) {
printf("Usage: ./Group5 <WinBMP 1-bpp image> <g5 compressed image>\n");
return -1;
}
pBMP = ReadBMP(argv[1], &w, &h, &bpp, NULL);
if (bpp == 1) {
uint8_t *s = pBMP;
printf("bmp size %d x %d\n", w, h);
iPitch = (w+7) >> 3;
pOut = (uint8_t *)malloc(OUTSIZE);
rc = g5_encode_init(&g5enc, w, h, pOut, OUTSIZE);
for (y=0; y<h && rc == G5_SUCCESS; y++) {
rc = g5_encode_addLine(&g5enc, s);
s += iPitch;
}
if (rc == G5_ENCODE_COMPLETE) {
FILE *fOut;
printf("Encode succeeded!\n");
iOutSize = g5_encode_getOutSize(&g5enc);
printf("Input size = %d, output size = %d\n", iPitch*h, iOutSize);
fOut = fopen(argv[2], "w+b");
if (fOut) {
fwrite(pOut, 1, iOutSize, fOut);
fflush(fOut);
fclose(fOut);
} else {
printf("Error opening output file: %s\n", argv[2]);
}
}
} else {
printf("Only 1-bpp images are supported.\n");
}
free(pBMP);
free(pOut);
return 0;
}
+188
View File
@@ -0,0 +1,188 @@
//
// bb_eink I/O wrapper functions for Raspberry Pi
// Copyright (c) 2024 BitBank Software, Inc.
// Written by Larry Bank (bitbank@pobox.com)
// Project started 9/11/2024
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Adapt these functions to whatever target platform you're using
// and the rest of the code can remain unchanged
//
#ifndef __BB_EI_IO__
#define __BB_EI_IO__
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/spi/spidev.h>
#include <gpiod.h>
#include <math.h>
struct gpiod_chip *chip = NULL;
struct gpiod_line *lines[64];
static int spi_fd; // SPI handle
void SPI_transfer(BBEIDISP *pBBEI, uint8_t *pBuf, int iLen)
{
struct spi_ioc_transfer spi;
memset(&spi, 0, sizeof(spi));
spi.tx_buf = (unsigned long)pBuf;
spi.len = iLen;
spi.speed_hz = pBBEI->iSpeed;
spi.bits_per_word = 8;
ioctl(spi_fd, SPI_IOC_MESSAGE(1), &spi);
} /* SPI_transfer() */
int digitalRead(int iPin)
{
return gpiod_line_get_value(lines[iPin]);
} /* digitalRead() */
void digitalWrite(int iPin, int iState)
{
gpiod_line_set_value(lines[iPin], iState);
} /* digitalWrite() */
void pinMode(int iPin, int iMode)
{
if (chip == NULL) {
chip = gpiod_chip_open_by_name("gpiochip0");
}
lines[iPin] = gpiod_chip_get_line(chip, iPin);
if (iMode == OUTPUT) {
gpiod_line_request_output(lines[iPin], CONSUMER, 0);
} else if (iMode == INPUT_PULLUP) {
gpiod_line_request_input_flags(lines[iPin], CONSUMER, GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP);
} else { // plain input
gpiod_line_request_input(lines[iPin], CONSUMER);
}
} /* pinMode() */
static void delay(int iMS)
{
usleep(iMS * 1000);
} /* delay() */
static void delayMicroseconds(int iMS)
{
usleep(iMS);
} /* delayMicroseconds() */
//
// Initialize the GPIO pins and SPI for use by bb_eink
//
void bbeiInitIO(BBEIDISP *pBBEI, uint32_t u32Speed)
{
pinMode(pBBEI->iDCPin, OUTPUT);
pinMode(pBBEI->iRSTPin, OUTPUT);
digitalWrite(pBBEI->iRSTPin, LOW);
delay(100);
digitalWrite(pBBEI->iRSTPin, HIGH);
delay(100);
if (pBBEI->iBUSYPin != 0xff) {
pinMode(pBBEI->iBUSYPin, INPUT);
}
pBBEI->iSpeed = u32Speed;
pinMode(pBBEI->iCSPin, OUTPUT);
digitalWrite(pBBEI->iCSPin, HIGH); // we have to manually control the CS pin
spi_fd = open("/dev/spidev0.1", O_RDWR); // DEBUG - open SPI channel 0
} /* bbeiInitIO() */
//
// Toggle the reset line to wake up the eink from deep sleep
//
void bbeiWakeUp(BBEIDISP *pBBEI)
{
digitalWrite(pBBEI->iRSTPin, LOW);
delay(10);
digitalWrite(pBBEI->iRSTPin, HIGH);
delay(10);
} /* bbeiWakeUp() */
//
// Wait for the busy status line to show idle
// The polarity of the busy signal is reversed on the UC81xx compared
// to the SSD16xx controllers
//
void bbeiWaitBusy(BBEIDISP *pBBEI)
{
uint8_t busy_idle = (pBBEI->chip_type == BBEI_CHIP_UC81xx) ? HIGH : LOW;
delay(1); // some panels need a short delay before testing the BUSY line
while (1) {
if (digitalRead(pBBEI->iBUSYPin) == busy_idle) break;
}
} /* bbeiWaitBusy() */
//
// Convenience function to write a command byte along with a data
// byte (it's single parameter)
//
void bbeiCMD2(BBEIDISP *pBBEI, uint8_t cmd1, uint8_t cmd2)
{
if (!pBBEI->is_awake) {
// if it's asleep, it can't receive commands
bbeiWakeUp(pBBEI);
pBBEI->is_awake = 1;
}
digitalWrite(pBBEI->iDCPin, LOW);
digitalWrite(pBBEI->iCSPin, LOW);
SPI_transfer(pBBEI, &cmd1, 1);
digitalWrite(pBBEI->iDCPin, HIGH);
SPI_transfer(pBBEI, &cmd2, 1); // second byte is data
digitalWrite(pBBEI->iCSPin, HIGH);
digitalWrite(pBBEI->iDCPin, HIGH); // leave data mode as the default
} /* bbeiCMD2() */
//
// Write a single byte as a COMMAND (D/C set low)
//
void bbeiWriteCmd(BBEIDISP *pBBEI, uint8_t cmd)
{
if (!pBBEI->is_awake) {
// if it's asleep, it can't receive commands
bbeiWakeUp(pBBEI);
pBBEI->is_awake = 1;
}
digitalWrite(pBBEI->iDCPin, LOW);
digitalWrite(pBBEI->iCSPin, LOW);
SPI_transfer(pBBEI, &cmd, 1);
digitalWrite(pBBEI->iCSPin, HIGH);
digitalWrite(pBBEI->iDCPin, HIGH); // leave data mode as the default
} /* bbeiWriteCmd() */
//
// Put the eink into light or deep sleep
//
void bbeiSleep(BBEIDISP *pBBEI, int bDeep)
{
if (pBBEI->chip_type == BBEI_CHIP_UC81xx) {
bbeiCMD2(pBBEI, UC8151_CDI, 0x17); // border floating
bbeiWriteCmd(pBBEI, UC8151_POFF); // power off
bbeiWaitBusy(pBBEI);
if (bDeep) {
bbeiCMD2(pBBEI, UC8151_DSLP, 0xa5); // deep sleep
}
} else {
bbeiCMD2(pBBEI, SSD1608_DEEP_SLEEP, (bDeep) ? 0x02 : 0x01); // deep sleep mode 1 keeps RAM, mode 2 loses RAM
}
pBBEI->is_awake = 0;
} /* bbeiSleep() */
//
// Write 1 or more bytes as DATA (D/C set high)
//
void bbeiWriteData(BBEIDISP *pBBEI, uint8_t *pData, int iLen)
{
digitalWrite(pBBEI->iCSPin, LOW);
SPI_transfer(pBBEI, pData, iLen);
digitalWrite(pBBEI->iCSPin, HIGH);
} /* bbeiWriteData() */
#endif // __BB_EI_IO__
+46
View File
@@ -0,0 +1,46 @@
#include "g5enc.inl"
#include "g5dec.inl"
//
// Group5 1-bit image compression library
// Written by Larry Bank
// Copyright (c) 2024 BitBank Software, Inc.
//
// Use of this software is governed by the Business Source License
// included in the file ./LICENSE.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// ./APL.txt.
//
// Decoder C++ wrapper functions
//
int G5DECODER::init(int iWidth, int iHeight, uint8_t *pData, int iDataSize)
{
return g5_decode_init(&_g5dec, iWidth, iHeight, pData, iDataSize);
} /* init() */
int G5DECODER::decodeLine(uint8_t *pOut)
{
return g5_decode_line(&_g5dec, pOut);
} /* decodeLine() */
//
// Encoder C++ wrapper functions
//
int G5ENCODER::init(int iWidth, int iHeight, uint8_t *pOut, int iOutSize)
{
return g5_encode_init(&_g5enc, iWidth, iHeight, pOut, iOutSize);
} /* init() */
int G5ENCODER::encodeLine(uint8_t *pPixels)
{
return g5_encode_encodeLine(&_g5enc, pPixels);
} /* encodeLine() */
int G5ENCODER::size()
{
return g5_encode_getOutSize(&_g5enc);
} /* size() */
+153
View File
@@ -0,0 +1,153 @@
#ifndef __GROUP5__
#define __GROUP5__
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
//
// Group5 1-bit image compression library
// Written by Larry Bank
// Copyright (c) 2024 BitBank Software, Inc.
//
// Use of this software is governed by the Business Source License
// included in the file ./LICENSE.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// ./APL.txt.
//
// The name "Group5" is derived from the CCITT Group4 standard
// This code is based on a lot of the good ideas from CCITT T.6
// for FAX image compression, but modified to work in a very
// constrained environment. The Huffman tables for horizontal
// mode have been replaced with a simple 2-bit flag followed by
// short or long counts of a fixed length. The short codes are
// always 3 bits (run lengths 0-7) and the long codes are the
// number of bits needed to encode the width of the image.
// For example, if a 320 pixel wide image is being compressed,
// the longest horizontal run needed is 320, which requires 9
// bits to encode. The 2 prefix bits have the following meaning:
// 00 = short, short (3+3 bits)
// 01 = short, long (3+N bits)
// 10 = long, short (N+3 bits)
// 11 = long, long (N+N bits)
// The rest of the code works identically to Group4 2D FAX
//
// Caution - this is the maximum number of color changes per line
// The default value is set low to work embedded systems with little RAM
// for font compression, this is plenty since each line of a character should have
// a maximum of 7 color changes
// You can define this in your compiler macros to override the default vlaue
//
#ifndef MAX_IMAGE_FLIPS
#define MAX_IMAGE_FLIPS 32
#endif
// Horizontal prefix bits
enum {
HORIZ_SHORT_SHORT=0,
HORIZ_SHORT_LONG,
HORIZ_LONG_SHORT,
HORIZ_LONG_LONG
};
// Return code for encoder and decoder
enum {
G5_SUCCESS = 0,
G5_INVALID_PARAMETER,
G5_DECODE_ERROR,
G5_UNSUPPORTED_FEATURE,
G5_ENCODE_COMPLETE,
G5_DECODE_COMPLETE,
G5_NOT_INITIALIZED,
G5_DATA_OVERFLOW,
};
//
// Decoder state
//
typedef struct g5_dec_image_tag
{
int iWidth, iHeight; // image size
int iError;
int y; // last y value drawn
int iVLCSize;
int iHLen; // length of 'long' horizontal codes for this image
int iPitch; // width in bytes of output buffer
uint32_t u32Accum; // fractional scaling accumulator
uint32_t ulBitOff, ulBits; // vlc decode variables
uint8_t *pSrc, *pBuf; // starting & current buffer pointer
int16_t *pCur, *pRef; // current state of current vs reference flips
int16_t CurFlips[MAX_IMAGE_FLIPS];
int16_t RefFlips[MAX_IMAGE_FLIPS];
} G5DECIMAGE;
// Due to unaligned memory causing an exception, we have to do these macros the slow way
#define TIFFMOTOLONG(p) (((uint32_t)(*p)<<24UL) + ((uint32_t)(*(p+1))<<16UL) + ((uint32_t)(*(p+2))<<8UL) + (uint32_t)(*(p+3)))
#define TOP_BIT 0x80000000
#define MAX_VALUE 0xffffffff
// Must be a 32-bit target processor
#define REGISTER_WIDTH 32
#define BIGUINT uint32_t
//
// G5 Encoder
//
typedef struct pil_buffered_bits
{
unsigned char *pBuf; // buffer pointer
uint32_t ulBits; // buffered bits
uint32_t ulBitOff; // current bit offset
uint32_t ulDataSize; // available data
} BUFFERED_BITS;
//
// Encoder state
//
typedef struct g5_enc_image_tag
{
int iWidth, iHeight; // image size
int iError;
int y; // last y encoded
int iOutSize;
int iDataSize; // generated output size
uint8_t *pOutBuf;
int16_t *pCur, *pRef; // pointers to swap current and reference lines
BUFFERED_BITS bb;
int16_t CurFlips[MAX_IMAGE_FLIPS];
int16_t RefFlips[MAX_IMAGE_FLIPS];
} G5ENCIMAGE;
#ifdef __cplusplus
//
// The G5 classes wrap portable C code which does the actual work
//
class G5ENCODER
{
public:
int init(int iWidth, int iHeight, uint8_t *pOut, int iOutSize);
int encodeLine(uint8_t *pPixels);
int size();
private:
G5ENCIMAGE _g5enc;
};
class G5DECODER
{
public:
int init(int iWidth, int iHeight, uint8_t *pData, int iDataSize);
int decodeLine(uint8_t *pOut);
private:
G5DECIMAGE _g5dec;
};
#else
// C function definitions
int g5_decode_init(G5DECIMAGE *pImage, int iWidth, int iHeight, uint8_t *pData, int iDataSize);
int g5_decode_line(G5DECIMAGE *pImage, uint8_t *pOut);
int g5_encode_init(G5ENCIMAGE *pImage, int iWidth, int iHeight, uint8_t *pOut, int iOutSize);
int g5_encode_encodeLine(G5ENCIMAGE *pImage, uint8_t *pPixels);
int g5_encode_getOutSize(G5ENCIMAGE *pImage);
#endif // __cplusplus
#endif // __GROUP5__
+1 -20
View File
@@ -19,7 +19,7 @@
//
#ifndef __BB_EI_IO__
#define __BB_EI_IO__
#ifdef ARDUINO
#include <Arduino.h>
#include <SPI.h>
//
@@ -114,23 +114,6 @@ void bbeiWriteCmd(BBEIDISP *pBBEI, uint8_t cmd)
digitalWrite(pBBEI->iDCPin, HIGH); // leave data mode as the default
} /* bbeiWriteCmd() */
//
// Put the eink into light or deep sleep
//
void bbeiSleep(BBEIDISP *pBBEI, int bDeep)
{
if (pBBEI->chip_type == BBEI_CHIP_UC81xx) {
bbeiCMD2(pBBEI, UC8151_CDI, 0x17); // border floating
bbeiWriteCmd(pBBEI, UC8151_POFF); // power off
bbeiWaitBusy(pBBEI);
if (bDeep) {
bbeiCMD2(pBBEI, UC8151_DSLP, 0xa5); // deep sleep
}
} else {
bbeiCMD2(pBBEI, SSD1608_DEEP_SLEEP, (bDeep) ? 0x02 : 0x01); // deep sleep mode 1 keeps RAM, mode 2 loses RAM
}
pBBEI->is_awake = 0;
} /* bbeiSleep() */
//
// Write 1 or more bytes as DATA (D/C set high)
//
void bbeiWriteData(BBEIDISP *pBBEI, uint8_t *pData, int iLen)
@@ -145,6 +128,4 @@ void bbeiWriteData(BBEIDISP *pBBEI, uint8_t *pData, int iLen)
#endif
} /* bbeiWriteData() */
#else // LINUX
#endif // ARDUINO / LINUX
#endif // __BB_EI_IO__
+18
View File
@@ -1037,6 +1037,24 @@ void bbeiSetPosition(BBEIDISP *pBBEI, int x, int y, int cx, int cy)
// bbeiCMD2(pBBEI, SSD1608_DATA_MODE, 0x3);
}
} /* bbeiSetPosition() */
//
// Put the eink into light or deep sleep
//
void bbeiSleep(BBEIDISP *pBBEI, int bDeep)
{
if (pBBEI->chip_type == BBEI_CHIP_UC81xx) {
bbeiCMD2(pBBEI, UC8151_CDI, 0x17); // border floating
bbeiWriteCmd(pBBEI, UC8151_POFF); // power off
bbeiWaitBusy(pBBEI);
if (bDeep) {
bbeiCMD2(pBBEI, UC8151_DSLP, 0xa5); // deep sleep
}
} else {
bbeiCMD2(pBBEI, SSD1608_DEEP_SLEEP, 0x01); // deep sleep mode 1 keeps RAM,only uses about 1uA
}
pBBEI->is_awake = 0;
} /* bbeiSleep() */
//
// More efficient means of sending commands, data and busy-pauses
//
+75 -81
View File
@@ -304,65 +304,38 @@ void bbeiDrawSprite(BBEIDISP *pBBEI, const uint8_t *pSprite, int cx, int cy, int
int bbeiSetPixel(BBEIDISP *pBBEI, int x, int y, unsigned char ucColor)
{
int i;
unsigned char uc, ucOld;
int iPitch, iSize;
iPitch = pBBEI->width;
iSize = iPitch * ((pBBEI->height+7)/8);
// only available for local buffer operations
if (!pBBEI || !pBBEI->ucScreen) return BBEI_ERROR_BAD_PARAMETER;
iPitch = (pBBEI->native_width+7)>>3;
iSize = iPitch * pBBEI->native_height;
i = ((y >> 3) * iPitch) + x;
i = (x >> 3) + (y * iPitch);
if (i < 0 || i > iSize-1) { // off the screen
return BBEI_ERROR_BAD_PARAMETER;
}
// Special case for 4-color e-ink
if (pBBEI->iFlags & BBEI_4COLOR) {
uint8_t ucMask = (1 << (y & 7));
ucColor ^= 1; // invert low bit
if (ucColor & 1)
pBBEI->ucScreen[i] |= ucMask;
else
pBBEI->ucScreen[i] &= ~ucMask;
if (ucColor & 2)
pBBEI->ucScreen[iSize + i] |= ucMask;
else
pBBEI->ucScreen[iSize + i] &= ~ucMask;
return BBEI_SUCCESS;
}
// special case for 3-color e-ink
if (pBBEI->iFlags & BBEI_3COLOR) {
if (ucColor >= BBEI_YELLOW) { // yellow/red has priority
pBBEI->ucScreen[iSize + i] |= (1 << (y & 7));
pBBEI->ucScreen[iSize + i] |= (0x80 >> (x & 7));
} else {
pBBEI->ucScreen[iSize + i] &= ~(1 << (y & 7)); // clear red plane bit
pBBEI->ucScreen[iSize + i] &= ~(0x80 >> (x & 7)); // clear red plane bit
if (ucColor == BBEI_WHITE) {
pBBEI->ucScreen[i] &= ~(1 << (y & 7));
pBBEI->ucScreen[i] &= ~(0x80 >> (x & 7));
} else { // must be black
pBBEI->ucScreen[i] |= (1 << (y & 7));
pBBEI->ucScreen[i] |= (0x80 >> (x & 7));
}
}
return BBEI_SUCCESS;
} else { // 2-color
if (ucColor == BBEI_WHITE) {
pBBEI->ucScreen[i] &= ~(0x80 >> (x & 7));
} else { // must be black
pBBEI->ucScreen[i] |= (0x80 >> (x & 7));
}
}
bbeiSetPosition(pBBEI, x, y, 8, 1);
if (pBBEI->ucScreen)
uc = ucOld = pBBEI->ucScreen[i];
else
uc = ucOld = 0;
uc &= ~(0x1 << (y & 7));
if (ucColor)
{
uc |= (0x1 << (y & 7));
}
if (uc != ucOld) // pixel changed
{
// obdSetPosition(x, y>>3);
if (pBBEI->ucScreen)
{
bbeiWriteData(pBBEI, &uc, 1);
pBBEI->ucScreen[i] = uc;
}
}
return BBEI_SUCCESS;
} /* obdSetPixel() */
@@ -614,7 +587,7 @@ void bbeiSetTextWrap(BBEIDISP *pBBEI, int bWrap)
//
void bbeiWriteStringCustom(BBEIDISP *pBBEI, BB_FONT *pFont, int x, int y, char *szMsg, int iColor, uint8_t iPlane)
{
int rc, i, h, w, end_y, dx, dy, ty, iSrcPitch, iPitch;
int rc, i, h, w, end_y, dx, dy, tx, ty, iSrcPitch, iPitch;
unsigned int c;
uint8_t *s, bInvert = 0;
BB_GLYPH *pGlyph;
@@ -671,44 +644,65 @@ uint8_t *pBits, u8CMD1, u8CMD2, u8CMD, u8EndMask;
if (ty < 0 || ty > 4096) ty = 4096; // DEBUG
rc = g5_decode_init(&g5dec, w, h, s, ty);
if (rc != G5_SUCCESS) return; // corrupt data?
// set the memory window for this character
iSrcPitch = (w+7)/8;
w += (dx & 7); // add parital byte
bbeiSetPosition(pBBEI, dx, dy, w, h);
iPitch = (w+7)/8;
// start writing into the correct plane
if (pBBEI->chip_type == BBEI_CHIP_UC81xx) {
u8CMD1 = UC8151_DTM2;
u8CMD2 = UC8151_DTM1;
} else {
u8CMD1 = SSD1608_WRITE_RAM;
u8CMD2 = SSD1608_WRITE_ALTRAM;
if (pBBEI->ucScreen) { // backbuffer, draw pixels
for (ty=dy; ty<end_y && ty < pBBEI->native_height; ty++) {
uint8_t u8, u8Count;
g5_decode_line(&g5dec, u8Cache);
s = u8Cache;
u8 = *s++;
u8Count = 8;
for (tx=x; tx<x+w; tx++) {
if (u8 & 0x80) {
bbeiSetPixel(pBBEI, tx, ty, iColor);
}
u8 <<= 1;
u8Count--;
if (u8Count == 0) {
u8Count = 8;
u8 = *s++;
}
}
}
} else { // draw directly into EPD memory
// set the memory window for this character
iSrcPitch = (w+7)/8;
w += (dx & 7); // add parital byte
bbeiSetPosition(pBBEI, dx, dy, w, h);
iPitch = (w+7)/8;
// start writing into the correct plane
if (pBBEI->chip_type == BBEI_CHIP_UC81xx) {
u8CMD1 = UC8151_DTM2;
u8CMD2 = UC8151_DTM1;
} else {
u8CMD1 = SSD1608_WRITE_RAM;
u8CMD2 = SSD1608_WRITE_ALTRAM;
}
u8CMD = u8CMD1;
if (iColor == BBEI_BLACK) bInvert = 1;
else if (iColor == BBEI_RED && (pBBEI->iFlags & BBEI_3COLOR)) {
u8CMD = u8CMD2; // second plane is red (inverted)
}
bbeiWriteCmd(pBBEI, u8CMD); // memory write command
for (ty=dy; ty<end_y && ty < pBBEI->native_height; ty++) {
g5_decode_line(&g5dec, u8Cache);
u8Cache[iSrcPitch-1] &= u8EndMask; // clean pixels beyond character width
u8Cache[iSrcPitch] = 0;
if (dx & 7) { // need to shift it over by 1-7 bits
uint8_t *s = u8Cache, uc1, uc0 = 0; // last shifted byte
uint8_t n = dx & 7; // shift amount
for (int j=0; j<w+7; j+= 8) {
uc1 = *s;
uc0 |= (uc1 >> n);
*s++ = uc0;
uc0 = uc1 << (8-n);
}
*s++ = uc0; // store final byte
*s++ = 0; // and a zero for good measure
}
if (bInvert) InvertBytes(u8Cache, iPitch);
bbeiWriteData(pBBEI, u8Cache, iPitch); // write each row into the EPD framebuffer
} // for y
}
u8CMD = u8CMD1;
if (iColor == BBEI_BLACK) bInvert = 1;
else if (iColor == BBEI_RED && (pBBEI->iFlags & BBEI_3COLOR)) {
u8CMD = u8CMD2; // second plane is red (inverted)
}
bbeiWriteCmd(pBBEI, u8CMD); // memory write command
for (ty=dy; ty<end_y && ty < pBBEI->native_height; ty++) {
g5_decode_line(&g5dec, u8Cache);
u8Cache[iSrcPitch-1] &= u8EndMask; // clean pixels beyond character width
u8Cache[iSrcPitch] = 0;
if (dx & 7) { // need to shift it over by 1-7 bits
uint8_t *s = u8Cache, uc1, uc0 = 0; // last shifted byte
uint8_t n = dx & 7; // shift amount
for (int j=0; j<w+7; j+= 8) {
uc1 = *s;
uc0 |= (uc1 >> n);
*s++ = uc0;
uc0 = uc1 << (8-n);
}
*s++ = uc0; // store final byte
*s++ = 0; // and a zero for good measure
}
if (bInvert) InvertBytes(u8Cache, iPitch);
bbeiWriteData(pBBEI, u8Cache, iPitch); // write each row into the EPD framebuffer
} // for y
} // if not drawing a space
if (pFont->rotation == 0 || pFont->rotation == 180) {
x += pGlyph->xAdvance; // width of this character
+1 -1
View File
@@ -34,7 +34,7 @@ void delay(int);
#endif // _LINUX_
#include "bb_eink.h"
#include "bb_ei_io.inl" // I/O (non-portable) code is in here
#include "arduino_io.inl" // I/O (non-portable) code is in here
#include "bb_ei.inl" // All of the display interface code is in here
#include "bb_ei_gfx.inl" // drawing code
#include "Group5.h" // Group5 data compression library
+46
View File
@@ -0,0 +1,46 @@
//
// C structures for defining a BB_FONT file
// Written by Larry Bank
// Copyright (c) 2024 BitBank Software, Inc.
//
// Use of this software is governed by the Business Source License
// included in the file ./LICENSE.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// ./APL.txt.
//
// The BB_FONT is at the start of the file
// followed by BB_GLYPH structures for each character
// and finally the packed bitmap data (one after another)
// is at the end of the file
//
#ifndef _BB_FONT_H_
#define _BB_FONT_H_
// 16-bit marker at the start of a BB_FONT file
// (BitBank FontFile)
#define BB_MARKER 0xBBFF
// Font info per character (glyph)
typedef struct {
uint16_t bitmapOffset; // Offset to compressed bitmap data for this glyph (starting from the end of the BB_GLYPH[] array)
uint8_t width; // bitmap width in pixels
uint8_t xAdvance; // total width in pixels (bitmap + padding)
uint16_t height; // bitmap height in pixels
int16_t xOffset; // left padding to upper left corner
int16_t yOffset; // padding from baseline to upper left corner (usually negative)
} BB_GLYPH;
// This structure is stored at the beginning of a BB_FONT file
typedef struct {
uint16_t u16Marker; // 16-bit Marker defining a BB_FONT file
uint16_t first; // first char (ASCII value)
uint16_t last; // last char (ASCII value)
uint16_t height; // total height of font
uint32_t rotation; // store this as 32-bits to not have a struct packing problem
BB_GLYPH glyphs[]; // Array of glyphs (one for each char)
} BB_FONT;
#endif // _BB_FONT_H_

Some files were not shown because too many files have changed in this diff Show More