A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 Jens Arnold
11 *
12 * Rockbox driver for 2bit vertically interleaved LCDs
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23
24#include "config.h"
25
26#include "lcd.h"
27#include "kernel.h"
28#include "thread.h"
29#include <stdlib.h>
30#include "string-extra.h" /* mem*() */
31#include "file.h"
32#include "debug.h"
33#include "system.h"
34#include "font.h"
35#include "rbunicode.h"
36#include "bidi.h"
37#include "scroll_engine.h"
38
39#ifndef LCDFN /* Not compiling for remote - define macros for main LCD. */
40#define LCDFN(fn) lcd_ ## fn
41#define FBFN(fn) fb_ ## fn
42#define LCDM(ma) LCD_ ## ma
43#define FBSIZE FRAMEBUFFER_SIZE
44#define LCDNAME "lcd_"
45#define LCDFB(x,y) FBADDR(x, y)
46#define MAIN_LCD
47#endif
48
49#ifdef MAIN_LCD
50#define THIS_STRIDE STRIDE_MAIN
51#else
52#define THIS_STRIDE STRIDE_REMOTE
53#endif
54
55#define CURRENT_VP LCDFN(current_viewport)
56/*** globals ***/
57
58static FBFN(data) LCDFN(static_framebuffer)[LCDM(FBHEIGHT)][LCDM(FBWIDTH)] IRAM_LCDFRAMEBUFFER;
59static void *LCDFN(frameaddress_default)(int x, int y);
60
61static const FBFN(data) patterns[4] = {0xFFFF, 0xFF00, 0x00FF, 0x0000};
62
63static FBFN(data) *backdrop = NULL;
64static long backdrop_offset IDATA_ATTR = 0;
65
66/* shouldn't be changed unless you want system-wide framebuffer changes! */
67struct frame_buffer_t LCDFN(framebuffer_default) =
68{
69 .FBFN(ptr) = &LCDFN(static_framebuffer)[0][0],
70 .get_address_fn = &LCDFN(frameaddress_default),
71 .stride = THIS_STRIDE(LCDM(WIDTH), LCDM(HEIGHT)),
72 .elems = (LCDM(FBWIDTH)*LCDM(FBHEIGHT)),
73};
74
75static struct viewport default_vp =
76{
77 .x = 0,
78 .y = 0,
79 .width = LCDM(WIDTH),
80 .height = LCDM(HEIGHT),
81 .flags = 0,
82 .font = FONT_SYSFIXED,
83 .drawmode = DRMODE_SOLID,
84 .buffer = NULL,
85 .fg_pattern = LCDM(DEFAULT_FG),
86 .bg_pattern = LCDM(DEFAULT_BG)
87};
88
89struct viewport * CURRENT_VP IBSS_ATTR;
90
91static unsigned fg_pattern IBSS_ATTR;
92static unsigned bg_pattern IBSS_ATTR;
93
94static void *LCDFN(frameaddress_default)(int x, int y)
95{
96 /* the default expects a buffer the same size as the screen */
97 struct frame_buffer_t *fb = CURRENT_VP->buffer;
98#if defined(MAIN_LCD) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
99 size_t element = (x * LCDM(NATIVE_STRIDE)(fb->stride)) + y;
100#else
101 size_t element = (y * LCDM(NATIVE_STRIDE)(fb->stride)) + x;
102#endif
103 return fb->FBFN(ptr) + element;/*(element % fb->elems);*/
104}
105
106#include "lcd-bitmap-common.c"
107
108/* LCD init */
109void LCDFN(init)(void)
110{
111 /* Initialize the viewport */
112 LCDFN(set_viewport)(NULL);
113
114 LCDFN(clear_display)();
115 LCDFN(init_device)();
116#ifdef MAIN_LCD
117 scroll_init();
118#endif
119}
120
121/*** parameter handling ***/
122
123#if !defined(MAIN_LCD) && defined(HAVE_LCD_COLOR)
124/* When compiling for remote LCD and the main LCD is colour. */
125unsigned lcd_remote_color_to_native(unsigned color)
126{
127 unsigned r = (color & 0xf800) >> 10;
128 unsigned g = (color & 0x07e0) >> 5;
129 unsigned b = (color & 0x001f) << 2;
130 /*
131 * |R|
132 * |Y'| = |0.299000 0.587000 0.114000| |G|
133 * |B|
134 */
135 return (5*r + 9*g + b) >> 8;
136}
137#endif
138
139void LCDFN(set_foreground)(unsigned brightness)
140{
141 CURRENT_VP->fg_pattern = brightness;
142 fg_pattern = patterns[brightness & 3];
143}
144
145unsigned LCDFN(get_foreground)(void)
146{
147 return CURRENT_VP->fg_pattern;
148}
149
150void LCDFN(set_background)(unsigned brightness)
151{
152 CURRENT_VP->bg_pattern = brightness;
153 bg_pattern = patterns[brightness & 3];
154}
155
156unsigned LCDFN(get_background)(void)
157{
158 return CURRENT_VP->bg_pattern;
159}
160
161/*** low-level drawing functions ***/
162
163static void setpixel(int x, int y)
164{
165 unsigned mask = 0x0101 << (y & 7);
166 FBFN(data) *address = LCDFB(x,y>>3);
167 unsigned data = *address;
168
169 *address = data ^ ((data ^ fg_pattern) & mask);
170}
171
172static void clearpixel(int x, int y)
173{
174 unsigned mask = 0x0101 << (y & 7);
175 FBFN(data) *address = LCDFB(x,y>>3);
176 unsigned data = *address;
177
178 *address = data ^ ((data ^ bg_pattern) & mask);
179}
180
181static void clearimgpixel(int x, int y)
182{
183 unsigned mask = 0x0101 << (y & 7);
184 FBFN(data) *address = LCDFB(x,y>>3);
185 unsigned data = *address;
186
187 *address = data ^ ((data ^ *(FBFN(data) *)((long)address
188 + backdrop_offset)) & mask);
189}
190
191static void flippixel(int x, int y)
192{
193 unsigned mask = 0x0101 << (y & 7);
194 FBFN(data) *address = LCDFB(x,y>>3);
195
196 *address ^= mask;
197}
198
199static void nopixel(int x, int y)
200{
201 (void)x;
202 (void)y;
203}
204
205LCDFN(pixelfunc_type)* const LCDFN(pixelfuncs_bgcolor)[8] = {
206 flippixel, nopixel, setpixel, setpixel,
207 nopixel, clearpixel, nopixel, clearpixel
208};
209
210LCDFN(pixelfunc_type)* const LCDFN(pixelfuncs_backdrop)[8] = {
211 flippixel, nopixel, setpixel, setpixel,
212 nopixel, clearimgpixel, nopixel, clearimgpixel
213};
214
215LCDFN(pixelfunc_type)* const *LCDFN(pixelfuncs) = LCDFN(pixelfuncs_bgcolor);
216
217/* 'mask' and 'bits' contain 2 bits per pixel */
218static void ICODE_ATTR flipblock(FBFN(data) *address, unsigned mask,
219 unsigned bits)
220{
221 *address ^= bits & mask;
222}
223
224static void ICODE_ATTR bgblock(FBFN(data) *address, unsigned mask,
225 unsigned bits)
226{
227 unsigned data = *address;
228
229 *address = data ^ ((data ^ bg_pattern) & mask & ~bits);
230}
231
232static void ICODE_ATTR bgimgblock(FBFN(data) *address, unsigned mask,
233 unsigned bits)
234{
235 unsigned data = *address;
236
237 *address = data ^ ((data ^ *(FBFN(data) *)((long)address
238 + backdrop_offset)) & mask & ~bits);
239}
240
241static void ICODE_ATTR fgblock(FBFN(data) *address, unsigned mask,
242 unsigned bits)
243{
244 unsigned data = *address;
245
246 *address = data ^ ((data ^ fg_pattern) & mask & bits);
247}
248
249static void ICODE_ATTR solidblock(FBFN(data) *address, unsigned mask,
250 unsigned bits)
251{
252 unsigned data = *address;
253 unsigned bgp = bg_pattern;
254
255 bits = bgp ^ ((bgp ^ fg_pattern) & bits);
256 *address = data ^ ((data ^ bits) & mask);
257}
258
259static void ICODE_ATTR solidimgblock(FBFN(data) *address, unsigned mask,
260 unsigned bits)
261{
262 unsigned data = *address;
263 unsigned bgp = *(FBFN(data) *)((long)address + backdrop_offset);
264
265 bits = bgp ^ ((bgp ^ fg_pattern) & bits);
266 *address = data ^ ((data ^ bits) & mask);
267}
268
269static void ICODE_ATTR flipinvblock(FBFN(data) *address, unsigned mask,
270 unsigned bits)
271{
272 *address ^= ~bits & mask;
273}
274
275static void ICODE_ATTR bginvblock(FBFN(data) *address, unsigned mask,
276 unsigned bits)
277{
278 unsigned data = *address;
279
280 *address = data ^ ((data ^ bg_pattern) & mask & bits);
281}
282
283static void ICODE_ATTR bgimginvblock(FBFN(data) *address, unsigned mask,
284 unsigned bits)
285{
286 unsigned data = *address;
287
288 *address = data ^ ((data ^ *(FBFN(data) *)((long)address
289 + backdrop_offset)) & mask & bits);
290}
291
292static void ICODE_ATTR fginvblock(FBFN(data) *address, unsigned mask,
293 unsigned bits)
294{
295 unsigned data = *address;
296
297 *address = data ^ ((data ^ fg_pattern) & mask & ~bits);
298}
299
300static void ICODE_ATTR solidinvblock(FBFN(data) *address, unsigned mask,
301 unsigned bits)
302{
303 unsigned data = *address;
304 unsigned fgp = fg_pattern;
305
306 bits = fgp ^ ((fgp ^ bg_pattern) & bits);
307 *address = data ^ ((data ^ bits) & mask);
308}
309
310static void ICODE_ATTR solidimginvblock(FBFN(data) *address, unsigned mask,
311 unsigned bits)
312{
313 unsigned data = *address;
314 unsigned fgp = fg_pattern;
315
316 bits = fgp ^ ((fgp ^ *(FBFN(data) *)((long)address
317 + backdrop_offset)) & bits);
318 *address = data ^ ((data ^ bits) & mask);
319}
320
321LCDFN(blockfunc_type)* const LCDFN(blockfuncs_bgcolor)[8] = {
322 flipblock, bgblock, fgblock, solidblock,
323 flipinvblock, bginvblock, fginvblock, solidinvblock
324};
325
326LCDFN(blockfunc_type)* const LCDFN(blockfuncs_backdrop)[8] = {
327 flipblock, bgimgblock, fgblock, solidimgblock,
328 flipinvblock, bgimginvblock, fginvblock, solidimginvblock
329};
330
331LCDFN(blockfunc_type)* const *LCDFN(blockfuncs) = LCDFN(blockfuncs_bgcolor);
332
333
334void LCDFN(set_backdrop)(FBFN(data) *bd)
335{
336 backdrop = bd;
337 if (bd)
338 {
339 backdrop_offset = (long)bd - (long)LCDFB(0, 0);
340 LCDFN(pixelfuncs) = LCDFN(pixelfuncs_backdrop);
341 LCDFN(blockfuncs) = LCDFN(blockfuncs_backdrop);
342 }
343 else
344 {
345 backdrop_offset = 0;
346 LCDFN(pixelfuncs) = LCDFN(pixelfuncs_bgcolor);
347 LCDFN(blockfuncs) = LCDFN(blockfuncs_bgcolor);
348 }
349}
350
351FBFN(data)* LCDFN(get_backdrop)(void)
352{
353 return backdrop;
354}
355
356static inline void setblock(FBFN(data) *address, unsigned mask, unsigned bits)
357{
358 unsigned data = *address;
359
360 bits ^= data;
361 *address = data ^ (bits & mask);
362}
363
364/*** drawing functions ***/
365
366/* Clear the whole display */
367void LCDFN(clear_display)(void)
368{
369 if (default_vp.drawmode & DRMODE_INVERSEVID)
370 {
371 memset(LCDFB(0, 0), patterns[default_vp.fg_pattern & 3],
372 FBSIZE);
373 }
374 else
375 {
376 if (backdrop)
377 memcpy(LCDFB(0, 0), backdrop, FBSIZE);
378 else
379 memset(LCDFB(0, 0), patterns[default_vp.bg_pattern & 3],
380 FBSIZE);
381 }
382
383 LCDFN(scroll_stop)();
384}
385
386/* Draw a horizontal line (optimised) */
387void LCDFN(hline)(int x1, int x2, int y)
388{
389 struct viewport *vp = CURRENT_VP;
390 int width;
391 FBFN(data) *dst, *dst_end;
392 unsigned mask;
393 LCDFN(blockfunc_type) *bfunc;
394
395 if (!clip_viewport_hline(vp, &x1, &x2, &y))
396 return;
397
398 width = x2 - x1 + 1;
399
400 bfunc = LCDFN(blockfuncs)[vp->drawmode];
401 dst = LCDFB(x1,y>>3);
402 mask = 0x0101 << (y & 7);
403
404 dst_end = dst + width;
405 do
406 bfunc(dst++, mask, 0xFFFFu);
407 while (dst < dst_end);
408}
409
410/* Draw a vertical line (optimised) */
411void LCDFN(vline)(int x, int y1, int y2)
412{
413 struct viewport *vp = CURRENT_VP;
414 int ny;
415 FBFN(data) *dst;
416 int stride_dst;
417 unsigned mask, mask_bottom;
418 LCDFN(blockfunc_type) *bfunc;
419
420 if (!clip_viewport_vline(vp, &x, &y1, &y2))
421 return;
422
423 bfunc = LCDFN(blockfuncs)[vp->drawmode];
424 dst = LCDFB(x,y1>>3);
425 stride_dst = vp->buffer->stride;
426 ny = y2 - (y1 & ~7);
427 mask = (0xFFu << (y1 & 7)) & 0xFFu;
428 mask |= mask << 8;
429 mask_bottom = 0xFFu >> (~ny & 7);
430 mask_bottom |= mask_bottom << 8;
431
432 for (; ny >= 8; ny -= 8)
433 {
434 bfunc(dst, mask, 0xFFFFu);
435 dst += stride_dst;
436 mask = 0xFFFFu;
437 }
438 mask &= mask_bottom;
439 bfunc(dst, mask, 0xFFFFu);
440}
441
442/* Fill a rectangular area */
443void LCDFN(fillrect)(int x, int y, int width, int height)
444{
445 struct viewport *vp = CURRENT_VP;
446 int ny;
447 FBFN(data) *dst, *dst_end;
448 int stride_dst;
449 unsigned mask, mask_bottom;
450 unsigned bits = 0;
451 LCDFN(blockfunc_type) *bfunc;
452 bool fillopt = false;
453
454 if (!clip_viewport_rect(vp, &x, &y, &width, &height, NULL, NULL))
455 return;
456
457 if (vp->drawmode & DRMODE_INVERSEVID)
458 {
459 if ((vp->drawmode & DRMODE_BG) && !backdrop)
460 {
461 fillopt = true;
462 bits = bg_pattern;
463 }
464 }
465 else
466 {
467 if (vp->drawmode & DRMODE_FG)
468 {
469 fillopt = true;
470 bits = fg_pattern;
471 }
472 }
473 bfunc = LCDFN(blockfuncs)[vp->drawmode];
474 dst = LCDFB(x,y>>3);
475 stride_dst = vp->buffer->stride;
476 ny = height - 1 + (y & 7);
477 mask = (0xFFu << (y & 7)) & 0xFFu;
478 mask |= mask << 8;
479 mask_bottom = 0xFFu >> (~ny & 7);
480 mask_bottom |= mask_bottom << 8;
481
482 for (; ny >= 8; ny -= 8)
483 {
484 if (fillopt && (mask == 0xFFFFu))
485 memset16(dst, bits, width);
486 else
487 {
488 FBFN(data) *dst_row = dst;
489
490 dst_end = dst_row + width;
491 do
492 bfunc(dst_row++, mask, 0xFFFFu);
493 while (dst_row < dst_end);
494 }
495
496 dst += stride_dst;
497 mask = 0xFFFFu;
498 }
499 mask &= mask_bottom;
500
501 if (fillopt && (mask == 0xFFFFu))
502 memset16(dst, bits, width);
503 else
504 {
505 dst_end = dst + width;
506 do
507 bfunc(dst++, mask, 0xFFFFu);
508 while (dst < dst_end);
509 }
510}
511
512/* About Rockbox' internal monochrome bitmap format:
513 *
514 * A bitmap contains one bit for every pixel that defines if that pixel is
515 * black (1) or white (0). Bits within a byte are arranged vertically, LSB
516 * at top.
517 * The bytes are stored in row-major order, with byte 0 being top left,
518 * byte 1 2nd from left etc. The first row of bytes defines pixel rows
519 * 0..7, the second row defines pixel row 8..15 etc.
520 *
521 * This is similar to the internal lcd hw format. */
522
523/* Draw a partial monochrome bitmap */
524void ICODE_ATTR LCDFN(mono_bitmap_part)(const unsigned char *src, int src_x,
525 int src_y, int stride, int x, int y,
526 int width, int height)
527{
528 struct viewport *vp = CURRENT_VP;
529 int shift, ny;
530 FBFN(data) *dst, *dst_end;
531 int stride_dst;
532 unsigned data, mask, mask_bottom;
533 LCDFN(blockfunc_type) *bfunc;
534
535 if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
536 return;
537
538 src += stride * (src_y >> 3) + src_x; /* move starting point */
539 src_y &= 7;
540 y -= src_y;
541 dst = LCDFB(x,y>>3);
542 stride_dst = vp->buffer->stride;
543 shift = y & 7;
544 ny = height - 1 + shift + src_y;
545
546 bfunc = LCDFN(blockfuncs)[vp->drawmode];
547 mask = 0xFFu << (shift + src_y);
548 /* not byte-doubled here because shift+src_y can be > 7 */
549 mask_bottom = 0xFFu >> (~ny & 7);
550 mask_bottom |= mask_bottom << 8;
551
552 if (shift == 0)
553 {
554 mask &= 0xFFu;
555 mask |= mask << 8;
556
557 for (; ny >= 8; ny -= 8)
558 {
559 const unsigned char *src_row = src;
560 FBFN(data) *dst_row = dst;
561
562 dst_end = dst_row + width;
563 do
564 {
565 data = *src_row++;
566 bfunc(dst_row++, mask, data | (data << 8));
567 }
568 while (dst_row < dst_end);
569
570 src += stride;
571 dst += stride_dst;
572 mask = 0xFFFFu;
573 }
574 mask &= mask_bottom;
575
576 dst_end = dst + width;
577 do
578 {
579 data = *src++;
580 bfunc(dst++, mask, data | (data << 8));
581 }
582 while (dst < dst_end);
583 }
584 else
585 {
586 unsigned ddata;
587
588 dst_end = dst + width;
589 do
590 {
591 const unsigned char *src_col = src++;
592 FBFN(data) *dst_col = dst++;
593 unsigned mask_col = mask & 0xFFu;
594
595 mask_col |= mask_col << 8;
596 data = 0;
597
598 for (y = ny; y >= 8; y -= 8)
599 {
600 data |= *src_col << shift;
601
602 if (mask_col)
603 {
604 ddata = data & 0xFFu;
605 bfunc(dst_col, mask_col, ddata | (ddata << 8));
606 mask_col = 0xFFFFu;
607 }
608 else
609 {
610 mask_col = (mask >> 8) & 0xFFu;
611 mask_col |= mask_col << 8;
612 }
613
614 src_col += stride;
615 dst_col += stride_dst;
616 data >>= 8;
617 }
618 data |= *src_col << shift;
619 mask_col &= mask_bottom;
620 ddata = data & 0xFFu;
621 bfunc(dst_col, mask_col, ddata | (ddata << 8));
622 }
623 while (dst < dst_end);
624 }
625}
626
627/* Draw a full monochrome bitmap */
628void LCDFN(mono_bitmap)(const unsigned char *src, int x, int y, int width,
629 int height)
630{
631 LCDFN(mono_bitmap_part)(src, 0, 0, width, x, y, width, height);
632}
633
634/* About Rockbox' internal native bitmap format:
635 *
636 * A bitmap contains one bit in each byte of a pair of bytes for every pixel.
637 * 00 = white, 01 = light grey, 10 = dark grey, 11 = black. Bits within a byte
638 * are arranged vertically, LSB at top.
639 * The pairs of bytes are stored as shorts, in row-major order, with word 0
640 * being top left, word 1 2nd from left etc. The first row of words defines
641 * pixel rows 0..7, the second row defines pixel row 8..15 etc.
642 *
643 * This is the same as the internal lcd hw format. */
644
645/* Draw a partial native bitmap */
646void ICODE_ATTR LCDFN(bitmap_part)(const FBFN(data) *src, int src_x,
647 int src_y, int stride, int x, int y,
648 int width, int height)
649{
650 struct viewport *vp = CURRENT_VP;
651 int shift, ny;
652 FBFN(data) *dst, *dst_end;
653 int stride_dst;
654 unsigned mask, mask_bottom;
655
656 if (!clip_viewport_rect(vp, &x, &y, &width, &height, &src_x, &src_y))
657 return;
658
659 src += stride * (src_y >> 3) + src_x; /* move starting point */
660 src_y &= 7;
661 y -= src_y;
662 dst = LCDFB(x,y>>3);
663 stride_dst = vp->buffer->stride;
664 shift = y & 7;
665 ny = height - 1 + shift + src_y;
666
667 mask = 0xFFu << (shift + src_y);
668 /* not byte-doubled here because shift+src_y can be > 7 */
669 mask_bottom = 0xFFu >> (~ny & 7);
670 mask_bottom |= mask_bottom << 8;
671
672 if (shift == 0)
673 {
674 mask &= 0xFFu;
675 mask |= mask << 8;
676
677 for (; ny >= 8; ny -= 8)
678 {
679 if (mask == 0xFFFFu)
680 memcpy(dst, src, width * sizeof(FBFN(data)));
681 else
682 {
683 const FBFN(data) *src_row = src;
684 FBFN(data) *dst_row = dst;
685
686 dst_end = dst_row + width;
687 do
688 setblock(dst_row++, mask, *src_row++);
689 while (dst_row < dst_end);
690 }
691 src += stride;
692 dst += stride_dst;
693 mask = 0xFFFFu;
694 }
695 mask &= mask_bottom;
696
697 if (mask == 0xFFFFu)
698 memcpy(dst, src, width * sizeof(FBFN(data)));
699 else
700 {
701 dst_end = dst + width;
702 do
703 setblock(dst++, mask, *src++);
704 while (dst < dst_end);
705 }
706 }
707 else
708 {
709 unsigned datamask = (0xFFu << shift) & 0xFFu;
710
711 datamask |= datamask << 8;
712
713 dst_end = dst + width;
714 do
715 {
716 const FBFN(data) *src_col = src++;
717 FBFN(data) *dst_col = dst++;
718 unsigned mask_col = mask & 0xFFu;
719 unsigned data, olddata = 0;
720
721 mask_col |= mask_col << 8;
722
723 for (y = ny; y >= 8; y -= 8)
724 {
725 data = *src_col << shift;
726
727 if (mask_col)
728 {
729 setblock(dst_col, mask_col,
730 olddata ^((olddata ^ data) & datamask));
731 mask_col = 0xFFFFu;
732 }
733 else
734 {
735 mask_col = (mask >> 8) & 0xFFu;
736 mask_col |= mask_col << 8;
737 }
738 src_col += stride;
739 dst_col += stride_dst;
740 olddata = data >> 8;
741 }
742 data = *src_col << shift;
743 setblock(dst_col, mask_col & mask_bottom,
744 olddata ^((olddata ^ data) & datamask));
745 }
746 while (dst < dst_end);
747 }
748}
749
750/* Draw a full native bitmap */
751void LCDFN(bitmap)(const FBFN(data) *src, int x, int y, int width, int height)
752{
753 LCDFN(bitmap_part)(src, 0, 0, width, x, y, width, height);
754}