206 lines
3.8 KiB
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;
|
|
}
|
|
|