void testWaterfall() { cout << "[main]: " << __func__ << "()" << endl; PNG in; in.readFromFile("in_05.png"); List<RGBAPixel> list = imageToList(in); list.waterfall(); PNG out = listToImage(list, in.width(), in.height()); out.writeToFile("waterfall_01.png"); checkSoln(__func__, out, "soln_waterfall_01.png"); in.readFromFile("in_06.png"); list = imageToList(in); list.waterfall(); out = listToImage(list, in.width(), in.height()); out.writeToFile("waterfall_02.png"); checkSoln(__func__, out, "soln_waterfall_02.png"); }
/** * This function blends, or averages, two PNGs together. That is, each pixel in * the returned image consists of the averaged components (red, green, blue) of * the two input images. * * @param firstImage The first of the two PNGs to be averaged together. * @param secondImage The second of the two PNGs to be averaged together. * * @return The averaged image. */ PNG blendImages(PNG firstImage, PNG secondImage) { for (size_t yi = 0; yi < firstImage.height(); yi++) { for (size_t xi = 0; xi < firstImage.width(); xi++) { firstImage(xi, yi)->green =(firstImage(xi, yi)->green+secondImage(xi, yi)->green)/2; firstImage(xi, yi)->red =(firstImage(xi, yi)->red+secondImage(xi, yi)->red)/2; firstImage(xi, yi)->blue =(firstImage(xi, yi)->blue+secondImage(xi, yi)->blue)/2; } } return firstImage; }
int main() { // Load in.png PNG * original = new PNG; original -> readFromFile("in.png"); int width = original->width(); int height = original->height(); cout << "width=" << width << " height=" << height << endl; // Create out.png PNG * output; output = setupOutput(width, height); // Loud our favorite color to color the outline RGBAPixel * myPixel = myFavoriteColor(192); // Go over the whole image, and if a pixel differs from that to its upper // left, color it my favorite color in the output for (int y = 1; y < height; y++) { for (int x = 1; x < width; x++) { // Calculate the pixel difference RGBAPixel * prev = (*original)(x - 1, y - 1); RGBAPixel * curr = (*original)(x, y); int diff = abs(curr->red - prev->red ) + abs(curr->green - prev->green) + abs(curr->blue - prev->blue ); // If the pixel is an edge pixel, // color the output pixel with my favorite color RGBAPixel * currOutPixel = (*output)(x,y); if (diff > 100) { currOutPixel->blue = myPixel->blue; currOutPixel->red = myPixel->red; currOutPixel->green = myPixel->green; } } } // Save the output file output->writeToFile("out.png"); // Clean up memory delete myPixel; delete output; delete original; return 0; }
void testReverse() { cout << "[main]: " << __func__ << "()" << endl; PNG in; in.readFromFile("in_02.png"); List<RGBAPixel> list = imageToList(in); list.reverse(); PNG out = listToImage(list, in.width(), in.height()); out.writeToFile("reverse.png"); checkSoln(__func__, out, "soln_reverse.png"); }
/** * This function brightens a rectangle of a PNG, increasing the components * (red, green, blue) of each pixel by the given amount. You must account * for potential overflow issues (color components can only store numbers * between 0 and 255). If you attempt to store a value greater than 255 * into a color component, the result will wrap around (and you won't be * able to check if it was greater than 255). * * @param original A PNG object which holds the image data to be modified. * @param amount The integer amount by which to increase each pixel's * components. * * @return The brightened image. */ PNG brighten(PNG original, int amount) { /// You can assume amount is positive. for (size_t yi = 0; yi < original.height(); yi++) { for (size_t xi = 0; xi < original.width(); xi++) { /// Your code here! //original(xi,yi)->red = 0; //original(xi,yi)->blue = 0; } } return original; }
PNG rotate(PNG original, PNG result) { //this function rotate the original file 180 degree and return the result size_t height = original.height(); size_t width = original.width(); for (size_t yi = 0; yi < height; yi++) { for (size_t xi = 0; xi < width; xi++) { result(width-xi-1,height-yi-1)->red = original(xi,yi)->red; result(width-xi-1,height-yi-1)->green = original(xi,yi)->green; result(width-xi-1,height-yi-1)->blue = original(xi,yi)->blue; result(width-xi-1,height-yi-1)->alpha = original(xi,yi)->alpha; } } return result; }
List<RGBAPixel> imageToList(PNG const & img, bool reverse = false) { List<RGBAPixel> list; for (int i = 0; i < img.width(); i++) { for (int j = 0; j < img.height(); j++) { if (reverse) list.insertFront(*img(i,j)); else list.insertBack(*img(i,j)); } } return list; }
/** * Flips an image around a horizontal axis in-place. * @param image - the image to flip */ void Flipper::flipSerial(PNG & image) { RGBAPixel temp; int height = image.height(); int width = image.width(); for(int i = 0; i < width; ++i) { for(int j = 0; j < height / 2; ++j) { temp = *image(i, j); *image(i, j) = *image(i, height - 1 - j); *image(i, height - 1 - j) = temp; } } }
/** * Flips an image around a horizontal axis in-place. * @param image - the image to flip */ void Flipper::flipParallel(PNG & image) { // TODO: parallelize the code below //RGBAPixel temp; int height = image.height(); int width = image.width(); #pragma omp parallel for for(int i = 0; i < width; ++i) { for(int j = 0; j < height / 2; ++j) { RGBAPixel temp = *image(i, j); *image(i, j) = *image(i, height - 1 - j); *image(i, height - 1 - j) = temp; } } }
/** * Flips the source image into a new PNG. * THIS FUNCTION IS GRADED. * @todo - parallelize verticalFlip() * @param source - the original PNG * @return - a copy of the parameter that has been flipped * over the vertical axis */ PNG ImageTools::verticalFlip(const PNG & source) { int width = source.width(); int height = source.height(); PNG output(width, height); #pragma omp parallel for for(int i = 0; i < width; ++i) { for(int j = 0; j < height; ++j) { // flip! output(i,j)->red=source(width-i-1,j)->red; output(i,j)->green=source(width-i-1,j)->green; output(i,j)->blue=source(width-i-1,j)->blue; } } return output; }
/** * This function brightens a rectangle of a PNG, increasing the components * (red, green, blue) of each pixel by the given amount. You must account * for potential overflow issues (color components can only store numbers * between 0 and 255). If you attempt to store a value greater than 255 * into a color component, the result will wrap around (and you won't be * able to check if it was greater than 255). * * @param original A PNG object which holds the image data to be modified. * @param amount The integer amount by which to increase each pixel's * components. * * @return The brightened image. */ PNG brighten(PNG original, int amount) { /// You can assume amount is positive. for (size_t yi = 0; yi < original.height(); yi++) { for (size_t xi = 0; xi < original.width(); xi++) { if(original(xi, yi)->red+amount<=255){ original(xi, yi)->red = original(xi, yi)->red+amount;} else original(xi, yi)->red = 255; if(original(xi, yi)->green+amount<=255){ original(xi, yi)->green = original(xi, yi)->green+amount;} else original(xi, yi)->green = 255; if(original(xi, yi)->blue+amount<=255){ original(xi, yi)->blue = original(xi, yi)->blue+amount;} else original(xi, yi)->blue = 255; } } return original; }
/** * This function blends, or averages, two PNGs together. That is, each pixel in * the returned image consists of the averaged components (red, green, blue) of * the two input images. * * @param firstImage The first of the two PNGs to be averaged together. * @param secondImage The second of the two PNGs to be averaged together. * * @return The averaged image. */ PNG blendImages(PNG firstImage, PNG secondImage) { for(size_t x = 0; x < firstImage.width(); x++){ for(size_t y = 0; y < firstImage.height(); y++){ size_t r1 = firstImage(x, y) ->red; size_t r2 = secondImage(x, y) ->red; size_t g1 = firstImage(x, y) ->green; size_t g2 = secondImage(x, y) ->green; size_t b1 = firstImage(x, y) ->blue; size_t b2 = secondImage(x, y) ->blue; size_t r = (r1 + r2)/2; size_t g = (g1 + g2)/2; size_t b = (b1 + b2)/2; firstImage(x,y) ->red = r; firstImage(x,y) ->green = g; firstImage(x,y) ->blue = b; } } /// Your code here! return firstImage; }
/** * This function blends, or averages, two PNGs together. That is, each pixel in * the returned image consists of the averaged components (red, green, blue) of * the two input images. * * @param firstImage The first of the two PNGs to be averaged together. * @param secondImage The second of the two PNGs to be averaged together. * * @return The averaged image. */ PNG blendImages(PNG firstImage, PNG secondImage) { /// Your code here! for (size_t yi = 0; yi < firstImage.height(); yi++) { for (size_t xi = 0; xi < firstImage.width(); xi++) { int c1 = firstImage(xi, yi)->red; int c2 = secondImage(xi, yi)->red; int c3=(c1+c2)/2; firstImage(xi,yi)->red= c3; c1 = firstImage(xi, yi)->blue; c2 = secondImage(xi, yi)->blue; c3=(c1+c2)/2; firstImage(xi,yi)->blue= c3; c1 = firstImage(xi, yi)->green; c2 = secondImage(xi, yi)->green; c3=(c1+c2)/2; firstImage(xi,yi)->green= c3; } } return firstImage; }
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. */ 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<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; }
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) { /** * @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 DFSfill(PNG& img, int x, int y, colorPicker & fillColor, int tolerance, int frameFreq) { animation anim; int count = 0; if( x > img.width()-1 || y > img.height()-1) return anim; bool done[img.width()][img.height()]; for(int i=0;i < img.width();i++) for(int j=0;j < img.height();j++) { done[i][j] = false; } int origred = img(x,y)->red; int origgreen = img(x,y)->green; int origblue = img(x,y)->blue; int redsqr = 0; int grnsqr = 0; int blusqr = 0; int tolcheck = 0; Stack<int> rackx; Stack<int> racky; int a = 0; int b = 0; rackx.push(x); racky.push(y); //done[x][y]= true; while(!(rackx.isEmpty())) { a = rackx.pop(); b = racky.pop(); redsqr = (img(a,b)->red-origred) * (img(a,b)->red-origred); grnsqr = (img(a,b)->green-origgreen) * (img(a,b)->green-origgreen); blusqr = (img(a,b)->blue-origblue) * (img(a,b)->blue-origblue); tolcheck = redsqr + grnsqr + blusqr; if((tolcheck <= tolerance && !done[a][b])) { *img(a,b) = fillColor(a,b); count++; //done[a][b] = true; if(a+1 < img.width()) { if(!done[a+1][b]) { rackx.push(a+1); racky.push(b); //done(right[0],right[1]) = tru } } if(b+1 < img.height()) { if(!done[a][b+1]) { rackx.push(a); racky.push(b+1); } } if(a-1 > 0) { if(!done[a-1][b]) { rackx.push(a-1); racky.push(b); } } if(b-1 > 0) { if(!done[a][b-1]) { rackx.push(a); racky.push(b-1); } } } done[a][b] = true; if(frameFreq == count ) { anim.addFrame(img); count = 0; } } return anim; }