Advertisement
kolychestiy

bmp_io.c

Dec 12th, 2021
164
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 10.81 KB | None | 0 0
  1. /**
  2.  * bmp_io.c -- функции чтения-записи файлов изображений в формате BMP
  3.  *
  4.  * Copyright (c) 2009, Student Name <student@cs.petrsu.ru>
  5.  *
  6.  * This code is licensed under a MIT-style license.
  7.  */
  8.  
  9. #include <stdlib.h>
  10. #include <stdio.h>
  11. #include <string.h>
  12. #include <stdint.h>
  13. #include <errno.h>
  14. #include "bmp.h"
  15.  
  16. #define BMP_HEADER_SIZE 54
  17. #define BMP_FILE_SIGNATURE 0x4D42
  18.  
  19. static void bmp_close(FILE* bmp_file, const char* bmp_path);
  20.  
  21. static void bmp_parse_header(bmp_header *header, const uint8_t *buf);
  22.  
  23. static void bmp_form_header(uint8_t *buf, const bmp_header *header);
  24.  
  25.  
  26. bmp_image bmp_read(char *bmp_path)
  27. {
  28.     FILE *bmp_file;
  29.     int i, j, k;
  30.    
  31.     uint8_t bmp_header_buf[BMP_HEADER_SIZE];
  32.    
  33.     bmp_image image;
  34.     image.pixel_array = NULL;
  35.  
  36.     /* Открываем файл с изображением и выводим диагностику в случае неудачи */
  37.     if ((bmp_file = fopen(bmp_path, "r")) == NULL) {
  38.        
  39.         fprintf(stderr, "File \"%s\" couldn't be opened.\n%s\n",
  40.                 bmp_path, strerror(errno));
  41.        
  42.         exit(EXIT_FAILURE);
  43.     }    
  44.    
  45.     /* Считываем файловый заголовок, выводим диагностику в случае неудачи */
  46.     if (fread(bmp_header_buf, 1, BMP_HEADER_SIZE, bmp_file)
  47.         < BMP_HEADER_SIZE) {
  48.        
  49.         int read_error = errno;
  50.        
  51.         /* Если файл слишком короткий, сообщаем об этом */
  52.         if (feof(bmp_file)) {
  53.             fprintf(stderr, "File \"%s\" is too short to be a BMP file.\n",
  54.                 bmp_path);
  55.         } else if (ferror(bmp_file)) {
  56.             fprintf(stderr, "File \"%s\" due to I/O error.\n", bmp_path);
  57.             fprintf(stderr, "The reason is: %s\n", strerror(read_error));            
  58.         }
  59.        
  60.         exit(EXIT_FAILURE);        
  61.     }
  62.    
  63.     /* Разбираем файловый заголовок */
  64.     bmp_parse_header(&image.header, bmp_header_buf);
  65.    
  66.    
  67.     if (image.header.signature != BMP_FILE_SIGNATURE) {
  68.         fprintf(stderr, "BMP signature doesn't match\n");
  69.         exit(EXIT_FAILURE);
  70.     }
  71.    
  72.     // Проверяем версию BMP (поддерживаются только заголовки BITMAPINFOHEADER)
  73.     if (image.header.pixel_array_offset != BMP_HEADER_SIZE) {
  74.         fprintf(stderr, "Unsupported BMP version\n");
  75.         exit(EXIT_FAILURE);      
  76.     }    
  77.        
  78.     // Проверяем глубину цвета (поддерживается только 24 бита на пиксель)
  79.     if (image.header.bits_per_pixel != 24) {
  80.         fprintf(stderr, "Unsupported BMP version\n");
  81.         exit(EXIT_FAILURE);
  82.     }    
  83.    
  84.     /* Считываем цвета точек в три различных цветовых плоскости */
  85.     int size = image.header.width * image.header.height;
  86.     image.pixel_array = (double*) malloc(3 * size * sizeof(double));
  87.  
  88.     if ((image.pixel_array) == NULL) {
  89.         printf("Not enough memoy for reading picture.\n\n");
  90.         exit(EXIT_FAILURE);
  91.     }
  92.  
  93.     /* Переходим к считыванию цветов пикселей */
  94.     fseek(bmp_file, BMP_HEADER_SIZE, SEEK_SET);
  95.  
  96.     /* Считываем значения цветовых компонентов, преобразуя к виду
  97.        с плавающей точкой */
  98.     for(i = 0; i < image.header.height; i++) {
  99.         for (j = 0; j < image.header.width; j++) {
  100.             double color;
  101.             // Считываем значение синего компонента цвета пикселя
  102.             color = (double) fgetc (bmp_file) / 255;
  103.             image.pixel_array[i * image.header.width + j] = color;
  104.             // Считываем значение зеленего компонента цвета пикселя
  105.             color = (double) fgetc (bmp_file) / 255;            
  106.             image.pixel_array[i * image.header.width + size + j] = color;
  107.             // Считываем значение красного компонента цвета пикселя
  108.             color = (double) fgetc (bmp_file) / 255;            
  109.             image.pixel_array[i * image.header.width + 2 * size + j] = color;
  110.         }
  111.        
  112.         // Пропускаем выравнивающие байты
  113.         if (image.header.width * 3 % 4 != 0) {
  114.             for (k = 0; k < 4 - image.header.width * 3 % 4; k++) {
  115.                 fgetc(bmp_file);
  116.             }
  117.         }
  118.     }
  119.    
  120.     /* Закрываем файл с изображением и выводим диагностику в случае неудачи */
  121.     bmp_close(bmp_file, bmp_path);
  122.    
  123.     return image;
  124. }
  125.  
  126.  
  127.  
  128.  
  129. void bmp_write(char *bmp_path, bmp_image image)
  130. {
  131.     FILE *bmp_file;
  132.     int i, j, k;
  133.    
  134.     uint8_t bmp_header_buf[BMP_HEADER_SIZE];
  135.    
  136.     /* Открываем файл и выводим диагностику в случае неудачи */
  137.     if ((bmp_file = fopen(bmp_path, "w")) == NULL) {
  138.        
  139.         fprintf(stderr, "File \"%s\" couldn't be opened.\n%s\n",
  140.                 bmp_path, strerror(errno));
  141.        
  142.         exit(EXIT_FAILURE);
  143.     }    
  144.  
  145.     /* Формируем файловый заголовок на основе структуры */
  146.     bmp_form_header(bmp_header_buf, &image.header);    
  147.    
  148.     /* Записываем файловый заголовок, выводим диагностику в случае неудачи */
  149.     if (fwrite(bmp_header_buf, 1, BMP_HEADER_SIZE, bmp_file) < BMP_HEADER_SIZE) {
  150.        
  151.         int write_error = errno;
  152.        
  153.         /* Если файл слишком короткий, сообщаем об этом */
  154.         if (ferror(bmp_file)) {
  155.             fprintf(stderr, "File \"%s\" due to I/O error.\n", bmp_path);
  156.             fprintf(stderr, "The reason is: %s\n", strerror(write_error));            
  157.         }
  158.        
  159.         exit(EXIT_FAILURE);        
  160.     }
  161.    
  162.     /* Записываем цвета точек в три различных цветовых плоскости */
  163.     int size = image.header.width * image.header.height;
  164.  
  165.     /* Переходим к считыванию цветов пикселей */
  166.     fseek(bmp_file, BMP_HEADER_SIZE, SEEK_SET);
  167.  
  168.     /* Записываем значения цветовых компонентов, преобразуя к виду
  169.        с плавающей точкой */
  170.     for(i = 0; i < image.header.height; i++) {
  171.         for (j = 0; j < image.header.width; j++) {
  172.             double color;
  173.             // Считываем значение синего компонента цвета пикселя
  174.             color = image.pixel_array[i * image.header.width + j];
  175.             fputc((uint8_t)(color * 255), bmp_file);
  176.             // Считываем значение зеленего компонента цвета пикселя          
  177.             color = image.pixel_array[i * image.header.width + size + j];
  178.             fputc((uint8_t)(color * 255), bmp_file);
  179.             // Считываем значение красного компонента цвета пикселя          
  180.             color = image.pixel_array[i * image.header.width + 2 * size + j];
  181.             fputc((uint8_t)(color * 255), bmp_file);
  182.         }
  183.        
  184.         // Пропускаем выравнивающие байты
  185.         if (image.header.width * 3 % 4 != 0) {
  186.             for (k = 0; k < 4 - image.header.width * 3 % 4; k++) {
  187.                 fputc(0, bmp_file);
  188.             }
  189.         }
  190.     }
  191.    
  192.     /* Закрываем файл с изображением и выводим диагностику в случае неудачи */
  193.     bmp_close(bmp_file, bmp_path);
  194.    
  195.     free(image.pixel_array);
  196. }
  197.  
  198.  
  199. /**
  200.  * @brief извлекает данные заголовков BMP-файла
  201.  */
  202. static void bmp_parse_header(bmp_header *header, const uint8_t *buf)
  203. {
  204.     // Заполняем структуру данными файлового заголовка
  205.     header->signature = *(uint16_t *)(buf + 0);
  206.     header->file_size = *(uint32_t *)(buf + 2);
  207.     header->reserved1 = *(uint16_t *)(buf + 6);
  208.     header->reserved2 = *(uint16_t *)(buf + 8);
  209.     header->pixel_array_offset = *(uint32_t *)(buf + 10);
  210.    
  211.     // Заполняем структуру данными DIB-заголовка
  212.     header->header_size = *(uint32_t *)(buf + 14);
  213.     header->width = *(int32_t *)(buf + 18);
  214.     header->height = *(int32_t *)(buf + 22);
  215.     header->color_planes = *(uint16_t *)(buf + 26);
  216.     header->bits_per_pixel = *(uint16_t *)(buf + 28);
  217.     header->compression_method = *(uint32_t *)(buf + 30);
  218.     header->image_size = *(uint32_t *)(buf + 34);
  219.     header->horizontal_resolution = *(int32_t *)(buf + 38);
  220.     header->vertical_resolution = *(int32_t *)(buf + 42);
  221.     header->colors = *(uint32_t *)(buf + 46);
  222.     header->important_colors = *(uint32_t *)(buf + 50);    
  223. }
  224.  
  225.  
  226. /**
  227.  * @brief подготавливает заголовок BMP-файла
  228.  */
  229. static void bmp_form_header(uint8_t *buf, const bmp_header *header)
  230. {
  231.     // Заполняем файловый заголовок
  232.     *(uint16_t *)(buf + 0) = header->signature;
  233.     *(uint32_t *)(buf + 2) = header->file_size;
  234.     *(uint16_t *)(buf + 6) = header->reserved1;
  235.     *(uint16_t *)(buf + 8) = header->reserved2;
  236.     *(uint32_t *)(buf + 10) = header->pixel_array_offset;
  237.  
  238.     // Заполняем информационные заголовок
  239.     *(uint32_t *)(buf + 14) = header->header_size;
  240.     *(int32_t *)(buf + 18) = header->width;
  241.     *(int32_t *)(buf + 22) = header->height;
  242.     *(uint16_t *)(buf + 26) = header->color_planes;
  243.     *(uint16_t *)(buf + 28) = header->bits_per_pixel;
  244.     *(uint32_t *)(buf + 30) = header->compression_method;
  245.     *(uint32_t *)(buf + 34) = header->image_size;
  246.     *(int32_t *)(buf + 38) = header->horizontal_resolution;
  247.     *(int32_t *)(buf + 42) = header->vertical_resolution;
  248.     *(uint32_t *)(buf + 46) = header->colors;
  249.     *(uint32_t *)(buf + 50) = header->important_colors;
  250. }
  251.  
  252.  
  253. /**
  254.  * @brief закрывает файл с изображением, контролируя ошибки
  255.  */
  256. static void bmp_close(FILE* bmp_file, const char* bmp_path)
  257. {
  258.     /* Закрываем файл с изображением */
  259.     if (fclose(bmp_file) != EOF)
  260.         return;
  261.        
  262.     /* Выводим диагностику в случае неудачной попытки закрытия файла */
  263.     int close_error = errno;
  264.        
  265.     fprintf(stderr, "File %s couldn't be closed appropriately\n", bmp_path);
  266.     fprintf(stderr, "The reason is: %s\n", strerror(close_error));
  267.        
  268.     exit(EXIT_FAILURE);
  269. }
  270.  
  271.  
  272.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement