Skip to content

Instantly share code, notes, and snippets.

@MikuAuahDark
Created October 17, 2015 14:02
Show Gist options
  • Save MikuAuahDark/0a716b3e7bad6e2c9542 to your computer and use it in GitHub Desktop.
Save MikuAuahDark/0a716b3e7bad6e2c9542 to your computer and use it in GitHub Desktop.
Crop PNG from 0x0 to specificed pixel
#include <iostream>
#include <string>
#include <cstdarg>
#include <png.h>
using namespace std;
// Prints message in stderr and exit program in release build.
// In debug build, it trigger debug breakpoint instead.
void failexit(const char* file,const char* msg) {
if(file)
cerr << "Cannot process " << file << ": ";
cerr << msg << endl;
#ifdef _DEBUG
__debugbreak();
#endif
exit(-1);
}
// Same as failexit, except second argument is formatted string like printf instead.
void failexitf(const char* file,const char* msg,...) {
if(file)
cerr << "Cannot process " << file << ": ";
va_list a;
va_start(a,msg);
vfprintf(stderr,msg,a);
va_end(a);
cerr << endl;
#ifdef _DEBUG
__debugbreak();
#endif
exit(-1);
}
int main(int argc,char** argv) {
png_byte** buffer1; // Reading
png_byte** buffer2; // Writing
png_byte* tmpbuf;
if(argc<4) {
cout << "Usage: " << argv[0] << " <in png> <crop x> <crop y> [out png=<in png>]" << endl;
cout << "Crops PNG from 0x0 to <crop x>x<crop y>" << endl;
cout << "Starting crop position can be changed with PNGCROP_SX and PNGCROP_SY environment variables" << endl;
return 1;
}
int crop_x,crop_y;
int sc_x,sc_y;
// get crop pixel
try {
crop_x=stoi(string(argv[2]));
try {
crop_y=stoi(string(argv[3]));
} catch(...) {
failexit(nullptr,"Argument 3 is not a number");
}
} catch(...) {
failexit(nullptr,"Argument 2 is not a number");
}
// get start crop pixel
{
char* temp;
try {
temp=getenv("PNGCROP_SX");
if(temp==nullptr) sc_x=0;
else sc_x=stoi(string(temp));
try {
temp=getenv("PNGCROP_SY");
if(temp==nullptr) sc_y=0;
else sc_y=stoi(string(temp));
} catch(...) {
failexit(nullptr,"PNGCROP_SY environment variable is not a number");
}
} catch(...) {
failexit(nullptr,"PNGCROP_SX environment variable is not a number");
}
}
int rx=crop_x-sc_x;
int ry=crop_y-sc_y;
// check values
if(crop_x<0 || crop_y<0)
failexitf(nullptr,"Argument 2 or 3 is negative. argv[2]=%d, argv[3]=%d",crop_x,crop_y);
if(sc_x<0 || sc_y<0)
failexitf(nullptr,"PNGCROP_SX(%d) or PNGCROP_SY(%d) environment variable is negative.",sc_x,sc_y);
if(rx==0 || ry==0)
failexit(nullptr,"Nothing to crop or start crop position is higher than end crop position");
char* file_out=argv[1];
if(argc>=5)
file_out=argv[4];
FILE* f=fopen(argv[1],"rb");
if(f==nullptr) {
cerr << "Cannot open " << argv[1] << ": " << strerror(errno) << endl;
return -1;
}
tmpbuf=new png_byte[8];
fread(tmpbuf,1,8,f);
if(png_sig_cmp(tmpbuf,0,8))
failexit(argv[1],"Invalid PNG");
delete[] tmpbuf;
png_struct* png_ptr=png_create_read_struct(PNG_LIBPNG_VER_STRING,nullptr,nullptr,nullptr);
if(png_ptr==nullptr)
failexit(nullptr,"Cannot create PNG reader structure");
png_info* info_ptr=png_create_info_struct(png_ptr);
if(info_ptr==nullptr)
failexit(nullptr,"Cannot create PNG info structure");
if(setjmp(png_jmpbuf(png_ptr)))
failexit(argv[1],"Error initializing input/output");
png_init_io(png_ptr,f);
png_set_sig_bytes(png_ptr,8);
png_read_info(png_ptr,info_ptr);
unsigned int width=png_get_image_width(png_ptr,info_ptr);
unsigned int height=png_get_image_height(png_ptr,info_ptr);
unsigned char color_type=png_get_color_type(png_ptr,info_ptr);
unsigned char bit_depth=png_get_bit_depth(png_ptr,info_ptr);
int number_of_passes=png_set_interlace_handling(png_ptr);
// Make sure that our cropped pixel doesn't exceed width and height of the image
if(abs(crop_x-sc_x)>=width || abs(crop_y-sc_y)>=height)
failexitf(argv[1],"Crop rectangle exceeded width or height. width=%d, height=%d, crop_x=%d, crop_y=%d, sc_x=%d, sc_y=%d",width,height,crop_x,crop_y,sc_x,sc_y);
// read png
int y=(-1);
if(setjmp(png_jmpbuf(png_ptr)))
failexitf(argv[1],"Error reading at y=%d",y);
try {
buffer1=new png_byte*[height];
for(y=0;y<height;y++)
buffer1[y]=new png_byte[png_get_rowbytes(png_ptr,info_ptr)];
} catch(exception e) {
failexit(nullptr,e.what());
}
png_read_image(png_ptr,buffer1);
// Let's start cropping.
int multipler=0;
if(color_type==PNG_COLOR_TYPE_RGB) multipler=3;
else if(color_type==PNG_COLOR_TYPE_RGBA) multipler=4;
else failexit(argv[1],"Unsupported color type");
try {
buffer2=new png_byte*[abs(ry)];
for(y=0;y<ry;y++) {
buffer2[y]=new png_byte[abs(rx)*multipler];
memcpy(buffer2[y],buffer1[y+sc_y]+sc_x*multipler,rx*multipler);
}
} catch(exception e) {
failexit(nullptr,e.what());
}
// Delete & reuse some things
png_destroy_read_struct(&png_ptr,&info_ptr,nullptr);
fclose(f);
f=fopen(file_out,"wb");
if(f==nullptr) {
cerr << "Cannot open " << file_out << ": " <<strerror(errno) << endl;
cerr << "Writing to stdout instead." << endl;
f=stdout;
}
png_ptr=png_create_write_struct(PNG_LIBPNG_VER_STRING,nullptr,nullptr,nullptr);
if(png_ptr==nullptr)
failexit(nullptr,"Cannot create PNG writer structure");
info_ptr=png_create_info_struct(png_ptr);
if(info_ptr==nullptr)
failexit(nullptr,"Cannot create PNG info structure");
if(setjmp(png_jmpbuf(png_ptr)))
failexit(file_out,"Error initializing input/output");
png_init_io(png_ptr,f);
png_set_IHDR(png_ptr,info_ptr,rx,ry,bit_depth,color_type,PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT);
png_write_info(png_ptr,info_ptr);
if(setjmp(png_jmpbuf(png_ptr)))
failexit(file_out,"Error writing");
png_write_image(png_ptr,buffer2);
if(setjmp(png_jmpbuf(png_ptr)))
failexit(file_out,"Error writing at end");
// End
png_write_end(png_ptr,info_ptr);
png_destroy_write_struct(&png_ptr,&info_ptr);
fclose(f);
// Free memory
for(y=0;y<height;y++)
delete[] buffer1[y];
delete[] buffer1;
for(y=0;y<ry;y++)
delete[] buffer2[y];
delete[] buffer2;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment