README.md 10.9 KB
Newer Older
Igor Zhirkov's avatar
Igor Zhirkov committed
1
# Assignment: Image rotation
Igor Zhirkov's avatar
Igor Zhirkov committed
2
---
Igor Zhirkov's avatar
Igor Zhirkov committed
3
Лабораторная работа: Поворот картинки
Igor Zhirkov's avatar
Igor Zhirkov committed
4

Igor Zhirkov's avatar
Igor Zhirkov committed
5 6
# Подготовка

Igor Zhirkov's avatar
Igor Zhirkov committed
7
- Прочитайте главу 12 (стр. 221, 231–239) и 13 (целиком) "Low-level programming: C, assembly and program execution". 
Igor Zhirkov's avatar
Igor Zhirkov committed
8 9 10 11 12 13 14 15 16

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

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

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

```c
17
// Описание для gcc и clang
Igor Zhirkov's avatar
Igor Zhirkov committed
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
#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
43
   struct pixel { uint8_t b, g, r; };
Igor Zhirkov's avatar
Igor Zhirkov committed
44 45
```

46 47
## Padding

Igor Zhirkov's avatar
Igor Zhirkov committed
48 49
Если ширина изображения в пикселах кратна четырём, то строчки идут одна за другой без пропусков.
Если ширина не кратна четырём, то она дополняется мусорными байтами до ближайшего числа байтов, кратного четырём.
50
Эти байты называются *padding*.
Igor Zhirkov's avatar
Igor Zhirkov committed
51 52 53 54 55 56

Пример:

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

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
Обратите внимание: отступы в *байтах*, не в пикселях.



# Пользователям компилятора от 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 учебника. 

Igor Zhirkov's avatar
Igor Zhirkov committed
92 93 94 95 96 97

# Задание


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

Igor Zhirkov's avatar
Igor Zhirkov committed
100 101 102 103 104 105
   ```c
   struct image {
     uint64_t width, height;
     struct pixel* data;
   };
   ```
106
   
Igor Zhirkov's avatar
Igor Zhirkov committed
107 108 109
   - Функции для работы с BMP-файлами и сериализации/десериализации внутреннего представления.

    ```c
110
    /*  deserializer   */
Igor Zhirkov's avatar
Igor Zhirkov committed
111 112 113 114 115 116 117
    enum read_status  {
        READ_OK = 0,
        READ_INVALID_SIGNATURE,
        READ_INVALID_BITS,
        READ_INVALID_HEADER
        /* коды других ошибок  */
        };
118
        
Igor Zhirkov's avatar
Igor Zhirkov committed
119
    enum read_status from_bmp( FILE* in, struct image* img );
120
    
Igor Zhirkov's avatar
Igor Zhirkov committed
121 122 123 124 125 126
    /*  serializer   */
    enum  write_status  {
        WRITE_OK = 0,
        WRITE_ERROR
        /* коды других ошибок  */
    };
127
    
Igor Zhirkov's avatar
Igor Zhirkov committed
128 129 130
    enum write_status to_bmp( FILE* out, struct image const* img );
    
    ```
131 132
   
   Как только мы считали изображение во внутренний формат, мы должны забыть, из какого формата оно было считано! Иначе будет сложнее добавлять новые входные форматы, отличные от BMP (почему?).
Igor Zhirkov's avatar
Igor Zhirkov committed
133

134 135 136 137 138 139 140 141
    - Функции `from_bmp` и `to_bmp` принимают уже открытый файл, что позволяет
      им работать с заранее открытыми файлами `stdin`, `stdout`, `stderr`.
      Они не должны ни открывать, ни закрывать файлы.
    
    - Вам потребуются функции, аналогичные `from_bmp` и `to_bmp`, которые будут
      принимать имена файлов и заниматься корректным открытием (`fopen`) и
      закрытием (`fclose`) файлов; на открытых файлах они могут запускать `from_bmp`
      и `to_bmp`.
Igor Zhirkov's avatar
Igor Zhirkov committed
142

143 144 145 146
      Имеет смысл разделять открытие/закрытие файлов и работу с ними. Уже
      открытие и закрытие могут сопровождаться ошибками (см. `man fopen` и 
      `man fclose`) и хочется отделить обработку ошибок открытия/закрытия и
      обработку ошибок чтения/записи.
Igor Zhirkov's avatar
Igor Zhirkov committed
147

148
    - Для ошибок открытия/закрытия, возможно, вам захочется ввести отдельные типы перечислений.
Igor Zhirkov's avatar
Igor Zhirkov committed
149

150
    - Вам потребуется функция для поворота картинки в её внутреннем представлении:
Igor Zhirkov's avatar
Igor Zhirkov committed
151

152 153 154 155
      ```c
      /* создаёт копию изображения, которая повёрнута на 90 градусов */
      struct image rotate( struct image const source );
      ```
Igor Zhirkov's avatar
Igor Zhirkov committed
156

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

159 160 161
  - Кроме того, дополнительные функции, которые вы ввели для удобства, но
    которые не относятся по смыслу ни к одному из этих модулей, можно выделить
    в отдельный модуль. Часто его называют `util.c` или как-то похоже.
Igor Zhirkov's avatar
Igor Zhirkov committed
162 163 164 165 166


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

- Функции должны получать все необходимые им данные через аргументы. 
Igor Zhirkov's avatar
Igor Zhirkov committed
167 168
- Глобальные переменные запрещены.
- Сообщения об ошибках должны выводиться в `stderr`. Общее правило:  результаты вычислений &mdash; в `stdout`, информация о том, *как* происходят вычисления &mdash; в `stderr`.
Igor Zhirkov's avatar
Igor Zhirkov committed
169
- Нельзя смешивать логику вычислений и ввод-вывод.
170
- Нельзя использовать `typedef` для определения структур ([объяснение](https://stepik.org/lesson/408350/step/2)), кроме структур из одного поля, которые являются аналогом `typedef`, но без неявных преобразований.
Igor Zhirkov's avatar
Igor Zhirkov committed
171 172 173
- Ваш код должен компилироваться с флагами `-std=c18 -pedantic -Wall -Werror` (gcc) или `-std=c18 -pedantic -Wall -Werror` (clang).


Igor Zhirkov's avatar
Igor Zhirkov committed
174
  Пользователям MS Visual Studio придётся тяжко, поддержка C11/C17 пока есть только в [Visual Studio 2019 version 16.8 Preview 3](https://devblogs.microsoft.com/cppblog/c11-and-c17-standard-support-arriving-in-msvc). Установите флаги `/W4` (warning level 4) и `/WX` (warnings as errors).
Igor Zhirkov's avatar
Igor Zhirkov committed
175

176 177 178 179
  Можете попробовать использовать [`cl-clang`](https://clang.llvm.org/docs/MSVCCompatibility.html).
  
  Проверять Ваш код мы будем с помощью `gcc` и `Makefile`.
  
Igor Zhirkov's avatar
Igor Zhirkov committed
180 181 182 183 184
- Типы:

  - Проверьте, что вы максимально возможным образом расставили `const`.
  - Проверьте, что индексы используют только тип `size_t`.
  - Проверьте, что вы используете только платформо-независимые типы, такие, как `int64_t` или `int_fast64_t`.
185
  
Igor Zhirkov's avatar
Igor Zhirkov committed
186 187
- Проверьте, что вы используете правильные спецификаторы ввода и вывода.
- Вы можете добавлять любое число вспомогательных функций для удобства, это поощряется.
188 189 190
- Проверьте архитектуру. **Решение внутри одного файла приниматься не будет**.
  Думайте о том, как бы вы хотели организовать код, чтобы легко добавлять входные форматы (не только BMP) и трансформации (не только поворот на 90 градусов).

Igor Zhirkov's avatar
Igor Zhirkov committed
191
- Пожалуйста, присылайте решение в виде 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 .
192
- Не забудьте написать `Makefile`. Он должен позволять при изменении одного `.c` файла пересобрать часть проекта не пересобирая всё остальное.