Well you have some work to do... You need to specify a function for each geometry form which would fill your image.
Here an example which is more C++ like where a class Image handle the methods to compute the geometric forms such as makeFillCircle(). Image class also handles the PBM output. Remark how easy it is now to save it using std::ostream operator<< !
#include <vector>
#include <iostream>
#include <algorithm>
struct Point {
Point(int xx, int yy) : x(xx), y(yy) {}
int x;
int y;
};
struct Image {
int width;
int height;
std::vector<int> data; // your 2D array of pixels in a linear/flatten storage
Image(size_t w, size_t h) : width(w), height(h), data(w * h, 0) {} // we fill data with 0s
void makeFillCircle(const Point& p, int radius) {
// we do not have to test every points (using bounding box)
for (int i = std::max(0, p.x - radius); i < std::min(width-1, p.x + radius) ; i ++) {
for (int j = std::max(0,p.y - radius); j < std::min(height-1, p.y + radius); j ++) {
// test if pixel (i,j) is inside the circle
if ( (p.x - i) * (p.x - i) + (p.y - j) * (p.y - j) < radius * radius ) {
data[i * width + j] = 1; //If yes set pixel on 1 !
}
}
}
}
};
std::ostream& operator<<(std::ostream& os, const Image& img) {
os << "P1\n" << img.width << " " << img.height << "\n";
for (auto el : img.data) { os << el << " "; }
return os;
}
int main() {
Image img(100,100);
img.makeFillCircle(Point(60,40), 30); // first big circle
img.makeFillCircle(Point(20,80), 10); // second big circle
std::cout << img << std::endl; // the output is the PBM file
}
Live Code
Result is :

And voilà... I let you the fun to create your own functions makeRectangle(), makeTriangle() for your needs ! That requires some little maths !
Edit BONUS
As asked in the comment, here a little member function which would save the image in a file. Add it in the Image class (you can also if you prefer have a free function outside the class). Since we have a ostream operator<< this is straightforward :
void save(const std::string& filename) {
std::ofstream ofs;
ofs.open(filename, std::ios::out);
ofs << (*this);
ofs.close();
}
An other way without modifying the code would be to catch the output of the program in a file with something like this on your terminal :
./main >> file.pgm