diff --git a/APL.txt b/APL.txt new file mode 100644 index 0000000..829ea33 --- /dev/null +++ b/APL.txt @@ -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. + diff --git a/LICENSE b/LICENSE index 0079ce3..06f7050 100644 --- a/LICENSE +++ b/LICENSE @@ -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 License’s 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 License’s 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 License’s 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 License’s 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. diff --git a/esp_idf/esp_idf_io.inl b/esp_idf/esp_idf_io.inl new file mode 100644 index 0000000..cce1459 --- /dev/null +++ b/esp_idf/esp_idf_io.inl @@ -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__ diff --git a/examples/font_drawing/Roboto_Black_40.h b/examples/font_drawing/Roboto_Black_40.h new file mode 100644 index 0000000..b082158 --- /dev/null +++ b/examples/font_drawing/Roboto_Black_40.h @@ -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}; diff --git a/examples/font_drawing/font_drawing.ino b/examples/font_drawing/font_drawing.ino new file mode 100644 index 0000000..61f8f9f --- /dev/null +++ b/examples/font_drawing/font_drawing.ino @@ -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 +#include "bb_font.h" +#include +#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>= 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() */ + diff --git a/examples/g5_oled/bart.h b/examples/g5_oled/bart.h new file mode 100644 index 0000000..18cf0dd --- /dev/null +++ b/examples/g5_oled/bart.h @@ -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}; diff --git a/examples/g5_oled/g5_oled.ino b/examples/g5_oled/g5_oled.ino new file mode 100644 index 0000000..38330ca --- /dev/null +++ b/examples/g5_oled/g5_oled.ino @@ -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 +#include +#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>= 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 +// This code requires the freetype library +// found here: www.freetype.org +// +#include +#include +#include +#include +#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> (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> 3] |= (0x80 >> (x & 7)); + } + s -= iSrcPitch; + } // for x + } // for y + break; + case 180: + iDstPitch = iSrcPitch; + for (y=0; y>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> (ty & 7); + s = &pSrc[ty>>3]; // work from bottom up + d = &pDst[y * iDstPitch]; + memset(d, 0, iDstPitch); + for (x=0; 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 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() */ diff --git a/library.properties b/library.properties index 5ee3bae..801ecdf 100644 --- a/library.properties +++ b/library.properties @@ -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 diff --git a/rpi/examples/cli_tool/Makefile b/rpi/examples/cli_tool/Makefile new file mode 100644 index 0000000..5332700 --- /dev/null +++ b/rpi/examples/cli_tool/Makefile @@ -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 diff --git a/rpi/examples/cli_tool/main.c b/rpi/examples/cli_tool/main.c new file mode 100644 index 0000000..f872989 --- /dev/null +++ b/rpi/examples/cli_tool/main.c @@ -0,0 +1,138 @@ +// +// main.c +// Group5 +// +// Created by Larry Bank on 8/19/24. +// + +#include +#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<> 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 \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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +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__ diff --git a/src/Group5.cpp b/src/Group5.cpp new file mode 100644 index 0000000..a37c179 --- /dev/null +++ b/src/Group5.cpp @@ -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() */ + diff --git a/src/Group5.h b/src/Group5.h new file mode 100644 index 0000000..0f73145 --- /dev/null +++ b/src/Group5.h @@ -0,0 +1,153 @@ +#ifndef __GROUP5__ +#define __GROUP5__ +#include +#include +#include +#include +// +// 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__ diff --git a/src/bb_ei_io.inl b/src/arduino_io.inl similarity index 87% rename from src/bb_ei_io.inl rename to src/arduino_io.inl index b6b43e4..8f1d9aa 100644 --- a/src/bb_ei_io.inl +++ b/src/arduino_io.inl @@ -19,7 +19,7 @@ // #ifndef __BB_EI_IO__ #define __BB_EI_IO__ -#ifdef ARDUINO + #include #include // @@ -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__ diff --git a/src/bb_ei.inl b/src/bb_ei.inl index 8fcab97..0281e0a 100644 --- a/src/bb_ei.inl +++ b/src/bb_ei.inl @@ -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 // diff --git a/src/bb_ei_gfx.inl b/src/bb_ei_gfx.inl index 9017a39..366b599 100644 --- a/src/bb_ei_gfx.inl +++ b/src/bb_ei_gfx.inl @@ -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; tynative_height; ty++) { + uint8_t u8, u8Count; + g5_decode_line(&g5dec, u8Cache); + s = u8Cache; + u8 = *s++; + u8Count = 8; + for (tx=x; txchip_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; tynative_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> 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; tynative_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> 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 diff --git a/src/bb_eink.cpp b/src/bb_eink.cpp index 7b3fce1..1bffa25 100644 --- a/src/bb_eink.cpp +++ b/src/bb_eink.cpp @@ -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 diff --git a/src/bb_font.h b/src/bb_font.h new file mode 100644 index 0000000..6a91d4f --- /dev/null +++ b/src/bb_font.h @@ -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_ diff --git a/src/g5dec.inl b/src/g5dec.inl new file mode 100644 index 0000000..5c1374a --- /dev/null +++ b/src/g5dec.inl @@ -0,0 +1,345 @@ +// +// Group5 +// A 1-bpp image decoder +// +// 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. +#include "Group5.h" + +/* + The code tree that follows has: bit_length, decode routine + These codes are for Group 4 (MMR) decoding + + 01 = vertneg1, 11h = vert1, 20h = horiz, 30h = pass, 12h = vert2 + 02 = vertneg2, 13h = vert3, 03 = vertneg3, 90h = trash +*/ + +static const uint8_t code_table[128] = + {0x90, 0, 0x40, 0, /* trash, uncompr mode - codes 0 and 1 */ + 3, 7, /* V(-3) pos = 2 */ + 0x13, 7, /* V(3) pos = 3 */ + 2, 6, 2, 6, /* V(-2) pos = 4,5 */ + 0x12, 6, 0x12, 6, /* V(2) pos = 6,7 */ + 0x30, 4, 0x30, 4, 0x30, 4, 0x30, 4, /* pass pos = 8->F */ + 0x30, 4, 0x30, 4, 0x30, 4, 0x30, 4, + 0x20, 3, 0x20, 3, 0x20, 3, 0x20, 3, /* horiz pos = 10->1F */ + 0x20, 3, 0x20, 3, 0x20, 3, 0x20, 3, + 0x20, 3, 0x20, 3, 0x20, 3, 0x20, 3, + 0x20, 3, 0x20, 3, 0x20, 3, 0x20, 3, +/* V(-1) pos = 20->2F */ + 1, 3, 1, 3, 1, 3, 1, 3, + 1, 3, 1, 3, 1, 3, 1, 3, + 1, 3, 1, 3, 1, 3, 1, 3, + 1, 3, 1, 3, 1, 3, 1, 3, + 0x11, 3, 0x11, 3, 0x11, 3, 0x11, 3, /* V(1) pos = 30->3F */ + 0x11, 3, 0x11, 3, 0x11, 3, 0x11, 3, + 0x11, 3, 0x11, 3, 0x11, 3, 0x11, 3, + 0x11, 3, 0x11, 3, 0x11, 3, 0x11, 3}; + +int g5_decode_init(G5DECIMAGE *pImage, int iWidth, int iHeight, uint8_t *pData, int iDataSize) +{ + if (pImage == NULL || iWidth < 1 || iHeight < 1 || pData == NULL || iDataSize < 1) + return G5_INVALID_PARAMETER; + + pImage->iVLCSize = iDataSize; + pImage->pSrc = pData; + pImage->ulBitOff = 0; + pImage->y = 0; + pImage->ulBits = TIFFMOTOLONG(pData); // preload the first 32 bits of data + pImage->iWidth = iWidth; + pImage->iHeight = iHeight; + return G5_SUCCESS; + +} /* g5_decode_init() */ + +static void G5DrawLine(G5DECIMAGE *pPage, int16_t *pCurFlips, uint8_t *pOut) +{ + int x, len, run; + uint8_t lBit, rBit, *p; + int iStart = 0, xright = pPage->iWidth; + uint8_t *pDest; + + iStart = 0; + + pDest = pOut; + len = (xright+7)>>3; // number of bytes to generate + for (x=0; x= xright || run == 0) + break; + if ((x + run) > 0) { /* If the run is visible, draw it */ + if (x < 0) { + run += x; /* draw only visible part of run */ + x = 0; + } + if ((x + run) > xright) { /* Don't let it go off right edge */ + run = xright - x; + } + /* Draw this run */ + lBit = 0xff << (8 - (x & 7)); + rBit = 0xff >> ((x + run) & 7); + len = ((x+run)>>3) - (x >> 3); + p = &pDest[x >> 3]; + if (len == 0) { + lBit |= rBit; + *p &= lBit; + } else { + *p++ &= lBit; + while (len > 1) { + *p++ = 0; + len--; + } + *p = rBit; + } + } // visible run + } /* while drawing line */ +} /* G5DrawLine() */ +// +// Initialize internal structures to decode the image +// +static void Decode_Begin(G5DECIMAGE *pPage) +{ + int i, xsize; + int16_t *CurFlips, *RefFlips; + uint8_t *pBuf, *pBufEnd; + + xsize = pPage->iWidth; + + RefFlips = pPage->RefFlips; + CurFlips = pPage->CurFlips; + + /* Seed the current and reference line with XSIZE for V(0) codes */ + for (i=0; i XSIZE + 3-16-94 */ + CurFlips[i] = RefFlips[i] = 0x7fff; + CurFlips[i+1] = RefFlips[i+1] = 0x7fff; + + pPage->pCur = CurFlips; + pPage->pRef = RefFlips; + + pBuf = pPage->pSrc; +// +// Some files may have leading 0's that would confuse the decoder +// Valid G5 data can't begin with a 0 +// + pBufEnd = &pBuf[128]; // DEBUG + while (pBuf < pBufEnd && pBuf[0] == 0) + { pBuf++; } + + pPage->pBuf = pBuf; + pPage->ulBits = TIFFMOTOLONG(pBuf); // load 32 bits to start + pPage->ulBitOff = 0; + // Calculate the number of bits needed for a long horizontal code + pPage->iHLen = 32 - __builtin_clz(pPage->iWidth); +} /* Decode_Begin() */ +// +// Decode a single line of G5 data (private function) +// +static int DecodeLine(G5DECIMAGE *pPage) +{ + signed int a0, a0_p, b1; + int16_t *pCur, *pRef, *RefFlips, *CurFlips; + int xsize, tot_run=0, tot_run1 = 0; + int32_t sCode; + uint32_t lBits; + uint32_t ulBits, ulBitOff; + uint8_t *pBuf/*, *pBufEnd*/; + uint32_t u32HMask, u32HLen; // horizontal code mask and length + + pCur = CurFlips = pPage->pCur; + pRef = RefFlips = pPage->pRef; + ulBits = pPage->ulBits; + ulBitOff = pPage->ulBitOff; + pBuf = pPage->pBuf; + // pBufEnd = &pPage->pSrc[pPage->iVLCSize]; + u32HLen = pPage->iHLen; + u32HMask = (1 << u32HLen) - 1; + a0 = -1; + xsize = pPage->iWidth; + + while (a0 < xsize) { /* Decode this line */ + if (ulBitOff > (REGISTER_WIDTH-8)) { // need at least 7 unused bits + pBuf += (ulBitOff >> 3); + ulBitOff &= 7; + ulBits = TIFFMOTOLONG(pBuf); + } + if ((int32_t)(ulBits << ulBitOff) < 0) { /* V(0) code is the most frequent case (1 bit) */ + a0 = *pRef++; + ulBitOff++; // length = 1 bit + *pCur++ = a0; + } else { /* Slower method for the less frequence codes */ + lBits = (ulBits >> ((REGISTER_WIDTH - 8) - ulBitOff)) & 0xfe; /* Only the first 7 bits are useful */ + sCode = code_table[lBits]; /* Get the code type as an 8-bit value */ + ulBitOff += code_table[lBits+1]; /* Get the code length */ + switch (sCode) { + case 1: /* V(-1) */ + case 2: /* V(-2) */ + case 3: /* V(-3) */ + a0 = *pRef - sCode; /* A0 = B1 - x */ + *pCur++ = a0; + if (pRef == RefFlips) { + pRef += 2; + } + pRef--; + while (a0 >= *pRef) { + pRef += 2; + } + break; + + case 0x11: /* V(1) */ + case 0x12: /* V(2) */ + case 0x13: /* V(3) */ + a0 = *pRef++; /* A0 = B1 */ + b1 = a0; + a0 += sCode & 7; /* A0 = B1 + x */ + if (b1 != xsize && a0 < xsize) { + while (a0 >= *pRef) { + pRef += 2; + } + } + if (a0 > xsize) { + a0 = xsize; + } + *pCur++ = a0; + break; + + case 0x20: /* Horizontal codes */ + if (ulBitOff > (REGISTER_WIDTH-16)) { // need at least 16 unused bits + pBuf += (ulBitOff >> 3); + ulBitOff &= 7; + ulBits = TIFFMOTOLONG(pBuf); + } + a0_p = a0; + if (a0 < 0) { + a0_p = 0; + } + lBits = (ulBits >> ((REGISTER_WIDTH - 2) - ulBitOff)) & 0x3; // get 2-bit prefix for code type + // There are 4 possible horizontal cases: short/short, short/long, long/short, long/long + // These are encoded in a 2-bit prefix code, followed by 3 bits for short or N bits for long code + // N is the log base 2 of the image width (e.g. 320 pixels requires 9 bits) + ulBitOff += 2; + switch (lBits) { + case HORIZ_SHORT_SHORT: + tot_run = (ulBits >> ((REGISTER_WIDTH - 3) - ulBitOff)) & 0x7; // get 3-bit short length + ulBitOff += 3; + tot_run1 = (ulBits >> ((REGISTER_WIDTH - 3) - ulBitOff)) & 0x7; // get 3-bit short length + ulBitOff += 3; + break; + case HORIZ_SHORT_LONG: + tot_run = (ulBits >> ((REGISTER_WIDTH - 3) - ulBitOff)) & 0x7; // get 3-bit short length + ulBitOff += 3; + tot_run1 = (ulBits >> ((REGISTER_WIDTH - u32HLen) - ulBitOff)) & u32HMask; // get long length + ulBitOff += u32HLen; + break; + case HORIZ_LONG_SHORT: + tot_run = (ulBits >> ((REGISTER_WIDTH - u32HLen) - ulBitOff)) & u32HMask; // get long length + ulBitOff += u32HLen; + tot_run1 = (ulBits >> ((REGISTER_WIDTH - 3) - ulBitOff)) & 0x7; // get 3-bit short length + ulBitOff += 3; + break; + case HORIZ_LONG_LONG: + tot_run = (ulBits >> ((REGISTER_WIDTH - u32HLen) - ulBitOff)) & u32HMask; // get long length + ulBitOff += u32HLen; + if (ulBitOff > (REGISTER_WIDTH-16)) { // need at least 16 unused bits + pBuf += (ulBitOff >> 3); + ulBitOff &= 7; + ulBits = TIFFMOTOLONG(pBuf); + } + tot_run1 = (ulBits >> ((REGISTER_WIDTH - u32HLen) - ulBitOff)) & u32HMask; // get long length + ulBitOff += u32HLen; + break; + } // switch on lBits + a0 = a0_p + tot_run; + *pCur++ = a0; + a0 += tot_run1; + if (a0 < xsize) { + while (a0 >= *pRef) { + pRef += 2; + } + } + *pCur++ = a0; + break; + + case 0x30: /* Pass code */ + pRef++; /* A0 = B2, iRef+=2 */ + a0 = *pRef++; + break; + + default: /* ERROR */ + pPage->iError = G5_DECODE_ERROR; + goto pilreadg5z; + } /* switch */ + } /* Slow climb */ + } + /*--- Convert flips data into run lengths ---*/ + *pCur++ = xsize; /* Terminate the line properly */ + *pCur++ = xsize; +pilreadg5z: + // Save the current VLC decoder state + pPage->ulBits = ulBits; + pPage->ulBitOff = ulBitOff; + pPage->pBuf = pBuf; + return pPage->iError; +} /* DecodeLine() */ +// +// Decompress the VLC data +// +int g5_decode_line(G5DECIMAGE *pPage, uint8_t *pOut) +{ + int rc; + uint8_t *pBufEnd; + int16_t *t1; + + if (pPage == NULL || pOut == NULL) + return G5_INVALID_PARAMETER; + if (pPage->y >= pPage->iHeight) + return G5_DECODE_COMPLETE; + + pPage->iError = G5_SUCCESS; + + if (pPage->y == 0) { // first time through + Decode_Begin(pPage); + } + pBufEnd = &pPage->pSrc[pPage->iVLCSize]; + + if (pPage->pBuf >= pBufEnd) { // read past the end, error + pPage->iError = G5_DECODE_ERROR; + return G5_DECODE_ERROR; + } + rc = DecodeLine(pPage); + if (rc == G5_SUCCESS) { + // Draw the current line + G5DrawLine(pPage, pPage->pCur, pOut); + /*--- Swap current and reference lines ---*/ + t1 = pPage->pRef; + pPage->pRef = pPage->pCur; + pPage->pCur = t1; + pPage->y++; + if (pPage->y >= pPage->iHeight) { + pPage->iError = G5_DECODE_COMPLETE; + } + } else { + pPage->iError = rc; + } + return pPage->iError; +} /* Decode() */ + diff --git a/src/g5enc.inl b/src/g5enc.inl new file mode 100644 index 0000000..9cf5427 --- /dev/null +++ b/src/g5enc.inl @@ -0,0 +1,303 @@ +// +// G5 Encoder +// A 1-bpp image encoding 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. +#include "Group5.h" + +/* Number of consecutive 1 bits in a byte from MSB to LSB */ +static uint8_t bitcount[256] = + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0-15 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 16-31 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 32-47 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 48-63 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 64-79 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 80-95 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 96-111 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 112-127 */ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 128-143 */ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 144-159 */ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 160-175 */ + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 176-191 */ + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* 192-207 */ + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, /* 208-223 */ + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, /* 224-239 */ + 4,4,4,4,4,4,4,4,5,5,5,5,6,6,7,8}; /* 240-255 */ + +/* Table of vertical codes for G5 encoding */ +/* code followed by length, starting with v(-3) */ +static const uint8_t vtable[14] = + {3,7, /* V(-3) = 0000011 */ + 3,6, /* V(-2) = 000011 */ + 3,3, /* V(-1) = 011 */ + 1,1, /* V(0) = 1 */ + 2,3, /* V(1) = 010 */ + 2,6, /* V(2) = 000010 */ + 2,7}; /* V(3) = 0000010 */ + + +static void G5ENCInsertCode(BUFFERED_BITS *bb, BIGUINT ulCode, int iLen) +{ + if ((bb->ulBitOff + iLen) > REGISTER_WIDTH) { // need to write data + bb->ulBits |= (ulCode >> (bb->ulBitOff + iLen - REGISTER_WIDTH)); // partial bits on first word + *(BIGUINT *)bb->pBuf = __builtin_bswap32(bb->ulBits); + bb->pBuf += sizeof(BIGUINT); + bb->ulBits = ulCode << ((REGISTER_WIDTH*2) - (bb->ulBitOff + iLen)); + bb->ulBitOff += iLen - REGISTER_WIDTH; + } else { + bb->ulBits |= (ulCode << (REGISTER_WIDTH - bb->ulBitOff - iLen)); + bb->ulBitOff += iLen; + } +} /* G5ENCInsertCode() */ +// +// Flush any buffered bits to the output +// +static void G5ENCFlushBits(BUFFERED_BITS *bb) +{ + while (bb->ulBitOff >= 8) + { + *bb->pBuf++ = (unsigned char) (bb->ulBits >> (REGISTER_WIDTH - 8)); + bb->ulBits <<= 8; + bb->ulBitOff -= 8; + } + *bb->pBuf++ = (unsigned char) (bb->ulBits >> (REGISTER_WIDTH - 8)); + bb->ulBitOff = 0; + bb->ulBits = 0; +} /* G5ENCFlushBits() */ +// +// Initialize the compressor +// This must be called before adding data to the output +// +int g5_encode_init(G5ENCIMAGE *pImage, int iWidth, int iHeight, uint8_t *pOut, int iOutSize) +{ + int iError = G5_SUCCESS; + + if (pImage == NULL || iHeight <= 0) + return G5_INVALID_PARAMETER; + pImage->iWidth = iWidth; // image size + pImage->iHeight = iHeight; + pImage->pCur = pImage->CurFlips; + pImage->pRef = pImage->RefFlips; + pImage->pOutBuf = pOut; // optional output buffer + pImage->iOutSize = iOutSize; // output buffer pre-allocated size + pImage->iDataSize = 0; // no data yet + pImage->y = 0; + for (int i=0; iRefFlips[i] = iWidth; + pImage->CurFlips[i] = iWidth; + } + pImage->bb.pBuf = pImage->pOutBuf; + pImage->bb.ulBits = 0; + pImage->bb.ulBitOff = 0; + pImage->iError = iError; + return iError; +} /* g5_encode_init() */ +// +// Internal function to convert uncompressed 1-bit per pixel data +// into the run-end data needed to feed the G5 encoder +// +static void G5ENCEncodeLine(unsigned char *buf, int xsize, int16_t *pDest) +{ +int iCount, xborder; +uint8_t i, c; +int8_t cBits; +int iLen; +int16_t x; + + xborder = xsize; + iCount = (xsize + 7) >> 3; /* Number of bytes per line */ + cBits = 8; + iLen = 0; /* Current run length */ + x = 0; + + c = *buf++; /* Get the first byte to start */ + iCount--; + while (iCount >=0) + { + i = bitcount[c]; /* Get the number of consecutive bits */ + iLen += i; /* Add this length to total run length */ + c <<= i; + cBits -= i; /* Minus the number in a byte */ + if (cBits <= 0) + { + iLen += cBits; /* Adjust length */ + cBits = 8; + c = *buf++; /* Get another data byte */ + iCount--; + continue; /* Keep doing white until color change */ + } + c = ~c; /* flip color to count black pixels */ + /* Store the white run length */ + xborder -= iLen; + if (xborder < 0) + { + iLen += xborder; /* Make sure run length is not past end */ + break; + } + x += iLen; + *pDest++ = x; + iLen = 0; +doblack: + i = bitcount[c]; /* Get consecutive bits */ + iLen += i; /* Add to total run length */ + c <<= i; + cBits -= i; + if (cBits <= 0) + { + iLen += cBits; /* Adjust length */ + cBits = 8; + c = *buf++; /* Get another data byte */ + c = ~c; /* Flip color to find black */ + iCount--; + if (iCount < 0) + break; + goto doblack; + } + /* Store the black run length */ + c = ~c; /* Flip color again to find white pixels */ + xborder -= iLen; + if (xborder < 0) + { + iLen += xborder; /* Make sure run length is not past end */ + break; + } + x += iLen; + *pDest++ = x; + iLen = 0; + } /* while */ + + x += iLen; + *pDest++ = x; + *pDest++ = x; // Store a few more XSIZE to end the line + *pDest++ = x; // so that the compressor doesn't go past + *pDest++ = x; // the end of the line +} /* G5ENCEncodeLine() */ +// +// Compress a line of pixels and add it to the output +// the input format is expected to be MSB (most significant bit) first +// for example, pixel 0 is in byte 0 at bit 7 (0x80) +// Returns G5ENC_SUCCESS for each line if all is well and G5ENC_IMAGE_COMPLETE +// for the last line +// +int g5_encode_encodeLine(G5ENCIMAGE *pImage, uint8_t *pPixels) +{ +int16_t a0, a0_c, b2, a1; +int dx, run1, run2; +int xsize, iErr, iHighWater; +int iCur, iRef, iLen; +int iHLen; // number of bits for long horizontal codes +int16_t *CurFlips, *RefFlips; +BUFFERED_BITS bb; + + if (pImage == NULL || pPixels == NULL) + return G5_INVALID_PARAMETER; + iHighWater = pImage->iOutSize - 32; + iHLen = 32 - __builtin_clz(pImage->iWidth); + memcpy(&bb, &pImage->bb, sizeof(BUFFERED_BITS)); // keep local copy + CurFlips = pImage->pCur; + RefFlips = pImage->pRef; + iErr = 0; + xsize = pImage->iWidth; /* For performance reasons */ + + // Convert the incoming line of pixels into run-end data + G5ENCEncodeLine(pPixels, pImage->iWidth, CurFlips); + + /* Encode this line as G5 */ + a0 = a0_c = 0; + iCur = iRef = 0; + while (a0 < xsize) { + b2 = RefFlips[iRef+1]; + a1 = CurFlips[iCur]; + if (b2 < a1) { /* Is b2 to the left of a1? */ + /* yes, do pass mode */ + a0 = b2; + iRef += 2; + G5ENCInsertCode(&bb, 1, 4); /* Pass code = 0001 */ + } else { /* Try vertical and horizontal mode */ + dx = RefFlips[iRef] - a1; /* b1 - a1 */ + if (dx > 3 || dx < -3) { /* Horizontal mode */ + G5ENCInsertCode(&bb, 1, 3); /* Horizontal code = 001 */ + run1 = CurFlips[iCur] - a0; + run2 = CurFlips[iCur+1] - CurFlips[iCur]; + if (run1 < 8) { + if (run2 < 8) { // short, short + G5ENCInsertCode(&bb, HORIZ_SHORT_SHORT, 2); /* short, short = 00 */ + G5ENCInsertCode(&bb, run1, 3); + G5ENCInsertCode(&bb, run2, 3); + } else { // short, long + G5ENCInsertCode(&bb, HORIZ_SHORT_LONG, 2); /* short, long = 01 */ + G5ENCInsertCode(&bb, run1, 3); + G5ENCInsertCode(&bb, run2, iHLen); + } + } else { // first run is long + if (run2 < 8) { // long, short + G5ENCInsertCode(&bb, HORIZ_LONG_SHORT, 2); /* long, short = 10 */ + G5ENCInsertCode(&bb, run1, iHLen); + G5ENCInsertCode(&bb, run2, 3); + } else { // long, long + G5ENCInsertCode(&bb, HORIZ_LONG_LONG, 2); /* long, long = 11 */ + G5ENCInsertCode(&bb, run1, iHLen); + G5ENCInsertCode(&bb, run2, iHLen); + } + } + a0 = CurFlips[iCur+1]; /* a0 = a2 */ + if (a0 != xsize) { + iCur += 2; /* Skip two color flips */ + while (RefFlips[iRef] != xsize && RefFlips[iRef] <= a0) { + iRef += 2; + } + } + } else { /* Vertical mode */ + dx = (dx + 3) * 2; /* Convert to index table */ + G5ENCInsertCode(&bb, vtable[dx], vtable[dx+1]); + a0 = a1; + a0_c = 1-a0_c; + if (a0 != xsize) { + if (iRef != 0) { + iRef -= 2; + } + iRef++; /* Skip a color change in cur and ref */ + iCur++; + while (RefFlips[iRef] <= a0 && RefFlips[iRef] != xsize) { + iRef += 2; + } + } + } /* vertical mode */ + } /* horiz/vert mode */ + } /* while x < xsize */ + iLen = (int)(bb.pBuf-pImage->pOutBuf); + if (iLen >= iHighWater) { // not enough space + pImage->iError = iErr = G5_DATA_OVERFLOW; // we don't have a better error + return iErr; + } + if (pImage->y == pImage->iHeight-1) { // last line of image + G5ENCFlushBits(&bb); // output the final buffered bits + // wrap up final output + pImage->iDataSize = (int)(bb.pBuf-pImage->pOutBuf); + iErr = G5_ENCODE_COMPLETE; + } + pImage->pCur = RefFlips; // swap current and reference lines + pImage->pRef = CurFlips; + pImage->y++; + memcpy(&pImage->bb, &bb, sizeof(bb)); + return iErr; +} /* g5_encode_encodeLine() */ +// +// Returns the number of bytes of G5 created by the encoder +// +int g5_encode_getOutSize(G5ENCIMAGE *pImage) +{ + int iSize = 0; + if (pImage != NULL) + iSize = pImage->iDataSize; + return iSize; +} /* g5_encode_getOutSize() */