Termbank
  1. A
    1. API
    2. ASAN
    3. Access specifiers
  2. C
    1. Constructor
    2. const
  3. G
    1. GDB
    2. g++
  4. M
    1. Make
    2. Memory Leak
  5. P
    1. Pointer
  6. R
    1. Recursion
  7. S
    1. std
  8. V
    1. Valgrind
Completed: / exercises

MP_COLLAGE: Classes, Inheritance, and Operators

Due date: 2024-04-09 23:59.
University of Oulu Collage

Description

Welcome to the final assignment of our image manipulation series. In this last installment, we will utilize the classes you are already familiar with from previous labs and MPs, including RGBAPixel, PNG, and Drawable, to create a versatile new class called Canvas. The purpose of this class is to enable blending of various shapes and images.
To accomplish this, this new Canvas class will inherit from Drawable. Another new CanvasItem class will inherit from PNG and will allow you to blend scaled, colorized and repositioned PNGs on top of each other. By the end of this assignment, you will have the means to create fun collages, such as the one on top of this page, using the Canvas class.

Download The Provided Files

Start with downloading and unzipping the provided files from here: mp_collage.zip.
In your provided mp_collage directory you will find many files, however, to complete your assignment you will only need to modify the following files:

Compiling and Running the Provided Code

We provided you with the set of files that properly compiles with:
make
The result is two executable files, collage and collage-asan. Try running
./collage
and you will find two new output files, out.png and collage.png, in your current directory. Check how they look right now (open the files in Windows or any other method).
The provided executable isn't functioning correctly at the moment, so your task is to fix the files to get the correct output images. Right now, collage.png only shows the background aurora image and a white rectangle, but once you make the necessary corrections, it should look just like the collage at the top of the page. You'll need to overlay four new images on the background, with a frame around three of them and a partially transparent University of Oulu logo.
Now, let's talk about out.png. The current code only renders the final layer, in which the text “This text is in the foreground” appears on the black background. Instead, the image is supposed to have some text, lines, and triangles all blended together using the alpha channel. Take a look at the animation below for a visual representation of all of the layers, and the final desired result:
Layers illustration
The solution images are also provided to you in soln_out.png and soln_collage.png. Now, let's begin resolving the problems by breaking down the tasks into separate exercises.
You can consider the assignment solved once the diff command applied to the solution images produces no output or differences between them.
diff out.png soln_out.png
diff collage.png soln_collage.png

Understanding The Provided Code

The goal of this assignment is to fix definitions of two new classes, Canvas and CanvasItem located in their appropriate files canvas.{cpp,h} and canvasitem.{cpp,h}.
A CanvasItem object is a subclass of the PNG class. This means it inherits all the member functions of the PNG class; so anything you could do with a PNG, you can also do with a CanvasItem. The CanvasItem class represents an individual item that can be added to a Canvas. It will hold information such as the position, size, and appearance of the item.
Now, the Canvas class is responsible for managing a collection of CanvasItems. It allows you to add, remove, and manipulate items within the canvas. We set it up so that the Canvas class inherits from Drawable, which allows it to implement the draw() function to render all of the CanvasItems on the Canvas.

Exercise 1: Data Structure to Hold CanvasItems in a Canvas

You first task is to establish a suitable way to store the CanvasItems within the Canvas class. In canvas.h, they are currently stored in a variable called items as a pointer to only a single CanvasItem:
CanvasItem* items;
Your task is to redefine the storage structure for items in any way you like. You can choose any data structure we have already covered in the course. The new structure should have the capability to hold a minimum of 10 CanvasItems and ensure that they retain the order in which they are added.

Exercise 2: Constructor, Destructor, Add, and draw Functions

Once you have modified the declaration of the variable items in the previous exercise, it is likely that the code has become broken and does not compile anymore. To ensure that it compiles again, you need to fix the following functions in the canvas.cpp file: the constructor, the destructor, the Add, and the draw functions.
Here are the requirements for each function:
After you fix the above functions your code should compile. Your Canvas class can now manage multiple items:
make clean
make
./collage
If you check the out.png and collage.png, you can see that the items are not yet rendered properly. Although rendering indeed happens in the correct order, you won't be able to see it yet because the CanvasItems are stacked on top of each other while transparency is not yet implemented. To fix this, you'll need to do some more work in canvas.cpp and canvasitem.cpp. You will also need to know a bit about alpha blending, methods in computer graphics for handling layers and transparency.

Exercise 3: CanvasItem Constructor

The constructor of the CanvasItem class needs to be properly configured to enable rendering multiple CanvasItems blended together on a Canvas while handling transparency correctly.
In the canvasitem.cpp file, modify the constructor to set the default alpha values of every pixel to 0, indicating transparency. Although at 0 alpha it won’t show, it’s best to be thorough and set the default values of all other color channels (red, green, and blue) to 255, which matches the PNG constructor and indicates full intensity. This ensures that newly created CanvasItems have transparent pixels by default. When drawing shapes, pixels outside of the shape’s area are unaffected and will therefore remain transparent. This way, drawing and blending a triangle, for example, will add just the triangle to the image, instead of a white rectangle containing a triangle.
Tip: Normally, when accessing individual pixels in a PNG image object, we use the function call operator () defined in the PNG class, like this:
RGBAPixel* pixel = image(x, y);
However, in the context of the CanvasItem class, which inherits from PNG, you can directly access this operator by dereferencing this, as in:
RGBAPixel* pixel = (*this)(x, y);
Alternatively, you could call the operator () explicitly:
RGBAPixel* pixel = operator()(x, y);

Exercise 4: getBlendedPixel Function

We need to have a proper way to access the color of individual pixels of each CanvasItem. As CanvasItem inherits from PNG, it seems tempting to access the RGBA values of an individual pixel of the CanvasItem, by simply calling the PNGs function call operator ():
RGBAPixel * PNG::operator()(size_t x, size_t y);
However, CanvasItem has a private variable called color_, defined in canvasitem.h that should be blended with each pixel without modifying the source image. In this case, we want to multiply each pixel with color_ component-wise. In other words, we need a function that gets the pixel in given coordinates, creates a copy of it, then multiplies it with color_ and returns it.
Your task is to finish writing the following function responsible for returning the values of blended pixels of the CanvasItem:
RGBAPixel getBlendedPixel(size_t x, size_t y);
To achieve this, use the following blending algorithm: multiply each RGBA channel of every CanvasItem pixel with the corresponding RGBA channel of color_, and then divide the result by 255. This division ensures that the resulting color values are normalized within the range of 0 to 255. Note: if our color values were defined as 0-1 instead of 0-255, that division would be unnecessary.
After fixing this, you will see the change in colors of the out.png file, as the text will appear red. That is because the foreground.png normally has white text, but now the white pixels are being multiplied with red, which results in red.

Exercise 5: draw Function

As you can see in the comments of the Canvas::draw function in canvas.cpp, there are a few more things that need to be fixed. You already made sure that the first loop iterates through the items in the correct order, however, there are three more aspects to fix about the function. You can tackle these issues in any order you like, but we listed them in the order of their location in the code of the Canvas::draw function:

Properly Arranging CanvasItems on Canvas

Currently, each item is placed onto the canvas at a default position and scale. However, we need to place them on the canvas according to the values of their item->scale() and item->position()
Start with uncommenting the following line:
// You should uncomment the below line and use in the section below
Vector2 pos = item->position();
As you are iterating through each pixel (x,y) of each item, you need to update these pixels to the new values (x1, y1) according to their sc and pos. Do this by updating the following lines:
// Modify the two lines below
int x1 = x;
int y1 = y;
Once you scale up a CanvasItem, it is possible for gaps to appear between adjacent pixels in the pixel structure. This means that after the item is scaled up, pixels that were originally adjacent may no longer be adjacent, resulting in empty spaces between them.
To address this issue, there are two loops in the code that iterate through the xs and ys coordinates of the adjacent pixels, filling in the gaps. To ensure that your (x1, y1) coordinates include these adjacent pixels, add xs to x1 and ys to y1:
// Add these lines to your code
x1 += xs;
y1 += ys;
Once this is fixed, you should see multiple boxes positioned around the image in collage.png!

Alpha Blending of CanvasItems

Alpha blending has not been implemented. Alpha blending is a technique that combines the colors of overlapping pixels based on their transparency, resulting in smooth and transparent composite images. The idea is to interpolate between the new and the old pixel based on the new pixel’s alpha value. This is called straight alpha blending. The formula looks like this:
result = (source.RGB * source.A) + (dest.RGB * (1 - source.A))
In other words when drawing a pixel with 0 alpha, nothing should be drawn. Alpha 255 means the previous pixel is completely replaced by the new pixel, and with alpha 1-254, the two colors are interpolated smoothly.
Note, however, that you must handle each RGB channel separately and of course follow the syntax and naming in our code, so don’t just copy that line!
Once this is fixed, you should see other layers below the foreground text in out.png!
After solving all of these, the structure of these images should start to become apparent, but some issues still remain to be fixed.

Exercise 6: Remove and Swap Function

Although out.png should show the correct output now, collage.png has some issues, which we can resolve by fixing some functions in canvas.cpp.
Finally, collage.png should match soln_collage.png! I also encourage experimenting with changing things around in main.cpp or adding your own images, since there are more things that you could do here, such as applying color to the collage images or scaling them. That might also reveal some flaws in our implementations. After all, image manipulation can get quite tricky!

Exercise 7: One More Thing

While you have corrected the output .png files, there is still one issue remaining: there are memory leaks in your program, which our autograder will detect. To fix the memory leaks, go through drawable.h and canvas.h files. Are all of the principles of inheritance discussed in lectures implemented properly? Once you fix any bugs, you will be done with this assignment.

Collage

Make sure you got the correct output for out.png by completing the following steps:
  • data structure to hold CanvasItems;
  • Canvas constructor, destructor, add, and draw functions;
  • CanvasItem constructor;
  • getBlendedPixel function;
  • draw function.
To get the correct output for collage.png, you also need to have implemented the Remove and Swap functions.
Finally, make sure your executable has no memory leaks.

Allowed filenames: canvas.h, canvas.cpp, canvasitem.h, canvasitem.cpp, drawable.h

Warning: You have not logged in. You cannot answer.

Acknowledgments

We would like to express our gratitude to prof. Cinda Heeren and the student staff of the UIUC CS Data Structures course for creating and sharing the programming exercise materials that have served as the basis for the labs used in this course.
Revised by: Elmeri Uotila and Anna LaValle
?
API stands for Application Programming Interface. In the context of this course, you can consider the header files to be such interfaces, as they determine what class functions and properties are public and thus accessible from anywhere.
AddressSanitizer (ASAN) is a memory error detector for C/C++. In this course, the makefiles will typically compile an executable that uses ASAN, with "-asan" at the end of its name.
The two notable access specifiers are:
  • public: class members defined after the public keyword are accessible from outside the class.
  • private: class members are generally private by default and thus not accessible from the outside
Constructor is a special non-static member function of a class that is used to initialize objects of its class type. A constructor is called upon initialization of an object. A constructor without arguments is a default constructor, but constructors that take arguments can be defined.
GDB, the GNU Project debugger, allows you to see what is going on `inside' another program while it executes -- or what another program was doing at the moment it crashed.
GDB can do four main kinds of things (plus other things in support of these) to help you catch bugs in the act:
  • Start your program, specifying anything that might affect its behavior.
  • Make your program stop on specified conditions.
  • Examine what has happened, when your program has stopped.
  • Change things in your program, so you can experiment with correcting the effects of one bug and go on to learn about another.
GNU Make is a tool which controls the generation of executables and other non-source files of a program from the program's source files. Make gets its knowledge of how to build your program from a file called the makefile, which lists each of the non-source files and how to compute it from other files. When you write a program, you should write a makefile for it, so that it is possible to use Make to build and install the program.
Memory leak means that the program is not freeing up memory it reserves. The memory will be freed when the program terminates, but if a program keeps leaking more and more memory without terminating, it can become a huge issue!
A typical way for memory leaks to occur is reserving memory with new and not calling delete before the pointer goes out of scope.
Pointer variables store a memory address as their value. In other words, they point to some data. The data can be accessed by dereferencing the pointer. (Either like *p or p->...)
A recursive function calls itself from within it. The recursion call must be conditional or it would lead to an infinite loop.
Valgrind is another tool besides ASAN that you can use in this course. It can detect many memory-related errors that are common in C and C++ programs and that can lead to crashes and unpredictable behaviour.
const is a keyword meant to mark something as immutable
  • A const object cannot be modified: attempt to do so directly is a compile-time error, and attempt to do so indirectly (e.g., by modifying the const object through a reference or pointer to non-const type) results in undefined behavior.
  • const keyword on an object's member function prevents the object from being modified in the function
  • Pointer declarations can have 2 const keywords, one to make the data that's pointed at unable to be modified and one to make the pointer itself unable to be modified
Using const improves code readability and prevents accidental modification of objects.
g++ is a C++ compiler that we primarily use for this course. It is the C++ compiler for the GNU compiler collection. You may sometimes see gcc being used instead of g++, which was originally the GNU C compiler, but calling gcc generally also compiles .cpp files as C++. However, calling g++ is preferred.
In C++, std stands for Standard Library, which is a collection of commonly useful classes and functions. Typically, these are defined in the std namespace.