Skip to content

Commit d2b207b

Browse files
authored
Merge pull request #170 from Libvisual/dancingparticles-ft2
libvisual-plugins: Replace use of t1lib in DancingParticles with FreeType 2 (closes #53).
2 parents b4a110e + 814d948 commit d2b207b

File tree

9 files changed

+285
-106
lines changed

9 files changed

+285
-106
lines changed

libvisual-plugins/CMakeLists.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,9 @@ IF(ENABLE_ALSA)
169169
ENDIF()
170170

171171
IF(ENABLE_DANCINGPARTICLES)
172-
FIND_PACKAGE(T1lib)
173-
IF(NOT T1LIB_FOUND)
174-
MESSAGE(WARNING "T1lib not found. The Dancing Particles plugin will not be built.")
172+
FIND_PACKAGE(Freetype)
173+
IF(NOT FREETYPE_FOUND)
174+
MESSAGE(WARNING "FreeType not found. The Dancing Particles plugin will not be built.")
175175
SET(ENABLE_DANCINGPARTICLES no)
176176
ENDIF()
177177

libvisual-plugins/cmake/FindT1lib.cmake

Lines changed: 0 additions & 26 deletions
This file was deleted.

libvisual-plugins/plugins/actor/dancingparticles/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ SET(SOURCES
77
gl.cpp
88
parameters.cpp
99
signal.cpp
10-
t1font.cpp
10+
font.cpp
1111
)
1212

1313
LV_BUILD_ACTOR_PLUGIN(dancingparticles
1414
SOURCES ${SOURCES}
1515
COMPILE_DEFS DATA_DIR="${DANCINGPARTICLES_DATA_DIR}"
16-
LINK_LIBS OpenGL::GL OpenGL::GLU T1lib::T1lib m
16+
LINK_LIBS OpenGL::GL OpenGL::GLU Freetype::Freetype m
1717
)
1818

1919
SET(DATA_FILES

libvisual-plugins/plugins/actor/dancingparticles/actor_dancingparticles.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "gl.h"
2626
#include "fastmath.h"
2727
#include "etoile.h"
28+
#include "font.h"
2829
#include <cmath>
2930

3031
#ifdef USE_OPENGL_ES
@@ -68,7 +69,7 @@ const VisPluginInfo *get_plugin_info ()
6869
info.author = N_("Original by: Pierre Tardy <tardyp@free.fr>, Port by: Dennis Smit <ds@nerds-incorporated.org>");
6970
info.version = "0.1";
7071
info.about = N_("Libvisual Dancing Particles plugin");
71-
info.help = N_("This plugin shows dancing particles");
72+
info.help = N_("This plugin shows dancing particles");
7273
info.license = VISUAL_PLUGIN_LICENSE_GPL,
7374

7475
info.init = lv_dancingparticles_init;
@@ -107,6 +108,8 @@ int lv_dancingparticles_init (VisPluginData *plugin)
107108

108109
build_sqrt_table ();
109110

111+
initFontRasterizer();
112+
110113
init_gl ();
111114

112115
return true;
@@ -116,6 +119,8 @@ void lv_dancingparticles_cleanup (VisPluginData *plugin)
116119
{
117120
auto priv = static_cast<DancingParticlesPrivate*> (visual_plugin_get_private (plugin));
118121

122+
destroyFontRasterizer();
123+
119124
visual_mem_free (priv);
120125
}
121126

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
#include "font.h"
2+
#include "etoile.h"
3+
#include "libvisual/lv_log.h"
4+
#include "libvisual/lv_video.h"
5+
#include <cassert>
6+
#include <cstring>
7+
#include <algorithm>
8+
#include <limits>
9+
#include <memory>
10+
#include <vector>
11+
12+
#include <ft2build.h>
13+
#include FT_FREETYPE_H
14+
#include FT_GLYPH_H
15+
16+
inline const char *fontPath = DATA_DIR "/font.pfb";
17+
inline const int fontSizePixels = 25;
18+
19+
inline FT_Library ftLibrary = nullptr;
20+
inline FT_Face ftFace = nullptr;
21+
22+
struct Glyph
23+
{
24+
FT_Vector pos;
25+
FT_Glyph ftGlyph;
26+
27+
Glyph(FT_Vector pos_, FT_Glyph ftGlyph_)
28+
: pos {pos_},
29+
ftGlyph {ftGlyph_}
30+
{}
31+
32+
Glyph(Glyph const&) = delete;
33+
34+
Glyph& operator=(Glyph const&) = delete;
35+
36+
~Glyph()
37+
{
38+
if (ftGlyph) {
39+
FT_Done_Glyph(ftGlyph);
40+
}
41+
}
42+
};
43+
44+
VisVideo *rasteriseText(FT_Face face, const string &text);
45+
void generateTextParticles(VisVideo* bitmap);
46+
47+
bool initFontRasterizer()
48+
{
49+
FT_Error error = FT_Init_FreeType(&ftLibrary);
50+
if(error)
51+
{
52+
visual_log(VISUAL_LOG_WARNING, "Unable to initialize FreeType2.");
53+
return false;
54+
}
55+
56+
error = FT_New_Face(ftLibrary, fontPath, 0, &ftFace);
57+
if(error)
58+
{
59+
visual_log(VISUAL_LOG_WARNING, "Unable to load font (%s).", fontPath);
60+
return false;
61+
}
62+
63+
error = FT_Set_Pixel_Sizes(ftFace, 0, fontSizePixels);
64+
if(error)
65+
{
66+
visual_log(VISUAL_LOG_WARNING, "Unable to load font at size (%d px).", fontSizePixels);
67+
return false;
68+
}
69+
70+
return true;
71+
}
72+
73+
void destroyFontRasterizer()
74+
{
75+
if (ftLibrary)
76+
{
77+
FT_Done_FreeType(ftLibrary);
78+
ftLibrary = nullptr;
79+
80+
// Already released by FT_Done_FreeType().
81+
ftFace = nullptr;
82+
}
83+
}
84+
85+
void loadString(const char *str)
86+
{
87+
if(!ftLibrary || !ftFace)
88+
return;
89+
90+
int length = std::min(int(std::strlen(str)), ptsNum/50);
91+
92+
auto bitmap = rasteriseText(ftFace, std::string(str, length));
93+
generateTextParticles(bitmap);
94+
}
95+
96+
void generateTextParticles(VisVideo* bitmap)
97+
{
98+
const auto pixels = static_cast<uint8_t const*>(visual_video_get_pixels(bitmap));
99+
100+
const int width = visual_video_get_width(bitmap);
101+
const int height = visual_video_get_height(bitmap);
102+
const int pitch = visual_video_get_pitch(bitmap);
103+
104+
unsigned int totalC = 0;
105+
106+
for(int y = 0; y < height ; y++)
107+
{
108+
for(int x = 0; x < width ; x++)
109+
{
110+
totalC += pixels[y*pitch+x];
111+
}
112+
}
113+
114+
numCenters = numCenters2 = ptsNum;
115+
116+
unsigned int particleCount = 0;
117+
unsigned int accumulatedC = 0;
118+
119+
for(int y = 0; y < height ; y++)
120+
{
121+
for(int x = 0; x < width ; x++)
122+
{
123+
unsigned char const c = pixels[y*pitch+x];
124+
125+
if(c != 0)
126+
{
127+
accumulatedC += c;
128+
const int countToReach = static_cast<int>(static_cast<double>(accumulatedC)/totalC * numCenters);
129+
const int allocCount = countToReach - particleCount;
130+
131+
for(int a = 0; a < allocCount; a++)
132+
{
133+
Centers[particleCount] = FloatPoint {static_cast<float>(x*4-width*2), static_cast<float>(height*8-y*16+a*4), 0.0f};
134+
particleCount++;
135+
}
136+
}
137+
}
138+
}
139+
140+
assert(static_cast<int>(particleCount) == ptsNum);
141+
}
142+
143+
VisVideo *rasteriseText(FT_Face face, const std::string &text)
144+
{
145+
// This is a very simple FT2 text rasteriser that supports languages with a one-one character to glyph mapping.
146+
// Anything else would require a text shaping engine like Harfbuzz.
147+
148+
FT_Error error;
149+
150+
const bool useKerning = FT_HAS_KERNING(face);
151+
152+
std::size_t charCount = text.length();
153+
154+
std::vector<std::unique_ptr<Glyph>> glyphs;
155+
glyphs.reserve(charCount);
156+
157+
auto ftGlyphSlot = face->glyph;
158+
159+
// Convert text string into a series of glyphs.
160+
161+
int penX = 0;
162+
int penY = 0;
163+
FT_UInt prevFtGlyphIndex = 0;
164+
165+
for(auto text_char : text)
166+
{
167+
const auto ftGlyphIndex = FT_Get_Char_Index(face, text_char);
168+
169+
error = FT_Load_Glyph(face, ftGlyphIndex, FT_LOAD_DEFAULT);
170+
if(error)
171+
continue;
172+
173+
FT_Glyph ftGlyph = nullptr;
174+
175+
error = FT_Get_Glyph(ftGlyphSlot, &ftGlyph);
176+
if(error)
177+
continue;
178+
179+
if(useKerning && prevFtGlyphIndex && ftGlyphIndex)
180+
{
181+
FT_Vector delta;
182+
FT_Get_Kerning(face, prevFtGlyphIndex, ftGlyphIndex, FT_KERNING_DEFAULT, &delta);
183+
penX += delta.x >> 6;
184+
}
185+
186+
auto glyph = std::make_unique<Glyph> (FT_Vector { penX, penY }, ftGlyph);
187+
glyphs.push_back(std::move(glyph));
188+
189+
penX += ftGlyphSlot->advance.x >> 6;
190+
191+
prevFtGlyphIndex = ftGlyphIndex;
192+
}
193+
194+
// Calculate bounding box of text rasterization.
195+
//
196+
// NOTE: According to the FT2 tutorial, FT_Glyph_Get_CBox() does not account for anti-aliasing.
197+
// However, the returned bounds appear accurate for our purpose.
198+
199+
FT_BBox textBBox
200+
{
201+
std::numeric_limits<int>::max(),
202+
std::numeric_limits<int>::max(),
203+
std::numeric_limits<int>::min(),
204+
std::numeric_limits<int>::min()
205+
};
206+
207+
for(auto const& glyph : glyphs)
208+
{
209+
FT_BBox glyphBBox { 0, 0, 0, 0 };
210+
FT_Glyph_Get_CBox(glyph.get()->ftGlyph, ft_glyph_bbox_pixels, &glyphBBox);
211+
212+
glyphBBox.xMin += glyph.get()->pos.x;
213+
glyphBBox.yMin += glyph.get()->pos.y;
214+
glyphBBox.xMax += glyph.get()->pos.x;
215+
glyphBBox.yMax += glyph.get()->pos.y;
216+
217+
textBBox.xMin = std::min(textBBox.xMin, glyphBBox.xMin);
218+
textBBox.yMin = std::min(textBBox.yMin, glyphBBox.yMin);
219+
textBBox.xMax = std::max(textBBox.xMax, glyphBBox.xMax);
220+
textBBox.yMax = std::max(textBBox.yMax, glyphBBox.yMax);
221+
}
222+
223+
if(textBBox.xMin > textBBox.xMax)
224+
return nullptr;
225+
226+
// Rasterize text.
227+
228+
const int textWidth = textBBox.xMax - textBBox.xMin;
229+
const int textHeight = textBBox.yMax - textBBox.yMin;
230+
231+
auto textBitmap = visual_video_new_with_buffer(textWidth, textHeight, VISUAL_VIDEO_DEPTH_8BIT);
232+
233+
for(auto const& glyph : glyphs)
234+
{
235+
const FT_Vector pen
236+
{
237+
(glyph.get()->pos.x - textBBox.xMin) << 6,
238+
(glyph.get()->pos.y - textBBox.yMin) << 6
239+
};
240+
241+
auto ftGlyph = glyph.get()->ftGlyph;
242+
243+
error = FT_Glyph_To_Bitmap(&ftGlyph, FT_RENDER_MODE_NORMAL, &pen, 0);
244+
if(!error)
245+
{
246+
auto bitmapFtGlyph = reinterpret_cast<FT_BitmapGlyph>(ftGlyph);
247+
auto bitmap = bitmapFtGlyph->bitmap;
248+
249+
auto source = visual_video_new_wrap_buffer(bitmap.buffer,
250+
false,
251+
bitmap.width,
252+
bitmap.rows,
253+
VISUAL_VIDEO_DEPTH_8BIT,
254+
bitmap.pitch);
255+
256+
visual_video_blit(textBitmap, source, bitmapFtGlyph->left, textHeight - bitmapFtGlyph->top, 255);
257+
258+
visual_video_unref(source);
259+
260+
FT_Done_Glyph(ftGlyph);
261+
}
262+
}
263+
264+
return textBitmap;
265+
}

libvisual-plugins/plugins/actor/dancingparticles/t1font.h renamed to libvisual-plugins/plugins/actor/dancingparticles/font.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
// load Centers with a form that corresponds to str
1+
bool initFontRasterizer();
2+
3+
void destroyFontRasterizer();
24

5+
// load Centers with a form that corresponds to str
36
void loadString(const char *str);
47

libvisual-plugins/plugins/actor/dancingparticles/gl.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,11 @@ extern int mingloudness ;
8888

8989
void draw_gl(void)
9090
{
91-
static float r=0, SIZE=1;
91+
static float SIZE=1;
9292

9393
float Size =(p.size + p.sizeloudness * gloudness)/2;
9494
SIZE = (SIZE +3*Size) /4;
95-
r++;
95+
9696
glClearColor(0,0,0,0);
9797
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
9898

0 commit comments

Comments
 (0)