Skip to content

Commit 75b86d0

Browse files
author
slukash
committed
[feat] add freetype bindings
1 parent 3437a4a commit 75b86d0

File tree

3 files changed

+705
-0
lines changed

3 files changed

+705
-0
lines changed

libraries/freetype.c3l/README.md

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
# FreeType bindings
2+
3+
It is tested only on arm macos.
4+
5+
To make it work on other systems you need to add your platform to `manifest.json`, something like this:
6+
7+
```json
8+
"macos-aarch64": {
9+
// Extra flags to the linker for this target:
10+
"link-args": [
11+
"-I/opt/homebrew/opt/freetype/include/freetype2",
12+
"-L/opt/homebrew/opt/freetype/lib"
13+
],
14+
// C3 libraries this target depends on:
15+
"dependencies": [],
16+
// The external libraries to link for this target:
17+
"linked-libraries": [
18+
"freetype"
19+
]
20+
}
21+
```
22+
23+
`link-args` and `linked-libraries` can be found with this commands.
24+
25+
```bash
26+
$ freetype-config --libs
27+
-L/opt/homebrew/opt/freetype/lib -lfreetype
28+
$ freetype-config --cflags
29+
-I/opt/homebrew/opt/freetype/include/freetype2
30+
```
31+
32+
Note that `-L/opt/homebrew/opt/freetype/lib` goes to `link-args` and not to `linked-libraries`.
33+
34+
Here is a simple program that draws text in the terminal:
35+
36+
```
37+
import freetype;
38+
import std::io;
39+
40+
// image of char
41+
struct Image {
42+
char** pixels;
43+
int x_max;
44+
int y_max;
45+
}
46+
47+
fn void draw_bitmap(CUChar* buffer, Image* image) {
48+
for (int x = 0; x < image.x_max; x++) {
49+
for (int y = 0; y < image.y_max; y++) {
50+
image.pixels[y][x] |= buffer[y * image.x_max + x];
51+
}
52+
}
53+
}
54+
55+
fn int main() {
56+
mem::@report_heap_allocs_in_scope() {
57+
ft::Library library;
58+
ft::Face face;
59+
CInt err;
60+
61+
// init freetype lib
62+
err = ft::initFreeType(&library);
63+
if (err != 0) {
64+
io::printfn("init free type err: %s", err);
65+
return err;
66+
}
67+
68+
// load face
69+
err = ft::newFace(library, "/Library/Fonts/Arial Unicode.ttf", 0, &face);
70+
if (err != ft::Errors.OK.code) {
71+
io::printfn("can't load font: %s", err);
72+
return err;
73+
}
74+
75+
// set font size
76+
err = ft::setPixelSizes(face, 20, 0);
77+
if (err != ft::Errors.OK.code) {
78+
io::printfn("can't set pixel sizes: %s", err);
79+
return err;
80+
}
81+
82+
// our string to print
83+
ZString str = "Hi_c3";
84+
85+
// allocate array of images for out text
86+
Allocator allocator = allocator::heap();
87+
Image* images = allocator::alloc_array(allocator, Image, (int)str.char_len());
88+
defer {
89+
for (int i = 0; i < str.char_len(); i++) {
90+
for (int j = 0; j < images[i].y_max; j++) {
91+
allocator::free(allocator, images[i].pixels[j]);
92+
}
93+
allocator::free(allocator, images[i].pixels);
94+
}
95+
allocator::free(allocator, images);
96+
}
97+
98+
// max height across all chars
99+
int height = 0;
100+
// total width of all chars
101+
int width = 0;
102+
103+
// loop over our string and generate chars
104+
for (int i = 0; i < str.char_len(); i++) {
105+
char c = str[i];
106+
// loading a char
107+
err = ft::loadChar(face, c, ft::LOAD_RENDER);
108+
if (err != ft::Errors.OK.code) {
109+
io::printfn("can't load char: %s", err);
110+
return err;
111+
}
112+
113+
Image image = Image {
114+
.x_max = face.glyph.bitmap.width,
115+
.y_max = face.glyph.bitmap.rows,
116+
.pixels = allocator::alloc_array(allocator, char*, image.y_max),
117+
};
118+
119+
// calculating max heigh
120+
if (image.y_max > height) {
121+
height = image.y_max;
122+
}
123+
// calculating total width
124+
// add 1 to make distance between chars
125+
width += image.x_max + 1;
126+
127+
for (int j = 0; j < image.y_max; j++) {
128+
image.pixels[j] = allocator::alloc_array(allocator, char, image.x_max);
129+
}
130+
131+
draw_bitmap(face.glyph.bitmap.buffer, &image);
132+
133+
images[i] = image;
134+
}
135+
136+
// horizontal render of our string
137+
char** final_image = allocator::alloc_array(allocator, char*, height);
138+
for (int i = 0; i < height; i++) {
139+
final_image[i] = allocator::alloc_array(allocator, char, width);
140+
}
141+
defer {
142+
for (int i = 0; i < height; i++) {
143+
allocator::free(allocator, final_image[i]);
144+
}
145+
allocator::free(allocator, final_image);
146+
}
147+
148+
// by default final_image contains '\0'
149+
// this will cause a broken output to the terminal so we replace it with whitespaces
150+
for (int y = 0; y < height; y++) {
151+
for (int x = 0; x < width+str.char_len(); x++) {
152+
final_image[y][x] = ' ';
153+
}
154+
}
155+
156+
// x offset of char
157+
int offset = 0;
158+
// iterate over our string
159+
for (int i = 0; i < str.char_len(); i++) {
160+
for (int y = 0; y < images[i].y_max; y++) {
161+
for (int x = 0; x < images[i].x_max; x++) {
162+
// v is vertical location of pixel
163+
// char are not equal by height so we need to move smaller chars down
164+
int v = height - images[i].y_max + y;
165+
// h is horizontal location of pixel
166+
// we need to offset it to the right
167+
int h = offset + x + i;
168+
if (images[i].pixels[y][x] == 0) {
169+
final_image[v][h] = ' ';
170+
} else if (images[i].pixels[y][x] < 128) {
171+
final_image[v][h] = '+';
172+
} else {
173+
final_image[v][h] = '*';
174+
}
175+
}
176+
}
177+
offset += images[i].x_max;
178+
}
179+
180+
// draw final_image
181+
for (int i = 0; i < height; i++) {
182+
for (int j = 0; j < width+str.char_len(); j++) {
183+
io::printf("%c", final_image[i][j]);
184+
}
185+
io::printf("\n");
186+
}
187+
188+
// clean after you are done
189+
ft::doneFace(face);
190+
ft::doneFreeType(library);
191+
};
192+
193+
return 0;
194+
}
195+
```
196+
197+
You should get something like this:
198+
```
199+
+** +** **+ +****++
200+
+** +** **+ +*******
201+
+** +** ***+++**+
202+
+** +** **+ +**
203+
+** +** **+ +*****+ +*+
204+
+** +** **+ +*******+ ++**+
205+
+*********** **+ ***+++**+ ****+
206+
+*********** **+ +**+ *****+
207+
+** +** **+ +** ++***+
208+
+** +** **+ +** **+
209+
+** +** **+ +**+ +**+ +**+
210+
+** +** **+ ***++++** ***+++***+
211+
+** +** **+ +*******+ +*******+
212+
+** +** **+ +**********+ +*****+ +*****+
213+
```

0 commit comments

Comments
 (0)