Ну а теперь — следующее испытание! Давайте напишем в файле resize.c
программу, называемую resize
. Она будет менять размеры несжатого 24-битного изображения BMP
с шагом в n
. Ваше приложение должно принимать ровно три аргумента командной строки, причем первый (n)
должен быть целым числом не более 100, второй — именем файла, который будет изменен, а третий — названием сохраненной версии измененного файла.
<codeUsage: ./resize n infile outfile>
С помощью такой программы мы могли бы создать large.bmp из файла small.bmp путем изменения размера последнего на 4 (т.е. путем умножения и ширины, и высоты на 4), как показано ниже.
./resize 4 small.bmp large.bmp
Для простоты можно начать задание скопировав еще раз copy.c
и назвав копию resize.c
.
Но сначала задайте себе вопросы и ответьте на них: что значит изменить размер BMP
(можно предположить, что n
помноженное на размер файла infile
не будет превышать 232 — 1).
Определите, какие поля в BITMAPFILEHEADER
и BITMAPINFOHEADER
вам нужно изменить.
Подумайте, нужно ли вам добавить или удалить поля scanlines
.
И, да, скажите спасибо, что мы не предлагаем вам рассмотреть все возможные значения n от 0 до 1! (хотя, если интересно — это задачка из хакерского задачника;)). Тем не менее, мы предполагаем, что при n = 1 программа будет работать правильно, и выходной файл outfile
будет таких же размеров как и исходный infile
.
Хотите проверить программку с помощью check50
? Наберите следующую команду:
check50 2015.fall.pset4.resize bmp.h resize.c
Есть желание поиграться с реализацией приложения, выполненной ассистентами CS50? Выполните следующее:
~cs50/pset4/resize
Ну а если хотите посмотреть, например, заголовки large.bmp
(в более дружественном для пользователя виде, чем позволяет xxd), нужно выполнить следующую команду:
~cs50/pset4/peek large.bmp
Еще лучше, если вы захотите сравнить свои заголовки с заголовками файлов ассистентов CS50. Вы можете выполнить команды внутри вашей директории ~/workspace/pset4/bmp (подумайте, что делает каждая команда).
./resize 4 small.bmp student.bmp
~cs50/pset4/resize 4 small.bmp staff.bmp
~cs50/pset4/peek student.bmp staff.bmp
Если вы использовали malloc
, не забудьте использовать free
, чтобы предотвратить утечку памяти. Попробуйте использовать valgrind
чтобы проверить наличие утечек.
Как решать?
- Открыть файл, который нам нужно увеличить, а также создать и открыть новый файл, в котором будет записано увеличенное изображение.
- Обновить информацию заголовка для выходного файла. Поскольку наше изображение в формате BMP и мы меняем его размер, нам нужно записать заголовок нового файла с новыми размерами. Что поменяется? Размер файла, а также размер изображения — его ширина и высота.
- Читаем исходящий файл, строка за строкой, пиксель за пикселем. Для этого мы снова обращаемся к нашей библиотеке файлового ввода/вывода и функции
fread
. Она принимает указатель на структуру, которая будет содержать прочитанные байты, размер одного элемента, который мы собираемся прочитать, количество таких элементов и указатель на файл, из которого мы будем читать. - Увеличиваем каждую строку по горизонтали согласно заданному масштабу, записываем результат в выходной файл
- Заполнить пробелы! Если количество пикселей в строке не кратно четырем, мы должны добавить «выравнивание» – нулевые байты. Нам понадобится формула для расчета размера выравнивания. Для записи нулевых байтов в выходной файл вы можете использовать функцию
fputc
, передав ей символ, который вы хотите записать и указатель на выходной файл. - Теперь, когда мы растянули строку по горизонтали и добавили выравнивание в выходной файл, нам нужно переместить текущую позицию в исходящем файле, поскольку нам нужно перескочить через выравнивание.
- Увеличить размер по вертикали. Это сложнее, но мы можем использовать пример кода из файла
copy.c
(copy.c
открывает исходящий файл, записывает заголовок в выходной файл, читает изображение из исходного файла по строкам, пиксель за пикселем, и записывает их в файл выхода). Исходя из этого, первым делом можно выполнить такую команду: cpcopy.c
resize.c

Если мы заглянем в описание заголовка, то увидите переменную biSizeImage. Она указывает на общий размер изображения в байтах, biWidth
— ширина изображения минус выравнивание, biHeight
— высота. Эти переменные находятся в структурах BITMAPFILEHEADER
и BITMAPINFOHEADER
. Вы можете найти их, если откроете файл bmp.h.

В описании структуры BITMAPINFOHEADER
есть список переменных. Для записи заголовка выходного файла нужно будет поменять значение высоты и ширины. Но есть также вероятность, что позднее вам понадобятся и оригинальные высота и ширина исходного файла. Поэтому лучше сохранить и то, и другое. Будьте внимательны к названиям переменных, чтобы случайно не записать ошибочные данные в заголовок выходного файла.


Как мы записываем файлы? У нас есть функция fwrite
, которой мы передаем указатель на структуру (там находятся данные для записи в файл), размер элемента, их количество и указатель на выходной файл. Для организации цикла можем воспользоваться уже привычным нам циклом for
.



Растянуть изображение по вертикали означает скопировать каждую строку несколько раз. Есть несколько разных способов это сделать. Например, с помощью перезаписи, когда мы сохраняем все пиксели одной строки в памяти и в цикле записываем эту строку в выходной файл столько раз, сколько это нужно. Другой метод – перекопирование: после чтения строки исходящего файла, записи его в выходной файл и выравнивания вернуться функцией fseek
назад в начало строки в исходящем файле и повторить все заново несколько раз.

ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ