// compile as: // cc -std=c99 -o bmp2laser `pkg-config --cflags --libs MagickWand` bmp2laser.c // A simple example equivalent to // convert logo: logo.jpg //#include #include #include #include // minimum width of black features //#define MINBLACK 12 int MINBLACK=12; // minimum width of white features under which they should not be reduced //#define MINWHITE 20 int MINWHITE=20; // size of features to erase and ignore #define SKIPMIN 2 // width of the image-edge areas where there is no copper-growth to be done // must be bigger than MINBLACK and MINWHITE #define EDGECELL_X 20 #define EDGECELL_Y 20 #define DEF_ITERATIONS 6 // maximum width and size of images the program can handle #define MAXWIDTH 20000 #define MAXSIZE 10000*2000 #define MAXSIZE 10000*2000 //unsigned char imgarr[MAXSIZE]; // FIXED SIZE! FIXME //unsigned char*imgarr1; //unsigned char*imgarr2; unsigned char*imgarrI; unsigned char*imgarrO; unsigned char*lineconv; int img_h,img_w,img_wb; void setimgwb() { img_wb=(img_w+7)/8;} size_t getimgsize() { return img_wb*img_h;} unsigned char getbit(int n) { return 1<>3); if(p)imgarrI[i]|=getbit (x&0x07); else imgarrI[i]&=getibit(x&0x07); } void setpixelO(int x,int y,int p) { size_t i; i=img_wb*y+(x>>3); if(p)imgarrO[i]|=getbit (x&0x07); else imgarrO[i]&=getibit(x&0x07); } int getpixel(int x,int y) { size_t i,i1; // if(y>=img_h)return 0; // if(x>=img_w)return 0; i=img_wb*y+(x>>3); if(imgarrI[i]&getbit(x&0x07))return 1;else return 0; } int getpixelO(int x,int y) { size_t i,i1; i=img_wb*y+(x>>3); if(imgarrO[i]&getbit(x&0x07))return 1;else return 0; } // set pixel including its neighbors int setbigpixelO(int x,int y,int c) { setpixelO(x,y,c); // in "+" shape setpixelO(x-1,y,c); setpixelO(x+1,y,c); setpixelO(x,y-1,c); setpixelO(x,y+1,c); // corners // setpixelO(x-1,y-1,c); // setpixelO(x+1,y+1,c); // setpixelO(x+1,y-1,c); // setpixelO(x-1,y+1,c); } // debug function for making text lookalike representation of the data void dumpimgarr() { FILE*f; int x,x1,y,t,c; printf("Dumping img.txt...\n"); f=fopen("img.txt","w");if(!f)return; for(y=0;y=MINBLACK)continue; // wider than limit black, skip it diff=MINBLACK-laa[t][1]; if(laa[t-1][1]<=MINWHITE)if(laa[t+1][1]<=MINWHITE)continue; // nowhere to go if(laa[t-1][1]-diff/2>MINWHITE)if(laa[t+1][1]-diff/2>MINWHITE){laa[t-1][1]-=diff/2;laa[t][1]+=diff;diff-=diff/2;laa[t+1][1]-=diff;tr++;continue;} // subtract from both ends if(laa[t-1][1]<=MINWHITE){laa[t+1][1]-=diff;laa[t][1]+=diff;tr++;continue;} else {laa[t-1][1]-=diff;laa[t][1]+=diff;tr++;continue;} } return tr; } // ugly way to widen the minimum horizonal black width so the laser can "bite" // todo: refactor using the cellular automaton void enforcehorizontalspaces() { int tx,ty,t2,t3; printf("Enforcing minimal horizontal spacing... \r");fflush(stdout); // dirty code - refactor! for(ty=1;ty1)if(laa[tx][1]<2)laa[tx][0]=laa[tx-1][0];//filter 1-pixel dirt if(laa[tx][0])setpixel(t3,ty,1); else // setpixel(t3,ty,0); // avoid introduction of vertical orphans if((getpixel(t3,ty-1)==1)&&(getpixel(t3,ty+1)==1))setpixel(t3,ty,1);else setpixel(t3,ty,0); t3++; } } } } void enforceverticalspaces() { int tx,ty,t2,t3; printf("Enforcing minimal vertical spacing... \r");fflush(stdout); // dirty code - refactor! for(tx=1;tx1)if(laa[ty][1]<2)laa[ty][0]=laa[ty-1][0];//filter 1-pixel dirt if(laa[ty][0])setpixel(tx,t3,1); else // setpixel(t3,ty,0); // avoid introduction of vertical orphans if((getpixel(tx-1,t3)==1)&&(getpixel(tx+1,t3)==1))setpixel(tx,t3,1);else setpixel(tx,t3,0); t3++; } } } } // definitions for the polar-coordinates lookup array // number of steps per circle - 24=15 deg each, 36=10 deg each #define ANGLECOUNT 24 #define ANGLESTEP 360/ANGLECOUNT // #define RADIUS MINBLACK #define RADIUSHALF MINBLACK/2 // which of the subarrays is sine and cosine #define a_SIN 0 #define a_COS 1 int arr_sincos[ANGLECOUNT][2]; int arr_sincoshalf[ANGLECOUNT][2]; // populate the arr_sincos array void precalculatearr_sincos() { int t; double d,di; for(t=0;tend)end+=ANGLECOUNT; mid=modang(((end-start)/2)+start); if(start<0)return 0; if(end<0)return 0; if(start>=0)ang[start]=2; if(end>=0)ang[modang(end)]=3; ang[mid]=9; //debug: display the pixel surrounding data /* for(t=0;t9, 2=>25, 3=>49 int sumsurrounding(int x,int y,int range) { int n=0,tx,ty; for(tx=x-range;tx<=x+range;tx++)for(ty=y-range;ty<=y+range;ty++) n+=getpixel(tx,ty); return n; } // decide in a cellular-automaton like where the copper areas should grow void handlecellpixel(int x,int y,int doconvex,int dogrow) { int c; int n; int tx,ty,range; c=getpixel(x,y); //just copy the border areas, do not grow there if( (x(img_w-EDGECELL_X)) || (y>(img_h-EDGECELL_Y)) ) {setpixelO(x,y,c);return;} /* // example for isolating edges if(getpixel(x,y-1)==c)if(getpixel(x,y+1)==c)if(getpixel(x-1,y)==c)if(getpixel(x+1,y)==c) if(getpixel(x-1,y-1)==c)if(getpixel(x-1,y+1)==c)if(getpixel(x+1,y-1)==c)if(getpixel(x+1,y+1)==c) {setpixelO(x,y,0);return;} setpixelO(x,y,1);return; */ // filter vertical orphans if(getpixel(x,y-1)!=c)if(getpixel(x,y+1)!=c){if(c)setpixelO(x,y,0);else setpixelO(x,y,1);} // skip white pixels, we are only growing copper and ignoring spaces if(c){setpixelO(x,y,c);return;} // skip pixels that are not on the edges if(getpixel(x,y-1)==c)if(getpixel(x,y+1)==c)if(getpixel(x-1,y)==c)if(getpixel(x+1,y)==c) if(getpixel(x-1,y-1)==c)if(getpixel(x-1,y+1)==c)if(getpixel(x+1,y-1)==c)if(getpixel(x+1,y+1)==c) {setpixelO(x,y,c);return;} // set pixels in convex places // total pixels=(2*range+1)^2-1 (assuming the center pixel is empty) - 1=>8, 2=>24, 3=>48 if(doconvex)if(sumsurrounding(x,y,2)>20){setbigpixelO(x,y,1);return;} // grow edge by pixel if enough space forward if(dogrow)if(isfreearound(c,x,y,MINBLACK)){setbigpixelO(x,y,1);return;} // nothing to do, just copy pixel setpixelO(x,y,c); } void convertbmp(char*fni,char*fno,int iternum) { MagickWand *mw = NULL; int t,t1,t2,t3,tr,i,tx,ty; MagickWandGenesis(); /* Create a wand */ mw = NewMagickWand(); /* Read the input image */ printf("Reading file... \n"); MagickReadImage(mw,fni); // adjust resolution to 1000 dpi MagickSetImageUnits(mw,PixelsPerInchResolution); MagickSetImageResolution(mw,1000,1000); img_w=MagickGetImageWidth(mw); img_h=MagickGetImageHeight(mw); //img_h=h;img_w=w; setimgwb(); imgarrI=(unsigned char*)malloc(img_wb*img_h*sizeof(char)+2); imgarrO=(unsigned char*)malloc(img_wb*img_h*sizeof(char)+2); //imgarrI=imgarr1; //imgarrO=imgarr2; lineconv=(unsigned char*)malloc(img_w*sizeof(char)+2); precalculatearr_sincos(); printf("Allocated %li+%li bytes \n",img_wb*img_h*sizeof(char)+2,img_w*sizeof(char)+2); if(img_w>=MAXWIDTH){fprintf(stderr,"ERROR: image wider than %i pixels, aborting.\n",MAXWIDTH);return;} if(img_wb*img_h/8>=MAXSIZE){fprintf(stderr,"ERROR: image data bigger than %i bytes, aborting.\n",MAXSIZE);return;} //imgarr=(unsigned char*)malloc(getimgsize()); printf("Quantizing... \n"); MagickQuantizeImage(mw,2,GRAYColorspace,0,MagickFalse,MagickFalse); // xlim=img_w;//ylim=540; // ylim=img_h; printf("Reading... \n"); for(ty=0;ty] [-b] [-w] \n"); printf("Defaults: -i%i -b%i -w%i\n",DEF_ITERATIONS,MINBLACK,MINWHITE); } int main(int argc,char*argv[]) { int n,t,iter=DEF_ITERATIONS; if(argc<3){help();return 0;} // input file, output file, number of iterations n=1; for(t=1;t