Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/common/gfx_fb.c
34680 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright 2020 Toomas Soome
5
* Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
6
* Copyright 2020 RackTop Systems, Inc.
7
*
8
* Redistribution and use in source and binary forms, with or without
9
* modification, are permitted provided that the following conditions
10
* are met:
11
* 1. Redistributions of source code must retain the above copyright
12
* notice, this list of conditions and the following disclaimer.
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
16
*
17
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
* SUCH DAMAGE.
28
*/
29
30
/*
31
* The workhorse here is gfxfb_blt(). It is implemented to mimic UEFI
32
* GOP Blt, and allows us to fill the rectangle on screen, copy
33
* rectangle from video to buffer and buffer to video and video to video.
34
* Such implementation does allow us to have almost identical implementation
35
* for both BIOS VBE and UEFI.
36
*
37
* ALL pixel data is assumed to be 32-bit BGRA (byte order Blue, Green, Red,
38
* Alpha) format, this allows us to only handle RGB data and not to worry
39
* about mixing RGB with indexed colors.
40
* Data exchange between memory buffer and video will translate BGRA
41
* and native format as following:
42
*
43
* 32-bit to/from 32-bit is trivial case.
44
* 32-bit to/from 24-bit is also simple - we just drop the alpha channel.
45
* 32-bit to/from 16-bit is more complicated, because we nee to handle
46
* data loss from 32-bit to 16-bit. While reading/writing from/to video, we
47
* need to apply masks of 16-bit color components. This will preserve
48
* colors for terminal text. For 32-bit truecolor PMG images, we need to
49
* translate 32-bit colors to 15/16 bit colors and this means data loss.
50
* There are different algorithms how to perform such color space reduction,
51
* we are currently using bitwise right shift to reduce color space and so far
52
* this technique seems to be sufficient (see also gfx_fb_putimage(), the
53
* end of for loop).
54
* 32-bit to/from 8-bit is the most troublesome because 8-bit colors are
55
* indexed. From video, we do get color indexes, and we do translate
56
* color index values to RGB. To write to video, we again need to translate
57
* RGB to color index. Additionally, we need to translate between VGA and
58
* console colors.
59
*
60
* Our internal color data is represented using BGRA format. But the hardware
61
* used indexed colors for 8-bit colors (0-255) and for this mode we do
62
* need to perform translation to/from BGRA and index values.
63
*
64
* - paletteentry RGB <-> index -
65
* BGRA BUFFER <----/ \ - VIDEO
66
* \ /
67
* - RGB (16/24/32) -
68
*
69
* To perform index to RGB translation, we use palette table generated
70
* from when we set up 8-bit mode video. We cannot read palette data from
71
* the hardware, because not all hardware supports reading it.
72
*
73
* BGRA to index is implemented in rgb_to_color_index() by searching
74
* palette array for closest match of RBG values.
75
*
76
* Note: In 8-bit mode, We do store first 16 colors to palette registers
77
* in VGA color order, this serves two purposes; firstly,
78
* if palette update is not supported, we still have correct 16 colors.
79
* Secondly, the kernel does get correct 16 colors when some other boot
80
* loader is used. However, the palette map for 8-bit colors is using
81
* console color ordering - this does allow us to skip translation
82
* from VGA colors to console colors, while we are reading RGB data.
83
*/
84
85
#include <sys/param.h>
86
#include <stand.h>
87
#include <teken.h>
88
#include <gfx_fb.h>
89
#include <sys/font.h>
90
#include <sys/splash.h>
91
#include <sys/linker.h>
92
#include <sys/module.h>
93
#include <sys/stdint.h>
94
#include <sys/endian.h>
95
#include <pnglite.h>
96
#include <bootstrap.h>
97
#include <lz4.h>
98
#if defined(EFI)
99
#include <efi.h>
100
#include <efilib.h>
101
#else
102
#include <vbe.h>
103
#endif
104
105
#include "modinfo.h"
106
107
/* VGA text mode does use bold font. */
108
#if !defined(VGA_8X16_FONT)
109
#define VGA_8X16_FONT "/boot/fonts/8x16b.fnt"
110
#endif
111
#if !defined(DEFAULT_8X16_FONT)
112
#define DEFAULT_8X16_FONT "/boot/fonts/8x16.fnt"
113
#endif
114
115
/*
116
* Must be sorted by font size in descending order
117
*/
118
font_list_t fonts = STAILQ_HEAD_INITIALIZER(fonts);
119
120
#define DEFAULT_FONT_DATA font_data_8x16
121
extern vt_font_bitmap_data_t font_data_8x16;
122
teken_gfx_t gfx_state = { 0 };
123
124
static struct {
125
unsigned char r; /* Red percentage value. */
126
unsigned char g; /* Green percentage value. */
127
unsigned char b; /* Blue percentage value. */
128
} color_def[NCOLORS] = {
129
{0, 0, 0}, /* black */
130
{50, 0, 0}, /* dark red */
131
{0, 50, 0}, /* dark green */
132
{77, 63, 0}, /* dark yellow */
133
{20, 40, 64}, /* dark blue */
134
{50, 0, 50}, /* dark magenta */
135
{0, 50, 50}, /* dark cyan */
136
{75, 75, 75}, /* light gray */
137
138
{18, 20, 21}, /* dark gray */
139
{100, 0, 0}, /* light red */
140
{0, 100, 0}, /* light green */
141
{100, 100, 0}, /* light yellow */
142
{45, 62, 81}, /* light blue */
143
{100, 0, 100}, /* light magenta */
144
{0, 100, 100}, /* light cyan */
145
{100, 100, 100}, /* white */
146
};
147
uint32_t cmap[NCMAP];
148
149
/*
150
* Between console's palette and VGA's one:
151
* - blue and red are swapped (1 <-> 4)
152
* - yellow and cyan are swapped (3 <-> 6)
153
*/
154
const int cons_to_vga_colors[NCOLORS] = {
155
0, 4, 2, 6, 1, 5, 3, 7,
156
8, 12, 10, 14, 9, 13, 11, 15
157
};
158
159
static const int vga_to_cons_colors[NCOLORS] = {
160
0, 1, 2, 3, 4, 5, 6, 7,
161
8, 9, 10, 11, 12, 13, 14, 15
162
};
163
164
/*
165
* It is reported very slow console draw in some systems.
166
* in order to exclude buggy gop->Blt(), we want option
167
* to use direct draw to framebuffer and avoid gop->Blt.
168
* Can be toggled with "gop" command.
169
*/
170
bool ignore_gop_blt = false;
171
172
struct text_pixel *screen_buffer;
173
#if defined(EFI)
174
static EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GlyphBuffer;
175
#else
176
static struct paletteentry *GlyphBuffer;
177
#endif
178
static size_t GlyphBufferSize;
179
180
static bool insert_font(char *, FONT_FLAGS);
181
static int font_set(struct env_var *, int, const void *);
182
static void * allocate_glyphbuffer(uint32_t, uint32_t);
183
static void gfx_fb_cursor_draw(teken_gfx_t *, const teken_pos_t *, bool);
184
185
/*
186
* Initialize gfx framework.
187
*/
188
void
189
gfx_framework_init(void)
190
{
191
/*
192
* Setup font list to have builtin font.
193
*/
194
(void) insert_font(NULL, FONT_BUILTIN);
195
gfx_interp_ref(); /* Draw in the gfx interpreter for this thing */
196
}
197
198
static uint8_t *
199
gfx_get_fb_address(void)
200
{
201
return (ptov((uint32_t)gfx_state.tg_fb.fb_addr));
202
}
203
204
/*
205
* Utility function to parse gfx mode line strings.
206
*/
207
bool
208
gfx_parse_mode_str(char *str, int *x, int *y, int *depth)
209
{
210
char *p, *end;
211
212
errno = 0;
213
p = str;
214
*x = strtoul(p, &end, 0);
215
if (*x == 0 || errno != 0)
216
return (false);
217
if (*end != 'x')
218
return (false);
219
p = end + 1;
220
*y = strtoul(p, &end, 0);
221
if (*y == 0 || errno != 0)
222
return (false);
223
if (*end != 'x') {
224
*depth = -1; /* auto select */
225
} else {
226
p = end + 1;
227
*depth = strtoul(p, &end, 0);
228
if (*depth == 0 || errno != 0 || *end != '\0')
229
return (false);
230
}
231
232
return (true);
233
}
234
235
/*
236
* Returns true if we set the color from pre-existing environment, false if
237
* just used existing defaults.
238
*/
239
static bool
240
gfx_fb_evalcolor(const char *envname, teken_color_t *cattr,
241
ev_sethook_t sethook, ev_unsethook_t unsethook)
242
{
243
const char *ptr;
244
char env[10];
245
int eflags = EV_VOLATILE | EV_NOKENV;
246
bool from_env = false;
247
248
ptr = getenv(envname);
249
if (ptr != NULL) {
250
*cattr = strtol(ptr, NULL, 10);
251
252
/*
253
* If we can't unset the value, then it's probably hooked
254
* properly and we can just carry on. Otherwise, we want to
255
* reinitialize it so that we can hook it for the console that
256
* we're resetting defaults for.
257
*/
258
if (unsetenv(envname) != 0)
259
return (true);
260
from_env = true;
261
262
/*
263
* If we're carrying over an existing value, we *do* want that
264
* to propagate to the kenv.
265
*/
266
eflags &= ~EV_NOKENV;
267
}
268
269
snprintf(env, sizeof(env), "%d", *cattr);
270
env_setenv(envname, eflags, env, sethook, unsethook);
271
272
return (from_env);
273
}
274
275
void
276
gfx_fb_setcolors(teken_attr_t *attr, ev_sethook_t sethook,
277
ev_unsethook_t unsethook)
278
{
279
const char *ptr;
280
bool need_setattr = false;
281
282
/*
283
* On first run, we setup an environment hook to process any color
284
* changes. If the env is already set, we pick up fg and bg color
285
* values from the environment.
286
*/
287
if (gfx_fb_evalcolor("teken.fg_color", &attr->ta_fgcolor,
288
sethook, unsethook))
289
need_setattr = true;
290
if (gfx_fb_evalcolor("teken.bg_color", &attr->ta_bgcolor,
291
sethook, unsethook))
292
need_setattr = true;
293
294
if (need_setattr)
295
teken_set_defattr(&gfx_state.tg_teken, attr);
296
}
297
298
static uint32_t
299
rgb_color_map(uint8_t index, uint32_t rmax, int roffset,
300
uint32_t gmax, int goffset, uint32_t bmax, int boffset)
301
{
302
uint32_t color, code, gray, level;
303
304
if (index < NCOLORS) {
305
#define CF(_f, _i) ((_f ## max * color_def[(_i)]._f / 100) << _f ## offset)
306
return (CF(r, index) | CF(g, index) | CF(b, index));
307
#undef CF
308
}
309
310
#define CF(_f, _c) ((_f ## max & _c) << _f ## offset)
311
/* 6x6x6 color cube */
312
if (index > 15 && index < 232) {
313
uint32_t red, green, blue;
314
315
for (red = 0; red < 6; red++) {
316
for (green = 0; green < 6; green++) {
317
for (blue = 0; blue < 6; blue++) {
318
code = 16 + (red * 36) +
319
(green * 6) + blue;
320
if (code != index)
321
continue;
322
red = red ? (red * 40 + 55) : 0;
323
green = green ? (green * 40 + 55) : 0;
324
blue = blue ? (blue * 40 + 55) : 0;
325
color = CF(r, red);
326
color |= CF(g, green);
327
color |= CF(b, blue);
328
return (color);
329
}
330
}
331
}
332
}
333
334
/* colors 232-255 are a grayscale ramp */
335
for (gray = 0; gray < 24; gray++) {
336
level = (gray * 10) + 8;
337
code = 232 + gray;
338
if (code == index)
339
break;
340
}
341
return (CF(r, level) | CF(g, level) | CF(b, level));
342
#undef CF
343
}
344
345
/*
346
* Support for color mapping.
347
* For 8, 24 and 32 bit depth, use mask size 8.
348
* 15/16 bit depth needs to use mask size from mode,
349
* or we will lose color information from 32-bit to 15/16 bit translation.
350
*/
351
uint32_t
352
gfx_fb_color_map(uint8_t index)
353
{
354
int rmask, gmask, bmask;
355
int roff, goff, boff, bpp;
356
357
roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
358
goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
359
boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
360
bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
361
362
if (bpp == 2)
363
rmask = gfx_state.tg_fb.fb_mask_red >> roff;
364
else
365
rmask = 0xff;
366
367
if (bpp == 2)
368
gmask = gfx_state.tg_fb.fb_mask_green >> goff;
369
else
370
gmask = 0xff;
371
372
if (bpp == 2)
373
bmask = gfx_state.tg_fb.fb_mask_blue >> boff;
374
else
375
bmask = 0xff;
376
377
return (rgb_color_map(index, rmask, 16, gmask, 8, bmask, 0));
378
}
379
380
/*
381
* Get indexed color from RGB. This function is used to write data to video
382
* memory when the adapter is set to use indexed colors.
383
* Since UEFI does only support 32-bit colors, we do not implement it for
384
* UEFI because there is no need for it and we do not have palette array
385
* for UEFI.
386
*/
387
static uint8_t
388
rgb_to_color_index(uint8_t r, uint8_t g, uint8_t b)
389
{
390
#if !defined(EFI)
391
uint32_t color, best, dist, k;
392
int diff;
393
394
color = 0;
395
best = 255 * 255 * 255;
396
for (k = 0; k < NCMAP; k++) {
397
diff = r - pe8[k].Red;
398
dist = diff * diff;
399
diff = g - pe8[k].Green;
400
dist += diff * diff;
401
diff = b - pe8[k].Blue;
402
dist += diff * diff;
403
404
/* Exact match, exit the loop */
405
if (dist == 0)
406
break;
407
408
if (dist < best) {
409
color = k;
410
best = dist;
411
}
412
}
413
if (k == NCMAP)
414
k = color;
415
return (k);
416
#else
417
(void) r;
418
(void) g;
419
(void) b;
420
return (0);
421
#endif
422
}
423
424
int
425
generate_cons_palette(uint32_t *palette, int format,
426
uint32_t rmax, int roffset, uint32_t gmax, int goffset,
427
uint32_t bmax, int boffset)
428
{
429
int i;
430
431
switch (format) {
432
case COLOR_FORMAT_VGA:
433
for (i = 0; i < NCOLORS; i++)
434
palette[i] = cons_to_vga_colors[i];
435
for (; i < NCMAP; i++)
436
palette[i] = i;
437
break;
438
case COLOR_FORMAT_RGB:
439
for (i = 0; i < NCMAP; i++)
440
palette[i] = rgb_color_map(i, rmax, roffset,
441
gmax, goffset, bmax, boffset);
442
break;
443
default:
444
return (ENODEV);
445
}
446
447
return (0);
448
}
449
450
static void
451
gfx_mem_wr1(uint8_t *base, size_t size, uint32_t o, uint8_t v)
452
{
453
454
if (o >= size)
455
return;
456
*(uint8_t *)(base + o) = v;
457
}
458
459
static void
460
gfx_mem_wr2(uint8_t *base, size_t size, uint32_t o, uint16_t v)
461
{
462
463
if (o >= size)
464
return;
465
*(uint16_t *)(base + o) = v;
466
}
467
468
static void
469
gfx_mem_wr4(uint8_t *base, size_t size, uint32_t o, uint32_t v)
470
{
471
472
if (o >= size)
473
return;
474
*(uint32_t *)(base + o) = v;
475
}
476
477
static int gfxfb_blt_fill(void *BltBuffer,
478
uint32_t DestinationX, uint32_t DestinationY,
479
uint32_t Width, uint32_t Height)
480
{
481
#if defined(EFI)
482
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
483
#else
484
struct paletteentry *p;
485
#endif
486
uint32_t data, bpp, pitch, y, x;
487
int roff, goff, boff;
488
size_t size;
489
off_t off;
490
uint8_t *destination;
491
492
if (BltBuffer == NULL)
493
return (EINVAL);
494
495
if (DestinationY + Height > gfx_state.tg_fb.fb_height)
496
return (EINVAL);
497
498
if (DestinationX + Width > gfx_state.tg_fb.fb_width)
499
return (EINVAL);
500
501
if (Width == 0 || Height == 0)
502
return (EINVAL);
503
504
p = BltBuffer;
505
roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
506
goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
507
boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
508
509
if (gfx_state.tg_fb.fb_bpp == 8) {
510
data = rgb_to_color_index(p->Red, p->Green, p->Blue);
511
} else {
512
data = (p->Red &
513
(gfx_state.tg_fb.fb_mask_red >> roff)) << roff;
514
data |= (p->Green &
515
(gfx_state.tg_fb.fb_mask_green >> goff)) << goff;
516
data |= (p->Blue &
517
(gfx_state.tg_fb.fb_mask_blue >> boff)) << boff;
518
}
519
520
bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
521
pitch = gfx_state.tg_fb.fb_stride * bpp;
522
destination = gfx_get_fb_address();
523
size = gfx_state.tg_fb.fb_size;
524
525
for (y = DestinationY; y < Height + DestinationY; y++) {
526
off = y * pitch + DestinationX * bpp;
527
for (x = 0; x < Width; x++) {
528
switch (bpp) {
529
case 1:
530
gfx_mem_wr1(destination, size, off,
531
(data < NCOLORS) ?
532
cons_to_vga_colors[data] : data);
533
break;
534
case 2:
535
gfx_mem_wr2(destination, size, off, data);
536
break;
537
case 3:
538
gfx_mem_wr1(destination, size, off,
539
(data >> 16) & 0xff);
540
gfx_mem_wr1(destination, size, off + 1,
541
(data >> 8) & 0xff);
542
gfx_mem_wr1(destination, size, off + 2,
543
data & 0xff);
544
break;
545
case 4:
546
gfx_mem_wr4(destination, size, off, data);
547
break;
548
default:
549
return (EINVAL);
550
}
551
off += bpp;
552
}
553
}
554
555
return (0);
556
}
557
558
static int
559
gfxfb_blt_video_to_buffer(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
560
uint32_t DestinationX, uint32_t DestinationY,
561
uint32_t Width, uint32_t Height, uint32_t Delta)
562
{
563
#if defined(EFI)
564
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
565
#else
566
struct paletteentry *p;
567
#endif
568
uint32_t x, sy, dy;
569
uint32_t bpp, pitch, copybytes;
570
off_t off;
571
uint8_t *source, *destination, *sb;
572
uint8_t rm, rp, gm, gp, bm, bp;
573
bool bgra;
574
575
if (BltBuffer == NULL)
576
return (EINVAL);
577
578
if (SourceY + Height >
579
gfx_state.tg_fb.fb_height)
580
return (EINVAL);
581
582
if (SourceX + Width > gfx_state.tg_fb.fb_width)
583
return (EINVAL);
584
585
if (Width == 0 || Height == 0)
586
return (EINVAL);
587
588
if (Delta == 0)
589
Delta = Width * sizeof (*p);
590
591
bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
592
pitch = gfx_state.tg_fb.fb_stride * bpp;
593
594
copybytes = Width * bpp;
595
596
rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
597
gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
598
bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
599
rm = gfx_state.tg_fb.fb_mask_red >> rp;
600
gm = gfx_state.tg_fb.fb_mask_green >> gp;
601
bm = gfx_state.tg_fb.fb_mask_blue >> bp;
602
603
/* If FB pixel format is BGRA, we can use direct copy. */
604
bgra = bpp == 4 &&
605
ffs(rm) - 1 == 8 && rp == 16 &&
606
ffs(gm) - 1 == 8 && gp == 8 &&
607
ffs(bm) - 1 == 8 && bp == 0;
608
609
for (sy = SourceY, dy = DestinationY; dy < Height + DestinationY;
610
sy++, dy++) {
611
off = sy * pitch + SourceX * bpp;
612
source = gfx_get_fb_address() + off;
613
destination = (uint8_t *)BltBuffer + dy * Delta +
614
DestinationX * sizeof (*p);
615
616
if (bgra) {
617
bcopy(source, destination, copybytes);
618
} else {
619
for (x = 0; x < Width; x++) {
620
uint32_t c = 0;
621
622
p = (void *)(destination + x * sizeof (*p));
623
sb = source + x * bpp;
624
switch (bpp) {
625
case 1:
626
c = *sb;
627
break;
628
case 2:
629
c = *(uint16_t *)sb;
630
break;
631
case 3:
632
c = sb[0] << 16 | sb[1] << 8 | sb[2];
633
break;
634
case 4:
635
c = *(uint32_t *)sb;
636
break;
637
default:
638
return (EINVAL);
639
}
640
641
if (bpp == 1) {
642
*(uint32_t *)p = gfx_fb_color_map(
643
(c < 16) ?
644
vga_to_cons_colors[c] : c);
645
} else {
646
p->Red = (c >> rp) & rm;
647
p->Green = (c >> gp) & gm;
648
p->Blue = (c >> bp) & bm;
649
p->Reserved = 0;
650
}
651
}
652
}
653
}
654
655
return (0);
656
}
657
658
static int
659
gfxfb_blt_buffer_to_video(void *BltBuffer, uint32_t SourceX, uint32_t SourceY,
660
uint32_t DestinationX, uint32_t DestinationY,
661
uint32_t Width, uint32_t Height, uint32_t Delta)
662
{
663
#if defined(EFI)
664
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
665
#else
666
struct paletteentry *p;
667
#endif
668
uint32_t x, sy, dy;
669
uint32_t bpp, pitch, copybytes;
670
off_t off;
671
uint8_t *source, *destination;
672
uint8_t rm, rp, gm, gp, bm, bp;
673
bool bgra;
674
675
if (BltBuffer == NULL)
676
return (EINVAL);
677
678
if (DestinationY + Height >
679
gfx_state.tg_fb.fb_height)
680
return (EINVAL);
681
682
if (DestinationX + Width > gfx_state.tg_fb.fb_width)
683
return (EINVAL);
684
685
if (Width == 0 || Height == 0)
686
return (EINVAL);
687
688
if (Delta == 0)
689
Delta = Width * sizeof (*p);
690
691
bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
692
pitch = gfx_state.tg_fb.fb_stride * bpp;
693
694
copybytes = Width * bpp;
695
696
rp = ffs(gfx_state.tg_fb.fb_mask_red) - 1;
697
gp = ffs(gfx_state.tg_fb.fb_mask_green) - 1;
698
bp = ffs(gfx_state.tg_fb.fb_mask_blue) - 1;
699
rm = gfx_state.tg_fb.fb_mask_red >> rp;
700
gm = gfx_state.tg_fb.fb_mask_green >> gp;
701
bm = gfx_state.tg_fb.fb_mask_blue >> bp;
702
703
/* If FB pixel format is BGRA, we can use direct copy. */
704
bgra = bpp == 4 &&
705
ffs(rm) - 1 == 8 && rp == 16 &&
706
ffs(gm) - 1 == 8 && gp == 8 &&
707
ffs(bm) - 1 == 8 && bp == 0;
708
709
for (sy = SourceY, dy = DestinationY; sy < Height + SourceY;
710
sy++, dy++) {
711
off = dy * pitch + DestinationX * bpp;
712
destination = gfx_get_fb_address() + off;
713
714
if (bgra) {
715
source = (uint8_t *)BltBuffer + sy * Delta +
716
SourceX * sizeof (*p);
717
bcopy(source, destination, copybytes);
718
} else {
719
for (x = 0; x < Width; x++) {
720
uint32_t c;
721
722
p = (void *)((uint8_t *)BltBuffer +
723
sy * Delta +
724
(SourceX + x) * sizeof (*p));
725
if (bpp == 1) {
726
c = rgb_to_color_index(p->Red,
727
p->Green, p->Blue);
728
} else {
729
c = (p->Red & rm) << rp |
730
(p->Green & gm) << gp |
731
(p->Blue & bm) << bp;
732
}
733
off = x * bpp;
734
switch (bpp) {
735
case 1:
736
gfx_mem_wr1(destination, copybytes,
737
off, (c < 16) ?
738
cons_to_vga_colors[c] : c);
739
break;
740
case 2:
741
gfx_mem_wr2(destination, copybytes,
742
off, c);
743
break;
744
case 3:
745
gfx_mem_wr1(destination, copybytes,
746
off, (c >> 16) & 0xff);
747
gfx_mem_wr1(destination, copybytes,
748
off + 1, (c >> 8) & 0xff);
749
gfx_mem_wr1(destination, copybytes,
750
off + 2, c & 0xff);
751
break;
752
case 4:
753
gfx_mem_wr4(destination, copybytes,
754
x * bpp, c);
755
break;
756
default:
757
return (EINVAL);
758
}
759
}
760
}
761
}
762
763
return (0);
764
}
765
766
static int
767
gfxfb_blt_video_to_video(uint32_t SourceX, uint32_t SourceY,
768
uint32_t DestinationX, uint32_t DestinationY,
769
uint32_t Width, uint32_t Height)
770
{
771
uint32_t bpp, copybytes;
772
int pitch;
773
uint8_t *source, *destination;
774
off_t off;
775
776
if (SourceY + Height >
777
gfx_state.tg_fb.fb_height)
778
return (EINVAL);
779
780
if (SourceX + Width > gfx_state.tg_fb.fb_width)
781
return (EINVAL);
782
783
if (DestinationY + Height >
784
gfx_state.tg_fb.fb_height)
785
return (EINVAL);
786
787
if (DestinationX + Width > gfx_state.tg_fb.fb_width)
788
return (EINVAL);
789
790
if (Width == 0 || Height == 0)
791
return (EINVAL);
792
793
bpp = roundup2(gfx_state.tg_fb.fb_bpp, 8) >> 3;
794
pitch = gfx_state.tg_fb.fb_stride * bpp;
795
796
copybytes = Width * bpp;
797
798
off = SourceY * pitch + SourceX * bpp;
799
source = gfx_get_fb_address() + off;
800
off = DestinationY * pitch + DestinationX * bpp;
801
destination = gfx_get_fb_address() + off;
802
803
if ((uintptr_t)destination > (uintptr_t)source) {
804
source += Height * pitch;
805
destination += Height * pitch;
806
pitch = -pitch;
807
}
808
809
while (Height-- > 0) {
810
bcopy(source, destination, copybytes);
811
source += pitch;
812
destination += pitch;
813
}
814
815
return (0);
816
}
817
818
static void
819
gfxfb_shadow_fill(uint32_t *BltBuffer,
820
uint32_t DestinationX, uint32_t DestinationY,
821
uint32_t Width, uint32_t Height)
822
{
823
uint32_t fbX, fbY;
824
825
if (gfx_state.tg_shadow_fb == NULL)
826
return;
827
828
fbX = gfx_state.tg_fb.fb_width;
829
fbY = gfx_state.tg_fb.fb_height;
830
831
if (BltBuffer == NULL)
832
return;
833
834
if (DestinationX + Width > fbX)
835
Width = fbX - DestinationX;
836
837
if (DestinationY + Height > fbY)
838
Height = fbY - DestinationY;
839
840
uint32_t y2 = Height + DestinationY;
841
for (uint32_t y1 = DestinationY; y1 < y2; y1++) {
842
uint32_t off = y1 * fbX + DestinationX;
843
844
for (uint32_t x = 0; x < Width; x++) {
845
gfx_state.tg_shadow_fb[off + x] = *BltBuffer;
846
}
847
}
848
}
849
850
int
851
gfxfb_blt(void *BltBuffer, GFXFB_BLT_OPERATION BltOperation,
852
uint32_t SourceX, uint32_t SourceY,
853
uint32_t DestinationX, uint32_t DestinationY,
854
uint32_t Width, uint32_t Height, uint32_t Delta)
855
{
856
int rv;
857
#if defined(EFI)
858
EFI_STATUS status;
859
EFI_GRAPHICS_OUTPUT *gop = gfx_state.tg_private;
860
EFI_TPL tpl;
861
862
/*
863
* We assume Blt() does work, if not, we will need to build exception
864
* list case by case. We only have boot services during part of our
865
* exectution. Once terminate boot services, these operations cannot be
866
* done as they are provided by protocols that disappear when exit
867
* boot services.
868
*/
869
if (!ignore_gop_blt && gop != NULL && boot_services_active) {
870
tpl = BS->RaiseTPL(TPL_NOTIFY);
871
switch (BltOperation) {
872
case GfxFbBltVideoFill:
873
gfxfb_shadow_fill(BltBuffer, DestinationX,
874
DestinationY, Width, Height);
875
status = gop->Blt(gop, BltBuffer, EfiBltVideoFill,
876
SourceX, SourceY, DestinationX, DestinationY,
877
Width, Height, Delta);
878
break;
879
880
case GfxFbBltVideoToBltBuffer:
881
status = gop->Blt(gop, BltBuffer,
882
EfiBltVideoToBltBuffer,
883
SourceX, SourceY, DestinationX, DestinationY,
884
Width, Height, Delta);
885
break;
886
887
case GfxFbBltBufferToVideo:
888
status = gop->Blt(gop, BltBuffer, EfiBltBufferToVideo,
889
SourceX, SourceY, DestinationX, DestinationY,
890
Width, Height, Delta);
891
break;
892
893
case GfxFbBltVideoToVideo:
894
status = gop->Blt(gop, BltBuffer, EfiBltVideoToVideo,
895
SourceX, SourceY, DestinationX, DestinationY,
896
Width, Height, Delta);
897
break;
898
899
default:
900
status = EFI_INVALID_PARAMETER;
901
break;
902
}
903
904
switch (status) {
905
case EFI_SUCCESS:
906
rv = 0;
907
break;
908
909
case EFI_INVALID_PARAMETER:
910
rv = EINVAL;
911
break;
912
913
case EFI_DEVICE_ERROR:
914
default:
915
rv = EIO;
916
break;
917
}
918
919
BS->RestoreTPL(tpl);
920
return (rv);
921
}
922
#endif
923
924
switch (BltOperation) {
925
case GfxFbBltVideoFill:
926
gfxfb_shadow_fill(BltBuffer, DestinationX, DestinationY,
927
Width, Height);
928
rv = gfxfb_blt_fill(BltBuffer, DestinationX, DestinationY,
929
Width, Height);
930
break;
931
932
case GfxFbBltVideoToBltBuffer:
933
rv = gfxfb_blt_video_to_buffer(BltBuffer, SourceX, SourceY,
934
DestinationX, DestinationY, Width, Height, Delta);
935
break;
936
937
case GfxFbBltBufferToVideo:
938
rv = gfxfb_blt_buffer_to_video(BltBuffer, SourceX, SourceY,
939
DestinationX, DestinationY, Width, Height, Delta);
940
break;
941
942
case GfxFbBltVideoToVideo:
943
rv = gfxfb_blt_video_to_video(SourceX, SourceY,
944
DestinationX, DestinationY, Width, Height);
945
break;
946
947
default:
948
rv = EINVAL;
949
break;
950
}
951
return (rv);
952
}
953
954
void
955
gfx_bitblt_bitmap(teken_gfx_t *state, const uint8_t *glyph,
956
const teken_attr_t *a, uint32_t alpha, bool cursor)
957
{
958
uint32_t width, height;
959
uint32_t fgc, bgc, bpl, cc, o;
960
int bpp, bit, byte;
961
bool invert = false;
962
963
bpp = 4; /* We only generate BGRA */
964
width = state->tg_font.vf_width;
965
height = state->tg_font.vf_height;
966
bpl = (width + 7) / 8; /* Bytes per source line. */
967
968
fgc = a->ta_fgcolor;
969
bgc = a->ta_bgcolor;
970
if (a->ta_format & TF_BOLD)
971
fgc |= TC_LIGHT;
972
if (a->ta_format & TF_BLINK)
973
bgc |= TC_LIGHT;
974
975
fgc = gfx_fb_color_map(fgc);
976
bgc = gfx_fb_color_map(bgc);
977
978
if (a->ta_format & TF_REVERSE)
979
invert = !invert;
980
if (cursor)
981
invert = !invert;
982
if (invert) {
983
uint32_t tmp;
984
985
tmp = fgc;
986
fgc = bgc;
987
bgc = tmp;
988
}
989
990
alpha = alpha << 24;
991
fgc |= alpha;
992
bgc |= alpha;
993
994
for (uint32_t y = 0; y < height; y++) {
995
for (uint32_t x = 0; x < width; x++) {
996
byte = y * bpl + x / 8;
997
bit = 0x80 >> (x % 8);
998
o = y * width * bpp + x * bpp;
999
cc = glyph[byte] & bit ? fgc : bgc;
1000
1001
gfx_mem_wr4(state->tg_glyph,
1002
state->tg_glyph_size, o, cc);
1003
}
1004
}
1005
}
1006
1007
/*
1008
* Draw prepared glyph on terminal point p.
1009
*/
1010
static void
1011
gfx_fb_printchar(teken_gfx_t *state, const teken_pos_t *p)
1012
{
1013
unsigned x, y, width, height;
1014
1015
width = state->tg_font.vf_width;
1016
height = state->tg_font.vf_height;
1017
x = state->tg_origin.tp_col + p->tp_col * width;
1018
y = state->tg_origin.tp_row + p->tp_row * height;
1019
1020
gfx_fb_cons_display(x, y, width, height, state->tg_glyph);
1021
}
1022
1023
/*
1024
* Store char with its attribute to buffer and put it on screen.
1025
*/
1026
void
1027
gfx_fb_putchar(void *arg, const teken_pos_t *p, teken_char_t c,
1028
const teken_attr_t *a)
1029
{
1030
teken_gfx_t *state = arg;
1031
const uint8_t *glyph;
1032
int idx;
1033
1034
idx = p->tp_col + p->tp_row * state->tg_tp.tp_col;
1035
if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
1036
return;
1037
1038
/* remove the cursor */
1039
if (state->tg_cursor_visible)
1040
gfx_fb_cursor_draw(state, &state->tg_cursor, false);
1041
1042
screen_buffer[idx].c = c;
1043
screen_buffer[idx].a = *a;
1044
1045
glyph = font_lookup(&state->tg_font, c, a);
1046
gfx_bitblt_bitmap(state, glyph, a, 0xff, false);
1047
gfx_fb_printchar(state, p);
1048
1049
/* display the cursor */
1050
if (state->tg_cursor_visible) {
1051
const teken_pos_t *c;
1052
1053
c = teken_get_cursor(&state->tg_teken);
1054
gfx_fb_cursor_draw(state, c, true);
1055
}
1056
}
1057
1058
void
1059
gfx_fb_fill(void *arg, const teken_rect_t *r, teken_char_t c,
1060
const teken_attr_t *a)
1061
{
1062
teken_gfx_t *state = arg;
1063
const uint8_t *glyph;
1064
teken_pos_t p;
1065
struct text_pixel *row;
1066
1067
TSENTER();
1068
1069
/* remove the cursor */
1070
if (state->tg_cursor_visible)
1071
gfx_fb_cursor_draw(state, &state->tg_cursor, false);
1072
1073
glyph = font_lookup(&state->tg_font, c, a);
1074
gfx_bitblt_bitmap(state, glyph, a, 0xff, false);
1075
1076
for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row;
1077
p.tp_row++) {
1078
row = &screen_buffer[p.tp_row * state->tg_tp.tp_col];
1079
for (p.tp_col = r->tr_begin.tp_col;
1080
p.tp_col < r->tr_end.tp_col; p.tp_col++) {
1081
row[p.tp_col].c = c;
1082
row[p.tp_col].a = *a;
1083
gfx_fb_printchar(state, &p);
1084
}
1085
}
1086
1087
/* display the cursor */
1088
if (state->tg_cursor_visible) {
1089
const teken_pos_t *c;
1090
1091
c = teken_get_cursor(&state->tg_teken);
1092
gfx_fb_cursor_draw(state, c, true);
1093
}
1094
1095
TSEXIT();
1096
}
1097
1098
static void
1099
gfx_fb_cursor_draw(teken_gfx_t *state, const teken_pos_t *pos, bool on)
1100
{
1101
const uint8_t *glyph;
1102
teken_pos_t p;
1103
int idx;
1104
1105
p = *pos;
1106
if (p.tp_col >= state->tg_tp.tp_col)
1107
p.tp_col = state->tg_tp.tp_col - 1;
1108
if (p.tp_row >= state->tg_tp.tp_row)
1109
p.tp_row = state->tg_tp.tp_row - 1;
1110
idx = p.tp_col + p.tp_row * state->tg_tp.tp_col;
1111
if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row)
1112
return;
1113
1114
glyph = font_lookup(&state->tg_font, screen_buffer[idx].c,
1115
&screen_buffer[idx].a);
1116
gfx_bitblt_bitmap(state, glyph, &screen_buffer[idx].a, 0xff, on);
1117
gfx_fb_printchar(state, &p);
1118
1119
state->tg_cursor = p;
1120
}
1121
1122
void
1123
gfx_fb_cursor(void *arg, const teken_pos_t *p)
1124
{
1125
teken_gfx_t *state = arg;
1126
1127
/* Switch cursor off in old location and back on in new. */
1128
if (state->tg_cursor_visible) {
1129
gfx_fb_cursor_draw(state, &state->tg_cursor, false);
1130
gfx_fb_cursor_draw(state, p, true);
1131
}
1132
}
1133
1134
void
1135
gfx_fb_param(void *arg, int cmd, unsigned int value)
1136
{
1137
teken_gfx_t *state = arg;
1138
const teken_pos_t *c;
1139
1140
switch (cmd) {
1141
case TP_SETLOCALCURSOR:
1142
/*
1143
* 0 means normal (usually block), 1 means hidden, and
1144
* 2 means blinking (always block) for compatibility with
1145
* syscons. We don't support any changes except hiding,
1146
* so must map 2 to 0.
1147
*/
1148
value = (value == 1) ? 0 : 1;
1149
/* FALLTHROUGH */
1150
case TP_SHOWCURSOR:
1151
c = teken_get_cursor(&state->tg_teken);
1152
gfx_fb_cursor_draw(state, c, true);
1153
if (value != 0)
1154
state->tg_cursor_visible = true;
1155
else
1156
state->tg_cursor_visible = false;
1157
break;
1158
default:
1159
/* Not yet implemented */
1160
break;
1161
}
1162
}
1163
1164
bool
1165
is_same_pixel(struct text_pixel *px1, struct text_pixel *px2)
1166
{
1167
if (px1->c != px2->c)
1168
return (false);
1169
1170
/* Is there image stored? */
1171
if ((px1->a.ta_format & TF_IMAGE) ||
1172
(px2->a.ta_format & TF_IMAGE))
1173
return (false);
1174
1175
if (px1->a.ta_format != px2->a.ta_format)
1176
return (false);
1177
if (px1->a.ta_fgcolor != px2->a.ta_fgcolor)
1178
return (false);
1179
if (px1->a.ta_bgcolor != px2->a.ta_bgcolor)
1180
return (false);
1181
1182
return (true);
1183
}
1184
1185
static void
1186
gfx_fb_copy_area(teken_gfx_t *state, const teken_rect_t *s,
1187
const teken_pos_t *d)
1188
{
1189
uint32_t sx, sy, dx, dy, width, height;
1190
uint32_t pitch, bytes;
1191
int step;
1192
1193
width = state->tg_font.vf_width;
1194
height = state->tg_font.vf_height;
1195
1196
sx = s->tr_begin.tp_col * width;
1197
sy = s->tr_begin.tp_row * height;
1198
dx = d->tp_col * width;
1199
dy = d->tp_row * height;
1200
1201
width *= (s->tr_end.tp_col - s->tr_begin.tp_col + 1);
1202
1203
/*
1204
* With no shadow fb, use video to video copy.
1205
*/
1206
if (state->tg_shadow_fb == NULL) {
1207
(void) gfxfb_blt(NULL, GfxFbBltVideoToVideo,
1208
sx + state->tg_origin.tp_col,
1209
sy + state->tg_origin.tp_row,
1210
dx + state->tg_origin.tp_col,
1211
dy + state->tg_origin.tp_row,
1212
width, height, 0);
1213
return;
1214
}
1215
1216
/*
1217
* With shadow fb, we need to copy data on both shadow and video,
1218
* to preserve the consistency. We only read data from shadow fb.
1219
*/
1220
1221
step = 1;
1222
pitch = state->tg_fb.fb_width;
1223
bytes = width * sizeof (*state->tg_shadow_fb);
1224
1225
/*
1226
* To handle overlapping areas, set up reverse copy here.
1227
*/
1228
if (dy * pitch + dx > sy * pitch + sx) {
1229
sy += height;
1230
dy += height;
1231
step = -step;
1232
}
1233
1234
while (height-- > 0) {
1235
uint32_t *source = &state->tg_shadow_fb[sy * pitch + sx];
1236
uint32_t *destination = &state->tg_shadow_fb[dy * pitch + dx];
1237
1238
bcopy(source, destination, bytes);
1239
(void) gfxfb_blt(destination, GfxFbBltBufferToVideo,
1240
0, 0, dx + state->tg_origin.tp_col,
1241
dy + state->tg_origin.tp_row, width, 1, 0);
1242
1243
sy += step;
1244
dy += step;
1245
}
1246
}
1247
1248
static void
1249
gfx_fb_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s, teken_pos_t *d)
1250
{
1251
teken_rect_t sr;
1252
teken_pos_t dp;
1253
unsigned soffset, doffset;
1254
bool mark = false;
1255
int x;
1256
1257
soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col;
1258
doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col;
1259
1260
for (x = 0; x < ncol; x++) {
1261
if (is_same_pixel(&screen_buffer[soffset + x],
1262
&screen_buffer[doffset + x])) {
1263
if (mark) {
1264
gfx_fb_copy_area(state, &sr, &dp);
1265
mark = false;
1266
}
1267
} else {
1268
screen_buffer[doffset + x] = screen_buffer[soffset + x];
1269
if (mark) {
1270
/* update end point */
1271
sr.tr_end.tp_col = s->tp_col + x;
1272
} else {
1273
/* set up new rectangle */
1274
mark = true;
1275
sr.tr_begin.tp_col = s->tp_col + x;
1276
sr.tr_begin.tp_row = s->tp_row;
1277
sr.tr_end.tp_col = s->tp_col + x;
1278
sr.tr_end.tp_row = s->tp_row;
1279
dp.tp_col = d->tp_col + x;
1280
dp.tp_row = d->tp_row;
1281
}
1282
}
1283
}
1284
if (mark) {
1285
gfx_fb_copy_area(state, &sr, &dp);
1286
}
1287
}
1288
1289
void
1290
gfx_fb_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p)
1291
{
1292
teken_gfx_t *state = arg;
1293
unsigned doffset, soffset;
1294
teken_pos_t d, s;
1295
int nrow, ncol, y; /* Has to be signed - >= 0 comparison */
1296
1297
/*
1298
* Copying is a little tricky. We must make sure we do it in
1299
* correct order, to make sure we don't overwrite our own data.
1300
*/
1301
1302
nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
1303
ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
1304
1305
if (p->tp_row + nrow > state->tg_tp.tp_row ||
1306
p->tp_col + ncol > state->tg_tp.tp_col)
1307
return;
1308
1309
soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col;
1310
doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col;
1311
1312
/* remove the cursor */
1313
if (state->tg_cursor_visible)
1314
gfx_fb_cursor_draw(state, &state->tg_cursor, false);
1315
1316
/*
1317
* Copy line by line.
1318
*/
1319
if (doffset <= soffset) {
1320
s = r->tr_begin;
1321
d = *p;
1322
for (y = 0; y < nrow; y++) {
1323
s.tp_row = r->tr_begin.tp_row + y;
1324
d.tp_row = p->tp_row + y;
1325
1326
gfx_fb_copy_line(state, ncol, &s, &d);
1327
}
1328
} else {
1329
for (y = nrow - 1; y >= 0; y--) {
1330
s.tp_row = r->tr_begin.tp_row + y;
1331
d.tp_row = p->tp_row + y;
1332
1333
gfx_fb_copy_line(state, ncol, &s, &d);
1334
}
1335
}
1336
1337
/* display the cursor */
1338
if (state->tg_cursor_visible) {
1339
const teken_pos_t *c;
1340
1341
c = teken_get_cursor(&state->tg_teken);
1342
gfx_fb_cursor_draw(state, c, true);
1343
}
1344
}
1345
1346
/*
1347
* Implements alpha blending for RGBA data, could use pixels for arguments,
1348
* but byte stream seems more generic.
1349
* The generic alpha blending is:
1350
* blend = alpha * fg + (1.0 - alpha) * bg.
1351
* Since our alpha is not from range [0..1], we scale appropriately.
1352
*/
1353
static uint8_t
1354
alpha_blend(uint8_t fg, uint8_t bg, uint8_t alpha)
1355
{
1356
uint16_t blend, h, l;
1357
1358
/* trivial corner cases */
1359
if (alpha == 0)
1360
return (bg);
1361
if (alpha == 0xFF)
1362
return (fg);
1363
blend = (alpha * fg + (0xFF - alpha) * bg);
1364
/* Division by 0xFF */
1365
h = blend >> 8;
1366
l = blend & 0xFF;
1367
if (h + l >= 0xFF)
1368
h++;
1369
return (h);
1370
}
1371
1372
/*
1373
* Implements alpha blending for RGBA data, could use pixels for arguments,
1374
* but byte stream seems more generic.
1375
* The generic alpha blending is:
1376
* blend = alpha * fg + (1.0 - alpha) * bg.
1377
* Since our alpha is not from range [0..1], we scale appropriately.
1378
*/
1379
static void
1380
bitmap_cpy(void *dst, void *src, uint32_t size)
1381
{
1382
#if defined(EFI)
1383
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *ps, *pd;
1384
#else
1385
struct paletteentry *ps, *pd;
1386
#endif
1387
uint32_t i;
1388
uint8_t a;
1389
1390
ps = src;
1391
pd = dst;
1392
1393
/*
1394
* we only implement alpha blending for depth 32.
1395
*/
1396
for (i = 0; i < size; i ++) {
1397
a = ps[i].Reserved;
1398
pd[i].Red = alpha_blend(ps[i].Red, pd[i].Red, a);
1399
pd[i].Green = alpha_blend(ps[i].Green, pd[i].Green, a);
1400
pd[i].Blue = alpha_blend(ps[i].Blue, pd[i].Blue, a);
1401
pd[i].Reserved = a;
1402
}
1403
}
1404
1405
static void *
1406
allocate_glyphbuffer(uint32_t width, uint32_t height)
1407
{
1408
size_t size;
1409
1410
size = sizeof (*GlyphBuffer) * width * height;
1411
if (size != GlyphBufferSize) {
1412
free(GlyphBuffer);
1413
GlyphBuffer = malloc(size);
1414
if (GlyphBuffer == NULL)
1415
return (NULL);
1416
GlyphBufferSize = size;
1417
}
1418
return (GlyphBuffer);
1419
}
1420
1421
void
1422
gfx_fb_cons_display(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
1423
void *data)
1424
{
1425
#if defined(EFI)
1426
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *buf, *p;
1427
#else
1428
struct paletteentry *buf, *p;
1429
#endif
1430
size_t size;
1431
1432
/*
1433
* If we do have shadow fb, we will use shadow to render data,
1434
* and copy shadow to video.
1435
*/
1436
if (gfx_state.tg_shadow_fb != NULL) {
1437
uint32_t pitch = gfx_state.tg_fb.fb_width;
1438
1439
/* Copy rectangle line by line. */
1440
p = data;
1441
for (uint32_t sy = 0; sy < height; sy++) {
1442
buf = (void *)(gfx_state.tg_shadow_fb +
1443
(y - gfx_state.tg_origin.tp_row) * pitch +
1444
x - gfx_state.tg_origin.tp_col);
1445
bitmap_cpy(buf, &p[sy * width], width);
1446
(void) gfxfb_blt(buf, GfxFbBltBufferToVideo,
1447
0, 0, x, y, width, 1, 0);
1448
y++;
1449
}
1450
return;
1451
}
1452
1453
/*
1454
* Common data to display is glyph, use preallocated
1455
* glyph buffer.
1456
*/
1457
if (gfx_state.tg_glyph_size != GlyphBufferSize)
1458
(void) allocate_glyphbuffer(width, height);
1459
1460
size = width * height * sizeof(*buf);
1461
if (size == GlyphBufferSize)
1462
buf = GlyphBuffer;
1463
else
1464
buf = malloc(size);
1465
if (buf == NULL)
1466
return;
1467
1468
if (gfxfb_blt(buf, GfxFbBltVideoToBltBuffer, x, y, 0, 0,
1469
width, height, 0) == 0) {
1470
bitmap_cpy(buf, data, width * height);
1471
(void) gfxfb_blt(buf, GfxFbBltBufferToVideo, 0, 0, x, y,
1472
width, height, 0);
1473
}
1474
if (buf != GlyphBuffer)
1475
free(buf);
1476
}
1477
1478
/*
1479
* Public graphics primitives.
1480
*/
1481
1482
static int
1483
isqrt(int num)
1484
{
1485
int res = 0;
1486
int bit = 1 << 30;
1487
1488
/* "bit" starts at the highest power of four <= the argument. */
1489
while (bit > num)
1490
bit >>= 2;
1491
1492
while (bit != 0) {
1493
if (num >= res + bit) {
1494
num -= res + bit;
1495
res = (res >> 1) + bit;
1496
} else {
1497
res >>= 1;
1498
}
1499
bit >>= 2;
1500
}
1501
return (res);
1502
}
1503
1504
static uint32_t
1505
gfx_fb_getcolor(void)
1506
{
1507
uint32_t c;
1508
const teken_attr_t *ap;
1509
1510
ap = teken_get_curattr(&gfx_state.tg_teken);
1511
if (ap->ta_format & TF_REVERSE) {
1512
c = ap->ta_bgcolor;
1513
if (ap->ta_format & TF_BLINK)
1514
c |= TC_LIGHT;
1515
} else {
1516
c = ap->ta_fgcolor;
1517
if (ap->ta_format & TF_BOLD)
1518
c |= TC_LIGHT;
1519
}
1520
1521
return (gfx_fb_color_map(c));
1522
}
1523
1524
/* set pixel in framebuffer using gfx coordinates */
1525
void
1526
gfx_fb_setpixel(uint32_t x, uint32_t y)
1527
{
1528
uint32_t c;
1529
1530
if (gfx_state.tg_fb_type == FB_TEXT)
1531
return;
1532
1533
c = gfx_fb_getcolor();
1534
1535
if (x >= gfx_state.tg_fb.fb_width ||
1536
y >= gfx_state.tg_fb.fb_height)
1537
return;
1538
1539
gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x, y, 1, 1, 0);
1540
}
1541
1542
/*
1543
* draw rectangle in framebuffer using gfx coordinates.
1544
*/
1545
void
1546
gfx_fb_drawrect(uint32_t x1, uint32_t y1, uint32_t x2, uint32_t y2,
1547
uint32_t fill)
1548
{
1549
uint32_t c;
1550
1551
if (gfx_state.tg_fb_type == FB_TEXT)
1552
return;
1553
1554
c = gfx_fb_getcolor();
1555
1556
if (fill != 0) {
1557
gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, x2 - x1,
1558
y2 - y1, 0);
1559
} else {
1560
gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, x2 - x1, 1, 0);
1561
gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y2, x2 - x1, 1, 0);
1562
gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x1, y1, 1, y2 - y1, 0);
1563
gfxfb_blt(&c, GfxFbBltVideoFill, 0, 0, x2, y1, 1, y2 - y1, 0);
1564
}
1565
}
1566
1567
void
1568
gfx_fb_line(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t wd)
1569
{
1570
int dx, sx, dy, sy;
1571
int err, e2, x2, y2, ed, width;
1572
1573
if (gfx_state.tg_fb_type == FB_TEXT)
1574
return;
1575
1576
width = wd;
1577
sx = x0 < x1? 1 : -1;
1578
sy = y0 < y1? 1 : -1;
1579
dx = x1 > x0? x1 - x0 : x0 - x1;
1580
dy = y1 > y0? y1 - y0 : y0 - y1;
1581
err = dx + dy;
1582
ed = dx + dy == 0 ? 1: isqrt(dx * dx + dy * dy);
1583
1584
for (;;) {
1585
gfx_fb_setpixel(x0, y0);
1586
e2 = err;
1587
x2 = x0;
1588
if ((e2 << 1) >= -dx) { /* x step */
1589
e2 += dy;
1590
y2 = y0;
1591
while (e2 < ed * width &&
1592
(y1 != (uint32_t)y2 || dx > dy)) {
1593
y2 += sy;
1594
gfx_fb_setpixel(x0, y2);
1595
e2 += dx;
1596
}
1597
if (x0 == x1)
1598
break;
1599
e2 = err;
1600
err -= dy;
1601
x0 += sx;
1602
}
1603
if ((e2 << 1) <= dy) { /* y step */
1604
e2 = dx-e2;
1605
while (e2 < ed * width &&
1606
(x1 != (uint32_t)x2 || dx < dy)) {
1607
x2 += sx;
1608
gfx_fb_setpixel(x2, y0);
1609
e2 += dy;
1610
}
1611
if (y0 == y1)
1612
break;
1613
err += dx;
1614
y0 += sy;
1615
}
1616
}
1617
}
1618
1619
/*
1620
* quadratic Bézier curve limited to gradients without sign change.
1621
*/
1622
void
1623
gfx_fb_bezier(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t x2,
1624
uint32_t y2, uint32_t wd)
1625
{
1626
int sx, sy, xx, yy, xy, width;
1627
int dx, dy, err, curvature;
1628
int i;
1629
1630
if (gfx_state.tg_fb_type == FB_TEXT)
1631
return;
1632
1633
width = wd;
1634
sx = x2 - x1;
1635
sy = y2 - y1;
1636
xx = x0 - x1;
1637
yy = y0 - y1;
1638
curvature = xx*sy - yy*sx;
1639
1640
if (sx*sx + sy*sy > xx*xx+yy*yy) {
1641
x2 = x0;
1642
x0 = sx + x1;
1643
y2 = y0;
1644
y0 = sy + y1;
1645
curvature = -curvature;
1646
}
1647
if (curvature != 0) {
1648
xx += sx;
1649
sx = x0 < x2? 1 : -1;
1650
xx *= sx;
1651
yy += sy;
1652
sy = y0 < y2? 1 : -1;
1653
yy *= sy;
1654
xy = (xx*yy) << 1;
1655
xx *= xx;
1656
yy *= yy;
1657
if (curvature * sx * sy < 0) {
1658
xx = -xx;
1659
yy = -yy;
1660
xy = -xy;
1661
curvature = -curvature;
1662
}
1663
dx = 4 * sy * curvature * (x1 - x0) + xx - xy;
1664
dy = 4 * sx * curvature * (y0 - y1) + yy - xy;
1665
xx += xx;
1666
yy += yy;
1667
err = dx + dy + xy;
1668
do {
1669
for (i = 0; i <= width; i++)
1670
gfx_fb_setpixel(x0 + i, y0);
1671
if (x0 == x2 && y0 == y2)
1672
return; /* last pixel -> curve finished */
1673
y1 = 2 * err < dx;
1674
if (2 * err > dy) {
1675
x0 += sx;
1676
dx -= xy;
1677
dy += yy;
1678
err += dy;
1679
}
1680
if (y1 != 0) {
1681
y0 += sy;
1682
dy -= xy;
1683
dx += xx;
1684
err += dx;
1685
}
1686
} while (dy < dx); /* gradient negates -> algorithm fails */
1687
}
1688
gfx_fb_line(x0, y0, x2, y2, width);
1689
}
1690
1691
/*
1692
* draw rectangle using terminal coordinates and current foreground color.
1693
*/
1694
void
1695
gfx_term_drawrect(uint32_t ux1, uint32_t uy1, uint32_t ux2, uint32_t uy2)
1696
{
1697
int x1, y1, x2, y2;
1698
int xshift, yshift;
1699
int width, i;
1700
uint32_t vf_width, vf_height;
1701
teken_rect_t r;
1702
1703
if (gfx_state.tg_fb_type == FB_TEXT)
1704
return;
1705
1706
vf_width = gfx_state.tg_font.vf_width;
1707
vf_height = gfx_state.tg_font.vf_height;
1708
width = vf_width / 4; /* line width */
1709
xshift = (vf_width - width) / 2;
1710
yshift = (vf_height - width) / 2;
1711
1712
/* Shift coordinates */
1713
if (ux1 != 0)
1714
ux1--;
1715
if (uy1 != 0)
1716
uy1--;
1717
ux2--;
1718
uy2--;
1719
1720
/* mark area used in terminal */
1721
r.tr_begin.tp_col = ux1;
1722
r.tr_begin.tp_row = uy1;
1723
r.tr_end.tp_col = ux2 + 1;
1724
r.tr_end.tp_row = uy2 + 1;
1725
1726
term_image_display(&gfx_state, &r);
1727
1728
/*
1729
* Draw horizontal lines width points thick, shifted from outer edge.
1730
*/
1731
x1 = (ux1 + 1) * vf_width + gfx_state.tg_origin.tp_col;
1732
y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
1733
x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1734
gfx_fb_drawrect(x1, y1, x2, y1 + width, 1);
1735
y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1736
y2 += vf_height - yshift - width;
1737
gfx_fb_drawrect(x1, y2, x2, y2 + width, 1);
1738
1739
/*
1740
* Draw vertical lines width points thick, shifted from outer edge.
1741
*/
1742
x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
1743
y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
1744
y1 += vf_height;
1745
y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1746
gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1747
x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1748
x1 += vf_width - xshift - width;
1749
gfx_fb_drawrect(x1, y1, x1 + width, y2, 1);
1750
1751
/* Draw upper left corner. */
1752
x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
1753
y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
1754
y1 += vf_height;
1755
1756
x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
1757
x2 += vf_width;
1758
y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
1759
for (i = 0; i <= width; i++)
1760
gfx_fb_bezier(x1 + i, y1, x1 + i, y2 + i, x2, y2 + i, width-i);
1761
1762
/* Draw lower left corner. */
1763
x1 = ux1 * vf_width + gfx_state.tg_origin.tp_col;
1764
x1 += vf_width;
1765
y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1766
y1 += vf_height - yshift;
1767
x2 = ux1 * vf_width + gfx_state.tg_origin.tp_col + xshift;
1768
y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1769
for (i = 0; i <= width; i++)
1770
gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1771
1772
/* Draw upper right corner. */
1773
x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1774
y1 = uy1 * vf_height + gfx_state.tg_origin.tp_row + yshift;
1775
x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1776
x2 += vf_width - xshift - width;
1777
y2 = uy1 * vf_height + gfx_state.tg_origin.tp_row;
1778
y2 += vf_height;
1779
for (i = 0; i <= width; i++)
1780
gfx_fb_bezier(x1, y1 + i, x2 + i, y1 + i, x2 + i, y2, width-i);
1781
1782
/* Draw lower right corner. */
1783
x1 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1784
y1 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1785
y1 += vf_height - yshift;
1786
x2 = ux2 * vf_width + gfx_state.tg_origin.tp_col;
1787
x2 += vf_width - xshift - width;
1788
y2 = uy2 * vf_height + gfx_state.tg_origin.tp_row;
1789
for (i = 0; i <= width; i++)
1790
gfx_fb_bezier(x1, y1 - i, x2 + i, y1 - i, x2 + i, y2, width-i);
1791
}
1792
1793
int
1794
gfx_fb_putimage(png_t *png, uint32_t ux1, uint32_t uy1, uint32_t ux2,
1795
uint32_t uy2, uint32_t flags)
1796
{
1797
#if defined(EFI)
1798
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *p;
1799
#else
1800
struct paletteentry *p;
1801
#endif
1802
uint8_t *data;
1803
uint32_t i, j, x, y, fheight, fwidth;
1804
int rs, gs, bs;
1805
uint8_t r, g, b, a;
1806
bool scale = false;
1807
bool trace = false;
1808
teken_rect_t rect;
1809
1810
trace = (flags & FL_PUTIMAGE_DEBUG) != 0;
1811
1812
if (gfx_state.tg_fb_type == FB_TEXT) {
1813
if (trace)
1814
printf("Framebuffer not active.\n");
1815
return (1);
1816
}
1817
1818
if (png->color_type != PNG_TRUECOLOR_ALPHA) {
1819
if (trace)
1820
printf("Not truecolor image.\n");
1821
return (1);
1822
}
1823
1824
if (ux1 > gfx_state.tg_fb.fb_width ||
1825
uy1 > gfx_state.tg_fb.fb_height) {
1826
if (trace)
1827
printf("Top left coordinate off screen.\n");
1828
return (1);
1829
}
1830
1831
if (png->width > UINT16_MAX || png->height > UINT16_MAX) {
1832
if (trace)
1833
printf("Image too large.\n");
1834
return (1);
1835
}
1836
1837
if (png->width < 1 || png->height < 1) {
1838
if (trace)
1839
printf("Image too small.\n");
1840
return (1);
1841
}
1842
1843
/*
1844
* If 0 was passed for either ux2 or uy2, then calculate the missing
1845
* part of the bottom right coordinate.
1846
*/
1847
scale = true;
1848
if (ux2 == 0 && uy2 == 0) {
1849
/* Both 0, use the native resolution of the image */
1850
ux2 = ux1 + png->width;
1851
uy2 = uy1 + png->height;
1852
scale = false;
1853
} else if (ux2 == 0) {
1854
/* Set ux2 from uy2/uy1 to maintain aspect ratio */
1855
ux2 = ux1 + (png->width * (uy2 - uy1)) / png->height;
1856
} else if (uy2 == 0) {
1857
/* Set uy2 from ux2/ux1 to maintain aspect ratio */
1858
uy2 = uy1 + (png->height * (ux2 - ux1)) / png->width;
1859
}
1860
1861
if (ux2 > gfx_state.tg_fb.fb_width ||
1862
uy2 > gfx_state.tg_fb.fb_height) {
1863
if (trace)
1864
printf("Bottom right coordinate off screen.\n");
1865
return (1);
1866
}
1867
1868
fwidth = ux2 - ux1;
1869
fheight = uy2 - uy1;
1870
1871
/*
1872
* If the original image dimensions have been passed explicitly,
1873
* disable scaling.
1874
*/
1875
if (fwidth == png->width && fheight == png->height)
1876
scale = false;
1877
1878
if (ux1 == 0) {
1879
/*
1880
* No top left X co-ordinate (real coordinates start at 1),
1881
* place as far right as it will fit.
1882
*/
1883
ux2 = gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col;
1884
ux1 = ux2 - fwidth;
1885
}
1886
1887
if (uy1 == 0) {
1888
/*
1889
* No top left Y co-ordinate (real coordinates start at 1),
1890
* place as far down as it will fit.
1891
*/
1892
uy2 = gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row;
1893
uy1 = uy2 - fheight;
1894
}
1895
1896
if (ux1 >= ux2 || uy1 >= uy2) {
1897
if (trace)
1898
printf("Image dimensions reversed.\n");
1899
return (1);
1900
}
1901
1902
if (fwidth < 2 || fheight < 2) {
1903
if (trace)
1904
printf("Target area too small\n");
1905
return (1);
1906
}
1907
1908
if (trace)
1909
printf("Image %ux%u -> %ux%u @%ux%u\n",
1910
png->width, png->height, fwidth, fheight, ux1, uy1);
1911
1912
rect.tr_begin.tp_col = ux1 / gfx_state.tg_font.vf_width;
1913
rect.tr_begin.tp_row = uy1 / gfx_state.tg_font.vf_height;
1914
rect.tr_end.tp_col = (ux1 + fwidth) / gfx_state.tg_font.vf_width;
1915
rect.tr_end.tp_row = (uy1 + fheight) / gfx_state.tg_font.vf_height;
1916
1917
/*
1918
* mark area used in terminal
1919
*/
1920
if (!(flags & FL_PUTIMAGE_NOSCROLL))
1921
term_image_display(&gfx_state, &rect);
1922
1923
if ((flags & FL_PUTIMAGE_BORDER))
1924
gfx_fb_drawrect(ux1, uy1, ux2, uy2, 0);
1925
1926
data = malloc(fwidth * fheight * sizeof(*p));
1927
p = (void *)data;
1928
if (data == NULL) {
1929
if (trace)
1930
printf("Out of memory.\n");
1931
return (1);
1932
}
1933
1934
/*
1935
* Build image for our framebuffer.
1936
*/
1937
1938
/* Helper to calculate the pixel index from the source png */
1939
#define GETPIXEL(xx, yy) (((yy) * png->width + (xx)) * png->bpp)
1940
1941
/*
1942
* For each of the x and y directions, calculate the number of pixels
1943
* in the source image that correspond to a single pixel in the target.
1944
* Use fixed-point arithmetic with 16-bits for each of the integer and
1945
* fractional parts.
1946
*/
1947
const uint32_t wcstep = ((png->width - 1) << 16) / (fwidth - 1);
1948
const uint32_t hcstep = ((png->height - 1) << 16) / (fheight - 1);
1949
1950
rs = 8 - (fls(gfx_state.tg_fb.fb_mask_red) -
1951
ffs(gfx_state.tg_fb.fb_mask_red) + 1);
1952
gs = 8 - (fls(gfx_state.tg_fb.fb_mask_green) -
1953
ffs(gfx_state.tg_fb.fb_mask_green) + 1);
1954
bs = 8 - (fls(gfx_state.tg_fb.fb_mask_blue) -
1955
ffs(gfx_state.tg_fb.fb_mask_blue) + 1);
1956
1957
uint32_t hc = 0;
1958
for (y = 0; y < fheight; y++) {
1959
uint32_t hc2 = (hc >> 9) & 0x7f;
1960
uint32_t hc1 = 0x80 - hc2;
1961
1962
uint32_t offset_y = hc >> 16;
1963
uint32_t offset_y1 = offset_y + 1;
1964
1965
uint32_t wc = 0;
1966
for (x = 0; x < fwidth; x++) {
1967
uint32_t wc2 = (wc >> 9) & 0x7f;
1968
uint32_t wc1 = 0x80 - wc2;
1969
1970
uint32_t offset_x = wc >> 16;
1971
uint32_t offset_x1 = offset_x + 1;
1972
1973
/* Target pixel index */
1974
j = y * fwidth + x;
1975
1976
if (!scale) {
1977
i = GETPIXEL(x, y);
1978
r = png->image[i];
1979
g = png->image[i + 1];
1980
b = png->image[i + 2];
1981
a = png->image[i + 3];
1982
} else {
1983
uint8_t pixel[4];
1984
1985
uint32_t p00 = GETPIXEL(offset_x, offset_y);
1986
uint32_t p01 = GETPIXEL(offset_x, offset_y1);
1987
uint32_t p10 = GETPIXEL(offset_x1, offset_y);
1988
uint32_t p11 = GETPIXEL(offset_x1, offset_y1);
1989
1990
/*
1991
* Given a 2x2 array of pixels in the source
1992
* image, combine them to produce a single
1993
* value for the pixel in the target image.
1994
* Each column of pixels is combined using
1995
* a weighted average where the top and bottom
1996
* pixels contribute hc1 and hc2 respectively.
1997
* The calculation for bottom pixel pB and
1998
* top pixel pT is:
1999
* (pT * hc1 + pB * hc2) / (hc1 + hc2)
2000
* Once the values are determined for the two
2001
* columns of pixels, then the columns are
2002
* averaged together in the same way but using
2003
* wc1 and wc2 for the weightings.
2004
*
2005
* Since hc1 and hc2 are chosen so that
2006
* hc1 + hc2 == 128 (and same for wc1 + wc2),
2007
* the >> 14 below is a quick way to divide by
2008
* (hc1 + hc2) * (wc1 + wc2)
2009
*/
2010
for (i = 0; i < 4; i++)
2011
pixel[i] = (
2012
(png->image[p00 + i] * hc1 +
2013
png->image[p01 + i] * hc2) * wc1 +
2014
(png->image[p10 + i] * hc1 +
2015
png->image[p11 + i] * hc2) * wc2)
2016
>> 14;
2017
2018
r = pixel[0];
2019
g = pixel[1];
2020
b = pixel[2];
2021
a = pixel[3];
2022
}
2023
2024
if (trace)
2025
printf("r/g/b: %x/%x/%x\n", r, g, b);
2026
/*
2027
* Rough colorspace reduction for 15/16 bit colors.
2028
*/
2029
p[j].Red = r >> rs;
2030
p[j].Green = g >> gs;
2031
p[j].Blue = b >> bs;
2032
p[j].Reserved = a;
2033
2034
wc += wcstep;
2035
}
2036
hc += hcstep;
2037
}
2038
2039
gfx_fb_cons_display(ux1, uy1, fwidth, fheight, data);
2040
free(data);
2041
return (0);
2042
}
2043
2044
/*
2045
* Reset font flags to FONT_AUTO.
2046
*/
2047
void
2048
reset_font_flags(void)
2049
{
2050
struct fontlist *fl;
2051
2052
STAILQ_FOREACH(fl, &fonts, font_next) {
2053
fl->font_flags = FONT_AUTO;
2054
}
2055
}
2056
2057
/* Return w^2 + h^2 or 0, if the dimensions are unknown */
2058
static unsigned
2059
edid_diagonal_squared(void)
2060
{
2061
unsigned w, h;
2062
2063
if (edid_info == NULL)
2064
return (0);
2065
2066
w = edid_info->display.max_horizontal_image_size;
2067
h = edid_info->display.max_vertical_image_size;
2068
2069
/* If either one is 0, we have aspect ratio, not size */
2070
if (w == 0 || h == 0)
2071
return (0);
2072
2073
/*
2074
* some monitors encode the aspect ratio instead of the physical size.
2075
*/
2076
if ((w == 16 && h == 9) || (w == 16 && h == 10) ||
2077
(w == 4 && h == 3) || (w == 5 && h == 4))
2078
return (0);
2079
2080
/*
2081
* translate cm to inch, note we scale by 100 here.
2082
*/
2083
w = w * 100 / 254;
2084
h = h * 100 / 254;
2085
2086
/* Return w^2 + h^2 */
2087
return (w * w + h * h);
2088
}
2089
2090
/*
2091
* calculate pixels per inch.
2092
*/
2093
static unsigned
2094
gfx_get_ppi(void)
2095
{
2096
unsigned dp, di;
2097
2098
di = edid_diagonal_squared();
2099
if (di == 0)
2100
return (0);
2101
2102
dp = gfx_state.tg_fb.fb_width *
2103
gfx_state.tg_fb.fb_width +
2104
gfx_state.tg_fb.fb_height *
2105
gfx_state.tg_fb.fb_height;
2106
2107
return (isqrt(dp / di));
2108
}
2109
2110
/*
2111
* Calculate font size from density independent pixels (dp):
2112
* ((16dp * ppi) / 160) * display_factor.
2113
* Here we are using fixed constants: 1dp == 160 ppi and
2114
* display_factor 2.
2115
*
2116
* We are rounding font size up and are searching for font which is
2117
* not smaller than calculated size value.
2118
*/
2119
static vt_font_bitmap_data_t *
2120
gfx_get_font(teken_unit_t rows, teken_unit_t cols, teken_unit_t height,
2121
teken_unit_t width)
2122
{
2123
unsigned ppi, size;
2124
vt_font_bitmap_data_t *font = NULL;
2125
struct fontlist *fl, *next;
2126
2127
/* Text mode is not supported here. */
2128
if (gfx_state.tg_fb_type == FB_TEXT)
2129
return (NULL);
2130
2131
ppi = gfx_get_ppi();
2132
if (ppi == 0)
2133
return (NULL);
2134
2135
/*
2136
* We will search for 16dp font.
2137
* We are using scale up by 10 for roundup.
2138
*/
2139
size = (16 * ppi * 10) / 160;
2140
/* Apply display factor 2. */
2141
size = roundup(size * 2, 10) / 10;
2142
2143
STAILQ_FOREACH(fl, &fonts, font_next) {
2144
/*
2145
* Skip too large fonts.
2146
*/
2147
font = fl->font_data;
2148
if (height / font->vfbd_height < rows ||
2149
width / font->vfbd_width < cols)
2150
continue;
2151
2152
next = STAILQ_NEXT(fl, font_next);
2153
2154
/*
2155
* If this is last font or, if next font is smaller,
2156
* we have our font. Make sure, it actually is loaded.
2157
*/
2158
if (next == NULL || next->font_data->vfbd_height < size) {
2159
if (font->vfbd_font == NULL ||
2160
fl->font_flags == FONT_RELOAD) {
2161
if (fl->font_load != NULL &&
2162
fl->font_name != NULL)
2163
font = fl->font_load(fl->font_name);
2164
}
2165
break;
2166
}
2167
font = NULL;
2168
}
2169
2170
return (font);
2171
}
2172
2173
static vt_font_bitmap_data_t *
2174
set_font(teken_unit_t *rows, teken_unit_t *cols, teken_unit_t h, teken_unit_t w)
2175
{
2176
vt_font_bitmap_data_t *font = NULL;
2177
struct fontlist *fl;
2178
unsigned height = h;
2179
unsigned width = w;
2180
2181
/*
2182
* First check for manually loaded font.
2183
*/
2184
STAILQ_FOREACH(fl, &fonts, font_next) {
2185
if (fl->font_flags == FONT_MANUAL) {
2186
font = fl->font_data;
2187
if (font->vfbd_font == NULL && fl->font_load != NULL &&
2188
fl->font_name != NULL) {
2189
font = fl->font_load(fl->font_name);
2190
}
2191
if (font == NULL || font->vfbd_font == NULL)
2192
font = NULL;
2193
break;
2194
}
2195
}
2196
2197
if (font == NULL)
2198
font = gfx_get_font(*rows, *cols, h, w);
2199
2200
if (font != NULL) {
2201
*rows = height / font->vfbd_height;
2202
*cols = width / font->vfbd_width;
2203
return (font);
2204
}
2205
2206
/*
2207
* Find best font for these dimensions, or use default.
2208
* If height >= VT_FB_MAX_HEIGHT and width >= VT_FB_MAX_WIDTH,
2209
* do not use smaller font than our DEFAULT_FONT_DATA.
2210
*/
2211
STAILQ_FOREACH(fl, &fonts, font_next) {
2212
font = fl->font_data;
2213
if ((*rows * font->vfbd_height <= height &&
2214
*cols * font->vfbd_width <= width) ||
2215
(height >= VT_FB_MAX_HEIGHT &&
2216
width >= VT_FB_MAX_WIDTH &&
2217
font->vfbd_height == DEFAULT_FONT_DATA.vfbd_height &&
2218
font->vfbd_width == DEFAULT_FONT_DATA.vfbd_width)) {
2219
if (font->vfbd_font == NULL ||
2220
fl->font_flags == FONT_RELOAD) {
2221
if (fl->font_load != NULL &&
2222
fl->font_name != NULL) {
2223
font = fl->font_load(fl->font_name);
2224
}
2225
if (font == NULL)
2226
continue;
2227
}
2228
*rows = height / font->vfbd_height;
2229
*cols = width / font->vfbd_width;
2230
break;
2231
}
2232
font = NULL;
2233
}
2234
2235
if (font == NULL) {
2236
/*
2237
* We have fonts sorted smallest last, try it before
2238
* falling back to builtin.
2239
*/
2240
fl = STAILQ_LAST(&fonts, fontlist, font_next);
2241
if (fl != NULL && fl->font_load != NULL &&
2242
fl->font_name != NULL) {
2243
font = fl->font_load(fl->font_name);
2244
}
2245
if (font == NULL)
2246
font = &DEFAULT_FONT_DATA;
2247
2248
*rows = height / font->vfbd_height;
2249
*cols = width / font->vfbd_width;
2250
}
2251
2252
return (font);
2253
}
2254
2255
static void
2256
cons_clear(void)
2257
{
2258
char clear[] = { '\033', 'c' };
2259
2260
/* Reset terminal */
2261
teken_input(&gfx_state.tg_teken, clear, sizeof(clear));
2262
gfx_state.tg_functions->tf_param(&gfx_state, TP_SHOWCURSOR, 0);
2263
}
2264
2265
void
2266
setup_font(teken_gfx_t *state, teken_unit_t height, teken_unit_t width)
2267
{
2268
vt_font_bitmap_data_t *font_data;
2269
teken_pos_t *tp = &state->tg_tp;
2270
char env[8];
2271
int i;
2272
2273
/*
2274
* set_font() will select a appropriate sized font for
2275
* the number of rows and columns selected. If we don't
2276
* have a font that will fit, then it will use the
2277
* default builtin font and adjust the rows and columns
2278
* to fit on the screen.
2279
*/
2280
font_data = set_font(&tp->tp_row, &tp->tp_col, height, width);
2281
2282
if (font_data == NULL)
2283
panic("out of memory");
2284
2285
for (i = 0; i < VFNT_MAPS; i++) {
2286
state->tg_font.vf_map[i] =
2287
font_data->vfbd_font->vf_map[i];
2288
state->tg_font.vf_map_count[i] =
2289
font_data->vfbd_font->vf_map_count[i];
2290
}
2291
2292
state->tg_font.vf_bytes = font_data->vfbd_font->vf_bytes;
2293
state->tg_font.vf_height = font_data->vfbd_font->vf_height;
2294
state->tg_font.vf_width = font_data->vfbd_font->vf_width;
2295
2296
snprintf(env, sizeof (env), "%ux%u",
2297
state->tg_font.vf_width, state->tg_font.vf_height);
2298
env_setenv("screen.font", EV_VOLATILE | EV_NOHOOK,
2299
env, font_set, env_nounset);
2300
}
2301
2302
/* Binary search for the glyph. Return 0 if not found. */
2303
static uint16_t
2304
font_bisearch(const vfnt_map_t *map, uint32_t len, teken_char_t src)
2305
{
2306
unsigned min, mid, max;
2307
2308
min = 0;
2309
max = len - 1;
2310
2311
/* Empty font map. */
2312
if (len == 0)
2313
return (0);
2314
/* Character below minimal entry. */
2315
if (src < map[0].vfm_src)
2316
return (0);
2317
/* Optimization: ASCII characters occur very often. */
2318
if (src <= map[0].vfm_src + map[0].vfm_len)
2319
return (src - map[0].vfm_src + map[0].vfm_dst);
2320
/* Character above maximum entry. */
2321
if (src > map[max].vfm_src + map[max].vfm_len)
2322
return (0);
2323
2324
/* Binary search. */
2325
while (max >= min) {
2326
mid = (min + max) / 2;
2327
if (src < map[mid].vfm_src)
2328
max = mid - 1;
2329
else if (src > map[mid].vfm_src + map[mid].vfm_len)
2330
min = mid + 1;
2331
else
2332
return (src - map[mid].vfm_src + map[mid].vfm_dst);
2333
}
2334
2335
return (0);
2336
}
2337
2338
/*
2339
* Return glyph bitmap. If glyph is not found, we will return bitmap
2340
* for the first (offset 0) glyph.
2341
*/
2342
uint8_t *
2343
font_lookup(const struct vt_font *vf, teken_char_t c, const teken_attr_t *a)
2344
{
2345
uint16_t dst;
2346
size_t stride;
2347
2348
/* Substitute bold with normal if not found. */
2349
if (a->ta_format & TF_BOLD) {
2350
dst = font_bisearch(vf->vf_map[VFNT_MAP_BOLD],
2351
vf->vf_map_count[VFNT_MAP_BOLD], c);
2352
if (dst != 0)
2353
goto found;
2354
}
2355
dst = font_bisearch(vf->vf_map[VFNT_MAP_NORMAL],
2356
vf->vf_map_count[VFNT_MAP_NORMAL], c);
2357
2358
found:
2359
stride = howmany(vf->vf_width, 8) * vf->vf_height;
2360
return (&vf->vf_bytes[dst * stride]);
2361
}
2362
2363
static int
2364
load_mapping(int fd, struct vt_font *fp, int n)
2365
{
2366
size_t i, size;
2367
ssize_t rv;
2368
vfnt_map_t *mp;
2369
2370
if (fp->vf_map_count[n] == 0)
2371
return (0);
2372
2373
size = fp->vf_map_count[n] * sizeof(*mp);
2374
mp = malloc(size);
2375
if (mp == NULL)
2376
return (ENOMEM);
2377
fp->vf_map[n] = mp;
2378
2379
rv = read(fd, mp, size);
2380
if (rv < 0 || (size_t)rv != size) {
2381
free(fp->vf_map[n]);
2382
fp->vf_map[n] = NULL;
2383
return (EIO);
2384
}
2385
2386
for (i = 0; i < fp->vf_map_count[n]; i++) {
2387
mp[i].vfm_src = be32toh(mp[i].vfm_src);
2388
mp[i].vfm_dst = be16toh(mp[i].vfm_dst);
2389
mp[i].vfm_len = be16toh(mp[i].vfm_len);
2390
}
2391
return (0);
2392
}
2393
2394
static int
2395
builtin_mapping(struct vt_font *fp, int n)
2396
{
2397
size_t size;
2398
struct vfnt_map *mp;
2399
2400
if (n >= VFNT_MAPS)
2401
return (EINVAL);
2402
2403
if (fp->vf_map_count[n] == 0)
2404
return (0);
2405
2406
size = fp->vf_map_count[n] * sizeof(*mp);
2407
mp = malloc(size);
2408
if (mp == NULL)
2409
return (ENOMEM);
2410
fp->vf_map[n] = mp;
2411
2412
memcpy(mp, DEFAULT_FONT_DATA.vfbd_font->vf_map[n], size);
2413
return (0);
2414
}
2415
2416
/*
2417
* Load font from builtin or from file.
2418
* We do need special case for builtin because the builtin font glyphs
2419
* are compressed and we do need to uncompress them.
2420
* Having single load_font() for both cases will help us to simplify
2421
* font switch handling.
2422
*/
2423
static vt_font_bitmap_data_t *
2424
load_font(char *path)
2425
{
2426
int fd, i;
2427
uint32_t glyphs;
2428
struct font_header fh;
2429
struct fontlist *fl;
2430
vt_font_bitmap_data_t *bp;
2431
struct vt_font *fp;
2432
size_t size;
2433
ssize_t rv;
2434
2435
/* Get our entry from the font list. */
2436
STAILQ_FOREACH(fl, &fonts, font_next) {
2437
if (strcmp(fl->font_name, path) == 0)
2438
break;
2439
}
2440
if (fl == NULL)
2441
return (NULL); /* Should not happen. */
2442
2443
bp = fl->font_data;
2444
if (bp->vfbd_font != NULL && fl->font_flags != FONT_RELOAD)
2445
return (bp);
2446
2447
fd = -1;
2448
/*
2449
* Special case for builtin font.
2450
* Builtin font is the very first font we load, we do not have
2451
* previous loads to be released.
2452
*/
2453
if (fl->font_flags == FONT_BUILTIN) {
2454
if ((fp = calloc(1, sizeof(struct vt_font))) == NULL)
2455
return (NULL);
2456
2457
fp->vf_width = DEFAULT_FONT_DATA.vfbd_width;
2458
fp->vf_height = DEFAULT_FONT_DATA.vfbd_height;
2459
2460
fp->vf_bytes = malloc(DEFAULT_FONT_DATA.vfbd_uncompressed_size);
2461
if (fp->vf_bytes == NULL) {
2462
free(fp);
2463
return (NULL);
2464
}
2465
2466
bp->vfbd_uncompressed_size =
2467
DEFAULT_FONT_DATA.vfbd_uncompressed_size;
2468
bp->vfbd_compressed_size =
2469
DEFAULT_FONT_DATA.vfbd_compressed_size;
2470
2471
if (lz4_decompress(DEFAULT_FONT_DATA.vfbd_compressed_data,
2472
fp->vf_bytes,
2473
DEFAULT_FONT_DATA.vfbd_compressed_size,
2474
DEFAULT_FONT_DATA.vfbd_uncompressed_size, 0) != 0) {
2475
free(fp->vf_bytes);
2476
free(fp);
2477
return (NULL);
2478
}
2479
2480
for (i = 0; i < VFNT_MAPS; i++) {
2481
fp->vf_map_count[i] =
2482
DEFAULT_FONT_DATA.vfbd_font->vf_map_count[i];
2483
if (builtin_mapping(fp, i) != 0)
2484
goto free_done;
2485
}
2486
2487
bp->vfbd_font = fp;
2488
return (bp);
2489
}
2490
2491
fd = open(path, O_RDONLY);
2492
if (fd < 0)
2493
return (NULL);
2494
2495
size = sizeof(fh);
2496
rv = read(fd, &fh, size);
2497
if (rv < 0 || (size_t)rv != size) {
2498
bp = NULL;
2499
goto done;
2500
}
2501
if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC, sizeof(fh.fh_magic)) != 0) {
2502
bp = NULL;
2503
goto done;
2504
}
2505
if ((fp = calloc(1, sizeof(struct vt_font))) == NULL) {
2506
bp = NULL;
2507
goto done;
2508
}
2509
for (i = 0; i < VFNT_MAPS; i++)
2510
fp->vf_map_count[i] = be32toh(fh.fh_map_count[i]);
2511
2512
glyphs = be32toh(fh.fh_glyph_count);
2513
fp->vf_width = fh.fh_width;
2514
fp->vf_height = fh.fh_height;
2515
2516
size = howmany(fp->vf_width, 8) * fp->vf_height * glyphs;
2517
bp->vfbd_uncompressed_size = size;
2518
if ((fp->vf_bytes = malloc(size)) == NULL)
2519
goto free_done;
2520
2521
rv = read(fd, fp->vf_bytes, size);
2522
if (rv < 0 || (size_t)rv != size)
2523
goto free_done;
2524
for (i = 0; i < VFNT_MAPS; i++) {
2525
if (load_mapping(fd, fp, i) != 0)
2526
goto free_done;
2527
}
2528
2529
/*
2530
* Reset builtin flag now as we have full font loaded.
2531
*/
2532
if (fl->font_flags == FONT_BUILTIN)
2533
fl->font_flags = FONT_AUTO;
2534
2535
/*
2536
* Release previously loaded entries. We can do this now, as
2537
* the new font is loaded. Note, there can be no console
2538
* output till the new font is in place and teken is notified.
2539
* We do need to keep fl->font_data for glyph dimensions.
2540
*/
2541
STAILQ_FOREACH(fl, &fonts, font_next) {
2542
if (fl->font_data->vfbd_font == NULL)
2543
continue;
2544
2545
for (i = 0; i < VFNT_MAPS; i++)
2546
free(fl->font_data->vfbd_font->vf_map[i]);
2547
free(fl->font_data->vfbd_font->vf_bytes);
2548
free(fl->font_data->vfbd_font);
2549
fl->font_data->vfbd_font = NULL;
2550
}
2551
2552
bp->vfbd_font = fp;
2553
bp->vfbd_compressed_size = 0;
2554
2555
done:
2556
if (fd != -1)
2557
close(fd);
2558
return (bp);
2559
2560
free_done:
2561
for (i = 0; i < VFNT_MAPS; i++)
2562
free(fp->vf_map[i]);
2563
free(fp->vf_bytes);
2564
free(fp);
2565
bp = NULL;
2566
goto done;
2567
}
2568
2569
struct name_entry {
2570
char *n_name;
2571
SLIST_ENTRY(name_entry) n_entry;
2572
};
2573
2574
SLIST_HEAD(name_list, name_entry);
2575
2576
/* Read font names from index file. */
2577
static struct name_list *
2578
read_list(char *fonts)
2579
{
2580
struct name_list *nl;
2581
struct name_entry *np;
2582
char *dir, *ptr;
2583
char buf[PATH_MAX];
2584
int fd, len;
2585
2586
TSENTER();
2587
2588
dir = strdup(fonts);
2589
if (dir == NULL)
2590
return (NULL);
2591
2592
ptr = strrchr(dir, '/');
2593
*ptr = '\0';
2594
2595
fd = open(fonts, O_RDONLY);
2596
if (fd < 0)
2597
return (NULL);
2598
2599
nl = malloc(sizeof(*nl));
2600
if (nl == NULL) {
2601
close(fd);
2602
return (nl);
2603
}
2604
2605
SLIST_INIT(nl);
2606
while ((len = fgetstr(buf, sizeof (buf), fd)) >= 0) {
2607
if (*buf == '#' || *buf == '\0')
2608
continue;
2609
2610
if (bcmp(buf, "MENU", 4) == 0)
2611
continue;
2612
2613
if (bcmp(buf, "FONT", 4) == 0)
2614
continue;
2615
2616
ptr = strchr(buf, ':');
2617
if (ptr == NULL)
2618
continue;
2619
else
2620
*ptr = '\0';
2621
2622
np = malloc(sizeof(*np));
2623
if (np == NULL) {
2624
close(fd);
2625
return (nl); /* return what we have */
2626
}
2627
if (asprintf(&np->n_name, "%s/%s", dir, buf) < 0) {
2628
free(np);
2629
close(fd);
2630
return (nl); /* return what we have */
2631
}
2632
SLIST_INSERT_HEAD(nl, np, n_entry);
2633
}
2634
close(fd);
2635
TSEXIT();
2636
return (nl);
2637
}
2638
2639
/*
2640
* Read the font properties and insert new entry into the list.
2641
* The font list is built in descending order.
2642
*/
2643
static bool
2644
insert_font(char *name, FONT_FLAGS flags)
2645
{
2646
struct font_header fh;
2647
struct fontlist *fp, *previous, *entry, *next;
2648
size_t size;
2649
ssize_t rv;
2650
int fd;
2651
char *font_name;
2652
2653
TSENTER();
2654
2655
font_name = NULL;
2656
if (flags == FONT_BUILTIN) {
2657
/*
2658
* We only install builtin font once, while setting up
2659
* initial console. Since this will happen very early,
2660
* we assume asprintf will not fail. Once we have access to
2661
* files, the builtin font will be replaced by font loaded
2662
* from file.
2663
*/
2664
if (!STAILQ_EMPTY(&fonts))
2665
return (false);
2666
2667
fh.fh_width = DEFAULT_FONT_DATA.vfbd_width;
2668
fh.fh_height = DEFAULT_FONT_DATA.vfbd_height;
2669
2670
(void) asprintf(&font_name, "%dx%d",
2671
DEFAULT_FONT_DATA.vfbd_width,
2672
DEFAULT_FONT_DATA.vfbd_height);
2673
} else {
2674
fd = open(name, O_RDONLY);
2675
if (fd < 0)
2676
return (false);
2677
rv = read(fd, &fh, sizeof(fh));
2678
close(fd);
2679
if (rv < 0 || (size_t)rv != sizeof(fh))
2680
return (false);
2681
2682
if (memcmp(fh.fh_magic, FONT_HEADER_MAGIC,
2683
sizeof(fh.fh_magic)) != 0)
2684
return (false);
2685
font_name = strdup(name);
2686
}
2687
2688
if (font_name == NULL)
2689
return (false);
2690
2691
/*
2692
* If we have an entry with the same glyph dimensions, replace
2693
* the file name and mark us. We only support unique dimensions.
2694
*/
2695
STAILQ_FOREACH(entry, &fonts, font_next) {
2696
if (fh.fh_width == entry->font_data->vfbd_width &&
2697
fh.fh_height == entry->font_data->vfbd_height) {
2698
free(entry->font_name);
2699
entry->font_name = font_name;
2700
entry->font_flags = FONT_RELOAD;
2701
TSEXIT();
2702
return (true);
2703
}
2704
}
2705
2706
fp = calloc(sizeof(*fp), 1);
2707
if (fp == NULL) {
2708
free(font_name);
2709
return (false);
2710
}
2711
fp->font_data = calloc(sizeof(*fp->font_data), 1);
2712
if (fp->font_data == NULL) {
2713
free(font_name);
2714
free(fp);
2715
return (false);
2716
}
2717
fp->font_name = font_name;
2718
fp->font_flags = flags;
2719
fp->font_load = load_font;
2720
fp->font_data->vfbd_width = fh.fh_width;
2721
fp->font_data->vfbd_height = fh.fh_height;
2722
2723
if (STAILQ_EMPTY(&fonts)) {
2724
STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2725
TSEXIT();
2726
return (true);
2727
}
2728
2729
previous = NULL;
2730
size = fp->font_data->vfbd_width * fp->font_data->vfbd_height;
2731
2732
STAILQ_FOREACH(entry, &fonts, font_next) {
2733
vt_font_bitmap_data_t *bd;
2734
2735
bd = entry->font_data;
2736
/* Should fp be inserted before the entry? */
2737
if (size > bd->vfbd_width * bd->vfbd_height) {
2738
if (previous == NULL) {
2739
STAILQ_INSERT_HEAD(&fonts, fp, font_next);
2740
} else {
2741
STAILQ_INSERT_AFTER(&fonts, previous, fp,
2742
font_next);
2743
}
2744
TSEXIT();
2745
return (true);
2746
}
2747
next = STAILQ_NEXT(entry, font_next);
2748
if (next == NULL ||
2749
size > next->font_data->vfbd_width *
2750
next->font_data->vfbd_height) {
2751
STAILQ_INSERT_AFTER(&fonts, entry, fp, font_next);
2752
TSEXIT();
2753
return (true);
2754
}
2755
previous = entry;
2756
}
2757
TSEXIT();
2758
return (true);
2759
}
2760
2761
static int
2762
font_set(struct env_var *ev __unused, int flags __unused, const void *value)
2763
{
2764
struct fontlist *fl;
2765
char *eptr;
2766
unsigned long x = 0, y = 0;
2767
2768
/*
2769
* Attempt to extract values from "XxY" string. In case of error,
2770
* we have unmaching glyph dimensions and will just output the
2771
* available values.
2772
*/
2773
if (value != NULL) {
2774
x = strtoul(value, &eptr, 10);
2775
if (*eptr == 'x')
2776
y = strtoul(eptr + 1, &eptr, 10);
2777
}
2778
STAILQ_FOREACH(fl, &fonts, font_next) {
2779
if (fl->font_data->vfbd_width == x &&
2780
fl->font_data->vfbd_height == y)
2781
break;
2782
}
2783
if (fl != NULL) {
2784
/* Reset any FONT_MANUAL flag. */
2785
reset_font_flags();
2786
2787
/* Mark this font manually loaded */
2788
fl->font_flags = FONT_MANUAL;
2789
cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2790
return (CMD_OK);
2791
}
2792
2793
printf("Available fonts:\n");
2794
STAILQ_FOREACH(fl, &fonts, font_next) {
2795
printf(" %dx%d\n", fl->font_data->vfbd_width,
2796
fl->font_data->vfbd_height);
2797
}
2798
return (CMD_OK);
2799
}
2800
2801
void
2802
bios_text_font(bool use_vga_font)
2803
{
2804
if (use_vga_font)
2805
(void) insert_font(VGA_8X16_FONT, FONT_MANUAL);
2806
else
2807
(void) insert_font(DEFAULT_8X16_FONT, FONT_MANUAL);
2808
}
2809
2810
void
2811
autoload_font(bool bios)
2812
{
2813
struct name_list *nl;
2814
struct name_entry *np;
2815
2816
TSENTER();
2817
2818
nl = read_list("/boot/fonts/INDEX.fonts");
2819
if (nl == NULL)
2820
return;
2821
2822
while (!SLIST_EMPTY(nl)) {
2823
np = SLIST_FIRST(nl);
2824
SLIST_REMOVE_HEAD(nl, n_entry);
2825
if (insert_font(np->n_name, FONT_AUTO) == false)
2826
printf("failed to add font: %s\n", np->n_name);
2827
free(np->n_name);
2828
free(np);
2829
}
2830
2831
/*
2832
* If vga text mode was requested, load vga.font (8x16 bold) font.
2833
*/
2834
if (bios) {
2835
bios_text_font(true);
2836
}
2837
2838
(void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2839
2840
TSEXIT();
2841
}
2842
2843
COMMAND_SET(load_font, "loadfont", "load console font from file", command_font);
2844
2845
static int
2846
command_font(int argc, char *argv[])
2847
{
2848
int i, c, rc;
2849
struct fontlist *fl;
2850
vt_font_bitmap_data_t *bd;
2851
bool list;
2852
2853
list = false;
2854
optind = 1;
2855
optreset = 1;
2856
rc = CMD_OK;
2857
2858
while ((c = getopt(argc, argv, "l")) != -1) {
2859
switch (c) {
2860
case 'l':
2861
list = true;
2862
break;
2863
case '?':
2864
default:
2865
return (CMD_ERROR);
2866
}
2867
}
2868
2869
argc -= optind;
2870
argv += optind;
2871
2872
if (argc > 1 || (list && argc != 0)) {
2873
printf("Usage: loadfont [-l] | [file.fnt]\n");
2874
return (CMD_ERROR);
2875
}
2876
2877
if (list) {
2878
STAILQ_FOREACH(fl, &fonts, font_next) {
2879
printf("font %s: %dx%d%s\n", fl->font_name,
2880
fl->font_data->vfbd_width,
2881
fl->font_data->vfbd_height,
2882
fl->font_data->vfbd_font == NULL? "" : " loaded");
2883
}
2884
return (CMD_OK);
2885
}
2886
2887
/* Clear scren */
2888
cons_clear();
2889
2890
if (argc == 1) {
2891
char *name = argv[0];
2892
2893
if (insert_font(name, FONT_MANUAL) == false) {
2894
printf("loadfont error: failed to load: %s\n", name);
2895
return (CMD_ERROR);
2896
}
2897
2898
(void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2899
return (CMD_OK);
2900
}
2901
2902
if (argc == 0) {
2903
/*
2904
* Walk entire font list, release any loaded font, and set
2905
* autoload flag. The font list does have at least the builtin
2906
* default font.
2907
*/
2908
STAILQ_FOREACH(fl, &fonts, font_next) {
2909
if (fl->font_data->vfbd_font != NULL) {
2910
2911
bd = fl->font_data;
2912
/*
2913
* Note the setup_font() is releasing
2914
* font bytes.
2915
*/
2916
for (i = 0; i < VFNT_MAPS; i++)
2917
free(bd->vfbd_font->vf_map[i]);
2918
free(fl->font_data->vfbd_font);
2919
fl->font_data->vfbd_font = NULL;
2920
fl->font_data->vfbd_uncompressed_size = 0;
2921
fl->font_flags = FONT_AUTO;
2922
}
2923
}
2924
(void) cons_update_mode(gfx_state.tg_fb_type != FB_TEXT);
2925
}
2926
return (rc);
2927
}
2928
2929
bool
2930
gfx_get_edid_resolution(struct vesa_edid_info *edid, edid_res_list_t *res)
2931
{
2932
struct resolution *rp, *p;
2933
2934
/*
2935
* Walk detailed timings tables (4).
2936
*/
2937
if ((edid->display.supported_features
2938
& EDID_FEATURE_PREFERRED_TIMING_MODE) != 0) {
2939
/* Walk detailed timing descriptors (4) */
2940
for (int i = 0; i < DET_TIMINGS; i++) {
2941
/*
2942
* Reserved value 0 is not used for display descriptor.
2943
*/
2944
if (edid->detailed_timings[i].pixel_clock == 0)
2945
continue;
2946
if ((rp = malloc(sizeof(*rp))) == NULL)
2947
continue;
2948
rp->width = GET_EDID_INFO_WIDTH(edid, i);
2949
rp->height = GET_EDID_INFO_HEIGHT(edid, i);
2950
if (rp->width > 0 && rp->width <= EDID_MAX_PIXELS &&
2951
rp->height > 0 && rp->height <= EDID_MAX_LINES)
2952
TAILQ_INSERT_TAIL(res, rp, next);
2953
else
2954
free(rp);
2955
}
2956
}
2957
2958
/*
2959
* Walk standard timings list (8).
2960
*/
2961
for (int i = 0; i < STD_TIMINGS; i++) {
2962
/* Is this field unused? */
2963
if (edid->standard_timings[i] == 0x0101)
2964
continue;
2965
2966
if ((rp = malloc(sizeof(*rp))) == NULL)
2967
continue;
2968
2969
rp->width = HSIZE(edid->standard_timings[i]);
2970
switch (RATIO(edid->standard_timings[i])) {
2971
case RATIO1_1:
2972
rp->height = HSIZE(edid->standard_timings[i]);
2973
if (edid->header.version > 1 ||
2974
edid->header.revision > 2) {
2975
rp->height = rp->height * 10 / 16;
2976
}
2977
break;
2978
case RATIO4_3:
2979
rp->height = HSIZE(edid->standard_timings[i]) * 3 / 4;
2980
break;
2981
case RATIO5_4:
2982
rp->height = HSIZE(edid->standard_timings[i]) * 4 / 5;
2983
break;
2984
case RATIO16_9:
2985
rp->height = HSIZE(edid->standard_timings[i]) * 9 / 16;
2986
break;
2987
}
2988
2989
/*
2990
* Create resolution list in decreasing order, except keep
2991
* first entry (preferred timing mode).
2992
*/
2993
TAILQ_FOREACH(p, res, next) {
2994
if (p->width * p->height < rp->width * rp->height) {
2995
/* Keep preferred mode first */
2996
if (TAILQ_FIRST(res) == p)
2997
TAILQ_INSERT_AFTER(res, p, rp, next);
2998
else
2999
TAILQ_INSERT_BEFORE(p, rp, next);
3000
break;
3001
}
3002
if (TAILQ_NEXT(p, next) == NULL) {
3003
TAILQ_INSERT_TAIL(res, rp, next);
3004
break;
3005
}
3006
}
3007
}
3008
return (!TAILQ_EMPTY(res));
3009
}
3010
3011
vm_offset_t
3012
build_font_module(vm_offset_t addr)
3013
{
3014
vt_font_bitmap_data_t *bd;
3015
struct vt_font *fd;
3016
struct preloaded_file *fp;
3017
size_t size;
3018
uint32_t checksum;
3019
int i;
3020
struct font_info fi;
3021
struct fontlist *fl;
3022
uint64_t fontp;
3023
3024
if (STAILQ_EMPTY(&fonts))
3025
return (addr);
3026
3027
/* We can't load first */
3028
if ((file_findfile(NULL, NULL)) == NULL) {
3029
printf("Can not load font module: %s\n",
3030
"the kernel is not loaded");
3031
return (addr);
3032
}
3033
3034
/* helper pointers */
3035
bd = NULL;
3036
STAILQ_FOREACH(fl, &fonts, font_next) {
3037
if (gfx_state.tg_font.vf_width == fl->font_data->vfbd_width &&
3038
gfx_state.tg_font.vf_height == fl->font_data->vfbd_height) {
3039
/*
3040
* Kernel does have better built in font.
3041
*/
3042
if (fl->font_flags == FONT_BUILTIN)
3043
return (addr);
3044
3045
bd = fl->font_data;
3046
break;
3047
}
3048
}
3049
if (bd == NULL)
3050
return (addr);
3051
fd = bd->vfbd_font;
3052
3053
fi.fi_width = fd->vf_width;
3054
checksum = fi.fi_width;
3055
fi.fi_height = fd->vf_height;
3056
checksum += fi.fi_height;
3057
fi.fi_bitmap_size = bd->vfbd_uncompressed_size;
3058
checksum += fi.fi_bitmap_size;
3059
3060
size = roundup2(sizeof (struct font_info), 8);
3061
for (i = 0; i < VFNT_MAPS; i++) {
3062
fi.fi_map_count[i] = fd->vf_map_count[i];
3063
checksum += fi.fi_map_count[i];
3064
size += fd->vf_map_count[i] * sizeof (struct vfnt_map);
3065
size += roundup2(size, 8);
3066
}
3067
size += bd->vfbd_uncompressed_size;
3068
3069
fi.fi_checksum = -checksum;
3070
3071
fp = file_findfile(NULL, md_kerntype);
3072
if (fp == NULL)
3073
panic("can't find kernel file");
3074
3075
fontp = addr;
3076
addr += archsw.arch_copyin(&fi, addr, sizeof (struct font_info));
3077
addr = roundup2(addr, 8);
3078
3079
/* Copy maps. */
3080
for (i = 0; i < VFNT_MAPS; i++) {
3081
if (fd->vf_map_count[i] != 0) {
3082
addr += archsw.arch_copyin(fd->vf_map[i], addr,
3083
fd->vf_map_count[i] * sizeof (struct vfnt_map));
3084
addr = roundup2(addr, 8);
3085
}
3086
}
3087
3088
/* Copy the bitmap. */
3089
addr += archsw.arch_copyin(fd->vf_bytes, addr, fi.fi_bitmap_size);
3090
3091
/* Looks OK so far; populate control structure */
3092
file_addmetadata(fp, MODINFOMD_FONT, sizeof(fontp), &fontp);
3093
return (addr);
3094
}
3095
3096
vm_offset_t
3097
build_splash_module(vm_offset_t addr)
3098
{
3099
struct preloaded_file *fp;
3100
struct splash_info si;
3101
const char *splash;
3102
png_t png;
3103
uint64_t splashp;
3104
int error;
3105
3106
/* We can't load first */
3107
if ((file_findfile(NULL, NULL)) == NULL) {
3108
printf("Can not load splash module: %s\n",
3109
"the kernel is not loaded");
3110
return (addr);
3111
}
3112
3113
fp = file_findfile(NULL, md_kerntype);
3114
if (fp == NULL)
3115
panic("can't find kernel file");
3116
3117
splash = getenv("splash");
3118
if (splash == NULL)
3119
return (addr);
3120
3121
/* Parse png */
3122
if ((error = png_open(&png, splash)) != PNG_NO_ERROR) {
3123
return (addr);
3124
}
3125
3126
si.si_width = png.width;
3127
si.si_height = png.height;
3128
si.si_depth = png.bpp;
3129
splashp = addr;
3130
addr += archsw.arch_copyin(&si, addr, sizeof (struct splash_info));
3131
addr = roundup2(addr, 8);
3132
3133
/* Copy the bitmap. */
3134
addr += archsw.arch_copyin(png.image, addr, png.png_datalen);
3135
3136
printf("Loading splash ok\n");
3137
file_addmetadata(fp, MODINFOMD_SPLASH, sizeof(splashp), &splashp);
3138
return (addr);
3139
}
3140
3141