├── CMakeLists.txt ├── .github └── workflows │ └── ci.yml ├── LICENSE ├── test.c ├── README.md ├── libattopng.h └── libattopng.c /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.9) 2 | project (libattopng) 3 | 4 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -Wall") 5 | 6 | include_directories(.) 7 | 8 | add_executable(libattopng test.c libattopng.c) 9 | target_link_libraries(libattopng) 10 | 11 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: libattopng test 2 | on: [push] 3 | jobs: 4 | test-module-latest: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - run: sudo apt install gcc make cmake 9 | - run: mkdir build && cd build && cmake .. && make && ./libattopng 10 | - run: sha1sum build/test_gray_alpha.png | grep -c 8b12966f11b50080fafb4d2987f96e23f3a54328 11 | - run: sha1sum build/test_gray_stream.png | grep -c a75c10351df7dc81502fa7aa8d844cb61f5df312 12 | - run: sha1sum build/test_gray.png | grep -c b56e135644c0ac7e24c54657c57da3b71b0e57ee 13 | - run: sha1sum build/test_palette.png | grep -c 97bd97ae765d18653b238d5c8a762ca29553eb44 14 | - run: sha1sum build/test_rgb.png | grep -c 53bde6364a9ecea2d88ecc5b5eb8ad77b9539125 15 | - run: sha1sum build/test_rgba.png | grep -c 8b396829b7e574e242f3723de31686443d6e588d 16 | 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Michael Schwarz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | #define W 250 6 | #define H 200 7 | 8 | #define RGBA(r, g, b, a) ((r) | ((g) << 8) | ((b) << 16) | ((a) << 24)) 9 | #define RGB(r, g, b) RGBA(r, g, b, 0xff) 10 | 11 | #define ALPHA(c, a) ((c) | ((a) << 8)) 12 | 13 | 14 | int main(int argc, char *argv[]) { 15 | libattopng_t *png = libattopng_new(W, H, PNG_PALETTE); 16 | uint32_t palette[] = { 17 | RGBA(0, 0, 0xff, 0xff), 18 | RGBA(0, 0xff, 0, 0x80), 19 | RGBA(0xff, 0, 0, 0xff), 20 | RGBA(0xff, 0, 0xff, 0x80) 21 | }; 22 | libattopng_set_palette(png, palette, 4); 23 | 24 | int x, y; 25 | for (y = 0; y < H; y++) { 26 | for (x = 0; x < W; x++) { 27 | libattopng_set_pixel(png, x, y, (x % 16) / 4); 28 | } 29 | } 30 | 31 | libattopng_save(png, "test_palette.png"); 32 | libattopng_destroy(png); 33 | 34 | // ----------------- 35 | 36 | png = libattopng_new(W, H, PNG_RGBA); 37 | 38 | for (y = 0; y < H; y++) { 39 | for (x = 0; x < W; x++) { 40 | libattopng_set_pixel(png, x, y, RGBA(x & 255, y & 255, 128, (255 - ((x / 2) & 255)))); 41 | } 42 | } 43 | libattopng_save(png, "test_rgba.png"); 44 | libattopng_destroy(png); 45 | 46 | // ----------------- 47 | 48 | png = libattopng_new(W, H, PNG_RGB); 49 | 50 | for (y = 0; y < H; y++) { 51 | for (x = 0; x < W; x++) { 52 | libattopng_set_pixel(png, x, y, RGB(x & 255, y & 255, 128)); 53 | } 54 | } 55 | libattopng_save(png, "test_rgb.png"); 56 | libattopng_destroy(png); 57 | 58 | // ----------------- 59 | 60 | png = libattopng_new(W, H, PNG_GRAYSCALE); 61 | 62 | for (y = 0; y < H; y++) { 63 | for (x = 0; x < W; x++) { 64 | libattopng_set_pixel(png, x, y, (((x + y) / 2) & 255)); 65 | } 66 | } 67 | libattopng_save(png, "test_gray.png"); 68 | libattopng_destroy(png); 69 | 70 | // ----------------- 71 | 72 | png = libattopng_new(W, H, PNG_GRAYSCALE_ALPHA); 73 | 74 | for (y = 0; y < H; y++) { 75 | for (x = 0; x < W; x++) { 76 | libattopng_set_pixel(png, x, y, ALPHA(((x + y) / 2) & 255, 255 - ((y / 2) & 255))); 77 | } 78 | } 79 | libattopng_save(png, "test_gray_alpha.png"); 80 | libattopng_destroy(png); 81 | 82 | // ----------------- 83 | 84 | png = libattopng_new(W, H, PNG_GRAYSCALE); 85 | 86 | libattopng_start_stream(png, 0, 0); 87 | for (x = 0; x < W * H; x++) { 88 | libattopng_put_pixel(png, 255 * x / (W * H)); 89 | } 90 | libattopng_save(png, "test_gray_stream.png"); 91 | libattopng_destroy(png); 92 | 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libattopng 2 | 3 | `libattopng` is a minimal C library to create uncompressed PNG images. 4 | It is cross-platform compatible, has no dependencies and a very small footprint of less than 10kB. 5 | The library supports palette, grayscale as well as raw RGB images all with and without transparency. 6 | 7 | # Usage 8 | 9 | Using `libattopng` is as simple as adding both `libattopng.h` and `libattopng.c` to your project. Independent of the used standard (from C89 to C11), the code will compile without warnings. The library does not require any special flags to compile. 10 | 11 | ## Example 12 | 13 | ### Color gradient with alpha 14 | ```C 15 | #define RGBA(r, g, b, a) ((r) | ((g) << 8) | ((b) << 16) | ((a) << 24)) 16 | 17 | libattopng_t* png = libattopng_new(250, 200, PNG_RGBA); 18 | 19 | int x, y; 20 | for (y = 0; y < 200; y++) { 21 | for (x = 0; x < 250; x++) { 22 | libattopng_set_pixel(png, x, y, RGBA(x & 255, y & 255, 128, (255 - ((x / 2) & 255)))); 23 | } 24 | } 25 | libattopng_save(png, "test_rgba.png"); 26 | libattopng_destroy(png); 27 | ``` 28 | 29 | ### Stripes with palette and alpha 30 | ```C 31 | #define RGBA(r, g, b, a) ((r) | ((g) << 8) | ((b) << 16) | ((a) << 24)) 32 | 33 | // ceate palette image 34 | libattopng_t *png = libattopng_new(256, 256, PNG_PALETTE); 35 | uint32_t palette[] = {RGBA(0, 0, 0xff, 0xff), RGBA(0, 0xff, 0, 0x80), RGBA(0xff, 0, 0, 0xff), RGBA(0xff, 0, 0xff, 0x80)}; 36 | // 4 colors: blue, green (50% alpha), red, cyan (50% alpha) 37 | libattopng_set_palette(png, palette, 4); 38 | 39 | int x, y; 40 | for (y = 0; y < 256; y++) { 41 | for (x = 0; x < 256; x++) { 42 | libattopng_set_pixel(png, x, y, (x % 16) / 4); 43 | } 44 | } 45 | 46 | libattopng_save(png, "test_palette.png"); 47 | libattopng_destroy(png); 48 | ``` 49 | 50 | ### More examples 51 | See `test.c` for more examples. 52 | 53 | # API documentation 54 | 55 | ### Brief content: 56 | 57 | **Functions**: 58 | 59 | > [`libattopng_new`](#function-libattopng_new): Create a new, empty PNG image to be used with all other functions. 60 | 61 | > [`libattopng_destroy`](#function-libattopng_destroy): Destroys the reference to a PNG image and free all associated memory. 62 | 63 | > [`libattopng_set_palette`](#function-libattopng_set_palette): Sets the image's palette if the image type is PNG_PALETTE. 64 | 65 | > [`libattopng_set_pixel`](#function-libattopng_set_pixel): Sets the pixel's color at the specified position 66 | 67 | > [`libattopng_get_pixel`](#function-libattopng_get_pixel): Returns the pixel's color at the specified position 68 | 69 | > [`libattopng_start_stream`](#function-libattopng_start_stream): Set the start position for a batch of pixels 70 | 71 | > [`libattopng_put_pixel`](#function-libattopng_put_pixel): Sets the pixel of the current pixel within a stream and advances to the next pixel 72 | 73 | > [`libattopng_get_data`](#function-libattopng_get_data): Returns the image as PNG data stream 74 | 75 | > [`libattopng_save`](#function-libattopng_save): Saves the image as a PNG file 76 | 77 | ## Functions 78 | 79 | The module `libattopng.h` defines the following functions. 80 | 81 | ### Function `libattopng_new` 82 | 83 | Create a new, empty PNG image to be used with all other functions. 84 | 85 | > It's the callers responsibility to free the data structure. See [`libattopng_destroy`](#function-libattopng_destroy) 86 | 87 | **Parameters:** 88 | 89 | - `width`: The width of the image in pixels 90 | - `height`: The height of the image in pixels 91 | - `type`: The type of image. Possible values are 92 | 93 | - PNG_GRAYSCALE (8bit grayscale), 94 | - PNG_GRAYSCALE_ALPHA (8bit grayscale with 8bit alpha), 95 | - PNG_PALETTE (palette with up to 256 entries, each 32bit RGBA) 96 | - PNG_RGB (24bit RGB values) 97 | - PNG_RGBA (32bit RGB values with alpha) 98 | 99 | **Returns:** 100 | 101 | - reference to a PNG image to be used with all other functions or NULL on error. 102 | Possible errors are: 103 | - Out of memory 104 | - Width and height combined exceed the maximum integer size 105 | 106 | ### Function `libattopng_destroy` 107 | 108 | Destroys the reference to a PNG image and free all associated memory. 109 | 110 | **Parameters:** 111 | 112 | - `png`: Reference to the image 113 | 114 | **Returns:** 115 | 116 | No return 117 | 118 | ### Function `libattopng_set_palette` 119 | 120 | Sets the image's palette if the image type is PNG_PALETTE. 121 | 122 | **Parameters:** 123 | 124 | - `png`: Reference to the image 125 | - `palette`: Color palette, each entry contains a 32bit RGBA value 126 | - `length`: Number of palette entries 127 | 128 | **Returns:** 129 | 130 | - 0 on success, 1 if the palette contained more than 256 entries 131 | 132 | ### Function `libattopng_set_pixel` 133 | 134 | Sets the pixel's color at the specified position 135 | 136 | **Parameters:** 137 | 138 | - `png`: Reference to the image 139 | - `x`: X coordinate 140 | - `y`: Y coordinate 141 | - `color`: The pixel value, depending on the type this is 142 | 143 | - the 8bit palette index (PNG_PALETTE) 144 | - the 8bit gray value (PNG_GRAYSCALE) 145 | - a 16bit value where the lower 8bit are the gray value and 146 | the upper 8bit are the opacity (PNG_GRAYSCALE_ALPHA) 147 | - a 24bit RGB value (PNG_RGB) 148 | - a 32bit RGBA value (PNG_RGBA) 149 | 150 | > If the coordinates are not within the bounds of the image, the functions does nothing. 151 | 152 | **Returns:** 153 | 154 | No return 155 | 156 | ### Function `libattopng_get_pixel` 157 | 158 | Returns the pixel's color at the specified position 159 | 160 | **Parameters:** 161 | 162 | - `png`: Reference to the image 163 | - `x`: X coordinate 164 | - `y`: Y coordinate 165 | 166 | **Returns:** 167 | 168 | - The pixel value, depending on the type this is 169 | 170 | - the 8bit palette index (PNG_PALETTE) 171 | - the 8bit gray value (PNG_GRAYSCALE) 172 | - a 16bit value where the lower 8bit are the gray value and 173 | the upper 8bit are the opacity (PNG_GRAYSCALE_ALPHA) 174 | - a 24bit RGB value (PNG_RGB) 175 | - a 32bit RGBA value (PNG_RGBA) 176 | - 0 if the coordinates are out of bounds 177 | 178 | ### Function `libattopng_start_stream` 179 | 180 | Set the start position for a batch of pixels 181 | 182 | **Parameters:** 183 | 184 | - `png`: Reference to the image 185 | - `x`: X coordinate 186 | - `y`: Y coordinate 187 | 188 | **Returns:** 189 | 190 | No return 191 | 192 | **See:** 193 | 194 | [`libattopng_put_pixel`](#f-libattopng_put_pixel) 195 | 196 | ### Function `libattopng_put_pixel` 197 | 198 | Sets the pixel of the current pixel within a stream and advances to the next pixel 199 | 200 | **Parameters:** 201 | 202 | - `png`: Reference to the image 203 | - `color`: The pixel value, depending on the type this is 204 | 205 | - the 8bit palette index (PNG_PALETTE) 206 | - the 8bit gray value (PNG_GRAYSCALE) 207 | - a 16bit value where the lower 8bit are the gray value and 208 | the upper 8bit are the opacity (PNG_GRAYSCALE_ALPHA) 209 | - a 24bit RGB value (PNG_RGB) 210 | - a 32bit RGBA value (PNG_RGBA) 211 | 212 | **Returns:** 213 | 214 | No return 215 | 216 | ### Function `libattopng_get_data` 217 | 218 | Returns the image as PNG data stream 219 | 220 | **Parameters:** 221 | 222 | - `png`: Reference to the image 223 | - `len`: The length of the data stream is written to this output parameter 224 | 225 | **Returns:** 226 | 227 | - A reference to the PNG output stream 228 | > The data stream is free'd when calling \ref libattopng_destroy and 229 | must not be free'd be the caller 230 | 231 | ### Function `libattopng_save` 232 | 233 | Saves the image as a PNG file 234 | 235 | **Parameters:** 236 | 237 | - `png`: Reference to the image 238 | - `filename`: Name of the file 239 | 240 | **Returns:** 241 | 242 | - 0 on success, 1 on error 243 | -------------------------------------------------------------------------------- /libattopng.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file libattopng.h 3 | * @brief A minimal C library to write uncompressed PNG files. 4 | * 5 | * libattopng is a minimal C library to create uncompressed PNG images. 6 | * It is cross-platform compatible, has no dependencies and a very small footprint. 7 | * The library supports palette, grayscale as well as raw RGB images all with and without transparency. 8 | * 9 | * @author Michael Schwarz 10 | * @date 29 Jan 2017 11 | */ 12 | 13 | #ifndef _LIBATTOPNG_H_ 14 | #define _LIBATTOPNG_H_ 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | #include 21 | #include 22 | 23 | /** 24 | * @brief PNG type. 25 | * 26 | * The type of PNG image. It determines how the pixels are stored. 27 | */ 28 | typedef enum { 29 | PNG_GRAYSCALE = 0, /**< 256 shades of gray, 8bit per pixel */ 30 | PNG_RGB = 2, /**< 24bit RGB values */ 31 | PNG_PALETTE = 3, /**< Up to 256 RGBA palette colors, 8bit per pixel */ 32 | PNG_GRAYSCALE_ALPHA = 4, /**< 256 shades of gray plus alpha channel, 16bit per pixel */ 33 | PNG_RGBA = 6 /**< 24bit RGB values plus 8bit alpha channel */ 34 | } libattopng_type_t; 35 | 36 | 37 | /** 38 | * @brief Reference to a PNG image 39 | * 40 | * This struct holds the internal state of the PNG. The members should never be used directly. 41 | */ 42 | typedef struct { 43 | libattopng_type_t type; /**< File type */ 44 | size_t capacity; /**< Reserved memory for raw data */ 45 | char *data; /**< Raw pixel data, format depends on type */ 46 | uint32_t *palette; /**< Palette for image */ 47 | size_t palette_length; /**< Entries for palette, 0 if unused */ 48 | size_t width; /**< Image width */ 49 | size_t height; /**< Image height */ 50 | 51 | char *out; /**< Buffer to store final PNG */ 52 | size_t out_pos; /**< Current size of output buffer */ 53 | size_t out_capacity; /**< Capacity of output buffer */ 54 | uint32_t crc; /**< Currecnt CRC32 checksum */ 55 | uint16_t s1; /**< Helper variables for Adler checksum */ 56 | uint16_t s2; /**< Helper variables for Adler checksum */ 57 | size_t bpp; /**< Bytes per pixel */ 58 | 59 | size_t stream_x; /**< Current x coordinate for pixel streaming */ 60 | size_t stream_y; /**< Current y coordinate for pixel streaming */ 61 | } libattopng_t; 62 | 63 | 64 | /** 65 | * @function libattopng_new 66 | * 67 | * @brief Create a new, empty PNG image to be used with all other functions. 68 | * 69 | * @param width The width of the image in pixels 70 | * @param height The height of the image in pixels 71 | * @param type The type of image. Possible values are 72 | * - PNG_GRAYSCALE (8bit grayscale), 73 | * - PNG_GRAYSCALE_ALPHA (8bit grayscale with 8bit alpha), 74 | * - PNG_PALETTE (palette with up to 256 entries, each 32bit RGBA) 75 | * - PNG_RGB (24bit RGB values) 76 | * - PNG_RGBA (32bit RGB values with alpha) 77 | * @return reference to a PNG image to be used with all other functions or NULL on error. 78 | * Possible errors are: 79 | * - Out of memory 80 | * - Width and height combined exceed the maximum integer size 81 | * @note It's the callers responsibility to free the data structure. 82 | * See @ref libattopng_destroy 83 | */ 84 | libattopng_t *libattopng_new(size_t width, size_t height, libattopng_type_t type); 85 | 86 | 87 | /** 88 | * @function libattopng_destroy 89 | * 90 | * @brief Destroys the reference to a PNG image and free all associated memory. 91 | * 92 | * @param png Reference to the image 93 | * 94 | */ 95 | void libattopng_destroy(libattopng_t *png); 96 | 97 | 98 | /** 99 | * @function libattopng_set_palette 100 | * 101 | * @brief Sets the image's palette if the image type is \ref PNG_PALETTE. 102 | * 103 | * @param png Reference to the image 104 | * @param palette Color palette, each entry contains a 32bit RGBA value 105 | * @param length Number of palette entries 106 | * @return 0 on success, 1 if the palette contained more than 256 entries 107 | */ 108 | int libattopng_set_palette(libattopng_t *png, uint32_t *palette, size_t length); 109 | 110 | 111 | /** 112 | * @function libattopng_set_pixel 113 | * 114 | * @brief Sets the pixel's color at the specified position 115 | * 116 | * @param png Reference to the image 117 | * @param x X coordinate 118 | * @param y Y coordinate 119 | * @param color The pixel value, depending on the type this is 120 | * - the 8bit palette index (\ref PNG_PALETTE) 121 | * - the 8bit gray value (\ref PNG_GRAYSCALE) 122 | * - a 16bit value where the lower 8bit are the gray value and 123 | * the upper 8bit are the opacity (\ref PNG_GRAYSCALE_ALPHA) 124 | * - a 24bit RGB value (\ref PNG_RGB) 125 | * - a 32bit RGBA value (\ref PNG_RGBA) 126 | * @note If the coordinates are not within the bounds of the image, 127 | * the functions does nothing. 128 | */ 129 | void libattopng_set_pixel(libattopng_t *png, size_t x, size_t y, uint32_t color); 130 | 131 | 132 | /** 133 | * @function libattopng_get_pixel 134 | * 135 | * @brief Returns the pixel's color at the specified position 136 | * 137 | * @param png Reference to the image 138 | * @param x X coordinate 139 | * @param y Y coordinate 140 | * @return The pixel value, depending on the type this is 141 | * - the 8bit palette index (\ref PNG_PALETTE) 142 | * - the 8bit gray value (\ref PNG_GRAYSCALE) 143 | * - a 16bit value where the lower 8bit are the gray value and 144 | * the upper 8bit are the opacity (\ref PNG_GRAYSCALE_ALPHA) 145 | * - a 24bit RGB value (\ref PNG_RGB) 146 | * - a 32bit RGBA value (\ref PNG_RGBA) 147 | * - 0 if the coordinates are out of bounds 148 | */ 149 | uint32_t libattopng_get_pixel(libattopng_t *png, size_t x, size_t y); 150 | 151 | 152 | /** 153 | * @function libattopng_start_stream 154 | * 155 | * @brief Set the start position for a batch of pixels 156 | * 157 | * @param png Reference to the image 158 | * @param x X coordinate 159 | * @param y Y coordinate 160 | * 161 | * @see libattopng_put_pixel 162 | */ 163 | void libattopng_start_stream(libattopng_t *png, size_t x, size_t y); 164 | 165 | 166 | /** 167 | * @function libattopng_put_pixel 168 | * 169 | * @brief Sets the pixel of the current pixel within a stream and advances to the next pixel 170 | * 171 | * @param png Reference to the image 172 | * @param color The pixel value, depending on the type this is 173 | * - the 8bit palette index (\ref PNG_PALETTE) 174 | * - the 8bit gray value (\ref PNG_GRAYSCALE) 175 | * - a 16bit value where the lower 8bit are the gray value and 176 | * the upper 8bit are the opacity (\ref PNG_GRAYSCALE_ALPHA) 177 | * - a 24bit RGB value (\ref PNG_RGB) 178 | * - a 32bit RGBA value (\ref PNG_RGBA) 179 | */ 180 | void libattopng_put_pixel(libattopng_t *png, uint32_t color); 181 | 182 | 183 | /** 184 | * @function libattopng_get_data 185 | * 186 | * @brief Returns the image as PNG data stream 187 | * 188 | * @param png Reference to the image 189 | * @param len The length of the data stream is written to this output parameter 190 | * @return A reference to the PNG output stream 191 | * @note The data stream is free'd when calling \ref libattopng_destroy and 192 | * must not be free'd be the caller 193 | */ 194 | char *libattopng_get_data(libattopng_t *png, size_t *len); 195 | 196 | 197 | /** 198 | * @function libattopng_save 199 | * 200 | * @brief Saves the image as a PNG file 201 | * 202 | * @param png Reference to the image 203 | * @param filename Name of the file 204 | * @return 0 on success, 1 on error 205 | */ 206 | int libattopng_save(libattopng_t *png, const char *filename); 207 | 208 | 209 | #ifdef __cplusplus 210 | } 211 | #endif 212 | #endif 213 | 214 | -------------------------------------------------------------------------------- /libattopng.c: -------------------------------------------------------------------------------- 1 | #include "libattopng.h" 2 | #include 3 | #include 4 | 5 | #define LIBATTOPNG_ADLER_BASE 65521 6 | 7 | static const uint32_t libattopng_crc32[256] = { 8 | 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 9 | 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 10 | 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 11 | 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 12 | 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 13 | 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 14 | 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 15 | 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 16 | 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 17 | 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 18 | 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 19 | 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 20 | 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 21 | 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 22 | 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 23 | 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 24 | 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 25 | 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 26 | 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 27 | 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 28 | 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 29 | 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 30 | 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 31 | 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 32 | 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 33 | 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 34 | 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 35 | 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 36 | 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d 37 | }; 38 | 39 | /* ------------------------------------------------------------------------ */ 40 | libattopng_t *libattopng_new(size_t width, size_t height, libattopng_type_t type) { 41 | libattopng_t *png; 42 | if (SIZE_MAX / 4 / width < height) { 43 | /* ensure no type leads to an integer overflow */ 44 | return NULL; 45 | } 46 | png = (libattopng_t *) calloc(sizeof(libattopng_t), 1); 47 | if (!png) { 48 | return NULL; 49 | } 50 | png->width = width; 51 | png->height = height; 52 | png->capacity = width * height; 53 | png->palette_length = 0; 54 | png->palette = NULL; 55 | png->out = NULL; 56 | png->out_capacity = 0; 57 | png->out_pos = 0; 58 | png->type = type; 59 | png->stream_x = 0; 60 | png->stream_y = 0; 61 | 62 | if (type == PNG_PALETTE) { 63 | png->palette = (uint32_t *) calloc(256, sizeof(uint32_t)); 64 | if (!png->palette) { 65 | free(png); 66 | return NULL; 67 | } 68 | png->bpp = 1; 69 | } else if (type == PNG_GRAYSCALE) { 70 | png->bpp = 1; 71 | } else if (type == PNG_GRAYSCALE_ALPHA) { 72 | png->capacity *= 2; 73 | png->bpp = 2; 74 | } else if (type == PNG_RGB) { 75 | png->capacity *= 4; 76 | png->bpp = 3; 77 | } else if (type == PNG_RGBA) { 78 | png->capacity *= 4; 79 | png->bpp = 4; 80 | } 81 | 82 | png->data = (char *) calloc(png->capacity, 1); 83 | if (!png->data) { 84 | free(png->palette); 85 | free(png); 86 | return NULL; 87 | } 88 | return png; 89 | } 90 | 91 | /* ------------------------------------------------------------------------ */ 92 | int libattopng_set_palette(libattopng_t *png, uint32_t *palette, size_t length) { 93 | if (length > 256) { 94 | return 1; 95 | } 96 | memmove(png->palette, palette, length * sizeof(uint32_t)); 97 | png->palette_length = length; 98 | return 0; 99 | } 100 | 101 | /* ------------------------------------------------------------------------ */ 102 | void libattopng_set_pixel(libattopng_t *png, size_t x, size_t y, uint32_t color) { 103 | if (!png || x >= png->width || y >= png->height) { 104 | return; 105 | } 106 | if (png->type == PNG_PALETTE || png->type == PNG_GRAYSCALE) { 107 | png->data[x + y * png->width] = (char) (color & 0xff); 108 | } else if (png->type == PNG_GRAYSCALE_ALPHA) { 109 | ((uint16_t *) png->data)[x + y * png->width] = (uint16_t) (color & 0xffff); 110 | } else { 111 | ((uint32_t *) png->data)[x + y * png->width] = color; 112 | } 113 | } 114 | 115 | /* ------------------------------------------------------------------------ */ 116 | uint32_t libattopng_get_pixel(libattopng_t* png, size_t x, size_t y) { 117 | uint32_t pixel = 0; 118 | if (!png || x >= png->width || y >= png->height) { 119 | return pixel; 120 | } 121 | if (png->type == PNG_PALETTE || png->type == PNG_GRAYSCALE) { 122 | pixel = (uint32_t)(png->data[x + y * png->width] & 0xff); 123 | } else if (png->type == PNG_GRAYSCALE_ALPHA) { 124 | pixel = (uint32_t)(((uint16_t *) png->data)[x + y * png->width] & 0xffff); 125 | } else { 126 | pixel = ((uint32_t *) png->data)[x + y * png->width]; 127 | } 128 | return pixel; 129 | } 130 | 131 | /* ------------------------------------------------------------------------ */ 132 | void libattopng_start_stream(libattopng_t* png, size_t x, size_t y) { 133 | if (!png || x >= png->width || y >= png->height) { 134 | return; 135 | } 136 | png->stream_x = x; 137 | png->stream_y = y; 138 | } 139 | 140 | /* ------------------------------------------------------------------------ */ 141 | void libattopng_put_pixel(libattopng_t* png, uint32_t color) { 142 | size_t x, y; 143 | if (!png) { 144 | return; 145 | } 146 | x = png->stream_x; 147 | y = png->stream_y; 148 | if (png->type == PNG_PALETTE || png->type == PNG_GRAYSCALE) { 149 | png->data[x + y * png->width] = (char) (color & 0xff); 150 | } else if (png->type == PNG_GRAYSCALE_ALPHA) { 151 | ((uint16_t *) png->data)[x + y * png->width] = (uint16_t) (color & 0xffff); 152 | } else { 153 | ((uint32_t *) png->data)[x + y * png->width] = color; 154 | } 155 | x++; 156 | if (x >= png->width) { 157 | x = 0; 158 | y++; 159 | if (y >= png->height) { 160 | y = 0; 161 | } 162 | } 163 | png->stream_x = x; 164 | png->stream_y = y; 165 | } 166 | 167 | /* ------------------------------------------------------------------------ */ 168 | static uint32_t libattopng_swap32(uint32_t num) { 169 | return ((num >> 24) & 0xff) | 170 | ((num << 8) & 0xff0000) | 171 | ((num >> 8) & 0xff00) | 172 | ((num << 24) & 0xff000000); 173 | } 174 | 175 | /* ------------------------------------------------------------------------ */ 176 | static uint32_t libattopng_crc(const unsigned char *data, size_t len, uint32_t crc) { 177 | size_t i; 178 | for (i = 0; i < len; i++) { 179 | crc = libattopng_crc32[(crc ^ data[i]) & 255] ^ (crc >> 8); 180 | } 181 | return crc; 182 | } 183 | 184 | /* ------------------------------------------------------------------------ */ 185 | static void libattopng_out_raw_write(libattopng_t *png, const char *data, size_t len) { 186 | size_t i; 187 | for (i = 0; i < len; i++) { 188 | png->out[png->out_pos++] = data[i]; 189 | } 190 | } 191 | 192 | /* ------------------------------------------------------------------------ */ 193 | static void libattopng_out_raw_uint(libattopng_t *png, uint32_t val) { 194 | *(uint32_t *) (png->out + png->out_pos) = val; 195 | png->out_pos += 4; 196 | } 197 | 198 | /* ------------------------------------------------------------------------ */ 199 | static void libattopng_out_raw_uint16(libattopng_t *png, uint16_t val) { 200 | *(uint16_t *) (png->out + png->out_pos) = val; 201 | png->out_pos += 2; 202 | } 203 | 204 | /* ------------------------------------------------------------------------ */ 205 | static void libattopng_out_raw_uint8(libattopng_t *png, uint8_t val) { 206 | *(uint8_t *) (png->out + png->out_pos) = val; 207 | png->out_pos++; 208 | } 209 | 210 | /* ------------------------------------------------------------------------ */ 211 | static void libattopng_new_chunk(libattopng_t *png, const char *name, size_t len) { 212 | png->crc = 0xffffffff; 213 | libattopng_out_raw_uint(png, libattopng_swap32((uint32_t) len)); 214 | png->crc = libattopng_crc((const unsigned char *) name, 4, png->crc); 215 | libattopng_out_raw_write(png, name, 4); 216 | } 217 | 218 | /* ------------------------------------------------------------------------ */ 219 | static void libattopng_end_chunk(libattopng_t *png) { 220 | libattopng_out_raw_uint(png, libattopng_swap32(~png->crc)); 221 | } 222 | 223 | /* ------------------------------------------------------------------------ */ 224 | static void libattopng_out_uint32(libattopng_t *png, uint32_t val) { 225 | png->crc = libattopng_crc((const unsigned char *) &val, 4, png->crc); 226 | libattopng_out_raw_uint(png, val); 227 | } 228 | 229 | /* ------------------------------------------------------------------------ */ 230 | static void libattopng_out_uint16(libattopng_t *png, uint16_t val) { 231 | png->crc = libattopng_crc((const unsigned char *) &val, 2, png->crc); 232 | libattopng_out_raw_uint16(png, val); 233 | } 234 | 235 | /* ------------------------------------------------------------------------ */ 236 | static void libattopng_out_uint8(libattopng_t *png, uint8_t val) { 237 | png->crc = libattopng_crc((const unsigned char *) &val, 1, png->crc); 238 | libattopng_out_raw_uint8(png, val); 239 | } 240 | 241 | /* ------------------------------------------------------------------------ */ 242 | static void libattopng_out_write(libattopng_t *png, const char *data, size_t len) { 243 | png->crc = libattopng_crc((const unsigned char *) data, len, png->crc); 244 | libattopng_out_raw_write(png, data, len); 245 | } 246 | 247 | /* ------------------------------------------------------------------------ */ 248 | static void libattopng_out_write_adler(libattopng_t *png, unsigned char data) { 249 | libattopng_out_write(png, (char *) &data, 1); 250 | png->s1 = (uint16_t) ((png->s1 + data) % LIBATTOPNG_ADLER_BASE); 251 | png->s2 = (uint16_t) ((png->s2 + png->s1) % LIBATTOPNG_ADLER_BASE); 252 | } 253 | 254 | /* ------------------------------------------------------------------------ */ 255 | static void libattopng_pixel_header(libattopng_t *png, size_t offset, size_t bpl) { 256 | if (offset > bpl) { 257 | /* not the last line */ 258 | libattopng_out_write(png, "\0", 1); 259 | libattopng_out_uint16(png, (uint16_t) bpl); 260 | libattopng_out_uint16(png, (uint16_t) ~bpl); 261 | } else { 262 | /* last line */ 263 | libattopng_out_write(png, "\1", 1); 264 | libattopng_out_uint16(png, (uint16_t) offset); 265 | libattopng_out_uint16(png, (uint16_t) ~offset); 266 | } 267 | } 268 | 269 | /* ------------------------------------------------------------------------ */ 270 | char *libattopng_get_data(libattopng_t *png, size_t *len) { 271 | size_t index, bpl, raw_size, size, p, pos, corr; 272 | unsigned char *pixel; 273 | if (!png) { 274 | return NULL; 275 | } 276 | if (png->out) { 277 | /* delete old output if any */ 278 | free(png->out); 279 | } 280 | png->out_capacity = png->capacity + 4096 * 8 + png->width * png->height; 281 | png->out = (char *) calloc(png->out_capacity, 1); 282 | png->out_pos = 0; 283 | if (!png->out) { 284 | return NULL; 285 | } 286 | 287 | libattopng_out_raw_write(png, "\211PNG\r\n\032\n", 8); 288 | 289 | /* IHDR */ 290 | libattopng_new_chunk(png, "IHDR", 13); 291 | libattopng_out_uint32(png, libattopng_swap32((uint32_t) (png->width))); 292 | libattopng_out_uint32(png, libattopng_swap32((uint32_t) (png->height))); 293 | libattopng_out_uint8(png, 8); /* bit depth */ 294 | libattopng_out_uint8(png, (uint8_t) png->type); 295 | libattopng_out_uint8(png, 0); /* compression */ 296 | libattopng_out_uint8(png, 0); /* filter */ 297 | libattopng_out_uint8(png, 0); /* interlace method */ 298 | libattopng_end_chunk(png); 299 | 300 | /* palette */ 301 | if (png->type == PNG_PALETTE) { 302 | char entry[3]; 303 | size_t s = png->palette_length; 304 | if (s < 16) { 305 | s = 16; /* minimum palette length */ 306 | } 307 | libattopng_new_chunk(png, "PLTE", 3 * s); 308 | for (index = 0; index < s; index++) { 309 | entry[0] = (char) (png->palette[index] & 255); 310 | entry[1] = (char) ((png->palette[index] >> 8) & 255); 311 | entry[2] = (char) ((png->palette[index] >> 16) & 255); 312 | libattopng_out_write(png, entry, 3); 313 | } 314 | libattopng_end_chunk(png); 315 | 316 | /* transparency */ 317 | libattopng_new_chunk(png, "tRNS", s); 318 | for (index = 0; index < s; index++) { 319 | entry[0] = (char) ((png->palette[index] >> 24) & 255); 320 | libattopng_out_write(png, entry, 1); 321 | } 322 | libattopng_end_chunk(png); 323 | } 324 | 325 | /* data */ 326 | bpl = 1 + png->bpp * png->width; 327 | if (bpl >= 65536) { 328 | fprintf(stderr, "[libattopng] ERROR: maximum supported width for this type of PNG is %d pixel\n", (int)(65535 / png->bpp)); 329 | return NULL; 330 | } 331 | raw_size = png->height * bpl; 332 | size = 2 + png->height * (5 + bpl) + 4; 333 | libattopng_new_chunk(png, "IDAT", size); 334 | libattopng_out_write(png, "\170\332", 2); 335 | 336 | pixel = (unsigned char *) png->data; 337 | png->s1 = 1; 338 | png->s2 = 0; 339 | index = 0; 340 | if (png->type == PNG_RGB) { 341 | corr = 1; 342 | } else { 343 | corr = 0; 344 | } 345 | for (pos = 0; pos < png->width * png->height; pos++) { 346 | if (index == 0) { 347 | /* line header */ 348 | libattopng_pixel_header(png, raw_size, bpl); 349 | libattopng_out_write_adler(png, 0); /* no filter */ 350 | raw_size--; 351 | } 352 | 353 | /* pixel */ 354 | for (p = 0; p < png->bpp; p++) { 355 | libattopng_out_write_adler(png, *pixel); 356 | pixel++; 357 | } 358 | pixel += corr; 359 | 360 | raw_size -= png->bpp; 361 | index = (index + 1) % png->width; 362 | } 363 | /* checksum */ 364 | png->s1 %= LIBATTOPNG_ADLER_BASE; 365 | png->s2 %= LIBATTOPNG_ADLER_BASE; 366 | libattopng_out_uint32(png, libattopng_swap32((uint32_t) ((png->s2 << 16) | png->s1))); 367 | libattopng_end_chunk(png); 368 | 369 | /* end of image */ 370 | libattopng_new_chunk(png, "IEND", 0); 371 | libattopng_end_chunk(png); 372 | 373 | if (len) { 374 | *len = png->out_pos; 375 | } 376 | return png->out; 377 | } 378 | 379 | /* ------------------------------------------------------------------------ */ 380 | int libattopng_save(libattopng_t *png, const char *filename) { 381 | size_t len; 382 | FILE* f; 383 | char *data = libattopng_get_data(png, &len); 384 | if (!data) { 385 | return 1; 386 | } 387 | f = fopen(filename, "wb"); 388 | if (!f) { 389 | return 1; 390 | } 391 | if (fwrite(data, len, 1, f) != 1) { 392 | fclose(f); 393 | return 1; 394 | } 395 | fclose(f); 396 | return 0; 397 | } 398 | 399 | /* ------------------------------------------------------------------------ */ 400 | void libattopng_destroy(libattopng_t *png) { 401 | if (!png) { 402 | return; 403 | } 404 | free(png->palette); 405 | png->palette = NULL; 406 | free(png->out); 407 | png->out = NULL; 408 | free(png->data); 409 | png->data = NULL; 410 | free(png); 411 | } 412 | --------------------------------------------------------------------------------