animation filler::fill( PNG & img, int x, int y, colorPicker & fillColor, int tolerance, int frameFreq ) { /** * @todo You need to implement this function! * * This is the basic description of a flood-fill algorithm: Every fill * algorithm requires an ordering structure, which is passed to this * function via its template parameter. For a breadth-first-search * fill, that structure is a Queue, for a depth-first-search, that * structure is a Stack. To begin the algorithm, you simply place the * given point in the ordering structure. Then, until the structure is * empty, you do the following: * * 1. Remove a point from the ordering structure. * * If it has not been processed before and if its color is * within the tolerance distance (up to and **including** * tolerance away in square-RGB-space-distance) to the original * point's pixel color [that is, \f$(currentRed - OriginalRed)^2 + (currentGreen - OriginalGreen)^2 + (currentBlue - OriginalBlue)^2 \leq tolerance\f$], then: * 1. indicate somehow that it has been processed (do not mark it * "processed" anywhere else in your code) * 2. change its color in the image using the appropriate * colorPicker * 3. add all of its neighbors to the ordering structure, and * 4. if it is the appropriate frame, send the current PNG to the * animation (as described below). * 2. When implementing your breadth-first-search and * depth-first-search fills, you will need to explore neighboring * pixels in some order. * * While the order in which you examine neighbors does not matter * for a proper fill, you must use the same order as we do for * your animations to come out like ours! The order you should put * neighboring pixels **ONTO** the queue or stack is as follows: * **RIGHT(+x), DOWN(+y), LEFT(-x), UP(-y). IMPORTANT NOTE: *UP* * here means towards the top of the image, so since an image has * smaller y coordinates at the top, this is in the *negative y* * direction. Similarly, *DOWN* means in the *positive y* * direction.** To reiterate, when you are exploring (filling out) * from a given pixel, you must first try to fill the pixel to * it's RIGHT, then the one DOWN from it, then to the LEFT and * finally UP. If you do them in a different order, your fill may * still work correctly, but your animations will be different * from the grading scripts! * 3. For every k pixels filled, **starting at the kth pixel**, you * must add a frame to the animation, where k = frameFreq. * * For example, if frameFreq is 4, then after the 4th pixel has * been filled you should add a frame to the animation, then again * after the 8th pixel, etc. You must only add frames for the * number of pixels that have been filled, not the number that * have been checked. So if frameFreq is set to 1, a pixel should * be filled every frame. */ animation mainframe; int row = img.height(); int col = img.width(); int xcord = x; int ycord = y; int counter = 0; //Every fill algorithm requires an ordering structure OrderingStructure <int> tempx; OrderingStructure <int> tempy; //making arrays for checking if pixel is processed already bool ** processed = new bool* [col]; for(int i = 0; i < col; i++){ processed[i] = new bool[row]; for(int j = 0; j < row; j++){ processed[i][j] = false; } } // you simply place the given point in the ordering structure tempx.add(xcord); tempy.add(ycord); RGBAPixel origin = *img(x,y); //Then, until the structure is empty, you do the following while(tempx.isEmpty() == false && tempy.isEmpty() == false){ //Remove a point from the ordering structure. xcord = tempx.peek(); ycord = tempy.peek(); tempx.remove(); tempy.remove(); int redval = img(xcord,ycord)->red-origin.red; int greenval = img(xcord,ycord)->green-origin.green; int blueval = img(xcord,ycord)->blue-origin.blue; //If it has not been processed before and if its color is within the tolerance distance if(!processed[xcord][ycord] ){ //indicate somehow that it has been processedprocessed[xcord][ycord] = true processed[xcord][ycord] = true; if (redval*redval + greenval*greenval + blueval*blueval <= tolerance){ //change its color in the image using the appropriate colorPicker *img(xcord,ycord) = fillColor(xcord,ycord); //add all of its neighbors to the ordering structure if(xcord+1 < row && ycord < col ) { tempx.add(xcord+1); tempy.add(ycord); } if(xcord < row && ycord + 1 < col ) { tempx.add(xcord); tempy.add(ycord+1); } if(xcord-1 >= 0 && xcord-1 < row && ycord < col ) { tempx.add(xcord-1); tempy.add(ycord); } if(xcord < row && ycord-1 < col && ycord -1 >= 0 ) { tempx.add(xcord); tempy.add(ycord-1); } //if it is the appropriate frame, send the current PNG to the animation counter++; if(counter % frameFreq == 0){ counter = 0; mainframe.addFrame(img); } } } } for(int i = 0; i < col; i++){ delete [] processed[i]; } delete [] processed; return mainframe; }
animation filler::fill( PNG & img, int x, int y, colorPicker & fillColor, int tolerance, int frameFreq ) { /** * @todo You need to implement this function! * * This is the basic description of a flood-fill algorithm: Every fill * algorithm requires an ordering structure, which is passed to this * function via its template parameter. For a breadth-first-search * fill, that structure is a Queue, for a depth-first-search, that * structure is a Stack. To begin the algorithm, you simply place the * given point in the ordering structure. Then, until the structure is * empty, you do the following: * * 1. Remove a point from the ordering structure. * * If it has not been processed before and if its color is * within the tolerance distance (up to and **including** * tolerance away in square-RGB-space-distance) to the original * point's pixel color [that is, \f$(currentRed - OriginalRed)^2 + (currentGreen - OriginalGreen)^2 + (currentBlue - OriginalBlue)^2 \leq tolerance\f$], then: * 1. indicate somehow that it has been processed (do not mark it * "processed" anywhere else in your code) * 2. change its color in the image using the appropriate * colorPicker * 3. add all of its neighbors to the ordering structure, and * 4. if it is the appropriate frame, send the current PNG to the * animation (as described below). * 2. When implementing your breadth-first-search and * depth-first-search fills, you will need to explore neighboring * pixels in some order. * * While the order in which you examine neighbors does not matter * for a proper fill, you must use the same order as we do for * your animations to come out like ours! The order you should put * neighboring pixels **ONTO** the queue or stack is as follows: * **RIGHT(+x), DOWN(+y), LEFT(-x), UP(-y). IMPORTANT NOTE: *UP* * here means towards the top of the image, so since an image has * smaller y coordinates at the top, this is in the *negative y* * direction. Similarly, *DOWN* means in the *positive y* * direction.** To reiterate, when you are exploring (filling out) * from a given pixel, you must first try to fill the pixel to * it's RIGHT, then the one DOWN from it, then to the LEFT and * finally UP. If you do them in a different order, your fill may * still work correctly, but your animations will be different * from the grading scripts! * 3. For every k pixels filled, **starting at the kth pixel**, you * must add a frame to the animation, where k = frameFreq. * * For example, if frameFreq is 4, then after the 4th pixel has * been filled you should add a frame to the animation, then again * after the 8th pixel, etc. You must only add frames for the * number of pixels that have been filled, not the number that * have been checked. So if frameFreq is set to 1, a pixel should * be filled every frame. */ OrderingStructure <int> xCoordinates; OrderingStructure <int> yCoordinates; animation myAnimation; int frameCount = 0; std::vector <bool> checkArray(img.height()*img.width()); for (size_t i = 0; i < img.height(); i++) { for(size_t j = 0; j < img.width(); j++) { checkArray[i*img.width()+j] = false; } } xCoordinates.add(x); yCoordinates.add(y); int origin_r = img(x,y)->red; int origin_g = img(x,y)->green; int origin_b = img(x,y)->blue; while(!xCoordinates.isEmpty()) { int currX = xCoordinates.remove(); int currY = yCoordinates.remove(); RGBAPixel currPixel = *img(currX,currY); int colorDistance = pow(currPixel.red-origin_r,2) + pow(currPixel.green-origin_g,2) + pow(currPixel.blue -origin_b,2); if((!checkArray[currY*img.width()+currX])&& colorDistance <= tolerance) { checkArray[currY*img.width()+currX] = true; RGBAPixel tempPixel = fillColor(currX,currY); img(currX,currY)->red = tempPixel.red; img(currX,currY)->green = tempPixel.green; img(currX,currY)->blue = tempPixel.blue; //right if(currX + 1 < int(img.height())) { xCoordinates.add(currX+1); yCoordinates.add(currY); } //down if(currY + 1 < int(img.height())) { xCoordinates.add(currX); yCoordinates.add(currY+1); } //left if(currX - 1 >= 0) { xCoordinates.add(currX-1); yCoordinates.add(currY); } //up if(currY - 1 >= 0) { xCoordinates.add(currX); yCoordinates.add(currY-1); } if((++frameCount) % frameFreq == 0) myAnimation.addFrame(img); } } return myAnimation; }
animation filler::fill( PNG & img, int x, int y, colorPicker & fillColor, int tolerance, int frameFreq ) { OrderingStructure<RGBAPixel> sq; //pixel structure OrderingStructure<int> sqX; //Xcoord structure OrderingStructure<int> sqY; //Ycoord structure int** marks = new int *[img.width()]; //array of pointers with size width 0 1 2 3 for(int i = 0; i < img.width(); i++) //array of pointers to arrays of size height x = 0->y = 0 1 { marks[i] = new int[img.height()]; for(int j = 0; j < img.height(); j++) //set coordinates to mark values e.g 0,1 = 0 marks[i][j] = 0; } int count = 0; //count when to add new frame animation fillAnimation; //animation to return RGBAPixel origin = (*img(x,y)); sq.add(origin); sqX.add(x); sqY.add(y); while(!sq.isEmpty()) { RGBAPixel pixel = sq.remove(); int xCoord = sqX.remove(); int yCoord = sqY.remove(); int originRed = origin.red, pixelRed = pixel.red, originBlue = origin.blue, pixelBlue = pixel.blue, originGreen = origin.green, pixelGreen = pixel.green; int toleranceValue = pow((originRed - pixelRed), 2) + pow((originBlue - pixelBlue), 2) + pow((originGreen - pixelGreen), 2); if(marks[xCoord][yCoord] == 0 && toleranceValue <= tolerance) { marks[xCoord][yCoord] = 1; count++; *img(xCoord,yCoord) = fillColor(xCoord,yCoord); if(count == frameFreq) { fillAnimation.addFrame(img); count = 0; } if(xCoord < img.width() - 1) { sqX.add(xCoord + 1); sqY.add(yCoord); sq.add(*img(xCoord+1,yCoord)); } if(yCoord < img.height() - 1) { sqX.add(xCoord); sqY.add(yCoord+1); sq.add(*img(xCoord,yCoord+1)); } if(xCoord - 1 >= 0) { sqX.add(xCoord-1); sqY.add(yCoord); sq.add(*img(xCoord-1,yCoord)); } if(yCoord - 1 >= 0) { sqX.add(xCoord); sqY.add(yCoord-1); sq.add(*img(xCoord,yCoord-1)); } } } return fillAnimation; }
animation filler::fill(PNG& img, int x, int y, colorPicker& fillColor, int tolerance, int frameFreq) { /** * @todo You need to implement this function! * * This is the basic description of a flood-fill algorithm: Every fill * algorithm requires an ordering structure, which is passed to this * function via its template parameter. For a breadth-first-search * fill, that structure is a Queue, for a depth-first-search, that * structure is a Stack. To begin the algorithm, you simply place the * given point in the ordering structure. Then, until the structure is * empty, you do the following: * * 1. Remove a point from the ordering structure. * * If it has not been processed before and if its color is * within the tolerance distance (up to and **including** * tolerance away in square-RGB-space-distance) to the original * point's pixel color [that is, \f$(currentRed - OriginalRed)^2 + (currentGreen - OriginalGreen)^2 + (currentBlue - OriginalBlue)^2 \leq tolerance\f$], then: * 1. indicate somehow that it has been processed (do not mark it * "processed" anywhere else in your code) * 2. change its color in the image using the appropriate * colorPicker * 3. add all of its neighbors to the ordering structure, and * 4. if it is the appropriate frame, send the current PNG to the * animation (as described below). * 2. When implementing your breadth-first-search and * depth-first-search fills, you will need to explore neighboring * pixels in some order. * * While the order in which you examine neighbors does not matter * for a proper fill, you must use the same order as we do for * your animations to come out like ours! The order you should put * neighboring pixels **ONTO** the queue or stack is as follows: * **RIGHT(+x), DOWN(+y), LEFT(-x), UP(-y). IMPORTANT NOTE: *UP* * here means towards the top of the image, so since an image has * smaller y coordinates at the top, this is in the *negative y* * direction. Similarly, *DOWN* means in the *positive y* * direction.** To reiterate, when you are exploring (filling out) * from a given pixel, you must first try to fill the pixel to * it's RIGHT, then the one DOWN from it, then to the LEFT and * finally UP. If you do them in a different order, your fill may * still work correctly, but your animations will be different * from the grading scripts! * 3. For every k pixels filled, **starting at the kth pixel**, you * must add a frame to the animation, where k = frameFreq. * * For example, if frameFreq is 4, then after the 4th pixel has * been filled you should add a frame to the animation, then again * after the 8th pixel, etc. You must only add frames for the * number of pixels that have been filled, not the number that * have been checked. So if frameFreq is set to 1, a pixel should * be filled every frame. */ OrderingStructure<int> xcoord; OrderingStructure<int> ycoord; xcoord.add(x); ycoord.add(y); animation a; int counter = 0; int h = img.height(); int w = img.width(); bool** processed = new bool*[h]; for (int i = 0; i < h; i++){ processed[i] = new bool[w]; for (int j = 0; j < w; j++){ processed[i][j] = false; } } int oriRed = img(x,y) -> red; int oriGreen = img(x,y) -> green; int oriBlue = img(x,y) -> blue; while (!xcoord.isEmpty() && !ycoord.isEmpty()){ x = xcoord.remove(); y = ycoord.remove(); int curRed = img(x,y) -> red; int curGreen = img(x,y) -> green; int curBlue = img(x,y) -> blue; int colorDist = (curRed - oriRed)*(curRed - oriRed) + (curGreen - oriGreen)*(curGreen - oriGreen) + (curBlue - oriBlue)*(curBlue - oriBlue); if (!processed[y][x] && colorDist <= tolerance){ processed[y][x] = true; img(x,y) -> red = fillColor(x,y).red; img(x,y) -> green = fillColor(x,y).green; img(x,y) -> blue = fillColor(x,y).blue; if (x + 1 < w && !processed[y][x+1]){ xcoord.add(x + 1); ycoord.add(y); } if (y + 1 < h && !processed[y+1][x]){ xcoord.add(x); ycoord.add(y + 1); } if (x - 1 >= 0 && !processed[y][x-1]){ xcoord.add(x - 1); ycoord.add(y); } if (y - 1 >= 0 && !processed[y-1][x]){ xcoord.add(x); ycoord.add(y - 1); } counter++; if (counter % frameFreq == 0) a.addFrame(img); } } for (int i = 0; i < h; i++) delete [] processed[i]; delete [] processed; return a; }
animation filler::fill( PNG & img, int x, int y, colorPicker & fillColor, int tolerance, int frameFreq ) { /** * @todo You need to implement this function! * * This is the basic description of a flood-fill algorithm: Every fill * algorithm requires an ordering structure, which is passed to this * function via its template parameter. For a breadth-first-search * fill, that structure is a Queue, for a depth-first-search, that * structure is a Stack. To begin the algorithm, you simply place the * given point in the ordering structure. Then, until the structure is * empty, you do the following: * * 1. Remove a point from the ordering structure. * * If it has not been processed before and if its color is * within the tolerance distance (up to and **including** * tolerance away in square-RGB-space-distance) to the original * point's pixel color [that is, \f$(currentRed - OriginalRed)^2 + (currentGreen - OriginalGreen)^2 + (currentBlue - OriginalBlue)^2 \leq tolerance\f$], then: * 1. indicate somehow that it has been processed (do not mark it * "processed" anywhere else in your code) * 2. change its color in the image using the appropriate * colorPicker * 3. add all of its neighbors to the ordering structure, and * 4. if it is the appropriate frame, send the current PNG to the * animation (as described below). * 2. When implementing your breadth-first-search and * depth-first-search fills, you will need to explore neighboring * pixels in some order. * * While the order in which you examine neighbors does not matter * for a proper fill, you must use the same order as we do for * your animations to come out like ours! The order you should put * neighboring pixels **ONTO** the queue or stack is as follows: * **RIGHT(+x), DOWN(+y), LEFT(-x), UP(-y). IMPORTANT NOTE: *UP* * here means towards the top of the image, so since an image has * smaller y coordinates at the top, this is in the *negative y* * direction. Similarly, *DOWN* means in the *positive y* * direction.** To reiterate, when you are exploring (filling out) * from a given pixel, you must first try to fill the pixel to * it's RIGHT, then the one DOWN from it, then to the LEFT and * finally UP. If you do them in a different order, your fill may * still work correctly, but your animations will be different * from the grading scripts! * 3. For every k pixels filled, **starting at the kth pixel**, you * must add a frame to the animation, where k = frameFreq. * * For example, if frameFreq is 4, then after the 4th pixel has * been filled you should add a frame to the animation, then again * after the 8th pixel, etc. You must only add frames for the * number of pixels that have been filled, not the number that * have been checked. So if frameFreq is set to 1, a pixel should * be filled every frame. */ OrderingStructure<RGBAPixel> pixels; //structure to return OrderingStructure<int> cx; //x axis OrderingStructure<int> cy; //y axis bool process[img.width()][img.height()]; for (int i = 0; i < img.width(); i++) { for (int j = 0; j <img.height(); j++) { process[i][j] = false; } } animation anim; //output int frame = 0; RGBAPixel origPixel = *(img(x,y)); //one pix to input int origRed = origPixel.red; int origGreen = origPixel.green; int origBlue = origPixel.blue; pixels.add(*img(x,y));//put into cx.add(x); cy.add(y); while (!pixels.isEmpty()) { //remove one point in the structure RGBAPixel currPixel = pixels.remove(); int currRed = currPixel.red; int currGreen = currPixel.green; int currBlue = currPixel.blue; int currX = cx.remove(); int currY = cy.remove(); int min = pow(origRed-currRed,2)+pow(origGreen-currGreen,2)+pow(origBlue-currBlue,2); if ((min <= tolerance) && !process[currX][currY]) { process[currX][currY] = true;//mark frame++; *(img(currX,currY)) = fillColor(currX,currY); if (currX+1 <img.width()) { //RIGHT pixels.add(*(img(currX+1,currY))); cx.add(currX+1); cy.add(currY); } if (currY+1 <img.height()) { //DOWN pixels.add(*(img(currX,currY+1))); cx.add(currX); cy.add(currY+1); } if (currX-1>= 0) { //LEFT pixels.add(*(img(currX-1,currY))); cx.add(currX-1); cy.add(currY); } if (currY-1>= 0) { //DOWN pixels.add(*(img(currX,currY-1))); cx.add(currX); cy.add(currY-1); } if (frame % frameFreq == 0) { //add the current image to the animation anim.addFrame(img); } } } return anim; }