utils: Add a dump_framebuffer() method
[renesas/kmsxx.git] / kms++util / src / drawing.cpp
1
2 #include <cmath>
3 #include <unistd.h>
4
5 #include <kms++/kms++.h>
6 #include <kms++util/kms++util.h>
7
8 using namespace std;
9
10 namespace kms
11 {
12 void draw_rgb_pixel(IFramebuffer& buf, unsigned x, unsigned y, RGB color)
13 {
14         if (x >= buf.width() || y >= buf.height())
15                 throw runtime_error("attempt to draw outside the buffer");
16
17         switch (buf.format()) {
18         case PixelFormat::XRGB8888:
19         case PixelFormat::ARGB8888: {
20                 uint32_t* p = (uint32_t*)(buf.map(0) + buf.stride(0) * y + x * 4);
21                 *p = color.argb8888();
22                 break;
23         }
24         case PixelFormat::XBGR8888:
25         case PixelFormat::ABGR8888: {
26                 uint32_t* p = (uint32_t*)(buf.map(0) + buf.stride(0) * y + x * 4);
27                 *p = color.abgr8888();
28                 break;
29         }
30         case PixelFormat::RGBX8888:
31         case PixelFormat::RGBA8888: {
32                 uint32_t* p = (uint32_t*)(buf.map(0) + buf.stride(0) * y + x * 4);
33                 *p = color.rgba8888();
34                 break;
35         }
36         case PixelFormat::BGRX8888:
37         case PixelFormat::BGRA8888: {
38                 uint32_t* p = (uint32_t*)(buf.map(0) + buf.stride(0) * y + x * 4);
39                 *p = color.bgra8888();
40                 break;
41         }
42         case PixelFormat::XRGB2101010:
43         case PixelFormat::ARGB2101010: {
44                 uint32_t* p = (uint32_t*)(buf.map(0) + buf.stride(0) * y + x * 4);
45                 *p = color.argb2101010();
46                 break;
47         }
48         case PixelFormat::XBGR2101010:
49         case PixelFormat::ABGR2101010: {
50                 uint32_t* p = (uint32_t*)(buf.map(0) + buf.stride(0) * y + x * 4);
51                 *p = color.abgr2101010();
52                 break;
53         }
54         case PixelFormat::RGBX1010102:
55         case PixelFormat::RGBA1010102: {
56                 uint32_t* p = (uint32_t*)(buf.map(0) + buf.stride(0) * y + x * 4);
57                 *p = color.rgba1010102();
58                 break;
59         }
60         case PixelFormat::BGRX1010102:
61         case PixelFormat::BGRA1010102: {
62                 uint32_t* p = (uint32_t*)(buf.map(0) + buf.stride(0) * y + x * 4);
63                 *p = color.bgra1010102();
64                 break;
65         }
66         case PixelFormat::RGB888: {
67                 uint8_t* p = buf.map(0) + buf.stride(0) * y + x * 3;
68                 p[0] = color.b;
69                 p[1] = color.g;
70                 p[2] = color.r;
71                 break;
72         }
73         case PixelFormat::BGR888: {
74                 uint8_t* p = buf.map(0) + buf.stride(0) * y + x * 3;
75                 p[0] = color.r;
76                 p[1] = color.g;
77                 p[2] = color.b;
78                 break;
79         }
80         case PixelFormat::RGB332: {
81                 uint8_t* p = (uint8_t*)(buf.map(0) + buf.stride(0) * y + x);
82                 *p = color.rgb332();
83                 break;
84         }
85         case PixelFormat::RGB565: {
86                 uint16_t* p = (uint16_t*)(buf.map(0) + buf.stride(0) * y + x * 2);
87                 *p = color.rgb565();
88                 break;
89         }
90         case PixelFormat::BGR565: {
91                 uint16_t* p = (uint16_t*)(buf.map(0) + buf.stride(0) * y + x * 2);
92                 *p = color.bgr565();
93                 break;
94         }
95         case PixelFormat::XRGB4444:
96         case PixelFormat::ARGB4444: {
97                 uint16_t* p = (uint16_t*)(buf.map(0) + buf.stride(0) * y + x * 2);
98                 *p = color.argb4444();
99                 break;
100         }
101         case PixelFormat::XRGB1555:
102         case PixelFormat::ARGB1555: {
103                 uint16_t* p = (uint16_t*)(buf.map(0) + buf.stride(0) * y + x * 2);
104                 *p = color.argb1555();
105                 break;
106         }
107         default:
108                 throw std::invalid_argument("invalid pixelformat");
109         }
110 }
111
112 void draw_yuv444_pixel(IFramebuffer& buf, unsigned x, unsigned y, YUV yuv)
113 {
114         if (x >= buf.width() || y >= buf.height())
115                 throw runtime_error("attempt to draw outside the buffer");
116
117         uint8_t* py = (uint8_t*)(buf.map(0) + buf.stride(0) * y + x);
118         uint8_t* pu = (uint8_t*)(buf.map(1) + buf.stride(1) * y + x);
119         uint8_t* pv = (uint8_t*)(buf.map(2) + buf.stride(2) * y + x);
120
121         switch (buf.format()) {
122         case PixelFormat::YUV444:
123                 py[0] = yuv.y;
124                 pu[0] = yuv.u;
125                 pv[0] = yuv.v;
126                 break;
127
128         case PixelFormat::YVU444:
129                 py[0] = yuv.y;
130                 pu[0] = yuv.v;
131                 pv[0] = yuv.u;
132                 break;
133
134         default:
135                 throw std::invalid_argument("invalid pixelformat");
136         }
137 }
138
139 static void draw_yuv422_packed_macropixel(IFramebuffer& buf, unsigned x, unsigned y,
140                                           YUV yuv1, YUV yuv2)
141 {
142         uint8_t* p = (uint8_t*)(buf.map(0) + buf.stride(0) * y + x * 2);
143
144         uint8_t y0 = yuv1.y;
145         uint8_t y1 = yuv2.y;
146         uint8_t u = (yuv1.u + yuv2.u) / 2;
147         uint8_t v = (yuv1.v + yuv2.v) / 2;
148
149         switch (buf.format()) {
150         case PixelFormat::UYVY:
151                 p[0] = u;
152                 p[1] = y0;
153                 p[2] = v;
154                 p[3] = y1;
155                 break;
156
157         case PixelFormat::YUYV:
158                 p[0] = y0;
159                 p[1] = u;
160                 p[2] = y1;
161                 p[3] = v;
162                 break;
163
164         case PixelFormat::YVYU:
165                 p[0] = y0;
166                 p[1] = v;
167                 p[2] = y1;
168                 p[3] = u;
169                 break;
170
171         case PixelFormat::VYUY:
172                 p[0] = v;
173                 p[1] = y0;
174                 p[2] = u;
175                 p[3] = y1;
176                 break;
177
178         default:
179                 throw std::invalid_argument("invalid pixelformat");
180         }
181 }
182
183 static void draw_yuv422_semiplanar_macropixel(IFramebuffer& buf, unsigned x, unsigned y,
184                                               YUV yuv1, YUV yuv2)
185 {
186         uint8_t* py = (uint8_t*)(buf.map(0) + buf.stride(0) * y + x);
187         uint8_t* puv = (uint8_t*)(buf.map(1) + buf.stride(1) * y + x);
188
189         uint8_t y0 = yuv1.y;
190         uint8_t y1 = yuv2.y;
191         uint8_t u = (yuv1.u + yuv2.u) / 2;
192         uint8_t v = (yuv1.v + yuv2.v) / 2;
193
194         switch (buf.format()) {
195         case PixelFormat::NV16:
196                 py[0] = y0;
197                 py[1] = y1;
198                 puv[0] = u;
199                 puv[1] = v;
200                 break;
201
202         case PixelFormat::NV61:
203                 py[0] = y0;
204                 py[1] = y1;
205                 puv[0] = v;
206                 puv[1] = u;
207                 break;
208
209         default:
210                 throw std::invalid_argument("invalid pixelformat");
211         }
212 }
213
214 static void draw_yuv422_planar_macropixel(IFramebuffer& buf, unsigned x, unsigned y,
215                                           YUV yuv1, YUV yuv2)
216 {
217         uint8_t* py = (uint8_t*)(buf.map(0) + buf.stride(0) * y + x);
218         uint8_t* pu = (uint8_t*)(buf.map(1) + buf.stride(1) * y + x / 2);
219         uint8_t* pv = (uint8_t*)(buf.map(2) + buf.stride(2) * y + x / 2);
220
221         uint8_t y0 = yuv1.y;
222         uint8_t y1 = yuv2.y;
223         uint8_t u = (yuv1.u + yuv2.u) / 2;
224         uint8_t v = (yuv1.v + yuv2.v) / 2;
225
226         switch (buf.format()) {
227         case PixelFormat::YUV422:
228                 py[0] = y0;
229                 py[1] = y1;
230                 pu[0] = u;
231                 pv[0] = v;
232                 break;
233
234         case PixelFormat::YVU422:
235                 py[0] = y0;
236                 py[1] = y1;
237                 pu[0] = v;
238                 pv[0] = u;
239                 break;
240
241         default:
242                 throw std::invalid_argument("invalid pixelformat");
243         }
244 }
245
246 void draw_yuv422_macropixel(IFramebuffer& buf, unsigned x, unsigned y, YUV yuv1, YUV yuv2)
247 {
248         if ((x + 1) >= buf.width() || y >= buf.height())
249                 throw runtime_error("attempt to draw outside the buffer");
250
251         ASSERT((x & 1) == 0);
252
253         switch (buf.format()) {
254         case PixelFormat::UYVY:
255         case PixelFormat::YUYV:
256         case PixelFormat::YVYU:
257         case PixelFormat::VYUY:
258                 draw_yuv422_packed_macropixel(buf, x, y, yuv1, yuv2);
259                 break;
260
261         case PixelFormat::NV16:
262         case PixelFormat::NV61:
263                 draw_yuv422_semiplanar_macropixel(buf, x, y, yuv1, yuv2);
264                 break;
265
266         case PixelFormat::YUV422:
267         case PixelFormat::YVU422:
268                 draw_yuv422_planar_macropixel(buf, x, y, yuv1, yuv2);
269                 break;
270
271         default:
272                 throw std::invalid_argument("invalid pixelformat");
273         }
274 }
275
276 static void draw_yuv420_semiplanar_macropixel(IFramebuffer& buf, unsigned x, unsigned y,
277                                               YUV yuv1, YUV yuv2, YUV yuv3, YUV yuv4)
278 {
279         uint8_t* py1 = (uint8_t*)(buf.map(0) + buf.stride(0) * (y + 0) + x);
280         uint8_t* py2 = (uint8_t*)(buf.map(0) + buf.stride(0) * (y + 1) + x);
281
282         uint8_t* puv = (uint8_t*)(buf.map(1) + buf.stride(1) * (y / 2) + x);
283
284         uint8_t y0 = yuv1.y;
285         uint8_t y1 = yuv2.y;
286         uint8_t y2 = yuv3.y;
287         uint8_t y3 = yuv4.y;
288         uint8_t u = (yuv1.u + yuv2.u + yuv3.u + yuv4.u) / 4;
289         uint8_t v = (yuv1.v + yuv2.v + yuv3.v + yuv4.v) / 4;
290
291         switch (buf.format()) {
292         case PixelFormat::NV12:
293                 py1[0] = y0;
294                 py1[1] = y1;
295                 py2[0] = y2;
296                 py2[1] = y3;
297                 puv[0] = u;
298                 puv[1] = v;
299                 break;
300
301         case PixelFormat::NV21:
302                 py1[0] = y0;
303                 py1[1] = y1;
304                 py2[0] = y2;
305                 py2[1] = y3;
306                 puv[0] = v;
307                 puv[1] = u;
308                 break;
309
310         default:
311                 throw std::invalid_argument("invalid pixelformat");
312         }
313 }
314
315 static void draw_yuv420_planar_macropixel(IFramebuffer& buf, unsigned x, unsigned y,
316                                           YUV yuv1, YUV yuv2, YUV yuv3, YUV yuv4)
317 {
318         uint8_t* py1 = (uint8_t*)(buf.map(0) + buf.stride(0) * (y + 0) + x);
319         uint8_t* py2 = (uint8_t*)(buf.map(0) + buf.stride(0) * (y + 1) + x);
320
321         uint8_t* pu = (uint8_t*)(buf.map(1) + buf.stride(1) * (y / 2) + x / 2);
322         uint8_t* pv = (uint8_t*)(buf.map(2) + buf.stride(2) * (y / 2) + x / 2);
323
324         uint8_t y0 = yuv1.y;
325         uint8_t y1 = yuv2.y;
326         uint8_t y2 = yuv3.y;
327         uint8_t y3 = yuv4.y;
328         uint8_t u = (yuv1.u + yuv2.u + yuv3.u + yuv4.u) / 4;
329         uint8_t v = (yuv1.v + yuv2.v + yuv3.v + yuv4.v) / 4;
330
331         switch (buf.format()) {
332         case PixelFormat::YUV420:
333                 py1[0] = y0;
334                 py1[1] = y1;
335                 py2[0] = y2;
336                 py2[1] = y3;
337                 pu[0] = u;
338                 pv[0] = v;
339                 break;
340
341         case PixelFormat::YVU420:
342                 py1[0] = y0;
343                 py1[1] = y1;
344                 py2[0] = y2;
345                 py2[1] = y3;
346                 pu[0] = v;
347                 pv[0] = u;
348                 break;
349
350         default:
351                 throw std::invalid_argument("invalid pixelformat");
352         }
353 }
354
355 void draw_yuv420_macropixel(IFramebuffer& buf, unsigned x, unsigned y,
356                             YUV yuv1, YUV yuv2, YUV yuv3, YUV yuv4)
357 {
358         if ((x + 1) >= buf.width() || (y + 1) >= buf.height())
359                 throw runtime_error("attempt to draw outside the buffer");
360
361         ASSERT((x & 1) == 0);
362         ASSERT((y & 1) == 0);
363
364         switch (buf.format()) {
365         case PixelFormat::NV12:
366         case PixelFormat::NV21:
367                 draw_yuv420_semiplanar_macropixel(buf, x, y, yuv1, yuv2, yuv3, yuv4);
368                 break;
369
370         case PixelFormat::YUV420:
371         case PixelFormat::YVU420:
372                 draw_yuv420_planar_macropixel(buf, x, y, yuv1, yuv2, yuv3, yuv4);
373                 break;
374
375         default:
376                 throw std::invalid_argument("invalid pixelformat");
377         }
378 }
379
380 void draw_rect(IFramebuffer& fb, uint32_t x, uint32_t y, uint32_t w, uint32_t h, RGB color)
381 {
382         unsigned i, j;
383         YUV yuvcolor = color.yuv();
384
385         switch (fb.format()) {
386         case PixelFormat::XRGB8888:
387         case PixelFormat::XBGR8888:
388         case PixelFormat::ARGB8888:
389         case PixelFormat::ABGR8888:
390         case PixelFormat::RGB888:
391         case PixelFormat::BGR888:
392         case PixelFormat::RGB565:
393         case PixelFormat::BGR565:
394         case PixelFormat::XRGB4444:
395         case PixelFormat::XRGB1555:
396         case PixelFormat::ARGB4444:
397         case PixelFormat::ARGB1555:
398         case PixelFormat::RGB332:
399                 for (j = 0; j < h; j++) {
400                         for (i = 0; i < w; i++) {
401                                 draw_rgb_pixel(fb, x + i, y + j, color);
402                         }
403                 }
404                 break;
405
406         case PixelFormat::YUV444:
407         case PixelFormat::YVU444:
408                 for (j = 0; j < h; j++) {
409                         for (i = 0; i < w; i++) {
410                                 draw_yuv444_pixel(fb, x + i, y + j, yuvcolor);
411                         }
412                 }
413                 break;
414
415         case PixelFormat::UYVY:
416         case PixelFormat::YUYV:
417         case PixelFormat::YVYU:
418         case PixelFormat::VYUY:
419         case PixelFormat::NV16:
420         case PixelFormat::NV61:
421         case PixelFormat::YUV422:
422         case PixelFormat::YVU422:
423                 for (j = 0; j < h; j++) {
424                         for (i = 0; i < w; i += 2) {
425                                 draw_yuv422_macropixel(fb, x + i, y + j, yuvcolor, yuvcolor);
426                         }
427                 }
428                 break;
429
430         case PixelFormat::NV12:
431         case PixelFormat::NV21:
432         case PixelFormat::YUV420:
433         case PixelFormat::YVU420:
434                 for (j = 0; j < h; j += 2) {
435                         for (i = 0; i < w; i += 2) {
436                                 draw_yuv420_macropixel(fb, x + i, y + j,
437                                                        yuvcolor, yuvcolor, yuvcolor, yuvcolor);
438                         }
439                 }
440                 break;
441         default:
442                 throw std::invalid_argument("draw_rect: unknown pixelformat");
443         }
444 }
445
446 void draw_horiz_line(IFramebuffer& fb, uint32_t x1, uint32_t x2, uint32_t y, RGB color)
447 {
448         for (uint32_t x = x1; x <= x2; ++x)
449                 draw_rgb_pixel(fb, x, y, color);
450 }
451
452 void draw_circle(IFramebuffer& fb, int32_t xCenter, int32_t yCenter, int32_t radius, RGB color)
453 {
454         int32_t r2 = radius * radius;
455
456         for (int y = -radius; y <= radius; y++) {
457                 int32_t x = (int)(sqrt(r2 - y * y) + 0.5);
458                 draw_horiz_line(fb, xCenter - x, xCenter + x, yCenter - y, color);
459         }
460 }
461
462 static bool get_char_pixel(char c, uint32_t x, uint32_t y)
463 {
464 #include "font_8x8.h"
465
466         uint8_t bits = fontdata_8x8[8 * c + y];
467         bool bit = (bits >> (7 - x)) & 1;
468
469         return bit;
470 }
471
472 static void draw_char(IFramebuffer& buf, uint32_t xpos, uint32_t ypos, char c, RGB color)
473 {
474         unsigned x, y;
475         YUV yuvcolor = color.yuv();
476
477         switch (buf.format()) {
478         case PixelFormat::XRGB8888:
479         case PixelFormat::XBGR8888:
480         case PixelFormat::ARGB8888:
481         case PixelFormat::ABGR8888:
482         case PixelFormat::RGB888:
483         case PixelFormat::BGR888:
484         case PixelFormat::RGB565:
485         case PixelFormat::BGR565:
486         case PixelFormat::XRGB4444:
487         case PixelFormat::XRGB1555:
488         case PixelFormat::ARGB4444:
489         case PixelFormat::ARGB1555:
490         case PixelFormat::RGB332:
491                 for (y = 0; y < 8; y++) {
492                         for (x = 0; x < 8; x++) {
493                                 bool b = get_char_pixel(c, x, y);
494
495                                 draw_rgb_pixel(buf, xpos + x, ypos + y, b ? color : RGB());
496                         }
497                 }
498                 break;
499
500         case PixelFormat::YUV444:
501         case PixelFormat::YVU444:
502                 for (y = 0; y < 8; y++) {
503                         for (x = 0; x < 8; x++) {
504                                 bool b = get_char_pixel(c, x, y);
505
506                                 draw_yuv444_pixel(buf, xpos + x, ypos + y, b ? yuvcolor : YUV(RGB()));
507                         }
508                 }
509                 break;
510
511         case PixelFormat::UYVY:
512         case PixelFormat::YUYV:
513         case PixelFormat::YVYU:
514         case PixelFormat::VYUY:
515         case PixelFormat::NV16:
516         case PixelFormat::NV61:
517         case PixelFormat::YUV422:
518         case PixelFormat::YVU422:
519                 for (y = 0; y < 8; y++) {
520                         for (x = 0; x < 8; x += 2) {
521                                 bool b0 = get_char_pixel(c, x, y);
522                                 bool b1 = get_char_pixel(c, x + 1, y);
523
524                                 draw_yuv422_macropixel(buf, xpos + x, ypos + y,
525                                                        b0 ? yuvcolor : YUV(RGB()), b1 ? yuvcolor : YUV(RGB()));
526                         }
527                 }
528                 break;
529
530         case PixelFormat::NV12:
531         case PixelFormat::NV21:
532         case PixelFormat::YUV420:
533         case PixelFormat::YVU420:
534                 for (y = 0; y < 8; y += 2) {
535                         for (x = 0; x < 8; x += 2) {
536                                 bool b00 = get_char_pixel(c, x, y);
537                                 bool b10 = get_char_pixel(c, x + 1, y);
538                                 bool b01 = get_char_pixel(c, x, y + 1);
539                                 bool b11 = get_char_pixel(c, x + 1, y + 1);
540
541                                 draw_yuv420_macropixel(buf, xpos + x, ypos + y,
542                                                        b00 ? yuvcolor : YUV(RGB()), b10 ? yuvcolor : YUV(RGB()),
543                                                        b01 ? yuvcolor : YUV(RGB()), b11 ? yuvcolor : YUV(RGB()));
544                         }
545                 }
546                 break;
547         default:
548                 throw std::invalid_argument("draw_char: unknown pixelformat");
549         }
550 }
551
552 void draw_text(IFramebuffer& buf, uint32_t x, uint32_t y, const string& str, RGB color)
553 {
554         for (unsigned i = 0; i < str.size(); i++)
555                 draw_char(buf, (x + 8 * i), y, str[i], color);
556 }
557
558 void dump_framebuffer(IFramebuffer& fb, int fd)
559 {
560         for (unsigned int i = 0; i < fb.num_planes(); ++i)
561                 ::write(fd, fb.map(i), fb.size(i));
562 }
563
564 } // namespace kms