/* Textorizer.c: vectorises a picture into an SVG using text strings * see: http://www.w3.org/People/maxf/textorizer/ * Copyright Max Froumentin 2006 * This software is distributed under the * W3C® SOFTWARE NOTICE AND LICENSE: * http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 */ #include #include #include #include #include int NbStrokes=1000; char *InFileName="in.png",*OutFileName="out.svg",*WordFileName="words.txt"; char *Words[10000]; int NbWords; /* Sobel convolution filter */ long Sx[3][3] = {{-1,0,1},{-2,0,2},{-1, 0, 1}}; long Sy[3][3] = {{-1,-2,-1},{ 0,0,0},{1,2,1}}; struct pixel { unsigned char r,g,b,a; }; long Width, Height; struct pixel *Pixels; #ifndef png_jmpbuf # define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) #endif #define PNG_BYTES_TO_CHECK 8 int check_if_png(char *file_name, FILE **fp) { unsigned char buf[PNG_BYTES_TO_CHECK]; /* Open the prospective PNG file. */ if ((*fp = fopen(file_name, "rb")) == NULL) return 0; /* Read in some of the signature bytes */ if (fread(buf, 1, PNG_BYTES_TO_CHECK, *fp) != PNG_BYTES_TO_CHECK) return 0; /* Compare the first PNG_BYTES_TO_CHECK bytes of the signature. Return nonzero (true) if they match */ /* extern PNG_EXPORT(int,png_check_sig) PNGARG((png_bytep sig, int num)); */ return(!png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK)); } int loadpng(FILE *fp) { unsigned long i,j,k; png_structp png_ptr; png_infop info_ptr; png_uint_32 width, height; png_bytep *row_pointers; int bit_depth, color_type; int interlace_type, compression_type, filter_type; /* create the png data structures */ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) return 0; info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return 0; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return 0; } png_init_io(png_ptr, fp); /* read file information */ /* If we have already read some of the signature */ png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_type); printf("* reading PNG image ("); printf("%ldx%ld, %d bit, ",width,height,bit_depth); switch (color_type) { case PNG_COLOR_TYPE_GRAY: printf("grey levels, "); break; case PNG_COLOR_TYPE_PALETTE: printf("palette, "); break; case PNG_COLOR_TYPE_RGB: printf("RGB, "); break; case PNG_COLOR_TYPE_RGB_ALPHA: printf("RGBA, "); break; case PNG_COLOR_TYPE_GRAY_ALPHA: printf("grey levels + alpha, "); break; } switch (interlace_type) { case PNG_INTERLACE_NONE: printf("not interlaced, "); break; case PNG_INTERLACE_ADAM7: printf("ADAM7-interlaced, "); break; } switch (compression_type) { case PNG_COMPRESSION_TYPE_BASE: printf(" compressed, "); break; } switch (filter_type) { case PNG_FILTER_TYPE_BASE: printf(" filtered, "); } printf(")\n"); /* tell libpng to strip 16 bit/color files down to 8 bits/color */ png_set_strip_16(png_ptr); /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single * byte into separate bytes (useful for paletted and grayscale images). */ png_set_packing(png_ptr); /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand(png_ptr); /* Expand paletted colors into true RGB triplets */ if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); /* Expand paletted or RGB images with transparency to full alpha channels * so the data will be available as RGBA quartets. */ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand(png_ptr); /* Add filler (or alpha) byte (before/after each RGB triplet) */ /* if (bit_depth == 8 && */ /* (color_type == PNG_COLOR_TYPE_RGB||color_type == PNG_COLOR_TYPE_GRAY)) */ png_set_filler(png_ptr, 0x000000ff, PNG_FILLER_AFTER); /* update info_ptr from the transforms. used to get a correct value for * png_get_rowbytes */ png_read_update_info(png_ptr, info_ptr); /* allocate the memory to hold a row of the image */ row_pointers = (png_bytep *)malloc(height*sizeof(png_bytep)); for (i = 0; i < height; i++) row_pointers[i] = malloc(png_get_rowbytes(png_ptr, info_ptr)); png_read_image(png_ptr, row_pointers); /* malloc pixmap data */ Width = width; Height = height; assert(Pixels = (struct pixel *)malloc((size_t)Width* (size_t)Height* sizeof(struct pixel))); k=0; for (i=0;i=Width) x=Width-1; if (y<0) y=0; if (y>=Height) y=Height-1; return (int)rint((Pixels[y*Width+x].r+Pixels[y*Width+x].g+Pixels[y*Width+x].b)/3); } int rAt(int x, int y) { return Pixels[y*Width+x].r; } int gAt(int x, int y) { return Pixels[y*Width+x].g; } int bAt(int x, int y) { return Pixels[y*Width+x].b; } void make_svg(char* svgfn) { int h,i,j,x,y,v; float dir,dx,dy; FILE *svgf=fopen(svgfn,"w"); fprintf(svgf,"\n"); fprintf(svgf,"\n"); fprintf(svgf,"\n"); fprintf(svgf," Bitmap to Letters demo\n"); fprintf(svgf,"\n"); fprintf(svgf,"\n"); /* and now for the meaty stuff */ for (h=0;h10) { /* arbitrary threshold on gradient */ h++; float B = 2*(Width+Height)/5000.; float scale= 2*(B+B*sqrt(dmag2)/100); if (dx==0) dir=M_PI/2; else if (dx > 0) dir=atan(dy/dx); else if (dy==0) dir=0; else if (dy > 0) dir=atan(-dx/dy)+M_PI/2; else dir=atan(dy/dx)+M_PI; fprintf(svgf," ",x,y,(int)rint(dir*360/6.28)+90,scale,rAt(x,y),gAt(x,y),bAt(x,y)); fprintf(svgf,"%s\n",Words[h%NbWords]); } } } fprintf(svgf," \n"); fprintf(svgf,"\n"); fclose(svgf); } int main(int argc, char **argv) { FILE *f; int i; if (argc==1 || !strcmp(argv[1],"-h")) { fprintf(stderr, "textorizer version $Id: textorizer.c,v 1.5 2006/03/01 14:16:15 mf Exp $\n\n"); fprintf(stderr,"usage: %s [options]\n",argv[0]); fprintf(stderr," -s : number of objects to display (default: 1000)\n"); fprintf(stderr," -o : SVG output file name (default: out.svg)\n"); fprintf(stderr," -i : PNG input file name (default: in.png)\n"); fprintf(stderr," -w : word file name (default: words.txt)\n"); return 0; } for (i=1;i