summaryrefslogtreecommitdiff
path: root/src/ftgl/FTFont/FTBufferFont.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ftgl/FTFont/FTBufferFont.cpp')
-rw-r--r--src/ftgl/FTFont/FTBufferFont.cpp349
1 files changed, 349 insertions, 0 deletions
diff --git a/src/ftgl/FTFont/FTBufferFont.cpp b/src/ftgl/FTFont/FTBufferFont.cpp
new file mode 100644
index 0000000..e48e2ab
--- /dev/null
+++ b/src/ftgl/FTFont/FTBufferFont.cpp
@@ -0,0 +1,349 @@
+/*
+ * FTGL - OpenGL font library
+ *
+ * Copyright (c) 2008 Sam Hocevar <sam@zoy.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <wchar.h>
+
+#include "FTGL/ftgl.h"
+
+#include "FTInternals.h"
+#include "FTBufferFontImpl.h"
+
+
+//
+// FTBufferFont
+//
+
+
+FTBufferFont::FTBufferFont(char const *fontFilePath) :
+ FTFont(new FTBufferFontImpl(this, fontFilePath))
+{}
+
+
+FTBufferFont::FTBufferFont(unsigned char const *pBufferBytes,
+ size_t bufferSizeInBytes) :
+ FTFont(new FTBufferFontImpl(this, pBufferBytes, bufferSizeInBytes))
+{}
+
+
+FTBufferFont::~FTBufferFont()
+{}
+
+
+FTGlyph* FTBufferFont::MakeGlyph(FT_GlyphSlot ftGlyph)
+{
+ FTBufferFontImpl *myimpl = dynamic_cast<FTBufferFontImpl *>(impl);
+ if(!myimpl)
+ {
+ return NULL;
+ }
+
+ return myimpl->MakeGlyphImpl(ftGlyph);
+}
+
+
+//
+// FTBufferFontImpl
+//
+
+
+FTBufferFontImpl::FTBufferFontImpl(FTFont *ftFont, const char* fontFilePath) :
+ FTFontImpl(ftFont, fontFilePath),
+ buffer(new FTBuffer())
+{
+ load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP;
+
+ glGenTextures(BUFFER_CACHE_SIZE, idCache);
+
+ for(int i = 0; i < BUFFER_CACHE_SIZE; i++)
+ {
+ stringCache[i] = NULL;
+ glBindTexture(GL_TEXTURE_2D, idCache[i]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ }
+
+ lastString = 0;
+}
+
+
+FTBufferFontImpl::FTBufferFontImpl(FTFont *ftFont,
+ const unsigned char *pBufferBytes,
+ size_t bufferSizeInBytes) :
+ FTFontImpl(ftFont, pBufferBytes, bufferSizeInBytes),
+ buffer(new FTBuffer())
+{
+ load_flags = FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP;
+
+ glGenTextures(BUFFER_CACHE_SIZE, idCache);
+
+ for(int i = 0; i < BUFFER_CACHE_SIZE; i++)
+ {
+ stringCache[i] = NULL;
+ glBindTexture(GL_TEXTURE_2D, idCache[i]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ }
+
+ lastString = 0;
+}
+
+
+FTBufferFontImpl::~FTBufferFontImpl()
+{
+ glDeleteTextures(BUFFER_CACHE_SIZE, idCache);
+
+ for(int i = 0; i < BUFFER_CACHE_SIZE; i++)
+ {
+ if(stringCache[i])
+ {
+ free(stringCache[i]);
+ }
+ }
+
+ delete buffer;
+}
+
+
+FTGlyph* FTBufferFontImpl::MakeGlyphImpl(FT_GlyphSlot ftGlyph)
+{
+ return new FTBufferGlyph(ftGlyph, buffer);
+}
+
+
+bool FTBufferFontImpl::FaceSize(const unsigned int size,
+ const unsigned int res)
+{
+ for(int i = 0; i < BUFFER_CACHE_SIZE; i++)
+ {
+ if(stringCache[i])
+ {
+ free(stringCache[i]);
+ stringCache[i] = NULL;
+ }
+ }
+
+ return FTFontImpl::FaceSize(size, res);
+}
+
+
+static inline GLuint NextPowerOf2(GLuint in)
+{
+ in -= 1;
+
+ in |= in >> 16;
+ in |= in >> 8;
+ in |= in >> 4;
+ in |= in >> 2;
+ in |= in >> 1;
+
+ return in + 1;
+}
+
+
+inline int StringCompare(void const *a, char const *b, int len)
+{
+ return len < 0 ? strcmp((char const *)a, b)
+ : strncmp((char const *)a, b, len);
+}
+
+
+inline int StringCompare(void const *a, wchar_t const *b, int len)
+{
+ return len < 0 ? wcscmp((wchar_t const *)a, b)
+ : wcsncmp((wchar_t const *)a, b, len);
+}
+
+
+inline char *StringCopy(char const *s, int len)
+{
+ if(len < 0)
+ {
+ return strdup(s);
+ }
+ else
+ {
+#ifdef HAVE_STRNDUP
+ return strndup(s, len);
+#else
+ char *s2 = (char*)malloc(len + 1);
+ memcpy(s2, s, len);
+ s2[len] = 0;
+ return s2;
+#endif
+ }
+}
+
+
+inline wchar_t *StringCopy(wchar_t const *s, int len)
+{
+ if(len < 0)
+ {
+#if defined HAVE_WCSDUP
+ return wcsdup(s);
+#else
+ len = (int)wcslen(s);
+#endif
+ }
+
+ wchar_t *s2 = (wchar_t *)malloc((len + 1) * sizeof(wchar_t));
+ memcpy(s2, s, len * sizeof(wchar_t));
+ s2[len] = 0;
+ return s2;
+}
+
+
+template <typename T>
+inline FTPoint FTBufferFontImpl::RenderI(const T* string, const int len,
+ FTPoint position, FTPoint spacing,
+ int renderMode)
+{
+ const float padding = 3.0f;
+ int width, height, texWidth, texHeight;
+ int cacheIndex = -1;
+ bool inCache = false;
+
+ // Protect blending functions, GL_BLEND and GL_TEXTURE_2D
+ glPushAttrib(GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT);
+
+ // Protect glPixelStorei() calls
+ glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
+
+ glEnable(GL_BLEND);
+ glEnable(GL_TEXTURE_2D);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // GL_ONE
+
+ // Search whether the string is already in a texture we uploaded
+ for(int n = 0; n < BUFFER_CACHE_SIZE; n++)
+ {
+ int i = (lastString + n + BUFFER_CACHE_SIZE) % BUFFER_CACHE_SIZE;
+
+ if(stringCache[i] && !StringCompare(stringCache[i], string, len))
+ {
+ cacheIndex = i;
+ inCache = true;
+ break;
+ }
+ }
+
+ // If the string was not found, we need to put it in the cache and compute
+ // its new bounding box.
+ if(!inCache)
+ {
+ // FIXME: this cache is not very efficient. We should first expire
+ // strings that are not used very often.
+ cacheIndex = lastString;
+ lastString = (lastString + 1) % BUFFER_CACHE_SIZE;
+
+ if(stringCache[cacheIndex])
+ {
+ free(stringCache[cacheIndex]);
+ }
+ // FIXME: only the first N bytes are copied; we want the first N chars.
+ stringCache[cacheIndex] = StringCopy(string, len);
+ bboxCache[cacheIndex] = BBox(string, len, FTPoint(), spacing);
+ }
+
+ FTBBox bbox = bboxCache[cacheIndex];
+
+ width = static_cast<int>(bbox.Upper().X() - bbox.Lower().X()
+ + padding + padding + 0.5);
+ height = static_cast<int>(bbox.Upper().Y() - bbox.Lower().Y()
+ + padding + padding + 0.5);
+
+ texWidth = NextPowerOf2(width);
+ texHeight = NextPowerOf2(height);
+
+ glBindTexture(GL_TEXTURE_2D, idCache[cacheIndex]);
+
+ // If the string was not found, we need to render the text in a new
+ // texture buffer, then upload it to the OpenGL layer.
+ if(!inCache)
+ {
+ buffer->Size(texWidth, texHeight);
+ buffer->Pos(FTPoint(padding, padding) - bbox.Lower());
+
+ advanceCache[cacheIndex] =
+ FTFontImpl::Render(string, len, FTPoint(), spacing, renderMode);
+
+ glBindTexture(GL_TEXTURE_2D, idCache[cacheIndex]);
+
+ glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ /* TODO: use glTexSubImage2D later? */
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texWidth, texHeight, 0,
+ GL_ALPHA, GL_UNSIGNED_BYTE, (GLvoid *)buffer->Pixels());
+
+ buffer->Size(0, 0);
+ }
+
+ FTPoint low = position + bbox.Lower();
+ FTPoint up = position + bbox.Upper();
+
+ glBegin(GL_QUADS);
+ glNormal3f(0.0f, 0.0f, 1.0f);
+ glTexCoord2f(padding / texWidth,
+ (texHeight - height + padding) / texHeight);
+ glVertex2f(low.Xf(), up.Yf());
+ glTexCoord2f(padding / texWidth,
+ (texHeight - padding) / texHeight);
+ glVertex2f(low.Xf(), low.Yf());
+ glTexCoord2f((width - padding) / texWidth,
+ (texHeight - padding) / texHeight);
+ glVertex2f(up.Xf(), low.Yf());
+ glTexCoord2f((width - padding) / texWidth,
+ (texHeight - height + padding) / texHeight);
+ glVertex2f(up.Xf(), up.Yf());
+ glEnd();
+
+ glPopClientAttrib();
+ glPopAttrib();
+
+ return position + advanceCache[cacheIndex];
+}
+
+
+FTPoint FTBufferFontImpl::Render(const char * string, const int len,
+ FTPoint position, FTPoint spacing,
+ int renderMode)
+{
+ return RenderI(string, len, position, spacing, renderMode);
+}
+
+
+FTPoint FTBufferFontImpl::Render(const wchar_t * string, const int len,
+ FTPoint position, FTPoint spacing,
+ int renderMode)
+{
+ return RenderI(string, len, position, spacing, renderMode);
+}
+