# CSCI 103 Fall 2017: Introduction to Programming

## Bitmap Graphics

• Lab 5 due Friday
• No HW this week (HW 5 next week)

### Working with Libraries

For Lab 5 and PA 2, you will be accessing new libraries that we will give you.

• bmplib library (Lab 5): direct bitmap pixel manipulation via arrays. Allows loading, saving, and editing 256-by-256 pixel images (grayscale and color).
• draw library (PA2): high-level drawing commands (lines, circles) and animation.

Remember that built-in C++ libraries are accessed with:

#include <iostream>

The syntax for a non-built-in library uses quotes:

#include "bmplib.h"

A C++ library includes two main parts:

1. the "header file," e.g. bmplib.h, which describes the functions provided by the library
2. the full source code, in this case bmplib.cpp

Let's look at bmplib.h...

After the boilerplate, we have

• definitions of constants that you can use once you include the library (e.g. SIZE is 256)
• prototypes of functions that the library provides

Earlier, we found out that prototypes are enough to use a function that is defined somewhere else.

But where are the functions actually defined?

Let's peek at bmplib.cpp...

Don't try to understand the full file. The only important thing is that each function declared in the header is actually fully defined here. So in summary,

• the .h file tells you what a library can do (API/Application Program Interface)
• the .cpp file tells you how it does it (implementation details)

### Compiling with Libraries

The bitmap library comes with a demonstration program, demo.cpp. But it uses the functions defined in the bmplib.cpp library. How can we compile two files at once?

Actually, we have some choices.

• You can compile two files together like this:
compile demo.cpp bmplib.cpp -o demo

This is the best approach for this course when you don't have a Makefile handy.

• You can create a Makefile and run make.
This is the preferred approach for CSCI 103 and we'll always give you a Makefile. You can peek at the Makefile... to use it, we simply run

make
• You can compile each file separately, and then combine them.
compile -c bmplib.cpp -o bmplib.o # create an "object"
compile -c demo.cpp   -o demo.o   # create an "object"
compile demo.o bmplib.o -o demo   # "link" them, make executable


You probably shouldn't be doing it this way in CS 103. But in a professional setting, this can help avoid lengthy recompile times.

### Grayscale Bitmap Processing

bmplib deals with 256-by-256 pixel (SIZE-by-SIZE) images:

• a grayscale image is an  unsigned char [SIZE][SIZE]
• black is 0, white is 255
• this is the coordinate scheme. It's NOT cartesian:
 bmplib uses [row][column] indexing. The first index is the row (top 0, bottom 255), and the second is the column (left 0, right 255). Cartesian coordinates

To get started:

mkdir demo-bmplib; cd demo-bmplib
wget ftp://bits.usc.edu/cs103-2/demo-bmplib.tar
tar -xvf demo-bmplib.tar
make                  # note: might ask for password first time.


If asked, type in "developer" (though the screen hides what you type). Then run:

./demo
eog cross.bmp

### Using bmplib in Programs

Here is a short example program:

// example.cpp
#include <cstdlib>
#include "bmplib.h"

int main() {
unsigned char image[SIZE][SIZE];
for (int i=0; i<SIZE; i++) {
for (int j=0; j<SIZE; j++) {
image[i][j] = rand() % 256;
}
}
showGSBMP(image); // from bmplib
}


What do we expect this program to display?

To compile and run, we use

compile example.cpp bmplib.cpp -o example
./example

Use this pattern for compiling everything else today.

Simple fill exercises. Try re-creating the following images in C++ by using a nested loop and putting NON-random values in the 2D array:

Here is a link to a solution for the first of the three.

### Plotting

In lab 5, you will create a program capable of plotting rectangles and ellipses. When you plot a shape, you are selectively changing a set of pixels. For example, here is code to plot a diagonal line:

#include "bmplib.h"

int main() {
unsigned char image[SIZE][SIZE];
// black background
for (int i=0; i<SIZE; i++) {
for (int j=0; j<SIZE; j++) {
image[i][j] = 0;
}
}
// plot white diagonal line
for (int t=0; t<SIZE; t++) {
image[t][t] = 255;
}
showGSBMP(image);
}


This is similar to demo.cpp. Here are three more plotting exercises for practice:

 X marks the spot The function y = x^2. Hint: to get all pixels, think of it as a square root. A sine wave

### Transformations

The bmplib library also lets us load existing files. Once loaded, we can transform them, like the following example demonstrates. You can get the image from

wget http://bits.usc.edu/files/cs103/graphics/elephant.bmp
#include "bmplib.h"

int main() {
unsigned char image[SIZE][SIZE];
for (int i=0; i<SIZE; i++) {
for (int j=0; j<SIZE; j++) {
image[i][j] = 255-image[i][j]; // invert color
}
}
showGSBMP(image);
}


This looks like:

 Original file Displayed image

So this example inverted the colors (white vs black).

Can we instead invert the rows (top vs bottom)? Here's some code that tried to accomplish this:

#include "bmplib.h"

int main() {
unsigned char image[SIZE][SIZE];
for (int i=0; i<SIZE; i++) {
for (int j=0; j<SIZE; j++) {
image[i][j] = image[255-i][j]; // flip top and bottom?
}
}
showGSBMP(image);
}


Let's try it out and see what happens...

What was the cause of this bug?

By the time we are reading the bottom half of the image, we've already copied the bottom into the top half.

Let's try it again, but using two separate 2D arrays for the original image and the transformed image.

#include "bmplib.h"

int main() {
unsigned char src[SIZE][SIZE];
unsigned char dest[SIZE][SIZE];
for (int i=0; i<SIZE; i++) {
for (int j=0; j<SIZE; j++) {
dest[i][j] = src[255-i][j];
}
}
showGSBMP(dest);
}


Transformation exercises. Try to re-create the following effects:

 Diagonal flip Tile Zoom

You can access all of the examples at: http://bits.usc.edu/files/cs103/graphics/

### Parallel Arrays and Redirecting Input

As part of the next programming assignment (N-Body), you need to be able to do two things.

• Store different types of information about a series of objects (the position, velocity, mass and name of planets).
• Read this data from a file, so that you don't have to type it in each time.

Let's talk about storing information first.

Suppose we want a program that keeps track of the name, age, and weight of several cats. Perhaps it reads input in this format:

3
Bella        9    4.5
Meowthuselah 2073 0.003
Garfield     57   30.0

where the first line is the number of cats to follow, and the numbers per line are a name, age, and weight.

How can we store this information?

Here's a sample program:

/*
catstats: accepts input in the format

3
Bella        9    4.5
Meowthuselah 2073 0.003
Garfield     57   30.0

First line is # of cats.
Each line contains name, age, and weight of a cat.

Program computes the maximum cat age, and
lists all the cats with age below half the maximum.

We assume # cats <= 100 and each name is <= 29 chars long
*/

#include <iostream>
using namespace std;

int main() {
// variables to store input
int numcats;
char name[100][30]; // 100 names, each <= 30 bytes
int age[100];
double weight[100];

// read the input
cin >> numcats;
for (int i=0; i<numcats; i++) {
cin >> name[i];
cin >> age[i];
cin >> weight[i];
}

// compute maximum age
int max_age = 0;
for (int i=0; i<numcats; i++)
if (age[i] > max_age)
max_age = age[i];

// print output
cout << "The maximum age is " << max_age << endl;
cout << "These cats have age less than half the maximum: " << endl;
for (int i=0; i<numcats; i++) {
if (age[i] < max_age / 2.0) { // avoid int division
cout << name[i] << endl;
}
}

}

We need to use arrays: one for the names, one for the ages, and one for the weights. This parallel arrays idiom is useful whenever we have to store identical types of information about a sequence of objects.

Let's try running this program on our VM on the sample input.

cd (whereever you like)
wget ftp://bits.usc.edu/cs103-2/catstats.tar
tar xvf catstats.tar
compile catstats

### Input Redirection

If we have a lot of cats, it gets quite inconvenient to re-type in the input every time we run the program. The shell therefore makes it possible to redirect the input from a file. (This is possible not only in Linux, but also in the OSX Terminal and the Windows Command Prompt.)

For example, the file mycats.txt has information about my cats (we'll open it up in gedit to check). Instead of typing it in manually, I will execute

./catstats < mycats.txt

which will execute the program, but taking input from the mycats.txt file instead of from the keyboard. Try it out! It should print

The maximum age is 2073
These cats have age less than half the maximum:
Bella
Garfield

What happens when we run the following?  ./catstats < longcats.txt

### Exercise

Modify catstats.cpp to create a program called fatcats that prints out the average weight and all the cats with weight above the average. You should get the following outputs on these test cases:

$./fatcats < mycats.txt The average weight is 11.501 Here are the cats with above-average weight: Meowthuselah Garfield$ ./fatcats < longcats.txt
The average weight is 8.19767
Here are the cats with above-average weight:
Bella
Max
Kitty
... some cats omitted
Jasmine
Tucker
Skittles

### Output Redirection

You can also redirect the output of a program. This can be quite convenient so that you can save the output and look at it in a text editor instead of always scrolling up your terminal. Look at how the > symbol is used:

./hello_world > hello.txt
• run hello_world, and print the output to hello.txt instead of the screen
./fatcats < mycats.txt > myfatcats.txt
• run fatcats, but using mycats.txt as the input source, and printing the ouput to myfatcats.txt instead of the screen