Added assignment 2 (done)
This commit is contained in:
parent
0de237cd78
commit
cb4cc2bbe2
8 changed files with 300 additions and 0 deletions
BIN
gallery/480x320.png
Normal file
BIN
gallery/480x320.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 304 B |
BIN
gallery/640x480.png
Normal file
BIN
gallery/640x480.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 323 B |
BIN
gallery/768x480.png
Normal file
BIN
gallery/768x480.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 330 B |
BIN
gallery/800x600.png
Normal file
BIN
gallery/800x600.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 343 B |
13
gallery/Makefile
Normal file
13
gallery/Makefile
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
all: test
|
||||||
|
|
||||||
|
gallery.o: gallery.c
|
||||||
|
$(CC) -Wall -c -o $@ $< $(CFLAGS)
|
||||||
|
|
||||||
|
test: gallery.o test.c
|
||||||
|
$(CC) -Wall -o $@ $^ $(CFLAGS)
|
||||||
|
|
||||||
|
tidy:
|
||||||
|
rm -f *.o
|
||||||
|
|
||||||
|
clean: tidy
|
||||||
|
rm -f test
|
206
gallery/gallery.c
Normal file
206
gallery/gallery.c
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
// 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 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct image_ll* n = i->next;
|
||||||
|
free(i);
|
||||||
|
g->size--;
|
||||||
|
|
||||||
|
if (g->size == 0) {
|
||||||
|
g->images = NULL;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
// inspired by https://codereview.stackexchange.com/q/149751
|
||||||
|
uint32_t 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 = ntohl(new->first.width);
|
||||||
|
new->first.height = 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;
|
||||||
|
}
|
||||||
|
|
43
gallery/gallery.h
Normal file
43
gallery/gallery.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef __GALLERY_H__
|
||||||
|
#define __GALLERY_H__
|
||||||
|
|
||||||
|
struct gallery;
|
||||||
|
|
||||||
|
/* Constructor: allocates memory and returns a pointer to a new gallery.
|
||||||
|
*/
|
||||||
|
struct gallery *gallery_new();
|
||||||
|
|
||||||
|
/* Destructor: frees all the memory allocated to the gallery.
|
||||||
|
*/
|
||||||
|
void gallery_destroy(struct gallery *g);
|
||||||
|
|
||||||
|
/* Adds a PNG by filename to the gallery. If the filename is already in the
|
||||||
|
* gallery, returns -1; if the file does not exist, or if it is not a valid PNG
|
||||||
|
* format, returns -2; otherwise, returns 1 on success.
|
||||||
|
*/
|
||||||
|
int gallery_add(struct gallery *g, char *filename);
|
||||||
|
|
||||||
|
/* Removes a photo from the gallery. Returns 1 on successful removal, otherwise
|
||||||
|
* 0 if the photo is not in the gallery.
|
||||||
|
*/
|
||||||
|
int gallery_rm(struct gallery *g, char *filename);
|
||||||
|
|
||||||
|
/* Returns the number of photos in the gallery.
|
||||||
|
*/
|
||||||
|
int gallery_count(struct gallery *g);
|
||||||
|
|
||||||
|
/* Removes photos from the gallery that do not match a predicate function. The
|
||||||
|
* predicate is passed the filename, width and height of each photo. The
|
||||||
|
* predicate should return 1 to keep the photo, and 0 to remove the photo. The
|
||||||
|
* function returns the number of photos present after filtering.
|
||||||
|
*/
|
||||||
|
int gallery_filter(struct gallery *g,
|
||||||
|
int (*f)(char *filename, int width, int height));
|
||||||
|
|
||||||
|
/* Returns the filename of the photo that is the best fit for the dimensions
|
||||||
|
* width*height. If there are multiple best fits, any may be returned. If there
|
||||||
|
* are no photos that fit, returns NULL.
|
||||||
|
*/
|
||||||
|
char *gallery_bestfit(struct gallery *g, int max_width, int max_height);
|
||||||
|
|
||||||
|
#endif // __GALLERY_H__
|
38
gallery/test.c
Normal file
38
gallery/test.c
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "gallery.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
struct gallery *g = gallery_new();
|
||||||
|
|
||||||
|
assert(gallery_add(g, "480x320.png") == 1);
|
||||||
|
assert(gallery_count(g) == 1);
|
||||||
|
|
||||||
|
assert(gallery_add(g, "640x480.png") == 1);
|
||||||
|
assert(gallery_count(g) == 2);
|
||||||
|
|
||||||
|
assert(gallery_add(g, "480x320.png") == -1); // already exists
|
||||||
|
assert(gallery_count(g) == 2);
|
||||||
|
|
||||||
|
assert(gallery_add(g, "test.c") == -2); // invalid PNG format
|
||||||
|
assert(gallery_count(g) == 2);
|
||||||
|
|
||||||
|
assert(strcmp(gallery_bestfit(g, 640, 480), "640x480.png") == 0);
|
||||||
|
assert(strcmp(gallery_bestfit(g, 640, 481), "640x480.png") == 0);
|
||||||
|
assert(strcmp(gallery_bestfit(g, 640, 479), "480x320.png") == 0);
|
||||||
|
assert(gallery_bestfit(g, 0, 0) == NULL);
|
||||||
|
|
||||||
|
int myfilter(char *fn, int w, int h) { return w < 640; }
|
||||||
|
assert(gallery_filter(g, myfilter) == 1);
|
||||||
|
assert(gallery_count(g) == 1);
|
||||||
|
|
||||||
|
assert(gallery_rm(g, "480x320.png") == 1);
|
||||||
|
assert(gallery_count(g) == 0);
|
||||||
|
|
||||||
|
gallery_destroy(g);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Reference in a new issue