xref: /plan9-contrib/sys/src/cmd/jpg/onechan.c (revision 6a9fc400c33447ef5e1cda7185cb4de2c8e8010e)
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <memdraw.h>
5 #include <bio.h>
6 #include "imagefile.h"
7 
8 /* Convert image to a single channel, one byte per pixel */
9 
10 static
11 int
notrans(ulong chan)12 notrans(ulong chan)
13 {
14 	switch(chan){
15 	case GREY1:
16 	case GREY2:
17 	case GREY4:
18 	case	CMAP8:
19 	case GREY8:
20 		return 1;
21 	}
22 	return 0;
23 }
24 
25 static
26 int
easycase(ulong chan)27 easycase(ulong chan)
28 {
29 	switch(chan){
30 	case RGB16:
31 	case RGB24:
32 	case RGBA32:
33 	case ARGB32:
34 		return 1;
35 	}
36 	return 0;
37 }
38 
39 /*
40  * Convert to one byte per pixel, RGBV or grey, depending
41  */
42 
43 static
44 uchar*
load(Image * image,Memimage * memimage)45 load(Image *image, Memimage *memimage)
46 {
47 	uchar *data, *p, *q0, *q1, *q2;
48 	uchar *rgbv;
49 	int depth, ndata, dx, dy, i, v;
50 	ulong chan, pixel;
51 	Rectangle r;
52 	Rawimage ri, *nri;
53 
54 	if(memimage == nil){
55 		r = image->r;
56 		depth = image->depth;
57 		chan = image->chan;
58 	}else{
59 		r = memimage->r;
60 		depth = memimage->depth;
61 		chan = memimage->chan;
62 	}
63 	dx = Dx(r);
64 	dy = Dy(r);
65 
66 	/*
67 	 * Read image data into memory
68 	 * potentially one extra byte on each end of each scan line.
69 	 */
70 	ndata = dy*(2+bytesperline(r, depth));
71 	data = malloc(ndata);
72 	if(data == nil)
73 		return nil;
74 	if(memimage != nil)
75 		ndata = unloadmemimage(memimage, r, data, ndata);
76 	else
77 		ndata = unloadimage(image, r, data, ndata);
78 	if(ndata < 0){
79 		werrstr("onechan: %r");
80 		free(data);
81 		return nil;
82 	}
83 
84 	/*
85 	 * Repack
86 	 */
87 	memset(&ri, 0, sizeof(ri));
88 	ri.r = r;
89 	ri.cmap = nil;
90 	ri.cmaplen = 0;
91 	ri.nchans = 3;
92 	ri.chanlen = dx*dy;
93 	ri.chans[0] = malloc(ri.chanlen);
94 	ri.chans[1] = malloc(ri.chanlen);
95 	ri.chans[2] = malloc(ri.chanlen);
96 	if(ri.chans[0]==nil || ri.chans[1]==nil || ri.chans[2]==nil){
97     Err:
98 		free(ri.chans[0]);
99 		free(ri.chans[1]);
100 		free(ri.chans[2]);
101 		free(data);
102 		return nil;
103 	}
104 	ri.chandesc = CRGB;
105 
106 	p = data;
107 	q0 = ri.chans[0];
108 	q1 = ri.chans[1];
109 	q2 = ri.chans[2];
110 
111 	switch(chan){
112 	default:
113 		werrstr("can't handle image type 0x%lux", chan);
114 		goto Err;
115 	case RGB16:
116 		for(i=0; i<ri.chanlen; i++, p+=2){
117 			pixel = (p[1]<<8)|p[0];	/* rrrrrggg gggbbbbb */
118 			v = (pixel & 0xF800) >> 8;
119 			*q0++ = v | (v>>5);
120 			v = (pixel & 0x07E0) >> 3;
121 			*q1++ = v | (v>>6);
122 			v = (pixel & 0x001F) << 3;
123 			*q2++ = v | (v>>5);
124 		}
125 		break;
126 	case RGB24:
127 		for(i=0; i<ri.chanlen; i++){
128 			*q2++ = *p++;
129 			*q1++ = *p++;
130 			*q0++ = *p++;
131 		}
132 		break;
133 	case RGBA32:
134 		for(i=0; i<ri.chanlen; i++){
135 			*q2++ = *p++;
136 			*q1++ = *p++;
137 			*q0++ = *p++;
138 			p++;
139 		}
140 		break;
141 	case ARGB32:
142 		for(i=0; i<ri.chanlen; i++){
143 			p++;
144 			*q2++ = *p++;
145 			*q1++ = *p++;
146 			*q0++ = *p++;
147 		}
148 		break;
149 	}
150 
151 	rgbv = nil;
152 	nri = torgbv(&ri, 1);
153 	if(nri != nil){
154 		rgbv = nri->chans[0];
155 		free(nri);
156 	}
157 
158 	free(ri.chans[0]);
159 	free(ri.chans[1]);
160 	free(ri.chans[2]);
161 	free(data);
162 	return rgbv;
163 }
164 
165 Image*
onechan(Image * i)166 onechan(Image *i)
167 {
168 	uchar *data;
169 	Image *ni;
170 
171 	if(notrans(i->chan))
172 		return i;
173 
174 	if(easycase(i->chan))
175 		data = load(i, nil);
176 	else{
177 		ni = allocimage(display, i->r, RGB24, 0, DNofill);
178 		if(ni == nil)
179 			return ni;
180 		draw(ni, ni->r, i, nil, i->r.min);
181 		data = load(ni, nil);
182 		freeimage(ni);
183 	}
184 
185 	if(data == nil)
186 		return nil;
187 
188 	ni = allocimage(display, i->r, CMAP8, 0, DNofill);
189 	if(ni != nil)
190 		if(loadimage(ni, ni->r, data, Dx(ni->r)*Dy(ni->r)) < 0){
191 			freeimage(ni);
192 			ni = nil;
193 		}
194 	free(data);
195 	return ni;
196 }
197 
198 Memimage*
memonechan(Memimage * i)199 memonechan(Memimage *i)
200 {
201 	uchar *data;
202 	Memimage *ni;
203 
204 	if(notrans(i->chan))
205 		return i;
206 
207 	if(easycase(i->chan))
208 		data = load(nil, i);
209 	else{
210 		ni = allocmemimage(i->r, RGB24);
211 		if(ni == nil)
212 			return ni;
213 		memimagedraw(ni, ni->r, i, i->r.min, nil, ZP, S);
214 		data = load(nil, ni);
215 		freememimage(ni);
216 	}
217 
218 	if(data == nil)
219 		return nil;
220 
221 	ni = allocmemimage(i->r, CMAP8);
222 	if(ni != nil)
223 		if(loadmemimage(ni, ni->r, data, Dx(ni->r)*Dy(ni->r)) < 0){
224 			freememimage(ni);
225 			ni = nil;
226 		}
227 	free(data);
228 	return ni;
229 }
230