]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/jpg/writeppm.c
awk: make empty FS unicodely-correct.
[plan9front.git] / sys / src / cmd / jpg / writeppm.c
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 static int bitc = 0;
16 static int nbit = 0;
17
18 static
19 void
20 Bputbit(Biobufhdr *b, int c)
21 {
22         if(c >= 0x0){
23                 bitc = (bitc << 1) | (c & 0x1);
24                 nbit++;
25         }else if(nbit > 0){
26                 for(; nbit < 8; nbit++)
27                         bitc <<= 1;
28         }
29         if(nbit == 8){
30                 Bputc(b, bitc);
31                 bitc = nbit = 0;
32         }
33 }
34
35 /*
36  * Write data
37  */
38 static
39 char*
40 writedata(Biobuf *fd, Image *image, Memimage *memimage, int rflag)
41 {
42         char *err;
43         uchar *data;
44         int i, x, y, ndata, depth, col, pix, xmask, pmask;
45         ulong chan;
46         Rectangle r;
47
48         if(memimage != nil){
49                 r = memimage->r;
50                 depth = memimage->depth;
51                 chan = memimage->chan;
52         }else{
53                 r = image->r;
54                 depth = image->depth;
55                 chan = image->chan;
56         }
57
58         /* 
59          * Read image data into memory
60          * potentially one extra byte on each end of each scan line
61          */
62         ndata = Dy(r)*(2+Dx(r)*depth/8);
63         data = malloc(ndata);
64         if(data == nil)
65                 return "WritePPM: malloc failed";
66         if(memimage != nil)
67                 ndata = unloadmemimage(memimage, r, data, ndata);
68         else
69                 ndata = unloadimage(image, r, data, ndata);
70         if(ndata < 0){
71                 err = malloc(ERRMAX);
72                 if(err == nil)
73                         return "WritePPM: malloc failed";
74                 snprint(err, ERRMAX, "WritePPM: %r");
75                 free(data);
76                 return err;
77         }
78
79         /* Encode and emit the data */
80         col = 0;
81         switch(chan){
82         case GREY1:
83         case GREY2:
84         case GREY4:
85                 pmask = (1<<depth)-1;
86                 xmask = 7>>log2[depth];
87                 for(y=r.min.y; y<r.max.y; y++){
88                         i = (y-r.min.y)*bytesperline(r, depth);
89                         for(x=r.min.x; x<r.max.x; x++){
90                                 pix = (data[i]>>depth*((xmask-x)&xmask))&pmask;
91                                 if(((x+1)&xmask) == 0)
92                                         i++;
93                                 if(chan == GREY1){
94                                         pix ^= 1;
95                                         if(rflag){
96                                                 Bputbit(fd, pix);
97                                                 continue;
98                                         }
99                                 } else {
100                                         if(rflag){
101                                                 Bputc(fd, pix);
102                                                 continue;
103                                         }
104                                 }
105                                 col += Bprint(fd, "%d", pix);
106                                 if(col >= MAXLINE-(2+1)){
107                                         Bprint(fd, "\n");
108                                         col = 0;
109                                 }else if(y < r.max.y-1 || x < r.max.x-1)
110                                         col += Bprint(fd, " ");
111                         }
112                         if(rflag)
113                                 Bputbit(fd, -1);
114                 }
115                 break;
116         case GREY8:
117                 for(i=0; i<ndata; i++){
118                         if(rflag){
119                                 Bputc(fd, data[i]);
120                                 continue;
121                         }
122                         col += Bprint(fd, "%d", data[i]);
123                         if(col >= MAXLINE-(4+1)){
124                                 Bprint(fd, "\n");
125                                 col = 0;
126                         }else if(i < ndata-1)
127                                 col += Bprint(fd, " ");
128                 }
129                 break;
130         case RGB24:
131                 for(i=0; i<ndata; i+=3){
132                         if(rflag){
133                                 Bputc(fd, data[i+2]);
134                                 Bputc(fd, data[i+1]);
135                                 Bputc(fd, data[i]);
136                                 continue;
137                         }
138                         col += Bprint(fd, "%d %d %d", data[i+2], data[i+1], data[i]);
139                         if(col >= MAXLINE-(4+4+4+1)){
140                                 Bprint(fd, "\n");
141                                 col = 0;
142                         }else if(i < ndata-3)
143                                 col += Bprint(fd, " ");
144                 }
145                 break;
146         default:
147                 return "WritePPM: can't handle channel type";
148         }
149
150         return nil;
151 }
152
153 static
154 char*
155 writeppm0(Biobuf *fd, Image *image, Memimage *memimage, Rectangle r, int chan, char *comment, int rflag)
156 {
157         char *err;
158
159         switch(chan){
160         case GREY1:
161                 Bprint(fd, "%s\n", rflag? "P4": "P1");
162                 break;
163         case GREY2:
164         case GREY4:
165         case GREY8:
166                 Bprint(fd, "%s\n", rflag? "P5": "P2");
167                 break;
168         case RGB24:
169                 Bprint(fd, "%s\n", rflag? "P6": "P3");
170                 break;
171         default:
172                 return "WritePPM: can't handle channel type";
173         }
174
175         if(comment!=nil && comment[0]!='\0'){
176                 Bprint(fd, "# %s", comment);
177                 if(comment[strlen(comment)-1] != '\n')
178                         Bprint(fd, "\n");
179         }
180         Bprint(fd, "%d %d\n", Dx(r), Dy(r));
181
182         /* maximum pixel value */
183         switch(chan){
184         case GREY2:
185                 Bprint(fd, "%d\n", 3);
186                 break;
187         case GREY4:
188                 Bprint(fd, "%d\n", 15);
189                 break;
190         case GREY8:
191         case RGB24:
192                 Bprint(fd, "%d\n", 255);
193                 break;
194         }
195
196         err = writedata(fd, image, memimage, rflag);
197
198         if(!rflag)
199                 Bprint(fd, "\n");
200         Bflush(fd);
201         return err;
202 }
203
204 char*
205 writeppm(Biobuf *fd, Image *image, char *comment, int rflag)
206 {
207         return writeppm0(fd, image, nil, image->r, image->chan, comment, rflag);
208 }
209
210 char*
211 memwriteppm(Biobuf *fd, Memimage *memimage, char *comment, int rflag)
212 {
213         return writeppm0(fd, nil, memimage, memimage->r, memimage->chan, comment, rflag);
214 }