| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852 |
- /*
- ------------------------------------------------------------------------------
- Licensing information can be found at the end of the file.
- ------------------------------------------------------------------------------
- cute_png.h - v1.05
- To create implementation (the function definitions)
- #define CUTE_PNG_IMPLEMENTATION
- in *one* C/CPP file (translation unit) that includes this file
- SUMMARY:
- This header wraps some very nice functions by Richard Mitton from his
- tigr (Tiny Graphics) library, with some additional features and small
- bug-fixes.
- Revision history:
- 1.00 (12/23/2016) initial release
- 1.01 (03/08/2017) tRNS chunk support for paletted images
- 1.02 (10/23/2017) support for explicitly loading paletted png images
- 1.03 (11/12/2017) construct atlas in memory
- 1.04 (08/23/2018) various bug fixes for filter and word decoder
- added `cp_load_blank`
- 1.05 (11/10/2022) added `cp_save_png_to_memory`
- EXAMPLES:
- Loading a PNG from disk, then freeing it
- cp_image_t img = cp_load_png("images/pic.png");
- ...
- free(img.pix);
- CUTE_PNG_MEMSET(&img, 0, sizeof(img));
- Loading a PNG from memory, then freeing it
- cp_image_t img = cp_load_png_mem(memory, sizeof(memory));
- ...
- free(img.pix);
- CUTE_PNG_MEMSET(&img, 0, sizeof(img));
- Saving a PNG to disk
- cp_save_png("images/example.png", &img);
- // img is just a raw RGBA buffer, and can come from anywhere,
- // not only from cp_load*** functions
- Creating a texture atlas in memory
- int w = 1024;
- int h = 1024;
- cp_atlas_image_t* imgs_out = (cp_atlas_image_t*)malloc(sizeof(cp_atlas_image_t) * my_png_count);
- cp_image_t atlas_img = cp_make_atlas(w, int h, my_png_array, my_png_count, imgs_out);
- // just pass an array of pointers to images along with the image count. Make sure to also
- // provide an array of `cp_atlas_image_t` for `cp_make_atlas` to output important UV info for the
- // images that fit into the atlas.
- Using the default atlas saver
- int errors = cp_default_save_atlas("atlas.png", "atlas.txt", atlas_img, atlas_imgs, img_count, names_of_all_images ? names_of_all_images : 0);
- if (errors) { ... }
- // Atlas info (like uv coordinates) are in "atlas.txt", and the image was writen to "atlas.png".
- // atlas_imgs was an array of `cp_atlas_image_t` from the `cp_make_atlas` function.
- Inflating a DEFLATE block (decompressing memory stored in DEFLATE format)
- cp_inflate(in, in_bytes, out, out_bytes);
- // this function requires knowledge of the un-compressed size
- // does *not* do any internal realloc! Will return errors if an
- // attempt to overwrite the out buffer is made
- CUSTOMIZATION
- There are various macros in this header you can customize by defining them before
- including cute_png.h. Simply define one to override the default behavior.
- CUTE_PNG_ALLOCA
- CUTE_PNG_ALLOC
- CUTE_PNG_FREE
- CUTE_PNG_CALLOC
- CUTE_PNG_REALLOC
- CUTE_PNG_MEMCPY
- CUTE_PNG_MEMCMP
- CUTE_PNG_MEMSET
- CUTE_PNG_ASSERT
- CUTE_PNG_FPRINTF
- CUTE_PNG_SEEK_SET
- CUTE_PNG_SEEK_END
- CUTE_PNG_FILE
- CUTE_PNG_FOPEN
- CUTE_PNG_FSEEK
- CUTE_PNG_FREAD
- CUTE_PNG_FTELL
- CUTE_PNG_FWRITE
- CUTE_PNG_FCLOSE
- CUTE_PNG_FERROR
- CUTE_PNG_ATLAS_MUST_FIT
- CUTE_PNG_ATLAS_FLIP_Y_AXIS_FOR_UV
- CUTE_PNG_ATLAS_EMPTY_COLOR
- */
- /*
- Contributors:
- Zachary Carter 1.01 - bug catch for tRNS chunk in paletted images
- Dennis Korpel 1.03 - fix some pointer/memory related bugs
- Dennis Korpel 1.04 - fix for filter on first row of pixels
- */
- #if !defined(CUTE_PNG_H)
- #ifdef _WIN32
- #if !defined(_CRT_SECURE_NO_WARNINGS)
- #define _CRT_SECURE_NO_WARNINGS
- #endif
- #endif
- #ifndef CUTE_PNG_ATLAS_MUST_FIT
- #define CUTE_PNG_ATLAS_MUST_FIT 1 // returns error from cp_make_atlas if *any* input image does not fit
- #endif // CUTE_PNG_ATLAS_MUST_FIT
- #ifndef CUTE_PNG_ATLAS_FLIP_Y_AXIS_FOR_UV
- #define CUTE_PNG_ATLAS_FLIP_Y_AXIS_FOR_UV 1 // flips output uv coordinate's y. Can be useful to "flip image on load"
- #endif // CUTE_PNG_ATLAS_FLIP_Y_AXIS_FOR_UV
- #ifndef CUTE_PNG_ATLAS_EMPTY_COLOR
- #define CUTE_PNG_ATLAS_EMPTY_COLOR 0x000000FF // the fill color for empty areas in a texture atlas (RGBA)
- #endif // CUTE_PNG_ATLAS_EMPTY_COLOR
- #include <stdint.h>
- #include <limits.h>
- typedef struct cp_pixel_t cp_pixel_t;
- typedef struct cp_image_t cp_image_t;
- typedef struct cp_indexed_image_t cp_indexed_image_t;
- typedef struct cp_atlas_image_t cp_atlas_image_t;
- // Read this in the event of errors from any function
- extern const char* cp_error_reason;
- // return 1 for success, 0 for failures
- int cp_inflate(void* in, int in_bytes, void* out, int out_bytes);
- int cp_save_png(const char* file_name, const cp_image_t* img);
- typedef struct cp_saved_png_t
- {
- int size; // Size of the `data` buffer.
- void* data; // Pointer to the saved png in memory.
- // NULL if something went wrong.
- // Call CUTE_PNG_FREE on `data` when done.
- } cp_saved_png_t;
- // Saves a png file to memory.
- // Call CUTE_PNG_FREE on .data when done.
- cp_saved_png_t cp_save_png_to_memory(const cp_image_t* img);
- // Constructs an atlas image in-memory. The atlas pixels are stored in the returned image. free the pixels
- // when done with them. The user must provide an array of cp_atlas_image_t for the `imgs` param. `imgs` holds
- // information about uv coordinates for an associated image in the `pngs` array. Output image has NULL
- // pixels buffer in the event of errors.
- cp_image_t cp_make_atlas(int atlasWidth, int atlasHeight, const cp_image_t* pngs, int png_count, cp_atlas_image_t* imgs_out);
- // A decent "default" function, ready to use out-of-the-box. Saves out an easy to parse text formatted info file
- // along with an atlas image. `names` param can be optionally NULL.
- int cp_default_save_atlas(const char* out_path_image, const char* out_path_atlas_txt, const cp_image_t* atlas, const cp_atlas_image_t* imgs, int img_count, const char** names);
- // these two functions return cp_image_t::pix as 0 in event of errors
- // call free on cp_image_t::pix when done, or call cp_free_png
- cp_image_t cp_load_png(const char *file_name);
- cp_image_t cp_load_png_mem(const void *png_data, int png_length);
- cp_image_t cp_load_blank(int w, int h); // Alloc's pixels, but `pix` memory is uninitialized.
- void cp_free_png(cp_image_t* img);
- void cp_flip_image_horizontal(cp_image_t* img);
- // Reads the w/h of the png without doing any other decompression or parsing.
- void cp_load_png_wh(const void* png_data, int png_length, int* w, int* h);
- // loads indexed (paletted) pngs, but does not depalette the image into RGBA pixels
- // these two functions return cp_indexed_image_t::pix as 0 in event of errors
- // call free on cp_indexed_image_t::pix when done, or call cp_free_indexed_png
- cp_indexed_image_t cp_load_indexed_png(const char* file_name);
- cp_indexed_image_t cp_load_indexed_png_mem(const void *png_data, int png_length);
- void cp_free_indexed_png(cp_indexed_image_t* img);
- // converts paletted image into a standard RGBA image
- // call free on cp_image_t::pix when done
- cp_image_t cp_depallete_indexed_image(cp_indexed_image_t* img);
- // Pre-process the pixels to transform the image data to a premultiplied alpha format.
- // Resource: http://www.essentialmath.com/GDC2015/VanVerth_Jim_DoingMathwRGB.pdf
- void cp_premultiply(cp_image_t* img);
- struct cp_pixel_t
- {
- uint8_t r;
- uint8_t g;
- uint8_t b;
- uint8_t a;
- };
- struct cp_image_t
- {
- int w;
- int h;
- cp_pixel_t* pix;
- };
- struct cp_indexed_image_t
- {
- int w;
- int h;
- uint8_t* pix;
- uint8_t palette_len;
- cp_pixel_t palette[256];
- };
- struct cp_atlas_image_t
- {
- int img_index; // index into the `imgs` array
- int w, h; // pixel w/h of original image
- float minx, miny; // u coordinate
- float maxx, maxy; // v coordinate
- int fit; // non-zero if image fit and was placed into the atlas
- };
- #define CUTE_PNG_H
- #endif
- #ifdef CUTE_PNG_IMPLEMENTATION
- #ifndef CUTE_PNG_IMPLEMENTATION_ONCE
- #define CUTE_PNG_IMPLEMENTATION_ONCE
- #if !defined(CUTE_PNG_ALLOCA)
- #define CUTE_PNG_ALLOCA alloca
- #ifdef _WIN32
- #include <malloc.h>
- #elif defined(__linux__)
- #include <alloca.h>
- #endif
- #endif
- #if !defined(CUTE_PNG_ALLOC)
- #include <stdlib.h>
- #define CUTE_PNG_ALLOC malloc
- #endif
- #if !defined(CUTE_PNG_FREE)
- #include <stdlib.h>
- #define CUTE_PNG_FREE free
- #endif
- #if !defined(CUTE_PNG_CALLOC)
- #include <stdlib.h>
- #define CUTE_PNG_CALLOC calloc
- #endif
- #if !defined(CUTE_PNG_REALLOC)
- #include <stdlib.h>
- #define CUTE_PNG_REALLOC realloc
- #endif
- #if !defined(CUTE_PNG_MEMCPY)
- #include <string.h>
- #define CUTE_PNG_MEMCPY memcpy
- #endif
- #if !defined(CUTE_PNG_MEMCMP)
- #include <string.h>
- #define CUTE_PNG_MEMCMP memcmp
- #endif
- #if !defined(CUTE_PNG_MEMSET)
- #include <string.h>
- #define CUTE_PNG_MEMSET memset
- #endif
- #if !defined(CUTE_PNG_ASSERT)
- #include <assert.h>
- #define CUTE_PNG_ASSERT assert
- #endif
- #if !defined(CUTE_PNG_FPRINTF)
- #include <stdio.h>
- #define CUTE_PNG_FPRINTF fprintf
- #endif
- #if !defined(CUTE_PNG_SEEK_SET)
- #include <stdio.h>
- #define CUTE_PNG_SEEK_SET SEEK_SET
- #endif
- #if !defined(CUTE_PNG_SEEK_END)
- #include <stdio.h>
- #define CUTE_PNG_SEEK_END SEEK_END
- #endif
- #if !defined(CUTE_PNG_FILE)
- #include <stdio.h>
- #define CUTE_PNG_FILE FILE
- #endif
- #if !defined(CUTE_PNG_FOPEN)
- #include <stdio.h>
- #define CUTE_PNG_FOPEN fopen
- #endif
- #if !defined(CUTE_PNG_FSEEK)
- #include <stdio.h>
- #define CUTE_PNG_FSEEK fseek
- #endif
- #if !defined(CUTE_PNG_FREAD)
- #include <stdio.h>
- #define CUTE_PNG_FREAD fread
- #endif
- #if !defined(CUTE_PNG_FTELL)
- #include <stdio.h>
- #define CUTE_PNG_FTELL ftell
- #endif
- #if !defined(CUTE_PNG_FWRITE)
- #include <stdio.h>
- #define CUTE_PNG_FWRITE fwrite
- #endif
- #if !defined(CUTE_PNG_FCLOSE)
- #include <stdio.h>
- #define CUTE_PNG_FCLOSE fclose
- #endif
- #if !defined(CUTE_PNG_FERROR)
- #include <stdio.h>
- #define CUTE_PNG_FERROR ferror
- #endif
- static cp_pixel_t cp_make_pixel_a(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
- {
- cp_pixel_t p;
- p.r = r; p.g = g; p.b = b; p.a = a;
- return p;
- }
- static cp_pixel_t cp_make_pixel(uint8_t r, uint8_t g, uint8_t b)
- {
- cp_pixel_t p;
- p.r = r; p.g = g; p.b = b; p.a = 0xFF;
- return p;
- }
- const char* cp_error_reason;
- #define CUTE_PNG_FAIL() do { goto cp_err; } while (0)
- #define CUTE_PNG_CHECK(X, Y) do { if (!(X)) { cp_error_reason = Y; CUTE_PNG_FAIL(); } } while (0)
- #define CUTE_PNG_CALL(X) do { if (!(X)) goto cp_err; } while (0)
- #define CUTE_PNG_LOOKUP_BITS 9
- #define CUTE_PNG_LOOKUP_COUNT (1 << CUTE_PNG_LOOKUP_BITS)
- #define CUTE_PNG_LOOKUP_MASK (CUTE_PNG_LOOKUP_COUNT - 1)
- #define CUTE_PNG_DEFLATE_MAX_BITLEN 15
- // DEFLATE tables from RFC 1951
- uint8_t cp_fixed_table[288 + 32] = {
- 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
- 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
- 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
- 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
- 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
- }; // 3.2.6
- uint8_t cp_permutation_order[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; // 3.2.7
- uint8_t cp_len_extra_bits[29 + 2] = { 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, 0,0 }; // 3.2.5
- uint32_t cp_len_base[29 + 2] = { 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, 0,0 }; // 3.2.5
- uint8_t cp_dist_extra_bits[30 + 2] = { 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, 0,0 }; // 3.2.5
- uint32_t cp_dist_base[30 + 2] = { 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, 0,0 }; // 3.2.5
- typedef struct cp_state_t
- {
- uint64_t bits;
- int count;
- uint32_t* words;
- int word_count;
- int word_index;
- int bits_left;
- int final_word_available;
- uint32_t final_word;
- char* out;
- char* out_end;
- char* begin;
- uint16_t lookup[CUTE_PNG_LOOKUP_COUNT];
- uint32_t lit[288];
- uint32_t dst[32];
- uint32_t len[19];
- uint32_t nlit;
- uint32_t ndst;
- uint32_t nlen;
- } cp_state_t;
- static int cp_would_overflow(cp_state_t* s, int num_bits)
- {
- return (s->bits_left + s->count) - num_bits < 0;
- }
- static char* cp_ptr(cp_state_t* s)
- {
- CUTE_PNG_ASSERT(!(s->bits_left & 7));
- return (char*)(s->words + s->word_index) - (s->count / 8);
- }
- static uint64_t cp_peak_bits(cp_state_t* s, int num_bits_to_read)
- {
- if (s->count < num_bits_to_read)
- {
- if (s->word_index < s->word_count)
- {
- uint32_t word = s->words[s->word_index++];
- s->bits |= (uint64_t)word << s->count;
- s->count += 32;
- CUTE_PNG_ASSERT(s->word_index <= s->word_count);
- }
- else if (s->final_word_available)
- {
- uint32_t word = s->final_word;
- s->bits |= (uint64_t)word << s->count;
- s->count += s->bits_left;
- s->final_word_available = 0;
- }
- }
- return s->bits;
- }
- static uint32_t cp_consume_bits(cp_state_t* s, int num_bits_to_read)
- {
- CUTE_PNG_ASSERT(s->count >= num_bits_to_read);
- uint32_t bits = s->bits & (((uint64_t)1 << num_bits_to_read) - 1);
- s->bits >>= num_bits_to_read;
- s->count -= num_bits_to_read;
- s->bits_left -= num_bits_to_read;
- return bits;
- }
- static uint32_t cp_read_bits(cp_state_t* s, int num_bits_to_read)
- {
- CUTE_PNG_ASSERT(num_bits_to_read <= 32);
- CUTE_PNG_ASSERT(num_bits_to_read >= 0);
- CUTE_PNG_ASSERT(s->bits_left > 0);
- CUTE_PNG_ASSERT(s->count <= 64);
- CUTE_PNG_ASSERT(!cp_would_overflow(s, num_bits_to_read));
- cp_peak_bits(s, num_bits_to_read);
- uint32_t bits = cp_consume_bits(s, num_bits_to_read);
- return bits;
- }
- static char* cp_read_file_to_memory(const char* path, int* size)
- {
- char* data = 0;
- CUTE_PNG_FILE* fp = CUTE_PNG_FOPEN(path, "rb");
- int sizeNum = 0;
- if (fp)
- {
- CUTE_PNG_FSEEK(fp, 0, CUTE_PNG_SEEK_END);
- sizeNum = CUTE_PNG_FTELL(fp);
- CUTE_PNG_FSEEK(fp, 0, CUTE_PNG_SEEK_SET);
- data = (char*)CUTE_PNG_ALLOC(sizeNum + 1);
- CUTE_PNG_FREAD(data, sizeNum, 1, fp);
- data[sizeNum] = 0;
- CUTE_PNG_FCLOSE(fp);
- }
- if (size) *size = sizeNum;
- return data;
- }
- static uint32_t cp_rev16(uint32_t a)
- {
- a = ((a & 0xAAAA) >> 1) | ((a & 0x5555) << 1);
- a = ((a & 0xCCCC) >> 2) | ((a & 0x3333) << 2);
- a = ((a & 0xF0F0) >> 4) | ((a & 0x0F0F) << 4);
- a = ((a & 0xFF00) >> 8) | ((a & 0x00FF) << 8);
- return a;
- }
- // RFC 1951 section 3.2.2
- static int cp_build(cp_state_t* s, uint32_t* tree, uint8_t* lens, int sym_count)
- {
- int n, codes[16], first[16], counts[16] = { 0 };
- // Frequency count
- for (n = 0; n < sym_count; n++) counts[lens[n]]++;
- // Distribute codes
- counts[0] = codes[0] = first[0] = 0;
- for (n = 1; n <= 15; ++n)
- {
- codes[n] = (codes[n - 1] + counts[n - 1]) << 1;
- first[n] = first[n - 1] + counts[n - 1];
- }
- if (s) CUTE_PNG_MEMSET(s->lookup, 0, sizeof(s->lookup));
- for (int i = 0; i < sym_count; ++i)
- {
- int len = lens[i];
- if (len != 0)
- {
- CUTE_PNG_ASSERT(len < 16);
- uint32_t code = codes[len]++;
- uint32_t slot = first[len]++;
- tree[slot] = (code << (32 - len)) | (i << 4) | len;
- if (s && len <= CUTE_PNG_LOOKUP_BITS)
- {
- int j = cp_rev16(code) >> (16 - len);
- while (j < (1 << CUTE_PNG_LOOKUP_BITS))
- {
- s->lookup[j] = (uint16_t)((len << CUTE_PNG_LOOKUP_BITS) | i);
- j += (1 << len);
- }
- }
- }
- }
- int max_index = first[15];
- return max_index;
- }
- static int cp_stored(cp_state_t* s)
- {
- char* p;
- // 3.2.3
- // skip any remaining bits in current partially processed byte
- cp_read_bits(s, s->count & 7);
- // 3.2.4
- // read LEN and NLEN, should complement each other
- uint16_t LEN = (uint16_t)cp_read_bits(s, 16);
- uint16_t NLEN = (uint16_t)cp_read_bits(s, 16);
- CUTE_PNG_CHECK(LEN == (uint16_t)(~NLEN), "Failed to find LEN and NLEN as complements within stored (uncompressed) stream.");
- CUTE_PNG_CHECK(s->bits_left / 8 <= (int)LEN, "Stored block extends beyond end of input stream.");
- p = cp_ptr(s);
- CUTE_PNG_MEMCPY(s->out, p, LEN);
- s->out += LEN;
- return 1;
- cp_err:
- return 0;
- }
- // 3.2.6
- static int cp_fixed(cp_state_t* s)
- {
- s->nlit = cp_build(s, s->lit, cp_fixed_table, 288);
- s->ndst = cp_build(0, s->dst, cp_fixed_table + 288, 32);
- return 1;
- }
- static int cp_decode(cp_state_t* s, uint32_t* tree, int hi)
- {
- uint64_t bits = cp_peak_bits(s, 16);
- uint32_t search = (cp_rev16((uint32_t)bits) << 16) | 0xFFFF;
- int lo = 0;
- while (lo < hi)
- {
- int guess = (lo + hi) >> 1;
- if (search < tree[guess]) hi = guess;
- else lo = guess + 1;
- }
- uint32_t key = tree[lo - 1];
- uint32_t len = (32 - (key & 0xF));
- CUTE_PNG_ASSERT((search >> len) == (key >> len));
- int code = cp_consume_bits(s, key & 0xF);
- (void)code;
- return (key >> 4) & 0xFFF;
- }
- // 3.2.7
- static int cp_dynamic(cp_state_t* s)
- {
- uint8_t lenlens[19] = { 0 };
- int nlit = 257 + cp_read_bits(s, 5);
- int ndst = 1 + cp_read_bits(s, 5);
- int nlen = 4 + cp_read_bits(s, 4);
- for (int i = 0 ; i < nlen; ++i)
- lenlens[cp_permutation_order[i]] = (uint8_t)cp_read_bits(s, 3);
- // Build the tree for decoding code lengths
- s->nlen = cp_build(0, s->len, lenlens, 19);
- uint8_t lens[288 + 32];
- for (int n = 0; n < nlit + ndst;)
- {
- int sym = cp_decode(s, s->len, s->nlen);
- switch (sym)
- {
- case 16: for (int i = 3 + cp_read_bits(s, 2); i; --i, ++n) lens[n] = lens[n - 1]; break;
- case 17: for (int i = 3 + cp_read_bits(s, 3); i; --i, ++n) lens[n] = 0; break;
- case 18: for (int i = 11 + cp_read_bits(s, 7); i; --i, ++n) lens[n] = 0; break;
- default: lens[n++] = (uint8_t)sym; break;
- }
- }
- s->nlit = cp_build(s, s->lit, lens, nlit);
- s->ndst = cp_build(0, s->dst, lens + nlit, ndst);
- return 1;
- }
- // 3.2.3
- static int cp_block(cp_state_t* s)
- {
- while (1)
- {
- int symbol = cp_decode(s, s->lit, s->nlit);
- if (symbol < 256)
- {
- CUTE_PNG_CHECK(s->out + 1 <= s->out_end, "Attempted to overwrite out buffer while outputting a symbol.");
- *s->out = (char)symbol;
- s->out += 1;
- }
- else if (symbol > 256)
- {
- symbol -= 257;
- int length = cp_read_bits(s, cp_len_extra_bits[symbol]) + cp_len_base[symbol];
- int distance_symbol = cp_decode(s, s->dst, s->ndst);
- int backwards_distance = cp_read_bits(s, cp_dist_extra_bits[distance_symbol]) + cp_dist_base[distance_symbol];
- CUTE_PNG_CHECK(s->out - backwards_distance >= s->begin, "Attempted to write before out buffer (invalid backwards distance).");
- CUTE_PNG_CHECK(s->out + length <= s->out_end, "Attempted to overwrite out buffer while outputting a string.");
- char* src = s->out - backwards_distance;
- char* dst = s->out;
- s->out += length;
- switch (backwards_distance)
- {
- case 1: // very common in images
- CUTE_PNG_MEMSET(dst, *src, length);
- break;
- default: while (length--) *dst++ = *src++;
- }
- }
- else break;
- }
- return 1;
- cp_err:
- return 0;
- }
- // 3.2.3
- int cp_inflate(void* in, int in_bytes, void* out, int out_bytes)
- {
- cp_state_t* s = (cp_state_t*)CUTE_PNG_CALLOC(1, sizeof(cp_state_t));
- s->bits = 0;
- s->count = 0;
- s->word_index = 0;
- s->bits_left = in_bytes * 8;
- // s->words is the in-pointer rounded up to a multiple of 4
- int first_bytes = (int) ((( (size_t) in + 3) & ~3) - (size_t) in);
- s->words = (uint32_t*)((char*)in + first_bytes);
- s->word_count = (in_bytes - first_bytes) / 4;
- int last_bytes = ((in_bytes - first_bytes) & 3);
- for (int i = 0; i < first_bytes; ++i)
- s->bits |= (uint64_t)(((uint8_t*)in)[i]) << (i * 8);
- s->final_word_available = last_bytes ? 1 : 0;
- s->final_word = 0;
- for(int i = 0; i < last_bytes; i++)
- s->final_word |= ((uint8_t*)in)[in_bytes - last_bytes+i] << (i * 8);
- s->count = first_bytes * 8;
- s->out = (char*)out;
- s->out_end = s->out + out_bytes;
- s->begin = (char*)out;
- int count = 0;
- int bfinal;
- do
- {
- bfinal = cp_read_bits(s, 1);
- int btype = cp_read_bits(s, 2);
- switch (btype)
- {
- case 0: CUTE_PNG_CALL(cp_stored(s)); break;
- case 1: cp_fixed(s); CUTE_PNG_CALL(cp_block(s)); break;
- case 2: cp_dynamic(s); CUTE_PNG_CALL(cp_block(s)); break;
- case 3: CUTE_PNG_CHECK(0, "Detected unknown block type within input stream.");
- }
- ++count;
- }
- while (!bfinal);
- CUTE_PNG_FREE(s);
- return 1;
- cp_err:
- CUTE_PNG_FREE(s);
- return 0;
- }
- static uint8_t cp_paeth(uint8_t a, uint8_t b, uint8_t c)
- {
- int p = a + b - c;
- int pa = abs(p - a);
- int pb = abs(p - b);
- int pc = abs(p - c);
- return (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c;
- }
- typedef struct cp_save_png_data_t
- {
- uint32_t crc;
- uint32_t adler;
- uint32_t bits;
- uint32_t prev;
- uint32_t runlen;
- int buflen;
- int bufcap;
- char* buffer;
- } cp_save_png_data_t;
- uint32_t CP_CRC_TABLE[] = {
- 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
- 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
- };
- static void cp_put8(cp_save_png_data_t* s, uint32_t a)
- {
- if (s->buflen >= s->bufcap)
- {
- s->bufcap *= 2;
- s->buffer = (char*)CUTE_PNG_REALLOC(s->buffer, s->bufcap);
- }
- s->buffer[s->buflen++] = a;
- s->crc = (s->crc >> 4) ^ CP_CRC_TABLE[(s->crc & 15) ^ (a & 15)];
- s->crc = (s->crc >> 4) ^ CP_CRC_TABLE[(s->crc & 15) ^ (a >> 4)];
- }
- static void cp_update_adler(cp_save_png_data_t* s, uint32_t v)
- {
- uint32_t s1 = s->adler & 0xFFFF;
- uint32_t s2 = (s->adler >> 16) & 0xFFFF;
- s1 = (s1 + v) % 65521;
- s2 = (s2 + s1) % 65521;
- s->adler = (s2 << 16) + s1;
- }
- static void cp_put32(cp_save_png_data_t* s, uint32_t v)
- {
- cp_put8(s, (v >> 24) & 0xFF);
- cp_put8(s, (v >> 16) & 0xFF);
- cp_put8(s, (v >> 8) & 0xFF);
- cp_put8(s, v & 0xFF);
- }
- static void cp_put_bits(cp_save_png_data_t* s, uint32_t data, uint32_t bitcount)
- {
- while (bitcount--)
- {
- uint32_t prev = s->bits;
- s->bits = (s->bits >> 1) | ((data & 1) << 7);
- data >>= 1;
- if (prev & 1)
- {
- cp_put8(s, s->bits);
- s->bits = 0x80;
- }
- }
- }
- static void cp_put_bitsr(cp_save_png_data_t* s, uint32_t data, uint32_t bitcount)
- {
- while (bitcount--)
- cp_put_bits(s, data >> bitcount, 1);
- }
- static void cp_begin_chunk(cp_save_png_data_t* s, const char* id, uint32_t len)
- {
- cp_put32(s, len);
- s->crc = 0xFFFFFFFF;
- cp_put8(s, (unsigned char)id[0]);
- cp_put8(s, (unsigned char)id[1]);
- cp_put8(s, (unsigned char)id[2]);
- cp_put8(s, (unsigned char)id[3]);
- }
- static void cp_encode_literal(cp_save_png_data_t* s, uint32_t v)
- {
- // Encode a literal/length using the built-in tables.
- // Could do better with a custom table but whatever.
- if (v < 144) cp_put_bitsr(s, 0x030 + v - 0, 8);
- else if (v < 256) cp_put_bitsr(s, 0x190 + v - 144, 9);
- else if (v < 280) cp_put_bitsr(s, 0x000 + v - 256, 7);
- else cp_put_bitsr(s, 0x0c0 + v - 280, 8);
- }
- static void cp_encode_len(cp_save_png_data_t* s, uint32_t code, uint32_t bits, uint32_t len)
- {
- cp_encode_literal(s, code + (len >> bits));
- cp_put_bits(s, len, bits);
- cp_put_bits(s, 0, 5);
- }
- static void cp_end_run(cp_save_png_data_t* s)
- {
- s->runlen--;
- cp_encode_literal(s, s->prev);
- if (s->runlen >= 67) cp_encode_len(s, 277, 4, s->runlen - 67);
- else if (s->runlen >= 35) cp_encode_len(s, 273, 3, s->runlen - 35);
- else if (s->runlen >= 19) cp_encode_len(s, 269, 2, s->runlen - 19);
- else if (s->runlen >= 11) cp_encode_len(s, 265, 1, s->runlen - 11);
- else if (s->runlen >= 3) cp_encode_len(s, 257, 0, s->runlen - 3);
- else while (s->runlen--) cp_encode_literal(s, s->prev);
- }
- static void cp_encode_byte(cp_save_png_data_t *s, uint8_t v)
- {
- cp_update_adler(s, v);
- // Simple RLE compression. We could do better by doing a search
- // to find matches, but this works pretty well TBH.
- if (s->prev == v && s->runlen < 115) s->runlen++;
- else
- {
- if (s->runlen) cp_end_run(s);
- s->prev = v;
- s->runlen = 1;
- }
- }
- static void cp_save_header(cp_save_png_data_t* s, cp_image_t* img)
- {
- const unsigned char* hdr = (const unsigned char*)"\211PNG\r\n\032\n";
- for (int i = 0; i < 8; ++i) {
- cp_put8(s, *hdr++);
- }
- cp_begin_chunk(s, "IHDR", 13);
- cp_put32(s, img->w);
- cp_put32(s, img->h);
- cp_put8(s, 8); // bit depth
- cp_put8(s, 6); // RGBA
- cp_put8(s, 0); // compression (deflate)
- cp_put8(s, 0); // filter (standard)
- cp_put8(s, 0); // interlace off
- cp_put32(s, ~s->crc);
- }
- static void cp_save_data(cp_save_png_data_t* s, cp_image_t* img, long dataPos, long* dataSize)
- {
- cp_begin_chunk(s, "IDAT", 0);
- cp_put8(s, 0x08); // zlib compression method
- cp_put8(s, 0x1D); // zlib compression flags
- cp_put_bits(s, 3, 3); // zlib last block + fixed dictionary
- for (int y = 0; y < img->h; ++y)
- {
- cp_pixel_t *row = &img->pix[y * img->w];
- cp_pixel_t prev = cp_make_pixel_a(0, 0, 0, 0);
- cp_encode_byte(s, 1); // sub filter
- for (int x = 0; x < img->w; ++x)
- {
- cp_encode_byte(s, row[x].r - prev.r);
- cp_encode_byte(s, row[x].g - prev.g);
- cp_encode_byte(s, row[x].b - prev.b);
- cp_encode_byte(s, row[x].a - prev.a);
- prev = row[x];
- }
- }
- cp_end_run(s);
- cp_encode_literal(s, 256); // terminator
- while (s->bits != 0x80) cp_put_bits(s, 0, 1);
- cp_put32(s, s->adler);
- *dataSize = (s->buflen - dataPos) - 8;
- cp_put32(s, ~s->crc);
- }
- cp_saved_png_t cp_save_png_to_memory(const cp_image_t* img)
- {
- cp_saved_png_t result = { 0 };
- cp_save_png_data_t s = { 0 };
- long dataPos, dataSize, fileSize;
- if (!img) return result;
- s.adler = 1;
- s.bits = 0x80;
- s.prev = 0xFFFF;
- s.bufcap = 1024;
- s.buffer = (char*)CUTE_PNG_ALLOC(1024);
- cp_save_header(&s, (cp_image_t*)img);
- dataPos = s.buflen;
- cp_save_data(&s, (cp_image_t*)img, dataPos, &dataSize);
- // End chunk.
- cp_begin_chunk(&s, "IEND", 0);
- cp_put32(&s, ~s.crc);
- // Write back payload size.
- fileSize = s.buflen;
- s.buflen = dataPos;
- cp_put32(&s, dataSize);
- result.size = fileSize;
- result.data = s.buffer;
- return result;
- }
- int cp_save_png(const char* file_name, const cp_image_t* img)
- {
- cp_saved_png_t s;
- long err;
- CUTE_PNG_FILE* fp = CUTE_PNG_FOPEN(file_name, "wb");
- if (!fp) return 1;
- s = cp_save_png_to_memory(img);
- CUTE_PNG_FWRITE(s.data, s.size, 1, fp);
- err = CUTE_PNG_FERROR(fp);
- CUTE_PNG_FCLOSE(fp);
- CUTE_PNG_FREE(s.data);
- return !err;
- }
- typedef struct cp_raw_png_t
- {
- const uint8_t* p;
- const uint8_t* end;
- } cp_raw_png_t;
- static uint32_t cp_make32(const uint8_t* s)
- {
- return (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
- }
- static const uint8_t* cp_chunk(cp_raw_png_t* png, const char* chunk, uint32_t minlen)
- {
- uint32_t len = cp_make32(png->p);
- const uint8_t* start = png->p;
- if (!CUTE_PNG_MEMCMP(start + 4, chunk, 4) && len >= minlen)
- {
- int offset = len + 12;
- if (png->p + offset <= png->end)
- {
- png->p += offset;
- return start + 8;
- }
- }
- return 0;
- }
- static const uint8_t* cp_find(cp_raw_png_t* png, const char* chunk, uint32_t minlen)
- {
- const uint8_t *start;
- while (png->p < png->end)
- {
- uint32_t len = cp_make32(png->p);
- start = png->p;
- png->p += len + 12;
- if (!CUTE_PNG_MEMCMP(start+4, chunk, 4) && len >= minlen && png->p <= png->end)
- return start + 8;
- }
- return 0;
- }
- static int cp_unfilter(int w, int h, int bpp, uint8_t* raw)
- {
- int len = w * bpp;
- uint8_t *prev;
- int x;
- if (h > 0)
- {
- #define FILTER_LOOP_FIRST(A) for (x = bpp; x < len; x++) raw[x] += A; break
- switch (*raw++)
- {
- case 0: break;
- case 1: FILTER_LOOP_FIRST(raw[x - bpp]);
- case 2: break;
- case 3: FILTER_LOOP_FIRST(raw[x - bpp] / 2);
- case 4: FILTER_LOOP_FIRST(cp_paeth(raw[x - bpp], 0, 0));
- default: return 0;
- }
- #undef FILTER_LOOP_FIRST
- }
- prev = raw;
- raw += len;
- for (int y = 1; y < h; y++, prev = raw, raw += len)
- {
- #define FILTER_LOOP(A, B) for (x = 0 ; x < bpp; x++) raw[x] += A; for (; x < len; x++) raw[x] += B; break
- switch (*raw++)
- {
- case 0: break;
- case 1: FILTER_LOOP(0 , raw[x - bpp] );
- case 2: FILTER_LOOP(prev[x] , prev[x]);
- case 3: FILTER_LOOP(prev[x] / 2, (raw[x - bpp] + prev[x]) / 2);
- case 4: FILTER_LOOP(prev[x] , cp_paeth(raw[x - bpp], prev[x], prev[x -bpp]));
- default: return 0;
- }
- #undef FILTER_LOOP
- }
- return 1;
- }
- static void cp_convert(int bpp, int w, int h, uint8_t* src, cp_pixel_t* dst)
- {
- for (int y = 0; y < h; y++)
- {
- // skip filter byte
- src++;
- for (int x = 0; x < w; x++, src += bpp)
- {
- switch (bpp)
- {
- case 1: *dst++ = cp_make_pixel(src[0], src[0], src[0]); break;
- case 2: *dst++ = cp_make_pixel_a(src[0], src[0], src[0], src[1]); break;
- case 3: *dst++ = cp_make_pixel(src[0], src[1], src[2]); break;
- case 4: *dst++ = cp_make_pixel_a(src[0], src[1], src[2], src[3]); break;
- }
- }
- }
- }
- // http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.tRNS
- static uint8_t cp_get_alpha_for_indexed_image(int index, const uint8_t* trns, uint32_t trns_len)
- {
- if (!trns) return 255;
- else if ((uint32_t)index >= trns_len) return 255;
- else return trns[index];
- }
- static void cp_depalette(int w, int h, uint8_t* src, cp_pixel_t* dst, const uint8_t* plte, const uint8_t* trns, uint32_t trns_len)
- {
- for (int y = 0; y < h; ++y)
- {
- // skip filter byte
- ++src;
- for (int x = 0; x < w; ++x, ++src)
- {
- int c = *src;
- uint8_t r = plte[c * 3];
- uint8_t g = plte[c * 3 + 1];
- uint8_t b = plte[c * 3 + 2];
- uint8_t a = cp_get_alpha_for_indexed_image(c, trns, trns_len);
- *dst++ = cp_make_pixel_a(r, g, b, a);
- }
- }
- }
- static uint32_t cp_get_chunk_byte_length(const uint8_t* chunk)
- {
- return cp_make32(chunk - 8);
- }
- static int cp_out_size(cp_image_t* img, int bpp)
- {
- return (img->w + 1) * img->h * bpp;
- }
- cp_image_t cp_load_png_mem(const void* png_data, int png_length)
- {
- const char* sig = "\211PNG\r\n\032\n";
- const uint8_t* ihdr, *first, *plte, *trns;
- int bit_depth, color_type, bpp, w, h, pix_bytes;
- int compression, filter, interlace;
- int datalen, offset;
- uint8_t* out;
- cp_image_t img = { 0 };
- uint8_t* data = 0;
- cp_raw_png_t png;
- png.p = (uint8_t*)png_data;
- png.end = (uint8_t*)png_data + png_length;
- CUTE_PNG_CHECK(!CUTE_PNG_MEMCMP(png.p, sig, 8), "incorrect file signature (is this a png file?)");
- png.p += 8;
- ihdr = cp_chunk(&png, "IHDR", 13);
- CUTE_PNG_CHECK(ihdr, "unable to find IHDR chunk");
- bit_depth = ihdr[8];
- color_type = ihdr[9];
- CUTE_PNG_CHECK(bit_depth == 8, "only bit-depth of 8 is supported");
- switch (color_type)
- {
- case 0: bpp = 1; break; // greyscale
- case 2: bpp = 3; break; // RGB
- case 3: bpp = 1; break; // paletted
- case 4: bpp = 2; break; // grey+alpha
- case 6: bpp = 4; break; // RGBA
- default: CUTE_PNG_CHECK(0, "unknown color type");
- }
- // +1 for filter byte (which is dumb! just stick this at file header...)
- w = cp_make32(ihdr) + 1;
- h = cp_make32(ihdr + 4);
- CUTE_PNG_CHECK(w >= 1, "invalid IHDR chunk found, image width was less than 1");
- CUTE_PNG_CHECK(h >= 1, "invalid IHDR chunk found, image height was less than 1");
- CUTE_PNG_CHECK((int64_t) w * h * sizeof(cp_pixel_t) < INT_MAX, "image too large");
- pix_bytes = w * h * sizeof(cp_pixel_t);
- img.w = w - 1;
- img.h = h;
- img.pix = (cp_pixel_t*)CUTE_PNG_ALLOC(pix_bytes);
- CUTE_PNG_CHECK(img.pix, "unable to allocate raw image space");
- compression = ihdr[10];
- filter = ihdr[11];
- interlace = ihdr[12];
- CUTE_PNG_CHECK(!compression, "only standard compression DEFLATE is supported");
- CUTE_PNG_CHECK(!filter, "only standard adaptive filtering is supported");
- CUTE_PNG_CHECK(!interlace, "interlacing is not supported");
- // PLTE must come before any IDAT chunk
- first = png.p;
- plte = cp_find(&png, "PLTE", 0);
- if (!plte) png.p = first;
- else first = png.p;
- // tRNS can come after PLTE
- trns = cp_find(&png, "tRNS", 0);
- if (!trns) png.p = first;
- else first = png.p;
- // Compute length of the DEFLATE stream through IDAT chunk data sizes
- datalen = 0;
- for (const uint8_t* idat = cp_find(&png, "IDAT", 0); idat; idat = cp_chunk(&png, "IDAT", 0))
- {
- uint32_t len = cp_get_chunk_byte_length(idat);
- datalen += len;
- }
- // Copy in IDAT chunk data sections to form the compressed DEFLATE stream
- png.p = first;
- data = (uint8_t*)CUTE_PNG_ALLOC(datalen);
- offset = 0;
- for (const uint8_t* idat = cp_find(&png, "IDAT", 0); idat; idat = cp_chunk(&png, "IDAT", 0))
- {
- uint32_t len = cp_get_chunk_byte_length(idat);
- CUTE_PNG_MEMCPY(data + offset, idat, len);
- offset += len;
- }
- // check for proper zlib structure in DEFLATE stream
- CUTE_PNG_CHECK(data && datalen >= 6, "corrupt zlib structure in DEFLATE stream");
- CUTE_PNG_CHECK((data[0] & 0x0f) == 0x08, "only zlib compression method (RFC 1950) is supported");
- CUTE_PNG_CHECK((data[0] & 0xf0) <= 0x70, "innapropriate window size detected");
- CUTE_PNG_CHECK(!(data[1] & 0x20), "preset dictionary is present and not supported");
- // check for integer overflow
- CUTE_PNG_CHECK(cp_out_size(&img, 4) >= 1, "invalid image size found");
- CUTE_PNG_CHECK(cp_out_size(&img, bpp) >= 1, "invalid image size found");
- out = (uint8_t*)img.pix + cp_out_size(&img, 4) - cp_out_size(&img, bpp);
- CUTE_PNG_CHECK(cp_inflate(data + 2, datalen - 6, out, pix_bytes), "DEFLATE algorithm failed");
- CUTE_PNG_CHECK(cp_unfilter(img.w, img.h, bpp, out), "invalid filter byte found");
- if (color_type == 3)
- {
- CUTE_PNG_CHECK(plte, "color type of indexed requires a PLTE chunk");
- uint32_t trns_len = trns ? cp_get_chunk_byte_length(trns) : 0;
- cp_depalette(img.w, img.h, out, img.pix, plte, trns, trns_len);
- }
- else cp_convert(bpp, img.w, img.h, out, img.pix);
- CUTE_PNG_FREE(data);
- return img;
- cp_err:
- CUTE_PNG_FREE(data);
- CUTE_PNG_FREE(img.pix);
- img.pix = 0;
- return img;
- }
- cp_image_t cp_load_blank(int w, int h)
- {
- cp_image_t img;
- img.w = w;
- img.h = h;
- img.pix = (cp_pixel_t*)CUTE_PNG_ALLOC(w * h * sizeof(cp_pixel_t));
- return img;
- }
- cp_image_t cp_load_png(const char *file_name)
- {
- cp_image_t img = { 0 };
- int len;
- void* data = cp_read_file_to_memory(file_name, &len);
- if (!data) return img;
- img = cp_load_png_mem(data, len);
- CUTE_PNG_FREE(data);
- return img;
- }
- void cp_free_png(cp_image_t* img)
- {
- CUTE_PNG_FREE(img->pix);
- img->pix = 0;
- img->w = img->h = 0;
- }
- void cp_flip_image_horizontal(cp_image_t* img)
- {
- cp_pixel_t* pix = img->pix;
- int w = img->w;
- int h = img->h;
- int flips = h / 2;
- for (int i = 0; i < flips; ++i)
- {
- cp_pixel_t* a = pix + w * i;
- cp_pixel_t* b = pix + w * (h - i - 1);
- for (int j = 0; j < w; ++j)
- {
- cp_pixel_t t = *a;
- *a = *b;
- *b = t;
- ++a;
- ++b;
- }
- }
- }
- void cp_load_png_wh(const void* png_data, int png_length, int* w_out, int* h_out)
- {
- const char* sig = "\211PNG\r\n\032\n";
- const uint8_t* ihdr;
- cp_raw_png_t png;
- int w, h;
- png.p = (uint8_t*)png_data;
- png.end = (uint8_t*)png_data + png_length;
- if (w_out) *w_out = 0;
- if (h_out) *h_out = 0;
- CUTE_PNG_CHECK(!CUTE_PNG_MEMCMP(png.p, sig, 8), "incorrect file signature (is this a png file?)");
- png.p += 8;
- ihdr = cp_chunk(&png, "IHDR", 13);
- CUTE_PNG_CHECK(ihdr, "unable to find IHDR chunk");
- // +1 for filter byte (which is dumb! just stick this at file header...)
- w = cp_make32(ihdr) + 1;
- h = cp_make32(ihdr + 4);
- if (w_out) *w_out = w - 1;
- if (h_out) *h_out = h;
- cp_err:;
- }
- cp_indexed_image_t cp_load_indexed_png(const char* file_name)
- {
- cp_indexed_image_t img = { 0 };
- int len;
- void* data = cp_read_file_to_memory(file_name, &len);
- if (!data) return img;
- img = cp_load_indexed_png_mem(data, len);
- CUTE_PNG_FREE(data);
- return img;
- }
- static void cp_unpack_indexed_rows(int w, int h, uint8_t* src, uint8_t* dst)
- {
- for (int y = 0; y < h; ++y)
- {
- // skip filter byte
- ++src;
- for (int x = 0; x < w; ++x, ++src)
- {
- *dst++ = *src;
- }
- }
- }
- void cp_unpack_palette(cp_pixel_t* dst, const uint8_t* plte, int plte_len, const uint8_t* trns, int trns_len)
- {
- for (int i = 0; i < plte_len * 3; i += 3)
- {
- unsigned char r = plte[i];
- unsigned char g = plte[i + 1];
- unsigned char b = plte[i + 2];
- unsigned char a = cp_get_alpha_for_indexed_image(i / 3, trns, trns_len);
- cp_pixel_t p = cp_make_pixel_a(r, g, b, a);
- *dst++ = p;
- }
- }
- cp_indexed_image_t cp_load_indexed_png_mem(const void *png_data, int png_length)
- {
- const char* sig = "\211PNG\r\n\032\n";
- const uint8_t* ihdr, *first, *plte, *trns;
- int bit_depth, color_type, bpp, w, h, pix_bytes;
- int compression, filter, interlace;
- int datalen, offset;
- int plte_len;
- uint8_t* out;
- cp_indexed_image_t img = { 0 };
- uint8_t* data = 0;
- cp_raw_png_t png;
- png.p = (uint8_t*)png_data;
- png.end = (uint8_t*)png_data + png_length;
- CUTE_PNG_CHECK(!CUTE_PNG_MEMCMP(png.p, sig, 8), "incorrect file signature (is this a png file?)");
- png.p += 8;
- ihdr = cp_chunk(&png, "IHDR", 13);
- CUTE_PNG_CHECK(ihdr, "unable to find IHDR chunk");
- bit_depth = ihdr[8];
- color_type = ihdr[9];
- bpp = 1; // bytes per pixel
- CUTE_PNG_CHECK(bit_depth == 8, "only bit-depth of 8 is supported");
- CUTE_PNG_CHECK(color_type == 3, "only indexed png images (images with a palette) are valid for cp_load_indexed_png_mem");
- // +1 for filter byte (which is dumb! just stick this at file header...)
- w = cp_make32(ihdr) + 1;
- h = cp_make32(ihdr + 4);
- CUTE_PNG_CHECK((int64_t) w * h * sizeof(uint8_t) < INT_MAX, "image too large");
- pix_bytes = w * h * sizeof(uint8_t);
- img.w = w - 1;
- img.h = h;
- img.pix = (uint8_t*)CUTE_PNG_ALLOC(pix_bytes);
- CUTE_PNG_CHECK(img.pix, "unable to allocate raw image space");
- compression = ihdr[10];
- filter = ihdr[11];
- interlace = ihdr[12];
- CUTE_PNG_CHECK(!compression, "only standard compression DEFLATE is supported");
- CUTE_PNG_CHECK(!filter, "only standard adaptive filtering is supported");
- CUTE_PNG_CHECK(!interlace, "interlacing is not supported");
- // PLTE must come before any IDAT chunk
- first = png.p;
- plte = cp_find(&png, "PLTE", 0);
- if (!plte) png.p = first;
- else first = png.p;
- // tRNS can come after PLTE
- trns = cp_find(&png, "tRNS", 0);
- if (!trns) png.p = first;
- else first = png.p;
- // Compute length of the DEFLATE stream through IDAT chunk data sizes
- datalen = 0;
- for (const uint8_t* idat = cp_find(&png, "IDAT", 0); idat; idat = cp_chunk(&png, "IDAT", 0))
- {
- uint32_t len = cp_get_chunk_byte_length(idat);
- datalen += len;
- }
- // Copy in IDAT chunk data sections to form the compressed DEFLATE stream
- png.p = first;
- data = (uint8_t*)CUTE_PNG_ALLOC(datalen);
- offset = 0;
- for (const uint8_t* idat = cp_find(&png, "IDAT", 0); idat; idat = cp_chunk(&png, "IDAT", 0))
- {
- uint32_t len = cp_get_chunk_byte_length(idat);
- CUTE_PNG_MEMCPY(data + offset, idat, len);
- offset += len;
- }
- // check for proper zlib structure in DEFLATE stream
- CUTE_PNG_CHECK(data && datalen >= 6, "corrupt zlib structure in DEFLATE stream");
- CUTE_PNG_CHECK((data[0] & 0x0f) == 0x08, "only zlib compression method (RFC 1950) is supported");
- CUTE_PNG_CHECK((data[0] & 0xf0) <= 0x70, "innapropriate window size detected");
- CUTE_PNG_CHECK(!(data[1] & 0x20), "preset dictionary is present and not supported");
- out = img.pix;
- CUTE_PNG_CHECK(cp_inflate(data + 2, datalen - 6, out, pix_bytes), "DEFLATE algorithm failed");
- CUTE_PNG_CHECK(cp_unfilter(img.w, img.h, bpp, out), "invalid filter byte found");
- cp_unpack_indexed_rows(img.w, img.h, out, img.pix);
- plte_len = cp_get_chunk_byte_length(plte) / 3;
- cp_unpack_palette(img.palette, plte, plte_len, trns, cp_get_chunk_byte_length(trns));
- img.palette_len = (uint8_t)plte_len;
- CUTE_PNG_FREE(data);
- return img;
- cp_err:
- CUTE_PNG_FREE(data);
- CUTE_PNG_FREE(img.pix);
- img.pix = 0;
- return img;
- }
- void cp_free_indexed_png(cp_indexed_image_t* img)
- {
- CUTE_PNG_FREE(img->pix);
- img->pix = 0;
- img->w = img->h = 0;
- }
- cp_image_t cp_depallete_indexed_image(cp_indexed_image_t* img)
- {
- cp_image_t out = { 0 };
- out.w = img->w;
- out.h = img->h;
- out.pix = (cp_pixel_t*)CUTE_PNG_ALLOC(sizeof(cp_pixel_t) * out.w * out.h);
- cp_pixel_t* dst = out.pix;
- uint8_t* src = img->pix;
- for (int y = 0; y < out.h; ++y)
- {
- for (int x = 0; x < out.w; ++x)
- {
- int index = *src++;
- cp_pixel_t p = img->palette[index];
- *dst++ = p;
- }
- }
- return out;
- }
- typedef struct cp_v2i_t
- {
- int x;
- int y;
- } cp_v2i_t;
- typedef struct cp_integer_image_t
- {
- int img_index;
- cp_v2i_t size;
- cp_v2i_t min;
- cp_v2i_t max;
- int fit;
- } cp_integer_image_t;
- static cp_v2i_t cp_v2i(int x, int y)
- {
- cp_v2i_t v;
- v.x = x;
- v.y = y;
- return v;
- }
- static cp_v2i_t cp_sub(cp_v2i_t a, cp_v2i_t b)
- {
- cp_v2i_t v;
- v.x = a.x - b.x;
- v.y = a.y - b.y;
- return v;
- }
- static cp_v2i_t cp_add(cp_v2i_t a, cp_v2i_t b)
- {
- cp_v2i_t v;
- v.x = a.x + b.x;
- v.y = a.y + b.y;
- return v;
- }
- typedef struct cp_atlas_node_t
- {
- cp_v2i_t size;
- cp_v2i_t min;
- cp_v2i_t max;
- } cp_atlas_node_t;
- static cp_atlas_node_t* cp_best_fit(int sp, const cp_image_t* png, cp_atlas_node_t* nodes)
- {
- int bestVolume = INT_MAX;
- cp_atlas_node_t *best_node = 0;
- int width = png->w;
- int height = png->h;
- int png_volume = width * height;
- for (int i = 0; i < sp; ++i)
- {
- cp_atlas_node_t *node = nodes + i;
- int can_contain = node->size.x >= width && node->size.y >= height;
- if (can_contain)
- {
- int node_volume = node->size.x * node->size.y;
- if (node_volume == png_volume) return node;
- if (node_volume < bestVolume)
- {
- bestVolume = node_volume;
- best_node = node;
- }
- }
- }
- return best_node;
- }
- static int cp_perimeter_pred(cp_integer_image_t* a, cp_integer_image_t* b)
- {
- int perimeterA = 2 * (a->size.x + a->size.y);
- int perimeterB = 2 * (b->size.x + b->size.y);
- return perimeterB < perimeterA;
- }
- void cp_premultiply(cp_image_t* img)
- {
- int w = img->w;
- int h = img->h;
- int stride = w * sizeof(cp_pixel_t);
- uint8_t* data = (uint8_t*)img->pix;
- for(int i = 0; i < (int)stride * h; i += sizeof(cp_pixel_t))
- {
- float a = (float)data[i + 3] / 255.0f;
- float r = (float)data[i + 0] / 255.0f;
- float g = (float)data[i + 1] / 255.0f;
- float b = (float)data[i + 2] / 255.0f;
- r *= a;
- g *= a;
- b *= a;
- data[i + 0] = (uint8_t)(r * 255.0f);
- data[i + 1] = (uint8_t)(g * 255.0f);
- data[i + 2] = (uint8_t)(b * 255.0f);
- }
- }
- static void cp_qsort(cp_integer_image_t* items, int count)
- {
- if (count <= 1) return;
- cp_integer_image_t pivot = items[count - 1];
- int low = 0;
- for (int i = 0; i < count - 1; ++i)
- {
- if (cp_perimeter_pred(items + i, &pivot))
- {
- cp_integer_image_t tmp = items[i];
- items[i] = items[low];
- items[low] = tmp;
- low++;
- }
- }
- items[count - 1] = items[low];
- items[low] = pivot;
- cp_qsort(items, low);
- cp_qsort(items + low + 1, count - 1 - low);
- }
- static void cp_write_pixel(char* mem, long color) {
- mem[0] = (color >> 24) & 0xFF;
- mem[1] = (color >> 16) & 0xFF;
- mem[2] = (color >> 8) & 0xFF;
- mem[3] = (color >> 0) & 0xFF;
- }
- cp_image_t cp_make_atlas(int atlas_width, int atlas_height, const cp_image_t* pngs, int png_count, cp_atlas_image_t* imgs_out)
- {
- float w0, h0, div, wTol, hTol;
- int atlas_image_size, atlas_stride, sp;
- void* atlas_pixels = 0;
- int atlas_node_capacity = png_count * 2;
- cp_image_t atlas_image;
- cp_integer_image_t* images = 0;
- cp_atlas_node_t* nodes = 0;
- atlas_image.w = atlas_width;
- atlas_image.h = atlas_height;
- atlas_image.pix = 0;
- CUTE_PNG_CHECK(pngs, "pngs array was NULL");
- CUTE_PNG_CHECK(imgs_out, "imgs_out array was NULL");
- images = (cp_integer_image_t*)CUTE_PNG_ALLOCA(sizeof(cp_integer_image_t) * png_count);
- nodes = (cp_atlas_node_t*)CUTE_PNG_ALLOC(sizeof(cp_atlas_node_t) * atlas_node_capacity);
- CUTE_PNG_CHECK(images, "out of mem");
- CUTE_PNG_CHECK(nodes, "out of mem");
- for (int i = 0; i < png_count; ++i)
- {
- const cp_image_t* png = pngs + i;
- cp_integer_image_t* image = images + i;
- image->fit = 0;
- image->size = cp_v2i(png->w, png->h);
- image->img_index = i;
- }
- // Sort PNGs from largest to smallest
- cp_qsort(images, png_count);
- // stack pointer, the stack is the nodes array which we will
- // allocate nodes from as necessary.
- sp = 1;
- nodes[0].min = cp_v2i(0, 0);
- nodes[0].max = cp_v2i(atlas_width, atlas_height);
- nodes[0].size = cp_v2i(atlas_width, atlas_height);
- // Nodes represent empty space in the atlas. Placing a texture into the
- // atlas involves splitting a node into two smaller pieces (or, if a
- // perfect fit is found, deleting the node).
- for (int i = 0; i < png_count; ++i)
- {
- cp_integer_image_t* image = images + i;
- const cp_image_t* png = pngs + image->img_index;
- int width = png->w;
- int height = png->h;
- cp_atlas_node_t *best_fit = cp_best_fit(sp, png, nodes);
- if (CUTE_PNG_ATLAS_MUST_FIT) CUTE_PNG_CHECK(best_fit, "Not enough room to place image in atlas.");
- else if (!best_fit)
- {
- image->fit = 0;
- continue;
- }
- image->min = best_fit->min;
- image->max = cp_add(image->min, image->size);
- if (best_fit->size.x == width && best_fit->size.y == height)
- {
- cp_atlas_node_t* last_node = nodes + --sp;
- *best_fit = *last_node;
- image->fit = 1;
- continue;
- }
- image->fit = 1;
- if (sp == atlas_node_capacity)
- {
- int new_capacity = atlas_node_capacity * 2;
- cp_atlas_node_t* new_nodes = (cp_atlas_node_t*)CUTE_PNG_ALLOC(sizeof(cp_atlas_node_t) * new_capacity);
- CUTE_PNG_CHECK(new_nodes, "out of mem");
- CUTE_PNG_MEMCPY(new_nodes, nodes, sizeof(cp_atlas_node_t) * sp);
- // best_fit became a dangling pointer, so relocate it
- best_fit = new_nodes + (best_fit - nodes);
- CUTE_PNG_FREE(nodes);
- nodes = new_nodes;
- atlas_node_capacity = new_capacity;
- }
- cp_atlas_node_t* new_node = nodes + sp++;
- new_node->min = best_fit->min;
- // Split bestFit along x or y, whichever minimizes
- // fragmentation of empty space
- cp_v2i_t d = cp_sub(best_fit->size, cp_v2i(width, height));
- if (d.x < d.y)
- {
- new_node->size.x = d.x;
- new_node->size.y = height;
- new_node->min.x += width;
- best_fit->size.y = d.y;
- best_fit->min.y += height;
- }
- else
- {
- new_node->size.x = width;
- new_node->size.y = d.y;
- new_node->min.y += height;
- best_fit->size.x = d.x;
- best_fit->min.x += width;
- }
- new_node->max = cp_add(new_node->min, new_node->size);
- }
- // Write the final atlas image, use CUTE_PNG_ATLAS_EMPTY_COLOR as base color
- atlas_stride = atlas_width * sizeof(cp_pixel_t);
- atlas_image_size = atlas_width * atlas_height * sizeof(cp_pixel_t);
- atlas_pixels = CUTE_PNG_ALLOC(atlas_image_size);
- CUTE_PNG_CHECK(atlas_pixels, "out of mem");
-
- for(int i = 0; i < atlas_image_size; i += sizeof(cp_pixel_t)) {
- cp_write_pixel((char*)atlas_pixels + i, CUTE_PNG_ATLAS_EMPTY_COLOR);
- }
- for (int i = 0; i < png_count; ++i)
- {
- cp_integer_image_t* image = images + i;
- if (image->fit)
- {
- const cp_image_t* png = pngs + image->img_index;
- char* pixels = (char*)png->pix;
- cp_v2i_t min = image->min;
- cp_v2i_t max = image->max;
- int atlas_offset = min.x * sizeof(cp_pixel_t);
- int tex_stride = png->w * sizeof(cp_pixel_t);
- for (int row = min.y, y = 0; row < max.y; ++row, ++y)
- {
- void* row_ptr = (char*)atlas_pixels + (row * atlas_stride + atlas_offset);
- CUTE_PNG_MEMCPY(row_ptr, pixels + y * tex_stride, tex_stride);
- }
- }
- }
- atlas_image.pix = (cp_pixel_t*)atlas_pixels;
- // squeeze UVs inward by 128th of a pixel
- // this prevents atlas bleeding. tune as necessary for good results.
- w0 = 1.0f / (float)(atlas_width);
- h0 = 1.0f / (float)(atlas_height);
- div = 1.0f / 128.0f;
- wTol = w0 * div;
- hTol = h0 * div;
- for (int i = 0; i < png_count; ++i)
- {
- cp_integer_image_t* image = images + i;
- cp_atlas_image_t* img_out = imgs_out + i;
- img_out->img_index = image->img_index;
- img_out->w = image->size.x;
- img_out->h = image->size.y;
- img_out->fit = image->fit;
- if (image->fit)
- {
- cp_v2i_t min = image->min;
- cp_v2i_t max = image->max;
- float min_x = (float)min.x * w0 + wTol;
- float min_y = (float)min.y * h0 + hTol;
- float max_x = (float)max.x * w0 - wTol;
- float max_y = (float)max.y * h0 - hTol;
- // flip image on y axis
- if (CUTE_PNG_ATLAS_FLIP_Y_AXIS_FOR_UV)
- {
- float tmp = min_y;
- min_y = max_y;
- max_y = tmp;
- }
- img_out->minx = min_x;
- img_out->miny = min_y;
- img_out->maxx = max_x;
- img_out->maxy = max_y;
- }
- }
- CUTE_PNG_FREE(nodes);
- return atlas_image;
- cp_err:
- CUTE_PNG_FREE(atlas_pixels);
- CUTE_PNG_FREE(nodes);
- atlas_image.pix = 0;
- return atlas_image;
- }
- int cp_default_save_atlas(const char* out_path_image, const char* out_path_atlas_txt, const cp_image_t* atlas, const cp_atlas_image_t* imgs, int img_count, const char** names)
- {
- CUTE_PNG_FILE* fp = CUTE_PNG_FOPEN(out_path_atlas_txt, "wt");
- CUTE_PNG_CHECK(fp, "unable to open out_path_atlas_txt in cp_default_save_atlas");
- CUTE_PNG_FPRINTF(fp, "%s\n%d\n\n", out_path_image, img_count);
- for (int i = 0; i < img_count; ++i)
- {
- const cp_atlas_image_t* image = imgs + i;
- const char* name = names ? names[image->img_index] : 0;
- if (image->fit)
- {
- int width = image->w;
- int height = image->h;
- float min_x = image->minx;
- float min_y = image->miny;
- float max_x = image->maxx;
- float max_y = image->maxy;
- if (name) CUTE_PNG_FPRINTF(fp, "{ \"%s\", w = %d, h = %d, u = { %.10f, %.10f }, v = { %.10f, %.10f } }\n", name, width, height, min_x, min_y, max_x, max_y);
- else CUTE_PNG_FPRINTF(fp, "{ w = %d, h = %d, u = { %.10f, %.10f }, v = { %.10f, %.10f } }\n", width, height, min_x, min_y, max_x, max_y);
- }
- }
- // Save atlas image PNG to disk
- CUTE_PNG_CHECK(cp_save_png(out_path_image, atlas), "failed to save atlas image to disk");
- cp_err:
- CUTE_PNG_FCLOSE(fp);
- return 0;
- }
- #endif // CUTE_PNG_IMPLEMENTATION_ONCE
- #endif // CUTE_PNG_IMPLEMENTATION
- /*
- ------------------------------------------------------------------------------
- This software is available under 2 licenses - you may choose the one you like.
- ------------------------------------------------------------------------------
- ALTERNATIVE A - zlib license
- Copyright (c) 2019 Randy Gaul http://www.randygaul.net
- 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.
- ------------------------------------------------------------------------------
- ALTERNATIVE B - Public Domain (www.unlicense.org)
- This is free and unencumbered software released into the public domain.
- Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
- software, either in source code form or as a compiled binary, for any purpose,
- commercial or non-commercial, and by any means.
- In jurisdictions that recognize copyright laws, the author or authors of this
- software dedicate any and all copyright interest in the software to the public
- domain. We make this dedication for the benefit of the public at large and to
- the detriment of our heirs and successors. We intend this dedication to be an
- overt act of relinquishment in perpetuity of all present and future rights to
- this software under copyright law.
- 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 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.
- ------------------------------------------------------------------------------
- */
|