Skip to content

Commit 5e29f6d

Browse files
committed
PLATFORM TEXT.H
1 parent e0336e1 commit 5e29f6d

2 files changed

Lines changed: 315 additions & 0 deletions

File tree

avs/vis_avs/text.h

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#pragma once
2+
3+
#include "effect_common.h"
4+
#include "pixel_format.h"
5+
6+
#include <string>
7+
#include <vector>
8+
9+
enum FontWeight {
10+
FONT_WEIGHT_DONTCARE = 0,
11+
FONT_WEIGHT_THIN = 100,
12+
FONT_WEIGHT_EXTRALIGHT = 200,
13+
FONT_WEIGHT_LIGHT = 300,
14+
FONT_WEIGHT_REGULAR = 400,
15+
FONT_WEIGHT_MEDIUM = 500,
16+
FONT_WEIGHT_SEMIBOLD = 600,
17+
FONT_WEIGHT_BOLD = 700,
18+
FONT_WEIGHT_EXTRABOLD = 800,
19+
FONT_WEIGHT_BLACK = 900,
20+
};
21+
22+
enum FontFamily {
23+
FONT_FAMILY_DONTCARE = 0,
24+
FONT_FAMILY_ROMAN = 1,
25+
FONT_FAMILY_SWISS = 2,
26+
FONT_FAMILY_MODERN = 3,
27+
FONT_FAMILY_SCRIPT = 4,
28+
FONT_FAMILY_DECORATIVE = 5,
29+
};
30+
31+
struct AVS_Font {
32+
std::string name;
33+
uint32_t weight = 0;
34+
uint32_t height = 0;
35+
bool italic = false;
36+
bool underline = false;
37+
bool strike_out = false;
38+
int64_t char_set = 0;
39+
FontFamily family = FONT_FAMILY_DONTCARE;
40+
static std::vector<AVS_Font> get_fonts();
41+
void* platform_font;
42+
};
43+
44+
enum TextBorderMode {
45+
TEXT_BORDER_NONE = 0,
46+
TEXT_BORDER_OUTLINE = 1,
47+
TEXT_BORDER_SHADOW = 2,
48+
};
49+
50+
struct TextPlatformContext; // defined in the platform-specific implementation
51+
52+
class AVS_Text {
53+
public:
54+
explicit AVS_Text(const AVS_Font& font) : font(font) {}
55+
~AVS_Text();
56+
void get_text_render_size(std::string string, size_t* out_w, size_t* out_h);
57+
void render(std::string string,
58+
pixel_rgb0_8* buffer,
59+
size_t w,
60+
size_t h,
61+
Horizontal_Positions h_align,
62+
Vertical_Positions v_align,
63+
pixel_rgb0_8 color,
64+
TextBorderMode border,
65+
pixel_rgb0_8 border_color,
66+
uint32_t border_size);
67+
68+
private:
69+
void reset(size_t w, size_t h);
70+
71+
const AVS_Font& font;
72+
TextPlatformContext* context = nullptr;
73+
uint32_t last_w = 0;
74+
uint32_t last_h = 0;
75+
};

avs/vis_avs/text_win32.cpp

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
#include "text.h"
2+
3+
#include <windows.h>
4+
5+
int family_to_win32_family(FontFamily family) {
6+
switch (family) {
7+
default:
8+
case FONT_FAMILY_DONTCARE: return 0;
9+
case FONT_FAMILY_ROMAN: return 1;
10+
case FONT_FAMILY_SWISS: return 2;
11+
case FONT_FAMILY_MODERN: return 3;
12+
case FONT_FAMILY_SCRIPT: return 4;
13+
case FONT_FAMILY_DECORATIVE: return 5;
14+
}
15+
}
16+
17+
FontFamily win32_pitchfamily_to_family(int pitch_and_family) {
18+
switch (pitch_and_family >> 4) {
19+
default:
20+
case 0: return FONT_FAMILY_DONTCARE;
21+
case 1: return FONT_FAMILY_ROMAN;
22+
case 2: return FONT_FAMILY_SWISS;
23+
case 3: return FONT_FAMILY_MODERN;
24+
case 4: return FONT_FAMILY_SCRIPT;
25+
case 5: return FONT_FAMILY_DECORATIVE;
26+
}
27+
}
28+
29+
uint32_t valign_to_dt(Vertical_Positions valign) {
30+
switch (valign) {
31+
case VPOS_TOP: return DT_TOP;
32+
default:
33+
case VPOS_CENTER: return DT_VCENTER;
34+
case VPOS_BOTTOM: return DT_BOTTOM;
35+
}
36+
}
37+
38+
uint32_t halign_to_dt(Horizontal_Positions halign) {
39+
switch (halign) {
40+
case HPOS_LEFT: return DT_LEFT;
41+
default:
42+
case HPOS_CENTER: return DT_CENTER;
43+
case HPOS_RIGHT: return DT_RIGHT;
44+
}
45+
}
46+
47+
AVS_Font avs_font_from_logfont(const LOGFONT* logfont) {
48+
if (logfont == nullptr) {
49+
return {};
50+
}
51+
return {
52+
.name = logfont->lfFaceName,
53+
.weight = logfont->lfWeight,
54+
.height = logfont->lfHeight,
55+
.italic = logfont->lfItalic,
56+
.underline = logfont->lfUnderline,
57+
.strike_out = logfont->lfStrikeOut,
58+
.char_set = logfont->lfCharSet,
59+
.family = win32_pitchfamily_to_family(logfont->lfPitchAndFamily),
60+
.platform_font = (void*)logfont,
61+
};
62+
}
63+
64+
int CALLBACK enum_font_fam_ex_proc(const LOGFONT* logfont,
65+
const TEXTMETRIC*,
66+
DWORD,
67+
LPARAM font_list) {
68+
((std::vector<AVS_Font>*)font_list)->push_back(avs_font_from_logfont(logfont));
69+
return 1; // return nonzero to continue enumerating more fonts
70+
}
71+
72+
std::vector<AVS_Font> AVS_Font::get_fonts() {
73+
static std::vector<AVS_Font> font_list = {};
74+
75+
LOGFONT match_all_fonts = {};
76+
// TODO [compat]: Find out if using ANSI_CHARSETS excludes fonts. Setting lfCharSet
77+
// to DEFAULT_CHARSET matches every font several times for all available charsets.
78+
// This is creates a lot of duplicates since AVS_Font doesn't have the concept of
79+
// "char sets".
80+
match_all_fonts.lfCharSet = ANSI_CHARSET;
81+
match_all_fonts.lfPitchAndFamily = 0;
82+
match_all_fonts.lfFaceName[0] = '\0';
83+
EnumFontFamiliesEx(
84+
GetDC(nullptr), &match_all_fonts, enum_font_fam_ex_proc, (LPARAM)&font_list, 0);
85+
86+
return font_list;
87+
}
88+
89+
struct TextPlatformContext {
90+
HDC device = nullptr;
91+
HBITMAP bitmap = nullptr;
92+
BITMAPINFO bm_info;
93+
RECT rect;
94+
HBITMAP previous_bitmap = nullptr;
95+
HFONT previous_font = nullptr;
96+
97+
TextPlatformContext(size_t w, size_t h, HFONT font);
98+
~TextPlatformContext();
99+
};
100+
101+
TextPlatformContext::TextPlatformContext(size_t w, size_t h, HFONT font)
102+
: device(CreateCompatibleDC(nullptr)),
103+
bitmap(CreateCompatibleBitmap(this->device, w, h)) {
104+
this->previous_bitmap = (HBITMAP)SelectObject(this->device, this->bitmap);
105+
this->previous_font = (HFONT)SelectObject(this->device, font);
106+
this->bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
107+
this->bm_info.bmiHeader.biWidth = 1;
108+
this->bm_info.bmiHeader.biHeight = 1;
109+
this->bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
110+
this->bm_info.bmiHeader.biWidth = w;
111+
this->bm_info.bmiHeader.biHeight = h;
112+
this->bm_info.bmiHeader.biPlanes = 1;
113+
this->bm_info.bmiHeader.biBitCount = 32; // ARGB
114+
this->bm_info.bmiHeader.biCompression = BI_RGB;
115+
this->rect.left = 0;
116+
this->rect.top = 0;
117+
this->rect.right = w;
118+
this->rect.bottom = h;
119+
SetBkMode(this->device, TRANSPARENT);
120+
SetBkColor(this->device, 0);
121+
}
122+
123+
TextPlatformContext::~TextPlatformContext() {
124+
if (this->bitmap != nullptr) {
125+
DeleteObject(this->bitmap);
126+
}
127+
if (this->previous_bitmap) {
128+
SelectObject(this->device, this->previous_bitmap);
129+
}
130+
if (this->previous_font) {
131+
SelectObject(this->device, this->previous_font);
132+
}
133+
if (this->device != nullptr) {
134+
DeleteDC(this->device);
135+
}
136+
}
137+
138+
AVS_Text::~AVS_Text() { delete this->context; }
139+
140+
void AVS_Text::get_text_render_size(std::string string,
141+
uint32_t* out_w,
142+
uint32_t* out_h) {
143+
if (out_w == nullptr || out_h == nullptr) {
144+
return;
145+
}
146+
SIZE size = {0, 0};
147+
GetTextExtentPoint32(this->context->device, string.c_str(), string.length(), &size);
148+
*out_w = size.cx;
149+
*out_h = size.cy;
150+
}
151+
152+
#define RGB_TO_BGR(color) \
153+
(((color) & 0xff0000) >> 16 | ((color) & 0xff00) | ((color) & 0xff) << 16)
154+
155+
void AVS_Text::render(std::string string,
156+
pixel_rgb0_8* buffer,
157+
size_t w,
158+
size_t h,
159+
Horizontal_Positions h_align,
160+
Vertical_Positions v_align,
161+
pixel_rgb0_8 color,
162+
TextBorderMode border,
163+
pixel_rgb0_8 border_color,
164+
size_t border_size) {
165+
(void)w;
166+
if (string.empty() || buffer == nullptr) {
167+
return;
168+
}
169+
if (this->last_h != h || this->last_w != w) {
170+
this->reset(w, h);
171+
this->last_w = w;
172+
this->last_h = h;
173+
}
174+
175+
auto device = this->context->device;
176+
auto bitmap = this->context->bitmap;
177+
auto bm_info = this->context->bm_info;
178+
auto rect = this->context->rect;
179+
180+
int alignment =
181+
valign_to_dt(v_align) | halign_to_dt(h_align) | DT_NOCLIP | DT_SINGLELINE;
182+
SetDIBits(device, bitmap, 0, h, (void*)buffer, &bm_info, DIB_RGB_COLORS);
183+
switch (border) {
184+
case TEXT_BORDER_OUTLINE:
185+
SetTextColor(device, RGB_TO_BGR(border_color));
186+
rect.left -= border_size;
187+
rect.right -= border_size;
188+
rect.top -= border_size;
189+
rect.bottom -= border_size;
190+
DrawText(device, string.c_str(), string.length(), &rect, alignment);
191+
rect.left += border_size;
192+
rect.right += border_size;
193+
DrawText(device, string.c_str(), string.length(), &rect, alignment);
194+
rect.left += border_size;
195+
rect.right += border_size;
196+
DrawText(device, string.c_str(), string.length(), &rect, alignment);
197+
rect.top += border_size;
198+
rect.bottom += border_size;
199+
DrawText(device, string.c_str(), string.length(), &rect, alignment);
200+
rect.top += border_size;
201+
rect.bottom += border_size;
202+
DrawText(device, string.c_str(), string.length(), &rect, alignment);
203+
rect.left -= border_size;
204+
rect.right -= border_size;
205+
DrawText(device, string.c_str(), string.length(), &rect, alignment);
206+
rect.left -= border_size;
207+
rect.right -= border_size;
208+
DrawText(device, string.c_str(), string.length(), &rect, alignment);
209+
rect.top -= border_size;
210+
rect.bottom -= border_size;
211+
DrawText(device, string.c_str(), string.length(), &rect, alignment);
212+
rect.left += border_size;
213+
rect.right += border_size;
214+
break;
215+
case TEXT_BORDER_SHADOW:
216+
SetTextColor(device, RGB_TO_BGR(border_color));
217+
rect.left += border_size;
218+
rect.right += border_size;
219+
rect.top += border_size;
220+
rect.bottom += border_size;
221+
DrawText(device, string.c_str(), string.length(), &rect, alignment);
222+
rect.left -= border_size;
223+
rect.right -= border_size;
224+
rect.top -= border_size;
225+
rect.bottom -= border_size;
226+
break;
227+
default: break;
228+
}
229+
SetTextColor(device, RGB_TO_BGR(color));
230+
DrawText(device, string.c_str(), string.length(), &rect, alignment);
231+
GetDIBits(device, bitmap, 0, h, (void*)buffer, &bm_info, DIB_RGB_COLORS);
232+
}
233+
234+
void AVS_Text::reset(size_t w, size_t h) {
235+
auto old_context = this->context;
236+
this->context = new TextPlatformContext(w, h, (HFONT)this->font.platform_font);
237+
if (old_context != nullptr) {
238+
delete old_context;
239+
}
240+
}

0 commit comments

Comments
 (0)