float GetStringWidth(string str) { TGlyph prevGlyph = null; float width = 0f; foreach (char c in str) { int charCode = (int)c; TGlyph glyph = Font.Glyphs.Get(charCode); if (glyph == null) { continue; } float kerning = 0f; if (prevGlyph != null) { kerning = prevGlyph.GetKerning(charCode); } width += glyph.xAdvance * Size + Tracking + kerning; prevGlyph = glyph; } return(width); }
void compute_string_bbox(out ANT_BBox abbox) { ANT_BBox bbox = new ANT_BBox(); ANT_BBox glyph_bbox = new ANT_BBox(); // initialize string bbox to "empty" values bbox.xMin = bbox.yMin = 32000; bbox.xMax = bbox.yMax = -32000; // for each glyph image, compute its bounding box, // translate it, and grow the string bbox for (int n = 0; n < num_glyphs; n++) { TGlyph glyph = glyphs[n]; ANT.ANT_Glyph_Get_CBox(glyph.image, ANT_Glyph_BBox_Mode.ANT_GLYPH_BBOX_PIXELS, glyph_bbox); glyph_bbox.xMin += glyph.pos.x; glyph_bbox.xMax += glyph.pos.x; glyph_bbox.yMin += glyph.pos.y; glyph_bbox.yMax += glyph.pos.y; if (glyph_bbox.xMin < bbox.xMin) { bbox.xMin = glyph_bbox.xMin; } if (glyph_bbox.yMin < bbox.yMin) { bbox.yMin = glyph_bbox.yMin; } if (glyph_bbox.xMax > bbox.xMax) { bbox.xMax = glyph_bbox.xMax; } if (glyph_bbox.yMax > bbox.yMax) { bbox.yMax = glyph_bbox.yMax; } } // check that we really grew the string bbox if (bbox.xMin > bbox.xMax) { bbox.xMin = 0; bbox.yMin = 0; bbox.xMax = 0; bbox.yMax = 0; } // return string bbox abbox = bbox; }
float BlitString(string str, float cursorX, float cursorY, List <int> vertexPointers = null) { TGlyph prevGlyph = null; foreach (char c in str) { int charCode = (int)c; TGlyph glyph = Font.Glyphs.Get(charCode); if (glyph == null) { continue; } if (charCode == 32) { cursorX += glyph.xAdvance * Size + Tracking; continue; } float kerning = 0f; if (prevGlyph != null) { kerning = prevGlyph.GetKerning(charCode); } if (vertexPointers != null) { vertexPointers.Add(m_Vertices.Count); } BlitQuad( new Rect( cursorX + glyph.xOffset * Size + kerning, cursorY + glyph.yOffset * Size, glyph.rect.width * Size, glyph.rect.height * Size ), glyph ); cursorX += glyph.xAdvance * Size + Tracking + kerning; prevGlyph = glyph; } if (cursorX > Width) { Width = cursorX; } return(cursorX); }
float GetStringWidth(string str) { TGlyph prevGlyph = null; bool inControlCode = false; float width = 0f; foreach (char c in str) { int charCode = (int)c; if (inControlCode) { inControlCode = false; if (charCode >= 48 && charCode <= 57) // 0-9 { continue; } } else { if (charCode == 92) // Backslash { inControlCode = true; continue; } } TGlyph glyph = Font.Glyphs.Get(charCode); if (glyph == null) { continue; } float kerning = 0f; if (prevGlyph != null) { kerning = prevGlyph.GetKerning(charCode) * Size; } width += glyph.xAdvance * Size + Tracking + kerning; prevGlyph = glyph; } return(width); }
void parseKernings(XmlNode kerningsNode) { foreach (XmlNode node in kerningsNode.ChildNodes) { int first = Convert.ToInt32(attribute(node, "first")); int second = Convert.ToInt32(attribute(node, "second")); float amount = Convert.ToSingle(attribute(node, "amount")) / Atlas.width * HScale; TGlyph glyph = Glyphs.Get(first); KerningDictionary kerning = glyph.kerning; if (kerning == null) { kerning = new KerningDictionary(); glyph.kerning = kerning; } kerning.Set(second, amount); KerningPairs++; } }
void BlitQuad(Rect quad, TGlyph glyph) { int index = m_Vertices.Count; Rect uvs = glyph.rect; m_Vertices.Add(new Vector3(quad.x, -quad.y, 0f)); m_Vertices.Add(new Vector3(quad.x + quad.width, -quad.y, 0f)); m_Vertices.Add(new Vector3(quad.x + quad.width, -quad.y - quad.height, 0f)); m_Vertices.Add(new Vector3(quad.x, -quad.y - quad.height, 0f)); m_UVs.Add(new Vector2(uvs.x / Font.HScale, 1 - uvs.y / Font.VScale)); m_UVs.Add(new Vector2((uvs.x + uvs.width) / Font.HScale, 1 - uvs.y / Font.VScale)); m_UVs.Add(new Vector2((uvs.x + uvs.width) / Font.HScale, 1 - (uvs.y + uvs.height) / Font.VScale)); m_UVs.Add(new Vector2(uvs.x / Font.HScale, 1 - (uvs.y + uvs.height) / Font.VScale)); switch (FillMode) { case TFillMode.SingleColor: m_Colors.Add(ColorTopLeft); m_Colors.Add(ColorTopLeft); m_Colors.Add(ColorTopLeft); m_Colors.Add(ColorTopLeft); break; case TFillMode.VerticalGradient: m_Colors.Add(ColorTopLeft); m_Colors.Add(ColorTopLeft); m_Colors.Add(ColorBottomLeft); m_Colors.Add(ColorBottomLeft); break; case TFillMode.HorizontalGradient: m_Colors.Add(ColorTopLeft); m_Colors.Add(ColorBottomLeft); m_Colors.Add(ColorBottomLeft); m_Colors.Add(ColorTopLeft); break; case TFillMode.QuadGradient: m_Colors.Add(ColorTopLeft); m_Colors.Add(ColorTopRight); m_Colors.Add(ColorBottomRight); m_Colors.Add(ColorBottomLeft); break; case TFillMode.StretchedTexture: m_UVs2.Add(new Vector2(0f, 1f)); m_UVs2.Add(new Vector2(1f, 1f)); m_UVs2.Add(new Vector2(1f, 0f)); m_UVs2.Add(new Vector2(0f, 0f)); break; case TFillMode.ProjectedTexture: float h = uvs.height / Font.LineHeight; float w = uvs.width / Font.LineHeight; m_UVs2.Add(new Vector2(glyph.xOffset, h - glyph.yOffset)); m_UVs2.Add(new Vector2(w - glyph.xOffset, h - glyph.yOffset)); m_UVs2.Add(new Vector2(w - glyph.xOffset, glyph.yOffset)); m_UVs2.Add(new Vector2(glyph.xOffset, glyph.yOffset)); break; default: break; } m_SubmeshTriangles[_currentMaterial].Add(index); m_SubmeshTriangles[_currentMaterial].Add(index + 1); m_SubmeshTriangles[_currentMaterial].Add(index + 2); m_SubmeshTriangles[_currentMaterial].Add(index); m_SubmeshTriangles[_currentMaterial].Add(index + 2); m_SubmeshTriangles[_currentMaterial].Add(index + 3); }
void BlitQuad(Rect quad, TGlyph glyph) { int index = m_Vertices.Count; Rect uvs = glyph.rect; m_Vertices.Add(new Vector3(quad.x, -quad.y, 0f)); m_Vertices.Add(new Vector3(quad.x + quad.width, -quad.y, 0f)); m_Vertices.Add(new Vector3(quad.x + quad.width, -quad.y - quad.height, 0f)); m_Vertices.Add(new Vector3(quad.x, -quad.y - quad.height, 0f)); m_UVs.Add(new Vector2(uvs.x / Font.HScale, 1 - uvs.y / Font.VScale)); m_UVs.Add(new Vector2((uvs.x + uvs.width) / Font.HScale, 1 - uvs.y / Font.VScale)); m_UVs.Add(new Vector2((uvs.x + uvs.width) / Font.HScale, 1 - (uvs.y + uvs.height) / Font.VScale)); m_UVs.Add(new Vector2(uvs.x / Font.HScale, 1 - (uvs.y + uvs.height) / Font.VScale)); switch (FillMode) { case TFillMode.SingleColor: m_Colors.Add(ColorTopLeft); m_Colors.Add(ColorTopLeft); m_Colors.Add(ColorTopLeft); m_Colors.Add(ColorTopLeft); break; case TFillMode.VerticalGradient: m_Colors.Add(ColorTopLeft); m_Colors.Add(ColorTopLeft); m_Colors.Add(ColorBottomLeft); m_Colors.Add(ColorBottomLeft); break; case TFillMode.HorizontalGradient: m_Colors.Add(ColorTopLeft); m_Colors.Add(ColorBottomLeft); m_Colors.Add(ColorBottomLeft); m_Colors.Add(ColorTopLeft); break; case TFillMode.QuadGradient: m_Colors.Add(ColorTopLeft); m_Colors.Add(ColorTopRight); m_Colors.Add(ColorBottomRight); m_Colors.Add(ColorBottomLeft); break; case TFillMode.StretchedTexture: m_UVs2.Add(new Vector2(0f, 1f)); m_UVs2.Add(new Vector2(1f, 1f)); m_UVs2.Add(new Vector2(1f, 0f)); m_UVs2.Add(new Vector2(0f, 0f)); break; case TFillMode.ProjectedTexture: float h = uvs.height / Font.LineHeight; float w = uvs.width / Font.LineHeight; m_UVs2.Add(new Vector2(glyph.xOffset, h - glyph.yOffset)); m_UVs2.Add(new Vector2(w - glyph.xOffset, h - glyph.yOffset)); m_UVs2.Add(new Vector2(w - glyph.xOffset, glyph.yOffset)); m_UVs2.Add(new Vector2(glyph.xOffset, glyph.yOffset)); break; default: break; } m_SubmeshTriangles[_currentMaterial].Add(index); m_SubmeshTriangles[_currentMaterial].Add(index + 1); m_SubmeshTriangles[_currentMaterial].Add(index + 2); m_SubmeshTriangles[_currentMaterial].Add(index); m_SubmeshTriangles[_currentMaterial].Add(index + 2); m_SubmeshTriangles[_currentMaterial].Add(index + 3); }
float BlitString(string str, float cursorX, float cursorY, List <int> vertexPointers = null) { TGlyph prevGlyph = null; Rect r; bool inControlCode = false; int requestedMaterial = 0; foreach (char c in str) { int charCode = (int)c; if (inControlCode) { inControlCode = false; if (charCode >= 48 && charCode <= 57) // 0-9 { requestedMaterial = charCode - 48; if (requestedMaterial < _materialCount) { _currentMaterial = requestedMaterial; } else { Debug.LogWarning(string.Format( "Requested material {0} out of range.", requestedMaterial )); } AddPlaceholderGlyphBounds(); continue; } } else { if (charCode == 92) // Backslash { inControlCode = true; AddPlaceholderGlyphBounds(); continue; } } TGlyph glyph = Font.Glyphs.Get(charCode); if (glyph == null) { continue; } if (charCode == 32) { // Assuming here that spaces should not be clickable. AddPlaceholderGlyphBounds(); cursorX += glyph.xAdvance * Size + Tracking; continue; } float kerning = 0f; if (prevGlyph != null) { kerning = prevGlyph.GetKerning(charCode) * Size; } if (vertexPointers != null) { vertexPointers.Add(m_Vertices.Count); } r = new Rect( cursorX + glyph.xOffset * Size + kerning, cursorY + glyph.yOffset * Size, glyph.rect.width * Size, glyph.rect.height * Size ); BlitQuad(r, glyph); // Click bounds for glyphs are based on allocated space, not rendered space. // Otherwise we'll end up with unclickable dead zones between glyphs. r.width = glyph.xAdvance * Size; // And Y coordinates are just not handled the same at all. r.y = -cursorY - r.height - glyph.yOffset * Size; StoreGlyphBounds(r); cursorX += glyph.xAdvance * Size + Tracking + kerning; prevGlyph = glyph; } if (cursorX > Width) { Width = cursorX; } return(cursorX); }
void DoWork() { ANT_Library library; ANT_Face face; ANT_GlyphSlot slot; ANT_Error error; string filename; string text; int num_chars; bool use_kerning; int previous; int pen_x, pen_y, n; filename = Common.Example_AltNETType_FontFileName; text = "AltNETType (transformation + centering + kerning)";// Common.Pangram; num_chars = text.Length; // ... initialize library ... // ... create face object ... // ... set character size ... { // initialize library error = ANT.ANT_Init_AltNETType(out library); // error handling omitted // create face object error = ANT.ANT_New_Face(library, filename, 0, out face); // error handling omitted if (error != ANT_Error.ANT_Err_Ok) { return; } // use 50pt at 100dpi // set character size error = ANT.ANT_Set_Char_Size(face, 28 * 64, 0, 96, 0); // error handling omitted } // a small shortcut slot = face.glyph; // start at (0,0) pen_x = 0; pen_y = 0; num_glyphs = 0; use_kerning = ANT.ANT_HAS_KERNING(face); previous = 0; ANT_Vector delta = new ANT_Vector(); for (n = 0; n < num_chars; n++) { glyph = glyphs[n]; if (glyph == null) { glyphs[n] = glyph = new TGlyph(); } // convert character code to glyph index glyph.index = ANT.ANT_Get_Char_Index(face, text[n]); // retrieve kerning distance and move pen position if (use_kerning && previous != 0 && glyph.index != 0) { ANT.ANT_Get_Kerning(face, previous, glyph.index, ANT_Kerning_Mode.ANT_KERNING_DEFAULT, delta); pen_x += delta.x >> 6; } // store current pen position if (glyph.pos == null) { glyph.pos = new ANT_Vector(); } glyph.pos.x = pen_x; glyph.pos.y = pen_y; // load glyph image into the slot without rendering error = ANT.ANT_Load_Glyph(face, glyph.index, ANT_LOAD.ANT_LOAD_DEFAULT); if (error != ANT_Error.ANT_Err_Ok) { // ignore errors, jump to next glyph continue; } // extract glyph image and store it in our table error = ANT.ANT_Get_Glyph(face.glyph, out glyph.image); if (error != ANT_Error.ANT_Err_Ok) { // ignore errors, jump to next glyph continue; } // translate the glyph image now ANT.ANT_Glyph_Transform(glyph.image, null, glyph.pos); // increment pen position pen_x += slot.advance.x >> 6; // record current glyph index previous = glyph.index; // increment number of glyphs num_glyphs++; } ANT_Vector start = new ANT_Vector(); // transformation matrix ANT_Matrix matrix = new ANT_Matrix(); ANT_Glyph image; ANT_Vector pen = new ANT_Vector(); ANT_BBox bbox = new ANT_BBox(); double angle; ANT_BBox string_bbox; compute_string_bbox(out string_bbox); int my_target_width = WIDTH; int my_target_height = HEIGHT; // compute string dimensions in integer pixels int string_width = string_bbox.xMax - string_bbox.xMin; int string_height = string_bbox.yMax - string_bbox.yMin; // use -30 degrees angle = (330.0 / 360) * 3.14159 * 2; // compute start pen position in 26.6 cartesian pixels start.x = (int)(((my_target_width - string_width * Math.Cos(angle) - string_height * Math.Sin(angle)) / 2 + string_bbox.yMax * Math.Sin(angle)) * 64); // start.y = ((my_target_height - string_height) / 2) * 64; // need to center bitmaps (not base line) start.y = (int)(((my_target_height - string_height * Math.Cos(angle) - string_width * Math.Sin(angle)) / 2 - string_bbox.yMin * Math.Cos(angle)) * 64); // set up transform (a rotation here) matrix.xx = (int)(Math.Cos(angle) * 0x10000L); matrix.xy = (int)(-Math.Sin(angle) * 0x10000L); matrix.yx = (int)(Math.Sin(angle) * 0x10000L); matrix.yy = (int)(Math.Cos(angle) * 0x10000L); pen.CopyFrom(start); for (n = 0; n < num_glyphs; n++) { // create a copy of the original glyph error = ANT.ANT_Glyph_Copy(glyphs[n].image, out image); if (error != ANT_Error.ANT_Err_Ok) { continue; } // transform copy (this will also translate it to the // correct position ANT.ANT_Glyph_Transform(image, matrix, pen); // check bounding box; if the transformed glyph image // is not in our target surface, we can avoid rendering it ANT.ANT_Glyph_Get_CBox(image, ANT_Glyph_BBox_Mode.ANT_GLYPH_BBOX_PIXELS, bbox); if (bbox.xMax <= 0 || bbox.xMin >= my_target_width || bbox.yMax <= 0 || bbox.yMin >= my_target_height) { //continue; } else { // convert glyph image to bitmap (destroy the glyph copy!) error = ANT.ANT_Glyph_To_Bitmap(ref image, ANT_Render_Mode.ANT_RENDER_MODE_NORMAL, null, // no additional translation true); // destroy copy in "image" if (error == ANT_Error.ANT_Err_Ok) { ANT_BitmapGlyph bit = (ANT_BitmapGlyph)image; draw_bitmap(bit.bitmap, bit.left, my_target_height - bit.top); } } // increment pen position -- // we don't have access to a slot structure, // so we have to use advances from glyph structure // (which are in 16.16 fixed float format) pen.x += image.advance.x >> 10; pen.y += image.advance.y >> 10; ANT.ANT_Done_Glyph(ref image); } // show_image(); ANT.ANT_Done_Face(ref face); ANT.ANT_Done_AltNETType(ref library); }
float BlitString(string str, float cursorX, float cursorY, List <int> vertexPointers = null) { TGlyph prevGlyph = null; Rect r; bool inControlCode = false; int requestedMaterial = 0; foreach (char c in str) { int charCode = (int)c; if (inControlCode) { inControlCode = false; if (charCode >= 48 && charCode <= 57) // 0-9 { requestedMaterial = charCode - 48; if (requestedMaterial < _materialCount) { _currentMaterial = requestedMaterial; } else { Debug.LogWarning(string.Format( "Requested material {0} out of range.", requestedMaterial )); } if (EnableClickSupport) { AddPlaceholderGlyphBounds(); } continue; } } else { if (charCode == 92) // Backslash { inControlCode = true; if (EnableClickSupport) { AddPlaceholderGlyphBounds(); } continue; } } TGlyph glyph = Font.Glyphs.Get(charCode); if (glyph == null) { continue; } if (charCode == 32) { // Assuming here that spaces should not be clickable. if (EnableClickSupport) { AddPlaceholderGlyphBounds(); } cursorX += glyph.xAdvance * Size + Tracking; continue; } float kerning = 0f; if (prevGlyph != null) { kerning = prevGlyph.GetKerning(charCode) * Size; } if (vertexPointers != null) { vertexPointers.Add(m_Vertices.Count); } r = new Rect( cursorX + glyph.xOffset * Size + kerning, cursorY + glyph.yOffset * Size, glyph.rect.width * Size, glyph.rect.height * Size ); BlitQuad(r, glyph); // Only need to store glyph bounds if click support is enabled. if (EnableClickSupport) { // Click bounds for glyphs are based on allocated space, not rendered space. // Otherwise we'll end up with unclickable dead zones between glyphs. r.width = glyph.xAdvance * Size; // And Y coordinates are just not handled the same at all. r.y = -cursorY - r.height - glyph.yOffset * Size; // Calculate relative world-space bounds for this glyph and store them. Bounds b = new Bounds( new Vector3(r.x + r.width / 2f, r.y + r.height / 2f, 0f), new Vector3(r.width, r.height, r.height) ); if (Stationary) { // Bake the rotation and position into the bounds so we // don't have to recalculate them on the fly later. b = TranslateBounds(b); } StoreGlyphBounds(b); } cursorX += glyph.xAdvance * Size + Tracking + kerning; prevGlyph = glyph; } if (cursorX > Width) { Width = cursorX; } return(cursorX); }