1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <memdraw.h>
5 #include <bio.h>
6
7 #define MAXLINE 70
8
9 /* imported from libdraw/arith.c to permit an extern log2 function */
10 static int log2[] = {
11 -1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4,
12 -1, -1, -1, -1, -1, -1, -1, 4 /* BUG */, -1, -1, -1, -1, -1, -1, -1, 5
13 };
14
15 /*
16 * Write data
17 */
18 static
19 char*
writedata(Biobuf * fd,Image * image,Memimage * memimage)20 writedata(Biobuf *fd, Image *image, Memimage *memimage)
21 {
22 char *err;
23 uchar *data;
24 int i, x, y, ndata, depth, col, pix, xmask, pmask;
25 ulong chan;
26 Rectangle r;
27
28 if(memimage != nil){
29 r = memimage->r;
30 depth = memimage->depth;
31 chan = memimage->chan;
32 }else{
33 r = image->r;
34 depth = image->depth;
35 chan = image->chan;
36 }
37
38 /*
39 * Read image data into memory
40 * potentially one extra byte on each end of each scan line
41 */
42 ndata = Dy(r)*(2+Dx(r)*depth/8);
43 data = malloc(ndata);
44 if(data == nil)
45 return "WritePPM: malloc failed";
46 if(memimage != nil)
47 ndata = unloadmemimage(memimage, r, data, ndata);
48 else
49 ndata = unloadimage(image, r, data, ndata);
50 if(ndata < 0){
51 err = malloc(ERRMAX);
52 if(err == nil)
53 return "WritePPM: malloc failed";
54 snprint(err, ERRMAX, "WriteGIF: %r");
55 free(data);
56 return err;
57 }
58
59 /* Encode and emit the data */
60 col = 0;
61 switch(chan){
62 case GREY1:
63 case GREY2:
64 case GREY4:
65 pmask = (1<<depth)-1;
66 xmask = 7>>log2[depth];
67 for(y=r.min.y; y<r.max.y; y++){
68 i = (y-r.min.y)*bytesperline(r, depth);
69 for(x=r.min.x; x<r.max.x; x++){
70 pix = (data[i]>>depth*((xmask-x)&xmask))&pmask;
71 if(((x+1)&xmask) == 0)
72 i++;
73 col += Bprint(fd, "%d ", pix);
74 if(col >= MAXLINE-(2+1)){
75 Bprint(fd, "\n");
76 col = 0;
77 }else
78 col += Bprint(fd, " ");
79 }
80 }
81 break;
82 case GREY8:
83 for(i=0; i<ndata; i++){
84 col += Bprint(fd, "%d ", data[i]);
85 if(col >= MAXLINE-(4+1)){
86 Bprint(fd, "\n");
87 col = 0;
88 }else
89 col += Bprint(fd, " ");
90 }
91 break;
92 case RGB24:
93 for(i=0; i<ndata; i+=3){
94 col += Bprint(fd, "%d %d %d", data[i+2], data[i+1], data[i]);
95 if(col >= MAXLINE-(4+4+4+1)){
96 Bprint(fd, "\n");
97 col = 0;
98 }else
99 col += Bprint(fd, " ");
100 }
101 break;
102 default:
103 return "WritePPM: can't handle channel type";
104 }
105
106 return nil;
107 }
108
109 static
110 char*
writeppm0(Biobuf * fd,Image * image,Memimage * memimage,Rectangle r,int chan,char * comment)111 writeppm0(Biobuf *fd, Image *image, Memimage *memimage, Rectangle r, int chan, char *comment)
112 {
113 char *err;
114
115 switch(chan){
116 case GREY1:
117 Bprint(fd, "P1\n");
118 break;
119 case GREY2:
120 case GREY4:
121 case GREY8:
122 Bprint(fd, "P2\n");
123 break;
124 case RGB24:
125 Bprint(fd, "P3\n");
126 break;
127 default:
128 return "WritePPM: can't handle channel type";
129 }
130
131 if(comment!=nil && comment[0]!='\0'){
132 Bprint(fd, "# %s", comment);
133 if(comment[strlen(comment)-1] != '\n')
134 Bprint(fd, "\n");
135 }
136 Bprint(fd, "%d %d\n", Dx(r), Dy(r));
137
138 /* maximum pixel value */
139 switch(chan){
140 case GREY2:
141 Bprint(fd, "%d\n", 3);
142 break;
143 case GREY4:
144 Bprint(fd, "%d\n", 15);
145 break;
146 case GREY8:
147 case RGB24:
148 Bprint(fd, "%d\n", 255);
149 break;
150 }
151
152 err = writedata(fd, image, memimage);
153
154 Bprint(fd, "\n");
155 Bflush(fd);
156 return err;
157 }
158
159 char*
writeppm(Biobuf * fd,Image * image,char * comment)160 writeppm(Biobuf *fd, Image *image, char *comment)
161 {
162 return writeppm0(fd, image, nil, image->r, image->chan, comment);
163 }
164
165 char*
memwriteppm(Biobuf * fd,Memimage * memimage,char * comment)166 memwriteppm(Biobuf *fd, Memimage *memimage, char *comment)
167 {
168 return writeppm0(fd, nil, memimage, memimage->r, memimage->chan, comment);
169 }
170