C
First, that struct that you called imagem and IMAGEM, actually it's just a pixel. So let's rename it:typedef struct pixel PIXEL;
struct pixel {
int r, g, b;
};
Better yet, combine struct and typedef in one thing only:typedef struct PIXEL {
int r, g, b;
} PIXEL;
Second, based on that, we will create another struct in order to better organize our data:typedef struct IMAGEM {
int largura;
int altura;
int maxcor;
PIXEL *pixels;
} IMAGEM;
Third, have all logic within main is not very good, because the code gets confused. The solution for this is to modulate your code by dividing it into functions.So, let's separate a function to create an image:IMAGEM *nova_imagem(int largura, int altura, int maxcor) {
IMAGEM *imagem = (IMAGEM *) malloc(sizeof(IMAGEM));
imagem->pixels = (PIXEL *) malloc(largura * altura * sizeof(PIXEL));
imagem->largura = largura;
imagem->altura = altura;
imagem->maxcor = maxcor;
return imagem;
}
Note that the image created is always sized largura * altura. Compare this to your previous code:if (g >= h){
M = malloc(g * sizeof(IMAGEM));
for (i=0;i<g;i++)
M[i] = malloc(g * sizeof(IMAGEM));
} else {
M = malloc(h * sizeof(IMAGEM));
for (i=0;i<g;i++)
M[i] = malloc(h * sizeof(IMAGEM));
}
Note that in your previous code, it will create the image with the size h * h or g * g, being you wanted or g * h or h * g. But as the two ties go from 0 to g - 1 you will book a unnecessary area in case of else. In addition, you are allocating a lot of memory, line by line and placing on several different pointers, when it is simpler and more efficient to put everything in a blocon only, just as my function above does.Well, proceeding with our program, we have to have a function that releases the memory of the created image:void destruir_imagem(IMAGEM *imagem) {
free(imagem->pixels);
free(imagem);
}
In addition, we need a way to access the image pixels for both reading their values and writing. This is equivalent to your &M[i][j], but as we did one allocation for all pixels, we have to use something smarter:PIXEL *pixel_da_imagem(IMAGEM *imagem, int x, int y) {
return &(imagem->pixels[y * imagem->largura + x]);
}
This formula y * imagem->largura + x it may seem weird, but it is because each line corresponds to a little bit of largura pixels, being all rows stored continately in memory in a single blocon. Therefore, each position that varies in y occasions a variation of largura positions inside the block.The advantage of this method is that we do not need to allocate each line one by one in the loop for, which simplifies the allocation and displacement of images, optimizes memory usage and also performance by ensuring all image pixels will be in the same memory region. The disadvantage is that the formula to access the pixels is a little bit more complicated, but only a little bit.With this, we will now create a function to read the figure data of a file:IMAGEM *ler_arquivo_ppm_p3(const char *nome_arquivo) {
FILE *arq = fopen(nome_arquivo, "r");
if (arq == NULL) return NULL;
int largura, altura, maxcor, x, y;
IMAGEM *imagem = NULL;
char formato[6];
fgets(formato, 6, arq);
if (strcmp("P3\n", formato) == 0) {
fscanf(arq, "%d", &largura);
fscanf(arq, "%d", &altura);
fscanf(arq, "%d", &maxcor);
imagem = nova_imagem(largura, altura, maxcor);
for (y = 0; y < altura; y++) {
for (x = 0; x < largura; x++) {
PIXEL *p = pixel_da_imagem(imagem, x, y);
fscanf(arq, "%d", &(p->r));
fscanf(arq, "%d", &(p->g));
fscanf(arq, "%d", &(p->b));
}
}
}
fclose(arq);
return imagem;
}
Note that I'm using fgets to read the format. The reason for this is that and so I do not risk having a buffer overflow when reading the image format.Also this function accepts files only in http://netpbm.sourceforge.net/doc/ppm.html#plainppm (no comment lines initiated by #), which I believe is the format you want. It checks if the file header starts with P3 and rejects the file if this is not the case, returning NULL. NULL is also returned if the file cannot be opened for some reason (the most common case is when the file does not exist).However, the function does not accept comments in the file (lines initiated by #) and probably something bad will happen if she reads an incomplete, truncated, faulty or malformed file that still has the header "P3". Fixing these details gives some extra work, but it's not that hard as well. Idem to modify the function to accept images in other formats.And now the function that writes the file:void salvar_arquivo_ppm_p3(const char *nome_arquivo, IMAGEM *imagem) {
FILE *arq = fopen(nome_arquivo, "w");
int x, y;
fprintf(arq, "P3\n%d %d\n%d", imagem->largura, imagem->altura, imagem->maxcor);
for (y = 0; y < imagem->altura; y++) {
for (x = 0; x < imagem->largura; x++) {
PIXEL *p = pixel_da_imagem(imagem, x, y);
fprintf(arq, "\n%d %d %d", p->r, p->g, p->b);
}
}
fclose(arq);
}
Nothing surprising here. But there are two important differences regarding your code. The first is the \n and the header space you forgot between the format name, the width, the height and the maxcor of the image. Without these characters, the generated file would be malformed.The second difference is more cosmetic, I just matched some of your fprintfs followed in a smaller number fprintfs.Done, now comes the legal part. Rotate the figure at 90 degrees. I could use a process where the original image is changed or where a copy with the changes is made and the original image is preserved. I find the second best alternative:IMAGEM *rotacionar_90_graus_direita(IMAGEM *original) {
int x, y;
int h = original->altura, w = original->largura;
IMAGEM *rotacionada = nova_imagem(h, w, original->maxcor);
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
PIXEL *p1 = pixel_da_imagem(original, x, y);
PIXEL *p2 = pixel_da_imagem(rotacionada, h - y - 1, x);
p2->r = p1->r;
p2->g = p1->g;
p2->b = p1->b;
}
}
return rotacionada;
}
Here it should be noted that to create the rotated image, I reversed the parameters of height and width. This is intectional, since the rotated image has as height, width of the original and vice versa. O maxcol of the rotated image is the same as the original.Rotated image pixels are defined by means of https://pt.wikipedia.org/wiki/Transforma%C3%A7%C3%A3o_linear T(x, y) = (y, -x), which is the linear turn of rotation to the right at 90 degrees. Negative values are shifted not to exit the appropriate position range, so that -x becomes w - x - 1. Similarly -y become h - y - 1. So here are the interesting transformation functions:t(x, y) = (y, w - x - 1): Rotation 90 degrees to the left (anti-clockwise).t(x, y) = (h - y - 1, x): Rotation 90 degrees to the right (clockwise).t(x, y) = (w - x - 1, h - y - 1): Rotation at 180 degrees.t(x, y) = (w - x - 1, y): Reflection horizontally.t(x, y) = (x, h - y - 1): Reflection vertically.t(x, y) = (y, x): Reflection on the main diagonal.t(x, y) = (h - y - 1, w - x - 1): Reflection on the secondary diagonal.t(x, y) = (x, y): No changes are made.Other types of transforms (straps, deformations, enlargements, trapezoid projections, cylindrical projections, rotations at arbitrary angles, etc.) demand more complicated formulas.Also note that the pixel values are simply copied. But if you do something different than just copy the pixel values (especially if you also look at the neighboring pixels or the statistical distribution of the pixels in the image), you can make effects such as invert colors, convert to black-and-white, highlight colors, make color filtering, recoloring, noise elimination, brightness and contrast adjustment, edge detection, object segmentation and recognition, etc.Now you just need to combine all this to make your program to rotate images:int main() {
IMAGEM *original = ler_arquivo_ppm_p3("imagem.ppm");
if (original == NULL) {
printf("Arquivo nao eh PPM P3 ou nao existe");
return 1;
}
IMAGEM *rotacionada = rotacionar_90_graus_direita(original);
salvar_arquivo_ppm_p3("imagemsaida.ppm", rotacionada);
destruir_imagem(original);
destruir_imagem(rotacionada);
return 0;
}
Look how cool! Your main was extremely simple and intuitive to understand!Adding everything, just look how your code gets:#include <stdio.h>
#include <stdlib.h>
typedef struct PIXEL {
int r, g, b;
} PIXEL;
typedef struct IMAGEM {
int largura;
int altura;
int maxcor;
PIXEL *pixels;
} IMAGEM;
IMAGEM *nova_imagem(int largura, int altura, int maxcor) {
IMAGEM *imagem = (IMAGEM *) malloc(sizeof(IMAGEM));
imagem->pixels = (PIXEL *) malloc(largura * altura * sizeof(PIXEL));
imagem->largura = largura;
imagem->altura = altura;
imagem->maxcor = maxcor;
return imagem;
}
void destruir_imagem(IMAGEM *imagem) {
free(imagem->pixels);
free(imagem);
}
PIXEL *pixel_da_imagem(IMAGEM *imagem, int x, int y) {
return &(imagem->pixels[y * imagem->largura + x]);
}
IMAGEM *ler_arquivo_ppm_p3(const char *nome_arquivo) {
FILE *arq = fopen(nome_arquivo, "r");
if (arq == NULL) return NULL;
int largura, altura, maxcor, x, y;
IMAGEM *imagem = NULL;
char formato[6];
fgets(formato, 6, arq);
if (strcmp("P3\n", formato) == 0) {
fscanf(arq, "%d", &largura);
fscanf(arq, "%d", &altura);
fscanf(arq, "%d", &maxcor);
imagem = nova_imagem(largura, altura, maxcor);
for (y = 0; y < altura; y++) {
for (x = 0; x < largura; x++) {
PIXEL *p = pixel_da_imagem(imagem, x, y);
fscanf(arq, "%d", &(p->r));
fscanf(arq, "%d", &(p->g));
fscanf(arq, "%d", &(p->b));
}
}
}
fclose(arq);
return imagem;
}
void salvar_arquivo_ppm_p3(const char *nome_arquivo, IMAGEM *imagem) {
FILE *arq = fopen(nome_arquivo, "w");
int x, y;
fprintf(arq, "P3\n%d %d\n%d", imagem->largura, imagem->altura, imagem->maxcor);
for (y = 0; y < imagem->altura; y++) {
for (x = 0; x < imagem->largura; x++) {
PIXEL *p = pixel_da_imagem(imagem, x, y);
fprintf(arq, "\n%d %d %d", p->r, p->g, p->b);
}
}
fclose(arq);
}
IMAGEM *rotacionar_90_graus_direita(IMAGEM *original) {
int x, y;
int h = original->altura, w = original->largura;
IMAGEM *rotacionada = nova_imagem(h, w, original->maxcor);
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
PIXEL *p1 = pixel_da_imagem(original, x, y);
PIXEL *p2 = pixel_da_imagem(rotacionada, h - y - 1, x);
p2->r = p1->r;
p2->g = p1->g;
p2->b = p1->b;
}
}
return rotacionada;
}
int main() {
IMAGEM *original = ler_arquivo_ppm_p3("imagem.ppm");
if (original == NULL) {
printf("Arquivo nao eh PPM P3 ou nao existe");
return 1;
}
IMAGEM *rotacionada = rotacionar_90_graus_direita(original);
salvar_arquivo_ppm_p3("imagemsaida.ppm", rotacionada);
destruir_imagem(original);
destruir_imagem(rotacionada);
return 0;
}
Let's test it all. Here's a PPM image P3 very small and simple:P3
4 2
255
255 255 255 255 0 0 0 255 0 0 0 255
0 0 0 0 255 255 255 0 255 255 255 0
Here is the result after running the program:P3
2 4
255
0 0 0
255 255 255
0 255 255
255 0 0
255 0 255
0 255 0
255 255 0
0 0 255
Shall we visualize? Here are the two images side by side (increased with a 1000% zoom, since each of them has only 8 pixels and would be too small to see right):Original image: Rotated image 90 degrees to the right: