#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ndb.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <draw.h>
#include "mapbook.h"
#include "ifslib.h"
#include "import_ppm.h"
#include "convolute.h"
#include "imgfs.h"
File *
create_new_file(File *dir, char* filename, int perm, int id_code, Mapbook *m) {
File *file;
Aux *a;
if (dir == nil || filename == nil) return nil;
incref(dir); /* so walk doesn't kill it immediately on failure */
file = walkfile(dir, filename);
if (file) {
file = nil;
} else {
a = new_aux(id_code, m);
file = createfile(dir, filename, getuser(), perm, &a->index);
incref(dir);
}
decref(dir);
return file;
}
void
add_channels(File* dir, Mapbook* m) {
create_new_file(dir, "ctrl", 0666, id_ctrl, m);
create_new_file(dir, "ppm", 0444, id_ppm, m);
create_new_file(dir, "width", 0666, id_width, m);
create_new_file(dir, "height", 0666, id_height, m);
create_new_file(dir, "red", 0666, id_red, m);
create_new_file(dir, "green", 0666, id_green, m);
create_new_file(dir, "blue", 0666, id_blue, m);
create_new_file(dir, "select", 0666, id_select, m);
create_new_file(dir, "alpha", 0666, id_alpha, m);
// as yet unused but present for when required
// nothing to stop them being used
/*
create_new_file(dir, "cyan", 0666, id_cyan, m);
create_new_file(dir, "magenta", 0666, id_magenta, m);
create_new_file(dir, "yellow", 0666, id_yellow, m);
create_new_file(dir, "black", 0666, id_black, m);
create_new_file(dir, "hue", 0666, id_cyan, m);
create_new_file(dir, "saturation", 0666, id_magenta, m);
create_new_file(dir, "lightness", 0666, id_yellow, m);
*/
}
Aux *
create_new_image_folder(char *filename) {
Aux *a;
File *file = walkfile(images_folder, filename);
if (file)
return nil;
a = new_aux(id_imgfolder, nil);
file = createfile(images_folder, filename, getuser(), DMDIR|0555, &a->index);
incref(images_folder);
add_channels(file, a->mapbook);
return a;
}
Aux *
get_aux(void *indexp) {
Aux *a = nil;
if(indexp) {
int index = *(int*)indexp;
for(a = aux_list; a; a = a->next)
if (a->index == index) break;
}
return a;
}
Aux *
new_aux(int id, Mapbook *m) {
Aux *a;
if (m == nil) {
m = (Mapbook*) emallocz(sizeof(Mapbook));
m->index = mapbook_count++;
}
if(aux_list == nil) {
aux_list = (Aux*) emallocz (sizeof(Aux));
aux_list->index = aux_count++;
aux_list->id = id;
aux_list->mapbook = m;
return aux_list;
}
a = aux_list;
while(a->next) a = a->next;
a->next = (Aux*) emallocz (sizeof(Aux));
a->next->id = id;
a->next->index = aux_count++;
a->next->mapbook = m;
return a->next;
};
void
free_aux(Aux *a) {
a->mapbook = free_mapbook(a->mapbook);
free(a);
}
int
channel_to_id(char *channel) {
if(strcmp("red", channel) == 0)
return id_red;
if(strcmp("green", channel) == 0)
return id_green;
if(strcmp("blue", channel) == 0)
return id_blue;
if(strcmp("cyan", channel) == 0)
return id_cyan;
if(strcmp("magenta", channel) == 0)
return id_magenta;
if(strcmp("yellow", channel) == 0)
return id_yellow;
if(strcmp("black", channel) == 0)
return id_black;
if(strcmp("hue", channel) == 0)
return id_hue;
if(strcmp("saturation", channel) == 0)
return id_saturation;
if(strcmp("lightness", channel) == 0)
return id_lightness;
if(strcmp("select", channel) == 0)
return id_select;
if(strcmp("alpha", channel) == 0)
return id_alpha;
return 0;
}
char *
process_ctl_message(Aux *aux, Req *r) {
int t;
char *tokens[4];
int id;
t = getfields(r->ifcall.data, tokens, 4, 1, " \t");
switch(t) {
case 3:
if(strcmp("import", tokens[0]) == 0) {
*(strchr(tokens[2], '\n')) = 0;
if(strcmp("ppm", tokens[1]) == 0)
return import_ppm(aux, tokens[2]);
}
case 4 :
if (strcmp("convolute", tokens[0]) == 0) {
id = channel_to_id(tokens[1]);
if(id) {
*(strchr(tokens[3], '\n')) = 0;
return convolute(aux, id, tokens[2], tokens[3]);
} else {
fprint(2, "Invalid channel name\n");
}
}
break;
}
return nil;
}
void
fswrite(Req *r) {
Aux *aux ;
Bitmap *target;
char *errstr = nil;
char *txt;
vlong dataend;
aux = get_aux(r->fid->file->aux);
if(aux== nil) {
respond(nil, "not found");
return;
}
if(aux->id & 0xFF00) { // it's a channel
target = get_or_create_bitmap(aux->mapbook, aux->id);
dataend = aux->mapbook->width * aux->mapbook->height;
if ((r->ifcall.offset + r->ifcall.count) > dataend) {
respond(r, "too much data");
return;
}
memcpy(&target->data[r->ifcall.offset], r->ifcall.data, r->ifcall.count);
r->ofcall.count = r->ifcall.count;
respond(r, nil);
return;
}
switch(aux->id) {
case id_new :
if (r->ifcall.count > 0) {
txt = (char*)emallocz(r->ifcall.count+1);
memcpy(txt, r->ifcall.data, r->ifcall.count);
txt[strcspn(txt, "\n/\0 ")] = 0;
if(create_new_image_folder(txt))
r->ofcall.count = r->ifcall.count;
else
errstr = smprint("create failed - file existed");
}
break;
case id_width :
errstr = set_mapbook_width(aux->mapbook, atoi(r->ifcall.data));
break;
case id_height :
errstr = set_mapbook_height(aux->mapbook, atoi(r->ifcall.data));
break;
case id_ctrl :
errstr = process_ctl_message(aux, r);
break;
}
respond(r, errstr);
free(errstr);
}
void
fill_ofcall_with_data(Fcall *ifcall, Fcall *ofcall, int datasize, char *data) {
if (ifcall->offset < datasize) {
if((ifcall->offset + ifcall->count) < datasize) {
ofcall->data = data + ifcall->offset;
ofcall->count = ifcall->count;
} else {
ofcall->data = data + ifcall->offset;
ofcall->count = datasize - ifcall->offset;
}
} else {
ofcall->data = nil;
ofcall->count = 0;
}
}
void
fill_ofcall_with_bitmap(Fcall *ifcall, Fcall *ofcall, Mapbook *m, int id) {
Bitmap *b;
long pixels;
if(b = get_bitmap(m, id)) {
pixels = m->width * m->height;
fill_ofcall_with_data(ifcall, ofcall, pixels, (char*) b->data);
} else {
ofcall->data = nil;
ofcall->count = 0;
}
}
char *
ppm_header(Mapbook *m) {
return smprint("P6 %06d %06d 255\n", m->width, m->height);
}
long
ppm_size(char *header, Mapbook *m) {
return strlen(header) + 3 * m->width * m->height;
}
char *
fill_ofcall_with_ppm(Fcall *ifcall, Fcall *ofcall, Mapbook *m) {
char *header;
int header_size;
long ppmsize, limit, req_length, start, pixel, end_pixel;
char *insert_point;
Bitmap *r, *g, *b;
ofcall->data = nil;
ofcall->count = 0;
if(!m) return ofcall->data;
header = ppm_header(m);
header_size = strlen(header);
ppmsize = ppm_size(header, m);
if(ifcall->offset < ppmsize) {
r = get_bitmap(m, id_red);
g = get_bitmap(m, id_green);
b = get_bitmap(m, id_blue);
req_length = ifcall->offset + ifcall->count;
limit = (req_length < ppmsize) ? req_length : ppmsize;
req_length = limit - ifcall->offset;
insert_point = ofcall->data = (char*) emallocz(req_length);
start = ifcall->offset;
if(start < header_size) {
ofcall->count = header_size - start;
if (ofcall->count > ifcall->count)
ofcall->count = ifcall->count;
memcpy(insert_point, &header[start], ofcall->count);
insert_point = &ofcall->data[ofcall->count];
} else
ofcall->count = 0;
if(ofcall->count == ifcall->count) return ofcall->data;
req_length -= ofcall->count;
start = ifcall->offset + ofcall->count - header_size;
pixel = start / 3;
/*
reads always finish on a pixel boundary so that the next read starts at a pixel boundary
though I have no proof that it *always* works
this switch was supposed deal with the boundaries but it was always unused so I commented it out
switch(start % 3) {
case 1 :
insert_point[0] = r->data[pixel-1];
insert_point = &insert_point[1];
ofcall->count++;
req_length--;
if (req_length == 0) return ofcall->data;
case 2:
insert_point[0] = b->data[pixel-1];
insert_point = &insert_point[1];
ofcall->count++;
req_length--;
if (req_length == 0) return ofcall->data;
}
*/
end_pixel = pixel + req_length / 3;
for(; pixel < end_pixel; pixel++) {
insert_point[0] = r->data[pixel];
ofcall->count++;
req_length--;
if (req_length == 0) return ofcall->data;
insert_point[1] = g->data[pixel];
ofcall->count++;
req_length--;
if (req_length == 0) return ofcall->data;
insert_point[2] = b->data[pixel];
ofcall->count++;
req_length--;
if (req_length == 0) return ofcall->data;
insert_point = &insert_point[3];
}
// reads always finish on a pixel boundary so that the next read starts at a pixel boundary
// similarly there was another switch here, it was wrong so I deleted it
}
return ofcall->data;
}
void
fsread(Req *r)
{
Aux *aux;
char *data=nil;
aux = get_aux(r->fid->file->aux);
if(!aux) {
respond(r, "not found");
return;
}
switch(aux->id){
case id_width :
data = smprint("%d", aux->mapbook->width);
readstr(r, data);
break;
case id_height :
data = smprint("%d", aux->mapbook->height);
readstr(r, data);
break;
case id_ppm :
data = fill_ofcall_with_ppm(&r->ifcall, &r->ofcall, aux->mapbook);
break;
default :
if(aux->id & 0xFF00) // it's a channel
fill_ofcall_with_bitmap(&r->ifcall, &r->ofcall, aux->mapbook, aux->id);
}
respond(r, nil);
free(data);
}
void
fsend (Srv *) {
Aux *aux, *next;
for(aux = aux_list; aux; aux = next) {
next = aux->next;
free_aux(aux);
}
aux_list = nil;
}
void
fstati(Req *r) {
Aux *a = get_aux(r->fid->file->aux);
char *data;
if(a)
switch(a->id) {
case id_width :
data = smprint("%d", a->mapbook->width);
r->d.length = strlen(data);
free(data);
break;
case id_height :
data = smprint("%d", a->mapbook->height);
r->d.length = strlen(data);
free(data);
break;
case id_ppm :
data = ppm_header(a->mapbook);
r->d.length = ppm_size(data, a->mapbook);
free(data);
break;
default :
if(a->id & 0xFF00) { // it's a channel
Mapbook *m = (Mapbook*) (a->mapbook);
if(m) {
if(get_bitmap(m, a->id)) {
r->d.length = m->width * m->height;
}
}
}
break;
}
respond(r, nil);
}
Srv fs =
{
.read= fsread,
.write= fswrite,
.end= fsend,
.stat= fstati,
};
static void
usage(void)
{
fprint(2, "usage: imgfs [-n mtpt] \n");
exits("usage");
}
void
main(int argc, char **argv)
{
char *mtpt;
mtpt = "/n/imgfs";
ARGBEGIN{
case 'n':
mtpt = ARGF();
break;
}ARGEND;
if(argc != 0) usage();
tree = fs.tree = alloctree(getuser(), getuser(), DMDIR|0555, nil);
create_new_file(tree->root, "new", 0666, id_new, nil);
images_folder = create_new_file(tree->root, "images", DMDIR|0777, id_images_folder, nil);
postmountsrv(&fs, nil, mtpt, MREPL);
exits(nil);
}
/*
Put
mk install
imgfs/imgfs
cd /n/imgfs
echo arab > new
cd images/arab
imgfs/import_ppm /usr/maht/imgfs/arabian.ppm
ls -l *
wc ppm
*/
|