diff --git a/docs/libs/bmp.md b/docs/libs/bmp.md new file mode 100644 index 0000000000..e3f5e01521 --- /dev/null +++ b/docs/libs/bmp.md @@ -0,0 +1,42 @@ +```eval_rst +.. include:: /header.rst +:github_url: |github_link_base|/libs/bmp.md +``` + +# BMP decoder + +This extension allows the use of BMP images in LVGL. +This implementation uses [bmp-decoder](https://github.com/caj-johnson/bmp-decoder) library. +The pixel are read on demand (not the whole image is loaded) so using BMP images requires very little RAM. + +If enabled in `lv_conf.h` by `LV_USE_BMP` LVGL will register a new image decoder automatically so BMP files can be directly used as image sources. For example: +``` +lv_img_set_src(my_img, "S:path/to/picture.bmp"); +``` + +Note that, a file system driver needs to registered to open images from files. Read more about it [here](https://docs.lvgl.io/master/overview/file-system.html) or just enable one in `lv_conf.h` with `LV_USE_FS_...` + +## Limitations +- Only BMP files are supported and BMP images as C array (`lv_img_dsc_t`) are not. It's because there is no practical differences between how the BMP files and LVGL's image format stores the image data. +- BMP files can be loaded only from file. If you want to store them in flash it's better to convert them to C array with [LVGL's image converter](https://lvgl.io/tools/imageconverter). +- The BMP files color format needs to match with `LV_COLOR_DEPTH`. Use GIMP to save the image in the required format. + Both RGB888 and ARGB888 works with `LV_COLOR_DEPTH 32` +- Palette is not supported. +- Because not the whole image is read in can not be zoomed or rotated. + + +## Example +```eval_rst + +.. include:: ../../../examples/libs/bmp/index.rst + +``` + +## API + +```eval_rst + +.. doxygenfile:: lv_bmp.h + :project: lvgl + +``` diff --git a/docs/libs/freetype.md b/docs/libs/freetype.md new file mode 100644 index 0000000000..9b5c96e452 --- /dev/null +++ b/docs/libs/freetype.md @@ -0,0 +1,39 @@ +```eval_rst +.. include:: /header.rst +:github_url: |github_link_base|/libs/bmp.md +``` + +# FreeType support +Interface to [FreeType](https://www.freetype.org/) to generate font bitmaps run time. + +## Install FreeType +- Download Freetype from [here](https://sourceforge.net/projects/freetype/files/) +- `make` +- `sudo make install` + +## Add FreeType to your project +- Add include path: `/usr/include/freetype2` (for GCC: `-I/usr/include/freetype2 -L/usr/local/lib`) +- Add library: `freetype` (for GCC: `-L/usr/local/lib -lfreetype`) + +## Usage + +Enable `LV_USE_FREETYPE` in `lv_conf.h`. + +See the examples below. + +Note that, the FreeType extension doesn't use LVGL's file system. +You can simply pass the path to the font as usual on your operating system or platform. + +## Learn more +- FreeType [tutorial](https://www.freetype.org/freetype2/docs/tutorial/step1.html) +- LVGL's [font interface](https://docs.lvgl.io/v7/en/html/overview/font.html#add-a-new-font-engine) + + +## API + +```eval_rst + +.. doxygenfile:: lv_freetype.h + :project: lvgl + +``` \ No newline at end of file diff --git a/docs/libs/fs.md b/docs/libs/fs.md new file mode 100644 index 0000000000..ad5965ba02 --- /dev/null +++ b/docs/libs/fs.md @@ -0,0 +1,19 @@ +```eval_rst +.. include:: /header.rst +:github_url: |github_link_base|/libs/fs.md +``` + +# File System Interfaces + +LVGL has a [File system](https://docs.lvgl.io/master/overview/file-system.html) module to provides an abstraction layer for various file system drivers. + +LVG has build in support for +- [FATFS](http://elm-chan.org/fsw/ff/00index_e.html) +- STDIO (Linux and Windows using C standard function .e.g fopen, fread) +- POSIX (Linux and Windows using POSIX function .e.g open, read) + +You still need to provide the drivers and libraries, this extensions provide only the bridge between FATFS, STDIO, POSIX and LVGL. + +## Usage + +In `lv_conf.h` set a driver letter for one or more `LV_FS_USE_...` define(s). After that you can access files using that driver letter. Setting `'\0'` will disable use of that interface. diff --git a/docs/libs/gif.md b/docs/libs/gif.md new file mode 100644 index 0000000000..0bb3bdaafe --- /dev/null +++ b/docs/libs/gif.md @@ -0,0 +1,48 @@ +```eval_rst +.. include:: /header.rst +:github_url: |github_link_base|/libs/gif.md +``` + +# GIF decoder +Allow to use of GIF images in LVGL. Based on https://github.com/lecram/gifdec + +When enabled in `lv_conf.h` with `LV_USE_GIF` `lv_gif_create(parent)` can be used to create a gif widget. + +`lv_gif_set_src(obj, src)` works very similarly to `lv_img_set_src`. As source It also accepts images as variables (`lv_img_dsc_t`) or files. + + +## Convert GIF files to C array +To convert a GIF file to byte values array use [LVGL's online converter](https://lvgl.io/tools/imageconverter). Select "Raw" color format and "C array" Output format. + + +## Use GIF images from file + +For example: +```c +lv_gif_set_src(obj, "S:path/to/example.gif"); +``` + +Note that, a file system driver needs to regsitered to open images from files. Read more about it [here](https://docs.lvgl.io/master/overview/file-system.html) or just enable one in `lv_conf.h` with `LV_USE_FS_...` + + +## Memory requirements +To decode and display a GIF animation the following amount of RAM is required: +- `LV_COLOR_DEPTH 8`: 3 x image width x image height +- `LV_COLOR_DEPTH 16`: 4 x image width x image height +- `LV_COLOR_DEPTH 32`: 5 x image width x image height + +## Example +```eval_rst + +.. include:: ../../../examples/libs/gif/index.rst + +``` + +## API + +```eval_rst + +.. doxygenfile:: lv_gif.h + :project: lvgl + +``` \ No newline at end of file diff --git a/docs/libs/index.md b/docs/libs/index.md new file mode 100644 index 0000000000..a4c0190bbb --- /dev/null +++ b/docs/libs/index.md @@ -0,0 +1,21 @@ +```eval_rst +.. include:: /header.rst +:github_url: |github_link_base|/libs/index.md +``` +# 3rd party libraries + + +```eval_rst + +.. toctree:: + :maxdepth: 1 + + fs + bpm + jpg + png + gif + freetype + qrcode +``` + diff --git a/docs/libs/png.md b/docs/libs/png.md new file mode 100644 index 0000000000..39b6bea345 --- /dev/null +++ b/docs/libs/png.md @@ -0,0 +1,31 @@ +```eval_rst +.. include:: /header.rst +:github_url: |github_link_base|/libs/png.md +``` + +# PNG decoder + +Allow the use of PNG images in LVGL. This implementation uses [lodepng](https://github.com/lvandeve/lodepng) library. + +If enabled in `lv_conf.h` by `LV_USE_PNG` LVGL will register a new image decoder automatically so PNG files can be directly used as any other image sources. + +Note that, a file system driver needs to registered to open images from files. Read more about it [here](https://docs.lvgl.io/master/overview/file-system.html) or just enable one in `lv_conf.h` with `LV_USE_FS_...` + +The whole PNG image is decoded so during decoding RAM equals to `image width x image height x 4` bytes are required. + +As it might take significant time to decode PNG images LVGL's [images caching](https://docs.lvgl.io/master/overview/image.html#image-caching) feature can be useful. + +## Example +```eval_rst + +.. include:: ../../../examples/libs/png/index.rst + +``` + +## API + +```eval_rst + +.. doxygenfile:: lv_png.h + :project: lvgl + diff --git a/docs/libs/qrcode.md b/docs/libs/qrcode.md new file mode 100644 index 0000000000..f8f97c2532 --- /dev/null +++ b/docs/libs/qrcode.md @@ -0,0 +1,28 @@ +```eval_rst +.. include:: /header.rst +:github_url: |github_link_base|/libs/qrcode.md +``` + +# QR code + +QR code generation with LVGL. Uses [QR-Code-generator](https://github.com/nayuki/QR-Code-generator) by [nayuki](https://github.com/nayuki). + +## Get started +- Download or clone this repository + - [Download](https://github.com/littlevgl/lv_lib_qrcode.git) from GitHub + - Clone: git clone https://github.com/littlevgl/lv_lib_qrcode.git +- Include the library: `#include "lv_lib_qrcode/lv_qrcode.h"` +- Test with the following code: +```c +const char * data = "Hello world"; + +/*Create a 100x100 QR code*/ +lv_obj_t * qr = lv_qrcode_create(lv_scr_act(), 100, lv_color_hex3(0x33f), lv_color_hex3(0xeef)); + +/*Set data*/ +lv_qrcode_update(qr, data, strlen(data)); +``` + +## Notes +- QR codes with less data are smaller but they scaled by an integer numbers number to best fit to the given size + diff --git a/docs/libs/sjpg.md b/docs/libs/sjpg.md new file mode 100644 index 0000000000..bb9ba07929 --- /dev/null +++ b/docs/libs/sjpg.md @@ -0,0 +1,77 @@ +```eval_rst +.. include:: /header.rst +:github_url: |github_link_base|/libs/sjpg.md +``` + +# JPG decoder + +Allow the use of JPG images in LVGL. Besides that it also allows the use of a custom format, called Split JPG (SJPG), which can be decided in more optimal way on embedded systems. + +## Overview + - Supports both normal JPG and the custom SJPG formats. + - Decoding normal JPG consumes RAM with the size fo the whole uncompressed image (recommended only for devices with more RAM) + - SJPG is a custom format based on "normal" JPG and specially made for LVGL. + - SJPG is 'split-jpeg' which is a bundle of small jpeg fragments with an sjpg header. + - SJPG size will be almost comparable to the jpg file or might be a slightly larger. + - File read from file and c-array are implemented. + - SJPEG frame fragment cache enables fast fetching of lines if availble in cache. + - By default the sjpg image cache will be image width * 2 * 16 bytes (can be modified) + - Currently only 16 bit image format is supported (TODO) + - Only the required partion of the JPG and SJPG images are decoded, therefore they can't be zoomed or rotated. + +## Usage + +If enabled in `lv_conf.h` by `LV_USE_SJPG` LVGL will register a new image decoder automatically so JPG and SJPG files can be directly used as image sources. For example: +``` +lv_img_set_src(my_img, "S:path/to/picture.jpg"); +``` + +Note that, a file system driver needs to registered to open images from files. Read more about it [here](https://docs.lvgl.io/master/overview/file-system.html) or just enable one in `lv_conf.h` with `LV_USE_FS_...` + + + +## Converter + +### Converting JPG to C array + - Use lvgl online tool https://lvgl.io/tools/imageconverter + - Color format = RAW, output format = C Array + +### Converting JPG to SJPG +python3 and the PIL library required. (PIL can be installed with `pip3 install pillow`) + +To create SJPG from JPG: +- Copy the image to convert into `lvgl/scripts` +- `cd lvgl/scripts` +- `python3 jpg_to_sjpg.py image_to_convert.jpg`. It creates both a C files and an SJPG image. + +The expected result is: +```sh +Conversion started... + +Input: + image_to_convert.jpg + RES = 640 x 480 + +Output: + Time taken = 1.66 sec + bin size = 77.1 KB + walpaper.sjpg (bin file) + walpaper.c (c array) + +All good! +``` + + +## Example +```eval_rst + +.. include:: ../../../examples/libs/sjpg/index.rst + +``` + +## API + +```eval_rst + +.. doxygenfile:: lv_sjpg.h + :project: lvgl diff --git a/examples/libs/bmp/example_16bit.bmp b/examples/libs/bmp/example_16bit.bmp new file mode 100644 index 0000000000..21ee5ca981 Binary files /dev/null and b/examples/libs/bmp/example_16bit.bmp differ diff --git a/examples/libs/bmp/example_24bit.bmp b/examples/libs/bmp/example_24bit.bmp new file mode 100644 index 0000000000..8f6f4fd6c7 Binary files /dev/null and b/examples/libs/bmp/example_24bit.bmp differ diff --git a/examples/libs/bmp/example_32bit.bmp b/examples/libs/bmp/example_32bit.bmp new file mode 100644 index 0000000000..2c9435ec34 Binary files /dev/null and b/examples/libs/bmp/example_32bit.bmp differ diff --git a/examples/libs/bmp/index.rst b/examples/libs/bmp/index.rst new file mode 100644 index 0000000000..d8f2a81d4f --- /dev/null +++ b/examples/libs/bmp/index.rst @@ -0,0 +1,6 @@ +Open a BMP image from file +""""""""""""""""""""""""""""""""""""""""""""""" + +.. lv_example:: libs/bmp/lv_example_bmp_1 + :language: c + diff --git a/examples/libs/bmp/lv_example_bmp.h b/examples/libs/bmp/lv_example_bmp.h new file mode 100644 index 0000000000..6a4bf4ba43 --- /dev/null +++ b/examples/libs/bmp/lv_example_bmp.h @@ -0,0 +1,38 @@ +/** + * @file lv_example_bmp.h + * + */ + +#ifndef LV_EXAMPLE_BMP_H +#define LV_EXAMPLE_BMP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void lv_example_bmp_1(void); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_EXAMPLE_BMP_H*/ diff --git a/examples/libs/bmp/lv_example_bmp_1.c b/examples/libs/bmp/lv_example_bmp_1.c new file mode 100644 index 0000000000..9bead1d0f4 --- /dev/null +++ b/examples/libs/bmp/lv_example_bmp_1.c @@ -0,0 +1,17 @@ +#include "../../lv_examples.h" +#if LV_USE_BMP && LV_BUILD_EXAMPLES + +/** + * Open a BMP file from a file + */ +void lv_example_bmp_1(void) +{ + lv_obj_t * img = lv_img_create(lv_scr_act()); + /* Assuming a File system is attached to letter 'A' + * E.g. set LV_USE_FS_STDIO 'A' in lv_conf.h */ + lv_img_set_src(img, "A:lvgl/examples/libs/bmp/example_32bit.bmp"); + lv_obj_center(img); + +} + +#endif diff --git a/examples/libs/freetype/arial.ttf b/examples/libs/freetype/arial.ttf new file mode 100644 index 0000000000..886789b85b Binary files /dev/null and b/examples/libs/freetype/arial.ttf differ diff --git a/examples/libs/freetype/index.rst b/examples/libs/freetype/index.rst new file mode 100644 index 0000000000..03f6263a70 --- /dev/null +++ b/examples/libs/freetype/index.rst @@ -0,0 +1,6 @@ +Open a front with FreeTpye +""""""""""""""""""""""""""""""""""""""""""""""" + +.. lv_example:: libs/freetype/lv_example_freetype_1 + :language: c + diff --git a/examples/libs/freetype/lv_example_freetype.h b/examples/libs/freetype/lv_example_freetype.h new file mode 100644 index 0000000000..cc7da2e18f --- /dev/null +++ b/examples/libs/freetype/lv_example_freetype.h @@ -0,0 +1,38 @@ +/** + * @file lv_example_freetype.h + * + */ + +#ifndef LV_EXAMPLE_FREETYPE_H +#define LV_EXAMPLE_FREETYPE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void lv_example_freetype_1(void); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_EXAMPLE_FREETYPE_H*/ diff --git a/examples/libs/freetype/lv_example_freetype_1.c b/examples/libs/freetype/lv_example_freetype_1.c new file mode 100644 index 0000000000..903141eed0 --- /dev/null +++ b/examples/libs/freetype/lv_example_freetype_1.c @@ -0,0 +1,30 @@ +#include "../../lv_examples.h" +#if LV_USE_FREETYPE && LV_BUILD_EXAMPLES + +/** + * Load a font with FreeType + */ +void lv_example_freetype_1(void) +{ + /*Create a font*/ + static lv_ft_info_t info; + /*FreeType uses C standard file system, so no driver letter is required.*/ + info.name = "./lvgl/examples/libs/freetype/arial.ttf"; + info.weight = 24; + info.style = FT_FONT_STYLE_NORMAL; + lv_ft_font_init(&info); + + /*Create style with the new font*/ + static lv_style_t style; + lv_style_init(&style); + lv_style_set_text_font(&style, info.font); + lv_style_set_text_align(&style, LV_TEXT_ALIGN_CENTER); + + /*Create a label with the new style*/ + lv_obj_t * label = lv_label_create(lv_scr_act()); + lv_obj_add_style(label, &style, 0); + lv_label_set_text(label, "Hello world\nI'm a font created with FreeType"); + lv_obj_center(label); +} + +#endif diff --git a/examples/libs/gif/bulb.gif b/examples/libs/gif/bulb.gif new file mode 100644 index 0000000000..04f10d0142 Binary files /dev/null and b/examples/libs/gif/bulb.gif differ diff --git a/examples/libs/gif/img_bulb_gif.c b/examples/libs/gif/img_bulb_gif.c new file mode 100644 index 0000000000..f18dc8daec --- /dev/null +++ b/examples/libs/gif/img_bulb_gif.c @@ -0,0 +1,1128 @@ +#include "../../../lvgl.h" + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_BULB_GIF +#define LV_ATTRIBUTE_IMG_BULB_GIF +#endif + +static const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_BULB_GIF uint8_t img_blub_gif_map[] = { + 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x3c, 0x00, 0x50, 0x00, 0xf7, 0x00, 0x00, 0xfa, 0xfb, 0xfb, + 0xfd, 0xfd, 0xfd, 0xff, 0xff, 0xff, 0xd9, 0xec, 0xfe, 0x1e, 0x93, 0xfe, 0x23, 0x95, 0xfd, 0x5f, + 0xb2, 0xff, 0x52, 0xac, 0xfe, 0xb1, 0xd8, 0xff, 0xce, 0xe7, 0xff, 0xa3, 0xd2, 0xff, 0x80, 0xc0, + 0xfe, 0xe2, 0xf1, 0xfe, 0xca, 0xe5, 0xff, 0xf4, 0xf7, 0xf9, 0x8b, 0xc4, 0xff, 0x7e, 0xbe, 0xff, + 0xe0, 0xee, 0xff, 0xc6, 0xe4, 0xff, 0xbc, 0xde, 0xff, 0xec, 0xf5, 0xff, 0x1d, 0x92, 0xfd, 0x3f, + 0x9f, 0xfe, 0x71, 0xbb, 0xff, 0x9f, 0xcf, 0xfe, 0xf2, 0xf9, 0xff, 0x31, 0x9c, 0xfe, 0x98, 0xcb, + 0xff, 0x49, 0xa8, 0xfe, 0xbe, 0xe0, 0xff, 0x6d, 0xb3, 0xfe, 0x4c, 0xa9, 0xfe, 0xc4, 0xe2, 0xff, + 0x4a, 0xa8, 0xfe, 0x45, 0xa3, 0xfa, 0xd2, 0xe7, 0xfc, 0xbd, 0xde, 0xff, 0xf9, 0xf9, 0xf9, 0xf7, + 0xf8, 0xf8, 0xe2, 0xe2, 0xe2, 0xdd, 0xde, 0xdd, 0xe5, 0xea, 0xef, 0xff, 0xb7, 0x50, 0xff, 0xba, + 0x56, 0xfd, 0xb7, 0x51, 0xff, 0xb7, 0x4f, 0xff, 0xb8, 0x50, 0xc5, 0xc4, 0xc1, 0xfa, 0xb7, 0x55, + 0xee, 0xb3, 0x5b, 0xf7, 0xb5, 0x57, 0xf1, 0xb5, 0x5e, 0xf5, 0xb6, 0x5b, 0xfd, 0xb7, 0x54, 0xcd, + 0xcd, 0xcc, 0xe5, 0xe8, 0xea, 0xcf, 0xb2, 0x85, 0xf5, 0xb8, 0x5f, 0xee, 0xef, 0xf0, 0xc0, 0xbf, + 0xbe, 0xe4, 0xb1, 0x66, 0xff, 0xb7, 0x51, 0xda, 0xda, 0xda, 0xec, 0xed, 0xee, 0xdf, 0xdf, 0xdf, + 0xd1, 0xd2, 0xd1, 0xe4, 0xe5, 0xe5, 0xff, 0xe5, 0xbf, 0xff, 0xc5, 0x73, 0xff, 0xeb, 0xce, 0xf0, + 0xf3, 0xf5, 0xc8, 0xc8, 0xc8, 0xed, 0xb4, 0x61, 0xd8, 0xd8, 0xd7, 0xff, 0xdb, 0xa7, 0xff, 0xe1, + 0xb7, 0xff, 0xc1, 0x68, 0xfc, 0xf7, 0xee, 0xff, 0xc9, 0x7c, 0xff, 0xd2, 0x92, 0xfa, 0xba, 0x5e, + 0xff, 0xcc, 0x84, 0xff, 0xe8, 0xc6, 0xff, 0xfb, 0xf6, 0xff, 0xf5, 0xe8, 0xff, 0xf9, 0xf1, 0xff, + 0xed, 0xd5, 0xc9, 0xba, 0xa0, 0xf1, 0xf1, 0xf1, 0xf6, 0xf6, 0xf6, 0xeb, 0xb7, 0x6a, 0xe3, 0xb5, + 0x6f, 0xff, 0xbd, 0x5f, 0xa9, 0xa9, 0xa9, 0xd6, 0xd6, 0xd6, 0xb2, 0xb2, 0xb2, 0xb9, 0xb9, 0xb9, + 0x97, 0x98, 0x96, 0x9e, 0x9e, 0x9e, 0xa5, 0xa5, 0xa5, 0x45, 0x45, 0x45, 0x46, 0x46, 0x46, 0x47, + 0x47, 0x47, 0x4a, 0x4a, 0x4a, 0x4d, 0x4d, 0x4d, 0x50, 0x50, 0x50, 0x53, 0x53, 0x53, 0x58, 0x58, + 0x58, 0x5a, 0x5a, 0x5a, 0x5d, 0x5d, 0x5d, 0x5f, 0x5f, 0x5f, 0x63, 0x63, 0x63, 0x66, 0x66, 0x66, + 0x6a, 0x6a, 0x6a, 0x6d, 0x6d, 0x6d, 0x70, 0x70, 0x70, 0x75, 0x75, 0x75, 0x79, 0x79, 0x79, 0x7c, + 0x7c, 0x7c, 0x80, 0x80, 0x80, 0x84, 0x84, 0x84, 0x88, 0x88, 0x88, 0x8b, 0x8b, 0x8b, 0x8f, 0x8f, + 0x8f, 0xd5, 0xd5, 0xd5, 0xe7, 0xe7, 0xe7, 0xea, 0xea, 0xea, 0xf4, 0xf4, 0xf4, 0xf2, 0xf4, 0xf5, + 0xff, 0xd6, 0x9b, 0xff, 0xde, 0xaf, 0xff, 0xfe, 0xfc, 0xff, 0xf1, 0xde, 0xff, 0xd6, 0x9c, 0xff, + 0xcf, 0x8b, 0xfa, 0xd6, 0xa1, 0xf7, 0xc4, 0x7b, 0xf8, 0xbd, 0x67, 0xeb, 0xb5, 0x65, 0xf9, 0xf9, + 0xf8, 0xf1, 0xdb, 0xbb, 0xdb, 0xd7, 0xce, 0xe3, 0xdb, 0xcc, 0xda, 0xd2, 0xc3, 0xe3, 0xd6, 0xc0, + 0xae, 0xae, 0xae, 0xe4, 0xcb, 0xa6, 0xe5, 0xe2, 0xdc, 0xd0, 0xb8, 0x90, 0x56, 0x56, 0x56, 0xcd, + 0xcb, 0xc6, 0xcf, 0xc4, 0xaf, 0xcf, 0xc9, 0xbc, 0xd0, 0xbe, 0xa0, 0xd8, 0xbb, 0x8e, 0xd8, 0xbf, + 0x98, 0xd8, 0xc5, 0xa5, 0xd8, 0xd4, 0xcb, 0xda, 0xb7, 0x82, 0xdb, 0xca, 0xaf, 0xdb, 0xcd, 0xb6, + 0xdd, 0xbe, 0x8e, 0xdd, 0xc3, 0x9b, 0xdd, 0xdb, 0xd5, 0xde, 0xc8, 0xa5, 0xe0, 0xbb, 0x82, 0xe2, + 0xb8, 0x79, 0xe4, 0xc2, 0x8e, 0xe4, 0xc6, 0x99, 0xe4, 0xd1, 0xb3, 0xe5, 0xbe, 0x84, 0xe5, 0xdf, + 0xd4, 0xe9, 0xdb, 0xc5, 0xe9, 0xde, 0xce, 0xea, 0xbe, 0x7b, 0xea, 0xc1, 0x85, 0xea, 0xd3, 0xb0, + 0xea, 0xd8, 0xbc, 0xeb, 0xba, 0x71, 0xeb, 0xc5, 0x8e, 0xeb, 0xcb, 0x9a, 0xeb, 0xe4, 0xd9, 0xec, + 0xd0, 0xa5, 0xec, 0xe9, 0xe3, 0xed, 0xec, 0xea, 0xef, 0xe7, 0xd9, 0xf0, 0xbc, 0x71, 0xf0, 0xc0, + 0x7a, 0xf1, 0xb9, 0x68, 0xf3, 0xf0, 0xea, 0xf4, 0xc7, 0x86, 0xf4, 0xcb, 0x8e, 0xf7, 0xc0, 0x71, + 0xf9, 0xf7, 0xf4, 0xfd, 0xf5, 0xeb, 0xf5, 0xd0, 0x99, 0xda, 0xcf, 0xbd, 0xdc, 0xb1, 0x6f, 0xf2, + 0xd7, 0xb0, 0xed, 0xdf, 0xca, 0xc8, 0xb3, 0x8f, 0xd9, 0xb3, 0x78, 0xd2, 0xb0, 0x7c, 0xf3, 0xed, + 0xe4, 0xf4, 0xf3, 0xf1, 0xf5, 0xea, 0xd8, 0xf5, 0xe3, 0xc9, 0xc1, 0xb7, 0xa3, 0xc5, 0xbe, 0xaf, + 0xd7, 0xac, 0x6b, 0xc1, 0xb2, 0x95, 0xd2, 0xd6, 0xd6, 0xcf, 0xa9, 0x70, 0xbc, 0xb9, 0xb1, 0xc7, + 0xa8, 0x76, 0x41, 0x84, 0xd5, 0xb0, 0xa2, 0x86, 0x39, 0x81, 0xd9, 0x78, 0x92, 0xad, 0x88, 0xa0, + 0xb5, 0xb9, 0xcf, 0xe9, 0xbf, 0xa5, 0x7b, 0x40, 0x83, 0xd4, 0x80, 0x95, 0xa8, 0x80, 0x96, 0xa9, + 0x49, 0x86, 0xcf, 0x87, 0x96, 0xa2, 0x48, 0x85, 0xcd, 0x60, 0x8c, 0xbe, 0x2e, 0x7e, 0xdf, 0x18, + 0x78, 0xee, 0x9e, 0xbd, 0xe1, 0x06, 0x74, 0xfc, 0x00, 0x72, 0xff, 0x01, 0x73, 0xff, 0x47, 0xa1, + 0xfe, 0x0e, 0x76, 0xf7, 0x16, 0x7d, 0xfc, 0x32, 0x8d, 0xfe, 0x61, 0xa7, 0xfe, 0x24, 0x85, 0xfc, + 0x55, 0xa1, 0xff, 0x54, 0xa1, 0xff, 0x1e, 0x88, 0xfa, 0x20, 0x7b, 0xea, 0x9e, 0x9c, 0x93, 0x61, + 0x9c, 0xdc, 0x6f, 0x90, 0xb3, 0x84, 0xaf, 0xe1, 0x54, 0x89, 0xc6, 0x24, 0x7c, 0xe7, 0xa7, 0x9f, + 0x8c, 0x68, 0x8e, 0xb7, 0x8f, 0x98, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xff, 0x0b, + 0x4e, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2e, 0x30, 0x03, 0x01, 0x00, 0x00, 0x00, + 0x21, 0xf9, 0x04, 0x05, 0x25, 0x00, 0x04, 0x00, 0x21, 0xfe, 0x23, 0x52, 0x65, 0x73, 0x69, 0x7a, + 0x65, 0x64, 0x20, 0x6f, 0x6e, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x65, 0x7a, + 0x67, 0x69, 0x66, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x2c, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x50, 0x00, 0x00, 0x08, 0xff, 0x00, 0x03, 0x08, 0x1c, 0x48, + 0xb0, 0xa0, 0xc1, 0x83, 0x08, 0x13, 0x2a, 0x5c, 0xc8, 0xb0, 0xa1, 0xc3, 0x87, 0x10, 0x23, 0x4a, + 0x9c, 0x48, 0xb1, 0xa2, 0xc5, 0x8b, 0x18, 0x33, 0x6a, 0xdc, 0xc8, 0xb1, 0xa3, 0xc7, 0x8f, 0x20, + 0x43, 0x8a, 0x1c, 0x49, 0xb2, 0xa4, 0xc9, 0x93, 0x28, 0x53, 0xaa, 0x5c, 0xc9, 0xb2, 0xa5, 0xcb, + 0x97, 0x30, 0x63, 0xca, 0x9c, 0x49, 0xb3, 0xa6, 0xcd, 0x9b, 0x38, 0x63, 0x52, 0x50, 0xf0, 0xae, + 0x5d, 0x3a, 0x75, 0xf1, 0x36, 0x44, 0x88, 0x99, 0xe1, 0x81, 0xba, 0x74, 0x48, 0x93, 0x22, 0x7d, + 0xc7, 0xc0, 0x65, 0x04, 0x77, 0x48, 0xdd, 0x61, 0x18, 0x81, 0x41, 0x69, 0xba, 0x76, 0x0d, 0x58, + 0x46, 0x80, 0xf7, 0x13, 0x41, 0x86, 0x06, 0x0f, 0xe2, 0x59, 0xfd, 0x99, 0x35, 0x65, 0x06, 0xa8, + 0xed, 0x06, 0x8c, 0x80, 0x3a, 0x36, 0x69, 0xbb, 0xa6, 0x28, 0x37, 0x20, 0x8d, 0x47, 0xe2, 0x68, + 0x5b, 0xa5, 0x1e, 0x50, 0x52, 0x38, 0xea, 0xce, 0xee, 0x5d, 0xab, 0x43, 0x4d, 0x22, 0x40, 0x1a, + 0xc1, 0xc3, 0xdf, 0xb1, 0x18, 0x4e, 0x1a, 0x4e, 0x17, 0xcf, 0xef, 0xe1, 0xa5, 0x27, 0xd9, 0x3e, + 0x1e, 0x0b, 0xef, 0xa4, 0xcf, 0xc9, 0x63, 0xd5, 0x45, 0xc6, 0x4c, 0x59, 0x31, 0x67, 0xab, 0xef, + 0x4e, 0x0e, 0xfe, 0x9c, 0x54, 0xc1, 0xc9, 0xbd, 0xa4, 0x7f, 0xc2, 0x35, 0xf9, 0x20, 0x35, 0x04, + 0xb3, 0x92, 0x27, 0xc3, 0xa3, 0xa0, 0x32, 0xc2, 0xe5, 0xc7, 0xea, 0x06, 0xb0, 0x1c, 0xe1, 0xb8, + 0xad, 0xba, 0xb2, 0x2c, 0xe5, 0x1e, 0xde, 0xf0, 0x32, 0x03, 0xd7, 0xbb, 0xed, 0x32, 0xc0, 0x14, + 0xde, 0xf6, 0x41, 0xcc, 0x11, 0x7f, 0x47, 0x10, 0xed, 0x8d, 0x54, 0x9d, 0xf2, 0x98, 0x08, 0x7a, + 0xab, 0x43, 0x40, 0x93, 0xc2, 0x88, 0xef, 0xdf, 0x57, 0xe7, 0x9c, 0xa4, 0xf9, 0xe7, 0x8b, 0xf9, + 0xf3, 0xe6, 0xb3, 0xc8, 0xec, 0xc3, 0xa6, 0xbd, 0xfb, 0xf6, 0x7d, 0x64, 0x9a, 0x78, 0xff, 0x1e, + 0xcb, 0xcc, 0x3d, 0xf4, 0xd9, 0xe4, 0xa1, 0x29, 0x44, 0x4d, 0x99, 0xff, 0x65, 0xa4, 0x01, 0x44, + 0x4d, 0x7d, 0xec, 0x71, 0x86, 0x19, 0x7a, 0x9c, 0x80, 0x13, 0x00, 0x00, 0x8c, 0xe7, 0xe0, 0x83, + 0x30, 0x99, 0xf0, 0x07, 0x16, 0x3a, 0xfc, 0x80, 0x8b, 0x1f, 0x7e, 0xf4, 0xa1, 0xe1, 0x86, 0x1c, + 0xf6, 0x81, 0xa1, 0x1f, 0xb8, 0xfc, 0xa0, 0x03, 0x16, 0x7f, 0x98, 0xf0, 0xd1, 0x0f, 0x7d, 0x08, + 0x71, 0x02, 0x10, 0x28, 0xf8, 0xe0, 0x43, 0x12, 0x49, 0x78, 0xc1, 0xc7, 0x8c, 0x34, 0xce, 0xe8, + 0x05, 0x8c, 0x2e, 0xa2, 0x00, 0xc4, 0x09, 0x42, 0xf4, 0xf1, 0x03, 0x84, 0x40, 0x06, 0x29, 0xe4, + 0x90, 0x44, 0x16, 0x69, 0xe4, 0x91, 0x48, 0x56, 0x74, 0x5d, 0x00, 0x4b, 0xce, 0x94, 0x01, 0x03, + 0x14, 0x30, 0x00, 0x00, 0x94, 0x51, 0x52, 0x40, 0x1b, 0x4c, 0x03, 0x50, 0x99, 0xc1, 0x00, 0x19, + 0x58, 0x49, 0x41, 0x93, 0x2d, 0x05, 0x04, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, + 0x2c, 0x19, 0x00, 0x33, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x08, 0x0c, 0x00, 0xb9, 0xd9, 0x43, + 0x07, 0x00, 0x80, 0xb0, 0x5b, 0x00, 0x02, 0x02, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, 0x00, 0x00, + 0x00, 0x2c, 0x19, 0x00, 0x32, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x08, 0x16, 0x00, 0xe9, 0xa9, + 0x03, 0x40, 0x90, 0xa0, 0xb3, 0x19, 0x57, 0xf0, 0xb1, 0x2b, 0x08, 0xc0, 0x8a, 0x95, 0x66, 0x29, + 0x00, 0x04, 0x04, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x02, 0x00, 0x2c, 0x18, 0x00, 0x32, + 0x00, 0x09, 0x00, 0x03, 0x00, 0x00, 0x08, 0x19, 0x00, 0x4b, 0x94, 0xba, 0xb2, 0xcd, 0x9c, 0x80, + 0x83, 0x08, 0x55, 0xb4, 0xe8, 0x61, 0xea, 0x8b, 0x3d, 0x74, 0x08, 0x0f, 0x5a, 0x99, 0x28, 0x2c, + 0x20, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x18, 0x00, 0x31, 0x00, 0x0a, + 0x00, 0x04, 0x00, 0x00, 0x08, 0x20, 0x00, 0x53, 0x6c, 0xb3, 0x47, 0x4f, 0x1d, 0x80, 0x83, 0x07, + 0x17, 0xd5, 0x68, 0xd1, 0x42, 0xcb, 0x33, 0x7b, 0xec, 0x10, 0x22, 0x64, 0xd8, 0x42, 0x86, 0x26, + 0x6e, 0x12, 0x33, 0x02, 0x08, 0x08, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x07, 0x00, 0x00, 0x00, 0x2c, + 0x18, 0x00, 0x30, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x08, 0x20, 0x00, 0x01, 0xa0, 0x53, 0x07, + 0xa0, 0xa0, 0x41, 0x00, 0xcb, 0x64, 0xc8, 0xd0, 0x42, 0x69, 0x9b, 0xb9, 0x83, 0x06, 0x5b, 0x48, + 0x34, 0xf5, 0xc5, 0x1e, 0xc4, 0x82, 0x2d, 0x54, 0x30, 0x0a, 0x08, 0x00, 0x21, 0xf9, 0x04, 0x05, + 0x03, 0x00, 0x00, 0x00, 0x2c, 0x18, 0x00, 0x30, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x08, 0x1d, + 0x00, 0xb9, 0x7d, 0x19, 0x18, 0xc9, 0x1e, 0x3a, 0x00, 0x08, 0x01, 0x10, 0x6a, 0xc1, 0x90, 0xa1, + 0x0c, 0x4a, 0xdb, 0xcc, 0x25, 0x9c, 0x08, 0x80, 0x61, 0x8d, 0x60, 0x01, 0x01, 0x00, 0x21, 0xf9, + 0x04, 0x05, 0x04, 0x00, 0x00, 0x00, 0x2c, 0x18, 0x00, 0x2f, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, + 0x08, 0x22, 0x00, 0x01, 0xb0, 0xd3, 0x67, 0xcf, 0x1e, 0x3b, 0x00, 0x08, 0x11, 0xc6, 0xaa, 0xd1, + 0xa2, 0x21, 0x0b, 0x2d, 0x5f, 0xec, 0xa1, 0x4b, 0x48, 0xb1, 0x61, 0x0b, 0x1a, 0xb7, 0x28, 0x6a, + 0xec, 0x01, 0x20, 0x20, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x18, 0x00, + 0x2f, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x08, 0x1f, 0x00, 0xed, 0x7d, 0xd1, 0xa4, 0x45, 0x91, + 0x16, 0x4a, 0x91, 0xec, 0xa9, 0x03, 0x60, 0xac, 0x05, 0x80, 0x87, 0x2d, 0x22, 0xb6, 0x40, 0x12, + 0xe4, 0xa1, 0xc5, 0x8b, 0x2d, 0x9a, 0x05, 0x04, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x07, 0x00, 0x00, + 0x00, 0x2c, 0x17, 0x00, 0x2e, 0x00, 0x0d, 0x00, 0x03, 0x00, 0x00, 0x08, 0x20, 0x00, 0x01, 0x00, + 0x40, 0x67, 0x2f, 0xd2, 0x97, 0x83, 0xdb, 0xe8, 0xa9, 0x13, 0x08, 0xe0, 0x8a, 0x8c, 0x16, 0x10, + 0x23, 0xb6, 0xd8, 0xf2, 0x85, 0xa1, 0x45, 0x8b, 0x2d, 0x0e, 0x01, 0x08, 0x08, 0x00, 0x21, 0xf9, + 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x17, 0x00, 0x2d, 0x00, 0x0d, 0x00, 0x03, 0x00, 0x00, + 0x08, 0x23, 0x00, 0x01, 0x08, 0x04, 0xc0, 0xce, 0x9e, 0x41, 0x7b, 0xec, 0x06, 0x02, 0xa0, 0x17, + 0xc9, 0x13, 0x8c, 0x16, 0x10, 0x5b, 0xb0, 0x30, 0xf5, 0x85, 0xdb, 0x22, 0x28, 0x2d, 0x14, 0x0e, + 0x6c, 0xc1, 0x25, 0x58, 0x40, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x17, + 0x00, 0x2c, 0x00, 0x0d, 0x00, 0x04, 0x00, 0x00, 0x08, 0x27, 0x00, 0x01, 0x08, 0x1c, 0xa8, 0x8e, + 0x1d, 0xbb, 0x81, 0x03, 0xd1, 0xd9, 0xfb, 0x62, 0x0a, 0x46, 0x8b, 0x16, 0x32, 0x3c, 0x7d, 0xb1, + 0xc7, 0xed, 0x4a, 0x8e, 0x87, 0x08, 0x1f, 0xb6, 0x30, 0x06, 0x80, 0x0b, 0xc2, 0x8f, 0x02, 0x03, + 0x02, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, 0x00, 0x00, 0x00, 0x2c, 0x17, 0x00, 0x2c, 0x00, 0x0e, + 0x00, 0x03, 0x00, 0x00, 0x08, 0x24, 0x00, 0x01, 0x08, 0x04, 0xc0, 0x0e, 0xdf, 0x33, 0x4f, 0xa6, + 0x4c, 0x51, 0xfa, 0x62, 0xaf, 0x1d, 0x3a, 0x7b, 0x91, 0x3c, 0xd5, 0x68, 0xd1, 0x42, 0x20, 0xc5, + 0x16, 0x2a, 0x9a, 0x19, 0xa3, 0x38, 0xb0, 0x63, 0xc7, 0x80, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x06, + 0x00, 0x00, 0x00, 0x2c, 0x16, 0x00, 0x2b, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x08, 0x2b, 0x00, + 0x01, 0x08, 0x1c, 0xa8, 0x4e, 0xdf, 0xb6, 0x2f, 0xcf, 0xbe, 0x44, 0xb2, 0x87, 0x6e, 0x20, 0x00, + 0x76, 0xf6, 0x9e, 0x69, 0x69, 0x41, 0xb1, 0x62, 0x8b, 0x1c, 0x29, 0x52, 0x5c, 0x31, 0x05, 0x83, + 0xa2, 0xc3, 0x8f, 0x02, 0x05, 0x81, 0x1c, 0x09, 0x20, 0x20, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, + 0x00, 0x00, 0x00, 0x2c, 0x15, 0x00, 0x2a, 0x00, 0x12, 0x00, 0x04, 0x00, 0x00, 0x08, 0x31, 0x00, + 0x01, 0x08, 0x1c, 0x38, 0x90, 0x9d, 0xbd, 0x6d, 0xdb, 0xf0, 0xd9, 0x43, 0x47, 0x70, 0xa0, 0x3a, + 0x7a, 0x91, 0x3c, 0xc1, 0x68, 0x41, 0x91, 0xa2, 0x8c, 0x4c, 0x02, 0xbf, 0x44, 0x8a, 0xf4, 0x4c, + 0x8b, 0x8a, 0x16, 0x0d, 0x05, 0xae, 0x20, 0x24, 0xb0, 0x59, 0x0f, 0x8a, 0x21, 0x53, 0x06, 0x04, + 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x01, 0x00, 0x2c, 0x14, 0x00, 0x29, 0x00, 0x13, 0x00, + 0x04, 0x00, 0x00, 0x08, 0x32, 0x00, 0x03, 0x08, 0x1c, 0x48, 0x30, 0x80, 0x3a, 0x76, 0xf6, 0xec, + 0xe9, 0x43, 0x57, 0xb0, 0x20, 0xc2, 0x67, 0x5a, 0x54, 0xb4, 0x98, 0x08, 0xc3, 0xd3, 0x11, 0x07, + 0x60, 0xb6, 0x25, 0xb4, 0x17, 0x89, 0x92, 0x8c, 0x89, 0x05, 0x5b, 0x70, 0x69, 0x12, 0x80, 0xd0, + 0x8a, 0x89, 0x28, 0x1b, 0x16, 0x0c, 0x08, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, + 0x2c, 0x14, 0x00, 0x28, 0x00, 0x14, 0x00, 0x04, 0x00, 0x00, 0x08, 0x35, 0x00, 0x01, 0x08, 0x1c, + 0x48, 0x90, 0x20, 0x3b, 0x76, 0xea, 0x06, 0xa2, 0x2b, 0x38, 0x50, 0x9d, 0xbe, 0x2f, 0x9e, 0x68, + 0xa8, 0x50, 0x01, 0xc3, 0xd4, 0x17, 0x0a, 0xc6, 0xae, 0x6c, 0xb3, 0x77, 0x50, 0x1f, 0xbe, 0x67, + 0x5a, 0x5a, 0x88, 0x1c, 0x28, 0xf2, 0x10, 0x80, 0x60, 0x4c, 0x44, 0xaa, 0x1c, 0xc9, 0x90, 0x60, + 0x40, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x07, 0x00, 0x00, 0x00, 0x2c, 0x13, 0x00, 0x28, 0x00, 0x15, + 0x00, 0x03, 0x00, 0x00, 0x08, 0x34, 0x00, 0xb9, 0xb1, 0x03, 0x40, 0xb0, 0xa0, 0x41, 0x76, 0xdb, + 0x9e, 0x99, 0x52, 0xa4, 0xc5, 0xd3, 0x33, 0x6e, 0x4d, 0x60, 0x68, 0x8a, 0xa4, 0x0f, 0x1d, 0x00, + 0x75, 0xec, 0xec, 0x45, 0xf2, 0x54, 0xa3, 0x85, 0xc7, 0x8f, 0x3d, 0x84, 0x01, 0x38, 0xf4, 0xb1, + 0x06, 0x0d, 0x1a, 0x2c, 0x3c, 0x1a, 0x5c, 0x09, 0x20, 0x20, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, + 0x00, 0x01, 0x00, 0x2c, 0x13, 0x00, 0x27, 0x00, 0x15, 0x00, 0x04, 0x00, 0x00, 0x08, 0x38, 0x00, + 0xcf, 0x99, 0x0b, 0x40, 0xb0, 0xa0, 0xc1, 0x00, 0xe8, 0xec, 0x7d, 0xb9, 0xa2, 0x89, 0xd2, 0xb3, + 0x73, 0x43, 0x54, 0x98, 0xfa, 0x62, 0x0f, 0x9d, 0x41, 0x73, 0xf8, 0x28, 0xd1, 0x68, 0xc1, 0xb1, + 0xe3, 0x92, 0x00, 0x3d, 0x3a, 0xc2, 0x30, 0xe5, 0xc9, 0x14, 0x2f, 0x15, 0x1c, 0x0f, 0xaa, 0x24, + 0xd8, 0xb1, 0xc5, 0xca, 0x83, 0x01, 0x01, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, 0x00, 0x00, 0x00, + 0x2c, 0x13, 0x00, 0x26, 0x00, 0x15, 0x00, 0x04, 0x00, 0x00, 0x08, 0x36, 0x00, 0xcf, 0xd9, 0x43, + 0x07, 0xa0, 0xa0, 0xc1, 0x83, 0x00, 0xd4, 0xd1, 0xdb, 0xf6, 0xa5, 0xe1, 0x97, 0x61, 0x2d, 0x78, + 0x3d, 0xc3, 0xc7, 0x0e, 0x21, 0x3a, 0x7b, 0x5f, 0xb4, 0xb4, 0xd8, 0xb8, 0xd1, 0x10, 0x00, 0x8e, + 0x2d, 0x6a, 0x98, 0xba, 0x42, 0xd2, 0x93, 0x8c, 0x8d, 0x08, 0x53, 0x7e, 0x04, 0xd9, 0x42, 0xa5, + 0xc1, 0x80, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x13, 0x00, 0x25, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x00, 0x08, 0x35, 0x00, 0xf1, 0xd9, 0x43, 0x07, 0xa0, 0xa0, 0xc1, 0x83, + 0x06, 0xd9, 0xd9, 0xfb, 0xf2, 0x65, 0x1b, 0xb0, 0x16, 0x30, 0x34, 0x7d, 0xa1, 0x87, 0x10, 0x80, + 0x3a, 0x7d, 0x5f, 0x4a, 0xb1, 0x68, 0xc1, 0x31, 0x51, 0x41, 0x8e, 0x1c, 0xb5, 0x5c, 0x61, 0xf8, + 0xe5, 0x0a, 0x2f, 0x8e, 0x15, 0x11, 0x82, 0x5c, 0x99, 0xb2, 0x60, 0x40, 0x00, 0x21, 0xf9, 0x04, + 0x05, 0x07, 0x00, 0x00, 0x00, 0x2c, 0x13, 0x00, 0x24, 0x00, 0x15, 0x00, 0x04, 0x00, 0x00, 0x08, + 0x38, 0x00, 0xf1, 0xd9, 0x63, 0x07, 0xa0, 0xa0, 0xc1, 0x83, 0x07, 0xd1, 0xe9, 0xb3, 0x87, 0x0f, + 0x4a, 0x0b, 0x16, 0xa6, 0xbe, 0xd8, 0x43, 0x87, 0xb0, 0xa0, 0xb9, 0x6d, 0x94, 0x64, 0xb4, 0x68, + 0x01, 0xa3, 0xe0, 0xc6, 0x8d, 0x34, 0x28, 0x45, 0xda, 0x86, 0x6f, 0xdb, 0x17, 0x53, 0x2c, 0x36, + 0x56, 0x3c, 0xf8, 0xb1, 0xe5, 0x4a, 0x00, 0x01, 0x01, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, + 0x00, 0x00, 0x2c, 0x13, 0x00, 0x23, 0x00, 0x15, 0x00, 0x04, 0x00, 0x00, 0x08, 0x35, 0x00, 0xcf, + 0xe9, 0x63, 0x07, 0xa0, 0xa0, 0xc1, 0x83, 0x08, 0xd5, 0xb1, 0xc3, 0x07, 0xac, 0x45, 0x0b, 0x5e, + 0x57, 0xf0, 0x11, 0x44, 0x58, 0x90, 0x9d, 0xbd, 0x67, 0x5a, 0x54, 0x40, 0x29, 0xe8, 0xd0, 0x61, + 0x0d, 0x4f, 0x5f, 0xf0, 0xd9, 0x1b, 0x19, 0x89, 0x12, 0x0c, 0x87, 0x14, 0x0d, 0x76, 0x5c, 0x89, + 0x12, 0x61, 0x40, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x13, 0x00, 0x22, + 0x00, 0x15, 0x00, 0x04, 0x00, 0x00, 0x08, 0x34, 0x00, 0x01, 0xb0, 0x43, 0x07, 0xa0, 0xa0, 0xc1, + 0x83, 0x08, 0x0d, 0x0e, 0x53, 0xa1, 0xc2, 0x89, 0xa6, 0x2f, 0xfa, 0xd4, 0x25, 0x2c, 0x88, 0xce, + 0xde, 0x17, 0x4f, 0xbf, 0x0c, 0xb6, 0xd8, 0xd8, 0x82, 0xd7, 0x33, 0x7c, 0xfa, 0xd8, 0xb1, 0xd3, + 0x87, 0xef, 0x59, 0x0e, 0x8e, 0x13, 0x39, 0xaa, 0x44, 0x79, 0x30, 0x20, 0x00, 0x21, 0xf9, 0x04, + 0x05, 0x04, 0x00, 0x00, 0x00, 0x2c, 0x13, 0x00, 0x22, 0x00, 0x15, 0x00, 0x03, 0x00, 0x00, 0x08, + 0x34, 0x00, 0x8f, 0x69, 0x41, 0xb2, 0x2c, 0xd5, 0x17, 0x7b, 0xe8, 0x00, 0x28, 0x5c, 0xc8, 0x90, + 0xde, 0x36, 0x47, 0x00, 0x5a, 0xb4, 0x00, 0xe0, 0x42, 0xa2, 0x0c, 0x4a, 0xdb, 0xf4, 0x25, 0x54, + 0xc7, 0xce, 0xde, 0x97, 0x2d, 0x2a, 0x5a, 0x44, 0x61, 0xb8, 0x50, 0xa2, 0x44, 0x18, 0x34, 0x64, + 0xb0, 0x90, 0xc8, 0x30, 0x20, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x2c, 0x13, + 0x00, 0x21, 0x00, 0x15, 0x00, 0x04, 0x00, 0x00, 0x08, 0x39, 0x00, 0x83, 0x55, 0xd3, 0xb4, 0x49, + 0x53, 0x35, 0x7c, 0xec, 0x00, 0x28, 0x5c, 0xc8, 0x10, 0x00, 0x3b, 0x1d, 0xce, 0x5a, 0xb4, 0x20, + 0xf4, 0x44, 0x22, 0x0b, 0x53, 0x5f, 0xec, 0xa1, 0x5b, 0xa8, 0x8e, 0xde, 0x17, 0x4d, 0x35, 0x04, + 0x35, 0x54, 0x28, 0x51, 0x22, 0x0c, 0x59, 0xa6, 0x4c, 0xc9, 0x28, 0x39, 0xb2, 0x65, 0xc9, 0x16, + 0x23, 0x03, 0x02, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, 0x00, 0x00, 0x00, 0x2c, 0x13, 0x00, 0x20, + 0x00, 0x15, 0x00, 0x04, 0x00, 0x00, 0x08, 0x38, 0x00, 0x01, 0x70, 0x8b, 0xf4, 0xe5, 0x92, 0xb3, + 0x6d, 0xfa, 0xd4, 0x01, 0x58, 0xc8, 0xb0, 0xe1, 0xc2, 0x26, 0x35, 0x5a, 0x30, 0x59, 0xd1, 0xa2, + 0xa2, 0x96, 0x67, 0xf6, 0xd8, 0x39, 0x64, 0x87, 0xef, 0x4a, 0x2f, 0x87, 0x0b, 0x2b, 0x56, 0x84, + 0x61, 0x0a, 0xd9, 0x15, 0x4a, 0xa6, 0x22, 0xb6, 0x00, 0x09, 0x52, 0x64, 0x45, 0x87, 0x01, 0x01, + 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x01, 0x00, 0x2c, 0x14, 0x00, 0x1f, 0x00, 0x14, 0x00, + 0x04, 0x00, 0x00, 0x08, 0x38, 0x00, 0x03, 0xe0, 0xc3, 0xf7, 0x25, 0xc0, 0x32, 0x7c, 0xe8, 0x02, + 0x28, 0x5c, 0xc8, 0x50, 0xa1, 0x31, 0x15, 0x2d, 0x88, 0x48, 0x71, 0xd1, 0x42, 0xc6, 0x95, 0x6d, + 0xe6, 0x1a, 0x06, 0x40, 0x77, 0x4e, 0xa3, 0xc2, 0x16, 0x20, 0x7b, 0x98, 0x7a, 0xf6, 0xa5, 0x24, + 0xa5, 0x1c, 0x2a, 0x96, 0x79, 0x5c, 0x08, 0xb2, 0x25, 0xc8, 0x85, 0x01, 0x01, 0x00, 0x21, 0xf9, + 0x04, 0x05, 0x03, 0x00, 0x01, 0x00, 0x2c, 0x14, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x04, 0x00, 0x00, + 0x08, 0x33, 0x00, 0x03, 0x08, 0xd4, 0x67, 0x2f, 0x00, 0x80, 0x11, 0x02, 0x13, 0x2a, 0x14, 0xb8, + 0xe8, 0x14, 0x0c, 0x2e, 0x84, 0x08, 0x11, 0x81, 0xe1, 0x29, 0x92, 0x3e, 0x75, 0x0b, 0x13, 0xb6, + 0x58, 0xd8, 0xa2, 0x63, 0x0b, 0x2d, 0xcf, 0xb6, 0xe1, 0xc3, 0xb7, 0xed, 0x8b, 0xa4, 0x8c, 0x0a, + 0x3d, 0xaa, 0xdc, 0x18, 0x10, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x07, 0x00, 0x00, 0x00, 0x2c, 0x15, + 0x00, 0x1d, 0x00, 0x12, 0x00, 0x04, 0x00, 0x00, 0x08, 0x30, 0x00, 0x01, 0x08, 0x44, 0x67, 0x4e, + 0xa0, 0xc1, 0x83, 0x07, 0x85, 0x5d, 0xd1, 0x02, 0x65, 0xc8, 0x14, 0x2b, 0x4a, 0xa0, 0xd9, 0x43, + 0x87, 0x50, 0x20, 0x91, 0x16, 0x07, 0x5b, 0x68, 0x6c, 0x31, 0xe3, 0xca, 0x36, 0x7b, 0xf4, 0xe8, + 0xd9, 0x63, 0x56, 0x31, 0xe3, 0xc6, 0x8d, 0xc3, 0x02, 0x02, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, + 0x00, 0x00, 0x00, 0x2c, 0x15, 0x00, 0x1d, 0x00, 0x12, 0x00, 0x03, 0x00, 0x00, 0x08, 0x2b, 0x00, + 0x01, 0x60, 0x01, 0x43, 0x69, 0xcb, 0x2b, 0x56, 0x29, 0xb8, 0xd9, 0x43, 0x07, 0xa0, 0xa1, 0xc3, + 0x86, 0x5c, 0x5a, 0xac, 0x78, 0xe8, 0xa4, 0x85, 0x0c, 0x4a, 0xdb, 0xe8, 0x31, 0x7c, 0xc8, 0xd1, + 0x61, 0x8b, 0x8f, 0x2d, 0x60, 0xd0, 0xc8, 0x21, 0x2c, 0x20, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, + 0x00, 0x02, 0x00, 0x2c, 0x16, 0x00, 0x1c, 0x00, 0x10, 0x00, 0x04, 0x00, 0x00, 0x08, 0x2f, 0x00, + 0x05, 0x98, 0x08, 0xf2, 0xe5, 0x0a, 0xb2, 0x2b, 0x5f, 0xec, 0xb1, 0x13, 0xc0, 0xb0, 0xa1, 0x30, + 0x22, 0x2d, 0x5a, 0x10, 0x59, 0x62, 0x45, 0x50, 0x0b, 0x18, 0x94, 0xb6, 0xd1, 0x6b, 0xc8, 0xb1, + 0x63, 0xc4, 0x88, 0x33, 0x3c, 0x35, 0xeb, 0x48, 0x92, 0x61, 0xc4, 0x15, 0x01, 0x01, 0x00, 0x21, + 0xf9, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x17, 0x00, 0x1b, 0x00, 0x0f, 0x00, 0x04, 0x00, + 0x00, 0x08, 0x29, 0x00, 0x01, 0x00, 0xf8, 0xe3, 0x63, 0xc7, 0x97, 0x6d, 0xf6, 0xde, 0x09, 0x5c, + 0x08, 0x60, 0xca, 0xa1, 0x1a, 0x2d, 0x22, 0x46, 0x84, 0xa1, 0xe9, 0xcb, 0x39, 0x86, 0x18, 0x05, + 0x4a, 0x54, 0xa1, 0xab, 0x59, 0xc6, 0x8f, 0x2d, 0x7a, 0x00, 0x08, 0x08, 0x00, 0x21, 0xf9, 0x04, + 0x05, 0x07, 0x00, 0x03, 0x00, 0x2c, 0x0e, 0x00, 0x1a, 0x00, 0x20, 0x00, 0x29, 0x00, 0x00, 0x08, + 0xff, 0x00, 0x07, 0x08, 0x1c, 0x48, 0x70, 0x20, 0xa1, 0x22, 0x52, 0x8a, 0x10, 0x02, 0x50, 0xb0, + 0xa1, 0xc3, 0x82, 0x4d, 0x04, 0x41, 0x69, 0x41, 0x91, 0xe2, 0x8a, 0x43, 0xc2, 0x1e, 0x6a, 0x1c, + 0xe8, 0x8c, 0x4b, 0x0b, 0x81, 0x5c, 0xb8, 0x0c, 0xa8, 0xd8, 0xc3, 0x18, 0xc3, 0x8d, 0x0d, 0x0f, + 0x7d, 0xe4, 0xb2, 0x44, 0xd8, 0xa0, 0x21, 0x04, 0x29, 0x46, 0x99, 0x82, 0x92, 0xa0, 0xb1, 0x8f, + 0x86, 0xaa, 0x58, 0x79, 0xb2, 0xe2, 0x63, 0xc1, 0x16, 0x86, 0x6a, 0x0a, 0x6c, 0xa6, 0x62, 0xc0, + 0xcc, 0x42, 0x3e, 0x1f, 0xb6, 0x58, 0x52, 0x13, 0x00, 0x30, 0x81, 0x4c, 0xa2, 0x24, 0xd5, 0xb8, + 0x22, 0x18, 0x4a, 0x67, 0x1f, 0x0b, 0x89, 0x14, 0xda, 0x02, 0xe6, 0xc6, 0x61, 0x03, 0x5c, 0x08, + 0x23, 0x22, 0x54, 0x60, 0x14, 0x94, 0x50, 0x04, 0xba, 0x98, 0x8a, 0xb2, 0xc6, 0x49, 0x87, 0xc1, + 0x8a, 0x96, 0x25, 0xa8, 0xa2, 0x89, 0xc6, 0xb8, 0x73, 0xe9, 0xda, 0x7d, 0xb8, 0xc8, 0x45, 0xde, + 0x81, 0x3d, 0x16, 0x6d, 0x7c, 0xfa, 0x77, 0x00, 0x61, 0x8d, 0xc6, 0x0a, 0x0f, 0x38, 0x84, 0x72, + 0x99, 0xdc, 0xb9, 0x2a, 0x96, 0xd5, 0x04, 0x9b, 0x97, 0x32, 0xca, 0xbd, 0x8a, 0x33, 0x6b, 0xde, + 0x5c, 0x36, 0xf1, 0x46, 0xc6, 0x85, 0x83, 0xad, 0xa0, 0xaa, 0x19, 0x34, 0xe7, 0x86, 0xcd, 0x34, + 0x16, 0xd1, 0xdc, 0xc4, 0x6f, 0x43, 0x17, 0x98, 0x15, 0x33, 0xea, 0x51, 0xd0, 0xf5, 0xe9, 0xdb, + 0xb8, 0x51, 0x62, 0x79, 0xb8, 0x5b, 0x33, 0x8a, 0x87, 0x40, 0x36, 0xeb, 0x78, 0xe8, 0x87, 0xf3, + 0x9d, 0x86, 0x74, 0xde, 0x66, 0x4e, 0x72, 0x86, 0xa0, 0x99, 0x20, 0xb7, 0x7d, 0xd4, 0x29, 0x53, + 0x66, 0x0e, 0x9f, 0xdc, 0x03, 0xb0, 0xf4, 0xc6, 0xce, 0xbd, 0x3b, 0x67, 0x00, 0x59, 0x08, 0x16, + 0x23, 0xaf, 0xd9, 0x47, 0xe0, 0x78, 0x81, 0x59, 0x18, 0x96, 0xc0, 0x35, 0xe0, 0x04, 0x90, 0xdf, + 0x03, 0x92, 0x0c, 0xf0, 0xb2, 0x91, 0xbe, 0xfc, 0x01, 0x28, 0x80, 0x9c, 0x18, 0x80, 0xab, 0x84, + 0xf7, 0x01, 0x01, 0x01, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x2c, 0x0e, 0x00, + 0x19, 0x00, 0x20, 0x00, 0x2a, 0x00, 0x00, 0x08, 0xff, 0x00, 0x07, 0x08, 0x1c, 0x48, 0x50, 0x20, + 0x00, 0x42, 0x45, 0xa4, 0x14, 0x21, 0x04, 0xa0, 0xa0, 0xc3, 0x87, 0x05, 0x85, 0x1d, 0x82, 0xd2, + 0xa2, 0x62, 0xc5, 0x1a, 0x87, 0x08, 0x41, 0xdc, 0x38, 0x90, 0x51, 0x8d, 0x8a, 0x03, 0x5c, 0xb8, + 0x10, 0x58, 0x51, 0xc5, 0xa1, 0x45, 0x1c, 0x1d, 0x02, 0x18, 0x56, 0x91, 0x8b, 0x20, 0x42, 0x53, + 0x86, 0x10, 0xac, 0x88, 0xa8, 0x49, 0x4a, 0x82, 0x2c, 0x07, 0x18, 0x6a, 0x42, 0x45, 0x09, 0x91, + 0x15, 0x0e, 0x5b, 0x10, 0x99, 0x72, 0x73, 0x00, 0xa3, 0x16, 0x03, 0x9e, 0x4c, 0x51, 0xe2, 0x02, + 0xe4, 0xc3, 0x16, 0x87, 0x6e, 0x0a, 0xfb, 0x38, 0xa0, 0xd0, 0x13, 0xa4, 0x29, 0x55, 0x34, 0x4b, + 0x79, 0xa8, 0x85, 0x0a, 0x22, 0x4e, 0x53, 0xb6, 0x18, 0xc6, 0x71, 0x51, 0x8d, 0x01, 0x51, 0xaa, + 0x8c, 0x2c, 0x3a, 0xa0, 0x46, 0xb0, 0x8d, 0xcd, 0x54, 0x0c, 0x58, 0xe1, 0x04, 0x6b, 0xd1, 0x16, + 0x5b, 0x21, 0x3a, 0xc3, 0x6a, 0xf7, 0xae, 0xb3, 0x8d, 0x47, 0xd9, 0x16, 0x6c, 0xb1, 0x64, 0xe3, + 0x90, 0xbe, 0x82, 0x5b, 0xfc, 0x85, 0xd8, 0x0c, 0x31, 0x5b, 0xbc, 0x1b, 0x9b, 0xc8, 0x15, 0x3c, + 0x50, 0x85, 0xcd, 0x8d, 0x88, 0x28, 0x0f, 0xf4, 0x95, 0x72, 0xaf, 0xe6, 0x01, 0x79, 0x39, 0x46, + 0xd1, 0x6c, 0xa8, 0xa8, 0x30, 0x2e, 0x82, 0x99, 0x50, 0x46, 0x7d, 0x93, 0x35, 0xe5, 0xc9, 0x9f, + 0x53, 0x92, 0x8d, 0x9d, 0x52, 0x18, 0x6c, 0x87, 0x2e, 0x84, 0xd1, 0xfe, 0xb5, 0x91, 0x37, 0xed, + 0xc2, 0x10, 0x65, 0xd2, 0x26, 0x74, 0xbb, 0xf2, 0x32, 0xda, 0x02, 0x8d, 0x15, 0xef, 0x61, 0x0c, + 0xf9, 0xc0, 0x26, 0x84, 0xa2, 0x47, 0xbf, 0xec, 0xbc, 0xfa, 0xc6, 0x3e, 0x10, 0xfd, 0x38, 0x3f, + 0x02, 0xd1, 0x86, 0x73, 0x14, 0x10, 0xbd, 0x38, 0x58, 0xff, 0xf3, 0xc6, 0x61, 0x1b, 0x2c, 0xd5, + 0xbf, 0x94, 0x21, 0x58, 0x26, 0x92, 0xf5, 0x01, 0x5f, 0xce, 0x08, 0x3c, 0x13, 0xa9, 0xe1, 0x7b, + 0x1d, 0x41, 0x82, 0xfc, 0x78, 0xcf, 0xbf, 0xbf, 0xff, 0x82, 0x26, 0xfc, 0x81, 0x85, 0x0e, 0x3f, + 0x68, 0x37, 0x00, 0x76, 0x03, 0x08, 0xa1, 0xe0, 0x82, 0x0a, 0x0a, 0x84, 0xa0, 0x1f, 0x3f, 0xe8, + 0x80, 0xc5, 0x1f, 0x26, 0x0c, 0x80, 0x45, 0x1f, 0x42, 0x0c, 0xe4, 0x43, 0x12, 0x03, 0x78, 0xc1, + 0x07, 0x1f, 0x10, 0x7d, 0x28, 0x5e, 0x12, 0x3e, 0x0c, 0x24, 0x44, 0x1f, 0x3a, 0xfc, 0x37, 0x50, + 0x40, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x2c, 0x0d, 0x00, 0x18, 0x00, 0x22, + 0x00, 0x2b, 0x00, 0x00, 0x08, 0xff, 0x00, 0x07, 0x08, 0x1c, 0x48, 0x70, 0xa0, 0x30, 0x67, 0x82, + 0x94, 0x08, 0x72, 0xb6, 0xac, 0xa0, 0xc3, 0x87, 0x0f, 0x9d, 0x21, 0x52, 0xd1, 0xa2, 0xa2, 0x45, + 0x60, 0xce, 0x00, 0x40, 0xdc, 0x68, 0x30, 0x8a, 0x45, 0x17, 0x5c, 0xb8, 0x08, 0xb4, 0x48, 0xa4, + 0x21, 0xc7, 0x87, 0x84, 0xb8, 0x54, 0x64, 0x32, 0xa4, 0x49, 0x80, 0x21, 0x04, 0x2b, 0xae, 0x68, + 0x76, 0xb2, 0xa0, 0x30, 0x95, 0x2a, 0x04, 0x4d, 0xb1, 0x52, 0x88, 0x89, 0x0b, 0x87, 0x2d, 0x5c, + 0xd0, 0xac, 0x39, 0x60, 0x11, 0x91, 0x16, 0x03, 0x96, 0x4c, 0x79, 0x62, 0x11, 0x29, 0x50, 0x2e, + 0xc1, 0x88, 0x1a, 0x73, 0x3a, 0xc4, 0x49, 0xc5, 0x93, 0x2d, 0x0e, 0xd5, 0x0c, 0x56, 0x23, 0xe8, + 0x8a, 0xab, 0x35, 0x5b, 0xd4, 0x68, 0x72, 0xd2, 0x19, 0xd2, 0x25, 0x45, 0x88, 0x0e, 0x6c, 0xe1, + 0xec, 0xe4, 0x30, 0xa4, 0x86, 0x3c, 0xaa, 0x1d, 0xd0, 0x62, 0xd8, 0x49, 0x28, 0x48, 0xc1, 0xce, + 0x05, 0xc6, 0x11, 0x40, 0x8d, 0xb9, 0x0e, 0xa1, 0xf4, 0xfd, 0x0b, 0x98, 0xa0, 0x60, 0x8e, 0x89, + 0x0a, 0x13, 0x44, 0xe4, 0x56, 0xf1, 0x40, 0xad, 0x1c, 0xcd, 0x3a, 0x6e, 0x91, 0x96, 0x63, 0xb0, + 0xc3, 0x85, 0xb9, 0x68, 0x3c, 0xb9, 0x44, 0x31, 0x5b, 0xa2, 0x00, 0x18, 0x03, 0x8e, 0xe2, 0xb8, + 0x74, 0xcd, 0x15, 0x6a, 0x85, 0x29, 0x96, 0x6c, 0xfa, 0x24, 0x69, 0x8e, 0x76, 0x4b, 0x37, 0x73, + 0xfa, 0x50, 0x05, 0x21, 0xd3, 0x00, 0x30, 0x3b, 0xe4, 0xdb, 0x1a, 0xf2, 0x43, 0xdf, 0xa5, 0x59, + 0x17, 0xa4, 0xdc, 0x5a, 0xe0, 0xa1, 0x1e, 0x0e, 0x81, 0x17, 0x17, 0xc6, 0x9c, 0x79, 0xd4, 0xe2, + 0xd0, 0x6b, 0x02, 0x01, 0x43, 0xbd, 0x3a, 0x75, 0x20, 0xd0, 0xbb, 0x40, 0x8c, 0x04, 0xfd, 0x05, + 0x44, 0x30, 0xd0, 0x51, 0xb0, 0x65, 0x71, 0xc8, 0xc6, 0x4b, 0xf4, 0x3b, 0x0e, 0xe9, 0x6c, 0x2e, + 0xae, 0xa3, 0x4e, 0x19, 0x81, 0x65, 0xe6, 0xf8, 0x89, 0x2e, 0xb0, 0x84, 0x8d, 0x31, 0x63, 0x8e, + 0x98, 0xa0, 0xcf, 0xbf, 0xbf, 0xff, 0x87, 0x25, 0x64, 0x81, 0x85, 0x0e, 0x3f, 0xcc, 0xd7, 0x47, + 0x1f, 0x03, 0x08, 0x21, 0xc4, 0x00, 0x27, 0x34, 0xe8, 0x60, 0x82, 0x0b, 0x1e, 0x38, 0x80, 0x1f, + 0x3f, 0xe8, 0x80, 0x45, 0x16, 0x25, 0x0c, 0x90, 0x85, 0x1f, 0x42, 0x9c, 0x00, 0x04, 0x0a, 0x3e, + 0x24, 0xe1, 0x05, 0x1f, 0x7c, 0xd4, 0x44, 0xa2, 0x17, 0x49, 0xf8, 0x80, 0x02, 0x10, 0x27, 0x08, + 0xe1, 0x47, 0x16, 0xa0, 0x01, 0x20, 0xe3, 0x8c, 0x6a, 0x05, 0x04, 0x00, 0x21, 0xf9, 0x04, 0x05, + 0x04, 0x00, 0x03, 0x00, 0x2c, 0x0c, 0x00, 0x17, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x00, 0x08, 0xff, + 0x00, 0x07, 0x08, 0x1c, 0x48, 0x90, 0xe0, 0x32, 0x67, 0x82, 0x12, 0x3a, 0x5b, 0x56, 0xb0, 0xa1, + 0xc3, 0x87, 0x00, 0x9c, 0x21, 0x6a, 0x41, 0xb1, 0xa2, 0x0a, 0x60, 0x43, 0x16, 0x3d, 0xdc, 0x58, + 0x70, 0xd9, 0x44, 0x8a, 0x03, 0x5c, 0xb8, 0x18, 0x48, 0x91, 0x49, 0x33, 0x8e, 0x1c, 0x9b, 0xad, + 0x68, 0x31, 0x80, 0xc9, 0x12, 0x2a, 0x53, 0x86, 0x14, 0x6c, 0xd1, 0xc3, 0x19, 0x4a, 0x87, 0xcd, + 0x5c, 0xb4, 0x70, 0xa1, 0x64, 0x0a, 0x15, 0x41, 0x51, 0xb8, 0x34, 0x6c, 0xa1, 0xc2, 0xe6, 0xcd, + 0x81, 0xc2, 0x56, 0xba, 0x90, 0x32, 0xa5, 0x90, 0x4e, 0x90, 0x43, 0x6b, 0x30, 0x3c, 0x3a, 0x20, + 0x0a, 0x4b, 0x2e, 0x54, 0x9c, 0xb0, 0x44, 0xd9, 0x02, 0x11, 0xd5, 0x66, 0x5b, 0x07, 0x40, 0xbd, + 0xd9, 0xe2, 0xe4, 0xcd, 0x61, 0x2c, 0xa5, 0x14, 0xa2, 0x2a, 0xb0, 0xc5, 0xb0, 0x9b, 0xc1, 0x6a, + 0x08, 0x2c, 0xe2, 0x84, 0xad, 0xc0, 0x1a, 0x00, 0x50, 0x82, 0x6d, 0x1b, 0x96, 0xaa, 0x0b, 0x61, + 0x28, 0x9d, 0xf5, 0xb5, 0x2b, 0xd6, 0xec, 0xc6, 0x63, 0x83, 0xed, 0xaa, 0x30, 0xec, 0x90, 0x02, + 0xbe, 0xc4, 0x6c, 0x55, 0x10, 0x42, 0xb9, 0x4c, 0x05, 0x61, 0x82, 0x2e, 0x9a, 0xa0, 0x04, 0x00, + 0xe5, 0xf2, 0x40, 0x60, 0x47, 0x0f, 0x79, 0x16, 0xb8, 0xe4, 0xe8, 0xb2, 0x1e, 0x9e, 0x6b, 0x68, + 0x0e, 0xed, 0xd9, 0x18, 0xdb, 0x60, 0x44, 0x08, 0x23, 0xca, 0x6b, 0x97, 0x09, 0xdb, 0xd8, 0xa9, + 0x47, 0xeb, 0x76, 0xd8, 0xc2, 0xa8, 0x67, 0x00, 0xa0, 0x39, 0x46, 0xd9, 0x3d, 0xc0, 0x37, 0x6f, + 0xc6, 0x9e, 0x9b, 0xa0, 0x7e, 0x88, 0x97, 0xf8, 0x00, 0xaf, 0x0f, 0x7f, 0x39, 0x1f, 0xe0, 0xfa, + 0xa1, 0x71, 0xdd, 0x8b, 0xde, 0x4e, 0xe7, 0x18, 0xac, 0x89, 0x77, 0xef, 0x1a, 0xb7, 0x8b, 0x78, + 0xe7, 0x98, 0x64, 0x87, 0xf9, 0xf3, 0xe6, 0x93, 0x6c, 0xdf, 0xd3, 0xa6, 0xbd, 0xfb, 0xf6, 0x61, + 0xb6, 0x77, 0x79, 0xff, 0x5e, 0xcc, 0xf6, 0x17, 0xf4, 0xdd, 0x83, 0xd9, 0x8e, 0x65, 0x4d, 0xc3, + 0x34, 0x3f, 0x88, 0xe7, 0x45, 0x1a, 0x04, 0xa1, 0x11, 0xc4, 0x78, 0x03, 0xf4, 0x31, 0xc6, 0x1c, + 0x72, 0x88, 0x21, 0x04, 0x82, 0x10, 0x46, 0x28, 0xa1, 0x40, 0x00, 0x64, 0xf1, 0x87, 0x0e, 0x3f, + 0xf8, 0x91, 0xe0, 0x00, 0x42, 0x3c, 0x78, 0xc2, 0x09, 0x0d, 0x7d, 0xc8, 0xe1, 0x83, 0x7d, 0x0c, + 0x80, 0x4b, 0x80, 0x7f, 0x64, 0x41, 0xdb, 0x0f, 0x7d, 0x9c, 0x00, 0x04, 0x0a, 0x3e, 0x24, 0xe1, + 0x05, 0x1f, 0x76, 0xf1, 0xe1, 0x45, 0x12, 0x3e, 0xa0, 0x00, 0xc4, 0x09, 0x7d, 0x04, 0x78, 0x13, + 0x00, 0xb4, 0x15, 0x04, 0xe4, 0x51, 0x01, 0x01, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x06, 0x00, 0x07, + 0x00, 0x2c, 0x0c, 0x00, 0x16, 0x00, 0x24, 0x00, 0x2d, 0x00, 0x00, 0x08, 0xff, 0x00, 0x0f, 0x08, + 0x1c, 0x48, 0xb0, 0xa0, 0x40, 0x00, 0x06, 0x13, 0x2a, 0x5c, 0x78, 0xa0, 0xd9, 0x21, 0x44, 0x30, + 0x54, 0xc0, 0x40, 0x74, 0xa8, 0x19, 0xc3, 0x8b, 0x04, 0x9b, 0x21, 0x6a, 0x61, 0xb0, 0x45, 0x0b, + 0x60, 0xce, 0x30, 0x2e, 0x04, 0x70, 0x48, 0x85, 0x40, 0x17, 0x4e, 0x94, 0x38, 0x29, 0xd8, 0x62, + 0x58, 0x30, 0x91, 0x05, 0x17, 0xfd, 0xe2, 0xb8, 0x42, 0x50, 0x93, 0x00, 0xc2, 0x94, 0x74, 0x44, + 0xd4, 0x04, 0xe6, 0xc0, 0x99, 0x07, 0x88, 0x50, 0xa9, 0x22, 0x88, 0x89, 0xc7, 0x84, 0x2d, 0x10, + 0x21, 0x84, 0xc9, 0x88, 0x23, 0x93, 0x26, 0x56, 0xb8, 0x70, 0x64, 0xd8, 0xc2, 0x18, 0x4c, 0x61, + 0x35, 0x06, 0x2e, 0x59, 0x01, 0xb3, 0x86, 0x30, 0x91, 0x87, 0x04, 0x32, 0x71, 0xe1, 0x53, 0x60, + 0xd8, 0x8b, 0x8b, 0xa0, 0x1c, 0x70, 0x51, 0xe5, 0x49, 0xd9, 0x03, 0x50, 0x96, 0x2e, 0x6c, 0xc6, + 0xd1, 0x85, 0x20, 0xae, 0x65, 0x55, 0x2c, 0xbb, 0x38, 0xe4, 0x2d, 0xcb, 0x90, 0x0c, 0xad, 0xfa, + 0x25, 0x08, 0x58, 0x21, 0x00, 0xc1, 0x83, 0x0f, 0xb4, 0x28, 0x9c, 0x90, 0x44, 0xe2, 0x81, 0x8b, + 0x2f, 0xd2, 0x7d, 0xac, 0xf7, 0x62, 0xb0, 0xac, 0x89, 0xe3, 0x62, 0x1c, 0xf6, 0xf8, 0xac, 0x64, + 0x93, 0x83, 0xbf, 0x8a, 0xe4, 0xfc, 0xb8, 0xb4, 0x69, 0xd3, 0x2f, 0x4f, 0xab, 0x2e, 0xc8, 0x68, + 0x35, 0x43, 0x26, 0x17, 0x11, 0x99, 0x5e, 0x72, 0x91, 0x71, 0x68, 0xd0, 0x0a, 0x53, 0x97, 0x4e, + 0xb4, 0x50, 0xf6, 0x69, 0xcf, 0x06, 0x5b, 0xaf, 0x26, 0x4b, 0x30, 0xab, 0xdc, 0xd3, 0x8b, 0x82, + 0x29, 0x57, 0xee, 0xba, 0x39, 0x43, 0x3e, 0x0a, 0xbd, 0xac, 0xb6, 0xa3, 0x10, 0xcf, 0x6a, 0x31, + 0x0a, 0xf7, 0xac, 0x06, 0xa3, 0xf0, 0xcb, 0x6a, 0x3f, 0x68, 0x0c, 0x9a, 0x3c, 0x11, 0xe2, 0xfa, + 0x88, 0x19, 0x82, 0x65, 0x76, 0x38, 0x47, 0xb1, 0x47, 0x8d, 0x1a, 0x3d, 0x49, 0x9c, 0x0f, 0x04, + 0x70, 0x5c, 0xbe, 0x7d, 0xd7, 0x58, 0x14, 0x9e, 0x60, 0xb8, 0x9f, 0xa0, 0x9f, 0x03, 0x3a, 0xe4, + 0x27, 0x50, 0x09, 0x7e, 0x90, 0x77, 0x80, 0x0f, 0xa7, 0x21, 0x78, 0x80, 0x10, 0x7e, 0x94, 0x50, + 0x56, 0x09, 0x10, 0x42, 0x78, 0x10, 0x46, 0x01, 0x01, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, 0x00, + 0x0d, 0x00, 0x2c, 0x0c, 0x00, 0x16, 0x00, 0x24, 0x00, 0x2d, 0x00, 0x00, 0x08, 0xff, 0x00, 0x1b, + 0x08, 0x1c, 0x48, 0xb0, 0xa0, 0xc0, 0x00, 0x06, 0x13, 0x2a, 0x5c, 0xd8, 0x20, 0x40, 0x09, 0x42, + 0x45, 0x8a, 0x34, 0x23, 0xb4, 0x08, 0x21, 0xc3, 0x8b, 0x03, 0x03, 0x2c, 0x3b, 0x54, 0xa3, 0x60, + 0x8d, 0x61, 0xcd, 0x2c, 0x62, 0x4c, 0x18, 0x20, 0xd8, 0x21, 0x15, 0x0d, 0x5a, 0xa8, 0x6c, 0x41, + 0x50, 0xc5, 0x2f, 0x61, 0x22, 0x47, 0x1e, 0x14, 0x46, 0x24, 0xa5, 0x8b, 0x27, 0x45, 0xa8, 0x08, + 0x32, 0xc8, 0x25, 0xa4, 0xcc, 0x81, 0xc2, 0xb8, 0xa4, 0x34, 0x44, 0x65, 0x4a, 0x11, 0x25, 0x35, + 0x0d, 0xae, 0x20, 0x14, 0x93, 0x61, 0x06, 0x1d, 0x35, 0x5b, 0x28, 0x19, 0x34, 0x44, 0x28, 0x43, + 0x2e, 0x4d, 0x64, 0x0e, 0x00, 0x23, 0x70, 0xc5, 0x94, 0x42, 0x32, 0x87, 0x35, 0x55, 0x28, 0xcc, + 0x45, 0xca, 0x16, 0x2b, 0x7e, 0xaa, 0x58, 0x86, 0x31, 0xc0, 0xa1, 0x94, 0x3b, 0x7f, 0x0a, 0x34, + 0x36, 0xb6, 0x60, 0x00, 0x28, 0x29, 0xad, 0x3c, 0x91, 0xdb, 0x20, 0x51, 0x5d, 0x82, 0xc2, 0x50, + 0xf2, 0x1d, 0xa8, 0x22, 0x2b, 0xc3, 0x66, 0x83, 0x09, 0xba, 0x60, 0xbb, 0x30, 0x43, 0xae, 0xc4, + 0x03, 0x5d, 0x10, 0x5a, 0x08, 0xe0, 0x1d, 0xe4, 0xc8, 0xbd, 0x2e, 0x06, 0xbe, 0xcc, 0x22, 0xd8, + 0xc5, 0x00, 0x89, 0x2e, 0x23, 0xfa, 0x4b, 0xd0, 0xd8, 0xe5, 0x25, 0x23, 0x85, 0xf5, 0x48, 0xbc, + 0xc2, 0x30, 0xc6, 0xb7, 0x83, 0x51, 0xcb, 0x5c, 0x74, 0xb9, 0xf6, 0x45, 0xd7, 0xb6, 0x73, 0x17, + 0x74, 0x86, 0x51, 0x30, 0xe4, 0x00, 0x88, 0x2e, 0x46, 0xb1, 0xcd, 0xfb, 0xb0, 0xed, 0x60, 0x1d, + 0x15, 0x42, 0x01, 0x90, 0x3b, 0xb8, 0xc2, 0x5f, 0xba, 0x65, 0x27, 0x2c, 0x6e, 0x9b, 0xb9, 0x6f, + 0x81, 0xab, 0x49, 0x0f, 0x0e, 0xd6, 0xa4, 0x7b, 0x77, 0xcf, 0xba, 0xc3, 0x63, 0x5c, 0x4c, 0xa2, + 0xd0, 0x47, 0xf8, 0x30, 0x0a, 0xd1, 0xeb, 0x8e, 0xa4, 0x70, 0x4c, 0xf8, 0x23, 0x0a, 0x5f, 0x84, + 0xff, 0xe3, 0xc6, 0xe0, 0x1a, 0x2c, 0xe2, 0x7d, 0x54, 0x22, 0x98, 0xc6, 0x8b, 0x78, 0x81, 0x3f, + 0x44, 0x62, 0x87, 0x1d, 0x5d, 0xf8, 0xf1, 0x1f, 0x41, 0x01, 0x68, 0x77, 0xe0, 0x82, 0x97, 0x05, + 0x60, 0xc2, 0x1f, 0xf8, 0xe1, 0x52, 0x90, 0x10, 0x0c, 0x51, 0x48, 0x90, 0x84, 0x58, 0xfc, 0x61, + 0x02, 0x42, 0x01, 0xfc, 0xd0, 0x87, 0x40, 0x28, 0xe4, 0x16, 0x62, 0x03, 0x7d, 0xfc, 0xa0, 0x60, + 0x43, 0x03, 0x01, 0xa0, 0xa2, 0x8a, 0x07, 0x61, 0x14, 0x10, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, + 0x00, 0x06, 0x00, 0x2c, 0x0c, 0x00, 0x17, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x00, 0x08, 0xff, 0x00, + 0x0d, 0x08, 0x1c, 0x48, 0x90, 0x60, 0x80, 0x83, 0x08, 0x03, 0x14, 0x5c, 0xc8, 0xb0, 0xa1, 0x81, + 0x84, 0x10, 0x0f, 0x3a, 0x9c, 0x68, 0xf0, 0xa0, 0xb0, 0x25, 0x86, 0x12, 0x71, 0x49, 0x64, 0x68, + 0xc9, 0x32, 0x84, 0x14, 0x1d, 0x1e, 0x6c, 0x32, 0xcc, 0x40, 0x8b, 0x93, 0x2d, 0x06, 0xaa, 0xf8, + 0xf5, 0x51, 0x61, 0xc8, 0x82, 0x07, 0x9b, 0x71, 0x39, 0xb9, 0xe2, 0xc9, 0x92, 0x27, 0x05, 0x7b, + 0x30, 0x92, 0xf8, 0xd2, 0xc0, 0x08, 0x0a, 0x01, 0x9c, 0xf5, 0x68, 0xe1, 0x42, 0x50, 0x95, 0x2a, + 0x56, 0x0a, 0x2d, 0x54, 0x61, 0x8c, 0x67, 0x48, 0x10, 0x24, 0x9a, 0x0d, 0xe5, 0x42, 0x48, 0x58, + 0x21, 0x17, 0x0e, 0x55, 0x38, 0x73, 0x19, 0x32, 0x40, 0x30, 0x26, 0x02, 0x8b, 0x58, 0xe1, 0x12, + 0x72, 0x85, 0x30, 0xae, 0x13, 0x03, 0x18, 0x33, 0xd9, 0x82, 0xc9, 0x8a, 0x9e, 0x87, 0xd0, 0x3a, + 0x5c, 0xf4, 0x96, 0xc9, 0x92, 0x9e, 0x02, 0x6b, 0x04, 0xeb, 0xda, 0x4c, 0x60, 0xa1, 0x22, 0x78, + 0x05, 0x36, 0x93, 0xbb, 0x50, 0x6d, 0xe0, 0x82, 0x82, 0x08, 0x0f, 0x04, 0x60, 0x20, 0x43, 0xc9, + 0xc3, 0x03, 0x87, 0x29, 0x16, 0xb8, 0x21, 0x1e, 0x84, 0x5f, 0x90, 0x23, 0x4f, 0x1e, 0x18, 0xe0, + 0x50, 0x66, 0x81, 0x71, 0x43, 0xde, 0xfd, 0x3c, 0xe4, 0x25, 0xa1, 0xcf, 0x2a, 0x96, 0xbd, 0x0c, + 0x00, 0x16, 0x32, 0xa2, 0xcd, 0x05, 0x9d, 0x65, 0x96, 0xdd, 0x33, 0x00, 0xa2, 0xc3, 0x51, 0x60, + 0x33, 0x24, 0xdb, 0xb3, 0x35, 0xe4, 0xb7, 0x9f, 0xf1, 0xaa, 0x08, 0xd9, 0x37, 0xf8, 0x63, 0x87, + 0xc7, 0x3f, 0xab, 0xce, 0x4a, 0x25, 0xb8, 0x40, 0xdb, 0x0e, 0x5f, 0x3b, 0x17, 0x28, 0xc8, 0x61, + 0xf5, 0xe9, 0x06, 0x8a, 0x33, 0xd4, 0xee, 0xbc, 0xf3, 0x42, 0x17, 0x6b, 0xb1, 0x0f, 0x7f, 0x6c, + 0x42, 0xa5, 0x7c, 0xf9, 0x26, 0xe2, 0xd3, 0xbf, 0x14, 0x02, 0xa6, 0xbd, 0xfb, 0xf6, 0x42, 0xd2, + 0xef, 0x68, 0x38, 0x5f, 0x7c, 0x10, 0x36, 0xf8, 0xf3, 0xe3, 0xb7, 0x91, 0xde, 0x8f, 0x01, 0xfd, + 0xf9, 0xc5, 0x27, 0x5e, 0x00, 0x62, 0x2c, 0x14, 0x86, 0x6e, 0x81, 0x99, 0x10, 0x06, 0x41, 0x7b, + 0x64, 0xa1, 0xde, 0x73, 0x27, 0xec, 0xb0, 0x03, 0x10, 0x08, 0x3e, 0x68, 0xe1, 0x85, 0xe9, 0x01, + 0x90, 0xc5, 0x1f, 0x58, 0xe8, 0xf0, 0x83, 0x1f, 0x7e, 0xf4, 0x21, 0xa2, 0x10, 0x24, 0x96, 0x68, + 0xa2, 0x88, 0x7d, 0x80, 0xf8, 0x83, 0x0e, 0x58, 0xfc, 0x91, 0x05, 0x63, 0x02, 0x01, 0xf0, 0x43, + 0x1f, 0x27, 0x00, 0x81, 0x82, 0x0f, 0x3e, 0x24, 0xe1, 0x45, 0x4f, 0x5e, 0x24, 0x81, 0x23, 0x0a, + 0x40, 0x9c, 0xd0, 0xc7, 0x0f, 0x30, 0x76, 0x95, 0xd0, 0x43, 0x20, 0x85, 0x14, 0x10, 0x00, 0x21, + 0xf9, 0x04, 0x05, 0x03, 0x00, 0x10, 0x00, 0x2c, 0x0d, 0x00, 0x17, 0x00, 0x22, 0x00, 0x2c, 0x00, + 0x00, 0x08, 0xff, 0x00, 0x21, 0x08, 0x1c, 0x48, 0x90, 0x02, 0x08, 0x82, 0x08, 0x13, 0x2a, 0x5c, + 0x28, 0x30, 0xc3, 0x00, 0x86, 0x10, 0x23, 0x12, 0x0c, 0x40, 0xb1, 0x62, 0x45, 0x89, 0x11, 0x2d, + 0x02, 0x10, 0x46, 0x45, 0x18, 0x00, 0x8b, 0x18, 0x15, 0x56, 0x6c, 0x36, 0xac, 0x46, 0x8b, 0x16, + 0x10, 0x6a, 0x44, 0x71, 0xb6, 0x88, 0x62, 0x48, 0x0a, 0x1b, 0x04, 0x52, 0xa4, 0x82, 0xe8, 0xa4, + 0x4d, 0x82, 0x4c, 0x9a, 0xb9, 0xc4, 0x48, 0x02, 0x02, 0xc5, 0x22, 0x2b, 0x5a, 0xb8, 0x78, 0x52, + 0x44, 0xd8, 0x10, 0x84, 0x2a, 0x8c, 0xed, 0x94, 0x48, 0xb1, 0x99, 0x8b, 0x16, 0x4c, 0x08, 0x35, + 0x59, 0x12, 0x85, 0x8b, 0x42, 0x41, 0x4b, 0x21, 0x06, 0x10, 0xb6, 0x42, 0x60, 0x93, 0x21, 0x5d, + 0x19, 0xaa, 0xd0, 0xc9, 0x34, 0xc0, 0xaf, 0x93, 0x44, 0x94, 0x60, 0x24, 0x92, 0x55, 0x24, 0x21, + 0x15, 0x2d, 0x9c, 0x58, 0xc5, 0x38, 0x36, 0x40, 0x46, 0xb5, 0x10, 0x84, 0x45, 0x09, 0x09, 0xe1, + 0x90, 0xdd, 0x84, 0x00, 0x32, 0xc8, 0x44, 0x24, 0x70, 0x6e, 0xc8, 0x44, 0x7f, 0x11, 0x22, 0x88, + 0x27, 0xb3, 0x06, 0x5f, 0x82, 0x35, 0x4a, 0x44, 0x2c, 0xc1, 0xe2, 0xf1, 0x40, 0x16, 0x8b, 0x32, + 0x3a, 0xb6, 0x9c, 0x12, 0x40, 0xc6, 0xbd, 0x9c, 0x11, 0x25, 0x66, 0xb8, 0x84, 0x33, 0x04, 0x41, + 0x18, 0x85, 0xf5, 0xb0, 0xec, 0x42, 0x58, 0xc8, 0x43, 0x96, 0x87, 0xf1, 0x6d, 0xc2, 0x84, 0xaf, + 0x61, 0xd3, 0x10, 0x57, 0x10, 0xe2, 0xdc, 0x6c, 0x35, 0x44, 0xdf, 0xa6, 0x8d, 0x45, 0x84, 0x8d, + 0x3b, 0xd8, 0x6d, 0x84, 0x2b, 0x82, 0xe1, 0x16, 0x48, 0x5c, 0x61, 0x73, 0xdc, 0xcd, 0x18, 0x46, + 0x5f, 0x0e, 0x21, 0xd8, 0x66, 0x84, 0x3d, 0x9a, 0x50, 0x17, 0xb8, 0x04, 0xb8, 0x40, 0x15, 0x8c, + 0xb6, 0x0f, 0x83, 0x14, 0x66, 0xa5, 0x7c, 0x79, 0xd7, 0xe2, 0xd3, 0x43, 0xc4, 0xf2, 0xa5, 0xbd, + 0xfb, 0xf6, 0x7f, 0xc4, 0x9f, 0x60, 0x43, 0xbf, 0x3e, 0xfd, 0x13, 0xe2, 0xe3, 0x2b, 0xfc, 0x21, + 0x3e, 0x40, 0x9e, 0x84, 0x77, 0x8c, 0xb6, 0x1c, 0x10, 0x68, 0x10, 0x74, 0x86, 0x0f, 0xea, 0x05, + 0x00, 0x04, 0x1e, 0x66, 0x98, 0x71, 0x87, 0x0f, 0x02, 0x6e, 0x17, 0xc0, 0x1f, 0x7f, 0x44, 0xa8, + 0xde, 0x85, 0x18, 0x66, 0x28, 0x13, 0x00, 0x26, 0xfc, 0x81, 0x85, 0x0e, 0x3f, 0xe0, 0xe2, 0x87, + 0x1f, 0x10, 0xf4, 0x61, 0xe2, 0x89, 0x28, 0x42, 0x30, 0x22, 0x2e, 0x3f, 0xe8, 0x80, 0xc5, 0x1f, + 0x26, 0x7c, 0xe4, 0x13, 0x00, 0x3f, 0xf4, 0x21, 0xc4, 0x09, 0x40, 0xa0, 0xe0, 0x83, 0x0f, 0x49, + 0x78, 0xe1, 0xc5, 0x42, 0x3e, 0x26, 0xb1, 0x23, 0x0a, 0x40, 0x00, 0x21, 0x44, 0x1f, 0x3f, 0xc8, + 0xf8, 0x98, 0x85, 0x0c, 0x05, 0x04, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x07, 0x00, 0x0d, 0x00, 0x2c, + 0x0d, 0x00, 0x17, 0x00, 0x21, 0x00, 0x2c, 0x00, 0x00, 0x08, 0xff, 0x00, 0x1b, 0x08, 0x1c, 0x48, + 0x10, 0x04, 0x02, 0x82, 0x08, 0x13, 0x2a, 0x5c, 0xc8, 0xb0, 0xa1, 0xc3, 0x87, 0x10, 0x23, 0x0a, + 0x0c, 0x40, 0x91, 0xa2, 0xc4, 0x81, 0x00, 0x14, 0x56, 0xdc, 0x58, 0x51, 0xe2, 0x83, 0x07, 0x04, + 0x2b, 0x12, 0x5a, 0x72, 0xc8, 0xd0, 0x30, 0x41, 0xcd, 0x00, 0x58, 0x84, 0x98, 0x71, 0x20, 0xc5, + 0x66, 0x88, 0x5a, 0xc8, 0x6c, 0x31, 0x90, 0x89, 0xb3, 0x95, 0x12, 0x29, 0x1a, 0x53, 0xd1, 0x62, + 0xc5, 0x93, 0x21, 0x87, 0x10, 0x1a, 0x0a, 0x16, 0x20, 0x67, 0x80, 0x43, 0x32, 0x0b, 0x35, 0x19, + 0x69, 0x28, 0x21, 0x22, 0xa2, 0x11, 0x03, 0x0c, 0x69, 0xd0, 0x62, 0x89, 0xb0, 0xa6, 0x0c, 0x87, + 0x15, 0x7d, 0x18, 0x40, 0xd8, 0x8a, 0x16, 0x2a, 0xa4, 0x30, 0x79, 0xd8, 0x6c, 0x6b, 0xc3, 0xa3, + 0x0d, 0x56, 0x10, 0x89, 0x18, 0xc5, 0x2c, 0xc1, 0x0c, 0x04, 0x01, 0xd4, 0x68, 0x20, 0x48, 0x4a, + 0x44, 0x15, 0xc2, 0x12, 0x82, 0x88, 0x47, 0x90, 0x90, 0xc0, 0x15, 0x2b, 0x24, 0x16, 0x71, 0x9b, + 0x30, 0x80, 0xb3, 0x8b, 0x03, 0x87, 0x10, 0x4e, 0x78, 0x18, 0x71, 0x83, 0x25, 0x8b, 0x11, 0x5a, + 0x71, 0xdc, 0xa0, 0x71, 0x43, 0x61, 0x2a, 0x10, 0xab, 0xf0, 0xeb, 0x30, 0x00, 0x30, 0xc4, 0x5c, + 0x5a, 0x3a, 0xb4, 0x1c, 0x71, 0x49, 0x44, 0x00, 0x6b, 0x23, 0x32, 0x59, 0x44, 0xd9, 0x61, 0x8d, + 0x66, 0x8e, 0x5d, 0x3c, 0x2c, 0x42, 0xd9, 0x74, 0x43, 0x41, 0xad, 0x03, 0xa4, 0x56, 0xc8, 0x64, + 0x4a, 0xeb, 0xca, 0x0c, 0x6d, 0xb7, 0x0e, 0x26, 0x3b, 0x21, 0xde, 0xdf, 0x0d, 0x02, 0x20, 0x52, + 0x98, 0x28, 0xf2, 0x45, 0xd8, 0x8c, 0x91, 0x13, 0x2c, 0x42, 0x9d, 0x3a, 0x15, 0xe9, 0xd8, 0x3b, + 0x47, 0xea, 0xc2, 0xbd, 0x7b, 0x72, 0xe9, 0x00, 0xde, 0xb4, 0x6a, 0x19, 0x4f, 0xde, 0x8d, 0xf3, + 0x9c, 0x78, 0xc8, 0x93, 0xaf, 0x73, 0x5e, 0xe2, 0x8e, 0x34, 0xf0, 0xe3, 0x7f, 0xc9, 0x6e, 0x22, + 0x0f, 0xc1, 0x3b, 0x59, 0xb2, 0x37, 0x00, 0x70, 0x24, 0x4c, 0x98, 0x17, 0x25, 0xe8, 0xe7, 0x52, + 0x7b, 0x02, 0x16, 0x68, 0x20, 0x72, 0x01, 0x00, 0x60, 0xc2, 0x1f, 0x58, 0xe8, 0xf0, 0xc3, 0x0f, + 0x0d, 0xf8, 0x21, 0x61, 0x42, 0x12, 0xfa, 0xd1, 0xc0, 0x83, 0x3a, 0x60, 0xf1, 0x87, 0x09, 0x2a, + 0x4d, 0x54, 0x02, 0x2e, 0x7d, 0x08, 0x71, 0x02, 0x10, 0x28, 0x34, 0xe0, 0x83, 0x0f, 0x49, 0x24, + 0xe1, 0xc5, 0x8a, 0x2c, 0xa6, 0x78, 0x62, 0x03, 0x28, 0x00, 0x71, 0x82, 0x10, 0x7d, 0xe0, 0x52, + 0x02, 0x81, 0xc8, 0x05, 0x04, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x0a, 0x00, 0x2c, 0x0c, + 0x00, 0x15, 0x00, 0x21, 0x00, 0x2d, 0x00, 0x00, 0x08, 0xff, 0x00, 0x15, 0x08, 0x1c, 0x48, 0xb0, + 0xe0, 0xc0, 0x0c, 0x08, 0x0c, 0x2a, 0x5c, 0x38, 0x70, 0x00, 0x80, 0x82, 0x00, 0x06, 0x30, 0x9c, + 0x58, 0x10, 0x03, 0xc5, 0x8b, 0x18, 0x33, 0x6a, 0xdc, 0xc8, 0xb1, 0xe3, 0xc2, 0x08, 0x12, 0x3d, + 0x52, 0x8c, 0xf0, 0x6e, 0x61, 0x80, 0x00, 0xc1, 0x9a, 0x04, 0x3b, 0x19, 0x40, 0xe4, 0xc0, 0x00, + 0xc2, 0x0e, 0x25, 0x52, 0x21, 0x10, 0xca, 0xa1, 0x66, 0x2d, 0x45, 0x4e, 0x39, 0xd4, 0xa3, 0x45, + 0x0b, 0x17, 0x2e, 0x08, 0x46, 0x11, 0x96, 0x73, 0x63, 0x30, 0x27, 0x0a, 0x5c, 0x14, 0x52, 0x30, + 0x65, 0x48, 0x41, 0x2e, 0x84, 0x8a, 0x62, 0x9c, 0x82, 0x48, 0x01, 0x17, 0x2b, 0x54, 0x0a, 0x71, + 0x51, 0xc8, 0x45, 0x98, 0xc6, 0x00, 0x87, 0x14, 0xb4, 0xb0, 0x32, 0x24, 0x28, 0x43, 0x44, 0x52, + 0x27, 0x0a, 0xeb, 0xd9, 0x82, 0x09, 0x4d, 0x8a, 0xce, 0x14, 0x66, 0x28, 0x08, 0x56, 0x01, 0x11, + 0x8d, 0x51, 0xd2, 0x8e, 0x88, 0x37, 0xf0, 0x81, 0x82, 0x00, 0x89, 0xec, 0x2e, 0xc5, 0xd8, 0x23, + 0xd8, 0xc5, 0x26, 0x1d, 0x55, 0x2c, 0xbb, 0xe8, 0xb5, 0x23, 0xa1, 0xc3, 0x89, 0x1f, 0x53, 0x04, + 0x50, 0xa3, 0xa3, 0xe1, 0x8b, 0x4f, 0x38, 0xa2, 0xc5, 0xd8, 0x8c, 0x63, 0x5c, 0x8c, 0x01, 0xaa, + 0x6a, 0x7c, 0x98, 0x91, 0x4a, 0x46, 0xb3, 0x2e, 0x17, 0xaa, 0xf8, 0xec, 0x39, 0x35, 0xc5, 0x28, + 0x0c, 0x89, 0x90, 0xf6, 0xd8, 0x79, 0x21, 0x6b, 0x8f, 0x00, 0xa0, 0x28, 0x2c, 0xec, 0xda, 0x90, + 0x42, 0xd1, 0xae, 0x2b, 0x13, 0xec, 0xe1, 0xba, 0xa0, 0x94, 0xe3, 0xc5, 0x93, 0x5f, 0xfc, 0x32, + 0xa6, 0xb9, 0x73, 0x30, 0xc5, 0x03, 0xd8, 0x71, 0xf3, 0x86, 0x3a, 0x75, 0x3b, 0xc9, 0xc5, 0x18, + 0xdc, 0x93, 0xdc, 0xc6, 0x99, 0xef, 0xe0, 0x8f, 0x24, 0x42, 0x0f, 0x30, 0x86, 0xa0, 0x98, 0xd9, + 0xae, 0x03, 0xf8, 0xf8, 0xf2, 0xc5, 0x47, 0x5a, 0xe5, 0xf0, 0xe3, 0xcb, 0x9f, 0xff, 0xb2, 0x84, + 0x02, 0x66, 0x58, 0x74, 0x28, 0xf8, 0x31, 0xd0, 0x8f, 0xff, 0xff, 0x03, 0xf1, 0xa7, 0x03, 0x16, + 0xcc, 0x64, 0xb1, 0x88, 0x54, 0x01, 0xe8, 0x47, 0x10, 0x0a, 0x02, 0xf9, 0xa0, 0x40, 0x12, 0x10, + 0x46, 0xa8, 0x80, 0x83, 0x0a, 0x30, 0xd8, 0x9f, 0x0e, 0x01, 0x04, 0x04, 0x00, 0x21, 0xf9, 0x04, + 0x05, 0x04, 0x00, 0x07, 0x00, 0x2c, 0x0b, 0x00, 0x14, 0x00, 0x22, 0x00, 0x2e, 0x00, 0x00, 0x08, + 0xff, 0x00, 0x0f, 0x08, 0x1c, 0x48, 0xb0, 0x60, 0x41, 0x00, 0x06, 0x13, 0x2a, 0x5c, 0x28, 0x90, + 0x82, 0x07, 0x86, 0x10, 0x07, 0x02, 0x88, 0x97, 0x21, 0xa2, 0x45, 0x85, 0x0a, 0x2e, 0x6a, 0xdc, + 0xc8, 0xb1, 0xa3, 0xc7, 0x8f, 0x09, 0x23, 0x3c, 0x04, 0x09, 0x71, 0x04, 0xc3, 0x26, 0xcd, 0xac, + 0x58, 0x11, 0x46, 0xb2, 0xa0, 0x33, 0x44, 0x2a, 0x5a, 0x08, 0x6c, 0xc1, 0x64, 0x49, 0x30, 0x92, + 0xc2, 0x9c, 0xc8, 0x74, 0xe1, 0x84, 0x08, 0x41, 0x2e, 0xcd, 0x3e, 0x12, 0xe2, 0x72, 0x80, 0xcb, + 0x90, 0x2a, 0xc2, 0x96, 0x14, 0x54, 0x31, 0xa4, 0xa3, 0x30, 0xa2, 0x4e, 0x84, 0x15, 0x21, 0x22, + 0xd3, 0xa0, 0x8a, 0xa0, 0x1b, 0xa3, 0x08, 0x24, 0x54, 0xa8, 0xaa, 0x42, 0x2e, 0x37, 0x2f, 0x36, + 0xab, 0xea, 0xc2, 0xeb, 0x42, 0xa5, 0x06, 0x2b, 0x4a, 0x14, 0x68, 0xe8, 0x40, 0x94, 0x22, 0x17, + 0x99, 0x18, 0x1c, 0x11, 0x8f, 0x20, 0x89, 0x03, 0x8b, 0x6a, 0x1c, 0x60, 0xa2, 0xd5, 0x62, 0x0b, + 0x96, 0x11, 0x09, 0x79, 0xc4, 0x0a, 0x91, 0xf0, 0x46, 0xb8, 0x11, 0xa9, 0x0c, 0xb6, 0xb8, 0xa8, + 0x07, 0xc7, 0xbf, 0x17, 0xfb, 0x6a, 0xf4, 0x79, 0xd1, 0x19, 0xc7, 0xa6, 0x2d, 0x17, 0x2e, 0xca, + 0x9c, 0x50, 0xaf, 0x47, 0xcf, 0x9c, 0x43, 0x6b, 0x44, 0x24, 0xda, 0x20, 0xe2, 0xd2, 0x03, 0xf3, + 0x2a, 0x6c, 0x92, 0x59, 0x32, 0x41, 0x60, 0xa1, 0x5d, 0x2c, 0x45, 0x5d, 0xa4, 0x36, 0xea, 0xdb, + 0x10, 0xbf, 0x84, 0x9e, 0x73, 0xc0, 0x4d, 0xef, 0xde, 0xbc, 0x39, 0xef, 0x31, 0x98, 0x27, 0xf4, + 0x11, 0x34, 0xc8, 0x93, 0xef, 0x10, 0x2d, 0x86, 0x60, 0x18, 0xd4, 0x7c, 0xba, 0x74, 0x09, 0x82, + 0xbb, 0xba, 0xf5, 0xeb, 0x9c, 0x4d, 0x1c, 0xc0, 0x32, 0x10, 0x57, 0xc1, 0x3e, 0x7d, 0x0a, 0x7a, + 0x14, 0x17, 0x88, 0xe5, 0x8f, 0x76, 0x83, 0x3f, 0x14, 0x26, 0x19, 0xe8, 0xc5, 0xcb, 0xc0, 0xf5, + 0x06, 0xfb, 0xa4, 0x0f, 0x08, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x1f, 0x00, 0x2c, 0x0a, + 0x00, 0x14, 0x00, 0x23, 0x00, 0x2e, 0x00, 0x00, 0x08, 0xff, 0x00, 0x3f, 0x08, 0x1c, 0x48, 0xb0, + 0x60, 0xc1, 0x0c, 0x24, 0x0c, 0x2a, 0x5c, 0x48, 0x70, 0xc4, 0x08, 0x83, 0x0c, 0x18, 0x4a, 0x54, + 0x18, 0x71, 0xa2, 0xc5, 0x8b, 0x17, 0x3d, 0x60, 0xdc, 0x78, 0x90, 0xa3, 0xc7, 0x8b, 0x0f, 0x3f, + 0x62, 0x04, 0x00, 0xe1, 0x9d, 0xc8, 0x93, 0x04, 0x01, 0x34, 0x3b, 0x14, 0x85, 0x08, 0xa2, 0x43, + 0xce, 0x82, 0x9d, 0x04, 0x41, 0xd0, 0x19, 0x93, 0x16, 0x2d, 0x08, 0xb6, 0x58, 0xb1, 0x64, 0x11, + 0x4a, 0x00, 0x87, 0x72, 0x72, 0x51, 0x52, 0x44, 0x90, 0x4e, 0x44, 0xc2, 0x4e, 0x3e, 0x69, 0xe1, + 0x42, 0x50, 0x15, 0x2b, 0x82, 0x9c, 0x14, 0x6c, 0xc1, 0xa4, 0xc9, 0x47, 0x63, 0x4c, 0xa5, 0x50, + 0x71, 0x92, 0x53, 0x61, 0x8b, 0x28, 0x1e, 0x09, 0xf5, 0xf8, 0xe0, 0x62, 0x08, 0x97, 0x89, 0x2d, + 0x9c, 0x71, 0x1c, 0xf6, 0x81, 0x0b, 0xd7, 0x8b, 0x88, 0x14, 0x66, 0x28, 0x18, 0xe0, 0x43, 0xb0, + 0x1a, 0x1f, 0x04, 0x0d, 0xc1, 0xa8, 0x22, 0x29, 0xc1, 0x01, 0xf1, 0x08, 0x7a, 0xa8, 0xdb, 0x2c, + 0xa7, 0x0b, 0x17, 0x1b, 0xd5, 0x5e, 0x54, 0xcc, 0xb1, 0xc5, 0x12, 0x8c, 0x8c, 0x39, 0x3e, 0xbe, + 0x58, 0xe4, 0x63, 0x5a, 0x8c, 0xc2, 0xba, 0x6e, 0x54, 0x41, 0x68, 0x23, 0x30, 0x8f, 0x5c, 0x00, + 0x6c, 0xdc, 0x2b, 0x99, 0x23, 0x00, 0x22, 0x1c, 0x65, 0x72, 0xec, 0x8c, 0x12, 0x23, 0x62, 0x89, + 0x3d, 0x2a, 0x9f, 0x9c, 0xdc, 0x7a, 0x22, 0xea, 0x85, 0x4c, 0x7c, 0xb6, 0x8e, 0x5c, 0x90, 0x36, + 0xca, 0x60, 0xaf, 0xa7, 0xfa, 0xad, 0x1d, 0xd7, 0x20, 0x94, 0xda, 0x03, 0x65, 0x23, 0xdf, 0x38, + 0x7c, 0xb9, 0x73, 0x86, 0x91, 0xba, 0x48, 0x9f, 0xfe, 0xbc, 0x84, 0x9b, 0x36, 0xd8, 0xb3, 0xb7, + 0x11, 0xbd, 0xfc, 0x8e, 0x41, 0x3a, 0xcf, 0xc1, 0xa8, 0x4c, 0x19, 0x4f, 0xfe, 0xcb, 0x73, 0x13, + 0x78, 0x08, 0xda, 0xf9, 0xf3, 0xfc, 0x43, 0x89, 0x17, 0x7b, 0xf6, 0xec, 0x30, 0xd1, 0xbe, 0xbe, + 0xfd, 0xfb, 0xf8, 0x17, 0x02, 0xc8, 0x22, 0x50, 0xc7, 0x8f, 0x0f, 0x7e, 0x0c, 0xd4, 0x47, 0x1f, + 0x02, 0x0d, 0x38, 0x50, 0x80, 0x3f, 0xe8, 0x20, 0x50, 0x16, 0xdc, 0x11, 0xb4, 0x48, 0x80, 0x27, + 0x0c, 0xe4, 0xc3, 0x07, 0x49, 0x08, 0xe4, 0x85, 0x17, 0x16, 0x62, 0x48, 0xe1, 0x07, 0x13, 0x0a, + 0x14, 0xa1, 0x1f, 0x8b, 0x04, 0x04, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x07, 0x00, 0x0c, 0x00, 0x2c, + 0x09, 0x00, 0x13, 0x00, 0x24, 0x00, 0x2f, 0x00, 0x00, 0x08, 0xff, 0x00, 0x19, 0x08, 0x1c, 0x48, + 0xb0, 0xa0, 0x41, 0x06, 0x11, 0x0e, 0x2a, 0x5c, 0x28, 0x10, 0xc4, 0xc1, 0x08, 0x1e, 0x18, 0x4a, + 0x2c, 0x08, 0xc1, 0xe1, 0xc4, 0x8b, 0x18, 0x19, 0x52, 0xc8, 0xc8, 0x91, 0xe0, 0xba, 0x0c, 0x1d, + 0x43, 0x8a, 0x1c, 0xa9, 0xd0, 0x03, 0x09, 0x92, 0x17, 0x37, 0x4a, 0x5c, 0x56, 0x44, 0x8a, 0x33, + 0x42, 0x00, 0x50, 0x82, 0x1c, 0x18, 0x6c, 0x09, 0x97, 0x16, 0x38, 0x71, 0xae, 0x38, 0x24, 0x6c, + 0xa4, 0x4a, 0x06, 0xcd, 0x6e, 0x32, 0x70, 0x41, 0x84, 0xc9, 0xc0, 0x16, 0x35, 0x96, 0xa0, 0x74, + 0xe6, 0x82, 0x01, 0x93, 0x21, 0x55, 0xaa, 0x28, 0x25, 0xd8, 0xe2, 0xd0, 0x48, 0x67, 0x2a, 0x18, + 0x18, 0xaa, 0x52, 0xc4, 0x09, 0x4e, 0x83, 0x55, 0x43, 0x36, 0x59, 0xc1, 0x80, 0x4b, 0x95, 0x27, + 0x2d, 0x18, 0xaa, 0x68, 0xd6, 0xf1, 0x50, 0x5a, 0x15, 0x4c, 0xd2, 0x4a, 0x44, 0xc4, 0xb1, 0x49, + 0x0d, 0x06, 0x4a, 0x0a, 0x61, 0x6c, 0xb1, 0xcc, 0xe0, 0xcc, 0x81, 0x01, 0x04, 0x3a, 0x4b, 0xab, + 0x24, 0xca, 0xde, 0xa9, 0x03, 0x23, 0xc4, 0x23, 0xf8, 0x6e, 0xa6, 0x55, 0x06, 0x5f, 0x31, 0x3e, + 0xbe, 0x38, 0x4c, 0xa4, 0xa1, 0x8c, 0x93, 0x39, 0x56, 0xc6, 0x28, 0x48, 0x64, 0x66, 0x89, 0x6c, + 0x3b, 0xb6, 0x70, 0x96, 0x71, 0x11, 0x97, 0x8e, 0x35, 0x82, 0x71, 0xec, 0xcc, 0xf1, 0xf3, 0xc4, + 0x45, 0x46, 0x31, 0x9e, 0x46, 0x29, 0x71, 0x05, 0x21, 0xda, 0x0c, 0x5d, 0x84, 0xc6, 0xad, 0xd0, + 0x18, 0x6d, 0x00, 0xb1, 0x0f, 0xce, 0xa6, 0x8d, 0xd8, 0x20, 0x6b, 0xda, 0xc2, 0xb2, 0x1e, 0xbc, + 0xcd, 0x1b, 0xca, 0xc1, 0x15, 0x31, 0x79, 0x93, 0x36, 0x38, 0x84, 0x37, 0x41, 0x2b, 0xd8, 0xb1, + 0x5b, 0xdf, 0x3e, 0xb1, 0x04, 0x83, 0x48, 0xe0, 0xc3, 0x33, 0x58, 0xf0, 0xce, 0x1b, 0x0b, 0x9b, + 0xf3, 0xe8, 0xcf, 0x63, 0xd9, 0x0e, 0x27, 0xfd, 0x79, 0x37, 0xd1, 0x79, 0x8f, 0x59, 0x43, 0xbf, + 0xbe, 0x18, 0xee, 0x3a, 0xe2, 0x10, 0x7c, 0x83, 0x8b, 0x3b, 0x03, 0x2c, 0x5d, 0xcc, 0x21, 0x47, + 0x17, 0x3a, 0xf8, 0x67, 0xe0, 0x81, 0x08, 0x26, 0xa8, 0x90, 0x77, 0xcc, 0x0c, 0xd4, 0x9f, 0x40, + 0x7d, 0x18, 0x14, 0xa1, 0x40, 0x0f, 0x32, 0xd0, 0x20, 0x79, 0x05, 0x35, 0x28, 0x04, 0x03, 0x40, + 0xa0, 0x50, 0x90, 0x17, 0x02, 0xf1, 0x21, 0x22, 0x03, 0x20, 0x12, 0x84, 0x02, 0x10, 0x0c, 0x6c, + 0xc8, 0x4c, 0x40, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x1d, 0x00, 0x2c, 0x08, 0x00, 0x12, + 0x00, 0x26, 0x00, 0x30, 0x00, 0x00, 0x08, 0xff, 0x00, 0x3b, 0x08, 0x1c, 0x48, 0xb0, 0xa0, 0x41, + 0x81, 0x00, 0x0e, 0x2a, 0x5c, 0x38, 0x30, 0x03, 0x85, 0x83, 0x0c, 0x3c, 0x30, 0x9c, 0x68, 0x70, + 0x84, 0x44, 0x83, 0x0c, 0x28, 0x6a, 0xdc, 0xc8, 0xb1, 0xa3, 0x46, 0x10, 0x03, 0x3c, 0x16, 0x44, + 0xb0, 0xf1, 0xa1, 0xc8, 0x93, 0x13, 0x23, 0x64, 0x40, 0xd9, 0x11, 0x81, 0x3c, 0x96, 0x1d, 0x33, + 0x2e, 0x5c, 0xe4, 0xec, 0x10, 0x22, 0x22, 0x88, 0x86, 0x39, 0x6b, 0x02, 0x13, 0x43, 0x00, 0x82, + 0x4b, 0xb8, 0xb4, 0x28, 0xd8, 0xa2, 0xc6, 0xa1, 0x60, 0x07, 0x11, 0x44, 0x98, 0xc8, 0xe0, 0x67, + 0x87, 0x26, 0x51, 0x86, 0x72, 0x29, 0x24, 0x45, 0x09, 0xc1, 0x16, 0x4c, 0x08, 0x19, 0x34, 0xa9, + 0xb1, 0x09, 0x91, 0x16, 0x2e, 0x04, 0x4d, 0x21, 0xb4, 0x24, 0x8a, 0xc1, 0x15, 0x5a, 0x51, 0x46, + 0x5d, 0x61, 0x85, 0x4a, 0xd4, 0x85, 0x4c, 0x90, 0x8a, 0x1c, 0x32, 0x94, 0x88, 0x15, 0x2e, 0x13, + 0x5b, 0x1c, 0x12, 0xb9, 0x08, 0xef, 0x0a, 0x17, 0x43, 0x29, 0xf6, 0x10, 0xe6, 0xd1, 0xd9, 0x50, + 0x2b, 0x1d, 0x97, 0x1c, 0x5c, 0x49, 0xd0, 0x69, 0x87, 0xbd, 0x1d, 0x0c, 0xad, 0xe0, 0x68, 0xc8, + 0x20, 0x80, 0x77, 0x04, 0xdf, 0x01, 0x70, 0x8a, 0xe8, 0x24, 0x30, 0x8f, 0x89, 0x4e, 0x42, 0xf1, + 0xd8, 0x59, 0xe4, 0xe7, 0x8e, 0xc3, 0x4e, 0x56, 0xee, 0xe8, 0xec, 0xa4, 0xe2, 0x8e, 0x4d, 0x6a, + 0x78, 0x74, 0x41, 0xd8, 0x23, 0x64, 0x98, 0x1a, 0xe5, 0x72, 0xe4, 0x89, 0xbb, 0xb7, 0xe0, 0x89, + 0x3d, 0x7c, 0x1b, 0xf3, 0x4d, 0xb1, 0x2f, 0x43, 0xdd, 0xb8, 0x05, 0x2d, 0xbc, 0xdd, 0x3b, 0xed, + 0xc1, 0x66, 0xc4, 0x3b, 0x2c, 0x92, 0x6d, 0xb0, 0x06, 0x72, 0xdf, 0xc1, 0x0b, 0x32, 0x8a, 0xce, + 0xbd, 0x3b, 0xcb, 0x3f, 0x5f, 0x0e, 0x66, 0x60, 0xe1, 0xde, 0x47, 0xa1, 0x1f, 0xee, 0x25, 0xda, + 0x18, 0x64, 0xf3, 0xa7, 0x7b, 0x18, 0x36, 0xf0, 0xe3, 0xeb, 0xf1, 0xde, 0x67, 0x0d, 0x41, 0x35, + 0x27, 0xbc, 0x77, 0xe8, 0x13, 0x26, 0x4d, 0x9a, 0x3d, 0x42, 0xe8, 0x37, 0x10, 0x00, 0x09, 0x09, + 0x68, 0xe0, 0x81, 0x08, 0x0a, 0x64, 0xc2, 0x1f, 0x58, 0xe8, 0xf0, 0x03, 0x2e, 0xe7, 0xed, 0xa7, + 0x50, 0x79, 0x1d, 0xf8, 0x81, 0xcb, 0x0f, 0x3a, 0x60, 0xf1, 0x87, 0x09, 0x0b, 0xe9, 0x40, 0xa1, + 0x40, 0x3e, 0x74, 0x90, 0x44, 0x07, 0x5e, 0x74, 0xc0, 0x07, 0x1f, 0x03, 0xa1, 0x58, 0xe2, 0x88, + 0x21, 0x0e, 0xd4, 0x87, 0x0e, 0x1d, 0x04, 0x04, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x24, + 0x00, 0x2c, 0x08, 0x00, 0x11, 0x00, 0x26, 0x00, 0x31, 0x00, 0x00, 0x08, 0xff, 0x00, 0x49, 0x08, + 0x1c, 0x48, 0xb0, 0xa0, 0xc1, 0x83, 0x08, 0x13, 0x1a, 0xcc, 0x80, 0x90, 0x02, 0x04, 0x85, 0x10, + 0x0d, 0xbe, 0x1b, 0x10, 0xb1, 0xa2, 0xc5, 0x8b, 0x18, 0x33, 0x16, 0x64, 0xa0, 0x51, 0x23, 0x82, + 0x0d, 0x1d, 0x43, 0x86, 0x04, 0x00, 0x40, 0x24, 0x41, 0x10, 0x18, 0x4c, 0x5a, 0xcc, 0x90, 0x52, + 0xa5, 0x4b, 0x12, 0xc2, 0x96, 0x44, 0x81, 0xd2, 0xa2, 0x06, 0x22, 0x41, 0xcb, 0x44, 0x7a, 0x28, + 0x39, 0x70, 0xca, 0xa1, 0x1e, 0x07, 0x5b, 0xfc, 0x12, 0x76, 0x50, 0x81, 0x02, 0x85, 0x01, 0x02, + 0x0c, 0x14, 0x46, 0x44, 0x20, 0x91, 0x25, 0x56, 0x04, 0x15, 0x5c, 0xd1, 0xec, 0x20, 0xcf, 0x88, + 0xc2, 0xb8, 0x90, 0x58, 0x31, 0x84, 0x84, 0x15, 0x25, 0x4e, 0x0c, 0xf6, 0xa8, 0xda, 0x71, 0x51, + 0x53, 0x2e, 0x84, 0x08, 0x35, 0x4d, 0xb8, 0x82, 0xa8, 0x46, 0x63, 0x03, 0xa5, 0xb8, 0x88, 0x38, + 0x4c, 0x63, 0x93, 0x1a, 0x24, 0x98, 0xac, 0xb0, 0xa8, 0x22, 0x67, 0x47, 0x42, 0x4f, 0x2e, 0x4a, + 0x2d, 0x78, 0x55, 0xa0, 0xd2, 0x81, 0x75, 0x49, 0x38, 0x99, 0x6b, 0x31, 0x8a, 0xc1, 0x0d, 0xf1, + 0x06, 0x7a, 0x48, 0x3a, 0x10, 0x58, 0x47, 0x28, 0x19, 0x31, 0x6b, 0x84, 0x91, 0xd1, 0xb2, 0xc6, + 0x44, 0x2f, 0x43, 0x13, 0xec, 0x2a, 0x5a, 0x21, 0x50, 0x8c, 0xa7, 0x4b, 0xab, 0x5e, 0xcd, 0xba, + 0xb4, 0x8a, 0x97, 0x83, 0x5b, 0xcb, 0x4e, 0x08, 0x77, 0xf6, 0xc0, 0x16, 0xb6, 0x4d, 0xaf, 0x7e, + 0x9d, 0xbb, 0xb7, 0x6f, 0x81, 0x5f, 0x82, 0x0b, 0xff, 0xdd, 0xa7, 0xb4, 0x09, 0x36, 0x07, 0xb1, + 0xa8, 0xde, 0x63, 0x30, 0xcf, 0x6a, 0x21, 0x6a, 0x08, 0xa6, 0x01, 0xd2, 0x7a, 0xcf, 0x19, 0x33, + 0x7a, 0x4e, 0xfc, 0xde, 0xce, 0xbd, 0x7b, 0xc7, 0xe2, 0xa2, 0x7f, 0x28, 0x08, 0xe4, 0x63, 0x90, + 0x7c, 0x42, 0xf1, 0x01, 0x01, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, 0x00, 0x26, 0x00, 0x2c, 0x07, + 0x00, 0x11, 0x00, 0x28, 0x00, 0x14, 0x00, 0x00, 0x08, 0x67, 0x00, 0x4d, 0x08, 0x1c, 0x48, 0xb0, + 0xa0, 0x41, 0x81, 0x14, 0x1e, 0x1c, 0x5c, 0xc8, 0x90, 0xe0, 0x88, 0x0d, 0x0b, 0x19, 0x78, 0x68, + 0x48, 0xf1, 0x20, 0x80, 0x8a, 0x18, 0x33, 0x6a, 0xdc, 0xc8, 0xb1, 0x21, 0x80, 0x77, 0x14, 0x3a, + 0x56, 0x8c, 0x70, 0x50, 0xa1, 0xc8, 0x81, 0x24, 0x42, 0x9e, 0x34, 0xa8, 0x72, 0x61, 0x80, 0x00, + 0x2b, 0x0b, 0x52, 0x90, 0x17, 0xb3, 0xa6, 0x4d, 0x8d, 0x19, 0x6e, 0x9a, 0x60, 0x40, 0x71, 0x00, + 0x00, 0x98, 0x36, 0xdf, 0x5d, 0xd4, 0x49, 0xb4, 0xa8, 0xd1, 0xa3, 0x48, 0x93, 0x2a, 0x2d, 0x98, + 0x53, 0xe0, 0x4f, 0xa4, 0x24, 0x22, 0x44, 0x88, 0x37, 0xf0, 0x01, 0x50, 0xa3, 0x14, 0x32, 0x04, + 0x04, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x06, 0x00, 0x24, 0x00, 0x2c, 0x07, 0x00, 0x10, 0x00, 0x28, + 0x00, 0x15, 0x00, 0x00, 0x06, 0x54, 0x40, 0x92, 0x70, 0x48, 0x2c, 0x1a, 0x85, 0x94, 0xc7, 0x71, + 0xc9, 0x24, 0x62, 0x28, 0x4b, 0x86, 0xa7, 0x49, 0x3d, 0x22, 0x94, 0xd5, 0xac, 0x76, 0xcb, 0xed, + 0x32, 0x47, 0x13, 0x2f, 0x95, 0x02, 0x59, 0x0e, 0xc4, 0xc3, 0x47, 0xa0, 0x18, 0x58, 0xa3, 0x9b, + 0xed, 0x77, 0x71, 0xc2, 0x90, 0x73, 0x41, 0xf6, 0xbc, 0x9e, 0xfa, 0xa0, 0xb8, 0xf3, 0x03, 0x78, + 0x4c, 0x71, 0x7b, 0x67, 0x7b, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x44, 0x00, 0x43, 0x00, + 0x7f, 0x8a, 0x08, 0x08, 0x43, 0x08, 0x93, 0x89, 0x0c, 0x41, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, + 0x00, 0x25, 0x00, 0x2c, 0x07, 0x00, 0x0f, 0x00, 0x29, 0x00, 0x16, 0x00, 0x00, 0x08, 0x67, 0x00, + 0x4b, 0x08, 0x1c, 0x48, 0xb0, 0xa0, 0x41, 0x81, 0x14, 0x36, 0x1c, 0x5c, 0xc8, 0x90, 0x60, 0x86, + 0x11, 0x0c, 0x19, 0x78, 0x68, 0x48, 0xf1, 0x60, 0x86, 0x75, 0x15, 0x33, 0x6a, 0xdc, 0xc8, 0xb1, + 0xa3, 0x46, 0x04, 0x00, 0x3c, 0x8a, 0x14, 0x38, 0x71, 0xa4, 0xc0, 0x01, 0x06, 0x01, 0x40, 0x34, + 0xc9, 0x30, 0x40, 0x00, 0x96, 0x30, 0x1b, 0xca, 0xab, 0x18, 0xe1, 0x41, 0x4c, 0x8e, 0x24, 0x6e, + 0x8a, 0xa4, 0xa0, 0x33, 0x23, 0x05, 0x97, 0x3d, 0x4b, 0xbc, 0x5b, 0x19, 0x94, 0x21, 0xd1, 0xa2, + 0x48, 0x93, 0x2a, 0x5d, 0xca, 0xb4, 0x69, 0x4c, 0x00, 0x2f, 0x97, 0x3e, 0x48, 0x28, 0x90, 0x44, + 0x54, 0xa5, 0x03, 0x00, 0x04, 0x04, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x26, 0x00, 0x2c, + 0x09, 0x00, 0x0e, 0x00, 0x28, 0x00, 0x17, 0x00, 0x00, 0x06, 0x57, 0x40, 0x93, 0x70, 0x48, 0x2c, + 0x1a, 0x29, 0x0a, 0xa3, 0x72, 0xc9, 0x5c, 0x32, 0x3c, 0xcd, 0xa8, 0x11, 0x81, 0x90, 0x5a, 0xad, + 0x94, 0xab, 0x76, 0xab, 0xa5, 0x44, 0xb8, 0xe0, 0x21, 0x04, 0x14, 0x36, 0x3d, 0x94, 0x19, 0x52, + 0xb9, 0x09, 0x08, 0x18, 0x03, 0xee, 0xb5, 0x7c, 0x1e, 0x3e, 0x37, 0x11, 0x10, 0xba, 0x30, 0x6e, + 0x05, 0xe8, 0xad, 0x08, 0x14, 0x7c, 0x7f, 0x26, 0x0d, 0x7e, 0x45, 0x83, 0x84, 0x10, 0x19, 0x84, + 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x42, 0x6d, 0x95, 0x1b, 0x26, 0x08, 0x89, 0x90, + 0x5f, 0x41, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x26, 0x00, 0x2c, 0x08, 0x00, 0x0d, 0x00, + 0x29, 0x00, 0x18, 0x00, 0x00, 0x08, 0x6e, 0x00, 0x4d, 0x08, 0x1c, 0x48, 0xb0, 0xa0, 0x41, 0x13, + 0x19, 0x48, 0x1c, 0x5c, 0xc8, 0xb0, 0x61, 0x41, 0x06, 0x1e, 0x1c, 0x4a, 0x2c, 0x08, 0x02, 0xc0, + 0xc4, 0x8b, 0x12, 0x37, 0x60, 0xdc, 0xc8, 0xf1, 0x22, 0x83, 0x8e, 0x20, 0x05, 0x32, 0x90, 0x67, + 0x11, 0x24, 0x85, 0x83, 0x09, 0x19, 0x2a, 0x04, 0x19, 0x20, 0xc0, 0xc1, 0x96, 0x21, 0x1d, 0xba, + 0x8c, 0x49, 0x53, 0xe2, 0xca, 0x86, 0x25, 0x6b, 0x0a, 0xa4, 0x90, 0xc1, 0xa1, 0x46, 0x9d, 0x1d, + 0x23, 0xcc, 0x04, 0xea, 0x10, 0x00, 0x4c, 0xa2, 0x04, 0x49, 0xbc, 0x43, 0x3a, 0xf1, 0x23, 0xd3, + 0xa7, 0x50, 0xa3, 0x4a, 0x9d, 0x4a, 0xb5, 0xaa, 0xd5, 0xa1, 0x54, 0x1f, 0x20, 0x78, 0x80, 0x75, + 0xea, 0x00, 0x06, 0x01, 0x01, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x07, 0x00, 0x24, 0x00, 0x2c, 0x07, + 0x00, 0x0c, 0x00, 0x2b, 0x00, 0x19, 0x00, 0x00, 0x08, 0x69, 0x00, 0x49, 0x08, 0x1c, 0x48, 0xb0, + 0xa0, 0xc1, 0x81, 0x0c, 0x0e, 0x2a, 0x5c, 0xc8, 0xf0, 0x20, 0x03, 0x0f, 0x0d, 0x23, 0x4a, 0x9c, + 0x48, 0x51, 0x21, 0x84, 0x8a, 0x18, 0x33, 0x6a, 0xdc, 0x88, 0x11, 0xc3, 0x04, 0x8e, 0x08, 0x14, + 0x02, 0x18, 0xc1, 0x30, 0xc3, 0xc6, 0x00, 0x28, 0x0f, 0xa6, 0xe4, 0xc8, 0xb2, 0xa5, 0xcb, 0x97, + 0x1a, 0x33, 0x7c, 0x84, 0x39, 0x71, 0xa5, 0x44, 0x0a, 0x34, 0x27, 0x82, 0x18, 0x10, 0x20, 0x67, + 0xc1, 0x0c, 0x24, 0x0b, 0xf6, 0xf4, 0x49, 0x50, 0x5e, 0x48, 0xa2, 0x48, 0x33, 0x36, 0x48, 0xca, + 0xd0, 0x26, 0xd3, 0xa7, 0x50, 0xa3, 0x4a, 0x9d, 0x2a, 0x94, 0xea, 0x83, 0x8f, 0x03, 0xa8, 0x0e, + 0xa0, 0x10, 0x10, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x25, 0x00, 0x2c, 0x07, 0x00, 0x0c, + 0x00, 0x2c, 0x00, 0x19, 0x00, 0x00, 0x06, 0x56, 0xc0, 0x92, 0x70, 0x48, 0x2c, 0x1a, 0x85, 0x14, + 0xcc, 0x71, 0xc9, 0x6c, 0x3a, 0x9f, 0xd0, 0xa8, 0x74, 0x3a, 0x04, 0x78, 0xa8, 0xd8, 0xac, 0x33, + 0xb3, 0xd1, 0x0a, 0x01, 0xcb, 0x40, 0xa4, 0xa9, 0xf0, 0x02, 0x02, 0x61, 0xb4, 0x77, 0xcd, 0x6e, + 0xbb, 0xd7, 0x01, 0xd2, 0xf8, 0x1d, 0x0d, 0xa8, 0xe9, 0x78, 0xe1, 0xe6, 0x9e, 0x6f, 0x52, 0xf8, + 0x7d, 0x25, 0x18, 0x65, 0x81, 0x4d, 0x18, 0x19, 0x85, 0x89, 0x53, 0x01, 0x5d, 0x8a, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x4d, 0x80, 0x8f, 0x0f, 0x20, 0x76, 0x93, 0x03, 0x14, 0x41, 0x00, 0x21, + 0xf9, 0x04, 0x05, 0x04, 0x00, 0x1c, 0x00, 0x2c, 0x0a, 0x00, 0x0b, 0x00, 0x2a, 0x00, 0x1a, 0x00, + 0x00, 0x08, 0x69, 0x00, 0x39, 0x08, 0x1c, 0x48, 0xb0, 0xa0, 0xc1, 0x0c, 0x06, 0x13, 0x2a, 0x5c, + 0xc8, 0x90, 0x81, 0x07, 0x86, 0x10, 0x23, 0x4a, 0x9c, 0x48, 0xb1, 0xa2, 0xc5, 0x8b, 0x0c, 0x23, + 0x20, 0xc4, 0xa8, 0x30, 0xc0, 0x46, 0x85, 0xf2, 0x28, 0x58, 0xcc, 0x40, 0x21, 0xc0, 0x42, 0x93, + 0x1c, 0x4f, 0xa6, 0x5c, 0xc9, 0xb2, 0x65, 0x44, 0x00, 0x1b, 0x5c, 0x52, 0x0c, 0x80, 0x52, 0xa6, + 0xcd, 0x9b, 0x12, 0x47, 0xe0, 0x54, 0x48, 0x41, 0x24, 0x87, 0x9a, 0x3b, 0x09, 0xca, 0x1b, 0x10, + 0xb4, 0xe8, 0xc4, 0x8f, 0x46, 0x0b, 0x02, 0x20, 0x9a, 0xb4, 0xa9, 0xd3, 0xa7, 0x50, 0xa3, 0x4a, + 0x35, 0x1a, 0x81, 0xe0, 0x03, 0x10, 0x4d, 0x91, 0x0e, 0xa0, 0x10, 0x10, 0x00, 0x21, 0xf9, 0x04, + 0x05, 0x03, 0x00, 0x1d, 0x00, 0x2c, 0x0a, 0x00, 0x0b, 0x00, 0x2b, 0x00, 0x1a, 0x00, 0x00, 0x06, + 0x54, 0xc0, 0x8e, 0x70, 0x48, 0x2c, 0x1a, 0x19, 0xc6, 0xa4, 0x72, 0xc9, 0x6c, 0x3a, 0x9f, 0xd0, + 0xa8, 0xd4, 0x48, 0x99, 0x5a, 0x93, 0x01, 0x00, 0x13, 0x04, 0xb2, 0x92, 0x32, 0xcc, 0xc0, 0xb5, + 0x19, 0x10, 0x8f, 0xcf, 0xe8, 0xb4, 0x75, 0xd4, 0x50, 0x4b, 0xcb, 0xee, 0xb8, 0x7c, 0x6c, 0x9e, + 0x3b, 0xe1, 0xf6, 0x24, 0xe3, 0x01, 0xce, 0x33, 0x49, 0x7e, 0x81, 0x6f, 0x11, 0x0f, 0x82, 0x58, + 0x75, 0x86, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8c, 0x01, 0x08, 0x44, 0x0f, 0x6d, 0x86, 0x01, + 0x48, 0x43, 0x03, 0x14, 0x41, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x07, 0x00, 0x12, 0x00, 0x2c, 0x13, + 0x00, 0x0d, 0x00, 0x23, 0x00, 0x18, 0x00, 0x00, 0x06, 0x47, 0x40, 0x89, 0x70, 0x48, 0x2c, 0x1a, + 0x8f, 0xc8, 0xa4, 0x24, 0x90, 0x51, 0x4a, 0x9a, 0xca, 0x40, 0x54, 0x49, 0xda, 0x28, 0x29, 0x52, + 0xa7, 0x76, 0xcb, 0xed, 0x7a, 0x8f, 0x01, 0xeb, 0xb7, 0x1b, 0xc8, 0x8e, 0xcf, 0xe8, 0xb4, 0x7a, + 0xcd, 0x06, 0xb0, 0x93, 0x10, 0xf1, 0x7b, 0xee, 0x8c, 0xd0, 0x8d, 0x4c, 0xca, 0x7d, 0xcf, 0xef, + 0xfb, 0xff, 0x80, 0x81, 0x73, 0x01, 0x18, 0x44, 0x0f, 0x03, 0x74, 0x01, 0x76, 0x43, 0x03, 0x19, + 0x41, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x1d, 0x00, 0x2c, 0x12, 0x00, 0x0c, 0x00, 0x25, + 0x00, 0x19, 0x00, 0x00, 0x05, 0x42, 0x60, 0x27, 0x8e, 0x64, 0x69, 0x9e, 0x68, 0x4a, 0x02, 0x91, + 0xea, 0xbe, 0x63, 0xe0, 0x52, 0x6e, 0x20, 0xc3, 0xe9, 0xf3, 0xde, 0x78, 0xef, 0xbb, 0x03, 0xc4, + 0x6f, 0x68, 0x1b, 0x1a, 0x8f, 0xc8, 0xa4, 0x72, 0xc9, 0x44, 0x0e, 0x00, 0xcd, 0x94, 0x27, 0x13, + 0xad, 0xee, 0x18, 0x0a, 0x6b, 0xa9, 0xa8, 0xed, 0x7a, 0xbf, 0xe0, 0xb0, 0x78, 0xdc, 0x0c, 0x60, + 0x48, 0x0f, 0x5a, 0x35, 0xd0, 0x1a, 0x3d, 0x43, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x0c, + 0x00, 0x2c, 0x1e, 0x00, 0x0c, 0x00, 0x19, 0x00, 0x19, 0x00, 0x00, 0x06, 0x38, 0x40, 0xca, 0x86, + 0x41, 0x2c, 0x1a, 0x8f, 0x48, 0x62, 0x20, 0x90, 0x6c, 0x3a, 0x9f, 0xd0, 0xa8, 0x74, 0x1a, 0xf0, + 0x4c, 0x9d, 0xcb, 0xab, 0x76, 0xcb, 0xed, 0x7a, 0xbf, 0xe0, 0xe7, 0x28, 0x12, 0x66, 0x90, 0xb9, + 0x81, 0xf3, 0x77, 0x09, 0x28, 0xbb, 0xdf, 0xf0, 0xb8, 0x7c, 0x4e, 0x2f, 0x06, 0x10, 0xc6, 0xe1, + 0x36, 0x6d, 0x8c, 0x04, 0x01, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, 0x00, 0x0e, 0x00, 0x2c, 0x1e, + 0x00, 0x0b, 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x00, 0x06, 0x39, 0x40, 0x47, 0xc0, 0x41, 0x2c, 0x1a, + 0x8f, 0xc8, 0x62, 0x60, 0x98, 0x6c, 0x3a, 0x9f, 0xd0, 0xa8, 0x74, 0x5a, 0x04, 0x51, 0x9f, 0x94, + 0xcc, 0x75, 0xcb, 0xed, 0x7a, 0xbf, 0xe0, 0xb0, 0xf8, 0x39, 0x02, 0x53, 0xc4, 0x01, 0x86, 0x35, + 0xcc, 0x1c, 0xbb, 0xdf, 0xf0, 0xb8, 0x7c, 0x4e, 0x57, 0xae, 0x1d, 0x8f, 0x32, 0x37, 0x70, 0x26, + 0x0e, 0x32, 0x41, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x06, 0x00, 0x0f, 0x00, 0x2c, 0x2a, 0x00, 0x0e, + 0x00, 0x0e, 0x00, 0x17, 0x00, 0x00, 0x06, 0x29, 0xc0, 0x87, 0x70, 0x48, 0x24, 0x46, 0x36, 0xc5, + 0xe4, 0x23, 0x10, 0x50, 0x3a, 0x9f, 0xd0, 0xa8, 0x74, 0x4a, 0xad, 0x5a, 0x85, 0x01, 0x92, 0x34, + 0x00, 0xb8, 0x7a, 0xbf, 0xe0, 0xb0, 0x58, 0x1c, 0x18, 0x0c, 0x15, 0xce, 0x40, 0x66, 0x18, 0x09, + 0x02, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, 0x00, 0x0c, 0x00, 0x2c, 0x2a, 0x00, 0x0e, 0x00, 0x0e, + 0x00, 0x17, 0x00, 0x00, 0x05, 0x22, 0x20, 0x13, 0x30, 0x64, 0x69, 0x92, 0xc1, 0x78, 0xae, 0x6c, + 0xeb, 0xbe, 0x70, 0x2c, 0xcf, 0x74, 0x7d, 0x06, 0x58, 0xac, 0xda, 0x7c, 0xef, 0xff, 0xc0, 0x5f, + 0x20, 0x42, 0xda, 0xb8, 0x02, 0x99, 0x52, 0x08, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x08, + 0x00, 0x2c, 0x33, 0x00, 0x17, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x05, 0x11, 0x60, 0x84, 0x8c, + 0x08, 0x43, 0x9e, 0x68, 0xaa, 0xae, 0x6c, 0x4b, 0x06, 0xe2, 0x18, 0x64, 0x63, 0x08, 0x00, 0x21, + 0xf9, 0x04, 0x05, 0x03, 0x00, 0x10, 0x00, 0x2c, 0x33, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0e, 0x00, + 0x00, 0x06, 0x12, 0xc0, 0x40, 0x00, 0x02, 0x11, 0x12, 0x8f, 0xc8, 0xa4, 0x72, 0xc9, 0x6c, 0x06, + 0x48, 0xc4, 0x00, 0x25, 0x08, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x07, 0x00, 0x0b, 0x00, 0x2c, 0x36, + 0x00, 0x23, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x05, 0x05, 0x60, 0xb6, 0x04, 0x4b, 0x08, 0x00, + 0x21, 0xf9, 0x04, 0x05, 0xed, 0x00, 0x03, 0x00, 0x2c, 0x36, 0x00, 0x23, 0x00, 0x02, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x03, 0x4c, 0x16, 0x05, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, + 0x2c, 0x13, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x23, 0x00, 0x00, 0x08, 0xff, 0x00, 0x07, 0x08, 0x1c, + 0x48, 0xb0, 0xa0, 0xc1, 0x83, 0x00, 0x9a, 0x1d, 0x4a, 0x54, 0xc3, 0x45, 0xa2, 0x61, 0xce, 0x16, + 0x1d, 0x1c, 0x18, 0xa0, 0x19, 0xb0, 0x89, 0x5c, 0x9c, 0x4d, 0x0c, 0x60, 0x4c, 0xc5, 0x00, 0x17, + 0x85, 0xa4, 0x10, 0x8a, 0x42, 0xb0, 0xc5, 0xb0, 0x29, 0x05, 0x03, 0x1c, 0x1a, 0xd0, 0xc2, 0x90, + 0x30, 0x2a, 0x4b, 0x0a, 0x71, 0x29, 0xd8, 0x22, 0x0a, 0x00, 0x8a, 0x1a, 0x5b, 0x14, 0x9a, 0xa2, + 0xc4, 0xc5, 0x44, 0x96, 0x2b, 0x05, 0x0a, 0x5b, 0xc1, 0xb2, 0x10, 0xc9, 0x9f, 0x02, 0x7b, 0x10, + 0x1a, 0xa0, 0xf2, 0xe3, 0x13, 0xa4, 0x25, 0x57, 0x2e, 0xaa, 0x31, 0xc0, 0x10, 0x15, 0xa8, 0x04, + 0x6b, 0x2c, 0x6a, 0x26, 0x50, 0x05, 0x51, 0xac, 0x02, 0x5b, 0x34, 0xd3, 0x08, 0x96, 0xa6, 0x33, + 0xb2, 0x65, 0x07, 0xb6, 0x38, 0x9b, 0xb6, 0xa4, 0xb3, 0xa5, 0x6d, 0xc3, 0x12, 0x9a, 0x1a, 0x77, + 0x00, 0x17, 0x00, 0x4d, 0xdb, 0xb6, 0x08, 0x2a, 0x8c, 0x6a, 0xda, 0x1a, 0xc2, 0x04, 0x06, 0x58, + 0x92, 0xb6, 0x05, 0x61, 0x8a, 0xc3, 0xc0, 0xee, 0x35, 0x38, 0x05, 0x11, 0x54, 0x93, 0x13, 0x09, + 0x79, 0xfc, 0x49, 0x44, 0xe2, 0xc1, 0x00, 0xbf, 0x7e, 0xaa, 0xe0, 0xfa, 0x13, 0xad, 0x41, 0xc7, + 0x48, 0x9b, 0x4c, 0x36, 0xe8, 0xf9, 0xf2, 0x45, 0x83, 0x2a, 0xae, 0x42, 0x6d, 0xf6, 0x55, 0x6d, + 0x50, 0xac, 0xc1, 0x08, 0xc1, 0x2d, 0x02, 0xb7, 0x6e, 0x5b, 0x1d, 0x62, 0xc6, 0x8c, 0x89, 0x34, + 0x00, 0x0c, 0x98, 0x17, 0x25, 0xb0, 0x9e, 0x68, 0xd3, 0xc6, 0x20, 0x2e, 0xac, 0x58, 0x0e, 0xb6, + 0x31, 0x01, 0x16, 0xcf, 0x9a, 0x35, 0x04, 0xc3, 0x94, 0x45, 0x81, 0x86, 0x60, 0x25, 0x21, 0x69, + 0x81, 0xe4, 0x19, 0x80, 0x66, 0x0f, 0xf6, 0xb8, 0xc1, 0x91, 0x06, 0x01, 0x04, 0x00, 0x21, 0xf9, + 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x2c, 0x13, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x23, 0x00, 0x00, + 0x08, 0xff, 0x00, 0x07, 0x08, 0x1c, 0x48, 0xb0, 0xa0, 0xc1, 0x83, 0x03, 0x02, 0x04, 0x20, 0xa4, + 0x04, 0x11, 0x94, 0x1a, 0x44, 0x86, 0x39, 0x5b, 0x84, 0x70, 0x60, 0x00, 0x2a, 0x51, 0x0e, 0xb6, + 0xe0, 0xb2, 0xa4, 0x62, 0x00, 0x67, 0x35, 0x5a, 0xb4, 0x70, 0xb2, 0xc4, 0x8a, 0x15, 0x17, 0x03, + 0x5b, 0x44, 0x09, 0x66, 0x30, 0x00, 0xa3, 0x01, 0x2d, 0x88, 0x58, 0xa9, 0x22, 0x45, 0xc9, 0x13, + 0x15, 0x04, 0x5b, 0x20, 0x62, 0x69, 0xb1, 0x99, 0x8a, 0x8d, 0x55, 0x86, 0xac, 0xa8, 0xd8, 0x62, + 0x98, 0xc5, 0x60, 0x5c, 0x60, 0xba, 0x70, 0x52, 0x51, 0x60, 0x0b, 0x15, 0xcd, 0x04, 0x06, 0x58, + 0x22, 0x52, 0x0a, 0xd3, 0xa6, 0x4e, 0x33, 0x2a, 0x04, 0xf6, 0x74, 0x48, 0x52, 0xac, 0x30, 0x55, + 0x08, 0x0b, 0x20, 0x0c, 0x66, 0x0b, 0xb0, 0x39, 0x9d, 0x05, 0x88, 0x8a, 0xb6, 0x60, 0x8b, 0x25, + 0x6b, 0xdb, 0xba, 0x15, 0x14, 0x57, 0x6e, 0x4a, 0xb8, 0xc2, 0x70, 0xda, 0x85, 0xa9, 0x36, 0x00, + 0x93, 0xbd, 0x4f, 0x9b, 0x24, 0xec, 0x68, 0xb7, 0xc5, 0x2f, 0xa9, 0x48, 0xe5, 0x8a, 0x64, 0x9b, + 0xd0, 0x99, 0xe2, 0x43, 0x05, 0x03, 0x40, 0x06, 0xab, 0x72, 0x4a, 0xe4, 0xc4, 0x4d, 0x5b, 0x18, + 0xb2, 0xdc, 0x52, 0x50, 0x66, 0x43, 0x00, 0x2a, 0xe6, 0x45, 0xd8, 0x82, 0x09, 0x4f, 0x84, 0x01, + 0xa0, 0x20, 0x84, 0x8a, 0x35, 0x80, 0x21, 0x84, 0x44, 0xd0, 0x7a, 0x36, 0xf8, 0x16, 0xed, 0xa2, + 0x61, 0x7a, 0x07, 0x32, 0x11, 0x8c, 0x36, 0xc0, 0x14, 0x61, 0x84, 0x8a, 0x0c, 0x71, 0xc6, 0x7b, + 0xaf, 0x71, 0x20, 0x72, 0xf0, 0x84, 0x19, 0x03, 0xe6, 0x08, 0x1f, 0x21, 0x01, 0xd0, 0x7a, 0x61, + 0x33, 0xa0, 0x4d, 0x41, 0x13, 0x68, 0x51, 0x1c, 0xa4, 0x13, 0x1d, 0xac, 0x09, 0x36, 0x68, 0x2a, + 0x11, 0x14, 0x8c, 0x24, 0xd7, 0x86, 0x19, 0x82, 0x71, 0xb0, 0xd8, 0xe5, 0x73, 0x67, 0x80, 0x1a, + 0x31, 0x3a, 0x8c, 0xb7, 0x0d, 0x08, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x07, 0x00, 0x03, 0x00, 0x2c, + 0x14, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x23, 0x00, 0x00, 0x08, 0xff, 0x00, 0x07, 0x08, 0x1c, 0x48, + 0xb0, 0xa0, 0xc1, 0x83, 0x02, 0x03, 0x00, 0x68, 0x26, 0xc8, 0x10, 0xa2, 0x28, 0x4a, 0x8a, 0x2c, + 0x42, 0x38, 0x30, 0x40, 0x00, 0x67, 0x4c, 0x0c, 0xb6, 0xe0, 0xb2, 0x64, 0xe2, 0xc1, 0x00, 0x4d, + 0xa2, 0x0c, 0x68, 0xe1, 0xc2, 0xd0, 0x12, 0x29, 0x4b, 0x54, 0x08, 0x6c, 0x41, 0x44, 0x98, 0xc1, + 0x00, 0xc2, 0x98, 0xb4, 0x50, 0x51, 0x48, 0x98, 0xb0, 0x22, 0x4b, 0x52, 0x0e, 0xdc, 0xe8, 0xb2, + 0xe2, 0x22, 0x22, 0x2d, 0x5a, 0x44, 0x69, 0xf2, 0xc4, 0x05, 0x42, 0x96, 0x1e, 0x2d, 0x1e, 0x1a, + 0x99, 0xd1, 0x28, 0xc5, 0xa0, 0x82, 0x12, 0x0a, 0xab, 0x21, 0xb4, 0x27, 0xc5, 0x95, 0x35, 0x82, + 0x0d, 0x08, 0xb0, 0x24, 0xe8, 0x0a, 0x22, 0x57, 0x77, 0xb6, 0x70, 0x66, 0xd1, 0x57, 0xd8, 0x83, + 0x2d, 0x86, 0x59, 0x84, 0x72, 0x56, 0x23, 0x22, 0x85, 0x2c, 0xda, 0x16, 0x6c, 0x91, 0xc8, 0x62, + 0x0d, 0xb9, 0x04, 0x5b, 0x00, 0xb3, 0x88, 0x08, 0xef, 0x4e, 0x43, 0x5b, 0x97, 0xfa, 0x0d, 0xba, + 0x64, 0x2b, 0x21, 0x95, 0x78, 0x67, 0xba, 0xb4, 0x28, 0x32, 0xf1, 0xb0, 0x8a, 0x84, 0x9c, 0x9e, + 0xe5, 0x49, 0x30, 0x80, 0xe0, 0xb0, 0x24, 0x9b, 0x15, 0x84, 0xd9, 0x03, 0xf3, 0x0a, 0xcd, 0x2f, + 0x1b, 0x1f, 0x6d, 0x89, 0x30, 0x40, 0xd4, 0xa3, 0x88, 0xb4, 0x52, 0x74, 0x76, 0x94, 0x4b, 0x93, + 0xb0, 0xac, 0xd1, 0xc6, 0xbe, 0x3a, 0xf5, 0x20, 0x13, 0x00, 0x67, 0x03, 0x34, 0x73, 0xb2, 0x22, + 0xef, 0xec, 0xdc, 0x00, 0xa8, 0x0c, 0x10, 0x74, 0xe8, 0xf7, 0x55, 0x13, 0x42, 0x4c, 0xf8, 0x25, + 0x78, 0x84, 0x4d, 0x9b, 0x3b, 0x62, 0x76, 0xf8, 0xd0, 0x11, 0x40, 0xee, 0x8b, 0x01, 0x6c, 0x06, + 0xb4, 0x19, 0x98, 0xa7, 0xfa, 0xd9, 0x23, 0x02, 0xd7, 0x10, 0x1f, 0x0c, 0x23, 0xd7, 0x8f, 0x99, + 0x32, 0x68, 0x08, 0xda, 0xc0, 0xfb, 0xa5, 0x0c, 0xc1, 0x3c, 0xb8, 0xf1, 0x1e, 0xa1, 0x33, 0xe0, + 0xcd, 0x17, 0xe5, 0xcb, 0x07, 0x00, 0xf0, 0x7e, 0x35, 0x20, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, + 0x00, 0x03, 0x00, 0x2c, 0x15, 0x00, 0x1b, 0x00, 0x18, 0x00, 0x22, 0x00, 0x00, 0x08, 0xff, 0x00, + 0x07, 0x08, 0x1c, 0x38, 0x20, 0x40, 0x80, 0x60, 0xcd, 0x96, 0x28, 0x11, 0xe4, 0x8c, 0x0a, 0xc1, + 0x87, 0x0f, 0x03, 0x08, 0x3b, 0x54, 0xe3, 0xa1, 0x0a, 0x44, 0xce, 0x20, 0x12, 0x34, 0xb8, 0xc4, + 0x45, 0x8b, 0x16, 0x2b, 0xa2, 0x14, 0x2a, 0xc4, 0x44, 0x60, 0x0b, 0x43, 0x4d, 0x34, 0x06, 0x00, + 0x30, 0xec, 0x23, 0x13, 0x29, 0x53, 0x84, 0x11, 0x22, 0x24, 0x68, 0x60, 0x0b, 0x22, 0x29, 0x37, + 0x06, 0x38, 0x34, 0xe0, 0x63, 0x11, 0x29, 0x44, 0x34, 0xf6, 0x44, 0x34, 0x65, 0x63, 0xc6, 0x16, + 0x4e, 0x98, 0xa8, 0x10, 0x6a, 0xb3, 0xa6, 0xc0, 0x95, 0x4c, 0x3e, 0x52, 0x31, 0xc4, 0xd4, 0xe6, + 0x8a, 0x9c, 0x01, 0x8a, 0xf4, 0x04, 0x59, 0x95, 0x60, 0x8b, 0x21, 0x4f, 0x79, 0x76, 0x85, 0x78, + 0xf2, 0x29, 0xa2, 0xb1, 0x64, 0xa1, 0x14, 0x0c, 0x90, 0x08, 0xed, 0xc3, 0x16, 0x2c, 0x00, 0x14, + 0x04, 0xe6, 0xd6, 0x6b, 0x0d, 0xb9, 0x01, 0xa2, 0xd4, 0xb5, 0x49, 0xb7, 0xa0, 0xd3, 0xbd, 0x2d, + 0xc4, 0x06, 0x20, 0xb4, 0xb4, 0xee, 0xc7, 0x66, 0x03, 0xf3, 0x02, 0xee, 0xfb, 0x94, 0xb0, 0xdb, + 0x16, 0x2a, 0x10, 0x6f, 0xd4, 0x3b, 0xf6, 0xa3, 0x58, 0xa3, 0x95, 0x03, 0x0b, 0x25, 0xd4, 0xb5, + 0x05, 0x97, 0x8c, 0x42, 0x83, 0x55, 0x6d, 0x11, 0x25, 0x67, 0xe8, 0xc2, 0x64, 0x0d, 0x15, 0xad, + 0x1a, 0x0c, 0xf5, 0x43, 0x26, 0xa2, 0xbb, 0x06, 0xf8, 0xa5, 0x31, 0xb2, 0xdb, 0x60, 0x82, 0x82, + 0x12, 0xa4, 0x5c, 0x57, 0xe2, 0x80, 0x43, 0x88, 0x0e, 0x99, 0xee, 0xea, 0xe7, 0x85, 0x90, 0xbd, + 0x0f, 0xc1, 0x0c, 0x60, 0x13, 0x67, 0xc0, 0x91, 0x3e, 0x01, 0xf6, 0x46, 0x12, 0xc8, 0x86, 0x8d, + 0xc0, 0x39, 0xca, 0xdd, 0x7e, 0xa1, 0x4e, 0x70, 0xba, 0xdb, 0x24, 0x65, 0x06, 0x9c, 0x17, 0x19, + 0xb8, 0xe6, 0xc4, 0x5e, 0x31, 0x0f, 0xbb, 0x44, 0xaf, 0x0b, 0x60, 0xc7, 0x00, 0x34, 0x74, 0x5e, + 0xac, 0x47, 0x5e, 0xb0, 0x6e, 0x40, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, 0x00, 0x03, 0x00, 0x2c, + 0x17, 0x00, 0x1b, 0x00, 0x16, 0x00, 0x22, 0x00, 0x00, 0x08, 0xf8, 0x00, 0x07, 0x08, 0x1c, 0x38, + 0x20, 0x40, 0x93, 0x66, 0xce, 0x86, 0x10, 0x5c, 0xc8, 0x30, 0x40, 0xb3, 0x28, 0x2a, 0x08, 0xb6, + 0x60, 0xb2, 0x64, 0x11, 0xc3, 0x81, 0xc2, 0xa2, 0x08, 0x6c, 0xb1, 0xc2, 0x49, 0x14, 0x22, 0x2e, + 0x04, 0x32, 0x69, 0xd6, 0x90, 0x10, 0x97, 0x01, 0x2d, 0x0c, 0x59, 0x99, 0xd2, 0x44, 0xd8, 0x14, + 0x8d, 0x02, 0x6b, 0x38, 0x5b, 0x28, 0xec, 0xe4, 0x00, 0x2e, 0xc2, 0x04, 0x31, 0x11, 0x18, 0x92, + 0x60, 0x0d, 0x92, 0x02, 0x01, 0x20, 0x1a, 0xe0, 0xa2, 0x50, 0x8b, 0x88, 0x17, 0x07, 0x32, 0xb1, + 0x18, 0x60, 0xe6, 0x00, 0x43, 0x84, 0x90, 0x26, 0xdd, 0xb8, 0xa4, 0xe0, 0x50, 0x94, 0x3d, 0xa7, + 0x2a, 0x1d, 0x20, 0x4c, 0xaa, 0xd6, 0x85, 0x2a, 0x84, 0x59, 0xf9, 0x9a, 0xb4, 0x85, 0x33, 0xa7, + 0x64, 0x17, 0xb6, 0x18, 0x82, 0x36, 0xed, 0xc0, 0xb5, 0x40, 0xdd, 0xbe, 0x2d, 0xd2, 0xc4, 0xab, + 0xdb, 0xb0, 0x01, 0xae, 0xca, 0x1d, 0x70, 0xb5, 0x6d, 0x5a, 0xb3, 0x02, 0x03, 0x10, 0x91, 0xdb, + 0x02, 0x11, 0x80, 0x81, 0x0a, 0xdd, 0xae, 0xa0, 0x42, 0x50, 0x98, 0x5b, 0x2e, 0x71, 0x83, 0xd6, + 0xf8, 0x5a, 0xd8, 0xf1, 0xc2, 0x00, 0x89, 0xb4, 0xae, 0xf0, 0x3b, 0x30, 0x00, 0x94, 0xa9, 0x5c, + 0x08, 0x4d, 0xf5, 0x9c, 0xb4, 0x86, 0x68, 0xad, 0x87, 0x2e, 0xb6, 0xa8, 0xfa, 0x15, 0xc0, 0x12, + 0x27, 0x59, 0x07, 0x30, 0x99, 0x22, 0xb7, 0xc9, 0xcc, 0x44, 0x51, 0x18, 0x7f, 0x3d, 0xf1, 0x05, + 0x45, 0x89, 0xbd, 0x0b, 0xe1, 0x08, 0xfc, 0x21, 0x57, 0x0c, 0xc3, 0x3b, 0x36, 0xd2, 0x76, 0xb9, + 0x08, 0x26, 0x6d, 0x10, 0x86, 0x6c, 0xfa, 0xb8, 0x0d, 0xb3, 0xb0, 0x39, 0x70, 0x35, 0xc8, 0x81, + 0x6b, 0x0f, 0x08, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x2c, 0x16, 0x00, 0x1b, + 0x00, 0x17, 0x00, 0x22, 0x00, 0x00, 0x08, 0xff, 0x00, 0x07, 0x08, 0x1c, 0x28, 0x50, 0x98, 0x33, + 0x41, 0x87, 0x0e, 0x49, 0x11, 0x46, 0xb0, 0x61, 0xc3, 0x66, 0x51, 0x54, 0xb4, 0x20, 0xa8, 0xc2, + 0x49, 0xb3, 0x00, 0x0e, 0x07, 0x06, 0x1b, 0xa6, 0x42, 0x20, 0x93, 0x27, 0x4a, 0x0a, 0x71, 0x99, + 0xa8, 0xe2, 0xd0, 0x94, 0x8c, 0x4d, 0x88, 0x4c, 0x8c, 0x42, 0x68, 0x0a, 0x21, 0x2b, 0x84, 0xa2, + 0x4c, 0x14, 0x18, 0x65, 0x51, 0xc3, 0x29, 0x88, 0x26, 0xae, 0x10, 0x26, 0x68, 0xa4, 0xc0, 0x99, + 0x03, 0x87, 0x61, 0x1c, 0x78, 0x68, 0xa5, 0x0b, 0x89, 0x19, 0x09, 0x3a, 0x1b, 0x2a, 0xac, 0xc6, + 0x00, 0x26, 0x55, 0x7c, 0x26, 0x1d, 0x08, 0x6c, 0xa8, 0xa0, 0x99, 0x2b, 0x80, 0x4e, 0x15, 0xd8, + 0x4c, 0x60, 0xce, 0xad, 0x49, 0x05, 0x05, 0x00, 0xe0, 0x14, 0x6c, 0x46, 0x43, 0x01, 0x82, 0x75, + 0x34, 0xeb, 0x10, 0x51, 0xda, 0xb5, 0x6c, 0x09, 0x46, 0xc1, 0x08, 0x25, 0x6e, 0xc3, 0x43, 0x18, + 0x0d, 0x69, 0x8d, 0xbb, 0x74, 0x80, 0xb3, 0xbd, 0x66, 0x5d, 0x30, 0x1c, 0xb0, 0x88, 0x8b, 0x5d, + 0x81, 0x42, 0x07, 0xfe, 0xb5, 0xbb, 0x73, 0xe8, 0x00, 0x00, 0x4c, 0xe2, 0xaa, 0xe8, 0x4b, 0x50, + 0x10, 0xdb, 0x1e, 0x94, 0x09, 0x36, 0x03, 0xdc, 0x90, 0x08, 0x21, 0xc7, 0x04, 0x85, 0x71, 0x26, + 0x6a, 0x33, 0x69, 0x93, 0xd1, 0x03, 0xf0, 0x6e, 0x3d, 0x9d, 0x14, 0x11, 0x00, 0xb3, 0x88, 0x32, + 0xaa, 0x20, 0xc4, 0xb6, 0xc9, 0x21, 0x26, 0x5a, 0xd1, 0x1e, 0x16, 0x56, 0x64, 0x40, 0x94, 0x43, + 0xc1, 0xd8, 0xfe, 0x38, 0xe2, 0x07, 0xb4, 0xdd, 0x1d, 0x02, 0xe5, 0x0c, 0x78, 0x21, 0xe4, 0x75, + 0xdc, 0x2f, 0x0e, 0xe3, 0x40, 0x67, 0x0b, 0x26, 0x63, 0xa4, 0xb8, 0x3e, 0x32, 0xa2, 0xb0, 0x2b, + 0xa6, 0xe1, 0x98, 0xc3, 0x01, 0x5e, 0x0c, 0x09, 0x40, 0x33, 0x47, 0xfc, 0xe1, 0xf3, 0x02, 0x03, + 0x02, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x07, 0x00, 0x03, 0x00, 0x2c, 0x15, 0x00, 0x1a, 0x00, 0x17, + 0x00, 0x23, 0x00, 0x00, 0x08, 0xff, 0x00, 0x07, 0x08, 0x1c, 0x48, 0xb0, 0xa0, 0xc1, 0x83, 0x03, + 0x00, 0x34, 0x3b, 0x84, 0x28, 0x11, 0x13, 0x43, 0x82, 0x08, 0x05, 0x40, 0x48, 0xd0, 0x19, 0x93, + 0x16, 0x18, 0x09, 0xaa, 0xf8, 0x25, 0x6c, 0xe2, 0xc1, 0x29, 0xc3, 0x30, 0xba, 0x78, 0x32, 0xc4, + 0xca, 0x10, 0x8c, 0x2d, 0x06, 0xac, 0x70, 0x16, 0xc0, 0xe3, 0xc0, 0x45, 0x51, 0x30, 0x3e, 0x11, + 0x26, 0x6c, 0x88, 0x92, 0x27, 0x28, 0x05, 0xaa, 0x60, 0xe9, 0x72, 0x40, 0xc8, 0x16, 0x86, 0xaa, + 0x14, 0x72, 0x91, 0xd1, 0xa0, 0x0b, 0x89, 0x03, 0x9d, 0x65, 0x74, 0xc1, 0xa5, 0x28, 0x42, 0x22, + 0x2d, 0x05, 0x5e, 0x64, 0x42, 0x85, 0x68, 0x4a, 0x8a, 0x03, 0x54, 0x14, 0x99, 0xd8, 0x0c, 0x23, + 0x97, 0x42, 0x03, 0xae, 0x62, 0xf5, 0xd9, 0x52, 0x90, 0xd3, 0xb1, 0x03, 0xa1, 0xb4, 0xfc, 0x89, + 0xb6, 0xa0, 0x8b, 0x26, 0x01, 0x0c, 0x9d, 0x6d, 0xab, 0xa2, 0x23, 0xdb, 0xb6, 0x03, 0xdf, 0x06, + 0x30, 0x2b, 0x16, 0xaf, 0xda, 0x00, 0x5d, 0xfb, 0xb6, 0x7d, 0xd2, 0x12, 0x40, 0x53, 0xbc, 0x49, + 0xa3, 0x2e, 0x99, 0x8b, 0x95, 0x08, 0x00, 0x8f, 0x00, 0x10, 0x09, 0xa6, 0xd8, 0xa3, 0x59, 0x54, + 0x81, 0x55, 0x27, 0x1b, 0x54, 0xb1, 0xe4, 0xf2, 0xc0, 0x43, 0x9a, 0x09, 0xba, 0xe0, 0x69, 0x70, + 0x99, 0x0a, 0xac, 0x2b, 0x2c, 0xf7, 0x24, 0x98, 0x88, 0xe2, 0x4e, 0xcf, 0x06, 0x87, 0x51, 0x3c, + 0xb4, 0xda, 0xa0, 0x12, 0x84, 0x35, 0xe0, 0x8e, 0x55, 0x7a, 0x70, 0x58, 0xed, 0x83, 0x82, 0x0e, + 0x0f, 0xac, 0x41, 0x08, 0xf1, 0x80, 0x60, 0xc5, 0x97, 0x2c, 0xa1, 0xf2, 0xdb, 0x60, 0x80, 0x2c, + 0xcd, 0xd1, 0xf2, 0x19, 0x30, 0x47, 0xcf, 0x18, 0x30, 0x36, 0x4e, 0x30, 0xc3, 0x6b, 0x43, 0x60, + 0x9b, 0x81, 0x6c, 0xe0, 0xe0, 0x21, 0x9d, 0x3e, 0x60, 0x0d, 0xc1, 0x3c, 0x78, 0xb1, 0xa4, 0x31, + 0xa3, 0x86, 0xe0, 0x0e, 0xc4, 0xef, 0x09, 0xd2, 0xc9, 0x62, 0x3c, 0x48, 0x9d, 0x33, 0x6b, 0xba, + 0xfc, 0x31, 0x3e, 0xb0, 0x04, 0xde, 0x80, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, + 0x2c, 0x13, 0x00, 0x1a, 0x00, 0x18, 0x00, 0x23, 0x00, 0x00, 0x08, 0xff, 0x00, 0x07, 0x08, 0x1c, + 0x48, 0xb0, 0xa0, 0xc1, 0x83, 0x02, 0x85, 0x2d, 0x89, 0x02, 0xa5, 0x45, 0x0d, 0x44, 0x82, 0x96, + 0x05, 0x08, 0x80, 0x70, 0xe0, 0x94, 0x43, 0x3d, 0x5a, 0xb4, 0x30, 0xf8, 0x4b, 0xd8, 0x44, 0x84, + 0xc2, 0x88, 0x68, 0x24, 0xb2, 0xc4, 0x8a, 0x20, 0x8d, 0x1b, 0x07, 0xac, 0x68, 0xf6, 0xb1, 0xa0, + 0x30, 0x2e, 0x2d, 0x56, 0x0c, 0x99, 0x62, 0x45, 0x89, 0x13, 0x94, 0x03, 0x7b, 0xb0, 0xa4, 0x38, + 0x70, 0x91, 0x48, 0x2e, 0x84, 0x08, 0x89, 0xd4, 0x78, 0x70, 0x85, 0x47, 0x9e, 0x03, 0x8c, 0x8d, + 0x94, 0xe2, 0x82, 0x68, 0xc5, 0x61, 0x2d, 0x9b, 0xd4, 0xd8, 0xb8, 0x02, 0x67, 0xc5, 0x01, 0x2a, + 0x24, 0x52, 0x74, 0xa6, 0x91, 0xd0, 0x13, 0xa7, 0x57, 0x07, 0x08, 0xfa, 0x38, 0x4c, 0xa3, 0x13, + 0x17, 0x61, 0x09, 0x46, 0xf9, 0x08, 0x0c, 0x6c, 0x5a, 0x81, 0x50, 0x26, 0x06, 0x68, 0x98, 0xf2, + 0xad, 0x40, 0x18, 0x72, 0xdb, 0xd6, 0xb5, 0x9b, 0x48, 0x6e, 0xd9, 0xbd, 0x6f, 0x0d, 0x7d, 0x1c, + 0xe2, 0xf6, 0xed, 0x92, 0x8f, 0x4d, 0x32, 0x02, 0xbe, 0xda, 0xe3, 0xa8, 0xc0, 0x43, 0x85, 0xaf, + 0x1e, 0x92, 0x2b, 0x30, 0x18, 0x93, 0xc8, 0x07, 0xb9, 0x34, 0x69, 0x29, 0x90, 0x50, 0xd5, 0xc5, + 0x05, 0x57, 0x10, 0xe2, 0x3c, 0xb0, 0x59, 0xc6, 0xab, 0x2a, 0x76, 0x22, 0x3c, 0x09, 0x5a, 0xa0, + 0x31, 0xd2, 0x05, 0x17, 0xc1, 0x44, 0xc8, 0x25, 0x18, 0x52, 0x84, 0x4a, 0x11, 0x4e, 0x4e, 0x4b, + 0xa8, 0x35, 0xcb, 0xb4, 0x8b, 0x6a, 0x1c, 0xec, 0x11, 0xcc, 0xae, 0x33, 0x15, 0x7b, 0x55, 0x30, + 0xba, 0x1d, 0x56, 0x98, 0x95, 0xe7, 0xcf, 0x3d, 0xda, 0x15, 0xc8, 0x7c, 0xba, 0xc0, 0x3f, 0x5f, + 0xb2, 0x6b, 0xcf, 0x9e, 0xc5, 0x6e, 0x1f, 0x81, 0x6c, 0x08, 0xb2, 0x26, 0xf9, 0xfe, 0xd6, 0x04, + 0x78, 0xf1, 0x58, 0xec, 0x06, 0xd8, 0xc3, 0x26, 0xfc, 0xc0, 0x3c, 0xd6, 0x85, 0xa8, 0x21, 0x98, + 0x06, 0x88, 0xf5, 0x01, 0x7d, 0xf6, 0x0c, 0x30, 0xa3, 0xe7, 0xc4, 0xfd, 0x81, 0x00, 0x00, 0x90, + 0x56, 0x40, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x2c, 0x10, 0x00, 0x1a, 0x00, + 0x18, 0x00, 0x23, 0x00, 0x00, 0x08, 0xff, 0x00, 0x07, 0x08, 0x1c, 0x48, 0xb0, 0xa0, 0xc1, 0x83, + 0x03, 0x83, 0x39, 0x3b, 0x64, 0x08, 0xd1, 0x13, 0x41, 0xcd, 0x00, 0x04, 0x08, 0x80, 0x70, 0xe0, + 0x22, 0x41, 0x2b, 0x5a, 0xb4, 0x28, 0xc8, 0xc4, 0xd9, 0x44, 0x8a, 0x07, 0x85, 0x11, 0xd1, 0xc8, + 0xa5, 0xd0, 0x00, 0x8d, 0x1b, 0x05, 0x46, 0x09, 0x36, 0xd1, 0xa0, 0x30, 0x2e, 0x2d, 0x56, 0x2c, + 0x99, 0x42, 0xc8, 0x10, 0x4a, 0x82, 0x44, 0x9a, 0xb4, 0xb4, 0x38, 0x92, 0x0b, 0x21, 0x42, 0x4e, + 0x34, 0x22, 0x8c, 0xf2, 0x71, 0xe0, 0xa1, 0x16, 0x2e, 0x08, 0x15, 0x71, 0x21, 0xb4, 0xa2, 0x47, + 0x90, 0xc2, 0x6a, 0xc4, 0x94, 0x92, 0xb1, 0xe2, 0x40, 0x26, 0x12, 0x29, 0x2e, 0xd1, 0xe8, 0xe2, + 0x64, 0x4a, 0xab, 0x03, 0x9a, 0xb5, 0x44, 0xd4, 0xc2, 0x10, 0x21, 0xaf, 0x60, 0x05, 0x1a, 0x9b, + 0x08, 0x40, 0x2a, 0x13, 0x43, 0x69, 0x09, 0x0e, 0x9b, 0x18, 0x4c, 0x45, 0xd3, 0xb8, 0x02, 0x11, + 0xd1, 0xb5, 0xfb, 0x15, 0xaf, 0xa1, 0x96, 0x50, 0xee, 0xe2, 0x3d, 0xd4, 0xf2, 0x89, 0xe0, 0xb8, + 0x4f, 0x07, 0x38, 0xbb, 0x89, 0xb7, 0x06, 0x4b, 0x8a, 0x00, 0x98, 0x1c, 0xb6, 0x4a, 0x18, 0x64, + 0x58, 0xbe, 0x71, 0x99, 0x3c, 0x26, 0x68, 0x8c, 0x71, 0x45, 0x15, 0x62, 0x2d, 0x0f, 0x34, 0x86, + 0xb9, 0xe2, 0x5c, 0xd1, 0x04, 0x9d, 0xf5, 0xe8, 0x6b, 0x50, 0xac, 0x55, 0x67, 0x76, 0x11, 0x42, + 0x91, 0x08, 0x76, 0x18, 0xeb, 0x81, 0x7f, 0xd3, 0x36, 0xa9, 0x81, 0x90, 0x70, 0xdc, 0xa3, 0x07, + 0x87, 0xa0, 0x46, 0x48, 0x88, 0x29, 0x41, 0x15, 0xc3, 0x00, 0xe0, 0x0d, 0x7b, 0xe8, 0xd0, 0x92, + 0x22, 0x84, 0x58, 0x2e, 0xaf, 0x98, 0x65, 0x79, 0x00, 0x2c, 0x27, 0x6c, 0x80, 0x19, 0xa3, 0x67, + 0x0e, 0x1b, 0x2f, 0x78, 0x01, 0xbc, 0x2d, 0x21, 0xd8, 0x46, 0xe0, 0x11, 0xbc, 0x01, 0xf4, 0x10, + 0x5c, 0x23, 0xd0, 0xc6, 0xf2, 0x17, 0x04, 0xd5, 0x98, 0x39, 0xf3, 0x63, 0xf9, 0xa2, 0x3b, 0x04, + 0xcb, 0x80, 0x99, 0x3e, 0x20, 0x4b, 0xa4, 0x36, 0x67, 0xd4, 0xe1, 0x1e, 0x7f, 0x02, 0x05, 0x50, + 0x42, 0x45, 0x01, 0x01, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, 0x00, 0x03, 0x00, 0x2c, 0x0f, 0x00, + 0x1b, 0x00, 0x17, 0x00, 0x22, 0x00, 0x00, 0x08, 0xff, 0x00, 0x07, 0x08, 0x1c, 0x28, 0x90, 0x4a, + 0x11, 0x67, 0xce, 0x9a, 0x05, 0x0b, 0xc0, 0x90, 0xa0, 0x43, 0x81, 0xc1, 0x96, 0x40, 0x69, 0x41, + 0xb0, 0xc6, 0xb0, 0x66, 0x0d, 0x1f, 0x0a, 0x6c, 0xc6, 0x85, 0xe2, 0x80, 0x16, 0x20, 0x09, 0x0e, + 0x5b, 0x18, 0xe0, 0xa1, 0xb3, 0x1e, 0x2d, 0xb8, 0x0c, 0xa1, 0xb2, 0x22, 0x24, 0x41, 0x22, 0x4d, + 0x32, 0x6e, 0x44, 0x19, 0xa5, 0x8a, 0x95, 0x28, 0x1e, 0x1f, 0x22, 0x9a, 0x52, 0x12, 0x62, 0xc7, + 0x28, 0x53, 0x0a, 0xb9, 0xd4, 0x38, 0xc0, 0x58, 0x46, 0x63, 0x2d, 0x56, 0x08, 0x53, 0x32, 0x94, + 0x68, 0x0d, 0x61, 0x0c, 0x17, 0xb5, 0x64, 0x32, 0x44, 0x05, 0xd1, 0x87, 0x82, 0x18, 0x36, 0x0b, + 0xd9, 0xf4, 0xea, 0x00, 0x44, 0x0c, 0x05, 0xa5, 0xa4, 0xe2, 0x22, 0xa7, 0xd7, 0x01, 0x2c, 0x16, + 0x05, 0x18, 0x96, 0x54, 0xd0, 0xd9, 0x87, 0x50, 0x9f, 0x74, 0x7d, 0xab, 0x02, 0xea, 0xa1, 0xb9, + 0x67, 0x7b, 0xa8, 0x75, 0x86, 0xd7, 0x2b, 0xd8, 0x00, 0xc2, 0x54, 0x98, 0x7d, 0x3b, 0x20, 0x6b, + 0x49, 0x43, 0x7d, 0x35, 0x3e, 0xed, 0x49, 0x08, 0xe5, 0xe0, 0xab, 0x87, 0x64, 0x2e, 0x49, 0x5c, + 0x31, 0xa6, 0x43, 0xa4, 0x94, 0x07, 0x0c, 0xeb, 0xe9, 0xd0, 0x59, 0xcb, 0xc7, 0x04, 0x9d, 0x71, + 0x76, 0x28, 0xcc, 0x49, 0x62, 0x42, 0xa3, 0x1d, 0x2e, 0xc2, 0x49, 0x54, 0x05, 0x95, 0xb3, 0xc1, + 0x98, 0x80, 0x56, 0x41, 0xe8, 0xed, 0x56, 0x8d, 0x5c, 0x16, 0x11, 0x66, 0xfd, 0x12, 0x23, 0x61, + 0x61, 0x88, 0x54, 0x10, 0x39, 0x34, 0x00, 0x2a, 0x61, 0x82, 0x00, 0x1c, 0xfa, 0x39, 0xf2, 0xe3, + 0x78, 0x00, 0x00, 0x42, 0x5e, 0x88, 0x91, 0xc3, 0x66, 0xc0, 0x0e, 0xc2, 0x01, 0xbe, 0xc4, 0x11, + 0xd8, 0xa6, 0xfa, 0x80, 0x2e, 0xc7, 0x23, 0x11, 0x1f, 0xf4, 0x2e, 0x9e, 0xb0, 0x10, 0x82, 0x67, + 0xca, 0x94, 0xf1, 0xe2, 0xbc, 0xfc, 0x40, 0x31, 0xc7, 0x05, 0x06, 0x38, 0x52, 0x07, 0xcd, 0x9c, + 0x1d, 0xc9, 0xe3, 0x0f, 0x4c, 0x2d, 0x30, 0x20, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x06, 0x00, 0x03, + 0x00, 0x2c, 0x0e, 0x00, 0x1b, 0x00, 0x17, 0x00, 0x22, 0x00, 0x00, 0x08, 0xff, 0x00, 0x07, 0x08, + 0x1c, 0x38, 0xb0, 0x09, 0x21, 0x42, 0xc2, 0x02, 0x04, 0x20, 0xc8, 0x90, 0x21, 0x95, 0x43, 0x4c, + 0x54, 0x0c, 0x84, 0x72, 0x88, 0xd0, 0xc2, 0x86, 0x03, 0x01, 0x1c, 0xea, 0x81, 0x51, 0xe0, 0xb0, + 0x26, 0x17, 0x19, 0x06, 0x8b, 0xd2, 0x62, 0x80, 0x93, 0x01, 0x2b, 0x4a, 0x32, 0x64, 0x62, 0x91, + 0x21, 0x00, 0x92, 0x2e, 0x86, 0x4c, 0x19, 0xb2, 0xa2, 0x23, 0x97, 0x84, 0x04, 0x05, 0xb5, 0x70, + 0x51, 0x84, 0x10, 0x91, 0x8e, 0x03, 0x9d, 0x84, 0x14, 0x56, 0x63, 0x80, 0x20, 0x2a, 0x5c, 0x80, + 0x12, 0x74, 0x76, 0x51, 0xa7, 0x8b, 0x26, 0x24, 0x95, 0x0e, 0x44, 0x74, 0x31, 0x91, 0xc0, 0xa4, + 0x52, 0x07, 0xaa, 0x10, 0x36, 0x40, 0x98, 0xc4, 0x27, 0x4c, 0x54, 0x66, 0x1d, 0xc0, 0xb4, 0x59, + 0x49, 0x42, 0x86, 0xc6, 0x0e, 0x5c, 0x12, 0xa0, 0x48, 0x49, 0x17, 0x6a, 0xd7, 0x06, 0x30, 0x1b, + 0x77, 0x69, 0x00, 0xaf, 0x75, 0x07, 0x12, 0x12, 0x08, 0x4c, 0xac, 0x5a, 0x28, 0x00, 0x04, 0x0e, + 0xf1, 0x3b, 0x56, 0xd0, 0xc5, 0x45, 0x61, 0xe3, 0xae, 0x68, 0x42, 0xb0, 0x19, 0x47, 0xb5, 0x87, + 0x42, 0x0a, 0x74, 0x26, 0x71, 0x6c, 0x33, 0x8c, 0x45, 0xb8, 0x10, 0x6e, 0xa8, 0x82, 0x31, 0xc6, + 0x26, 0x87, 0x2a, 0x77, 0xac, 0x11, 0x18, 0xa8, 0x33, 0xb8, 0xa3, 0x25, 0x63, 0xa4, 0xdc, 0xb1, + 0xc7, 0xa2, 0xac, 0x87, 0x3a, 0x52, 0xcd, 0x1a, 0x0c, 0xeb, 0x40, 0x17, 0x51, 0xb8, 0x5a, 0x4e, + 0x99, 0xd4, 0x99, 0xe7, 0xb8, 0x8b, 0x84, 0x95, 0x1e, 0x50, 0x02, 0xc5, 0x17, 0x21, 0x71, 0x03, + 0xfc, 0x38, 0x32, 0x00, 0x8e, 0xc0, 0x2f, 0x6a, 0x8f, 0xdc, 0x11, 0xc8, 0x66, 0x60, 0x18, 0xb5, + 0xd0, 0x1b, 0x8a, 0x51, 0xab, 0x03, 0xa3, 0x8d, 0xb8, 0x3b, 0x18, 0xee, 0x0c, 0x19, 0x3e, 0x36, + 0x08, 0x9e, 0x4a, 0x74, 0x88, 0xe7, 0x05, 0x1a, 0x10, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, 0x00, + 0x03, 0x00, 0x2c, 0x0e, 0x00, 0x1b, 0x00, 0x17, 0x00, 0x22, 0x00, 0x00, 0x08, 0xff, 0x00, 0x07, + 0x08, 0x1c, 0x38, 0x90, 0x8a, 0xb3, 0x21, 0x43, 0x8a, 0x08, 0x23, 0xc8, 0x90, 0xe1, 0x94, 0x25, + 0x89, 0x5a, 0x0c, 0x6c, 0xa1, 0x02, 0x91, 0x33, 0x00, 0x0d, 0x07, 0x06, 0x20, 0x44, 0x44, 0xa0, + 0x8a, 0x86, 0x2d, 0x88, 0x50, 0xc9, 0x18, 0xa0, 0xd9, 0x8a, 0x01, 0x2b, 0x96, 0x34, 0x31, 0x04, + 0x72, 0x45, 0x33, 0x86, 0x01, 0x84, 0xad, 0x08, 0x29, 0x8c, 0x90, 0x21, 0x17, 0x19, 0x5b, 0xac, + 0x58, 0xa8, 0x11, 0x51, 0x0b, 0x26, 0x55, 0x96, 0xe0, 0xcc, 0x28, 0xb0, 0x05, 0x22, 0x8d, 0xce, + 0x06, 0xb4, 0xb0, 0x22, 0x85, 0x28, 0xc3, 0x16, 0x49, 0x07, 0x04, 0xf0, 0xd9, 0x42, 0xd0, 0x49, + 0xa7, 0x13, 0x7d, 0x09, 0x14, 0xf6, 0xf1, 0x2a, 0xd6, 0x89, 0x2c, 0x82, 0x05, 0x48, 0xda, 0x82, + 0x8a, 0x93, 0xaf, 0x04, 0x55, 0x34, 0x0b, 0xb0, 0x44, 0xe9, 0x13, 0xaf, 0x68, 0x5b, 0x14, 0x61, + 0xab, 0x14, 0xed, 0xd3, 0xb9, 0x51, 0xed, 0x12, 0x6c, 0x41, 0x28, 0xc0, 0x32, 0xbd, 0x0c, 0x6b, + 0x04, 0x93, 0x9a, 0x08, 0x70, 0xd1, 0x5f, 0x02, 0xe9, 0x1a, 0x56, 0x9b, 0x78, 0x11, 0x13, 0xc0, + 0x2d, 0x58, 0x6a, 0x6c, 0x36, 0x14, 0xad, 0x0a, 0x42, 0x30, 0x8b, 0xc0, 0x75, 0x7a, 0xb4, 0xe1, + 0xc6, 0x8e, 0x58, 0x5b, 0xb4, 0x25, 0x39, 0xe5, 0x50, 0xe8, 0x22, 0x58, 0x03, 0x98, 0x26, 0xda, + 0xe2, 0x25, 0x56, 0x00, 0x9d, 0x41, 0x62, 0xfe, 0x4a, 0xa8, 0x32, 0xc1, 0x15, 0x83, 0xbf, 0x06, + 0x18, 0xf6, 0x94, 0x89, 0x6b, 0xb4, 0x4d, 0x7e, 0xd5, 0xe8, 0xa8, 0xd0, 0xb0, 0xd4, 0x00, 0x01, + 0x34, 0xfe, 0x08, 0x82, 0x05, 0x30, 0x80, 0x13, 0x2f, 0x06, 0xcc, 0x11, 0x18, 0xdd, 0x6e, 0x97, + 0x37, 0x0d, 0x23, 0xe9, 0xed, 0x92, 0xf1, 0x8b, 0x5e, 0x20, 0x19, 0x7d, 0x00, 0x0f, 0x1e, 0xc3, + 0x50, 0x4c, 0x72, 0xc0, 0x2f, 0xe8, 0xa0, 0x99, 0x8e, 0xd1, 0x38, 0xd1, 0x80, 0x00, 0x21, 0xf9, + 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x2c, 0x0f, 0x00, 0x1b, 0x00, 0x18, 0x00, 0x22, 0x00, 0x00, + 0x08, 0xff, 0x00, 0x07, 0x08, 0x1c, 0x28, 0x30, 0x40, 0xb3, 0x43, 0x51, 0x88, 0x20, 0x1a, 0xb6, + 0x84, 0x0a, 0xc1, 0x87, 0x03, 0x03, 0x18, 0x24, 0x42, 0xb0, 0x85, 0x45, 0x15, 0x51, 0x08, 0x41, + 0x8c, 0x18, 0xe0, 0xd0, 0x80, 0x16, 0x2e, 0x0c, 0x2d, 0xe1, 0x32, 0xd0, 0xa2, 0x0b, 0x67, 0x1b, + 0x25, 0x1e, 0xb2, 0xf8, 0x44, 0x98, 0x30, 0x29, 0x24, 0x4b, 0xb6, 0x50, 0xc1, 0xe8, 0xa1, 0x44, + 0x46, 0x16, 0x05, 0x55, 0x39, 0xe4, 0x62, 0xe3, 0xc5, 0x66, 0x04, 0x03, 0x08, 0x73, 0xd1, 0xe2, + 0x49, 0x15, 0x27, 0x1b, 0x65, 0x32, 0x59, 0x54, 0x30, 0xc0, 0x30, 0x8b, 0x4e, 0xa2, 0x24, 0xad, + 0xd8, 0x02, 0xe5, 0x80, 0x00, 0xc1, 0x6a, 0xb4, 0x70, 0x12, 0x73, 0xaa, 0x40, 0x8b, 0x88, 0xae, + 0x06, 0x70, 0xd6, 0x62, 0x00, 0xa1, 0x27, 0x5e, 0x65, 0xaa, 0x08, 0x26, 0x51, 0x90, 0x45, 0x2e, + 0x3d, 0xd3, 0x7e, 0x9c, 0x49, 0x48, 0xa5, 0x45, 0xb9, 0x54, 0x9b, 0x49, 0x34, 0x86, 0xf7, 0xa1, + 0xc5, 0xba, 0x63, 0xfb, 0x56, 0x5c, 0x2b, 0x51, 0xa3, 0xe0, 0xb9, 0x61, 0xc5, 0x02, 0x3b, 0x6c, + 0x71, 0x49, 0x53, 0xab, 0x78, 0xdf, 0x06, 0x6b, 0x1a, 0x40, 0x6a, 0xe4, 0xaa, 0x41, 0x03, 0x34, + 0x61, 0x22, 0xd7, 0xe2, 0x30, 0x88, 0x12, 0x85, 0x51, 0xf4, 0xda, 0x82, 0xcb, 0x64, 0xd0, 0x58, + 0x13, 0x27, 0x6d, 0x21, 0x68, 0xaa, 0xc4, 0x26, 0x5d, 0x21, 0xb6, 0x70, 0xe8, 0x35, 0x70, 0x52, + 0x28, 0x72, 0x25, 0x72, 0xde, 0x68, 0x39, 0x6d, 0x00, 0xc7, 0xb2, 0x3d, 0xe2, 0x15, 0x16, 0xfb, + 0x23, 0xa2, 0x26, 0x7d, 0x85, 0x0a, 0x12, 0x34, 0xc4, 0x2c, 0xf2, 0xc3, 0x29, 0x25, 0x0a, 0xee, + 0xe3, 0xe5, 0x08, 0x98, 0x31, 0x61, 0xee, 0xc0, 0x01, 0xd2, 0x37, 0x0b, 0x1b, 0x36, 0x03, 0xdb, + 0xb4, 0x24, 0x19, 0x10, 0x44, 0xf0, 0x9d, 0x8d, 0x5e, 0x04, 0x83, 0x21, 0x58, 0xe9, 0x8c, 0x9a, + 0x3f, 0x82, 0xff, 0xd0, 0x79, 0xf8, 0x02, 0x3a, 0xb3, 0x31, 0x6b, 0xce, 0xd8, 0x09, 0x12, 0x00, + 0xfa, 0xc0, 0x12, 0x49, 0x05, 0x04, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x2c, + 0x11, 0x00, 0x1a, 0x00, 0x18, 0x00, 0x23, 0x00, 0x00, 0x08, 0xff, 0x00, 0x07, 0x08, 0x1c, 0x48, + 0xb0, 0xa0, 0xc1, 0x83, 0x03, 0x03, 0x04, 0x73, 0x66, 0x08, 0x8a, 0x8a, 0x1a, 0xc0, 0x0e, 0x35, + 0x43, 0x48, 0x30, 0x40, 0x00, 0x67, 0x5c, 0x0a, 0xb6, 0xd8, 0x48, 0x64, 0x22, 0x42, 0x85, 0x86, + 0x36, 0x72, 0x51, 0x52, 0xc4, 0x8a, 0x0b, 0x81, 0x1b, 0x55, 0x18, 0x3b, 0x18, 0x60, 0x8a, 0x93, + 0x16, 0x2a, 0x94, 0x54, 0x21, 0x24, 0xc8, 0x10, 0xc1, 0x8d, 0x2d, 0x56, 0x56, 0x0c, 0x30, 0x6c, + 0xe3, 0x10, 0x61, 0x36, 0x0f, 0xa6, 0x74, 0x96, 0x30, 0x40, 0x33, 0x15, 0x3e, 0x99, 0x50, 0x1c, + 0x20, 0x72, 0x91, 0x40, 0x8b, 0x88, 0x5a, 0x70, 0x21, 0xb2, 0x74, 0xe0, 0x46, 0xa2, 0x16, 0x09, + 0x6d, 0x5c, 0x22, 0xa5, 0x2a, 0xca, 0x16, 0x88, 0x06, 0x58, 0x5c, 0x22, 0x72, 0x85, 0x57, 0xa6, + 0x2d, 0x7a, 0x2c, 0xb2, 0x78, 0x68, 0xe3, 0xd9, 0xaf, 0x2a, 0x84, 0x59, 0x1c, 0xf6, 0xf6, 0xa6, + 0x0a, 0x42, 0x6c, 0xeb, 0x5a, 0x8d, 0x6b, 0x71, 0x88, 0x5e, 0xb4, 0x35, 0x4a, 0x64, 0xfd, 0xbb, + 0x31, 0xa8, 0x45, 0xaa, 0x75, 0xaf, 0x3e, 0xbd, 0x98, 0xb8, 0x05, 0x11, 0x00, 0x45, 0x83, 0x7a, + 0x4d, 0xeb, 0x71, 0x71, 0x30, 0xc4, 0x4b, 0x61, 0x12, 0x2d, 0x68, 0x51, 0x58, 0x46, 0x8a, 0x5b, + 0x3f, 0x32, 0x06, 0x2d, 0x99, 0x65, 0x80, 0xb0, 0x08, 0x7b, 0x08, 0xab, 0x1a, 0xc0, 0xaf, 0x50, + 0xba, 0x5e, 0x85, 0xa9, 0x10, 0xba, 0xb9, 0x2a, 0x00, 0x28, 0x07, 0xe3, 0x9e, 0xbd, 0x58, 0xa3, + 0xa0, 0xca, 0xba, 0x01, 0x9a, 0x10, 0xb2, 0x42, 0xdc, 0xca, 0xb2, 0xbf, 0xc8, 0x0b, 0x62, 0x19, + 0xd3, 0xa5, 0x4b, 0xa4, 0x2f, 0xd0, 0xc1, 0x98, 0x78, 0x2b, 0xa4, 0x20, 0x1b, 0x36, 0x03, 0xfc, + 0xbc, 0xfd, 0x83, 0xdd, 0xe0, 0x9f, 0xba, 0x7a, 0xd6, 0x0c, 0x18, 0xbc, 0xbe, 0x27, 0x40, 0x5d, + 0x20, 0x69, 0x08, 0xaa, 0x39, 0xf1, 0xf7, 0x84, 0x9e, 0x33, 0x03, 0xf6, 0x54, 0x4f, 0x0e, 0x00, + 0x32, 0xc5, 0x80, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x07, 0x00, 0x03, 0x00, 0x2c, 0x13, 0x00, 0x1a, + 0x00, 0x17, 0x00, 0x23, 0x00, 0x00, 0x08, 0xff, 0x00, 0x07, 0x08, 0x1c, 0x48, 0xb0, 0xa0, 0xc1, + 0x83, 0x03, 0x02, 0x04, 0x73, 0x66, 0x08, 0xca, 0x80, 0x15, 0x88, 0x0e, 0x35, 0x43, 0x48, 0x10, + 0xc0, 0x92, 0x15, 0x07, 0x5b, 0x20, 0x22, 0x84, 0x30, 0x40, 0x93, 0x28, 0x03, 0x5a, 0x70, 0x51, + 0x52, 0x84, 0x10, 0x48, 0x81, 0x2d, 0x5c, 0x38, 0x33, 0xe8, 0x91, 0x48, 0x4a, 0x41, 0x53, 0x08, + 0x09, 0x2a, 0xc4, 0xa5, 0xa0, 0x8a, 0x95, 0x04, 0x03, 0x80, 0x6c, 0x31, 0x44, 0xd8, 0xc9, 0x8c, + 0x2e, 0x38, 0x0a, 0x0c, 0x80, 0xb3, 0x45, 0x94, 0x9a, 0x14, 0x43, 0x22, 0x1a, 0xb8, 0x88, 0x4b, + 0x0b, 0x27, 0x4a, 0x92, 0x12, 0x54, 0x31, 0x31, 0xc0, 0xc4, 0x16, 0x87, 0x04, 0x49, 0x1d, 0xd8, + 0x62, 0x58, 0xc2, 0x43, 0x21, 0x5b, 0x6c, 0xe5, 0xea, 0x30, 0xc0, 0xaf, 0xb1, 0x05, 0x53, 0x06, + 0x0b, 0xb0, 0x14, 0x2d, 0x57, 0x17, 0x4d, 0xcc, 0xba, 0xe5, 0xda, 0x63, 0x2d, 0xd8, 0xb9, 0x21, + 0x13, 0x25, 0x2c, 0x82, 0x37, 0xe4, 0xdd, 0xa6, 0x78, 0x5b, 0x4c, 0x4c, 0xb8, 0x64, 0xae, 0xd1, + 0x8a, 0x6d, 0xb7, 0xb6, 0x58, 0x41, 0x25, 0xa7, 0x30, 0xa4, 0x49, 0x5b, 0xf4, 0xc0, 0x99, 0x93, + 0x32, 0xc5, 0x1a, 0x7c, 0x0f, 0xb2, 0x4d, 0x7a, 0x33, 0xa9, 0xe5, 0xb4, 0x77, 0x29, 0x36, 0x71, + 0x81, 0xb0, 0x86, 0x30, 0xa9, 0x01, 0xf4, 0x1e, 0xf4, 0x8a, 0x9a, 0x75, 0xda, 0xcf, 0x08, 0x85, + 0x25, 0x46, 0x89, 0x68, 0x11, 0x5a, 0x8f, 0x54, 0x38, 0x4a, 0x29, 0x32, 0xa5, 0xaf, 0x6f, 0x21, + 0x78, 0xf6, 0x88, 0xe9, 0x32, 0x60, 0x87, 0x0d, 0x2f, 0x01, 0xc6, 0xfa, 0x68, 0xc3, 0xbc, 0xe0, + 0x9f, 0xb1, 0x42, 0x0e, 0xc6, 0x49, 0xbe, 0x15, 0x40, 0x1c, 0x35, 0x6b, 0x08, 0x8e, 0x71, 0xcb, + 0xc7, 0x0c, 0x41, 0x37, 0x3f, 0xe6, 0xfa, 0x0b, 0xc0, 0x33, 0x20, 0x4d, 0x18, 0x5c, 0xbe, 0x4b, + 0x6c, 0x0d, 0x08, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x2c, 0x13, 0x00, 0x1a, + 0x00, 0x17, 0x00, 0x23, 0x00, 0x00, 0x08, 0xdf, 0x00, 0x07, 0x08, 0x1c, 0x48, 0xb0, 0xa0, 0xc1, + 0x83, 0x02, 0x9b, 0x2c, 0x89, 0x32, 0x80, 0x85, 0x40, 0x41, 0x84, 0x10, 0x12, 0x5c, 0x64, 0xac, + 0xc6, 0x41, 0x15, 0xbf, 0x84, 0x49, 0x14, 0x46, 0x44, 0x20, 0x13, 0x41, 0x56, 0x06, 0x30, 0x21, + 0xb8, 0xa2, 0xc8, 0x41, 0x61, 0x5c, 0x06, 0xac, 0x58, 0x32, 0x20, 0xe4, 0x13, 0x17, 0x05, 0x5d, + 0x98, 0x24, 0x38, 0xa5, 0x63, 0x4b, 0x42, 0x36, 0x11, 0xae, 0xd0, 0x38, 0xd0, 0xd8, 0xc0, 0x27, + 0x2b, 0x24, 0x0e, 0xfc, 0x35, 0xb0, 0x89, 0x45, 0x43, 0x4f, 0x84, 0x16, 0x5c, 0x26, 0x70, 0x88, + 0xc0, 0x25, 0x85, 0x94, 0x12, 0x14, 0x24, 0x30, 0xa9, 0x54, 0x83, 0x88, 0x04, 0x02, 0xbb, 0x6a, + 0x10, 0x4a, 0x80, 0x01, 0x50, 0xb8, 0x16, 0xac, 0x01, 0x60, 0xc0, 0x56, 0xb1, 0x03, 0xbd, 0x0e, + 0x18, 0x86, 0x76, 0x20, 0xc3, 0x01, 0x4e, 0xdb, 0x0e, 0x60, 0x39, 0xc0, 0x68, 0xdb, 0x1e, 0x3c, + 0x07, 0x1c, 0x6a, 0xcb, 0x76, 0x60, 0xb0, 0x91, 0x5c, 0xb9, 0x34, 0x29, 0x48, 0x28, 0xa8, 0xd4, + 0x15, 0x11, 0xe5, 0xaa, 0x6c, 0x26, 0x31, 0xe7, 0x41, 0x17, 0x8c, 0x15, 0x5f, 0x15, 0xa6, 0x42, + 0x67, 0x30, 0xa9, 0x61, 0x0f, 0xee, 0x95, 0x3c, 0x20, 0xb2, 0x54, 0xc7, 0x02, 0x9d, 0x94, 0xe5, + 0x9a, 0xb7, 0x48, 0xb3, 0xd1, 0x9c, 0xb9, 0xf6, 0xd1, 0x53, 0xf0, 0x48, 0x10, 0xae, 0x28, 0x10, + 0x62, 0xb9, 0xda, 0xe7, 0x20, 0x1c, 0xd4, 0x4a, 0xe5, 0xa4, 0xf6, 0x72, 0xa6, 0x20, 0x2e, 0xc5, + 0x68, 0x06, 0xf8, 0x49, 0x2d, 0x34, 0x20, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, 0x00, 0x03, 0x00, + 0x2c, 0x13, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x23, 0x00, 0x00, 0x08, 0xf6, 0x00, 0x07, 0x08, 0x1c, + 0x48, 0xb0, 0xa0, 0xc1, 0x83, 0x02, 0x09, 0x1d, 0x42, 0x54, 0xc3, 0x05, 0x14, 0x43, 0x4b, 0x9a, + 0x20, 0x24, 0x28, 0xec, 0x57, 0x8b, 0x83, 0x35, 0x0e, 0x05, 0x9b, 0xe8, 0x6c, 0x85, 0xc0, 0x28, + 0x43, 0x06, 0x0c, 0xb9, 0x38, 0x90, 0x88, 0xb0, 0x83, 0xce, 0x7a, 0x0c, 0x60, 0x62, 0xa5, 0x8a, + 0x94, 0x42, 0x4e, 0x48, 0x0e, 0xe4, 0x72, 0x92, 0x20, 0xa1, 0x1a, 0x03, 0x88, 0xb8, 0xe4, 0x32, + 0x71, 0x00, 0x22, 0x00, 0x04, 0x11, 0x09, 0x64, 0x52, 0x48, 0xe6, 0xc4, 0x90, 0x02, 0x8b, 0x5c, + 0x2c, 0xc4, 0xb3, 0xe7, 0x40, 0x26, 0x40, 0x07, 0x18, 0x1a, 0xe0, 0x82, 0x0a, 0x13, 0xa7, 0x04, + 0x9b, 0x0d, 0x00, 0x80, 0x13, 0xab, 0xc1, 0x25, 0x03, 0x84, 0x19, 0xf5, 0x2a, 0xf0, 0xd0, 0x80, + 0x65, 0x63, 0xc9, 0x0e, 0x0b, 0x20, 0x96, 0x6c, 0x41, 0xb3, 0x5c, 0xdd, 0x12, 0x04, 0x2b, 0x55, + 0xee, 0x40, 0x42, 0x02, 0x9d, 0xa5, 0x75, 0x0a, 0x2c, 0x80, 0x40, 0x00, 0x44, 0xe4, 0xaa, 0x70, + 0x96, 0x55, 0x25, 0x59, 0x43, 0x7e, 0x09, 0xea, 0xf5, 0x4a, 0x64, 0xa3, 0xc1, 0x61, 0x58, 0x89, + 0x48, 0x3c, 0xd8, 0xc4, 0xe3, 0x44, 0x9a, 0x3d, 0xcd, 0x72, 0x74, 0x4a, 0x68, 0xef, 0xca, 0xa8, + 0x13, 0x17, 0x75, 0x35, 0x28, 0xc8, 0xab, 0x50, 0xbb, 0x36, 0x9b, 0x12, 0x44, 0x4c, 0x16, 0x00, + 0x15, 0x42, 0x56, 0x06, 0x26, 0x46, 0x6d, 0xd7, 0x4f, 0x18, 0x31, 0x63, 0x04, 0x82, 0x79, 0x71, + 0x04, 0x74, 0x4f, 0x20, 0x08, 0x75, 0x78, 0xc5, 0xb5, 0xc6, 0xa0, 0x9b, 0x12, 0x5e, 0x03, 0xd4, + 0x29, 0x4e, 0x30, 0x8c, 0x5b, 0x1f, 0x05, 0xd9, 0xf4, 0x91, 0x8b, 0x82, 0xe0, 0x74, 0xda, 0x58, + 0x03, 0x02, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x03, 0x00, 0x2c, 0x13, 0x00, 0x1a, 0x00, + 0x16, 0x00, 0x23, 0x00, 0x00, 0x08, 0xf8, 0x00, 0x07, 0x08, 0x1c, 0x48, 0xb0, 0xa0, 0xc1, 0x83, + 0x00, 0x9c, 0x0d, 0x4b, 0xc4, 0x62, 0x40, 0xa2, 0x61, 0xce, 0x00, 0x1c, 0x24, 0xe8, 0x8c, 0xc9, + 0x44, 0x26, 0xce, 0x02, 0x4c, 0x3c, 0xd4, 0x62, 0xc0, 0x8a, 0x42, 0x45, 0x08, 0x11, 0x29, 0x78, + 0x48, 0x62, 0xc1, 0x61, 0x1d, 0x9f, 0x34, 0xa1, 0xb2, 0xc4, 0xa3, 0xc1, 0x61, 0x1a, 0x07, 0x1a, + 0xeb, 0xa8, 0x64, 0x40, 0x21, 0x15, 0x13, 0x05, 0x2e, 0x89, 0x49, 0xc5, 0x85, 0x40, 0x25, 0x4e, + 0x72, 0x0e, 0xac, 0x21, 0x4c, 0xe0, 0x30, 0x8f, 0x86, 0x3a, 0x0a, 0x1d, 0x78, 0x28, 0x40, 0xb0, + 0x1a, 0x36, 0x09, 0x29, 0x5d, 0x3a, 0x00, 0x4a, 0x80, 0x66, 0x1d, 0x5d, 0xac, 0xa0, 0x4a, 0x90, + 0x25, 0xd7, 0x83, 0xcd, 0x18, 0x7d, 0x35, 0x58, 0x64, 0xc8, 0xd8, 0x82, 0xcd, 0xb0, 0x9e, 0x15, + 0xa8, 0x42, 0x58, 0xb0, 0x1e, 0x6b, 0x07, 0x30, 0xd1, 0x78, 0x74, 0xad, 0x20, 0x81, 0x84, 0xe0, + 0x8e, 0x5d, 0xd1, 0x84, 0xe9, 0x54, 0xaa, 0xce, 0x08, 0x02, 0x30, 0xf4, 0xb5, 0x69, 0xc1, 0x60, + 0x23, 0x97, 0x1a, 0x36, 0xd8, 0x0c, 0x67, 0x4e, 0x44, 0x26, 0x0f, 0x46, 0xc9, 0xa9, 0x82, 0x90, + 0xd0, 0xc0, 0x13, 0xa3, 0xc4, 0x9c, 0xd8, 0xc4, 0xb1, 0x41, 0xcc, 0x42, 0x13, 0x1d, 0x6c, 0x4b, + 0xb5, 0xd9, 0x56, 0x82, 0x2a, 0x0e, 0x7d, 0x0d, 0x66, 0x59, 0xa0, 0x95, 0xd6, 0x71, 0xcf, 0xea, + 0x10, 0x33, 0xa6, 0x4b, 0xa4, 0x01, 0x5f, 0xc0, 0xec, 0x58, 0x44, 0xf5, 0xc4, 0x00, 0x36, 0x6c, + 0x0a, 0xfa, 0xa1, 0x8a, 0x25, 0x78, 0xc1, 0x36, 0x59, 0xb8, 0xe6, 0xf9, 0x1d, 0x1b, 0x48, 0x1a, + 0x82, 0x6a, 0x84, 0x8c, 0x3d, 0xa1, 0x67, 0xc0, 0x99, 0x3d, 0xd2, 0x63, 0x0b, 0x0d, 0x08, 0x00, + 0x21, 0xf9, 0x04, 0x05, 0x07, 0x00, 0x03, 0x00, 0x2c, 0x13, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x23, + 0x00, 0x00, 0x08, 0xf8, 0x00, 0x07, 0x08, 0x1c, 0x48, 0xb0, 0xa0, 0xc1, 0x83, 0x4d, 0x86, 0xfc, + 0x82, 0xa2, 0xa2, 0x06, 0xa2, 0x43, 0xcd, 0x02, 0x1c, 0x1c, 0x08, 0x40, 0x50, 0x8d, 0x89, 0x88, + 0x08, 0x4d, 0x6c, 0x82, 0x48, 0x20, 0x13, 0x41, 0x56, 0xa4, 0xb4, 0x20, 0xe8, 0xc2, 0x99, 0x44, + 0x82, 0x4d, 0x88, 0x0c, 0x70, 0xb1, 0x64, 0x8a, 0x15, 0x41, 0x51, 0x46, 0x12, 0x54, 0xe1, 0xac, + 0x60, 0x94, 0x95, 0x56, 0xa8, 0x38, 0x91, 0x79, 0xd0, 0x85, 0x46, 0x81, 0xce, 0x46, 0x72, 0x91, + 0xc2, 0x65, 0x22, 0x41, 0x44, 0x12, 0x17, 0x15, 0x65, 0x52, 0xd4, 0xe8, 0xcc, 0x66, 0x03, 0x8a, + 0x8c, 0x2c, 0x22, 0xc8, 0x69, 0xc1, 0x43, 0x03, 0xb0, 0x0e, 0x60, 0x62, 0xb5, 0x60, 0xa2, 0x00, + 0x37, 0xbb, 0x1a, 0x54, 0xb1, 0xa8, 0xa3, 0xd8, 0x82, 0x2a, 0x82, 0x85, 0x3d, 0x3b, 0x90, 0xac, + 0x56, 0xb6, 0x02, 0x81, 0x05, 0x90, 0x0a, 0x57, 0x20, 0x56, 0xa5, 0x75, 0x55, 0x40, 0x1d, 0xb0, + 0x84, 0xa7, 0xd8, 0x28, 0x27, 0x01, 0x98, 0x15, 0xbb, 0x42, 0x18, 0x41, 0x61, 0x5c, 0xbb, 0xba, + 0xd8, 0x4b, 0x90, 0xd0, 0x0a, 0xab, 0x34, 0x27, 0xf6, 0x75, 0x7a, 0xe8, 0xa4, 0x41, 0x00, 0x89, + 0x0f, 0xd6, 0x68, 0xe2, 0x74, 0x89, 0xd1, 0xb7, 0x13, 0x7f, 0x1e, 0x64, 0x3c, 0x11, 0xc0, 0x63, + 0x83, 0x3d, 0x38, 0x5b, 0x75, 0xd6, 0xc3, 0xa0, 0xb1, 0xb3, 0x4d, 0xac, 0x58, 0x19, 0x68, 0xb8, + 0xae, 0xed, 0x3f, 0x91, 0x72, 0x0f, 0xfc, 0xf2, 0x25, 0x4b, 0x57, 0x21, 0x6c, 0x6c, 0x0b, 0xf4, + 0x6d, 0x90, 0x0d, 0x33, 0xb1, 0x7b, 0x82, 0x13, 0xd4, 0x73, 0xf6, 0x84, 0x1a, 0x82, 0x69, 0x80, + 0xb0, 0x15, 0xb2, 0x47, 0xa0, 0x9e, 0x13, 0xb6, 0x01, 0x58, 0x0d, 0x08, 0x00, 0x21, 0xf9, 0x04, + 0x05, 0x35, 0x00, 0x03, 0x00, 0x2c, 0x13, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x23, 0x00, 0x00, 0x08, + 0xd2, 0x00, 0x07, 0x08, 0x1c, 0x48, 0xb0, 0xa0, 0xc1, 0x83, 0xc2, 0x96, 0x44, 0x19, 0xd0, 0x42, + 0xa0, 0xa0, 0x65, 0x07, 0x09, 0x4e, 0x39, 0xd4, 0x23, 0xe2, 0x2f, 0x61, 0x11, 0x85, 0x11, 0x11, + 0x48, 0x64, 0xc9, 0x00, 0x41, 0x05, 0x57, 0x34, 0x33, 0x28, 0x8c, 0xcb, 0x80, 0x15, 0x43, 0x04, + 0x2a, 0x71, 0x62, 0xb0, 0xc7, 0xc8, 0x81, 0x8b, 0x36, 0x72, 0x21, 0x44, 0x68, 0x63, 0xc4, 0x93, + 0x18, 0x05, 0x1a, 0xe3, 0x38, 0xc0, 0xc5, 0xcd, 0x81, 0xc3, 0x04, 0x36, 0xa9, 0x21, 0x70, 0xc5, + 0xcf, 0x82, 0x10, 0x9d, 0x09, 0x24, 0xf4, 0xe4, 0x28, 0x41, 0x90, 0x41, 0x07, 0x38, 0xf1, 0xe9, + 0x54, 0xe0, 0x42, 0x60, 0x55, 0x0d, 0x42, 0x09, 0x00, 0x25, 0x6b, 0x41, 0x18, 0x01, 0xb0, 0x7a, + 0x1d, 0x98, 0x28, 0x40, 0xd4, 0xb1, 0x03, 0x0c, 0x0d, 0x48, 0x89, 0x76, 0x80, 0xc7, 0x26, 0x15, + 0xc7, 0xf6, 0xc8, 0x79, 0x08, 0x6d, 0x5d, 0x81, 0xc1, 0x98, 0x78, 0xe5, 0xd2, 0x84, 0x20, 0x21, + 0xa3, 0x4e, 0x57, 0x10, 0x32, 0xd8, 0x2c, 0xee, 0xcf, 0x97, 0x06, 0x41, 0xfe, 0xdc, 0x19, 0x71, + 0x91, 0xc9, 0x88, 0x5c, 0x82, 0x2d, 0x6e, 0x4b, 0xb0, 0x21, 0xe5, 0x45, 0x44, 0x0f, 0x4a, 0x76, + 0xaa, 0xc2, 0x20, 0xa3, 0xac, 0x39, 0x29, 0x8b, 0x16, 0xf8, 0xa5, 0xf4, 0x17, 0xb4, 0x7d, 0x22, + 0xa6, 0x3e, 0x6a, 0x22, 0x22, 0x16, 0xd1, 0x79, 0xb2, 0x0a, 0x19, 0x3d, 0x60, 0xb5, 0x19, 0xda, + 0x3f, 0x03, 0x02, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x1a, 0x00, 0x1b, + 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x04, 0x0a, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, + 0x00, 0x04, 0x00, 0x2c, 0x16, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x00, 0x08, 0x23, 0x00, + 0x09, 0x08, 0x1c, 0x48, 0xb0, 0x20, 0x05, 0x05, 0xef, 0xda, 0x99, 0xb3, 0x77, 0x8e, 0x0f, 0x2e, + 0x02, 0x19, 0x1e, 0xa0, 0xb3, 0xf7, 0x45, 0x8b, 0x0a, 0x82, 0xbd, 0xc0, 0x50, 0xa2, 0x51, 0x70, + 0x60, 0x40, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x06, 0x00, 0x03, 0x00, 0x2c, 0x15, 0x00, 0x1b, 0x00, + 0x10, 0x00, 0x04, 0x00, 0x00, 0x08, 0x32, 0x00, 0x07, 0x08, 0x1c, 0x28, 0x30, 0x9d, 0xba, 0x78, + 0x1b, 0x22, 0x10, 0x1c, 0xa8, 0x2e, 0x9d, 0xc3, 0x74, 0xe8, 0xe8, 0xe9, 0xa3, 0xe7, 0x81, 0x81, + 0xc0, 0x08, 0xee, 0x1c, 0xba, 0xc3, 0x30, 0xa2, 0x11, 0x8c, 0x81, 0x30, 0x4a, 0x0d, 0xc0, 0x82, + 0xcf, 0x1e, 0x3e, 0x6e, 0x00, 0x16, 0x2e, 0x0c, 0x08, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, 0x00, + 0x04, 0x00, 0x2c, 0x14, 0x00, 0x1c, 0x00, 0x12, 0x00, 0x04, 0x00, 0x00, 0x08, 0x38, 0x00, 0x09, + 0x08, 0x1c, 0x48, 0x30, 0x9d, 0xc1, 0x74, 0xef, 0x08, 0x2a, 0x1c, 0x88, 0xc1, 0x20, 0x3a, 0x74, + 0xea, 0xda, 0x35, 0x10, 0x18, 0x01, 0x5e, 0x3a, 0x75, 0x08, 0x32, 0x34, 0xc0, 0x00, 0xe6, 0xd9, + 0x16, 0x19, 0x30, 0x68, 0x98, 0xfa, 0xd2, 0x87, 0x00, 0x3e, 0x76, 0xed, 0x06, 0xe0, 0x02, 0xe3, + 0xa9, 0xc6, 0xc2, 0x81, 0x01, 0x01, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x02, 0x00, 0x2c, + 0x14, 0x00, 0x1d, 0x00, 0x13, 0x00, 0x05, 0x00, 0x00, 0x08, 0x3e, 0x00, 0x05, 0x08, 0x1c, 0x48, + 0x50, 0x60, 0xba, 0x83, 0x05, 0x13, 0x0a, 0x78, 0x10, 0xef, 0xa0, 0xc3, 0x74, 0xea, 0x1a, 0x08, + 0xc8, 0xe0, 0x2e, 0x9d, 0xc0, 0x11, 0x15, 0x21, 0xda, 0x8b, 0x74, 0xc5, 0x93, 0x27, 0x4a, 0x5f, + 0xf0, 0x19, 0x99, 0x66, 0x4e, 0x5d, 0x3c, 0x12, 0xea, 0xd8, 0xd9, 0x7b, 0xa6, 0x45, 0x21, 0x41, + 0x5d, 0xa6, 0x4c, 0x69, 0x61, 0xe1, 0x92, 0x60, 0x40, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, + 0x00, 0x00, 0x2c, 0x13, 0x00, 0x1f, 0x00, 0x15, 0x00, 0x04, 0x00, 0x00, 0x08, 0x39, 0x00, 0x01, + 0x08, 0x1c, 0x48, 0x30, 0x9d, 0xc1, 0x83, 0x06, 0xdb, 0x31, 0x10, 0xb8, 0xc1, 0xe0, 0x40, 0x84, + 0xe9, 0xd0, 0xd9, 0xfb, 0x42, 0xf1, 0x8b, 0x3d, 0x0f, 0x00, 0x82, 0x7d, 0x31, 0xe7, 0x4e, 0x1d, + 0x42, 0x73, 0xdb, 0x28, 0xc1, 0x20, 0x28, 0xb0, 0xc6, 0x40, 0x15, 0xcb, 0x88, 0x3d, 0x7b, 0x46, + 0x49, 0x96, 0x0a, 0x92, 0x30, 0x03, 0x02, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x07, 0x00, 0x01, 0x00, + 0x2c, 0x13, 0x00, 0x20, 0x00, 0x15, 0x00, 0x04, 0x00, 0x00, 0x08, 0x39, 0x00, 0x03, 0x08, 0x1c, + 0x48, 0x50, 0x60, 0xba, 0x83, 0x08, 0xd3, 0x09, 0xa4, 0xa0, 0x4e, 0x61, 0xc1, 0x84, 0xe9, 0xd8, + 0xd9, 0x9b, 0xa8, 0x0f, 0x5d, 0x84, 0x55, 0xdb, 0xcc, 0x45, 0xf0, 0x00, 0x51, 0x9d, 0xbd, 0x2f, + 0xa6, 0x6a, 0x10, 0x94, 0x11, 0x4b, 0x20, 0x0b, 0x5f, 0x5f, 0xb6, 0xe1, 0xc3, 0x17, 0xe9, 0x19, + 0x92, 0x82, 0x05, 0x03, 0x02, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x13, + 0x00, 0x21, 0x00, 0x15, 0x00, 0x04, 0x00, 0x00, 0x08, 0x3b, 0x00, 0x01, 0x08, 0x1c, 0x48, 0x90, + 0x60, 0xba, 0x83, 0x08, 0x01, 0x20, 0x38, 0x58, 0x50, 0x20, 0x42, 0x84, 0xea, 0xd8, 0xa1, 0x4b, + 0x87, 0x01, 0xd3, 0x36, 0x73, 0xf1, 0xd4, 0x3d, 0x44, 0x88, 0xce, 0xde, 0x17, 0x53, 0x32, 0x6a, + 0xc0, 0xd0, 0xf2, 0x49, 0x20, 0x0b, 0x4f, 0x5f, 0xec, 0x99, 0x63, 0xc7, 0xce, 0xde, 0x36, 0x4a, + 0x32, 0x1a, 0x0a, 0x0c, 0x08, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, 0x00, 0x00, 0x00, 0x2c, 0x13, + 0x00, 0x22, 0x00, 0x15, 0x00, 0x05, 0x00, 0x00, 0x08, 0x3c, 0x00, 0x01, 0x08, 0x1c, 0x48, 0xb0, + 0x20, 0x80, 0x74, 0x08, 0x05, 0x7a, 0x48, 0x68, 0x50, 0x20, 0xc2, 0x87, 0x0f, 0xdf, 0x81, 0xc1, + 0xc7, 0x0e, 0xa2, 0x45, 0x84, 0xec, 0xf0, 0x7d, 0xa1, 0xe4, 0x49, 0x53, 0x35, 0x81, 0x2c, 0x3c, + 0x7d, 0xb1, 0xa7, 0xee, 0xa1, 0x3a, 0x7d, 0x5f, 0x3c, 0xd5, 0x68, 0x58, 0x50, 0x86, 0xa9, 0x52, + 0xa6, 0x64, 0xb0, 0x14, 0x18, 0x10, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, 0x2c, + 0x13, 0x00, 0x24, 0x00, 0x15, 0x00, 0x04, 0x00, 0x00, 0x08, 0x36, 0x00, 0xdd, 0xa5, 0x4b, 0x07, + 0xa0, 0xa0, 0xc1, 0x83, 0x00, 0x06, 0x2a, 0x4c, 0x07, 0x6f, 0x9b, 0x3d, 0x76, 0x0b, 0x0d, 0x2e, + 0x1c, 0x68, 0x0e, 0x5f, 0xa4, 0x48, 0xdb, 0x0a, 0xd6, 0xf0, 0x14, 0x89, 0xde, 0xc4, 0x74, 0xe8, + 0xec, 0x7d, 0x31, 0xa5, 0x02, 0x21, 0x42, 0x2d, 0x57, 0xbe, 0xa8, 0xa4, 0x94, 0xc3, 0x64, 0xc1, + 0x80, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x07, 0x00, 0x01, 0x00, 0x2c, 0x13, 0x00, 0x25, 0x00, 0x15, + 0x00, 0x04, 0x00, 0x00, 0x08, 0x36, 0x00, 0xdb, 0xa5, 0x4b, 0x17, 0xa0, 0xa0, 0xc1, 0x83, 0x06, + 0x07, 0x0e, 0x54, 0x87, 0x8f, 0x1e, 0x3a, 0x85, 0x08, 0x03, 0x28, 0x5c, 0xc8, 0x4e, 0x9f, 0xbd, + 0x82, 0x32, 0x34, 0x6d, 0xa3, 0x37, 0x71, 0x22, 0x3b, 0x7b, 0xcf, 0xb4, 0xa8, 0x88, 0x18, 0x40, + 0x85, 0xa9, 0x2f, 0xdb, 0xec, 0xa9, 0x8c, 0x44, 0x49, 0x46, 0xc4, 0x80, 0x00, 0x21, 0xf9, 0x04, + 0x05, 0x03, 0x00, 0x01, 0x00, 0x2c, 0x13, 0x00, 0x26, 0x00, 0x15, 0x00, 0x04, 0x00, 0x00, 0x08, + 0x34, 0x00, 0xdd, 0xa5, 0x4b, 0x17, 0xa0, 0xa0, 0xc1, 0x83, 0x07, 0x07, 0xa6, 0x83, 0xe7, 0x41, + 0xe1, 0x40, 0x84, 0x06, 0x1d, 0x0e, 0x7c, 0xe7, 0x4c, 0x86, 0x2c, 0x4a, 0x5f, 0xcc, 0x49, 0x94, + 0x68, 0x6e, 0xdb, 0x15, 0x59, 0x8c, 0x20, 0x9a, 0xfa, 0x62, 0x8f, 0x9d, 0xba, 0x74, 0xec, 0xec, + 0x7d, 0x31, 0x85, 0x30, 0x20, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x13, + 0x00, 0x28, 0x00, 0x15, 0x00, 0x04, 0x00, 0x00, 0x08, 0x38, 0x00, 0x11, 0xa4, 0x1b, 0x38, 0x10, + 0x80, 0xc1, 0x83, 0x08, 0x09, 0xa6, 0x53, 0x60, 0x84, 0x12, 0xa5, 0x67, 0xdb, 0xe8, 0x29, 0x4c, + 0x07, 0x60, 0x22, 0x3a, 0x7b, 0x91, 0x8c, 0x20, 0x04, 0xa0, 0x62, 0xcb, 0x17, 0x7b, 0xe8, 0x14, + 0xb2, 0xb3, 0xf7, 0x6c, 0xc6, 0xc6, 0x93, 0x00, 0x60, 0x98, 0xf2, 0xe4, 0x49, 0x0b, 0x8b, 0x93, + 0x01, 0x01, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, 0x00, 0x01, 0x00, 0x2c, 0x13, 0x00, 0x29, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x00, 0x08, 0x3a, 0x00, 0x29, 0xa8, 0x4b, 0x47, 0x30, 0x5d, 0x80, 0x83, + 0x08, 0x13, 0x06, 0x20, 0xa8, 0x8e, 0x41, 0x00, 0x6e, 0x91, 0x22, 0x6d, 0xa3, 0x37, 0x90, 0x60, + 0xc2, 0x82, 0x04, 0x21, 0x28, 0x3c, 0xa8, 0xe5, 0x8b, 0x3d, 0x74, 0x18, 0xd3, 0xa9, 0xa3, 0x17, + 0x49, 0x53, 0xb0, 0x8d, 0x09, 0x65, 0x78, 0x7a, 0xf6, 0xe5, 0x0b, 0x25, 0x2d, 0x2a, 0x12, 0x06, + 0x04, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x06, 0x00, 0x04, 0x00, 0x2c, 0x14, 0x00, 0x2a, 0x00, 0x13, + 0x00, 0x04, 0x00, 0x00, 0x08, 0x30, 0x00, 0x1f, 0xa4, 0x1b, 0x38, 0x90, 0x80, 0xc1, 0x83, 0x08, + 0x0f, 0x66, 0xc0, 0x87, 0xcf, 0x1e, 0x3d, 0x74, 0x04, 0x13, 0x12, 0x84, 0x47, 0x21, 0x21, 0x8d, + 0x2b, 0xf8, 0xd8, 0x11, 0xdc, 0xd8, 0x6e, 0x40, 0x42, 0x84, 0x30, 0x3c, 0x7d, 0xd9, 0x86, 0x6f, + 0xdb, 0x17, 0x4f, 0x07, 0x03, 0x02, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x04, 0x00, 0x01, 0x00, 0x2c, + 0x15, 0x00, 0x2b, 0x00, 0x11, 0x00, 0x04, 0x00, 0x00, 0x08, 0x2c, 0x00, 0xdd, 0xa5, 0x1b, 0x98, + 0x2e, 0x80, 0xc1, 0x83, 0x08, 0x0d, 0x46, 0xa0, 0xa7, 0x8f, 0x1e, 0x3a, 0x82, 0x09, 0x0d, 0xaa, + 0x4b, 0x08, 0xc3, 0x53, 0x24, 0x7d, 0xea, 0x08, 0x0e, 0x54, 0xd7, 0x20, 0xe2, 0x41, 0x2d, 0xcf, + 0xb6, 0xd9, 0x1b, 0xc9, 0xcd, 0x60, 0x40, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, + 0x2c, 0x16, 0x00, 0x2c, 0x00, 0x0e, 0x00, 0x04, 0x00, 0x00, 0x08, 0x27, 0x00, 0xdb, 0xa5, 0x1b, + 0x08, 0xa0, 0xa0, 0xc1, 0x83, 0x23, 0xd8, 0x99, 0x43, 0x37, 0x90, 0xe0, 0xc1, 0x83, 0x35, 0xb4, + 0x3c, 0xb3, 0xc7, 0xae, 0xe1, 0xc0, 0x0d, 0x0f, 0x0b, 0xc2, 0xa0, 0x14, 0xc9, 0x1e, 0x3d, 0x73, + 0x19, 0x02, 0x02, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x17, 0x00, 0x2d, + 0x00, 0x0c, 0x00, 0x04, 0x00, 0x00, 0x08, 0x24, 0x00, 0xd5, 0xa5, 0x4b, 0x07, 0xa0, 0xa0, 0x41, + 0x83, 0x1b, 0xd8, 0xb1, 0x1b, 0x38, 0xf0, 0x60, 0x41, 0x28, 0x2a, 0x64, 0x78, 0xfa, 0x62, 0x0f, + 0x1d, 0xc3, 0x76, 0x0e, 0x0b, 0xe6, 0x78, 0x86, 0x4f, 0xdf, 0x86, 0x80, 0x00, 0x21, 0xf9, 0x04, + 0x05, 0x07, 0x00, 0x00, 0x00, 0x2c, 0x17, 0x00, 0x2e, 0x00, 0x0c, 0x00, 0x04, 0x00, 0x00, 0x08, + 0x23, 0x00, 0x01, 0xa4, 0x4b, 0x07, 0xa0, 0xa0, 0x41, 0x83, 0x19, 0xe0, 0xb1, 0x63, 0xa7, 0x6e, + 0x20, 0xc1, 0x83, 0x06, 0x69, 0x78, 0xfa, 0xa2, 0xaf, 0x61, 0xba, 0x07, 0x10, 0x0d, 0x6a, 0x79, + 0x86, 0x6f, 0x44, 0x40, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x18, 0x00, + 0x2f, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, 0x08, 0x23, 0x00, 0x01, 0xa4, 0x1b, 0x08, 0xa0, 0xa0, + 0x41, 0x00, 0x1b, 0xd8, 0xd1, 0x33, 0x87, 0x6e, 0x60, 0x3a, 0x83, 0xcd, 0x54, 0x14, 0x84, 0x61, + 0xea, 0x8b, 0x3d, 0x75, 0x0f, 0x0f, 0x1e, 0xe4, 0xf5, 0xcc, 0x41, 0x40, 0x00, 0x21, 0xf9, 0x04, + 0x05, 0x04, 0x00, 0x00, 0x00, 0x2c, 0x18, 0x00, 0x30, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, 0x08, + 0x1e, 0x00, 0x01, 0xa4, 0x1b, 0x98, 0x0e, 0x80, 0xc1, 0x83, 0x23, 0xcc, 0xd9, 0x5b, 0x68, 0x4e, + 0xdd, 0xc0, 0x83, 0x10, 0x01, 0x20, 0x79, 0x66, 0x8f, 0x5d, 0x86, 0x88, 0x18, 0x03, 0x02, 0x00, + 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x18, 0x00, 0x31, 0x00, 0x0a, 0x00, 0x03, + 0x00, 0x00, 0x08, 0x1a, 0x00, 0x01, 0xa4, 0x1b, 0x48, 0x10, 0x80, 0xc1, 0x0c, 0xf4, 0xf0, 0x45, + 0x5a, 0x68, 0x8f, 0x5d, 0x3a, 0x75, 0x06, 0x23, 0x1a, 0x64, 0x61, 0x8a, 0x5b, 0x40, 0x00, 0x21, + 0xf9, 0x04, 0x05, 0x07, 0x00, 0x00, 0x00, 0x2c, 0x19, 0x00, 0x32, 0x00, 0x09, 0x00, 0x02, 0x00, + 0x00, 0x08, 0x14, 0x00, 0xd5, 0xa5, 0x1b, 0x48, 0x10, 0x00, 0x00, 0x12, 0x91, 0x28, 0x99, 0x32, + 0xe5, 0xe9, 0x0b, 0x3e, 0x04, 0x01, 0x01, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, + 0x2c, 0x19, 0x00, 0x33, 0x00, 0x09, 0x00, 0x02, 0x00, 0x00, 0x08, 0x15, 0x00, 0x11, 0xa8, 0x53, + 0x67, 0xce, 0x9e, 0x3d, 0x73, 0xe8, 0x00, 0x00, 0xa0, 0x70, 0x4b, 0xa1, 0x42, 0x66, 0x00, 0x02, + 0x02, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x1a, 0x00, 0x33, 0x00, 0x07, + 0x00, 0x02, 0x00, 0x00, 0x08, 0x0e, 0x00, 0x01, 0xa4, 0x1b, 0x38, 0x50, 0x1d, 0x80, 0x11, 0x08, + 0x11, 0x32, 0x08, 0x08, 0x00, 0x21, 0xf9, 0x04, 0x05, 0x21, 0x00, 0x00, 0x00, 0x2c, 0x1d, 0x00, + 0x34, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x84, 0x0b, 0x00, 0x3b, +}; + +const lv_img_dsc_t img_bulb_gif = { + .header.always_zero = 0, + .header.w = 0, + .header.h = 0, + .data_size = 0, + .header.cf = LV_IMG_CF_RAW, + .data = img_blub_gif_map, +}; diff --git a/examples/libs/gif/index.rst b/examples/libs/gif/index.rst new file mode 100644 index 0000000000..d27c22bcb8 --- /dev/null +++ b/examples/libs/gif/index.rst @@ -0,0 +1,6 @@ +Open a GIF image from file and variable +""""""""""""""""""""""""""""""""""""""""""""""" + +.. lv_example:: libs/bmp/lv_example_gif_1 + :language: c + diff --git a/examples/libs/gif/lv_example_gif.h b/examples/libs/gif/lv_example_gif.h new file mode 100644 index 0000000000..558ff917cd --- /dev/null +++ b/examples/libs/gif/lv_example_gif.h @@ -0,0 +1,38 @@ +/** + * @file lv_example_gif.h + * + */ + +#ifndef LV_EXAMPLE_GIF_H +#define LV_EXAMPLE_GIF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void lv_example_gif_1(void); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_EXAMPLE_GIF_H*/ diff --git a/examples/libs/gif/lv_example_gif_1.c b/examples/libs/gif/lv_example_gif_1.c new file mode 100644 index 0000000000..66f2a3db61 --- /dev/null +++ b/examples/libs/gif/lv_example_gif_1.c @@ -0,0 +1,23 @@ +#include "../../lv_examples.h" +#if LV_USE_GIF && LV_BUILD_EXAMPLES + +/** + * Open a GIF image from a file and a variable + */ +void lv_example_gif_1(void) +{ + LV_IMG_DECLARE(img_bulb_gif); + lv_obj_t * img; + + img = lv_gif_create(lv_scr_act()); + lv_gif_set_src(img, &img_bulb_gif); + lv_obj_align(img, LV_ALIGN_LEFT_MID, 20, 0); + + img = lv_gif_create(lv_scr_act()); + /* Assuming a File system is attached to letter 'A' + * E.g. set LV_USE_FS_STDIO 'A' in lv_conf.h */ + lv_gif_set_src(img, "A:lvgl/examples/libs/gif/bulb.gif"); + lv_obj_align(img, LV_ALIGN_RIGHT_MID, -20, 0); +} + +#endif diff --git a/examples/libs/lv_example_libs.h b/examples/libs/lv_example_libs.h new file mode 100644 index 0000000000..a63524b84e --- /dev/null +++ b/examples/libs/lv_example_libs.h @@ -0,0 +1,43 @@ +/** + * @file lv_example_libs.h + * + */ + +#ifndef LV_EXAMPLE_LIBS_H +#define LV_EXAMPLE_LIBS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "bmp/lv_example_bmp.h" +#include "gif/lv_example_gif.h" +#include "png/lv_example_png.h" +#include "sjpg/lv_example_sjpg.h" +#include "qrcode/lv_example_qrcode.h" +#include "freetype/lv_example_freetype.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_EXAMPLE_LIBS_H*/ diff --git a/examples/libs/png/img_wing_png.c b/examples/libs/png/img_wing_png.c new file mode 100644 index 0000000000..be69be1ee5 --- /dev/null +++ b/examples/libs/png/img_wing_png.c @@ -0,0 +1,348 @@ +#include "../../../lvgl.h" +#if LV_USE_PNG && LV_BUILD_EXAMPLES + + +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_PNG_DECODER_TEST +#define LV_ATTRIBUTE_IMG_PNG_DECODER_TEST +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_PNG_DECODER_TEST uint8_t img_wink_png_map[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x32, 0x08, 0x06, 0x00, 0x00, 0x00, 0x1e, 0x3f, 0x88, + 0xb1, 0x00, 0x00, 0x00, 0x06, 0x62, 0x4b, 0x47, 0x44, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0xa0, + 0xbd, 0xa7, 0x93, 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, 0x00, + 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, + 0x07, 0xe5, 0x05, 0x07, 0x0c, 0x1b, 0x26, 0xad, 0x4b, 0x20, 0x5b, 0x00, 0x00, 0x00, 0x1d, 0x69, + 0x54, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, + 0x64, 0x2e, 0x65, 0x07, 0x00, 0x00, 0x13, 0x8a, 0x49, 0x44, 0x41, 0x54, 0x68, 0xde, 0xad, 0x9a, + 0x69, 0x94, 0x5d, 0xd5, 0x75, 0xe7, 0x7f, 0xe7, 0xdc, 0x7b, 0xdf, 0x7d, 0x43, 0xd5, 0xab, 0xe9, + 0x55, 0x49, 0x05, 0xaa, 0xd2, 0x80, 0x10, 0x12, 0x48, 0xa8, 0x24, 0x04, 0x66, 0x72, 0x6c, 0xec, + 0xd8, 0xd0, 0x6e, 0x27, 0xc4, 0x6d, 0x4c, 0x08, 0x21, 0xdd, 0xed, 0x24, 0xbd, 0x1c, 0x67, 0x39, + 0xcb, 0x6d, 0x33, 0xc7, 0x6e, 0x9b, 0xc6, 0x18, 0x64, 0x90, 0x43, 0xda, 0xb1, 0x1d, 0x7f, 0x31, + 0x89, 0x93, 0xb8, 0x33, 0x38, 0x84, 0x15, 0x63, 0xe3, 0x38, 0x84, 0x80, 0x99, 0x67, 0x24, 0x10, + 0x83, 0x06, 0x84, 0x84, 0x54, 0x52, 0x95, 0x6a, 0x7e, 0xf5, 0xa6, 0x3b, 0x9c, 0xb3, 0xfb, 0xc3, + 0xbd, 0x6f, 0x28, 0xa1, 0xac, 0x0e, 0x8e, 0x6b, 0xad, 0xb3, 0x6e, 0xd5, 0x7b, 0xa7, 0xce, 0xd9, + 0xff, 0xb3, 0xa7, 0xff, 0xde, 0xf7, 0x28, 0x7e, 0x8e, 0x9f, 0x97, 0x6f, 0x18, 0xe2, 0xec, 0x3b, + 0x8f, 0x03, 0xf0, 0xe0, 0xd5, 0x5b, 0x19, 0x1e, 0xdd, 0xa7, 0x4b, 0xab, 0x95, 0x5a, 0x98, 0xee, + 0xe9, 0x6d, 0xcc, 0x95, 0xcf, 0xcb, 0xf5, 0xc8, 0xe6, 0x6c, 0x97, 0x3e, 0x5d, 0x3b, 0x76, 0xd0, + 0x51, 0xaa, 0x28, 0x56, 0x10, 0xc3, 0xa2, 0x89, 0xd5, 0x74, 0x58, 0xe3, 0x50, 0xa3, 0xa6, 0x5e, + 0x15, 0xed, 0xbf, 0x30, 0x30, 0x5c, 0x38, 0x52, 0x9f, 0x1a, 0x8f, 0xe7, 0xaa, 0x3d, 0xb2, 0xed, + 0xee, 0xe3, 0xd2, 0x5e, 0x7f, 0x19, 0x67, 0xdf, 0x39, 0xf9, 0xae, 0x64, 0x52, 0xef, 0x66, 0xf2, + 0x4b, 0xd7, 0x0d, 0xb3, 0x65, 0xc7, 0x31, 0x00, 0x7e, 0x70, 0xee, 0x6a, 0xb5, 0xed, 0x13, 0xc7, + 0xfd, 0x58, 0x7a, 0x46, 0x95, 0xa9, 0x7c, 0xa2, 0xab, 0xa4, 0xaf, 0xcc, 0xe4, 0xe3, 0xb3, 0xf3, + 0xbd, 0x1a, 0x37, 0x23, 0x80, 0x80, 0xa8, 0xe4, 0x69, 0x01, 0x63, 0x51, 0x06, 0xc4, 0x28, 0x6c, + 0x03, 0x1a, 0x65, 0xa1, 0x51, 0xd3, 0xc7, 0xeb, 0x55, 0x1e, 0x08, 0x43, 0xef, 0xaf, 0x5d, 0x47, + 0xbf, 0x30, 0xb7, 0xa0, 0xcb, 0x5b, 0xef, 0x9e, 0x8a, 0x9a, 0xfb, 0xed, 0xba, 0x6e, 0x19, 0x9b, + 0x77, 0x4c, 0xfe, 0xe2, 0x80, 0xec, 0xff, 0xf6, 0xc7, 0x88, 0x8f, 0xfd, 0x88, 0xf5, 0x5f, 0x49, + 0xf6, 0x78, 0xe3, 0xcb, 0x5d, 0x5d, 0x5e, 0x2e, 0x7b, 0x6e, 0xb6, 0x18, 0x7f, 0x26, 0xdf, 0x6b, + 0xff, 0x4b, 0xd7, 0xa0, 0x42, 0x39, 0x2e, 0x38, 0x59, 0x20, 0x83, 0x68, 0x0f, 0x94, 0x87, 0xd2, + 0x39, 0x40, 0x83, 0x44, 0x88, 0xa9, 0x83, 0x09, 0xc0, 0x84, 0xa8, 0x30, 0x40, 0x05, 0x01, 0x84, + 0x06, 0x22, 0x45, 0xa3, 0x6c, 0xa9, 0x2e, 0xea, 0xb7, 0xea, 0x81, 0xf7, 0x4d, 0x1b, 0xdb, 0xfb, + 0x0e, 0x8e, 0x17, 0x0f, 0x5f, 0xf2, 0xdd, 0x23, 0x31, 0xc0, 0xab, 0x5f, 0x7b, 0x2f, 0x67, 0xdd, + 0xf8, 0xd8, 0x7f, 0x1c, 0xc8, 0xae, 0x1b, 0x47, 0xd8, 0xfc, 0xb5, 0xc3, 0x00, 0xfc, 0xf4, 0xd2, + 0x62, 0x66, 0xed, 0x07, 0x33, 0x9b, 0xb3, 0xdd, 0xe6, 0xf3, 0xdd, 0x83, 0x72, 0x55, 0xae, 0x17, + 0x54, 0xa6, 0x1b, 0x71, 0xfb, 0x20, 0xbb, 0x12, 0xd5, 0xb3, 0x09, 0x95, 0x3f, 0x1d, 0x72, 0x23, + 0xe0, 0xf5, 0x82, 0xd2, 0xed, 0x85, 0x24, 0x86, 0x60, 0x06, 0xea, 0x87, 0x91, 0xea, 0x5e, 0x98, + 0x7f, 0x05, 0x16, 0x0f, 0x41, 0x75, 0x0e, 0x55, 0xab, 0xa0, 0x02, 0x4b, 0x54, 0x83, 0xc5, 0xb2, + 0x3a, 0xdc, 0x08, 0xdd, 0xdb, 0xe6, 0xe6, 0xd5, 0x8f, 0x37, 0x7d, 0x7d, 0x61, 0x1c, 0xe0, 0xa5, + 0xeb, 0x57, 0xb0, 0xe5, 0xae, 0x23, 0x3f, 0x3f, 0x90, 0x5d, 0xd7, 0x95, 0xd8, 0xbc, 0x63, 0x3a, + 0xb5, 0xdb, 0x81, 0xc1, 0xbe, 0x11, 0xf9, 0xaf, 0x85, 0x12, 0x37, 0x15, 0x06, 0x28, 0x39, 0xd9, + 0x02, 0xf8, 0xa3, 0xd0, 0x77, 0x1e, 0xaa, 0xff, 0x62, 0xc8, 0xad, 0x04, 0x31, 0xe9, 0xb0, 0x24, + 0xf6, 0x74, 0x92, 0xed, 0x54, 0x3a, 0x50, 0x50, 0x3f, 0x02, 0x33, 0xcf, 0x20, 0x53, 0xcf, 0xc3, + 0xec, 0x01, 0x58, 0x9c, 0x46, 0x87, 0x50, 0xaf, 0xc2, 0x62, 0xc5, 0xf9, 0x49, 0xad, 0xae, 0xef, + 0x58, 0x73, 0x6b, 0xf9, 0x31, 0x80, 0x9d, 0x37, 0x0c, 0x33, 0x76, 0xe7, 0xb1, 0x77, 0x0f, 0x64, + 0xd7, 0xf5, 0x7d, 0x6c, 0xbe, 0x6b, 0x0e, 0x80, 0x3d, 0xb7, 0x0e, 0x9c, 0xd5, 0x73, 0x8a, 0xfa, + 0x52, 0xf7, 0x32, 0x75, 0xa5, 0xdf, 0x25, 0x90, 0x1d, 0x81, 0xbe, 0x8b, 0x50, 0x43, 0x97, 0x42, + 0x66, 0x28, 0x39, 0x6d, 0x31, 0x27, 0x59, 0x45, 0xda, 0xcf, 0x14, 0xa0, 0x12, 0x83, 0x60, 0xdb, + 0xf3, 0xb5, 0x87, 0x8a, 0xab, 0xc8, 0xec, 0x0b, 0xc8, 0xb1, 0xa7, 0x60, 0xe2, 0x15, 0x54, 0xb5, + 0x82, 0xc4, 0x9a, 0x85, 0x05, 0xa6, 0x2a, 0x55, 0xe7, 0xe6, 0xaf, 0xfd, 0xf0, 0xb2, 0xef, 0x7d, + 0xfb, 0xc5, 0x1f, 0xc4, 0x3b, 0x3f, 0xd7, 0xcb, 0xd8, 0xdd, 0xf3, 0xff, 0x7e, 0x20, 0x2f, 0xdf, + 0x50, 0xe2, 0xec, 0x3b, 0x13, 0x4d, 0xec, 0xbb, 0xbd, 0xff, 0xe2, 0xbe, 0x11, 0xe7, 0xae, 0xc2, + 0xa0, 0x3e, 0xdf, 0xcb, 0x6b, 0xe8, 0xda, 0x8c, 0x5a, 0x76, 0x39, 0x14, 0xcf, 0x4e, 0x65, 0x3c, + 0x19, 0x00, 0x8b, 0x12, 0x69, 0x09, 0xac, 0xc4, 0x22, 0x62, 0x81, 0xb6, 0xb6, 0x94, 0x98, 0x44, + 0x6b, 0x62, 0x12, 0xa0, 0xda, 0x43, 0xa2, 0x05, 0x98, 0x7c, 0x16, 0x79, 0xfb, 0x31, 0x98, 0x3c, + 0x80, 0xb2, 0x0e, 0xd5, 0x2a, 0x71, 0xb9, 0xa2, 0xbf, 0xf2, 0xe1, 0xff, 0x75, 0xda, 0x57, 0x5f, + 0x63, 0x97, 0xd9, 0x79, 0xfd, 0x72, 0xc6, 0xee, 0x9a, 0xf8, 0xff, 0x03, 0xd9, 0x79, 0xdd, 0x10, + 0x63, 0x3b, 0x92, 0xd0, 0xba, 0xff, 0x8e, 0xfe, 0x4b, 0xfa, 0x56, 0x39, 0xdf, 0x28, 0x94, 0xdc, + 0x8d, 0xae, 0xef, 0x42, 0xdf, 0xc5, 0xa8, 0xe1, 0x2b, 0xc0, 0x1f, 0x6e, 0x0b, 0xd0, 0x71, 0xfa, + 0x0a, 0x0b, 0xa9, 0xd0, 0x4b, 0x4e, 0x5e, 0x92, 0xcf, 0x15, 0x6d, 0xe1, 0x45, 0x2c, 0x8a, 0x14, + 0x60, 0x13, 0x14, 0x02, 0xca, 0x81, 0x85, 0x03, 0x70, 0xe8, 0x11, 0xe4, 0xd0, 0x73, 0x28, 0xeb, + 0xd0, 0x68, 0x88, 0x99, 0x9d, 0x77, 0x76, 0x8c, 0x7c, 0xa9, 0x72, 0x13, 0xc0, 0xce, 0x9b, 0x4f, + 0x63, 0xec, 0x8e, 0x37, 0xff, 0x6d, 0x20, 0xaf, 0x7c, 0x69, 0x3d, 0x9b, 0x6e, 0x7d, 0x23, 0xd1, + 0xc4, 0xf6, 0xd2, 0x7b, 0xfa, 0x47, 0xf5, 0x9f, 0x77, 0x2d, 0xf3, 0xd6, 0x3b, 0x9e, 0x03, 0xfd, + 0xef, 0x47, 0x9d, 0x72, 0x25, 0x78, 0xfd, 0x20, 0x31, 0xda, 0x71, 0x00, 0x41, 0x4c, 0x94, 0x0a, + 0xd3, 0x71, 0xe2, 0x1d, 0xc2, 0xa9, 0xa6, 0xbf, 0x88, 0x41, 0x3a, 0xb5, 0xd0, 0xfa, 0xbc, 0x03, + 0x88, 0x4d, 0xd7, 0x50, 0x1a, 0x82, 0x39, 0xe4, 0xd0, 0xa3, 0xb0, 0xff, 0x31, 0xb0, 0x2e, 0x41, + 0x40, 0x70, 0x7c, 0x46, 0x6f, 0x5f, 0xfd, 0xbf, 0x2b, 0xb7, 0xc8, 0x93, 0xb0, 0xff, 0xe0, 0xe7, + 0x38, 0xfd, 0xea, 0xbb, 0x4f, 0x0e, 0x44, 0x9e, 0x06, 0x75, 0x3e, 0xec, 0xbf, 0x63, 0x68, 0x4d, + 0xf7, 0x72, 0xfe, 0xae, 0x6f, 0xa5, 0x7f, 0x8e, 0x76, 0x25, 0xf1, 0x87, 0x53, 0xaf, 0x49, 0x40, + 0xd8, 0x08, 0x9d, 0xcd, 0x30, 0x77, 0xe8, 0x6d, 0x4c, 0x14, 0xd2, 0x37, 0x3c, 0x88, 0x52, 0xd2, + 0x21, 0x98, 0x39, 0x41, 0xf8, 0x26, 0xc0, 0x7f, 0x43, 0x78, 0x39, 0x11, 0x4c, 0xf2, 0x14, 0x80, + 0xb8, 0x06, 0x6f, 0xfd, 0x0c, 0xde, 0x7c, 0x0a, 0xf0, 0x68, 0x04, 0xcc, 0x1e, 0x3e, 0xea, 0x5d, + 0xbf, 0x61, 0xfb, 0xc2, 0x3d, 0x6f, 0x5c, 0xeb, 0xa9, 0xf5, 0x5f, 0x8f, 0x5a, 0x26, 0xd1, 0x8a, + 0x8f, 0xbb, 0x6f, 0x2e, 0xa1, 0xce, 0x87, 0xdd, 0x37, 0xf6, 0xf4, 0xe6, 0x7a, 0xf8, 0x4a, 0xdf, + 0x68, 0x36, 0x01, 0xd1, 0x75, 0x26, 0x6a, 0xf9, 0xc7, 0x50, 0x6e, 0x2f, 0xd8, 0x06, 0xda, 0x85, + 0xf1, 0x5d, 0x3b, 0xf9, 0xfe, 0x1f, 0x7e, 0x95, 0x3f, 0xbe, 0xe6, 0xb3, 0x4c, 0xee, 0x7f, 0x13, + 0x4c, 0x80, 0xb2, 0x01, 0x2a, 0x7d, 0x62, 0x1b, 0x88, 0x69, 0x20, 0x36, 0x00, 0x69, 0x80, 0x0d, + 0xc0, 0x86, 0x1d, 0x23, 0x48, 0x86, 0x49, 0x9f, 0x36, 0x99, 0x23, 0x26, 0x40, 0x6c, 0x88, 0x98, + 0x10, 0xe2, 0x46, 0x22, 0xd8, 0xc8, 0x36, 0x18, 0x5c, 0x05, 0x62, 0xf1, 0x33, 0xf4, 0x9f, 0xb2, + 0x2c, 0xba, 0xe1, 0xa9, 0x4f, 0x97, 0xc6, 0xd6, 0x7f, 0x3d, 0x92, 0x97, 0x3e, 0x37, 0xc0, 0x12, + 0x20, 0xaf, 0x6f, 0xbf, 0x90, 0x8d, 0x77, 0x4c, 0xf3, 0xd3, 0x4b, 0xbb, 0xbd, 0xc2, 0x90, 0x7f, + 0x79, 0xef, 0x4a, 0xef, 0x6a, 0x27, 0xa3, 0x21, 0xb3, 0x1c, 0x55, 0xfa, 0x00, 0x64, 0x96, 0x23, + 0xa6, 0x86, 0x92, 0x64, 0xd3, 0x87, 0xee, 0xf9, 0x1b, 0x0e, 0xbc, 0xf0, 0x0a, 0xc3, 0x83, 0x73, + 0x44, 0xf3, 0x7b, 0xc0, 0xd4, 0x11, 0xdb, 0x48, 0x47, 0x80, 0x48, 0x08, 0xd2, 0x21, 0x6c, 0x4b, + 0xe0, 0xce, 0xbf, 0xc3, 0xb6, 0xf0, 0x26, 0x15, 0xde, 0x86, 0xe9, 0x77, 0x8d, 0xf6, 0x50, 0x0a, + 0xd6, 0x5c, 0x08, 0x19, 0x8d, 0x52, 0x90, 0xf5, 0x39, 0x63, 0xf5, 0x68, 0xfd, 0x36, 0x80, 0x2d, + 0x77, 0xcf, 0x2c, 0x05, 0xb2, 0xe1, 0xa6, 0x27, 0x01, 0xd8, 0xf0, 0xab, 0xbd, 0xc3, 0x85, 0x41, + 0xbd, 0x23, 0xdb, 0xe3, 0x23, 0x78, 0xd0, 0xbd, 0x11, 0xba, 0xb7, 0x82, 0xa9, 0x82, 0x6d, 0xa0, + 0x1c, 0x61, 0xdf, 0x53, 0xcf, 0x72, 0xf4, 0x8d, 0xfd, 0xfc, 0xca, 0x15, 0x9a, 0x2b, 0x3e, 0xbd, + 0x8e, 0xe5, 0xab, 0x4a, 0x40, 0x08, 0x92, 0x0e, 0xdb, 0x00, 0xd3, 0xe8, 0x10, 0xbe, 0x53, 0xc0, + 0xf6, 0x48, 0x34, 0x76, 0xc2, 0x77, 0xa6, 0x91, 0x1e, 0x40, 0x73, 0xad, 0x44, 0xa3, 0x2a, 0x9b, + 0x85, 0xe5, 0xa7, 0x03, 0x06, 0xc7, 0x81, 0x62, 0xb7, 0x5c, 0xb0, 0xf7, 0x0b, 0xdd, 0xbf, 0xdf, + 0xcc, 0x75, 0x00, 0xfa, 0xf5, 0x3b, 0xce, 0x4f, 0xf2, 0xc6, 0xb5, 0x3d, 0x59, 0x1b, 0x36, 0x3e, + 0xdd, 0x37, 0x9a, 0x2d, 0x89, 0x08, 0xe4, 0x4e, 0x41, 0xf5, 0x5d, 0x90, 0x2e, 0xdc, 0x40, 0xd9, + 0x10, 0x1c, 0xc3, 0x6b, 0x8f, 0xbf, 0xc4, 0x59, 0x1b, 0xa6, 0xd9, 0xf4, 0xc1, 0x8b, 0x29, 0x9d, + 0xf7, 0x07, 0x78, 0x3d, 0x2b, 0x52, 0x61, 0x52, 0x00, 0x9d, 0x26, 0x74, 0x52, 0xd3, 0x09, 0x96, + 0x9e, 0xbe, 0x09, 0xda, 0xda, 0x6b, 0x3d, 0x1b, 0x09, 0x00, 0x09, 0x50, 0x12, 0x02, 0x21, 0xaa, + 0x74, 0x2a, 0xe8, 0xc4, 0x25, 0x5c, 0x87, 0xfe, 0xc1, 0x7e, 0x73, 0xcd, 0x7d, 0x57, 0xad, 0x1c, + 0x68, 0x26, 0x6c, 0x77, 0xc3, 0xcd, 0x4f, 0x03, 0xd0, 0xbf, 0xba, 0xbb, 0x94, 0xef, 0x33, 0x9f, + 0xd7, 0x9e, 0x83, 0xe0, 0x43, 0x7e, 0x2d, 0xf8, 0xa7, 0xa0, 0x4c, 0x2d, 0x71, 0x44, 0x25, 0x44, + 0x0b, 0x55, 0xa4, 0x72, 0x80, 0xb5, 0xdb, 0xce, 0x64, 0x60, 0xe3, 0xaf, 0x22, 0xda, 0x4f, 0x4f, + 0x51, 0x3a, 0x9c, 0xd9, 0xb4, 0x9d, 0xb7, 0x95, 0x3f, 0x52, 0x87, 0xb6, 0x16, 0x6b, 0x63, 0xac, + 0x89, 0x40, 0x2c, 0x5a, 0x83, 0xd6, 0xd2, 0xfe, 0x3f, 0x3a, 0x02, 0x45, 0xcb, 0xf1, 0xd3, 0x40, + 0xe1, 0x79, 0xe0, 0x29, 0x08, 0xc0, 0xd1, 0xe0, 0xfb, 0x9c, 0x35, 0xb6, 0x6e, 0xe6, 0x93, 0xc0, + 0x0e, 0x00, 0x07, 0x60, 0xe7, 0xb5, 0x3d, 0xae, 0x93, 0x91, 0x4f, 0x2d, 0xdb, 0x50, 0xb8, 0x14, + 0xad, 0xc1, 0x2f, 0xa1, 0x4a, 0xef, 0x47, 0x39, 0x99, 0xc4, 0x61, 0x89, 0xd1, 0x8e, 0xe5, 0xf0, + 0xee, 0x37, 0xc9, 0x98, 0x43, 0xac, 0x7e, 0xcf, 0x2f, 0x93, 0xe9, 0x1e, 0x02, 0x13, 0x82, 0x8d, + 0x41, 0xa2, 0x64, 0x10, 0x81, 0x8d, 0x11, 0x1b, 0xa7, 0xd9, 0xbe, 0x63, 0xd8, 0x88, 0xe9, 0xa3, + 0xd3, 0x3c, 0xf3, 0xc0, 0x8b, 0x3c, 0x75, 0xff, 0xf3, 0xbc, 0xf1, 0xcc, 0x1e, 0x2a, 0xb3, 0x0b, + 0x14, 0xba, 0x1c, 0x72, 0x79, 0x85, 0x52, 0x06, 0x4d, 0x8c, 0x22, 0x42, 0x49, 0x9c, 0xac, 0x23, + 0x21, 0xd8, 0x28, 0xd9, 0xa7, 0x32, 0x0e, 0x8b, 0x53, 0x60, 0x54, 0x33, 0x7d, 0xf9, 0x5a, 0x8b, + 0xda, 0xdc, 0x7d, 0xf6, 0x7d, 0xf7, 0xbe, 0x7e, 0x2c, 0x74, 0x13, 0x6d, 0x14, 0x7c, 0xbf, 0xcb, + 0xfe, 0x8e, 0xce, 0x38, 0x88, 0x38, 0x90, 0x29, 0x81, 0xbf, 0x0c, 0x69, 0x6a, 0x43, 0x2c, 0x28, + 0x85, 0x69, 0xcc, 0xb3, 0x62, 0xec, 0x42, 0xba, 0x4b, 0x43, 0xd8, 0xb8, 0xb6, 0x24, 0xd9, 0xc9, + 0x92, 0x70, 0x6a, 0x96, 0x84, 0x54, 0xad, 0x85, 0xb9, 0xa9, 0x05, 0xfe, 0xf5, 0xff, 0x3e, 0xcc, + 0xe4, 0xbe, 0x37, 0x39, 0x73, 0x4b, 0x0f, 0x5d, 0x3d, 0x1e, 0x8d, 0xd9, 0x79, 0x9e, 0xbb, 0x7f, + 0x1f, 0xe7, 0x7e, 0x64, 0x1b, 0x83, 0x23, 0x7d, 0x2c, 0xcc, 0x87, 0xcc, 0x97, 0x03, 0x72, 0xbe, + 0xd0, 0xdf, 0x2d, 0x29, 0xa0, 0x06, 0x34, 0x8e, 0x23, 0x0b, 0x87, 0x50, 0x19, 0x07, 0x22, 0x01, + 0x23, 0xb8, 0x2e, 0xf8, 0x19, 0xb5, 0x7e, 0xcb, 0xda, 0xbd, 0x97, 0x02, 0xf7, 0xba, 0xcf, 0x7d, + 0x66, 0x98, 0xfa, 0x3c, 0xeb, 0xfa, 0x46, 0xdc, 0xd3, 0x11, 0x05, 0x4e, 0x1e, 0x95, 0x5b, 0x09, + 0xa6, 0x9e, 0x2c, 0x92, 0x0a, 0x6a, 0x03, 0xcb, 0xea, 0x4d, 0xab, 0x00, 0xc1, 0x5a, 0x83, 0xc2, + 0x24, 0x7e, 0x83, 0xc5, 0x88, 0x4e, 0xcc, 0x40, 0xe2, 0x0e, 0xda, 0x61, 0x5b, 0x79, 0x43, 0x8c, + 0xc5, 0xcf, 0x84, 0xbc, 0xef, 0x23, 0x25, 0x06, 0x4a, 0x7d, 0xf8, 0xc5, 0x65, 0x90, 0x29, 0x24, + 0x59, 0x4c, 0x37, 0xa8, 0x4e, 0x2f, 0xf2, 0xf0, 0xcf, 0x8e, 0xf1, 0xd0, 0xb3, 0x65, 0x76, 0xef, + 0x5b, 0xe4, 0x03, 0x5b, 0x35, 0x9f, 0xba, 0x3c, 0x87, 0x23, 0xb5, 0x54, 0x0e, 0x9b, 0xd8, 0x8e, + 0x23, 0xe0, 0x28, 0x50, 0x92, 0x90, 0x00, 0xcd, 0x48, 0x5f, 0x2f, 0x17, 0x00, 0xf7, 0xba, 0x5d, + 0xa5, 0x50, 0x3b, 0x9e, 0xfe, 0xb5, 0x7c, 0xa9, 0x88, 0xa0, 0x40, 0xfb, 0xe0, 0x2f, 0x03, 0x53, + 0x4b, 0xcd, 0x66, 0xa9, 0x50, 0x00, 0x3a, 0x98, 0x62, 0x71, 0x62, 0x9c, 0xc9, 0x63, 0x53, 0x64, + 0x33, 0x2e, 0x2b, 0x46, 0x06, 0x90, 0xae, 0x21, 0xac, 0x93, 0x49, 0xe7, 0x37, 0x35, 0x12, 0x03, + 0x31, 0xd8, 0x90, 0x7c, 0x3e, 0xa4, 0x70, 0xc6, 0xa9, 0x88, 0x69, 0x60, 0xa2, 0x2a, 0x54, 0x66, + 0x70, 0x1c, 0xc3, 0xf4, 0x74, 0x9d, 0xef, 0xde, 0x37, 0xc5, 0x4f, 0x9e, 0xb1, 0x6c, 0x5c, 0x57, + 0xe4, 0xca, 0x8b, 0x84, 0x4d, 0x2b, 0x0d, 0xd1, 0x62, 0x15, 0xa7, 0x4b, 0x81, 0xf2, 0x40, 0x27, + 0x72, 0x88, 0x23, 0x28, 0x27, 0x2d, 0x71, 0x0c, 0xb8, 0x8e, 0xe0, 0x67, 0xec, 0xfa, 0x7f, 0xf8, + 0xf5, 0x95, 0xfd, 0x6e, 0x71, 0x59, 0x84, 0x72, 0xf3, 0x97, 0xb4, 0xa8, 0xb5, 0xe3, 0x83, 0x53, + 0x48, 0x9d, 0xd8, 0x74, 0x38, 0xad, 0xa0, 0x94, 0xa0, 0x17, 0x0f, 0xf2, 0xc4, 0x03, 0x0f, 0xf3, + 0x8f, 0x3f, 0x3b, 0xc2, 0xa4, 0x9c, 0x42, 0x57, 0x06, 0xce, 0xec, 0x1e, 0xe7, 0xaa, 0xcb, 0x37, + 0xd0, 0xbf, 0xe1, 0x5c, 0x6c, 0x26, 0x07, 0x12, 0x27, 0x66, 0x21, 0x21, 0x22, 0x21, 0x22, 0x8d, + 0xc4, 0xf4, 0x62, 0x83, 0x12, 0x83, 0x02, 0x94, 0xeb, 0x51, 0xad, 0x5a, 0xbe, 0xf5, 0xf7, 0x65, + 0x0e, 0xcd, 0xaf, 0xe1, 0x1b, 0xb7, 0x6d, 0x61, 0x53, 0xdf, 0x01, 0xe2, 0x50, 0x13, 0xeb, 0x6e, + 0xa2, 0xb9, 0x37, 0x80, 0xe3, 0x49, 0x1e, 0x91, 0x94, 0xfa, 0x6b, 0x95, 0x24, 0x0c, 0xa5, 0x40, + 0x04, 0xc7, 0x01, 0xd7, 0x91, 0x15, 0x6b, 0x86, 0x67, 0xce, 0x70, 0x73, 0xa7, 0x9e, 0xa7, 0x4d, + 0xf9, 0xd5, 0x73, 0x40, 0x27, 0x84, 0xcd, 0xc9, 0x27, 0x9a, 0x30, 0x01, 0x82, 0x49, 0xf9, 0x8f, + 0x05, 0xa5, 0xd1, 0xf5, 0xc3, 0xfc, 0xe3, 0xf7, 0x7f, 0xc8, 0x9f, 0x3c, 0xd6, 0x83, 0xb7, 0xf5, + 0xcb, 0xac, 0x1e, 0x7b, 0x0f, 0x91, 0x11, 0x1e, 0xdc, 0xf3, 0x1c, 0x6f, 0xfd, 0xcd, 0xb7, 0xf8, + 0xd2, 0x55, 0x4f, 0xd2, 0xb5, 0xe9, 0xa2, 0x94, 0xb6, 0x47, 0x69, 0x24, 0x22, 0x39, 0xd5, 0x44, + 0x7c, 0x04, 0x41, 0xa1, 0x51, 0x1e, 0xfc, 0xdd, 0x3f, 0xcf, 0x12, 0x66, 0xcf, 0x66, 0xfb, 0x6d, + 0x57, 0xd2, 0xb7, 0xf8, 0x02, 0x8b, 0x0b, 0x45, 0x0a, 0xcb, 0x47, 0xc8, 0xd6, 0xde, 0xc6, 0xb7, + 0x71, 0x12, 0xb4, 0x44, 0xb5, 0xfe, 0x17, 0x9d, 0x9a, 0x97, 0x6e, 0x93, 0x2b, 0xa5, 0xd5, 0xf2, + 0xde, 0x5e, 0xbd, 0xca, 0x9d, 0x7c, 0x65, 0x7c, 0xf8, 0x94, 0xf5, 0x3a, 0x9f, 0x14, 0x3d, 0x1a, + 0xb4, 0x9f, 0x94, 0xa5, 0x36, 0x5c, 0xc2, 0x4a, 0x1d, 0x15, 0xf0, 0xec, 0xbf, 0x3e, 0xc3, 0x77, + 0x9f, 0x1d, 0xe0, 0xcc, 0xdf, 0xbc, 0x9d, 0xf7, 0xff, 0xda, 0xc7, 0xc9, 0xf9, 0xc9, 0xb7, 0xb5, + 0xf0, 0xc3, 0x3c, 0xfe, 0xc0, 0x39, 0x7c, 0xe3, 0xfe, 0x4f, 0xf2, 0xc5, 0x55, 0x47, 0xb1, 0xc5, + 0x15, 0x2d, 0x66, 0xac, 0x50, 0x88, 0x48, 0x5a, 0x2d, 0x5a, 0x40, 0xa3, 0x5d, 0x87, 0x57, 0x5e, + 0x9d, 0x67, 0x31, 0x1a, 0xe4, 0xb7, 0x7f, 0xf3, 0x7d, 0x0c, 0x66, 0xea, 0x94, 0xe7, 0x0f, 0x53, + 0x28, 0xe6, 0x61, 0xef, 0x3f, 0x63, 0xe7, 0xf6, 0x42, 0xb7, 0x86, 0xbe, 0xde, 0xb4, 0x10, 0x63, + 0xe9, 0xd0, 0x4b, 0x88, 0xef, 0x40, 0xc6, 0x63, 0xb9, 0x8e, 0xca, 0xf3, 0x6b, 0xfd, 0x6e, 0xb7, + 0xb5, 0x2d, 0xa2, 0x96, 0x64, 0x60, 0x6c, 0x80, 0x22, 0x22, 0x9e, 0x3a, 0xc4, 0xf7, 0x1f, 0x9a, + 0x61, 0xe5, 0xa5, 0x9f, 0xe3, 0xe3, 0x9f, 0xf8, 0x38, 0xa7, 0x75, 0xc1, 0x4a, 0x0f, 0x46, 0x5d, + 0x58, 0xdb, 0x0d, 0x57, 0xfc, 0xc6, 0x65, 0x54, 0x2f, 0xde, 0xce, 0xae, 0xa7, 0x5f, 0x45, 0x3b, + 0x5e, 0xa2, 0x5d, 0x9c, 0x04, 0x8a, 0xd2, 0x9c, 0x28, 0xcd, 0xc1, 0x63, 0x96, 0xcd, 0x1b, 0x86, + 0x38, 0xed, 0xd4, 0x90, 0xf2, 0xde, 0x07, 0xf1, 0x33, 0x35, 0xd4, 0x5b, 0x8f, 0x63, 0x8f, 0xec, + 0x85, 0x48, 0x43, 0xa1, 0xa7, 0x5d, 0x49, 0xb6, 0x34, 0xd2, 0x04, 0xd5, 0xe6, 0xba, 0x4a, 0xe1, + 0x3a, 0x8e, 0xed, 0x75, 0x91, 0x60, 0xb9, 0x9b, 0xcd, 0x75, 0x14, 0x73, 0x61, 0x9b, 0x5a, 0xd0, + 0x0c, 0x9f, 0xf0, 0xe8, 0xd3, 0xfb, 0x31, 0xcb, 0xdf, 0xcb, 0x07, 0x2e, 0xfb, 0x28, 0x6b, 0x7a, + 0xa0, 0xa8, 0xc0, 0xd3, 0x90, 0x77, 0xe0, 0xde, 0x07, 0xc7, 0x79, 0xee, 0x65, 0x4d, 0x39, 0x7a, + 0x2f, 0x7f, 0xff, 0x7a, 0x89, 0xcd, 0x1f, 0xaa, 0x83, 0xca, 0xa4, 0x94, 0x5c, 0x2d, 0x31, 0x0f, + 0xc7, 0xd1, 0x1c, 0x3c, 0x5c, 0x61, 0x59, 0xaf, 0xe2, 0x8c, 0xd5, 0x75, 0xe2, 0xe3, 0xbb, 0x50, + 0xf6, 0x18, 0xba, 0x72, 0x14, 0xe2, 0xe3, 0xe8, 0x5e, 0x17, 0xfa, 0xbb, 0xc1, 0xd7, 0x69, 0x80, + 0x69, 0x96, 0xc8, 0x27, 0xf0, 0xf5, 0xe6, 0x53, 0x04, 0xa5, 0x9c, 0x6e, 0xd7, 0x71, 0x29, 0x2a, + 0xad, 0x12, 0x1c, 0x22, 0x49, 0xb4, 0x6a, 0x51, 0x03, 0x0b, 0xca, 0x42, 0x5c, 0x63, 0xe7, 0xbe, + 0x32, 0x83, 0xeb, 0x2f, 0x61, 0xeb, 0xfa, 0x15, 0x94, 0xdc, 0x64, 0x1f, 0x0d, 0xfc, 0xd5, 0xdf, + 0x1e, 0xe0, 0xc1, 0xfd, 0xfd, 0xbc, 0xbd, 0xff, 0x11, 0xf2, 0xf9, 0x5e, 0xe6, 0xb8, 0x1a, 0x6a, + 0x8f, 0x40, 0xd7, 0x8a, 0x14, 0xc0, 0xd2, 0xea, 0x47, 0x04, 0xb2, 0xae, 0x61, 0xb4, 0x77, 0x81, + 0xa2, 0xaa, 0x53, 0x3e, 0x78, 0x98, 0x4c, 0x21, 0xc6, 0xf1, 0x23, 0x18, 0xea, 0x05, 0xd7, 0x87, + 0xdc, 0x08, 0x2a, 0xbf, 0x0a, 0x2a, 0x7b, 0x90, 0xda, 0xc1, 0xd4, 0xe7, 0x4e, 0x5e, 0x49, 0x2b, + 0x25, 0xe0, 0x78, 0x59, 0xad, 0xb2, 0x5e, 0xbe, 0xb3, 0x44, 0x45, 0xea, 0x28, 0x5b, 0x69, 0x73, + 0x2c, 0x13, 0x40, 0x65, 0x96, 0xd8, 0x1f, 0xa6, 0x67, 0xf9, 0x5a, 0xfa, 0x7c, 0xc8, 0xa4, 0x7e, + 0x77, 0x64, 0x7c, 0x91, 0xe9, 0x28, 0xc7, 0x1f, 0x7d, 0xb6, 0x97, 0xb3, 0x7a, 0x9e, 0xe0, 0xa1, + 0x3f, 0xbb, 0x9c, 0x89, 0x60, 0x82, 0xf2, 0x54, 0x39, 0xd5, 0xbe, 0x24, 0x92, 0x77, 0x6c, 0x6c, + 0xad, 0xa5, 0x34, 0x90, 0xa7, 0xb4, 0x62, 0x05, 0xe2, 0x17, 0xc9, 0x14, 0x5d, 0x9c, 0x5c, 0x16, + 0xb2, 0x03, 0x90, 0x2d, 0xa2, 0xfc, 0x6e, 0xb4, 0x6b, 0x51, 0x32, 0x0f, 0x85, 0xd1, 0xa5, 0x55, + 0xa8, 0x9c, 0xa4, 0xbe, 0x15, 0x50, 0x26, 0xa8, 0x6b, 0xa9, 0xd5, 0x17, 0x97, 0xcc, 0x30, 0x21, + 0x04, 0x53, 0x60, 0x42, 0xc4, 0x34, 0x40, 0x42, 0x2a, 0x73, 0x65, 0xdc, 0xc2, 0x20, 0x7d, 0xa5, + 0xa1, 0x56, 0xf4, 0x03, 0x78, 0xf8, 0xf1, 0x69, 0x3e, 0xf8, 0x4b, 0x03, 0x0c, 0x74, 0xc1, 0x15, + 0x1f, 0xfb, 0x28, 0x71, 0x58, 0xe5, 0xe0, 0xce, 0x7f, 0x61, 0x7e, 0xa1, 0xd1, 0x2e, 0x6b, 0x91, + 0x8e, 0x67, 0xb2, 0x4f, 0xc2, 0xb1, 0x34, 0xa2, 0x32, 0x64, 0x8a, 0x79, 0x9c, 0x4c, 0x06, 0x14, + 0x58, 0xa3, 0x98, 0xde, 0x3b, 0xc3, 0x9b, 0x0f, 0xed, 0xe6, 0xc8, 0xa3, 0x4f, 0x20, 0x33, 0x2f, + 0xbe, 0xb3, 0x1a, 0x97, 0x25, 0x4b, 0x21, 0x4a, 0x63, 0x85, 0xba, 0x6b, 0x6c, 0xa6, 0x62, 0x8d, + 0x45, 0xb9, 0xa9, 0xfa, 0x24, 0x46, 0xa2, 0x69, 0x5a, 0x31, 0x4e, 0x2c, 0x36, 0xac, 0xe0, 0xe7, + 0x4e, 0xc5, 0xc9, 0x16, 0x08, 0x0c, 0xe4, 0x74, 0xe2, 0x77, 0x95, 0xc8, 0x72, 0x60, 0x41, 0x71, + 0x26, 0x70, 0xdf, 0x7d, 0xf7, 0x21, 0x22, 0xe8, 0x8c, 0x8f, 0xb4, 0xba, 0x26, 0x69, 0xb5, 0x27, + 0x36, 0xd5, 0x4c, 0x93, 0x40, 0x4a, 0x12, 0xc9, 0x9a, 0x12, 0x29, 0x01, 0x2b, 0x68, 0xad, 0x28, + 0x0e, 0x67, 0x71, 0x7d, 0x8b, 0x76, 0x84, 0xc6, 0xdc, 0x3c, 0xd9, 0x1e, 0x67, 0x29, 0x02, 0x51, + 0x4b, 0xb4, 0x2c, 0x02, 0x46, 0x54, 0x59, 0xa3, 0x33, 0x6f, 0x47, 0x35, 0xb3, 0x74, 0xb2, 0x69, + 0x40, 0x78, 0x14, 0xe2, 0x29, 0xc4, 0x2e, 0xd0, 0x95, 0x37, 0xe4, 0x3c, 0x4b, 0xb9, 0x11, 0x33, + 0x1b, 0x42, 0x35, 0x9d, 0x3e, 0x54, 0xd0, 0x3c, 0xb0, 0xc7, 0x63, 0xeb, 0x65, 0xff, 0x8d, 0x3f, + 0xf9, 0xe6, 0x9f, 0xa2, 0xbd, 0x2c, 0x6b, 0xce, 0xb9, 0x84, 0xbe, 0x81, 0x7c, 0x42, 0x1c, 0xe9, + 0xa8, 0xd3, 0x5b, 0xd9, 0xbe, 0x03, 0x80, 0x2c, 0x7d, 0x2a, 0x47, 0x98, 0x3f, 0x52, 0xa5, 0x7b, + 0xc8, 0xa7, 0xd0, 0xe7, 0xe2, 0x75, 0x79, 0x29, 0xb3, 0x4e, 0x0f, 0xd9, 0xb4, 0xe2, 0x4f, 0xe7, + 0xcf, 0x7c, 0x1c, 0xe9, 0x29, 0x37, 0x53, 0x1a, 0xde, 0x17, 0x2e, 0x8e, 0xe3, 0x17, 0x7d, 0xa4, + 0x89, 0x56, 0x35, 0x37, 0x88, 0x11, 0xd1, 0x38, 0x85, 0x2c, 0x03, 0xde, 0x14, 0xe5, 0xfa, 0x34, + 0x13, 0x01, 0xc4, 0x31, 0x04, 0x59, 0x18, 0xbb, 0x60, 0x88, 0x1f, 0x7f, 0x6b, 0x9c, 0x75, 0x1f, + 0xb9, 0x0b, 0xaf, 0x6f, 0x03, 0xc5, 0x91, 0xb3, 0xd8, 0x96, 0x7f, 0x91, 0xe2, 0xf0, 0x00, 0x56, + 0xc2, 0xa5, 0xf5, 0x7a, 0x53, 0x1b, 0x9d, 0x0d, 0x0a, 0xda, 0xc0, 0x94, 0x08, 0xb5, 0xb9, 0x00, + 0xbf, 0xe0, 0x24, 0xa6, 0xeb, 0x24, 0xb4, 0x6a, 0x09, 0xd8, 0x4e, 0x2b, 0x6d, 0x6b, 0x64, 0xaa, + 0x5a, 0xb3, 0xe3, 0xfa, 0x8c, 0xb1, 0x17, 0xa6, 0xc3, 0x9a, 0x4c, 0xb4, 0x66, 0x34, 0x4f, 0x2d, + 0x1d, 0x36, 0x88, 0x88, 0xa2, 0x2c, 0xeb, 0xbb, 0x67, 0x58, 0x19, 0xbf, 0xc4, 0xc2, 0xf4, 0x24, + 0x93, 0xb3, 0xd3, 0xec, 0x9b, 0x8d, 0xa8, 0x14, 0x0b, 0x6c, 0xdd, 0xa6, 0xc9, 0x1e, 0xb1, 0x6c, + 0x39, 0xf7, 0x26, 0xfa, 0x2a, 0xa3, 0x7c, 0xb8, 0xf7, 0x27, 0x88, 0xe3, 0x22, 0x4d, 0x9e, 0xd5, + 0xe4, 0x5e, 0x36, 0x46, 0x59, 0x83, 0xb2, 0x29, 0x53, 0xee, 0x04, 0x63, 0x0d, 0x41, 0x25, 0xa0, + 0x3a, 0x55, 0xa7, 0x6b, 0x28, 0x93, 0x36, 0x33, 0x3a, 0xb5, 0x65, 0x9b, 0x36, 0x94, 0x6a, 0x44, + 0x5a, 0x66, 0x25, 0x22, 0xc7, 0x8e, 0x1f, 0x57, 0x6f, 0x39, 0x9f, 0x1c, 0x2b, 0x69, 0x6b, 0x38, + 0xb7, 0x7b, 0x38, 0x73, 0x16, 0xa2, 0xdb, 0x6d, 0x4d, 0xad, 0x31, 0xa1, 0xa1, 0x3e, 0x5d, 0x27, + 0x8a, 0x72, 0x0c, 0xac, 0x58, 0xc3, 0x5a, 0xbd, 0x97, 0xc1, 0xa3, 0x0f, 0xe0, 0x4c, 0xbe, 0x40, + 0x50, 0x99, 0x60, 0xb6, 0x6a, 0xe9, 0x1a, 0x1d, 0x24, 0xbf, 0x26, 0x4b, 0xed, 0xd8, 0xd3, 0x9c, + 0x2b, 0xb7, 0xf3, 0x89, 0xff, 0xec, 0x63, 0x95, 0xea, 0x28, 0xb2, 0x4c, 0xda, 0x59, 0x31, 0xed, + 0x96, 0x50, 0xab, 0x49, 0x17, 0x23, 0x51, 0x9d, 0x60, 0xbe, 0x4e, 0x65, 0xaa, 0x46, 0xff, 0x68, + 0x1e, 0xc7, 0x11, 0xc4, 0x9a, 0x25, 0xcd, 0x3c, 0xac, 0x05, 0x2b, 0xa8, 0x40, 0x20, 0x48, 0xca, + 0x1e, 0x48, 0x2c, 0xa3, 0x11, 0xe8, 0xc7, 0x36, 0xdc, 0x59, 0xfe, 0xae, 0x5b, 0x9e, 0x10, 0xf1, + 0xbb, 0xd5, 0x0f, 0xa3, 0x5a, 0x7c, 0xa5, 0x9b, 0xd5, 0x09, 0x95, 0x68, 0xd6, 0x12, 0x56, 0xf0, + 0xf3, 0x21, 0xbe, 0x3f, 0x89, 0xcc, 0x1e, 0x46, 0x2a, 0x01, 0x03, 0x51, 0xcc, 0xa0, 0xa7, 0x31, + 0x95, 0x7b, 0x98, 0x9e, 0x18, 0xe1, 0x70, 0xe9, 0x6a, 0x82, 0xc6, 0x0a, 0x4e, 0x73, 0x7f, 0xc4, + 0x2f, 0xff, 0xa7, 0x59, 0xac, 0xdb, 0x9f, 0x16, 0x53, 0xd2, 0xd1, 0x98, 0x6b, 0x6a, 0xa1, 0xdd, + 0xd7, 0x52, 0xa6, 0x46, 0xb4, 0x38, 0x4f, 0xa3, 0x96, 0x25, 0x0a, 0x72, 0xf4, 0xae, 0xf0, 0x71, + 0xdc, 0x08, 0x31, 0x1d, 0x16, 0xd1, 0xaa, 0x6f, 0x04, 0x8c, 0x4d, 0x8a, 0x2a, 0x63, 0x9b, 0x14, + 0x1e, 0x63, 0x55, 0x50, 0x6b, 0xb0, 0x1b, 0x94, 0xb8, 0x47, 0x5e, 0x1d, 0x90, 0xd3, 0x2e, 0xaa, + 0x3e, 0x54, 0x9d, 0x0a, 0x83, 0xde, 0x51, 0xd7, 0x4f, 0x9c, 0x53, 0x81, 0xb5, 0x38, 0x9e, 0xc5, + 0x09, 0xe7, 0xb1, 0x13, 0x0b, 0xb0, 0x00, 0x36, 0x48, 0x42, 0xa1, 0xf8, 0x02, 0x7d, 0x9a, 0xc1, + 0xae, 0xc3, 0x2c, 0x2b, 0x3d, 0xc7, 0xba, 0x89, 0x47, 0x29, 0x8c, 0x81, 0xf5, 0x73, 0x48, 0x1c, + 0xa0, 0xb4, 0x4a, 0xc9, 0x66, 0x02, 0x46, 0x68, 0x97, 0xb3, 0xca, 0x54, 0x30, 0x95, 0x59, 0x1a, + 0x0b, 0x31, 0xb1, 0x1e, 0x41, 0xe7, 0x87, 0x28, 0x16, 0xe7, 0x70, 0x64, 0x3e, 0x05, 0x61, 0xdb, + 0x1e, 0x9d, 0x96, 0xc7, 0x88, 0x45, 0xc5, 0xa9, 0x26, 0x4c, 0x3b, 0xb1, 0x5a, 0x2b, 0xfb, 0x67, + 0x66, 0xbd, 0x47, 0x01, 0xdc, 0x8f, 0xfc, 0x78, 0x2f, 0x07, 0x3f, 0x50, 0x9c, 0xae, 0x4e, 0x67, + 0x7e, 0xd0, 0xb3, 0xc2, 0x5c, 0x93, 0x78, 0x5a, 0x47, 0x93, 0xd9, 0x2b, 0x40, 0xc1, 0x49, 0xa8, + 0x79, 0x2c, 0x28, 0xcf, 0x85, 0x42, 0x06, 0x0a, 0x3e, 0xd2, 0xb5, 0x06, 0x72, 0xc3, 0x28, 0x7f, + 0x9a, 0x6a, 0xbd, 0x8c, 0xaa, 0x96, 0xc9, 0x14, 0x32, 0x78, 0x05, 0xbf, 0x1d, 0x5a, 0x34, 0x28, + 0x53, 0x87, 0x70, 0x81, 0xb8, 0x3c, 0x4f, 0x6d, 0xc1, 0x26, 0xaf, 0x20, 0x8a, 0xa3, 0x64, 0xf3, + 0x19, 0xdc, 0xe8, 0x28, 0x04, 0x8b, 0xe0, 0x06, 0xed, 0x28, 0xd7, 0xa1, 0xcd, 0x96, 0x6f, 0x44, + 0x0a, 0x62, 0x69, 0x2d, 0x1b, 0xc7, 0xd0, 0x68, 0xa8, 0xdd, 0x63, 0x77, 0xcf, 0x3d, 0x05, 0x0a, + 0x17, 0xe0, 0xc0, 0x53, 0x9e, 0x19, 0xdd, 0xa6, 0xbe, 0x59, 0x9d, 0x0e, 0xaf, 0x29, 0x0c, 0xa6, + 0xdc, 0x48, 0x00, 0xe5, 0x42, 0xa6, 0x0b, 0xbc, 0x2e, 0x28, 0x0a, 0x0a, 0x05, 0xda, 0x05, 0x2f, + 0x03, 0x8e, 0x87, 0xf2, 0x8b, 0x94, 0x0f, 0xee, 0xa7, 0x36, 0x35, 0x89, 0x58, 0x8b, 0x97, 0xf7, + 0x71, 0x32, 0xe0, 0xc6, 0x0a, 0x31, 0x01, 0x71, 0xb5, 0x4a, 0xb0, 0xb0, 0x88, 0x09, 0x2c, 0xb8, + 0x3d, 0x38, 0xb9, 0x33, 0xf0, 0x96, 0xf5, 0xa3, 0x5c, 0x0f, 0x4f, 0x37, 0x50, 0x8d, 0x23, 0x48, + 0x5c, 0x41, 0x7b, 0x89, 0xe1, 0x4b, 0xb3, 0x9f, 0x2c, 0x69, 0xe3, 0xa1, 0xa9, 0x8d, 0x28, 0xe9, + 0x38, 0x11, 0xb7, 0xc3, 0x95, 0xb1, 0xea, 0xf8, 0xf4, 0x9c, 0x73, 0x3f, 0x28, 0x9e, 0xff, 0x5d, + 0x70, 0xdf, 0xf8, 0x0c, 0xac, 0xff, 0xe6, 0x8c, 0xec, 0x1d, 0x5b, 0xb6, 0xbb, 0x7c, 0x34, 0xbe, + 0xaf, 0x50, 0x72, 0x3f, 0x96, 0xe8, 0x2e, 0x0d, 0x73, 0xda, 0x49, 0x98, 0xac, 0xe7, 0x26, 0x86, + 0xa9, 0x9c, 0x94, 0x6b, 0x18, 0xd4, 0xe2, 0xeb, 0xd4, 0x26, 0x6b, 0xf4, 0xac, 0x1c, 0x48, 0x6a, + 0x0d, 0x2b, 0x44, 0x95, 0x06, 0xc1, 0x6c, 0x05, 0xc1, 0x26, 0x85, 0x4f, 0xbe, 0x17, 0xaf, 0xcb, + 0x07, 0xeb, 0xa2, 0x1c, 0xc1, 0x55, 0x0b, 0x10, 0xd4, 0x92, 0x5e, 0x99, 0x13, 0xa1, 0x33, 0x51, + 0x1a, 0xe6, 0x53, 0x1f, 0xb2, 0xcd, 0xca, 0x32, 0x15, 0x20, 0x16, 0x08, 0x15, 0x84, 0xd2, 0x2a, + 0x6f, 0x8c, 0x81, 0x6a, 0x4d, 0x5e, 0xdc, 0x7e, 0xcf, 0x39, 0x7f, 0x0b, 0x0f, 0x53, 0x7a, 0xdf, + 0xef, 0xa1, 0xe3, 0xd2, 0x46, 0x00, 0xfe, 0xfa, 0x8b, 0x23, 0x55, 0x13, 0x39, 0x77, 0x94, 0xc7, + 0x43, 0x51, 0xca, 0x24, 0x91, 0x43, 0x4c, 0x5a, 0x8b, 0x27, 0xe1, 0x33, 0x29, 0xb8, 0xa2, 0xf4, + 0xf7, 0x08, 0xab, 0x5c, 0x7a, 0x57, 0x14, 0xc9, 0xe4, 0x34, 0x99, 0xbc, 0xc6, 0x2f, 0x38, 0xe4, + 0x7a, 0x5d, 0xf2, 0x25, 0x9f, 0xc2, 0x60, 0x8e, 0x7c, 0x29, 0x87, 0x5f, 0xf0, 0xf0, 0x5c, 0xc1, + 0xf3, 0x02, 0x5c, 0x35, 0x8f, 0x8a, 0x27, 0x51, 0x7a, 0x1e, 0x9d, 0x09, 0x50, 0x6e, 0xd8, 0x06, + 0x61, 0x6d, 0x1b, 0x44, 0xd3, 0x37, 0x8c, 0x41, 0x85, 0xef, 0xd4, 0x46, 0x1c, 0xcb, 0xc4, 0xe4, + 0x94, 0xfb, 0xad, 0xef, 0x1d, 0x7f, 0x38, 0xde, 0xf7, 0xfb, 0xb0, 0xea, 0xb7, 0xbe, 0x83, 0xde, + 0x78, 0xcb, 0x6e, 0x0e, 0xff, 0x0f, 0xf8, 0x32, 0xcf, 0x33, 0x7b, 0x48, 0x76, 0x57, 0xa7, 0xed, + 0x2d, 0x61, 0x25, 0x4a, 0x5e, 0x0b, 0x48, 0xda, 0x50, 0xb0, 0x71, 0xaa, 0x6a, 0xb3, 0xa4, 0xbd, + 0x03, 0xe0, 0xe5, 0x55, 0x0b, 0x18, 0x12, 0xe1, 0xb8, 0x09, 0x81, 0x75, 0x3d, 0xd0, 0xda, 0xa2, + 0x74, 0x84, 0xce, 0x04, 0xe9, 0x08, 0x51, 0x5e, 0x84, 0x72, 0x62, 0x50, 0x69, 0xeb, 0xa8, 0xf3, + 0xb0, 0x3a, 0x41, 0xd8, 0x14, 0x44, 0xa0, 0x20, 0xb0, 0x2d, 0xdf, 0x30, 0x86, 0x78, 0x66, 0xce, + 0xfd, 0xf1, 0xa6, 0x1d, 0xe5, 0x1f, 0x01, 0x34, 0x4a, 0x1b, 0xda, 0x2d, 0xd3, 0x85, 0xb5, 0xbf, + 0x04, 0xc0, 0xd6, 0x3f, 0x3e, 0x5e, 0xaf, 0xcd, 0x98, 0x3f, 0x9f, 0x3f, 0x12, 0x3f, 0x91, 0x24, + 0xb3, 0xb8, 0x0d, 0xa6, 0xd9, 0xbb, 0x6a, 0x6a, 0x47, 0xe2, 0x0e, 0x60, 0x51, 0x5b, 0x63, 0x36, + 0x4c, 0xb5, 0x16, 0xb6, 0xe7, 0x49, 0x94, 0x24, 0x47, 0x96, 0x1e, 0x8c, 0xb4, 0xd6, 0x32, 0x4b, + 0x5a, 0x4b, 0x88, 0x4d, 0x4c, 0xa9, 0xa1, 0x20, 0x68, 0x9b, 0x94, 0xb5, 0x50, 0x5e, 0xe4, 0xb5, + 0xd7, 0x0f, 0x9e, 0x72, 0x2d, 0xc0, 0xae, 0xdf, 0x83, 0x4d, 0xb7, 0xbe, 0xde, 0x06, 0xb2, 0xf1, + 0xc6, 0x47, 0xd9, 0x7d, 0x73, 0xd2, 0x43, 0x3d, 0xe3, 0x96, 0xb9, 0xb7, 0x83, 0xb2, 0xbd, 0x6e, + 0xfe, 0xed, 0x70, 0x4e, 0xa9, 0x64, 0x23, 0x31, 0xa6, 0xad, 0x11, 0x1b, 0x2d, 0x05, 0x61, 0x3b, + 0xcc, 0xae, 0x25, 0x64, 0xe7, 0xf7, 0x71, 0x87, 0x69, 0x46, 0x2d, 0x8d, 0x4a, 0x6b, 0x4e, 0xc7, + 0xb0, 0x36, 0x99, 0x17, 0x5a, 0x54, 0xa0, 0xa0, 0x21, 0x09, 0xa0, 0x34, 0xad, 0xd4, 0xeb, 0x32, + 0xf9, 0xd6, 0x78, 0xee, 0x77, 0x2e, 0xfd, 0xb3, 0x3d, 0x0b, 0x00, 0xa5, 0x8f, 0xde, 0xc6, 0x3b, + 0x5e, 0x2b, 0x6c, 0xbc, 0x63, 0x9a, 0x97, 0x6f, 0x5e, 0x05, 0xc0, 0xaa, 0x6b, 0x7f, 0xfd, 0x99, + 0xea, 0xb4, 0x7c, 0xaa, 0x32, 0x11, 0xc6, 0x09, 0x1d, 0x6f, 0x6e, 0xfc, 0xef, 0x18, 0x9d, 0xfe, + 0xf4, 0x8e, 0xef, 0x92, 0x43, 0x69, 0x1f, 0x4c, 0xdc, 0x36, 0xa9, 0xe6, 0x41, 0x45, 0x92, 0x80, + 0xa8, 0xb7, 0x41, 0x00, 0x04, 0x81, 0xcc, 0x1f, 0x3a, 0x9a, 0xfb, 0x83, 0xf3, 0xbe, 0x31, 0xf3, + 0xfc, 0xee, 0x1b, 0xfa, 0x00, 0x38, 0xf5, 0xa3, 0x5f, 0x6c, 0x01, 0x71, 0x3a, 0x69, 0xe4, 0x9f, + 0x3e, 0x3e, 0xcf, 0xce, 0xeb, 0x96, 0xf1, 0x9d, 0x27, 0x1f, 0x61, 0xe0, 0xa7, 0xa5, 0xbd, 0x1b, + 0x2e, 0x0e, 0xa7, 0x9d, 0x8c, 0xf9, 0x90, 0x97, 0x4d, 0xe7, 0x75, 0x30, 0x57, 0xd5, 0x34, 0x01, + 0x75, 0x32, 0x32, 0xd8, 0xd9, 0x69, 0x34, 0xed, 0x17, 0x3e, 0x9d, 0x1d, 0x48, 0x89, 0x97, 0x6a, + 0x42, 0x6c, 0x02, 0xa2, 0xde, 0x01, 0x22, 0x4d, 0x23, 0x41, 0x28, 0x73, 0x87, 0x8e, 0xe4, 0x6e, + 0xd8, 0xb4, 0x63, 0xf6, 0xaf, 0xf6, 0xfc, 0x61, 0x2f, 0x67, 0x6e, 0x7f, 0xe7, 0x0b, 0x51, 0xe7, + 0xc4, 0x0f, 0xbe, 0xf3, 0x64, 0x95, 0x97, 0x6f, 0x2c, 0x71, 0xe5, 0x13, 0x13, 0x36, 0xff, 0x4f, + 0x63, 0x2f, 0x6d, 0xbc, 0x60, 0x7a, 0xde, 0xf5, 0xcc, 0x45, 0x9e, 0x8f, 0x9f, 0x74, 0xc3, 0xe5, + 0x24, 0x54, 0xdc, 0x2e, 0xb5, 0xef, 0xce, 0x46, 0xf4, 0x12, 0xa6, 0x7b, 0x42, 0xa3, 0xbb, 0x09, + 0xc0, 0x58, 0x08, 0x40, 0x35, 0x7d, 0x22, 0x90, 0x16, 0xc5, 0x6a, 0x34, 0xd4, 0xd1, 0x83, 0xe3, + 0xd9, 0xff, 0xb9, 0xf1, 0xae, 0xd9, 0xbf, 0xd8, 0xf7, 0x85, 0x5e, 0xb5, 0xee, 0xf6, 0xf9, 0x77, + 0xf7, 0x7a, 0x7a, 0xf7, 0x17, 0x96, 0xb3, 0xf1, 0xab, 0xc9, 0xdb, 0xd3, 0xb7, 0xee, 0xec, 0xfd, + 0xef, 0x7d, 0x23, 0xdc, 0x92, 0xeb, 0x57, 0x2b, 0xb5, 0xab, 0xd3, 0x74, 0xad, 0xda, 0x04, 0xf3, + 0xc4, 0x06, 0x41, 0x93, 0x72, 0x73, 0x32, 0xc0, 0xd2, 0xe4, 0x17, 0x69, 0x88, 0x15, 0x54, 0x90, + 0x46, 0xa7, 0x50, 0x92, 0xde, 0xae, 0x4d, 0x32, 0x77, 0xb5, 0xaa, 0x5e, 0x3c, 0x38, 0x9e, 0xfd, + 0xec, 0x39, 0xff, 0x67, 0xe6, 0xf1, 0xbd, 0x5f, 0xe8, 0x65, 0xdd, 0x57, 0xe7, 0x7f, 0xce, 0x0b, + 0x03, 0xd7, 0x0f, 0xb0, 0xf9, 0xae, 0xe4, 0xad, 0xd0, 0x6b, 0x5f, 0xec, 0x79, 0xff, 0xe0, 0x69, + 0x72, 0x73, 0xbe, 0x5f, 0x5d, 0xe2, 0xe5, 0x94, 0xa7, 0x1d, 0xdd, 0x6e, 0x21, 0x35, 0x8b, 0xe7, + 0x13, 0x8b, 0x69, 0x78, 0x27, 0x1d, 0xb7, 0x92, 0x0e, 0x20, 0x14, 0x54, 0xa8, 0x92, 0x3c, 0x11, + 0x25, 0x77, 0x55, 0x4c, 0x0c, 0x51, 0xa4, 0xa6, 0xe6, 0xcb, 0x3c, 0xf0, 0xda, 0x81, 0xbe, 0x9b, + 0x3e, 0x74, 0xcf, 0x91, 0x89, 0xd7, 0x6f, 0xea, 0x63, 0xc3, 0xf6, 0xb9, 0xff, 0xe0, 0x15, 0x8e, + 0xeb, 0x86, 0xd9, 0x9c, 0x5e, 0xa4, 0x79, 0xe4, 0xaa, 0xfe, 0xe1, 0xb5, 0x17, 0xc6, 0xbf, 0xdb, + 0x55, 0x52, 0x57, 0x78, 0x79, 0x39, 0x3b, 0x93, 0x07, 0xa5, 0x75, 0xfb, 0xf0, 0x4f, 0xd6, 0xe6, + 0x68, 0x6a, 0x47, 0x52, 0x00, 0x46, 0x52, 0x02, 0x98, 0x68, 0x40, 0x45, 0x80, 0x11, 0x6c, 0x24, + 0x04, 0x91, 0x0e, 0xc3, 0x90, 0xc7, 0xa6, 0x67, 0xf5, 0xf7, 0xd6, 0xdd, 0xbe, 0xf8, 0x97, 0xc9, + 0xfe, 0x83, 0x6c, 0xde, 0x31, 0xf5, 0x8b, 0xb9, 0x54, 0xf3, 0xc6, 0x9d, 0xef, 0x43, 0x55, 0x1e, + 0xe5, 0x8c, 0x5b, 0x13, 0x69, 0x77, 0xdf, 0xd4, 0x77, 0xce, 0xc0, 0xca, 0xf8, 0x57, 0xb2, 0xdd, + 0xea, 0x43, 0x8e, 0x6b, 0x2f, 0xf4, 0x0b, 0xe0, 0x64, 0xd4, 0xd2, 0x66, 0x87, 0x95, 0x56, 0xa1, + 0x89, 0x11, 0xc4, 0x2a, 0x54, 0x2c, 0x10, 0x2b, 0x94, 0x91, 0x94, 0xc9, 0x0a, 0x51, 0x43, 0x08, + 0x23, 0xbd, 0x18, 0xc7, 0x3c, 0x5a, 0xa9, 0xe9, 0x7f, 0x3a, 0x74, 0x34, 0x7f, 0xef, 0x7b, 0xbf, + 0x33, 0x79, 0xec, 0xdd, 0x5e, 0xbd, 0x7a, 0x57, 0xd7, 0x9c, 0x76, 0x5d, 0xb7, 0x9c, 0xcd, 0x3b, + 0xda, 0xb7, 0x0e, 0x5e, 0xbe, 0xbe, 0xef, 0xac, 0x81, 0xd1, 0x68, 0x8b, 0x9b, 0xd1, 0xe7, 0xbb, + 0xae, 0xdd, 0xe6, 0x38, 0x6c, 0xd0, 0xda, 0x16, 0xbd, 0x0c, 0xb8, 0xae, 0x4a, 0x62, 0xbb, 0x55, + 0xad, 0xca, 0x4e, 0x62, 0x88, 0x03, 0x21, 0x0a, 0x04, 0x13, 0xeb, 0xb2, 0x31, 0x6a, 0xa7, 0xb1, + 0x3c, 0xd5, 0x08, 0xd4, 0xb3, 0xe3, 0x13, 0x99, 0x67, 0x2e, 0xf8, 0xf6, 0xcc, 0xf8, 0xc9, 0xee, + 0xc1, 0xfc, 0xc2, 0x81, 0xb4, 0x6e, 0x47, 0x5c, 0x3b, 0xc4, 0xd8, 0xd7, 0x8f, 0xb7, 0xfe, 0xfe, + 0xe1, 0x65, 0x23, 0xc5, 0xd3, 0xb7, 0x2c, 0x9c, 0x5a, 0xec, 0xb7, 0x43, 0x4e, 0xd6, 0x5d, 0xae, + 0x2c, 0x23, 0x62, 0xed, 0x90, 0x42, 0x7a, 0x94, 0x98, 0x0c, 0xa2, 0xb5, 0x18, 0x09, 0xb0, 0x7a, + 0x11, 0xd4, 0x04, 0xc2, 0xdb, 0x51, 0x64, 0x8e, 0xcd, 0x96, 0x9d, 0x89, 0xe7, 0xf7, 0xac, 0x3c, + 0xf4, 0xdb, 0xf7, 0xbf, 0x12, 0xb4, 0xd6, 0xfe, 0x7c, 0x3f, 0x63, 0x7f, 0x34, 0xfb, 0xae, 0x65, + 0xfa, 0x7f, 0x4c, 0xe8, 0x21, 0x04, 0x29, 0xb0, 0x36, 0x16, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, + 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +}; + +const lv_img_dsc_t img_wink_png = { + .header.always_zero = 0, + .header.w = 50, + .header.h = 50, + .data_size = 5158, + .header.cf = LV_IMG_CF_RAW_ALPHA, + .data = img_wink_png_map, +}; + +#endif /*LV_USE_PNG && LV_BUILD_EXAMPLES*/ diff --git a/examples/libs/png/index.rst b/examples/libs/png/index.rst new file mode 100644 index 0000000000..3b5ca1f5bd --- /dev/null +++ b/examples/libs/png/index.rst @@ -0,0 +1,6 @@ +Open a PNG image from file and variable +""""""""""""""""""""""""""""""""""""""""""""""" + +.. lv_example:: libs/bmp/lv_example_png_1 + :language: c + diff --git a/examples/libs/png/lv_example_png.h b/examples/libs/png/lv_example_png.h new file mode 100644 index 0000000000..c964f3ab8d --- /dev/null +++ b/examples/libs/png/lv_example_png.h @@ -0,0 +1,38 @@ +/** + * @file lv_example_png.h + * + */ + +#ifndef LV_EXAMPLE_PNG_H +#define LV_EXAMPLE_PNG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void lv_example_png_1(void); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_EXAMPLE_PNG_H*/ diff --git a/examples/libs/png/lv_example_png_1.c b/examples/libs/png/lv_example_png_1.c new file mode 100644 index 0000000000..91be046a73 --- /dev/null +++ b/examples/libs/png/lv_example_png_1.c @@ -0,0 +1,23 @@ +#include "../../lv_examples.h" +#if LV_USE_PNG && LV_BUILD_EXAMPLES + +/** + * Open a PNG image from a file and a variable + */ +void lv_example_png_1(void) +{ + LV_IMG_DECLARE(img_wink_png); + lv_obj_t * img; + + img = lv_img_create(lv_scr_act()); + lv_img_set_src(img, &img_wink_png); + lv_obj_align(img, LV_ALIGN_LEFT_MID, 20, 0); + + img = lv_img_create(lv_scr_act()); + /* Assuming a File system is attached to letter 'A' + * E.g. set LV_USE_FS_STDIO 'A' in lv_conf.h */ + lv_img_set_src(img, "A:lvgl/examples/libs/png/wink.png"); + lv_obj_align(img, LV_ALIGN_RIGHT_MID, -20, 0); +} + +#endif diff --git a/examples/libs/png/wink.png b/examples/libs/png/wink.png new file mode 100644 index 0000000000..a161d5c9f4 Binary files /dev/null and b/examples/libs/png/wink.png differ diff --git a/examples/libs/qrcode/index.rst b/examples/libs/qrcode/index.rst new file mode 100644 index 0000000000..8c57ea9a6b --- /dev/null +++ b/examples/libs/qrcode/index.rst @@ -0,0 +1,6 @@ +Create a QR Code +""""""""""""""""""""""""""""""""""""""""""""""" + +.. lv_example:: libs/bmp/lv_example_qrcode_1 + :language: c + diff --git a/examples/libs/qrcode/lv_example_qrcode.h b/examples/libs/qrcode/lv_example_qrcode.h new file mode 100644 index 0000000000..3d119032fe --- /dev/null +++ b/examples/libs/qrcode/lv_example_qrcode.h @@ -0,0 +1,38 @@ +/** + * @file lv_example_qrcode.h + * + */ + +#ifndef LV_EXAMPLE_QRCODE_H +#define LV_EXAMPLE_QRCODE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void lv_example_qrcode_1(void); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_EXAMPLE_QRCODE_H*/ diff --git a/examples/libs/qrcode/lv_example_qrcode_1.c b/examples/libs/qrcode/lv_example_qrcode_1.c new file mode 100644 index 0000000000..7e50fec832 --- /dev/null +++ b/examples/libs/qrcode/lv_example_qrcode_1.c @@ -0,0 +1,32 @@ +#include "../../lv_examples.h" +#if LV_USE_QRCODE && LV_BUILD_EXAMPLES + +/** + * Create a QR Code + */ +void lv_example_qrcode_1(void) +{ + lv_color_t bg_color = lv_palette_lighten(LV_PALETTE_LIGHT_BLUE, 5); + lv_color_t fg_color = lv_palette_darken(LV_PALETTE_BLUE, 4); + + lv_obj_t * qr = lv_qrcode_create(lv_scr_act(), 150, fg_color, bg_color); + + /*Set data*/ + const char * data = "https://lvgl.io"; + lv_qrcode_update(qr, data, strlen(data)); + lv_obj_center(qr); + + /*Add a border with bg_color*/ + lv_obj_set_style_border_color(qr, bg_color, 0); + lv_obj_set_style_border_width(qr, 5, 0); +} + +#endif + + + + + + + + diff --git a/examples/libs/sjpg/lv_example_sjpg.h b/examples/libs/sjpg/lv_example_sjpg.h new file mode 100644 index 0000000000..113557c44d --- /dev/null +++ b/examples/libs/sjpg/lv_example_sjpg.h @@ -0,0 +1,38 @@ +/** + * @file lv_example_sjpg.h + * + */ + +#ifndef LV_EXAMPLE_SJPG_H +#define LV_EXAMPLE_SJPG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void lv_example_sjpg_1(void); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_EXAMPLE_SJPG_H*/ diff --git a/examples/libs/sjpg/lv_example_sjpg_1.c b/examples/libs/sjpg/lv_example_sjpg_1.c new file mode 100644 index 0000000000..40b698b0a2 --- /dev/null +++ b/examples/libs/sjpg/lv_example_sjpg_1.c @@ -0,0 +1,14 @@ +#include "../../lv_examples.h" +#if LV_USE_SJPG && LV_BUILD_EXAMPLES + +void lv_example_sjpg_1(void) +{ + lv_obj_t * wp; + + wp = lv_img_create(lv_scr_act()); + /* Assuming a File system is attached to letter 'A' + * E.g. set LV_USE_FS_STDIO 'A' in lv_conf.h */ + lv_img_set_src(wp, "A:lvgl/examples/libs/sjpg/small_image.sjpg"); +} + +#endif diff --git a/examples/libs/sjpg/small_image.sjpg b/examples/libs/sjpg/small_image.sjpg new file mode 100644 index 0000000000..cab12e6dbd Binary files /dev/null and b/examples/libs/sjpg/small_image.sjpg differ diff --git a/examples/lv_examples.h b/examples/lv_examples.h index 3ecc5d7752..94b8ca78a0 100644 --- a/examples/lv_examples.h +++ b/examples/lv_examples.h @@ -24,6 +24,7 @@ extern "C" { #include "event/lv_example_event.h" #include "styles/lv_example_style.h" #include "others/lv_example_others.h" +#include "libs/lv_example_libs.h" /********************* * DEFINES diff --git a/lv_conf_template.h b/lv_conf_template.h index ceedb04158..9a3d188d91 100644 --- a/lv_conf_template.h +++ b/lv_conf_template.h @@ -537,6 +537,44 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ /*A layout similar to Grid in CSS.*/ #define LV_USE_GRID 1 +/*--------------------- + * 3rd party libraries + *--------------------*/ + +/*File system interfaces for common APIs + *To enable set a driver letter for that API*/ +#define LV_USE_FS_STDIO '\0' /*Uses fopen, fread, etc*/ +//#define LV_FS_STDIO_PATH "/home/john/" /*Set the working directory. If commented it will be "./" */ + +#define LV_USE_FS_POSIX '\0' /*Uses open, read, etc*/ +//#define LV_FS_POSIX_PATH "/home/john/" /*Set the working directory. If commented it will be "./" */ + +#define LV_USE_FS_FATFS '\0' /*Uses f_open, f_read, etc*/ + +/*PNG decoder library*/ +#define LV_USE_PNG 0 + +/*BMP decoder library*/ +#define LV_USE_BMP 0 + +/* JPG + split JPG decoder library. + * Split JPG is a custom format optimized for embedded systems. */ +#define LV_USE_SJPG 0 + +/*GIF decoder library*/ +#define LV_USE_GIF 0 + +/*QR code library*/ +#define LV_USE_QRCODE 0 + +/*FreeType library*/ +#define LV_USE_FREETYPE 0 +#if LV_USE_FREETYPE +/*Memory used by FreeType to cache characters [bytes] (-1: no caching)*/ +# define LV_FREETYPE_CACHE_SIZE (16 * 1024) +#endif + + /*================== * EXAMPLES *==================*/ diff --git a/lvgl.h b/lvgl.h index 556cf88beb..7b65919045 100644 --- a/lvgl.h +++ b/lvgl.h @@ -71,6 +71,7 @@ extern "C" { #include "src/extra/layouts/lv_layouts.h" #include "src/extra/themes/lv_themes.h" #include "src/extra/others/lv_others.h" +#include "src/extra/libs/lv_libs.h" /********************* * DEFINES diff --git a/scripts/jpg_to_sjpg.py b/scripts/jpg_to_sjpg.py new file mode 100755 index 0000000000..fc533f6794 --- /dev/null +++ b/scripts/jpg_to_sjpg.py @@ -0,0 +1,138 @@ +################################################################## +# sjpeg converter script version 1.0 +# Dependencies: (PYTHON-3) +################################################################## +SJPG_FILE_FORMAT_VERSION = "V1.00" # +JPEG_SPLIT_HEIGHT = 16 +################################################################## +import math, os, sys, time +from PIL import Image + + +OUTPUT_FILE_NAME = "" +INPUT_FILE = "" + + +if len(sys.argv) == 2: + INPUT_FILE = sys.argv[1] + OUTPUT_FILE_NAME = INPUT_FILE.split("/")[-1].split("\\")[-1].split(".")[0] +else: + print("usage:\n\t python " + sys.argv[0] + " input_file.jpg") + sys.exit(0) + +try: + im = Image.open(INPUT_FILE) +except: + print("\nFile not found!") + sys.exit(0) + + +print("\nConversion started...\n") +start_time = time.time() +width, height = im.size + +print("Input:") +print("\t" + INPUT_FILE) +print("\tRES = " + str(width) + " x " + str(height) + '\n') + + +lenbuf = [] +block_size = JPEG_SPLIT_HEIGHT; +spilts = math.ceil(height/block_size) + +c_code = '''//LVGL SJPG C ARRAY\n#include "lvgl/lvgl.h"\n\nconst uint8_t ''' + OUTPUT_FILE_NAME + '''_map[] = {\n''' + +sjpeg_data = bytearray() +sjpeg = bytearray() + + +row_remaining = height; +for i in range(spilts): + if row_remaining < block_size: + crop = im.crop((0, i*block_size, width, row_remaining + i*block_size)) + else: + crop = im.crop((0, i*block_size, width, block_size + i*block_size)) + + row_remaining = row_remaining - block_size; + crop.save(str(i)+".jpg", quality=90) + + + + +for i in range(spilts): + f = open(str(i)+".jpg", "rb") + a = f.read() + f.close() + sjpeg_data = sjpeg_data + a + lenbuf.append(len(a)) + +header = bytearray() + +#4 BYTES +header = header + bytearray("_SJPG__".encode("UTF-8")); + +#6 BYTES VERSION +header = header + bytearray(("\x00" + SJPG_FILE_FORMAT_VERSION + "\x00").encode("UTF-8")); + +#WIDTH 2 BYTES +header = header + width.to_bytes(2, byteorder='little'); + +#HEIGHT 2 BYTES +header = header + height.to_bytes(2, byteorder='little'); + +#NUMBER OF ITEMS 2 BYTES +header = header + spilts.to_bytes(2, byteorder='little'); + +#NUMBER OF ITEMS 2 BYTES +header = header + int(JPEG_SPLIT_HEIGHT).to_bytes(2, byteorder='little'); + +for item_len in lenbuf: + # WIDTH 2 BYTES + header = header + item_len.to_bytes(2, byteorder='little'); + + +data = bytearray() + +sjpeg = header + sjpeg_data; + +if 1: + for i in range(len(lenbuf)): + os.remove(str(i) + ".jpg") + + +f = open(OUTPUT_FILE_NAME+".sjpg","wb"); +f.write(sjpeg) +f.close() + +new_line_threshold = 0 +for i in range(len(sjpeg)): + c_code = c_code + "\t" + str(hex(sjpeg[i])) + "," + new_line_threshold = new_line_threshold + 1 + if (new_line_threshold >= 16): + c_code = c_code + "\n" + new_line_threshold = 0 + + +c_code = c_code + "\n};\n\nlv_img_dsc_t " +c_code = c_code + OUTPUT_FILE_NAME + " = {\n" +c_code = c_code + "\t.header.always_zero = 0,\n" +c_code = c_code + "\t.header.w = " + str(width) + ",\n" +c_code = c_code + "\t.header.h = " + str(height) + ",\n" +c_code = c_code + "\t.data_size = " + str(len(sjpeg)) + ",\n" +c_code = c_code + "\t.header.cf = LV_IMG_CF_RAW,\n" +c_code = c_code + "\t.data = " + OUTPUT_FILE_NAME+"_map" + ",\n};" + + +f = open(OUTPUT_FILE_NAME + '.c', 'w') +f.write(c_code) +f.close() + + +time_taken = (time.time() - start_time) + +print("Output:") +print("\tTime taken = " + str(round(time_taken,2)) + " sec") +print("\tbin size = " + str(round(len(sjpeg)/1024, 1)) + " KB" ) +print("\t" + OUTPUT_FILE_NAME + ".sjpg\t(bin file)" + "\n\t" + OUTPUT_FILE_NAME + ".c\t\t(c array)") + +print("\nAll good!") diff --git a/src/extra/libs/bmp/lv_bmp.c b/src/extra/libs/bmp/lv_bmp.c new file mode 100644 index 0000000000..8204bae2d9 --- /dev/null +++ b/src/extra/libs/bmp/lv_bmp.c @@ -0,0 +1,228 @@ +/** + * @file lv_bmp.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../../lvgl.h" +#if LV_USE_BMP + +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_fs_file_t f; + unsigned int px_offset; + int px_width; + int px_height; + unsigned int bpp; + int row_size_bytes; +} bmp_dsc_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header); +static lv_res_t decoder_open(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc); + + +static lv_res_t decoder_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, + lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf); + +static void decoder_close(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +void lv_bmp_init(void) +{ + lv_img_decoder_t * dec = lv_img_decoder_create(); + lv_img_decoder_set_info_cb(dec, decoder_info); + lv_img_decoder_set_open_cb(dec, decoder_open); + lv_img_decoder_set_read_line_cb(dec, decoder_read_line); + lv_img_decoder_set_close_cb(dec, decoder_close); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Get info about a PNG image + * @param src can be file name or pointer to a C array + * @param header store the info here + * @return LV_RES_OK: no error; LV_RES_INV: can't get the info + */ +static lv_res_t decoder_info(lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header) +{ + LV_UNUSED(decoder); + + lv_img_src_t src_type = lv_img_src_get_type(src); /*Get the source type*/ + + /*If it's a BMP file...*/ + if(src_type == LV_IMG_SRC_FILE) { + const char * fn = src; + if(!strcmp(&fn[strlen(fn) - 3], "bmp")) { /*Check the extension*/ + /*Save the data in the header*/ + lv_fs_file_t f; + lv_fs_res_t res = lv_fs_open(&f, src, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) return LV_RES_INV; + uint8_t headers[54]; + + lv_fs_read(&f, headers, 54, NULL); + uint32_t w; + uint32_t h; + memcpy(&w, headers + 18, 4); + memcpy(&h, headers + 22, 4); + header->w = w; + header->h = h; + header->always_zero = 0; + lv_fs_close(&f); +#if LV_COLOR_DEPTH == 32 + uint16_t bpp; + memcpy(&bpp, headers + 28, 2); + header->cf = bpp == 32 ? LV_IMG_CF_TRUE_COLOR_ALPHA : LV_IMG_CF_TRUE_COLOR; +#else + header->cf = LV_IMG_CF_TRUE_COLOR; +#endif + return LV_RES_OK; + } + } + /* BMP file as data not supported for simplicity. + * Convert them to LVGL compatible C arrays directly. */ + else if(src_type == LV_IMG_SRC_VARIABLE) { + return LV_RES_INV; + } + + return LV_RES_INV; /*If didn't succeeded earlier then it's an error*/ +} + + +/** + * Open a PNG image and return the decided image + * @param src can be file name or pointer to a C array + * @param style style of the image object (unused now but certain formats might use it) + * @return pointer to the decoded image or `LV_IMG_DECODER_OPEN_FAIL` if failed + */ +static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); + + /*If it's a PNG file...*/ + if(dsc->src_type == LV_IMG_SRC_FILE) { + const char * fn = dsc->src; + + if(strcmp(&fn[strlen(fn) - 3], "bmp")) return LV_RES_INV; /*Check the extension*/ + + bmp_dsc_t b; + memset(&b, 0x00, sizeof(b)); + + lv_fs_res_t res = lv_fs_open(&b.f, dsc->src, LV_FS_MODE_RD); + if(res == LV_RES_OK) return LV_RES_INV; + + uint8_t header[54]; + lv_fs_read(&b.f, header, 54, NULL); + + if (0x42 != header[0] || 0x4d != header[1]) { + return LV_RES_INV; + } + + memcpy(&b.px_offset, header + 10, 4); + memcpy(&b.px_width, header + 18, 4); + memcpy(&b.px_height, header + 22, 4); + memcpy(&b.bpp, header + 28, 2); + b.row_size_bytes = (b.bpp * b.px_width) / 8; + + dsc->user_data = lv_mem_alloc(sizeof(bmp_dsc_t)); + LV_ASSERT_MALLOC(dsc->user_data); + if(dsc->user_data == NULL) return LV_RES_INV; + memcpy(dsc->user_data, &b, sizeof(b)); + + dsc->img_data = NULL; + return LV_RES_OK; + } + /* BMP file as data not supported for simplicity. + * Convert them to LVGL compatible C arrays directly. */ + else if(dsc->src_type == LV_IMG_SRC_VARIABLE) { + return LV_RES_INV; + } + + return LV_RES_INV; /*If not returned earlier then it failed*/ +} + + +static lv_res_t decoder_read_line(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, + lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf) +{ + LV_UNUSED(decoder); + + bmp_dsc_t * b = dsc->user_data; + y = (b->px_height - 1) - y; /*BMP images are stored upside down*/ + uint32_t p = b->px_offset + b->row_size_bytes * y; + p += x * (b->bpp / 8); + lv_fs_seek(&b->f, p, LV_FS_SEEK_SET); + lv_fs_read(&b->f, buf, len * (b->bpp / 8), NULL); + +#if LV_COLOR_DEPTH == 32 + if(b->bpp == 32) { + lv_coord_t i; + for(i = 0; i < len; i++) { + uint8_t b0 = buf[i * 4]; + uint8_t b1 = buf[i * 4 + 1]; + uint8_t b2 = buf[i * 4 + 2]; + uint8_t b3 = buf[i * 4 + 3]; + lv_color32_t *c = (lv_color32_t*)&buf[i*4]; + c->ch.red = b2; + c->ch.green = b1; + c->ch.blue = b0; + c->ch.alpha = b3; + } + } + if(b->bpp == 24) { + lv_coord_t i; + + for(i = len - 1; i >= 0; i--) { + uint8_t * t = &buf[i * 3]; + lv_color32_t *c = (lv_color32_t*)&buf[i*4]; + c->ch.red = t[2]; + c->ch.green = t[1]; + c->ch.blue = t[0]; + c->ch.alpha = 0xff; + } + } +#endif + + return LV_RES_OK; +} + + +/** + * Free the allocated resources + */ +static void decoder_close(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc) +{ + LV_UNUSED(decoder); + bmp_dsc_t * b = dsc->user_data; + lv_fs_close(&b->f); + lv_mem_free(dsc->user_data); + +} + +#endif /*LV_USE_BMP*/ diff --git a/src/extra/libs/bmp/lv_bmp.h b/src/extra/libs/bmp/lv_bmp.h new file mode 100644 index 0000000000..5190d3f8c4 --- /dev/null +++ b/src/extra/libs/bmp/lv_bmp.h @@ -0,0 +1,42 @@ +/** + * @file lv_templ.h + * + */ + +#ifndef LV_BMP_H +#define LV_BMP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lv_conf_internal.h" +#if LV_USE_BMP + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void lv_bmp_init(void); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_BMP*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_BMP_H*/ diff --git a/src/extra/libs/freetype/arial.ttf b/src/extra/libs/freetype/arial.ttf new file mode 100644 index 0000000000..886789b85b Binary files /dev/null and b/src/extra/libs/freetype/arial.ttf differ diff --git a/src/extra/libs/freetype/lv_freetype.c b/src/extra/libs/freetype/lv_freetype.c new file mode 100644 index 0000000000..4dcdcd1302 --- /dev/null +++ b/src/extra/libs/freetype/lv_freetype.c @@ -0,0 +1,480 @@ +/** + * @file lv_freetype.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_freetype.h" +#if LV_USE_FREETYPE + +#include "ft2build.h" +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include FT_CACHE_H +#include FT_SIZES_H + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +typedef struct { + char * name; +} lv_face_info_t; + +typedef struct { + lv_ll_t face_ll; +} lv_faces_control_t; + +typedef struct { +#if LV_FREETYPE_CACHE_SIZE >= 0 + void * face_id; +#else + FT_Size size; +#endif + lv_font_t * font; + uint16_t style; + uint16_t height; +} lv_font_fmt_ft_dsc_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +#if LV_FREETYPE_CACHE_SIZE >= 0 +static FT_Error font_face_requester(FTC_FaceID face_id, + FT_Library library_is, FT_Pointer req_data, FT_Face * aface); +static bool lv_ft_font_init_cache(lv_ft_info_t * info); +static void lv_ft_font_destroy_cache(lv_font_t * font); +static bool lv_ft_font_init_cache(lv_ft_info_t * info); +static void lv_ft_font_destroy_cache(lv_font_t * font); +#else +static FT_Face face_find_in_list(lv_ft_info_t * info); +static void face_add_to_list(FT_Face face); +static void face_remove_from_list(FT_Face face); +static void face_generic_finalizer(void * object); +static bool lv_ft_font_init_nocache(lv_ft_info_t * info); +static void lv_ft_font_destroy_nocache(lv_font_t * font); +#endif + +/********************** +* STATIC VARIABLES +**********************/ +static FT_Library library; + +#if LV_FREETYPE_CACHE_SIZE >= 0 + static FTC_Manager cache_manager; + static FTC_CMapCache cmap_cache; + static FTC_SBitCache sbit_cache; + static FTC_SBit sbit; +#else + static lv_faces_control_t face_control; +#endif + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +bool lv_freetype_init(uint16_t max_faces, uint16_t max_sizes, uint32_t max_bytes) +{ + FT_Error error = FT_Init_FreeType(&library); + if(error) { + LV_LOG_ERROR("init freeType error(%d)", error); + return false; + } + +#if LV_FREETYPE_CACHE_SIZE >= 0 + error = FTC_Manager_New(library, max_faces, max_sizes, + max_bytes, font_face_requester, NULL, &cache_manager); + if(error) { + FT_Done_FreeType(library); + LV_LOG_ERROR("Failed to open cache manager"); + return false; + } + + error = FTC_CMapCache_New(cache_manager, &cmap_cache); + if(error) { + LV_LOG_ERROR("Failed to open Cmap Cache"); + goto Fail; + } + + error = FTC_SBitCache_New(cache_manager, &sbit_cache); + if(error) { + LV_LOG_ERROR("Failed to open sbit cache"); + goto Fail; + } + + return true; +Fail: + FTC_Manager_Done(cache_manager); + FT_Done_FreeType(library); + return false; +#else + LV_UNUSED(max_faces); + LV_UNUSED(max_sizes); + LV_UNUSED(max_bytes); + _lv_ll_init(&face_control.face_ll, sizeof(FT_Face *)); + return true; +#endif/* LV_FREETYPE_CACHE_SIZE */ +} + +void lv_freetype_destroy(void) +{ +#if LV_FREETYPE_CACHE_SIZE >= 0 + FTC_Manager_Done(cache_manager); +#endif + FT_Done_FreeType(library); +} + +bool lv_ft_font_init(lv_ft_info_t * info) +{ +#if LV_FREETYPE_CACHE_SIZE >= 0 + return lv_ft_font_init_cache(info); +#else + return lv_ft_font_init_nocache(info); +#endif +} + +void lv_ft_font_destroy(lv_font_t * font) +{ +#if LV_FREETYPE_CACHE_SIZE >= 0 + lv_ft_font_destroy_cache(font); +#else + lv_ft_font_destroy_nocache(font); +#endif +} + +/********************** + * STATIC FUNCTIONS + **********************/ +#if LV_FREETYPE_CACHE_SIZE >= 0 + +static FT_Error font_face_requester(FTC_FaceID face_id, + FT_Library library_is, FT_Pointer req_data, FT_Face * aface) +{ + LV_UNUSED(library_is); + LV_UNUSED(req_data); + + lv_face_info_t * info = (lv_face_info_t *)face_id; + FT_Error error = FT_New_Face(library, info->name, 0, aface); + if(error) { + LV_LOG_ERROR("FT_New_Face error:%d\n", error); + return error; + } + return FT_Err_Ok; +} + +static bool get_glyph_dsc_cb_cache(const lv_font_t * font, + lv_font_glyph_dsc_t * dsc_out, uint32_t unicode_letter, uint32_t unicode_letter_next) +{ + LV_UNUSED(unicode_letter_next); + if(unicode_letter < 0x20) { + dsc_out->adv_w = 0; + dsc_out->box_h = 0; + dsc_out->box_w = 0; + dsc_out->ofs_x = 0; + dsc_out->ofs_y = 0; + dsc_out->bpp = 0; + return true; + } + + lv_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)(font->dsc); + + FT_Face face; + FTC_ImageTypeRec desc_sbit_type; + FTC_FaceID face_id = (FTC_FaceID)dsc->face_id; + FTC_Manager_LookupFace(cache_manager, face_id, &face); + + desc_sbit_type.face_id = face_id; + desc_sbit_type.flags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL; + desc_sbit_type.height = dsc->height; + desc_sbit_type.width = dsc->height; + FT_UInt charmap_index = FT_Get_Charmap_Index(face->charmap); + FT_UInt glyph_index = FTC_CMapCache_Lookup(cmap_cache, face_id, charmap_index, unicode_letter); + FT_Error error = FTC_SBitCache_Lookup(sbit_cache, &desc_sbit_type, glyph_index, &sbit, NULL); + if(error) { + LV_LOG_ERROR("SBitCache_Lookup error"); + } + + dsc_out->adv_w = sbit->xadvance; + dsc_out->box_h = sbit->height; /*Height of the bitmap in [px]*/ + dsc_out->box_w = sbit->width; /*Width of the bitmap in [px]*/ + dsc_out->ofs_x = sbit->left; /*X offset of the bitmap in [pf]*/ + dsc_out->ofs_y = sbit->top - sbit->height; /*Y offset of the bitmap measured from the as line*/ + dsc_out->bpp = 8; /*Bit per pixel: 1/2/4/8*/ + + return true; +} + +static const uint8_t * get_glyph_bitmap_cb_cache(const lv_font_t * font, uint32_t unicode_letter) +{ + LV_UNUSED(font); + LV_UNUSED(unicode_letter); + return (const uint8_t *)sbit->buffer; +} + +static bool lv_ft_font_init_cache(lv_ft_info_t * info) +{ + lv_font_fmt_ft_dsc_t * dsc = lv_mem_alloc(sizeof(lv_font_fmt_ft_dsc_t)); + if(dsc == NULL) return false; + + dsc->font = lv_mem_alloc(sizeof(lv_font_t)); + if(dsc->font == NULL) { + lv_mem_free(dsc); + return false; + } + + lv_face_info_t * face_info = NULL; + face_info = lv_mem_alloc(sizeof(lv_face_info_t) + strlen(info->name) + 1); + if(face_info == NULL) { + goto Fail; + } + face_info->name = ((char *)face_info) + sizeof(lv_face_info_t); + strcpy(face_info->name, info->name); + + dsc->face_id = face_info; + dsc->height = info->weight; + dsc->style = info->style; + + /* use to get font info */ + FT_Size face_size; + struct FTC_ScalerRec_ scaler; + scaler.face_id = (FTC_FaceID)dsc->face_id; + scaler.width = info->weight; + scaler.height = info->weight; + scaler.pixel = 1; + FT_Error error = FTC_Manager_LookupSize(cache_manager, &scaler, &face_size); + if(error) { + lv_mem_free(face_info); + LV_LOG_ERROR("Failed to LookupSize"); + goto Fail; + } + + lv_font_t * font = dsc->font; + font->dsc = dsc; + font->get_glyph_dsc = get_glyph_dsc_cb_cache; + font->get_glyph_bitmap = get_glyph_bitmap_cb_cache; + font->subpx = LV_FONT_SUBPX_NONE; + font->line_height = (face_size->face->size->metrics.height >> 6); + font->base_line = -(face_size->face->size->metrics.descender >> 6); + font->underline_position = face_size->face->underline_position; + font->underline_thickness = face_size->face->underline_thickness; + + /* return to user */ + info->font = font; + + return true; + +Fail: + lv_mem_free(dsc->font); + lv_mem_free(dsc); + return false; +} + +void lv_ft_font_destroy_cache(lv_font_t * font) +{ + if(font == NULL) { + return; + } + + lv_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)(font->dsc); + if(dsc) { + lv_mem_free(dsc->face_id); + lv_mem_free(dsc->font); + lv_mem_free(dsc); + } +} + +#else/* LV_FREETYPE_CACHE_SIZE */ + +static FT_Face face_find_in_list(lv_ft_info_t * info) +{ + lv_face_info_t * face_info; + FT_Face * pface = _lv_ll_get_head(&face_control.face_ll); + while(pface) { + face_info = (lv_face_info_t *)(*pface)->generic.data; + if(strcmp(face_info->name, info->name) == 0) { + return *pface; + } + pface = _lv_ll_get_next(&face_control.face_ll, pface); + } + + return NULL; +} + +static void face_add_to_list(FT_Face face) +{ + FT_Face * pface; + pface = (FT_Face *)_lv_ll_ins_tail(&face_control.face_ll); + *pface = face; +} + +static void face_remove_from_list(FT_Face face) +{ + FT_Face * pface = _lv_ll_get_head(&face_control.face_ll); + while(pface) { + if(*pface == face) { + _lv_ll_remove(&face_control.face_ll, pface); + lv_mem_free(pface); + break; + } + pface = _lv_ll_get_next(&face_control.face_ll, pface); + } +} + +static void face_generic_finalizer(void * object) +{ + FT_Face face = (FT_Face)object; + face_remove_from_list(face); + if(face->generic.data) { + lv_face_info_t * face_info = (lv_face_info_t *)face->generic.data; + lv_mem_free(face_info); + } + LV_LOG_INFO("face finalizer(%p)\n", face); +} + +static bool get_glyph_dsc_cb_nocache(const lv_font_t * font, + lv_font_glyph_dsc_t * dsc_out, uint32_t unicode_letter, uint32_t unicode_letter_next) +{ + LV_UNUSED(unicode_letter_next); + if(unicode_letter < 0x20) { + dsc_out->adv_w = 0; + dsc_out->box_h = 0; + dsc_out->box_w = 0; + dsc_out->ofs_x = 0; + dsc_out->ofs_y = 0; + dsc_out->bpp = 0; + return true; + } + + FT_Error error; + lv_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)(font->dsc); + FT_Face face = dsc->size->face; + + FT_UInt glyph_index = FT_Get_Char_Index(face, unicode_letter); + + if(face->size != dsc->size) { + FT_Activate_Size(dsc->size); + } + + error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); + if(error) { + return false; + } + + error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); + if(error) { + return false; + } + + dsc_out->adv_w = (face->glyph->metrics.horiAdvance >> 6); + dsc_out->box_h = face->glyph->bitmap.rows; /*Height of the bitmap in [px]*/ + dsc_out->box_w = face->glyph->bitmap.width; /*Width of the bitmap in [px]*/ + dsc_out->ofs_x = face->glyph->bitmap_left; /*X offset of the bitmap in [pf]*/ + dsc_out->ofs_y = face->glyph->bitmap_top - + face->glyph->bitmap.rows; /*Y offset of the bitmap measured from the as line*/ + dsc_out->bpp = 8; /*Bit per pixel: 1/2/4/8*/ + + return true; +} + +static const uint8_t * get_glyph_bitmap_cb_nocache(const lv_font_t * font, uint32_t unicode_letter) +{ + LV_UNUSED(unicode_letter); + lv_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)(font->dsc); + FT_Face face = dsc->size->face; + return (const uint8_t *)(face->glyph->bitmap.buffer); +} + +static bool lv_ft_font_init_nocache(lv_ft_info_t * info) +{ + lv_font_fmt_ft_dsc_t * dsc = lv_mem_alloc(sizeof(lv_font_fmt_ft_dsc_t)); + if(dsc == NULL) return false; + + dsc->font = lv_mem_alloc(sizeof(lv_font_t)); + if(dsc->font == NULL) { + lv_mem_free(dsc); + return false; + } + + lv_face_info_t * face_info = NULL; + FT_Face face = face_find_in_list(info); + if(face == NULL) { + face_info = lv_mem_alloc(sizeof(lv_face_info_t) + strlen(info->name) + 1); + if(face_info == NULL) { + goto Fail; + } + FT_Error error = FT_New_Face(library, info->name, 0, &face); + if(error) { + lv_mem_free(face_info); + LV_LOG_WARN("create face error(%d)", error); + goto Fail; + } + + /* link face and face info */ + face_info->name = ((char *)face_info) + sizeof(lv_face_info_t); + strcpy(face_info->name, info->name); + face->generic.data = face_info; + face->generic.finalizer = face_generic_finalizer; + face_add_to_list(face); + } + else { + FT_Size size; + FT_Error error = FT_New_Size(face, &size); + if(error) { + goto Fail; + } + FT_Activate_Size(size); + FT_Reference_Face(face); + } + + FT_Set_Pixel_Sizes(face, 0, info->weight); + dsc->size = face->size; + dsc->height = info->weight; + dsc->style = info->style; + + lv_font_t * font = dsc->font; + font->dsc = dsc; + font->get_glyph_dsc = get_glyph_dsc_cb_nocache; + font->get_glyph_bitmap = get_glyph_bitmap_cb_nocache; + font->line_height = (face->size->metrics.height >> 6); + font->base_line = -(face->size->metrics.descender >> 6); + font->underline_position = face->underline_position; + font->underline_thickness = face->underline_thickness; + font->subpx = LV_FONT_SUBPX_NONE; + + info->font = font; + return true; + +Fail: + lv_mem_free(dsc->font); + lv_mem_free(dsc); + return false; +} + +static void lv_ft_font_destroy_nocache(lv_font_t * font) +{ + if(font == NULL) { + return; + } + + lv_font_fmt_ft_dsc_t * dsc = (lv_font_fmt_ft_dsc_t *)(font->dsc); + if(dsc) { + FT_Face face = dsc->size->face; + FT_Done_Size(dsc->size); + FT_Done_Face(face); + lv_mem_free(dsc->font); + lv_mem_free(dsc); + } +} + +#endif/* LV_FREETYPE_CACHE_SIZE */ + +#endif /*LV_USE_FREETYPE*/ diff --git a/src/extra/libs/freetype/lv_freetype.h b/src/extra/libs/freetype/lv_freetype.h new file mode 100644 index 0000000000..3f1fe70763 --- /dev/null +++ b/src/extra/libs/freetype/lv_freetype.h @@ -0,0 +1,81 @@ +/** + * @file lv_freetype.h + * + */ +#ifndef LV_FREETYPE_H +#define LV_FREETYPE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lvgl.h" +#if LV_USE_FREETYPE + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +typedef enum { + FT_FONT_STYLE_NORMAL = 0, + FT_FONT_STYLE_ITALIC = 1 << 0, + FT_FONT_STYLE_BOLD = 1 << 1 +} LV_FT_FONT_STYLE; + +typedef struct { + const char * name; /* The name of the font file */ + lv_font_t * font; /* point to lvgl font */ + uint16_t weight; /* font size */ + uint16_t style; /* font style */ +} lv_ft_info_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * init freetype library + * @param max_faces Maximum number of opened FT_Face objects managed by this cache instance. Use 0 for defaults. + * @param max_sizes Maximum number of opened FT_Size objects managed by this cache instance. Use 0 for defaults. + * @param max_bytes Maximum number of bytes to use for cached data nodes. Use 0 for defaults. + * Note that this value does not account for managed FT_Face and FT_Size objects. + * @return true on success, otherwise false. + */ +bool lv_freetype_init(uint16_t max_faces, uint16_t max_sizes, uint32_t max_bytes); + +/** + * Destroy freetype library + */ +void lv_freetype_destroy(void); + +/** + * Creates a font with info parameter specified. + * @param info See lv_ft_info_t for details. + * when success, lv_ft_info_t->font point to the font you created. + * @return true on success, otherwise false. + */ +bool lv_ft_font_init(lv_ft_info_t * info); + +/** + * Destroy a font that has been created. + * @param font pointer to font. + */ +void lv_ft_font_destroy(lv_font_t * font); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_FREETYPE*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV_FREETYPE_H */ diff --git a/src/extra/libs/fs/lv_fs_fatfs.c b/src/extra/libs/fs/lv_fs_fatfs.c new file mode 100644 index 0000000000..95823bbdb7 --- /dev/null +++ b/src/extra/libs/fs/lv_fs_fatfs.c @@ -0,0 +1,258 @@ +/** + * @file lv_fs_fatfs.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../../lvgl.h" + +#if LV_USE_FS_FATFS != '\0' +#include "ff.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void fs_init(void); + +static void * fs_open (lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode); +static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p); +static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br); +static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw); +static lv_fs_res_t fs_seek (lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence); +static lv_fs_res_t fs_tell (lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p); +static void * fs_dir_open (lv_fs_drv_t * drv, const char *path); +static lv_fs_res_t fs_dir_read (lv_fs_drv_t * drv, void * dir_p, char *fn); +static lv_fs_res_t fs_dir_close (lv_fs_drv_t * drv, void * dir_p); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_fs_if_fatfs_init(void) +{ + /*---------------------------------------------------- + * Initialize your storage device and File System + * -------------------------------------------------*/ + fs_init(); + + /*--------------------------------------------------- + * Register the file system interface in LittlevGL + *--------------------------------------------------*/ + + /* Add a simple drive to open images */ + static lv_fs_drv_t fs_drv; /*A driver descriptor*/ + lv_fs_drv_init(&fs_drv); + + /*Set up fields...*/ + fs_drv.letter = LV_USE_FS_FATFS; + fs_drv.open_cb = fs_open; + fs_drv.close_cb = fs_close; + fs_drv.read_cb = fs_read; + fs_drv.write_cb = fs_write; + fs_drv.seek_cb = fs_seek; + fs_drv.tell_cb = fs_tell; + + fs_drv.dir_close_cb = fs_dir_close; + fs_drv.dir_open_cb = fs_dir_open; + fs_drv.dir_read_cb = fs_dir_read; + + lv_fs_drv_register(&fs_drv); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/* Initialize your Storage device and File system. */ +static void fs_init(void) +{ + /* Initialize the SD card and FatFS itself. + * Better to do it in your code to keep this library utouched for easy updating*/ +} + +/** + * Open a file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable + * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt) + * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static void * fs_open (lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode) +{ + uint8_t flags = 0; + + if(mode == LV_FS_MODE_WR) flags = FA_WRITE | FA_OPEN_ALWAYS; + else if(mode == LV_FS_MODE_RD) flags = FA_READ; + else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = FA_READ | FA_WRITE | FA_OPEN_ALWAYS; + + FIL * f = lv_mem_alloc(sizeof(FIL)); + if(f == NULL) return NULL; + + FRESULT res = f_open(f, path, flags); + + if(res == FR_OK) { + f_lseek(f, 0); + return f; + } else { + return NULL; + } +} + + +/** + * Close an opened file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. (opened with lv_ufs_open) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p) +{ + f_close(file_p); + return LV_FS_RES_OK; +} + +/** + * Read data from an opened file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. + * @param buf pointer to a memory block where to store the read data + * @param btr number of Bytes To Read + * @param br the real number of read bytes (Byte Read) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br) +{ + FRESULT res = f_read(file_p, buf, btr, (UINT*)br); + if(res == FR_OK) return LV_FS_RES_OK; + else return LV_FS_RES_UNKNOWN; +} + +/** + * Write into a file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable + * @param buf pointer to a buffer with the bytes to write + * @param btr Bytes To Write + * @param br the number of real written bytes (Bytes Written). NULL if unused. + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw) +{ + FRESULT res = f_write(file_p, buf, btw, (UINT*)bw); + if(res == FR_OK) return LV_FS_RES_OK; + else return LV_FS_RES_UNKNOWN; +} + +/** + * Set the read write pointer. Also expand the file size if necessary. + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. (opened with lv_ufs_open ) + * @param pos the new position of read write pointer + * @param whence only LV_SEEK_SET is supported + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_seek (lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence) +{ + LV_UNUSED(whence); + f_lseek(file_p, pos); + return LV_FS_RES_OK; +} + +/** + * Give the position of the read write pointer + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. + * @param pos_p pointer to to store the result + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_tell (lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p) +{ + *pos_p = f_tell(((FIL *)file_p)); + return LV_FS_RES_OK; +} + +/** + * Initialize a 'fs_read_dir_t' variable for directory reading + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to a 'fs_read_dir_t' variable + * @param path path to a directory + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static void * fs_dir_open (lv_fs_drv_t * drv, const char *path) +{ + DIR * d = lv_mem_alloc(sizeof(DIR)); + if(d == NULL) return NULL; + + FRESULT res = f_opendir(d, path); + if(res != FR_OK) { + lv_mem_free(d); + d = NULL; + } + return d; +} + +/** + * Read the next filename form a directory. + * The name of the directories will begin with '/' + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to an initialized 'fs_read_dir_t' variable + * @param fn pointer to a buffer to store the filename + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_dir_read (lv_fs_drv_t * drv, void * dir_p, char *fn) +{ + FRESULT res; + FILINFO fno; + fn[0] = '\0'; + + do { + res = f_readdir(dir_p, &fno); + if(res != FR_OK) return LV_FS_RES_UNKNOWN; + + if(fno.fattrib & AM_DIR) { + fn[0] = '/'; + strcpy(&fn[1], fno.fname); + } + else strcpy(fn, fno.fname); + + } while(strcmp(fn, "/.") == 0 || strcmp(fn, "/..") == 0); + + return LV_FS_RES_OK; +} + +/** + * Close the directory reading + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to an initialized 'fs_read_dir_t' variable + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_dir_close (lv_fs_drv_t * drv, void * dir_p) +{ + f_closedir(dir_p); + return LV_FS_RES_OK; +} + +#endif /*LV_USE_FS_FATFS*/ diff --git a/src/extra/libs/fs/lv_fs_libs.h b/src/extra/libs/fs/lv_fs_libs.h new file mode 100644 index 0000000000..d01aa68f2e --- /dev/null +++ b/src/extra/libs/fs/lv_fs_libs.h @@ -0,0 +1,50 @@ +/** + * @file lv_fs_libs.h + * + */ + +#ifndef LV_FS_LIBS_H +#define LV_FS_LIBS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +#if LV_USE_FS_FATFS != '\0' + void lv_fs_fatfs_init(void); +#endif + +#if LV_USE_FS_STDIO != '\0' + void lv_fs_stdio_init(void); +#endif + +#if LV_USE_FS_POSIX != '\0' + void lv_fs_posix_init(void); +#endif + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_FS_LIBS_H*/ + diff --git a/src/extra/libs/fs/lv_fs_posix.c b/src/extra/libs/fs/lv_fs_posix.c new file mode 100644 index 0000000000..fa961b1c9a --- /dev/null +++ b/src/extra/libs/fs/lv_fs_posix.c @@ -0,0 +1,334 @@ +/** + * @file lv_fs_posix.c + * + */ + + +/********************* + * INCLUDES + *********************/ +#include "../../../lvgl.h" +#if LV_USE_FS_POSIX != '\0' + +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#endif + +/********************* + * DEFINES + *********************/ +#ifndef LV_FS_POSIX_PATH +# ifndef WIN32 +# define LV_FS_POSIX_PATH "./" /*Project root*/ +# else +# define LV_FS_POSIX_PATH ".\\" /*Project root*/ +# endif +#endif /*LV_FS_PATH*/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void * fs_open (lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode); +static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p); +static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br); +static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw); +static lv_fs_res_t fs_seek (lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence); +static lv_fs_res_t fs_tell (lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p); +static void * fs_dir_open (lv_fs_drv_t * drv, const char *path); +static lv_fs_res_t fs_dir_read (lv_fs_drv_t * drv, void * dir_p, char *fn); +static lv_fs_res_t fs_dir_close (lv_fs_drv_t * drv, void * dir_p); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Register a driver for the File system interface + */ +void lv_fs_posix_init(void) +{ + /*--------------------------------------------------- + * Register the file system interface in LittlevGL + *--------------------------------------------------*/ + + /* Add a simple drive to open images */ + static lv_fs_drv_t fs_drv; /*A driver descriptor*/ + lv_fs_drv_init(&fs_drv); + + /*Set up fields...*/ + fs_drv.letter = LV_USE_FS_POSIX; + fs_drv.open_cb = fs_open; + fs_drv.close_cb = fs_close; + fs_drv.read_cb = fs_read; + fs_drv.write_cb = fs_write; + fs_drv.seek_cb = fs_seek; + fs_drv.tell_cb = fs_tell; + + fs_drv.dir_close_cb = fs_dir_close; + fs_drv.dir_open_cb = fs_dir_open; + fs_drv.dir_read_cb = fs_dir_read; + + lv_fs_drv_register(&fs_drv); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Open a file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable + * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt) + * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static void * fs_open (lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode) +{ + (void) drv; /*Unused*/ + errno = 0; + + uint32_t flags = 0; + if(mode == LV_FS_MODE_WR) flags = O_WRONLY; + else if(mode == LV_FS_MODE_RD) flags = O_RDONLY; + else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = O_RDWR; + + /*Make the path relative to the current directory (the projects root folder)*/ + char buf[256]; + sprintf(buf, LV_FS_POSIX_PATH "%s", path); + + + int f = open(buf, flags); + if(f < 0) return NULL; + + /*Be sure we are the beginning of the file*/ + lseek(f, 0, SEEK_SET); + + int * fp = lv_mem_alloc(sizeof(int)); + if(fp == NULL) return NULL; + *fp = f; + + return fp; +} + + +/** + * Close an opened file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. (opened with lv_ufs_open) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p) +{ + (void) drv; /*Unused*/ + int * fp = file_p; + close(*fp); + lv_mem_free(file_p); + return LV_FS_RES_OK; +} + +/** + * Read data from an opened file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. + * @param buf pointer to a memory block where to store the read data + * @param btr number of Bytes To Read + * @param br the real number of read bytes (Byte Read) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br) +{ + (void) drv; /*Unused*/ + int * fp = file_p; + *br = read(*fp, buf, btr); + return LV_FS_RES_OK; +} + +/** + * Write into a file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable + * @param buf pointer to a buffer with the bytes to write + * @param btr Bytes To Write + * @param br the number of real written bytes (Bytes Written). NULL if unused. + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw) +{ + (void) drv; /*Unused*/ + int * fp = file_p; + *bw = write(*fp, buf, btw); + return LV_FS_RES_OK; +} + +/** + * Set the read write pointer. Also expand the file size if necessary. + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. (opened with lv_ufs_open ) + * @param pos the new position of read write pointer + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_seek (lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence) +{ + (void) drv; /*Unused*/ + int * fp = file_p; + lseek(*fp, pos, whence); + return LV_FS_RES_OK; +} + +/** + * Give the position of the read write pointer + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. + * @param pos_p pointer to to store the result + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_tell (lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p) +{ + (void) drv; /*Unused*/ + int * fp = file_p; + *pos_p = lseek(*fp, 0, SEEK_CUR); + return LV_FS_RES_OK; +} + + +#ifdef WIN32 +static char next_fn[256]; +#endif + +/** + * Initialize a 'fs_read_dir_t' variable for directory reading + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to a 'fs_read_dir_t' variable + * @param path path to a directory + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static void * fs_dir_open (lv_fs_drv_t * drv, const char *path) +{ + LV_UNUSED(drv); + +#ifndef WIN32 + /*Make the path relative to the current directory (the projects root folder)*/ + char buf[256]; + sprintf(buf, LV_FS_POSIX_PATH "/%s", path); + return opendir(buf); +#else + HANDLE d = INVALID_HANDLE_VALUE; + WIN32_FIND_DATA fdata; + + /*Make the path relative to the current directory (the projects root folder)*/ + char buf[256]; + sprintf(buf, LV_FS_PC_PATH "\\%s\\*", path); + + strcpy(next_fn, ""); + d = FindFirstFile(buf, &fdata); + do { + if (strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0) { + continue; + } else { + + if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + sprintf(next_fn, "/%s", fdata.cFileName); + } else { + sprintf(next_fn, "%s", fdata.cFileName); + } + break; + } + } while(FindNextFileA(d, &fdata)); + + return d; +#endif +} + +/** + * Read the next filename form a directory. + * The name of the directories will begin with '/' + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to an initialized 'fs_read_dir_t' variable + * @param fn pointer to a buffer to store the filename + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_dir_read (lv_fs_drv_t * drv, void * dir_p, char *fn) +{ + LV_UNUSED(drv); + LV_UNUSED(dir_p); + LV_UNUSED(fn); + +// #ifndef WIN32 +// struct dirent *entry; +// do { +// entry = readdir(dir_p); + +// if(entry) { +// if(entry->d_type == DT_DIR) sprintf(fn, "/%s", entry->d_name); +// else strcpy(fn, entry->d_name); +// } else { +// strcpy(fn, ""); +// } +// } while(strcmp(fn, "/.") == 0 || strcmp(fn, "/..") == 0); +// #else +// strcpy(fn, next_fn); + +// strcpy(next_fn, ""); +// WIN32_FIND_DATA fdata; + +// if(FindNextFile(dir_p, &fdata) == false) return LV_FS_RES_OK; +// do { +// if (strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0) { +// continue; +// } else { + +// if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) +// { +// sprintf(next_fn, "/%s", fdata.cFileName); +// } else { +// sprintf(next_fn, "%s", fdata.cFileName); +// } +// break; +// } +// } while(FindNextFile(dir_p, &fdata)); + +// #endif + return LV_FS_RES_OK; +} + +/** + * Close the directory reading + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to an initialized 'fs_read_dir_t' variable + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_dir_close (lv_fs_drv_t * drv, void * dir_p) +{ + LV_UNUSED(drv); +#ifndef WIN32 + closedir(dir_p); +#else + FindClose(dir_p); +#endif + return LV_FS_RES_OK; +} + +#endif /*LV_USE_FS_POSIX*/ diff --git a/src/extra/libs/fs/lv_fs_stdio.c b/src/extra/libs/fs/lv_fs_stdio.c new file mode 100644 index 0000000000..d72ceb813b --- /dev/null +++ b/src/extra/libs/fs/lv_fs_stdio.c @@ -0,0 +1,330 @@ +/** + * @file lv_fs_stdio.c + * + */ + + +/********************* + * INCLUDES + *********************/ +#include "../../../lvgl.h" +#if LV_USE_FS_STDIO != '\0' + +#include +#include +#include +#include +#ifdef WIN32 +#include +#endif + +/********************* + * DEFINES + *********************/ +#ifndef LV_FS_STDIO_PATH +# ifndef WIN32 +# define LV_FS_STDIO_PATH "./" /*Project root*/ +# else +# define LV_FS_STDIO_PATH ".\\" /*Project root*/ +# endif +#endif /*LV_FS_STDIO_PATH*/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void * fs_open (lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode); +static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p); +static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br); +static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw); +static lv_fs_res_t fs_seek (lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence); +static lv_fs_res_t fs_tell (lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p); +static void * fs_dir_open (lv_fs_drv_t * drv, const char *path); +static lv_fs_res_t fs_dir_read (lv_fs_drv_t * drv, void * dir_p, char *fn); +static lv_fs_res_t fs_dir_close (lv_fs_drv_t * drv, void * dir_p); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Register a driver for the File system interface + */ +void lv_fs_stdio_init(void) +{ + /*--------------------------------------------------- + * Register the file system interface in LittlevGL + *--------------------------------------------------*/ + + /* Add a simple drive to open images */ + static lv_fs_drv_t fs_drv; /*A driver descriptor*/ + lv_fs_drv_init(&fs_drv); + + /*Set up fields...*/ + fs_drv.letter = LV_USE_FS_STDIO; + fs_drv.open_cb = fs_open; + fs_drv.close_cb = fs_close; + fs_drv.read_cb = fs_read; + fs_drv.write_cb = fs_write; + fs_drv.seek_cb = fs_seek; + fs_drv.tell_cb = fs_tell; + + fs_drv.dir_close_cb = fs_dir_close; + fs_drv.dir_open_cb = fs_dir_open; + fs_drv.dir_read_cb = fs_dir_read; + + lv_fs_drv_register(&fs_drv); + + char cur_path[512]; + getcwd(cur_path, sizeof(cur_path)); + LV_LOG_INFO("STDIO file system is initialized with %s root directory.", cur_path); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Open a file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable + * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt) + * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static void * fs_open (lv_fs_drv_t * drv, const char * path, lv_fs_mode_t mode) +{ + (void) drv; /*Unused*/ + errno = 0; + + const char * flags = ""; + + if(mode == LV_FS_MODE_WR) flags = "wb"; + else if(mode == LV_FS_MODE_RD) flags = "rb"; + else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = "rb+"; + + /*Make the path relative to the current directory (the projects root folder)*/ + +#ifndef WIN32 + char buf[256]; + sprintf(buf, LV_FS_STDIO_PATH "%s", path); +#else + char buf[256]; + sprintf(buf, LV_FS_STDIO_PATH "%s", path); +#endif + + FILE * f = fopen(buf, flags); + if(f == NULL) return NULL; + + /*Be sure we are the beginning of the file*/ + fseek(f, 0, SEEK_SET); + + return f; +} + + +/** + * Close an opened file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. (opened with lv_ufs_open) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p) +{ + (void) drv; /*Unused*/ + fclose(file_p); + return LV_FS_RES_OK; +} + +/** + * Read data from an opened file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. + * @param buf pointer to a memory block where to store the read data + * @param btr number of Bytes To Read + * @param br the real number of read bytes (Byte Read) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br) +{ + (void) drv; /*Unused*/ + *br = fread(buf, 1, btr, file_p); + return LV_FS_RES_OK; +} + +/** + * Write into a file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable + * @param buf pointer to a buffer with the bytes to write + * @param btr Bytes To Write + * @param br the number of real written bytes (Bytes Written). NULL if unused. + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw) +{ + (void) drv; /*Unused*/ + *bw = fwrite(buf, 1, btw, file_p); + return LV_FS_RES_OK; +} + +/** + * Set the read write pointer. Also expand the file size if necessary. + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. (opened with lv_ufs_open ) + * @param pos the new position of read write pointer + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_seek (lv_fs_drv_t * drv, void * file_p, uint32_t pos, lv_fs_whence_t whence) +{ + (void) drv; /*Unused*/ + fseek(file_p, pos, whence); + return LV_FS_RES_OK; +} + +/** + * Give the position of the read write pointer + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. + * @param pos_p pointer to to store the result + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_tell (lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p) +{ + (void) drv; /*Unused*/ + *pos_p = ftell(file_p); + return LV_FS_RES_OK; +} + + +#ifdef WIN32 +static char next_fn[256]; +#endif + +/** + * Initialize a 'fs_read_dir_t' variable for directory reading + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to a 'fs_read_dir_t' variable + * @param path path to a directory + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static void * fs_dir_open (lv_fs_drv_t * drv, const char *path) +{ + (void) drv; /*Unused*/ +#ifndef WIN32 + /*Make the path relative to the current directory (the projects root folder)*/ + char buf[256]; + sprintf(buf, LV_FS_STDIO_PATH "/%s", path); + return opendir(buf); +#else + HANDLE d = INVALID_HANDLE_VALUE; + WIN32_FIND_DATA fdata; + + /*Make the path relative to the current directory (the projects root folder)*/ + char buf[256]; + sprintf(buf, LV_FS_PC_PATH "\\%s\\*", path); + + strcpy(next_fn, ""); + d = FindFirstFile(buf, &fdata); + do { + if (strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0) { + continue; + } else { + + if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + sprintf(next_fn, "/%s", fdata.cFileName); + } else { + sprintf(next_fn, "%s", fdata.cFileName); + } + break; + } + } while(FindNextFileA(d, &fdata)); + + return d; +#endif +} + +/** + * Read the next filename form a directory. + * The name of the directories will begin with '/' + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to an initialized 'fs_read_dir_t' variable + * @param fn pointer to a buffer to store the filename + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_dir_read (lv_fs_drv_t * drv, void * dir_p, char *fn) +{ + (void) drv; /*Unused*/ + +#ifndef WIN32 + struct dirent *entry; + do { + entry = readdir(dir_p); + + if(entry) { + if(entry->d_type == DT_DIR) sprintf(fn, "/%s", entry->d_name); + else strcpy(fn, entry->d_name); + } else { + strcpy(fn, ""); + } + } while(strcmp(fn, "/.") == 0 || strcmp(fn, "/..") == 0); +#else + strcpy(fn, next_fn); + + strcpy(next_fn, ""); + WIN32_FIND_DATA fdata; + + if(FindNextFile(dir_p, &fdata) == false) return LV_FS_RES_OK; + do { + if (strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0) { + continue; + } else { + + if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + sprintf(next_fn, "/%s", fdata.cFileName); + } else { + sprintf(next_fn, "%s", fdata.cFileName); + } + break; + } + } while(FindNextFile(dir_p, &fdata)); + +#endif + return LV_FS_RES_OK; +} + +/** + * Close the directory reading + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to an initialized 'fs_read_dir_t' variable + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_dir_close (lv_fs_drv_t * drv, void * dir_p) +{ + (void) drv; /*Unused*/ +#ifndef WIN32 + closedir(dir_p); +#else + FindClose(dir_p); +#endif + return LV_FS_RES_OK; +} + +#endif /*LV_FS_FATFS*/ diff --git a/src/extra/libs/gif/gifdec.c b/src/extra/libs/gif/gifdec.c new file mode 100644 index 0000000000..4773d67174 --- /dev/null +++ b/src/extra/libs/gif/gifdec.c @@ -0,0 +1,649 @@ +#include "gifdec.h" +#include "../../../misc/lv_log.h" +#include "../../../misc/lv_mem.h" +#if LV_USE_GIF + +#include +#include +#include +#include + +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define MAX(A, B) ((A) > (B) ? (A) : (B)) + +typedef struct Entry { + uint16_t length; + uint16_t prefix; + uint8_t suffix; +} Entry; + +typedef struct Table { + int bulk; + int nentries; + Entry *entries; +} Table; + +static gd_GIF * gif_open(gd_GIF * gif); +static bool f_gif_open(gd_GIF * gif, const void * path, bool is_file); +static void f_gif_read(gd_GIF * gif, void * buf, size_t len); +static int f_gif_seek(gd_GIF * gif, size_t pos, int k); +static void f_gif_close(gd_GIF * gif); + +static uint16_t +read_num(gd_GIF * gif) +{ + uint8_t bytes[2]; + + f_gif_read(gif, bytes, 2); + return bytes[0] + (((uint16_t) bytes[1]) << 8); +} + + + +gd_GIF * +gd_open_gif_file(const char *fname) +{ + gd_GIF gif_base; + memset(&gif_base, 0, sizeof(gif_base)); + + bool res = f_gif_open(&gif_base, fname, true); + if(!res) return NULL; + + return gif_open(&gif_base); +} + + +gd_GIF * +gd_open_gif_data(const void *data) +{ + gd_GIF gif_base; + memset(&gif_base, 0, sizeof(gif_base)); + + bool res = f_gif_open(&gif_base, data, false); + if(!res) return NULL; + + return gif_open(&gif_base); +} + +static gd_GIF * gif_open(gd_GIF * gif_base) +{ + uint8_t sigver[3]; + uint16_t width, height, depth; + uint8_t fdsz, bgidx, aspect; + int i; + uint8_t *bgcolor; + int gct_sz; + gd_GIF *gif; + + /* Header */ + f_gif_read(gif_base, sigver, 3); + if (memcmp(sigver, "GIF", 3) != 0) { + fprintf(stderr, "invalid signature\n"); + goto fail; + } + /* Version */ + f_gif_read(gif_base, sigver, 3); + if (memcmp(sigver, "89a", 3) != 0) { + LV_LOG_WARN("invalid version\n"); + goto fail; + } + /* Width x Height */ + width = read_num(gif_base); + height = read_num(gif_base); + /* FDSZ */ + f_gif_read(gif_base, &fdsz, 1); + /* Presence of GCT */ + if (!(fdsz & 0x80)) { + LV_LOG_WARN("no global color table\n"); + goto fail; + } + /* Color Space's Depth */ + depth = ((fdsz >> 4) & 7) + 1; + /* Ignore Sort Flag. */ + /* GCT Size */ + gct_sz = 1 << ((fdsz & 0x07) + 1); + /* Background Color Index */ + f_gif_read(gif_base, &bgidx, 1); + /* Aspect Ratio */ + f_gif_read(gif_base, &aspect, 1); + /* Create gd_GIF Structure. */ +#if LV_COLOR_DEPTH == 32 + gif = lv_mem_alloc(sizeof(gd_GIF) + 5 * width * height); +#elif LV_COLOR_DEPTH == 16 + gif = lv_mem_alloc(sizeof(gd_GIF) + 4 * width * height); +#elif LV_COLOR_DEPTH == 8 + gif = lv_mem_alloc(sizeof(gd_GIF) + 3 * width * height); +#endif + + if (!gif) goto fail; + memcpy(gif, gif_base, sizeof(gd_GIF)); + gif->width = width; + gif->height = height; + gif->depth = depth; + /* Read GCT */ + gif->gct.size = gct_sz; + f_gif_read(gif, gif->gct.colors, 3 * gif->gct.size); + gif->palette = &gif->gct; + gif->bgindex = bgidx; + gif->canvas = (uint8_t *) &gif[1]; +#if LV_COLOR_DEPTH == 32 + gif->frame = &gif->canvas[4 * width * height]; +#elif LV_COLOR_DEPTH == 16 + gif->frame = &gif->canvas[3 * width * height]; +#elif LV_COLOR_DEPTH == 8 + gif->frame = &gif->canvas[2 * width * height]; +#endif + if (gif->bgindex) + memset(gif->frame, gif->bgindex, gif->width * gif->height); + bgcolor = &gif->palette->colors[gif->bgindex*3]; + + if (bgcolor[0] || bgcolor[1] || bgcolor [2]) + for (i = 0; i < gif->width * gif->height; i++) { +#if LV_COLOR_DEPTH == 32 + gif->canvas[i*4 + 0] = *(bgcolor + 2); + gif->canvas[i*4 + 1] = *(bgcolor + 1); + gif->canvas[i*4 + 2] = *(bgcolor + 0); + gif->canvas[i*4 + 3] = 0xff; +#elif LV_COLOR_DEPTH == 16 + lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2)); + gif->canvas[i*3 + 0] = c.full & 0xff; + gif->canvas[i*3 + 1] = (c.full >> 8) & 0xff; + gif->canvas[i*3 + 2] = 0xff; +#elif LV_COLOR_DEPTH == 8 + lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2)); + gif->canvas[i*2 + 0] = c.full; + gif->canvas[i*2 + 1] = 0xff; +#endif + } + gif->anim_start = f_gif_seek(gif, 0, SEEK_CUR); + goto ok; +fail: + f_gif_close(gif_base); +ok: + return gif; +} + +static void +discard_sub_blocks(gd_GIF *gif) +{ + uint8_t size; + + do { + f_gif_read(gif, &size, 1); + f_gif_seek(gif, size, SEEK_CUR); + } while (size); +} + +static void +read_plain_text_ext(gd_GIF *gif) +{ + if (gif->plain_text) { + uint16_t tx, ty, tw, th; + uint8_t cw, ch, fg, bg; + off_t sub_block; + f_gif_seek(gif, 1, SEEK_CUR); /* block size = 12 */ + tx = read_num(gif); + ty = read_num(gif); + tw = read_num(gif); + th = read_num(gif); + f_gif_read(gif, &cw, 1); + f_gif_read(gif, &ch, 1); + f_gif_read(gif, &fg, 1); + f_gif_read(gif, &bg, 1); + sub_block = f_gif_seek(gif, 0, SEEK_CUR); + gif->plain_text(gif, tx, ty, tw, th, cw, ch, fg, bg); + f_gif_seek(gif, sub_block, SEEK_SET); + } else { + /* Discard plain text metadata. */ + f_gif_seek(gif, 13, SEEK_CUR); + } + /* Discard plain text sub-blocks. */ + discard_sub_blocks(gif); +} + +static void +read_graphic_control_ext(gd_GIF *gif) +{ + uint8_t rdit; + + /* Discard block size (always 0x04). */ + f_gif_seek(gif, 1, SEEK_CUR); + f_gif_read(gif, &rdit, 1); + gif->gce.disposal = (rdit >> 2) & 3; + gif->gce.input = rdit & 2; + gif->gce.transparency = rdit & 1; + gif->gce.delay = read_num(gif); + f_gif_read(gif, &gif->gce.tindex, 1); + /* Skip block terminator. */ + f_gif_seek(gif, 1, SEEK_CUR); +} + +static void +read_comment_ext(gd_GIF *gif) +{ + if (gif->comment) { + off_t sub_block = f_gif_seek(gif, 0, SEEK_CUR); + gif->comment(gif); + f_gif_seek(gif, sub_block, SEEK_SET); + } + /* Discard comment sub-blocks. */ + discard_sub_blocks(gif); +} + +static void +read_application_ext(gd_GIF *gif) +{ + char app_id[8]; + char app_auth_code[3]; + + /* Discard block size (always 0x0B). */ + f_gif_seek(gif, 1, SEEK_CUR); + /* Application Identifier. */ + f_gif_read(gif, app_id, 8); + /* Application Authentication Code. */ + f_gif_read(gif, app_auth_code, 3); + if (!strncmp(app_id, "NETSCAPE", sizeof(app_id))) { + /* Discard block size (0x03) and constant byte (0x01). */ + f_gif_seek(gif, 2, SEEK_CUR); + gif->loop_count = read_num(gif); + /* Skip block terminator. */ + f_gif_seek(gif, 1, SEEK_CUR); + } else if (gif->application) { + off_t sub_block = f_gif_seek(gif, 0, SEEK_CUR); + gif->application(gif, app_id, app_auth_code); + f_gif_seek(gif, sub_block, SEEK_SET); + discard_sub_blocks(gif); + } else { + discard_sub_blocks(gif); + } +} + +static void +read_ext(gd_GIF *gif) +{ + uint8_t label; + + f_gif_read(gif, &label, 1); + switch (label) { + case 0x01: + read_plain_text_ext(gif); + break; + case 0xF9: + read_graphic_control_ext(gif); + break; + case 0xFE: + read_comment_ext(gif); + break; + case 0xFF: + read_application_ext(gif); + break; + default: + LV_LOG_WARN("unknown extension: %02X\n", label); + } +} + +static Table * +new_table(int key_size) +{ + int key; + int init_bulk = MAX(1 << (key_size + 1), 0x100); + Table *table = lv_mem_alloc(sizeof(*table) + sizeof(Entry) * init_bulk); + if (table) { + table->bulk = init_bulk; + table->nentries = (1 << key_size) + 2; + table->entries = (Entry *) &table[1]; + for (key = 0; key < (1 << key_size); key++) + table->entries[key] = (Entry) {1, 0xFFF, key}; + } + return table; +} + +/* Add table entry. Return value: + * 0 on success + * +1 if key size must be incremented after this addition + * -1 if could not realloc table */ +static int +add_entry(Table **tablep, uint16_t length, uint16_t prefix, uint8_t suffix) +{ + Table *table = *tablep; + if (table->nentries == table->bulk) { + table->bulk *= 2; + table = lv_mem_realloc(table, sizeof(*table) + sizeof(Entry) * table->bulk); + if (!table) return -1; + table->entries = (Entry *) &table[1]; + *tablep = table; + } + table->entries[table->nentries] = (Entry) {length, prefix, suffix}; + table->nentries++; + if ((table->nentries & (table->nentries - 1)) == 0) + return 1; + return 0; +} + +static uint16_t +get_key(gd_GIF *gif, int key_size, uint8_t *sub_len, uint8_t *shift, uint8_t *byte) +{ + int bits_read; + int rpad; + int frag_size; + uint16_t key; + + key = 0; + for (bits_read = 0; bits_read < key_size; bits_read += frag_size) { + rpad = (*shift + bits_read) % 8; + if (rpad == 0) { + /* Update byte. */ + if (*sub_len == 0) { + f_gif_read(gif, sub_len, 1); /* Must be nonzero! */ + if (*sub_len == 0) return 0x1000; + } + f_gif_read(gif, byte, 1); + (*sub_len)--; + } + frag_size = MIN(key_size - bits_read, 8 - rpad); + key |= ((uint16_t) ((*byte) >> rpad)) << bits_read; + } + /* Clear extra bits to the left. */ + key &= (1 << key_size) - 1; + *shift = (*shift + key_size) % 8; + return key; +} + +/* Compute output index of y-th input line, in frame of height h. */ +static int +interlaced_line_index(int h, int y) +{ + int p; /* number of lines in current pass */ + + p = (h - 1) / 8 + 1; + if (y < p) /* pass 1 */ + return y * 8; + y -= p; + p = (h - 5) / 8 + 1; + if (y < p) /* pass 2 */ + return y * 8 + 4; + y -= p; + p = (h - 3) / 4 + 1; + if (y < p) /* pass 3 */ + return y * 4 + 2; + y -= p; + /* pass 4 */ + return y * 2 + 1; +} + +/* Decompress image pixels. + * Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */ +static int +read_image_data(gd_GIF *gif, int interlace) +{ + uint8_t sub_len, shift, byte; + int init_key_size, key_size, table_is_full; + int frm_off, frm_size, str_len, i, p, x, y; + uint16_t key, clear, stop; + int ret; + Table *table; + Entry entry; + off_t start, end; + + f_gif_read(gif, &byte, 1); + key_size = (int) byte; + start = f_gif_seek(gif, 0, SEEK_CUR); + discard_sub_blocks(gif); + end = f_gif_seek(gif, 0, SEEK_CUR); + f_gif_seek(gif, start, SEEK_SET); + clear = 1 << key_size; + stop = clear + 1; + table = new_table(key_size); + key_size++; + init_key_size = key_size; + sub_len = shift = 0; + key = get_key(gif, key_size, &sub_len, &shift, &byte); /* clear code */ + frm_off = 0; + ret = 0; + frm_size = gif->fw*gif->fh; + while (frm_off < frm_size) { + if (key == clear) { + key_size = init_key_size; + table->nentries = (1 << (key_size - 1)) + 2; + table_is_full = 0; + } else if (!table_is_full) { + ret = add_entry(&table, str_len + 1, key, entry.suffix); + if (ret == -1) { + lv_mem_free(table); + return -1; + } + if (table->nentries == 0x1000) { + ret = 0; + table_is_full = 1; + } + } + key = get_key(gif, key_size, &sub_len, &shift, &byte); + if (key == clear) continue; + if (key == stop || key == 0x1000) break; + if (ret == 1) key_size++; + entry = table->entries[key]; + str_len = entry.length; + for (i = 0; i < str_len; i++) { + p = frm_off + entry.length - 1; + x = p % gif->fw; + y = p / gif->fw; + if (interlace) + y = interlaced_line_index((int) gif->fh, y); + gif->frame[(gif->fy + y) * gif->width + gif->fx + x] = entry.suffix; + if (entry.prefix == 0xFFF) + break; + else + entry = table->entries[entry.prefix]; + } + frm_off += str_len; + if (key < table->nentries - 1 && !table_is_full) + table->entries[table->nentries - 1].suffix = entry.suffix; + } + lv_mem_free(table); + if (key == stop) f_gif_read(gif, &sub_len, 1); /* Must be zero! */ + f_gif_seek(gif, end, SEEK_SET); + return 0; +} + +/* Read image. + * Return 0 on success or -1 on out-of-memory (w.r.t. LZW code table). */ +static int +read_image(gd_GIF *gif) +{ + uint8_t fisrz; + int interlace; + + /* Image Descriptor. */ + gif->fx = read_num(gif); + gif->fy = read_num(gif); + gif->fw = read_num(gif); + gif->fh = read_num(gif); + f_gif_read(gif, &fisrz, 1); + interlace = fisrz & 0x40; + /* Ignore Sort Flag. */ + /* Local Color Table? */ + if (fisrz & 0x80) { + /* Read LCT */ + gif->lct.size = 1 << ((fisrz & 0x07) + 1); + f_gif_read(gif, gif->lct.colors, 3 * gif->lct.size); + gif->palette = &gif->lct; + } else + gif->palette = &gif->gct; + /* Image Data. */ + return read_image_data(gif, interlace); +} + +static void +render_frame_rect(gd_GIF *gif, uint8_t *buffer) +{ + int i, j, k; + uint8_t index, *color; + i = gif->fy * gif->width + gif->fx; + for (j = 0; j < gif->fh; j++) { + for (k = 0; k < gif->fw; k++) { + index = gif->frame[(gif->fy + j) * gif->width + gif->fx + k]; + color = &gif->palette->colors[index*3]; + if (!gif->gce.transparency || index != gif->gce.tindex) { +#if LV_COLOR_DEPTH == 32 + buffer[(i+k)*4 + 0] = *(color + 2); + buffer[(i+k)*4 + 1] = *(color + 1); + buffer[(i+k)*4 + 2] = *(color + 0); + buffer[(i+k)*4 + 3] = 0xFF; +#elif LV_COLOR_DEPTH == 16 + lv_color_t c = lv_color_make(*(color + 0), *(color + 1), *(color + 2)); + buffer[(i+k)*3 + 0] = c.full & 0xff; + buffer[(i+k)*3 + 1] = (c.full >> 8) & 0xff; + buffer[(i+k)*3 + 2] = 0xff; +#elif LV_COLOR_DEPTH == 8 + lv_color_t c = lv_color_make(*(color + 0), *(color + 1), *(color + 2)); + buffer[(i+k)*2 + 0] = c.full; + buffer[(i+k)*2 + 1] = 0xff; +#endif + } + } + i += gif->width; + } +} + +static void +dispose(gd_GIF *gif) +{ + int i, j, k; + uint8_t *bgcolor; + switch (gif->gce.disposal) { + case 2: /* Restore to background color. */ + bgcolor = &gif->palette->colors[gif->bgindex*3]; + + uint8_t opa = 0xff; + if(gif->gce.transparency) opa = 0x00; + + i = gif->fy * gif->width + gif->fx; + for (j = 0; j < gif->fh; j++) { + for (k = 0; k < gif->fw; k++) { +#if LV_COLOR_DEPTH == 32 + gif->canvas[(i+k)*4 + 0] = *(bgcolor + 2); + gif->canvas[(i+k)*4 + 1] = *(bgcolor + 1); + gif->canvas[(i+k)*4 + 2] = *(bgcolor + 0); + gif->canvas[(i+k)*4 + 3] = opa; +#elif LV_COLOR_DEPTH == 16 + lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2)); + gif->canvas[(i+k)*3 + 0] = c.full & 0xff; + gif->canvas[(i+k)*3 + 1] = (c.full >> 8) & 0xff; + gif->canvas[(i+k)*3 + 2] = opa; +#elif LV_COLOR_DEPTH == 8 + lv_color_t c = lv_color_make(*(bgcolor + 0), *(bgcolor + 1), *(bgcolor + 2)); + gif->canvas[(i+k)*2 + 0] = c.full; + gif->canvas[(i+k)*2 + 1] = opa; +#endif + } + i += gif->width; + } + break; + case 3: /* Restore to previous, i.e., don't update canvas.*/ + break; + default: + /* Add frame non-transparent pixels to canvas. */ + render_frame_rect(gif, gif->canvas); + } +} + +/* Return 1 if got a frame; 0 if got GIF trailer; -1 if error. */ +int +gd_get_frame(gd_GIF *gif) +{ + char sep; + + dispose(gif); + f_gif_read(gif, &sep, 1); + while (sep != ',') { + if (sep == ';') + return 0; + if (sep == '!') + read_ext(gif); + else return -1; + f_gif_read(gif, &sep, 1); + } + if (read_image(gif) == -1) + return -1; + return 1; +} + +void +gd_render_frame(gd_GIF *gif, uint8_t *buffer) +{ +// uint32_t i; +// uint32_t j; +// for(i = 0, j = 0; i < gif->width * gif->height * 3; i+= 3, j+=4) { +// buffer[j + 0] = gif->canvas[i + 2]; +// buffer[j + 1] = gif->canvas[i + 1]; +// buffer[j + 2] = gif->canvas[i + 0]; +// buffer[j + 3] = 0xFF; +// } +// memcpy(buffer, gif->canvas, gif->width * gif->height * 3); + render_frame_rect(gif, buffer); +} + +void +gd_rewind(gd_GIF *gif) +{ + f_gif_seek(gif, gif->anim_start, SEEK_SET); +} + +void +gd_close_gif(gd_GIF *gif) +{ + f_gif_close(gif); + lv_mem_free(gif); +} + +static bool f_gif_open(gd_GIF * gif, const void * path, bool is_file) +{ + gif->f_rw_p = 0; + gif->data = NULL; + gif->is_file = is_file; + + if(is_file) { + lv_fs_res_t res = lv_fs_open(&gif->fd, path, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) return false; + else return true; + } else { + gif->data = path; + return true; + } +} + +static void f_gif_read(gd_GIF * gif, void * buf, size_t len) +{ + if(gif->is_file) { + lv_fs_read(&gif->fd, buf, len, NULL); + } else + { + memcpy(buf, &gif->data[gif->f_rw_p], len); + gif->f_rw_p += len; + } +} + +static int f_gif_seek(gd_GIF * gif, size_t pos, int k) +{ + if(k == SEEK_CUR) k = LV_FS_SEEK_CUR; + else if(k == SEEK_SET) k = LV_FS_SEEK_SET; + if(gif->is_file) { + lv_fs_seek(&gif->fd, pos, k); + uint32_t x; + lv_fs_tell(&gif->fd, &x); + return x; + } else { + if(k == LV_FS_SEEK_CUR) gif->f_rw_p += pos; + else if(k == LV_FS_SEEK_SET) gif->f_rw_p = pos; + return gif->f_rw_p; + } +} + +static void f_gif_close(gd_GIF * gif) +{ + if(gif->is_file) { + lv_fs_close(&gif->fd); + } +} + +#endif /*LV_USE_GIF*/ diff --git a/src/extra/libs/gif/gifdec.h b/src/extra/libs/gif/gifdec.h new file mode 100644 index 0000000000..00f17c1da8 --- /dev/null +++ b/src/extra/libs/gif/gifdec.h @@ -0,0 +1,60 @@ +#ifndef GIFDEC_H +#define GIFDEC_H + +#include +#include "../../../misc/lv_fs.h" + +#if LV_USE_GIF + +typedef struct gd_Palette { + int size; + uint8_t colors[0x100 * 3]; +} gd_Palette; + +typedef struct gd_GCE { + uint16_t delay; + uint8_t tindex; + uint8_t disposal; + int input; + int transparency; +} gd_GCE; + + + +typedef struct gd_GIF { + lv_fs_file_t fd; + const char * data; + uint8_t is_file; + uint32_t f_rw_p; + int32_t anim_start; + uint16_t width, height; + uint16_t depth; + uint16_t loop_count; + gd_GCE gce; + gd_Palette *palette; + gd_Palette lct, gct; + void (*plain_text)( + struct gd_GIF *gif, uint16_t tx, uint16_t ty, + uint16_t tw, uint16_t th, uint8_t cw, uint8_t ch, + uint8_t fg, uint8_t bg + ); + void (*comment)(struct gd_GIF *gif); + void (*application)(struct gd_GIF *gif, char id[8], char auth[3]); + uint16_t fx, fy, fw, fh; + uint8_t bgindex; + uint8_t *canvas, *frame; +} gd_GIF; + +gd_GIF * gd_open_gif_file(const char *fname); + +gd_GIF * gd_open_gif_data(const void *data); + +void gd_render_frame(gd_GIF *gif, uint8_t *buffer); + +int gd_get_frame(gd_GIF *gif); +void gd_rewind(gd_GIF *gif); +void gd_close_gif(gd_GIF *gif); + +#endif /*LV_USE_GIF*/ + +#endif /* GIFDEC_H */ diff --git a/src/extra/libs/gif/lv_gif.c b/src/extra/libs/gif/lv_gif.c new file mode 100644 index 0000000000..b4ac95de9d --- /dev/null +++ b/src/extra/libs/gif/lv_gif.c @@ -0,0 +1,154 @@ +/** + * @file lv_gifenc.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_gif.h" +#if LV_USE_GIF + +#include "gifdec.h" + +/********************* + * DEFINES + *********************/ +#define MY_CLASS &lv_gif_class + +/********************** + * TYPEDEFS + **********************/ + + +/********************** + * STATIC PROTOTYPES + **********************/ +static void lv_gif_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj); +static void lv_gif_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj); +static void next_frame_task_cb(lv_timer_t * t); + +/********************** + * STATIC VARIABLES + **********************/ +const lv_obj_class_t lv_gif_class = { + .constructor_cb = lv_gif_constructor, + .destructor_cb = lv_gif_destructor, + .instance_size = sizeof(lv_gif_t), + .base_class = &lv_img_class +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +lv_obj_t * lv_gif_create(lv_obj_t * parent) +{ + + LV_LOG_INFO("begin"); + lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent); + lv_obj_class_init_obj(obj); + return obj; +} + +void lv_gif_set_src(lv_obj_t * obj, const void * src) +{ + lv_gif_t * gifobj = (lv_gif_t *) obj; + + /*Close previous gif if any*/ + if(gifobj->gif) { + lv_img_cache_invalidate_src(&gifobj->imgdsc); + gd_close_gif(gifobj->gif); + gifobj->gif = NULL; + gifobj->imgdsc.data = NULL; + } + + if(lv_img_src_get_type(src) == LV_IMG_SRC_VARIABLE) { + const lv_img_dsc_t * img_dsc = src; + gifobj->gif = gd_open_gif_data(img_dsc->data); + } + else if(lv_img_src_get_type(src) == LV_IMG_SRC_FILE) { + gifobj->gif = gd_open_gif_file(src); + } + if(gifobj->gif == NULL) { + LV_LOG_WARN("Could't load the source"); + return; + } + + gifobj->imgdsc.data = gifobj->gif->canvas; + gifobj->imgdsc.header.always_zero = 0; + gifobj->imgdsc.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA; + gifobj->imgdsc.header.h = gifobj->gif->height; + gifobj->imgdsc.header.w = gifobj->gif->width; + gifobj->last_call = lv_tick_get(); + + lv_img_set_src(obj, &gifobj->imgdsc); + + lv_timer_resume(gifobj->timer); + lv_timer_reset(gifobj->timer); + + next_frame_task_cb(gifobj->timer); + +} + +void lv_gif_restart(lv_obj_t * obj) +{ + lv_gif_t * gifobj = (lv_gif_t *) obj; + gd_rewind(gifobj->gif); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +static void lv_gif_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) +{ + LV_UNUSED(class_p); + + lv_gif_t * gifobj = (lv_gif_t *) obj; + + gifobj->timer = lv_timer_create(next_frame_task_cb, 10, obj); + lv_timer_pause(gifobj->timer); +} + +static void lv_gif_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj) +{ + LV_UNUSED(class_p); + lv_gif_t * gifobj = (lv_gif_t *) obj; + lv_img_cache_invalidate_src(&gifobj->imgdsc); + gd_close_gif(gifobj->gif); + lv_timer_del(gifobj->timer); +} + +static void next_frame_task_cb(lv_timer_t * t) +{ + lv_obj_t * obj = t->user_data; + lv_gif_t * gifobj = (lv_gif_t *) obj; + uint32_t elaps = lv_tick_elaps(gifobj->last_call); + if(elaps < gifobj->gif->gce.delay * 10) return; + + gifobj->last_call = lv_tick_get(); + + int has_next = gd_get_frame(gifobj->gif); + if(has_next == 0) { + /*It was the last repeat*/ + if(gifobj->gif->loop_count == 1) { + lv_res_t res = lv_event_send(obj, LV_EVENT_READY, NULL); + if(res != LV_FS_RES_OK) return; + } else { + if(gifobj->gif->loop_count > 1) gifobj->gif->loop_count--; + gd_rewind(gifobj->gif); + } + } + + gd_render_frame(gifobj->gif, gifobj->imgdsc.data); + + lv_img_cache_invalidate_src(lv_img_get_src(obj)); + lv_obj_invalidate(obj); +} + +#endif /*LV_USE_GIF*/ diff --git a/src/extra/libs/gif/lv_gif.h b/src/extra/libs/gif/lv_gif.h new file mode 100644 index 0000000000..a871c614e6 --- /dev/null +++ b/src/extra/libs/gif/lv_gif.h @@ -0,0 +1,58 @@ +/** + * @file lv_gif.h + * + */ + +#ifndef LV_GIF_H +#define LV_GIF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include "../../../lvgl.h" +#if LV_USE_GIF + +#include "gifdec.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + lv_img_t img; + gd_GIF *gif; + lv_timer_t * timer; + lv_img_dsc_t imgdsc; + uint32_t last_call; +}lv_gif_t; + +extern const lv_obj_class_t lv_gif_class; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +lv_obj_t * lv_gif_create(lv_obj_t * parent); +void lv_gif_set_src(lv_obj_t * obj, const void * src); +void lv_gif_restart(lv_obj_t * gif); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_GIF*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_GIF_H*/ diff --git a/src/extra/libs/lv_libs.h b/src/extra/libs/lv_libs.h new file mode 100644 index 0000000000..a3f3909a49 --- /dev/null +++ b/src/extra/libs/lv_libs.h @@ -0,0 +1,49 @@ +/** + * @file lv_libs.h + * + */ + +#ifndef LV_LIBS_H +#define LV_LIBS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "bmp/lv_bmp.h" +#include "fs/lv_fs_libs.h" +#include "png/lv_png.h" +#include "gif/lv_gif.h" +#include "qrcode/lv_qrcode.h" +#include "sjpg/lv_sjpg.h" +#include "freetype/lv_freetype.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/********************** + * MACROS + **********************/ +#if LV_USE_LOG && LV_LOG_TRACE_LAYOUT +# define LV_TRACE_LAYOUT(...) LV_LOG_TRACE( __VA_ARGS__) +#else +# define LV_TRACE_LAYOUT(...) +#endif + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_LAYOUTS_H*/ diff --git a/src/extra/libs/png/lodepng.c b/src/extra/libs/png/lodepng.c new file mode 100644 index 0000000000..19c2312399 --- /dev/null +++ b/src/extra/libs/png/lodepng.c @@ -0,0 +1,6466 @@ +/* +LodePNG version 20201017 + +Copyright (c) 2005-2020 Lode Vandevenne + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +/* +The manual and changelog are in the header file "lodepng.h" +Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for C. +*/ + +#include "lodepng.h" +#if LV_USE_PNG + +#ifdef LODEPNG_COMPILE_DISK +#include /* LONG_MAX */ +#include /* file handling */ +#endif /* LODEPNG_COMPILE_DISK */ + +#ifdef LODEPNG_COMPILE_ALLOCATORS +#include /* allocations */ +#endif /* LODEPNG_COMPILE_ALLOCATORS */ + +#if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/ +#pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/ +#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ +#endif /*_MSC_VER */ + +const char* LODEPNG_VERSION_STRING = "20201017"; + +/* +This source file is built up in the following large parts. The code sections +with the "LODEPNG_COMPILE_" #defines divide this up further in an intermixed way. +-Tools for C and common code for PNG and Zlib +-C Code for Zlib (huffman, deflate, ...) +-C Code for PNG (file format chunks, adam7, PNG filters, color conversions, ...) +-The C++ wrapper around all of the above +*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // Tools for C, and common code for PNG and Zlib. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*The malloc, realloc and free functions defined here with "lodepng_" in front +of the name, so that you can easily change them to others related to your +platform if needed. Everything else in the code calls these. Pass +-DLODEPNG_NO_COMPILE_ALLOCATORS to the compiler, or comment out +#define LODEPNG_COMPILE_ALLOCATORS in the header, to disable the ones here and +define them in your own project's source files without needing to change +lodepng source code. Don't forget to remove "static" if you copypaste them +from here.*/ + +#ifdef LODEPNG_COMPILE_ALLOCATORS +static void* lodepng_malloc(size_t size) { +#ifdef LODEPNG_MAX_ALLOC + if(size > LODEPNG_MAX_ALLOC) return 0; +#endif + return malloc(size); +} + +/* NOTE: when realloc returns NULL, it leaves the original memory untouched */ +static void* lodepng_realloc(void* ptr, size_t new_size) { +#ifdef LODEPNG_MAX_ALLOC + if(new_size > LODEPNG_MAX_ALLOC) return 0; +#endif + return realloc(ptr, new_size); +} + +static void lodepng_free(void* ptr) { + free(ptr); +} +#else /*LODEPNG_COMPILE_ALLOCATORS*/ +/* TODO: support giving additional void* payload to the custom allocators */ +void* lodepng_malloc(size_t size); +void* lodepng_realloc(void* ptr, size_t new_size); +void lodepng_free(void* ptr); +#endif /*LODEPNG_COMPILE_ALLOCATORS*/ + +/* convince the compiler to inline a function, for use when this measurably improves performance */ +/* inline is not available in C90, but use it when supported by the compiler */ +#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || (defined(__cplusplus) && (__cplusplus >= 199711L)) +#define LODEPNG_INLINE inline +#else +#define LODEPNG_INLINE /* not available */ +#endif + +/* restrict is not available in C90, but use it when supported by the compiler */ +#if (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) ||\ + (defined(_MSC_VER) && (_MSC_VER >= 1400)) || \ + (defined(__WATCOMC__) && (__WATCOMC__ >= 1250) && !defined(__cplusplus)) +#define LODEPNG_RESTRICT __restrict +#else +#define LODEPNG_RESTRICT /* not available */ +#endif + +/* Replacements for C library functions such as memcpy and strlen, to support platforms +where a full C library is not available. The compiler can recognize them and compile +to something as fast. */ + +static void lodepng_memcpy(void* LODEPNG_RESTRICT dst, + const void* LODEPNG_RESTRICT src, size_t size) { + size_t i; + for(i = 0; i < size; i++) ((char*)dst)[i] = ((const char*)src)[i]; +} + +static void lodepng_memset(void* LODEPNG_RESTRICT dst, + int value, size_t num) { + size_t i; + for(i = 0; i < num; i++) ((char*)dst)[i] = (char)value; +} + +/* does not check memory out of bounds, do not use on untrusted data */ +static size_t lodepng_strlen(const char* a) { + const char* orig = a; + /* avoid warning about unused function in case of disabled COMPILE... macros */ + (void)(&lodepng_strlen); + while(*a) a++; + return (size_t)(a - orig); +} + +#define LODEPNG_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define LODEPNG_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define LODEPNG_ABS(x) ((x) < 0 ? -(x) : (x)) + +#if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_DECODER) +/* Safely check if adding two integers will overflow (no undefined +behavior, compiler removing the code, etc...) and output result. */ +static int lodepng_addofl(size_t a, size_t b, size_t* result) { + *result = a + b; /* Unsigned addition is well defined and safe in C90 */ + return *result < a; +} +#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_DECODER)*/ + +#ifdef LODEPNG_COMPILE_DECODER +/* Safely check if multiplying two integers will overflow (no undefined +behavior, compiler removing the code, etc...) and output result. */ +static int lodepng_mulofl(size_t a, size_t b, size_t* result) { + *result = a * b; /* Unsigned multiplication is well defined and safe in C90 */ + return (a != 0 && *result / a != b); +} + +#ifdef LODEPNG_COMPILE_ZLIB +/* Safely check if a + b > c, even if overflow could happen. */ +static int lodepng_gtofl(size_t a, size_t b, size_t c) { + size_t d; + if(lodepng_addofl(a, b, &d)) return 1; + return d > c; +} +#endif /*LODEPNG_COMPILE_ZLIB*/ +#endif /*LODEPNG_COMPILE_DECODER*/ + + +/* +Often in case of an error a value is assigned to a variable and then it breaks +out of a loop (to go to the cleanup phase of a function). This macro does that. +It makes the error handling code shorter and more readable. + +Example: if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83); +*/ +#define CERROR_BREAK(errorvar, code){\ + errorvar = code;\ + break;\ +} + +/*version of CERROR_BREAK that assumes the common case where the error variable is named "error"*/ +#define ERROR_BREAK(code) CERROR_BREAK(error, code) + +/*Set error var to the error code, and return it.*/ +#define CERROR_RETURN_ERROR(errorvar, code){\ + errorvar = code;\ + return code;\ +} + +/*Try the code, if it returns error, also return the error.*/ +#define CERROR_TRY_RETURN(call){\ + unsigned error = call;\ + if(error) return error;\ +} + +/*Set error var to the error code, and return from the void function.*/ +#define CERROR_RETURN(errorvar, code){\ + errorvar = code;\ + return;\ +} + +/* +About uivector, ucvector and string: +-All of them wrap dynamic arrays or text strings in a similar way. +-LodePNG was originally written in C++. The vectors replace the std::vectors that were used in the C++ version. +-The string tools are made to avoid problems with compilers that declare things like strncat as deprecated. +-They're not used in the interface, only internally in this file as static functions. +-As with many other structs in this file, the init and cleanup functions serve as ctor and dtor. +*/ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_ENCODER +/*dynamic vector of unsigned ints*/ +typedef struct uivector { + unsigned* data; + size_t size; /*size in number of unsigned longs*/ + size_t allocsize; /*allocated size in bytes*/ +} uivector; + +static void uivector_cleanup(void* p) { + ((uivector*)p)->size = ((uivector*)p)->allocsize = 0; + lodepng_free(((uivector*)p)->data); + ((uivector*)p)->data = NULL; +} + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned uivector_resize(uivector* p, size_t size) { + size_t allocsize = size * sizeof(unsigned); + if(allocsize > p->allocsize) { + size_t newsize = allocsize + (p->allocsize >> 1u); + void* data = lodepng_realloc(p->data, newsize); + if(data) { + p->allocsize = newsize; + p->data = (unsigned*)data; + } + else return 0; /*error: not enough memory*/ + } + p->size = size; + return 1; /*success*/ +} + +static void uivector_init(uivector* p) { + p->data = NULL; + p->size = p->allocsize = 0; +} + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned uivector_push_back(uivector* p, unsigned c) { + if(!uivector_resize(p, p->size + 1)) return 0; + p->data[p->size - 1] = c; + return 1; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_ZLIB*/ + +/* /////////////////////////////////////////////////////////////////////////// */ + +/*dynamic vector of unsigned chars*/ +typedef struct ucvector { + unsigned char* data; + size_t size; /*used size*/ + size_t allocsize; /*allocated size*/ +} ucvector; + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned ucvector_resize(ucvector* p, size_t size) { + if(size > p->allocsize) { + size_t newsize = size + (p->allocsize >> 1u); + void* data = lodepng_realloc(p->data, newsize); + if(data) { + p->allocsize = newsize; + p->data = (unsigned char*)data; + } + else return 0; /*error: not enough memory*/ + } + p->size = size; + return 1; /*success*/ +} + +static ucvector ucvector_init(unsigned char* buffer, size_t size) { + ucvector v; + v.data = buffer; + v.allocsize = v.size = size; + return v; +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_PNG +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + +/*free string pointer and set it to NULL*/ +static void string_cleanup(char** out) { + lodepng_free(*out); + *out = NULL; +} + +/*also appends null termination character*/ +static char* alloc_string_sized(const char* in, size_t insize) { + char* out = (char*)lodepng_malloc(insize + 1); + if(out) { + lodepng_memcpy(out, in, insize); + out[insize] = 0; + } + return out; +} + +/* dynamically allocates a new string with a copy of the null terminated input text */ +static char* alloc_string(const char* in) { + return alloc_string_sized(in, lodepng_strlen(in)); +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +/* ////////////////////////////////////////////////////////////////////////// */ + +#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_PNG) +static unsigned lodepng_read32bitInt(const unsigned char* buffer) { + return (((unsigned)buffer[0] << 24u) | ((unsigned)buffer[1] << 16u) | + ((unsigned)buffer[2] << 8u) | (unsigned)buffer[3]); +} +#endif /*defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_PNG)*/ + +#if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER) +/*buffer must have at least 4 allocated bytes available*/ +static void lodepng_set32bitInt(unsigned char* buffer, unsigned value) { + buffer[0] = (unsigned char)((value >> 24) & 0xff); + buffer[1] = (unsigned char)((value >> 16) & 0xff); + buffer[2] = (unsigned char)((value >> 8) & 0xff); + buffer[3] = (unsigned char)((value ) & 0xff); +} +#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / File IO / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_DISK + +/* returns negative value on error. This should be pure C compatible, so no fstat. */ +static long lodepng_filesize(const char* filename) { + lv_fs_file_t f; + lv_fs_res_t res = lv_fs_open(&f, filename, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) return -1; + uint32_t size = 0; + if(lv_fs_seek(&f, 0, SEEK_END) != 0) { + lv_fs_close(&f); + return -1; + } + + lv_fs_tell(&f, &size); + lv_fs_close(&f); + return size; +} + +/* load file into buffer that already has the correct allocated size. Returns error code.*/ +static unsigned lodepng_buffer_file(unsigned char* out, size_t size, const char* filename) { + lv_fs_file_t f; + lv_fs_res_t res = lv_fs_open(&f, filename, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) return 78; + + uint32_t br; + res = lv_fs_read(&f, out, size, &br); + if(res != LV_FS_RES_OK) return 78; + if (br != size) return 78; + lv_fs_close(&f); + return 0; +} + +unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) { + long size = lodepng_filesize(filename); + if(size < 0) return 78; + *outsize = (size_t)size; + + *out = (unsigned char*)lodepng_malloc((size_t)size); + if(!(*out) && size > 0) return 83; /*the above malloc failed*/ + + return lodepng_buffer_file(*out, (size_t)size, filename); +} + +/*write given buffer to the file, overwriting the file, it doesn't append to it.*/ +unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename) { + FILE* file; + file = fopen(filename, "wb" ); + if(!file) return 79; + fwrite(buffer, 1, buffersize, file); + fclose(file); + return 0; +} + +#endif /*LODEPNG_COMPILE_DISK*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // End of common code and tools. Begin of Zlib related code. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_ENCODER + +typedef struct { + ucvector* data; + unsigned char bp; /*ok to overflow, indicates bit pos inside byte*/ +} LodePNGBitWriter; + +static void LodePNGBitWriter_init(LodePNGBitWriter* writer, ucvector* data) { + writer->data = data; + writer->bp = 0; +} + +/*TODO: this ignores potential out of memory errors*/ +#define WRITEBIT(writer, bit){\ + /* append new byte */\ + if(((writer->bp) & 7u) == 0) {\ + if(!ucvector_resize(writer->data, writer->data->size + 1)) return;\ + writer->data->data[writer->data->size - 1] = 0;\ + }\ + (writer->data->data[writer->data->size - 1]) |= (bit << ((writer->bp) & 7u));\ + ++writer->bp;\ +} + +/* LSB of value is written first, and LSB of bytes is used first */ +static void writeBits(LodePNGBitWriter* writer, unsigned value, size_t nbits) { + if(nbits == 1) { /* compiler should statically compile this case if nbits == 1 */ + WRITEBIT(writer, value); + } else { + /* TODO: increase output size only once here rather than in each WRITEBIT */ + size_t i; + for(i = 0; i != nbits; ++i) { + WRITEBIT(writer, (unsigned char)((value >> i) & 1)); + } + } +} + +/* This one is to use for adding huffman symbol, the value bits are written MSB first */ +static void writeBitsReversed(LodePNGBitWriter* writer, unsigned value, size_t nbits) { + size_t i; + for(i = 0; i != nbits; ++i) { + /* TODO: increase output size only once here rather than in each WRITEBIT */ + WRITEBIT(writer, (unsigned char)((value >> (nbits - 1u - i)) & 1u)); + } +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +typedef struct { + const unsigned char* data; + size_t size; /*size of data in bytes*/ + size_t bitsize; /*size of data in bits, end of valid bp values, should be 8*size*/ + size_t bp; + unsigned buffer; /*buffer for reading bits. NOTE: 'unsigned' must support at least 32 bits*/ +} LodePNGBitReader; + +/* data size argument is in bytes. Returns error if size too large causing overflow */ +static unsigned LodePNGBitReader_init(LodePNGBitReader* reader, const unsigned char* data, size_t size) { + size_t temp; + reader->data = data; + reader->size = size; + /* size in bits, return error if overflow (if size_t is 32 bit this supports up to 500MB) */ + if(lodepng_mulofl(size, 8u, &reader->bitsize)) return 105; + /*ensure incremented bp can be compared to bitsize without overflow even when it would be incremented 32 too much and + trying to ensure 32 more bits*/ + if(lodepng_addofl(reader->bitsize, 64u, &temp)) return 105; + reader->bp = 0; + reader->buffer = 0; + return 0; /*ok*/ +} + +/* +ensureBits functions: +Ensures the reader can at least read nbits bits in one or more readBits calls, +safely even if not enough bits are available. +Returns 1 if there are enough bits available, 0 if not. +*/ + +/*See ensureBits documentation above. This one ensures exactly 1 bit */ +/*static unsigned ensureBits1(LodePNGBitReader* reader) { + if(reader->bp >= reader->bitsize) return 0; + reader->buffer = (unsigned)reader->data[reader->bp >> 3u] >> (reader->bp & 7u); + return 1; +}*/ + +/*See ensureBits documentation above. This one ensures up to 9 bits */ +static unsigned ensureBits9(LodePNGBitReader* reader, size_t nbits) { + size_t start = reader->bp >> 3u; + size_t size = reader->size; + if(start + 1u < size) { + reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u); + reader->buffer >>= (reader->bp & 7u); + return 1; + } else { + reader->buffer = 0; + if(start + 0u < size) reader->buffer |= reader->data[start + 0]; + reader->buffer >>= (reader->bp & 7u); + return reader->bp + nbits <= reader->bitsize; + } +} + +/*See ensureBits documentation above. This one ensures up to 17 bits */ +static unsigned ensureBits17(LodePNGBitReader* reader, size_t nbits) { + size_t start = reader->bp >> 3u; + size_t size = reader->size; + if(start + 2u < size) { + reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | + ((unsigned)reader->data[start + 2] << 16u); + reader->buffer >>= (reader->bp & 7u); + return 1; + } else { + reader->buffer = 0; + if(start + 0u < size) reader->buffer |= reader->data[start + 0]; + if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u); + reader->buffer >>= (reader->bp & 7u); + return reader->bp + nbits <= reader->bitsize; + } +} + +/*See ensureBits documentation above. This one ensures up to 25 bits */ +static LODEPNG_INLINE unsigned ensureBits25(LodePNGBitReader* reader, size_t nbits) { + size_t start = reader->bp >> 3u; + size_t size = reader->size; + if(start + 3u < size) { + reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | + ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u); + reader->buffer >>= (reader->bp & 7u); + return 1; + } else { + reader->buffer = 0; + if(start + 0u < size) reader->buffer |= reader->data[start + 0]; + if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u); + if(start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u); + reader->buffer >>= (reader->bp & 7u); + return reader->bp + nbits <= reader->bitsize; + } +} + +/*See ensureBits documentation above. This one ensures up to 32 bits */ +static LODEPNG_INLINE unsigned ensureBits32(LodePNGBitReader* reader, size_t nbits) { + size_t start = reader->bp >> 3u; + size_t size = reader->size; + if(start + 4u < size) { + reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | + ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u); + reader->buffer >>= (reader->bp & 7u); + reader->buffer |= (((unsigned)reader->data[start + 4] << 24u) << (8u - (reader->bp & 7u))); + return 1; + } else { + reader->buffer = 0; + if(start + 0u < size) reader->buffer |= reader->data[start + 0]; + if(start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u); + if(start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u); + if(start + 3u < size) reader->buffer |= ((unsigned)reader->data[start + 3] << 24u); + reader->buffer >>= (reader->bp & 7u); + return reader->bp + nbits <= reader->bitsize; + } +} + +/* Get bits without advancing the bit pointer. Must have enough bits available with ensureBits. Max nbits is 31. */ +static unsigned peekBits(LodePNGBitReader* reader, size_t nbits) { + /* The shift allows nbits to be only up to 31. */ + return reader->buffer & ((1u << nbits) - 1u); +} + +/* Must have enough bits available with ensureBits */ +static void advanceBits(LodePNGBitReader* reader, size_t nbits) { + reader->buffer >>= nbits; + reader->bp += nbits; +} + +/* Must have enough bits available with ensureBits */ +static unsigned readBits(LodePNGBitReader* reader, size_t nbits) { + unsigned result = peekBits(reader, nbits); + advanceBits(reader, nbits); + return result; +} + +#if 0 /*Disable because tests fail due to unused declaration*/ +/* Public for testing only. steps and result must have numsteps values. */ +static unsigned lode_png_test_bitreader(const unsigned char* data, size_t size, + size_t numsteps, const size_t* steps, unsigned* result) { + size_t i; + LodePNGBitReader reader; + unsigned error = LodePNGBitReader_init(&reader, data, size); + if(error) return 0; + for(i = 0; i < numsteps; i++) { + size_t step = steps[i]; + unsigned ok; + if(step > 25) ok = ensureBits32(&reader, step); + else if(step > 17) ok = ensureBits25(&reader, step); + else if(step > 9) ok = ensureBits17(&reader, step); + else ok = ensureBits9(&reader, step); + if(!ok) return 0; + result[i] = readBits(&reader, step); + } + return 1; +} +#endif + +#endif /*LODEPNG_COMPILE_DECODER*/ + +static unsigned reverseBits(unsigned bits, unsigned num) { + /*TODO: implement faster lookup table based version when needed*/ + unsigned i, result = 0; + for(i = 0; i < num; i++) result |= ((bits >> (num - i - 1u)) & 1u) << i; + return result; +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Deflate - Huffman / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#define FIRST_LENGTH_CODE_INDEX 257 +#define LAST_LENGTH_CODE_INDEX 285 +/*256 literals, the end code, some length codes, and 2 unused codes*/ +#define NUM_DEFLATE_CODE_SYMBOLS 288 +/*the distance codes have their own symbols, 30 used, 2 unused*/ +#define NUM_DISTANCE_SYMBOLS 32 +/*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/ +#define NUM_CODE_LENGTH_CODES 19 + +/*the base lengths represented by codes 257-285*/ +static const unsigned LENGTHBASE[29] + = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, + 67, 83, 99, 115, 131, 163, 195, 227, 258}; + +/*the extra bits used by codes 257-285 (added to base length)*/ +static const unsigned LENGTHEXTRA[29] + = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 5, 5, 5, 5, 0}; + +/*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/ +static const unsigned DISTANCEBASE[30] + = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, + 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; + +/*the extra bits of backwards distances (added to base)*/ +static const unsigned DISTANCEEXTRA[30] + = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; + +/*the order in which "code length alphabet code lengths" are stored as specified by deflate, out of this the huffman +tree of the dynamic huffman tree lengths is generated*/ +static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES] + = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* ////////////////////////////////////////////////////////////////////////// */ + +/* +Huffman tree struct, containing multiple representations of the tree +*/ +typedef struct HuffmanTree { + unsigned* codes; /*the huffman codes (bit patterns representing the symbols)*/ + unsigned* lengths; /*the lengths of the huffman codes*/ + unsigned maxbitlen; /*maximum number of bits a single code can get*/ + unsigned numcodes; /*number of symbols in the alphabet = number of codes*/ + /* for reading only */ + unsigned char* table_len; /*length of symbol from lookup table, or max length if secondary lookup needed*/ + unsigned short* table_value; /*value of symbol from lookup table, or pointer to secondary table if needed*/ +} HuffmanTree; + +static void HuffmanTree_init(HuffmanTree* tree) { + tree->codes = 0; + tree->lengths = 0; + tree->table_len = 0; + tree->table_value = 0; +} + +static void HuffmanTree_cleanup(HuffmanTree* tree) { + lodepng_free(tree->codes); + lodepng_free(tree->lengths); + lodepng_free(tree->table_len); + lodepng_free(tree->table_value); +} + +/* amount of bits for first huffman table lookup (aka root bits), see HuffmanTree_makeTable and huffmanDecodeSymbol.*/ +/* values 8u and 9u work the fastest */ +#define FIRSTBITS 9u + +/* a symbol value too big to represent any valid symbol, to indicate reading disallowed huffman bits combination, +which is possible in case of only 0 or 1 present symbols. */ +#define INVALIDSYMBOL 65535u + +/* make table for huffman decoding */ +static unsigned HuffmanTree_makeTable(HuffmanTree* tree) { + static const unsigned headsize = 1u << FIRSTBITS; /*size of the first table*/ + static const unsigned mask = (1u << FIRSTBITS) /*headsize*/ - 1u; + size_t i, numpresent, pointer, size; /*total table size*/ + unsigned* maxlens = (unsigned*)lodepng_malloc(headsize * sizeof(unsigned)); + if(!maxlens) return 83; /*alloc fail*/ + + /* compute maxlens: max total bit length of symbols sharing prefix in the first table*/ + lodepng_memset(maxlens, 0, headsize * sizeof(*maxlens)); + for(i = 0; i < tree->numcodes; i++) { + unsigned symbol = tree->codes[i]; + unsigned l = tree->lengths[i]; + unsigned index; + if(l <= FIRSTBITS) continue; /*symbols that fit in first table don't increase secondary table size*/ + /*get the FIRSTBITS MSBs, the MSBs of the symbol are encoded first. See later comment about the reversing*/ + index = reverseBits(symbol >> (l - FIRSTBITS), FIRSTBITS); + maxlens[index] = LODEPNG_MAX(maxlens[index], l); + } + /* compute total table size: size of first table plus all secondary tables for symbols longer than FIRSTBITS */ + size = headsize; + for(i = 0; i < headsize; ++i) { + unsigned l = maxlens[i]; + if(l > FIRSTBITS) size += (1u << (l - FIRSTBITS)); + } + tree->table_len = (unsigned char*)lodepng_malloc(size * sizeof(*tree->table_len)); + tree->table_value = (unsigned short*)lodepng_malloc(size * sizeof(*tree->table_value)); + if(!tree->table_len || !tree->table_value) { + lodepng_free(maxlens); + /* freeing tree->table values is done at a higher scope */ + return 83; /*alloc fail*/ + } + /*initialize with an invalid length to indicate unused entries*/ + for(i = 0; i < size; ++i) tree->table_len[i] = 16; + + /*fill in the first table for long symbols: max prefix size and pointer to secondary tables*/ + pointer = headsize; + for(i = 0; i < headsize; ++i) { + unsigned l = maxlens[i]; + if(l <= FIRSTBITS) continue; + tree->table_len[i] = l; + tree->table_value[i] = pointer; + pointer += (1u << (l - FIRSTBITS)); + } + lodepng_free(maxlens); + + /*fill in the first table for short symbols, or secondary table for long symbols*/ + numpresent = 0; + for(i = 0; i < tree->numcodes; ++i) { + unsigned l = tree->lengths[i]; + unsigned symbol = tree->codes[i]; /*the huffman bit pattern. i itself is the value.*/ + /*reverse bits, because the huffman bits are given in MSB first order but the bit reader reads LSB first*/ + unsigned reverse = reverseBits(symbol, l); + if(l == 0) continue; + numpresent++; + + if(l <= FIRSTBITS) { + /*short symbol, fully in first table, replicated num times if l < FIRSTBITS*/ + unsigned num = 1u << (FIRSTBITS - l); + unsigned j; + for(j = 0; j < num; ++j) { + /*bit reader will read the l bits of symbol first, the remaining FIRSTBITS - l bits go to the MSB's*/ + unsigned index = reverse | (j << l); + if(tree->table_len[index] != 16) return 55; /*invalid tree: long symbol shares prefix with short symbol*/ + tree->table_len[index] = l; + tree->table_value[index] = i; + } + } else { + /*long symbol, shares prefix with other long symbols in first lookup table, needs second lookup*/ + /*the FIRSTBITS MSBs of the symbol are the first table index*/ + unsigned index = reverse & mask; + unsigned maxlen = tree->table_len[index]; + /*log2 of secondary table length, should be >= l - FIRSTBITS*/ + unsigned tablelen = maxlen - FIRSTBITS; + unsigned start = tree->table_value[index]; /*starting index in secondary table*/ + unsigned num = 1u << (tablelen - (l - FIRSTBITS)); /*amount of entries of this symbol in secondary table*/ + unsigned j; + if(maxlen < l) return 55; /*invalid tree: long symbol shares prefix with short symbol*/ + for(j = 0; j < num; ++j) { + unsigned reverse2 = reverse >> FIRSTBITS; /* l - FIRSTBITS bits */ + unsigned index2 = start + (reverse2 | (j << (l - FIRSTBITS))); + tree->table_len[index2] = l; + tree->table_value[index2] = i; + } + } + } + + if(numpresent < 2) { + /* In case of exactly 1 symbol, in theory the huffman symbol needs 0 bits, + but deflate uses 1 bit instead. In case of 0 symbols, no symbols can + appear at all, but such huffman tree could still exist (e.g. if distance + codes are never used). In both cases, not all symbols of the table will be + filled in. Fill them in with an invalid symbol value so returning them from + huffmanDecodeSymbol will cause error. */ + for(i = 0; i < size; ++i) { + if(tree->table_len[i] == 16) { + /* As length, use a value smaller than FIRSTBITS for the head table, + and a value larger than FIRSTBITS for the secondary table, to ensure + valid behavior for advanceBits when reading this symbol. */ + tree->table_len[i] = (i < headsize) ? 1 : (FIRSTBITS + 1); + tree->table_value[i] = INVALIDSYMBOL; + } + } + } else { + /* A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. + If that is not the case (due to too long length codes), the table will not + have been fully used, and this is an error (not all bit combinations can be + decoded): an oversubscribed huffman tree, indicated by error 55. */ + for(i = 0; i < size; ++i) { + if(tree->table_len[i] == 16) return 55; + } + } + + return 0; +} + +/* +Second step for the ...makeFromLengths and ...makeFromFrequencies functions. +numcodes, lengths and maxbitlen must already be filled in correctly. return +value is error. +*/ +static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) { + unsigned* blcount; + unsigned* nextcode; + unsigned error = 0; + unsigned bits, n; + + tree->codes = (unsigned*)lodepng_malloc(tree->numcodes * sizeof(unsigned)); + blcount = (unsigned*)lodepng_malloc((tree->maxbitlen + 1) * sizeof(unsigned)); + nextcode = (unsigned*)lodepng_malloc((tree->maxbitlen + 1) * sizeof(unsigned)); + if(!tree->codes || !blcount || !nextcode) error = 83; /*alloc fail*/ + + if(!error) { + for(n = 0; n != tree->maxbitlen + 1; n++) blcount[n] = nextcode[n] = 0; + /*step 1: count number of instances of each code length*/ + for(bits = 0; bits != tree->numcodes; ++bits) ++blcount[tree->lengths[bits]]; + /*step 2: generate the nextcode values*/ + for(bits = 1; bits <= tree->maxbitlen; ++bits) { + nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1u; + } + /*step 3: generate all the codes*/ + for(n = 0; n != tree->numcodes; ++n) { + if(tree->lengths[n] != 0) { + tree->codes[n] = nextcode[tree->lengths[n]]++; + /*remove superfluous bits from the code*/ + tree->codes[n] &= ((1u << tree->lengths[n]) - 1u); + } + } + } + + lodepng_free(blcount); + lodepng_free(nextcode); + + if(!error) error = HuffmanTree_makeTable(tree); + return error; +} + +/* +given the code lengths (as stored in the PNG file), generate the tree as defined +by Deflate. maxbitlen is the maximum bits that a code in the tree can have. +return value is error. +*/ +static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen, + size_t numcodes, unsigned maxbitlen) { + unsigned i; + tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned)); + if(!tree->lengths) return 83; /*alloc fail*/ + for(i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i]; + tree->numcodes = (unsigned)numcodes; /*number of symbols*/ + tree->maxbitlen = maxbitlen; + return HuffmanTree_makeFromLengths2(tree); +} + +#ifdef LODEPNG_COMPILE_ENCODER + +/*BPM: Boundary Package Merge, see "A Fast and Space-Economical Algorithm for Length-Limited Coding", +Jyrki Katajainen, Alistair Moffat, Andrew Turpin, 1995.*/ + +/*chain node for boundary package merge*/ +typedef struct BPMNode { + int weight; /*the sum of all weights in this chain*/ + unsigned index; /*index of this leaf node (called "count" in the paper)*/ + struct BPMNode* tail; /*the next nodes in this chain (null if last)*/ + int in_use; +} BPMNode; + +/*lists of chains*/ +typedef struct BPMLists { + /*memory pool*/ + unsigned memsize; + BPMNode* memory; + unsigned numfree; + unsigned nextfree; + BPMNode** freelist; + /*two heads of lookahead chains per list*/ + unsigned listsize; + BPMNode** chains0; + BPMNode** chains1; +} BPMLists; + +/*creates a new chain node with the given parameters, from the memory in the lists */ +static BPMNode* bpmnode_create(BPMLists* lists, int weight, unsigned index, BPMNode* tail) { + unsigned i; + BPMNode* result; + + /*memory full, so garbage collect*/ + if(lists->nextfree >= lists->numfree) { + /*mark only those that are in use*/ + for(i = 0; i != lists->memsize; ++i) lists->memory[i].in_use = 0; + for(i = 0; i != lists->listsize; ++i) { + BPMNode* node; + for(node = lists->chains0[i]; node != 0; node = node->tail) node->in_use = 1; + for(node = lists->chains1[i]; node != 0; node = node->tail) node->in_use = 1; + } + /*collect those that are free*/ + lists->numfree = 0; + for(i = 0; i != lists->memsize; ++i) { + if(!lists->memory[i].in_use) lists->freelist[lists->numfree++] = &lists->memory[i]; + } + lists->nextfree = 0; + } + + result = lists->freelist[lists->nextfree++]; + result->weight = weight; + result->index = index; + result->tail = tail; + return result; +} + +/*sort the leaves with stable mergesort*/ +static void bpmnode_sort(BPMNode* leaves, size_t num) { + BPMNode* mem = (BPMNode*)lodepng_malloc(sizeof(*leaves) * num); + size_t width, counter = 0; + for(width = 1; width < num; width *= 2) { + BPMNode* a = (counter & 1) ? mem : leaves; + BPMNode* b = (counter & 1) ? leaves : mem; + size_t p; + for(p = 0; p < num; p += 2 * width) { + size_t q = (p + width > num) ? num : (p + width); + size_t r = (p + 2 * width > num) ? num : (p + 2 * width); + size_t i = p, j = q, k; + for(k = p; k < r; k++) { + if(i < q && (j >= r || a[i].weight <= a[j].weight)) b[k] = a[i++]; + else b[k] = a[j++]; + } + } + counter++; + } + if(counter & 1) lodepng_memcpy(leaves, mem, sizeof(*leaves) * num); + lodepng_free(mem); +} + +/*Boundary Package Merge step, numpresent is the amount of leaves, and c is the current chain.*/ +static void boundaryPM(BPMLists* lists, BPMNode* leaves, size_t numpresent, int c, int num) { + unsigned lastindex = lists->chains1[c]->index; + + if(c == 0) { + if(lastindex >= numpresent) return; + lists->chains0[c] = lists->chains1[c]; + lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, 0); + } else { + /*sum of the weights of the head nodes of the previous lookahead chains.*/ + int sum = lists->chains0[c - 1]->weight + lists->chains1[c - 1]->weight; + lists->chains0[c] = lists->chains1[c]; + if(lastindex < numpresent && sum > leaves[lastindex].weight) { + lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, lists->chains1[c]->tail); + return; + } + lists->chains1[c] = bpmnode_create(lists, sum, lastindex, lists->chains1[c - 1]); + /*in the end we are only interested in the chain of the last list, so no + need to recurse if we're at the last one (this gives measurable speedup)*/ + if(num + 1 < (int)(2 * numpresent - 2)) { + boundaryPM(lists, leaves, numpresent, c - 1, num); + boundaryPM(lists, leaves, numpresent, c - 1, num); + } + } +} + +unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, + size_t numcodes, unsigned maxbitlen) { + unsigned error = 0; + unsigned i; + size_t numpresent = 0; /*number of symbols with non-zero frequency*/ + BPMNode* leaves; /*the symbols, only those with > 0 frequency*/ + + if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/ + if((1u << maxbitlen) < (unsigned)numcodes) return 80; /*error: represent all symbols*/ + + leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves)); + if(!leaves) return 83; /*alloc fail*/ + + for(i = 0; i != numcodes; ++i) { + if(frequencies[i] > 0) { + leaves[numpresent].weight = (int)frequencies[i]; + leaves[numpresent].index = i; + ++numpresent; + } + } + + lodepng_memset(lengths, 0, numcodes * sizeof(*lengths)); + + /*ensure at least two present symbols. There should be at least one symbol + according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To + make these work as well ensure there are at least two symbols. The + Package-Merge code below also doesn't work correctly if there's only one + symbol, it'd give it the theoretical 0 bits but in practice zlib wants 1 bit*/ + if(numpresent == 0) { + lengths[0] = lengths[1] = 1; /*note that for RFC 1951 section 3.2.7, only lengths[0] = 1 is needed*/ + } else if(numpresent == 1) { + lengths[leaves[0].index] = 1; + lengths[leaves[0].index == 0 ? 1 : 0] = 1; + } else { + BPMLists lists; + BPMNode* node; + + bpmnode_sort(leaves, numpresent); + + lists.listsize = maxbitlen; + lists.memsize = 2 * maxbitlen * (maxbitlen + 1); + lists.nextfree = 0; + lists.numfree = lists.memsize; + lists.memory = (BPMNode*)lodepng_malloc(lists.memsize * sizeof(*lists.memory)); + lists.freelist = (BPMNode**)lodepng_malloc(lists.memsize * sizeof(BPMNode*)); + lists.chains0 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); + lists.chains1 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); + if(!lists.memory || !lists.freelist || !lists.chains0 || !lists.chains1) error = 83; /*alloc fail*/ + + if(!error) { + for(i = 0; i != lists.memsize; ++i) lists.freelist[i] = &lists.memory[i]; + + bpmnode_create(&lists, leaves[0].weight, 1, 0); + bpmnode_create(&lists, leaves[1].weight, 2, 0); + + for(i = 0; i != lists.listsize; ++i) { + lists.chains0[i] = &lists.memory[0]; + lists.chains1[i] = &lists.memory[1]; + } + + /*each boundaryPM call adds one chain to the last list, and we need 2 * numpresent - 2 chains.*/ + for(i = 2; i != 2 * numpresent - 2; ++i) boundaryPM(&lists, leaves, numpresent, (int)maxbitlen - 1, (int)i); + + for(node = lists.chains1[maxbitlen - 1]; node; node = node->tail) { + for(i = 0; i != node->index; ++i) ++lengths[leaves[i].index]; + } + } + + lodepng_free(lists.memory); + lodepng_free(lists.freelist); + lodepng_free(lists.chains0); + lodepng_free(lists.chains1); + } + + lodepng_free(leaves); + return error; +} + +/*Create the Huffman tree given the symbol frequencies*/ +static unsigned HuffmanTree_makeFromFrequencies(HuffmanTree* tree, const unsigned* frequencies, + size_t mincodes, size_t numcodes, unsigned maxbitlen) { + unsigned error = 0; + while(!frequencies[numcodes - 1] && numcodes > mincodes) --numcodes; /*trim zeroes*/ + tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned)); + if(!tree->lengths) return 83; /*alloc fail*/ + tree->maxbitlen = maxbitlen; + tree->numcodes = (unsigned)numcodes; /*number of symbols*/ + + error = lodepng_huffman_code_lengths(tree->lengths, frequencies, numcodes, maxbitlen); + if(!error) error = HuffmanTree_makeFromLengths2(tree); + return error; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/ +static unsigned generateFixedLitLenTree(HuffmanTree* tree) { + unsigned i, error = 0; + unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); + if(!bitlen) return 83; /*alloc fail*/ + + /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/ + for(i = 0; i <= 143; ++i) bitlen[i] = 8; + for(i = 144; i <= 255; ++i) bitlen[i] = 9; + for(i = 256; i <= 279; ++i) bitlen[i] = 7; + for(i = 280; i <= 287; ++i) bitlen[i] = 8; + + error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15); + + lodepng_free(bitlen); + return error; +} + +/*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/ +static unsigned generateFixedDistanceTree(HuffmanTree* tree) { + unsigned i, error = 0; + unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); + if(!bitlen) return 83; /*alloc fail*/ + + /*there are 32 distance codes, but 30-31 are unused*/ + for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5; + error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15); + + lodepng_free(bitlen); + return error; +} + +#ifdef LODEPNG_COMPILE_DECODER + +/* +returns the code. The bit reader must already have been ensured at least 15 bits +*/ +static unsigned huffmanDecodeSymbol(LodePNGBitReader* reader, const HuffmanTree* codetree) { + unsigned short code = peekBits(reader, FIRSTBITS); + unsigned short l = codetree->table_len[code]; + unsigned short value = codetree->table_value[code]; + if(l <= FIRSTBITS) { + advanceBits(reader, l); + return value; + } else { + unsigned index2; + advanceBits(reader, FIRSTBITS); + index2 = value + peekBits(reader, l - FIRSTBITS); + advanceBits(reader, codetree->table_len[index2] - FIRSTBITS); + return codetree->table_value[index2]; + } +} +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Inflator (Decompressor) / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*get the tree of a deflated block with fixed tree, as specified in the deflate specification +Returns error code.*/ +static unsigned getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d) { + unsigned error = generateFixedLitLenTree(tree_ll); + if(error) return error; + return generateFixedDistanceTree(tree_d); +} + +/*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/ +static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, + LodePNGBitReader* reader) { + /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/ + unsigned error = 0; + unsigned n, HLIT, HDIST, HCLEN, i; + + /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/ + unsigned* bitlen_ll = 0; /*lit,len code lengths*/ + unsigned* bitlen_d = 0; /*dist code lengths*/ + /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/ + unsigned* bitlen_cl = 0; + HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/ + + if(!ensureBits17(reader, 14)) return 49; /*error: the bit pointer is or will go past the memory*/ + + /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/ + HLIT = readBits(reader, 5) + 257; + /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/ + HDIST = readBits(reader, 5) + 1; + /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/ + HCLEN = readBits(reader, 4) + 4; + + bitlen_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned)); + if(!bitlen_cl) return 83 /*alloc fail*/; + + HuffmanTree_init(&tree_cl); + + while(!error) { + /*read the code length codes out of 3 * (amount of code length codes) bits*/ + if(lodepng_gtofl(reader->bp, HCLEN * 3, reader->bitsize)) { + ERROR_BREAK(50); /*error: the bit pointer is or will go past the memory*/ + } + for(i = 0; i != HCLEN; ++i) { + ensureBits9(reader, 3); /*out of bounds already checked above */ + bitlen_cl[CLCL_ORDER[i]] = readBits(reader, 3); + } + for(i = HCLEN; i != NUM_CODE_LENGTH_CODES; ++i) { + bitlen_cl[CLCL_ORDER[i]] = 0; + } + + error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7); + if(error) break; + + /*now we can use this tree to read the lengths for the tree that this function will return*/ + bitlen_ll = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); + bitlen_d = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); + if(!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/); + lodepng_memset(bitlen_ll, 0, NUM_DEFLATE_CODE_SYMBOLS * sizeof(*bitlen_ll)); + lodepng_memset(bitlen_d, 0, NUM_DISTANCE_SYMBOLS * sizeof(*bitlen_d)); + + /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/ + i = 0; + while(i < HLIT + HDIST) { + unsigned code; + ensureBits25(reader, 22); /* up to 15 bits for huffman code, up to 7 extra bits below*/ + code = huffmanDecodeSymbol(reader, &tree_cl); + if(code <= 15) /*a length code*/ { + if(i < HLIT) bitlen_ll[i] = code; + else bitlen_d[i - HLIT] = code; + ++i; + } else if(code == 16) /*repeat previous*/ { + unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/ + unsigned value; /*set value to the previous code*/ + + if(i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/ + + replength += readBits(reader, 2); + + if(i < HLIT + 1) value = bitlen_ll[i - 1]; + else value = bitlen_d[i - HLIT - 1]; + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) { + if(i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/ + if(i < HLIT) bitlen_ll[i] = value; + else bitlen_d[i - HLIT] = value; + ++i; + } + } else if(code == 17) /*repeat "0" 3-10 times*/ { + unsigned replength = 3; /*read in the bits that indicate repeat length*/ + replength += readBits(reader, 3); + + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) { + if(i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/ + + if(i < HLIT) bitlen_ll[i] = 0; + else bitlen_d[i - HLIT] = 0; + ++i; + } + } else if(code == 18) /*repeat "0" 11-138 times*/ { + unsigned replength = 11; /*read in the bits that indicate repeat length*/ + replength += readBits(reader, 7); + + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) { + if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/ + + if(i < HLIT) bitlen_ll[i] = 0; + else bitlen_d[i - HLIT] = 0; + ++i; + } + } else /*if(code == INVALIDSYMBOL)*/ { + ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/ + } + /*check if any of the ensureBits above went out of bounds*/ + if(reader->bp > reader->bitsize) { + /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol + (10=no endcode, 11=wrong jump outside of tree)*/ + /* TODO: revise error codes 10,11,50: the above comment is no longer valid */ + ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ + } + } + if(error) break; + + if(bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/ + + /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/ + error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15); + if(error) break; + error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15); + + break; /*end of error-while*/ + } + + lodepng_free(bitlen_cl); + lodepng_free(bitlen_ll); + lodepng_free(bitlen_d); + HuffmanTree_cleanup(&tree_cl); + + return error; +} + +/*inflate a block with dynamic of fixed Huffman tree. btype must be 1 or 2.*/ +static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader, + unsigned btype, size_t max_output_size) { + unsigned error = 0; + HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/ + HuffmanTree tree_d; /*the huffman tree for distance codes*/ + + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + + if(btype == 1) error = getTreeInflateFixed(&tree_ll, &tree_d); + else /*if(btype == 2)*/ error = getTreeInflateDynamic(&tree_ll, &tree_d, reader); + + while(!error) /*decode all symbols until end reached, breaks at end code*/ { + /*code_ll is literal, length or end code*/ + unsigned code_ll; + ensureBits25(reader, 20); /* up to 15 for the huffman symbol, up to 5 for the length extra bits */ + code_ll = huffmanDecodeSymbol(reader, &tree_ll); + if(code_ll <= 255) /*literal symbol*/ { + if(!ucvector_resize(out, out->size + 1)) ERROR_BREAK(83 /*alloc fail*/); + out->data[out->size - 1] = (unsigned char)code_ll; + } else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ { + unsigned code_d, distance; + unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/ + size_t start, backward, length; + + /*part 1: get length base*/ + length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX]; + + /*part 2: get extra bits and add the value of that to length*/ + numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX]; + if(numextrabits_l != 0) { + /* bits already ensured above */ + length += readBits(reader, numextrabits_l); + } + + /*part 3: get distance code*/ + ensureBits32(reader, 28); /* up to 15 for the huffman symbol, up to 13 for the extra bits */ + code_d = huffmanDecodeSymbol(reader, &tree_d); + if(code_d > 29) { + if(code_d <= 31) { + ERROR_BREAK(18); /*error: invalid distance code (30-31 are never used)*/ + } else /* if(code_d == INVALIDSYMBOL) */{ + ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/ + } + } + distance = DISTANCEBASE[code_d]; + + /*part 4: get extra bits from distance*/ + numextrabits_d = DISTANCEEXTRA[code_d]; + if(numextrabits_d != 0) { + /* bits already ensured above */ + distance += readBits(reader, numextrabits_d); + } + + /*part 5: fill in all the out[n] values based on the length and dist*/ + start = out->size; + if(distance > start) ERROR_BREAK(52); /*too long backward distance*/ + backward = start - distance; + + if(!ucvector_resize(out, out->size + length)) ERROR_BREAK(83 /*alloc fail*/); + if(distance < length) { + size_t forward; + lodepng_memcpy(out->data + start, out->data + backward, distance); + start += distance; + for(forward = distance; forward < length; ++forward) { + out->data[start++] = out->data[backward++]; + } + } else { + lodepng_memcpy(out->data + start, out->data + backward, length); + } + } else if(code_ll == 256) { + break; /*end code, break the loop*/ + } else /*if(code_ll == INVALIDSYMBOL)*/ { + ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/ + } + /*check if any of the ensureBits above went out of bounds*/ + if(reader->bp > reader->bitsize) { + /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol + (10=no endcode, 11=wrong jump outside of tree)*/ + /* TODO: revise error codes 10,11,50: the above comment is no longer valid */ + ERROR_BREAK(51); /*error, bit pointer jumps past memory*/ + } + if(max_output_size && out->size > max_output_size) { + ERROR_BREAK(109); /*error, larger than max size*/ + } + } + + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + + return error; +} + +static unsigned inflateNoCompression(ucvector* out, LodePNGBitReader* reader, + const LodePNGDecompressSettings* settings) { + size_t bytepos; + size_t size = reader->size; + unsigned LEN, NLEN, error = 0; + + /*go to first boundary of byte*/ + bytepos = (reader->bp + 7u) >> 3u; + + /*read LEN (2 bytes) and NLEN (2 bytes)*/ + if(bytepos + 4 >= size) return 52; /*error, bit pointer will jump past memory*/ + LEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2; + NLEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2; + + /*check if 16-bit NLEN is really the one's complement of LEN*/ + if(!settings->ignore_nlen && LEN + NLEN != 65535) { + return 21; /*error: NLEN is not one's complement of LEN*/ + } + + if(!ucvector_resize(out, out->size + LEN)) return 83; /*alloc fail*/ + + /*read the literal data: LEN bytes are now stored in the out buffer*/ + if(bytepos + LEN > size) return 23; /*error: reading outside of in buffer*/ + + lodepng_memcpy(out->data + out->size - LEN, reader->data + bytepos, LEN); + bytepos += LEN; + + reader->bp = bytepos << 3u; + + return error; +} + +static unsigned lodepng_inflatev(ucvector* out, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) { + unsigned BFINAL = 0; + LodePNGBitReader reader; + unsigned error = LodePNGBitReader_init(&reader, in, insize); + + if(error) return error; + + while(!BFINAL) { + unsigned BTYPE; + if(!ensureBits9(&reader, 3)) return 52; /*error, bit pointer will jump past memory*/ + BFINAL = readBits(&reader, 1); + BTYPE = readBits(&reader, 2); + + if(BTYPE == 3) return 20; /*error: invalid BTYPE*/ + else if(BTYPE == 0) error = inflateNoCompression(out, &reader, settings); /*no compression*/ + else error = inflateHuffmanBlock(out, &reader, BTYPE, settings->max_output_size); /*compression, BTYPE 01 or 10*/ + if(!error && settings->max_output_size && out->size > settings->max_output_size) error = 109; + if(error) break; + } + + return error; +} + +unsigned lodepng_inflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) { + ucvector v = ucvector_init(*out, *outsize); + unsigned error = lodepng_inflatev(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; + return error; +} + +static unsigned inflatev(ucvector* out, const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) { + if(settings->custom_inflate) { + unsigned error = settings->custom_inflate(&out->data, &out->size, in, insize, settings); + out->allocsize = out->size; + if(error) { + /*the custom inflate is allowed to have its own error codes, however, we translate it to code 110*/ + error = 110; + /*if there's a max output size, and the custom zlib returned error, then indicate that error instead*/ + if(settings->max_output_size && out->size > settings->max_output_size) error = 109; + } + return error; + } else { + return lodepng_inflatev(out, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Deflator (Compressor) / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static const size_t MAX_SUPPORTED_DEFLATE_LENGTH = 258; + +/*search the index in the array, that has the largest value smaller than or equal to the given value, +given array must be sorted (if no value is smaller, it returns the size of the given array)*/ +static size_t searchCodeIndex(const unsigned* array, size_t array_size, size_t value) { + /*binary search (only small gain over linear). TODO: use CPU log2 instruction for getting symbols instead*/ + size_t left = 1; + size_t right = array_size - 1; + + while(left <= right) { + size_t mid = (left + right) >> 1; + if(array[mid] >= value) right = mid - 1; + else left = mid + 1; + } + if(left >= array_size || array[left] > value) left--; + return left; +} + +static void addLengthDistance(uivector* values, size_t length, size_t distance) { + /*values in encoded vector are those used by deflate: + 0-255: literal bytes + 256: end + 257-285: length/distance pair (length code, followed by extra length bits, distance code, extra distance bits) + 286-287: invalid*/ + + unsigned length_code = (unsigned)searchCodeIndex(LENGTHBASE, 29, length); + unsigned extra_length = (unsigned)(length - LENGTHBASE[length_code]); + unsigned dist_code = (unsigned)searchCodeIndex(DISTANCEBASE, 30, distance); + unsigned extra_distance = (unsigned)(distance - DISTANCEBASE[dist_code]); + + size_t pos = values->size; + /*TODO: return error when this fails (out of memory)*/ + unsigned ok = uivector_resize(values, values->size + 4); + if(ok) { + values->data[pos + 0] = length_code + FIRST_LENGTH_CODE_INDEX; + values->data[pos + 1] = extra_length; + values->data[pos + 2] = dist_code; + values->data[pos + 3] = extra_distance; + } +} + +/*3 bytes of data get encoded into two bytes. The hash cannot use more than 3 +bytes as input because 3 is the minimum match length for deflate*/ +static const unsigned HASH_NUM_VALUES = 65536; +static const unsigned HASH_BIT_MASK = 65535; /*HASH_NUM_VALUES - 1, but C90 does not like that as initializer*/ + +typedef struct Hash { + int* head; /*hash value to head circular pos - can be outdated if went around window*/ + /*circular pos to prev circular pos*/ + unsigned short* chain; + int* val; /*circular pos to hash value*/ + + /*TODO: do this not only for zeros but for any repeated byte. However for PNG + it's always going to be the zeros that dominate, so not important for PNG*/ + int* headz; /*similar to head, but for chainz*/ + unsigned short* chainz; /*those with same amount of zeros*/ + unsigned short* zeros; /*length of zeros streak, used as a second hash chain*/ +} Hash; + +static unsigned hash_init(Hash* hash, unsigned windowsize) { + unsigned i; + hash->head = (int*)lodepng_malloc(sizeof(int) * HASH_NUM_VALUES); + hash->val = (int*)lodepng_malloc(sizeof(int) * windowsize); + hash->chain = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + + hash->zeros = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + hash->headz = (int*)lodepng_malloc(sizeof(int) * (MAX_SUPPORTED_DEFLATE_LENGTH + 1)); + hash->chainz = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + + if(!hash->head || !hash->chain || !hash->val || !hash->headz|| !hash->chainz || !hash->zeros) { + return 83; /*alloc fail*/ + } + + /*initialize hash table*/ + for(i = 0; i != HASH_NUM_VALUES; ++i) hash->head[i] = -1; + for(i = 0; i != windowsize; ++i) hash->val[i] = -1; + for(i = 0; i != windowsize; ++i) hash->chain[i] = i; /*same value as index indicates uninitialized*/ + + for(i = 0; i <= MAX_SUPPORTED_DEFLATE_LENGTH; ++i) hash->headz[i] = -1; + for(i = 0; i != windowsize; ++i) hash->chainz[i] = i; /*same value as index indicates uninitialized*/ + + return 0; +} + +static void hash_cleanup(Hash* hash) { + lodepng_free(hash->head); + lodepng_free(hash->val); + lodepng_free(hash->chain); + + lodepng_free(hash->zeros); + lodepng_free(hash->headz); + lodepng_free(hash->chainz); +} + + + +static unsigned getHash(const unsigned char* data, size_t size, size_t pos) { + unsigned result = 0; + if(pos + 2 < size) { + /*A simple shift and xor hash is used. Since the data of PNGs is dominated + by zeroes due to the filters, a better hash does not have a significant + effect on speed in traversing the chain, and causes more time spend on + calculating the hash.*/ + result ^= ((unsigned)data[pos + 0] << 0u); + result ^= ((unsigned)data[pos + 1] << 4u); + result ^= ((unsigned)data[pos + 2] << 8u); + } else { + size_t amount, i; + if(pos >= size) return 0; + amount = size - pos; + for(i = 0; i != amount; ++i) result ^= ((unsigned)data[pos + i] << (i * 8u)); + } + return result & HASH_BIT_MASK; +} + +static unsigned countZeros(const unsigned char* data, size_t size, size_t pos) { + const unsigned char* start = data + pos; + const unsigned char* end = start + MAX_SUPPORTED_DEFLATE_LENGTH; + if(end > data + size) end = data + size; + data = start; + while(data != end && *data == 0) ++data; + /*subtracting two addresses returned as 32-bit number (max value is MAX_SUPPORTED_DEFLATE_LENGTH)*/ + return (unsigned)(data - start); +} + +/*wpos = pos & (windowsize - 1)*/ +static void updateHashChain(Hash* hash, size_t wpos, unsigned hashval, unsigned short numzeros) { + hash->val[wpos] = (int)hashval; + if(hash->head[hashval] != -1) hash->chain[wpos] = hash->head[hashval]; + hash->head[hashval] = (int)wpos; + + hash->zeros[wpos] = numzeros; + if(hash->headz[numzeros] != -1) hash->chainz[wpos] = hash->headz[numzeros]; + hash->headz[numzeros] = (int)wpos; +} + +/* +LZ77-encode the data. Return value is error code. The input are raw bytes, the output +is in the form of unsigned integers with codes representing for example literal bytes, or +length/distance pairs. +It uses a hash table technique to let it encode faster. When doing LZ77 encoding, a +sliding window (of windowsize) is used, and all past bytes in that window can be used as +the "dictionary". A brute force search through all possible distances would be slow, and +this hash technique is one out of several ways to speed this up. +*/ +static unsigned encodeLZ77(uivector* out, Hash* hash, + const unsigned char* in, size_t inpos, size_t insize, unsigned windowsize, + unsigned minmatch, unsigned nicematch, unsigned lazymatching) { + size_t pos; + unsigned i, error = 0; + /*for large window lengths, assume the user wants no compression loss. Otherwise, max hash chain length speedup.*/ + unsigned maxchainlength = windowsize >= 8192 ? windowsize : windowsize / 8u; + unsigned maxlazymatch = windowsize >= 8192 ? MAX_SUPPORTED_DEFLATE_LENGTH : 64; + + unsigned usezeros = 1; /*not sure if setting it to false for windowsize < 8192 is better or worse*/ + unsigned numzeros = 0; + + unsigned offset; /*the offset represents the distance in LZ77 terminology*/ + unsigned length; + unsigned lazy = 0; + unsigned lazylength = 0, lazyoffset = 0; + unsigned hashval; + unsigned current_offset, current_length; + unsigned prev_offset; + const unsigned char *lastptr, *foreptr, *backptr; + unsigned hashpos; + + if(windowsize == 0 || windowsize > 32768) return 60; /*error: windowsize smaller/larger than allowed*/ + if((windowsize & (windowsize - 1)) != 0) return 90; /*error: must be power of two*/ + + if(nicematch > MAX_SUPPORTED_DEFLATE_LENGTH) nicematch = MAX_SUPPORTED_DEFLATE_LENGTH; + + for(pos = inpos; pos < insize; ++pos) { + size_t wpos = pos & (windowsize - 1); /*position for in 'circular' hash buffers*/ + unsigned chainlength = 0; + + hashval = getHash(in, insize, pos); + + if(usezeros && hashval == 0) { + if(numzeros == 0) numzeros = countZeros(in, insize, pos); + else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; + } else { + numzeros = 0; + } + + updateHashChain(hash, wpos, hashval, numzeros); + + /*the length and offset found for the current position*/ + length = 0; + offset = 0; + + hashpos = hash->chain[wpos]; + + lastptr = &in[insize < pos + MAX_SUPPORTED_DEFLATE_LENGTH ? insize : pos + MAX_SUPPORTED_DEFLATE_LENGTH]; + + /*search for the longest string*/ + prev_offset = 0; + for(;;) { + if(chainlength++ >= maxchainlength) break; + current_offset = (unsigned)(hashpos <= wpos ? wpos - hashpos : wpos - hashpos + windowsize); + + if(current_offset < prev_offset) break; /*stop when went completely around the circular buffer*/ + prev_offset = current_offset; + if(current_offset > 0) { + /*test the next characters*/ + foreptr = &in[pos]; + backptr = &in[pos - current_offset]; + + /*common case in PNGs is lots of zeros. Quickly skip over them as a speedup*/ + if(numzeros >= 3) { + unsigned skip = hash->zeros[hashpos]; + if(skip > numzeros) skip = numzeros; + backptr += skip; + foreptr += skip; + } + + while(foreptr != lastptr && *backptr == *foreptr) /*maximum supported length by deflate is max length*/ { + ++backptr; + ++foreptr; + } + current_length = (unsigned)(foreptr - &in[pos]); + + if(current_length > length) { + length = current_length; /*the longest length*/ + offset = current_offset; /*the offset that is related to this longest length*/ + /*jump out once a length of max length is found (speed gain). This also jumps + out if length is MAX_SUPPORTED_DEFLATE_LENGTH*/ + if(current_length >= nicematch) break; + } + } + + if(hashpos == hash->chain[hashpos]) break; + + if(numzeros >= 3 && length > numzeros) { + hashpos = hash->chainz[hashpos]; + if(hash->zeros[hashpos] != numzeros) break; + } else { + hashpos = hash->chain[hashpos]; + /*outdated hash value, happens if particular value was not encountered in whole last window*/ + if(hash->val[hashpos] != (int)hashval) break; + } + } + + if(lazymatching) { + if(!lazy && length >= 3 && length <= maxlazymatch && length < MAX_SUPPORTED_DEFLATE_LENGTH) { + lazy = 1; + lazylength = length; + lazyoffset = offset; + continue; /*try the next byte*/ + } + if(lazy) { + lazy = 0; + if(pos == 0) ERROR_BREAK(81); + if(length > lazylength + 1) { + /*push the previous character as literal*/ + if(!uivector_push_back(out, in[pos - 1])) ERROR_BREAK(83 /*alloc fail*/); + } else { + length = lazylength; + offset = lazyoffset; + hash->head[hashval] = -1; /*the same hashchain update will be done, this ensures no wrong alteration*/ + hash->headz[numzeros] = -1; /*idem*/ + --pos; + } + } + } + if(length >= 3 && offset > windowsize) ERROR_BREAK(86 /*too big (or overflown negative) offset*/); + + /*encode it as length/distance pair or literal value*/ + if(length < 3) /*only lengths of 3 or higher are supported as length/distance pair*/ { + if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); + } else if(length < minmatch || (length == 3 && offset > 4096)) { + /*compensate for the fact that longer offsets have more extra bits, a + length of only 3 may be not worth it then*/ + if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); + } else { + addLengthDistance(out, length, offset); + for(i = 1; i < length; ++i) { + ++pos; + wpos = pos & (windowsize - 1); + hashval = getHash(in, insize, pos); + if(usezeros && hashval == 0) { + if(numzeros == 0) numzeros = countZeros(in, insize, pos); + else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; + } else { + numzeros = 0; + } + updateHashChain(hash, wpos, hashval, numzeros); + } + } + } /*end of the loop through each character of input*/ + + return error; +} + +/* /////////////////////////////////////////////////////////////////////////// */ + +static unsigned deflateNoCompression(ucvector* out, const unsigned char* data, size_t datasize) { + /*non compressed deflate block data: 1 bit BFINAL,2 bits BTYPE,(5 bits): it jumps to start of next byte, + 2 bytes LEN, 2 bytes NLEN, LEN bytes literal DATA*/ + + size_t i, numdeflateblocks = (datasize + 65534u) / 65535u; + unsigned datapos = 0; + for(i = 0; i != numdeflateblocks; ++i) { + unsigned BFINAL, BTYPE, LEN, NLEN; + unsigned char firstbyte; + size_t pos = out->size; + + BFINAL = (i == numdeflateblocks - 1); + BTYPE = 0; + + LEN = 65535; + if(datasize - datapos < 65535u) LEN = (unsigned)datasize - datapos; + NLEN = 65535 - LEN; + + if(!ucvector_resize(out, out->size + LEN + 5)) return 83; /*alloc fail*/ + + firstbyte = (unsigned char)(BFINAL + ((BTYPE & 1u) << 1u) + ((BTYPE & 2u) << 1u)); + out->data[pos + 0] = firstbyte; + out->data[pos + 1] = (unsigned char)(LEN & 255); + out->data[pos + 2] = (unsigned char)(LEN >> 8u); + out->data[pos + 3] = (unsigned char)(NLEN & 255); + out->data[pos + 4] = (unsigned char)(NLEN >> 8u); + lodepng_memcpy(out->data + pos + 5, data + datapos, LEN); + datapos += LEN; + } + + return 0; +} + +/* +write the lz77-encoded data, which has lit, len and dist codes, to compressed stream using huffman trees. +tree_ll: the tree for lit and len codes. +tree_d: the tree for distance codes. +*/ +static void writeLZ77data(LodePNGBitWriter* writer, const uivector* lz77_encoded, + const HuffmanTree* tree_ll, const HuffmanTree* tree_d) { + size_t i = 0; + for(i = 0; i != lz77_encoded->size; ++i) { + unsigned val = lz77_encoded->data[i]; + writeBitsReversed(writer, tree_ll->codes[val], tree_ll->lengths[val]); + if(val > 256) /*for a length code, 3 more things have to be added*/ { + unsigned length_index = val - FIRST_LENGTH_CODE_INDEX; + unsigned n_length_extra_bits = LENGTHEXTRA[length_index]; + unsigned length_extra_bits = lz77_encoded->data[++i]; + + unsigned distance_code = lz77_encoded->data[++i]; + + unsigned distance_index = distance_code; + unsigned n_distance_extra_bits = DISTANCEEXTRA[distance_index]; + unsigned distance_extra_bits = lz77_encoded->data[++i]; + + writeBits(writer, length_extra_bits, n_length_extra_bits); + writeBitsReversed(writer, tree_d->codes[distance_code], tree_d->lengths[distance_code]); + writeBits(writer, distance_extra_bits, n_distance_extra_bits); + } + } +} + +/*Deflate for a block of type "dynamic", that is, with freely, optimally, created huffman trees*/ +static unsigned deflateDynamic(LodePNGBitWriter* writer, Hash* hash, + const unsigned char* data, size_t datapos, size_t dataend, + const LodePNGCompressSettings* settings, unsigned final) { + unsigned error = 0; + + /* + A block is compressed as follows: The PNG data is lz77 encoded, resulting in + literal bytes and length/distance pairs. This is then huffman compressed with + two huffman trees. One huffman tree is used for the lit and len values ("ll"), + another huffman tree is used for the dist values ("d"). These two trees are + stored using their code lengths, and to compress even more these code lengths + are also run-length encoded and huffman compressed. This gives a huffman tree + of code lengths "cl". The code lengths used to describe this third tree are + the code length code lengths ("clcl"). + */ + + /*The lz77 encoded data, represented with integers since there will also be length and distance codes in it*/ + uivector lz77_encoded; + HuffmanTree tree_ll; /*tree for lit,len values*/ + HuffmanTree tree_d; /*tree for distance codes*/ + HuffmanTree tree_cl; /*tree for encoding the code lengths representing tree_ll and tree_d*/ + unsigned* frequencies_ll = 0; /*frequency of lit,len codes*/ + unsigned* frequencies_d = 0; /*frequency of dist codes*/ + unsigned* frequencies_cl = 0; /*frequency of code length codes*/ + unsigned* bitlen_lld = 0; /*lit,len,dist code lengths (int bits), literally (without repeat codes).*/ + unsigned* bitlen_lld_e = 0; /*bitlen_lld encoded with repeat codes (this is a rudimentary run length compression)*/ + size_t datasize = dataend - datapos; + + /* + If we could call "bitlen_cl" the the code length code lengths ("clcl"), that is the bit lengths of codes to represent + tree_cl in CLCL_ORDER, then due to the huffman compression of huffman tree representations ("two levels"), there are + some analogies: + bitlen_lld is to tree_cl what data is to tree_ll and tree_d. + bitlen_lld_e is to bitlen_lld what lz77_encoded is to data. + bitlen_cl is to bitlen_lld_e what bitlen_lld is to lz77_encoded. + */ + + unsigned BFINAL = final; + size_t i; + size_t numcodes_ll, numcodes_d, numcodes_lld, numcodes_lld_e, numcodes_cl; + unsigned HLIT, HDIST, HCLEN; + + uivector_init(&lz77_encoded); + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + HuffmanTree_init(&tree_cl); + /* could fit on stack, but >1KB is on the larger side so allocate instead */ + frequencies_ll = (unsigned*)lodepng_malloc(286 * sizeof(*frequencies_ll)); + frequencies_d = (unsigned*)lodepng_malloc(30 * sizeof(*frequencies_d)); + frequencies_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(*frequencies_cl)); + + if(!frequencies_ll || !frequencies_d || !frequencies_cl) error = 83; /*alloc fail*/ + + /*This while loop never loops due to a break at the end, it is here to + allow breaking out of it to the cleanup phase on error conditions.*/ + while(!error) { + lodepng_memset(frequencies_ll, 0, 286 * sizeof(*frequencies_ll)); + lodepng_memset(frequencies_d, 0, 30 * sizeof(*frequencies_d)); + lodepng_memset(frequencies_cl, 0, NUM_CODE_LENGTH_CODES * sizeof(*frequencies_cl)); + + if(settings->use_lz77) { + error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, + settings->minmatch, settings->nicematch, settings->lazymatching); + if(error) break; + } else { + if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83 /*alloc fail*/); + for(i = datapos; i < dataend; ++i) lz77_encoded.data[i - datapos] = data[i]; /*no LZ77, but still will be Huffman compressed*/ + } + + /*Count the frequencies of lit, len and dist codes*/ + for(i = 0; i != lz77_encoded.size; ++i) { + unsigned symbol = lz77_encoded.data[i]; + ++frequencies_ll[symbol]; + if(symbol > 256) { + unsigned dist = lz77_encoded.data[i + 2]; + ++frequencies_d[dist]; + i += 3; + } + } + frequencies_ll[256] = 1; /*there will be exactly 1 end code, at the end of the block*/ + + /*Make both huffman trees, one for the lit and len codes, one for the dist codes*/ + error = HuffmanTree_makeFromFrequencies(&tree_ll, frequencies_ll, 257, 286, 15); + if(error) break; + /*2, not 1, is chosen for mincodes: some buggy PNG decoders require at least 2 symbols in the dist tree*/ + error = HuffmanTree_makeFromFrequencies(&tree_d, frequencies_d, 2, 30, 15); + if(error) break; + + numcodes_ll = LODEPNG_MIN(tree_ll.numcodes, 286); + numcodes_d = LODEPNG_MIN(tree_d.numcodes, 30); + /*store the code lengths of both generated trees in bitlen_lld*/ + numcodes_lld = numcodes_ll + numcodes_d; + bitlen_lld = (unsigned*)lodepng_malloc(numcodes_lld * sizeof(*bitlen_lld)); + /*numcodes_lld_e never needs more size than bitlen_lld*/ + bitlen_lld_e = (unsigned*)lodepng_malloc(numcodes_lld * sizeof(*bitlen_lld_e)); + if(!bitlen_lld || !bitlen_lld_e) ERROR_BREAK(83); /*alloc fail*/ + numcodes_lld_e = 0; + + for(i = 0; i != numcodes_ll; ++i) bitlen_lld[i] = tree_ll.lengths[i]; + for(i = 0; i != numcodes_d; ++i) bitlen_lld[numcodes_ll + i] = tree_d.lengths[i]; + + /*run-length compress bitlen_ldd into bitlen_lld_e by using repeat codes 16 (copy length 3-6 times), + 17 (3-10 zeroes), 18 (11-138 zeroes)*/ + for(i = 0; i != numcodes_lld; ++i) { + unsigned j = 0; /*amount of repetitions*/ + while(i + j + 1 < numcodes_lld && bitlen_lld[i + j + 1] == bitlen_lld[i]) ++j; + + if(bitlen_lld[i] == 0 && j >= 2) /*repeat code for zeroes*/ { + ++j; /*include the first zero*/ + if(j <= 10) /*repeat code 17 supports max 10 zeroes*/ { + bitlen_lld_e[numcodes_lld_e++] = 17; + bitlen_lld_e[numcodes_lld_e++] = j - 3; + } else /*repeat code 18 supports max 138 zeroes*/ { + if(j > 138) j = 138; + bitlen_lld_e[numcodes_lld_e++] = 18; + bitlen_lld_e[numcodes_lld_e++] = j - 11; + } + i += (j - 1); + } else if(j >= 3) /*repeat code for value other than zero*/ { + size_t k; + unsigned num = j / 6u, rest = j % 6u; + bitlen_lld_e[numcodes_lld_e++] = bitlen_lld[i]; + for(k = 0; k < num; ++k) { + bitlen_lld_e[numcodes_lld_e++] = 16; + bitlen_lld_e[numcodes_lld_e++] = 6 - 3; + } + if(rest >= 3) { + bitlen_lld_e[numcodes_lld_e++] = 16; + bitlen_lld_e[numcodes_lld_e++] = rest - 3; + } + else j -= rest; + i += j; + } else /*too short to benefit from repeat code*/ { + bitlen_lld_e[numcodes_lld_e++] = bitlen_lld[i]; + } + } + + /*generate tree_cl, the huffmantree of huffmantrees*/ + for(i = 0; i != numcodes_lld_e; ++i) { + ++frequencies_cl[bitlen_lld_e[i]]; + /*after a repeat code come the bits that specify the number of repetitions, + those don't need to be in the frequencies_cl calculation*/ + if(bitlen_lld_e[i] >= 16) ++i; + } + + error = HuffmanTree_makeFromFrequencies(&tree_cl, frequencies_cl, + NUM_CODE_LENGTH_CODES, NUM_CODE_LENGTH_CODES, 7); + if(error) break; + + /*compute amount of code-length-code-lengths to output*/ + numcodes_cl = NUM_CODE_LENGTH_CODES; + /*trim zeros at the end (using CLCL_ORDER), but minimum size must be 4 (see HCLEN below)*/ + while(numcodes_cl > 4u && tree_cl.lengths[CLCL_ORDER[numcodes_cl - 1u]] == 0) { + numcodes_cl--; + } + + /* + Write everything into the output + + After the BFINAL and BTYPE, the dynamic block consists out of the following: + - 5 bits HLIT, 5 bits HDIST, 4 bits HCLEN + - (HCLEN+4)*3 bits code lengths of code length alphabet + - HLIT + 257 code lengths of lit/length alphabet (encoded using the code length + alphabet, + possible repetition codes 16, 17, 18) + - HDIST + 1 code lengths of distance alphabet (encoded using the code length + alphabet, + possible repetition codes 16, 17, 18) + - compressed data + - 256 (end code) + */ + + /*Write block type*/ + writeBits(writer, BFINAL, 1); + writeBits(writer, 0, 1); /*first bit of BTYPE "dynamic"*/ + writeBits(writer, 1, 1); /*second bit of BTYPE "dynamic"*/ + + /*write the HLIT, HDIST and HCLEN values*/ + /*all three sizes take trimmed ending zeroes into account, done either by HuffmanTree_makeFromFrequencies + or in the loop for numcodes_cl above, which saves space. */ + HLIT = (unsigned)(numcodes_ll - 257); + HDIST = (unsigned)(numcodes_d - 1); + HCLEN = (unsigned)(numcodes_cl - 4); + writeBits(writer, HLIT, 5); + writeBits(writer, HDIST, 5); + writeBits(writer, HCLEN, 4); + + /*write the code lengths of the code length alphabet ("bitlen_cl")*/ + for(i = 0; i != numcodes_cl; ++i) writeBits(writer, tree_cl.lengths[CLCL_ORDER[i]], 3); + + /*write the lengths of the lit/len AND the dist alphabet*/ + for(i = 0; i != numcodes_lld_e; ++i) { + writeBitsReversed(writer, tree_cl.codes[bitlen_lld_e[i]], tree_cl.lengths[bitlen_lld_e[i]]); + /*extra bits of repeat codes*/ + if(bitlen_lld_e[i] == 16) writeBits(writer, bitlen_lld_e[++i], 2); + else if(bitlen_lld_e[i] == 17) writeBits(writer, bitlen_lld_e[++i], 3); + else if(bitlen_lld_e[i] == 18) writeBits(writer, bitlen_lld_e[++i], 7); + } + + /*write the compressed data symbols*/ + writeLZ77data(writer, &lz77_encoded, &tree_ll, &tree_d); + /*error: the length of the end code 256 must be larger than 0*/ + if(tree_ll.lengths[256] == 0) ERROR_BREAK(64); + + /*write the end code*/ + writeBitsReversed(writer, tree_ll.codes[256], tree_ll.lengths[256]); + + break; /*end of error-while*/ + } + + /*cleanup*/ + uivector_cleanup(&lz77_encoded); + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + HuffmanTree_cleanup(&tree_cl); + lodepng_free(frequencies_ll); + lodepng_free(frequencies_d); + lodepng_free(frequencies_cl); + lodepng_free(bitlen_lld); + lodepng_free(bitlen_lld_e); + + return error; +} + +static unsigned deflateFixed(LodePNGBitWriter* writer, Hash* hash, + const unsigned char* data, + size_t datapos, size_t dataend, + const LodePNGCompressSettings* settings, unsigned final) { + HuffmanTree tree_ll; /*tree for literal values and length codes*/ + HuffmanTree tree_d; /*tree for distance codes*/ + + unsigned BFINAL = final; + unsigned error = 0; + size_t i; + + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + + error = generateFixedLitLenTree(&tree_ll); + if(!error) error = generateFixedDistanceTree(&tree_d); + + if(!error) { + writeBits(writer, BFINAL, 1); + writeBits(writer, 1, 1); /*first bit of BTYPE*/ + writeBits(writer, 0, 1); /*second bit of BTYPE*/ + + if(settings->use_lz77) /*LZ77 encoded*/ { + uivector lz77_encoded; + uivector_init(&lz77_encoded); + error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, + settings->minmatch, settings->nicematch, settings->lazymatching); + if(!error) writeLZ77data(writer, &lz77_encoded, &tree_ll, &tree_d); + uivector_cleanup(&lz77_encoded); + } else /*no LZ77, but still will be Huffman compressed*/ { + for(i = datapos; i < dataend; ++i) { + writeBitsReversed(writer, tree_ll.codes[data[i]], tree_ll.lengths[data[i]]); + } + } + /*add END code*/ + if(!error) writeBitsReversed(writer,tree_ll.codes[256], tree_ll.lengths[256]); + } + + /*cleanup*/ + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + + return error; +} + +static unsigned lodepng_deflatev(ucvector* out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) { + unsigned error = 0; + size_t i, blocksize, numdeflateblocks; + Hash hash; + LodePNGBitWriter writer; + + LodePNGBitWriter_init(&writer, out); + + if(settings->btype > 2) return 61; + else if(settings->btype == 0) return deflateNoCompression(out, in, insize); + else if(settings->btype == 1) blocksize = insize; + else /*if(settings->btype == 2)*/ { + /*on PNGs, deflate blocks of 65-262k seem to give most dense encoding*/ + blocksize = insize / 8u + 8; + if(blocksize < 65536) blocksize = 65536; + if(blocksize > 262144) blocksize = 262144; + } + + numdeflateblocks = (insize + blocksize - 1) / blocksize; + if(numdeflateblocks == 0) numdeflateblocks = 1; + + error = hash_init(&hash, settings->windowsize); + + if(!error) { + for(i = 0; i != numdeflateblocks && !error; ++i) { + unsigned final = (i == numdeflateblocks - 1); + size_t start = i * blocksize; + size_t end = start + blocksize; + if(end > insize) end = insize; + + if(settings->btype == 1) error = deflateFixed(&writer, &hash, in, start, end, settings, final); + else if(settings->btype == 2) error = deflateDynamic(&writer, &hash, in, start, end, settings, final); + } + } + + hash_cleanup(&hash); + + return error; +} + +unsigned lodepng_deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) { + ucvector v = ucvector_init(*out, *outsize); + unsigned error = lodepng_deflatev(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; + return error; +} + +static unsigned deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) { + if(settings->custom_deflate) { + unsigned error = settings->custom_deflate(out, outsize, in, insize, settings); + /*the custom deflate is allowed to have its own error codes, however, we translate it to code 111*/ + return error ? 111 : 0; + } else { + return lodepng_deflate(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Adler32 / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len) { + unsigned s1 = adler & 0xffffu; + unsigned s2 = (adler >> 16u) & 0xffffu; + + while(len != 0u) { + unsigned i; + /*at least 5552 sums can be done before the sums overflow, saving a lot of module divisions*/ + unsigned amount = len > 5552u ? 5552u : len; + len -= amount; + for(i = 0; i != amount; ++i) { + s1 += (*data++); + s2 += s1; + } + s1 %= 65521u; + s2 %= 65521u; + } + + return (s2 << 16u) | s1; +} + +/*Return the adler32 of the bytes data[0..len-1]*/ +static unsigned adler32(const unsigned char* data, unsigned len) { + return update_adler32(1u, data, len); +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Zlib / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_DECODER + +static unsigned lodepng_zlib_decompressv(ucvector* out, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) { + unsigned error = 0; + unsigned CM, CINFO, FDICT; + + if(insize < 2) return 53; /*error, size of zlib data too small*/ + /*read information from zlib header*/ + if((in[0] * 256 + in[1]) % 31 != 0) { + /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/ + return 24; + } + + CM = in[0] & 15; + CINFO = (in[0] >> 4) & 15; + /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/ + FDICT = (in[1] >> 5) & 1; + /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/ + + if(CM != 8 || CINFO > 7) { + /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/ + return 25; + } + if(FDICT != 0) { + /*error: the specification of PNG says about the zlib stream: + "The additional flags shall not specify a preset dictionary."*/ + return 26; + } + + error = inflatev(out, in + 2, insize - 2, settings); + if(error) return error; + + if(!settings->ignore_adler32) { + unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]); + unsigned checksum = adler32(out->data, (unsigned)(out->size)); + if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/ + } + + return 0; /*no error*/ +} + + +unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGDecompressSettings* settings) { + ucvector v = ucvector_init(*out, *outsize); + unsigned error = lodepng_zlib_decompressv(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; + return error; +} + +/*expected_size is expected output size, to avoid intermediate allocations. Set to 0 if not known. */ +static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size, + const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { + unsigned error; + if(settings->custom_zlib) { + error = settings->custom_zlib(out, outsize, in, insize, settings); + if(error) { + /*the custom zlib is allowed to have its own error codes, however, we translate it to code 110*/ + error = 110; + /*if there's a max output size, and the custom zlib returned error, then indicate that error instead*/ + if(settings->max_output_size && *outsize > settings->max_output_size) error = 109; + } + } else { + ucvector v = ucvector_init(*out, *outsize); + if(expected_size) { + /*reserve the memory to avoid intermediate reallocations*/ + ucvector_resize(&v, *outsize + expected_size); + v.size = *outsize; + } + error = lodepng_zlib_decompressv(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; + } + return error; +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER + +unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) { + size_t i; + unsigned error; + unsigned char* deflatedata = 0; + size_t deflatesize = 0; + + error = deflate(&deflatedata, &deflatesize, in, insize, settings); + + *out = NULL; + *outsize = 0; + if(!error) { + *outsize = deflatesize + 6; + *out = (unsigned char*)lodepng_malloc(*outsize); + if(!*out) error = 83; /*alloc fail*/ + } + + if(!error) { + unsigned ADLER32 = adler32(in, (unsigned)insize); + /*zlib data: 1 byte CMF (CM+CINFO), 1 byte FLG, deflate data, 4 byte ADLER32 checksum of the Decompressed data*/ + unsigned CMF = 120; /*0b01111000: CM 8, CINFO 7. With CINFO 7, any window size up to 32768 can be used.*/ + unsigned FLEVEL = 0; + unsigned FDICT = 0; + unsigned CMFFLG = 256 * CMF + FDICT * 32 + FLEVEL * 64; + unsigned FCHECK = 31 - CMFFLG % 31; + CMFFLG += FCHECK; + + (*out)[0] = (unsigned char)(CMFFLG >> 8); + (*out)[1] = (unsigned char)(CMFFLG & 255); + for(i = 0; i != deflatesize; ++i) (*out)[i + 2] = deflatedata[i]; + lodepng_set32bitInt(&(*out)[*outsize - 4], ADLER32); + } + + lodepng_free(deflatedata); + return error; +} + +/* compress using the default or custom zlib function */ +static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) { + if(settings->custom_zlib) { + unsigned error = settings->custom_zlib(out, outsize, in, insize, settings); + /*the custom zlib is allowed to have its own error codes, however, we translate it to code 111*/ + return error ? 111 : 0; + } else { + return lodepng_zlib_compress(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#else /*no LODEPNG_COMPILE_ZLIB*/ + +#ifdef LODEPNG_COMPILE_DECODER +static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size, + const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { + if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ + (void)expected_size; + return settings->custom_zlib(out, outsize, in, insize, settings); +} +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER +static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) { + if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ + return settings->custom_zlib(out, outsize, in, insize, settings); +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#endif /*LODEPNG_COMPILE_ZLIB*/ + +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_ENCODER + +/*this is a good tradeoff between speed and compression ratio*/ +#define DEFAULT_WINDOWSIZE 2048 + +void lodepng_compress_settings_init(LodePNGCompressSettings* settings) { + /*compress with dynamic huffman tree (not in the mathematical sense, just not the predefined one)*/ + settings->btype = 2; + settings->use_lz77 = 1; + settings->windowsize = DEFAULT_WINDOWSIZE; + settings->minmatch = 3; + settings->nicematch = 128; + settings->lazymatching = 1; + + settings->custom_zlib = 0; + settings->custom_deflate = 0; + settings->custom_context = 0; +} + +const LodePNGCompressSettings lodepng_default_compress_settings = {2, 1, DEFAULT_WINDOWSIZE, 3, 128, 1, 0, 0, 0}; + + +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) { + settings->ignore_adler32 = 0; + settings->ignore_nlen = 0; + settings->max_output_size = 0; + + settings->custom_zlib = 0; + settings->custom_inflate = 0; + settings->custom_context = 0; +} + +const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0, 0, 0}; + +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // End of Zlib related code. Begin of PNG related code. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_PNG + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / CRC32 / */ +/* ////////////////////////////////////////////////////////////////////////// */ + + +#ifndef LODEPNG_NO_COMPILE_CRC +/* CRC polynomial: 0xedb88320 */ +static unsigned lodepng_crc32_table[256] = { + 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u, + 249268274u, 2044508324u, 3772115230u, 2547177864u, 162941995u, 2125561021u, 3887607047u, 2428444049u, + 498536548u, 1789927666u, 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u, + 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, 4195302755u, 2366115317u, + 997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u, + 901097722u, 1119000684u, 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u, + 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, 3485111705u, 3099436303u, + 671266974u, 1594198024u, 3322730930u, 2970347812u, 795835527u, 1483230225u, 3244367275u, 3060149565u, + 1994146192u, 31158534u, 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u, + 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, 2439277719u, 3865271297u, + 1802195444u, 476864866u, 2238001368u, 4066508878u, 1812370925u, 453092731u, 2181625025u, 4111451223u, + 1706088902u, 314042704u, 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u, + 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u, + 1131014506u, 879679996u, 2909243462u, 3663771856u, 1141124467u, 855842277u, 2852801631u, 3708648649u, + 1342533948u, 654459306u, 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u, + 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, 3082640443u, 3233442989u, + 3988292384u, 2596254646u, 62317068u, 1957810842u, 3939845945u, 2647816111u, 81470997u, 1943803523u, + 3814918930u, 2489596804u, 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u, + 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, 426522225u, 1852507879u, + 4275313526u, 2312317920u, 282753626u, 1742555852u, 4189708143u, 2394877945u, 397917763u, 1622183637u, + 3604390888u, 2714866558u, 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u, + 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, 829329135u, 1181335161u, + 3412177804u, 3160834842u, 628085408u, 1382605366u, 3423369109u, 3138078467u, 570562233u, 1426400815u, + 3317316542u, 2998733608u, 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u, + 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, 1913087877u, 83908371u, + 2512341634u, 3803740692u, 2075208622u, 213261112u, 2463272603u, 3855990285u, 2094854071u, 198958881u, + 2262029012u, 4057260610u, 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u, + 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, 1634467795u, 376229701u, + 2685067896u, 3608007406u, 1308918612u, 956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u, + 2932959818u, 3654703836u, 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u, + 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, 1423857449u, 601450431u, + 3009837614u, 3294710456u, 1567103746u, 711928724u, 3020668471u, 3272380065u, 1510334235u, 755167117u +}; + +/*Return the CRC of the bytes buf[0..len-1].*/ +unsigned lodepng_crc32(const unsigned char* data, size_t length) { + unsigned r = 0xffffffffu; + size_t i; + for(i = 0; i < length; ++i) { + r = lodepng_crc32_table[(r ^ data[i]) & 0xffu] ^ (r >> 8u); + } + return r ^ 0xffffffffu; +} +#else /* !LODEPNG_NO_COMPILE_CRC */ +unsigned lodepng_crc32(const unsigned char* data, size_t length); +#endif /* !LODEPNG_NO_COMPILE_CRC */ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Reading and writing PNG color channel bits / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/* The color channel bits of less-than-8-bit pixels are read with the MSB of bytes first, +so LodePNGBitWriter and LodePNGBitReader can't be used for those. */ + +static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) { + unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1); + ++(*bitpointer); + return result; +} + +/* TODO: make this faster */ +static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) { + unsigned result = 0; + size_t i; + for(i = 0 ; i < nbits; ++i) { + result <<= 1u; + result |= (unsigned)readBitFromReversedStream(bitpointer, bitstream); + } + return result; +} + +static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) { + /*the current bit in bitstream may be 0 or 1 for this to work*/ + if(bit == 0) bitstream[(*bitpointer) >> 3u] &= (unsigned char)(~(1u << (7u - ((*bitpointer) & 7u)))); + else bitstream[(*bitpointer) >> 3u] |= (1u << (7u - ((*bitpointer) & 7u))); + ++(*bitpointer); +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG chunks / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +unsigned lodepng_chunk_length(const unsigned char* chunk) { + return lodepng_read32bitInt(&chunk[0]); +} + +void lodepng_chunk_type(char type[5], const unsigned char* chunk) { + unsigned i; + for(i = 0; i != 4; ++i) type[i] = (char)chunk[4 + i]; + type[4] = 0; /*null termination char*/ +} + +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type) { + if(lodepng_strlen(type) != 4) return 0; + return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]); +} + +unsigned char lodepng_chunk_ancillary(const unsigned char* chunk) { + return((chunk[4] & 32) != 0); +} + +unsigned char lodepng_chunk_private(const unsigned char* chunk) { + return((chunk[6] & 32) != 0); +} + +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk) { + return((chunk[7] & 32) != 0); +} + +unsigned char* lodepng_chunk_data(unsigned char* chunk) { + return &chunk[8]; +} + +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk) { + return &chunk[8]; +} + +unsigned lodepng_chunk_check_crc(const unsigned char* chunk) { + unsigned length = lodepng_chunk_length(chunk); + unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]); + /*the CRC is taken of the data and the 4 chunk type letters, not the length*/ + unsigned checksum = lodepng_crc32(&chunk[4], length + 4); + if(CRC != checksum) return 1; + else return 0; +} + +void lodepng_chunk_generate_crc(unsigned char* chunk) { + unsigned length = lodepng_chunk_length(chunk); + unsigned CRC = lodepng_crc32(&chunk[4], length + 4); + lodepng_set32bitInt(chunk + 8 + length, CRC); +} + +unsigned char* lodepng_chunk_next(unsigned char* chunk, unsigned char* end) { + if(chunk >= end || end - chunk < 12) return end; /*too small to contain a chunk*/ + if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47 + && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) { + /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */ + return chunk + 8; + } else { + size_t total_chunk_length; + unsigned char* result; + if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end; + result = chunk + total_chunk_length; + if(result < chunk) return end; /*pointer overflow*/ + return result; + } +} + +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end) { + if(chunk >= end || end - chunk < 12) return end; /*too small to contain a chunk*/ + if(chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47 + && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) { + /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */ + return chunk + 8; + } else { + size_t total_chunk_length; + const unsigned char* result; + if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end; + result = chunk + total_chunk_length; + if(result < chunk) return end; /*pointer overflow*/ + return result; + } +} + +unsigned char* lodepng_chunk_find(unsigned char* chunk, unsigned char* end, const char type[5]) { + for(;;) { + if(chunk >= end || end - chunk < 12) return 0; /* past file end: chunk + 12 > end */ + if(lodepng_chunk_type_equals(chunk, type)) return chunk; + chunk = lodepng_chunk_next(chunk, end); + } +} + +const unsigned char* lodepng_chunk_find_const(const unsigned char* chunk, const unsigned char* end, const char type[5]) { + for(;;) { + if(chunk >= end || end - chunk < 12) return 0; /* past file end: chunk + 12 > end */ + if(lodepng_chunk_type_equals(chunk, type)) return chunk; + chunk = lodepng_chunk_next_const(chunk, end); + } +} + +unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk) { + unsigned i; + size_t total_chunk_length, new_length; + unsigned char *chunk_start, *new_buffer; + + if(lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return 77; + if(lodepng_addofl(*outsize, total_chunk_length, &new_length)) return 77; + + new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); + if(!new_buffer) return 83; /*alloc fail*/ + (*out) = new_buffer; + (*outsize) = new_length; + chunk_start = &(*out)[new_length - total_chunk_length]; + + for(i = 0; i != total_chunk_length; ++i) chunk_start[i] = chunk[i]; + + return 0; +} + +/*Sets length and name and allocates the space for data and crc but does not +set data or crc yet. Returns the start of the chunk in chunk. The start of +the data is at chunk + 8. To finalize chunk, add the data, then use +lodepng_chunk_generate_crc */ +static unsigned lodepng_chunk_init(unsigned char** chunk, + ucvector* out, + unsigned length, const char* type) { + size_t new_length = out->size; + if(lodepng_addofl(new_length, length, &new_length)) return 77; + if(lodepng_addofl(new_length, 12, &new_length)) return 77; + if(!ucvector_resize(out, new_length)) return 83; /*alloc fail*/ + *chunk = out->data + new_length - length - 12u; + + /*1: length*/ + lodepng_set32bitInt(*chunk, length); + + /*2: chunk name (4 letters)*/ + lodepng_memcpy(*chunk + 4, type, 4); + + return 0; +} + +/* like lodepng_chunk_create but with custom allocsize */ +static unsigned lodepng_chunk_createv(ucvector* out, + unsigned length, const char* type, const unsigned char* data) { + unsigned char* chunk; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, length, type)); + + /*3: the data*/ + lodepng_memcpy(chunk + 8, data, length); + + /*4: CRC (of the chunkname characters and the data)*/ + lodepng_chunk_generate_crc(chunk); + + return 0; +} + +unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, + unsigned length, const char* type, const unsigned char* data) { + ucvector v = ucvector_init(*out, *outsize); + unsigned error = lodepng_chunk_createv(&v, length, type, data); + *out = v.data; + *outsize = v.size; + return error; +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Color types, channels, bits / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*checks if the colortype is valid and the bitdepth bd is allowed for this colortype. +Return value is a LodePNG error code.*/ +static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd) { + switch(colortype) { + case LCT_GREY: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; + case LCT_RGB: if(!( bd == 8 || bd == 16)) return 37; break; + case LCT_PALETTE: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; + case LCT_GREY_ALPHA: if(!( bd == 8 || bd == 16)) return 37; break; + case LCT_RGBA: if(!( bd == 8 || bd == 16)) return 37; break; + case LCT_MAX_OCTET_VALUE: return 31; /* invalid color type */ + default: return 31; /* invalid color type */ + } + return 0; /*allowed color type / bits combination*/ +} + +static unsigned getNumColorChannels(LodePNGColorType colortype) { + switch(colortype) { + case LCT_GREY: return 1; + case LCT_RGB: return 3; + case LCT_PALETTE: return 1; + case LCT_GREY_ALPHA: return 2; + case LCT_RGBA: return 4; + case LCT_MAX_OCTET_VALUE: return 0; /* invalid color type */ + default: return 0; /*invalid color type*/ + } +} + +static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth) { + /*bits per pixel is amount of channels * bits per channel*/ + return getNumColorChannels(colortype) * bitdepth; +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +void lodepng_color_mode_init(LodePNGColorMode* info) { + info->key_defined = 0; + info->key_r = info->key_g = info->key_b = 0; + info->colortype = LCT_RGBA; + info->bitdepth = 8; + info->palette = 0; + info->palettesize = 0; +} + +/*allocates palette memory if needed, and initializes all colors to black*/ +static void lodepng_color_mode_alloc_palette(LodePNGColorMode* info) { + size_t i; + /*if the palette is already allocated, it will have size 1024 so no reallocation needed in that case*/ + /*the palette must have room for up to 256 colors with 4 bytes each.*/ + if(!info->palette) info->palette = (unsigned char*)lodepng_malloc(1024); + if(!info->palette) return; /*alloc fail*/ + for(i = 0; i != 256; ++i) { + /*Initialize all unused colors with black, the value used for invalid palette indices. + This is an error according to the PNG spec, but common PNG decoders make it black instead. + That makes color conversion slightly faster due to no error handling needed.*/ + info->palette[i * 4 + 0] = 0; + info->palette[i * 4 + 1] = 0; + info->palette[i * 4 + 2] = 0; + info->palette[i * 4 + 3] = 255; + } +} + +void lodepng_color_mode_cleanup(LodePNGColorMode* info) { + lodepng_palette_clear(info); +} + +unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source) { + lodepng_color_mode_cleanup(dest); + lodepng_memcpy(dest, source, sizeof(LodePNGColorMode)); + if(source->palette) { + dest->palette = (unsigned char*)lodepng_malloc(1024); + if(!dest->palette && source->palettesize) return 83; /*alloc fail*/ + lodepng_memcpy(dest->palette, source->palette, source->palettesize * 4); + } + return 0; +} + +LodePNGColorMode lodepng_color_mode_make(LodePNGColorType colortype, unsigned bitdepth) { + LodePNGColorMode result; + lodepng_color_mode_init(&result); + result.colortype = colortype; + result.bitdepth = bitdepth; + return result; +} + +static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b) { + size_t i; + if(a->colortype != b->colortype) return 0; + if(a->bitdepth != b->bitdepth) return 0; + if(a->key_defined != b->key_defined) return 0; + if(a->key_defined) { + if(a->key_r != b->key_r) return 0; + if(a->key_g != b->key_g) return 0; + if(a->key_b != b->key_b) return 0; + } + if(a->palettesize != b->palettesize) return 0; + for(i = 0; i != a->palettesize * 4; ++i) { + if(a->palette[i] != b->palette[i]) return 0; + } + return 1; +} + +void lodepng_palette_clear(LodePNGColorMode* info) { + if(info->palette) lodepng_free(info->palette); + info->palette = 0; + info->palettesize = 0; +} + +unsigned lodepng_palette_add(LodePNGColorMode* info, + unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + if(!info->palette) /*allocate palette if empty*/ { + lodepng_color_mode_alloc_palette(info); + if(!info->palette) return 83; /*alloc fail*/ + } + if(info->palettesize >= 256) { + return 108; /*too many palette values*/ + } + info->palette[4 * info->palettesize + 0] = r; + info->palette[4 * info->palettesize + 1] = g; + info->palette[4 * info->palettesize + 2] = b; + info->palette[4 * info->palettesize + 3] = a; + ++info->palettesize; + return 0; +} + +/*calculate bits per pixel out of colortype and bitdepth*/ +unsigned lodepng_get_bpp(const LodePNGColorMode* info) { + return lodepng_get_bpp_lct(info->colortype, info->bitdepth); +} + +unsigned lodepng_get_channels(const LodePNGColorMode* info) { + return getNumColorChannels(info->colortype); +} + +unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info) { + return info->colortype == LCT_GREY || info->colortype == LCT_GREY_ALPHA; +} + +unsigned lodepng_is_alpha_type(const LodePNGColorMode* info) { + return (info->colortype & 4) != 0; /*4 or 6*/ +} + +unsigned lodepng_is_palette_type(const LodePNGColorMode* info) { + return info->colortype == LCT_PALETTE; +} + +unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info) { + size_t i; + for(i = 0; i != info->palettesize; ++i) { + if(info->palette[i * 4 + 3] < 255) return 1; + } + return 0; +} + +unsigned lodepng_can_have_alpha(const LodePNGColorMode* info) { + return info->key_defined + || lodepng_is_alpha_type(info) + || lodepng_has_palette_alpha(info); +} + +static size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { + size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth); + size_t n = (size_t)w * (size_t)h; + return ((n / 8u) * bpp) + ((n & 7u) * bpp + 7u) / 8u; +} + +size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) { + return lodepng_get_raw_size_lct(w, h, color->colortype, color->bitdepth); +} + + +#ifdef LODEPNG_COMPILE_PNG + +/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer, +and in addition has one extra byte per line: the filter byte. So this gives a larger +result than lodepng_get_raw_size. Set h to 1 to get the size of 1 row including filter byte. */ +static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, unsigned bpp) { + /* + 1 for the filter byte, and possibly plus padding bits per line. */ + /* Ignoring casts, the expression is equal to (w * bpp + 7) / 8 + 1, but avoids overflow of w * bpp */ + size_t line = ((size_t)(w / 8u) * bpp) + 1u + ((w & 7u) * bpp + 7u) / 8u; + return (size_t)h * line; +} + +#ifdef LODEPNG_COMPILE_DECODER +/*Safely checks whether size_t overflow can be caused due to amount of pixels. +This check is overcautious rather than precise. If this check indicates no overflow, +you can safely compute in a size_t (but not an unsigned): +-(size_t)w * (size_t)h * 8 +-amount of bytes in IDAT (including filter, padding and Adam7 bytes) +-amount of bytes in raw color model +Returns 1 if overflow possible, 0 if not. +*/ +static int lodepng_pixel_overflow(unsigned w, unsigned h, + const LodePNGColorMode* pngcolor, const LodePNGColorMode* rawcolor) { + size_t bpp = LODEPNG_MAX(lodepng_get_bpp(pngcolor), lodepng_get_bpp(rawcolor)); + size_t numpixels, total; + size_t line; /* bytes per line in worst case */ + + if(lodepng_mulofl((size_t)w, (size_t)h, &numpixels)) return 1; + if(lodepng_mulofl(numpixels, 8, &total)) return 1; /* bit pointer with 8-bit color, or 8 bytes per channel color */ + + /* Bytes per scanline with the expression "(w / 8u) * bpp) + ((w & 7u) * bpp + 7u) / 8u" */ + if(lodepng_mulofl((size_t)(w / 8u), bpp, &line)) return 1; + if(lodepng_addofl(line, ((w & 7u) * bpp + 7u) / 8u, &line)) return 1; + + if(lodepng_addofl(line, 5, &line)) return 1; /* 5 bytes overhead per line: 1 filterbyte, 4 for Adam7 worst case */ + if(lodepng_mulofl(line, h, &total)) return 1; /* Total bytes in worst case */ + + return 0; /* no overflow */ +} +#endif /*LODEPNG_COMPILE_DECODER*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + +static void LodePNGUnknownChunks_init(LodePNGInfo* info) { + unsigned i; + for(i = 0; i != 3; ++i) info->unknown_chunks_data[i] = 0; + for(i = 0; i != 3; ++i) info->unknown_chunks_size[i] = 0; +} + +static void LodePNGUnknownChunks_cleanup(LodePNGInfo* info) { + unsigned i; + for(i = 0; i != 3; ++i) lodepng_free(info->unknown_chunks_data[i]); +} + +static unsigned LodePNGUnknownChunks_copy(LodePNGInfo* dest, const LodePNGInfo* src) { + unsigned i; + + LodePNGUnknownChunks_cleanup(dest); + + for(i = 0; i != 3; ++i) { + size_t j; + dest->unknown_chunks_size[i] = src->unknown_chunks_size[i]; + dest->unknown_chunks_data[i] = (unsigned char*)lodepng_malloc(src->unknown_chunks_size[i]); + if(!dest->unknown_chunks_data[i] && dest->unknown_chunks_size[i]) return 83; /*alloc fail*/ + for(j = 0; j < src->unknown_chunks_size[i]; ++j) { + dest->unknown_chunks_data[i][j] = src->unknown_chunks_data[i][j]; + } + } + + return 0; +} + +/******************************************************************************/ + +static void LodePNGText_init(LodePNGInfo* info) { + info->text_num = 0; + info->text_keys = NULL; + info->text_strings = NULL; +} + +static void LodePNGText_cleanup(LodePNGInfo* info) { + size_t i; + for(i = 0; i != info->text_num; ++i) { + string_cleanup(&info->text_keys[i]); + string_cleanup(&info->text_strings[i]); + } + lodepng_free(info->text_keys); + lodepng_free(info->text_strings); +} + +static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) { + size_t i = 0; + dest->text_keys = NULL; + dest->text_strings = NULL; + dest->text_num = 0; + for(i = 0; i != source->text_num; ++i) { + CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i])); + } + return 0; +} + +static unsigned lodepng_add_text_sized(LodePNGInfo* info, const char* key, const char* str, size_t size) { + char** new_keys = (char**)(lodepng_realloc(info->text_keys, sizeof(char*) * (info->text_num + 1))); + char** new_strings = (char**)(lodepng_realloc(info->text_strings, sizeof(char*) * (info->text_num + 1))); + + if(new_keys) info->text_keys = new_keys; + if(new_strings) info->text_strings = new_strings; + + if(!new_keys || !new_strings) return 83; /*alloc fail*/ + + ++info->text_num; + info->text_keys[info->text_num - 1] = alloc_string(key); + info->text_strings[info->text_num - 1] = alloc_string_sized(str, size); + if(!info->text_keys[info->text_num - 1] || !info->text_strings[info->text_num - 1]) return 83; /*alloc fail*/ + + return 0; +} + +unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) { + return lodepng_add_text_sized(info, key, str, lodepng_strlen(str)); +} + +void lodepng_clear_text(LodePNGInfo* info) { + LodePNGText_cleanup(info); +} + +/******************************************************************************/ + +static void LodePNGIText_init(LodePNGInfo* info) { + info->itext_num = 0; + info->itext_keys = NULL; + info->itext_langtags = NULL; + info->itext_transkeys = NULL; + info->itext_strings = NULL; +} + +static void LodePNGIText_cleanup(LodePNGInfo* info) { + size_t i; + for(i = 0; i != info->itext_num; ++i) { + string_cleanup(&info->itext_keys[i]); + string_cleanup(&info->itext_langtags[i]); + string_cleanup(&info->itext_transkeys[i]); + string_cleanup(&info->itext_strings[i]); + } + lodepng_free(info->itext_keys); + lodepng_free(info->itext_langtags); + lodepng_free(info->itext_transkeys); + lodepng_free(info->itext_strings); +} + +static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) { + size_t i = 0; + dest->itext_keys = NULL; + dest->itext_langtags = NULL; + dest->itext_transkeys = NULL; + dest->itext_strings = NULL; + dest->itext_num = 0; + for(i = 0; i != source->itext_num; ++i) { + CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i], + source->itext_transkeys[i], source->itext_strings[i])); + } + return 0; +} + +void lodepng_clear_itext(LodePNGInfo* info) { + LodePNGIText_cleanup(info); +} + +static unsigned lodepng_add_itext_sized(LodePNGInfo* info, const char* key, const char* langtag, + const char* transkey, const char* str, size_t size) { + char** new_keys = (char**)(lodepng_realloc(info->itext_keys, sizeof(char*) * (info->itext_num + 1))); + char** new_langtags = (char**)(lodepng_realloc(info->itext_langtags, sizeof(char*) * (info->itext_num + 1))); + char** new_transkeys = (char**)(lodepng_realloc(info->itext_transkeys, sizeof(char*) * (info->itext_num + 1))); + char** new_strings = (char**)(lodepng_realloc(info->itext_strings, sizeof(char*) * (info->itext_num + 1))); + + if(new_keys) info->itext_keys = new_keys; + if(new_langtags) info->itext_langtags = new_langtags; + if(new_transkeys) info->itext_transkeys = new_transkeys; + if(new_strings) info->itext_strings = new_strings; + + if(!new_keys || !new_langtags || !new_transkeys || !new_strings) return 83; /*alloc fail*/ + + ++info->itext_num; + + info->itext_keys[info->itext_num - 1] = alloc_string(key); + info->itext_langtags[info->itext_num - 1] = alloc_string(langtag); + info->itext_transkeys[info->itext_num - 1] = alloc_string(transkey); + info->itext_strings[info->itext_num - 1] = alloc_string_sized(str, size); + + return 0; +} + +unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, + const char* transkey, const char* str) { + return lodepng_add_itext_sized(info, key, langtag, transkey, str, lodepng_strlen(str)); +} + +/* same as set but does not delete */ +static unsigned lodepng_assign_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size) { + if(profile_size == 0) return 100; /*invalid ICC profile size*/ + + info->iccp_name = alloc_string(name); + info->iccp_profile = (unsigned char*)lodepng_malloc(profile_size); + + if(!info->iccp_name || !info->iccp_profile) return 83; /*alloc fail*/ + + lodepng_memcpy(info->iccp_profile, profile, profile_size); + info->iccp_profile_size = profile_size; + + return 0; /*ok*/ +} + +unsigned lodepng_set_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size) { + if(info->iccp_name) lodepng_clear_icc(info); + info->iccp_defined = 1; + + return lodepng_assign_icc(info, name, profile, profile_size); +} + +void lodepng_clear_icc(LodePNGInfo* info) { + string_cleanup(&info->iccp_name); + lodepng_free(info->iccp_profile); + info->iccp_profile = NULL; + info->iccp_profile_size = 0; + info->iccp_defined = 0; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +void lodepng_info_init(LodePNGInfo* info) { + lodepng_color_mode_init(&info->color); + info->interlace_method = 0; + info->compression_method = 0; + info->filter_method = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + info->background_defined = 0; + info->background_r = info->background_g = info->background_b = 0; + + LodePNGText_init(info); + LodePNGIText_init(info); + + info->time_defined = 0; + info->phys_defined = 0; + + info->gama_defined = 0; + info->chrm_defined = 0; + info->srgb_defined = 0; + info->iccp_defined = 0; + info->iccp_name = NULL; + info->iccp_profile = NULL; + + LodePNGUnknownChunks_init(info); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +void lodepng_info_cleanup(LodePNGInfo* info) { + lodepng_color_mode_cleanup(&info->color); +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + LodePNGText_cleanup(info); + LodePNGIText_cleanup(info); + + lodepng_clear_icc(info); + + LodePNGUnknownChunks_cleanup(info); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) { + lodepng_info_cleanup(dest); + lodepng_memcpy(dest, source, sizeof(LodePNGInfo)); + lodepng_color_mode_init(&dest->color); + CERROR_TRY_RETURN(lodepng_color_mode_copy(&dest->color, &source->color)); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + CERROR_TRY_RETURN(LodePNGText_copy(dest, source)); + CERROR_TRY_RETURN(LodePNGIText_copy(dest, source)); + if(source->iccp_defined) { + CERROR_TRY_RETURN(lodepng_assign_icc(dest, source->iccp_name, source->iccp_profile, source->iccp_profile_size)); + } + + LodePNGUnknownChunks_init(dest); + CERROR_TRY_RETURN(LodePNGUnknownChunks_copy(dest, source)); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + return 0; +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +/*index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to*/ +static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in) { + unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/ + /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/ + unsigned p = index & m; + in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/ + in = in << (bits * (m - p)); + if(p == 0) out[index * bits / 8u] = in; + else out[index * bits / 8u] |= in; +} + +typedef struct ColorTree ColorTree; + +/* +One node of a color tree +This is the data structure used to count the number of unique colors and to get a palette +index for a color. It's like an octree, but because the alpha channel is used too, each +node has 16 instead of 8 children. +*/ +struct ColorTree { + ColorTree* children[16]; /*up to 16 pointers to ColorTree of next level*/ + int index; /*the payload. Only has a meaningful value if this is in the last level*/ +}; + +static void color_tree_init(ColorTree* tree) { + lodepng_memset(tree->children, 0, 16 * sizeof(*tree->children)); + tree->index = -1; +} + +static void color_tree_cleanup(ColorTree* tree) { + int i; + for(i = 0; i != 16; ++i) { + if(tree->children[i]) { + color_tree_cleanup(tree->children[i]); + lodepng_free(tree->children[i]); + } + } +} + +/*returns -1 if color not present, its index otherwise*/ +static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + int bit = 0; + for(bit = 0; bit < 8; ++bit) { + int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); + if(!tree->children[i]) return -1; + else tree = tree->children[i]; + } + return tree ? tree->index : -1; +} + +#ifdef LODEPNG_COMPILE_ENCODER +static int color_tree_has(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + return color_tree_get(tree, r, g, b, a) >= 0; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/*color is not allowed to already exist. +Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist") +Returns error code, or 0 if ok*/ +static unsigned color_tree_add(ColorTree* tree, + unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index) { + int bit; + for(bit = 0; bit < 8; ++bit) { + int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); + if(!tree->children[i]) { + tree->children[i] = (ColorTree*)lodepng_malloc(sizeof(ColorTree)); + if(!tree->children[i]) return 83; /*alloc fail*/ + color_tree_init(tree->children[i]); + } + tree = tree->children[i]; + } + tree->index = (int)index; + return 0; +} + +/*put a pixel, given its RGBA color, into image of any color type*/ +static unsigned rgba8ToPixel(unsigned char* out, size_t i, + const LodePNGColorMode* mode, ColorTree* tree /*for palette*/, + unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + if(mode->colortype == LCT_GREY) { + unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/ + if(mode->bitdepth == 8) out[i] = gray; + else if(mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = gray; + else { + /*take the most significant bits of gray*/ + gray = ((unsigned)gray >> (8u - mode->bitdepth)) & ((1u << mode->bitdepth) - 1u); + addColorBits(out, i, mode->bitdepth, gray); + } + } else if(mode->colortype == LCT_RGB) { + if(mode->bitdepth == 8) { + out[i * 3 + 0] = r; + out[i * 3 + 1] = g; + out[i * 3 + 2] = b; + } else { + out[i * 6 + 0] = out[i * 6 + 1] = r; + out[i * 6 + 2] = out[i * 6 + 3] = g; + out[i * 6 + 4] = out[i * 6 + 5] = b; + } + } else if(mode->colortype == LCT_PALETTE) { + int index = color_tree_get(tree, r, g, b, a); + if(index < 0) return 82; /*color not in palette*/ + if(mode->bitdepth == 8) out[i] = index; + else addColorBits(out, i, mode->bitdepth, (unsigned)index); + } else if(mode->colortype == LCT_GREY_ALPHA) { + unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/ + if(mode->bitdepth == 8) { + out[i * 2 + 0] = gray; + out[i * 2 + 1] = a; + } else if(mode->bitdepth == 16) { + out[i * 4 + 0] = out[i * 4 + 1] = gray; + out[i * 4 + 2] = out[i * 4 + 3] = a; + } + } else if(mode->colortype == LCT_RGBA) { + if(mode->bitdepth == 8) { + out[i * 4 + 0] = r; + out[i * 4 + 1] = g; + out[i * 4 + 2] = b; + out[i * 4 + 3] = a; + } else { + out[i * 8 + 0] = out[i * 8 + 1] = r; + out[i * 8 + 2] = out[i * 8 + 3] = g; + out[i * 8 + 4] = out[i * 8 + 5] = b; + out[i * 8 + 6] = out[i * 8 + 7] = a; + } + } + + return 0; /*no error*/ +} + +/*put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type*/ +static void rgba16ToPixel(unsigned char* out, size_t i, + const LodePNGColorMode* mode, + unsigned short r, unsigned short g, unsigned short b, unsigned short a) { + if(mode->colortype == LCT_GREY) { + unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/ + out[i * 2 + 0] = (gray >> 8) & 255; + out[i * 2 + 1] = gray & 255; + } else if(mode->colortype == LCT_RGB) { + out[i * 6 + 0] = (r >> 8) & 255; + out[i * 6 + 1] = r & 255; + out[i * 6 + 2] = (g >> 8) & 255; + out[i * 6 + 3] = g & 255; + out[i * 6 + 4] = (b >> 8) & 255; + out[i * 6 + 5] = b & 255; + } else if(mode->colortype == LCT_GREY_ALPHA) { + unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/ + out[i * 4 + 0] = (gray >> 8) & 255; + out[i * 4 + 1] = gray & 255; + out[i * 4 + 2] = (a >> 8) & 255; + out[i * 4 + 3] = a & 255; + } else if(mode->colortype == LCT_RGBA) { + out[i * 8 + 0] = (r >> 8) & 255; + out[i * 8 + 1] = r & 255; + out[i * 8 + 2] = (g >> 8) & 255; + out[i * 8 + 3] = g & 255; + out[i * 8 + 4] = (b >> 8) & 255; + out[i * 8 + 5] = b & 255; + out[i * 8 + 6] = (a >> 8) & 255; + out[i * 8 + 7] = a & 255; + } +} + +/*Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type.*/ +static void getPixelColorRGBA8(unsigned char* r, unsigned char* g, + unsigned char* b, unsigned char* a, + const unsigned char* in, size_t i, + const LodePNGColorMode* mode) { + if(mode->colortype == LCT_GREY) { + if(mode->bitdepth == 8) { + *r = *g = *b = in[i]; + if(mode->key_defined && *r == mode->key_r) *a = 0; + else *a = 255; + } else if(mode->bitdepth == 16) { + *r = *g = *b = in[i * 2 + 0]; + if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; + else *a = 255; + } else { + unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ + size_t j = i * mode->bitdepth; + unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); + *r = *g = *b = (value * 255) / highest; + if(mode->key_defined && value == mode->key_r) *a = 0; + else *a = 255; + } + } else if(mode->colortype == LCT_RGB) { + if(mode->bitdepth == 8) { + *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2]; + if(mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0; + else *a = 255; + } else { + *r = in[i * 6 + 0]; + *g = in[i * 6 + 2]; + *b = in[i * 6 + 4]; + if(mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; + else *a = 255; + } + } else if(mode->colortype == LCT_PALETTE) { + unsigned index; + if(mode->bitdepth == 8) index = in[i]; + else { + size_t j = i * mode->bitdepth; + index = readBitsFromReversedStream(&j, in, mode->bitdepth); + } + /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/ + *r = mode->palette[index * 4 + 0]; + *g = mode->palette[index * 4 + 1]; + *b = mode->palette[index * 4 + 2]; + *a = mode->palette[index * 4 + 3]; + } else if(mode->colortype == LCT_GREY_ALPHA) { + if(mode->bitdepth == 8) { + *r = *g = *b = in[i * 2 + 0]; + *a = in[i * 2 + 1]; + } else { + *r = *g = *b = in[i * 4 + 0]; + *a = in[i * 4 + 2]; + } + } else if(mode->colortype == LCT_RGBA) { + if(mode->bitdepth == 8) { + *r = in[i * 4 + 0]; + *g = in[i * 4 + 1]; + *b = in[i * 4 + 2]; + *a = in[i * 4 + 3]; + } else { + *r = in[i * 8 + 0]; + *g = in[i * 8 + 2]; + *b = in[i * 8 + 4]; + *a = in[i * 8 + 6]; + } + } +} + +/*Similar to getPixelColorRGBA8, but with all the for loops inside of the color +mode test cases, optimized to convert the colors much faster, when converting +to the common case of RGBA with 8 bit per channel. buffer must be RGBA with +enough memory.*/ +static void getPixelColorsRGBA8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels, + const unsigned char* LODEPNG_RESTRICT in, + const LodePNGColorMode* mode) { + unsigned num_channels = 4; + size_t i; + if(mode->colortype == LCT_GREY) { + if(mode->bitdepth == 8) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = buffer[1] = buffer[2] = in[i]; + buffer[3] = 255; + } + if(mode->key_defined) { + buffer -= numpixels * num_channels; + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + if(buffer[0] == mode->key_r) buffer[3] = 0; + } + } + } else if(mode->bitdepth == 16) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = buffer[1] = buffer[2] = in[i * 2]; + buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255; + } + } else { + unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ + size_t j = 0; + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); + buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; + buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255; + } + } + } else if(mode->colortype == LCT_RGB) { + if(mode->bitdepth == 8) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + lodepng_memcpy(buffer, &in[i * 3], 3); + buffer[3] = 255; + } + if(mode->key_defined) { + buffer -= numpixels * num_channels; + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + if(buffer[0] == mode->key_r && buffer[1]== mode->key_g && buffer[2] == mode->key_b) buffer[3] = 0; + } + } + } else { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = in[i * 6 + 0]; + buffer[1] = in[i * 6 + 2]; + buffer[2] = in[i * 6 + 4]; + buffer[3] = mode->key_defined + && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255; + } + } + } else if(mode->colortype == LCT_PALETTE) { + if(mode->bitdepth == 8) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + unsigned index = in[i]; + /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/ + lodepng_memcpy(buffer, &mode->palette[index * 4], 4); + } + } else { + size_t j = 0; + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth); + /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/ + lodepng_memcpy(buffer, &mode->palette[index * 4], 4); + } + } + } else if(mode->colortype == LCT_GREY_ALPHA) { + if(mode->bitdepth == 8) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; + buffer[3] = in[i * 2 + 1]; + } + } else { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; + buffer[3] = in[i * 4 + 2]; + } + } + } else if(mode->colortype == LCT_RGBA) { + if(mode->bitdepth == 8) { + lodepng_memcpy(buffer, in, numpixels * 4); + } else { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = in[i * 8 + 0]; + buffer[1] = in[i * 8 + 2]; + buffer[2] = in[i * 8 + 4]; + buffer[3] = in[i * 8 + 6]; + } + } + } +} + +/*Similar to getPixelColorsRGBA8, but with 3-channel RGB output.*/ +static void getPixelColorsRGB8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels, + const unsigned char* LODEPNG_RESTRICT in, + const LodePNGColorMode* mode) { + const unsigned num_channels = 3; + size_t i; + if(mode->colortype == LCT_GREY) { + if(mode->bitdepth == 8) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = buffer[1] = buffer[2] = in[i]; + } + } else if(mode->bitdepth == 16) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = buffer[1] = buffer[2] = in[i * 2]; + } + } else { + unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ + size_t j = 0; + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); + buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; + } + } + } else if(mode->colortype == LCT_RGB) { + if(mode->bitdepth == 8) { + lodepng_memcpy(buffer, in, numpixels * 3); + } else { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = in[i * 6 + 0]; + buffer[1] = in[i * 6 + 2]; + buffer[2] = in[i * 6 + 4]; + } + } + } else if(mode->colortype == LCT_PALETTE) { + if(mode->bitdepth == 8) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + unsigned index = in[i]; + /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/ + lodepng_memcpy(buffer, &mode->palette[index * 4], 3); + } + } else { + size_t j = 0; + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth); + /*out of bounds of palette not checked: see lodepng_color_mode_alloc_palette.*/ + lodepng_memcpy(buffer, &mode->palette[index * 4], 3); + } + } + } else if(mode->colortype == LCT_GREY_ALPHA) { + if(mode->bitdepth == 8) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; + } + } else { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; + } + } + } else if(mode->colortype == LCT_RGBA) { + if(mode->bitdepth == 8) { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + lodepng_memcpy(buffer, &in[i * 4], 3); + } + } else { + for(i = 0; i != numpixels; ++i, buffer += num_channels) { + buffer[0] = in[i * 8 + 0]; + buffer[1] = in[i * 8 + 2]; + buffer[2] = in[i * 8 + 4]; + } + } + } +} + +/*Get RGBA16 color of pixel with index i (y * width + x) from the raw image with +given color type, but the given color type must be 16-bit itself.*/ +static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a, + const unsigned char* in, size_t i, const LodePNGColorMode* mode) { + if(mode->colortype == LCT_GREY) { + *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1]; + if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; + else *a = 65535; + } else if(mode->colortype == LCT_RGB) { + *r = 256u * in[i * 6 + 0] + in[i * 6 + 1]; + *g = 256u * in[i * 6 + 2] + in[i * 6 + 3]; + *b = 256u * in[i * 6 + 4] + in[i * 6 + 5]; + if(mode->key_defined + && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; + else *a = 65535; + } else if(mode->colortype == LCT_GREY_ALPHA) { + *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1]; + *a = 256u * in[i * 4 + 2] + in[i * 4 + 3]; + } else if(mode->colortype == LCT_RGBA) { + *r = 256u * in[i * 8 + 0] + in[i * 8 + 1]; + *g = 256u * in[i * 8 + 2] + in[i * 8 + 3]; + *b = 256u * in[i * 8 + 4] + in[i * 8 + 5]; + *a = 256u * in[i * 8 + 6] + in[i * 8 + 7]; + } +} + +unsigned lodepng_convert(unsigned char* out, const unsigned char* in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, + unsigned w, unsigned h) { + size_t i; + ColorTree tree; + size_t numpixels = (size_t)w * (size_t)h; + unsigned error = 0; + + if(mode_in->colortype == LCT_PALETTE && !mode_in->palette) { + return 107; /* error: must provide palette if input mode is palette */ + } + + if(lodepng_color_mode_equal(mode_out, mode_in)) { + size_t numbytes = lodepng_get_raw_size(w, h, mode_in); + lodepng_memcpy(out, in, numbytes); + return 0; + } + + if(mode_out->colortype == LCT_PALETTE) { + size_t palettesize = mode_out->palettesize; + const unsigned char* palette = mode_out->palette; + size_t palsize = (size_t)1u << mode_out->bitdepth; + /*if the user specified output palette but did not give the values, assume + they want the values of the input color type (assuming that one is palette). + Note that we never create a new palette ourselves.*/ + if(palettesize == 0) { + palettesize = mode_in->palettesize; + palette = mode_in->palette; + /*if the input was also palette with same bitdepth, then the color types are also + equal, so copy literally. This to preserve the exact indices that were in the PNG + even in case there are duplicate colors in the palette.*/ + if(mode_in->colortype == LCT_PALETTE && mode_in->bitdepth == mode_out->bitdepth) { + size_t numbytes = lodepng_get_raw_size(w, h, mode_in); + lodepng_memcpy(out, in, numbytes); + return 0; + } + } + if(palettesize < palsize) palsize = palettesize; + color_tree_init(&tree); + for(i = 0; i != palsize; ++i) { + const unsigned char* p = &palette[i * 4]; + error = color_tree_add(&tree, p[0], p[1], p[2], p[3], (unsigned)i); + if(error) break; + } + } + + if(!error) { + if(mode_in->bitdepth == 16 && mode_out->bitdepth == 16) { + for(i = 0; i != numpixels; ++i) { + unsigned short r = 0, g = 0, b = 0, a = 0; + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); + rgba16ToPixel(out, i, mode_out, r, g, b, a); + } + } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) { + getPixelColorsRGBA8(out, numpixels, in, mode_in); + } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) { + getPixelColorsRGB8(out, numpixels, in, mode_in); + } else { + unsigned char r = 0, g = 0, b = 0, a = 0; + for(i = 0; i != numpixels; ++i) { + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); + error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a); + if(error) break; + } + } + } + + if(mode_out->colortype == LCT_PALETTE) { + color_tree_cleanup(&tree); + } + + return error; +} + + +/* Converts a single rgb color without alpha from one type to another, color bits truncated to +their bitdepth. In case of single channel (gray or palette), only the r channel is used. Slow +function, do not use to process all pixels of an image. Alpha channel not supported on purpose: +this is for bKGD, supporting alpha may prevent it from finding a color in the palette, from the +specification it looks like bKGD should ignore the alpha values of the palette since it can use +any palette index but doesn't have an alpha channel. Idem with ignoring color key. */ +static unsigned lodepng_convert_rgb( + unsigned* r_out, unsigned* g_out, unsigned* b_out, + unsigned r_in, unsigned g_in, unsigned b_in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in) { + unsigned r = 0, g = 0, b = 0; + unsigned mul = 65535 / ((1u << mode_in->bitdepth) - 1u); /*65535, 21845, 4369, 257, 1*/ + unsigned shift = 16 - mode_out->bitdepth; + + if(mode_in->colortype == LCT_GREY || mode_in->colortype == LCT_GREY_ALPHA) { + r = g = b = r_in * mul; + } else if(mode_in->colortype == LCT_RGB || mode_in->colortype == LCT_RGBA) { + r = r_in * mul; + g = g_in * mul; + b = b_in * mul; + } else if(mode_in->colortype == LCT_PALETTE) { + if(r_in >= mode_in->palettesize) return 82; + r = mode_in->palette[r_in * 4 + 0] * 257u; + g = mode_in->palette[r_in * 4 + 1] * 257u; + b = mode_in->palette[r_in * 4 + 2] * 257u; + } else { + return 31; + } + + /* now convert to output format */ + if(mode_out->colortype == LCT_GREY || mode_out->colortype == LCT_GREY_ALPHA) { + *r_out = r >> shift ; + } else if(mode_out->colortype == LCT_RGB || mode_out->colortype == LCT_RGBA) { + *r_out = r >> shift ; + *g_out = g >> shift ; + *b_out = b >> shift ; + } else if(mode_out->colortype == LCT_PALETTE) { + unsigned i; + /* a 16-bit color cannot be in the palette */ + if((r >> 8) != (r & 255) || (g >> 8) != (g & 255) || (b >> 8) != (b & 255)) return 82; + for(i = 0; i < mode_out->palettesize; i++) { + unsigned j = i * 4; + if((r >> 8) == mode_out->palette[j + 0] && (g >> 8) == mode_out->palette[j + 1] && + (b >> 8) == mode_out->palette[j + 2]) { + *r_out = i; + return 0; + } + } + return 82; + } else { + return 31; + } + + return 0; +} + +#ifdef LODEPNG_COMPILE_ENCODER + +void lodepng_color_stats_init(LodePNGColorStats* stats) { + /*stats*/ + stats->colored = 0; + stats->key = 0; + stats->key_r = stats->key_g = stats->key_b = 0; + stats->alpha = 0; + stats->numcolors = 0; + stats->bits = 1; + stats->numpixels = 0; + /*settings*/ + stats->allow_palette = 1; + stats->allow_greyscale = 1; +} + +/*function used for debug purposes with C++*/ +/*void printColorStats(LodePNGColorStats* p) { + std::cout << "colored: " << (int)p->colored << ", "; + std::cout << "key: " << (int)p->key << ", "; + std::cout << "key_r: " << (int)p->key_r << ", "; + std::cout << "key_g: " << (int)p->key_g << ", "; + std::cout << "key_b: " << (int)p->key_b << ", "; + std::cout << "alpha: " << (int)p->alpha << ", "; + std::cout << "numcolors: " << (int)p->numcolors << ", "; + std::cout << "bits: " << (int)p->bits << std::endl; +}*/ + +/*Returns how many bits needed to represent given value (max 8 bit)*/ +static unsigned getValueRequiredBits(unsigned char value) { + if(value == 0 || value == 255) return 1; + /*The scaling of 2-bit and 4-bit values uses multiples of 85 and 17*/ + if(value % 17 == 0) return value % 85 == 0 ? 2 : 4; + return 8; +} + +/*stats must already have been inited. */ +unsigned lodepng_compute_color_stats(LodePNGColorStats* stats, + const unsigned char* in, unsigned w, unsigned h, + const LodePNGColorMode* mode_in) { + size_t i; + ColorTree tree; + size_t numpixels = (size_t)w * (size_t)h; + unsigned error = 0; + + /* mark things as done already if it would be impossible to have a more expensive case */ + unsigned colored_done = lodepng_is_greyscale_type(mode_in) ? 1 : 0; + unsigned alpha_done = lodepng_can_have_alpha(mode_in) ? 0 : 1; + unsigned numcolors_done = 0; + unsigned bpp = lodepng_get_bpp(mode_in); + unsigned bits_done = (stats->bits == 1 && bpp == 1) ? 1 : 0; + unsigned sixteen = 0; /* whether the input image is 16 bit */ + unsigned maxnumcolors = 257; + if(bpp <= 8) maxnumcolors = LODEPNG_MIN(257, stats->numcolors + (1u << bpp)); + + stats->numpixels += numpixels; + + /*if palette not allowed, no need to compute numcolors*/ + if(!stats->allow_palette) numcolors_done = 1; + + color_tree_init(&tree); + + /*If the stats was already filled in from previous data, fill its palette in tree + and mark things as done already if we know they are the most expensive case already*/ + if(stats->alpha) alpha_done = 1; + if(stats->colored) colored_done = 1; + if(stats->bits == 16) numcolors_done = 1; + if(stats->bits >= bpp) bits_done = 1; + if(stats->numcolors >= maxnumcolors) numcolors_done = 1; + + if(!numcolors_done) { + for(i = 0; i < stats->numcolors; i++) { + const unsigned char* color = &stats->palette[i * 4]; + error = color_tree_add(&tree, color[0], color[1], color[2], color[3], i); + if(error) goto cleanup; + } + } + + /*Check if the 16-bit input is truly 16-bit*/ + if(mode_in->bitdepth == 16 && !sixteen) { + unsigned short r = 0, g = 0, b = 0, a = 0; + for(i = 0; i != numpixels; ++i) { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); + if((r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) || + (b & 255) != ((b >> 8) & 255) || (a & 255) != ((a >> 8) & 255)) /*first and second byte differ*/ { + stats->bits = 16; + sixteen = 1; + bits_done = 1; + numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/ + break; + } + } + } + + if(sixteen) { + unsigned short r = 0, g = 0, b = 0, a = 0; + + for(i = 0; i != numpixels; ++i) { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); + + if(!colored_done && (r != g || r != b)) { + stats->colored = 1; + colored_done = 1; + } + + if(!alpha_done) { + unsigned matchkey = (r == stats->key_r && g == stats->key_g && b == stats->key_b); + if(a != 65535 && (a != 0 || (stats->key && !matchkey))) { + stats->alpha = 1; + stats->key = 0; + alpha_done = 1; + } else if(a == 0 && !stats->alpha && !stats->key) { + stats->key = 1; + stats->key_r = r; + stats->key_g = g; + stats->key_b = b; + } else if(a == 65535 && stats->key && matchkey) { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + stats->alpha = 1; + stats->key = 0; + alpha_done = 1; + } + } + if(alpha_done && numcolors_done && colored_done && bits_done) break; + } + + if(stats->key && !stats->alpha) { + for(i = 0; i != numpixels; ++i) { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); + if(a != 0 && r == stats->key_r && g == stats->key_g && b == stats->key_b) { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + stats->alpha = 1; + stats->key = 0; + alpha_done = 1; + } + } + } + } else /* < 16-bit */ { + unsigned char r = 0, g = 0, b = 0, a = 0; + for(i = 0; i != numpixels; ++i) { + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); + + if(!bits_done && stats->bits < 8) { + /*only r is checked, < 8 bits is only relevant for grayscale*/ + unsigned bits = getValueRequiredBits(r); + if(bits > stats->bits) stats->bits = bits; + } + bits_done = (stats->bits >= bpp); + + if(!colored_done && (r != g || r != b)) { + stats->colored = 1; + colored_done = 1; + if(stats->bits < 8) stats->bits = 8; /*PNG has no colored modes with less than 8-bit per channel*/ + } + + if(!alpha_done) { + unsigned matchkey = (r == stats->key_r && g == stats->key_g && b == stats->key_b); + if(a != 255 && (a != 0 || (stats->key && !matchkey))) { + stats->alpha = 1; + stats->key = 0; + alpha_done = 1; + if(stats->bits < 8) stats->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } else if(a == 0 && !stats->alpha && !stats->key) { + stats->key = 1; + stats->key_r = r; + stats->key_g = g; + stats->key_b = b; + } else if(a == 255 && stats->key && matchkey) { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + stats->alpha = 1; + stats->key = 0; + alpha_done = 1; + if(stats->bits < 8) stats->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + } + + if(!numcolors_done) { + if(!color_tree_has(&tree, r, g, b, a)) { + error = color_tree_add(&tree, r, g, b, a, stats->numcolors); + if(error) goto cleanup; + if(stats->numcolors < 256) { + unsigned char* p = stats->palette; + unsigned n = stats->numcolors; + p[n * 4 + 0] = r; + p[n * 4 + 1] = g; + p[n * 4 + 2] = b; + p[n * 4 + 3] = a; + } + ++stats->numcolors; + numcolors_done = stats->numcolors >= maxnumcolors; + } + } + + if(alpha_done && numcolors_done && colored_done && bits_done) break; + } + + if(stats->key && !stats->alpha) { + for(i = 0; i != numpixels; ++i) { + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); + if(a != 0 && r == stats->key_r && g == stats->key_g && b == stats->key_b) { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + stats->alpha = 1; + stats->key = 0; + alpha_done = 1; + if(stats->bits < 8) stats->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + } + } + + /*make the stats's key always 16-bit for consistency - repeat each byte twice*/ + stats->key_r += (stats->key_r << 8); + stats->key_g += (stats->key_g << 8); + stats->key_b += (stats->key_b << 8); + } + +cleanup: + color_tree_cleanup(&tree); + return error; +} + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*Adds a single color to the color stats. The stats must already have been inited. The color must be given as 16-bit +(with 2 bytes repeating for 8-bit and 65535 for opaque alpha channel). This function is expensive, do not call it for +all pixels of an image but only for a few additional values. */ +static unsigned lodepng_color_stats_add(LodePNGColorStats* stats, + unsigned r, unsigned g, unsigned b, unsigned a) { + unsigned error = 0; + unsigned char image[8]; + LodePNGColorMode mode; + lodepng_color_mode_init(&mode); + image[0] = r >> 8; image[1] = r; image[2] = g >> 8; image[3] = g; + image[4] = b >> 8; image[5] = b; image[6] = a >> 8; image[7] = a; + mode.bitdepth = 16; + mode.colortype = LCT_RGBA; + error = lodepng_compute_color_stats(stats, image, 1, 1, &mode); + lodepng_color_mode_cleanup(&mode); + return error; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/*Computes a minimal PNG color model that can contain all colors as indicated by the stats. +The stats should be computed with lodepng_compute_color_stats. +mode_in is raw color profile of the image the stats were computed on, to copy palette order from when relevant. +Minimal PNG color model means the color type and bit depth that gives smallest amount of bits in the output image, +e.g. gray if only grayscale pixels, palette if less than 256 colors, color key if only single transparent color, ... +This is used if auto_convert is enabled (it is by default). +*/ +static unsigned auto_choose_color(LodePNGColorMode* mode_out, + const LodePNGColorMode* mode_in, + const LodePNGColorStats* stats) { + unsigned error = 0; + unsigned palettebits; + size_t i, n; + size_t numpixels = stats->numpixels; + unsigned palette_ok, gray_ok; + + unsigned alpha = stats->alpha; + unsigned key = stats->key; + unsigned bits = stats->bits; + + mode_out->key_defined = 0; + + if(key && numpixels <= 16) { + alpha = 1; /*too few pixels to justify tRNS chunk overhead*/ + key = 0; + if(bits < 8) bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + + gray_ok = !stats->colored; + if(!stats->allow_greyscale) gray_ok = 0; + if(!gray_ok && bits < 8) bits = 8; + + n = stats->numcolors; + palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8)); + palette_ok = n <= 256 && bits <= 8 && n != 0; /*n==0 means likely numcolors wasn't computed*/ + if(numpixels < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/ + if(gray_ok && !alpha && bits <= palettebits) palette_ok = 0; /*gray is less overhead*/ + if(!stats->allow_palette) palette_ok = 0; + + if(palette_ok) { + const unsigned char* p = stats->palette; + lodepng_palette_clear(mode_out); /*remove potential earlier palette*/ + for(i = 0; i != stats->numcolors; ++i) { + error = lodepng_palette_add(mode_out, p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3]); + if(error) break; + } + + mode_out->colortype = LCT_PALETTE; + mode_out->bitdepth = palettebits; + + if(mode_in->colortype == LCT_PALETTE && mode_in->palettesize >= mode_out->palettesize + && mode_in->bitdepth == mode_out->bitdepth) { + /*If input should have same palette colors, keep original to preserve its order and prevent conversion*/ + lodepng_color_mode_cleanup(mode_out); + lodepng_color_mode_copy(mode_out, mode_in); + } + } else /*8-bit or 16-bit per channel*/ { + mode_out->bitdepth = bits; + mode_out->colortype = alpha ? (gray_ok ? LCT_GREY_ALPHA : LCT_RGBA) + : (gray_ok ? LCT_GREY : LCT_RGB); + if(key) { + unsigned mask = (1u << mode_out->bitdepth) - 1u; /*stats always uses 16-bit, mask converts it*/ + mode_out->key_r = stats->key_r & mask; + mode_out->key_g = stats->key_g & mask; + mode_out->key_b = stats->key_b & mask; + mode_out->key_defined = 1; + } + } + + return error; +} + +#endif /* #ifdef LODEPNG_COMPILE_ENCODER */ + +/* +Paeth predictor, used by PNG filter type 4 +The parameters are of type short, but should come from unsigned chars, the shorts +are only needed to make the paeth calculation correct. +*/ +static unsigned char paethPredictor(short a, short b, short c) { + short pa = LODEPNG_ABS(b - c); + short pb = LODEPNG_ABS(a - c); + short pc = LODEPNG_ABS(a + b - c - c); + /* return input value associated with smallest of pa, pb, pc (with certain priority if equal) */ + if(pb < pa) { a = b; pa = pb; } + return (pc < pa) ? c : a; +} + +/*shared values used by multiple Adam7 related functions*/ + +static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/ +static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/ +static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/ +static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/ + +/* +Outputs various dimensions and positions in the image related to the Adam7 reduced images. +passw: output containing the width of the 7 passes +passh: output containing the height of the 7 passes +filter_passstart: output containing the index of the start and end of each + reduced image with filter bytes +padded_passstart output containing the index of the start and end of each + reduced image when without filter bytes but with padded scanlines +passstart: output containing the index of the start and end of each reduced + image without padding between scanlines, but still padding between the images +w, h: width and height of non-interlaced image +bpp: bits per pixel +"padded" is only relevant if bpp is less than 8 and a scanline or image does not + end at a full byte +*/ +static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8], + size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp) { + /*the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass*/ + unsigned i; + + /*calculate width and height in pixels of each pass*/ + for(i = 0; i != 7; ++i) { + passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i]; + passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i]; + if(passw[i] == 0) passh[i] = 0; + if(passh[i] == 0) passw[i] = 0; + } + + filter_passstart[0] = padded_passstart[0] = passstart[0] = 0; + for(i = 0; i != 7; ++i) { + /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/ + filter_passstart[i + 1] = filter_passstart[i] + + ((passw[i] && passh[i]) ? passh[i] * (1u + (passw[i] * bpp + 7u) / 8u) : 0); + /*bits padded if needed to fill full byte at end of each scanline*/ + padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7u) / 8u); + /*only padded at end of reduced image*/ + passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7u) / 8u; + } +} + +#ifdef LODEPNG_COMPILE_DECODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG Decoder / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*read the information from the header and store it in the LodePNGInfo. return value is error*/ +unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, + const unsigned char* in, size_t insize) { + unsigned width, height; + LodePNGInfo* info = &state->info_png; + if(insize == 0 || in == 0) { + CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/ + } + if(insize < 33) { + CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/ + } + + /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/ + /* TODO: remove this. One should use a new LodePNGState for new sessions */ + lodepng_info_cleanup(info); + lodepng_info_init(info); + + if(in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 + || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) { + CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/ + } + if(lodepng_chunk_length(in + 8) != 13) { + CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/ + } + if(!lodepng_chunk_type_equals(in + 8, "IHDR")) { + CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/ + } + + /*read the values given in the header*/ + width = lodepng_read32bitInt(&in[16]); + height = lodepng_read32bitInt(&in[20]); + /*TODO: remove the undocumented feature that allows to give null pointers to width or height*/ + if(w) *w = width; + if(h) *h = height; + info->color.bitdepth = in[24]; + info->color.colortype = (LodePNGColorType)in[25]; + info->compression_method = in[26]; + info->filter_method = in[27]; + info->interlace_method = in[28]; + + /*errors returned only after the parsing so other values are still output*/ + + /*error: invalid image size*/ + if(width == 0 || height == 0) CERROR_RETURN_ERROR(state->error, 93); + /*error: invalid colortype or bitdepth combination*/ + state->error = checkColorValidity(info->color.colortype, info->color.bitdepth); + if(state->error) return state->error; + /*error: only compression method 0 is allowed in the specification*/ + if(info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32); + /*error: only filter method 0 is allowed in the specification*/ + if(info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33); + /*error: only interlace methods 0 and 1 exist in the specification*/ + if(info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34); + + if(!state->decoder.ignore_crc) { + unsigned CRC = lodepng_read32bitInt(&in[29]); + unsigned checksum = lodepng_crc32(&in[12], 17); + if(CRC != checksum) { + CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/ + } + } + + return state->error; +} + +static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, + size_t bytewidth, unsigned char filterType, size_t length) { + /* + For PNG filter method 0 + unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, + the filter works byte per byte (bytewidth = 1) + precon is the previous unfiltered scanline, recon the result, scanline the current one + the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead + recon and scanline MAY be the same memory address! precon must be disjoint. + */ + + size_t i; + switch(filterType) { + case 0: + for(i = 0; i != length; ++i) recon[i] = scanline[i]; + break; + case 1: + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth]; + break; + case 2: + if(precon) { + for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i]; + } else { + for(i = 0; i != length; ++i) recon[i] = scanline[i]; + } + break; + case 3: + if(precon) { + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1u); + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1u); + } else { + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1u); + } + break; + case 4: + if(precon) { + for(i = 0; i != bytewidth; ++i) { + recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/ + } + + /* Unroll independent paths of the paeth predictor. A 6x and 8x version would also be possible but that + adds too much code. Whether this actually speeds anything up at all depends on compiler and settings. */ + if(bytewidth >= 4) { + for(; i + 3 < length; i += 4) { + size_t j = i - bytewidth; + unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2], s3 = scanline[i + 3]; + unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2], r3 = recon[j + 3]; + unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2], p3 = precon[i + 3]; + unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2], q3 = precon[j + 3]; + recon[i + 0] = s0 + paethPredictor(r0, p0, q0); + recon[i + 1] = s1 + paethPredictor(r1, p1, q1); + recon[i + 2] = s2 + paethPredictor(r2, p2, q2); + recon[i + 3] = s3 + paethPredictor(r3, p3, q3); + } + } else if(bytewidth >= 3) { + for(; i + 2 < length; i += 3) { + size_t j = i - bytewidth; + unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2]; + unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2]; + unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2]; + unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2]; + recon[i + 0] = s0 + paethPredictor(r0, p0, q0); + recon[i + 1] = s1 + paethPredictor(r1, p1, q1); + recon[i + 2] = s2 + paethPredictor(r2, p2, q2); + } + } else if(bytewidth >= 2) { + for(; i + 1 < length; i += 2) { + size_t j = i - bytewidth; + unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1]; + unsigned char r0 = recon[j + 0], r1 = recon[j + 1]; + unsigned char p0 = precon[i + 0], p1 = precon[i + 1]; + unsigned char q0 = precon[j + 0], q1 = precon[j + 1]; + recon[i + 0] = s0 + paethPredictor(r0, p0, q0); + recon[i + 1] = s1 + paethPredictor(r1, p1, q1); + } + } + + for(; i != length; ++i) { + recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); + } + } else { + for(i = 0; i != bytewidth; ++i) { + recon[i] = scanline[i]; + } + for(i = bytewidth; i < length; ++i) { + /*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/ + recon[i] = (scanline[i] + recon[i - bytewidth]); + } + } + break; + default: return 36; /*error: invalid filter type given*/ + } + return 0; +} + +static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) { + /* + For PNG filter method 0 + this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times) + out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline + w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel + in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes) + */ + + unsigned y; + unsigned char* prevline = 0; + + /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ + size_t bytewidth = (bpp + 7u) / 8u; + /*the width of a scanline in bytes, not including the filter type*/ + size_t linebytes = lodepng_get_raw_size_idat(w, 1, bpp) - 1u; + + for(y = 0; y < h; ++y) { + size_t outindex = linebytes * y; + size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + unsigned char filterType = in[inindex]; + + CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes)); + + prevline = &out[outindex]; + } + + return 0; +} + +/* +in: Adam7 interlaced image, with no padding bits between scanlines, but between + reduced images so that each reduced image starts at a byte. +out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h +bpp: bits per pixel +out has the following size in bits: w * h * bpp. +in is possibly bigger due to padding bits between reduced images. +out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation +(because that's likely a little bit faster) +NOTE: comments about padding bits are only relevant if bpp < 8 +*/ +static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) { + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + if(bpp >= 8) { + for(i = 0; i != 7; ++i) { + unsigned x, y, b; + size_t bytewidth = bpp / 8u; + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) { + size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth; + size_t pixeloutstart = ((ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * (size_t)w + + ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bytewidth; + for(b = 0; b < bytewidth; ++b) { + out[pixeloutstart + b] = in[pixelinstart + b]; + } + } + } + } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ { + for(i = 0; i != 7; ++i) { + unsigned x, y, b; + unsigned ilinebits = bpp * passw[i]; + unsigned olinebits = bpp * w; + size_t obp, ibp; /*bit pointers (for out and in buffer)*/ + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) { + ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); + obp = (ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bpp; + for(b = 0; b < bpp; ++b) { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + } + } + } +} + +static void removePaddingBits(unsigned char* out, const unsigned char* in, + size_t olinebits, size_t ilinebits, unsigned h) { + /* + After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need + to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers + for the Adam7 code, the color convert code and the output to the user. + in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must + have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits + also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7 + only useful if (ilinebits - olinebits) is a value in the range 1..7 + */ + unsigned y; + size_t diff = ilinebits - olinebits; + size_t ibp = 0, obp = 0; /*input and output bit pointers*/ + for(y = 0; y < h; ++y) { + size_t x; + for(x = 0; x < olinebits; ++x) { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + ibp += diff; + } +} + +/*out must be buffer big enough to contain full image, and in must contain the full decompressed data from +the IDAT chunks (with filter index bytes and possible padding bits) +return value is error*/ +static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, + unsigned w, unsigned h, const LodePNGInfo* info_png) { + /* + This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. + Steps: + *) if no Adam7: 1) unfilter 2) remove padding bits (= possible extra bits per scanline if bpp < 8) + *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace + NOTE: the in buffer will be overwritten with intermediate data! + */ + unsigned bpp = lodepng_get_bpp(&info_png->color); + if(bpp == 0) return 31; /*error: invalid colortype*/ + + if(info_png->interlace_method == 0) { + if(bpp < 8 && w * bpp != ((w * bpp + 7u) / 8u) * 8u) { + CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp)); + removePaddingBits(out, in, w * bpp, ((w * bpp + 7u) / 8u) * 8u, h); + } + /*we can immediately filter into the out buffer, no other steps needed*/ + else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp)); + } else /*interlace_method is 1 (Adam7)*/ { + unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + for(i = 0; i != 7; ++i) { + CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp)); + /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline, + move bytes instead of bits or move not at all*/ + if(bpp < 8) { + /*remove padding bits in scanlines; after this there still may be padding + bits between the different reduced images: each reduced image still starts nicely at a byte*/ + removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, + ((passw[i] * bpp + 7u) / 8u) * 8u, passh[i]); + } + } + + Adam7_deinterlace(out, in, w, h, bpp); + } + + return 0; +} + +static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) { + unsigned pos = 0, i; + color->palettesize = chunkLength / 3u; + if(color->palettesize == 0 || color->palettesize > 256) return 38; /*error: palette too small or big*/ + lodepng_color_mode_alloc_palette(color); + if(!color->palette && color->palettesize) { + color->palettesize = 0; + return 83; /*alloc fail*/ + } + + for(i = 0; i != color->palettesize; ++i) { + color->palette[4 * i + 0] = data[pos++]; /*R*/ + color->palette[4 * i + 1] = data[pos++]; /*G*/ + color->palette[4 * i + 2] = data[pos++]; /*B*/ + color->palette[4 * i + 3] = 255; /*alpha*/ + } + + return 0; /* OK */ +} + +static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) { + unsigned i; + if(color->colortype == LCT_PALETTE) { + /*error: more alpha values given than there are palette entries*/ + if(chunkLength > color->palettesize) return 39; + + for(i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i]; + } else if(color->colortype == LCT_GREY) { + /*error: this chunk must be 2 bytes for grayscale image*/ + if(chunkLength != 2) return 30; + + color->key_defined = 1; + color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1]; + } else if(color->colortype == LCT_RGB) { + /*error: this chunk must be 6 bytes for RGB image*/ + if(chunkLength != 6) return 41; + + color->key_defined = 1; + color->key_r = 256u * data[0] + data[1]; + color->key_g = 256u * data[2] + data[3]; + color->key_b = 256u * data[4] + data[5]; + } + else return 42; /*error: tRNS chunk not allowed for other color models*/ + + return 0; /* OK */ +} + + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*background color chunk (bKGD)*/ +static unsigned readChunk_bKGD(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { + if(info->color.colortype == LCT_PALETTE) { + /*error: this chunk must be 1 byte for indexed color image*/ + if(chunkLength != 1) return 43; + + /*error: invalid palette index, or maybe this chunk appeared before PLTE*/ + if(data[0] >= info->color.palettesize) return 103; + + info->background_defined = 1; + info->background_r = info->background_g = info->background_b = data[0]; + } else if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) { + /*error: this chunk must be 2 bytes for grayscale image*/ + if(chunkLength != 2) return 44; + + /*the values are truncated to bitdepth in the PNG file*/ + info->background_defined = 1; + info->background_r = info->background_g = info->background_b = 256u * data[0] + data[1]; + } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) { + /*error: this chunk must be 6 bytes for grayscale image*/ + if(chunkLength != 6) return 45; + + /*the values are truncated to bitdepth in the PNG file*/ + info->background_defined = 1; + info->background_r = 256u * data[0] + data[1]; + info->background_g = 256u * data[2] + data[3]; + info->background_b = 256u * data[4] + data[5]; + } + + return 0; /* OK */ +} + +/*text chunk (tEXt)*/ +static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { + unsigned error = 0; + char *key = 0, *str = 0; + + while(!error) /*not really a while loop, only used to break on error*/ { + unsigned length, string2_begin; + + length = 0; + while(length < chunkLength && data[length] != 0) ++length; + /*even though it's not allowed by the standard, no error is thrown if + there's no null termination char, if the text is empty*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + lodepng_memcpy(key, data, length); + key[length] = 0; + + string2_begin = length + 1; /*skip keyword null terminator*/ + + length = (unsigned)(chunkLength < string2_begin ? 0 : chunkLength - string2_begin); + str = (char*)lodepng_malloc(length + 1); + if(!str) CERROR_BREAK(error, 83); /*alloc fail*/ + + lodepng_memcpy(str, data + string2_begin, length); + str[length] = 0; + + error = lodepng_add_text(info, key, str); + + break; + } + + lodepng_free(key); + lodepng_free(str); + + return error; +} + +/*compressed text chunk (zTXt)*/ +static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecoderSettings* decoder, + const unsigned char* data, size_t chunkLength) { + unsigned error = 0; + + /*copy the object to change parameters in it*/ + LodePNGDecompressSettings zlibsettings = decoder->zlibsettings; + + unsigned length, string2_begin; + char *key = 0; + unsigned char* str = 0; + size_t size = 0; + + while(!error) /*not really a while loop, only used to break on error*/ { + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; + if(length + 2 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + lodepng_memcpy(key, data, length); + key[length] = 0; + + if(data[length + 1] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ + + string2_begin = length + 2; + if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ + + length = (unsigned)chunkLength - string2_begin; + zlibsettings.max_output_size = decoder->max_text_size; + /*will fail if zlib error, e.g. if length is too small*/ + error = zlib_decompress(&str, &size, 0, &data[string2_begin], + length, &zlibsettings); + /*error: compressed text larger than decoder->max_text_size*/ + if(error && size > zlibsettings.max_output_size) error = 112; + if(error) break; + error = lodepng_add_text_sized(info, key, (char*)str, size); + break; + } + + lodepng_free(key); + lodepng_free(str); + + return error; +} + +/*international text chunk (iTXt)*/ +static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecoderSettings* decoder, + const unsigned char* data, size_t chunkLength) { + unsigned error = 0; + unsigned i; + + /*copy the object to change parameters in it*/ + LodePNGDecompressSettings zlibsettings = decoder->zlibsettings; + + unsigned length, begin, compressed; + char *key = 0, *langtag = 0, *transkey = 0; + + while(!error) /*not really a while loop, only used to break on error*/ { + /*Quick check if the chunk length isn't too small. Even without check + it'd still fail with other error checks below if it's too short. This just gives a different error code.*/ + if(chunkLength < 5) CERROR_BREAK(error, 30); /*iTXt chunk too short*/ + + /*read the key*/ + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; + if(length + 3 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination char, corrupt?*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + lodepng_memcpy(key, data, length); + key[length] = 0; + + /*read the compression method*/ + compressed = data[length + 1]; + if(data[length + 2] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ + + /*even though it's not allowed by the standard, no error is thrown if + there's no null termination char, if the text is empty for the next 3 texts*/ + + /*read the langtag*/ + begin = length + 3; + length = 0; + for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; + + langtag = (char*)lodepng_malloc(length + 1); + if(!langtag) CERROR_BREAK(error, 83); /*alloc fail*/ + + lodepng_memcpy(langtag, data + begin, length); + langtag[length] = 0; + + /*read the transkey*/ + begin += length + 1; + length = 0; + for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; + + transkey = (char*)lodepng_malloc(length + 1); + if(!transkey) CERROR_BREAK(error, 83); /*alloc fail*/ + + lodepng_memcpy(transkey, data + begin, length); + transkey[length] = 0; + + /*read the actual text*/ + begin += length + 1; + + length = (unsigned)chunkLength < begin ? 0 : (unsigned)chunkLength - begin; + + if(compressed) { + unsigned char* str = 0; + size_t size = 0; + zlibsettings.max_output_size = decoder->max_text_size; + /*will fail if zlib error, e.g. if length is too small*/ + error = zlib_decompress(&str, &size, 0, &data[begin], + length, &zlibsettings); + /*error: compressed text larger than decoder->max_text_size*/ + if(error && size > zlibsettings.max_output_size) error = 112; + if(!error) error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)str, size); + lodepng_free(str); + } else { + error = lodepng_add_itext_sized(info, key, langtag, transkey, (char*)(data + begin), length); + } + + break; + } + + lodepng_free(key); + lodepng_free(langtag); + lodepng_free(transkey); + + return error; +} + +static unsigned readChunk_tIME(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { + if(chunkLength != 7) return 73; /*invalid tIME chunk size*/ + + info->time_defined = 1; + info->time.year = 256u * data[0] + data[1]; + info->time.month = data[2]; + info->time.day = data[3]; + info->time.hour = data[4]; + info->time.minute = data[5]; + info->time.second = data[6]; + + return 0; /* OK */ +} + +static unsigned readChunk_pHYs(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { + if(chunkLength != 9) return 74; /*invalid pHYs chunk size*/ + + info->phys_defined = 1; + info->phys_x = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3]; + info->phys_y = 16777216u * data[4] + 65536u * data[5] + 256u * data[6] + data[7]; + info->phys_unit = data[8]; + + return 0; /* OK */ +} + +static unsigned readChunk_gAMA(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { + if(chunkLength != 4) return 96; /*invalid gAMA chunk size*/ + + info->gama_defined = 1; + info->gama_gamma = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3]; + + return 0; /* OK */ +} + +static unsigned readChunk_cHRM(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { + if(chunkLength != 32) return 97; /*invalid cHRM chunk size*/ + + info->chrm_defined = 1; + info->chrm_white_x = 16777216u * data[ 0] + 65536u * data[ 1] + 256u * data[ 2] + data[ 3]; + info->chrm_white_y = 16777216u * data[ 4] + 65536u * data[ 5] + 256u * data[ 6] + data[ 7]; + info->chrm_red_x = 16777216u * data[ 8] + 65536u * data[ 9] + 256u * data[10] + data[11]; + info->chrm_red_y = 16777216u * data[12] + 65536u * data[13] + 256u * data[14] + data[15]; + info->chrm_green_x = 16777216u * data[16] + 65536u * data[17] + 256u * data[18] + data[19]; + info->chrm_green_y = 16777216u * data[20] + 65536u * data[21] + 256u * data[22] + data[23]; + info->chrm_blue_x = 16777216u * data[24] + 65536u * data[25] + 256u * data[26] + data[27]; + info->chrm_blue_y = 16777216u * data[28] + 65536u * data[29] + 256u * data[30] + data[31]; + + return 0; /* OK */ +} + +static unsigned readChunk_sRGB(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) { + if(chunkLength != 1) return 98; /*invalid sRGB chunk size (this one is never ignored)*/ + + info->srgb_defined = 1; + info->srgb_intent = data[0]; + + return 0; /* OK */ +} + +static unsigned readChunk_iCCP(LodePNGInfo* info, const LodePNGDecoderSettings* decoder, + const unsigned char* data, size_t chunkLength) { + unsigned error = 0; + unsigned i; + size_t size = 0; + /*copy the object to change parameters in it*/ + LodePNGDecompressSettings zlibsettings = decoder->zlibsettings; + + unsigned length, string2_begin; + + info->iccp_defined = 1; + if(info->iccp_name) lodepng_clear_icc(info); + + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; + if(length + 2 >= chunkLength) return 75; /*no null termination, corrupt?*/ + if(length < 1 || length > 79) return 89; /*keyword too short or long*/ + + info->iccp_name = (char*)lodepng_malloc(length + 1); + if(!info->iccp_name) return 83; /*alloc fail*/ + + info->iccp_name[length] = 0; + for(i = 0; i != length; ++i) info->iccp_name[i] = (char)data[i]; + + if(data[length + 1] != 0) return 72; /*the 0 byte indicating compression must be 0*/ + + string2_begin = length + 2; + if(string2_begin > chunkLength) return 75; /*no null termination, corrupt?*/ + + length = (unsigned)chunkLength - string2_begin; + zlibsettings.max_output_size = decoder->max_icc_size; + error = zlib_decompress(&info->iccp_profile, &size, 0, + &data[string2_begin], + length, &zlibsettings); + /*error: ICC profile larger than decoder->max_icc_size*/ + if(error && size > zlibsettings.max_output_size) error = 113; + info->iccp_profile_size = size; + if(!error && !info->iccp_profile_size) error = 100; /*invalid ICC profile size*/ + return error; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos, + const unsigned char* in, size_t insize) { + const unsigned char* chunk = in + pos; + unsigned chunkLength; + const unsigned char* data; + unsigned unhandled = 0; + unsigned error = 0; + + if(pos + 4 > insize) return 30; + chunkLength = lodepng_chunk_length(chunk); + if(chunkLength > 2147483647) return 63; + data = lodepng_chunk_data_const(chunk); + if(data + chunkLength + 4 > in + insize) return 30; + + if(lodepng_chunk_type_equals(chunk, "PLTE")) { + error = readChunk_PLTE(&state->info_png.color, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "tRNS")) { + error = readChunk_tRNS(&state->info_png.color, data, chunkLength); +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + } else if(lodepng_chunk_type_equals(chunk, "bKGD")) { + error = readChunk_bKGD(&state->info_png, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "tEXt")) { + error = readChunk_tEXt(&state->info_png, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "zTXt")) { + error = readChunk_zTXt(&state->info_png, &state->decoder, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "iTXt")) { + error = readChunk_iTXt(&state->info_png, &state->decoder, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "tIME")) { + error = readChunk_tIME(&state->info_png, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "pHYs")) { + error = readChunk_pHYs(&state->info_png, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "gAMA")) { + error = readChunk_gAMA(&state->info_png, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "cHRM")) { + error = readChunk_cHRM(&state->info_png, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "sRGB")) { + error = readChunk_sRGB(&state->info_png, data, chunkLength); + } else if(lodepng_chunk_type_equals(chunk, "iCCP")) { + error = readChunk_iCCP(&state->info_png, &state->decoder, data, chunkLength); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } else { + /* unhandled chunk is ok (is not an error) */ + unhandled = 1; + } + + if(!error && !unhandled && !state->decoder.ignore_crc) { + if(lodepng_chunk_check_crc(chunk)) return 57; /*invalid CRC*/ + } + + return error; +} + +/*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ +static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize) { + unsigned char IEND = 0; + const unsigned char* chunk; + unsigned char* idat; /*the data from idat chunks, zlib compressed*/ + size_t idatsize = 0; + unsigned char* scanlines = 0; + size_t scanlines_size = 0, expected_size = 0; + size_t outsize = 0; + + /*for unknown chunk order*/ + unsigned unknown = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + + + /* safe output values in case error happens */ + *out = 0; + *w = *h = 0; + + state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/ + if(state->error) return; + + if(lodepng_pixel_overflow(*w, *h, &state->info_png.color, &state->info_raw)) { + CERROR_RETURN(state->error, 92); /*overflow possible due to amount of pixels*/ + } + + /*the input filesize is a safe upper bound for the sum of idat chunks size*/ + idat = (unsigned char*)lodepng_malloc(insize); + if(!idat) CERROR_RETURN(state->error, 83); /*alloc fail*/ + + chunk = &in[33]; /*first byte of the first chunk after the header*/ + + /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. + IDAT data is put at the start of the in buffer*/ + while(!IEND && !state->error) { + unsigned chunkLength; + const unsigned char* data; /*the data in the chunk*/ + + /*error: size of the in buffer too small to contain next chunk*/ + if((size_t)((chunk - in) + 12) > insize || chunk < in) { + if(state->decoder.ignore_end) break; /*other errors may still happen though*/ + CERROR_BREAK(state->error, 30); + } + + /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/ + chunkLength = lodepng_chunk_length(chunk); + /*error: chunk length larger than the max PNG chunk size*/ + if(chunkLength > 2147483647) { + if(state->decoder.ignore_end) break; /*other errors may still happen though*/ + CERROR_BREAK(state->error, 63); + } + + if((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) { + CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk*/ + } + + data = lodepng_chunk_data_const(chunk); + + unknown = 0; + + /*IDAT chunk, containing compressed image data*/ + if(lodepng_chunk_type_equals(chunk, "IDAT")) { + size_t newsize; + if(lodepng_addofl(idatsize, chunkLength, &newsize)) CERROR_BREAK(state->error, 95); + if(newsize > insize) CERROR_BREAK(state->error, 95); + lodepng_memcpy(idat + idatsize, data, chunkLength); + idatsize += chunkLength; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + critical_pos = 3; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } else if(lodepng_chunk_type_equals(chunk, "IEND")) { + /*IEND chunk*/ + IEND = 1; + } else if(lodepng_chunk_type_equals(chunk, "PLTE")) { + /*palette chunk (PLTE)*/ + state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength); + if(state->error) break; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + critical_pos = 2; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } else if(lodepng_chunk_type_equals(chunk, "tRNS")) { + /*palette transparency chunk (tRNS). Even though this one is an ancillary chunk , it is still compiled + in without 'LODEPNG_COMPILE_ANCILLARY_CHUNKS' because it contains essential color information that + affects the alpha channel of pixels. */ + state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength); + if(state->error) break; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*background color chunk (bKGD)*/ + } else if(lodepng_chunk_type_equals(chunk, "bKGD")) { + state->error = readChunk_bKGD(&state->info_png, data, chunkLength); + if(state->error) break; + } else if(lodepng_chunk_type_equals(chunk, "tEXt")) { + /*text chunk (tEXt)*/ + if(state->decoder.read_text_chunks) { + state->error = readChunk_tEXt(&state->info_png, data, chunkLength); + if(state->error) break; + } + } else if(lodepng_chunk_type_equals(chunk, "zTXt")) { + /*compressed text chunk (zTXt)*/ + if(state->decoder.read_text_chunks) { + state->error = readChunk_zTXt(&state->info_png, &state->decoder, data, chunkLength); + if(state->error) break; + } + } else if(lodepng_chunk_type_equals(chunk, "iTXt")) { + /*international text chunk (iTXt)*/ + if(state->decoder.read_text_chunks) { + state->error = readChunk_iTXt(&state->info_png, &state->decoder, data, chunkLength); + if(state->error) break; + } + } else if(lodepng_chunk_type_equals(chunk, "tIME")) { + state->error = readChunk_tIME(&state->info_png, data, chunkLength); + if(state->error) break; + } else if(lodepng_chunk_type_equals(chunk, "pHYs")) { + state->error = readChunk_pHYs(&state->info_png, data, chunkLength); + if(state->error) break; + } else if(lodepng_chunk_type_equals(chunk, "gAMA")) { + state->error = readChunk_gAMA(&state->info_png, data, chunkLength); + if(state->error) break; + } else if(lodepng_chunk_type_equals(chunk, "cHRM")) { + state->error = readChunk_cHRM(&state->info_png, data, chunkLength); + if(state->error) break; + } else if(lodepng_chunk_type_equals(chunk, "sRGB")) { + state->error = readChunk_sRGB(&state->info_png, data, chunkLength); + if(state->error) break; + } else if(lodepng_chunk_type_equals(chunk, "iCCP")) { + state->error = readChunk_iCCP(&state->info_png, &state->decoder, data, chunkLength); + if(state->error) break; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } else /*it's not an implemented chunk type, so ignore it: skip over the data*/ { + /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/ + if(!state->decoder.ignore_critical && !lodepng_chunk_ancillary(chunk)) { + CERROR_BREAK(state->error, 69); + } + + unknown = 1; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + if(state->decoder.remember_unknown_chunks) { + state->error = lodepng_chunk_append(&state->info_png.unknown_chunks_data[critical_pos - 1], + &state->info_png.unknown_chunks_size[critical_pos - 1], chunk); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } + + if(!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ { + if(lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/ + } + + if(!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize); + } + + if(!state->error && state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) { + state->error = 106; /* error: PNG file must have PLTE chunk if color type is palette */ + } + + if(!state->error) { + /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation. + If the decompressed size does not match the prediction, the image must be corrupt.*/ + if(state->info_png.interlace_method == 0) { + size_t bpp = lodepng_get_bpp(&state->info_png.color); + expected_size = lodepng_get_raw_size_idat(*w, *h, bpp); + } else { + size_t bpp = lodepng_get_bpp(&state->info_png.color); + /*Adam-7 interlaced: expected size is the sum of the 7 sub-images sizes*/ + expected_size = 0; + expected_size += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, bpp); + if(*w > 4) expected_size += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, bpp); + expected_size += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, bpp); + if(*w > 2) expected_size += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, bpp); + expected_size += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, bpp); + if(*w > 1) expected_size += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, bpp); + expected_size += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, bpp); + } + + state->error = zlib_decompress(&scanlines, &scanlines_size, expected_size, idat, idatsize, &state->decoder.zlibsettings); + } + if(!state->error && scanlines_size != expected_size) state->error = 91; /*decompressed size doesn't match prediction*/ + lodepng_free(idat); + + if(!state->error) { + outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color); + *out = (unsigned char*)lodepng_malloc(outsize); + if(!*out) state->error = 83; /*alloc fail*/ + } + if(!state->error) { + lodepng_memset(*out, 0, outsize); + state->error = postProcessScanlines(*out, scanlines, *w, *h, &state->info_png); + } + lodepng_free(scanlines); +} + +unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize) { + *out = 0; + decodeGeneric(out, w, h, state, in, insize); + if(state->error) return state->error; + if(!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) { + /*same color type, no copying or converting of data needed*/ + /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype + the raw image has to the end user*/ + if(!state->decoder.color_convert) { + state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color); + if(state->error) return state->error; + } + } else { /*color conversion needed*/ + unsigned char* data = *out; + size_t outsize; + + /*TODO: check if this works according to the statement in the documentation: "The converter can convert + from grayscale input color type, to 8-bit grayscale or grayscale with alpha"*/ + if(!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA) + && !(state->info_raw.bitdepth == 8)) { + return 56; /*unsupported color mode conversion*/ + } + + outsize = lodepng_get_raw_size(*w, *h, &state->info_raw); + *out = (unsigned char*)lodepng_malloc(outsize); + if(!(*out)) { + state->error = 83; /*alloc fail*/ + } + else state->error = lodepng_convert(*out, data, &state->info_raw, + &state->info_png.color, *w, *h); + lodepng_free(data); + } + return state->error; +} + +unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, + size_t insize, LodePNGColorType colortype, unsigned bitdepth) { + unsigned error; + LodePNGState state; + lodepng_state_init(&state); + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*disable reading things that this function doesn't output*/ + state.decoder.read_text_chunks = 0; + state.decoder.remember_unknown_chunks = 0; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + error = lodepng_decode(out, w, h, &state, in, insize); + lodepng_state_cleanup(&state); + return error; +} + +unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) { + return lodepng_decode_memory(out, w, h, in, insize, LCT_RGBA, 8); +} + +unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) { + return lodepng_decode_memory(out, w, h, in, insize, LCT_RGB, 8); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename, + LodePNGColorType colortype, unsigned bitdepth) { + unsigned char* buffer = 0; + size_t buffersize; + unsigned error; + /* safe output values in case error happens */ + *out = 0; + *w = *h = 0; + error = lodepng_load_file(&buffer, &buffersize, filename); + if(!error) error = lodepng_decode_memory(out, w, h, buffer, buffersize, colortype, bitdepth); + lodepng_free(buffer); + return error; +} + +unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) { + return lodepng_decode_file(out, w, h, filename, LCT_RGBA, 8); +} + +unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) { + return lodepng_decode_file(out, w, h, filename, LCT_RGB, 8); +} +#endif /*LODEPNG_COMPILE_DISK*/ + +void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) { + settings->color_convert = 1; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + settings->read_text_chunks = 1; + settings->remember_unknown_chunks = 0; + settings->max_text_size = 16777216; + settings->max_icc_size = 16777216; /* 16MB is much more than enough for any reasonable ICC profile */ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + settings->ignore_crc = 0; + settings->ignore_critical = 0; + settings->ignore_end = 0; + lodepng_decompress_settings_init(&settings->zlibsettings); +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) + +void lodepng_state_init(LodePNGState* state) { +#ifdef LODEPNG_COMPILE_DECODER + lodepng_decoder_settings_init(&state->decoder); +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER + lodepng_encoder_settings_init(&state->encoder); +#endif /*LODEPNG_COMPILE_ENCODER*/ + lodepng_color_mode_init(&state->info_raw); + lodepng_info_init(&state->info_png); + state->error = 1; +} + +void lodepng_state_cleanup(LodePNGState* state) { + lodepng_color_mode_cleanup(&state->info_raw); + lodepng_info_cleanup(&state->info_png); +} + +void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source) { + lodepng_state_cleanup(dest); + *dest = *source; + lodepng_color_mode_init(&dest->info_raw); + lodepng_info_init(&dest->info_png); + dest->error = lodepng_color_mode_copy(&dest->info_raw, &source->info_raw); if(dest->error) return; + dest->error = lodepng_info_copy(&dest->info_png, &source->info_png); if(dest->error) return; +} + +#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ + +#ifdef LODEPNG_COMPILE_ENCODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG Encoder / */ +/* ////////////////////////////////////////////////////////////////////////// */ + + +static unsigned writeSignature(ucvector* out) { + size_t pos = out->size; + const unsigned char signature[] = {137, 80, 78, 71, 13, 10, 26, 10}; + /*8 bytes PNG signature, aka the magic bytes*/ + if(!ucvector_resize(out, out->size + 8)) return 83; /*alloc fail*/ + lodepng_memcpy(out->data + pos, signature, 8); + return 0; +} + +static unsigned addChunk_IHDR(ucvector* out, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth, unsigned interlace_method) { + unsigned char *chunk, *data; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 13, "IHDR")); + data = chunk + 8; + + lodepng_set32bitInt(data + 0, w); /*width*/ + lodepng_set32bitInt(data + 4, h); /*height*/ + data[8] = (unsigned char)bitdepth; /*bit depth*/ + data[9] = (unsigned char)colortype; /*color type*/ + data[10] = 0; /*compression method*/ + data[11] = 0; /*filter method*/ + data[12] = interlace_method; /*interlace method*/ + + lodepng_chunk_generate_crc(chunk); + return 0; +} + +/* only adds the chunk if needed (there is a key or palette with alpha) */ +static unsigned addChunk_PLTE(ucvector* out, const LodePNGColorMode* info) { + unsigned char* chunk; + size_t i, j = 8; + + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, info->palettesize * 3, "PLTE")); + + for(i = 0; i != info->palettesize; ++i) { + /*add all channels except alpha channel*/ + chunk[j++] = info->palette[i * 4 + 0]; + chunk[j++] = info->palette[i * 4 + 1]; + chunk[j++] = info->palette[i * 4 + 2]; + } + + lodepng_chunk_generate_crc(chunk); + return 0; +} + +static unsigned addChunk_tRNS(ucvector* out, const LodePNGColorMode* info) { + unsigned char* chunk = 0; + + if(info->colortype == LCT_PALETTE) { + size_t i, amount = info->palettesize; + /*the tail of palette values that all have 255 as alpha, does not have to be encoded*/ + for(i = info->palettesize; i != 0; --i) { + if(info->palette[4 * (i - 1) + 3] != 255) break; + --amount; + } + if(amount) { + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, amount, "tRNS")); + /*add the alpha channel values from the palette*/ + for(i = 0; i != amount; ++i) chunk[8 + i] = info->palette[4 * i + 3]; + } + } else if(info->colortype == LCT_GREY) { + if(info->key_defined) { + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 2, "tRNS")); + chunk[8] = (unsigned char)(info->key_r >> 8); + chunk[9] = (unsigned char)(info->key_r & 255); + } + } else if(info->colortype == LCT_RGB) { + if(info->key_defined) { + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 6, "tRNS")); + chunk[8] = (unsigned char)(info->key_r >> 8); + chunk[9] = (unsigned char)(info->key_r & 255); + chunk[10] = (unsigned char)(info->key_g >> 8); + chunk[11] = (unsigned char)(info->key_g & 255); + chunk[12] = (unsigned char)(info->key_b >> 8); + chunk[13] = (unsigned char)(info->key_b & 255); + } + } + + if(chunk) lodepng_chunk_generate_crc(chunk); + return 0; +} + +static unsigned addChunk_IDAT(ucvector* out, const unsigned char* data, size_t datasize, + LodePNGCompressSettings* zlibsettings) { + unsigned error = 0; + unsigned char* zlib = 0; + size_t zlibsize = 0; + + error = zlib_compress(&zlib, &zlibsize, data, datasize, zlibsettings); + if(!error) { + error = lodepng_chunk_createv(out, zlibsize, "IDAT", zlib); + } + lodepng_free(zlib); + return error; +} + +static unsigned addChunk_IEND(ucvector* out) { + return lodepng_chunk_createv(out, 0, "IEND", 0); +} + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + +static unsigned addChunk_tEXt(ucvector* out, const char* keyword, const char* textstring) { + unsigned char* chunk = 0; + size_t keysize = lodepng_strlen(keyword), textsize = lodepng_strlen(textstring); + size_t size = keysize + 1 + textsize; + if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/ + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, size, "tEXt")); + lodepng_memcpy(chunk + 8, keyword, keysize); + chunk[8 + keysize] = 0; /*null termination char*/ + lodepng_memcpy(chunk + 9 + keysize, textstring, textsize); + lodepng_chunk_generate_crc(chunk); + return 0; +} + +static unsigned addChunk_zTXt(ucvector* out, const char* keyword, const char* textstring, + LodePNGCompressSettings* zlibsettings) { + unsigned error = 0; + unsigned char* chunk = 0; + unsigned char* compressed = 0; + size_t compressedsize = 0; + size_t textsize = lodepng_strlen(textstring); + size_t keysize = lodepng_strlen(keyword); + if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/ + + error = zlib_compress(&compressed, &compressedsize, + (const unsigned char*)textstring, textsize, zlibsettings); + if(!error) { + size_t size = keysize + 2 + compressedsize; + error = lodepng_chunk_init(&chunk, out, size, "zTXt"); + } + if(!error) { + lodepng_memcpy(chunk + 8, keyword, keysize); + chunk[8 + keysize] = 0; /*null termination char*/ + chunk[9 + keysize] = 0; /*compression method: 0*/ + lodepng_memcpy(chunk + 10 + keysize, compressed, compressedsize); + lodepng_chunk_generate_crc(chunk); + } + + lodepng_free(compressed); + return error; +} + +static unsigned addChunk_iTXt(ucvector* out, unsigned compress, const char* keyword, const char* langtag, + const char* transkey, const char* textstring, LodePNGCompressSettings* zlibsettings) { + unsigned error = 0; + unsigned char* chunk = 0; + unsigned char* compressed = 0; + size_t compressedsize = 0; + size_t textsize = lodepng_strlen(textstring); + size_t keysize = lodepng_strlen(keyword), langsize = lodepng_strlen(langtag), transsize = lodepng_strlen(transkey); + + if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/ + + if(compress) { + error = zlib_compress(&compressed, &compressedsize, + (const unsigned char*)textstring, textsize, zlibsettings); + } + if(!error) { + size_t size = keysize + 3 + langsize + 1 + transsize + 1 + (compress ? compressedsize : textsize); + error = lodepng_chunk_init(&chunk, out, size, "iTXt"); + } + if(!error) { + size_t pos = 8; + lodepng_memcpy(chunk + pos, keyword, keysize); + pos += keysize; + chunk[pos++] = 0; /*null termination char*/ + chunk[pos++] = (compress ? 1 : 0); /*compression flag*/ + chunk[pos++] = 0; /*compression method: 0*/ + lodepng_memcpy(chunk + pos, langtag, langsize); + pos += langsize; + chunk[pos++] = 0; /*null termination char*/ + lodepng_memcpy(chunk + pos, transkey, transsize); + pos += transsize; + chunk[pos++] = 0; /*null termination char*/ + if(compress) { + lodepng_memcpy(chunk + pos, compressed, compressedsize); + } else { + lodepng_memcpy(chunk + pos, textstring, textsize); + } + lodepng_chunk_generate_crc(chunk); + } + + lodepng_free(compressed); + return error; +} + +static unsigned addChunk_bKGD(ucvector* out, const LodePNGInfo* info) { + unsigned char* chunk = 0; + if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) { + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 2, "bKGD")); + chunk[8] = (unsigned char)(info->background_r >> 8); + chunk[9] = (unsigned char)(info->background_r & 255); + } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) { + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 6, "bKGD")); + chunk[8] = (unsigned char)(info->background_r >> 8); + chunk[9] = (unsigned char)(info->background_r & 255); + chunk[10] = (unsigned char)(info->background_g >> 8); + chunk[11] = (unsigned char)(info->background_g & 255); + chunk[12] = (unsigned char)(info->background_b >> 8); + chunk[13] = (unsigned char)(info->background_b & 255); + } else if(info->color.colortype == LCT_PALETTE) { + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 1, "bKGD")); + chunk[8] = (unsigned char)(info->background_r & 255); /*palette index*/ + } + if(chunk) lodepng_chunk_generate_crc(chunk); + return 0; +} + +static unsigned addChunk_tIME(ucvector* out, const LodePNGTime* time) { + unsigned char* chunk; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 7, "tIME")); + chunk[8] = (unsigned char)(time->year >> 8); + chunk[9] = (unsigned char)(time->year & 255); + chunk[10] = (unsigned char)time->month; + chunk[11] = (unsigned char)time->day; + chunk[12] = (unsigned char)time->hour; + chunk[13] = (unsigned char)time->minute; + chunk[14] = (unsigned char)time->second; + lodepng_chunk_generate_crc(chunk); + return 0; +} + +static unsigned addChunk_pHYs(ucvector* out, const LodePNGInfo* info) { + unsigned char* chunk; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 9, "pHYs")); + lodepng_set32bitInt(chunk + 8, info->phys_x); + lodepng_set32bitInt(chunk + 12, info->phys_y); + chunk[16] = info->phys_unit; + lodepng_chunk_generate_crc(chunk); + return 0; +} + +static unsigned addChunk_gAMA(ucvector* out, const LodePNGInfo* info) { + unsigned char* chunk; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 4, "gAMA")); + lodepng_set32bitInt(chunk + 8, info->gama_gamma); + lodepng_chunk_generate_crc(chunk); + return 0; +} + +static unsigned addChunk_cHRM(ucvector* out, const LodePNGInfo* info) { + unsigned char* chunk; + CERROR_TRY_RETURN(lodepng_chunk_init(&chunk, out, 32, "cHRM")); + lodepng_set32bitInt(chunk + 8, info->chrm_white_x); + lodepng_set32bitInt(chunk + 12, info->chrm_white_y); + lodepng_set32bitInt(chunk + 16, info->chrm_red_x); + lodepng_set32bitInt(chunk + 20, info->chrm_red_y); + lodepng_set32bitInt(chunk + 24, info->chrm_green_x); + lodepng_set32bitInt(chunk + 28, info->chrm_green_y); + lodepng_set32bitInt(chunk + 32, info->chrm_blue_x); + lodepng_set32bitInt(chunk + 36, info->chrm_blue_y); + lodepng_chunk_generate_crc(chunk); + return 0; +} + +static unsigned addChunk_sRGB(ucvector* out, const LodePNGInfo* info) { + unsigned char data = info->srgb_intent; + return lodepng_chunk_createv(out, 1, "sRGB", &data); +} + +static unsigned addChunk_iCCP(ucvector* out, const LodePNGInfo* info, LodePNGCompressSettings* zlibsettings) { + unsigned error = 0; + unsigned char* chunk = 0; + unsigned char* compressed = 0; + size_t compressedsize = 0; + size_t keysize = lodepng_strlen(info->iccp_name); + + if(keysize < 1 || keysize > 79) return 89; /*error: invalid keyword size*/ + error = zlib_compress(&compressed, &compressedsize, + info->iccp_profile, info->iccp_profile_size, zlibsettings); + if(!error) { + size_t size = keysize + 2 + compressedsize; + error = lodepng_chunk_init(&chunk, out, size, "iCCP"); + } + if(!error) { + lodepng_memcpy(chunk + 8, info->iccp_name, keysize); + chunk[8 + keysize] = 0; /*null termination char*/ + chunk[9 + keysize] = 0; /*compression method: 0*/ + lodepng_memcpy(chunk + 10 + keysize, compressed, compressedsize); + lodepng_chunk_generate_crc(chunk); + } + + lodepng_free(compressed); + return error; +} + +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +static void filterScanline(unsigned char* out, const unsigned char* scanline, const unsigned char* prevline, + size_t length, size_t bytewidth, unsigned char filterType) { + size_t i; + switch(filterType) { + case 0: /*None*/ + for(i = 0; i != length; ++i) out[i] = scanline[i]; + break; + case 1: /*Sub*/ + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - scanline[i - bytewidth]; + break; + case 2: /*Up*/ + if(prevline) { + for(i = 0; i != length; ++i) out[i] = scanline[i] - prevline[i]; + } else { + for(i = 0; i != length; ++i) out[i] = scanline[i]; + } + break; + case 3: /*Average*/ + if(prevline) { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - (prevline[i] >> 1); + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) >> 1); + } else { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - (scanline[i - bytewidth] >> 1); + } + break; + case 4: /*Paeth*/ + if(prevline) { + /*paethPredictor(0, prevline[i], 0) is always prevline[i]*/ + for(i = 0; i != bytewidth; ++i) out[i] = (scanline[i] - prevline[i]); + for(i = bytewidth; i < length; ++i) { + out[i] = (scanline[i] - paethPredictor(scanline[i - bytewidth], prevline[i], prevline[i - bytewidth])); + } + } else { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + /*paethPredictor(scanline[i - bytewidth], 0, 0) is always scanline[i - bytewidth]*/ + for(i = bytewidth; i < length; ++i) out[i] = (scanline[i] - scanline[i - bytewidth]); + } + break; + default: return; /*invalid filter type given*/ + } +} + +/* integer binary logarithm, max return value is 31 */ +static size_t ilog2(size_t i) { + size_t result = 0; + if(i >= 65536) { result += 16; i >>= 16; } + if(i >= 256) { result += 8; i >>= 8; } + if(i >= 16) { result += 4; i >>= 4; } + if(i >= 4) { result += 2; i >>= 2; } + if(i >= 2) { result += 1; /*i >>= 1;*/ } + return result; +} + +/* integer approximation for i * log2(i), helper function for LFS_ENTROPY */ +static size_t ilog2i(size_t i) { + size_t l; + if(i == 0) return 0; + l = ilog2(i); + /* approximate i*log2(i): l is integer logarithm, ((i - (1u << l)) << 1u) + linearly approximates the missing fractional part multiplied by i */ + return i * l + ((i - (1u << l)) << 1u); +} + +static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, + const LodePNGColorMode* color, const LodePNGEncoderSettings* settings) { + /* + For PNG filter method 0 + out must be a buffer with as size: h + (w * h * bpp + 7u) / 8u, because there are + the scanlines with 1 extra byte per scanline + */ + + unsigned bpp = lodepng_get_bpp(color); + /*the width of a scanline in bytes, not including the filter type*/ + size_t linebytes = lodepng_get_raw_size_idat(w, 1, bpp) - 1u; + + /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ + size_t bytewidth = (bpp + 7u) / 8u; + const unsigned char* prevline = 0; + unsigned x, y; + unsigned error = 0; + LodePNGFilterStrategy strategy = settings->filter_strategy; + + /* + There is a heuristic called the minimum sum of absolute differences heuristic, suggested by the PNG standard: + * If the image type is Palette, or the bit depth is smaller than 8, then do not filter the image (i.e. + use fixed filtering, with the filter None). + * (The other case) If the image type is Grayscale or RGB (with or without Alpha), and the bit depth is + not smaller than 8, then use adaptive filtering heuristic as follows: independently for each row, apply + all five filters and select the filter that produces the smallest sum of absolute values per row. + This heuristic is used if filter strategy is LFS_MINSUM and filter_palette_zero is true. + + If filter_palette_zero is true and filter_strategy is not LFS_MINSUM, the above heuristic is followed, + but for "the other case", whatever strategy filter_strategy is set to instead of the minimum sum + heuristic is used. + */ + if(settings->filter_palette_zero && + (color->colortype == LCT_PALETTE || color->bitdepth < 8)) strategy = LFS_ZERO; + + if(bpp == 0) return 31; /*error: invalid color type*/ + + if(strategy >= LFS_ZERO && strategy <= LFS_FOUR) { + unsigned char type = (unsigned char)strategy; + for(y = 0; y != h; ++y) { + size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + size_t inindex = linebytes * y; + out[outindex] = type; /*filter type byte*/ + filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type); + prevline = &in[inindex]; + } + } else if(strategy == LFS_MINSUM) { + /*adaptive filtering*/ + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + size_t smallest = 0; + unsigned char type, bestType = 0; + + for(type = 0; type != 5; ++type) { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) error = 83; /*alloc fail*/ + } + + if(!error) { + for(y = 0; y != h; ++y) { + /*try the 5 filter types*/ + for(type = 0; type != 5; ++type) { + size_t sum = 0; + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + + /*calculate the sum of the result*/ + if(type == 0) { + for(x = 0; x != linebytes; ++x) sum += (unsigned char)(attempt[type][x]); + } else { + for(x = 0; x != linebytes; ++x) { + /*For differences, each byte should be treated as signed, values above 127 are negative + (converted to signed char). Filtertype 0 isn't a difference though, so use unsigned there. + This means filtertype 0 is almost never chosen, but that is justified.*/ + unsigned char s = attempt[type][x]; + sum += s < 128 ? s : (255U - s); + } + } + + /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || sum < smallest) { + bestType = type; + smallest = sum; + } + } + + prevline = &in[y * linebytes]; + + /*now fill the out values*/ + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + } + + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); + } else if(strategy == LFS_ENTROPY) { + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + size_t bestSum = 0; + unsigned type, bestType = 0; + unsigned count[256]; + + for(type = 0; type != 5; ++type) { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) error = 83; /*alloc fail*/ + } + + if(!error) { + for(y = 0; y != h; ++y) { + /*try the 5 filter types*/ + for(type = 0; type != 5; ++type) { + size_t sum = 0; + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + lodepng_memset(count, 0, 256 * sizeof(*count)); + for(x = 0; x != linebytes; ++x) ++count[attempt[type][x]]; + ++count[type]; /*the filter type itself is part of the scanline*/ + for(x = 0; x != 256; ++x) { + sum += ilog2i(count[x]); + } + /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || sum > bestSum) { + bestType = type; + bestSum = sum; + } + } + + prevline = &in[y * linebytes]; + + /*now fill the out values*/ + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + } + + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); + } else if(strategy == LFS_PREDEFINED) { + for(y = 0; y != h; ++y) { + size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + size_t inindex = linebytes * y; + unsigned char type = settings->predefined_filters[y]; + out[outindex] = type; /*filter type byte*/ + filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type); + prevline = &in[inindex]; + } + } else if(strategy == LFS_BRUTE_FORCE) { + /*brute force filter chooser. + deflate the scanline after every filter attempt to see which one deflates best. + This is very slow and gives only slightly smaller, sometimes even larger, result*/ + size_t size[5]; + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + size_t smallest = 0; + unsigned type = 0, bestType = 0; + unsigned char* dummy; + LodePNGCompressSettings zlibsettings; + lodepng_memcpy(&zlibsettings, &settings->zlibsettings, sizeof(LodePNGCompressSettings)); + /*use fixed tree on the attempts so that the tree is not adapted to the filtertype on purpose, + to simulate the true case where the tree is the same for the whole image. Sometimes it gives + better result with dynamic tree anyway. Using the fixed tree sometimes gives worse, but in rare + cases better compression. It does make this a bit less slow, so it's worth doing this.*/ + zlibsettings.btype = 1; + /*a custom encoder likely doesn't read the btype setting and is optimized for complete PNG + images only, so disable it*/ + zlibsettings.custom_zlib = 0; + zlibsettings.custom_deflate = 0; + for(type = 0; type != 5; ++type) { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) error = 83; /*alloc fail*/ + } + if(!error) { + for(y = 0; y != h; ++y) /*try the 5 filter types*/ { + for(type = 0; type != 5; ++type) { + unsigned testsize = (unsigned)linebytes; + /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/ + + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + size[type] = 0; + dummy = 0; + zlib_compress(&dummy, &size[type], attempt[type], testsize, &zlibsettings); + lodepng_free(dummy); + /*check if this is smallest size (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || size[type] < smallest) { + bestType = type; + smallest = size[type]; + } + } + prevline = &in[y * linebytes]; + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + } + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); + } + else return 88; /* unknown filter strategy */ + + return error; +} + +static void addPaddingBits(unsigned char* out, const unsigned char* in, + size_t olinebits, size_t ilinebits, unsigned h) { + /*The opposite of the removePaddingBits function + olinebits must be >= ilinebits*/ + unsigned y; + size_t diff = olinebits - ilinebits; + size_t obp = 0, ibp = 0; /*bit pointers*/ + for(y = 0; y != h; ++y) { + size_t x; + for(x = 0; x < ilinebits; ++x) { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + /*obp += diff; --> no, fill in some value in the padding bits too, to avoid + "Use of uninitialised value of size ###" warning from valgrind*/ + for(x = 0; x != diff; ++x) setBitOfReversedStream(&obp, out, 0); + } +} + +/* +in: non-interlaced image with size w*h +out: the same pixels, but re-ordered according to PNG's Adam7 interlacing, with + no padding bits between scanlines, but between reduced images so that each + reduced image starts at a byte. +bpp: bits per pixel +there are no padding bits, not between scanlines, not between reduced images +in has the following size in bits: w * h * bpp. +out is possibly bigger due to padding bits between reduced images +NOTE: comments about padding bits are only relevant if bpp < 8 +*/ +static void Adam7_interlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) { + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + if(bpp >= 8) { + for(i = 0; i != 7; ++i) { + unsigned x, y, b; + size_t bytewidth = bpp / 8u; + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) { + size_t pixelinstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; + size_t pixeloutstart = passstart[i] + (y * passw[i] + x) * bytewidth; + for(b = 0; b < bytewidth; ++b) { + out[pixeloutstart + b] = in[pixelinstart + b]; + } + } + } + } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ { + for(i = 0; i != 7; ++i) { + unsigned x, y, b; + unsigned ilinebits = bpp * passw[i]; + unsigned olinebits = bpp * w; + size_t obp, ibp; /*bit pointers (for out and in buffer)*/ + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) { + ibp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; + obp = (8 * passstart[i]) + (y * ilinebits + x * bpp); + for(b = 0; b < bpp; ++b) { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + } + } + } +} + +/*out must be buffer big enough to contain uncompressed IDAT chunk data, and in must contain the full image. +return value is error**/ +static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const unsigned char* in, + unsigned w, unsigned h, + const LodePNGInfo* info_png, const LodePNGEncoderSettings* settings) { + /* + This function converts the pure 2D image with the PNG's colortype, into filtered-padded-interlaced data. Steps: + *) if no Adam7: 1) add padding bits (= possible extra bits per scanline if bpp < 8) 2) filter + *) if adam7: 1) Adam7_interlace 2) 7x add padding bits 3) 7x filter + */ + unsigned bpp = lodepng_get_bpp(&info_png->color); + unsigned error = 0; + + if(info_png->interlace_method == 0) { + *outsize = h + (h * ((w * bpp + 7u) / 8u)); /*image size plus an extra byte per scanline + possible padding bits*/ + *out = (unsigned char*)lodepng_malloc(*outsize); + if(!(*out) && (*outsize)) error = 83; /*alloc fail*/ + + if(!error) { + /*non multiple of 8 bits per scanline, padding bits needed per scanline*/ + if(bpp < 8 && w * bpp != ((w * bpp + 7u) / 8u) * 8u) { + unsigned char* padded = (unsigned char*)lodepng_malloc(h * ((w * bpp + 7u) / 8u)); + if(!padded) error = 83; /*alloc fail*/ + if(!error) { + addPaddingBits(padded, in, ((w * bpp + 7u) / 8u) * 8u, w * bpp, h); + error = filter(*out, padded, w, h, &info_png->color, settings); + } + lodepng_free(padded); + } else { + /*we can immediately filter into the out buffer, no other steps needed*/ + error = filter(*out, in, w, h, &info_png->color, settings); + } + } + } else /*interlace_method is 1 (Adam7)*/ { + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned char* adam7; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + *outsize = filter_passstart[7]; /*image size plus an extra byte per scanline + possible padding bits*/ + *out = (unsigned char*)lodepng_malloc(*outsize); + if(!(*out)) error = 83; /*alloc fail*/ + + adam7 = (unsigned char*)lodepng_malloc(passstart[7]); + if(!adam7 && passstart[7]) error = 83; /*alloc fail*/ + + if(!error) { + unsigned i; + + Adam7_interlace(adam7, in, w, h, bpp); + for(i = 0; i != 7; ++i) { + if(bpp < 8) { + unsigned char* padded = (unsigned char*)lodepng_malloc(padded_passstart[i + 1] - padded_passstart[i]); + if(!padded) ERROR_BREAK(83); /*alloc fail*/ + addPaddingBits(padded, &adam7[passstart[i]], + ((passw[i] * bpp + 7u) / 8u) * 8u, passw[i] * bpp, passh[i]); + error = filter(&(*out)[filter_passstart[i]], padded, + passw[i], passh[i], &info_png->color, settings); + lodepng_free(padded); + } else { + error = filter(&(*out)[filter_passstart[i]], &adam7[padded_passstart[i]], + passw[i], passh[i], &info_png->color, settings); + } + + if(error) break; + } + } + + lodepng_free(adam7); + } + + return error; +} + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t datasize) { + unsigned char* inchunk = data; + while((size_t)(inchunk - data) < datasize) { + CERROR_TRY_RETURN(lodepng_chunk_append(&out->data, &out->size, inchunk)); + out->allocsize = out->size; /*fix the allocsize again*/ + inchunk = lodepng_chunk_next(inchunk, data + datasize); + } + return 0; +} + +static unsigned isGrayICCProfile(const unsigned char* profile, unsigned size) { + /* + It is a gray profile if bytes 16-19 are "GRAY", rgb profile if bytes 16-19 + are "RGB ". We do not perform any full parsing of the ICC profile here, other + than check those 4 bytes to grayscale profile. Other than that, validity of + the profile is not checked. This is needed only because the PNG specification + requires using a non-gray color model if there is an ICC profile with "RGB " + (sadly limiting compression opportunities if the input data is grayscale RGB + data), and requires using a gray color model if it is "GRAY". + */ + if(size < 20) return 0; + return profile[16] == 'G' && profile[17] == 'R' && profile[18] == 'A' && profile[19] == 'Y'; +} + +static unsigned isRGBICCProfile(const unsigned char* profile, unsigned size) { + /* See comment in isGrayICCProfile*/ + if(size < 20) return 0; + return profile[16] == 'R' && profile[17] == 'G' && profile[18] == 'B' && profile[19] == ' '; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +unsigned lodepng_encode(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGState* state) { + unsigned char* data = 0; /*uncompressed version of the IDAT chunk data*/ + size_t datasize = 0; + ucvector outv = ucvector_init(NULL, 0); + LodePNGInfo info; + const LodePNGInfo* info_png = &state->info_png; + + lodepng_info_init(&info); + + /*provide some proper output values if error will happen*/ + *out = 0; + *outsize = 0; + state->error = 0; + + /*check input values validity*/ + if((info_png->color.colortype == LCT_PALETTE || state->encoder.force_palette) + && (info_png->color.palettesize == 0 || info_png->color.palettesize > 256)) { + state->error = 68; /*invalid palette size, it is only allowed to be 1-256*/ + goto cleanup; + } + if(state->encoder.zlibsettings.btype > 2) { + state->error = 61; /*error: invalid btype*/ + goto cleanup; + } + if(info_png->interlace_method > 1) { + state->error = 71; /*error: invalid interlace mode*/ + goto cleanup; + } + state->error = checkColorValidity(info_png->color.colortype, info_png->color.bitdepth); + if(state->error) goto cleanup; /*error: invalid color type given*/ + state->error = checkColorValidity(state->info_raw.colortype, state->info_raw.bitdepth); + if(state->error) goto cleanup; /*error: invalid color type given*/ + + /* color convert and compute scanline filter types */ + lodepng_info_copy(&info, &state->info_png); + if(state->encoder.auto_convert) { + LodePNGColorStats stats; + lodepng_color_stats_init(&stats); +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + if(info_png->iccp_defined && + isGrayICCProfile(info_png->iccp_profile, info_png->iccp_profile_size)) { + /*the PNG specification does not allow to use palette with a GRAY ICC profile, even + if the palette has only gray colors, so disallow it.*/ + stats.allow_palette = 0; + } + if(info_png->iccp_defined && + isRGBICCProfile(info_png->iccp_profile, info_png->iccp_profile_size)) { + /*the PNG specification does not allow to use grayscale color with RGB ICC profile, so disallow gray.*/ + stats.allow_greyscale = 0; + } +#endif /* LODEPNG_COMPILE_ANCILLARY_CHUNKS */ + state->error = lodepng_compute_color_stats(&stats, image, w, h, &state->info_raw); + if(state->error) goto cleanup; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + if(info_png->background_defined) { + /*the background chunk's color must be taken into account as well*/ + unsigned r = 0, g = 0, b = 0; + LodePNGColorMode mode16 = lodepng_color_mode_make(LCT_RGB, 16); + lodepng_convert_rgb(&r, &g, &b, info_png->background_r, info_png->background_g, info_png->background_b, &mode16, &info_png->color); + state->error = lodepng_color_stats_add(&stats, r, g, b, 65535); + if(state->error) goto cleanup; + } +#endif /* LODEPNG_COMPILE_ANCILLARY_CHUNKS */ + state->error = auto_choose_color(&info.color, &state->info_raw, &stats); + if(state->error) goto cleanup; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*also convert the background chunk*/ + if(info_png->background_defined) { + if(lodepng_convert_rgb(&info.background_r, &info.background_g, &info.background_b, + info_png->background_r, info_png->background_g, info_png->background_b, &info.color, &info_png->color)) { + state->error = 104; + goto cleanup; + } + } +#endif /* LODEPNG_COMPILE_ANCILLARY_CHUNKS */ + } +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + if(info_png->iccp_defined) { + unsigned gray_icc = isGrayICCProfile(info_png->iccp_profile, info_png->iccp_profile_size); + unsigned rgb_icc = isRGBICCProfile(info_png->iccp_profile, info_png->iccp_profile_size); + unsigned gray_png = info.color.colortype == LCT_GREY || info.color.colortype == LCT_GREY_ALPHA; + if(!gray_icc && !rgb_icc) { + state->error = 100; /* Disallowed profile color type for PNG */ + goto cleanup; + } + if(gray_icc != gray_png) { + /*Not allowed to use RGB/RGBA/palette with GRAY ICC profile or vice versa, + or in case of auto_convert, it wasn't possible to find appropriate model*/ + state->error = state->encoder.auto_convert ? 102 : 101; + goto cleanup; + } + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) { + unsigned char* converted; + size_t size = ((size_t)w * (size_t)h * (size_t)lodepng_get_bpp(&info.color) + 7u) / 8u; + + converted = (unsigned char*)lodepng_malloc(size); + if(!converted && size) state->error = 83; /*alloc fail*/ + if(!state->error) { + state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h); + } + if(!state->error) { + state->error = preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder); + } + lodepng_free(converted); + if(state->error) goto cleanup; + } else { + state->error = preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder); + if(state->error) goto cleanup; + } + + /* output all PNG chunks */ { +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + size_t i; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*write signature and chunks*/ + state->error = writeSignature(&outv); + if(state->error) goto cleanup; + /*IHDR*/ + state->error = addChunk_IHDR(&outv, w, h, info.color.colortype, info.color.bitdepth, info.interlace_method); + if(state->error) goto cleanup; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*unknown chunks between IHDR and PLTE*/ + if(info.unknown_chunks_data[0]) { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[0], info.unknown_chunks_size[0]); + if(state->error) goto cleanup; + } + /*color profile chunks must come before PLTE */ + if(info.iccp_defined) { + state->error = addChunk_iCCP(&outv, &info, &state->encoder.zlibsettings); + if(state->error) goto cleanup; + } + if(info.srgb_defined) { + state->error = addChunk_sRGB(&outv, &info); + if(state->error) goto cleanup; + } + if(info.gama_defined) { + state->error = addChunk_gAMA(&outv, &info); + if(state->error) goto cleanup; + } + if(info.chrm_defined) { + state->error = addChunk_cHRM(&outv, &info); + if(state->error) goto cleanup; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*PLTE*/ + if(info.color.colortype == LCT_PALETTE) { + state->error = addChunk_PLTE(&outv, &info.color); + if(state->error) goto cleanup; + } + if(state->encoder.force_palette && (info.color.colortype == LCT_RGB || info.color.colortype == LCT_RGBA)) { + /*force_palette means: write suggested palette for truecolor in PLTE chunk*/ + state->error = addChunk_PLTE(&outv, &info.color); + if(state->error) goto cleanup; + } + /*tRNS (this will only add if when necessary) */ + state->error = addChunk_tRNS(&outv, &info.color); + if(state->error) goto cleanup; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*bKGD (must come between PLTE and the IDAt chunks*/ + if(info.background_defined) { + state->error = addChunk_bKGD(&outv, &info); + if(state->error) goto cleanup; + } + /*pHYs (must come before the IDAT chunks)*/ + if(info.phys_defined) { + state->error = addChunk_pHYs(&outv, &info); + if(state->error) goto cleanup; + } + + /*unknown chunks between PLTE and IDAT*/ + if(info.unknown_chunks_data[1]) { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[1], info.unknown_chunks_size[1]); + if(state->error) goto cleanup; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*IDAT (multiple IDAT chunks must be consecutive)*/ + state->error = addChunk_IDAT(&outv, data, datasize, &state->encoder.zlibsettings); + if(state->error) goto cleanup; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*tIME*/ + if(info.time_defined) { + state->error = addChunk_tIME(&outv, &info.time); + if(state->error) goto cleanup; + } + /*tEXt and/or zTXt*/ + for(i = 0; i != info.text_num; ++i) { + if(lodepng_strlen(info.text_keys[i]) > 79) { + state->error = 66; /*text chunk too large*/ + goto cleanup; + } + if(lodepng_strlen(info.text_keys[i]) < 1) { + state->error = 67; /*text chunk too small*/ + goto cleanup; + } + if(state->encoder.text_compression) { + state->error = addChunk_zTXt(&outv, info.text_keys[i], info.text_strings[i], &state->encoder.zlibsettings); + if(state->error) goto cleanup; + } else { + state->error = addChunk_tEXt(&outv, info.text_keys[i], info.text_strings[i]); + if(state->error) goto cleanup; + } + } + /*LodePNG version id in text chunk*/ + if(state->encoder.add_id) { + unsigned already_added_id_text = 0; + for(i = 0; i != info.text_num; ++i) { + const char* k = info.text_keys[i]; + /* Could use strcmp, but we're not calling or reimplementing this C library function for this use only */ + if(k[0] == 'L' && k[1] == 'o' && k[2] == 'd' && k[3] == 'e' && + k[4] == 'P' && k[5] == 'N' && k[6] == 'G' && k[7] == '\0') { + already_added_id_text = 1; + break; + } + } + if(already_added_id_text == 0) { + state->error = addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/ + if(state->error) goto cleanup; + } + } + /*iTXt*/ + for(i = 0; i != info.itext_num; ++i) { + if(lodepng_strlen(info.itext_keys[i]) > 79) { + state->error = 66; /*text chunk too large*/ + goto cleanup; + } + if(lodepng_strlen(info.itext_keys[i]) < 1) { + state->error = 67; /*text chunk too small*/ + goto cleanup; + } + state->error = addChunk_iTXt( + &outv, state->encoder.text_compression, + info.itext_keys[i], info.itext_langtags[i], info.itext_transkeys[i], info.itext_strings[i], + &state->encoder.zlibsettings); + if(state->error) goto cleanup; + } + + /*unknown chunks between IDAT and IEND*/ + if(info.unknown_chunks_data[2]) { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[2], info.unknown_chunks_size[2]); + if(state->error) goto cleanup; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + state->error = addChunk_IEND(&outv); + if(state->error) goto cleanup; + } + +cleanup: + lodepng_info_cleanup(&info); + lodepng_free(data); + + /*instead of cleaning the vector up, give it to the output*/ + *out = outv.data; + *outsize = outv.size; + + return state->error; +} + +unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, const unsigned char* image, + unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { + unsigned error; + LodePNGState state; + lodepng_state_init(&state); + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; + state.info_png.color.colortype = colortype; + state.info_png.color.bitdepth = bitdepth; + lodepng_encode(out, outsize, image, w, h, &state); + error = state.error; + lodepng_state_cleanup(&state); + return error; +} + +unsigned lodepng_encode32(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) { + return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGBA, 8); +} + +unsigned lodepng_encode24(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) { + return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGB, 8); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned lodepng_encode_file(const char* filename, const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) { + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode_memory(&buffer, &buffersize, image, w, h, colortype, bitdepth); + if(!error) error = lodepng_save_file(buffer, buffersize, filename); + lodepng_free(buffer); + return error; +} + +unsigned lodepng_encode32_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) { + return lodepng_encode_file(filename, image, w, h, LCT_RGBA, 8); +} + +unsigned lodepng_encode24_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) { + return lodepng_encode_file(filename, image, w, h, LCT_RGB, 8); +} +#endif /*LODEPNG_COMPILE_DISK*/ + +void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings) { + lodepng_compress_settings_init(&settings->zlibsettings); + settings->filter_palette_zero = 1; + settings->filter_strategy = LFS_MINSUM; + settings->auto_convert = 1; + settings->force_palette = 0; + settings->predefined_filters = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + settings->add_id = 0; + settings->text_compression = 1; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ERROR_TEXT +/* +This returns the description of a numerical error code in English. This is also +the documentation of all the error codes. +*/ +const char* lodepng_error_text(unsigned code) { + switch(code) { + case 0: return "no error, everything went ok"; + case 1: return "nothing done yet"; /*the Encoder/Decoder has done nothing yet, error checking makes no sense yet*/ + case 10: return "end of input memory reached without huffman end code"; /*while huffman decoding*/ + case 11: return "error in code tree made it jump outside of huffman tree"; /*while huffman decoding*/ + case 13: return "problem while processing dynamic deflate block"; + case 14: return "problem while processing dynamic deflate block"; + case 15: return "problem while processing dynamic deflate block"; + /*this error could happen if there are only 0 or 1 symbols present in the huffman code:*/ + case 16: return "invalid code while processing dynamic deflate block"; + case 17: return "end of out buffer memory reached while inflating"; + case 18: return "invalid distance code while inflating"; + case 19: return "end of out buffer memory reached while inflating"; + case 20: return "invalid deflate block BTYPE encountered while decoding"; + case 21: return "NLEN is not ones complement of LEN in a deflate block"; + + /*end of out buffer memory reached while inflating: + This can happen if the inflated deflate data is longer than the amount of bytes required to fill up + all the pixels of the image, given the color depth and image dimensions. Something that doesn't + happen in a normal, well encoded, PNG image.*/ + case 22: return "end of out buffer memory reached while inflating"; + case 23: return "end of in buffer memory reached while inflating"; + case 24: return "invalid FCHECK in zlib header"; + case 25: return "invalid compression method in zlib header"; + case 26: return "FDICT encountered in zlib header while it's not used for PNG"; + case 27: return "PNG file is smaller than a PNG header"; + /*Checks the magic file header, the first 8 bytes of the PNG file*/ + case 28: return "incorrect PNG signature, it's no PNG or corrupted"; + case 29: return "first chunk is not the header chunk"; + case 30: return "chunk length too large, chunk broken off at end of file"; + case 31: return "illegal PNG color type or bpp"; + case 32: return "illegal PNG compression method"; + case 33: return "illegal PNG filter method"; + case 34: return "illegal PNG interlace method"; + case 35: return "chunk length of a chunk is too large or the chunk too small"; + case 36: return "illegal PNG filter type encountered"; + case 37: return "illegal bit depth for this color type given"; + case 38: return "the palette is too small or too big"; /*0, or more than 256 colors*/ + case 39: return "tRNS chunk before PLTE or has more entries than palette size"; + case 40: return "tRNS chunk has wrong size for grayscale image"; + case 41: return "tRNS chunk has wrong size for RGB image"; + case 42: return "tRNS chunk appeared while it was not allowed for this color type"; + case 43: return "bKGD chunk has wrong size for palette image"; + case 44: return "bKGD chunk has wrong size for grayscale image"; + case 45: return "bKGD chunk has wrong size for RGB image"; + case 48: return "empty input buffer given to decoder. Maybe caused by non-existing file?"; + case 49: return "jumped past memory while generating dynamic huffman tree"; + case 50: return "jumped past memory while generating dynamic huffman tree"; + case 51: return "jumped past memory while inflating huffman block"; + case 52: return "jumped past memory while inflating"; + case 53: return "size of zlib data too small"; + case 54: return "repeat symbol in tree while there was no value symbol yet"; + /*jumped past tree while generating huffman tree, this could be when the + tree will have more leaves than symbols after generating it out of the + given lengths. They call this an oversubscribed dynamic bit lengths tree in zlib.*/ + case 55: return "jumped past tree while generating huffman tree"; + case 56: return "given output image colortype or bitdepth not supported for color conversion"; + case 57: return "invalid CRC encountered (checking CRC can be disabled)"; + case 58: return "invalid ADLER32 encountered (checking ADLER32 can be disabled)"; + case 59: return "requested color conversion not supported"; + case 60: return "invalid window size given in the settings of the encoder (must be 0-32768)"; + case 61: return "invalid BTYPE given in the settings of the encoder (only 0, 1 and 2 are allowed)"; + /*LodePNG leaves the choice of RGB to grayscale conversion formula to the user.*/ + case 62: return "conversion from color to grayscale not supported"; + /*(2^31-1)*/ + case 63: return "length of a chunk too long, max allowed for PNG is 2147483647 bytes per chunk"; + /*this would result in the inability of a deflated block to ever contain an end code. It must be at least 1.*/ + case 64: return "the length of the END symbol 256 in the Huffman tree is 0"; + case 66: return "the length of a text chunk keyword given to the encoder is longer than the maximum of 79 bytes"; + case 67: return "the length of a text chunk keyword given to the encoder is smaller than the minimum of 1 byte"; + case 68: return "tried to encode a PLTE chunk with a palette that has less than 1 or more than 256 colors"; + case 69: return "unknown chunk type with 'critical' flag encountered by the decoder"; + case 71: return "invalid interlace mode given to encoder (must be 0 or 1)"; + case 72: return "while decoding, invalid compression method encountering in zTXt or iTXt chunk (it must be 0)"; + case 73: return "invalid tIME chunk size"; + case 74: return "invalid pHYs chunk size"; + /*length could be wrong, or data chopped off*/ + case 75: return "no null termination char found while decoding text chunk"; + case 76: return "iTXt chunk too short to contain required bytes"; + case 77: return "integer overflow in buffer size"; + case 78: return "failed to open file for reading"; /*file doesn't exist or couldn't be opened for reading*/ + case 79: return "failed to open file for writing"; + case 80: return "tried creating a tree of 0 symbols"; + case 81: return "lazy matching at pos 0 is impossible"; + case 82: return "color conversion to palette requested while a color isn't in palette, or index out of bounds"; + case 83: return "memory allocation failed"; + case 84: return "given image too small to contain all pixels to be encoded"; + case 86: return "impossible offset in lz77 encoding (internal bug)"; + case 87: return "must provide custom zlib function pointer if LODEPNG_COMPILE_ZLIB is not defined"; + case 88: return "invalid filter strategy given for LodePNGEncoderSettings.filter_strategy"; + case 89: return "text chunk keyword too short or long: must have size 1-79"; + /*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/ + case 90: return "windowsize must be a power of two"; + case 91: return "invalid decompressed idat size"; + case 92: return "integer overflow due to too many pixels"; + case 93: return "zero width or height is invalid"; + case 94: return "header chunk must have a size of 13 bytes"; + case 95: return "integer overflow with combined idat chunk size"; + case 96: return "invalid gAMA chunk size"; + case 97: return "invalid cHRM chunk size"; + case 98: return "invalid sRGB chunk size"; + case 99: return "invalid sRGB rendering intent"; + case 100: return "invalid ICC profile color type, the PNG specification only allows RGB or GRAY"; + case 101: return "PNG specification does not allow RGB ICC profile on gray color types and vice versa"; + case 102: return "not allowed to set grayscale ICC profile with colored pixels by PNG specification"; + case 103: return "invalid palette index in bKGD chunk. Maybe it came before PLTE chunk?"; + case 104: return "invalid bKGD color while encoding (e.g. palette index out of range)"; + case 105: return "integer overflow of bitsize"; + case 106: return "PNG file must have PLTE chunk if color type is palette"; + case 107: return "color convert from palette mode requested without setting the palette data in it"; + case 108: return "tried to add more than 256 values to a palette"; + /*this limit can be configured in LodePNGDecompressSettings*/ + case 109: return "tried to decompress zlib or deflate data larger than desired max_output_size"; + case 110: return "custom zlib or inflate decompression failed"; + case 111: return "custom zlib or deflate compression failed"; + /*max text size limit can be configured in LodePNGDecoderSettings. This error prevents + unreasonable memory consumption when decoding due to impossibly large text sizes.*/ + case 112: return "compressed text unreasonably large"; + /*max ICC size limit can be configured in LodePNGDecoderSettings. This error prevents + unreasonable memory consumption when decoding due to impossibly large ICC profile*/ + case 113: return "ICC profile unreasonably large"; + } + return "unknown error code"; +} +#endif /*LODEPNG_COMPILE_ERROR_TEXT*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // C++ Wrapper // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_CPP +namespace lodepng { + +#ifdef LODEPNG_COMPILE_DISK +unsigned load_file(std::vector& buffer, const std::string& filename) { + long size = lodepng_filesize(filename.c_str()); + if(size < 0) return 78; + buffer.resize((size_t)size); + return size == 0 ? 0 : lodepng_buffer_file(&buffer[0], (size_t)size, filename.c_str()); +} + +/*write given buffer to the file, overwriting the file, it doesn't append to it.*/ +unsigned save_file(const std::vector& buffer, const std::string& filename) { + return lodepng_save_file(buffer.empty() ? 0 : &buffer[0], buffer.size(), filename.c_str()); +} +#endif /* LODEPNG_COMPILE_DISK */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_DECODER +unsigned decompress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGDecompressSettings& settings) { + unsigned char* buffer = 0; + size_t buffersize = 0; + unsigned error = zlib_decompress(&buffer, &buffersize, 0, in, insize, &settings); + if(buffer) { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned decompress(std::vector& out, const std::vector& in, + const LodePNGDecompressSettings& settings) { + return decompress(out, in.empty() ? 0 : &in[0], in.size(), settings); +} +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +unsigned compress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings& settings) { + unsigned char* buffer = 0; + size_t buffersize = 0; + unsigned error = zlib_compress(&buffer, &buffersize, in, insize, &settings); + if(buffer) { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned compress(std::vector& out, const std::vector& in, + const LodePNGCompressSettings& settings) { + return compress(out, in.empty() ? 0 : &in[0], in.size(), settings); +} +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_ZLIB */ + + +#ifdef LODEPNG_COMPILE_PNG + +State::State() { + lodepng_state_init(this); +} + +State::State(const State& other) { + lodepng_state_init(this); + lodepng_state_copy(this, &other); +} + +State::~State() { + lodepng_state_cleanup(this); +} + +State& State::operator=(const State& other) { + lodepng_state_copy(this, &other); + return *this; +} + +#ifdef LODEPNG_COMPILE_DECODER + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, const unsigned char* in, + size_t insize, LodePNGColorType colortype, unsigned bitdepth) { + unsigned char* buffer = 0; + unsigned error = lodepng_decode_memory(&buffer, &w, &h, in, insize, colortype, bitdepth); + if(buffer && !error) { + State state; + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; + size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + } + lodepng_free(buffer); + return error; +} + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const std::vector& in, LodePNGColorType colortype, unsigned bitdepth) { + return decode(out, w, h, in.empty() ? 0 : &in[0], (unsigned)in.size(), colortype, bitdepth); +} + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const unsigned char* in, size_t insize) { + unsigned char* buffer = NULL; + unsigned error = lodepng_decode(&buffer, &w, &h, &state, in, insize); + if(buffer && !error) { + size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + } + lodepng_free(buffer); + return error; +} + +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const std::vector& in) { + return decode(out, w, h, state, in.empty() ? 0 : &in[0], in.size()); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned decode(std::vector& out, unsigned& w, unsigned& h, const std::string& filename, + LodePNGColorType colortype, unsigned bitdepth) { + std::vector buffer; + /* safe output values in case error happens */ + w = h = 0; + unsigned error = load_file(buffer, filename); + if(error) return error; + return decode(out, w, h, buffer, colortype, bitdepth); +} +#endif /* LODEPNG_COMPILE_DECODER */ +#endif /* LODEPNG_COMPILE_DISK */ + +#ifdef LODEPNG_COMPILE_ENCODER +unsigned encode(std::vector& out, const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) { + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode_memory(&buffer, &buffersize, in, w, h, colortype, bitdepth); + if(buffer) { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) { + if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; + return encode(out, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); +} + +unsigned encode(std::vector& out, + const unsigned char* in, unsigned w, unsigned h, + State& state) { + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode(&buffer, &buffersize, in, w, h, &state); + if(buffer) { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + State& state) { + if(lodepng_get_raw_size(w, h, &state.info_raw) > in.size()) return 84; + return encode(out, in.empty() ? 0 : &in[0], w, h, state); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned encode(const std::string& filename, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) { + std::vector buffer; + unsigned error = encode(buffer, in, w, h, colortype, bitdepth); + if(!error) error = save_file(buffer, filename); + return error; +} + +unsigned encode(const std::string& filename, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) { + if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; + return encode(filename, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); +} +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_PNG */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ + +#endif /*LV_USE_PNG*/ diff --git a/src/extra/libs/png/lodepng.h b/src/extra/libs/png/lodepng.h new file mode 100644 index 0000000000..dbfed72e5c --- /dev/null +++ b/src/extra/libs/png/lodepng.h @@ -0,0 +1,1981 @@ +/* +LodePNG version 20201017 + +Copyright (c) 2005-2020 Lode Vandevenne + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#ifndef LODEPNG_H +#define LODEPNG_H + +#include /*for size_t*/ + +#include "../../../lvgl.h" +#if LV_USE_PNG +extern const char* LODEPNG_VERSION_STRING; + +/* +The following #defines are used to create code sections. They can be disabled +to disable code sections, which can give faster compile time and smaller binary. +The "NO_COMPILE" defines are designed to be used to pass as defines to the +compiler command to disable them without modifying this header, e.g. +-DLODEPNG_NO_COMPILE_ZLIB for gcc. +In addition to those below, you can also define LODEPNG_NO_COMPILE_CRC to +allow implementing a custom lodepng_crc32. +*/ +/*deflate & zlib. If disabled, you must specify alternative zlib functions in +the custom_zlib field of the compress and decompress settings*/ +#ifndef LODEPNG_NO_COMPILE_ZLIB +#define LODEPNG_COMPILE_ZLIB +#endif + +/*png encoder and png decoder*/ +#ifndef LODEPNG_NO_COMPILE_PNG +#define LODEPNG_COMPILE_PNG +#endif + +/*deflate&zlib decoder and png decoder*/ +#ifndef LODEPNG_NO_COMPILE_DECODER +#define LODEPNG_COMPILE_DECODER +#endif + +/*deflate&zlib encoder and png encoder*/ +#ifndef LODEPNG_NO_COMPILE_ENCODER +#define LODEPNG_COMPILE_ENCODER +#endif + +/*the optional built in harddisk file loading and saving functions*/ +#ifndef LODEPNG_NO_COMPILE_DISK +#define LODEPNG_COMPILE_DISK +#endif + +/*support for chunks other than IHDR, IDAT, PLTE, tRNS, IEND: ancillary and unknown chunks*/ +#ifndef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS +#define LODEPNG_COMPILE_ANCILLARY_CHUNKS +#endif + +/*ability to convert error numerical codes to English text string*/ +#ifndef LODEPNG_NO_COMPILE_ERROR_TEXT +#define LODEPNG_COMPILE_ERROR_TEXT +#endif + +/*Compile the default allocators (C's free, malloc and realloc). If you disable this, +you can define the functions lodepng_free, lodepng_malloc and lodepng_realloc in your +source files with custom allocators.*/ +#ifndef LODEPNG_NO_COMPILE_ALLOCATORS +#define LODEPNG_COMPILE_ALLOCATORS +#endif + +/*compile the C++ version (you can disable the C++ wrapper here even when compiling for C++)*/ +#ifdef __cplusplus +#ifndef LODEPNG_NO_COMPILE_CPP +#define LODEPNG_COMPILE_CPP +#endif +#endif + +#ifdef LODEPNG_COMPILE_CPP +#include +#include +#endif /*LODEPNG_COMPILE_CPP*/ + +#ifdef LODEPNG_COMPILE_PNG +/*The PNG color types (also used for raw image).*/ +typedef enum LodePNGColorType { + LCT_GREY = 0, /*grayscale: 1,2,4,8,16 bit*/ + LCT_RGB = 2, /*RGB: 8,16 bit*/ + LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/ + LCT_GREY_ALPHA = 4, /*grayscale with alpha: 8,16 bit*/ + LCT_RGBA = 6, /*RGB with alpha: 8,16 bit*/ + /*LCT_MAX_OCTET_VALUE lets the compiler allow this enum to represent any invalid + byte value from 0 to 255 that could be present in an invalid PNG file header. Do + not use, compare with or set the name LCT_MAX_OCTET_VALUE, instead either use + the valid color type names above, or numeric values like 1 or 7 when checking for + particular disallowed color type byte values, or cast to integer to print it.*/ + LCT_MAX_OCTET_VALUE = 255 +} LodePNGColorType; + +#ifdef LODEPNG_COMPILE_DECODER +/* +Converts PNG data in memory to raw pixel data. +out: Output parameter. Pointer to buffer that will contain the raw pixel data. + After decoding, its size is w * h * (bytes per pixel) bytes larger than + initially. Bytes per pixel depends on colortype and bitdepth. + Must be freed after usage with free(*out). + Note: for 16-bit per channel colors, uses big endian format like PNG does. +w: Output parameter. Pointer to width of pixel data. +h: Output parameter. Pointer to height of pixel data. +in: Memory buffer with the PNG file. +insize: size of the in buffer. +colortype: the desired color type for the raw output image. See explanation on PNG color types. +bitdepth: the desired bit depth for the raw output image. See explanation on PNG color types. +Return value: LodePNG error code (0 means no error). +*/ +unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_decode_memory, but always decodes to 32-bit RGBA raw image*/ +unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize); + +/*Same as lodepng_decode_memory, but always decodes to 24-bit RGB raw image*/ +unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize); + +#ifdef LODEPNG_COMPILE_DISK +/* +Load PNG from disk, from file with given name. +Same as the other decode functions, but instead takes a filename as input. +*/ +unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.*/ +unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename); + +/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/ +unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename); +#endif /*LODEPNG_COMPILE_DISK*/ +#endif /*LODEPNG_COMPILE_DECODER*/ + + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Converts raw pixel data into a PNG image in memory. The colortype and bitdepth + of the output PNG image cannot be chosen, they are automatically determined + by the colortype, bitdepth and content of the input pixel data. + Note: for 16-bit per channel colors, needs big endian format like PNG does. +out: Output parameter. Pointer to buffer that will contain the PNG image data. + Must be freed after usage with free(*out). +outsize: Output parameter. Pointer to the size in bytes of the out buffer. +image: The raw pixel data to encode. The size of this buffer should be + w * h * (bytes per pixel), bytes per pixel depends on colortype and bitdepth. +w: width of the raw pixel data in pixels. +h: height of the raw pixel data in pixels. +colortype: the color type of the raw input image. See explanation on PNG color types. +bitdepth: the bit depth of the raw input image. See explanation on PNG color types. +Return value: LodePNG error code (0 means no error). +*/ +unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_encode_memory, but always encodes from 32-bit RGBA raw image.*/ +unsigned lodepng_encode32(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h); + +/*Same as lodepng_encode_memory, but always encodes from 24-bit RGB raw image.*/ +unsigned lodepng_encode24(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h); + +#ifdef LODEPNG_COMPILE_DISK +/* +Converts raw pixel data into a PNG file on disk. +Same as the other encode functions, but instead takes a filename as output. +NOTE: This overwrites existing files without warning! +*/ +unsigned lodepng_encode_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.*/ +unsigned lodepng_encode32_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h); + +/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/ +unsigned lodepng_encode24_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h); +#endif /*LODEPNG_COMPILE_DISK*/ +#endif /*LODEPNG_COMPILE_ENCODER*/ + + +#ifdef LODEPNG_COMPILE_CPP +namespace lodepng { +#ifdef LODEPNG_COMPILE_DECODER +/*Same as lodepng_decode_memory, but decodes to an std::vector. The colortype +is the format to output the pixels to. Default is RGBA 8-bit per channel.*/ +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const unsigned char* in, size_t insize, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const std::vector& in, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#ifdef LODEPNG_COMPILE_DISK +/* +Converts PNG file from disk to raw pixel data in memory. +Same as the other decode functions, but instead takes a filename as input. +*/ +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + const std::string& filename, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +/*Same as lodepng_encode_memory, but encodes to an std::vector. colortype +is that of the raw input data. The output PNG color type will be auto chosen.*/ +unsigned encode(std::vector& out, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#ifdef LODEPNG_COMPILE_DISK +/* +Converts 32-bit RGBA raw pixel data into a PNG file on disk. +Same as the other encode functions, but instead takes a filename as output. +NOTE: This overwrites existing files without warning! +*/ +unsigned encode(const std::string& filename, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned encode(const std::string& filename, + const std::vector& in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_ENCODER */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ERROR_TEXT +/*Returns an English description of the numerical error code.*/ +const char* lodepng_error_text(unsigned code); +#endif /*LODEPNG_COMPILE_ERROR_TEXT*/ + +#ifdef LODEPNG_COMPILE_DECODER +/*Settings for zlib decompression*/ +typedef struct LodePNGDecompressSettings LodePNGDecompressSettings; +struct LodePNGDecompressSettings { + /* Check LodePNGDecoderSettings for more ignorable errors such as ignore_crc */ + unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/ + unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/ + + /*Maximum decompressed size, beyond this the decoder may (and is encouraged to) stop decoding, + return an error, output a data size > max_output_size and all the data up to that point. This is + not hard limit nor a guarantee, but can prevent excessive memory usage. This setting is + ignored by the PNG decoder, but is used by the deflate/zlib decoder and can be used by custom ones. + Set to 0 to impose no limit (the default).*/ + size_t max_output_size; + + /*use custom zlib decoder instead of built in one (default: null). + Should return 0 if success, any non-0 if error (numeric value not exposed).*/ + unsigned (*custom_zlib)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGDecompressSettings*); + /*use custom deflate decoder instead of built in one (default: null) + if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate). + Should return 0 if success, any non-0 if error (numeric value not exposed).*/ + unsigned (*custom_inflate)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGDecompressSettings*); + + const void* custom_context; /*optional custom settings for custom functions*/ +}; + +extern const LodePNGDecompressSettings lodepng_default_decompress_settings; +void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Settings for zlib compression. Tweaking these settings tweaks the balance +between speed and compression ratio. +*/ +typedef struct LodePNGCompressSettings LodePNGCompressSettings; +struct LodePNGCompressSettings /*deflate = compress*/ { + /*LZ77 related settings*/ + unsigned btype; /*the block type for LZ (0, 1, 2 or 3, see zlib standard). Should be 2 for proper compression.*/ + unsigned use_lz77; /*whether or not to use LZ77. Should be 1 for proper compression.*/ + unsigned windowsize; /*must be a power of two <= 32768. higher compresses more but is slower. Default value: 2048.*/ + unsigned minmatch; /*minimum lz77 length. 3 is normally best, 6 can be better for some PNGs. Default: 0*/ + unsigned nicematch; /*stop searching if >= this length found. Set to 258 for best compression. Default: 128*/ + unsigned lazymatching; /*use lazy matching: better compression but a bit slower. Default: true*/ + + /*use custom zlib encoder instead of built in one (default: null)*/ + unsigned (*custom_zlib)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGCompressSettings*); + /*use custom deflate encoder instead of built in one (default: null) + if custom_zlib is used, custom_deflate is ignored since only the built in + zlib function will call custom_deflate*/ + unsigned (*custom_deflate)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGCompressSettings*); + + const void* custom_context; /*optional custom settings for custom functions*/ +}; + +extern const LodePNGCompressSettings lodepng_default_compress_settings; +void lodepng_compress_settings_init(LodePNGCompressSettings* settings); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_PNG +/* +Color mode of an image. Contains all information required to decode the pixel +bits to RGBA colors. This information is the same as used in the PNG file +format, and is used both for PNG and raw image data in LodePNG. +*/ +typedef struct LodePNGColorMode { + /*header (IHDR)*/ + LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/ + unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/ + + /* + palette (PLTE and tRNS) + + Dynamically allocated with the colors of the palette, including alpha. + This field may not be allocated directly, use lodepng_color_mode_init first, + then lodepng_palette_add per color to correctly initialize it (to ensure size + of exactly 1024 bytes). + + The alpha channels must be set as well, set them to 255 for opaque images. + + When decoding, by default you can ignore this palette, since LodePNG already + fills the palette colors in the pixels of the raw RGBA output. + + The palette is only supported for color type 3. + */ + unsigned char* palette; /*palette in RGBARGBA... order. Must be either 0, or when allocated must have 1024 bytes*/ + size_t palettesize; /*palette size in number of colors (amount of used bytes is 4 * palettesize)*/ + + /* + transparent color key (tRNS) + + This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit. + For grayscale PNGs, r, g and b will all 3 be set to the same. + + When decoding, by default you can ignore this information, since LodePNG sets + pixels with this key to transparent already in the raw RGBA output. + + The color key is only supported for color types 0 and 2. + */ + unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/ + unsigned key_r; /*red/grayscale component of color key*/ + unsigned key_g; /*green component of color key*/ + unsigned key_b; /*blue component of color key*/ +} LodePNGColorMode; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_color_mode_init(LodePNGColorMode* info); +void lodepng_color_mode_cleanup(LodePNGColorMode* info); +/*return value is error code (0 means no error)*/ +unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source); +/* Makes a temporary LodePNGColorMode that does not need cleanup (no palette) */ +LodePNGColorMode lodepng_color_mode_make(LodePNGColorType colortype, unsigned bitdepth); + +void lodepng_palette_clear(LodePNGColorMode* info); +/*add 1 color to the palette*/ +unsigned lodepng_palette_add(LodePNGColorMode* info, + unsigned char r, unsigned char g, unsigned char b, unsigned char a); + +/*get the total amount of bits per pixel, based on colortype and bitdepth in the struct*/ +unsigned lodepng_get_bpp(const LodePNGColorMode* info); +/*get the amount of color channels used, based on colortype in the struct. +If a palette is used, it counts as 1 channel.*/ +unsigned lodepng_get_channels(const LodePNGColorMode* info); +/*is it a grayscale type? (only colortype 0 or 4)*/ +unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info); +/*has it got an alpha channel? (only colortype 2 or 6)*/ +unsigned lodepng_is_alpha_type(const LodePNGColorMode* info); +/*has it got a palette? (only colortype 3)*/ +unsigned lodepng_is_palette_type(const LodePNGColorMode* info); +/*only returns true if there is a palette and there is a value in the palette with alpha < 255. +Loops through the palette to check this.*/ +unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info); +/* +Check if the given color info indicates the possibility of having non-opaque pixels in the PNG image. +Returns true if the image can have translucent or invisible pixels (it still be opaque if it doesn't use such pixels). +Returns false if the image can only have opaque pixels. +In detail, it returns true only if it's a color type with alpha, or has a palette with non-opaque values, +or if "key_defined" is true. +*/ +unsigned lodepng_can_have_alpha(const LodePNGColorMode* info); +/*Returns the byte size of a raw image buffer with given width, height and color mode*/ +size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*The information of a Time chunk in PNG.*/ +typedef struct LodePNGTime { + unsigned year; /*2 bytes used (0-65535)*/ + unsigned month; /*1-12*/ + unsigned day; /*1-31*/ + unsigned hour; /*0-23*/ + unsigned minute; /*0-59*/ + unsigned second; /*0-60 (to allow for leap seconds)*/ +} LodePNGTime; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/*Information about the PNG image, except pixels, width and height.*/ +typedef struct LodePNGInfo { + /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/ + unsigned compression_method;/*compression method of the original file. Always 0.*/ + unsigned filter_method; /*filter method of the original file*/ + unsigned interlace_method; /*interlace method of the original file: 0=none, 1=Adam7*/ + LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /* + Suggested background color chunk (bKGD) + + This uses the same color mode and bit depth as the PNG (except no alpha channel), + with values truncated to the bit depth in the unsigned integer. + + For grayscale and palette PNGs, the value is stored in background_r. The values + in background_g and background_b are then unused. + + So when decoding, you may get these in a different color mode than the one you requested + for the raw pixels. + + When encoding with auto_convert, you must use the color model defined in info_png.color for + these values. The encoder normally ignores info_png.color when auto_convert is on, but will + use it to interpret these values (and convert copies of them to its chosen color model). + + When encoding, avoid setting this to an expensive color, such as a non-gray value + when the image is gray, or the compression will be worse since it will be forced to + write the PNG with a more expensive color mode (when auto_convert is on). + + The decoder does not use this background color to edit the color of pixels. This is a + completely optional metadata feature. + */ + unsigned background_defined; /*is a suggested background color given?*/ + unsigned background_r; /*red/gray/palette component of suggested background color*/ + unsigned background_g; /*green component of suggested background color*/ + unsigned background_b; /*blue component of suggested background color*/ + + /* + Non-international text chunks (tEXt and zTXt) + + The char** arrays each contain num strings. The actual messages are in + text_strings, while text_keys are keywords that give a short description what + the actual text represents, e.g. Title, Author, Description, or anything else. + + All the string fields below including strings, keys, names and language tags are null terminated. + The PNG specification uses null characters for the keys, names and tags, and forbids null + characters to appear in the main text which is why we can use null termination everywhere here. + + A keyword is minimum 1 character and maximum 79 characters long (plus the + additional null terminator). It's discouraged to use a single line length + longer than 79 characters for texts. + + Don't allocate these text buffers yourself. Use the init/cleanup functions + correctly and use lodepng_add_text and lodepng_clear_text. + + Standard text chunk keywords and strings are encoded using Latin-1. + */ + size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/ + char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/ + char** text_strings; /*the actual text*/ + + /* + International text chunks (iTXt) + Similar to the non-international text chunks, but with additional strings + "langtags" and "transkeys", and the following text encodings are used: + keys: Latin-1, langtags: ASCII, transkeys and strings: UTF-8. + keys must be 1-79 characters (plus the additional null terminator), the other + strings are any length. + */ + size_t itext_num; /*the amount of international texts in this PNG*/ + char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/ + char** itext_langtags; /*language tag for this text's language, ISO/IEC 646 string, e.g. ISO 639 language tag*/ + char** itext_transkeys; /*keyword translated to the international language - UTF-8 string*/ + char** itext_strings; /*the actual international text - UTF-8 string*/ + + /*time chunk (tIME)*/ + unsigned time_defined; /*set to 1 to make the encoder generate a tIME chunk*/ + LodePNGTime time; + + /*phys chunk (pHYs)*/ + unsigned phys_defined; /*if 0, there is no pHYs chunk and the values below are undefined, if 1 else there is one*/ + unsigned phys_x; /*pixels per unit in x direction*/ + unsigned phys_y; /*pixels per unit in y direction*/ + unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/ + + /* + Color profile related chunks: gAMA, cHRM, sRGB, iCPP + + LodePNG does not apply any color conversions on pixels in the encoder or decoder and does not interpret these color + profile values. It merely passes on the information. If you wish to use color profiles and convert colors, please + use these values with a color management library. + + See the PNG, ICC and sRGB specifications for more information about the meaning of these values. + */ + + /* gAMA chunk: optional, overridden by sRGB or iCCP if those are present. */ + unsigned gama_defined; /* Whether a gAMA chunk is present (0 = not present, 1 = present). */ + unsigned gama_gamma; /* Gamma exponent times 100000 */ + + /* cHRM chunk: optional, overridden by sRGB or iCCP if those are present. */ + unsigned chrm_defined; /* Whether a cHRM chunk is present (0 = not present, 1 = present). */ + unsigned chrm_white_x; /* White Point x times 100000 */ + unsigned chrm_white_y; /* White Point y times 100000 */ + unsigned chrm_red_x; /* Red x times 100000 */ + unsigned chrm_red_y; /* Red y times 100000 */ + unsigned chrm_green_x; /* Green x times 100000 */ + unsigned chrm_green_y; /* Green y times 100000 */ + unsigned chrm_blue_x; /* Blue x times 100000 */ + unsigned chrm_blue_y; /* Blue y times 100000 */ + + /* + sRGB chunk: optional. May not appear at the same time as iCCP. + If gAMA is also present gAMA must contain value 45455. + If cHRM is also present cHRM must contain respectively 31270,32900,64000,33000,30000,60000,15000,6000. + */ + unsigned srgb_defined; /* Whether an sRGB chunk is present (0 = not present, 1 = present). */ + unsigned srgb_intent; /* Rendering intent: 0=perceptual, 1=rel. colorimetric, 2=saturation, 3=abs. colorimetric */ + + /* + iCCP chunk: optional. May not appear at the same time as sRGB. + + LodePNG does not parse or use the ICC profile (except its color space header field for an edge case), a + separate library to handle the ICC data (not included in LodePNG) format is needed to use it for color + management and conversions. + + For encoding, if iCCP is present, gAMA and cHRM are recommended to be added as well with values that match the ICC + profile as closely as possible, if you wish to do this you should provide the correct values for gAMA and cHRM and + enable their '_defined' flags since LodePNG will not automatically compute them from the ICC profile. + + For encoding, the ICC profile is required by the PNG specification to be an "RGB" profile for non-gray + PNG color types and a "GRAY" profile for gray PNG color types. If you disable auto_convert, you must ensure + the ICC profile type matches your requested color type, else the encoder gives an error. If auto_convert is + enabled (the default), and the ICC profile is not a good match for the pixel data, this will result in an encoder + error if the pixel data has non-gray pixels for a GRAY profile, or a silent less-optimal compression of the pixel + data if the pixels could be encoded as grayscale but the ICC profile is RGB. + + To avoid this do not set an ICC profile in the image unless there is a good reason for it, and when doing so + make sure you compute it carefully to avoid the above problems. + */ + unsigned iccp_defined; /* Whether an iCCP chunk is present (0 = not present, 1 = present). */ + char* iccp_name; /* Null terminated string with profile name, 1-79 bytes */ + /* + The ICC profile in iccp_profile_size bytes. + Don't allocate this buffer yourself. Use the init/cleanup functions + correctly and use lodepng_set_icc and lodepng_clear_icc. + */ + unsigned char* iccp_profile; + unsigned iccp_profile_size; /* The size of iccp_profile in bytes */ + + /* End of color profile related chunks */ + + + /* + unknown chunks: chunks not known by LodePNG, passed on byte for byte. + + There are 3 buffers, one for each position in the PNG where unknown chunks can appear. + Each buffer contains all unknown chunks for that position consecutively. + The 3 positions are: + 0: between IHDR and PLTE, 1: between PLTE and IDAT, 2: between IDAT and IEND. + + For encoding, do not store critical chunks or known chunks that are enabled with a "_defined" flag + above in here, since the encoder will blindly follow this and could then encode an invalid PNG file + (such as one with two IHDR chunks or the disallowed combination of sRGB with iCCP). But do use + this if you wish to store an ancillary chunk that is not supported by LodePNG (such as sPLT or hIST), + or any non-standard PNG chunk. + + Do not allocate or traverse this data yourself. Use the chunk traversing functions declared + later, such as lodepng_chunk_next and lodepng_chunk_append, to read/write this struct. + */ + unsigned char* unknown_chunks_data[3]; + size_t unknown_chunks_size[3]; /*size in bytes of the unknown chunks, given for protection*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGInfo; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_info_init(LodePNGInfo* info); +void lodepng_info_cleanup(LodePNGInfo* info); +/*return value is error code (0 means no error)*/ +unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str); /*push back both texts at once*/ +void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/ + +unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, + const char* transkey, const char* str); /*push back the 4 texts of 1 chunk at once*/ +void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/ + +/*replaces if exists*/ +unsigned lodepng_set_icc(LodePNGInfo* info, const char* name, const unsigned char* profile, unsigned profile_size); +void lodepng_clear_icc(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/* +Converts raw buffer from one color type to another color type, based on +LodePNGColorMode structs to describe the input and output color type. +See the reference manual at the end of this header file to see which color conversions are supported. +return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported) +The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel +of the output color type (lodepng_get_bpp). +For < 8 bpp images, there should not be padding bits at the end of scanlines. +For 16-bit per channel colors, uses big endian format like PNG does. +Return value is LodePNG error code +*/ +unsigned lodepng_convert(unsigned char* out, const unsigned char* in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, + unsigned w, unsigned h); + +#ifdef LODEPNG_COMPILE_DECODER +/* +Settings for the decoder. This contains settings for the PNG and the Zlib +decoder, but not the Info settings from the Info structs. +*/ +typedef struct LodePNGDecoderSettings { + LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/ + + /* Check LodePNGDecompressSettings for more ignorable errors such as ignore_adler32 */ + unsigned ignore_crc; /*ignore CRC checksums*/ + unsigned ignore_critical; /*ignore unknown critical chunks*/ + unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/ + /* TODO: make a system involving warnings with levels and a strict mode instead. Other potentially recoverable + errors: srgb rendering intent value, size of content of ancillary chunks, more than 79 characters for some + strings, placement/combination rules for ancillary chunks, crc of unknown chunks, allowed characters + in string keys, etc... */ + + unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/ + + /*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/ + unsigned remember_unknown_chunks; + + /* maximum size for decompressed text chunks. If a text chunk's text is larger than this, an error is returned, + unless reading text chunks is disabled or this limit is set higher or disabled. Set to 0 to allow any size. + By default it is a value that prevents unreasonably large strings from hogging memory. */ + size_t max_text_size; + + /* maximum size for compressed ICC chunks. If the ICC profile is larger than this, an error will be returned. Set to + 0 to allow any size. By default this is a value that prevents ICC profiles that would be much larger than any + legitimate profile could be to hog memory. */ + size_t max_icc_size; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGDecoderSettings; + +void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/*automatically use color type with less bits per pixel if losslessly possible. Default: AUTO*/ +typedef enum LodePNGFilterStrategy { + /*every filter at zero*/ + LFS_ZERO = 0, + /*every filter at 1, 2, 3 or 4 (paeth), unlike LFS_ZERO not a good choice, but for testing*/ + LFS_ONE = 1, + LFS_TWO = 2, + LFS_THREE = 3, + LFS_FOUR = 4, + /*Use filter that gives minimum sum, as described in the official PNG filter heuristic.*/ + LFS_MINSUM, + /*Use the filter type that gives smallest Shannon entropy for this scanline. Depending + on the image, this is better or worse than minsum.*/ + LFS_ENTROPY, + /* + Brute-force-search PNG filters by compressing each filter for each scanline. + Experimental, very slow, and only rarely gives better compression than MINSUM. + */ + LFS_BRUTE_FORCE, + /*use predefined_filters buffer: you specify the filter type for each scanline*/ + LFS_PREDEFINED +} LodePNGFilterStrategy; + +/*Gives characteristics about the integer RGBA colors of the image (count, alpha channel usage, bit depth, ...), +which helps decide which color model to use for encoding. +Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms.*/ +typedef struct LodePNGColorStats { + unsigned colored; /*not grayscale*/ + unsigned key; /*image is not opaque and color key is possible instead of full alpha*/ + unsigned short key_r; /*key values, always as 16-bit, in 8-bit case the byte is duplicated, e.g. 65535 means 255*/ + unsigned short key_g; + unsigned short key_b; + unsigned alpha; /*image is not opaque and alpha channel or alpha palette required*/ + unsigned numcolors; /*amount of colors, up to 257. Not valid if bits == 16 or allow_palette is disabled.*/ + unsigned char palette[1024]; /*Remembers up to the first 256 RGBA colors, in no particular order, only valid when numcolors is valid*/ + unsigned bits; /*bits per channel (not for palette). 1,2 or 4 for grayscale only. 16 if 16-bit per channel required.*/ + size_t numpixels; + + /*user settings for computing/using the stats*/ + unsigned allow_palette; /*default 1. if 0, disallow choosing palette colortype in auto_choose_color, and don't count numcolors*/ + unsigned allow_greyscale; /*default 1. if 0, choose RGB or RGBA even if the image only has gray colors*/ +} LodePNGColorStats; + +void lodepng_color_stats_init(LodePNGColorStats* stats); + +/*Get a LodePNGColorStats of the image. The stats must already have been inited. +Returns error code (e.g. alloc fail) or 0 if ok.*/ +unsigned lodepng_compute_color_stats(LodePNGColorStats* stats, + const unsigned char* image, unsigned w, unsigned h, + const LodePNGColorMode* mode_in); + +/*Settings for the encoder.*/ +typedef struct LodePNGEncoderSettings { + LodePNGCompressSettings zlibsettings; /*settings for the zlib encoder, such as window size, ...*/ + + unsigned auto_convert; /*automatically choose output PNG color type. Default: true*/ + + /*If true, follows the official PNG heuristic: if the PNG uses a palette or lower than + 8 bit depth, set all filters to zero. Otherwise use the filter_strategy. Note that to + completely follow the official PNG heuristic, filter_palette_zero must be true and + filter_strategy must be LFS_MINSUM*/ + unsigned filter_palette_zero; + /*Which filter strategy to use when not using zeroes due to filter_palette_zero. + Set filter_palette_zero to 0 to ensure always using your chosen strategy. Default: LFS_MINSUM*/ + LodePNGFilterStrategy filter_strategy; + /*used if filter_strategy is LFS_PREDEFINED. In that case, this must point to a buffer with + the same length as the amount of scanlines in the image, and each value must <= 5. You + have to cleanup this buffer, LodePNG will never free it. Don't forget that filter_palette_zero + must be set to 0 to ensure this is also used on palette or low bitdepth images.*/ + const unsigned char* predefined_filters; + + /*force creating a PLTE chunk if colortype is 2 or 6 (= a suggested palette). + If colortype is 3, PLTE is _always_ created.*/ + unsigned force_palette; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*add LodePNG identifier and version as a text chunk, for debugging*/ + unsigned add_id; + /*encode text chunks as zTXt chunks instead of tEXt chunks, and use compression in iTXt chunks*/ + unsigned text_compression; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGEncoderSettings; + +void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings); +#endif /*LODEPNG_COMPILE_ENCODER*/ + + +#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) +/*The settings, state and information for extended encoding and decoding.*/ +typedef struct LodePNGState { +#ifdef LODEPNG_COMPILE_DECODER + LodePNGDecoderSettings decoder; /*the decoding settings*/ +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER + LodePNGEncoderSettings encoder; /*the encoding settings*/ +#endif /*LODEPNG_COMPILE_ENCODER*/ + LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/ + LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/ + unsigned error; +} LodePNGState; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_state_init(LodePNGState* state); +void lodepng_state_cleanup(LodePNGState* state); +void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source); +#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ + +#ifdef LODEPNG_COMPILE_DECODER +/* +Same as lodepng_decode_memory, but uses a LodePNGState to allow custom settings and +getting much more information about the PNG image and color mode. +*/ +unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize); + +/* +Read the PNG header, but not the actual data. This returns only the information +that is in the IHDR chunk of the PNG, such as width, height and color type. The +information is placed in the info_png field of the LodePNGState. +*/ +unsigned lodepng_inspect(unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize); +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* +Reads one metadata chunk (other than IHDR) of the PNG file and outputs what it +read in the state. Returns error code on failure. +Use lodepng_inspect first with a new state, then e.g. lodepng_chunk_find_const +to find the desired chunk type, and if non null use lodepng_inspect_chunk (with +chunk_pointer - start_of_file as pos). +Supports most metadata chunks from the PNG standard (gAMA, bKGD, tEXt, ...). +Ignores unsupported, unknown, non-metadata or IHDR chunks (without error). +Requirements: &in[pos] must point to start of a chunk, must use regular +lodepng_inspect first since format of most other chunks depends on IHDR, and if +there is a PLTE chunk, that one must be inspected before tRNS or bKGD. +*/ +unsigned lodepng_inspect_chunk(LodePNGState* state, size_t pos, + const unsigned char* in, size_t insize); + +#ifdef LODEPNG_COMPILE_ENCODER +/*This function allocates the out buffer with standard malloc and stores the size in *outsize.*/ +unsigned lodepng_encode(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGState* state); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/* +The lodepng_chunk functions are normally not needed, except to traverse the +unknown chunks stored in the LodePNGInfo struct, or add new ones to it. +It also allows traversing the chunks of an encoded PNG file yourself. + +The chunk pointer always points to the beginning of the chunk itself, that is +the first byte of the 4 length bytes. + +In the PNG file format, chunks have the following format: +-4 bytes length: length of the data of the chunk in bytes (chunk itself is 12 bytes longer) +-4 bytes chunk type (ASCII a-z,A-Z only, see below) +-length bytes of data (may be 0 bytes if length was 0) +-4 bytes of CRC, computed on chunk name + data + +The first chunk starts at the 8th byte of the PNG file, the entire rest of the file +exists out of concatenated chunks with the above format. + +PNG standard chunk ASCII naming conventions: +-First byte: uppercase = critical, lowercase = ancillary +-Second byte: uppercase = public, lowercase = private +-Third byte: must be uppercase +-Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy +*/ + +/* +Gets the length of the data of the chunk. Total chunk length has 12 bytes more. +There must be at least 4 bytes to read from. If the result value is too large, +it may be corrupt data. +*/ +unsigned lodepng_chunk_length(const unsigned char* chunk); + +/*puts the 4-byte type in null terminated string*/ +void lodepng_chunk_type(char type[5], const unsigned char* chunk); + +/*check if the type is the given type*/ +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type); + +/*0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard)*/ +unsigned char lodepng_chunk_ancillary(const unsigned char* chunk); + +/*0: public, 1: private (see PNG standard)*/ +unsigned char lodepng_chunk_private(const unsigned char* chunk); + +/*0: the chunk is unsafe to copy, 1: the chunk is safe to copy (see PNG standard)*/ +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk); + +/*get pointer to the data of the chunk, where the input points to the header of the chunk*/ +unsigned char* lodepng_chunk_data(unsigned char* chunk); +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk); + +/*returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!)*/ +unsigned lodepng_chunk_check_crc(const unsigned char* chunk); + +/*generates the correct CRC from the data and puts it in the last 4 bytes of the chunk*/ +void lodepng_chunk_generate_crc(unsigned char* chunk); + +/* +Iterate to next chunks, allows iterating through all chunks of the PNG file. +Input must be at the beginning of a chunk (result of a previous lodepng_chunk_next call, +or the 8th byte of a PNG file which always has the first chunk), or alternatively may +point to the first byte of the PNG file (which is not a chunk but the magic header, the +function will then skip over it and return the first real chunk). +Will output pointer to the start of the next chunk, or at or beyond end of the file if there +is no more chunk after this or possibly if the chunk is corrupt. +Start this process at the 8th byte of the PNG file. +In a non-corrupt PNG file, the last chunk should have name "IEND". +*/ +unsigned char* lodepng_chunk_next(unsigned char* chunk, unsigned char* end); +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end); + +/*Finds the first chunk with the given type in the range [chunk, end), or returns NULL if not found.*/ +unsigned char* lodepng_chunk_find(unsigned char* chunk, unsigned char* end, const char type[5]); +const unsigned char* lodepng_chunk_find_const(const unsigned char* chunk, const unsigned char* end, const char type[5]); + +/* +Appends chunk to the data in out. The given chunk should already have its chunk header. +The out variable and outsize are updated to reflect the new reallocated buffer. +Returns error code (0 if it went ok) +*/ +unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk); + +/* +Appends new chunk to out. The chunk to append is given by giving its length, type +and data separately. The type is a 4-letter string. +The out variable and outsize are updated to reflect the new reallocated buffer. +Returne error code (0 if it went ok) +*/ +unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, unsigned length, + const char* type, const unsigned char* data); + + +/*Calculate CRC32 of buffer*/ +unsigned lodepng_crc32(const unsigned char* buf, size_t len); +#endif /*LODEPNG_COMPILE_PNG*/ + + +#ifdef LODEPNG_COMPILE_ZLIB +/* +This zlib part can be used independently to zlib compress and decompress a +buffer. It cannot be used to create gzip files however, and it only supports the +part of zlib that is required for PNG, it does not support dictionaries. +*/ + +#ifdef LODEPNG_COMPILE_DECODER +/*Inflate a buffer. Inflate is the decompression step of deflate. Out buffer must be freed after use.*/ +unsigned lodepng_inflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings); + +/* +Decompresses Zlib data. Reallocates the out buffer and appends the data. The +data must be according to the zlib specification. +Either, *out must be NULL and *outsize must be 0, or, *out must be a valid +buffer and *outsize its size in bytes. out must be freed by user after usage. +*/ +unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Compresses data with Zlib. Reallocates the out buffer and appends the data. +Zlib adds a small header and trailer around the deflate data. +The data is output in the format of the zlib specification. +Either, *out must be NULL and *outsize must be 0, or, *out must be a valid +buffer and *outsize its size in bytes. out must be freed by user after usage. +*/ +unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings); + +/* +Find length-limited Huffman code for given frequencies. This function is in the +public interface only for tests, it's used internally by lodepng_deflate. +*/ +unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, + size_t numcodes, unsigned maxbitlen); + +/*Compress a buffer with deflate. See RFC 1951. Out buffer must be freed after use.*/ +unsigned lodepng_deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings); + +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_ZLIB*/ + +#ifdef LODEPNG_COMPILE_DISK +/* +Load a file from disk into buffer. The function allocates the out buffer, and +after usage you should free it. +out: output parameter, contains pointer to loaded buffer. +outsize: output parameter, size of the allocated out buffer +filename: the path to the file to load +return value: error code (0 means ok) +*/ +unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename); + +/* +Save a file from buffer to disk. Warning, if it exists, this function overwrites +the file without warning! +buffer: the buffer to write +buffersize: size of the buffer to write +filename: the path to the file to save to +return value: error code (0 means ok) +*/ +unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename); +#endif /*LODEPNG_COMPILE_DISK*/ + +#ifdef LODEPNG_COMPILE_CPP +/* The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers. */ +namespace lodepng { +#ifdef LODEPNG_COMPILE_PNG +class State : public LodePNGState { + public: + State(); + State(const State& other); + ~State(); + State& operator=(const State& other); +}; + +#ifdef LODEPNG_COMPILE_DECODER +/* Same as other lodepng::decode, but using a State for more settings and information. */ +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const unsigned char* in, size_t insize); +unsigned decode(std::vector& out, unsigned& w, unsigned& h, + State& state, + const std::vector& in); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* Same as other lodepng::encode, but using a State for more settings and information. */ +unsigned encode(std::vector& out, + const unsigned char* in, unsigned w, unsigned h, + State& state); +unsigned encode(std::vector& out, + const std::vector& in, unsigned w, unsigned h, + State& state); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DISK +/* +Load a file from disk into an std::vector. +return value: error code (0 means ok) +*/ +unsigned load_file(std::vector& buffer, const std::string& filename); + +/* +Save the binary data in an std::vector to a file on disk. The file is overwritten +without warning. +*/ +unsigned save_file(const std::vector& buffer, const std::string& filename); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_PNG */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_DECODER +/* Zlib-decompress an unsigned char buffer */ +unsigned decompress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); + +/* Zlib-decompress an std::vector */ +unsigned decompress(std::vector& out, const std::vector& in, + const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +/* Zlib-compress an unsigned char buffer */ +unsigned compress(std::vector& out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings& settings = lodepng_default_compress_settings); + +/* Zlib-compress an std::vector */ +unsigned compress(std::vector& out, const std::vector& in, + const LodePNGCompressSettings& settings = lodepng_default_compress_settings); +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_ZLIB */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ + +/* +TODO: +[.] test if there are no memory leaks or security exploits - done a lot but needs to be checked often +[.] check compatibility with various compilers - done but needs to be redone for every newer version +[X] converting color to 16-bit per channel types +[X] support color profile chunk types (but never let them touch RGB values by default) +[ ] support all public PNG chunk types (almost done except sBIT, sPLT and hIST) +[ ] make sure encoder generates no chunks with size > (2^31)-1 +[ ] partial decoding (stream processing) +[X] let the "isFullyOpaque" function check color keys and transparent palettes too +[X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl" +[ ] allow treating some errors like warnings, when image is recoverable (e.g. 69, 57, 58) +[ ] make warnings like: oob palette, checksum fail, data after iend, wrong/unknown crit chunk, no null terminator in text, ... +[ ] error messages with line numbers (and version) +[ ] errors in state instead of as return code? +[ ] new errors/warnings like suspiciously big decompressed ztxt or iccp chunk +[ ] let the C++ wrapper catch exceptions coming from the standard library and return LodePNG error codes +[ ] allow user to provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ... +[ ] allow user to give data (void*) to custom allocator +[X] provide alternatives for C library functions not present on some platforms (memcpy, ...) +*/ + +#endif /*LV_USE_PNG*/ + +#endif /*LODEPNG_H inclusion guard*/ + +/* +LodePNG Documentation +--------------------- + +0. table of contents +-------------------- + + 1. about + 1.1. supported features + 1.2. features not supported + 2. C and C++ version + 3. security + 4. decoding + 5. encoding + 6. color conversions + 6.1. PNG color types + 6.2. color conversions + 6.3. padding bits + 6.4. A note about 16-bits per channel and endianness + 7. error values + 8. chunks and PNG editing + 9. compiler support + 10. examples + 10.1. decoder C++ example + 10.2. decoder C example + 11. state settings reference + 12. changes + 13. contact information + + +1. about +-------- + +PNG is a file format to store raster images losslessly with good compression, +supporting different color types and alpha channel. + +LodePNG is a PNG codec according to the Portable Network Graphics (PNG) +Specification (Second Edition) - W3C Recommendation 10 November 2003. + +The specifications used are: + +*) Portable Network Graphics (PNG) Specification (Second Edition): + http://www.w3.org/TR/2003/REC-PNG-20031110 +*) RFC 1950 ZLIB Compressed Data Format version 3.3: + http://www.gzip.org/zlib/rfc-zlib.html +*) RFC 1951 DEFLATE Compressed Data Format Specification ver 1.3: + http://www.gzip.org/zlib/rfc-deflate.html + +The most recent version of LodePNG can currently be found at +http://lodev.org/lodepng/ + +LodePNG works both in C (ISO C90) and C++, with a C++ wrapper that adds +extra functionality. + +LodePNG exists out of two files: +-lodepng.h: the header file for both C and C++ +-lodepng.c(pp): give it the name lodepng.c or lodepng.cpp (or .cc) depending on your usage + +If you want to start using LodePNG right away without reading this doc, get the +examples from the LodePNG website to see how to use it in code, or check the +smaller examples in chapter 13 here. + +LodePNG is simple but only supports the basic requirements. To achieve +simplicity, the following design choices were made: There are no dependencies +on any external library. There are functions to decode and encode a PNG with +a single function call, and extended versions of these functions taking a +LodePNGState struct allowing to specify or get more information. By default +the colors of the raw image are always RGB or RGBA, no matter what color type +the PNG file uses. To read and write files, there are simple functions to +convert the files to/from buffers in memory. + +This all makes LodePNG suitable for loading textures in games, demos and small +programs, ... It's less suitable for full fledged image editors, loading PNGs +over network (it requires all the image data to be available before decoding can +begin), life-critical systems, ... + +1.1. supported features +----------------------- + +The following features are supported by the decoder: + +*) decoding of PNGs with any color type, bit depth and interlace mode, to a 24- or 32-bit color raw image, + or the same color type as the PNG +*) encoding of PNGs, from any raw image to 24- or 32-bit color, or the same color type as the raw image +*) Adam7 interlace and deinterlace for any color type +*) loading the image from harddisk or decoding it from a buffer from other sources than harddisk +*) support for alpha channels, including RGBA color model, translucent palettes and color keying +*) zlib decompression (inflate) +*) zlib compression (deflate) +*) CRC32 and ADLER32 checksums +*) colorimetric color profile conversions: currently experimentally available in lodepng_util.cpp only, + plus alternatively ability to pass on chroma/gamma/ICC profile information to other color management system. +*) handling of unknown chunks, allowing making a PNG editor that stores custom and unknown chunks. +*) the following chunks are supported by both encoder and decoder: + IHDR: header information + PLTE: color palette + IDAT: pixel data + IEND: the final chunk + tRNS: transparency for palettized images + tEXt: textual information + zTXt: compressed textual information + iTXt: international textual information + bKGD: suggested background color + pHYs: physical dimensions + tIME: modification time + cHRM: RGB chromaticities + gAMA: RGB gamma correction + iCCP: ICC color profile + sRGB: rendering intent + +1.2. features not supported +--------------------------- + +The following features are _not_ supported: + +*) some features needed to make a conformant PNG-Editor might be still missing. +*) partial loading/stream processing. All data must be available and is processed in one call. +*) The following public chunks are not (yet) supported but treated as unknown chunks by LodePNG: + sBIT + hIST + sPLT + + +2. C and C++ version +-------------------- + +The C version uses buffers allocated with alloc that you need to free() +yourself. You need to use init and cleanup functions for each struct whenever +using a struct from the C version to avoid exploits and memory leaks. + +The C++ version has extra functions with std::vectors in the interface and the +lodepng::State class which is a LodePNGState with constructor and destructor. + +These files work without modification for both C and C++ compilers because all +the additional C++ code is in "#ifdef __cplusplus" blocks that make C-compilers +ignore it, and the C code is made to compile both with strict ISO C90 and C++. + +To use the C++ version, you need to rename the source file to lodepng.cpp +(instead of lodepng.c), and compile it with a C++ compiler. + +To use the C version, you need to rename the source file to lodepng.c (instead +of lodepng.cpp), and compile it with a C compiler. + + +3. Security +----------- + +Even if carefully designed, it's always possible that LodePNG contains possible +exploits. If you discover one, please let me know, and it will be fixed. + +When using LodePNG, care has to be taken with the C version of LodePNG, as well +as the C-style structs when working with C++. The following conventions are used +for all C-style structs: + +-if a struct has a corresponding init function, always call the init function when making a new one +-if a struct has a corresponding cleanup function, call it before the struct disappears to avoid memory leaks +-if a struct has a corresponding copy function, use the copy function instead of "=". + The destination must also be inited already. + + +4. Decoding +----------- + +Decoding converts a PNG compressed image to a raw pixel buffer. + +Most documentation on using the decoder is at its declarations in the header +above. For C, simple decoding can be done with functions such as +lodepng_decode32, and more advanced decoding can be done with the struct +LodePNGState and lodepng_decode. For C++, all decoding can be done with the +various lodepng::decode functions, and lodepng::State can be used for advanced +features. + +When using the LodePNGState, it uses the following fields for decoding: +*) LodePNGInfo info_png: it stores extra information about the PNG (the input) in here +*) LodePNGColorMode info_raw: here you can say what color mode of the raw image (the output) you want to get +*) LodePNGDecoderSettings decoder: you can specify a few extra settings for the decoder to use + +LodePNGInfo info_png +-------------------- + +After decoding, this contains extra information of the PNG image, except the actual +pixels, width and height because these are already gotten directly from the decoder +functions. + +It contains for example the original color type of the PNG image, text comments, +suggested background color, etc... More details about the LodePNGInfo struct are +at its declaration documentation. + +LodePNGColorMode info_raw +------------------------- + +When decoding, here you can specify which color type you want +the resulting raw image to be. If this is different from the colortype of the +PNG, then the decoder will automatically convert the result. This conversion +always works, except if you want it to convert a color PNG to grayscale or to +a palette with missing colors. + +By default, 32-bit color is used for the result. + +LodePNGDecoderSettings decoder +------------------------------ + +The settings can be used to ignore the errors created by invalid CRC and Adler32 +chunks, and to disable the decoding of tEXt chunks. + +There's also a setting color_convert, true by default. If false, no conversion +is done, the resulting data will be as it was in the PNG (after decompression) +and you'll have to puzzle the colors of the pixels together yourself using the +color type information in the LodePNGInfo. + + +5. Encoding +----------- + +Encoding converts a raw pixel buffer to a PNG compressed image. + +Most documentation on using the encoder is at its declarations in the header +above. For C, simple encoding can be done with functions such as +lodepng_encode32, and more advanced decoding can be done with the struct +LodePNGState and lodepng_encode. For C++, all encoding can be done with the +various lodepng::encode functions, and lodepng::State can be used for advanced +features. + +Like the decoder, the encoder can also give errors. However it gives less errors +since the encoder input is trusted, the decoder input (a PNG image that could +be forged by anyone) is not trusted. + +When using the LodePNGState, it uses the following fields for encoding: +*) LodePNGInfo info_png: here you specify how you want the PNG (the output) to be. +*) LodePNGColorMode info_raw: here you say what color type of the raw image (the input) has +*) LodePNGEncoderSettings encoder: you can specify a few settings for the encoder to use + +LodePNGInfo info_png +-------------------- + +When encoding, you use this the opposite way as when decoding: for encoding, +you fill in the values you want the PNG to have before encoding. By default it's +not needed to specify a color type for the PNG since it's automatically chosen, +but it's possible to choose it yourself given the right settings. + +The encoder will not always exactly match the LodePNGInfo struct you give, +it tries as close as possible. Some things are ignored by the encoder. The +encoder uses, for example, the following settings from it when applicable: +colortype and bitdepth, text chunks, time chunk, the color key, the palette, the +background color, the interlace method, unknown chunks, ... + +When encoding to a PNG with colortype 3, the encoder will generate a PLTE chunk. +If the palette contains any colors for which the alpha channel is not 255 (so +there are translucent colors in the palette), it'll add a tRNS chunk. + +LodePNGColorMode info_raw +------------------------- + +You specify the color type of the raw image that you give to the input here, +including a possible transparent color key and palette you happen to be using in +your raw image data. + +By default, 32-bit color is assumed, meaning your input has to be in RGBA +format with 4 bytes (unsigned chars) per pixel. + +LodePNGEncoderSettings encoder +------------------------------ + +The following settings are supported (some are in sub-structs): +*) auto_convert: when this option is enabled, the encoder will +automatically choose the smallest possible color mode (including color key) that +can encode the colors of all pixels without information loss. +*) btype: the block type for LZ77. 0 = uncompressed, 1 = fixed huffman tree, + 2 = dynamic huffman tree (best compression). Should be 2 for proper + compression. +*) use_lz77: whether or not to use LZ77 for compressed block types. Should be + true for proper compression. +*) windowsize: the window size used by the LZ77 encoder (1 - 32768). Has value + 2048 by default, but can be set to 32768 for better, but slow, compression. +*) force_palette: if colortype is 2 or 6, you can make the encoder write a PLTE + chunk if force_palette is true. This can used as suggested palette to convert + to by viewers that don't support more than 256 colors (if those still exist) +*) add_id: add text chunk "Encoder: LodePNG " to the image. +*) text_compression: default 1. If 1, it'll store texts as zTXt instead of tEXt chunks. + zTXt chunks use zlib compression on the text. This gives a smaller result on + large texts but a larger result on small texts (such as a single program name). + It's all tEXt or all zTXt though, there's no separate setting per text yet. + + +6. color conversions +-------------------- + +An important thing to note about LodePNG, is that the color type of the PNG, and +the color type of the raw image, are completely independent. By default, when +you decode a PNG, you get the result as a raw image in the color type you want, +no matter whether the PNG was encoded with a palette, grayscale or RGBA color. +And if you encode an image, by default LodePNG will automatically choose the PNG +color type that gives good compression based on the values of colors and amount +of colors in the image. It can be configured to let you control it instead as +well, though. + +To be able to do this, LodePNG does conversions from one color mode to another. +It can convert from almost any color type to any other color type, except the +following conversions: RGB to grayscale is not supported, and converting to a +palette when the palette doesn't have a required color is not supported. This is +not supported on purpose: this is information loss which requires a color +reduction algorithm that is beyond the scope of a PNG encoder (yes, RGB to gray +is easy, but there are multiple ways if you want to give some channels more +weight). + +By default, when decoding, you get the raw image in 32-bit RGBA or 24-bit RGB +color, no matter what color type the PNG has. And by default when encoding, +LodePNG automatically picks the best color model for the output PNG, and expects +the input image to be 32-bit RGBA or 24-bit RGB. So, unless you want to control +the color format of the images yourself, you can skip this chapter. + +6.1. PNG color types +-------------------- + +A PNG image can have many color types, ranging from 1-bit color to 64-bit color, +as well as palettized color modes. After the zlib decompression and unfiltering +in the PNG image is done, the raw pixel data will have that color type and thus +a certain amount of bits per pixel. If you want the output raw image after +decoding to have another color type, a conversion is done by LodePNG. + +The PNG specification gives the following color types: + +0: grayscale, bit depths 1, 2, 4, 8, 16 +2: RGB, bit depths 8 and 16 +3: palette, bit depths 1, 2, 4 and 8 +4: grayscale with alpha, bit depths 8 and 16 +6: RGBA, bit depths 8 and 16 + +Bit depth is the amount of bits per pixel per color channel. So the total amount +of bits per pixel is: amount of channels * bitdepth. + +6.2. color conversions +---------------------- + +As explained in the sections about the encoder and decoder, you can specify +color types and bit depths in info_png and info_raw to change the default +behaviour. + +If, when decoding, you want the raw image to be something else than the default, +you need to set the color type and bit depth you want in the LodePNGColorMode, +or the parameters colortype and bitdepth of the simple decoding function. + +If, when encoding, you use another color type than the default in the raw input +image, you need to specify its color type and bit depth in the LodePNGColorMode +of the raw image, or use the parameters colortype and bitdepth of the simple +encoding function. + +If, when encoding, you don't want LodePNG to choose the output PNG color type +but control it yourself, you need to set auto_convert in the encoder settings +to false, and specify the color type you want in the LodePNGInfo of the +encoder (including palette: it can generate a palette if auto_convert is true, +otherwise not). + +If the input and output color type differ (whether user chosen or auto chosen), +LodePNG will do a color conversion, which follows the rules below, and may +sometimes result in an error. + +To avoid some confusion: +-the decoder converts from PNG to raw image +-the encoder converts from raw image to PNG +-the colortype and bitdepth in LodePNGColorMode info_raw, are those of the raw image +-the colortype and bitdepth in the color field of LodePNGInfo info_png, are those of the PNG +-when encoding, the color type in LodePNGInfo is ignored if auto_convert + is enabled, it is automatically generated instead +-when decoding, the color type in LodePNGInfo is set by the decoder to that of the original + PNG image, but it can be ignored since the raw image has the color type you requested instead +-if the color type of the LodePNGColorMode and PNG image aren't the same, a conversion + between the color types is done if the color types are supported. If it is not + supported, an error is returned. If the types are the same, no conversion is done. +-even though some conversions aren't supported, LodePNG supports loading PNGs from any + colortype and saving PNGs to any colortype, sometimes it just requires preparing + the raw image correctly before encoding. +-both encoder and decoder use the same color converter. + +The function lodepng_convert does the color conversion. It is available in the +interface but normally isn't needed since the encoder and decoder already call +it. + +Non supported color conversions: +-color to grayscale when non-gray pixels are present: no error is thrown, but +the result will look ugly because only the red channel is taken (it assumes all +three channels are the same in this case so ignores green and blue). The reason +no error is given is to allow converting from three-channel grayscale images to +one-channel even if there are numerical imprecisions. +-anything to palette when the palette does not have an exact match for a from-color +in it: in this case an error is thrown + +Supported color conversions: +-anything to 8-bit RGB, 8-bit RGBA, 16-bit RGB, 16-bit RGBA +-any gray or gray+alpha, to gray or gray+alpha +-anything to a palette, as long as the palette has the requested colors in it +-removing alpha channel +-higher to smaller bitdepth, and vice versa + +If you want no color conversion to be done (e.g. for speed or control): +-In the encoder, you can make it save a PNG with any color type by giving the +raw color mode and LodePNGInfo the same color mode, and setting auto_convert to +false. +-In the decoder, you can make it store the pixel data in the same color type +as the PNG has, by setting the color_convert setting to false. Settings in +info_raw are then ignored. + +6.3. padding bits +----------------- + +In the PNG file format, if a less than 8-bit per pixel color type is used and the scanlines +have a bit amount that isn't a multiple of 8, then padding bits are used so that each +scanline starts at a fresh byte. But that is NOT true for the LodePNG raw input and output. +The raw input image you give to the encoder, and the raw output image you get from the decoder +will NOT have these padding bits, e.g. in the case of a 1-bit image with a width +of 7 pixels, the first pixel of the second scanline will the 8th bit of the first byte, +not the first bit of a new byte. + +6.4. A note about 16-bits per channel and endianness +---------------------------------------------------- + +LodePNG uses unsigned char arrays for 16-bit per channel colors too, just like +for any other color format. The 16-bit values are stored in big endian (most +significant byte first) in these arrays. This is the opposite order of the +little endian used by x86 CPU's. + +LodePNG always uses big endian because the PNG file format does so internally. +Conversions to other formats than PNG uses internally are not supported by +LodePNG on purpose, there are myriads of formats, including endianness of 16-bit +colors, the order in which you store R, G, B and A, and so on. Supporting and +converting to/from all that is outside the scope of LodePNG. + +This may mean that, depending on your use case, you may want to convert the big +endian output of LodePNG to little endian with a for loop. This is certainly not +always needed, many applications and libraries support big endian 16-bit colors +anyway, but it means you cannot simply cast the unsigned char* buffer to an +unsigned short* buffer on x86 CPUs. + + +7. error values +--------------- + +All functions in LodePNG that return an error code, return 0 if everything went +OK, or a non-zero code if there was an error. + +The meaning of the LodePNG error values can be retrieved with the function +lodepng_error_text: given the numerical error code, it returns a description +of the error in English as a string. + +Check the implementation of lodepng_error_text to see the meaning of each code. + +It is not recommended to use the numerical values to programmatically make +different decisions based on error types as the numbers are not guaranteed to +stay backwards compatible. They are for human consumption only. Programmatically +only 0 or non-0 matter. + + +8. chunks and PNG editing +------------------------- + +If you want to add extra chunks to a PNG you encode, or use LodePNG for a PNG +editor that should follow the rules about handling of unknown chunks, or if your +program is able to read other types of chunks than the ones handled by LodePNG, +then that's possible with the chunk functions of LodePNG. + +A PNG chunk has the following layout: + +4 bytes length +4 bytes type name +length bytes data +4 bytes CRC + +8.1. iterating through chunks +----------------------------- + +If you have a buffer containing the PNG image data, then the first chunk (the +IHDR chunk) starts at byte number 8 of that buffer. The first 8 bytes are the +signature of the PNG and are not part of a chunk. But if you start at byte 8 +then you have a chunk, and can check the following things of it. + +NOTE: none of these functions check for memory buffer boundaries. To avoid +exploits, always make sure the buffer contains all the data of the chunks. +When using lodepng_chunk_next, make sure the returned value is within the +allocated memory. + +unsigned lodepng_chunk_length(const unsigned char* chunk): + +Get the length of the chunk's data. The total chunk length is this length + 12. + +void lodepng_chunk_type(char type[5], const unsigned char* chunk): +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type): + +Get the type of the chunk or compare if it's a certain type + +unsigned char lodepng_chunk_critical(const unsigned char* chunk): +unsigned char lodepng_chunk_private(const unsigned char* chunk): +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk): + +Check if the chunk is critical in the PNG standard (only IHDR, PLTE, IDAT and IEND are). +Check if the chunk is private (public chunks are part of the standard, private ones not). +Check if the chunk is safe to copy. If it's not, then, when modifying data in a critical +chunk, unsafe to copy chunks of the old image may NOT be saved in the new one if your +program doesn't handle that type of unknown chunk. + +unsigned char* lodepng_chunk_data(unsigned char* chunk): +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk): + +Get a pointer to the start of the data of the chunk. + +unsigned lodepng_chunk_check_crc(const unsigned char* chunk): +void lodepng_chunk_generate_crc(unsigned char* chunk): + +Check if the crc is correct or generate a correct one. + +unsigned char* lodepng_chunk_next(unsigned char* chunk): +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk): + +Iterate to the next chunk. This works if you have a buffer with consecutive chunks. Note that these +functions do no boundary checking of the allocated data whatsoever, so make sure there is enough +data available in the buffer to be able to go to the next chunk. + +unsigned lodepng_chunk_append(unsigned char** out, size_t* outsize, const unsigned char* chunk): +unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, unsigned length, + const char* type, const unsigned char* data): + +These functions are used to create new chunks that are appended to the data in *out that has +length *outsize. The append function appends an existing chunk to the new data. The create +function creates a new chunk with the given parameters and appends it. Type is the 4-letter +name of the chunk. + +8.2. chunks in info_png +----------------------- + +The LodePNGInfo struct contains fields with the unknown chunk in it. It has 3 +buffers (each with size) to contain 3 types of unknown chunks: +the ones that come before the PLTE chunk, the ones that come between the PLTE +and the IDAT chunks, and the ones that come after the IDAT chunks. +It's necessary to make the distinction between these 3 cases because the PNG +standard forces to keep the ordering of unknown chunks compared to the critical +chunks, but does not force any other ordering rules. + +info_png.unknown_chunks_data[0] is the chunks before PLTE +info_png.unknown_chunks_data[1] is the chunks after PLTE, before IDAT +info_png.unknown_chunks_data[2] is the chunks after IDAT + +The chunks in these 3 buffers can be iterated through and read by using the same +way described in the previous subchapter. + +When using the decoder to decode a PNG, you can make it store all unknown chunks +if you set the option settings.remember_unknown_chunks to 1. By default, this +option is off (0). + +The encoder will always encode unknown chunks that are stored in the info_png. +If you need it to add a particular chunk that isn't known by LodePNG, you can +use lodepng_chunk_append or lodepng_chunk_create to the chunk data in +info_png.unknown_chunks_data[x]. + +Chunks that are known by LodePNG should not be added in that way. E.g. to make +LodePNG add a bKGD chunk, set background_defined to true and add the correct +parameters there instead. + + +9. compiler support +------------------- + +No libraries other than the current standard C library are needed to compile +LodePNG. For the C++ version, only the standard C++ library is needed on top. +Add the files lodepng.c(pp) and lodepng.h to your project, include +lodepng.h where needed, and your program can read/write PNG files. + +It is compatible with C90 and up, and C++03 and up. + +If performance is important, use optimization when compiling! For both the +encoder and decoder, this makes a large difference. + +Make sure that LodePNG is compiled with the same compiler of the same version +and with the same settings as the rest of the program, or the interfaces with +std::vectors and std::strings in C++ can be incompatible. + +CHAR_BITS must be 8 or higher, because LodePNG uses unsigned chars for octets. + +*) gcc and g++ + +LodePNG is developed in gcc so this compiler is natively supported. It gives no +warnings with compiler options "-Wall -Wextra -pedantic -ansi", with gcc and g++ +version 4.7.1 on Linux, 32-bit and 64-bit. + +*) Clang + +Fully supported and warning-free. + +*) Mingw + +The Mingw compiler (a port of gcc for Windows) should be fully supported by +LodePNG. + +*) Visual Studio and Visual C++ Express Edition + +LodePNG should be warning-free with warning level W4. Two warnings were disabled +with pragmas though: warning 4244 about implicit conversions, and warning 4996 +where it wants to use a non-standard function fopen_s instead of the standard C +fopen. + +Visual Studio may want "stdafx.h" files to be included in each source file and +give an error "unexpected end of file while looking for precompiled header". +This is not standard C++ and will not be added to the stock LodePNG. You can +disable it for lodepng.cpp only by right clicking it, Properties, C/C++, +Precompiled Headers, and set it to Not Using Precompiled Headers there. + +NOTE: Modern versions of VS should be fully supported, but old versions, e.g. +VS6, are not guaranteed to work. + +*) Compilers on Macintosh + +LodePNG has been reported to work both with gcc and LLVM for Macintosh, both for +C and C++. + +*) Other Compilers + +If you encounter problems on any compilers, feel free to let me know and I may +try to fix it if the compiler is modern and standards compliant. + + +10. examples +------------ + +This decoder example shows the most basic usage of LodePNG. More complex +examples can be found on the LodePNG website. + +10.1. decoder C++ example +------------------------- + +#include "lodepng.h" +#include + +int main(int argc, char *argv[]) { + const char* filename = argc > 1 ? argv[1] : "test.png"; + + //load and decode + std::vector image; + unsigned width, height; + unsigned error = lodepng::decode(image, width, height, filename); + + //if there's an error, display it + if(error) std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl; + + //the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it, ... +} + +10.2. decoder C example +----------------------- + +#include "lodepng.h" + +int main(int argc, char *argv[]) { + unsigned error; + unsigned char* image; + size_t width, height; + const char* filename = argc > 1 ? argv[1] : "test.png"; + + error = lodepng_decode32_file(&image, &width, &height, filename); + + if(error) printf("decoder error %u: %s\n", error, lodepng_error_text(error)); + + / * use image here * / + + free(image); + return 0; +} + +11. state settings reference +---------------------------- + +A quick reference of some settings to set on the LodePNGState + +For decoding: + +state.decoder.zlibsettings.ignore_adler32: ignore ADLER32 checksums +state.decoder.zlibsettings.custom_...: use custom inflate function +state.decoder.ignore_crc: ignore CRC checksums +state.decoder.ignore_critical: ignore unknown critical chunks +state.decoder.ignore_end: ignore missing IEND chunk. May fail if this corruption causes other errors +state.decoder.color_convert: convert internal PNG color to chosen one +state.decoder.read_text_chunks: whether to read in text metadata chunks +state.decoder.remember_unknown_chunks: whether to read in unknown chunks +state.info_raw.colortype: desired color type for decoded image +state.info_raw.bitdepth: desired bit depth for decoded image +state.info_raw....: more color settings, see struct LodePNGColorMode +state.info_png....: no settings for decoder but ouput, see struct LodePNGInfo + +For encoding: + +state.encoder.zlibsettings.btype: disable compression by setting it to 0 +state.encoder.zlibsettings.use_lz77: use LZ77 in compression +state.encoder.zlibsettings.windowsize: tweak LZ77 windowsize +state.encoder.zlibsettings.minmatch: tweak min LZ77 length to match +state.encoder.zlibsettings.nicematch: tweak LZ77 match where to stop searching +state.encoder.zlibsettings.lazymatching: try one more LZ77 matching +state.encoder.zlibsettings.custom_...: use custom deflate function +state.encoder.auto_convert: choose optimal PNG color type, if 0 uses info_png +state.encoder.filter_palette_zero: PNG filter strategy for palette +state.encoder.filter_strategy: PNG filter strategy to encode with +state.encoder.force_palette: add palette even if not encoding to one +state.encoder.add_id: add LodePNG identifier and version as a text chunk +state.encoder.text_compression: use compressed text chunks for metadata +state.info_raw.colortype: color type of raw input image you provide +state.info_raw.bitdepth: bit depth of raw input image you provide +state.info_raw: more color settings, see struct LodePNGColorMode +state.info_png.color.colortype: desired color type if auto_convert is false +state.info_png.color.bitdepth: desired bit depth if auto_convert is false +state.info_png.color....: more color settings, see struct LodePNGColorMode +state.info_png....: more PNG related settings, see struct LodePNGInfo + + +12. changes +----------- + +The version number of LodePNG is the date of the change given in the format +yyyymmdd. + +Some changes aren't backwards compatible. Those are indicated with a (!) +symbol. + +Not all changes are listed here, the commit history in github lists more: +https://github.com/lvandeve/lodepng + +*) 17 okt 2020: prevent decoding too large text/icc chunks by default. +*) 06 mar 2020: simplified some of the dynamic memory allocations. +*) 12 jan 2020: (!) added 'end' argument to lodepng_chunk_next to allow correct + overflow checks. +*) 14 aug 2019: around 25% faster decoding thanks to huffman lookup tables. +*) 15 jun 2019: (!) auto_choose_color API changed (for bugfix: don't use palette + if gray ICC profile) and non-ICC LodePNGColorProfile renamed to + LodePNGColorStats. +*) 30 dec 2018: code style changes only: removed newlines before opening braces. +*) 10 sep 2018: added way to inspect metadata chunks without full decoding. +*) 19 aug 2018: (!) fixed color mode bKGD is encoded with and made it use + palette index in case of palette. +*) 10 aug 2018: (!) added support for gAMA, cHRM, sRGB and iCCP chunks. This + change is backwards compatible unless you relied on unknown_chunks for those. +*) 11 jun 2018: less restrictive check for pixel size integer overflow +*) 14 jan 2018: allow optionally ignoring a few more recoverable errors +*) 17 sep 2017: fix memory leak for some encoder input error cases +*) 27 nov 2016: grey+alpha auto color model detection bugfix +*) 18 apr 2016: Changed qsort to custom stable sort (for platforms w/o qsort). +*) 09 apr 2016: Fixed colorkey usage detection, and better file loading (within + the limits of pure C90). +*) 08 dec 2015: Made load_file function return error if file can't be opened. +*) 24 okt 2015: Bugfix with decoding to palette output. +*) 18 apr 2015: Boundary PM instead of just package-merge for faster encoding. +*) 24 aug 2014: Moved to github +*) 23 aug 2014: Reduced needless memory usage of decoder. +*) 28 jun 2014: Removed fix_png setting, always support palette OOB for + simplicity. Made ColorProfile public. +*) 09 jun 2014: Faster encoder by fixing hash bug and more zeros optimization. +*) 22 dec 2013: Power of two windowsize required for optimization. +*) 15 apr 2013: Fixed bug with LAC_ALPHA and color key. +*) 25 mar 2013: Added an optional feature to ignore some PNG errors (fix_png). +*) 11 mar 2013: (!) Bugfix with custom free. Changed from "my" to "lodepng_" + prefix for the custom allocators and made it possible with a new #define to + use custom ones in your project without needing to change lodepng's code. +*) 28 jan 2013: Bugfix with color key. +*) 27 okt 2012: Tweaks in text chunk keyword length error handling. +*) 8 okt 2012: (!) Added new filter strategy (entropy) and new auto color mode. + (no palette). Better deflate tree encoding. New compression tweak settings. + Faster color conversions while decoding. Some internal cleanups. +*) 23 sep 2012: Reduced warnings in Visual Studio a little bit. +*) 1 sep 2012: (!) Removed #define's for giving custom (de)compression functions + and made it work with function pointers instead. +*) 23 jun 2012: Added more filter strategies. Made it easier to use custom alloc + and free functions and toggle #defines from compiler flags. Small fixes. +*) 6 may 2012: (!) Made plugging in custom zlib/deflate functions more flexible. +*) 22 apr 2012: (!) Made interface more consistent, renaming a lot. Removed + redundant C++ codec classes. Reduced amount of structs. Everything changed, + but it is cleaner now imho and functionality remains the same. Also fixed + several bugs and shrunk the implementation code. Made new samples. +*) 6 nov 2011: (!) By default, the encoder now automatically chooses the best + PNG color model and bit depth, based on the amount and type of colors of the + raw image. For this, autoLeaveOutAlphaChannel replaced by auto_choose_color. +*) 9 okt 2011: simpler hash chain implementation for the encoder. +*) 8 sep 2011: lz77 encoder lazy matching instead of greedy matching. +*) 23 aug 2011: tweaked the zlib compression parameters after benchmarking. + A bug with the PNG filtertype heuristic was fixed, so that it chooses much + better ones (it's quite significant). A setting to do an experimental, slow, + brute force search for PNG filter types is added. +*) 17 aug 2011: (!) changed some C zlib related function names. +*) 16 aug 2011: made the code less wide (max 120 characters per line). +*) 17 apr 2011: code cleanup. Bugfixes. Convert low to 16-bit per sample colors. +*) 21 feb 2011: fixed compiling for C90. Fixed compiling with sections disabled. +*) 11 dec 2010: encoding is made faster, based on suggestion by Peter Eastman + to optimize long sequences of zeros. +*) 13 nov 2010: added LodePNG_InfoColor_hasPaletteAlpha and + LodePNG_InfoColor_canHaveAlpha functions for convenience. +*) 7 nov 2010: added LodePNG_error_text function to get error code description. +*) 30 okt 2010: made decoding slightly faster +*) 26 okt 2010: (!) changed some C function and struct names (more consistent). + Reorganized the documentation and the declaration order in the header. +*) 08 aug 2010: only changed some comments and external samples. +*) 05 jul 2010: fixed bug thanks to warnings in the new gcc version. +*) 14 mar 2010: fixed bug where too much memory was allocated for char buffers. +*) 02 sep 2008: fixed bug where it could create empty tree that linux apps could + read by ignoring the problem but windows apps couldn't. +*) 06 jun 2008: added more error checks for out of memory cases. +*) 26 apr 2008: added a few more checks here and there to ensure more safety. +*) 06 mar 2008: crash with encoding of strings fixed +*) 02 feb 2008: support for international text chunks added (iTXt) +*) 23 jan 2008: small cleanups, and #defines to divide code in sections +*) 20 jan 2008: support for unknown chunks allowing using LodePNG for an editor. +*) 18 jan 2008: support for tIME and pHYs chunks added to encoder and decoder. +*) 17 jan 2008: ability to encode and decode compressed zTXt chunks added + Also various fixes, such as in the deflate and the padding bits code. +*) 13 jan 2008: Added ability to encode Adam7-interlaced images. Improved + filtering code of encoder. +*) 07 jan 2008: (!) changed LodePNG to use ISO C90 instead of C++. A + C++ wrapper around this provides an interface almost identical to before. + Having LodePNG be pure ISO C90 makes it more portable. The C and C++ code + are together in these files but it works both for C and C++ compilers. +*) 29 dec 2007: (!) changed most integer types to unsigned int + other tweaks +*) 30 aug 2007: bug fixed which makes this Borland C++ compatible +*) 09 aug 2007: some VS2005 warnings removed again +*) 21 jul 2007: deflate code placed in new namespace separate from zlib code +*) 08 jun 2007: fixed bug with 2- and 4-bit color, and small interlaced images +*) 04 jun 2007: improved support for Visual Studio 2005: crash with accessing + invalid std::vector element [0] fixed, and level 3 and 4 warnings removed +*) 02 jun 2007: made the encoder add a tag with version by default +*) 27 may 2007: zlib and png code separated (but still in the same file), + simple encoder/decoder functions added for more simple usage cases +*) 19 may 2007: minor fixes, some code cleaning, new error added (error 69), + moved some examples from here to lodepng_examples.cpp +*) 12 may 2007: palette decoding bug fixed +*) 24 apr 2007: changed the license from BSD to the zlib license +*) 11 mar 2007: very simple addition: ability to encode bKGD chunks. +*) 04 mar 2007: (!) tEXt chunk related fixes, and support for encoding + palettized PNG images. Plus little interface change with palette and texts. +*) 03 mar 2007: Made it encode dynamic Huffman shorter with repeat codes. + Fixed a bug where the end code of a block had length 0 in the Huffman tree. +*) 26 feb 2007: Huffman compression with dynamic trees (BTYPE 2) now implemented + and supported by the encoder, resulting in smaller PNGs at the output. +*) 27 jan 2007: Made the Adler-32 test faster so that a timewaste is gone. +*) 24 jan 2007: gave encoder an error interface. Added color conversion from any + greyscale type to 8-bit greyscale with or without alpha. +*) 21 jan 2007: (!) Totally changed the interface. It allows more color types + to convert to and is more uniform. See the manual for how it works now. +*) 07 jan 2007: Some cleanup & fixes, and a few changes over the last days: + encode/decode custom tEXt chunks, separate classes for zlib & deflate, and + at last made the decoder give errors for incorrect Adler32 or Crc. +*) 01 jan 2007: Fixed bug with encoding PNGs with less than 8 bits per channel. +*) 29 dec 2006: Added support for encoding images without alpha channel, and + cleaned out code as well as making certain parts faster. +*) 28 dec 2006: Added "Settings" to the encoder. +*) 26 dec 2006: The encoder now does LZ77 encoding and produces much smaller files now. + Removed some code duplication in the decoder. Fixed little bug in an example. +*) 09 dec 2006: (!) Placed output parameters of public functions as first parameter. + Fixed a bug of the decoder with 16-bit per color. +*) 15 okt 2006: Changed documentation structure +*) 09 okt 2006: Encoder class added. It encodes a valid PNG image from the + given image buffer, however for now it's not compressed. +*) 08 sep 2006: (!) Changed to interface with a Decoder class +*) 30 jul 2006: (!) LodePNG_InfoPng , width and height are now retrieved in different + way. Renamed decodePNG to decodePNGGeneric. +*) 29 jul 2006: (!) Changed the interface: image info is now returned as a + struct of type LodePNG::LodePNG_Info, instead of a vector, which was a bit clumsy. +*) 28 jul 2006: Cleaned the code and added new error checks. + Corrected terminology "deflate" into "inflate". +*) 23 jun 2006: Added SDL example in the documentation in the header, this + example allows easy debugging by displaying the PNG and its transparency. +*) 22 jun 2006: (!) Changed way to obtain error value. Added + loadFile function for convenience. Made decodePNG32 faster. +*) 21 jun 2006: (!) Changed type of info vector to unsigned. + Changed position of palette in info vector. Fixed an important bug that + happened on PNGs with an uncompressed block. +*) 16 jun 2006: Internally changed unsigned into unsigned where + needed, and performed some optimizations. +*) 07 jun 2006: (!) Renamed functions to decodePNG and placed them + in LodePNG namespace. Changed the order of the parameters. Rewrote the + documentation in the header. Renamed files to lodepng.cpp and lodepng.h +*) 22 apr 2006: Optimized and improved some code +*) 07 sep 2005: (!) Changed to std::vector interface +*) 12 aug 2005: Initial release (C++, decoder only) + + +13. contact information +----------------------- + +Feel free to contact me with suggestions, problems, comments, ... concerning +LodePNG. If you encounter a PNG image that doesn't work properly with this +decoder, feel free to send it and I'll use it to find and fix the problem. + +My email address is (puzzle the account and domain together with an @ symbol): +Domain: gmail dot com. +Account: lode dot vandevenne. + + +Copyright (c) 2005-2020 Lode Vandevenne +*/ diff --git a/src/extra/libs/png/lv_png.c b/src/extra/libs/png/lv_png.c new file mode 100644 index 0000000000..22ec122dac --- /dev/null +++ b/src/extra/libs/png/lv_png.c @@ -0,0 +1,237 @@ +/** + * @file lv_png.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../../../lvgl.h" +#if LV_USE_PNG + +#include "lv_png.h" +#include "lodepng.h" +#include +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t decoder_info(struct _lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header); +static lv_res_t decoder_open(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc); +static void decoder_close(lv_img_decoder_t * dec, lv_img_decoder_dsc_t * dsc); +static void convert_color_depth(uint8_t * img, uint32_t px_cnt); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Register the PNG decoder functions in LittlevGL + */ +void lv_png_init(void) +{ + lv_img_decoder_t * dec = lv_img_decoder_create(); + lv_img_decoder_set_info_cb(dec, decoder_info); + lv_img_decoder_set_open_cb(dec, decoder_open); + lv_img_decoder_set_close_cb(dec, decoder_close); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Get info about a PNG image + * @param src can be file name or pointer to a C array + * @param header store the info here + * @return LV_RES_OK: no error; LV_RES_INV: can't get the info + */ +static lv_res_t decoder_info(struct _lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header) +{ + (void) decoder; /*Unused*/ + lv_img_src_t src_type = lv_img_src_get_type(src); /*Get the source type*/ + + /*If it's a PNG file...*/ + if(src_type == LV_IMG_SRC_FILE) { + const char * fn = src; + if(!strcmp(&fn[strlen(fn) - 3], "png")) { /*Check the extension*/ + + /* Read the width and height from the file. They have a constant location: + * [16..23]: width + * [24..27]: height + */ + uint32_t size[2]; + lv_fs_file_t f; + lv_fs_res_t res = lv_fs_open(&f, fn, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) return LV_RES_INV; + lv_fs_seek(&f, 16, LV_FS_SEEK_SET); + uint32_t rn; + lv_fs_read(&f, &size, 8, &rn); + if(rn != 8) return LV_RES_INV; + lv_fs_close(&f); + /*Save the data in the header*/ + header->always_zero = 0; + header->cf = LV_IMG_CF_RAW_ALPHA; + /*The width and height are stored in Big endian format so convert them to little endian*/ + header->w = (lv_coord_t) ((size[0] & 0xff000000) >> 24) + ((size[0] & 0x00ff0000) >> 8); + header->h = (lv_coord_t) ((size[1] & 0xff000000) >> 24) + ((size[1] & 0x00ff0000) >> 8); + + return LV_RES_OK; + } + } + /*If it's a PNG file in a C array...*/ + else if(src_type == LV_IMG_SRC_VARIABLE) { + const lv_img_dsc_t * img_dsc = src; + header->always_zero = 0; + header->cf = img_dsc->header.cf; /*Save the color format*/ + header->w = img_dsc->header.w; /*Save the color width*/ + header->h = img_dsc->header.h; /*Save the color height*/ + return LV_RES_OK; + } + + return LV_RES_INV; /*If didn't succeeded earlier then it's an error*/ +} + + +/** + * Open a PNG image and return the decided image + * @param src can be file name or pointer to a C array + * @param style style of the image object (unused now but certain formats might use it) + * @return pointer to the decoded image or `LV_IMG_DECODER_OPEN_FAIL` if failed + */ +static lv_res_t decoder_open(lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc) +{ + + (void) decoder; /*Unused*/ + uint32_t error; /*For the return values of PNG decoder functions*/ + + uint8_t * img_data = NULL; + + /*If it's a PNG file...*/ + if(dsc->src_type == LV_IMG_SRC_FILE) { + const char * fn = dsc->src; + + if(!strcmp(&fn[strlen(fn) - 3], "png")) { /*Check the extension*/ + + /*Load the PNG file into buffer. It's still compressed (not decoded)*/ + unsigned char * png_data; /*Pointer to the loaded data. Same as the original file just loaded into the RAM*/ + size_t png_data_size; /*Size of `png_data` in bytes*/ + + error = lodepng_load_file(&png_data, &png_data_size, fn); /*Load the file*/ + if(error) { + printf("error %u: %s\n", error, lodepng_error_text(error)); + return LV_RES_INV; + } + + /*Decode the PNG image*/ + uint32_t png_width; /*Will be the width of the decoded image*/ + uint32_t png_height; /*Will be the width of the decoded image*/ + + /*Decode the loaded image in ARGB8888 */ + error = lodepng_decode32(&img_data, &png_width, &png_height, png_data, png_data_size); + free(png_data); /*Free the loaded file*/ + if(error) { + printf("error %u: %s\n", error, lodepng_error_text(error)); + return LV_RES_INV; + } + + /*Convert the image to the system's color depth*/ + convert_color_depth(img_data, png_width * png_height); + dsc->img_data = img_data; + return LV_RES_OK; /*The image is fully decoded. Return with its pointer*/ + } + } + /*If it's a PNG file in a C array...*/ + else if(dsc->src_type == LV_IMG_SRC_VARIABLE) { + const lv_img_dsc_t * img_dsc = dsc->src; + uint32_t png_width; /*No used, just required by he decoder*/ + uint32_t png_height; /*No used, just required by he decoder*/ + + /*Decode the image in ARGB8888 */ + error = lodepng_decode32(&img_data, &png_width, &png_height, img_dsc->data, img_dsc->data_size); + + if(error) { + return LV_RES_INV; + } + + /*Convert the image to the system's color depth*/ + convert_color_depth(img_data, png_width * png_height); + + dsc->img_data = img_data; + return LV_RES_OK; /*Return with its pointer*/ + } + + return LV_RES_INV; /*If not returned earlier then it failed*/ +} + +/** + * Free the allocated resources + */ +static void decoder_close(lv_img_decoder_t *decoder, lv_img_decoder_dsc_t *dsc) +{ + (void)decoder; /*Unused*/ + if (dsc->img_data) { + free((uint8_t *)dsc->img_data); + dsc->img_data = NULL; + } +} + +/** + * If the display is not in 32 bit format (ARGB888) then covert the image to the current color depth + * @param img the ARGB888 image + * @param px_cnt number of pixels in `img` + */ +static void convert_color_depth(uint8_t * img, uint32_t px_cnt) +{ +#if LV_COLOR_DEPTH == 32 + lv_color32_t * img_argb = (lv_color32_t*)img; + lv_color_t c; + lv_color_t * img_c = (lv_color_t *) img; + uint32_t i; + for(i = 0; i < px_cnt; i++) { + c = lv_color_make(img_argb[i].ch.red, img_argb[i].ch.green, img_argb[i].ch.blue); + img_c[i].ch.red = c.ch.blue; + img_c[i].ch.blue = c.ch.red; + } +#elif LV_COLOR_DEPTH == 16 + lv_color32_t * img_argb = (lv_color32_t*)img; + lv_color_t c; + uint32_t i; + for(i = 0; i < px_cnt; i++) { + c = lv_color_make(img_argb[i].ch.blue, img_argb[i].ch.green, img_argb[i].ch.red); + img[i*3 + 2] = img_argb[i].ch.alpha; + img[i*3 + 1] = c.full >> 8; + img[i*3 + 0] = c.full & 0xFF; + } +#elif LV_COLOR_DEPTH == 8 + lv_color32_t * img_argb = (lv_color32_t*)img; + lv_color_t c; + uint32_t i; + for(i = 0; i < px_cnt; i++) { + c = lv_color_make(img_argb[i].red, img_argb[i].green, img_argb[i].blue); + img[i*2 + 1] = img_argb[i].alpha; + img[i*2 + 0] = c.full + } +#endif +} + +#endif /*LV_USE_PNG*/ + + diff --git a/src/extra/libs/png/lv_png.h b/src/extra/libs/png/lv_png.h new file mode 100644 index 0000000000..1e9b02d656 --- /dev/null +++ b/src/extra/libs/png/lv_png.h @@ -0,0 +1,46 @@ +/** + * @file lv_png.h + * + */ + +#ifndef LV_PNG_H +#define LV_PNG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lv_conf_internal.h" +#if LV_USE_PNG + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Register the PNG decoder functions in LittlevGL + */ +void lv_png_init(void); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_PNG*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_PNG_H*/ diff --git a/src/extra/libs/qrcode/lv_qrcode.c b/src/extra/libs/qrcode/lv_qrcode.c new file mode 100644 index 0000000000..d1dd05bb78 --- /dev/null +++ b/src/extra/libs/qrcode/lv_qrcode.c @@ -0,0 +1,174 @@ +/** + * @file lv_qrcode.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_qrcode.h" +#if LV_USE_QRCODE + +#include "qrcodegen.h" + +/********************* + * DEFINES + *********************/ +#define QR_SIZE 140 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create an empty QR code (an `lv_canvas`) object. + * @param parent point to an object where to create the QR code + * @param size width and height of the QR code + * @param dark_color dark color of the QR code + * @param light_color light color of the QR code + * @return pointer to the created QR code object + */ +lv_obj_t * lv_qrcode_create(lv_obj_t * parent, lv_coord_t size, lv_color_t dark_color, lv_color_t light_color) +{ + uint32_t buf_size = LV_CANVAS_BUF_SIZE_INDEXED_1BIT(size, size); + uint8_t * buf = lv_mem_alloc(buf_size); + LV_ASSERT_MALLOC(buf); + if(buf == NULL) return NULL; + + lv_obj_t * canvas = lv_canvas_create(parent); + if(canvas == NULL) return NULL; + + lv_canvas_set_buffer(canvas, buf, size, size, LV_IMG_CF_INDEXED_1BIT); + lv_canvas_set_palette(canvas, 0, dark_color); + lv_canvas_set_palette(canvas, 1, light_color); + + return canvas; + +} + +/** + * Set the data of a QR code object + * @param qrcode pointer to aQ code object + * @param data data to display + * @param data_len length of data in bytes + * @return LV_RES_OK: if no error; LV_RES_INV: on error + */ +lv_res_t lv_qrcode_update(lv_obj_t * qrcode, const void * data, uint32_t data_len) +{ + lv_color_t c; + c.full = 1; + lv_canvas_fill_bg(qrcode, c, LV_OPA_COVER); + + if(data_len > qrcodegen_BUFFER_LEN_MAX) return LV_RES_INV; + + uint8_t * qr0 = lv_mem_alloc(qrcodegen_BUFFER_LEN_MAX); + LV_ASSERT_MALLOC(qr0); + uint8_t * data_tmp = lv_mem_alloc(qrcodegen_BUFFER_LEN_MAX); + LV_ASSERT_MALLOC(data_tmp); + memcpy(data_tmp, data, data_len); + + bool ok = qrcodegen_encodeBinary(data_tmp, data_len, + qr0, qrcodegen_Ecc_MEDIUM, + qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, + qrcodegen_Mask_AUTO, true); + + if (!ok) { + lv_mem_free(qr0); + lv_mem_free(data_tmp); + return LV_RES_INV; + } + + + lv_img_dsc_t * imgdsc = lv_canvas_get_img(qrcode); + lv_coord_t obj_w = imgdsc->header.w; + int qr_size = qrcodegen_getSize(qr0); + int scale = obj_w / qr_size; + int scaled = qr_size * scale; + int margin = (obj_w - scaled) / 2; + uint8_t * buf_u8 = (uint8_t *)imgdsc->data + 8; /*+8 skip the palette*/ + + /* Copy the qr code canvas: + * A simple `lv_canvas_set_px` would work but it's slow for so many pixels. + * So buffer 1 byte (8 px) from the qr code and set it in the canvas image */ + uint32_t row_byte_cnt = (imgdsc->header.w + 7) >> 3; + int y; + for (y = margin; y < scaled + margin; y+=scale) { + uint8_t b = 0; + uint8_t p = 0; + bool aligned = false; + int x; + for (x = margin; x < scaled + margin; x++) { + bool a = qrcodegen_getModule(qr0, (x - margin) / scale, (y - margin) / scale); + + if(aligned == false && (x & 0x7) == 0) aligned = true; + + if(aligned == false) { + c.full = a ? 0 : 1; + lv_canvas_set_px(qrcode, x, y, c); + } else { + if(!a) b |= (1 << (7 - p)); + p++; + if(p == 8) { + uint32_t px = row_byte_cnt * y + (x >> 3); + buf_u8[px] = b; + b = 0; + p = 0; + } + } + } + + /*Process the last byte of the row*/ + if(p) { + /*Make the rest of the bits white*/ + b |= (1 << (8 - p)) - 1; + + uint32_t px = row_byte_cnt * y + (x >> 3); + buf_u8[px] = b; + } + + /*The Qr is probably scaled so simply to the repeated rows*/ + int s; + const uint8_t * row_ori = buf_u8 + row_byte_cnt * y; + for(s = 1; s < scale; s++) { + memcpy((uint8_t*)buf_u8 + row_byte_cnt * (y + s), row_ori, row_byte_cnt); + } + } + + lv_mem_free(qr0); + lv_mem_free(data_tmp); + return LV_RES_OK; +} + +/** + * Delete a QR code object + * @param qrcode pointer to a QR code obejct + */ +void lv_qrcode_delete(lv_obj_t * qrcode) +{ + lv_img_dsc_t * img = lv_canvas_get_img(qrcode); + lv_img_cache_invalidate_src(img); + lv_mem_free((void*)img->data); + lv_obj_del(qrcode); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*LV_USE_QRCODE*/ diff --git a/src/extra/libs/qrcode/lv_qrcode.h b/src/extra/libs/qrcode/lv_qrcode.h new file mode 100644 index 0000000000..550fadcc2e --- /dev/null +++ b/src/extra/libs/qrcode/lv_qrcode.h @@ -0,0 +1,66 @@ +/** + * @file lv_qrcode + * + */ + +#ifndef LV_QRCODE_H +#define LV_QRCODE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "../../../lvgl.h" +#if LV_USE_QRCODE + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create an empty QR code (an `lv_canvas`) object. + * @param parent point to an object where to create the QR code + * @param size width and height of the QR code + * @param dark_color dark color of the QR code + * @param light_color light color of the QR code + * @return pointer to the created QR code object + */ +lv_obj_t * lv_qrcode_create(lv_obj_t * parent, lv_coord_t size, lv_color_t dark_color, lv_color_t light_color); + +/** + * Set the data of a QR code object + * @param qrcode pointer to aQ code object + * @param data data to display + * @param data_len length of data in bytes + * @return LV_RES_OK: if no error; LV_RES_INV: on error + */ +lv_res_t lv_qrcode_update(lv_obj_t * qrcode, const void * data, uint32_t data_len); + +/** + * Delete a QR code object + * @param qrcode pointer to a QR code object + */ +void lv_qrcode_delete(lv_obj_t * qrcode); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_QRCODE*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_QRCODE_H*/ diff --git a/src/extra/libs/qrcode/qrcodegen.c b/src/extra/libs/qrcode/qrcodegen.c new file mode 100644 index 0000000000..67c1917391 --- /dev/null +++ b/src/extra/libs/qrcode/qrcodegen.c @@ -0,0 +1,1009 @@ +/* + * QR Code generator library (C) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include +#include +#include +#include +#include "qrcodegen.h" + +#ifndef QRCODEGEN_TEST + #define testable static // Keep functions private +#else + #define testable // Expose private functions +#endif + + +/*---- Forward declarations for private functions ----*/ + +// Regarding all public and private functions defined in this source file: +// - They require all pointer/array arguments to be not null unless the array length is zero. +// - They only read input scalar/array arguments, write to output pointer/array +// arguments, and return scalar values; they are "pure" functions. +// - They don't read mutable global variables or write to any global variables. +// - They don't perform I/O, read the clock, print to console, etc. +// - They allocate a small and constant amount of stack memory. +// - They don't allocate or free any memory on the heap. +// - They don't recurse or mutually recurse. All the code +// could be inlined into the top-level public functions. +// - They run in at most quadratic time with respect to input arguments. +// Most functions run in linear time, and some in constant time. +// There are no unbounded loops or non-obvious termination conditions. +// - They are completely thread-safe if the caller does not give the +// same writable buffer to concurrent calls to these functions. + +testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen); + +testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); +testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl); +testable int getNumRawDataModules(int ver); + +testable void calcReedSolomonGenerator(int degree, uint8_t result[]); +testable void calcReedSolomonRemainder(const uint8_t data[], int dataLen, + const uint8_t generator[], int degree, uint8_t result[]); +testable uint8_t finiteFieldMultiply(uint8_t x, uint8_t y); + +testable void initializeFunctionModules(int version, uint8_t qrcode[]); +static void drawWhiteFunctionModules(uint8_t qrcode[], int version); +static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]); +testable int getAlignmentPatternPositions(int version, uint8_t result[7]); +static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]); + +static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]); +static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask); +static long getPenaltyScore(const uint8_t qrcode[]); +static void addRunToHistory(unsigned char run, unsigned char history[7]); +static bool hasFinderLikePattern(const unsigned char runHistory[7]); + +testable bool getModule(const uint8_t qrcode[], int x, int y); +testable void setModule(uint8_t qrcode[], int x, int y, bool isBlack); +testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isBlack); +static bool getBit(int x, int i); + +testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars); +testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version); +static int numCharCountBits(enum qrcodegen_Mode mode, int version); + + + +/*---- Private tables of constants ----*/ + +// The set of all legal characters in alphanumeric mode, where each character +// value maps to the index in the string. For checking text and encoding segments. +static const char *ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + +// For generating error correction codes. +testable const int8_t ECC_CODEWORDS_PER_BLOCK[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Low + {-1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, // Medium + {-1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // Quartile + {-1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30}, // High +}; + +#define qrcodegen_REED_SOLOMON_DEGREE_MAX 30 // Based on the table above + +// For generating error correction codes. +testable const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41] = { + // Version: (note that index 0 is for padding, and is set to an illegal value) + //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + {-1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25}, // Low + {-1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49}, // Medium + {-1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68}, // Quartile + {-1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81}, // High +}; + +// For automatic mask pattern selection. +static const int PENALTY_N1 = 3; +static const int PENALTY_N2 = 3; +static const int PENALTY_N3 = 40; +static const int PENALTY_N4 = 10; + + + +/*---- High-level QR Code encoding functions ----*/ + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { + + size_t textLen = strlen(text); + if (textLen == 0) + return qrcodegen_encodeSegmentsAdvanced(NULL, 0, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode); + size_t bufLen = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion); + + struct qrcodegen_Segment seg; + if (qrcodegen_isNumeric(text)) { + if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_NUMERIC, textLen) > bufLen) + goto fail; + seg = qrcodegen_makeNumeric(text, tempBuffer); + } else if (qrcodegen_isAlphanumeric(text)) { + if (qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_ALPHANUMERIC, textLen) > bufLen) + goto fail; + seg = qrcodegen_makeAlphanumeric(text, tempBuffer); + } else { + if (textLen > bufLen) + goto fail; + for (size_t i = 0; i < textLen; i++) + tempBuffer[i] = (uint8_t)text[i]; + seg.mode = qrcodegen_Mode_BYTE; + seg.bitLength = calcSegmentBitLength(seg.mode, textLen); + if (seg.bitLength == -1) + goto fail; + seg.numChars = (int)textLen; + seg.data = tempBuffer; + } + return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode); + +fail: + qrcode[0] = 0; // Set size to invalid value for safety + return false; +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl) { + + struct qrcodegen_Segment seg; + seg.mode = qrcodegen_Mode_BYTE; + seg.bitLength = calcSegmentBitLength(seg.mode, dataLen); + if (seg.bitLength == -1) { + qrcode[0] = 0; // Set size to invalid value for safety + return false; + } + seg.numChars = (int)dataLen; + seg.data = dataAndTemp; + return qrcodegen_encodeSegmentsAdvanced(&seg, 1, ecl, minVersion, maxVersion, mask, boostEcl, dataAndTemp, qrcode); +} + + +// Appends the given number of low-order bits of the given value to the given byte-based +// bit buffer, increasing the bit length. Requires 0 <= numBits <= 16 and val < 2^numBits. +testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen) { + assert(0 <= numBits && numBits <= 16 && (unsigned long)val >> numBits == 0); + for (int i = numBits - 1; i >= 0; i--, (*bitLen)++) + buffer[*bitLen >> 3] |= ((val >> i) & 1) << (7 - (*bitLen & 7)); +} + + + +/*---- Low-level QR Code encoding functions ----*/ + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, + enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]) { + return qrcodegen_encodeSegmentsAdvanced(segs, len, ecl, + qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, -1, true, tempBuffer, qrcode); +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, + int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]) { + assert(segs != NULL || len == 0); + assert(qrcodegen_VERSION_MIN <= minVersion && minVersion <= maxVersion && maxVersion <= qrcodegen_VERSION_MAX); + assert(0 <= (int)ecl && (int)ecl <= 3 && -1 <= (int)mask && (int)mask <= 7); + + // Find the minimal version number to use + int version, dataUsedBits; + for (version = minVersion; ; version++) { + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = getTotalBits(segs, len, version); + if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) + break; // This version number is found to be suitable + if (version >= maxVersion) { // All versions in the range could not fit the given data + qrcode[0] = 0; // Set size to invalid value for safety + return false; + } + } + assert(dataUsedBits != -1); + + // Increase the error correction level while the data still fits in the current version number + for (int i = (int)qrcodegen_Ecc_MEDIUM; i <= (int)qrcodegen_Ecc_HIGH; i++) { // From low to high + if (boostEcl && dataUsedBits <= getNumDataCodewords(version, (enum qrcodegen_Ecc)i) * 8) + ecl = (enum qrcodegen_Ecc)i; + } + + // Concatenate all segments to create the data bit string + memset(qrcode, 0, qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); + int bitLen = 0; + for (size_t i = 0; i < len; i++) { + const struct qrcodegen_Segment *seg = &segs[i]; + appendBitsToBuffer((int)seg->mode, 4, qrcode, &bitLen); + appendBitsToBuffer(seg->numChars, numCharCountBits(seg->mode, version), qrcode, &bitLen); + for (int j = 0; j < seg->bitLength; j++) + appendBitsToBuffer((seg->data[j >> 3] >> (7 - (j & 7))) & 1, 1, qrcode, &bitLen); + } + assert(bitLen == dataUsedBits); + + // Add terminator and pad up to a byte if applicable + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; + assert(bitLen <= dataCapacityBits); + int terminatorBits = dataCapacityBits - bitLen; + if (terminatorBits > 4) + terminatorBits = 4; + appendBitsToBuffer(0, terminatorBits, qrcode, &bitLen); + appendBitsToBuffer(0, (8 - bitLen % 8) % 8, qrcode, &bitLen); + assert(bitLen % 8 == 0); + + // Pad with alternating bytes until data capacity is reached + for (uint8_t padByte = 0xEC; bitLen < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + appendBitsToBuffer(padByte, 8, qrcode, &bitLen); + + // Draw function and data codeword modules + addEccAndInterleave(qrcode, version, ecl, tempBuffer); + initializeFunctionModules(version, qrcode); + drawCodewords(tempBuffer, getNumRawDataModules(version) / 8, qrcode); + drawWhiteFunctionModules(qrcode, version); + initializeFunctionModules(version, tempBuffer); + + // Handle masking + if (mask == qrcodegen_Mask_AUTO) { // Automatically choose best mask + long minPenalty = LONG_MAX; + for (int i = 0; i < 8; i++) { + enum qrcodegen_Mask msk = (enum qrcodegen_Mask)i; + applyMask(tempBuffer, qrcode, msk); + drawFormatBits(ecl, msk, qrcode); + long penalty = getPenaltyScore(qrcode); + if (penalty < minPenalty) { + mask = msk; + minPenalty = penalty; + } + applyMask(tempBuffer, qrcode, msk); // Undoes the mask due to XOR + } + } + assert(0 <= (int)mask && (int)mask <= 7); + applyMask(tempBuffer, qrcode, mask); + drawFormatBits(ecl, mask, qrcode); + return true; +} + + + +/*---- Error correction code generation functions ----*/ + +// Appends error correction bytes to each block of the given data array, then interleaves +// bytes from the blocks and stores them in the result array. data[0 : dataLen] contains +// the input data. data[dataLen : rawCodewords] is used as a temporary work area and will +// be clobbered by this function. The final answer is stored in result[0 : rawCodewords]. +testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]) { + // Calculate parameter numbers + assert(0 <= (int)ecl && (int)ecl < 4 && qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); + int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version]; + int blockEccLen = ECC_CODEWORDS_PER_BLOCK [(int)ecl][version]; + int rawCodewords = getNumRawDataModules(version) / 8; + int dataLen = getNumDataCodewords(version, ecl); + int numShortBlocks = numBlocks - rawCodewords % numBlocks; + int shortBlockDataLen = rawCodewords / numBlocks - blockEccLen; + + // Split data into blocks, calculate ECC, and interleave + // (not concatenate) the bytes into a single sequence + uint8_t generator[qrcodegen_REED_SOLOMON_DEGREE_MAX]; + calcReedSolomonGenerator(blockEccLen, generator); + const uint8_t *dat = data; + for (int i = 0; i < numBlocks; i++) { + int datLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); + uint8_t *ecc = &data[dataLen]; // Temporary storage + calcReedSolomonRemainder(dat, datLen, generator, blockEccLen, ecc); + for (int j = 0, k = i; j < datLen; j++, k += numBlocks) { // Copy data + if (j == shortBlockDataLen) + k -= numShortBlocks; + result[k] = dat[j]; + } + for (int j = 0, k = dataLen + i; j < blockEccLen; j++, k += numBlocks) // Copy ECC + result[k] = ecc[j]; + dat += datLen; + } +} + + +// Returns the number of 8-bit codewords that can be used for storing data (not ECC), +// for the given version number and error correction level. The result is in the range [9, 2956]. +testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl) { + int v = version, e = (int)ecl; + assert(0 <= e && e < 4); + return getNumRawDataModules(v) / 8 + - ECC_CODEWORDS_PER_BLOCK [e][v] + * NUM_ERROR_CORRECTION_BLOCKS[e][v]; +} + + +// Returns the number of data bits that can be stored in a QR Code of the given version number, after +// all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. +// The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. +testable int getNumRawDataModules(int ver) { + assert(qrcodegen_VERSION_MIN <= ver && ver <= qrcodegen_VERSION_MAX); + int result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + int numAlign = ver / 7 + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) + result -= 36; + } + return result; +} + + + +/*---- Reed-Solomon ECC generator functions ----*/ + +// Calculates the Reed-Solomon generator polynomial of the given degree, storing in result[0 : degree]. +testable void calcReedSolomonGenerator(int degree, uint8_t result[]) { + // Start with the monomial x^0 + assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); + memset(result, 0, degree * sizeof(result[0])); + result[degree - 1] = 1; + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // drop the highest term, and store the rest of the coefficients in order of descending powers. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + uint8_t root = 1; + for (int i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (int j = 0; j < degree; j++) { + result[j] = finiteFieldMultiply(result[j], root); + if (j + 1 < degree) + result[j] ^= result[j + 1]; + } + root = finiteFieldMultiply(root, 0x02); + } +} + + +// Calculates the remainder of the polynomial data[0 : dataLen] when divided by the generator[0 : degree], where all +// polynomials are in big endian and the generator has an implicit leading 1 term, storing the result in result[0 : degree]. +testable void calcReedSolomonRemainder(const uint8_t data[], int dataLen, + const uint8_t generator[], int degree, uint8_t result[]) { + + // Perform polynomial division + assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); + memset(result, 0, degree * sizeof(result[0])); + for (int i = 0; i < dataLen; i++) { + uint8_t factor = data[i] ^ result[0]; + memmove(&result[0], &result[1], (degree - 1) * sizeof(result[0])); + result[degree - 1] = 0; + for (int j = 0; j < degree; j++) + result[j] ^= finiteFieldMultiply(generator[j], factor); + } +} + +#undef qrcodegen_REED_SOLOMON_DEGREE_MAX + + +// Returns the product of the two given field elements modulo GF(2^8/0x11D). +// All inputs are valid. This could be implemented as a 256*256 lookup table. +testable uint8_t finiteFieldMultiply(uint8_t x, uint8_t y) { + // Russian peasant multiplication + uint8_t z = 0; + for (int i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >> 7) * 0x11D); + z ^= ((y >> i) & 1) * x; + } + return z; +} + + + +/*---- Drawing function modules ----*/ + +// Clears the given QR Code grid with white modules for the given +// version's size, then marks every function module as black. +testable void initializeFunctionModules(int version, uint8_t qrcode[]) { + // Initialize QR Code + int qrsize = version * 4 + 17; + memset(qrcode, 0, ((qrsize * qrsize + 7) / 8 + 1) * sizeof(qrcode[0])); + qrcode[0] = (uint8_t)qrsize; + + // Fill horizontal and vertical timing patterns + fillRectangle(6, 0, 1, qrsize, qrcode); + fillRectangle(0, 6, qrsize, 1, qrcode); + + // Fill 3 finder patterns (all corners except bottom right) and format bits + fillRectangle(0, 0, 9, 9, qrcode); + fillRectangle(qrsize - 8, 0, 8, 9, qrcode); + fillRectangle(0, qrsize - 8, 9, 8, qrcode); + + // Fill numerous alignment patterns + uint8_t alignPatPos[7]; + int numAlign = getAlignmentPatternPositions(version, alignPatPos); + for (int i = 0; i < numAlign; i++) { + for (int j = 0; j < numAlign; j++) { + // Don't draw on the three finder corners + if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))) + fillRectangle(alignPatPos[i] - 2, alignPatPos[j] - 2, 5, 5, qrcode); + } + } + + // Fill version blocks + if (version >= 7) { + fillRectangle(qrsize - 11, 0, 3, 6, qrcode); + fillRectangle(0, qrsize - 11, 6, 3, qrcode); + } +} + + +// Draws white function modules and possibly some black modules onto the given QR Code, without changing +// non-function modules. This does not draw the format bits. This requires all function modules to be previously +// marked black (namely by initializeFunctionModules()), because this may skip redrawing black function modules. +static void drawWhiteFunctionModules(uint8_t qrcode[], int version) { + // Draw horizontal and vertical timing patterns + int qrsize = qrcodegen_getSize(qrcode); + for (int i = 7; i < qrsize - 7; i += 2) { + setModule(qrcode, 6, i, false); + setModule(qrcode, i, 6, false); + } + + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + for (int dy = -4; dy <= 4; dy++) { + for (int dx = -4; dx <= 4; dx++) { + int dist = abs(dx); + if (abs(dy) > dist) + dist = abs(dy); + if (dist == 2 || dist == 4) { + setModuleBounded(qrcode, 3 + dx, 3 + dy, false); + setModuleBounded(qrcode, qrsize - 4 + dx, 3 + dy, false); + setModuleBounded(qrcode, 3 + dx, qrsize - 4 + dy, false); + } + } + } + + // Draw numerous alignment patterns + uint8_t alignPatPos[7]; + int numAlign = getAlignmentPatternPositions(version, alignPatPos); + for (int i = 0; i < numAlign; i++) { + for (int j = 0; j < numAlign; j++) { + if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) + continue; // Don't draw on the three finder corners + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) + setModule(qrcode, alignPatPos[i] + dx, alignPatPos[j] + dy, dx == 0 && dy == 0); + } + } + } + + // Draw version blocks + if (version >= 7) { + // Calculate error correction code and pack bits + int rem = version; // version is uint6, in the range [7, 40] + for (int i = 0; i < 12; i++) + rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); + long bits = (long)version << 12 | rem; // uint18 + assert(bits >> 18 == 0); + + // Draw two copies + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 3; j++) { + int k = qrsize - 11 + j; + setModule(qrcode, k, i, (bits & 1) != 0); + setModule(qrcode, i, k, (bits & 1) != 0); + bits >>= 1; + } + } + } +} + + +// Draws two copies of the format bits (with its own error correction code) based +// on the given mask and error correction level. This always draws all modules of +// the format bits, unlike drawWhiteFunctionModules() which might skip black modules. +static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]) { + // Calculate error correction code and pack bits + assert(0 <= (int)mask && (int)mask <= 7); + static const int table[] = {1, 0, 3, 2}; + int data = table[(int)ecl] << 3 | (int)mask; // errCorrLvl is uint2, mask is uint3 + int rem = data; + for (int i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >> 9) * 0x537); + int bits = (data << 10 | rem) ^ 0x5412; // uint15 + assert(bits >> 15 == 0); + + // Draw first copy + for (int i = 0; i <= 5; i++) + setModule(qrcode, 8, i, getBit(bits, i)); + setModule(qrcode, 8, 7, getBit(bits, 6)); + setModule(qrcode, 8, 8, getBit(bits, 7)); + setModule(qrcode, 7, 8, getBit(bits, 8)); + for (int i = 9; i < 15; i++) + setModule(qrcode, 14 - i, 8, getBit(bits, i)); + + // Draw second copy + int qrsize = qrcodegen_getSize(qrcode); + for (int i = 0; i < 8; i++) + setModule(qrcode, qrsize - 1 - i, 8, getBit(bits, i)); + for (int i = 8; i < 15; i++) + setModule(qrcode, 8, qrsize - 15 + i, getBit(bits, i)); + setModule(qrcode, 8, qrsize - 8, true); // Always black +} + + +// Calculates and stores an ascending list of positions of alignment patterns +// for this version number, returning the length of the list (in the range [0,7]). +// Each position is in the range [0,177), and are used on both the x and y axes. +// This could be implemented as lookup table of 40 variable-length lists of unsigned bytes. +testable int getAlignmentPatternPositions(int version, uint8_t result[7]) { + if (version == 1) + return 0; + int numAlign = version / 7 + 2; + int step = (version == 32) ? 26 : + (version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; + for (int i = numAlign - 1, pos = version * 4 + 10; i >= 1; i--, pos -= step) + result[i] = pos; + result[0] = 6; + return numAlign; +} + + +// Sets every pixel in the range [left : left + width] * [top : top + height] to black. +static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]) { + for (int dy = 0; dy < height; dy++) { + for (int dx = 0; dx < width; dx++) + setModule(qrcode, left + dx, top + dy, true); + } +} + + + +/*---- Drawing data modules and masking ----*/ + +// Draws the raw codewords (including data and ECC) onto the given QR Code. This requires the initial state of +// the QR Code to be black at function modules and white at codeword modules (including unused remainder bits). +static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]) { + int qrsize = qrcodegen_getSize(qrcode); + int i = 0; // Bit index into the data + // Do the funny zigzag scan + for (int right = qrsize - 1; right >= 1; right -= 2) { // Index of right column in each column pair + if (right == 6) + right = 5; + for (int vert = 0; vert < qrsize; vert++) { // Vertical counter + for (int j = 0; j < 2; j++) { + int x = right - j; // Actual x coordinate + bool upward = ((right + 1) & 2) == 0; + int y = upward ? qrsize - 1 - vert : vert; // Actual y coordinate + if (!getModule(qrcode, x, y) && i < dataLen * 8) { + bool black = getBit(data[i >> 3], 7 - (i & 7)); + setModule(qrcode, x, y, black); + i++; + } + // If this QR Code has any remainder bits (0 to 7), they were assigned as + // 0/false/white by the constructor and are left unchanged by this method + } + } + } + assert(i == dataLen * 8); +} + + +// XORs the codeword modules in this QR Code with the given mask pattern. +// The function modules must be marked and the codeword bits must be drawn +// before masking. Due to the arithmetic of XOR, calling applyMask() with +// the same mask value a second time will undo the mask. A final well-formed +// QR Code needs exactly one (not zero, two, etc.) mask applied. +static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask) { + assert(0 <= (int)mask && (int)mask <= 7); // Disallows qrcodegen_Mask_AUTO + int qrsize = qrcodegen_getSize(qrcode); + for (int y = 0; y < qrsize; y++) { + for (int x = 0; x < qrsize; x++) { + if (getModule(functionModules, x, y)) + continue; + bool invert; + switch ((int)mask) { + case 0: invert = (x + y) % 2 == 0; break; + case 1: invert = y % 2 == 0; break; + case 2: invert = x % 3 == 0; break; + case 3: invert = (x + y) % 3 == 0; break; + case 4: invert = (x / 3 + y / 2) % 2 == 0; break; + case 5: invert = x * y % 2 + x * y % 3 == 0; break; + case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; + case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; + default: assert(false); return; + } + bool val = getModule(qrcode, x, y); + setModule(qrcode, x, y, val ^ invert); + } + } +} + + +// Calculates and returns the penalty score based on state of the given QR Code's current modules. +// This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. +static long getPenaltyScore(const uint8_t qrcode[]) { + int qrsize = qrcodegen_getSize(qrcode); + long result = 0; + + // Adjacent modules in row having same color, and finder-like patterns + for (int y = 0; y < qrsize; y++) { + unsigned char runHistory[7] = {0}; + bool color = false; + unsigned char runX = 0; + for (int x = 0; x < qrsize; x++) { + if (getModule(qrcode, x, y) == color) { + runX++; + if (runX == 5) + result += PENALTY_N1; + else if (runX > 5) + result++; + } else { + addRunToHistory(runX, runHistory); + if (!color && hasFinderLikePattern(runHistory)) + result += PENALTY_N3; + color = getModule(qrcode, x, y); + runX = 1; + } + } + addRunToHistory(runX, runHistory); + if (color) + addRunToHistory(0, runHistory); // Dummy run of white + if (hasFinderLikePattern(runHistory)) + result += PENALTY_N3; + } + // Adjacent modules in column having same color, and finder-like patterns + for (int x = 0; x < qrsize; x++) { + unsigned char runHistory[7] = {0}; + bool color = false; + unsigned char runY = 0; + for (int y = 0; y < qrsize; y++) { + if (getModule(qrcode, x, y) == color) { + runY++; + if (runY == 5) + result += PENALTY_N1; + else if (runY > 5) + result++; + } else { + addRunToHistory(runY, runHistory); + if (!color && hasFinderLikePattern(runHistory)) + result += PENALTY_N3; + color = getModule(qrcode, x, y); + runY = 1; + } + } + addRunToHistory(runY, runHistory); + if (color) + addRunToHistory(0, runHistory); // Dummy run of white + if (hasFinderLikePattern(runHistory)) + result += PENALTY_N3; + } + + // 2*2 blocks of modules having same color + for (int y = 0; y < qrsize - 1; y++) { + for (int x = 0; x < qrsize - 1; x++) { + bool color = getModule(qrcode, x, y); + if ( color == getModule(qrcode, x + 1, y) && + color == getModule(qrcode, x, y + 1) && + color == getModule(qrcode, x + 1, y + 1)) + result += PENALTY_N2; + } + } + + // Balance of black and white modules + int black = 0; + for (int y = 0; y < qrsize; y++) { + for (int x = 0; x < qrsize; x++) { + if (getModule(qrcode, x, y)) + black++; + } + } + int total = qrsize * qrsize; // Note that size is odd, so black/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% + int k = (int)((labs(black * 20L - total * 10L) + total - 1) / total) - 1; + result += k * PENALTY_N4; + return result; +} + + +// Inserts the given value to the front of the given array, which shifts over the +// existing values and deletes the last value. A helper function for getPenaltyScore(). +static void addRunToHistory(unsigned char run, unsigned char history[7]) { + memmove(&history[1], &history[0], 6 * sizeof(history[0])); + history[0] = run; +} + + +// Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and +// surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore(). +// Must only be called immediately after a run of white modules has ended. +static bool hasFinderLikePattern(const unsigned char runHistory[7]) { + unsigned char n = runHistory[1]; + // The maximum QR Code size is 177, hence the run length n <= 177. + // Arithmetic is promoted to int, so n*4 will not overflow. + return n > 0 && runHistory[2] == n && runHistory[4] == n && runHistory[5] == n + && runHistory[3] == n * 3 && (runHistory[0] >= n * 4 || runHistory[6] >= n * 4); +} + + + +/*---- Basic QR Code information ----*/ + +// Public function - see documentation comment in header file. +int qrcodegen_getSize(const uint8_t qrcode[]) { + assert(qrcode != NULL); + int result = qrcode[0]; + assert((qrcodegen_VERSION_MIN * 4 + 17) <= result + && result <= (qrcodegen_VERSION_MAX * 4 + 17)); + return result; +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y) { + assert(qrcode != NULL); + int qrsize = qrcode[0]; + return (0 <= x && x < qrsize && 0 <= y && y < qrsize) && getModule(qrcode, x, y); +} + + +// Gets the module at the given coordinates, which must be in bounds. +testable bool getModule(const uint8_t qrcode[], int x, int y) { + int qrsize = qrcode[0]; + assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize); + int index = y * qrsize + x; + return getBit(qrcode[(index >> 3) + 1], index & 7); +} + + +// Sets the module at the given coordinates, which must be in bounds. +testable void setModule(uint8_t qrcode[], int x, int y, bool isBlack) { + int qrsize = qrcode[0]; + assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize); + int index = y * qrsize + x; + int bitIndex = index & 7; + int byteIndex = (index >> 3) + 1; + if (isBlack) + qrcode[byteIndex] |= 1 << bitIndex; + else + qrcode[byteIndex] &= (1 << bitIndex) ^ 0xFF; +} + + +// Sets the module at the given coordinates, doing nothing if out of bounds. +testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isBlack) { + int qrsize = qrcode[0]; + if (0 <= x && x < qrsize && 0 <= y && y < qrsize) + setModule(qrcode, x, y, isBlack); +} + + +// Returns true iff the i'th bit of x is set to 1. Requires x >= 0 and 0 <= i <= 14. +static bool getBit(int x, int i) { + return ((x >> i) & 1) != 0; +} + + + +/*---- Segment handling ----*/ + +// Public function - see documentation comment in header file. +bool qrcodegen_isAlphanumeric(const char *text) { + assert(text != NULL); + for (; *text != '\0'; text++) { + if (strchr(ALPHANUMERIC_CHARSET, *text) == NULL) + return false; + } + return true; +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_isNumeric(const char *text) { + assert(text != NULL); + for (; *text != '\0'; text++) { + if (*text < '0' || *text > '9') + return false; + } + return true; +} + + +// Public function - see documentation comment in header file. +size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars) { + int temp = calcSegmentBitLength(mode, numChars); + if (temp == -1) + return SIZE_MAX; + assert(0 <= temp && temp <= INT16_MAX); + return ((size_t)temp + 7) / 8; +} + + +// Returns the number of data bits needed to represent a segment +// containing the given number of characters using the given mode. Notes: +// - Returns -1 on failure, i.e. numChars > INT16_MAX or +// the number of needed bits exceeds INT16_MAX (i.e. 32767). +// - Otherwise, all valid results are in the range [0, INT16_MAX]. +// - For byte mode, numChars measures the number of bytes, not Unicode code points. +// - For ECI mode, numChars must be 0, and the worst-case number of bits is returned. +// An actual ECI segment can have shorter data. For non-ECI modes, the result is exact. +testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars) { + // All calculations are designed to avoid overflow on all platforms + if (numChars > (unsigned int)INT16_MAX) + return -1; + long result = (long)numChars; + if (mode == qrcodegen_Mode_NUMERIC) + result = (result * 10 + 2) / 3; // ceil(10/3 * n) + else if (mode == qrcodegen_Mode_ALPHANUMERIC) + result = (result * 11 + 1) / 2; // ceil(11/2 * n) + else if (mode == qrcodegen_Mode_BYTE) + result *= 8; + else if (mode == qrcodegen_Mode_KANJI) + result *= 13; + else if (mode == qrcodegen_Mode_ECI && numChars == 0) + result = 3 * 8; + else { // Invalid argument + assert(false); + return -1; + } + assert(result >= 0); + if (result > (unsigned int)INT16_MAX) + return -1; + return (int)result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]) { + assert(data != NULL || len == 0); + struct qrcodegen_Segment result; + result.mode = qrcodegen_Mode_BYTE; + result.bitLength = calcSegmentBitLength(result.mode, len); + assert(result.bitLength != -1); + result.numChars = (int)len; + if (len > 0) + memcpy(buf, data, len * sizeof(buf[0])); + result.data = buf; + return result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]) { + assert(digits != NULL); + struct qrcodegen_Segment result; + size_t len = strlen(digits); + result.mode = qrcodegen_Mode_NUMERIC; + int bitLen = calcSegmentBitLength(result.mode, len); + assert(bitLen != -1); + result.numChars = (int)len; + if (bitLen > 0) + memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); + result.bitLength = 0; + + unsigned int accumData = 0; + int accumCount = 0; + for (; *digits != '\0'; digits++) { + char c = *digits; + assert('0' <= c && c <= '9'); + accumData = accumData * 10 + (unsigned int)(c - '0'); + accumCount++; + if (accumCount == 3) { + appendBitsToBuffer(accumData, 10, buf, &result.bitLength); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 or 2 digits remaining + appendBitsToBuffer(accumData, accumCount * 3 + 1, buf, &result.bitLength); + assert(result.bitLength == bitLen); + result.data = buf; + return result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]) { + assert(text != NULL); + struct qrcodegen_Segment result; + size_t len = strlen(text); + result.mode = qrcodegen_Mode_ALPHANUMERIC; + int bitLen = calcSegmentBitLength(result.mode, len); + assert(bitLen != -1); + result.numChars = (int)len; + if (bitLen > 0) + memset(buf, 0, ((size_t)bitLen + 7) / 8 * sizeof(buf[0])); + result.bitLength = 0; + + unsigned int accumData = 0; + int accumCount = 0; + for (; *text != '\0'; text++) { + const char *temp = strchr(ALPHANUMERIC_CHARSET, *text); + assert(temp != NULL); + accumData = accumData * 45 + (unsigned int)(temp - ALPHANUMERIC_CHARSET); + accumCount++; + if (accumCount == 2) { + appendBitsToBuffer(accumData, 11, buf, &result.bitLength); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 character remaining + appendBitsToBuffer(accumData, 6, buf, &result.bitLength); + assert(result.bitLength == bitLen); + result.data = buf; + return result; +} + + +// Public function - see documentation comment in header file. +struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]) { + struct qrcodegen_Segment result; + result.mode = qrcodegen_Mode_ECI; + result.numChars = 0; + result.bitLength = 0; + if (assignVal < 0) + assert(false); + else if (assignVal < (1 << 7)) { + memset(buf, 0, 1 * sizeof(buf[0])); + appendBitsToBuffer(assignVal, 8, buf, &result.bitLength); + } else if (assignVal < (1 << 14)) { + memset(buf, 0, 2 * sizeof(buf[0])); + appendBitsToBuffer(2, 2, buf, &result.bitLength); + appendBitsToBuffer(assignVal, 14, buf, &result.bitLength); + } else if (assignVal < 1000000L) { + memset(buf, 0, 3 * sizeof(buf[0])); + appendBitsToBuffer(6, 3, buf, &result.bitLength); + appendBitsToBuffer(assignVal >> 10, 11, buf, &result.bitLength); + appendBitsToBuffer(assignVal & 0x3FF, 10, buf, &result.bitLength); + } else + assert(false); + result.data = buf; + return result; +} + + +// Calculates the number of bits needed to encode the given segments at the given version. +// Returns a non-negative number if successful. Otherwise returns -1 if a segment has too +// many characters to fit its length field, or the total bits exceeds INT16_MAX. +testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version) { + assert(segs != NULL || len == 0); + long result = 0; + for (size_t i = 0; i < len; i++) { + int numChars = segs[i].numChars; + int bitLength = segs[i].bitLength; + assert(0 <= numChars && numChars <= INT16_MAX); + assert(0 <= bitLength && bitLength <= INT16_MAX); + int ccbits = numCharCountBits(segs[i].mode, version); + assert(0 <= ccbits && ccbits <= 16); + if (numChars >= (1L << ccbits)) + return -1; // The segment's length doesn't fit the field's bit width + result += 4L + ccbits + bitLength; + if (result > INT16_MAX) + return -1; // The sum might overflow an int type + } + assert(0 <= result && result <= INT16_MAX); + return (int)result; +} + + +// Returns the bit width of the character count field for a segment in the given mode +// in a QR Code at the given version number. The result is in the range [0, 16]. +static int numCharCountBits(enum qrcodegen_Mode mode, int version) { + assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); + int i = (version + 7) / 17; + switch (mode) { + case qrcodegen_Mode_NUMERIC : { static const int temp[] = {10, 12, 14}; return temp[i]; } + case qrcodegen_Mode_ALPHANUMERIC: { static const int temp[] = { 9, 11, 13}; return temp[i]; } + case qrcodegen_Mode_BYTE : { static const int temp[] = { 8, 16, 16}; return temp[i]; } + case qrcodegen_Mode_KANJI : { static const int temp[] = { 8, 10, 12}; return temp[i]; } + case qrcodegen_Mode_ECI : return 0; + default: assert(false); return -1; // Dummy value + } +} diff --git a/src/extra/libs/qrcode/qrcodegen.h b/src/extra/libs/qrcode/qrcodegen.h new file mode 100644 index 0000000000..30f9db2304 --- /dev/null +++ b/src/extra/libs/qrcode/qrcodegen.h @@ -0,0 +1,311 @@ +/* + * QR Code generator library (C) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#pragma once + +#include +#include +#include + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + * This library creates QR Code symbols, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + * A QR Code structure is an immutable square grid of black and white cells. + * The library provides functions to create a QR Code from text or binary data. + * The library covers the QR Code Model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. + * + * Ways to create a QR Code object: + * - High level: Take the payload data and call qrcodegen_encodeText() or qrcodegen_encodeBinary(). + * - Low level: Custom-make the list of segments and call + * qrcodegen_encodeSegments() or qrcodegen_encodeSegmentsAdvanced(). + * (Note that all ways require supplying the desired error correction level and various byte buffers.) + */ + + +/*---- Enum and struct types----*/ + +/* + * The error correction level in a QR Code symbol. + */ +enum qrcodegen_Ecc { + // Must be declared in ascending order of error protection + // so that an internal qrcodegen function works properly + qrcodegen_Ecc_LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords + qrcodegen_Ecc_MEDIUM , // The QR Code can tolerate about 15% erroneous codewords + qrcodegen_Ecc_QUARTILE, // The QR Code can tolerate about 25% erroneous codewords + qrcodegen_Ecc_HIGH , // The QR Code can tolerate about 30% erroneous codewords +}; + + +/* + * The mask pattern used in a QR Code symbol. + */ +enum qrcodegen_Mask { + // A special value to tell the QR Code encoder to + // automatically select an appropriate mask pattern + qrcodegen_Mask_AUTO = -1, + // The eight actual mask patterns + qrcodegen_Mask_0 = 0, + qrcodegen_Mask_1, + qrcodegen_Mask_2, + qrcodegen_Mask_3, + qrcodegen_Mask_4, + qrcodegen_Mask_5, + qrcodegen_Mask_6, + qrcodegen_Mask_7, +}; + + +/* + * Describes how a segment's data bits are interpreted. + */ +enum qrcodegen_Mode { + qrcodegen_Mode_NUMERIC = 0x1, + qrcodegen_Mode_ALPHANUMERIC = 0x2, + qrcodegen_Mode_BYTE = 0x4, + qrcodegen_Mode_KANJI = 0x8, + qrcodegen_Mode_ECI = 0x7, +}; + + +/* + * A segment of character/binary/control data in a QR Code symbol. + * The mid-level way to create a segment is to take the payload data + * and call a factory function such as qrcodegen_makeNumeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and initialize a qrcodegen_Segment struct with appropriate values. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + * Moreover, the maximum allowed bit length is 32767 because + * the largest QR Code (version 40) has 31329 modules. + */ +struct qrcodegen_Segment { + // The mode indicator of this segment. + enum qrcodegen_Mode mode; + + // The length of this segment's unencoded data. Measured in characters for + // numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + // Always zero or positive. Not the same as the data's bit length. + int numChars; + + // The data bits of this segment, packed in bitwise big endian. + // Can be null if the bit length is zero. + uint8_t *data; + + // The number of valid data bits used in the buffer. Requires + // 0 <= bitLength <= 32767, and bitLength <= (capacity of data array) * 8. + // The character count (numChars) must agree with the mode and the bit buffer length. + int bitLength; +}; + + + +/*---- Macro constants and functions ----*/ + +#define qrcodegen_VERSION_MIN 1 // The minimum version number supported in the QR Code Model 2 standard +#define qrcodegen_VERSION_MAX 40 // The maximum version number supported in the QR Code Model 2 standard + +// Calculates the number of bytes needed to store any QR Code up to and including the given version number, +// as a compile-time constant. For example, 'uint8_t buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];' +// can store any single QR Code from version 1 to 25 (inclusive). The result fits in an int (or int16). +// Requires qrcodegen_VERSION_MIN <= n <= qrcodegen_VERSION_MAX. +#define qrcodegen_BUFFER_LEN_FOR_VERSION(n) ((((n) * 4 + 17) * ((n) * 4 + 17) + 7) / 8 + 1) + +// The worst-case number of bytes needed to store one QR Code, up to and including +// version 40. This value equals 3918, which is just under 4 kilobytes. +// Use this more convenient value to avoid calculating tighter memory bounds for buffers. +#define qrcodegen_BUFFER_LEN_MAX qrcodegen_BUFFER_LEN_FOR_VERSION(qrcodegen_VERSION_MAX) + + + +/*---- Functions (high level) to generate QR Codes ----*/ + +/* + * Encodes the given text string to a QR Code, returning true if encoding succeeded. + * If the data is too long to fit in any version in the given range + * at the given ECC level, then false is returned. + * - The input text must be encoded in UTF-8 and contain no NULs. + * - The variables ecl and mask must correspond to enum constant values. + * - Requires 1 <= minVersion <= maxVersion <= 40. + * - The arrays tempBuffer and qrcode must each have a length + * of at least qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion). + * - After the function returns, tempBuffer contains no useful data. + * - If successful, the resulting QR Code may use numeric, + * alphanumeric, or byte mode to encode the text. + * - In the most optimistic case, a QR Code at version 40 with low ECC + * can hold any UTF-8 string up to 2953 bytes, or any alphanumeric string + * up to 4296 characters, or any digit string up to 7089 characters. + * These numbers represent the hard upper limit of the QR Code standard. + * - Please consult the QR Code specification for information on + * data capacities per version, ECC level, and text encoding mode. + */ +bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); + + +/* + * Encodes the given binary data to a QR Code, returning true if encoding succeeded. + * If the data is too long to fit in any version in the given range + * at the given ECC level, then false is returned. + * - The input array range dataAndTemp[0 : dataLen] should normally be + * valid UTF-8 text, but is not required by the QR Code standard. + * - The variables ecl and mask must correspond to enum constant values. + * - Requires 1 <= minVersion <= maxVersion <= 40. + * - The arrays dataAndTemp and qrcode must each have a length + * of at least qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion). + * - After the function returns, the contents of dataAndTemp may have changed, + * and does not represent useful data anymore. + * - If successful, the resulting QR Code will use byte mode to encode the data. + * - In the most optimistic case, a QR Code at version 40 with low ECC can hold any byte + * sequence up to length 2953. This is the hard upper limit of the QR Code standard. + * - Please consult the QR Code specification for information on + * data capacities per version, ECC level, and text encoding mode. + */ +bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcode[], + enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); + + +/*---- Functions (low level) to generate QR Codes ----*/ + +/* + * Renders a QR Code representing the given segments at the given error correction level. + * The smallest possible QR Code version is automatically chosen for the output. Returns true if + * QR Code creation succeeded, or false if the data is too long to fit in any version. The ECC level + * of the result may be higher than the ecl argument if it can be done without increasing the version. + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). + * To save memory, the segments' data buffers can alias/overlap tempBuffer, and will + * result in them being clobbered, but the QR Code output will still be correct. + * But the qrcode array must not overlap tempBuffer or any segment's data buffer. + */ +bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, + enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]); + + +/* + * Renders a QR Code representing the given segments with the given encoding parameters. + * Returns true if QR Code creation succeeded, or false if the data is too long to fit in the range of versions. + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask number is either between 0 to 7 (inclusive) to force that + * mask, or -1 to automatically choose an appropriate mask (which may be slow). + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). + * To save memory, the segments' data buffers can alias/overlap tempBuffer, and will + * result in them being clobbered, but the QR Code output will still be correct. + * But the qrcode array must not overlap tempBuffer or any segment's data buffer. + */ +bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, + int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]); + + +/* + * Tests whether the given string can be encoded as a segment in alphanumeric mode. + * A string is encodable iff each character is in the following set: 0 to 9, A to Z + * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ +bool qrcodegen_isAlphanumeric(const char *text); + + +/* + * Tests whether the given string can be encoded as a segment in numeric mode. + * A string is encodable iff each character is in the range 0 to 9. + */ +bool qrcodegen_isNumeric(const char *text); + + +/* + * Returns the number of bytes (uint8_t) needed for the data buffer of a segment + * containing the given number of characters using the given mode. Notes: + * - Returns SIZE_MAX on failure, i.e. numChars > INT16_MAX or + * the number of needed bits exceeds INT16_MAX (i.e. 32767). + * - Otherwise, all valid results are in the range [0, ceil(INT16_MAX / 8)], i.e. at most 4096. + * - It is okay for the user to allocate more bytes for the buffer than needed. + * - For byte mode, numChars measures the number of bytes, not Unicode code points. + * - For ECI mode, numChars must be 0, and the worst-case number of bytes is returned. + * An actual ECI segment can have shorter data. For non-ECI modes, the result is exact. + */ +size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars); + + +/* + * Returns a segment representing the given binary data encoded in + * byte mode. All input byte arrays are acceptable. Any text string + * can be converted to UTF-8 bytes and encoded as a byte mode segment. + */ +struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]); + + +/* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ +struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[]); + + +/* + * Returns a segment representing the given text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ +struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t buf[]); + + +/* + * Returns a segment representing an Extended Channel Interpretation + * (ECI) designator with the given assignment value. + */ +struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]); + + +/*---- Functions to extract raw data from QR Codes ----*/ + +/* + * Returns the side length of the given QR Code, assuming that encoding succeeded. + * The result is in the range [21, 177]. Note that the length of the array buffer + * is related to the side length - every 'uint8_t qrcode[]' must have length at least + * qrcodegen_BUFFER_LEN_FOR_VERSION(version), which equals ceil(size^2 / 8 + 1). + */ +int qrcodegen_getSize(const uint8_t qrcode[]); + + +/* + * Returns the color of the module (pixel) at the given coordinates, which is false + * for white or true for black. The top left corner has the coordinates (x=0, y=0). + * If the given coordinates are out of bounds, then false (white) is returned. + */ +bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y); + + +#ifdef __cplusplus +} +#endif diff --git a/src/extra/libs/sjpg/lv_sjpg.c b/src/extra/libs/sjpg/lv_sjpg.c new file mode 100644 index 0000000000..1e485a1bc2 --- /dev/null +++ b/src/extra/libs/sjpg/lv_sjpg.c @@ -0,0 +1,871 @@ +/** + * @file lv_sjpg.c + * + */ + +/*---------------------------------------------------------------------------------------------------------------------------------- +/ Added normal JPG support [7/10/2020] +/ ---------- +/ SJPEG is a custom created modified JPEG file format for small embedded platforms. +/ It will contain multiple JPEG fragments all embedded into a single file with a custom header. +/ This makes JPEG decoding easier using any JPEG library. Overall file size will be almost +/ similar to the parent jpeg file. We can generate sjpeg from any jpeg using a python script +/ provided along with this project. +/ (by vinodstanur | 2020 ) +/ SJPEG FILE STRUCTURE +/ -------------------------------------------------------------------------------------------------------------------------------- +/ Bytes | Value | +/ -------------------------------------------------------------------------------------------------------------------------------- +/ +/ 0 - 7 | "_SJPG__" followed by '\0' +/ +/ 8 - 13 | "V1.00" followed by '\0' [VERSION OF SJPG FILE for future compatibiliby] +/ +/ 14 - 15 | X_RESOLUTION (width) [little endian] +/ +/ 16 - 17 | Y_RESOLUTION (height) [little endian] +/ +/ 18 - 19 | TOTAL_FRAMES inside sjpeg [little endian] +/ +/ 20 - 21 | JPEG BLOCK WIDTH (16 normally) [little endian] +/ +/ 22 - [(TOTAL_FRAMES*2 )] | SIZE OF EACH JPEG SPLIT FRAGMENTS (FRAME_INFO_ARRAY) +/ +/ SJPEG data | Each JPEG frame can be extracted from SJPEG data by parsing the FRAME_INFO_ARRAY one time. +/ +/---------------------------------------------------------------------------------------------------------------------------------- +/ JPEG DECODER +/ ------------ +/ We are using TJpgDec - Tiny JPEG Decompressor library from ELM-CHAN for decoding each split-jpeg fragments. +/ The tjpgd.c and tjpgd.h is not modified and those are used as it is. So if any update comes for the tiny-jpeg, +/ just replace those files with updated files. +/---------------------------------------------------------------------------------------------------------------------------------*/ + +/********************* + * INCLUDES + *********************/ + +#include "../../../lvgl.h" +#if LV_USE_SJPG + +#include "tjpgd.h" +#include "lv_sjpg.h" + + +/********************* + * DEFINES + *********************/ +#define TJPGD_WORKBUFF_SIZE 4096 //Recommended by TJPGD libray + +//NEVER EDIT THESE OFFSET VALUES +#define SJPEG_VERSION_OFFSET 8 +#define SJPEG_X_RES_OFFSET 14 +#define SJPEG_y_RES_OFFSET 16 +#define SJPEG_TOTAL_FRAMES_OFFSET 18 +#define SJPEG_BLOCK_WIDTH_OFFSET 20 +#define SJPEG_FRAME_INFO_ARRAY_OFFSET 22 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t decoder_info( lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header ); +static lv_res_t decoder_open( lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc ); +static lv_res_t decoder_read_line( lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x,lv_coord_t y, lv_coord_t len, uint8_t * buf ); +static void decoder_close( lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc ); +static size_t input_func ( JDEC* jd, uint8_t* buff, size_t ndata ); +static int is_jpg( const uint8_t *raw_data ); +static void lv_sjpg_cleanup( SJPEG* sjpeg ); +static void lv_sjpg_free( SJPEG* sjpeg ); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ +void lv_split_jpeg_init( void ) +{ + lv_img_decoder_t* dec = lv_img_decoder_create( ); + lv_img_decoder_set_info_cb( dec, decoder_info ); + lv_img_decoder_set_open_cb( dec, decoder_open ); + lv_img_decoder_set_close_cb( dec, decoder_close ); + lv_img_decoder_set_read_line_cb( dec, decoder_read_line ); +} + +/********************** + * STATIC FUNCTIONS + **********************/ +/** + * Get info about an SJPG / JPG image + * @param decoder pointer to the decoder where this function belongs + * @param src can be file name or pointer to a C array + * @param header store the info here + * @return LV_RES_OK: no error; LV_RES_INV: can't get the info + */ +static lv_res_t decoder_info( lv_img_decoder_t * decoder, const void * src, lv_img_header_t * header ) +{ + LV_UNUSED(decoder); + + /*Check whether the type `src` is known by the decoder*/ + /* Read the SJPG/JPG header and find `width` and `height` */ + + lv_img_src_t src_type = lv_img_src_get_type(src); /*Get the source type*/ + + lv_res_t ret = LV_RES_OK; + + if(src_type == LV_IMG_SRC_VARIABLE) { + uint8_t *raw_sjpeg_data = (uint8_t *)((lv_img_dsc_t * )src)->data; + const uint32_t raw_sjpeg_data_size = ((lv_img_dsc_t *)src)->data_size; + + if(!strncmp((char *)raw_sjpeg_data, "_SJPG__", strlen("_SJPG__") )) { + + raw_sjpeg_data += 14; //seek to res info ... refer sjpeg format + header->always_zero = 0; + header->cf = LV_IMG_CF_RAW; + + header->w = *raw_sjpeg_data++; + header->w |= *raw_sjpeg_data++ << 8; + + header->h = *raw_sjpeg_data++; + header->h |= *raw_sjpeg_data++ << 8; + + return ret; + + } else if( is_jpg( raw_sjpeg_data ) == true ) { + header->always_zero = 0; + header->cf = LV_IMG_CF_RAW; + + uint8_t *workb_temp = lv_mem_alloc( TJPGD_WORKBUFF_SIZE ); + if(!workb_temp) return LV_RES_INV; + + io_source_t io_source_temp; + io_source_temp.type = SJPEG_IO_SOURCE_C_ARRAY; + io_source_temp.raw_sjpg_data = raw_sjpeg_data; + io_source_temp.raw_sjpg_data_size = raw_sjpeg_data_size; + io_source_temp.raw_sjpg_data_next_read_pos = 0; + + JDEC jd_tmp; + + JRESULT rc = jd_prepare( &jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp); + if(rc == JDR_OK ) { + header->w = jd_tmp.width; + header->h = jd_tmp.height; + + } else { + ret = LV_RES_INV; + goto end; + } + + end: + lv_mem_free(workb_temp); + + return ret; + + } + } + else if( src_type == LV_IMG_SRC_FILE ) { + const char * fn = src; + if(!strcmp(&fn[strlen(fn) - 5], ".sjpg")) { + + uint8_t buff[22]; + memset(buff, 0, sizeof(buff)); + + lv_fs_file_t file; + lv_fs_res_t res = lv_fs_open(&file , fn, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) return 78; + + uint32_t rn; + res = lv_fs_read(&file, buff, 8, &rn); + if(res != LV_FS_RES_OK || rn != 8) { + lv_fs_close(&file); + return LV_RES_INV; + } + + if(strcmp((char *)buff, "_SJPG__") == 0 ) { + lv_fs_seek(&file, 14, LV_FS_SEEK_SET); + res = lv_fs_read(&file, buff, 4, &rn); + if(res != LV_FS_RES_OK || rn != 4 ) { + lv_fs_close(&file); + return LV_RES_INV; + } + header->always_zero = 0; + header->cf = LV_IMG_CF_RAW; + uint8_t *raw_sjpeg_data = buff; + header->w = *raw_sjpeg_data++; + header->w |= *raw_sjpeg_data++ << 8; + header->h = *raw_sjpeg_data++; + header->h |= *raw_sjpeg_data++ << 8; + lv_fs_close(&file); + return LV_RES_OK; + + } + } else if(!strcmp(&fn[strlen(fn) - 4], ".jpg")) { + lv_fs_file_t file; + lv_fs_res_t res = lv_fs_open(&file , fn, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) return 78; + + uint8_t *workb_temp = lv_mem_alloc( TJPGD_WORKBUFF_SIZE ); + if(!workb_temp) { + lv_fs_close(&file); + return LV_RES_INV; + } + + io_source_t io_source_temp; + io_source_temp.type = SJPEG_IO_SOURCE_DISK; + io_source_temp.raw_sjpg_data_next_read_pos = 0; + io_source_temp.img_cache_buff = NULL; + io_source_temp.lv_file = file; + JDEC jd_tmp; + + JRESULT rc = jd_prepare( &jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp); + lv_mem_free(workb_temp); + lv_fs_close(&file); + + if(rc == JDR_OK ) { + header->always_zero = 0; + header->cf = LV_IMG_CF_RAW; + header->w = jd_tmp.width; + header->h = jd_tmp.height; + return LV_RES_OK; + } + } + } + return LV_RES_INV; +} + +static int img_data_cb( JDEC* jd, void* data, JRECT* rect ) +{ + io_source_t *io = jd->device; + uint8_t *cache = io->img_cache_buff; + const int xres = io->img_cache_x_res; + uint8_t *buf = data; + const int INPUT_PIXEL_SIZE = 3; + const int row_width = rect->right - rect->left + 1; // Row width in pixels. + const int row_size = row_width * INPUT_PIXEL_SIZE; // Row size (bytes). + + for( int y = rect->top; y <= rect->bottom; y++ ) { + int row_offset = y * xres * INPUT_PIXEL_SIZE + rect->left * INPUT_PIXEL_SIZE; + memcpy( cache + row_offset, buf, row_size ); + buf += row_size; + } + + return 1; +} + +static size_t input_func ( JDEC* jd, uint8_t* buff, size_t ndata ) +{ + io_source_t *io = jd->device; + + if(!io) return 0; + + if(io->type == SJPEG_IO_SOURCE_C_ARRAY) { + const uint32_t bytes_left = io->raw_sjpg_data_size - io->raw_sjpg_data_next_read_pos; + const uint32_t to_read = ndata <= bytes_left ? (uint32_t)ndata : bytes_left; + if (to_read == 0) + return 0; + if(buff) { + memcpy(buff, io->raw_sjpg_data + io->raw_sjpg_data_next_read_pos, to_read); + } + io->raw_sjpg_data_next_read_pos += to_read; + return to_read; + } + else if(io->type == SJPEG_IO_SOURCE_DISK) { + + lv_fs_file_t* lv_file_p = &(io->lv_file); + + if( buff ) { + uint32_t rn = 0; + lv_fs_read(lv_file_p, buff, (uint32_t)ndata, &rn); + return rn; + } else { + uint32_t pos; + lv_fs_tell(lv_file_p, &pos); + lv_fs_seek(lv_file_p, (uint32_t)(ndata + pos), LV_FS_SEEK_SET); + return ndata; + } + } + return 0; +} + +/** + * Open SJPG image and return the decided image + * @param decoder pointer to the decoder where this function belongs + * @param dsc pointer to a descriptor which describes this decoding session + * @return LV_RES_OK: no error; LV_RES_INV: can't get the info + */ +static lv_res_t decoder_open( lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc ) +{ + LV_UNUSED(decoder); + lv_res_t lv_ret = LV_RES_OK; + + if(dsc->src_type == LV_IMG_SRC_VARIABLE) { + uint8_t *data; + SJPEG* sjpeg = ( SJPEG* ) dsc->user_data; + if( sjpeg == NULL ) { + sjpeg = lv_mem_alloc( sizeof( SJPEG ) ); + if( !sjpeg ) return LV_RES_INV; + + memset(sjpeg, 0, sizeof(SJPEG)); + + dsc->user_data = sjpeg; + sjpeg->sjpeg_data = (uint8_t *)( ( lv_img_dsc_t* )(dsc->src) )->data; + sjpeg->sjpeg_data_size = ( ( lv_img_dsc_t* )(dsc->src) )->data_size; + } + + if( !strncmp( ( char * ) sjpeg->sjpeg_data, "_SJPG__", strlen("_SJPG__") ) ) { + + data = sjpeg->sjpeg_data; + data += 14; + + sjpeg->sjpeg_x_res = *data++; + sjpeg->sjpeg_x_res |= *data++ << 8; + + sjpeg->sjpeg_y_res = *data++; + sjpeg->sjpeg_y_res |= *data++ << 8; + + sjpeg->sjpeg_total_frames = *data++; + sjpeg->sjpeg_total_frames |= *data++ << 8; + + sjpeg->sjpeg_single_frame_height = *data++; + sjpeg->sjpeg_single_frame_height |= *data++ << 8; + + sjpeg->frame_base_array = lv_mem_alloc( sizeof(uint8_t *) * sjpeg->sjpeg_total_frames ); + if( ! sjpeg->frame_base_array ) { + lv_sjpg_cleanup( sjpeg ); + sjpeg = NULL; + return LV_RES_INV; + } + + sjpeg->frame_base_offset = NULL; + + uint8_t *img_frame_base = data + sjpeg->sjpeg_total_frames *2; + sjpeg->frame_base_array[0] = img_frame_base; + + for( int i = 1; i < sjpeg->sjpeg_total_frames; i++ ) { + int offset = *data++; + offset |= *data++ <<8; + sjpeg->frame_base_array[i] = sjpeg->frame_base_array[i-1] + offset; + } + sjpeg->sjpeg_cache_frame_index = -1; + sjpeg->frame_cache = (void *)lv_mem_alloc( sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3/*2*/ ); + if( ! sjpeg->frame_cache ) { + lv_sjpg_cleanup( sjpeg ); + sjpeg = NULL; + return LV_RES_INV; + } + sjpeg->io.img_cache_buff = sjpeg->frame_cache; + sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res; + sjpeg->workb = lv_mem_alloc( TJPGD_WORKBUFF_SIZE ); + if( ! sjpeg->workb ) { + lv_sjpg_cleanup( sjpeg ); + sjpeg = NULL; + return LV_RES_INV; + } + + sjpeg->tjpeg_jd = lv_mem_alloc( sizeof( JDEC ) ); + if( ! sjpeg->tjpeg_jd ) { + lv_sjpg_cleanup( sjpeg ); + sjpeg = NULL; + return LV_RES_INV; + } + sjpeg->io.type = SJPEG_IO_SOURCE_C_ARRAY; + sjpeg->io.lv_file.file_d = NULL; + dsc->img_data = NULL; + return lv_ret; + } + + else if( is_jpg( sjpeg->sjpeg_data ) == true ) { + + uint8_t *workb_temp = lv_mem_alloc( TJPGD_WORKBUFF_SIZE ); + if( ! workb_temp ) { + lv_sjpg_cleanup( sjpeg ); + sjpeg = NULL; + return LV_RES_INV; + } + io_source_t io_source_temp; + io_source_temp.type = SJPEG_IO_SOURCE_C_ARRAY; + io_source_temp.raw_sjpg_data = sjpeg->sjpeg_data; + io_source_temp.raw_sjpg_data_size = sjpeg->sjpeg_data_size; + io_source_temp.raw_sjpg_data_next_read_pos = 0; + + JDEC jd_tmp; + JRESULT rc = jd_prepare( &jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp); + lv_mem_free(workb_temp); + + + if(rc == JDR_OK ) { + sjpeg->sjpeg_x_res = jd_tmp.width; + sjpeg->sjpeg_y_res = jd_tmp.height; + sjpeg->sjpeg_total_frames = 1; + sjpeg->sjpeg_single_frame_height = jd_tmp.height; + + sjpeg->frame_base_array = lv_mem_alloc( sizeof(uint8_t *) * sjpeg->sjpeg_total_frames ); + if( ! sjpeg->frame_base_array ) { + lv_sjpg_cleanup( sjpeg ); + sjpeg = NULL; + return LV_RES_INV; + } + sjpeg->frame_base_offset = NULL; + + uint8_t *img_frame_base = sjpeg->sjpeg_data; + sjpeg->frame_base_array[0] = img_frame_base; + + sjpeg->sjpeg_cache_frame_index = -1; + sjpeg->frame_cache = (void *)lv_mem_alloc( sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3 ); + if( ! sjpeg->frame_cache ) { + lv_sjpg_cleanup( sjpeg ); + sjpeg = NULL; + return LV_RES_INV; + } + + sjpeg->io.img_cache_buff = sjpeg->frame_cache; + sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res; + sjpeg->workb = lv_mem_alloc( TJPGD_WORKBUFF_SIZE ); + if( ! sjpeg->workb ) { + lv_sjpg_cleanup( sjpeg ); + sjpeg = NULL; + return LV_RES_INV; + } + + sjpeg->tjpeg_jd = lv_mem_alloc( sizeof( JDEC ) ); + if( ! sjpeg->tjpeg_jd ) { + lv_sjpg_cleanup( sjpeg ); + sjpeg = NULL; + return LV_RES_INV; + } + + sjpeg->io.type = SJPEG_IO_SOURCE_C_ARRAY; + sjpeg->io.lv_file.file_d = NULL; + dsc->img_data = NULL; + return lv_ret; + } else { + lv_ret = LV_RES_INV; + goto end; + } + + end: + lv_mem_free(workb_temp); + + return lv_ret; + } + } + else if(dsc->src_type == LV_IMG_SRC_FILE) { + /* If all fine, then the file will be kept open */ + const char * fn = dsc->src; + uint8_t *data; + + if(!strcmp(&fn[strlen(fn) - 5], ".sjpg")) { + + uint8_t buff[22]; + memset(buff, 0, sizeof(buff)); + + + lv_fs_file_t lv_file; + lv_fs_res_t res = lv_fs_open(&lv_file , fn, LV_FS_MODE_RD); + if(res != LV_FS_RES_OK) { + return 78; + } + + + uint32_t rn; + res = lv_fs_read(&lv_file, buff, 22, &rn); + if(res != LV_FS_RES_OK || rn != 22 ) { + lv_fs_close(&lv_file); + return LV_RES_INV; + } + + if(strcmp((char *)buff, "_SJPG__") == 0 ) { + + SJPEG* sjpeg = ( SJPEG* ) dsc->user_data; + if(sjpeg == NULL) { + sjpeg = lv_mem_alloc(sizeof(SJPEG)); + + if( ! sjpeg ) { + lv_fs_close(&lv_file); + return LV_RES_INV; + } + memset(sjpeg, 0, sizeof(SJPEG)); + + dsc->user_data = sjpeg; + sjpeg->sjpeg_data = (uint8_t *)( (lv_img_dsc_t* )(dsc->src) )->data; + sjpeg->sjpeg_data_size = ( (lv_img_dsc_t* )(dsc->src) )->data_size; + } + data = buff; + data += 14; + + sjpeg->sjpeg_x_res = *data++; + sjpeg->sjpeg_x_res |= *data++ << 8; + + sjpeg->sjpeg_y_res = *data++; + sjpeg->sjpeg_y_res |= *data++ << 8; + + sjpeg->sjpeg_total_frames = *data++; + sjpeg->sjpeg_total_frames |= *data++ << 8; + + sjpeg->sjpeg_single_frame_height = *data++; + sjpeg->sjpeg_single_frame_height |= *data++ << 8; + + sjpeg->frame_base_array = NULL;//lv_mem_alloc( sizeof(uint8_t *) * sjpeg->sjpeg_total_frames ); + sjpeg->frame_base_offset = lv_mem_alloc( sizeof(int) * sjpeg->sjpeg_total_frames ); + if( ! sjpeg->frame_base_offset ) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + int img_frame_start_offset = (SJPEG_FRAME_INFO_ARRAY_OFFSET + sjpeg->sjpeg_total_frames *2); + sjpeg->frame_base_offset[0] = img_frame_start_offset; //pointer used to save integer for now... + + for( int i = 1; i < sjpeg->sjpeg_total_frames; i++ ) { + res = lv_fs_read(&lv_file, buff, 2, &rn); + if(res != LV_FS_RES_OK || rn != 2 ) { + lv_fs_close(&lv_file); + return LV_RES_INV; + } + + data = buff; + int offset = *data++; + offset |= *data++ <<8; + sjpeg->frame_base_offset[i] = sjpeg->frame_base_offset[i-1] + offset; + } + + sjpeg->sjpeg_cache_frame_index = -1; //INVALID AT BEGINNING for a forced compare mismatch at first time. + sjpeg->frame_cache = (void *)lv_mem_alloc( sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3 ); + if( ! sjpeg->frame_cache ) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + sjpeg->io.img_cache_buff = sjpeg->frame_cache; + sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res; + sjpeg->workb = lv_mem_alloc( TJPGD_WORKBUFF_SIZE ); + if( ! sjpeg->workb ) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + + sjpeg->tjpeg_jd = lv_mem_alloc( sizeof( JDEC ) ); + if( ! sjpeg->tjpeg_jd ) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + + sjpeg->io.type = SJPEG_IO_SOURCE_DISK; + sjpeg->io.lv_file = lv_file; + dsc->img_data = NULL; + return LV_RES_OK; + } + } + else if( !strcmp(&fn[strlen(fn) - 4], ".jpg" ) ) { + + lv_fs_file_t lv_file; + lv_fs_res_t res = lv_fs_open( &lv_file , fn, LV_FS_MODE_RD ); + if(res != LV_FS_RES_OK) { + return LV_RES_INV; + } + + SJPEG* sjpeg = ( SJPEG* ) dsc->user_data; + if(sjpeg == NULL) { + sjpeg = lv_mem_alloc( sizeof( SJPEG ) ); + if( ! sjpeg ) { + lv_fs_close( &lv_file ); + return LV_RES_INV; + } + + memset(sjpeg, 0, sizeof(SJPEG)); + dsc->user_data = sjpeg; + sjpeg->sjpeg_data = (uint8_t *)( (lv_img_dsc_t* )(dsc->src) )->data; + sjpeg->sjpeg_data_size = ( (lv_img_dsc_t *)(dsc->src) )->data_size; + } + + uint8_t *workb_temp = lv_mem_alloc( TJPGD_WORKBUFF_SIZE ); + if( ! workb_temp ) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + + io_source_t io_source_temp; + io_source_temp.type = SJPEG_IO_SOURCE_DISK; + io_source_temp.raw_sjpg_data_next_read_pos = 0; + io_source_temp.img_cache_buff = NULL; + io_source_temp.lv_file = lv_file; + + JDEC jd_tmp; + + JRESULT rc = jd_prepare( &jd_tmp, input_func, workb_temp, (size_t)TJPGD_WORKBUFF_SIZE, &io_source_temp); + + lv_mem_free(workb_temp); + + + if(rc == JDR_OK ) { + sjpeg->sjpeg_x_res = jd_tmp.width; + sjpeg->sjpeg_y_res = jd_tmp.height; + sjpeg->sjpeg_total_frames = 1; + sjpeg->sjpeg_single_frame_height = jd_tmp.height; + + sjpeg->frame_base_array = NULL; + sjpeg->frame_base_offset = lv_mem_alloc( sizeof(uint8_t *) * sjpeg->sjpeg_total_frames ); + if( ! sjpeg->frame_base_offset ) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + + int img_frame_start_offset = 0; + sjpeg->frame_base_offset[0] = img_frame_start_offset; + + sjpeg->sjpeg_cache_frame_index = -1; + sjpeg->frame_cache = (void *)lv_mem_alloc( sjpeg->sjpeg_x_res * sjpeg->sjpeg_single_frame_height * 3 ); + if( ! sjpeg->frame_cache ) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + + sjpeg->io.img_cache_buff = sjpeg->frame_cache; + sjpeg->io.img_cache_x_res = sjpeg->sjpeg_x_res; + sjpeg->workb = lv_mem_alloc( TJPGD_WORKBUFF_SIZE ); + if( ! sjpeg->workb ) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + + sjpeg->tjpeg_jd = lv_mem_alloc( sizeof( JDEC ) ); + if( ! sjpeg->tjpeg_jd ) { + lv_fs_close(&lv_file); + lv_sjpg_cleanup(sjpeg); + return LV_RES_INV; + } + + sjpeg->io.type = SJPEG_IO_SOURCE_DISK; + sjpeg->io.lv_file = lv_file; + dsc->img_data = NULL; + return LV_RES_OK; + + } else { + if(dsc->user_data) lv_mem_free(dsc->user_data); + lv_fs_close(&lv_file); + return LV_RES_INV; + } + } + } + + return LV_RES_INV; +} + +/** + * Decode `len` pixels starting from the given `x`, `y` coordinates and store them in `buf`. + * Required only if the "open" function can't open the whole decoded pixel array. (dsc->img_data == NULL) + * @param decoder pointer to the decoder the function associated with + * @param dsc pointer to decoder descriptor + * @param x start x coordinate + * @param y start y coordinate + * @param len number of pixels to decode + * @param buf a buffer to store the decoded pixels + * @return LV_RES_OK: ok; LV_RES_INV: failed + */ + +static lv_res_t decoder_read_line( lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc, lv_coord_t x, lv_coord_t y, lv_coord_t len, uint8_t * buf ) +{ + LV_UNUSED(decoder); + if(dsc->src_type == LV_IMG_SRC_VARIABLE) { + SJPEG* sjpeg = ( SJPEG* ) dsc->user_data; + JRESULT rc; + + int sjpeg_req_frame_index = y / sjpeg->sjpeg_single_frame_height; + + /*If line not from cache, refresh cache */ + if(sjpeg_req_frame_index != sjpeg->sjpeg_cache_frame_index) { + sjpeg->io.raw_sjpg_data = sjpeg->frame_base_array[ sjpeg_req_frame_index ]; + if (sjpeg_req_frame_index == (sjpeg->sjpeg_total_frames - 1)) { + /*This is the last frame. */ + const uint32_t frame_offset = (uint32_t)(sjpeg->io.raw_sjpg_data - sjpeg->sjpeg_data); + sjpeg->io.raw_sjpg_data_size = sjpeg->sjpeg_data_size - frame_offset; + } else { + sjpeg->io.raw_sjpg_data_size = + (uint32_t)(sjpeg->frame_base_array[sjpeg_req_frame_index + 1] - sjpeg->io.raw_sjpg_data); + } + sjpeg->io.raw_sjpg_data_next_read_pos = 0; + rc = jd_prepare( sjpeg->tjpeg_jd, input_func, sjpeg->workb, (size_t)TJPGD_WORKBUFF_SIZE, &(sjpeg->io)); + if(rc != JDR_OK ) return LV_RES_INV; + rc = jd_decomp ( sjpeg->tjpeg_jd, img_data_cb, 0); + if(rc != JDR_OK ) return LV_RES_INV; + sjpeg->sjpeg_cache_frame_index = sjpeg_req_frame_index; + } + + int offset = 0; + uint8_t *cache = (uint8_t *)sjpeg->frame_cache + x*3 + ( y % sjpeg->sjpeg_single_frame_height ) * sjpeg->sjpeg_x_res*3; + +#if LV_COLOR_DEPTH == 32 + for( int i = 0; i < len; i++ ) { + buf[offset + 3] = 0xff; + buf[offset + 2] = *cache++; + buf[offset + 1] = *cache++; + buf[offset + 0] = *cache++; + offset += 4; + } + +#elif LV_COLOR_DEPTH == 16 + + for( int i = 0; i < len; i++ ) { + uint16_t col_16bit = (*cache++ & 0xf8) << 8; + col_16bit |= (*cache++ & 0xFC) << 3; + col_16bit |= (*cache++ >> 3); +#if LV_BIG_ENDIAN_SYSTEM == 1 + buf[offset++] = col_16bit >> 8; + buf[offset++] = col_16bit & 0xff; +#else + buf[offset++] = col_16bit & 0xff; + buf[offset++] = col_16bit >> 8; +#endif // LV_BIG_ENDIAN_SYSTEM + } + +#elif LV_COLOR_DEPTH == 8 + + for( int i = 0; i < len; i++ ) { + uint8_t col_8bit = (*cache++ & 0xC0); + col_8bit |= (*cache++ & 0xe0) >> 2; + col_8bit |= (*cache++ & 0xe0) >> 5; + buf[offset++] = col_8bit; + } +#else +#error Unsupported LV_COLOR_DEPTH + + +#endif // LV_COLOR_DEPTH + return LV_RES_OK; + } + else if(dsc->src_type == LV_IMG_SRC_FILE) { + SJPEG* sjpeg = ( SJPEG* ) dsc->user_data; + JRESULT rc; + int sjpeg_req_frame_index = y / sjpeg->sjpeg_single_frame_height; + + lv_fs_file_t* lv_file_p = &(sjpeg->io.lv_file); + if(!lv_file_p) goto end; + + /*If line not from cache, refresh cache */ + if(sjpeg_req_frame_index != sjpeg->sjpeg_cache_frame_index) { + sjpeg->io.raw_sjpg_data_next_read_pos = (int)(sjpeg->frame_base_offset [ sjpeg_req_frame_index ]); + lv_fs_seek( &(sjpeg->io.lv_file), sjpeg->io.raw_sjpg_data_next_read_pos, LV_FS_SEEK_SET); + + rc = jd_prepare( sjpeg->tjpeg_jd, input_func, sjpeg->workb, (size_t)TJPGD_WORKBUFF_SIZE, &(sjpeg->io)); + if(rc != JDR_OK ) return LV_RES_INV; + + rc = jd_decomp ( sjpeg->tjpeg_jd, img_data_cb, 0); + if(rc != JDR_OK ) return LV_RES_INV; + + sjpeg->sjpeg_cache_frame_index = sjpeg_req_frame_index; + } + + int offset = 0; + uint8_t *cache = (uint8_t *)sjpeg->frame_cache + x*3 + ( y % sjpeg->sjpeg_single_frame_height ) * sjpeg->sjpeg_x_res*3; + +#if LV_COLOR_DEPTH == 32 + for( int i = 0; i < len; i++ ) { + buf[offset + 3] = 0xff; + buf[offset + 2] = *cache++; + buf[offset + 1] = *cache++; + buf[offset + 0] = *cache++; + offset += 4; + } +#elif LV_COLOR_DEPTH == 16 + + for( int i = 0; i < len; i++ ) { + uint16_t col_8bit = (*cache++ & 0xf8) << 8; + col_8bit |= (*cache++ & 0xFC) << 3; + col_8bit |= (*cache++ >> 3); +#if LV_BIG_ENDIAN_SYSTEM == 1 + buf[offset++] = col_8bit >> 8; + buf[offset++] = col_8bit & 0xff; +#else + buf[offset++] = col_8bit & 0xff; + buf[offset++] = col_8bit >> 8; +#endif // LV_BIG_ENDIAN_SYSTEM + } + +#elif LV_COLOR_DEPTH == 8 + + for( int i = 0; i < len; i++ ) { + uint8_t col_8bit = (*cache++ & 0xC0); + col_8bit |= (*cache++ & 0xe0) >> 2; + col_8bit |= (*cache++ & 0xe0) >> 5; + buf[offset++] = col_8bit; + } + +#else +#error Unsupported LV_COLOR_DEPTH + + +#endif // LV_COLOR_DEPTH + + return LV_RES_OK; + } + end: + return LV_RES_INV; +} + +/** + * Free the allocated resources + * @param decoder pointer to the decoder where this function belongs + * @param dsc pointer to a descriptor which describes this decoding session + */ +static void decoder_close( lv_img_decoder_t * decoder, lv_img_decoder_dsc_t * dsc ) +{ + LV_UNUSED(decoder); + /*Free all allocated data*/ + SJPEG* sjpeg = ( SJPEG* ) dsc->user_data; + if(!sjpeg) return; + + switch(dsc->src_type) { + case LV_IMG_SRC_FILE: + if(sjpeg->io.lv_file.file_d) { + lv_fs_close(&(sjpeg->io.lv_file)); + } + lv_sjpg_free(sjpeg); + break; + + case LV_IMG_SRC_VARIABLE: + lv_sjpg_free(sjpeg); + break; + + default: + ; + } +} + +static int is_jpg( const uint8_t *raw_data ) +{ + const uint8_t jpg_signature[] = {0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46};//ÿØÿà�JFIF + return memcmp( jpg_signature, raw_data, sizeof( jpg_signature ) ) == 0; +} + +static void lv_sjpg_free( SJPEG* sjpeg ) +{ + if(sjpeg->frame_cache) lv_mem_free(sjpeg->frame_cache); + if(sjpeg->frame_base_array) lv_mem_free(sjpeg->frame_base_array); + if(sjpeg->frame_base_offset) lv_mem_free(sjpeg->frame_base_offset); + if(sjpeg->tjpeg_jd) lv_mem_free(sjpeg->tjpeg_jd); + if(sjpeg->workb) lv_mem_free(sjpeg->workb); +} + +static void lv_sjpg_cleanup( SJPEG* sjpeg ) +{ + if(! sjpeg ) return; + + lv_sjpg_free( sjpeg ); + lv_mem_free( sjpeg ); +} + +#endif /*LV_USE_SJPG*/ diff --git a/src/extra/libs/sjpg/lv_sjpg.h b/src/extra/libs/sjpg/lv_sjpg.h new file mode 100644 index 0000000000..34bd51d7d9 --- /dev/null +++ b/src/extra/libs/sjpg/lv_sjpg.h @@ -0,0 +1,81 @@ +/** + * @file lv_sjpg.h + * + */ + +#ifndef LV_SJPEG_H +#define LV_SJPEG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#include +#include "tjpgd.h" + +#include "../../../misc/lv_fs.h" +#if LV_USE_SJPG + +/********************* + * DEFINES + *********************/ + + +/********************** + * TYPEDEFS + **********************/ +enum io_source_type { + SJPEG_IO_SOURCE_C_ARRAY, + SJPEG_IO_SOURCE_DISK, +}; + +typedef struct { + enum io_source_type type; + lv_fs_file_t lv_file; + uint8_t* img_cache_buff; + int img_cache_x_res; + int img_cache_y_res; + uint8_t *raw_sjpg_data; //Used when type==SJPEG_IO_SOURCE_C_ARRAY. + uint32_t raw_sjpg_data_size; //Num bytes pointed to by raw_sjpg_data. + uint32_t raw_sjpg_data_next_read_pos; //Used for all types. +} io_source_t; + + +typedef struct { + uint8_t *sjpeg_data; + uint32_t sjpeg_data_size; + int sjpeg_x_res; + int sjpeg_y_res; + int sjpeg_total_frames; + int sjpeg_single_frame_height; + int sjpeg_cache_frame_index; + uint8_t **frame_base_array; //to save base address of each split frames upto sjpeg_total_frames. + int *frame_base_offset; //to save base offset for fseek + uint8_t *frame_cache; + uint8_t* workb; //JPG work buffer for jpeg library + JDEC *tjpeg_jd; + io_source_t io; +} SJPEG; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +void lv_split_jpeg_init(void); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_SJPG*/ + +#ifdef __cplusplus +} +#endif + +#endif /* LV_SJPEG_H */ diff --git a/src/extra/libs/sjpg/tjpgd.c b/src/extra/libs/sjpg/tjpgd.c new file mode 100644 index 0000000000..53158e75b3 --- /dev/null +++ b/src/extra/libs/sjpg/tjpgd.c @@ -0,0 +1,1155 @@ +/*----------------------------------------------------------------------------/ +/ TJpgDec - Tiny JPEG Decompressor R0.03 (C)ChaN, 2021 +/-----------------------------------------------------------------------------/ +/ The TJpgDec is a generic JPEG decompressor module for tiny embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following terms. +/ +/ Copyright (C) 2021, ChaN, all right reserved. +/ +/ * The TJpgDec module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/ +/-----------------------------------------------------------------------------/ +/ Oct 04, 2011 R0.01 First release. +/ Feb 19, 2012 R0.01a Fixed decompression fails when scan starts with an escape seq. +/ Sep 03, 2012 R0.01b Added JD_TBLCLIP option. +/ Mar 16, 2019 R0.01c Supprted stdint.h. +/ Jul 01, 2020 R0.01d Fixed wrong integer type usage. +/ May 08, 2021 R0.02 Supprted grayscale image. Separated configuration options. +/ Jun 11, 2021 R0.02a Some performance improvement. +/ Jul 01, 2021 R0.03 Added JD_FASTDECODE option. +/ Some performance improvement. +/----------------------------------------------------------------------------*/ + +#include "tjpgd.h" +#if LV_USE_SJPG + +#if JD_FASTDECODE == 2 +#define HUFF_BIT 10 /* Bit length to apply fast huffman decode */ +#define HUFF_LEN (1 << HUFF_BIT) +#define HUFF_MASK (HUFF_LEN - 1) +#endif + + +/*-----------------------------------------------*/ +/* Zigzag-order to raster-order conversion table */ +/*-----------------------------------------------*/ + +static const uint8_t Zig[64] = { /* Zigzag-order to raster-order conversion table */ + 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 +}; + + + +/*-------------------------------------------------*/ +/* Input scale factor of Arai algorithm */ +/* (scaled up 16 bits for fixed point operations) */ +/*-------------------------------------------------*/ + +static const uint16_t Ipsf[64] = { /* See also aa_idct.png */ + (uint16_t)(1.00000*8192), (uint16_t)(1.38704*8192), (uint16_t)(1.30656*8192), (uint16_t)(1.17588*8192), (uint16_t)(1.00000*8192), (uint16_t)(0.78570*8192), (uint16_t)(0.54120*8192), (uint16_t)(0.27590*8192), + (uint16_t)(1.38704*8192), (uint16_t)(1.92388*8192), (uint16_t)(1.81226*8192), (uint16_t)(1.63099*8192), (uint16_t)(1.38704*8192), (uint16_t)(1.08979*8192), (uint16_t)(0.75066*8192), (uint16_t)(0.38268*8192), + (uint16_t)(1.30656*8192), (uint16_t)(1.81226*8192), (uint16_t)(1.70711*8192), (uint16_t)(1.53636*8192), (uint16_t)(1.30656*8192), (uint16_t)(1.02656*8192), (uint16_t)(0.70711*8192), (uint16_t)(0.36048*8192), + (uint16_t)(1.17588*8192), (uint16_t)(1.63099*8192), (uint16_t)(1.53636*8192), (uint16_t)(1.38268*8192), (uint16_t)(1.17588*8192), (uint16_t)(0.92388*8192), (uint16_t)(0.63638*8192), (uint16_t)(0.32442*8192), + (uint16_t)(1.00000*8192), (uint16_t)(1.38704*8192), (uint16_t)(1.30656*8192), (uint16_t)(1.17588*8192), (uint16_t)(1.00000*8192), (uint16_t)(0.78570*8192), (uint16_t)(0.54120*8192), (uint16_t)(0.27590*8192), + (uint16_t)(0.78570*8192), (uint16_t)(1.08979*8192), (uint16_t)(1.02656*8192), (uint16_t)(0.92388*8192), (uint16_t)(0.78570*8192), (uint16_t)(0.61732*8192), (uint16_t)(0.42522*8192), (uint16_t)(0.21677*8192), + (uint16_t)(0.54120*8192), (uint16_t)(0.75066*8192), (uint16_t)(0.70711*8192), (uint16_t)(0.63638*8192), (uint16_t)(0.54120*8192), (uint16_t)(0.42522*8192), (uint16_t)(0.29290*8192), (uint16_t)(0.14932*8192), + (uint16_t)(0.27590*8192), (uint16_t)(0.38268*8192), (uint16_t)(0.36048*8192), (uint16_t)(0.32442*8192), (uint16_t)(0.27590*8192), (uint16_t)(0.21678*8192), (uint16_t)(0.14932*8192), (uint16_t)(0.07612*8192) +}; + + + +/*---------------------------------------------*/ +/* Conversion table for fast clipping process */ +/*---------------------------------------------*/ + +#if JD_TBLCLIP + +#define BYTECLIP(v) Clip8[(unsigned int)(v) & 0x3FF] + +static const uint8_t Clip8[1024] = { + /* 0..255 */ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, + /* 256..511 */ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + /* -512..-257 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* -256..-1 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +#else /* JD_TBLCLIP */ + +static uint8_t BYTECLIP (int val) +{ + if (val < 0) return 0; + if (val > 255) return 255; + return (uint8_t)val; +} + +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Allocate a memory block from memory pool */ +/*-----------------------------------------------------------------------*/ + +static void* alloc_pool ( /* Pointer to allocated memory block (NULL:no memory available) */ + JDEC* jd, /* Pointer to the decompressor object */ + size_t ndata /* Number of bytes to allocate */ +) +{ + char *rp = 0; + + + ndata = (ndata + 3) & ~3; /* Align block size to the word boundary */ + + if (jd->sz_pool >= ndata) { + jd->sz_pool -= ndata; + rp = (char*)jd->pool; /* Get start of available memory pool */ + jd->pool = (void*)(rp + ndata); /* Allocate requierd bytes */ + } + + return (void*)rp; /* Return allocated memory block (NULL:no memory to allocate) */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create de-quantization and prescaling tables with a DQT segment */ +/*-----------------------------------------------------------------------*/ + +static JRESULT create_qt_tbl ( /* 0:OK, !0:Failed */ + JDEC* jd, /* Pointer to the decompressor object */ + const uint8_t* data, /* Pointer to the quantizer tables */ + size_t ndata /* Size of input data */ +) +{ + unsigned int i, zi; + uint8_t d; + int32_t *pb; + + + while (ndata) { /* Process all tables in the segment */ + if (ndata < 65) return JDR_FMT1; /* Err: table size is unaligned */ + ndata -= 65; + d = *data++; /* Get table property */ + if (d & 0xF0) return JDR_FMT1; /* Err: not 8-bit resolution */ + i = d & 3; /* Get table ID */ + pb = alloc_pool(jd, 64 * sizeof (int32_t));/* Allocate a memory block for the table */ + if (!pb) return JDR_MEM1; /* Err: not enough memory */ + jd->qttbl[i] = pb; /* Register the table */ + for (i = 0; i < 64; i++) { /* Load the table */ + zi = Zig[i]; /* Zigzag-order to raster-order conversion */ + pb[zi] = (int32_t)((uint32_t)*data++ * Ipsf[zi]); /* Apply scale factor of Arai algorithm to the de-quantizers */ + } + } + + return JDR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create huffman code tables with a DHT segment */ +/*-----------------------------------------------------------------------*/ + +static JRESULT create_huffman_tbl ( /* 0:OK, !0:Failed */ + JDEC* jd, /* Pointer to the decompressor object */ + const uint8_t* data, /* Pointer to the packed huffman tables */ + size_t ndata /* Size of input data */ +) +{ + unsigned int i, j, b, cls, num; + size_t np; + uint8_t d, *pb, *pd; + uint16_t hc, *ph; + + + while (ndata) { /* Process all tables in the segment */ + if (ndata < 17) return JDR_FMT1; /* Err: wrong data size */ + ndata -= 17; + d = *data++; /* Get table number and class */ + if (d & 0xEE) return JDR_FMT1; /* Err: invalid class/number */ + cls = d >> 4; num = d & 0x0F; /* class = dc(0)/ac(1), table number = 0/1 */ + pb = alloc_pool(jd, 16); /* Allocate a memory block for the bit distribution table */ + if (!pb) return JDR_MEM1; /* Err: not enough memory */ + jd->huffbits[num][cls] = pb; + for (np = i = 0; i < 16; i++) { /* Load number of patterns for 1 to 16-bit code */ + np += (pb[i] = *data++); /* Get sum of code words for each code */ + } + ph = alloc_pool(jd, np * sizeof (uint16_t));/* Allocate a memory block for the code word table */ + if (!ph) return JDR_MEM1; /* Err: not enough memory */ + jd->huffcode[num][cls] = ph; + hc = 0; + for (j = i = 0; i < 16; i++) { /* Re-build huffman code word table */ + b = pb[i]; + while (b--) ph[j++] = hc++; + hc <<= 1; + } + + if (ndata < np) return JDR_FMT1; /* Err: wrong data size */ + ndata -= np; + pd = alloc_pool(jd, np); /* Allocate a memory block for the decoded data */ + if (!pd) return JDR_MEM1; /* Err: not enough memory */ + jd->huffdata[num][cls] = pd; + for (i = 0; i < np; i++) { /* Load decoded data corresponds to each code word */ + d = *data++; + if (!cls && d > 11) return JDR_FMT1; + pd[i] = d; + } +#if JD_FASTDECODE == 2 + { /* Create fast huffman decode table */ + unsigned int span, td, ti; + uint16_t *tbl_ac = 0; + uint8_t *tbl_dc = 0; + + if (cls) { + tbl_ac = alloc_pool(jd, HUFF_LEN * sizeof (uint16_t)); /* LUT for AC elements */ + if (!tbl_ac) return JDR_MEM1; /* Err: not enough memory */ + jd->hufflut_ac[num] = tbl_ac; + memset(tbl_ac, 0xFF, HUFF_LEN * sizeof (uint16_t)); /* Default value (0xFFFF: may be long code) */ + } else { + tbl_dc = alloc_pool(jd, HUFF_LEN * sizeof (uint8_t)); /* LUT for AC elements */ + if (!tbl_dc) return JDR_MEM1; /* Err: not enough memory */ + jd->hufflut_dc[num] = tbl_dc; + memset(tbl_dc, 0xFF, HUFF_LEN * sizeof (uint8_t)); /* Default value (0xFF: may be long code) */ + } + for (i = b = 0; b < HUFF_BIT; b++) { /* Create LUT */ + for (j = pb[b]; j; j--) { + ti = ph[i] << (HUFF_BIT - 1 - b) & HUFF_MASK; /* Index of input pattern for the code */ + if (cls) { + td = pd[i++] | ((b + 1) << 8); /* b15..b8: code length, b7..b0: zero run and data length */ + for (span = 1 << (HUFF_BIT - 1 - b); span; span--, tbl_ac[ti++] = (uint16_t)td) ; + } else { + td = pd[i++] | ((b + 1) << 4); /* b7..b4: code length, b3..b0: data length */ + for (span = 1 << (HUFF_BIT - 1 - b); span; span--, tbl_dc[ti++] = (uint8_t)td) ; + } + } + } + jd->longofs[num][cls] = i; /* Code table offset for long code */ + } +#endif + } + + return JDR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Extract a huffman decoded data from input stream */ +/*-----------------------------------------------------------------------*/ + +static int huffext ( /* >=0: decoded data, <0: error code */ + JDEC* jd, /* Pointer to the decompressor object */ + unsigned int id, /* Table ID (0:Y, 1:C) */ + unsigned int cls /* Table class (0:DC, 1:AC) */ +) +{ + size_t dc = jd->dctr; + uint8_t *dp = jd->dptr; + unsigned int d, flg = 0; + +#if JD_FASTDECODE == 0 + uint8_t bm, nd, bl; + const uint8_t *hb = jd->huffbits[id][cls]; /* Bit distribution table */ + const uint16_t *hc = jd->huffcode[id][cls]; /* Code word table */ + const uint8_t *hd = jd->huffdata[id][cls]; /* Data table */ + + + bm = jd->dbit; /* Bit mask to extract */ + d = 0; bl = 16; /* Max code length */ + do { + if (!bm) { /* Next byte? */ + if (!dc) { /* No input data is available, re-fill input buffer */ + dp = jd->inbuf; /* Top of input buffer */ + dc = jd->infunc(jd, dp, JD_SZBUF); + if (!dc) return 0 - (int)JDR_INP; /* Err: read error or wrong stream termination */ + } else { + dp++; /* Next data ptr */ + } + dc--; /* Decrement number of available bytes */ + if (flg) { /* In flag sequence? */ + flg = 0; /* Exit flag sequence */ + if (*dp != 0) return 0 - (int)JDR_FMT1; /* Err: unexpected flag is detected (may be collapted data) */ + *dp = 0xFF; /* The flag is a data 0xFF */ + } else { + if (*dp == 0xFF) { /* Is start of flag sequence? */ + flg = 1; continue; /* Enter flag sequence, get trailing byte */ + } + } + bm = 0x80; /* Read from MSB */ + } + d <<= 1; /* Get a bit */ + if (*dp & bm) d++; + bm >>= 1; + + for (nd = *hb++; nd; nd--) { /* Search the code word in this bit length */ + if (d == *hc++) { /* Matched? */ + jd->dbit = bm; jd->dctr = dc; jd->dptr = dp; + return *hd; /* Return the decoded data */ + } + hd++; + } + bl--; + } while (bl); + +#else + const uint8_t *hb, *hd; + const uint16_t *hc; + unsigned int nc, bl, wbit = jd->dbit % 32; + uint32_t w = jd->wreg & ((1UL << wbit) - 1); + + + while (wbit < 16) { /* Prepare 16 bits into the working register */ + if (jd->marker) { + d = 0xFF; /* Input stream has stalled for a marker. Generate stuff bits */ + } else { + if (!dc) { /* Buffer empty, re-fill input buffer */ + dp = jd->inbuf; /* Top of input buffer */ + dc = jd->infunc(jd, dp, JD_SZBUF); + if (!dc) return 0 - (int)JDR_INP; /* Err: read error or wrong stream termination */ + } + d = *dp++; dc--; + if (flg) { /* In flag sequence? */ + flg = 0; /* Exit flag sequence */ + if (d != 0) jd->marker = d; /* Not an escape of 0xFF but a marker */ + d = 0xFF; + } else { + if (d == 0xFF) { /* Is start of flag sequence? */ + flg = 1; continue; /* Enter flag sequence, get trailing byte */ + } + } + } + w = w << 8 | d; /* Shift 8 bits in the working register */ + wbit += 8; + } + jd->dctr = dc; jd->dptr = dp; + jd->wreg = w; + +#if JD_FASTDECODE == 2 + /* Table serch for the short codes */ + d = (unsigned int)(w >> (wbit - HUFF_BIT)); /* Short code as table index */ + if (cls) { /* AC element */ + d = jd->hufflut_ac[id][d]; /* Table decode */ + if (d != 0xFFFF) { /* It is done if hit in short code */ + jd->dbit = wbit - (d >> 8); /* Snip the code length */ + return d & 0xFF; /* b7..0: zero run and following data bits */ + } + } else { /* DC element */ + d = jd->hufflut_dc[id][d]; /* Table decode */ + if (d != 0xFF) { /* It is done if hit in short code */ + jd->dbit = wbit - (d >> 4); /* Snip the code length */ + return d & 0xF; /* b3..0: following data bits */ + } + } + + /* Incremental serch for the codes longer than HUFF_BIT */ + hb = jd->huffbits[id][cls] + HUFF_BIT; /* Bit distribution table */ + hc = jd->huffcode[id][cls] + jd->longofs[id][cls]; /* Code word table */ + hd = jd->huffdata[id][cls] + jd->longofs[id][cls]; /* Data table */ + bl = HUFF_BIT + 1; +#else + /* Incremental serch for all codes */ + hb = jd->huffbits[id][cls]; /* Bit distribution table */ + hc = jd->huffcode[id][cls]; /* Code word table */ + hd = jd->huffdata[id][cls]; /* Data table */ + bl = 1; +#endif + for ( ; bl <= 16; bl++) { /* Incremental search */ + nc = *hb++; + if (nc) { + d = w >> (wbit - bl); + do { /* Search the code word in this bit length */ + if (d == *hc++) { /* Matched? */ + jd->dbit = wbit - bl; /* Snip the huffman code */ + return *hd; /* Return the decoded data */ + } + hd++; + } while (--nc); + } + } +#endif + + return 0 - (int)JDR_FMT1; /* Err: code not found (may be collapted data) */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Extract N bits from input stream */ +/*-----------------------------------------------------------------------*/ + +static int bitext ( /* >=0: extracted data, <0: error code */ + JDEC* jd, /* Pointer to the decompressor object */ + unsigned int nbit /* Number of bits to extract (1 to 16) */ +) +{ + size_t dc = jd->dctr; + uint8_t *dp = jd->dptr; + unsigned int d, flg = 0; + +#if JD_FASTDECODE == 0 + uint8_t mbit = jd->dbit; + + d = 0; + do { + if (!mbit) { /* Next byte? */ + if (!dc) { /* No input data is available, re-fill input buffer */ + dp = jd->inbuf; /* Top of input buffer */ + dc = jd->infunc(jd, dp, JD_SZBUF); + if (!dc) return 0 - (int)JDR_INP; /* Err: read error or wrong stream termination */ + } else { + dp++; /* Next data ptr */ + } + dc--; /* Decrement number of available bytes */ + if (flg) { /* In flag sequence? */ + flg = 0; /* Exit flag sequence */ + if (*dp != 0) return 0 - (int)JDR_FMT1; /* Err: unexpected flag is detected (may be collapted data) */ + *dp = 0xFF; /* The flag is a data 0xFF */ + } else { + if (*dp == 0xFF) { /* Is start of flag sequence? */ + flg = 1; continue; /* Enter flag sequence */ + } + } + mbit = 0x80; /* Read from MSB */ + } + d <<= 1; /* Get a bit */ + if (*dp & mbit) d |= 1; + mbit >>= 1; + nbit--; + } while (nbit); + + jd->dbit = mbit; jd->dctr = dc; jd->dptr = dp; + return (int)d; + +#else + unsigned int wbit = jd->dbit % 32; + uint32_t w = jd->wreg & ((1UL << wbit) - 1); + + + while (wbit < nbit) { /* Prepare nbit bits into the working register */ + if (jd->marker) { + d = 0xFF; /* Input stream stalled, generate stuff bits */ + } else { + if (!dc) { /* Buffer empty, re-fill input buffer */ + dp = jd->inbuf; /* Top of input buffer */ + dc = jd->infunc(jd, dp, JD_SZBUF); + if (!dc) return 0 - (int)JDR_INP; /* Err: read error or wrong stream termination */ + } + d = *dp++; dc--; + if (flg) { /* In flag sequence? */ + flg = 0; /* Exit flag sequence */ + if (d != 0) jd->marker = d; /* Not an escape of 0xFF but a marker */ + d = 0xFF; + } else { + if (d == 0xFF) { /* Is start of flag sequence? */ + flg = 1; continue; /* Enter flag sequence, get trailing byte */ + } + } + } + w = w << 8 | d; /* Get 8 bits into the working register */ + wbit += 8; + } + jd->wreg = w; jd->dbit = wbit - nbit; + jd->dctr = dc; jd->dptr = dp; + + return (int)(w >> ((wbit - nbit) % 32)); +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Process restart interval */ +/*-----------------------------------------------------------------------*/ + +static JRESULT restart ( + JDEC* jd, /* Pointer to the decompressor object */ + uint16_t rstn /* Expected restert sequense number */ +) +{ + unsigned int i; + uint8_t *dp = jd->dptr; + size_t dc = jd->dctr; + +#if JD_FASTDECODE == 0 + uint16_t d = 0; + + /* Get two bytes from the input stream */ + for (i = 0; i < 2; i++) { + if (!dc) { /* No input data is available, re-fill input buffer */ + dp = jd->inbuf; + dc = jd->infunc(jd, dp, JD_SZBUF); + if (!dc) return JDR_INP; + } else { + dp++; + } + dc--; + d = d << 8 | *dp; /* Get a byte */ + } + jd->dptr = dp; jd->dctr = dc; jd->dbit = 0; + + /* Check the marker */ + if ((d & 0xFFD8) != 0xFFD0 || (d & 7) != (rstn & 7)) { + return JDR_FMT1; /* Err: expected RSTn marker is not detected (may be collapted data) */ + } + +#else + uint16_t marker; + + + if (jd->marker) { /* Generate a maker if it has been detected */ + marker = 0xFF00 | jd->marker; + jd->marker = 0; + } else { + marker = 0; + for (i = 0; i < 2; i++) { /* Get a restart marker */ + if (!dc) { /* No input data is available, re-fill input buffer */ + dp = jd->inbuf; + dc = jd->infunc(jd, dp, JD_SZBUF); + if (!dc) return JDR_INP; + } + marker = (marker << 8) | *dp++; /* Get a byte */ + dc--; + } + jd->dptr = dp; jd->dctr = dc; + } + + /* Check the marker */ + if ((marker & 0xFFD8) != 0xFFD0 || (marker & 7) != (rstn & 7)) { + return JDR_FMT1; /* Err: expected RSTn marker was not detected (may be collapted data) */ + } + + jd->dbit = 0; /* Discard stuff bits */ +#endif + + jd->dcv[2] = jd->dcv[1] = jd->dcv[0] = 0; /* Reset DC offset */ + return JDR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Apply Inverse-DCT in Arai Algorithm (see also aa_idct.png) */ +/*-----------------------------------------------------------------------*/ + +static void block_idct ( + int32_t* src, /* Input block data (de-quantized and pre-scaled for Arai Algorithm) */ + jd_yuv_t* dst /* Pointer to the destination to store the block as byte array */ +) +{ + const int32_t M13 = (int32_t)(1.41421*4096), M2 = (int32_t)(1.08239*4096), M4 = (int32_t)(2.61313*4096), M5 = (int32_t)(1.84776*4096); + int32_t v0, v1, v2, v3, v4, v5, v6, v7; + int32_t t10, t11, t12, t13; + int i; + + /* Process columns */ + for (i = 0; i < 8; i++) { + v0 = src[8 * 0]; /* Get even elements */ + v1 = src[8 * 2]; + v2 = src[8 * 4]; + v3 = src[8 * 6]; + + t10 = v0 + v2; /* Process the even elements */ + t12 = v0 - v2; + t11 = (v1 - v3) * M13 >> 12; + v3 += v1; + t11 -= v3; + v0 = t10 + v3; + v3 = t10 - v3; + v1 = t11 + t12; + v2 = t12 - t11; + + v4 = src[8 * 7]; /* Get odd elements */ + v5 = src[8 * 1]; + v6 = src[8 * 5]; + v7 = src[8 * 3]; + + t10 = v5 - v4; /* Process the odd elements */ + t11 = v5 + v4; + t12 = v6 - v7; + v7 += v6; + v5 = (t11 - v7) * M13 >> 12; + v7 += t11; + t13 = (t10 + t12) * M5 >> 12; + v4 = t13 - (t10 * M2 >> 12); + v6 = t13 - (t12 * M4 >> 12) - v7; + v5 -= v6; + v4 -= v5; + + src[8 * 0] = v0 + v7; /* Write-back transformed values */ + src[8 * 7] = v0 - v7; + src[8 * 1] = v1 + v6; + src[8 * 6] = v1 - v6; + src[8 * 2] = v2 + v5; + src[8 * 5] = v2 - v5; + src[8 * 3] = v3 + v4; + src[8 * 4] = v3 - v4; + + src++; /* Next column */ + } + + /* Process rows */ + src -= 8; + for (i = 0; i < 8; i++) { + v0 = src[0] + (128L << 8); /* Get even elements (remove DC offset (-128) here) */ + v1 = src[2]; + v2 = src[4]; + v3 = src[6]; + + t10 = v0 + v2; /* Process the even elements */ + t12 = v0 - v2; + t11 = (v1 - v3) * M13 >> 12; + v3 += v1; + t11 -= v3; + v0 = t10 + v3; + v3 = t10 - v3; + v1 = t11 + t12; + v2 = t12 - t11; + + v4 = src[7]; /* Get odd elements */ + v5 = src[1]; + v6 = src[5]; + v7 = src[3]; + + t10 = v5 - v4; /* Process the odd elements */ + t11 = v5 + v4; + t12 = v6 - v7; + v7 += v6; + v5 = (t11 - v7) * M13 >> 12; + v7 += t11; + t13 = (t10 + t12) * M5 >> 12; + v4 = t13 - (t10 * M2 >> 12); + v6 = t13 - (t12 * M4 >> 12) - v7; + v5 -= v6; + v4 -= v5; + + /* Descale the transformed values 8 bits and output a row */ +#if JD_FASTDECODE >= 1 + dst[0] = (int16_t)((v0 + v7) >> 8); + dst[7] = (int16_t)((v0 - v7) >> 8); + dst[1] = (int16_t)((v1 + v6) >> 8); + dst[6] = (int16_t)((v1 - v6) >> 8); + dst[2] = (int16_t)((v2 + v5) >> 8); + dst[5] = (int16_t)((v2 - v5) >> 8); + dst[3] = (int16_t)((v3 + v4) >> 8); + dst[4] = (int16_t)((v3 - v4) >> 8); +#else + dst[0] = BYTECLIP((v0 + v7) >> 8); + dst[7] = BYTECLIP((v0 - v7) >> 8); + dst[1] = BYTECLIP((v1 + v6) >> 8); + dst[6] = BYTECLIP((v1 - v6) >> 8); + dst[2] = BYTECLIP((v2 + v5) >> 8); + dst[5] = BYTECLIP((v2 - v5) >> 8); + dst[3] = BYTECLIP((v3 + v4) >> 8); + dst[4] = BYTECLIP((v3 - v4) >> 8); +#endif + + dst += 8; src += 8; /* Next row */ + } +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load all blocks in an MCU into working buffer */ +/*-----------------------------------------------------------------------*/ + +static JRESULT mcu_load ( + JDEC* jd /* Pointer to the decompressor object */ +) +{ + int32_t *tmp = (int32_t*)jd->workbuf; /* Block working buffer for de-quantize and IDCT */ + int d, e; + unsigned int blk, nby, i, bc, z, id, cmp; + jd_yuv_t *bp; + const int32_t *dqf; + + + nby = jd->msx * jd->msy; /* Number of Y blocks (1, 2 or 4) */ + bp = jd->mcubuf; /* Pointer to the first block of MCU */ + + for (blk = 0; blk < nby + 2; blk++) { /* Get nby Y blocks and two C blocks */ + cmp = (blk < nby) ? 0 : blk - nby + 1; /* Component number 0:Y, 1:Cb, 2:Cr */ + + if (cmp && jd->ncomp != 3) { /* Clear C blocks if not exist (monochrome image) */ + for (i = 0; i < 64; bp[i++] = 128) ; + + } else { /* Load Y/C blocks from input stream */ + id = cmp ? 1 : 0; /* Huffman table ID of this component */ + + /* Extract a DC element from input stream */ + d = huffext(jd, id, 0); /* Extract a huffman coded data (bit length) */ + if (d < 0) return (JRESULT)(0 - d); /* Err: invalid code or input */ + bc = (unsigned int)d; + d = jd->dcv[cmp]; /* DC value of previous block */ + if (bc) { /* If there is any difference from previous block */ + e = bitext(jd, bc); /* Extract data bits */ + if (e < 0) return (JRESULT)(0 - e); /* Err: input */ + bc = 1 << (bc - 1); /* MSB position */ + if (!(e & bc)) e -= (bc << 1) - 1; /* Restore negative value if needed */ + d += e; /* Get current value */ + jd->dcv[cmp] = (int16_t)d; /* Save current DC value for next block */ + } + dqf = jd->qttbl[jd->qtid[cmp]]; /* De-quantizer table ID for this component */ + tmp[0] = d * dqf[0] >> 8; /* De-quantize, apply scale factor of Arai algorithm and descale 8 bits */ + + /* Extract following 63 AC elements from input stream */ + memset(&tmp[1], 0, 63 * sizeof (int32_t)); /* Initialize all AC elements */ + z = 1; /* Top of the AC elements (in zigzag-order) */ + do { + d = huffext(jd, id, 1); /* Extract a huffman coded value (zero runs and bit length) */ + if (d == 0) break; /* EOB? */ + if (d < 0) return (JRESULT)(0 - d); /* Err: invalid code or input error */ + bc = (unsigned int)d; + z += bc >> 4; /* Skip leading zero run */ + if (z >= 64) return JDR_FMT1; /* Too long zero run */ + if (bc &= 0x0F) { /* Bit length? */ + d = bitext(jd, bc); /* Extract data bits */ + if (d < 0) return (JRESULT)(0 - d); /* Err: input device */ + bc = 1 << (bc - 1); /* MSB position */ + if (!(d & bc)) d -= (bc << 1) - 1; /* Restore negative value if needed */ + i = Zig[z]; /* Get raster-order index */ + tmp[i] = d * dqf[i] >> 8; /* De-quantize, apply scale factor of Arai algorithm and descale 8 bits */ + } + } while (++z < 64); /* Next AC element */ + + if (JD_FORMAT != 2 || !cmp) { /* C components may not be processed if in grayscale output */ + if (z == 1 || (JD_USE_SCALE && jd->scale == 3)) { /* If no AC element or scale ratio is 1/8, IDCT can be ommited and the block is filled with DC value */ + d = (jd_yuv_t)((*tmp / 256) + 128); + if (JD_FASTDECODE >= 1) { + for (i = 0; i < 64; bp[i++] = d) ; + } else { + memset(bp, d, 64); + } + } else { + block_idct(tmp, bp); /* Apply IDCT and store the block to the MCU buffer */ + } + } + } + + bp += 64; /* Next block */ + } + + return JDR_OK; /* All blocks have been loaded successfully */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Output an MCU: Convert YCrCb to RGB and output it in RGB form */ +/*-----------------------------------------------------------------------*/ + +static JRESULT mcu_output ( + JDEC* jd, /* Pointer to the decompressor object */ + int (*outfunc)(JDEC*, void*, JRECT*), /* RGB output function */ + unsigned int img_x, /* MCU location in the image */ + unsigned int img_y /* MCU location in the image */ +) +{ + const int CVACC = (sizeof (int) > 2) ? 1024 : 128; /* Adaptive accuracy for both 16-/32-bit systems */ + unsigned int ix, iy, mx, my, rx, ry; + int yy, cb, cr; + jd_yuv_t *py, *pc; + uint8_t *pix; + JRECT rect; + + + mx = jd->msx * 8; my = jd->msy * 8; /* MCU size (pixel) */ + rx = (img_x + mx <= jd->width) ? mx : jd->width - img_x; /* Output rectangular size (it may be clipped at right/bottom end of image) */ + ry = (img_y + my <= jd->height) ? my : jd->height - img_y; + if (JD_USE_SCALE) { + rx >>= jd->scale; ry >>= jd->scale; + if (!rx || !ry) return JDR_OK; /* Skip this MCU if all pixel is to be rounded off */ + img_x >>= jd->scale; img_y >>= jd->scale; + } + rect.left = img_x; rect.right = img_x + rx - 1; /* Rectangular area in the frame buffer */ + rect.top = img_y; rect.bottom = img_y + ry - 1; + + + if (!JD_USE_SCALE || jd->scale != 3) { /* Not for 1/8 scaling */ + pix = (uint8_t*)jd->workbuf; + + if (JD_FORMAT != 2) { /* RGB output (build an RGB MCU from Y/C component) */ + for (iy = 0; iy < my; iy++) { + pc = py = jd->mcubuf; + if (my == 16) { /* Double block height? */ + pc += 64 * 4 + (iy >> 1) * 8; + if (iy >= 8) py += 64; + } else { /* Single block height */ + pc += mx * 8 + iy * 8; + } + py += iy * 8; + for (ix = 0; ix < mx; ix++) { + cb = pc[0] - 128; /* Get Cb/Cr component and remove offset */ + cr = pc[64] - 128; + if (mx == 16) { /* Double block width? */ + if (ix == 8) py += 64 - 8; /* Jump to next block if double block heigt */ + pc += ix & 1; /* Step forward chroma pointer every two pixels */ + } else { /* Single block width */ + pc++; /* Step forward chroma pointer every pixel */ + } + yy = *py++; /* Get Y component */ + *pix++ = /*R*/ BYTECLIP(yy + ((int)(1.402 * CVACC) * cr) / CVACC); + *pix++ = /*G*/ BYTECLIP(yy - ((int)(0.344 * CVACC) * cb + (int)(0.714 * CVACC) * cr) / CVACC); + *pix++ = /*B*/ BYTECLIP(yy + ((int)(1.772 * CVACC) * cb) / CVACC); + } + } + } else { /* Monochrome output (build a grayscale MCU from Y comopnent) */ + for (iy = 0; iy < my; iy++) { + py = jd->mcubuf + iy * 8; + if (my == 16) { /* Double block height? */ + if (iy >= 8) py += 64; + } + for (ix = 0; ix < mx; ix++) { + if (mx == 16) { /* Double block width? */ + if (ix == 8) py += 64 - 8; /* Jump to next block if double block height */ + } + *pix++ = (uint8_t)*py++; /* Get and store a Y value as grayscale */ + } + } + } + + /* Descale the MCU rectangular if needed */ + if (JD_USE_SCALE && jd->scale) { + unsigned int x, y, r, g, b, s, w, a; + uint8_t *op; + + /* Get averaged RGB value of each square correcponds to a pixel */ + s = jd->scale * 2; /* Number of shifts for averaging */ + w = 1 << jd->scale; /* Width of square */ + a = (mx - w) * (JD_FORMAT != 2 ? 3 : 1); /* Bytes to skip for next line in the square */ + op = (uint8_t*)jd->workbuf; + for (iy = 0; iy < my; iy += w) { + for (ix = 0; ix < mx; ix += w) { + pix = (uint8_t*)jd->workbuf + (iy * mx + ix) * (JD_FORMAT != 2 ? 3 : 1); + r = g = b = 0; + for (y = 0; y < w; y++) { /* Accumulate RGB value in the square */ + for (x = 0; x < w; x++) { + r += *pix++; /* Accumulate R or Y (monochrome output) */ + if (JD_FORMAT != 2) { /* RGB output? */ + g += *pix++; /* Accumulate G */ + b += *pix++; /* Accumulate B */ + } + } + pix += a; + } /* Put the averaged pixel value */ + *op++ = (uint8_t)(r >> s); /* Put R or Y (monochrome output) */ + if (JD_FORMAT != 2) { /* RGB output? */ + *op++ = (uint8_t)(g >> s); /* Put G */ + *op++ = (uint8_t)(b >> s); /* Put B */ + } + } + } + } + + } else { /* For only 1/8 scaling (left-top pixel in each block are the DC value of the block) */ + + /* Build a 1/8 descaled RGB MCU from discrete comopnents */ + pix = (uint8_t*)jd->workbuf; + pc = jd->mcubuf + mx * my; + cb = pc[0] - 128; /* Get Cb/Cr component and restore right level */ + cr = pc[64] - 128; + for (iy = 0; iy < my; iy += 8) { + py = jd->mcubuf; + if (iy == 8) py += 64 * 2; + for (ix = 0; ix < mx; ix += 8) { + yy = *py; /* Get Y component */ + py += 64; + if (JD_FORMAT != 2) { + *pix++ = /*R*/ BYTECLIP(yy + ((int)(1.402 * CVACC) * cr / CVACC)); + *pix++ = /*G*/ BYTECLIP(yy - ((int)(0.344 * CVACC) * cb + (int)(0.714 * CVACC) * cr) / CVACC); + *pix++ = /*B*/ BYTECLIP(yy + ((int)(1.772 * CVACC) * cb / CVACC)); + } else { + *pix++ = yy; + } + } + } + } + + /* Squeeze up pixel table if a part of MCU is to be truncated */ + mx >>= jd->scale; + if (rx < mx) { /* Is the MCU spans rigit edge? */ + uint8_t *s, *d; + unsigned int x, y; + + s = d = (uint8_t*)jd->workbuf; + for (y = 0; y < ry; y++) { + for (x = 0; x < rx; x++) { /* Copy effective pixels */ + *d++ = *s++; + if (JD_FORMAT != 2) { + *d++ = *s++; + *d++ = *s++; + } + } + s += (mx - rx) * (JD_FORMAT != 2 ? 3 : 1); /* Skip truncated pixels */ + } + } + + /* Convert RGB888 to RGB565 if needed */ + if (JD_FORMAT == 1) { + uint8_t *s = (uint8_t*)jd->workbuf; + uint16_t w, *d = (uint16_t*)s; + unsigned int n = rx * ry; + + do { + w = (*s++ & 0xF8) << 8; /* RRRRR----------- */ + w |= (*s++ & 0xFC) << 3; /* -----GGGGGG----- */ + w |= *s++ >> 3; /* -----------BBBBB */ + *d++ = w; + } while (--n); + } + + /* Output the rectangular */ + return outfunc(jd, jd->workbuf, &rect) ? JDR_OK : JDR_INTR; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Analyze the JPEG image and Initialize decompressor object */ +/*-----------------------------------------------------------------------*/ + +#define LDB_WORD(ptr) (uint16_t)(((uint16_t)*((uint8_t*)(ptr))<<8)|(uint16_t)*(uint8_t*)((ptr)+1)) + + +JRESULT jd_prepare ( + JDEC* jd, /* Blank decompressor object */ + size_t (*infunc)(JDEC*, uint8_t*, size_t), /* JPEG strem input function */ + void* pool, /* Working buffer for the decompression session */ + size_t sz_pool, /* Size of working buffer */ + void* dev /* I/O device identifier for the session */ +) +{ + uint8_t *seg, b; + uint16_t marker; + unsigned int n, i, ofs; + size_t len; + JRESULT rc; + + + memset(jd, 0, sizeof (JDEC)); /* Clear decompression object (this might be a problem if machine's null pointer is not all bits zero) */ + jd->pool = pool; /* Work memroy */ + jd->sz_pool = sz_pool; /* Size of given work memory */ + jd->infunc = infunc; /* Stream input function */ + jd->device = dev; /* I/O device identifier */ + + jd->inbuf = seg = alloc_pool(jd, JD_SZBUF); /* Allocate stream input buffer */ + if (!seg) return JDR_MEM1; + + ofs = marker = 0; /* Find SOI marker */ + do { + if (jd->infunc(jd, seg, 1) != 1) return JDR_INP; /* Err: SOI was not detected */ + ofs++; + marker = marker << 8 | seg[0]; + } while (marker != 0xFFD8); + + for (;;) { /* Parse JPEG segments */ + /* Get a JPEG marker */ + if (jd->infunc(jd, seg, 4) != 4) return JDR_INP; + marker = LDB_WORD(seg); /* Marker */ + len = LDB_WORD(seg + 2); /* Length field */ + if (len <= 2 || (marker >> 8) != 0xFF) return JDR_FMT1; + len -= 2; /* Segent content size */ + ofs += 4 + len; /* Number of bytes loaded */ + + switch (marker & 0xFF) { + case 0xC0: /* SOF0 (baseline JPEG) */ + if (len > JD_SZBUF) return JDR_MEM2; + if (jd->infunc(jd, seg, len) != len) return JDR_INP; /* Load segment data */ + + jd->width = LDB_WORD(&seg[3]); /* Image width in unit of pixel */ + jd->height = LDB_WORD(&seg[1]); /* Image height in unit of pixel */ + jd->ncomp = seg[5]; /* Number of color components */ + if (jd->ncomp != 3 && jd->ncomp != 1) return JDR_FMT3; /* Err: Supports only Grayscale and Y/Cb/Cr */ + + /* Check each image component */ + for (i = 0; i < jd->ncomp; i++) { + b = seg[7 + 3 * i]; /* Get sampling factor */ + if (i == 0) { /* Y component */ + if (b != 0x11 && b != 0x22 && b != 0x21) { /* Check sampling factor */ + return JDR_FMT3; /* Err: Supports only 4:4:4, 4:2:0 or 4:2:2 */ + } + jd->msx = b >> 4; jd->msy = b & 15; /* Size of MCU [blocks] */ + } else { /* Cb/Cr component */ + if (b != 0x11) return JDR_FMT3; /* Err: Sampling factor of Cb/Cr must be 1 */ + } + jd->qtid[i] = seg[8 + 3 * i]; /* Get dequantizer table ID for this component */ + if (jd->qtid[i] > 3) return JDR_FMT3; /* Err: Invalid ID */ + } + break; + + case 0xDD: /* DRI - Define Restart Interval */ + if (len > JD_SZBUF) return JDR_MEM2; + if (jd->infunc(jd, seg, len) != len) return JDR_INP; /* Load segment data */ + + jd->nrst = LDB_WORD(seg); /* Get restart interval (MCUs) */ + break; + + case 0xC4: /* DHT - Define Huffman Tables */ + if (len > JD_SZBUF) return JDR_MEM2; + if (jd->infunc(jd, seg, len) != len) return JDR_INP; /* Load segment data */ + + rc = create_huffman_tbl(jd, seg, len); /* Create huffman tables */ + if (rc) return rc; + break; + + case 0xDB: /* DQT - Define Quaitizer Tables */ + if (len > JD_SZBUF) return JDR_MEM2; + if (jd->infunc(jd, seg, len) != len) return JDR_INP; /* Load segment data */ + + rc = create_qt_tbl(jd, seg, len); /* Create de-quantizer tables */ + if (rc) return rc; + break; + + case 0xDA: /* SOS - Start of Scan */ + if (len > JD_SZBUF) return JDR_MEM2; + if (jd->infunc(jd, seg, len) != len) return JDR_INP; /* Load segment data */ + + if (!jd->width || !jd->height) return JDR_FMT1; /* Err: Invalid image size */ + if (seg[0] != jd->ncomp) return JDR_FMT3; /* Err: Wrong color components */ + + /* Check if all tables corresponding to each components have been loaded */ + for (i = 0; i < jd->ncomp; i++) { + b = seg[2 + 2 * i]; /* Get huffman table ID */ + if (b != 0x00 && b != 0x11) return JDR_FMT3; /* Err: Different table number for DC/AC element */ + n = i ? 1 : 0; /* Component class */ + if (!jd->huffbits[n][0] || !jd->huffbits[n][1]) { /* Check huffman table for this component */ + return JDR_FMT1; /* Err: Nnot loaded */ + } + if (!jd->qttbl[jd->qtid[i]]) { /* Check dequantizer table for this component */ + return JDR_FMT1; /* Err: Not loaded */ + } + } + + /* Allocate working buffer for MCU and pixel output */ + n = jd->msy * jd->msx; /* Number of Y blocks in the MCU */ + if (!n) return JDR_FMT1; /* Err: SOF0 has not been loaded */ + len = n * 64 * 2 + 64; /* Allocate buffer for IDCT and RGB output */ + if (len < 256) len = 256; /* but at least 256 byte is required for IDCT */ + jd->workbuf = alloc_pool(jd, len); /* and it may occupy a part of following MCU working buffer for RGB output */ + if (!jd->workbuf) return JDR_MEM1; /* Err: not enough memory */ + jd->mcubuf = alloc_pool(jd, (n + 2) * 64 * sizeof (jd_yuv_t)); /* Allocate MCU working buffer */ + if (!jd->mcubuf) return JDR_MEM1; /* Err: not enough memory */ + + /* Align stream read offset to JD_SZBUF */ + if (ofs %= JD_SZBUF) { + jd->dctr = jd->infunc(jd, seg + ofs, (size_t)(JD_SZBUF - ofs)); + } + jd->dptr = seg + ofs - (JD_FASTDECODE ? 0 : 1); + + return JDR_OK; /* Initialization succeeded. Ready to decompress the JPEG image. */ + + case 0xC1: /* SOF1 */ + case 0xC2: /* SOF2 */ + case 0xC3: /* SOF3 */ + case 0xC5: /* SOF5 */ + case 0xC6: /* SOF6 */ + case 0xC7: /* SOF7 */ + case 0xC9: /* SOF9 */ + case 0xCA: /* SOF10 */ + case 0xCB: /* SOF11 */ + case 0xCD: /* SOF13 */ + case 0xCE: /* SOF14 */ + case 0xCF: /* SOF15 */ + case 0xD9: /* EOI */ + return JDR_FMT3; /* Unsuppoted JPEG standard (may be progressive JPEG) */ + + default: /* Unknown segment (comment, exif or etc..) */ + /* Skip segment data (null pointer specifies to remove data from the stream) */ + if (jd->infunc(jd, 0, len) != len) return JDR_INP; + } + } +} + + + + +/*-----------------------------------------------------------------------*/ +/* Start to decompress the JPEG picture */ +/*-----------------------------------------------------------------------*/ + +JRESULT jd_decomp ( + JDEC* jd, /* Initialized decompression object */ + int (*outfunc)(JDEC*, void*, JRECT*), /* RGB output function */ + uint8_t scale /* Output de-scaling factor (0 to 3) */ +) +{ + unsigned int x, y, mx, my; + uint16_t rst, rsc; + JRESULT rc; + + + if (scale > (JD_USE_SCALE ? 3 : 0)) return JDR_PAR; + jd->scale = scale; + + mx = jd->msx * 8; my = jd->msy * 8; /* Size of the MCU (pixel) */ + + jd->dcv[2] = jd->dcv[1] = jd->dcv[0] = 0; /* Initialize DC values */ + rst = rsc = 0; + + rc = JDR_OK; + for (y = 0; y < jd->height; y += my) { /* Vertical loop of MCUs */ + for (x = 0; x < jd->width; x += mx) { /* Horizontal loop of MCUs */ + if (jd->nrst && rst++ == jd->nrst) { /* Process restart interval if enabled */ + rc = restart(jd, rsc++); + if (rc != JDR_OK) return rc; + rst = 1; + } + rc = mcu_load(jd); /* Load an MCU (decompress huffman coded stream, dequantize and apply IDCT) */ + if (rc != JDR_OK) return rc; + rc = mcu_output(jd, outfunc, x, y); /* Output the MCU (YCbCr to RGB, scaling and output) */ + if (rc != JDR_OK) return rc; + } + } + + return rc; +} + +#endif /*LV_USE_SJPG*/ diff --git a/src/extra/libs/sjpg/tjpgd.h b/src/extra/libs/sjpg/tjpgd.h new file mode 100644 index 0000000000..8c7d4ee95a --- /dev/null +++ b/src/extra/libs/sjpg/tjpgd.h @@ -0,0 +1,102 @@ +/*----------------------------------------------------------------------------/ +/ TJpgDec - Tiny JPEG Decompressor R0.03 include file (C)ChaN, 2021 +/----------------------------------------------------------------------------*/ +#ifndef DEF_TJPGDEC +#define DEF_TJPGDEC + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../../../lv_conf_internal.h" +#if LV_USE_SJPG + +#include "tjpgdcnf.h" +#include + +#if defined(_WIN32) /* VC++ or some compiler without stdint.h */ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef short int16_t; +typedef unsigned long uint32_t; +typedef long int32_t; +#else /* Embedded platform */ +#include +#endif + +#if JD_FASTDECODE >= 1 +typedef int16_t jd_yuv_t; +#else +typedef uint8_t jd_yuv_t; +#endif + + +/* Error code */ +typedef enum { + JDR_OK = 0, /* 0: Succeeded */ + JDR_INTR, /* 1: Interrupted by output function */ + JDR_INP, /* 2: Device error or wrong termination of input stream */ + JDR_MEM1, /* 3: Insufficient memory pool for the image */ + JDR_MEM2, /* 4: Insufficient stream input buffer */ + JDR_PAR, /* 5: Parameter error */ + JDR_FMT1, /* 6: Data format error (may be broken data) */ + JDR_FMT2, /* 7: Right format but not supported */ + JDR_FMT3 /* 8: Not supported JPEG standard */ +} JRESULT; + +/* Rectangular region in the output image */ +typedef struct { + uint16_t left; /* Left end */ + uint16_t right; /* Right end */ + uint16_t top; /* Top end */ + uint16_t bottom; /* Bottom end */ +} JRECT; + +/* Decompressor object structure */ +typedef struct JDEC JDEC; +struct JDEC { + size_t dctr; /* Number of bytes available in the input buffer */ + uint8_t* dptr; /* Current data read ptr */ + uint8_t* inbuf; /* Bit stream input buffer */ + uint8_t dbit; /* Number of bits availavble in wreg or reading bit mask */ + uint8_t scale; /* Output scaling ratio */ + uint8_t msx, msy; /* MCU size in unit of block (width, height) */ + uint8_t qtid[3]; /* Quantization table ID of each component, Y, Cb, Cr */ + uint8_t ncomp; /* Number of color components 1:grayscale, 3:color */ + int16_t dcv[3]; /* Previous DC element of each component */ + uint16_t nrst; /* Restart inverval */ + uint16_t width, height; /* Size of the input image (pixel) */ + uint8_t* huffbits[2][2]; /* Huffman bit distribution tables [id][dcac] */ + uint16_t* huffcode[2][2]; /* Huffman code word tables [id][dcac] */ + uint8_t* huffdata[2][2]; /* Huffman decoded data tables [id][dcac] */ + int32_t* qttbl[4]; /* Dequantizer tables [id] */ +#if JD_FASTDECODE >= 1 + uint32_t wreg; /* Working shift register */ + uint8_t marker; /* Detected marker (0:None) */ +#if JD_FASTDECODE == 2 + uint8_t longofs[2][2]; /* Table offset of long code [id][dcac] */ + uint16_t* hufflut_ac[2]; /* Fast huffman decode tables for AC short code [id] */ + uint8_t* hufflut_dc[2]; /* Fast huffman decode tables for DC short code [id] */ +#endif +#endif + void* workbuf; /* Working buffer for IDCT and RGB output */ + jd_yuv_t* mcubuf; /* Working buffer for the MCU */ + void* pool; /* Pointer to available memory pool */ + size_t sz_pool; /* Size of momory pool (bytes available) */ + size_t (*infunc)(JDEC*, uint8_t*, size_t); /* Pointer to jpeg stream input function */ + void* device; /* Pointer to I/O device identifiler for the session */ +}; + + + +/* TJpgDec API functions */ +JRESULT jd_prepare (JDEC* jd, size_t (*infunc)(JDEC*,uint8_t*,size_t), void* pool, size_t sz_pool, void* dev); +JRESULT jd_decomp (JDEC* jd, int (*outfunc)(JDEC*,void*,JRECT*), uint8_t scale); + +#endif /*LV_USE_SJPG*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _TJPGDEC */ diff --git a/src/extra/libs/sjpg/tjpgdcnf.h b/src/extra/libs/sjpg/tjpgdcnf.h new file mode 100644 index 0000000000..6d425e6f14 --- /dev/null +++ b/src/extra/libs/sjpg/tjpgdcnf.h @@ -0,0 +1,33 @@ +/*----------------------------------------------*/ +/* TJpgDec System Configurations R0.03 */ +/*----------------------------------------------*/ + +#define JD_SZBUF 512 +/* Specifies size of stream input buffer */ + +#define JD_FORMAT 0 +/* Specifies output pixel format. +/ 0: RGB888 (24-bit/pix) +/ 1: RGB565 (16-bit/pix) +/ 2: Grayscale (8-bit/pix) +*/ + +#define JD_USE_SCALE 1 +/* Switches output descaling feature. +/ 0: Disable +/ 1: Enable +*/ + +#define JD_TBLCLIP 1 +/* Use table conversion for saturation arithmetic. A bit faster, but increases 1 KB of code size. +/ 0: Disable +/ 1: Enable +*/ + +#define JD_FASTDECODE 0 +/* Optimization level +/ 0: Basic optimization. Suitable for 8/16-bit MCUs. +/ 1: + 32-bit barrel shifter. Suitable for 32-bit MCUs. +/ 2: + Table conversion for huffman decoding (wants 6 << HUFF_BIT bytes of RAM) +*/ + diff --git a/src/extra/lv_extra.c b/src/extra/lv_extra.c index 1de4df29f2..703b719858 100644 --- a/src/extra/lv_extra.c +++ b/src/extra/lv_extra.c @@ -41,6 +41,38 @@ void lv_extra_init(void) #if LV_USE_GRID lv_grid_init(); #endif + +#if LV_USE_FS_FATFS != '\0' + lv_fs_fatfs_init(); +#endif + +#if LV_USE_FS_STDIO != '\0' + lv_fs_stdio_init(); +#endif + +#if LV_USE_FS_POSIX != '\0' + lv_fs_posix_init(); +#endif + +#if LV_USE_PNG + lv_png_init(); +#endif + +#if LV_USE_SJPG + lv_split_jpeg_init(); +#endif + +#if LV_USE_BMP + lv_bmp_init(); +#endif + +#if LV_USE_FREETYPE + /*Init freetype library + *Cache max 64 faces and 1 size*/ + lv_freetype_init(0, 0, LV_FREETYPE_CACHE_SIZE); + +#endif + } /********************** diff --git a/src/lv_conf_internal.h b/src/lv_conf_internal.h index 7abc9c8eac..7620831da5 100644 --- a/src/lv_conf_internal.h +++ b/src/lv_conf_internal.h @@ -1563,6 +1563,104 @@ e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ # endif #endif +/*--------------------- + * 3rd party libraries + *--------------------*/ + +/*File system interfaces for common APIs + *To enable set a driver letter for that API*/ +#ifndef LV_USE_FS_STDIO +# ifdef CONFIG_LV_USE_FS_STDIO +# define LV_USE_FS_STDIO CONFIG_LV_USE_FS_STDIO +# else +# define LV_USE_FS_STDIO '\0' /*Uses fopen, fread, etc*/ +# endif +#endif +//#define LV_FS_STDIO_PATH "/home/john/" /*Set the working directory. If commented it will be "./" */ + +#ifndef LV_USE_FS_POSIX +# ifdef CONFIG_LV_USE_FS_POSIX +# define LV_USE_FS_POSIX CONFIG_LV_USE_FS_POSIX +# else +# define LV_USE_FS_POSIX '\0' /*Uses open, read, etc*/ +# endif +#endif +//#define LV_FS_POSIX_PATH "/home/john/" /*Set the working directory. If commented it will be "./" */ + +#ifndef LV_USE_FS_FATFS +# ifdef CONFIG_LV_USE_FS_FATFS +# define LV_USE_FS_FATFS CONFIG_LV_USE_FS_FATFS +# else +# define LV_USE_FS_FATFS '\0' /*Uses f_open, f_read, etc*/ +# endif +#endif + +/*PNG decoder library*/ +#ifndef LV_USE_PNG +# ifdef CONFIG_LV_USE_PNG +# define LV_USE_PNG CONFIG_LV_USE_PNG +# else +# define LV_USE_PNG 0 +# endif +#endif + +/*BMP decoder library*/ +#ifndef LV_USE_BMP +# ifdef CONFIG_LV_USE_BMP +# define LV_USE_BMP CONFIG_LV_USE_BMP +# else +# define LV_USE_BMP 0 +# endif +#endif + +/* JPG + split JPG decoder library. + * Split JPG is a custom format optimized for embedded systems. */ +#ifndef LV_USE_SJPG +# ifdef CONFIG_LV_USE_SJPG +# define LV_USE_SJPG CONFIG_LV_USE_SJPG +# else +# define LV_USE_SJPG 0 +# endif +#endif + +/*GIF decoder library*/ +#ifndef LV_USE_GIF +# ifdef CONFIG_LV_USE_GIF +# define LV_USE_GIF CONFIG_LV_USE_GIF +# else +# define LV_USE_GIF 0 +# endif +#endif + +/*QR code library*/ +#ifndef LV_USE_QRCODE +# ifdef CONFIG_LV_USE_QRCODE +# define LV_USE_QRCODE CONFIG_LV_USE_QRCODE +# else +# define LV_USE_QRCODE 0 +# endif +#endif + +/*FreeType library*/ +#ifndef LV_USE_FREETYPE +# ifdef CONFIG_LV_USE_FREETYPE +# define LV_USE_FREETYPE CONFIG_LV_USE_FREETYPE +# else +# define LV_USE_FREETYPE 0 +# endif +#endif +#if LV_USE_FREETYPE +/*Memory used by FreeType to cache characters [bytes]*/ +#ifndef LV_FREETYPE_CACHE_SIZE +# ifdef CONFIG_LV_FREETYPE_CACHE_SIZE +# define LV_FREETYPE_CACHE_SIZE CONFIG_LV_FREETYPE_CACHE_SIZE +# else +# define LV_FREETYPE_CACHE_SIZE (16 * 1024) +# endif +#endif +#endif + + /*================== * EXAMPLES *==================*/ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c7cd3e8a7e..6fd4cce93f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -151,6 +151,13 @@ set(LVGL_TEST_OPTIONS_FULL_32BIT -DLV_LABEL_TEXT_SELECTION=1 -DLV_BUILD_EXAMPLES=1 -DLV_FONT_DEFAULT=&lv_font_montserrat_24 + -DLV_USE_FS_STDIO='A' + -DLV_USE_FS_POSIX='B' + -DLV_USE_PNG=1 + -DLV_USE_BMP=1 + -DLV_USE_SJPG=1 + -DLV_USE_GIF=1 + -DLV_USE_QRCODE=1 ) set(LVGL_TEST_OPTIONS_TEST