public override void SetCount() { scale = frame.distance / 3; scaledPadding = scale * padding; scaledCharacterSize = scale * characterSize; scaledLineHeight = scale * lineHeight; //count = text.length glyphs = new List <glyph>(); count = 0; string[] words = text.Split(' '); int column = 0; int row = 0; float value = scaledPadding * 3; foreach (string word in words) { char[] letters = word.ToCharArray(); if (value + letters.Length * scaledCharacterSize >= frame.width - scaledPadding) { row++; value = scaledPadding; column = 0; } foreach (char c in letters) { glyph g = new glyph(column, row, count, c); glyphs.Add(g); value += scaledCharacterSize; column++; count++; //print((int)c); //print(column); } column++; value += scaledCharacterSize; } //print(words[0]); }
// texture/bitmap queries //------------------------------------------------- // get_char_texture_and_bounds - return the // texture for a character and compute the // bounds of the final bitmap //------------------------------------------------- public render_texture get_char_texture_and_bounds(float height, float aspect, char32_t chnum, ref render_bounds bounds) { glyph gl = get_char(chnum); // on entry, assume x0,y0 are the top,left coordinate of the cell and add // the character bounding box to that position float scale = m_scale * height; bounds.x0 += (float)gl.xoffs * scale * aspect; // compute x1,y1 from there based on the bitmap size bounds.x1 = bounds.x0 + (float)gl.bmwidth * scale * aspect; bounds.y1 = bounds.y0 + (float)m_height * scale; // return the texture return(gl.texture); }
private static void drawLetterSVGFont(double scale, string actualCharString) { // https://gitlab.com/oskay/svg-fonts // <missing-glyph horiz-adv-x="378" /> // <glyph unicode="!" glyph-name="exclam" horiz-adv-x="359" d="M 444 665 L 403 592 L 295 362 L 232 214 M 204 63 L 182 40.9" /> //Logger.Trace("drawLetterSVGFont"); scale = gcHeight / (21 * 31.52); //=661.92 factor_Em2Px = 150; scale = 0.0254 *1000 = 25,4 glyph tmpGlyph = null; if (svgGlyphs.ContainsKey(actualCharString)) { //Logger.Trace("drawLetterSVGFont '{0}'", actualCharString); tmpGlyph = svgGlyphs[actualCharString]; } else if (svgGlyphs.ContainsKey("missing-glyph")) { Logger.Trace("drawLetterSVGFont 'missing - glyph'"); tmpGlyph = svgGlyphs["missing-glyph"]; } else { Logger.Error("Glyph not found"); return; } // var tmpX = offsetX;// + tmpGlyph.x * scale; if (tmpGlyph.d.Length > 0) { string separators = @"(?=[A-Za-z])"; var tokens = Regex.Split(tmpGlyph.d, separators).Where(t => !string.IsNullOrEmpty(t)); int token_cnt = 0; foreach (string token in tokens) { drawToken(token, offsetX + gcOffX, offsetY + gcOffY, scale, token_cnt++, false); } } offsetX += tmpGlyph.x * scale + gcFontDistance; //double.Parse(svgsplit[1]) * scale + gcFontDistance; isSameWord = true; }
//------------------------------------------------- // get_scaled_bitmap_and_bounds - return a // scaled bitmap and bounding rect for a char //------------------------------------------------- public void get_scaled_bitmap_and_bounds(bitmap_argb32 dest, float height, float aspect, char32_t chnum, out rectangle bounds) { bounds = default; glyph gl = get_char(chnum); // on entry, assume x0,y0 are the top,left coordinate of the cell and add // the character bounding box to that position float scale = m_scale * height; bounds.min_x = (int)((float)(gl.xoffs) * scale * aspect); bounds.min_y = 0; // compute x1,y1 from there based on the bitmap size bounds.set_width((int)((float)(gl.bmwidth) * scale * aspect)); bounds.set_height((int)((float)(m_height) * scale)); // if the bitmap isn't big enough, bail if (dest.width() < bounds.width() || dest.height() < bounds.height()) { return; } // if no texture, fill the target if (gl.texture == null) { dest.fill(0); return; } throw new emu_unimplemented(); #if false // scale the font bitmap_argb32 tempbitmap = new bitmap_argb32(&dest.pix(0), bounds.width(), bounds.height(), dest.rowpixels()); render_texture.hq_scale(tempbitmap, gl.bitmap, gl.bitmap.cliprect(), null); #endif }
void render_font_command_glyph() { // FIXME: this is copy/pasta from the BDC loading, and it shouldn't be injected into every font emu_file file = new emu_file(OPEN_FLAG_READ); if (!file.open_ram(new MemoryU8(font_uicmd14), (u32)font_uicmd14.Length)) { // get the file size, read the header, and check that it looks good u64 filesize = file.size(); bdc_header header = new bdc_header(); if (!header.read(file)) { osd_printf_warning("render_font::render_font_command_glyph: error reading BDC header\n"); file.close(); return; } else if (!header.check_magic() || (bdc_header.MAJVERSION != header.get_major_version()) || (bdc_header.MINVERSION != header.get_minor_version())) { LOG("render_font::render_font_command_glyph: incompatible BDC file\n"); file.close(); return; } // get global properties from the header m_height_cmd = header.get_height(); m_yoffs_cmd = header.get_y_offset(); u32 numchars = header.get_glyph_count(); if ((file.tell() + ((u64)numchars * bdc_table_entry.size())) > filesize) { LOG("render_font::render_font_command_glyph: BDC file is too small to hold glyph table\n"); file.close(); return; } // now read the rest of the data u64 remaining = filesize - file.tell(); //try { m_rawdata_cmd.resize(remaining); } //catch (...) //{ // global.osd_printf_error("render_font::render_font_command_glyph: allocation error\n"); //} for (u64 bytes_read = 0; remaining > bytes_read;) { u32 chunk = (u32)std.min(u32.MaxValue, remaining); if (file.read(new Pointer <u8>(m_rawdata_cmd, (int)bytes_read), chunk) != chunk) { osd_printf_error("render_font::render_font_command_glyph: error reading BDC data\n"); m_rawdata_cmd.clear(); file.close(); return; } bytes_read += chunk; } file.close(); // extract the data from the data size_t offset = (size_t)numchars * bdc_table_entry.size(); bdc_table_entry entry = new bdc_table_entry(m_rawdata_cmd.empty() ? null : new Pointer <u8>(m_rawdata_cmd)); for (unsigned chindex = 0; chindex < numchars; chindex++, entry = entry.get_next()) { // if we don't have a subtable yet, make one int chnum = (int)entry.get_encoding(); LOG("render_font::render_font_command_glyph: loading character {0}\n", chnum); if (m_glyphs_cmd[chnum / 256] == null) { //try { m_glyphs_cmd[chnum / 256] = new List <glyph>(256); // new glyph[256]; for (int i = 0; i < 256; i++) { m_glyphs_cmd[chnum / 256].Add(new glyph()); } } //catch (...) //{ // osd_printf_error("render_font::render_font_command_glyph: allocation error\n"); // m_rawdata_cmd.clear(); // return; //} } // fill in the entry glyph gl = m_glyphs_cmd[chnum / 256][chnum % 256]; gl.width = entry.get_x_advance(); gl.xoffs = entry.get_bb_x_offset(); gl.yoffs = entry.get_bb_y_offset(); gl.bmwidth = entry.get_bb_width(); gl.bmheight = entry.get_bb_height(); gl.rawdata = new Pointer <u8>(m_rawdata_cmd, (int)offset); // advance the offset past the character offset += (size_t)((gl.bmwidth * gl.bmheight + 7) / 8); if (m_rawdata_cmd.size() < offset) { osd_printf_verbose("render_font::render_font_command_glyph: BDC file too small to hold all glyphs\n"); m_rawdata_cmd.clear(); return; } } } }
//------------------------------------------------- // load_cached - load a font in cached format //------------------------------------------------- bool load_cached(emu_file file, u64 length, u32 hash) { // get the file size, read the header, and check that it looks good u64 filesize = file.size(); bdc_header header = new bdc_header(); if (!header.read(file)) { osd_printf_warning("render_font::load_cached: error reading BDC header\n"); return(false); } else if (!header.check_magic() || (bdc_header.MAJVERSION != header.get_major_version()) || (bdc_header.MINVERSION != header.get_minor_version())) { LOG("render_font::load_cached: incompatible BDC file\n"); return(false); } else if (length != 0 && ((header.get_original_length() != length) || (header.get_original_hash() != hash))) { LOG("render_font::load_cached: BDC file does not match original BDF file\n"); return(false); } // get global properties from the header m_height = header.get_height(); m_scale = 1.0f / (float)(m_height); m_yoffs = header.get_y_offset(); m_defchar = header.get_default_character(); u32 numchars = header.get_glyph_count(); if (file.tell() + ((u64)numchars * bdc_table_entry.size()) > filesize) { LOG("render_font::load_cached: BDC file is too small to hold glyph table\n"); return(false); } // now read the rest of the data u64 remaining = filesize - file.tell(); try { m_rawdata.resize(remaining); } catch (Exception) { osd_printf_error("render_font::load_cached: allocation error\n"); } for (u64 bytes_read = 0; remaining > bytes_read;) { u32 chunk = (u32)std.min(u32.MaxValue, remaining); if (file.read(new Pointer <u8>(m_rawdata, (int)bytes_read), chunk) != chunk) //if (file.read(&m_rawdata[bytes_read], chunk) != chunk) { osd_printf_error("render_font::load_cached: error reading BDC data\n"); m_rawdata.clear(); return(false); } bytes_read += chunk; } // extract the data from the data size_t offset = (size_t)numchars * bdc_table_entry.size(); bdc_table_entry entry = new bdc_table_entry(m_rawdata.empty() ? null : new Pointer <u8>(m_rawdata)); for (unsigned chindex = 0; chindex < numchars; chindex++, entry = entry.get_next()) { // if we don't have a subtable yet, make one int chnum = (int)entry.get_encoding(); LOG("render_font::load_cached: loading character {0}\n", chnum); if (m_glyphs[chnum / 256] == null || m_glyphs[chnum / 256].Count == 0) { //try { m_glyphs[chnum / 256] = new List <glyph>(256); // new glyph[256]; for (int i = 0; i < 256; i++) { m_glyphs[chnum / 256].Add(new glyph()); } } //catch (Exception ) //{ // global.osd_printf_error("render_font::load_cached: allocation error\n"); // m_rawdata.clear(); // return false; //} } // fill in the entry glyph gl = m_glyphs[chnum / 256][chnum % 256]; gl.width = entry.get_x_advance(); gl.xoffs = entry.get_bb_x_offset(); gl.yoffs = entry.get_bb_y_offset(); gl.bmwidth = entry.get_bb_width(); gl.bmheight = entry.get_bb_height(); gl.rawdata = new Pointer <u8>(m_rawdata, (int)offset); // advance the offset past the character offset += (size_t)((gl.bmwidth * gl.bmheight + 7) / 8); if (m_rawdata.size() < offset) { osd_printf_verbose("render_font::load_cached: BDC file too small to hold all glyphs\n"); m_rawdata.clear(); return(false); } } // got everything m_format = format.CACHED; return(true); }
//------------------------------------------------- // char_expand - expand the raw data for a // character into a bitmap //------------------------------------------------- void char_expand(char32_t chnum, glyph gl) { LOG("render_font::char_expand: expanding character {0}\n", chnum); rgb_t fgcol = (gl.color != 0 ? gl.color : new rgb_t(0xff, 0xff, 0xff, 0xff)); rgb_t bgcol = new rgb_t(0x00, 0xff, 0xff, 0xff); bool is_cmd = ((chnum >= COMMAND_UNICODE) && (chnum < COMMAND_UNICODE + MAX_GLYPH_FONT)); if (is_cmd) { throw new emu_unimplemented(); #if false // punt if nothing there if (gl.bmwidth == 0 || gl.bmheight == 0 || gl.rawdata == null) { return; } // allocate a new bitmap of the size we need gl.bitmap.allocate(gl.bmwidth, m_height_cmd); gl.bitmap.fill(0); // extract the data const char *ptr = gl.rawdata; byte accum = 0; byte accumbit = 7; for (int y = 0; y < gl.bmheight; y++) { int desty = y + m_height_cmd + m_yoffs_cmd - gl.yoffs - gl.bmheight; u32 *dest = (desty >= 0 && desty < m_height_cmd) ? &gl.bitmap.pix(desty, 0) : nullptr; { for (int x = 0; x < gl.bmwidth; x++) { if (accumbit == 7) { accum = *ptr++; } if (dest != null) { *dest++ = (accum & (1 << accumbit)) ? color : rgb_t(0x00, 0xff, 0xff, 0xff); } accumbit = (accumbit - 1) & 7; } } } #endif } else if (m_format == format.OSD) { throw new emu_unimplemented(); #if false // if we're an OSD font, query the info #endif } else if (gl.bmwidth == 0 || gl.bmheight == 0 || gl.rawdata == null) { // abort if nothing there LOG("render_font::char_expand: empty bitmap bounds or no raw data\n"); return; } else { // other formats need to parse their data LOG("render_font::char_expand: building bitmap from raw data\n"); // allocate a new bitmap of the size we need gl.bitmap.allocate(gl.bmwidth, m_height); gl.bitmap.fill(0); // extract the data Pointer <u8> ptr = new Pointer <u8>(gl.rawdata); //const char *ptr = gl.rawdata; u8 accum = 0; u8 accumbit = 7; for (int y = 0; y < gl.bmheight; y++) { int desty = y + m_height + m_yoffs - gl.yoffs - gl.bmheight; PointerU32 dest = ((0 <= desty) && (m_height > desty)) ? gl.bitmap.pix(desty) : null; //u32 *dest(((0 <= desty) && (m_height > desty)) ? &gl.bitmap.pix(desty) : nullptr); if (m_format == format.TEXT) { if (dest != null) { for (int x = 0; gl.bmwidth > x;) { // scan for the next hex digit int bits = -1; while ('\r' != ptr[0] && '\n' != ptr[0] && 0 > bits) // while (('\r' != *ptr) && ('\n' != *ptr) && (0 > bits)) { if (ptr[0] >= '0' && ptr[0] <= '9') { bits = ptr[0] - '0'; //bits = *ptr++ - '0'; ptr++; } else if (ptr[0] >= 'A' && ptr[0] <= 'F') { bits = ptr[0] - 'A' + 10; ptr++; } else if (ptr[0] >= 'a' && ptr[0] <= 'f') { bits = ptr[0] - 'a' + 10; ptr++; } else { ptr++; } } // expand the four bits dest[0] = (bits & 8) != 0 ? fgcol : bgcol; dest++; //*dest++ = (bits & 8) ? fgcol : bgcol; if (gl.bmwidth > ++x) { dest[0] = (bits & 4) != 0 ? fgcol : bgcol; dest++; } //*dest++ = (bits & 4) ? fgcol : bgcol; if (gl.bmwidth > ++x) { dest[0] = (bits & 2) != 0 ? fgcol : bgcol; dest++; } //*dest++ = (bits & 2) ? fgcol : bgcol; if (gl.bmwidth > ++x) { dest[0] = (bits & 1) != 0 ? fgcol : bgcol; dest++; } //*dest++ = (bits & 1) ? fgcol : bgcol; x++; } // advance to the next line ptr = next_line(ptr); } } else if (m_format == format.CACHED) { for (int x = 0; x < gl.bmwidth; x++) { if (accumbit == 7) { accum = ptr[0]; ptr++; } if (dest != null) { dest[0] = (accum & (1 << accumbit)) != 0 ? fgcol : bgcol; dest++; } //*dest++ = (accum & (1 << accumbit)) ? fgcol : bgcol; accumbit = (u8)((accumbit - 1) & 7); } } } } // wrap a texture around the bitmap gl.texture = m_manager.texture_alloc(render_texture.hq_scale); gl.texture.set_bitmap(gl.bitmap, gl.bitmap.cliprect(), texture_format.TEXFORMAT_ARGB32); }
// helpers //------------------------------------------------- // get_char - return a pointer to a character // in a font, expanding if necessary //------------------------------------------------- glyph get_char(char32_t chnum) { unsigned page = chnum / 256; if (page >= m_glyphs.Length) { if ((0 <= m_defchar) && (chnum != m_defchar)) { return(get_char((char32_t)m_defchar)); } else { return(get_char_dummy_glyph); } } else if (m_glyphs[page] == null || m_glyphs[page].Count == 0) { //mamep: make table for command glyph if ((m_format == format.OSD) || ((chnum >= COMMAND_UNICODE) && (chnum < COMMAND_UNICODE + MAX_GLYPH_FONT))) { m_glyphs[page] = new List <glyph>(256); // new glyph[256]; for (int i = 0; i < 256; i++) { m_glyphs[page].Add(new glyph()); } } else if ((0 <= m_defchar) && (chnum != m_defchar)) { return(get_char((char32_t)m_defchar)); } else { return(get_char_dummy_glyph); } } // if the character isn't generated yet, do it now glyph gl = m_glyphs[page][(int)(chnum % 256)]; if (!gl.bitmap.valid()) { //mamep: command glyph support if (m_height_cmd != 0 && chnum >= COMMAND_UNICODE && chnum < COMMAND_UNICODE + MAX_GLYPH_FONT) { glyph glyph_ch = m_glyphs_cmd[page][(int)(chnum % 256)]; float scale = (float)m_height / (float)m_height_cmd; if (m_format == format.OSD) { scale *= 0.90f; } if (!glyph_ch.bitmap.valid()) { char_expand(chnum, glyph_ch); } //mamep: for color glyph gl.color = glyph_ch.color; gl.width = (int)(glyph_ch.width * scale + 0.5f); gl.xoffs = (int)(glyph_ch.xoffs * scale + 0.5f); gl.yoffs = (int)(glyph_ch.yoffs * scale + 0.5f); gl.bmwidth = (int)(glyph_ch.bmwidth * scale + 0.5f); gl.bmheight = (int)(glyph_ch.bmheight * scale + 0.5f); gl.bitmap.allocate(gl.bmwidth, gl.bmheight); rectangle clip = new rectangle( 0, glyph_ch.bitmap.width() - 1, 0, glyph_ch.bitmap.height() - 1); render_texture.hq_scale(gl.bitmap, glyph_ch.bitmap, clip, null); /* wrap a texture around the bitmap */ gl.texture = m_manager.texture_alloc(render_texture.hq_scale); gl.texture.set_bitmap(gl.bitmap, gl.bitmap.cliprect(), texture_format.TEXFORMAT_ARGB32); } else { char_expand(chnum, gl); } } return(gl); }
public void MakeGlyphs(string parsedText) { string[] words = parsedText.Split(' '); int first = 0; foreach (string word in words) { // makes sure we skip the first space of the section if (first != 0) { column++; locationX += scaledAdvance; } else { first = 1; } char[] letters = word.ToCharArray(); float newLine = 0; float wordWidth = 0; foreach (char c in letters) { if (c == '\n') { } else { wordWidth += scaledAdvance;// * scale } } if (locationX + wordWidth >= frame.width - scaledPadding * 2) { row++; locationY += scaledLineHeight; locationX = scaledPadding; column = 0; } foreach (char c in letters) { if (c == '\n') { row++; locationY += scaledLineHeight; locationX = scaledPadding; column = 0; } else { if (NovaMono.info.ContainsKey(c)) { float[] v1 = NovaMono.info[c]; // print( c ); glyph g = new glyph(locationX, locationY, count, v1, currentTextureVal, currentScaleOffset, currentHueOffset, currentSpecial); glyphs.Add(g); locationX += scaledAdvance; column++; count++; } else { DebugThis("character not found"); print(c); print(row); print(column); } } } } }