lupng.cView |
---|
| 1 … | + |
| 2 … | + * The MIT License (MIT) |
| 3 … | + * |
| 4 … | + * Copyright (c) 2014 Jan Solanti |
| 5 … | + * |
| 6 … | + * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 … | + * of this software and associated documentation files (the "Software"), to deal |
| 8 … | + * in the Software without restriction, including without limitation the rights |
| 9 … | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 10 … | + * copies of the Software, and to permit persons to whom the Software is |
| 11 … | + * furnished to do so, subject to the following conditions: |
| 12 … | + * |
| 13 … | + * The above copyright notice and this permission notice shall be included in all |
| 14 … | + * copies or substantial portions of the Software. |
| 15 … | + * |
| 16 … | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 … | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 18 … | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 19 … | + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 20 … | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 21 … | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 22 … | + * SOFTWARE. |
| 23 … | + */ |
| 24 … | + |
| 25 … | +#include <stdio.h> |
| 26 … | +#include <stdlib.h> |
| 27 … | +#include <string.h> |
| 28 … | + |
| 29 … | +#ifndef LUPNG_USE_ZLIB |
| 30 … | +#include <miniz.h> |
| 31 … | +#else |
| 32 … | +#include <zlib.h> |
| 33 … | +#endif |
| 34 … | + |
| 35 … | +#include "lupng.h" |
| 36 … | + |
| 37 … | +#define PNG_NONE 0 |
| 38 … | +#define PNG_IHDR 0x01 |
| 39 … | +#define PNG_PLTE 0x02 |
| 40 … | +#define PNG_IDAT 0x04 |
| 41 … | +#define PNG_IEND 0x08 |
| 42 … | + |
| 43 … | +#define PNG_GRAYSCALE 0 |
| 44 … | +#define PNG_TRUECOLOR 2 |
| 45 … | + |
| 46 … | +#define PNG_PALETTED 3 |
| 47 … | +#define PNG_GRAYSCALE_ALPHA 4 |
| 48 … | +#define PNG_TRUECOLOR_ALPHA 6 |
| 49 … | + |
| 50 … | +#define PNG_FILTER_NONE 0 |
| 51 … | +#define PNG_FILTER_SUB 1 |
| 52 … | +#define PNG_FILTER_UP 2 |
| 53 … | +#define PNG_FILTER_AVERAGE 3 |
| 54 … | +#define PNG_FILTER_PAETH 4 |
| 55 … | + |
| 56 … | +#define PNG_SIG_SIZE 8 |
| 57 … | + |
| 58 … | +#define PNG_DONE 1 |
| 59 … | +#define PNG_OK 0 |
| 60 … | +#define PNG_ERROR -1 |
| 61 … | + |
| 62 … | +#define BUF_SIZE 8192 |
| 63 … | +#define MAX(x, y) (x > y ? x : y) |
| 64 … | + |
| 65 … | + |
| 66 … | + |
| 67 … | + |
| 68 … | + * CRC computation as per PNG spec |
| 69 … | + ********************************************************/ |
| 70 … | + |
| 71 … | + |
| 72 … | + using the polynomial from the PNG spec, 0xEDB88320L. */ |
| 73 … | +static const uint32_t crcTable[] = |
| 74 … | +{ |
| 75 … | + 0x0, 0x77073096, 0xEE0E612C, 0x990951BA, 0x76DC419, 0x706AF48F, |
| 76 … | + 0xE963A535, 0x9E6495A3, 0xEDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, |
| 77 … | + 0x9B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, |
| 78 … | + 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, |
| 79 … | + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, |
| 80 … | + 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, |
| 81 … | + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, |
| 82 … | + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, |
| 83 … | + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, |
| 84 … | + 0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, |
| 85 … | + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x1DB7106, |
| 86 … | + 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x6B6B51F, 0x9FBFE4A5, 0xE8B8D433, |
| 87 … | + 0x7807C9A2, 0xF00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x86D3D2D, |
| 88 … | + 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, |
| 89 … | + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, |
| 90 … | + 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, |
| 91 … | + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, |
| 92 … | + 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, |
| 93 … | + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, |
| 94 … | + 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, |
| 95 … | + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, |
| 96 … | + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x3B6E20C, 0x74B1D29A, |
| 97 … | + 0xEAD54739, 0x9DD277AF, 0x4DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, |
| 98 … | + 0xD6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0xA00AE27, 0x7D079EB1, |
| 99 … | + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, |
| 100 … | + 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, |
| 101 … | + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, |
| 102 … | + 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, |
| 103 … | + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, |
| 104 … | + 0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, |
| 105 … | + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, |
| 106 … | + 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, |
| 107 … | + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x26D930A, 0x9C0906A9, 0xEB0E363F, |
| 108 … | + 0x72076785, 0x5005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0xCB61B38, |
| 109 … | + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0xBDBDF21, 0x86D3D2D4, 0xF1D4E242, |
| 110 … | + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, |
| 111 … | + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, |
| 112 … | + 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, |
| 113 … | + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, |
| 114 … | + 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, |
| 115 … | + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, |
| 116 … | + 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, |
| 117 … | + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D |
| 118 … | +}; |
| 119 … | + |
| 120 … | + |
| 121 … | + should be initialized to all 1's, and the transmitted value |
| 122 … | + is the 1's complement of the final running CRC (see the |
| 123 … | + crc() routine below)). */ |
| 124 … | +static uint32_t updateCrc(uint32_t crc, unsigned char *buf, |
| 125 … | + int len) |
| 126 … | +{ |
| 127 … | + unsigned long c = crc; |
| 128 … | + int n; |
| 129 … | + |
| 130 … | + for (n = 0; n < len; n++) |
| 131 … | + c = crcTable[(c ^ buf[n]) & 0xFF] ^ (c >> 8); |
| 132 … | + |
| 133 … | + return c; |
| 134 … | +} |
| 135 … | + |
| 136 … | + |
| 137 … | +static uint32_t crc(unsigned char *buf, int len) |
| 138 … | +{ |
| 139 … | + return updateCrc(0xFFFFFFFFL, buf, len) ^ 0xFFFFFFFFL; |
| 140 … | +} |
| 141 … | + |
| 142 … | + |
| 143 … | + |
| 144 … | + |
| 145 … | + * Helper structs |
| 146 … | + ********************************************************/ |
| 147 … | + |
| 148 … | +typedef struct |
| 149 … | +{ |
| 150 … | + uint32_t length; |
| 151 … | + uint8_t *type; |
| 152 … | + uint8_t *data; |
| 153 … | + uint32_t crc; |
| 154 … | +} PngChunk; |
| 155 … | + |
| 156 … | +typedef struct { |
| 157 … | + void *userPtr; |
| 158 … | + PngReadProc readProc; |
| 159 … | + PngWriteProc writeProc; |
| 160 … | + int8_t chunksFound; |
| 161 … | + |
| 162 … | + |
| 163 … | + uint32_t width; |
| 164 … | + uint32_t height; |
| 165 … | + uint8_t depth; |
| 166 … | + uint8_t colorType; |
| 167 … | + uint8_t channels; |
| 168 … | + uint8_t compression; |
| 169 … | + uint8_t filter; |
| 170 … | + uint8_t interlace; |
| 171 … | + |
| 172 … | + |
| 173 … | + uint32_t paletteItems; |
| 174 … | + uint8_t *palette; |
| 175 … | + |
| 176 … | + |
| 177 … | + z_stream stream; |
| 178 … | + size_t scanlineBytes; |
| 179 … | + int32_t currentCol; |
| 180 … | + int32_t currentRow; |
| 181 … | + uint32_t currentElem; |
| 182 … | + size_t currentByte; |
| 183 … | + int bytesPerPixel; |
| 184 … | + uint8_t *currentScanline; |
| 185 … | + uint8_t *previousScanline; |
| 186 … | + uint8_t currentFilter; |
| 187 … | + uint8_t interlacePass; |
| 188 … | + size_t compressedBytes; |
| 189 … | + |
| 190 … | + |
| 191 … | + int tmpCount; |
| 192 … | + uint8_t tmpBytes[2]; |
| 193 … | + |
| 194 … | + |
| 195 … | + LuImage *img; |
| 196 … | +} PngInfoStruct; |
| 197 … | + |
| 198 … | + |
| 199 … | +static const uint8_t PNG_SIG[] = |
| 200 … | + |
| 201 … | +{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; |
| 202 … | + |
| 203 … | +static const int startingRow[] = { 0, 0, 0, 4, 0, 2, 0, 1 }; |
| 204 … | +static const int startingCol[] = { 0, 0, 4, 0, 2, 0, 1, 0 }; |
| 205 … | +static const int rowIncrement[] = { 1, 8, 8, 8, 4, 4, 2, 2 }; |
| 206 … | +static const int colIncrement[] = { 1, 8, 8, 4, 4, 2, 2, 1 }; |
| 207 … | + |
| 208 … | + |
| 209 … | + |
| 210 … | + |
| 211 … | + * Helper functions |
| 212 … | + ********************************************************/ |
| 213 … | + |
| 214 … | +static inline void releaseChunk(PngChunk *chunk) |
| 215 … | +{ |
| 216 … | + |
| 217 … | + free((void *)chunk->type); |
| 218 … | + free((void *)chunk); |
| 219 … | +} |
| 220 … | + |
| 221 … | +static inline uint32_t swap32(uint32_t n) |
| 222 … | +{ |
| 223 … | + union { |
| 224 … | + unsigned char np[4]; |
|
| 225 … | + uint32_t i; |
| 226 … | + } u; |
| 227 … | + u.i = n; |
| 228 … | + |
| 229 … | + return ((uint32_t)u.np[0] << 24) | |
| 230 … | + ((uint32_t)u.np[1] << 16) | |
| 231 … | + ((uint32_t)u.np[2] << 8) | |
| 232 … | + (uint32_t)u.np[3]; |
| 233 … | +} |
| 234 … | + |
| 235 … | +static inline uint16_t swap16(uint16_t n) |
| 236 … | +{ |
| 237 … | + union { |
| 238 … | + unsigned char np[2]; |
| 239 … | + uint16_t i; |
| 240 … | + } u; |
| 241 … | + u.i = n; |
| 242 … | + |
| 243 … | + return ((uint16_t)u.np[0] << 8) | (uint16_t)u.np[1]; |
| 244 … | +} |
| 245 … | + |
| 246 … | +static int bytesEqual(const uint8_t *a, const uint8_t *b, size_t count) |
| 247 … | +{ |
| 248 … | + size_t i; |
| 249 … | + for (i = 0; i < count; ++i) |
| 250 … | + { |
| 251 … | + if (*(a+i) != *(b+i)) |
| 252 … | + return 0; |
| 253 … | + } |
| 254 … | + |
| 255 … | + return 1; |
| 256 … | +} |
| 257 … | + |
| 258 … | + |
| 259 … | + |
| 260 … | + |
| 261 … | + * Png filter functions |
| 262 … | + ********************************************************/ |
| 263 … | +static inline int absi(int val) |
| 264 … | +{ |
| 265 … | + return val > 0 ? val : -val; |
| 266 … | +} |
| 267 … | + |
| 268 … | +static inline uint8_t raw(PngInfoStruct *info, int32_t col) |
| 269 … | +{ |
| 270 … | + if (col < 0) |
| 271 … | + return 0; |
| 272 … | + return info->currentScanline[col]; |
| 273 … | +} |
| 274 … | + |
| 275 … | +static inline uint8_t prior(PngInfoStruct *info, int32_t col) |
| 276 … | +{ |
| 277 … | + if (info->currentRow <= startingRow[info->interlacePass] || col < 0) |
| 278 … | + return 0; |
| 279 … | + return info->previousScanline[col]; |
| 280 … | +} |
| 281 … | + |
| 282 … | +static inline uint8_t paethPredictor(uint8_t a, uint8_t b, uint8_t c) |
| 283 … | +{ |
| 284 … | + unsigned int A = a, B = b, C = c; |
| 285 … | + int p = (int)A + (int)B - (int)C; |
| 286 … | + int pa = absi(p - (int)A); |
| 287 … | + int pb = absi(p - (int)B); |
| 288 … | + int pc = absi(p - (int)C); |
| 289 … | + |
| 290 … | + if (pa <= pb && pa <= pc) |
| 291 … | + return a; |
| 292 … | + if (pb <= pc) |
| 293 … | + return b; |
| 294 … | + return c; |
| 295 … | +} |
| 296 … | + |
| 297 … | +static inline uint8_t deSub(PngInfoStruct *info, uint8_t filtered) |
| 298 … | +{ |
| 299 … | + return filtered + raw(info, info->currentByte-info->bytesPerPixel); |
| 300 … | +} |
| 301 … | + |
| 302 … | +static inline uint8_t deUp(PngInfoStruct *info, uint8_t filtered) |
| 303 … | +{ |
| 304 … | + return filtered + prior(info, info->currentByte); |
| 305 … | +} |
| 306 … | + |
| 307 … | +static inline uint8_t deAverage(PngInfoStruct *info, uint8_t filtered) |
| 308 … | +{ |
| 309 … | + uint16_t avg = (uint16_t)(raw(info, info->currentByte-info->bytesPerPixel) |
| 310 … | + + prior(info, info->currentByte)); |
| 311 … | + avg >>= 1; |
| 312 … | + return filtered + avg; |
| 313 … | +} |
| 314 … | + |
| 315 … | +static inline uint8_t dePaeth(PngInfoStruct *info, uint8_t filtered) |
| 316 … | +{ |
| 317 … | + return filtered + paethPredictor( |
| 318 … | + raw(info, info->currentByte-info->bytesPerPixel), |
| 319 … | + prior(info, info->currentByte), |
| 320 … | + prior(info, info->currentByte-info->bytesPerPixel)); |
| 321 … | +} |
| 322 … | + |
| 323 … | +static inline uint8_t none(PngInfoStruct *info) |
| 324 … | +{ |
| 325 … | + return raw(info, info->currentByte); |
| 326 … | +} |
| 327 … | + |
| 328 … | +static inline uint8_t sub(PngInfoStruct *info) |
| 329 … | +{ |
| 330 … | + return raw(info, info->currentByte) - raw(info, info->currentByte-info->bytesPerPixel); |
| 331 … | +} |
| 332 … | + |
| 333 … | +static inline uint8_t up(PngInfoStruct *info) |
| 334 … | +{ |
| 335 … | + return raw(info, info->currentByte) - prior(info, info->currentByte); |
| 336 … | +} |
| 337 … | + |
| 338 … | +static inline uint8_t average(PngInfoStruct *info) |
| 339 … | +{ |
| 340 … | + uint16_t avg = (uint16_t)(raw(info, info->currentByte-info->bytesPerPixel) |
| 341 … | + + prior(info, info->currentByte)); |
| 342 … | + avg >>= 1; |
| 343 … | + return raw(info, info->currentByte) - avg; |
| 344 … | +} |
| 345 … | + |
| 346 … | +static inline uint8_t paeth(PngInfoStruct *info) |
| 347 … | +{ |
| 348 … | + return raw(info, info->currentByte) - paethPredictor( |
| 349 … | + raw(info, info->currentByte-info->bytesPerPixel), |
| 350 … | + prior(info, info->currentByte), |
| 351 … | + prior(info, info->currentByte-info->bytesPerPixel)); |
| 352 … | +} |
| 353 … | + |
| 354 … | + |
| 355 … | + |
| 356 … | + |
| 357 … | + * Actual implementation |
| 358 … | + ********************************************************/ |
| 359 … | +static inline int parseIhdr(PngInfoStruct *info, PngChunk *chunk) |
| 360 … | +{ |
| 361 … | + if (info->chunksFound) |
| 362 … | + { |
| 363 … | + printf("PNG: malformed PNG file!\n"); |
| 364 … | + return PNG_ERROR; |
| 365 … | + } |
| 366 … | + |
| 367 … | + info->chunksFound |= PNG_IHDR; |
| 368 … | + info->width = swap32(*(uint32_t *)chunk->data); |
| 369 … | + info->height = swap32(*((uint32_t *)chunk->data + 1)); |
| 370 … | + info->depth = *(chunk->data + 8); |
| 371 … | + info->colorType = *(chunk->data + 9); |
| 372 … | + info->compression = *(chunk->data + 10); |
| 373 … | + info->filter = *(chunk->data + 11); |
| 374 … | + info->interlace = *(chunk->data + 12); |
| 375 … | + |
| 376 … | + switch (info->colorType) |
| 377 … | + { |
| 378 … | + case PNG_GRAYSCALE: |
| 379 … | + info->channels = 1; |
| 380 … | + break; |
| 381 … | + case PNG_TRUECOLOR: |
| 382 … | + info->channels = 3; |
| 383 … | + break; |
| 384 … | + case PNG_PALETTED: |
| 385 … | + info->channels = 3; |
| 386 … | + break; |
| 387 … | + case PNG_GRAYSCALE_ALPHA: |
| 388 … | + info->channels = 2; |
| 389 … | + break; |
| 390 … | + case PNG_TRUECOLOR_ALPHA: |
| 391 … | + info->channels = 4; |
| 392 … | + break; |
| 393 … | + default: |
| 394 … | + printf("PNG: illegal color type: %i\n", |
| 395 … | + (unsigned int)info->colorType); |
| 396 … | + return PNG_ERROR; |
| 397 … | + break; |
| 398 … | + } |
| 399 … | + |
| 400 … | + if ((info->colorType != PNG_GRAYSCALE && info->colorType != PNG_PALETTED && |
| 401 … | + info->depth < 8) || |
| 402 … | + (info->colorType == PNG_PALETTED && info->depth == 16) || |
| 403 … | + info->depth > 16) |
| 404 … | + { |
| 405 … | + printf("PNG: illegal bit depth for color type\n"); |
| 406 … | + return PNG_ERROR; |
| 407 … | + } |
| 408 … | + |
| 409 … | + if (info->compression) |
| 410 … | + { |
| 411 … | + printf("PNG: unknown compression method: %i\n", |
| 412 … | + (unsigned int)info->compression); |
| 413 … | + return PNG_ERROR; |
| 414 … | + } |
| 415 … | + |
| 416 … | + if (info->filter) |
| 417 … | + { |
| 418 … | + printf("PNG: unknown filter scheme: %i\n", |
| 419 … | + (unsigned int)info->filter); |
| 420 … | + return PNG_ERROR; |
| 421 … | + } |
| 422 … | + |
| 423 … | + memset(&(info->stream), 0, sizeof(info->stream)); |
| 424 … | + if(inflateInit(&(info->stream)) != Z_OK) |
| 425 … | + { |
| 426 … | + printf("PNG: inflateInit failed!\n"); |
| 427 … | + return PNG_ERROR; |
| 428 … | + } |
| 429 … | + info->img = luImageCreate(info->width, info->height, |
| 430 … | + info->channels, info->depth < 16 ? 8 : 16); |
| 431 … | + info->scanlineBytes = MAX((info->width * info->channels * info->depth) >> 3, 1); |
| 432 … | + info->currentScanline = (uint8_t *)malloc(info->scanlineBytes); |
| 433 … | + info->previousScanline = (uint8_t *)malloc(info->scanlineBytes); |
| 434 … | + info->currentCol = -1; |
| 435 … | + info->interlacePass = info->interlace ? 1 : 0; |
| 436 … | + info->bytesPerPixel = MAX((info->channels * info->depth) >> 3, 1); |
| 437 … | + if (!info->img || !info->currentScanline || !info->previousScanline) |
| 438 … | + { |
| 439 … | + printf("PNG: memory allocation failed!\n"); |
| 440 … | + return PNG_ERROR; |
| 441 … | + } |
| 442 … | + |
| 443 … | + return PNG_OK; |
| 444 … | +} |
| 445 … | + |
| 446 … | +static inline int parsePlte(PngInfoStruct *info, PngChunk *chunk) |
| 447 … | +{ |
| 448 … | + if (info->chunksFound & PNG_IDAT || !(info->chunksFound & PNG_IHDR)) |
| 449 … | + { |
| 450 … | + printf("PNG: malformed PNG file!\n"); |
| 451 … | + return PNG_ERROR; |
| 452 … | + } |
| 453 … | + |
| 454 … | + info->chunksFound |= PNG_PLTE; |
| 455 … | + |
| 456 … | + if (chunk->length % 3 != 0) |
| 457 … | + { |
| 458 … | + printf("PNG: invalid palette size!\n"); |
| 459 … | + return PNG_ERROR; |
| 460 … | + } |
| 461 … | + |
| 462 … | + info->paletteItems = chunk->length/3; |
| 463 … | + info->palette = (uint8_t *)malloc(chunk->length); |
| 464 … | + if (!info->palette) |
| 465 … | + { |
| 466 … | + printf("PNG: memory allocation failed!\n"); |
| 467 … | + return PNG_ERROR; |
| 468 … | + } |
| 469 … | + memcpy(info->palette, chunk->data, chunk->length); |
| 470 … | + |
| 471 … | + return PNG_OK; |
| 472 … | +} |
| 473 … | + |
| 474 … | +static inline void stretchBits(uint8_t inByte, uint8_t outBytes[8], int depth) |
| 475 … | +{ |
| 476 … | + int i; |
| 477 … | + switch (depth) { |
| 478 … | + case 1: |
| 479 … | + for (i = 0; i < 8; ++i) |
| 480 … | + outBytes[i] = (inByte >> (7-i)) & 0x01; |
| 481 … | + break; |
| 482 … | + |
| 483 … | + case 2: |
| 484 … | + outBytes[0] = (inByte >> 6) & 0x03; |
| 485 … | + outBytes[1] = (inByte >> 4) & 0x03; |
| 486 … | + outBytes[2] = (inByte >> 2) & 0x03; |
| 487 … | + outBytes[3] = inByte & 0x03; |
| 488 … | + break; |
| 489 … | + |
| 490 … | + case 4: |
| 491 … | + outBytes[0] = (inByte >> 4) & 0x0F; |
| 492 … | + outBytes[1] = inByte & 0x0F; |
| 493 … | + break; |
| 494 … | + |
| 495 … | + default: |
| 496 … | + break; |
| 497 … | + } |
| 498 … | +} |
| 499 … | + |
| 500 … | + |
| 501 … | +static inline int insertByte(PngInfoStruct *info, uint8_t byte) |
| 502 … | +{ |
| 503 … | + int advance = 0; |
| 504 … | + const uint8_t scale[] = {0x00, 0xFF, 0x55, 0x00, 0x11, 0x00, 0x00, 0x00}; |
| 505 … | + |
| 506 … | + |
| 507 … | + size_t idx = info->currentRow * info->width * info->channels |
| 508 … | + + info->currentCol * info->channels |
| 509 … | + + info->currentElem; |
| 510 … | + |
| 511 … | + if (info->colorType != PNG_PALETTED) |
| 512 … | + { |
| 513 … | + if (info->depth == 8) |
| 514 … | + info->img->data[idx] = byte; |
| 515 … | + |
| 516 … | + else if (info->depth < 8) |
| 517 … | + info->img->data[idx] = byte * scale[info->depth]; |
| 518 … | + |
| 519 … | + else |
| 520 … | + { |
| 521 … | + info->tmpBytes[info->tmpCount] = byte; |
| 522 … | + if (info->tmpCount) |
| 523 … | + { |
| 524 … | + uint16_t val = *(uint16_t *)info->tmpBytes; |
| 525 … | + val = swap16(val); |
| 526 … | + info->tmpCount = 0; |
| 527 … | + |
| 528 … | + ((uint16_t *)(info->img->data))[idx] = val; |
| 529 … | + } |
| 530 … | + else |
| 531 … | + { |
| 532 … | + ++info->tmpCount; |
| 533 … | + return 0; |
| 534 … | + } |
| 535 … | + } |
| 536 … | + |
| 537 … | + ++info->currentElem; |
| 538 … | + if (info->currentElem >= info->channels) |
| 539 … | + { |
| 540 … | + advance = 1; |
| 541 … | + info->currentElem = 0; |
| 542 … | + } |
| 543 … | + } |
| 544 … | + else |
| 545 … | + { |
| 546 … | + |
| 547 … | + if (byte < info->paletteItems) |
| 548 … | + { |
| 549 … | + info->img->data[idx ] = info->palette[3*byte ]; |
| 550 … | + info->img->data[idx+1] = info->palette[3*byte+1]; |
| 551 … | + info->img->data[idx+2] = info->palette[3*byte+2]; |
| 552 … | + } |
| 553 … | + else |
| 554 … | + { |
| 555 … | + printf("PNG: invalid palette index encountered!\n"); |
| 556 … | + } |
| 557 … | + advance = 1; |
| 558 … | + } |
| 559 … | + |
| 560 … | + if (advance) |
| 561 … | + { |
| 562 … | + |
| 563 … | + info->currentCol += colIncrement[info->interlacePass]; |
| 564 … | + |
| 565 … | + if (info->currentCol >= info->width) |
| 566 … | + { |
| 567 … | + uint8_t *tmp = info->currentScanline; |
| 568 … | + info->currentScanline = info->previousScanline; |
| 569 … | + info->previousScanline = tmp; |
| 570 … | + |
| 571 … | + info->currentCol = -1; |
| 572 … | + info->currentByte = 0; |
| 573 … | + |
| 574 … | + info->currentRow += rowIncrement[info->interlacePass]; |
| 575 … | + if (info->currentRow >= info->height && info->interlace) |
| 576 … | + { |
| 577 … | + ++info->interlacePass; |
| 578 … | + while (startingCol[info->interlacePass] >= info->width || |
| 579 … | + startingRow[info->interlacePass] >= info->height) |
| 580 … | + ++info->interlacePass; |
| 581 … | + info->currentRow = startingRow[info->interlacePass]; |
| 582 … | + } |
| 583 … | + return 1; |
| 584 … | + } |
| 585 … | + } |
| 586 … | + |
| 587 … | + return 0; |
| 588 … | +} |
| 589 … | + |
| 590 … | +static inline int parseIdat(PngInfoStruct *info, PngChunk *chunk) |
| 591 … | +{ |
| 592 … | + unsigned char filtered[BUF_SIZE]; |
| 593 … | + int status = Z_OK; |
| 594 … | + |
| 595 … | + if (!(info->chunksFound & PNG_IHDR)) |
| 596 … | + { |
| 597 … | + printf("PNG: malformed PNG file!\n"); |
| 598 … | + return PNG_ERROR; |
| 599 … | + } |
| 600 … | + |
| 601 … | + if (info->colorType == PNG_PALETTED && !(info->chunksFound & PNG_PLTE)) |
| 602 … | + { |
| 603 … | + printf("PNG: palette required but missing!\n"); |
| 604 … | + return PNG_ERROR; |
| 605 … | + } |
| 606 … | + |
| 607 … | + info->chunksFound |= PNG_IDAT; |
| 608 … | + info->stream.next_in = (unsigned char *)chunk->data; |
| 609 … | + info->stream.avail_in = chunk->length; |
| 610 … | + do |
| 611 … | + { |
| 612 … | + info->stream.next_out = filtered; |
| 613 … | + info->stream.avail_out = BUF_SIZE; |
| 614 … | + status = inflate(&(info->stream), Z_NO_FLUSH); |
| 615 … | + size_t decompressed = BUF_SIZE - info->stream.avail_out; |
| 616 … | + size_t i; |
| 617 … | + |
| 618 … | + if (status != Z_OK && |
| 619 … | + status != Z_STREAM_END && |
| 620 … | + status != Z_BUF_ERROR && |
| 621 … | + status != Z_NEED_DICT) |
| 622 … | + { |
| 623 … | + printf("PNG: inflate error!\n"); |
| 624 … | + return PNG_ERROR; |
| 625 … | + } |
| 626 … | + |
| 627 … | + for (i = 0; i < decompressed; ++i) |
| 628 … | + { |
| 629 … | + if (info->currentCol < 0) |
| 630 … | + { |
| 631 … | + info->currentCol = startingCol[info->interlacePass]; |
| 632 … | + info->currentFilter = filtered[i]; |
| 633 … | + } |
| 634 … | + else |
| 635 … | + { |
| 636 … | + uint8_t rawByte = 0; |
| 637 … | + uint8_t fullBytes[8] = {0}; |
| 638 … | + switch (info->currentFilter) |
| 639 … | + { |
| 640 … | + case PNG_FILTER_NONE: |
| 641 … | + rawByte = filtered[i]; |
| 642 … | + break; |
| 643 … | + case PNG_FILTER_SUB: |
| 644 … | + rawByte = deSub(info, filtered[i]); |
| 645 … | + break; |
| 646 … | + case PNG_FILTER_UP: |
| 647 … | + rawByte = deUp(info, filtered[i]); |
| 648 … | + break; |
| 649 … | + case PNG_FILTER_AVERAGE: |
| 650 … | + rawByte = deAverage(info, filtered[i]); |
| 651 … | + break; |
| 652 … | + case PNG_FILTER_PAETH: |
| 653 … | + rawByte = dePaeth(info, filtered[i]); |
| 654 … | + break; |
| 655 … | + default: |
| 656 … | + break; |
| 657 … | + } |
| 658 … | + |
| 659 … | + info->currentScanline[info->currentByte] = rawByte; |
| 660 … | + ++info->currentByte; |
| 661 … | + |
| 662 … | + if (info->depth < 8) |
| 663 … | + { |
| 664 … | + int j; |
| 665 … | + stretchBits(rawByte, fullBytes, info->depth); |
| 666 … | + for (j = 0; j < 8/info->depth; ++j) |
| 667 … | + if(insertByte(info, fullBytes[j])) |
| 668 … | + break; |
| 669 … | + } |
| 670 … | + else |
| 671 … | + insertByte(info, rawByte); |
| 672 … | + } |
| 673 … | + } |
| 674 … | + } while ((info->stream.avail_in > 0 || info->stream.avail_out == 0)); |
| 675 … | + |
| 676 … | + return PNG_OK; |
| 677 … | +} |
| 678 … | + |
| 679 … | +static inline PngChunk *readChunk(PngInfoStruct *info) |
| 680 … | +{ |
| 681 … | + PngChunk *chunk = (PngChunk *)malloc(sizeof(PngChunk)); |
| 682 … | + size_t read = 0; |
| 683 … | + if (!chunk) |
| 684 … | + { |
| 685 … | + printf("PNG: memory allocation failed!\n"); |
| 686 … | + return 0; |
| 687 … | + } |
| 688 … | + |
| 689 … | + info->readProc((void *)&chunk->length, 4, 1, info->userPtr); |
| 690 … | + chunk->length = swap32(chunk->length); |
| 691 … | + chunk->type = (uint8_t *)malloc(chunk->length + 4); |
| 692 … | + chunk->data = chunk->type + 4; |
| 693 … | + |
| 694 … | + info->readProc((void *)chunk->type, 1, chunk->length + 4, info->userPtr); |
| 695 … | + read = info->readProc((void *)&chunk->crc, 4, 1, info->userPtr); |
| 696 … | + chunk->crc = swap32(chunk->crc); |
| 697 … | + |
| 698 … | + if (read != 1) |
| 699 … | + { |
| 700 … | + printf("PNG: read error\n"); |
| 701 … | + releaseChunk(chunk); |
| 702 … | + return 0; |
| 703 … | + } |
| 704 … | + |
| 705 … | + if (crc(chunk->type, chunk->length+4) != chunk->crc) |
| 706 … | + { |
| 707 … | + printf("PNG: CRC mismatch in \'%.4s\'\n", (char *)chunk->type); |
| 708 … | + releaseChunk(chunk); |
| 709 … | + return 0; |
| 710 … | + } |
| 711 … | + |
| 712 … | + return chunk; |
| 713 … | +} |
| 714 … | + |
| 715 … | +static inline int handleChunk(PngInfoStruct *info, PngChunk *chunk) |
| 716 … | +{ |
| 717 … | + |
| 718 … | + if (!(chunk->type[0] & 0x20)) |
| 719 … | + { |
| 720 … | + if (bytesEqual(chunk->type, (const uint8_t *)"IHDR", 4)) |
| 721 … | + return parseIhdr(info, chunk); |
| 722 … | + if (bytesEqual(chunk->type, (const uint8_t *)"PLTE", 4)) |
| 723 … | + return parsePlte(info, chunk); |
| 724 … | + if (bytesEqual(chunk->type, (const uint8_t *)"IDAT", 4)) |
| 725 … | + return parseIdat(info, chunk); |
| 726 … | + if (bytesEqual(chunk->type, (const uint8_t *)"IEND", 4)) |
| 727 … | + { |
| 728 … | + info->chunksFound |= PNG_IEND; |
| 729 … | + if (!(info->chunksFound & PNG_IDAT)) |
| 730 … | + { |
| 731 … | + printf("PNG: no IDAT chunk found\n"); |
| 732 … | + return PNG_ERROR; |
| 733 … | + } |
| 734 … | + return PNG_DONE; |
| 735 … | + } |
| 736 … | + } |
| 737 … | + |
| 738 … | + |
| 739 … | + return PNG_OK; |
| 740 … | +} |
| 741 … | + |
| 742 … | +LuImage *luPngRead(PngReadProc readProc, void *userPtr) |
| 743 … | +{ |
| 744 … | + PngInfoStruct info = |
| 745 … | + { |
| 746 … | + userPtr, |
| 747 … | + readProc, |
| 748 … | + 0, |
| 749 … | + PNG_NONE |
| 750 … | + }; |
| 751 … | + uint8_t signature[PNG_SIG_SIZE]; |
| 752 … | + int status = PNG_ERROR; |
| 753 … | + |
| 754 … | + info.readProc((void *)signature, 1, PNG_SIG_SIZE, userPtr); |
| 755 … | + if (bytesEqual(signature, PNG_SIG, PNG_SIG_SIZE)) |
| 756 … | + { |
| 757 … | + PngChunk *chunk; |
| 758 … | + while ((chunk = readChunk(&info))) |
| 759 … | + { |
| 760 … | + status = handleChunk(&info, chunk); |
| 761 … | + releaseChunk(chunk); |
| 762 … | + |
| 763 … | + if (status != PNG_OK) |
| 764 … | + break; |
| 765 … | + } |
| 766 … | + } |
| 767 … | + else |
| 768 … | + printf("PNG: invalid header\n"); |
| 769 … | + |
| 770 … | + if (info.currentScanline) |
| 771 … | + free((void *)info.currentScanline); |
| 772 … | + if (info.previousScanline) |
| 773 … | + free((void *)info.previousScanline); |
| 774 … | + if (info.palette) |
| 775 … | + free((void *)info.palette); |
| 776 … | + inflateEnd(&info.stream); |
| 777 … | + |
| 778 … | + if (status == PNG_DONE) |
| 779 … | + return info.img; |
| 780 … | + else |
| 781 … | + if (info.img) |
| 782 … | + luImageRelease(info.img); |
| 783 … | + |
| 784 … | + return 0; |
| 785 … | +} |
| 786 … | + |
| 787 … | +static inline int writeIhdr(PngInfoStruct *info) |
| 788 … | +{ |
| 789 … | + static uint8_t buf[17]; |
| 790 … | + static const uint8_t colorType[] = { |
| 791 … | + PNG_GRAYSCALE, |
| 792 … | + PNG_GRAYSCALE_ALPHA, |
| 793 … | + PNG_TRUECOLOR, |
| 794 … | + PNG_TRUECOLOR_ALPHA |
| 795 … | + }; |
| 796 … | + size_t written = 0; |
| 797 … | + PngChunk c; |
| 798 … | + |
| 799 … | + if (info->img->channels > 4) |
| 800 … | + { |
| 801 … | + printf("PNG: too many channels in image\n"); |
| 802 … | + return PNG_ERROR; |
| 803 … | + } |
| 804 … | + |
| 805 … | + c.length = swap32(13); |
| 806 … | + c.type = buf; |
| 807 … | + c.data = c.type + 4; |
| 808 … | + |
| 809 … | + memcpy((void *)c.type, (void *)"IHDR", 4); |
| 810 … | + *(uint32_t *)(c.data) = swap32((uint32_t)info->img->width); |
| 811 … | + *(uint32_t *)(c.data + 4) = swap32((uint32_t)info->img->height); |
| 812 … | + *(c.data + 8) = info->img->depth; |
| 813 … | + *(c.data + 9) = colorType[info->img->channels-1]; |
| 814 … | + *(c.data + 10) = 0; |
| 815 … | + *(c.data + 11) = 0; |
| 816 … | + *(c.data + 12) = 0; |
| 817 … | + |
| 818 … | + c.crc = swap32(crc(c.type, 17)); |
| 819 … | + |
| 820 … | + written += info->writeProc((void *)&c.length, 4, 1, info->userPtr) * 4; |
| 821 … | + written += info->writeProc((void *)c.type, 1, 4, info->userPtr); |
| 822 … | + written += info->writeProc((void *)c.data, 1, 13, info->userPtr); |
| 823 … | + written += info->writeProc((void *)&c.crc, 4, 1, info->userPtr) * 4; |
| 824 … | + |
| 825 … | + if (written != 25) |
| 826 … | + { |
| 827 … | + printf("PNG: write error\n"); |
| 828 … | + return PNG_ERROR; |
| 829 … | + } |
| 830 … | + |
| 831 … | + return PNG_OK; |
| 832 … | +} |
| 833 … | + |
| 834 … | +static inline int writeIdat(PngInfoStruct *info, uint8_t *buf, size_t buflen) |
| 835 … | +{ |
| 836 … | + size_t written = 0; |
| 837 … | + PngChunk c; |
| 838 … | + |
| 839 … | + c.length = swap32(buflen-4); |
| 840 … | + c.crc = swap32(crc(buf, buflen)); |
| 841 … | + |
| 842 … | + written += info->writeProc((void *)&c.length, 4, 1, info->userPtr) * 4; |
| 843 … | + written += info->writeProc((void *)buf, 1, buflen, info->userPtr); |
| 844 … | + written += info->writeProc((void *)&c.crc, 4, 1, info->userPtr) * 4; |
| 845 … | + |
| 846 … | + if (written != buflen+8) |
| 847 … | + { |
| 848 … | + printf("PNG: write error\n"); |
| 849 … | + return PNG_ERROR; |
| 850 … | + } |
| 851 … | + |
| 852 … | + return PNG_OK; |
| 853 … | +} |
| 854 … | + |
| 855 … | +static inline void advanceBytep(PngInfoStruct *info, int is16bit) |
| 856 … | +{ |
| 857 … | + if (is16bit) |
| 858 … | + { |
| 859 … | + if (info->currentByte%2) |
| 860 … | + --info->currentByte; |
| 861 … | + else |
| 862 … | + info->currentByte+=3; |
| 863 … | + } |
| 864 … | + else |
| 865 … | + ++info->currentByte; |
| 866 … | +} |
| 867 … | + |
| 868 … | +static inline size_t filterScanline(PngInfoStruct *info, |
| 869 … | + uint8_t(*f)(PngInfoStruct *info), |
| 870 … | + uint8_t filter, |
| 871 … | + uint8_t *filterCandidate, |
| 872 … | + int is16bit) |
| 873 … | +{ |
| 874 … | + size_t curSum = 0; |
| 875 … | + filterCandidate[0] = filter; |
| 876 … | + size_t fc; |
| 877 … | + for (info->currentByte = is16bit ? 1 : 0, fc = 1; |
| 878 … | + info->currentByte < info->scanlineBytes; ++fc, advanceBytep(info, is16bit) ) |
| 879 … | + { |
| 880 … | + uint8_t val = f(info); |
| 881 … | + filterCandidate[fc] = val; |
| 882 … | + curSum += val; |
| 883 … | + } |
| 884 … | + |
| 885 … | + return curSum; |
| 886 … | +} |
| 887 … | + |
| 888 … | + |
| 889 … | + * Processes the input image and calls writeIdat for every BUF_SIZE compressed |
| 890 … | + * bytes. |
| 891 … | + */ |
| 892 … | +static inline int processPixels(PngInfoStruct *info) |
| 893 … | +{ |
| 894 … | + uint8_t idatBuf[BUF_SIZE+4] = {'I', 'D', 'A', 'T'}; |
| 895 … | + uint8_t *compressed = idatBuf+4; |
| 896 … | + uint8_t *filterCandidate = (uint8_t *)malloc(info->scanlineBytes+1); |
| 897 … | + uint8_t *bestCandidate = (uint8_t *)malloc(info->scanlineBytes+1); |
| 898 … | + size_t minSum = (size_t)-1, curSum = 0; |
| 899 … | + int status = Z_OK; |
| 900 … | + int is16bit = info->img->depth == 16; |
| 901 … | + |
| 902 … | + if (!filterCandidate || !bestCandidate) |
| 903 … | + { |
| 904 … | + printf("PNG: memory allocation failed!\n"); |
| 905 … | + } |
| 906 … | + |
| 907 … | + memset(&(info->stream), 0, sizeof(info->stream)); |
| 908 … | + if(deflateInit(&(info->stream), Z_DEFAULT_COMPRESSION) != Z_OK) |
| 909 … | + { |
| 910 … | + printf("PNG: deflateInit failed!\n"); |
| 911 … | + free(filterCandidate); |
| 912 … | + free(bestCandidate); |
| 913 … | + return PNG_ERROR; |
| 914 … | + } |
| 915 … | + |
| 916 … | + info->stream.avail_out = BUF_SIZE; |
| 917 … | + info->stream.next_out = compressed; |
| 918 … | + |
| 919 … | + for (info->currentRow = 0; info->currentRow < info->img->height; |
| 920 … | + ++info->currentRow) |
| 921 … | + { |
| 922 … | + int flush = (info->currentRow < info->img->height-1) ? |
| 923 … | + Z_NO_FLUSH : Z_FINISH; |
| 924 … | + minSum = (size_t)-1; |
| 925 … | + |
| 926 … | + |
| 927 … | + * 1st time it doesn't matter, the filters never look at the previous |
| 928 … | + * scanline when processing row 0. And next time it'll be valid. |
| 929 … | + */ |
| 930 … | + info->previousScanline = info->currentScanline; |
| 931 … | + info->currentScanline = info->img->data + (info->currentRow*info->scanlineBytes); |
| 932 … | + |
| 933 … | + |
| 934 … | + * Try to choose the best filter for each scanline. |
| 935 … | + * Breaks in case of overflow, but hey it's just a heuristic. |
| 936 … | + */ |
| 937 … | + for (info->currentFilter = PNG_FILTER_NONE; info->currentFilter <= PNG_FILTER_PAETH; ++info->currentFilter) |
| 938 … | + { |
| 939 … | + |
| 940 … | + switch (info->currentFilter) |
| 941 … | + { |
| 942 … | + case PNG_FILTER_NONE: |
| 943 … | + curSum = filterScanline(info, none, PNG_FILTER_NONE, filterCandidate, is16bit); |
| 944 … | + break; |
| 945 … | + |
| 946 … | + case PNG_FILTER_SUB: |
| 947 … | + curSum = filterScanline(info, sub, PNG_FILTER_SUB, filterCandidate, is16bit); |
| 948 … | + break; |
| 949 … | + |
| 950 … | + case PNG_FILTER_UP: |
| 951 … | + curSum = filterScanline(info, up, PNG_FILTER_UP, filterCandidate, is16bit); |
| 952 … | + break; |
| 953 … | + |
| 954 … | + case PNG_FILTER_AVERAGE: |
| 955 … | + curSum = filterScanline(info, average, PNG_FILTER_AVERAGE, filterCandidate, is16bit); |
| 956 … | + break; |
| 957 … | + |
| 958 … | + case PNG_FILTER_PAETH: |
| 959 … | + curSum = filterScanline(info, paeth, PNG_FILTER_PAETH, filterCandidate, is16bit); |
| 960 … | + break; |
| 961 … | + |
| 962 … | + default: |
| 963 … | + break; |
| 964 … | + } |
| 965 … | + |
| 966 … | + if (curSum < minSum || !info->currentFilter) |
| 967 … | + { |
| 968 … | + uint8_t *tmp = bestCandidate; |
| 969 … | + bestCandidate = filterCandidate; |
| 970 … | + filterCandidate = tmp; |
| 971 … | + minSum = curSum; |
| 972 … | + } |
| 973 … | + } |
| 974 … | + |
| 975 … | + info->stream.avail_in = info->scanlineBytes+1; |
| 976 … | + info->stream.next_in = bestCandidate; |
| 977 … | + |
| 978 … | + |
| 979 … | + do |
| 980 … | + { |
| 981 … | + status = deflate(&info->stream, flush); |
| 982 … | + |
| 983 … | + if (info->stream.avail_out < BUF_SIZE) |
| 984 … | + { |
| 985 … | + writeIdat(info, idatBuf, BUF_SIZE-info->stream.avail_out+4); |
| 986 … | + info->stream.next_out = compressed; |
| 987 … | + info->stream.avail_out = BUF_SIZE; |
| 988 … | + } |
| 989 … | + } while ((flush == Z_FINISH && status != Z_STREAM_END) |
| 990 … | + || (flush == Z_NO_FLUSH && info->stream.avail_in)); |
| 991 … | + |
| 992 … | + } |
| 993 … | + |
| 994 … | + return PNG_OK; |
| 995 … | +} |
| 996 … | + |
| 997 … | +static inline int writeIend(PngInfoStruct *info) |
| 998 … | +{ |
| 999 … | + PngChunk c = { 0, (uint8_t *)"IEND", 0, 0 }; |
| 1000 … | + size_t written = 0; |
| 1001 … | + c.crc = swap32(crc(c.type, 4)); |
| 1002 … | + |
| 1003 … | + written += info->writeProc((void *)&c.length, 4, 1, info->userPtr) * 4; |
| 1004 … | + written += info->writeProc((void *)c.type, 1, 4, info->userPtr); |
| 1005 … | + written += info->writeProc((void *)&c.crc, 4, 1, info->userPtr) * 4; |
| 1006 … | + |
| 1007 … | + if (written != 12) |
| 1008 … | + { |
| 1009 … | + printf("PNG: write error\n"); |
| 1010 … | + return PNG_ERROR; |
| 1011 … | + } |
| 1012 … | + |
| 1013 … | + return PNG_OK; |
| 1014 … | +} |
| 1015 … | + |
| 1016 … | +int luPngWrite(PngWriteProc writeProc, void *userPtr, LuImage *img) |
| 1017 … | +{ |
| 1018 … | + PngInfoStruct info = { |
| 1019 … | + userPtr, |
| 1020 … | + 0, |
| 1021 … | + writeProc, |
| 1022 … | + PNG_NONE |
| 1023 … | + }; |
| 1024 … | + |
| 1025 … | + info.img = img; |
| 1026 … | + info.bytesPerPixel = (info.img->channels * info.img->depth) >> 3; |
| 1027 … | + |
| 1028 … | + if (writeProc((void *)PNG_SIG, 1, PNG_SIG_SIZE, userPtr) != PNG_SIG_SIZE) |
| 1029 … | + { |
| 1030 … | + printf("PNG: write error\n"); |
| 1031 … | + return PNG_ERROR; |
| 1032 … | + } |
| 1033 … | + |
| 1034 … | + if (writeIhdr(&info) != PNG_OK) |
| 1035 … | + return PNG_ERROR; |
| 1036 … | + |
| 1037 … | + info.scanlineBytes = (img->depth >> 3) * img->channels * img->width; |
| 1038 … | + if (processPixels(&info) != PNG_OK) |
| 1039 … | + { |
| 1040 … | + deflateEnd(&(info.stream)); |
| 1041 … | + return PNG_ERROR; |
| 1042 … | + } |
| 1043 … | + |
| 1044 … | + deflateEnd(&(info.stream)); |
| 1045 … | + return writeIend(&info); |
| 1046 … | +} |
| 1047 … | + |
| 1048 … | + |
| 1049 … | +void luImageRelease(LuImage *img) |
| 1050 … | +{ |
| 1051 … | + free((void *)img->data); |
| 1052 … | + free((void *)img); |
| 1053 … | +} |
| 1054 … | + |
| 1055 … | +LuImage *luImageCreate(size_t width, size_t height, uint8_t channels, uint8_t depth) |
| 1056 … | +{ |
| 1057 … | + LuImage *img; |
| 1058 … | + |
| 1059 … | + if (depth != 8 && depth != 16) |
| 1060 … | + { |
| 1061 … | + printf("Image: only bit depths 8 and 16 are supported!\n"); |
| 1062 … | + return 0; |
| 1063 … | + } |
| 1064 … | + |
| 1065 … | + img = (LuImage *)malloc(sizeof(LuImage)); |
| 1066 … | + if (!img) |
| 1067 … | + return 0; |
| 1068 … | + |
| 1069 … | + img->width = width; |
| 1070 … | + img->height = height; |
| 1071 … | + img->channels = channels; |
| 1072 … | + img->depth = depth; |
| 1073 … | + img->dataSize = (size_t)((depth >> 3) * width * height * channels); |
| 1074 … | + img->data = (uint8_t *)malloc(img->dataSize); |
| 1075 … | + |
| 1076 … | + return img; |
| 1077 … | +} |