1 | // Aseprite |
2 | // Copyright (C) 2022 Igara Studio S.A. |
3 | // Copyright (C) 2001-2018 David Capello |
4 | // |
5 | // This program is distributed under the terms of |
6 | // the End-User License Agreement for Aseprite. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "app/util/freetype_utils.h" |
13 | |
14 | #include "doc/blend_funcs.h" |
15 | #include "doc/blend_internals.h" |
16 | #include "doc/color.h" |
17 | #include "doc/image.h" |
18 | #include "doc/primitives.h" |
19 | #include "ft/algorithm.h" |
20 | #include "ft/face.h" |
21 | #include "ft/hb_shaper.h" |
22 | #include "ft/lib.h" |
23 | |
24 | #include <memory> |
25 | #include <stdexcept> |
26 | |
27 | namespace app { |
28 | |
29 | doc::Image* render_text(const std::string& fontfile, int fontsize, |
30 | const std::string& text, |
31 | doc::color_t color, |
32 | bool antialias) |
33 | { |
34 | std::unique_ptr<doc::Image> image(nullptr); |
35 | ft::Lib ft; |
36 | |
37 | ft::Face face(ft.open(fontfile)); |
38 | if (face.isValid()) { |
39 | // Set font size |
40 | face.setSize(fontsize); |
41 | face.setAntialias(antialias); |
42 | |
43 | // Calculate text size |
44 | gfx::Rect bounds = ft::calc_text_bounds(face, text); |
45 | |
46 | // Render the image and copy it to the clipboard |
47 | if (!bounds.isEmpty()) { |
48 | image.reset(doc::Image::create(doc::IMAGE_RGB, bounds.w, bounds.h)); |
49 | doc::clear_image(image.get(), 0); |
50 | |
51 | ft::ForEachGlyph<ft::Face> feg(face, text); |
52 | while (feg.next()) { |
53 | auto glyph = feg.glyph(); |
54 | if (!glyph) |
55 | continue; |
56 | |
57 | int t, yimg = - bounds.y + int(glyph->y); |
58 | |
59 | for (int v=0; v<int(glyph->bitmap->rows); ++v, ++yimg) { |
60 | const uint8_t* p = glyph->bitmap->buffer + v*glyph->bitmap->pitch; |
61 | int ximg = - bounds.x + int(glyph->x); |
62 | int bit = 0; |
63 | |
64 | for (int u=0; u<int(glyph->bitmap->width); ++u, ++ximg) { |
65 | int alpha; |
66 | |
67 | if (antialias) { |
68 | alpha = *(p++); |
69 | } |
70 | else { |
71 | alpha = ((*p) & (1 << (7 - (bit++))) ? 255: 0); |
72 | if (bit == 8) { |
73 | bit = 0; |
74 | ++p; |
75 | } |
76 | } |
77 | |
78 | int output_alpha = MUL_UN8(doc::rgba_geta(color), alpha, t); |
79 | if (output_alpha) { |
80 | doc::color_t output_color = |
81 | doc::rgba(doc::rgba_getr(color), |
82 | doc::rgba_getg(color), |
83 | doc::rgba_getb(color), |
84 | output_alpha); |
85 | |
86 | doc::put_pixel( |
87 | image.get(), ximg, yimg, |
88 | doc::rgba_blender_normal( |
89 | doc::get_pixel(image.get(), ximg, yimg), |
90 | output_color)); |
91 | } |
92 | } |
93 | } |
94 | } |
95 | } |
96 | else { |
97 | throw std::runtime_error("There is no text" ); |
98 | } |
99 | } |
100 | else { |
101 | throw std::runtime_error("Error loading font face" ); |
102 | } |
103 | |
104 | return (image ? image.release(): nullptr); |
105 | } |
106 | |
107 | } // namespace app |
108 | |