This repository has been archived on 2021-10-31. You can view files and clone it, but cannot push or open issues or pull requests.
sys_prog/gallery/gallery.c

206 lines
3.8 KiB
C

// vim: set ts=8 sw=8 noet tw=80:
/**
* gallery.c - Claudio Maggioni
*
* External sources of information used:
* - man section 3 for the several library functions;
* - libpng.org documentation of PNG header an IHDR;
* - https://codereview.stackexchange.com/q/149751 for inspiration of ntohl
* function implementation.
*/
#include "gallery.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
struct image {
uint32_t width;
uint32_t height;
char * name;
};
struct image_ll {
struct image_ll* prev;
struct image first;
struct image_ll* next;
};
struct gallery {
size_t size;
struct image_ll* images;
};
static struct image_ll* find_by_name(struct gallery*, const char*);
static uint32_t my_ntohl (uint32_t);
static struct image_ll* ll_remove(struct gallery*, struct image_ll*);
const size_t INIT_CAP = 128;
const uint8_t HEADER[8] = { '\211', 'P', 'N', 'G', '\r', '\n', '\032', '\n' };
struct image_ll* find_by_name(struct gallery* g, const char* name) {
for (struct image_ll* i = g->images; i; i = i->next) {
if (i->first.name == name || !strcmp(i->first.name, name)) {
return i;
}
}
return NULL;
}
struct image_ll* ll_remove(struct gallery* g, struct image_ll* i) {
if (i->next) {
i->next->prev = i->prev;
}
if (i->prev) {
i->prev->next = i->next;
} else {
g->images = i->next;
}
struct image_ll* n = i->next;
free(i);
g->size--;
return n;
}
// inspired by https://codereview.stackexchange.com/q/149751
uint32_t my_ntohl (const uint32_t nlong) {
uint8_t* data = (uint8_t*) &nlong;
return ((uint32_t) data[3]) |
((uint32_t) data[2] << 8) |
((uint32_t) data[1] << 16) |
((uint32_t) data[0] << 24);
}
struct gallery *gallery_new() {
struct gallery* g = malloc(sizeof(struct gallery));
if (!g) {
return NULL;
}
g->images = NULL;
g->size = 0;
return g;
}
void gallery_destroy(struct gallery *g) {
while (g->images) {
struct image_ll* i = g->images;
g->images = g->images->next;
free(i);
}
free(g);
}
/*
* remark: gallery_add(...) returns undocumented error code -3 when the malloc
* for the new image data struct fails
*/
int gallery_add(struct gallery *g, char* filename) {
if (find_by_name(g, filename)) {
return -1;
}
FILE* f = fopen(filename, "r");
if (!f) {
return -2;
}
uint8_t file_head[8];
if (fread(&file_head, 1, 8, f) != 8) {
fclose(f);
return -2;
}
if (memcmp(file_head, HEADER, sizeof(HEADER))) {
return -2;
}
if (fseek(f, 8, SEEK_CUR) == -1) {
perror("fseek failed");
fclose(f);
return -2;
}
struct image_ll* new = malloc(sizeof(struct image_ll));
if (!new) {
fclose(f);
return -3;
}
if (fread(&new->first, 8, 1, f) != 1) {
free(new);
fclose(f);
return -2;
}
new->first.width = my_ntohl(new->first.width);
new->first.height = my_ntohl(new->first.height);
new->first.name = filename;
new->prev = NULL;
new->next = g->images;
if (g->images) {
g->images->prev = new;
}
g->images = new;
g->size++;
return 1;
}
int gallery_rm(struct gallery* g, char* filename) {
struct image_ll* i = find_by_name(g, filename);
if (!i) {
return 0;
}
ll_remove(g, i);
return 1;
}
int gallery_count(struct gallery* g) {
return g->size;
}
int gallery_filter(struct gallery *g, int (*f)(char *name, int w, int h)) {
for (struct image_ll* i = g->images; i;) {
if (!f(i->first.name, i->first.width, i->first.height)) {
i = ll_remove(g, i);
} else {
i = i->next;
}
}
return g->size;
}
char* gallery_bestfit(struct gallery* g, int max_width, int max_height) {
struct image* best = NULL;
for (struct image_ll* i = g->images; i; i = i->next) {
uint32_t w = i->first.width;
uint32_t h = i->first.height;
if (w > max_width || h > max_height) {
continue;
}
if (!best || w * h > best->width * best->height) {
best = &i->first;
}
}
return best ? best->name : NULL;
}