Skip to content

Commit f072eef

Browse files
committed
Clone the assignment-image-rotation
1 parent 14e0803 commit f072eef

16 files changed

+338
-2
lines changed

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ stands for transposition). *B*, *G*, *R* stand for blue, green, and red levels.
1818
In vector from the transformation can be described as follows:
1919

2020
<p align="center">
21-
<img src="docs/transform-matrix.png" alt="transform-pixel" />
21+
<img src="docs/transform-matrix.png" alt="transform-pixel"
22+
width="250" height="70" />
2223
</p>
2324

2425
In scalar form, we can rewrite it as
2526

2627
<p align="center">
27-
<img src="docs/transform-system.png" alt="transform-system" />
28+
<img src="docs/transform-system.png" alt="transform-system"
29+
width="250" height="70" />
2830
</p>
2931

3032
We will have to use saturation arithmetic. It means, that all operations such as

include/bmp.h

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef BMP_H
2+
#define BMP_H
3+
4+
#include <stdio.h>
5+
6+
#include "image.h"
7+
#include "iostatus.h"
8+
9+
enum read_status from_bmp(FILE *, struct image *);
10+
enum write_status to_bmp(FILE *, struct image const *);
11+
12+
#endif

include/dimensions.h

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#ifndef DIMENSIONS_H
2+
#define DIMENSIONS_H
3+
4+
#include <stdint.h>
5+
6+
struct dimensions {
7+
uint32_t width, height;
8+
};
9+
10+
struct dimensions dimensions_create(uint32_t, uint32_t);
11+
struct dimensions dimensions_reverse(struct dimensions);
12+
13+
#endif

include/file.h

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef FILE_H
2+
#define FILE_H
3+
4+
#include "stdio.h"
5+
6+
enum open_status { OPEN_OK = 0, OPEN_ERROR };
7+
enum close_status { CLOSE_OK = 0, CLOSE_ERROR };
8+
9+
enum open_status file_open(FILE **, const char *, const char *);
10+
enum close_status file_close(FILE **);
11+
12+
#endif

include/image.h

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#ifndef IMAGE_H
2+
#define IMAGE_H
3+
4+
#include "dimensions.h"
5+
#include "pixel.h"
6+
7+
struct image {
8+
struct dimensions dims;
9+
struct pixel *data;
10+
};
11+
12+
struct image image_create(struct dimensions);
13+
void image_destroy(struct image *);
14+
15+
#endif

include/iostatus.h

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#ifndef IO_STATUS_H
2+
#define IO_STATUS_H
3+
4+
enum read_status {
5+
READ_OK = 0,
6+
READ_INVALID_SIGNATURE,
7+
READ_INVALID_BITS,
8+
READ_INVALID_HEADER
9+
};
10+
11+
enum write_status { WRITE_OK = 0, WRITE_ERROR };
12+
13+
#endif

include/pixel.h

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef PIXEL_H
2+
#define PIXEL_H
3+
4+
#include <stdint.h>
5+
6+
struct pixel { uint8_t r, g, b; };
7+
8+
#endif

include/transform.h

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef TRANSFORM_H
2+
#define TRANSFORM_H
3+
4+
#include "image.h"
5+
6+
struct image rotate_left(struct image);
7+
8+
#endif

include/utils.h

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#ifndef UTILS_H
2+
#define UTILS_H
3+
4+
void err(const char *, ...);
5+
6+
#endif

src/bmp.c

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#include "bmp.h"
2+
3+
#define FOR_HEADER(FOR_FIELD) \
4+
FOR_FIELD(uint16_t, bfType) \
5+
FOR_FIELD(uint32_t, bfileSize) \
6+
FOR_FIELD(uint32_t, bfReserved) \
7+
FOR_FIELD(uint32_t, bOffBits) \
8+
FOR_FIELD(uint32_t, biSize) \
9+
FOR_FIELD(uint32_t, biWidth) \
10+
FOR_FIELD(uint32_t, biHeight) \
11+
FOR_FIELD(uint16_t, biPlanes) \
12+
FOR_FIELD(uint16_t, biBitCount) \
13+
FOR_FIELD(uint32_t, biCompression) \
14+
FOR_FIELD(uint32_t, biSizeImage) \
15+
FOR_FIELD(uint32_t, biXPelsPerMeter) \
16+
FOR_FIELD(uint32_t, biYPelsPerMeter) \
17+
FOR_FIELD(uint32_t, biClrUsed) \
18+
FOR_FIELD(uint32_t, biClrImportant)
19+
20+
#define DECLARE_FIELD(type, name) type name;
21+
22+
struct __attribute__((packed)) header {
23+
FOR_HEADER(DECLARE_FIELD)
24+
};
25+
26+
#undef FOR_HEADER
27+
#undef DECLARE_FIELD
28+
29+
static const uint16_t SIGNATURE = 0x4d42;
30+
static const uint32_t HEADER_INFO_SIZE = 40;
31+
static const uint16_t BITS_PER_PIXEL = 24;
32+
33+
static inline uint32_t get_padding(uint32_t width) { return width % 4; }
34+
35+
static struct header generate_header(const struct dimensions dims) {
36+
const uint32_t head_size = sizeof(struct header);
37+
const uint32_t img_size = sizeof(struct pixel)
38+
* dims.height * (dims.width + get_padding(dims.width));
39+
const uint32_t file_size = head_size + img_size;
40+
return (struct header) {
41+
.bfType = SIGNATURE,
42+
.bfileSize = file_size,
43+
.bfReserved = 0,
44+
.bOffBits = head_size,
45+
.biSize = HEADER_INFO_SIZE,
46+
.biWidth = dims.width,
47+
.biHeight = dims.height,
48+
.biPlanes = 1,
49+
.biBitCount = BITS_PER_PIXEL,
50+
.biCompression = 0,
51+
.biSizeImage = img_size,
52+
.biXPelsPerMeter = 0,
53+
.biYPelsPerMeter = 0,
54+
.biClrUsed = 0,
55+
.biClrImportant = 0
56+
};
57+
}
58+
59+
static enum read_status read_header(FILE *in, struct header *head) {
60+
enum read_status retval = READ_OK;
61+
if (fread(head, sizeof(struct header), 1, in) < 1 ||
62+
head->biSize != HEADER_INFO_SIZE || head->biPlanes != 1 ||
63+
head->biBitCount != BITS_PER_PIXEL || head->biCompression != 0)
64+
retval = READ_INVALID_HEADER;
65+
else if (head->bfType != SIGNATURE)
66+
retval = READ_INVALID_SIGNATURE;
67+
return retval;
68+
}
69+
70+
static enum read_status read_image(FILE *in, struct image *img) {
71+
enum read_status retval = READ_OK;
72+
const struct dimensions dims = img->dims;
73+
for (uint32_t i = 0; i < dims.height; i++) {
74+
if (fread(&img->data[i * dims.width], sizeof(struct pixel), dims.width, in)
75+
< dims.width || fseek(in, get_padding(dims.width), SEEK_CUR) != 0) {
76+
retval = READ_INVALID_BITS;
77+
break;
78+
}
79+
}
80+
return retval;
81+
}
82+
83+
enum read_status from_bmp(FILE *in, struct image *img) {
84+
enum read_status retval = READ_OK;
85+
/* read the header of bitmap file */
86+
struct header head = {0};
87+
if ((retval = read_header(in, &head)) != 0)
88+
return retval;
89+
/* set file pointer to data section of a bitmap file */
90+
if (fseek(in, head.bOffBits, SEEK_SET) != 0)
91+
return READ_INVALID_BITS;
92+
/* read the image from the data section of a bitmap file */
93+
*img = image_create(dimensions_create(head.biWidth, head.biHeight));
94+
if ((retval = read_image(in, img)) != 0)
95+
image_destroy(img);
96+
return retval;
97+
}
98+
99+
enum write_status to_bmp(FILE *out, struct image const *img) {
100+
enum write_status retval = WRITE_OK;
101+
/* write the header of bitmap file */
102+
const struct header head = generate_header(img->dims);
103+
if (fwrite(&head, sizeof(struct header), 1, out) < 1)
104+
return WRITE_ERROR;
105+
/* write data section */
106+
const struct dimensions dims = img->dims;
107+
for (uint32_t o = 0, i = 0; i < dims.height; i++) {
108+
if (fwrite(&img->data[i * dims.width], sizeof(struct pixel), dims.width, out)
109+
< dims.width || fwrite(&o, get_padding(dims.width), 1, out) < 1) {
110+
retval = WRITE_ERROR;
111+
break;
112+
}
113+
}
114+
return retval;
115+
}

src/dimensions.c

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include "dimensions.h"
2+
3+
struct dimensions dimensions_create(uint32_t width, uint32_t height) {
4+
return (struct dimensions) { width, height };
5+
}
6+
7+
struct dimensions dimensions_reverse(struct dimensions dims) {
8+
return (struct dimensions) { dims.height, dims.width };
9+
}

src/file.c

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include "file.h"
2+
3+
enum open_status file_open(FILE **file, const char *pathname,
4+
const char *mode) {
5+
if ((*file = fopen(pathname, mode)) != NULL) {
6+
return OPEN_OK;
7+
} else {
8+
return OPEN_ERROR;
9+
}
10+
}
11+
12+
enum close_status file_close(FILE **file) {
13+
if (fclose(*file) != EOF) {
14+
return CLOSE_OK;
15+
} else {
16+
return CLOSE_ERROR;
17+
}
18+
}

src/image.c

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <malloc.h>
2+
3+
#include "image.h"
4+
5+
static inline size_t get_size(struct dimensions dims) {
6+
return dims.width * dims.height * sizeof(struct pixel);
7+
}
8+
9+
struct image image_create(struct dimensions dims) {
10+
return (struct image) { dims, malloc(get_size(dims)) };
11+
}
12+
13+
void image_destroy(struct image *img) { free(img->data); }

src/main.c

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include <stdio.h>
2+
3+
#include "bmp.h"
4+
#include "file.h"
5+
#include "image.h"
6+
#include "transform.h"
7+
#include "utils.h"
8+
9+
#define EXECUTABLE "image-transformer"
10+
11+
static const char *r_msg[] = {
12+
[READ_INVALID_SIGNATURE] ="Invalid bitmap signature",
13+
[READ_INVALID_BITS] = "Invalid number of bits",
14+
[READ_INVALID_HEADER] = "Invalid bitmap header"
15+
};
16+
static const char *w_msg[] = {
17+
[WRITE_ERROR] = "Error while writing"
18+
};
19+
20+
void usage() {
21+
fprintf(stderr,
22+
"Usage: ./" EXECUTABLE " <source-image> <transformed-image>\n");
23+
}
24+
25+
int main(int argc, char *argv[]) {
26+
if (argc != 3) usage();
27+
if (argc < 3) err("Not enough arguments\n");
28+
if (argc > 3) err("Too many arguments\n");
29+
30+
FILE *in, *out;
31+
if (file_open(&in, argv[1], "rb") != 0) {
32+
err("Could not open the file for reading\n");
33+
}
34+
if (file_open(&out, argv[2], "wb") != 0) {
35+
file_close(&in);
36+
err("Could not create a file for writing\n");
37+
}
38+
39+
struct image source = {0};
40+
41+
enum read_status rstat = from_bmp(in, &source);
42+
file_close(&in);
43+
if (rstat != READ_OK) {
44+
fprintf(stderr, "%s\n", r_msg[rstat]);
45+
return rstat;
46+
}
47+
48+
struct image transformed = rotate_left(source);
49+
50+
enum write_status wstat = to_bmp(out, &transformed);
51+
file_close(&out);
52+
if (wstat != WRITE_OK) {
53+
image_destroy(&source);
54+
image_destroy(&transformed);
55+
fprintf(stderr, "%s\n", w_msg[wstat]);
56+
return wstat;
57+
}
58+
59+
image_destroy(&source);
60+
image_destroy(&transformed);
61+
62+
return 0;
63+
}

src/transform.c

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include "transform.h"
2+
3+
struct image rotate_left(struct image const src) {
4+
struct dimensions dims = dimensions_reverse(src.dims);
5+
struct image img = image_create(dims);
6+
for (uint32_t i = 0; i < dims.width; i = i + 1) {
7+
for (uint32_t j = 0; j < dims.height; j = j + 1) {
8+
img.data[i + j * dims.width] =
9+
src.data[(dims.width - i - 1) * dims.height + j];
10+
}
11+
}
12+
return img;
13+
}

src/utils.c

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <stdarg.h>
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
5+
#include "utils.h"
6+
7+
void err(const char *msg, ...) {
8+
va_list args;
9+
va_start(args, msg);
10+
// There is a bug in clang-tidy that makes it consider args as uninitialized
11+
// NOLINT helps suppress this message
12+
// See: https://bugs.llvm.org/show_bug.cgi?id=41311
13+
vfprintf(stderr, msg, args); // NOLINT
14+
va_end(args);
15+
exit(1);
16+
}

0 commit comments

Comments
 (0)