/////////////////////////////////////////////////////////////////////////////// // // Run simplified version of Hertzmann's painterly image filter. // You probably will want to use the Draw_Stroke funciton and the // Stroke class to help. // Return success of operation. // /////////////////////////////////////////////////////////////////////////////// bool TargaImage::NPR_Paint() { TargaImage ref_image = TargaImage(*this); TargaImage canvas = TargaImage(width, height); //Constant color that will get painted on int x, y, i, j, g, m; int r0, g0, b0, r1, g1, b1; typedef tuple<int, int, double> error_point; double d, area_error; int T = 25; //Error threshold static const unsigned int arr[] = {7, 3, 1}; //Brush sizes vector<int> rs (arr, arr + sizeof(arr) / sizeof(arr[0]) ); int rad; //***** //Paint //***** for(size_t b=0; b < rs.size(); b++) { //For each brush size ref_image.Filter_Gaussian_N(2*rs[b]+1); //referenceImage g = 2*rs[b]+1; //Could also be 2*rs[b]+1 rad = 3*rs[b]; //***Paint a layer*** vector <Stroke> strokes; //Find all of the grid block centers vector < tuple<int, int> > centers; centers.reserve( (width/g + 1)* (height/g + 1) ); for( x = g/2; x < g*(width/g); x+=g ) { for( y = g/2; y < g*(height/g); y+=g ) { centers.push_back( make_tuple(x, y) ); } } //Construct overlapping blocks around right and bottom edge, Possibly Unnecessary if (width % g) { //Hold x constant equal to width-g/2-1, loop down the column increasing y x = width - g/2 - 1; for( y = g/2; y < g*(height/g); y+=g ) { centers.push_back( make_tuple(x, y) ); } } if (height % g) { y = height - g/2 - 1; //Hold y constant equal to height-g/2-1, loop across the row increasing x for( x = g/2; x < g*(width/g); x+=g ) { centers.push_back( make_tuple(x, y) ); } } //if both x_extra and y_extra then need final bottom right corner block, probably unnecessary for( size_t c=0; c < centers.size(); c++ ) { //For each grid center tie (x, y) = centers[c]; //Find the error in the area/region/block vector < error_point > errors; errors.reserve(g*g); area_error = 0; for (j = -g/2; j<=g/2; ++j) { for (i = -g/2; i<=g/2; ++i) { m = (width*(y+j) + (x+i))*4; //Location of current pixel tie (r0, g0, b0) = make_tuple(ref_image.data[m], ref_image.data[m+1], ref_image.data[m+2]); tie (r1, g1, b1) = make_tuple(canvas.data[m], canvas.data[m+1], canvas.data[m+2]); //Calculate euclidean distance d = sqrt( (double) (r1-r0)*(r1-r0) + (g1-g0)*(g1-g0) + (b1-b0)*(b1-b0) ); //Save the coordinate and its error errors.push_back(error_point(x+i, y+j, d)); area_error += d; }//i }//j if( area_error > T ) { //Find largest error point sort(errors.begin(), errors.end(), cmp_errors); tie (i, j, d) = errors.front(); //Build a Stroke at loacation x,y with radius and value of r,g,b,alpha m = (width*j + i)*4; //Location of current pixel Stroke s = Stroke(rad, i, j, ref_image.data[m], ref_image.data[m+1], ref_image.data[m+2], ref_image.data[m+3]); strokes.push_back(s); } }//c //******* //Paint all strokes in random order onto canvas //******* random_shuffle ( strokes.begin(), strokes.end() ); vector <Stroke>::iterator it; for (it = strokes.begin(); it != strokes.end(); it++) { canvas.Paint_Stroke(*it); } //Reset the reference image to the unchanged image data for the next smaller pass memcpy(ref_image.data, data, sizeof(unsigned char) * width * height * 4); }//b //Write the canvas data to the displayed image memcpy(data, canvas.data, sizeof(unsigned char) * width * height * 4); //Overwrite alpha values for(i=3; i<width*height*4; i+=4) { data[i] = 255; } return true; }