1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
7 // Functions related to fonts.
9 #define DE_NOT_IN_MODULE
10 #include "deark-config.h"
11 #include "deark-private.h"
13 static int is_valid_char(struct de_bitmap_font_char
*ch
)
16 if(!ch
->bitmap
) return 0;
17 if(ch
->width
<0 || ch
->height
<0) return 0;
18 if(ch
->extraspace_l
<0 || ch
->extraspace_r
<0) return 0;
19 if(ch
->width
==0 && (ch
->extraspace_l
>0 || ch
->extraspace_r
>0)) {
20 // Allow 0-width character if extraspace makes that sensible.
23 else if(ch
->width
<1 || ch
->height
<1) {
29 struct de_bitmap_font
*de_create_bitmap_font(deark
*c
)
31 struct de_bitmap_font
*font
;
32 font
= de_malloc(c
, sizeof(struct de_bitmap_font
));
33 font
->index_of_replacement_char
= -1;
37 void de_destroy_bitmap_font(deark
*c
, struct de_bitmap_font
*font
)
42 static void paint_character_internal(deark
*c
, de_bitmap
*img
,
43 struct de_bitmap_font_char
*ch
,
44 i64 xpos
, i64 ypos
, u32 fgcol
, unsigned int flags
)
47 i64 num_x_pixels_to_paint
;
50 num_x_pixels_to_paint
= (i64
)ch
->width
;
51 if((flags
&DE_PAINTFLAG_VGA9COL
) && ch
->width
==8) {
53 num_x_pixels_to_paint
= 9;
56 for(j
=0; j
<ch
->height
; j
++) {
60 if(flags
&DE_PAINTFLAG_TOPHALF
) {
63 else if(flags
&DE_PAINTFLAG_BOTTOMHALF
) {
64 j_src
= (ch
->height
+j
)/2;
67 for(i
=0; i
<num_x_pixels_to_paint
; i
++) {
68 i64 i_src
; // -1 = No source position
73 if(flags
&DE_PAINTFLAG_LEFTHALF
) {
76 else if(flags
&DE_PAINTFLAG_RIGHTHALF
) {
77 i_src
= (num_x_pixels_to_paint
+i
)/2;
80 if(i_src
==8 && vga9col_flag
) {
81 // Manufacture a column 8.
82 if(ch
->codepoint_nonunicode
>=0xb0 && ch
->codepoint_nonunicode
<=0xdf) {
83 i_src
= 7; // Make this pixel a duplicate of the one in col #7.
86 i_src
= -1; // Make this pixel a background pixel.
90 if(i_src
>=0 && i_src
<ch
->width
) {
91 x
= ch
->bitmap
[j_src
*ch
->rowspan
+ i_src
/8];
92 if(x
& (1<<(7-i_src
%8))) {
100 de_bitmap_setpixel_rgba(img
, xpos
+i
, ypos
+j
, clr
);
106 // Paint a character at the given index in the given font, to the given bitmap.
107 void de_font_paint_character_idx(deark
*c
, de_bitmap
*img
,
108 struct de_bitmap_font
*font
, i64 char_idx
,
109 i64 xpos
, i64 ypos
, de_color fgcol
, de_color bgcol
,
112 struct de_bitmap_font_char
*ch
;
114 if(char_idx
<0 || char_idx
>=font
->num_chars
) return;
115 ch
= &font
->char_array
[char_idx
];
116 if(!is_valid_char(ch
)) return;
117 if(ch
->width
> font
->nominal_width
) return;
118 if(ch
->height
> font
->nominal_height
) return;
120 // Paint a "canvas" for the char, if needed.
122 // If the "extraspace" feature is used, paint an additional canvas of
123 // a different color.
124 if(!(flags
&DE_PAINTFLAG_TRNSBKGD
) && (ch
->extraspace_l
|| ch
->extraspace_r
)) {
125 i64 canvas_x
, canvas_y
;
126 i64 canvas_w
, canvas_h
;
129 canvas_y
= ypos
+ch
->v_offset
;
130 canvas_w
= (i64
)ch
->extraspace_l
+ (i64
)ch
->width
+ (i64
)ch
->extraspace_r
;
131 canvas_h
= ch
->height
;
132 // (We don't need to support both the "extraspace" and the VGA9COL
133 // feature at the same time.)
135 if(ch
->width
==0 || ch
->height
==0) {
136 // Bit of a hack. If we have a zero-size (spacer-only) character,
137 // paint the extraspace as the full height the cell.
138 // (In principle, maybe it should be painted this way for *all*
139 // characters, but I'm not sure how to make that look good.)
141 canvas_h
= font
->nominal_height
;
144 if(canvas_y
+ canvas_h
> ypos
+ font
->nominal_height
) {
145 goto after_extraspace
;
148 de_bitmap_rect(img
, canvas_x
, canvas_y
,
149 canvas_w
, canvas_h
, DE_MAKE_RGB(192,255,192), 0);
154 if(ch
->width
<=0 || ch
->height
<=0) goto after_foreground
; // Nothing to do
156 // Paint the canvas for the main part of the character.
157 if(!(flags
&DE_PAINTFLAG_TRNSBKGD
)) {
158 i64 canvas_x
, canvas_y
;
159 i64 canvas_w
, canvas_h
;
161 canvas_x
= xpos
+ ch
->extraspace_l
;
162 canvas_y
= ypos
+ch
->v_offset
;
163 canvas_w
= ch
->width
;
164 if((flags
&DE_PAINTFLAG_VGA9COL
) && ch
->width
==8) {
167 canvas_h
= ch
->height
;
169 if(canvas_y
+ canvas_h
> ypos
+ font
->nominal_height
) {
170 goto after_foreground
;
172 de_bitmap_rect(img
, canvas_x
, canvas_y
,
173 canvas_w
, canvas_h
, bgcol
, 0);
176 // Paint the character foreground.
177 paint_character_internal(c
, img
, ch
,
178 ch
->extraspace_l
+ xpos
, ch
->v_offset
+ ypos
, fgcol
, flags
);
184 // Given a codepoint, returns the character index in the font.
185 // 'codepoint' is expected to be a Unicode codepoint. If the font does not
186 // have Unicode codepoints, the non-Unicode codepoint will be used instead.
187 // Returns -1 if not found.
188 static i64
get_char_idx_by_cp(deark
*c
, struct de_bitmap_font
*font
, i32 codepoint
)
192 // TODO: Sometimes, a font has multiple characters that map to the same
193 // codepoint. We should have a way to find the *best* such character,
194 // which might not be the first one.
196 for(i
=0; i
<font
->num_chars
; i
++) {
197 if(font
->has_unicode_codepoints
) {
198 if(font
->char_array
[i
].codepoint_unicode
== codepoint
)
202 if(font
->char_array
[i
].codepoint_nonunicode
== codepoint
)
209 // 'codepoint' is expected to be a Unicode codepoint. If the font does not
210 // have Unicode codepoints, the non-Unicode codepoint will be used instead.
211 void de_font_paint_character_cp(deark
*c
, de_bitmap
*img
,
212 struct de_bitmap_font
*font
, i32 codepoint
,
213 i64 xpos
, i64 ypos
, de_color fgcol
, de_color bgcol
, unsigned int flags
)
217 char_idx
= get_char_idx_by_cp(c
, font
, codepoint
);
219 if(font
->index_of_replacement_char
>=0) {
220 char_idx
= font
->index_of_replacement_char
;
224 char_idx
= get_char_idx_by_cp(c
, font
, '?');
226 if(char_idx
<0 || char_idx
>=font
->num_chars
) {
229 de_font_paint_character_idx(c
, img
, font
, char_idx
, xpos
, ypos
, fgcol
, bgcol
, flags
);
232 struct dfont_char_data
{
233 i32 codepoint_unicode
;
237 static const struct dfont_char_data dfont_data
[16] = {
238 {48, {0x30,0x48,0x48,0x48,0x48,0x48,0x30}}, // 0
239 {49, {0x10,0x30,0x10,0x10,0x10,0x10,0x38}}, // 1
240 {50, {0x70,0x08,0x08,0x30,0x40,0x40,0x78}}, // 2
241 {51, {0x70,0x08,0x08,0x30,0x08,0x08,0x70}}, // 3
242 {52, {0x48,0x48,0x48,0x78,0x08,0x08,0x08}}, // 4
243 {53, {0x78,0x40,0x40,0x70,0x08,0x08,0x70}}, // 5
244 {54, {0x38,0x40,0x40,0x70,0x48,0x48,0x30}}, // 6
245 {55, {0x78,0x08,0x08,0x10,0x10,0x10,0x10}}, // 7
246 {56, {0x30,0x48,0x48,0x30,0x48,0x48,0x30}}, // 8
247 {57, {0x30,0x48,0x48,0x38,0x08,0x08,0x70}}, // 9
248 {65, {0x30,0x48,0x48,0x78,0x48,0x48,0x48}}, // A
249 {66, {0x70,0x48,0x48,0x70,0x48,0x48,0x70}}, // B
250 {67, {0x30,0x48,0x40,0x40,0x40,0x48,0x30}}, // C
251 {68, {0x70,0x58,0x48,0x48,0x48,0x58,0x70}}, // D
252 {69, {0x78,0x40,0x40,0x70,0x40,0x40,0x78}}, // E
253 {70, {0x78,0x40,0x40,0x70,0x40,0x40,0x40}} // F
256 static struct de_bitmap_font
*make_digit_font(deark
*c
)
258 struct de_bitmap_font
*dfont
= NULL
;
261 dfont
= de_create_bitmap_font(c
);
262 dfont
->num_chars
= 16;
263 dfont
->nominal_width
= 6;
264 dfont
->nominal_height
= 7;
265 dfont
->has_unicode_codepoints
= 1;
266 dfont
->char_array
= de_mallocarray(c
, dfont
->num_chars
, sizeof(struct de_bitmap_font_char
));
268 for(i
=0; i
<dfont
->num_chars
; i
++) {
269 dfont
->char_array
[i
].codepoint_unicode
= dfont_data
[i
].codepoint_unicode
;
270 dfont
->char_array
[i
].width
= dfont
->nominal_width
;
271 dfont
->char_array
[i
].height
= dfont
->nominal_height
;
272 dfont
->char_array
[i
].rowspan
= 1;
273 dfont
->char_array
[i
].bitmap
= (u8
*)dfont_data
[i
].bitmap
;
279 struct font_render_ctx
{
280 struct de_bitmap_font
*font
;
281 i32 min_codepoint
; // currently unused
284 int render_as_unicode
;
286 // Array of the actual codepoints we will use when dumping the font
287 // to an image. Size is font->num_chars.
291 #define DNFLAG_HEX 0x1
292 #define DNFLAG_LEADING_ZEROES 0x2
293 #define DNFLAG_HCENTER 0x4
295 // (xpos,ypos) is the lower-right corner
296 // (or the bottom-center, if hcenter==1).
297 static void draw_number(deark
*c
, de_bitmap
*img
,
298 struct de_bitmap_font
*dfont
, i64 n
, i64 xpos1
, i64 ypos1
,
307 if(flags
& DNFLAG_HEX
) {
308 if(flags
& DNFLAG_LEADING_ZEROES
)
309 de_snprintf(buf
, sizeof(buf
), "%04X", (unsigned int)n
);
311 de_snprintf(buf
, sizeof(buf
), "%X", (unsigned int)n
);
314 de_snprintf(buf
, sizeof(buf
), "%u", (unsigned int)n
);
316 len
= (i64
)de_strlen(buf
);
318 if(flags
& DNFLAG_HCENTER
)
319 xpos_start
= xpos1
-(dfont
->nominal_width
*len
)/2;
321 xpos_start
= xpos1
-dfont
->nominal_width
*len
;
323 // Make sure number doesn't go beyond the image
324 if(xpos_start
+ dfont
->nominal_width
*len
> img
->width
) {
325 xpos_start
= img
->width
- dfont
->nominal_width
*len
;
328 for(i
=len
-1; i
>=0; i
--) {
329 xpos
= xpos_start
+ dfont
->nominal_width
*i
;
330 ypos
= ypos1
-dfont
->nominal_height
;
331 de_font_paint_character_cp(c
, img
, dfont
, buf
[i
], xpos
, ypos
,
332 DE_MAKE_GRAY(255), 0, DE_PAINTFLAG_TRNSBKGD
);
336 static void get_min_max_codepoint(struct font_render_ctx
*fctx
)
340 fctx
->min_codepoint
= 0x10ffff;
341 fctx
->max_codepoint
= 0;
342 fctx
->num_valid_chars
= 0;
344 for(i
=0; i
<fctx
->font
->num_chars
; i
++) {
345 if(!is_valid_char(&fctx
->font
->char_array
[i
])) continue;
346 if(fctx
->codepoint_tmp
[i
] == DE_CODEPOINT_INVALID
) continue;
347 fctx
->num_valid_chars
++;
348 if(fctx
->codepoint_tmp
[i
] < fctx
->min_codepoint
)
349 fctx
->min_codepoint
= fctx
->codepoint_tmp
[i
];
350 if(fctx
->codepoint_tmp
[i
] > fctx
->max_codepoint
)
351 fctx
->max_codepoint
= fctx
->codepoint_tmp
[i
];
355 // Put the actual codepont to use in the font->char_array[].codepoint_tmp field.
356 static void fixup_codepoints(deark
*c
, struct font_render_ctx
*fctx
)
360 i64 num_uncoded_chars
= 0;
361 u8
*used_codepoint_map
= NULL
;
362 u8 codepoint_already_used
;
364 if(!fctx
->render_as_unicode
) {
365 for(i
=0; i
<fctx
->font
->num_chars
; i
++) {
366 fctx
->codepoint_tmp
[i
] = fctx
->font
->char_array
[i
].codepoint_nonunicode
;
371 // An array of bits to remember if we've seen a codepoint before (BMP only).
372 // A character with a duplicate codepoint will be moved to another
373 // location, so that it doesn't get painted over the previous one.
374 used_codepoint_map
= de_malloc(c
, 65536/8);
376 for(i
=0; i
<fctx
->font
->num_chars
; i
++) {
377 if(!is_valid_char(&fctx
->font
->char_array
[i
])) continue;
378 c1
= fctx
->font
->char_array
[i
].codepoint_unicode
;
380 codepoint_already_used
= 0;
381 if(c1
>=0 && c1
<65536) {
382 // Check if we've seen this codepoint before.
383 codepoint_already_used
= used_codepoint_map
[c1
/8] & (1<<(c1
%8));
385 // Remember that we've seen this codepoint.
386 used_codepoint_map
[c1
/8] |= 1<<(c1
%8);
389 if(codepoint_already_used
|| c1
==DE_CODEPOINT_INVALID
) {
390 if(codepoint_already_used
) {
391 de_dbg2(c
, "moving duplicate codepoint U+%04x at index %d to private use area",
392 (unsigned int)c1
, (int)i
);
394 // Move uncoded characters to a Private Use area.
395 // (Supplementary Private Use Area-A = U+F0000 - U+FFFFD)
396 if(DE_CODEPOINT_MOVED
+ num_uncoded_chars
<= DE_CODEPOINT_MOVED_MAX
) {
397 fctx
->codepoint_tmp
[i
] = (i32
)(DE_CODEPOINT_MOVED
+ num_uncoded_chars
);
402 fctx
->codepoint_tmp
[i
] = c1
;
407 de_free(c
, used_codepoint_map
);
410 struct row_info_struct
{
415 struct col_info_struct
{
420 static void checkerboard_bkgd(de_bitmap
*img
, i64 xpos
, i64 ypos
, i64 w
, i64 h
)
424 for(jj
=0; jj
<h
; jj
++) {
425 for(ii
=0; ii
<w
; ii
++) {
426 de_bitmap_setpixel_gray(img
, xpos
+ii
, ypos
+jj
, (ii
/2+jj
/2)%2 ? 176 : 192);
431 void de_font_bitmap_font_to_image(deark
*c
, struct de_bitmap_font
*font1
, de_finfo
*fi
,
432 unsigned int createflags
)
434 struct font_render_ctx
*fctx
= NULL
;
436 de_bitmap
*img
= NULL
;
438 i64 img_leftmargin
, img_topmargin
;
439 i64 img_rightmargin
, img_bottommargin
;
440 i64 img_vpixelsperchar
;
441 i64 img_width
, img_height
;
442 i64 num_table_rows_to_display
;
443 i64 num_table_rows_total
;
445 struct de_bitmap_font
*dfont
= NULL
;
446 i64 chars_per_row
= 32;
448 struct row_info_struct
*row_info
= NULL
;
449 struct col_info_struct
*col_info
= NULL
;
454 unsigned int dnflags
;
456 fctx
= de_malloc(c
, sizeof(struct font_render_ctx
));
459 if(fctx
->font
->num_chars
<1) goto done
;
460 if(fctx
->font
->num_chars
>17*65536) goto done
;
461 if(fctx
->font
->nominal_width
>512 || fctx
->font
->nominal_height
>512) {
462 de_err(c
, "Font size too big (%d"DE_CHAR_TIMES
"%d). Not supported.",
463 (int)fctx
->font
->nominal_width
, (int)fctx
->font
->nominal_height
);
467 // -1 = "no preference"
468 unicode_req
= de_get_ext_option_bool(c
, "font:tounicode", -1);
471 (fctx
->font
->has_nonunicode_codepoints
|| !fctx
->font
->has_unicode_codepoints
))
473 ; // Render as nonunicode.
475 else if(fctx
->font
->has_unicode_codepoints
&&
476 (unicode_req
>0 || fctx
->font
->prefer_unicode
|| !fctx
->font
->has_nonunicode_codepoints
))
478 fctx
->render_as_unicode
= 1;
481 s
= de_get_ext_option(c
, "font:charsperrow");
483 chars_per_row
= de_atoi64(s
);
484 if(chars_per_row
<1) chars_per_row
=1;
487 dfont
= make_digit_font(c
);
489 fctx
->codepoint_tmp
= de_mallocarray(c
, fctx
->font
->num_chars
, sizeof(i32
));
490 fixup_codepoints(c
, fctx
);
492 get_min_max_codepoint(fctx
);
493 if(fctx
->num_valid_chars
<1) goto done
;
494 num_table_rows_total
= fctx
->max_codepoint
/chars_per_row
+1;
496 // TODO: Clean up these margin calculations, and make it more general.
497 if(fctx
->render_as_unicode
) {
498 img_leftmargin
= (i64
)dfont
->nominal_width
* 5 + 6;
501 if(fctx
->max_codepoint
>= 1000)
502 img_leftmargin
= (i64
)dfont
->nominal_width
* 5 + 6;
504 img_leftmargin
= (i64
)dfont
->nominal_width
* 3 + 6;
506 img_topmargin
= (i64
)dfont
->nominal_height
+ 6;
508 img_bottommargin
= 1;
510 // Scan the characters, and record relevant information.
511 row_info
= de_mallocarray(c
, num_table_rows_total
, sizeof(struct row_info_struct
));
512 col_info
= de_mallocarray(c
, chars_per_row
, sizeof(struct col_info_struct
));
513 for(i
=0; i
<chars_per_row
; i
++) {
514 #define MIN_CHAR_CELL_WIDTH 5
515 col_info
[i
].display_width
= MIN_CHAR_CELL_WIDTH
;
518 for(k
=0; k
<fctx
->font
->num_chars
; k
++) {
519 i64 char_display_width
;
521 if(fctx
->codepoint_tmp
[k
] == DE_CODEPOINT_INVALID
) continue;
522 if(!is_valid_char(&fctx
->font
->char_array
[k
])) continue;
523 rownum
= fctx
->codepoint_tmp
[k
] / chars_per_row
;
524 colnum
= fctx
->codepoint_tmp
[k
] % chars_per_row
;
525 if(rownum
<0 || rownum
>=num_table_rows_total
) {
526 de_internal_err_fatal(c
, "bad rownum");
529 // Remember that there is at least one valid character in this character's row.
530 row_info
[rownum
].is_visible
= 1;
532 // Track the maximum width of any character in this character's column.
533 char_display_width
= (i64
)fctx
->font
->char_array
[k
].width
+
534 (i64
)fctx
->font
->char_array
[k
].extraspace_l
+
535 (i64
)fctx
->font
->char_array
[k
].extraspace_r
;
536 if(char_display_width
> col_info
[colnum
].display_width
) {
537 col_info
[colnum
].display_width
= char_display_width
;
541 img_vpixelsperchar
= (i64
)fctx
->font
->nominal_height
+ 1;
543 // Figure out how many rows are used, and where to draw them.
544 num_table_rows_to_display
= 0;
546 curpos
= img_topmargin
;
547 for(j
=0; j
<num_table_rows_total
; j
++) {
548 if(!row_info
[j
].is_visible
) continue;
550 // If we skipped one or more rows, leave some extra vertical space.
551 if(num_table_rows_to_display
>0 && !row_info
[j
-1].is_visible
) curpos
+=3;
554 row_info
[j
].display_pos
= curpos
;
555 curpos
+= img_vpixelsperchar
;
556 num_table_rows_to_display
++;
558 if(num_table_rows_to_display
<1) goto done
;
560 // Figure out the positions of the columns.
561 curpos
= img_leftmargin
;
562 for(i
=0; i
<chars_per_row
; i
++) {
563 col_info
[i
].display_pos
= curpos
;
564 curpos
+= col_info
[i
].display_width
+ 1;
567 img_width
= col_info
[chars_per_row
-1].display_pos
+
568 col_info
[chars_per_row
-1].display_width
+ img_rightmargin
;
569 img_height
= row_info
[last_valid_row
].display_pos
+
570 img_vpixelsperchar
-1 + img_bottommargin
;
572 img
= de_bitmap_create(c
, img_width
, img_height
, 3);
575 de_bitmap_rect(img
, 0, 0, img
->width
, img
->height
, DE_MAKE_RGB(32,32,144), 0);
577 // Draw/clear the cell backgrounds
578 for(j
=0; j
<num_table_rows_total
; j
++) {
579 if(!row_info
[j
].is_visible
) continue;
580 ypos
= row_info
[j
].display_pos
;
582 for(i
=0; i
<chars_per_row
; i
++) {
583 xpos
= col_info
[i
].display_pos
;
584 de_bitmap_rect(img
, xpos
, ypos
,
585 col_info
[i
].display_width
, img_vpixelsperchar
-1,
586 DE_MAKE_RGB(112,112,160), 0);
590 // Draw the labels in the top margin.
592 // TODO: Better label spacing logic.
593 if(fctx
->font
->nominal_width
<= 12)
598 for(i
=0; i
<chars_per_row
; i
++) {
599 if(i
%label_stride
!= 0) continue;
600 xpos
= col_info
[i
].display_pos
+ col_info
[i
].display_width
/2;
601 ypos
= img_topmargin
- 3;
603 dnflags
= DNFLAG_HCENTER
;
604 if(fctx
->render_as_unicode
) dnflags
|= DNFLAG_HEX
;
606 draw_number(c
, img
, dfont
, i
, xpos
, ypos
, dnflags
);
609 // Draw the labels in the left margin.
610 for(j
=0; j
<num_table_rows_total
; j
++) {
611 if(!row_info
[j
].is_visible
) continue;
612 xpos
= img_leftmargin
- 3;
613 ypos
= row_info
[j
].display_pos
+ (img_vpixelsperchar
+ dfont
->nominal_height
+ 1)/2;
616 if(fctx
->render_as_unicode
) dnflags
|= DNFLAG_HEX
| DNFLAG_LEADING_ZEROES
;
618 draw_number(c
, img
, dfont
, j
*chars_per_row
, xpos
, ypos
, dnflags
);
621 // Render the glyphs.
622 for(k
=0; k
<fctx
->font
->num_chars
; k
++) {
623 if(fctx
->codepoint_tmp
[k
] == DE_CODEPOINT_INVALID
) continue;
624 if(!is_valid_char(&fctx
->font
->char_array
[k
])) continue;
626 rownum
= fctx
->codepoint_tmp
[k
] / chars_per_row
;
627 colnum
= fctx
->codepoint_tmp
[k
] % chars_per_row
;
628 if(rownum
<0 || rownum
>=num_table_rows_total
) {
629 de_internal_err_fatal(c
, "bad rownum");
632 xpos
= col_info
[colnum
].display_pos
;
633 ypos
= row_info
[rownum
].display_pos
;
635 checkerboard_bkgd(img
, xpos
, ypos
,
636 col_info
[colnum
].display_width
, img_vpixelsperchar
-1);
638 de_font_paint_character_idx(c
, img
, fctx
->font
, k
, xpos
, ypos
,
639 DE_STOCKCOLOR_BLACK
, DE_STOCKCOLOR_WHITE
, 0);
642 de_bitmap_write_to_file_finfo(img
, fi
, createflags
);
646 de_free(c
, dfont
->char_array
);
647 de_destroy_bitmap_font(c
, dfont
);
649 de_bitmap_destroy(img
);
650 de_free(c
, row_info
);
651 de_free(c
, col_info
);
653 de_free(c
, fctx
->codepoint_tmp
);
658 // Do we recognize the font as a standard VGA CP437 font?
659 // This function is quick and dirty. Ideally we would:
660 // * look at each character, instead or requiring the whole font to be identical
661 // * recognize fonts with other character sets
662 int de_font_is_standard_vga_font(deark
*c
, u32 crc
)
665 case 0x2c3cf7d2U
: // e.g.: ndh - Ada.xb
666 case 0x3c0aa3eeU
: // https://commons.wikimedia.org/w/index.php?title=File:Codepage-437.png&oldid=153353189
667 case 0x71e15998U
: // Used in many XBIN files.
668 case 0xb6133c6eU
: // blocktronics_baud_dudes/k1-strax.xb (8x14)
669 case 0xb7cb6e5cU
: // e.g.: T1-XBIN.XB