I am exploring .tga files.
I have fully working code that looks like this:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
const int letterHeight = 34;
const int spacer = 5;
typedef struct{
uint8_t idlength;
uint8_t colourmaptype;
uint8_t datatypecode;
uint16_t colourmaporigin;
uint16_t colourmaplength;
uint8_t colourmapdepth;
uint16_t x_origin;
uint16_t y_origin;
uint16_t width;
uint16_t height;
uint8_t bitsperpixel;
uint8_t imagedescriptor;
} TGA_Header;
typedef struct
{
uint8_t B;
uint8_t G;
uint8_t R;
} Pixel;
typedef struct{
TGA_Header header;
Pixel* pixels;
int width;
int height;
} Image;
void readHeader(TGA_Header* header, FILE* input_F){
fread(&header->idlength, sizeof(header->idlength), 1, input_F);
fread(&header->colourmaptype, sizeof(header->colourmaptype), 1, input_F);
fread(&header->datatypecode, sizeof(header->datatypecode), 1, input_F);
fread(&header->colourmaporigin, sizeof(header->colourmaporigin), 1, input_F);
fread(&header->colourmaplength, sizeof(header->colourmaplength), 1, input_F);
fread(&header->colourmapdepth, sizeof(header->colourmapdepth), 1, input_F);
fread(&header->x_origin, sizeof(header->x_origin), 1, input_F);
fread(&header->y_origin, sizeof(header->y_origin), 1, input_F);
fread(&header->width, sizeof(header->width), 1, input_F);
fread(&header->height, sizeof(header->height), 1, input_F);
fread(&header->bitsperpixel, sizeof(header->bitsperpixel), 1, input_F);
fread(&header->imagedescriptor, sizeof(header->imagedescriptor), 1, input_F);
}
void writeHeader(TGA_Header* header, FILE* output_F){
fwrite(&header->idlength, sizeof(header->idlength), 1, output_F);
fwrite(&header->colourmaptype, sizeof(header->colourmaptype), 1, output_F);
fwrite(&header->datatypecode, sizeof(header->datatypecode), 1, output_F);
fwrite(&header->colourmaporigin, sizeof(header->colourmaporigin), 1, output_F);
fwrite(&header->colourmaplength, sizeof(header->colourmaplength), 1, output_F);
fwrite(&header->colourmapdepth, sizeof(header->colourmapdepth), 1, output_F);
fwrite(&header->x_origin, sizeof(header->x_origin), 1, output_F);
fwrite(&header->y_origin, sizeof(header->y_origin), 1, output_F);
fwrite(&header->width, sizeof(header->width), 1, output_F);
fwrite(&header->height, sizeof(header->height), 1, output_F);
fwrite(&header->bitsperpixel, sizeof(header->bitsperpixel), 1, output_F);
fwrite(&header->imagedescriptor, sizeof(header->imagedescriptor), 1, output_F);
}
void image_load(Image* image, const char* path){
FILE* input_F = fopen(path, "rb");
readHeader(&image->header, input_F);
image->width = image->header.width;
image->height = image->header.height;
image->pixels = (Pixel*) malloc(sizeof(Pixel) * image->header.width * image->header.height);
fread(image->pixels, sizeof(Pixel), image->header.width * image->header.height, input_F);
fclose(input_F);
}
void image_create(Image* image, const char* path){
FILE* output_F = fopen(path, "wb");
writeHeader(&image->header, output_F);
fwrite(image->pixels, sizeof(Pixel), image->header.width * image->header.height, output_F);
fclose(output_F);
}
void load_letters(Image (*letters)[26], const char* f){
char path[101];
for(int i=0; i<26; i++){
strcpy(path, f);
strcat(path, "/");
char c[2] = {(char)(65+i), '\0'};
strcat(path, c);
strcat(path, ".tga\0");
image_load(&(*letters)[i], &path[0]);
}
}
void drawLetter(Image* image, Image* letter, int X, int Y){
Y += letterHeight - letter->height;
int letter_y = letter->height;
int letter_x = letter->width;
int image_x = image->width;
for(int y=0; y<letter_y; y++){
for(int x=0; x<letter_x; x++){
if(letter->pixels[y*letter_x+x].R != (uint8_t)0 || letter->pixels[y*letter_x+x].G != (uint8_t)0 || letter->pixels[y*letter_x+x].B != (uint8_t)0){
image->pixels[(y+Y)*image_x+(x+X)] = letter->pixels[y*letter_x+x];
}
}
}
}
void drawString(Image* image, Image (*letters)[26], char (*text)[101], int Y){
int dejToSzajzym = 0;
for(int i=0; i<strlen((*text)); i++){
dejToSzajzym += (*letters)[(int)(*text)[i] - 65].width;
}
dejToSzajzym = dejToSzajzym/2;
dejToSzajzym = image->width/2 - dejToSzajzym;
for(int i=0; i<strlen(*text); i++){
if((*text)[i] != ' '){
drawLetter(image, &(*letters)[(int)(*text)[i] - 65], dejToSzajzym, Y);
dejToSzajzym += (*letters)[(int)(*text)[i] - 65].width;
}else{
dejToSzajzym += 10;
}
}
}
int main(int argc, char* argv[]){
Image* image;
Image letters[26];
image_load(image, "img1.tga");
load_letters(&letters, "font");
/*
char buffer[100];
*/
drawString(image, &letters, "LOL", 5);
image_create(image, "image.tga");
free(image->pixels);
image->pixels = NULL;
for(int i=0; i<26; i++){
free(letters[i].pixels);
letters[i].pixels = NULL;
}
return 0;
}
But when I write the declaration of buffer
as shown (could be anywhere in main
) the program immediately breaks.
It doesn´t even need to do anything.
error:
Unable to open 'memmove-vec-unaligned-erms.S': Unable to read file '/build/glibc-YYA7BZ/glibc-
2.31/sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S'
(Error: Unable to resolve non-existing file '/build/glibc-YYA7BZ/glibc-2.31/sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S').
BTW: Isn't there any easier way to copy the header data?
As noted by Retired Ninja in their comment, your primary problem is that
Image *image;
doesn't initializeimage
to point anywhere in particular. You pass the uninitialized pointer toimage_load()
, which then scribbles on memory — and you've no idea where. This is all undefined behaviour. Adding the variablebuffer
moves something around and changes the behaviour, but it is still undefined — anything goes and any (mis-)behaviour is valid, especially crashes. You must fix that! One way would be to change the definition toImage image;
and pass&image
toimage_load()
and the other functions that expect anImage *
.Yes, there is, and there are a couple of ways to do it. The fundamental observation is that you could write the header with
fwrite(header, sizeof(*header), 1, fp)
, and read it withfread(header, sizeof(*header), 1, fp)
.However, with the data structure as currently defined, there is some padding in the structure — one byte after
datatypecode
and another aftercolourmapdepth
. If you movedcolourmapdepth
afterdatatypecode
(or anywhere near the start of the structure before the firstuint16_t
member), you'd save two bytes in memory and have no padding bytes on disk. OTOH, there's not a lot of harm in the padding bytes being read/written. It isn't clear to me whether you're dealing with an externally imposed header structure or whether you're free to modify it.The best way to avoid padding in a structure is to put the most stringently aligned types at the start of the structure (
uint16_t
is more stringently aligned thanuint8_t
) and less stringently aligned types at the end. That normally avoids holes in the structure. There can still be padding at the end of the structure even so.