diff --git a/README.md b/README.md index 83071cd..947fddc 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ Random single-file programs I've written in my spare time for small tasks. * **bleep:** I got bored and decided to write a pc speaker music program. * **ckmextract:** Extracts ESP and BSA from Skyrim steam workshop mod archives. +* **cropng:** Crops PNGs and applies grAb offsets (while respecting existing ones). * **cube2enviro:** A simple GL 4.4 program. Loads a cubemap and draws a flattened hemisphere environment map that can be used in Unreal. * **ddsinfo:** Shows contents of a DDS header. * **dood:** Reads an ENDOOM lump and mirrors every word "down the middle". @@ -39,4 +40,4 @@ Random single-file programs I've written in my spare time for small tasks. * **withhands:** Talk like W.D. Gaster. * **zimagekver:** Quick program to extract version info from an ARM Linux kernel image. -All programs and code here are under the MIT license. \ No newline at end of file +All programs and code here are under the MIT license. diff --git a/cropng.c b/cropng.c new file mode 100644 index 0000000..36780a1 --- /dev/null +++ b/cropng.c @@ -0,0 +1,304 @@ +#define _DEFAULT_SOURCE +#include +#include +#include +#include +#include +#include +#include + +const char* fmtname( int bd, int col ) +{ + if ( col == PNG_COLOR_TYPE_PALETTE ) + return " paletted"; + else if ( col == PNG_COLOR_TYPE_GRAY_ALPHA ) + return (bd==16)?" 16bpc grayscale":" grayscale"; + else if ( col == PNG_COLOR_TYPE_RGB_ALPHA ) + return (bd==16)?" 16bpc rgb":" rgb"; + return ""; +} + +int loadpng( const char *fname, uint8_t **idata, uint32_t *w, uint32_t *h, int32_t *x, int32_t *y, int *pxsize, png_color **plte, int *nplt, uint8_t **trns, int *ntrns, uint8_t *bd, uint8_t *col ) +{ + if ( !fname ) return 0; + png_structp pngp; + png_infop infp; + unsigned int sread = 0; + FILE *pf; + if ( !(pf = fopen(fname,"rb")) ) + { + fprintf(stderr," Cannot open for reading: %s.\n",strerror(errno)); + return 0; + } + pngp = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0); + if ( !pngp ) + { + fclose(pf); + return 0; + } + infp = png_create_info_struct(pngp); + if ( !infp ) + { + fclose(pf); + png_destroy_read_struct(&pngp,0,0); + return 0; + } + if ( setjmp(png_jmpbuf(pngp)) ) + { + png_destroy_read_struct(&pngp,&infp,0); + fclose(pf); + return 0; + } + png_init_io(pngp,pf); + png_set_sig_bytes(pngp,sread); + png_set_keep_unknown_chunks(pngp,PNG_HANDLE_CHUNK_ALWAYS,(uint8_t*)"grAb\0alPh\0",2); + png_read_png(pngp,infp,PNG_TRANSFORM_PACKING,0); + *w = png_get_image_width(pngp,infp); + *h = png_get_image_height(pngp,infp); + *bd = png_get_bit_depth(pngp,infp); + if ( (*bd != 8) && (*bd != 16) ) + { + fprintf(stderr," Unsupported bit depth of '%d' (only 8bpc and 16bpc supported).\n",*bd); + png_destroy_read_struct(&pngp,&infp,0); + fclose(pf); + return 0; + } + *col = png_get_color_type(pngp,infp); + if ( (*col == PNG_COLOR_TYPE_GRAY) || (*col == PNG_COLOR_TYPE_RGB) ) + { + fprintf(stderr," Image has no alpha channel.\n"); + png_destroy_read_struct(&pngp,&infp,0); + fclose(pf); + return 0; + } + else if ( *col == PNG_COLOR_TYPE_PALETTE ) + { + *pxsize = 1; + if ( !png_get_valid(pngp,infp,PNG_INFO_tRNS) ) + { + fprintf(stderr," Paletted image has no tRNS chunk.\n"); + png_destroy_read_struct(&pngp,&infp,0); + fclose(pf); + return 0; + } + // for whatever reason these arrays have to be allocated and memcpy'd + // otherwise they'll be corrupted when we use them later + // + // there's no logical explanation for this + // the manual doesn't mention anything about it + uint8_t *trns_local; + png_get_tRNS(pngp,infp,&trns_local,ntrns,0); + *trns = malloc(*ntrns); + memcpy(*trns,trns_local,*ntrns); + png_color *plte_local; + png_get_PLTE(pngp,infp,&plte_local,nplt); + *plte = malloc(sizeof(png_color)*(*nplt)); + memcpy(*plte,plte_local,sizeof(png_color)*(*nplt)); + } + else if ( *col == PNG_COLOR_TYPE_GRAY_ALPHA ) *pxsize = (*bd>>3)*2; + else if ( *col == PNG_COLOR_TYPE_RGB_ALPHA ) *pxsize = (*bd>>3)*4; + else + { + fprintf(stderr," Image has unrecognized color type '%d'.\n",*col); + png_destroy_read_struct(&pngp,&infp,0); + fclose(pf); + return 0; + } + png_unknown_chunkp unk; + int nunk = png_get_unknown_chunks(pngp,infp,&unk); + for ( int i=0; i cropw ) cropw = cx; + if ( cy > croph ) croph = cy; + } + printf(" Crop region: (%u,%u) (%u,%u).\n",cropx,cropy,cropw,croph); + cropw -= cropx-1; + croph -= cropy-1; + uint8_t *odata = calloc(cropw*croph,pxsize); + uint32_t ow = cropw, oh = croph; + int32_t ox = x-cropx, oy = y-cropy; + for ( uint32_t cy=0; cy ...\n" +"\n" +"cropng will trim excess transparency from PNG images while updating grAb\n" +"offsets to compensate (existing ones will be re-used as a base)\n" +"\n" +"files are updated in-place, but a backup will be kept in case errors happen.\n" +"\n" +"cropng supports paletted, color and grayscale PNGs at both 8bpc and 16bpc.\n" +"it cannot handle files with alPh chunks, nor bit depths other than 8 and 16.\n" +"\n" +"note: any extra chunks such as comments or exif data will be stripped.\n" + ); + return 1; + } + for ( int i=1; i