1
2 #include <cmath>
3
4 #include <kms++/kms++.h>
5 #include <kms++util/kms++util.h>
6 #include <kms++util/endian.h>
7
8 using namespace std;
9
10 namespace kms
11 {
draw_rgb_pixel(IFramebuffer & buf,unsigned x,unsigned y,RGB color)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
draw_yuv444_pixel(IFramebuffer & buf,unsigned x,unsigned y,YUV yuv)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
draw_yuv422_packed_macropixel(IFramebuffer & buf,unsigned x,unsigned y,YUV yuv1,YUV yuv2)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
draw_y2xx_packed_macropixel(IFramebuffer & buf,unsigned x,unsigned y,YUV yuv1,YUV yuv2)183 static void draw_y2xx_packed_macropixel(IFramebuffer& buf, unsigned x, unsigned y,
184 YUV yuv1, YUV yuv2)
185 {
186 const uint32_t macro_size = 4;
187 uint16_t* p = (uint16_t*)(buf.map(0) + buf.stride(0) * y + x * macro_size);
188
189 switch (buf.format()) {
190 case PixelFormat::Y210: {
191 // XXX naive expansion to 10 bits, similar to 10-bit funcs in class RGB
192 uint16_t y0 = yuv1.y << 2;
193 uint16_t y1 = yuv2.y << 2;
194 uint16_t cb = ((yuv1.u << 2) + (yuv2.u << 2)) / 2;
195 uint16_t cr = ((yuv1.v << 2) + (yuv2.v << 2)) / 2;
196
197 // The 10 bits occupy the msb, so we shift left by 16-10 = 6
198 write16le(&p[0], y0 << 6);
199 write16le(&p[1], cb << 6);
200 write16le(&p[2], y1 << 6);
201 write16le(&p[3], cr << 6);
202 break;
203 }
204
205 case PixelFormat::Y212: {
206 // XXX naive expansion to 12 bits
207 uint16_t y0 = yuv1.y << 4;
208 uint16_t y1 = yuv2.y << 4;
209 uint16_t cb = ((yuv1.u << 4) + (yuv2.u << 4)) / 2;
210 uint16_t cr = ((yuv1.v << 4) + (yuv2.v << 4)) / 2;
211
212 // The 10 bits occupy the msb, so we shift left by 16-12 = 4
213 write16le(&p[0], y0 << 4);
214 write16le(&p[1], cb << 4);
215 write16le(&p[2], y1 << 4);
216 write16le(&p[3], cr << 4);
217 break;
218 }
219
220 case PixelFormat::Y216: {
221 // XXX naive expansion to 16 bits
222 uint16_t y0 = yuv1.y << 8;
223 uint16_t y1 = yuv2.y << 8;
224 uint16_t cb = ((yuv1.u << 8) + (yuv2.u << 8)) / 2;
225 uint16_t cr = ((yuv1.v << 8) + (yuv2.v << 8)) / 2;
226
227 write16le(&p[0], y0);
228 write16le(&p[1], cb);
229 write16le(&p[2], y1);
230 write16le(&p[3], cr);
231 break;
232 }
233
234 default:
235 throw std::invalid_argument("invalid pixelformat");
236 }
237 }
238
draw_yuv422_semiplanar_macropixel(IFramebuffer & buf,unsigned x,unsigned y,YUV yuv1,YUV yuv2)239 static void draw_yuv422_semiplanar_macropixel(IFramebuffer& buf, unsigned x, unsigned y,
240 YUV yuv1, YUV yuv2)
241 {
242 uint8_t* py = (uint8_t*)(buf.map(0) + buf.stride(0) * y + x);
243 uint8_t* puv = (uint8_t*)(buf.map(1) + buf.stride(1) * y + x);
244
245 uint8_t y0 = yuv1.y;
246 uint8_t y1 = yuv2.y;
247 uint8_t u = (yuv1.u + yuv2.u) / 2;
248 uint8_t v = (yuv1.v + yuv2.v) / 2;
249
250 switch (buf.format()) {
251 case PixelFormat::NV16:
252 py[0] = y0;
253 py[1] = y1;
254 puv[0] = u;
255 puv[1] = v;
256 break;
257
258 case PixelFormat::NV61:
259 py[0] = y0;
260 py[1] = y1;
261 puv[0] = v;
262 puv[1] = u;
263 break;
264
265 default:
266 throw std::invalid_argument("invalid pixelformat");
267 }
268 }
269
draw_yuv422_planar_macropixel(IFramebuffer & buf,unsigned x,unsigned y,YUV yuv1,YUV yuv2)270 static void draw_yuv422_planar_macropixel(IFramebuffer& buf, unsigned x, unsigned y,
271 YUV yuv1, YUV yuv2)
272 {
273 uint8_t* py = (uint8_t*)(buf.map(0) + buf.stride(0) * y + x);
274 uint8_t* pu = (uint8_t*)(buf.map(1) + buf.stride(1) * y + x / 2);
275 uint8_t* pv = (uint8_t*)(buf.map(2) + buf.stride(2) * y + x / 2);
276
277 uint8_t y0 = yuv1.y;
278 uint8_t y1 = yuv2.y;
279 uint8_t u = (yuv1.u + yuv2.u) / 2;
280 uint8_t v = (yuv1.v + yuv2.v) / 2;
281
282 switch (buf.format()) {
283 case PixelFormat::YUV422:
284 py[0] = y0;
285 py[1] = y1;
286 pu[0] = u;
287 pv[0] = v;
288 break;
289
290 case PixelFormat::YVU422:
291 py[0] = y0;
292 py[1] = y1;
293 pu[0] = v;
294 pv[0] = u;
295 break;
296
297 default:
298 throw std::invalid_argument("invalid pixelformat");
299 }
300 }
301
draw_yuv422_macropixel(IFramebuffer & buf,unsigned x,unsigned y,YUV yuv1,YUV yuv2)302 void draw_yuv422_macropixel(IFramebuffer& buf, unsigned x, unsigned y, YUV yuv1, YUV yuv2)
303 {
304 if ((x + 1) >= buf.width() || y >= buf.height())
305 throw runtime_error("attempt to draw outside the buffer");
306
307 ASSERT((x & 1) == 0);
308
309 switch (buf.format()) {
310 case PixelFormat::UYVY:
311 case PixelFormat::YUYV:
312 case PixelFormat::YVYU:
313 case PixelFormat::VYUY:
314 draw_yuv422_packed_macropixel(buf, x, y, yuv1, yuv2);
315 break;
316
317 case PixelFormat::Y210:
318 case PixelFormat::Y212:
319 case PixelFormat::Y216:
320 draw_y2xx_packed_macropixel(buf, x, y, yuv1, yuv2);
321 break;
322
323 case PixelFormat::NV16:
324 case PixelFormat::NV61:
325 draw_yuv422_semiplanar_macropixel(buf, x, y, yuv1, yuv2);
326 break;
327
328 case PixelFormat::YUV422:
329 case PixelFormat::YVU422:
330 draw_yuv422_planar_macropixel(buf, x, y, yuv1, yuv2);
331 break;
332
333 default:
334 throw std::invalid_argument("invalid pixelformat");
335 }
336 }
337
draw_yuv420_semiplanar_macropixel(IFramebuffer & buf,unsigned x,unsigned y,YUV yuv1,YUV yuv2,YUV yuv3,YUV yuv4)338 static void draw_yuv420_semiplanar_macropixel(IFramebuffer& buf, unsigned x, unsigned y,
339 YUV yuv1, YUV yuv2, YUV yuv3, YUV yuv4)
340 {
341 uint8_t* py1 = (uint8_t*)(buf.map(0) + buf.stride(0) * (y + 0) + x);
342 uint8_t* py2 = (uint8_t*)(buf.map(0) + buf.stride(0) * (y + 1) + x);
343
344 uint8_t* puv = (uint8_t*)(buf.map(1) + buf.stride(1) * (y / 2) + x);
345
346 uint8_t y0 = yuv1.y;
347 uint8_t y1 = yuv2.y;
348 uint8_t y2 = yuv3.y;
349 uint8_t y3 = yuv4.y;
350 uint8_t u = (yuv1.u + yuv2.u + yuv3.u + yuv4.u) / 4;
351 uint8_t v = (yuv1.v + yuv2.v + yuv3.v + yuv4.v) / 4;
352
353 switch (buf.format()) {
354 case PixelFormat::NV12:
355 py1[0] = y0;
356 py1[1] = y1;
357 py2[0] = y2;
358 py2[1] = y3;
359 puv[0] = u;
360 puv[1] = v;
361 break;
362
363 case PixelFormat::NV21:
364 py1[0] = y0;
365 py1[1] = y1;
366 py2[0] = y2;
367 py2[1] = y3;
368 puv[0] = v;
369 puv[1] = u;
370 break;
371
372 default:
373 throw std::invalid_argument("invalid pixelformat");
374 }
375 }
376
draw_yuv420_planar_macropixel(IFramebuffer & buf,unsigned x,unsigned y,YUV yuv1,YUV yuv2,YUV yuv3,YUV yuv4)377 static void draw_yuv420_planar_macropixel(IFramebuffer& buf, unsigned x, unsigned y,
378 YUV yuv1, YUV yuv2, YUV yuv3, YUV yuv4)
379 {
380 uint8_t* py1 = (uint8_t*)(buf.map(0) + buf.stride(0) * (y + 0) + x);
381 uint8_t* py2 = (uint8_t*)(buf.map(0) + buf.stride(0) * (y + 1) + x);
382
383 uint8_t* pu = (uint8_t*)(buf.map(1) + buf.stride(1) * (y / 2) + x / 2);
384 uint8_t* pv = (uint8_t*)(buf.map(2) + buf.stride(2) * (y / 2) + x / 2);
385
386 uint8_t y0 = yuv1.y;
387 uint8_t y1 = yuv2.y;
388 uint8_t y2 = yuv3.y;
389 uint8_t y3 = yuv4.y;
390 uint8_t u = (yuv1.u + yuv2.u + yuv3.u + yuv4.u) / 4;
391 uint8_t v = (yuv1.v + yuv2.v + yuv3.v + yuv4.v) / 4;
392
393 switch (buf.format()) {
394 case PixelFormat::YUV420:
395 py1[0] = y0;
396 py1[1] = y1;
397 py2[0] = y2;
398 py2[1] = y3;
399 pu[0] = u;
400 pv[0] = v;
401 break;
402
403 case PixelFormat::YVU420:
404 py1[0] = y0;
405 py1[1] = y1;
406 py2[0] = y2;
407 py2[1] = y3;
408 pu[0] = v;
409 pv[0] = u;
410 break;
411
412 default:
413 throw std::invalid_argument("invalid pixelformat");
414 }
415 }
416
draw_yuv420_macropixel(IFramebuffer & buf,unsigned x,unsigned y,YUV yuv1,YUV yuv2,YUV yuv3,YUV yuv4)417 void draw_yuv420_macropixel(IFramebuffer& buf, unsigned x, unsigned y,
418 YUV yuv1, YUV yuv2, YUV yuv3, YUV yuv4)
419 {
420 if ((x + 1) >= buf.width() || (y + 1) >= buf.height())
421 throw runtime_error("attempt to draw outside the buffer");
422
423 ASSERT((x & 1) == 0);
424 ASSERT((y & 1) == 0);
425
426 switch (buf.format()) {
427 case PixelFormat::NV12:
428 case PixelFormat::NV21:
429 draw_yuv420_semiplanar_macropixel(buf, x, y, yuv1, yuv2, yuv3, yuv4);
430 break;
431
432 case PixelFormat::YUV420:
433 case PixelFormat::YVU420:
434 draw_yuv420_planar_macropixel(buf, x, y, yuv1, yuv2, yuv3, yuv4);
435 break;
436
437 default:
438 throw std::invalid_argument("invalid pixelformat");
439 }
440 }
441
draw_rect(IFramebuffer & fb,uint32_t x,uint32_t y,uint32_t w,uint32_t h,RGB color)442 void draw_rect(IFramebuffer& fb, uint32_t x, uint32_t y, uint32_t w, uint32_t h, RGB color)
443 {
444 unsigned i, j;
445 YUV yuvcolor = color.yuv();
446
447 switch (fb.format()) {
448 case PixelFormat::XRGB8888:
449 case PixelFormat::XBGR8888:
450 case PixelFormat::ARGB8888:
451 case PixelFormat::ABGR8888:
452 case PixelFormat::RGB888:
453 case PixelFormat::BGR888:
454 case PixelFormat::RGB565:
455 case PixelFormat::BGR565:
456 case PixelFormat::XRGB4444:
457 case PixelFormat::XRGB1555:
458 case PixelFormat::ARGB4444:
459 case PixelFormat::ARGB1555:
460 case PixelFormat::RGB332:
461 for (j = 0; j < h; j++) {
462 for (i = 0; i < w; i++) {
463 draw_rgb_pixel(fb, x + i, y + j, color);
464 }
465 }
466 break;
467
468 case PixelFormat::YUV444:
469 case PixelFormat::YVU444:
470 for (j = 0; j < h; j++) {
471 for (i = 0; i < w; i++) {
472 draw_yuv444_pixel(fb, x + i, y + j, yuvcolor);
473 }
474 }
475 break;
476
477 case PixelFormat::UYVY:
478 case PixelFormat::YUYV:
479 case PixelFormat::YVYU:
480 case PixelFormat::VYUY:
481 case PixelFormat::NV16:
482 case PixelFormat::NV61:
483 case PixelFormat::YUV422:
484 case PixelFormat::YVU422:
485 for (j = 0; j < h; j++) {
486 for (i = 0; i < w; i += 2) {
487 draw_yuv422_macropixel(fb, x + i, y + j, yuvcolor, yuvcolor);
488 }
489 }
490 break;
491
492 case PixelFormat::NV12:
493 case PixelFormat::NV21:
494 case PixelFormat::YUV420:
495 case PixelFormat::YVU420:
496 for (j = 0; j < h; j += 2) {
497 for (i = 0; i < w; i += 2) {
498 draw_yuv420_macropixel(fb, x + i, y + j,
499 yuvcolor, yuvcolor, yuvcolor, yuvcolor);
500 }
501 }
502 break;
503 default:
504 throw std::invalid_argument("draw_rect: unknown pixelformat");
505 }
506 }
507
draw_horiz_line(IFramebuffer & fb,uint32_t x1,uint32_t x2,uint32_t y,RGB color)508 void draw_horiz_line(IFramebuffer& fb, uint32_t x1, uint32_t x2, uint32_t y, RGB color)
509 {
510 for (uint32_t x = x1; x <= x2; ++x)
511 draw_rgb_pixel(fb, x, y, color);
512 }
513
draw_circle(IFramebuffer & fb,int32_t xCenter,int32_t yCenter,int32_t radius,RGB color)514 void draw_circle(IFramebuffer& fb, int32_t xCenter, int32_t yCenter, int32_t radius, RGB color)
515 {
516 int32_t r2 = radius * radius;
517
518 for (int y = -radius; y <= radius; y++) {
519 int32_t x = (int)(sqrt(r2 - y * y) + 0.5);
520 draw_horiz_line(fb, xCenter - x, xCenter + x, yCenter - y, color);
521 }
522 }
523
get_char_pixel(char c,uint32_t x,uint32_t y)524 static bool get_char_pixel(char c, uint32_t x, uint32_t y)
525 {
526 #include "font_8x8.h"
527
528 uint8_t bits = fontdata_8x8[8 * c + y];
529 bool bit = (bits >> (7 - x)) & 1;
530
531 return bit;
532 }
533
draw_char(IFramebuffer & buf,uint32_t xpos,uint32_t ypos,char c,RGB color)534 static void draw_char(IFramebuffer& buf, uint32_t xpos, uint32_t ypos, char c, RGB color)
535 {
536 unsigned x, y;
537 YUV yuvcolor = color.yuv();
538
539 switch (buf.format()) {
540 case PixelFormat::XRGB8888:
541 case PixelFormat::XBGR8888:
542 case PixelFormat::ARGB8888:
543 case PixelFormat::ABGR8888:
544 case PixelFormat::RGB888:
545 case PixelFormat::BGR888:
546 case PixelFormat::RGB565:
547 case PixelFormat::BGR565:
548 case PixelFormat::XRGB4444:
549 case PixelFormat::XRGB1555:
550 case PixelFormat::ARGB4444:
551 case PixelFormat::ARGB1555:
552 case PixelFormat::RGB332:
553 for (y = 0; y < 8; y++) {
554 for (x = 0; x < 8; x++) {
555 bool b = get_char_pixel(c, x, y);
556
557 draw_rgb_pixel(buf, xpos + x, ypos + y, b ? color : RGB());
558 }
559 }
560 break;
561
562 case PixelFormat::YUV444:
563 case PixelFormat::YVU444:
564 for (y = 0; y < 8; y++) {
565 for (x = 0; x < 8; x++) {
566 bool b = get_char_pixel(c, x, y);
567
568 draw_yuv444_pixel(buf, xpos + x, ypos + y, b ? yuvcolor : YUV(RGB()));
569 }
570 }
571 break;
572
573 case PixelFormat::UYVY:
574 case PixelFormat::YUYV:
575 case PixelFormat::YVYU:
576 case PixelFormat::VYUY:
577 case PixelFormat::NV16:
578 case PixelFormat::NV61:
579 case PixelFormat::YUV422:
580 case PixelFormat::YVU422:
581 for (y = 0; y < 8; y++) {
582 for (x = 0; x < 8; x += 2) {
583 bool b0 = get_char_pixel(c, x, y);
584 bool b1 = get_char_pixel(c, x + 1, y);
585
586 draw_yuv422_macropixel(buf, xpos + x, ypos + y,
587 b0 ? yuvcolor : YUV(RGB()), b1 ? yuvcolor : YUV(RGB()));
588 }
589 }
590 break;
591
592 case PixelFormat::NV12:
593 case PixelFormat::NV21:
594 case PixelFormat::YUV420:
595 case PixelFormat::YVU420:
596 for (y = 0; y < 8; y += 2) {
597 for (x = 0; x < 8; x += 2) {
598 bool b00 = get_char_pixel(c, x, y);
599 bool b10 = get_char_pixel(c, x + 1, y);
600 bool b01 = get_char_pixel(c, x, y + 1);
601 bool b11 = get_char_pixel(c, x + 1, y + 1);
602
603 draw_yuv420_macropixel(buf, xpos + x, ypos + y,
604 b00 ? yuvcolor : YUV(RGB()), b10 ? yuvcolor : YUV(RGB()),
605 b01 ? yuvcolor : YUV(RGB()), b11 ? yuvcolor : YUV(RGB()));
606 }
607 }
608 break;
609 default:
610 throw std::invalid_argument("draw_char: unknown pixelformat");
611 }
612 }
613
draw_text(IFramebuffer & buf,uint32_t x,uint32_t y,const string & str,RGB color)614 void draw_text(IFramebuffer& buf, uint32_t x, uint32_t y, const string& str, RGB color)
615 {
616 for (unsigned i = 0; i < str.size(); i++)
617 draw_char(buf, (x + 8 * i), y, str[i], color);
618 }
619
620 } // namespace kms
621