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