- •Аннотация
- •Содержание
- •Введение
- •1. Реализация
- •1.1. Рисование окружности
- •1.2. Наложение фильтра
- •1.3. Разделение изображения на части
- •1.4. Взаимодействие с программой
- •1.5. Дополнительные функции
- •1.6. Соглашение
- •2. Примеры
- •Заключение
- •Список использованных источников
- •Приложение а иСходный код программы
1.6. Соглашение
Взаимодействие с программой происходит с помощью CLI (Command Line Interface). Сначала считываются основные аргументы, затем уже флаги для каждого основного аргумента. За 1 раз программа обрабатывает 1 основной аргумент. Важно: все побочные аргументы должны идти строго после основного аргумента, иначе программа выведет help страницу, и обработка изображения не случится. Имя файла для обработки должно идти после своего флага --file. Однако флаг с именем файла может быть указан в любом месте после основного флага операции. В случае, если программе будут поданы не валидные аргументы для ширины, цвета и т.д. (например, строка, вместо числа), программа выведет сообщение о не валидности, и обработка произведена не будет. Пользователь может передать один флаг только один раз (за исключением -h/--help и -i/--info). Программа работает с PNG-файлами без сжатия, 24 бита на цвет.
2. Примеры
1. Функция help см. рис. 1.
Рисунок 1 – Пример функции help
2. Функция info см. рис. 2.
Рисунок 2 – Пример функции info
3. Функция circle см. рис. 3, см. рис. 4.
./a.out -c --centre 600,320 --radius 200 --color 154,24,125 --width 70 --fill 255,255,255 --file Example.png
Рисунок 3 – Пример функции circle №1
./a.out --circle --start 250,250 --end 600,600 --color 34,65,165 --width 30 --file Example.png
Рисунок 4 – Пример функции circle №2
4. Функция filter см. рис. 5, см. рис. 6.
./a.out -f -b 255 --file Example.png
Рисунок 5 – Пример функции filter №1
./a.out --filter -g 0 --file Example.png
Рисунок 6 – Пример функции filter №2
5. Функция split см. рис. 7, см. рис. 8.
./a.out -s -n 2 -m 4 --color 255,84,22 --width 50 --file Example.png
Рисунок 7 – Пример функции split №1
./a.out -s -n 5 -m 7 --color 65,24,187 --width 4 --file Example.png
Рисунок 8 – Пример функции split №2
Заключение
В процессе выполнения курсовой работы была разработана программа для обработки PNG-изображений. Разработанная программа имеет интерфейс командной строки и может гибко взаимодействовать с форматом. Кроме того, на основе классических алгоритмов растеризации для отрисовки геометрических фигур были реализованы функции. Программа имеет help страницу и возможность вывода необходимой информации на экран.
Список использованных источников
1. Язык программирования Си: монография / Б.Керниган, Д.Ритчи; Пер. с
англ. под ред. В.С.Штаркмана. - 3-е изд., испр. - СПб. : Невский диалект, 2001. - 351 с.
2. Реализации алгоритмов/Алгоритм Брезенхэма – Викиучебник URL: https://ru.wikibooks.org/wiki/Реализации_алгоритмов/Алгоритм_Брезенхэма
3. libpng-manual.txt URL: http://www.libpng.org/pub/png/libpng-manual.txt
4. Лекция по программированию URL: https://youtu.be/fCQRboLFAbg
5. Заливка – Википедия URL: https://ru.wikipedia.org/wiki/Заливка
Приложение а иСходный код программы
Название файла: main.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <png.h>
#include <getopt.h>
#include <stdbool.h>
typedef struct Png{
int width, height;
png_byte color_type;
png_byte bit_depth;
png_structp png_ptr;
png_infop info_ptr;
int number_of_passes;
png_bytep *row_pointers;
} PNG;
bool read_png(char *file_name, PNG *image);
void write_png(char *file_name, PNG *image);
void filter(PNG *image, char color, int value);
void split(PNG *image, int N, int M, int W, int* color);
void circle(PNG* image, int* start, int* end, int* center, int r, int w, int* color, int* fill);
bool scan_color(char* str_color, int* color){
if(str_color == NULL)
return false;
char *end1 = NULL, *end2 = NULL, *end3 = NULL;
char* c1 = strtok(str_color, ",");
char* c2 = strtok(NULL, ",");
char* c3 = strtok(NULL, ",");
if(c1 != NULL && c2 != NULL && c3 != NULL){
color[0] = (int) strtol(c1, &end1, 10);
color[1] = (int) strtol(c2, &end2, 10);
color[2] = (int) strtol(c3, &end3, 10);
if(*end1 == '\0' || *end2 == '\0' || *end3 == '\0'){
if(color[0] >= 0 && color[0] <= 255 && color[1] >= 0 && color[1] <= 255 && color[2] >= 0 && color[2] <= 255){
return true;
}
}
}
return false;
}
bool scan_coordinates(char* str, int* coord){
if(str == NULL)
return false;
char *end1 = NULL, *end2 = NULL;
char* c1 = strtok(str, ",");
char* c2 = strtok(NULL, ",");
if(c1 != NULL && c2 != NULL){
coord[0] = (int) strtol(c1, &end1, 10);
coord[1] = (int) strtol(c2, &end2, 10);
if(*end1 == '\0' || *end2 == '\0'){
if(coord[0] >= 0 && coord[1] >= 0){
return true;
}
}
}
return false;
}
bool scan_number(char* str, int* number){
if(str == NULL)
return false;
char* end = NULL;
*number = (int) strtol(str, &end, 10);
if(*end != '\0')
return false;
if(*number < 0)
return false;
return true;
}
void case_filter(int argc, char* argv[]){
opterr = 0;
const char* short_options = "r:g:b:";
const struct option long_options[] = {
{"red", required_argument, NULL, 'r'},
{"green", required_argument, NULL, 'g'},
{"blue", required_argument, NULL, 'b'},
{"file", required_argument, NULL, 'i'},
{NULL, 0, NULL, 0}};
int rez, option_index, value = -1, sum = 0;
char *file_name = NULL;
char color = '0';
while((rez = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1){
switch(rez){
case 'r':
case 'g':
case 'b':
color = rez;
if(optarg == NULL){
fprintf(stderr, "Неправильный ввод числа\n");
return;
}
char* end = NULL;
value = (int) strtol(optarg, &end, 10);
if(*end != '\0'){
fprintf(stderr, "Неправильный ввод числа\n");
return;
}
if(!(value == 255 || value == 0)){
fprintf(stderr, "Неправильный ввод числа\n");
return;
}
sum++;
break;
case 'i':
if(optarg == NULL){
fprintf(stderr, "Неправильный ввод имени файла\n");
return;
}
file_name = optarg;
sum++;
break;
default:
fprintf(stderr, "Неправильный ввод флага\n");
return;
}
}
if(sum != 2 || file_name == NULL || color == '0'){
fprintf(stderr, "Введено неправильное количество аргументов\n");
return;
}
PNG image;
if(read_png(file_name, &image)){
filter(&image, color, value);
write_png(file_name, &image);
}
}
void case_circle(int argc, char* argv[]){
opterr = 0;
const char* short_options = "";
const struct option long_options[] = {
{"start", required_argument, NULL, 't'},
{"end", required_argument, NULL, 'e'},
{"centre", required_argument, NULL, 'x'},
{"radius", required_argument, NULL, 'a'},
{"width", required_argument, NULL, 'w'},
{"color", required_argument, NULL, 'o'},
{"fill", required_argument, NULL, 'l'},
{"file", required_argument, NULL, 'i'},
{NULL, 0, NULL, 0}};
int rez, option_index, w = -1, r = -1, start[2] = {-1, -1}, end[2] = {-1, -1}, center[2] = {-1, -1}, color[3] = {-1, -1, -1}, fill[3] = {-1, -1, -1}, sum = 0;
char *file_name = NULL;
bool bool_start = false, bool_end = false, bool_center = false, bool_color = false, bool_fill = false;
while((rez = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1){
switch(rez){
case 't':
if(!(scan_coordinates(optarg, start))){
fprintf(stderr, "Неправильный ввод координат\n");
return;
}
bool_start = true;
sum++;
break;
case 'e':
if(!(scan_coordinates(optarg, end))){
fprintf(stderr, "Неправильный ввод координат\n");
return;
}
bool_end = true;
sum++;
break;
case 'x':
if(!(scan_coordinates(optarg, center))){
fprintf(stderr, "Неправильный ввод координат\n");
return;
}
bool_center = true;
sum++;
break;
case 'a':
if(!(scan_number(optarg, &r))){
fprintf(stderr, "Неправильный ввод числа\n");
return;
}
sum++;
break;
case 'w':
if(!(scan_number(optarg, &w))){
fprintf(stderr, "Неправильный ввод числа\n");
return;
}
sum++;
break;
case 'o':
if(!(scan_color(optarg, color))){
fprintf(stderr, "Неправильный ввод цвета\n");
return;
}
bool_color = true;
sum++;
break;
case 'l':
if(!(scan_color(optarg, fill))){
fprintf(stderr, "Неправильный ввод цвета\n");
return;
}
bool_fill = true;
sum++;
break;
case 'i':
if(optarg == NULL){
fprintf(stderr, "Неправильный ввод имени файла\n");
return;
}
file_name = optarg;
sum++;
break;
default:
fprintf(stderr, "Неправильный ввод флага\n");
return;
}
}
if(!(file_name != NULL && w != -1 && bool_color == true && (bool_start == true && bool_end == true && (sum == 5 || sum == 6 && bool_fill == true) ||
bool_center == true && r != -1 && (sum == 5 || sum == 6 && bool_fill == true)))){
fprintf(stderr, "Введено неправильное количество аргументов\n");
return;
}
PNG image;
if(read_png(file_name, &image)){
circle(&image, start, end, center, r, w, color, fill);
write_png(file_name, &image);
}
}
void case_split(int argc, char* argv[]){
opterr = 0;
const char* short_options = "n:m:";
const struct option long_options[] = {
{"width", required_argument, NULL, 'w'},
{"color", required_argument, NULL, 'o'},
{"file", required_argument, NULL, 'i'},
{NULL, 0, NULL, 0}};
int rez, option_index, n = -1, m = -1, w = -1, color[3] = {-1, -1, -1}, sum = 0;
char *file_name = NULL;
bool bool_color = false;
while((rez = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1){
switch(rez){
case 'n':
if(!(scan_number(optarg, &n))){
fprintf(stderr, "Неправильный ввод числа\n");
return;
}
sum++;
break;
case 'm':
if(!(scan_number(optarg, &m))){
fprintf(stderr, "Неправильный ввод числа\n");
return;
}
sum++;
break;
case 'w':
if(!(scan_number(optarg, &w))){
fprintf(stderr, "Неправильный ввод числа\n");
return;
}
sum++;
break;
case 'o':
if(!(scan_color(optarg, color))){
fprintf(stderr, "Неправильный ввод числа\n");
return;
}
bool_color = true;
sum++;
break;
case 'i':
if(optarg == NULL){
fprintf(stderr, "Неправильный ввод имени файла\n");
return;
}
file_name = optarg;
sum++;
break;
default:
fprintf(stderr, "Неправильный ввод флага\n");
return;
}
}
if(sum != 5 || file_name == NULL || n == -1 || m == -1 || w == -1 || bool_color == false){
fprintf(stderr, "Введено неправильное количество аргументов\n");
return;
}
PNG image;
if(read_png(file_name, &image)){
split(&image, n, m, w, color);
write_png(file_name, &image);
}
}
void help(){
puts("Руководство по использованию программы:");
puts("Программа обрабатывает PNG-файлы c глубиной изображения 24 бита на пиксель\n");
puts("Обозначения:");
puts("\t x,y - целые, неотрицательный числа");
puts("\t a - целое, положительное число");
puts("\t c - число равное либо 0, либо 255");
puts("\tr,g,b - целые числа в диапозоне [0,255]\n");
puts("Функции/флаги:");
puts("-c/--circle -- рисование окружности");
puts("\t--start x,y - координаты левого верхнего квадрата, в который будет вписана окружность");
puts("\t--end x,y - координаты правого нижнего квадрата, в который будет вписана окружность");
puts("\tили");
puts("\t--centre x,y - координаты центра окружности");
puts("\t--radius a - радиус окружности\n");
puts("\t--width a - значение толщины окружности");
puts("\t--color r,g,b - значения цветов окружности");
puts("\t--fill r,g,b - значения цветов заливки окружности (данный флаг может отсутствовать)");
puts("\t--file <путь> - файл для записи изображения\n");
puts("-f/--filter -- наложение фильтра на изображение");
puts("\t-r c - значение цвета, устанавливаемое в красный цвет");
puts("\t-g c - значение цвета, устанавливаемое в зеленый цвет");
puts("\t-b c - значение цвета, устанавливаемое в синий цвет");
puts("\t--file <путь> - файл для записи изображения\n");
puts("-s/--split -- разделение изображения на N*M частей");
puts("\t-n a - количество частей по оси Y");
puts("\t-m a - количество частей по оси X");
puts("\t--width a - значение толищны линии");
puts("\t--color r,g,b - значения цветов линии");
puts("\t--file <путь> - файл для записи изображения\n");
puts("-h/--help - вывод справки о программе");
puts("-i/--info <путь> - вывод информации о PNG-файле");
}
void info(char* file_name){
PNG image;
if(read_png(file_name, &image)){
puts("Информация о файле:");
printf("Ширина изображения: %d\n", image.width);
printf("Высота изображения: %d\n", image.height);
printf("Тип цвета: %d\n", image.color_type);
printf("Глубина цвета: %d\n", image.bit_depth);
}
write_png(file_name, &image);
}
int main(int argc, char *argv[]){
opterr = 0;
const char* short_options = "hi:cfs";
const struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"info", required_argument, NULL, 'i'},
{"circle", no_argument, NULL, 'c'},
{"filter", no_argument, NULL, 'f'},
{"split", no_argument, NULL, 's'},
{NULL, 0, NULL, 0}};
int rez, option_index;
rez = getopt_long(argc, argv, short_options, long_options, &option_index);
switch(rez){
case 'h':
help();
break;
case 'i':
info(optarg);
break;
case 'c':
case_circle(argc, argv);
break;
case 'f':
case_filter(argc, argv);
break;
case 's':
case_split(argc, argv);
break;
default:
fprintf(stderr, "Неправильный ввод флага\n");
help();
}
return 0;
}
void put_pixel(PNG *image, int x, int y, int* color){
image->row_pointers[y][x * 3] = color[0];
image->row_pointers[y][x * 3 + 1] = color[1];
image->row_pointers[y][x * 3 + 2] = color[2];
}
void draw_vertical_line(PNG *image, int x, int* color){
if(x < 0 || x > image->width)
return;
for(int y = 0; y < image->height; y++){
put_pixel(image, x, y, color);
}
}
void draw_horizontal_line(PNG *image, int y, int* color){
if(y < 0 || y > image->height)
return;
for(int x = 0; x < image->width; x++){
put_pixel(image, x, y, color);
}
}
void xLine(PNG* image, int x1, int x2, int y, int* color){
while(x1 <= x2){
if(x1 >= 0 && x1 < image->width && y >= 0 && y < image->height)
put_pixel(image, x1, y, color);
x1++;
}
}
void yLine(PNG* image, int x, int y1, int y2, int* color){
while(y1 <= y2){
if(y1 >= 0 && y1 < image->height && x >= 0 && x < image->width)
put_pixel(image, x, y1, color);
y1++;
}
}
void fill_circle(PNG* image, int x, int y, int* color, int* fill){
if(0 <= y && y < image->height && 0 <= x && x < image->width
&& !(image->row_pointers[y][x * 3] == fill[0] && image->row_pointers[y][x * 3 + 1] == fill[1] && image->row_pointers[y][x * 3 + 2] == fill[2])
&& !(image->row_pointers[y][x * 3] == color[0] && image->row_pointers[y][x * 3 + 1] == color[1] && image->row_pointers[y][x * 3 + 2] == color[2])){
put_pixel(image, x, y, fill);
fill_circle(image, x + 1, y, color, fill);
fill_circle(image, x, y + 1, color, fill);
fill_circle(image, x - 1, y, color, fill);
fill_circle(image, x, y - 1, color, fill);
}
}
void circle_part(PNG* image, int x0, int y0, int r, int w, int* color, int part){
int out, in, y = 0;
if(w % 2){
out = r + w / 2;
in = r - w / 2;
}
else{
out = r + w / 2;
in = r - w / 2 + 1;
}
int erro = 1 - out, erri = 1 - in;
while(out >= y){
switch(part){
case 1:
xLine(image, x0 + in, x0 + out, y0 - y, color);
yLine(image, x0 + y, y0 - out, y0 - in, color);
break;
case 2:
xLine(image, x0 - out, x0 - in, y0 - y, color);
yLine(image, x0 - y, y0 - out, y0 - in, color);
break;
case 3:
xLine(image, x0 - out, x0 - in, y0 + y, color);
yLine(image, x0 - y, y0 + in, y0 + out, color);
break;
case 4:
xLine(image, x0 + in, x0 + out, y0 + y, color);
yLine(image, x0 + y, y0 + in, y0 + out, color);
}
y++;
if(erro < 0)
erro += 2 * y + 1;
else{
out--;
erro += 2 * (y - out + 1);
}
if(y > r - w / 2)
in = y;
else{
if(erri < 0)
erri += 2 * y + 1;
else{
in--;
erri += 2 * (y - in + 1);
}
}
}
}
void circle(PNG* image, int* start, int* end, int* center, int r, int w, int* color, int* fill){
int R = -1;
if(center[0] == -1 && center[1] == -1 && r == -1){
if(end[0] < start[0] || end[1] < start[1]){
fprintf(stderr, "Неправильный ввод координат\n");
return;
}
if((end[0] - start[0]) != (end[1] - start[1])){
fprintf(stderr, "Неправильный ввод координат, не квадрат\n");
return;
}
if((end[0] - start[0]) % 2){
int r = (end[0] - start[0]) / 2;
int x0 = start[0] + r, y0 = start[1] + r;
circle_part(image, x0, y0, r, w, color, 2);
circle_part(image, x0 + 1, y0, r, w, color, 1);
circle_part(image, x0, y0 + 1, r, w, color, 3);
circle_part(image, x0 + 1, y0 + 1, r, w, color, 4);
if(fill[0] != -1 && fill[1] != -1 && fill[2] != -1){
fill_circle(image, x0, y0, color, fill);
}
return;
}
R = (end[0] - start[0]) / 2;
center[0] = start[0] + R;
center[1] = start[1] + R;
}
if(R != -1)
r = R;
int out, in, y = 0;
if(w % 2){
out = r + w / 2;
in = r - w / 2;
}
else{
out = r + w / 2;
in = r - w / 2 + 1;
}
int erro = 1 - out, erri = 1 - in;
while(out >= y){
xLine(image, center[0] + in, center[0] + out, center[1] + y, color);
yLine(image, center[0] + y, center[1] + in, center[1] + out, color);
xLine(image, center[0] - out, center[0] - in, center[1] + y, color);
yLine(image, center[0] - y, center[1] + in, center[1] + out, color);
xLine(image, center[0] - out, center[0] - in, center[1] - y, color);
yLine(image, center[0] - y, center[1] - out, center[1] - in, color);
xLine(image, center[0] + in, center[0] + out, center[1] - y, color);
yLine(image, center[0] + y, center[1] - out, center[1] - in, color);
y++;
if(erro < 0)
erro += 2 * y + 1;
else{
out--;
erro += 2 * (y - out + 1);
}
if(y > r - w / 2)
in = y;
else{
if(erri < 0)
erri += 2 * y + 1;
else{
in--;
erri += 2 * (y - in + 1);
}
}
}
if(fill[0] != -1 && fill[1] != -1 && fill[2] != -1){
fill_circle(image, center[0], center[1], color, fill);
}
}
void split(PNG *image, int N, int M, int W, int* color){
if(N > image->height || M > image->width){
fprintf(stderr, "Неправильный ввод числа\n");
return;
}
float len = (float) (image->width - (M - 1)) / M;
float x = len;
while(x < image->width){
for(int i = x - W / 2; i < x - W / 2 + W; i++){
draw_vertical_line(image, i, color);
}
x += len + 1;
}
len = (float) (image->height - (N - 1)) / N;
float y = len;
while(y < image->height){
for(int i = y - W / 2; i < y - W / 2 + W; i++){
draw_horizontal_line(image, i, color);
}
y += len + 1;
}
}
void filter(PNG *image, char color, int value){
switch (color){
case 'r':
for(int y = 0; y < image->height; y++){
png_byte *row = image->row_pointers[y];
for(int x = 0; x < image->width * 3; x += 3){
row[x] = value;
}
}
break;
case 'g':
for(int y = 0; y < image->height; y++){
png_byte *row = image->row_pointers[y];
for(int x = 1; x < image->width * 3; x += 3){
row[x] = value;
}
}
break;
case 'b':
for(int y = 0; y < image->height; y++){
png_byte *row = image->row_pointers[y];
for(int x = 2; x < image->width * 3; x += 3){
row[x] = value;
}
}
}
}
bool read_png(char *file_name, PNG *image){
char header[8];
FILE *fp = fopen(file_name, "rb");
if(!fp){
fprintf(stderr, "Ошибка в открытии файла\n");
return false;
}
fread(header, sizeof(char), 8, fp);
image->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(!image->png_ptr){
fprintf(stderr, "Ошибка в png_create_read_struct\n");
return false;
}
image->info_ptr = png_create_info_struct(image->png_ptr);
if (!image->info_ptr){
fprintf(stderr, "Ошибка в png_create_info_struct failed\n");
return false;
}
if(setjmp(png_jmpbuf(image->png_ptr))){
fprintf(stderr, "Ошибка во время init_io\n");
return false;
}
png_init_io(image->png_ptr, fp);
png_set_sig_bytes(image->png_ptr, 8);
png_read_info(image->png_ptr, image->info_ptr);
image->width = png_get_image_width(image->png_ptr, image->info_ptr);
image->height = png_get_image_height(image->png_ptr, image->info_ptr);
image->color_type = png_get_color_type(image->png_ptr, image->info_ptr);
image->bit_depth = png_get_bit_depth(image->png_ptr, image->info_ptr);
image->number_of_passes = png_set_interlace_handling(image->png_ptr);
png_read_update_info(image->png_ptr, image->info_ptr);
if (setjmp(png_jmpbuf(image->png_ptr))){
fprintf(stderr, "Ошибка во время read_image\n");
return false;
}
image->row_pointers = (png_bytep *) malloc(sizeof(png_bytep) * image->height);
for(int y = 0; y < image->height; y++)
image->row_pointers[y] = (png_byte *) malloc(png_get_rowbytes(image->png_ptr, image->info_ptr));
png_read_image(image->png_ptr, image->row_pointers);
fclose(fp);
return true;
}
void write_png(char *file_name, struct Png *image){
FILE *fp = fopen(file_name, "wb");
if (!fp){
fprintf(stderr, "Ошибка в открытии файла\n");
return;
}
image->png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!image->png_ptr){
fprintf(stderr, "Ошибка в png_create_write_struct\n");
return;
}
image->info_ptr = png_create_info_struct(image->png_ptr);
if (!image->info_ptr){
fprintf(stderr, "Ошибка в png_create_write_info_struct\n");
return;
}
if (setjmp(png_jmpbuf(image->png_ptr))){
fprintf(stderr, "Ошибка во время init_io\n");
return;
}
png_init_io(image->png_ptr, fp);
if (setjmp(png_jmpbuf(image->png_ptr))){
fprintf(stderr, "Ошибка во время записи байт\n");
return;
}
png_set_IHDR(image->png_ptr, image->info_ptr, image->width, image->height, image->bit_depth, image->color_type,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_write_info(image->png_ptr, image->info_ptr);
if (setjmp(png_jmpbuf(image->png_ptr))){
fprintf(stderr, "Ошибка во время записи байт\n");
return;
}
png_write_image(image->png_ptr, image->row_pointers);
if (setjmp(png_jmpbuf(image->png_ptr))){
fprintf(stderr, "Ошибка во время конца записи\n");
return;
}
png_write_end(image->png_ptr, NULL);
for(int y = 0; y < image->height; y++)
free(image->row_pointers[y]);
free(image->row_pointers);
fclose(fp);
}
