void *read_pgm_file(char *filename); /* Read a PGM file into the data structure
and return it. NULL reads from stdin */
void *new_black_pgm_file(int rows, int cols); /* Create a new black PGM file */
void write_pgm_file(void *pgm, char *filename); /* Write the PGM file to a file (NULL for stdout) */
int pgm_get_pixel(void *pgm, int r, int c); /* Get the value of a pixel */
void pgm_set_pixel(void *pgm, int r, int c, int value); /* Set the value of a pixel */
void jettison_pgm(void *pgm); /* Call free() on relevant pointers */
|
This is going to be implemented in jpgm.c. Let's just look at the internal struct definition and new_black_pgm_file().
#define talloc(type, num) (type *) malloc(sizeof(type)*(num))
typedef struct {
int rows;
int cols;
int **pixels;
} Pgm;
void *new_black_pgm_file(int rows, int cols)
{
Pgm *p;
int i, j;
p = talloc(Pgm, 1);
if (p == NULL) { return NULL; }
p->rows = rows;
p->cols = cols;
p->pixels = talloc(int *, p->rows);
if (p->pixels == NULL) { return NULL; }
for (i = 0; i < p->rows; i++) {
p->pixels[i] = talloc(int, p->cols);
if (p->pixels[i] == NULL) { return NULL; }
for (j = 0; j < p->cols; j++) p->pixels[i][j] = 0;
}
return (void *) p;
}
|
The Pgm struct is straightforward, holding rows, columns, and a two-dimensional array of pixels. If you haven't seen it before, talloc() is a nice little macro that simplifies malloc() calls. The pixels are all set to zero (black), And a pointer to the struct is returned, but cast to a (void *) so that information is hidden from whoever uses it.
Let's also look at write_pgm_file():
void write_pgm_file(void *pgm, char *filename)
{
FILE *f;
Pgm *p;
int i, j;
if (filename == NULL) {
f = stdout;
} else {
f = fopen(filename, "w");
if (f == NULL) { perror(filename); exit(1); }
}
p = (Pgm *) pgm;
fprintf(f, "P2\n%d %d\n255\n", p->cols, p->rows);
for (i = 0; i < p->rows; i++) {
for (j = 0; j < p->cols; j++) {
fprintf(f, "%d\n", p->pixels[i][j]);
}
}
if (filename != NULL) fclose(f);
}
|
This too is straightforward. First, we cast the pgm argument to a (Pgm *). Then we write out the PGM header information and the pixels.
Now, the program make_black_pgm.c uses these two procedures to create a black pgm file. It is excpetionally straightforward, and notice how it simply uses the (void *) representation of the PGM file:
main(int argc, char **argv)
{
int r, c;
void *pgm;
if (argc != 3
|| sscanf(argv[1], "%d", &r) != 1 || r <= 0
|| sscanf(argv[2], "%d", &c) != 1 || c <= 0) {
fprintf(stderr, "usage: make_black_pgm rows cols\n");
exit(1);
}
pgm = new_black_pgm_file(r, c);
write_pgm_file(pgm, NULL);
exit(0);
}
|
Try it out:
UNIX> make_black_pgm 25 100 > black.pgm |
This makes the following PGM file:
main(int argc, char **argv)
{
int r, c, i, j;
void *pgm;
if (argc != 3
|| sscanf(argv[1], "%d", &r) != 1 || r <= 0
|| sscanf(argv[2], "%d", &c) != 1 || c <= 0) {
fprintf(stderr, "usage: make_shaded_pgm rows cols\n");
exit(1);
}
pgm = new_black_pgm_file(r, c);
for (i = 0; i < r; i++) {
for (j = 0; j < c; j++) {
pgm_set_pixel(pgm, i, j, 255*i/r);
}
}
write_pgm_file(pgm, NULL);
exit(0);
}
|
Try it out:
UNIX> make_shaded_pgm 40 40 > shaded.pgm |
This makes the following PGM file:
int pgm_get_pixel(void *pgm, int r, int c)
{
Pgm *p;
p = (Pgm *) pgm;
if (r < 0 || r >= p->rows || c < 0 || c >= p->cols) return -1;
return (p->pixels[r][c]);
}
void pgm_set_pixel(void *pgm, int r, int c, int value)
{
Pgm *p;
p = (Pgm *) pgm;
if (r < 0 || r >= p->rows || c < 0 || c >= p->cols) return;
if (value < 0 || value > 255) return;
p->pixels[r][c] = value;
}
|
main(int argc, char **argv)
{
void *pgm;
pgm = read_pgm_file(NULL);
if (pgm == NULL) exit(1);
write_pgm_file(pgm, NULL);
}
|
Try it out:
UNIX> rw_pgm < meepit.pgm > output.pgm |
This makes a second copy of meepit.pgm:
meepit.pgm![]() |
output.pgm![]() |
Here's neg_pgm.c:
main(int argc, char **argv)
{
int i, j, pix;
void *pgm;
pgm = read_pgm_file(NULL);
for (i = 0; i < pgm_rows(pgm); i++) {
for (j = 0; j < pgm_cols(pgm); j++) {
pix = pgm_get_pixel(pgm, i, j);
pgm_set_pixel(pgm, i, j, 255-pix);
}
}
write_pgm_file(pgm, NULL);
}
|
And here's hflip_pgm.c:
main(int argc, char **argv)
{
int i, j, pix;
void *pgm, *newpgm;
pgm = read_pgm_file(NULL);
newpgm = new_black_pgm_file(pgm_rows(pgm), pgm_cols(pgm));
for (i = 0; i < pgm_rows(pgm); i++) {
for (j = 0; j < pgm_cols(pgm); j++) {
pix = pgm_get_pixel(pgm, i, j);
pgm_set_pixel(newpgm, i, pgm_cols(pgm)-j-1, pix);
}
}
write_pgm_file(newpgm, NULL);
}
|
Try them out:
UNIX> neg_pgm < meepit.pgm > meepit-neg.pgm UNIX> hflip_pgm < meepit.pgm > meepit-hflip.pgm |
Here are the outputs:
meepit.pgm![]() |
meepit-neg.pgm![]() |
meepit-hflip.pgm![]() |