Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <bio.h>

#define MAXLINE 70

/* imported from libdraw/arith.c to permit an extern log2 function */
static int log2[] = {
        -1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4,
        -1, -1, -1, -1, -1, -1, -1, 4 /* BUG */, -1, -1, -1, -1, -1, -1, -1, 5
};

/*
 * Write data
 */
static
char*
writedata(Biobuf *fd, Image *image, Memimage *memimage)
{
        char *err;
        uchar *data;
        int i, x, y, ndata, depth, col, pix, xmask, pmask;
        ulong chan;
        Rectangle r;

        if(memimage != nil){
                r = memimage->r;
                depth = memimage->depth;
                chan = memimage->chan;
        }else{
                r = image->r;
                depth = image->depth;
                chan = image->chan;
        }

        /* 
         * Read image data into memory
         * potentially one extra byte on each end of each scan line
         */
        ndata = Dy(r)*(2+Dx(r)*depth/8);
        data = malloc(ndata);
        if(data == nil)
                return "WritePPM: malloc failed";
        if(memimage != nil)
                ndata = unloadmemimage(memimage, r, data, ndata);
        else
                ndata = unloadimage(image, r, data, ndata);
        if(ndata < 0){
                err = malloc(ERRMAX);
                if(err == nil)
                        return "WritePPM: malloc failed";
                snprint(err, ERRMAX, "WriteGIF: %r");
                free(data);
                return err;
        }

        /* Encode and emit the data */
        col = 0;
        switch(chan){
        case GREY1:
        case GREY2:
        case GREY4:
                pmask = (1<<depth)-1;
                xmask = 7>>log2[depth];
                for(y=r.min.y; y<r.max.y; y++){
                        i = (y-r.min.y)*bytesperline(r, depth);
                        for(x=r.min.x; x<r.max.x; x++){
                                pix = (data[i]>>depth*((xmask-x)&xmask))&pmask;
                                if(((x+1)&xmask) == 0)
                                        i++;
                                col += Bprint(fd, "%d ", pix);
                                if(col >= MAXLINE-(2+1)){
                                        Bprint(fd, "\n");
                                        col = 0;
                                }else
                                        col += Bprint(fd, " ");
                        }
                }
                break;
        case    GREY8:
                for(i=0; i<ndata; i++){
                        col += Bprint(fd, "%d ", data[i]);
                        if(col >= MAXLINE-(4+1)){
                                Bprint(fd, "\n");
                                col = 0;
                        }else
                                col += Bprint(fd, " ");
                }
                break;
        case RGB24:
                for(i=0; i<ndata; i+=3){
                        col += Bprint(fd, "%d %d %d", data[i+2], data[i+1], data[i]);
                        if(col >= MAXLINE-(4+4+4+1)){
                                Bprint(fd, "\n");
                                col = 0;
                        }else
                                col += Bprint(fd, " ");
                }
                break;
        default:
                return "WritePPM: can't handle channel type";
        }

        return nil;
}

static
char*
writeppm0(Biobuf *fd, Image *image, Memimage *memimage, Rectangle r, int chan, char *comment)
{
        char *err;

        switch(chan){
        case GREY1:
                Bprint(fd, "P1\n");
                break;
        case GREY2:
        case GREY4:
        case    GREY8:
                Bprint(fd, "P2\n");
                break;
        case RGB24:
                Bprint(fd, "P3\n");
                break;
        default:
                return "WritePPM: can't handle channel type";
        }

        if(comment!=nil && comment[0]!='\0'){
                Bprint(fd, "# %s", comment);
                if(comment[strlen(comment)-1] != '\n')
                        Bprint(fd, "\n");
        }
        Bprint(fd, "%d %d\n", Dx(r), Dy(r));

        /* maximum pixel value */
        switch(chan){
        case GREY2:
                Bprint(fd, "%d\n", 3);
                break;
        case GREY4:
                Bprint(fd, "%d\n", 15);
                break;
        case    GREY8:
        case RGB24:
                Bprint(fd, "%d\n", 255);
                break;
        }

        err = writedata(fd, image, memimage);

        Bprint(fd, "\n");
        Bflush(fd);
        return err;
}

char*
writeppm(Biobuf *fd, Image *image, char *comment)
{
        return writeppm0(fd, image, nil, image->r, image->chan, comment);
}

char*
memwriteppm(Biobuf *fd, Memimage *memimage, char *comment)
{
        return writeppm0(fd, nil, memimage, memimage->r, memimage->chan, comment);
}