1 /* Copyright 2013-2014 Pierre Ossman <ossman@cendio.se> for Cendio AB
2  *
3  * This is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This software is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this software; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
16  * USA.
17  */
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #include <rfb/PixelFormat.h>
24 
25 static const rdr::U8 pixelRed = 0xf1;
26 static const rdr::U8 pixelGreen = 0xc3;
27 static const rdr::U8 pixelBlue = 0x97;
28 
29 static const int fbWidth = 40;
30 static const int fbHeight = 30;
31 static const int fbArea = fbWidth * fbHeight;
32 // Maximum bpp, plus some room for unaligned fudging
33 static const int fbMalloc = (fbArea * 4) + 4;
34 
35 typedef bool (*testfn) (const rfb::PixelFormat&, const rfb::PixelFormat&);
36 
37 struct TestEntry {
38   const char *label;
39   testfn fn;
40 };
41 
42 #define min(a,b) (((a) < (b)) ? (a) : (b))
43 
44 namespace rfb {
45 
makePixel(const rfb::PixelFormat & pf,rdr::U8 * buffer)46 void makePixel(const rfb::PixelFormat &pf,
47                rdr::U8 *buffer)
48 {
49   rfb::Pixel p;
50 
51   p = 0;
52   p |= (pixelRed >> (8 - pf.redBits)) << pf.redShift;
53   p |= (pixelGreen >> (8 - pf.greenBits)) << pf.greenShift;
54   p |= (pixelBlue >> (8 - pf.blueBits)) << pf.blueShift;
55 
56   // FIXME: Should we reimplement this as well?
57   pf.bufferFromPixel(buffer, p);
58 }
59 
verifyPixel(const rfb::PixelFormat & dstpf,const rfb::PixelFormat & srcpf,const rdr::U8 * buffer)60 bool verifyPixel(const rfb::PixelFormat &dstpf,
61                  const rfb::PixelFormat &srcpf,
62                  const rdr::U8 *buffer)
63 {
64   rfb::Pixel p;
65   int r, g, b;
66   int re, ge, be;
67 
68   // FIXME: Should we reimplement this as well?
69   p = dstpf.pixelFromBuffer(buffer);
70 
71   r = (p >> dstpf.redShift) & dstpf.redMax;
72   g = (p >> dstpf.greenShift) & dstpf.greenMax;
73   b = (p >> dstpf.blueShift) & dstpf.blueMax;
74 
75   r = (r * 255 + dstpf.redMax/2) / dstpf.redMax;
76   g = (g * 255 + dstpf.greenMax/2) / dstpf.greenMax;
77   b = (b * 255 + dstpf.blueMax/2) / dstpf.blueMax;
78 
79   // The allowed error depends on:
80   //
81   //  a) The number of bits the format can hold
82   //  b) The number of bits the source format could hold
83 
84   re = (1 << (8 - min(dstpf.redBits, srcpf.redBits))) - 1;
85   ge = (1 << (8 - min(dstpf.greenBits, srcpf.greenBits))) - 1;
86   be = (1 << (8 - min(dstpf.blueBits, srcpf.blueBits))) - 1;
87 
88   if (abs(r - pixelRed) > re)
89     return false;
90   if (abs(g - pixelGreen) > ge)
91     return false;
92   if (abs(b - pixelBlue) > be)
93     return false;
94 
95   return true;
96 }
97 
98 }
99 
100 using rfb::makePixel;
101 using rfb::verifyPixel;
102 
testPixel(const rfb::PixelFormat & dstpf,const rfb::PixelFormat & srcpf)103 static bool testPixel(const rfb::PixelFormat &dstpf,
104                       const rfb::PixelFormat &srcpf)
105 {
106   rfb::Pixel p;
107   rdr::U8 buffer[4];
108 
109   makePixel(srcpf, buffer);
110 
111   p = srcpf.pixelFromBuffer(buffer);
112   p = dstpf.pixelFromPixel(srcpf, p);
113   memset(buffer, 0, sizeof(buffer));
114   dstpf.bufferFromPixel(buffer, p);
115 
116   if (!verifyPixel(dstpf, srcpf, buffer))
117     return false;
118 
119   return true;
120 }
121 
testBuffer(const rfb::PixelFormat & dstpf,const rfb::PixelFormat & srcpf)122 static bool testBuffer(const rfb::PixelFormat &dstpf,
123                        const rfb::PixelFormat &srcpf)
124 {
125   int i, x, y, unaligned;
126   rdr::U8 bufIn[fbMalloc], bufOut[fbMalloc];
127 
128   // Once aligned, and once unaligned
129   for (unaligned = 0;unaligned < 2;unaligned++) {
130     for (i = 0;i < fbArea;i++)
131       makePixel(srcpf, bufIn + unaligned + i*srcpf.bpp/8);
132 
133     memset(bufOut, 0, sizeof(bufOut));
134     dstpf.bufferFromBuffer(bufOut + unaligned, srcpf,
135                            bufIn + unaligned, fbArea);
136 
137     for (i = 0;i < fbArea;i++) {
138       if (!verifyPixel(dstpf, srcpf, bufOut + unaligned + i*dstpf.bpp/8))
139         return false;
140     }
141 
142     memset(bufIn, 0, sizeof(bufIn));
143     for (y = 0;y < fbHeight;y++) {
144       for (x = 0;x < fbWidth/2;x++)
145         makePixel(srcpf, bufIn + unaligned + (x + y*fbWidth)*srcpf.bpp/8);
146     }
147 
148     memset(bufOut, 0, sizeof(bufOut));
149     dstpf.bufferFromBuffer(bufOut + unaligned, srcpf, bufIn + unaligned,
150                            fbWidth/2, fbHeight, fbWidth, fbWidth);
151 
152     for (y = 0;y < fbHeight;y++) {
153       for (x = 0;x < fbWidth;x++) {
154         if (x < fbWidth/2) {
155           if (!verifyPixel(dstpf, srcpf,
156                            bufOut + unaligned + (x + y*fbWidth)*dstpf.bpp/8))
157             return false;
158         } else {
159           const rdr::U8 zero[4] = { 0, 0, 0, 0 };
160           if (memcmp(bufOut + unaligned + (x + y*fbWidth)*dstpf.bpp/8, zero,
161                      dstpf.bpp/8) != 0)
162             return false;
163         }
164       }
165     }
166   }
167 
168   return true;
169 }
170 
testRGB(const rfb::PixelFormat & dstpf,const rfb::PixelFormat & srcpf)171 static bool testRGB(const rfb::PixelFormat &dstpf,
172                     const rfb::PixelFormat &srcpf)
173 {
174   int i, x, y, unaligned;
175   rdr::U8 bufIn[fbMalloc], bufRGB[fbMalloc], bufOut[fbMalloc];
176 
177   // Once aligned, and once unaligned
178   for (unaligned = 0;unaligned < 2;unaligned++) {
179     for (i = 0;i < fbArea;i++)
180       makePixel(srcpf, bufIn + unaligned + i*srcpf.bpp/8);
181 
182     memset(bufRGB, 0, sizeof(bufRGB));
183     srcpf.rgbFromBuffer(bufRGB + unaligned, bufIn + unaligned, fbArea);
184 
185     memset(bufOut, 0, sizeof(bufOut));
186     dstpf.bufferFromRGB(bufOut + unaligned, bufRGB + unaligned, fbArea);
187 
188     for (i = 0;i < fbArea;i++) {
189       if (!verifyPixel(dstpf, srcpf, bufOut + unaligned + i*dstpf.bpp/8))
190         return false;
191     }
192 
193     memset(bufIn, 0, sizeof(bufIn));
194     for (y = 0;y < fbHeight;y++) {
195       for (x = 0;x < fbWidth/2;x++)
196         makePixel(srcpf, bufIn + unaligned + (x + y*fbWidth)*srcpf.bpp/8);
197     }
198 
199     memset(bufRGB, 0, sizeof(bufRGB));
200     srcpf.rgbFromBuffer(bufRGB + unaligned, bufIn + unaligned,
201                         fbWidth/2, fbWidth, fbHeight);
202 
203     memset(bufOut, 0, sizeof(bufOut));
204     dstpf.bufferFromRGB(bufOut + unaligned, bufRGB + unaligned,
205                         fbWidth/2, fbWidth, fbHeight);
206 
207     for (y = 0;y < fbHeight;y++) {
208       for (x = 0;x < fbWidth;x++) {
209         if (x < fbWidth/2) {
210           if (!verifyPixel(dstpf, srcpf,
211                            bufOut + unaligned + (x + y*fbWidth)*dstpf.bpp/8))
212             return false;
213         } else {
214           const rdr::U8 zero[4] = { 0, 0, 0, 0 };
215           if (memcmp(bufOut + unaligned + (x + y*fbWidth)*dstpf.bpp/8, zero,
216                      dstpf.bpp/8) != 0)
217             return false;
218         }
219       }
220     }
221   }
222 
223   return true;
224 }
225 
testPixelRGB(const rfb::PixelFormat & dstpf,const rfb::PixelFormat & srcpf)226 static bool testPixelRGB(const rfb::PixelFormat &dstpf,
227                          const rfb::PixelFormat &srcpf)
228 {
229   rfb::Pixel p;
230   rdr::U16 r16, g16, b16;
231   rdr::U8 r8, g8, b8;
232   rdr::U8 buffer[4];
233 
234   makePixel(srcpf, buffer);
235 
236   p = srcpf.pixelFromBuffer(buffer);
237   srcpf.rgbFromPixel(p, &r16, &g16, &b16);
238   p = dstpf.pixelFromRGB(r16, g16, b16);
239   memset(buffer, 0, sizeof(buffer));
240   dstpf.bufferFromPixel(buffer, p);
241 
242   if (!verifyPixel(dstpf, srcpf, buffer))
243     return false;
244 
245   makePixel(srcpf, buffer);
246 
247   p = srcpf.pixelFromBuffer(buffer);
248   srcpf.rgbFromPixel(p, &r8, &g8, &b8);
249   p = dstpf.pixelFromRGB(r8, g8, b8);
250   memset(buffer, 0, sizeof(buffer));
251   dstpf.bufferFromPixel(buffer, p);
252 
253   if (!verifyPixel(dstpf, srcpf, buffer))
254     return false;
255 
256   return true;
257 }
258 
259 struct TestEntry tests[] = {
260   {"Pixel from pixel", testPixel},
261   {"Buffer from buffer", testBuffer},
262   {"Buffer to/from RGB", testRGB},
263   {"Pixel to/from RGB", testPixelRGB},
264 };
265 
doTests(const rfb::PixelFormat & dstpf,const rfb::PixelFormat & srcpf)266 static void doTests(const rfb::PixelFormat &dstpf,
267                     const rfb::PixelFormat &srcpf)
268 {
269   size_t i;
270   char dstb[256], srcb[256];
271 
272   dstpf.print(dstb, sizeof(dstb));
273   srcpf.print(srcb, sizeof(srcb));
274 
275   printf("\n");
276   printf("%s to %s\n", srcb, dstb);
277   printf("\n");
278 
279   for (i = 0;i < sizeof(tests)/sizeof(tests[0]);i++) {
280     printf("    %s: ", tests[i].label);
281     fflush(stdout);
282     if (tests[i].fn(dstpf, srcpf))
283       printf("OK");
284     else
285       printf("FAILED");
286     printf("\n");
287   }
288 }
289 
main(int argc,char ** argv)290 int main(int argc, char **argv)
291 {
292   rfb::PixelFormat dstpf, srcpf;
293 
294   printf("Pixel Conversion Correctness Test\n");
295 
296   /* rgb888 targets */
297 
298   dstpf.parse("rgb888");
299 
300   srcpf.parse("rgb888");
301   doTests(dstpf, srcpf);
302 
303   srcpf.parse("bgr888");
304   doTests(dstpf, srcpf);
305 
306   srcpf.parse("rgb565");
307   doTests(dstpf, srcpf);
308 
309   srcpf.parse("rgb232");
310   doTests(dstpf, srcpf);
311 
312   /* rgb565 targets */
313 
314   dstpf.parse("rgb565");
315 
316   srcpf.parse("rgb888");
317   doTests(dstpf, srcpf);
318 
319   srcpf.parse("bgr565");
320   doTests(dstpf, srcpf);
321 
322   srcpf.parse("rgb232");
323   doTests(dstpf, srcpf);
324 
325   /* rgb232 targets */
326 
327   dstpf.parse("rgb232");
328 
329   srcpf.parse("rgb888");
330   doTests(dstpf, srcpf);
331 
332   srcpf.parse("rgb565");
333   doTests(dstpf, srcpf);
334 
335   srcpf.parse("bgr232");
336   doTests(dstpf, srcpf);
337 
338   /* endian conversion (both ways) */
339 
340   dstpf = rfb::PixelFormat(32, 24, false, true, 255, 255, 255, 0, 8, 16);
341   srcpf = rfb::PixelFormat(32, 24, true, true, 255, 255, 255, 0, 8, 16);
342 
343   doTests(dstpf, srcpf);
344 
345   doTests(srcpf, dstpf);
346 
347   dstpf = rfb::PixelFormat(16, 16, false, true, 31, 63, 31, 0, 5, 11);
348   srcpf = rfb::PixelFormat(16, 16, true, true, 31, 63, 31, 0, 5, 11);
349 
350   doTests(dstpf, srcpf);
351 
352   doTests(srcpf, dstpf);
353 
354   // Pesky case that is very asymetrical
355   dstpf = rfb::PixelFormat(32, 24, false, true, 255, 255, 255, 0, 8, 16);
356   srcpf = rfb::PixelFormat(32, 24, true, true, 255, 255, 255, 0, 24, 8);
357 
358   doTests(dstpf, srcpf);
359 
360   doTests(srcpf, dstpf);
361 }
362