Adding FreeType font generation

This commit is contained in:
Matt Tuttle
2014-07-13 15:49:08 -05:00
parent 7e207a8232
commit 6d7388212a
10 changed files with 361 additions and 17 deletions

55
lime/graphics/Font.hx Normal file
View File

@@ -0,0 +1,55 @@
package lime.graphics;
import lime.utils.UInt8Array;
import lime.system.System;
typedef GlyphData = {
var image:Image;
var glyphs:Array<Dynamic>;
};
class Font {
#if (cpp || neko)
public var handle:Dynamic;
#end
public function new (fontFace:String) {
#if (cpp || neko)
handle = lime_font_load (fontFace);
#end
}
public function createImage (size:Int, glyphs:String="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^`'\"/\\&*()[]{}<>|:;_-+=?,. "):GlyphData {
#if (cpp || neko)
var data = lime_font_load_glyphs (handle, size, glyphs);
if (data == null) {
return null;
} else {
return {
glyphs: data.glyphs,
image: new Image (new UInt8Array (data.image.data), data.image.width, data.image.height, data.image.bpp)
};
}
#end
return null;
}
#if (cpp || neko)
private static var lime_font_load = System.load ("lime", "lime_font_load", 1);
private static var lime_font_load_glyphs = System.load ("lime", "lime_font_load_glyphs", 3);
#end
}

View File

@@ -21,6 +21,7 @@ class Image {
private static var __context:CanvasRenderingContext2D; private static var __context:CanvasRenderingContext2D;
#end #end
public var bpp:Int;
public var data (get, set):ImageData; public var data (get, set):ImageData;
public var height:Int; public var height:Int;
public var offsetX:Int; public var offsetX:Int;
@@ -35,7 +36,7 @@ class Image {
private var __data:ImageData; private var __data:ImageData;
public function new (src:ImageSource = null, width:Int = 0, height:Int = 0) { public function new (src:ImageSource = null, width:Int = 0, height:Int = 0, bpp:Int = 4) {
this.src = src; this.src = src;
#if (!js && !flash) #if (!js && !flash)
@@ -44,6 +45,7 @@ class Image {
this.width = textureWidth = width; this.width = textureWidth = width;
this.height = textureHeight = height; this.height = textureHeight = height;
this.bpp = bpp;
} }
@@ -100,7 +102,7 @@ class Image {
for (x in 0...width) { for (x in 0...width) {
newData.setPixel (y * textureWidth * 4 + x * 4, newData.getPixel (y * width * 4 + x * 4)); newData.setPixel (y * textureWidth + x, newData.getPixel (y * width + x));
} }
@@ -115,15 +117,15 @@ class Image {
public static function loadFromFile (path:String) { public static function loadFromFile (path:String) {
#if flash #if (cpp || neko)
throw "Can not load image from file in Flash";
#elseif (cpp || neko)
var imageData = lime_image_load (path); var imageData = lime_image_load (path);
return (imageData == null ? null : new Image (new UInt8Array (imageData.data), imageData.width, imageData.height)); return (imageData == null ? null : new Image (new UInt8Array (imageData.data), imageData.width, imageData.height, imageData.bpp));
#else
throw "Image.loadFromFile not supported on this target";
#end #end
} }

View File

@@ -10,6 +10,7 @@
<set name="LIME_CURL" value="1" /> <set name="LIME_CURL" value="1" />
<set name="LIME_JPEG" value="1" /> <set name="LIME_JPEG" value="1" />
<set name="LIME_FREETYPE" value="1" />
<!-- <set name="LIME_NEKO" value="1" if="linux" /> --> <!-- <set name="LIME_NEKO" value="1" if="linux" /> -->
<set name="LIME_OGG" value="1" /> <set name="LIME_OGG" value="1" />
<set name="LIME_OPENAL" value="1" /> <set name="LIME_OPENAL" value="1" />
@@ -37,6 +38,12 @@
</section> </section>
<section if="LIME_FREETYPE">
<compilerflag value="-I${NATIVE_TOOLKIT_PATH}/freetype/include" />
</section>
<section if="LIME_NEKO"> <section if="LIME_NEKO">
<compilerflag value="-I${NATIVE_TOOLKIT_PATH}/neko/vm/" /> <compilerflag value="-I${NATIVE_TOOLKIT_PATH}/neko/vm/" />
@@ -83,9 +90,10 @@
<file name="src/system/ios/System.mm" if="ios" /> <file name="src/system/ios/System.mm" if="ios" />
<file name="src/app/UpdateEvent.cpp" /> <file name="src/app/UpdateEvent.cpp" />
<file name="src/graphics/Font.cpp" if="LIME_FREETYPE" />
<file name="src/graphics/Image.cpp" /> <file name="src/graphics/Image.cpp" />
<file name="src/graphics/JPEG.cpp" /> <file name="src/graphics/JPEG.cpp" if="LIME_JPEG" />
<file name="src/graphics/PNG.cpp" /> <file name="src/graphics/PNG.cpp" if="LIME_PNG" />
<file name="src/graphics/RenderEvent.cpp" /> <file name="src/graphics/RenderEvent.cpp" />
<file name="src/system/System.cpp" /> <file name="src/system/System.cpp" />
<file name="src/ui/KeyEvent.cpp" /> <file name="src/ui/KeyEvent.cpp" />
@@ -97,6 +105,7 @@
</files> </files>
<include name="lib/curl/files.xml" /> <include name="lib/curl/files.xml" />
<include name="lib/freetype/files.xml" />
<include name="lib/jpeg/files.xml" /> <include name="lib/jpeg/files.xml" />
<include name="lib/neko/files.xml" /> <include name="lib/neko/files.xml" />
<include name="lib/ogg/files.xml" /> <include name="lib/ogg/files.xml" />
@@ -116,6 +125,7 @@
<files id="native-toolkit-curl" if="LIME_CURL" /> <files id="native-toolkit-curl" if="LIME_CURL" />
<files id="native-toolkit-jpeg" if="LIME_JPEG" /> <files id="native-toolkit-jpeg" if="LIME_JPEG" />
<files id="native-toolkit-freetype" if="LIME_JPEG" />
<files id="native-toolkit-neko" if="LIME_NEKO" /> <files id="native-toolkit-neko" if="LIME_NEKO" />
<files id="native-toolkit-ogg" if="LIME_OGG" /> <files id="native-toolkit-ogg" if="LIME_OGG" />
<files id="native-toolkit-openal" if="LIME_OPENAL" unless="mac || iphone" /> <files id="native-toolkit-openal" if="LIME_OPENAL" unless="mac || iphone" />

View File

@@ -0,0 +1,34 @@
#ifndef LIME_GRAPHICS_FONT_H
#define LIME_GRAPHICS_FONT_H
#include <hx/CFFI.h>
#include <list>
#include <graphics/Image.h>
#include <ft2build.h>
#include FT_FREETYPE_H
namespace lime {
class Font {
public:
Font (const char *fontFace);
value LoadGlyphs (int size, const char *glyphs, Image *image);
private:
FT_Face face;
};
}
#endif

View File

@@ -17,10 +17,13 @@ namespace lime {
Image (); Image ();
~Image (); ~Image ();
void Resize (int width, int height, int bpp = 4);
void Blit (const unsigned char *data, int x, int y, int width, int height);
value Value (); value Value ();
int width; int width;
int height; int height;
int bpp; // bytes per pixel
ByteArray *data; ByteArray *data;

View File

@@ -10,6 +10,7 @@
#include <hx/CFFI.h> #include <hx/CFFI.h>
#include <app/Application.h> #include <app/Application.h>
#include <app/UpdateEvent.h> #include <app/UpdateEvent.h>
#include <graphics/Font.h>
#include <graphics/Image.h> #include <graphics/Image.h>
#include <graphics/PNG.h> #include <graphics/PNG.h>
#include <graphics/JPEG.h> #include <graphics/JPEG.h>
@@ -73,6 +74,26 @@ namespace lime {
} }
value lime_font_load (value fontFace) {
Font *font = new Font (val_string (fontFace));
return alloc_float ((intptr_t)font);
}
value lime_font_load_glyphs (value fontHandle, value size, value glyphs) {
Image image;
Font *font = (Font*)(intptr_t)val_float (fontHandle);
value data = alloc_empty_object ();
alloc_field (data, val_id ("glyphs"), font->LoadGlyphs (val_int (size), val_string (glyphs), &image));
alloc_field (data, val_id ("image"), image.Value ());
return data;
}
value lime_key_event_manager_register (value callback, value eventObject) { value lime_key_event_manager_register (value callback, value eventObject) {
KeyEvent::callback = new AutoGCRoot (callback); KeyEvent::callback = new AutoGCRoot (callback);
@@ -216,6 +237,8 @@ namespace lime {
DEFINE_PRIM (lime_application_exec, 1); DEFINE_PRIM (lime_application_exec, 1);
DEFINE_PRIM (lime_application_get_ticks, 0); DEFINE_PRIM (lime_application_get_ticks, 0);
DEFINE_PRIM (lime_image_load, 1); DEFINE_PRIM (lime_image_load, 1);
DEFINE_PRIM (lime_font_load, 1);
DEFINE_PRIM (lime_font_load_glyphs, 3);
DEFINE_PRIM (lime_key_event_manager_register, 2); DEFINE_PRIM (lime_key_event_manager_register, 2);
DEFINE_PRIM (lime_lzma_encode, 1); DEFINE_PRIM (lime_lzma_encode, 1);
DEFINE_PRIM (lime_lzma_decode, 1); DEFINE_PRIM (lime_lzma_decode, 1);

View File

@@ -0,0 +1,185 @@
#include <graphics/Font.h>
typedef struct {
char c;
FT_UInt index;
FT_Pos height;
} GlyphInfo;
namespace lime {
static int id_x;
static int id_y;
static int id_width;
static int id_height;
static int id_char;
static bool init = false;
bool CompareGlyph (const GlyphInfo &a, const GlyphInfo &b) {
return a.height > b.height;
}
Font::Font (const char *fontFace) {
int error;
FT_Library library;
error = FT_Init_FreeType (&library);
if (error) {
printf ("Could not initialize FreeType\n");
}
error = FT_New_Face (library, fontFace, 0, &face);
if (error == FT_Err_Unknown_File_Format) {
printf ("Invalid font type\n");
} else if (error) {
printf ("Failed to load font face %s\n", fontFace);
}
}
value Font::LoadGlyphs (int size, const char *glyphs, Image *image) {
if (!init) {
id_width = val_id ("width");
id_height = val_id ("height");
id_x = val_id ("x");
id_y = val_id ("y");
id_char = val_id ("char");
init = true;
}
std::list<GlyphInfo> glyphList;
int numGlyphs = strlen (glyphs);
char c[2] = " ";
FT_Set_Pixel_Sizes (face, 0, size);
for (int i = 0; i < numGlyphs; i++) {
GlyphInfo info;
info.c = glyphs[i];
info.index = FT_Get_Char_Index (face, info.c);
if (FT_Load_Glyph (face, info.index, FT_LOAD_DEFAULT) != 0) continue;
info.height = face->glyph->metrics.height;
glyphList.push_back(info);
}
glyphList.sort(CompareGlyph);
image->Resize(128, 128, 1);
int x = 0, y = 0, maxRows = 0;
unsigned char *bytes = image->data->Bytes ();
value rects = alloc_array (glyphList.size());
int rectsIndex = 0;
for (std::list<GlyphInfo>::iterator it = glyphList.begin(); it != glyphList.end(); it++) {
FT_Load_Glyph (face, (*it).index, FT_LOAD_DEFAULT);
c[0] = (*it).c;
if (FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL) != 0) continue;
FT_Bitmap bitmap = face->glyph->bitmap;
if (x + bitmap.width > image->width) {
y += maxRows + 1;
x = maxRows = 0;
}
if (y + bitmap.rows > image->height) {
if (image->width < image->height) {
image->width *= 2;
} else {
image->height *= 2;
}
image->Resize (image->width, image->height, 1);
rectsIndex = 0;
it = glyphList.begin ();
it--;
x = y = maxRows = 0;
continue;
}
if (image->bpp == 1) {
image->Blit (bitmap.buffer, x, y, bitmap.width, bitmap.rows);
} else {
for (int row = 0; row < bitmap.rows; row++) {
unsigned char *out = &bytes[((row + y) * image->width + x) * image->bpp];
const unsigned char *line = &bitmap.buffer[row * bitmap.width]; // scanline
const unsigned char *const end = line + bitmap.width;
while (line != end) {
*out++ = 0xFF;
*out++ = 0xFF;
*out++ = 0xFF;
*out++ = *line;
line++;
}
}
}
value v = alloc_empty_object ();
alloc_field (v, id_x, alloc_int (x));
alloc_field (v, id_y, alloc_int (y));
alloc_field (v, id_width, alloc_int (bitmap.width));
alloc_field (v, id_height, alloc_int (bitmap.rows));
alloc_field (v, id_char, alloc_string (c));
val_array_set_i (rects, rectsIndex++, v);
x += bitmap.width + 1;
if (bitmap.rows > maxRows) {
maxRows = bitmap.rows;
}
}
return rects;
}
}

View File

@@ -7,6 +7,7 @@ namespace lime {
static int id_data; static int id_data;
static int id_height; static int id_height;
static int id_width; static int id_width;
static int id_bpp;
static bool init = false; static bool init = false;
@@ -14,6 +15,7 @@ namespace lime {
width = 0; width = 0;
height = 0; height = 0;
bpp = 4;
data = 0; data = 0;
} }
@@ -26,6 +28,37 @@ namespace lime {
} }
void Image::Resize (int width, int height, int bpp) {
this->bpp = bpp;
this->width = width;
this->height = height;
if (this->data) delete this->data;
this->data = new ByteArray (width * height * bpp);
}
void Image::Blit (const unsigned char *data, int x, int y, int width, int height) {
if (x < 0 || x + width > this->width ||
y < 0 || y + height > this->height) {
return;
}
unsigned char *bytes = this->data->Bytes ();
for (int i = 0; i < height; i++) {
memcpy (&bytes[(i + y) * this->width + x], &data[i * width], width * bpp);
}
}
value Image::Value() { value Image::Value() {
if (!init) { if (!init) {
@@ -33,6 +66,7 @@ namespace lime {
id_width = val_id ("width"); id_width = val_id ("width");
id_height = val_id ("height"); id_height = val_id ("height");
id_data = val_id ("data"); id_data = val_id ("data");
id_bpp = val_id ("bpp");
init = true; init = true;
} }
@@ -40,6 +74,7 @@ namespace lime {
mValue = alloc_empty_object (); mValue = alloc_empty_object ();
alloc_field (mValue, id_width, alloc_int (width)); alloc_field (mValue, id_width, alloc_int (width));
alloc_field (mValue, id_height, alloc_int (height)); alloc_field (mValue, id_height, alloc_int (height));
alloc_field (mValue, id_bpp, alloc_int (bpp));
alloc_field (mValue, id_data, data->mValue); alloc_field (mValue, id_data, data->mValue);
return mValue; return mValue;

View File

@@ -28,10 +28,8 @@ namespace lime {
if (jpeg_read_header (&cinfo, TRUE) == JPEG_HEADER_OK) { if (jpeg_read_header (&cinfo, TRUE) == JPEG_HEADER_OK) {
jpeg_start_decompress (&cinfo); jpeg_start_decompress (&cinfo);
image->width = cinfo.output_width;
image->height = cinfo.output_height;
int components = cinfo.num_components; int components = cinfo.num_components;
image->data = new ByteArray (image->width * image->height * 4); image->Resize(cinfo.output_width, cinfo.output_height);
unsigned char *bytes = image->data->Bytes (); unsigned char *bytes = image->data->Bytes ();
unsigned char *scanline = new unsigned char [image->width * image->height * components]; unsigned char *scanline = new unsigned char [image->width * image->height * components];

View File

@@ -72,10 +72,9 @@ namespace lime {
if (bit_depth == 16) if (bit_depth == 16)
png_set_strip_16 (png_ptr); png_set_strip_16 (png_ptr);
const unsigned int stride = width * 4; int bpp = 4;
image->width = width; const unsigned int stride = width * bpp;
image->height = height; image->Resize(width, height, bpp);
image->data = new ByteArray (height * stride);
png_bytepp row_ptrs = new png_bytep[height]; png_bytepp row_ptrs = new png_bytep[height];
unsigned char *bytes = image->data->Bytes (); unsigned char *bytes = image->data->Bytes ();