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); }
public static Geometry ANT_Outline_ToGeometry(ANT_Outline outline, bool flipY, Matrix4 mtx) { if (outline == null) { return(null); } ANTOutlineGeometry path = new ANTOutlineGeometry(); double x1, y1, x2, y2, x3, y3; int n; // index of contour in outline int first; // index of first point in contour int tag; // current point's state first = 0; ANT_Vector_Array points = outline.points; for (n = 0; n < outline.n_contours; n++) { int last; // index of last point in contour last = outline.contours[n]; int limit = last; outline_v_start.CopyFrom(points[first]); outline_v_last.CopyFrom(points[last]); outline_v_control.CopyFrom(outline_v_start); int point_index = first; ANT_Byte_Array tags_buf = outline.tags; int tags_index = first; tag = ANT.ANT_CURVE_TAG(tags_buf[tags_index + 0]); // A contour cannot start with a cubic control point! if (tag == ANT_CURVE_Tag.ANT_CURVE_TAG_CUBIC) { return(null); } // check first point to determine origin if (tag == ANT_CURVE_Tag.ANT_CURVE_TAG_CONIC) { // first point is conic control. Yes, this happens. if (ANT.ANT_CURVE_TAG(tags_buf[last]) == ANT_CURVE_Tag.ANT_CURVE_TAG_ON) { // start at last point if it is on the curve outline_v_start.CopyFrom(outline_v_last); limit--; } else { // if both first and last points are conic, // start at their middle and record its position // for closure outline_v_start.x = (outline_v_start.x + outline_v_last.x) / 2; outline_v_start.y = (outline_v_start.y + outline_v_last.y) / 2; outline_v_last.CopyFrom(outline_v_start); } point_index--; tags_index--; } x1 = ANT.ANT_int26p6_to_double(outline_v_start.x); y1 = ANT.ANT_int26p6_to_double(outline_v_start.y); if (flipY) { y1 = -y1; } mtx.Transform(ref x1, ref y1); path.MoveTo(x1, y1); while (point_index < limit) { point_index++; ANT_Vector point = points[point_index]; tags_index++; tag = ANT.ANT_CURVE_TAG(tags_buf[tags_index + 0]); switch (tag) { case ANT_CURVE_Tag.ANT_CURVE_TAG_ON: // emit a single line_to { x1 = ANT.ANT_int26p6_to_double(point.x); y1 = ANT.ANT_int26p6_to_double(point.y); if (flipY) { y1 = -y1; } mtx.Transform(ref x1, ref y1); path.LineTo(x1, y1); continue; } case ANT_CURVE_Tag.ANT_CURVE_TAG_CONIC: // consume conic arcs { outline_v_control.x = point.x; outline_v_control.y = point.y; Do_Conic: if (point_index < limit) { point_index++; point = points[point_index]; tags_index++; tag = ANT.ANT_CURVE_TAG(tags_buf[tags_index + 0]); outline_vec.CopyFrom(point); if (tag == ANT_CURVE_Tag.ANT_CURVE_TAG_ON) { x1 = ANT.ANT_int26p6_to_double(outline_v_control.x); y1 = ANT.ANT_int26p6_to_double(outline_v_control.y); x2 = ANT.ANT_int26p6_to_double(outline_vec.x); y2 = ANT.ANT_int26p6_to_double(outline_vec.y); if (flipY) { y1 = -y1; y2 = -y2; } mtx.Transform(ref x1, ref y1); mtx.Transform(ref x2, ref y2); path.Сurve3(x1, y1, x2, y2); continue; } if (tag != ANT_CURVE_Tag.ANT_CURVE_TAG_CONIC) { return(null); } outline_v_middle.x = (outline_v_control.x + outline_vec.x) / 2; outline_v_middle.y = (outline_v_control.y + outline_vec.y) / 2; x1 = ANT.ANT_int26p6_to_double(outline_v_control.x); y1 = ANT.ANT_int26p6_to_double(outline_v_control.y); x2 = ANT.ANT_int26p6_to_double(outline_v_middle.x); y2 = ANT.ANT_int26p6_to_double(outline_v_middle.y); if (flipY) { y1 = -y1; y2 = -y2; } mtx.Transform(ref x1, ref y1); mtx.Transform(ref x2, ref y2); path.Сurve3(x1, y1, x2, y2); outline_v_control.CopyFrom(outline_vec); goto Do_Conic; } x1 = ANT.ANT_int26p6_to_double(outline_v_control.x); y1 = ANT.ANT_int26p6_to_double(outline_v_control.y); x2 = ANT.ANT_int26p6_to_double(outline_v_start.x); y2 = ANT.ANT_int26p6_to_double(outline_v_start.y); if (flipY) { y1 = -y1; y2 = -y2; } mtx.Transform(ref x1, ref y1); mtx.Transform(ref x2, ref y2); path.Сurve3(x1, y1, x2, y2); goto Close; } default: { if (point_index + 1 > limit || ANT.ANT_CURVE_TAG(tags_buf[tags_index + 1]) != ANT_CURVE_Tag.ANT_CURVE_TAG_CUBIC) { return(null); } outline_vec1.x = points[point_index + 0].x; outline_vec1.y = points[point_index + 0].y; outline_vec2.x = points[point_index + 1].x; outline_vec2.y = points[point_index + 1].y; point_index += 2; { point = points[point_index]; } tags_index += 2; if (point_index <= limit) { outline_vec.CopyFrom(point); x1 = ANT.ANT_int26p6_to_double(outline_vec1.x); y1 = ANT.ANT_int26p6_to_double(outline_vec1.y); x2 = ANT.ANT_int26p6_to_double(outline_vec2.x); y2 = ANT.ANT_int26p6_to_double(outline_vec2.y); x3 = ANT.ANT_int26p6_to_double(outline_vec.x); y3 = ANT.ANT_int26p6_to_double(outline_vec.y); if (flipY) { y1 = -y1; y2 = -y2; y3 = -y3; } mtx.Transform(ref x1, ref y1); mtx.Transform(ref x2, ref y2); mtx.Transform(ref x3, ref y3); path.Сurve4(x1, y1, x2, y2, x3, y3); continue; } x1 = ANT.ANT_int26p6_to_double(outline_vec1.x); y1 = ANT.ANT_int26p6_to_double(outline_vec1.y); x2 = ANT.ANT_int26p6_to_double(outline_vec2.x); y2 = ANT.ANT_int26p6_to_double(outline_vec2.y); x3 = ANT.ANT_int26p6_to_double(outline_v_start.x); y3 = ANT.ANT_int26p6_to_double(outline_v_start.y); if (flipY) { y1 = -y1; y2 = -y2; y3 = -y3; } mtx.Transform(ref x1, ref y1); mtx.Transform(ref x2, ref y2); mtx.Transform(ref x3, ref y3); path.Сurve4(x1, y1, x2, y2, x3, y3); goto Close; } } } path.ClosePolygon(); Close: first = last + 1; } return(path); }