# Assignment: Image rotation
---
Лабораторная работа: Поворот картинки

# Подготовка

- Прочитайте главу 12 (стр. 221, 231–239) и 13 (целиком) "Low-level programming: C, assembly and program execution". 

На защите мы можем обсуждать любые вопросы из учебника из глав 8–13 включительно.

# Структура BMP файла

BMP файл состоит из заголовка и растрового массива.
Заголовок задаётся следующей структурой (обратите внимание на атрибут `packed`):

```c
// Описание для gcc и clang
#include  <stdint.h>
struct bmp_header __attribute__((packed))
{
        uint16_t bfType;
        uint32_t  bfileSize;
        uint32_t bfReserved;
        uint32_t bOffBits;
        uint32_t biSize;
        uint32_t biWidth;
        uint32_t  biHeight;
        uint16_t  biPlanes;
        uint16_t biBitCount;
        uint32_t biCompression;
        uint32_t biSizeImage;
        uint32_t biXPelsPerMeter;
        uint32_t biYPelsPerMeter;
        uint32_t biClrUsed;
        uint32_t  biClrImportant;
};
```

Сразу после него (всегда ли?) идёт растровый массив, в котором последовательно хранятся пиксели по строчкам.
Каждый пиксель задаётся структурой размером 3 байта:

```c
   struct pixel { uint8_t b, g, r; };
```

## Padding

Если ширина изображения в пикселах кратна четырём, то строчки идут одна за другой без пропусков.
Если ширина не кратна четырём, то она дополняется мусорными байтами до ближайшего числа байтов, кратного четырём.
Эти байты называются *padding*.

Пример:

1. Изображение имеет ширину 12 пикселей = 12 * 3 байт = 36 байт. Ширина кратна четырём, каждая следующая строчка начинается сразу после предыдущей.
2. Изображение имеет ширину 5 пикселей. 5 * 3 = 15 байт, ближайшее число кратное четырём (округление вверх) это 16. После каждой строчки будет отступ в один мусорный байт перед началом следующей.

Обратите внимание: отступы в *байтах*, не в пикселях.



# Пользователям компилятора от Microsoft 

Вам придётся задать структуру по-другому, без атрибута `packed`:

```c
#include  <stdint.h>
#pragma pack(push, 1)
struct bmp_header 
{
        uint16_t bfType;
        uint32_t  bfileSize;
        uint32_t bfReserved;
        uint32_t bOffBits;
        uint32_t biSize;
        uint32_t biWidth;
        uint32_t  biHeight;
        uint16_t  biPlanes;
        uint16_t biBitCount;
        uint32_t biCompression;
        uint32_t biSizeImage;
        uint32_t biXPelsPerMeter;
        uint32_t biYPelsPerMeter;
        uint32_t biClrUsed;
        uint32_t  biClrImportant;
};
#pragma pack(pop)
```

 
Объяснение этого прочтите находится на страницах 235&ndash;239 учебника. 


# Задание

**ВНИМАНИЕ** `view-header` это программа для просмотра заголовков BMP файлов. Это не заготовка для решения! Можете скомпилировать её с помощью `make` и проверять заголовки на битность; в решении вам нужно поддерживать только 24-битные BMP файлы.



- Необходимо реализовать поворот изображения в формате BMP на 90 градусов.
- Архитектура приложения должна содержать три чётко разделённых части:
   - Описание внутреннего представления картинки `struct image`, очищенное от деталей формата, и функции для работы с ним: создание, деинициализация и т.д.

   ```c
   struct image {
     uint64_t width, height;
     struct pixel* data;
   };
   ```
   
   - Функции для работы с BMP-файлами и сериализации/десериализации внутреннего представления.

    ```c
    /*  deserializer   */
    enum read_status  {
        READ_OK = 0,
        READ_INVALID_SIGNATURE,
        READ_INVALID_BITS,
        READ_INVALID_HEADER
        /* коды других ошибок  */
        };
        
    enum read_status from_bmp( FILE* in, struct image* img );
    
    /*  serializer   */
    enum  write_status  {
        WRITE_OK = 0,
        WRITE_ERROR
        /* коды других ошибок  */
    };
    
    enum write_status to_bmp( FILE* out, struct image const* img );
    
    ```
   
   Как только мы считали изображение во внутренний формат, мы должны забыть, из какого формата оно было считано! Иначе будет сложнее добавлять новые входные форматы, отличные от BMP (почему?).

    - Функции `from_bmp` и `to_bmp` принимают уже открытый файл, что позволяет
      им работать с заранее открытыми файлами `stdin`, `stdout`, `stderr`.
      Они не должны ни открывать, ни закрывать файлы.
    
    - Вам потребуются функции, аналогичные `from_bmp` и `to_bmp`, которые будут
      принимать имена файлов и заниматься корректным открытием (`fopen`) и
      закрытием (`fclose`) файлов; на открытых файлах они могут запускать `from_bmp`
      и `to_bmp`.

      Имеет смысл разделять открытие/закрытие файлов и работу с ними. Уже
      открытие и закрытие могут сопровождаться ошибками (см. `man fopen` и 
      `man fclose`) и хочется отделить обработку ошибок открытия/закрытия и
      обработку ошибок чтения/записи.

    - Для ошибок открытия/закрытия, возможно, вам захочется ввести отдельные типы перечислений.

    - Вам потребуется функция для поворота картинки в её внутреннем представлении:

      ```c
      /* создаёт копию изображения, которая повёрнута на 90 градусов */
      struct image rotate( struct image const source );
      ```

  Эти части имеет смысл держать **в разных модулях (файлах с расширением `.c`)**. Разумеется, для каждого нужен соответствующий заголовочный файл.

  - Кроме того, дополнительные функции, которые вы ввели для удобства, но
    которые не относятся по смыслу ни к одному из этих модулей, можно выделить
    в отдельный модуль. Часто его называют `util.c` или как-то похоже.


# Для самопроверки

- Прочитайте [правила хорошего стиля](https://gitlab.se.ifmo.ru/c-language/c-code-guidelines/-/blob/master/README.md). Ваше решение должно им соответствовать.
- Архитектура: думайте о том, как бы вы хотели организовать код, чтобы легко добавлять входные форматы (не только BMP) и трансформации (не только поворот на 90 градусов).
- Пожалуйста, присылайте решение в виде pull-request. [Инструкция](https://gitlab.se.ifmo.ru/cse/main/-/wikis/%D0%9A%D0%B0%D0%BA-%D0%BF%D0%BE%D1%81%D0%BB%D0%B0%D1%82%D1%8C-%D0%B7%D0%B0%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BD%D0%B0-%D0%BF%D1%80%D0%BE%D0%B2%D0%B5%D1%80%D0%BA%D1%83).  В крайнем случае допускается ссылка на репозиторий на https://gitlab.se.ifmo.ru или https://github.com .