Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold, float outlineThickness) const { // The glyph to return Glyph glyph; // First, transform our ugly void* to a FT_Face FT_Face face = static_cast<FT_Face>(m_face); if (!face) return glyph; // Set the character size if (!setCurrentSize(characterSize)) return glyph; // Load the glyph corresponding to the code point FT_Int32 flags = FT_LOAD_TARGET_NORMAL | FT_LOAD_FORCE_AUTOHINT; if (outlineThickness != 0) flags |= FT_LOAD_NO_BITMAP; if (FT_Load_Char(face, codePoint, flags) != 0) return glyph; // Retrieve the glyph FT_Glyph glyphDesc; if (FT_Get_Glyph(face->glyph, &glyphDesc) != 0) return glyph; // Apply bold and outline (there is no fallback for outline) if necessary -- first technique using outline (highest quality) FT_Pos weight = 1 << 6; bool outline = (glyphDesc->format == FT_GLYPH_FORMAT_OUTLINE); if (outline) { if (bold) { FT_OutlineGlyph outlineGlyph = (FT_OutlineGlyph)glyphDesc; FT_Outline_Embolden(&outlineGlyph->outline, weight); } if (outlineThickness != 0) { FT_Stroker stroker = static_cast<FT_Stroker>(m_stroker); FT_Stroker_Set(stroker, static_cast<FT_Fixed>(outlineThickness * static_cast<float>(1 << 6)), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); FT_Glyph_Stroke(&glyphDesc, stroker, true); } } // Convert the glyph to a bitmap (i.e. rasterize it) FT_Glyph_To_Bitmap(&glyphDesc, FT_RENDER_MODE_NORMAL, 0, 1); FT_Bitmap& bitmap = reinterpret_cast<FT_BitmapGlyph>(glyphDesc)->bitmap; // Apply bold if necessary -- fallback technique using bitmap (lower quality) if (!outline) { if (bold) FT_Bitmap_Embolden(static_cast<FT_Library>(m_library), &bitmap, weight, weight); if (outlineThickness != 0) err() << "Failed to outline glyph (no fallback available)" << std::endl; } // Compute the glyph's advance offset glyph.advance = static_cast<float>(face->glyph->metrics.horiAdvance) / static_cast<float>(1 << 6); if (bold) glyph.advance += static_cast<float>(weight) / static_cast<float>(1 << 6); int width = bitmap.width; int height = bitmap.rows; if ((width > 0) && (height > 0)) { // Leave a small padding around characters, so that filtering doesn't // pollute them with pixels from neighbors const unsigned int padding = 1; width += 2 * padding; height += 2 * padding; // Get the glyphs page corresponding to the character size Page& page = m_pages[characterSize]; // Find a good position for the new glyph into the texture glyph.textureRect = findGlyphRect(page, width, height); // Make sure the texture data is positioned in the center // of the allocated texture rectangle glyph.textureRect.left += padding; glyph.textureRect.top += padding; glyph.textureRect.width -= 2 * padding; glyph.textureRect.height -= 2 * padding; // Compute the glyph's bounding box glyph.bounds.left = static_cast<float>(face->glyph->metrics.horiBearingX) / static_cast<float>(1 << 6); glyph.bounds.top = -static_cast<float>(face->glyph->metrics.horiBearingY) / static_cast<float>(1 << 6); glyph.bounds.width = static_cast<float>(face->glyph->metrics.width) / static_cast<float>(1 << 6) + outlineThickness * 2; glyph.bounds.height = static_cast<float>(face->glyph->metrics.height) / static_cast<float>(1 << 6) + outlineThickness * 2; // Resize the pixel buffer to the new size and fill it with transparent white pixels m_pixelBuffer.resize(width * height * 4); Uint8* current = &m_pixelBuffer[0]; Uint8* end = current + width * height * 4; while (current != end) { (*current++) = 255; (*current++) = 255; (*current++) = 255; (*current++) = 0; } // Extract the glyph's pixels from the bitmap const Uint8* pixels = bitmap.buffer; if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { // Pixels are 1 bit monochrome values for (unsigned int y = padding; y < height - padding; ++y) { for (unsigned int x = padding; x < width - padding; ++x) { // The color channels remain white, just fill the alpha channel std::size_t index = x + y * width; m_pixelBuffer[index * 4 + 3] = ((pixels[(x - padding) / 8]) & (1 << (7 - ((x - padding) % 8)))) ? 255 : 0; } pixels += bitmap.pitch; } } else { // Pixels are 8 bits gray levels for (unsigned int y = padding; y < height - padding; ++y) { for (unsigned int x = padding; x < width - padding; ++x) { // The color channels remain white, just fill the alpha channel std::size_t index = x + y * width; m_pixelBuffer[index * 4 + 3] = pixels[x - padding]; } pixels += bitmap.pitch; } } // Write the pixels to the texture unsigned int x = glyph.textureRect.left - padding; unsigned int y = glyph.textureRect.top - padding; unsigned int w = glyph.textureRect.width + 2 * padding; unsigned int h = glyph.textureRect.height + 2 * padding; page.texture.update(&m_pixelBuffer[0], w, h, x, y); } // Delete the FT glyph FT_Done_Glyph(glyphDesc); // Done :) return glyph; }
void SubtitleRenderer::load_glyph(InternalChar ch) { VGfloat escapement[2]{}; auto load_glyph_internal = [&](FT_Face ft_face, VGFont vg_font, bool border) { try { auto glyph_index = FT_Get_Char_Index(ft_face, ch.codepoint()); ENFORCE(!FT_Load_Glyph(ft_face, glyph_index, FT_LOAD_NO_HINTING)); FT_Glyph glyph; ENFORCE(!FT_Get_Glyph(ft_face->glyph, &glyph)); SCOPE_EXIT {FT_Done_Glyph(glyph);}; if (border) ENFORCE(!FT_Glyph_StrokeBorder(&glyph, ft_stroker_, 0, 1)); ENFORCE(!FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, NULL, 1)); FT_BitmapGlyph bit_glyph = (FT_BitmapGlyph) glyph; FT_Bitmap& bitmap = bit_glyph->bitmap; VGImage image{}; VGfloat glyph_origin[2]{}; if (bitmap.width > 0 && bitmap.rows > 0) { constexpr VGfloat blur_stddev = 0.52; const int padding = static_cast<int>(3*blur_stddev + 0.5); const int image_width = bitmap.width + padding*2; const int image_height = bitmap.rows + padding*2; image = vgCreateImage(VG_A_8, image_width, image_height, VG_IMAGE_QUALITY_NONANTIALIASED); assert(image); if (bitmap.pitch > 0) { vgImageSubData(image, bitmap.buffer + bitmap.pitch*(bitmap.rows-1), -bitmap.pitch, VG_A_8, padding, padding, bitmap.width, bitmap.rows); assert(!vgGetError()); } else { vgImageSubData(image, bitmap.buffer, bitmap.pitch, VG_A_8, padding, padding, bitmap.width, bitmap.rows); assert(!vgGetError()); } auto softened_image = vgCreateImage(VG_A_8, image_width, image_height, VG_IMAGE_QUALITY_NONANTIALIASED); assert(softened_image); // Even out hard and soft edges vgGaussianBlur(softened_image, image, blur_stddev, blur_stddev, VG_TILE_FILL); assert(!vgGetError()); vgDestroyImage(image); assert(!vgGetError()); image = softened_image; glyph_origin[0] = static_cast<VGfloat>(padding - bit_glyph->left); glyph_origin[1] = static_cast<VGfloat>(padding + bitmap.rows - bit_glyph->top - 1); } escapement[0] = static_cast<VGfloat>((ft_face->glyph->advance.x + 32) / 64); escapement[1] = 0; vgSetGlyphToImage(vg_font, ch.val, image, glyph_origin, escapement); assert(!vgGetError()); if (image) { vgDestroyImage(image); assert(!vgGetError()); } } catch(...) { escapement[0] = 0; escapement[1] = 0; vgSetGlyphToImage(vg_font, ch.val, VG_INVALID_HANDLE, escapement, escapement); assert(!vgGetError()); } }; if (!ch.italic()) { load_glyph_internal(ft_face_, vg_font_, false); glyphs_[ch].advance = escapement[0]; load_glyph_internal(ft_face_, vg_font_border_, true); } else { load_glyph_internal(ft_face_italic_, vg_font_, false); glyphs_[ch].advance = escapement[0]; load_glyph_internal(ft_face_italic_, vg_font_border_, true); } }
bool xFT2FontCharLoader::_loadResource(xWCharType& _char , xFT2FontChar*& pRes, int& ResSize,unsigned int arg) { //if(FT_Load_Glyph( m_FT_Face, FT_Get_Char_Index( m_FT_Face, _char ), FT_LOAD_DEFAULT )) // throw std::runtime_error("FT_Load_Glyph failed"); FT_Int32 load_flags = FT_LOAD_RENDER|FT_LOAD_FORCE_AUTOHINT ; if(m_bAntilias) load_flags |= ( FT_LOAD_TARGET_NORMAL| FT_LOAD_TARGET_LIGHT); else load_flags |= ( FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO ); if(pRes == NULL) { pRes = new xFT2FontChar(m_pRenderer , this ); if(pRes == NULL) return false; } //得到字模 FT_UInt glyph_index; /* retrieve glyph index from character code */ glyph_index = FT_Get_Char_Index( m_FT_Face, _char ); /* load glyph image into the slot (erase previous one) */ int error = FT_Load_Glyph( m_FT_Face, glyph_index, FT_LOAD_DEFAULT ); //if(m_bBold) //{ // int xStrenth = GetBoldenStrength(m_bBold , m_w); // int yStrenth = GetBoldenStrength(m_bBold , m_h); // FT2_GlyphSlot_Embolden(m_FT_Face->glyph , xStrenth , yStrenth ); //} if(m_bAntilias) { error = FT_Render_Glyph( m_FT_Face->glyph, FT_RENDER_MODE_NORMAL ); } else { error = FT_Render_Glyph( m_FT_Face->glyph, FT_RENDER_MODE_NORMAL ); } FT_Glyph glyph; if(FT_Get_Glyph( m_FT_Face->glyph, &glyph )) { XEVOL_LOG(eXL_DEBUG_HIGH,"FT_Load_Glyph failed\n"); return false; } FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph; //取道位图数据 FT_Bitmap& bitmap = bitmap_glyph->bitmap; //把位图数据拷贝自己定义的数据区里.这样旧可以画到需要的东西上面了。 int width = bitmap.width; int height = bitmap.rows; m_FT_Face->size->metrics.y_ppem; m_FT_Face->glyph->metrics.horiAdvance; pRes->m_adv_x = m_FT_Face->glyph->advance.x / 64.0f; pRes->m_adv_y = m_FT_Face->size->metrics.y_ppem; //m_FT_Face->glyph->metrics.horiBearingY / 64.0f; pRes->m_left = (float)bitmap_glyph->left; pRes->m_top = (float)bitmap_glyph->top; int tex_pitch = 0; unsigned char* tex_pixel = NULL; IBaseTexture* pTexture = NULL; #ifndef _FONT_FULL_TEXTURE_ if(pRes->m_pTexture != NULL) { pRes->m_pTexture->KillObject(); } if(width == 0 || height == 0) { FT_Done_Glyph(glyph); return true; } if(m_pRenderer->isTextureSupport( PIXELFORMAT_ALPHA8 ) ) { pRes->m_pTexture = m_pRenderer->createTexture( width,height, PIXELFORMAT_ALPHA8 , false);//Alpha8); } else { pRes->m_pTexture = m_pRenderer->createTexture( width,height, PIXELFORMAT_B8G8R8A8 , false);//RGBA); } if(pRes->m_pTexture == NULL) return width == 0 && height == 0; pTexture = pRes->m_pTexture; xTextureLockArea lockInfo; pTexture->lock(eLock_WriteDiscard , lockInfo); tex_pixel = (unsigned char*)lockInfo.m_pixels ; tex_pitch = lockInfo.m_picth ; pRes->m_tex_w = width ; pRes->m_tex_h = height; #else pTexture = m_pTexture; pRes->m_tex_idx = m_idxManager.useIndex(); pRes->m_tex_y = pRes->m_tex_idx / m_nCharOfRow; int nFontWidth = m_tex_w / m_nCharOfRow; int nFontHeight = m_tex_h / m_nCharOfRow; pRes->m_tex_y = nFontHeight * pRes->m_tex_y; pRes->m_tex_x = (pRes->m_tex_idx % m_nCharOfRow ) * nFontWidth; pRes->m_tex_w = width ; pRes->m_tex_h = height; xTextureLockArea lockInfo; pTexture->lock(eLock_WriteDiscard , lockInfo); tex_pixel = (unsigned char*)lockInfo.m_pixels ; tex_pitch = lockInfo.m_picth ; if(pTexture->format() == PIXELFORMAT_B8G8R8A8) { tex_pixel += lockInfo.m_picth * pRes->m_tex_y + pRes->m_tex_x * 4; } else { tex_pixel += lockInfo.m_picth * pRes->m_tex_y + pRes->m_tex_x; } #endif float grayScale = 1.0f; if( m_bAntilias == false ) grayScale = 1.3f; //灰度图 if(bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) { for(int y=0; y < height ; y++) { for(int x=0; x < width; x++) { int _y = y; unsigned char _vl = 0; if(x < bitmap.width && y < bitmap.rows) { _vl = (x>=bitmap.width || y>=bitmap.rows) ? 0 : bitmap.buffer[x + bitmap.width*y]; } //if(_vl > 25 && _vl < 220) _vl = 220; unsigned int iVal = _vl; iVal = (int)(grayScale * iVal); if(iVal > 255) iVal = 255; if(pTexture->format() == PIXELFORMAT_B8G8R8A8) { tex_pixel[(4*x + _y * tex_pitch)+0] = 0xff; tex_pixel[(4*x + _y * tex_pitch)+1] = 0xff; tex_pixel[(4*x + _y * tex_pitch)+2] = 0xff; tex_pixel[(4*x + _y * tex_pitch)+3] = (unsigned char)iVal; } else { tex_pixel[(1*x + _y * tex_pitch)+0] = (unsigned char)iVal; } } } } else if(bitmap.pixel_mode == FT_PIXEL_MODE_MONO) //单色图 { for(int y=0; y < height ; y++) { for(int x=0; x < width; x++) { int _y = y; unsigned char _vl = 0; if(x < bitmap.width && y < bitmap.rows) { _vl = ((bitmap.buffer[(y * bitmap.pitch) + x / 8] << (x % 8)) & 0x80) ? 0xFFFFFFFF : 0x00000000; } unsigned int iVal = _vl; iVal = (int)(grayScale * iVal); if(iVal > 255) iVal = 255; if(pTexture->format() == PIXELFORMAT_B8G8R8A8) { tex_pixel[(4*x + _y * tex_pitch)+0] = 0xff; tex_pixel[(4*x + _y * tex_pitch)+1] = 0xff; tex_pixel[(4*x + _y * tex_pitch)+2] = 0xff; tex_pixel[(4*x + _y * tex_pitch)+3] = (unsigned char)iVal; } else { tex_pixel[(1*x + _y * tex_pitch)+0] = (unsigned char)iVal; } } } } pTexture->unlock(lockInfo); pTexture->validate(); FT_Done_Glyph(glyph); ResSize = 1; return true; }
void agg_text_renderer<T>::render(glyph_positions const& pos) { glyphs_.clear(); prepare_glyphs(pos); FT_Error error; FT_Vector start; FT_Vector start_halo; int height = pixmap_.height(); pixel_position const& base_point = pos.get_base_point(); start.x = static_cast<FT_Pos>(base_point.x * (1 << 6)); start.y = static_cast<FT_Pos>((height - base_point.y) * (1 << 6)); start_halo = start; start.x += transform_.tx * 64; start.y += transform_.ty * 64; start_halo.x += halo_transform_.tx * 64; start_halo.y += halo_transform_.ty * 64; FT_Matrix halo_matrix; halo_matrix.xx = halo_transform_.sx * 0x10000L; halo_matrix.xy = halo_transform_.shx * 0x10000L; halo_matrix.yy = halo_transform_.sy * 0x10000L; halo_matrix.yx = halo_transform_.shy * 0x10000L; FT_Matrix matrix; matrix.xx = transform_.sx * 0x10000L; matrix.xy = transform_.shx * 0x10000L; matrix.yy = transform_.sy * 0x10000L; matrix.yx = transform_.shy * 0x10000L; // default formatting double halo_radius = 0; color black(0,0,0); unsigned fill = black.rgba(); unsigned halo_fill = black.rgba(); double text_opacity = 1.0; double halo_opacity = 1.0; for (auto const& glyph : glyphs_) { halo_fill = glyph.properties.halo_fill.rgba(); halo_opacity = glyph.properties.halo_opacity; halo_radius = glyph.properties.halo_radius * scale_factor_; // make sure we've got reasonable values. if (halo_radius <= 0.0 || halo_radius > 1024.0) continue; FT_Glyph g; error = FT_Glyph_Copy(glyph.image, &g); if (!error) { FT_Glyph_Transform(g, &halo_matrix, &start_halo); if (rasterizer_ == HALO_RASTERIZER_FULL) { stroker_->init(halo_radius); FT_Glyph_Stroke(&g, stroker_->get(), 1); error = FT_Glyph_To_Bitmap(&g, FT_RENDER_MODE_NORMAL, 0, 1); if (!error) { FT_BitmapGlyph bit = reinterpret_cast<FT_BitmapGlyph>(g); composite_bitmap(pixmap_, &bit->bitmap, halo_fill, bit->left, height - bit->top, halo_opacity, halo_comp_op_); } } else { error = FT_Glyph_To_Bitmap(&g, FT_RENDER_MODE_NORMAL, 0, 1); if (!error) { FT_BitmapGlyph bit = reinterpret_cast<FT_BitmapGlyph>(g); render_halo(&bit->bitmap, halo_fill, bit->left, height - bit->top, halo_radius, halo_opacity, halo_comp_op_); } } } FT_Done_Glyph(g); } // render actual text for (auto & glyph : glyphs_) { fill = glyph.properties.fill.rgba(); text_opacity = glyph.properties.text_opacity; FT_Glyph_Transform(glyph.image, &matrix, &start); error = FT_Glyph_To_Bitmap(&glyph.image ,FT_RENDER_MODE_NORMAL, 0, 1); if (!error) { FT_BitmapGlyph bit = reinterpret_cast<FT_BitmapGlyph>(glyph.image); composite_bitmap(pixmap_, &bit->bitmap, fill, bit->left, height - bit->top, text_opacity, comp_op_); } FT_Done_Glyph(glyph.image); } }
YUV_error text_to_overlay_player(p_info_rec* p_info, overlay* ovly, char* text, int max_width, int min_width, int x_margin, int y_margin, int center, int tab_width, int enable_align_right, char* font, const int size, const int aspect_ratio_num, const int aspect_ratio_den) { info_rec* info; BYTE* srcLine; BYTE* dstLine; int j; int error; int result; // initialise our data if (*p_info == NULL) { result = allocate_info(p_info); if (result < 0) return result; } info = *p_info; result = set_font(info, font, size, aspect_ratio_num, aspect_ratio_den); if (result < 0) return result; // render text using freetype2 { #define MAX_GLYPHS 100 FT_GlyphSlot slot = info->face->glyph; /* a small shortcut */ FT_UInt glyph_idx; FT_UInt last_glyph; FT_Vector delta; FT_Bool use_kerning; int pen_x, pen_y, n, margin; FT_Glyph glyphs[MAX_GLYPHS]; /* glyph image */ FT_Vector pos [MAX_GLYPHS]; /* glyph position */ FT_UInt num_glyphs; FT_UInt render_num_glyphs; FT_UInt vis_last_glyph; int vis_last; // index of end of last "word" int vis_width; // width to vis_last int x_offset = 0; int align_right = 0; int align_right_glyph = 0; int align_right_shift; use_kerning = FT_HAS_KERNING(info->face); // compute initial pen position using font metrics n = info->face->bbox.yMax - info->face->bbox.yMin;//info->face->ascender - info->face->descender; pen_x = ((-info->face->bbox.xMin * size) + (n / 2)) / n; pen_y = ((info->face->bbox.yMax * size) + (n / 2)) / n; margin = pen_x; // convert each character to a glyph and set its position num_glyphs = 0; vis_last = -1; vis_last_glyph = 0; vis_width = 0; last_glyph = FT_Get_Char_Index(info->face, ' '); for (n = 0; n < (int)strlen(text); n++) { if (text[n] == '\n') break; if (num_glyphs >= MAX_GLYPHS) break; if (tab_width > 0 && text[n] == '\t') { pen_x += size - (pen_x % size); } // a '>>' sequence results in alignment of the following text to the right else if (enable_align_right && !align_right && text[n] == '>' && text[n + 1] == '>') { n += 1; align_right = 1; align_right_glyph = num_glyphs; continue; } else { glyph_idx = FT_Get_Char_Index(info->face, text[n]); if (use_kerning && last_glyph && glyph_idx) { FT_Get_Kerning(info->face, last_glyph, glyph_idx, FT_KERNING_DEFAULT, &delta); pen_x += delta.x / 64; } error = FT_Load_Glyph(info->face, glyph_idx, FT_LOAD_DEFAULT); if (error) continue; /* ignore errors */ error = FT_Get_Glyph(info->face->glyph, &glyphs[num_glyphs]); if (error) continue; /* ignore errors */ if (pen_y - slot->bitmap_top < 0) { fprintf(stderr, "Character '%c' ascends too high\n", text[n]); pen_y = slot->bitmap_top; } if (pen_y - slot->bitmap_top + slot->bitmap.rows > size) { fprintf(stderr, "Character '%c' descends too low\n", text[n]); exit(1); } pos[num_glyphs].x = pen_x; pos[num_glyphs].y = pen_y; pen_x += slot->advance.x / 64; last_glyph = glyph_idx; num_glyphs++; } if (pen_x + (margin * 2) + (x_margin * 2) > max_width) break; if (text[n] != ' ' && (text[n+1] == ' ' || text[n+1] == '\0' || text[n+1] == '\n')) { vis_last_glyph = num_glyphs; vis_last = n; vis_width = pen_x; } } // truncate string to end of last word if (vis_last < 0) // haven't found end of first word! { vis_last_glyph = num_glyphs; vis_last = n - 1; vis_width = pen_x; } if (vis_width + (margin * 2) + (x_margin * 2) > max_width) { vis_last_glyph--; vis_last--; vis_width -= slot->advance.x / 64; } render_num_glyphs = vis_last_glyph; result = vis_last + 1; // do right alignment if there is space if (align_right) { align_right_shift = max_width - (vis_width + (margin * 2) + (x_margin * 2)); if (align_right_shift > 0) { for (n = align_right_glyph; n < (int)render_num_glyphs; n++) { pos[n].x += align_right_shift; } } } // find start of next word while (text[result] == ' ' || text[result] == '\n') result++; // set overlay dimensions ovly->w = vis_width + (margin * 2) + (x_margin * 2); // set width >= min_width and center if (ovly->w < min_width && min_width <= max_width) { if (center) { x_offset = (min_width - ovly->w) / 2; } ovly->w = min_width; } ovly->h = size + 2 * y_margin; // fprintf(stderr, "Area of '%s' = (%d x %d)\n", text, ovly->w, ovly->h); ovly->ssx = -1; ovly->ssy = -1; // alloc memory ovly->buff = malloc(ovly->w * ovly->h * 2); if (ovly->buff == NULL) return YUV_no_memory; memset(ovly->buff, 0, ovly->w * ovly->h * 2); ovly->Cbuff = NULL; // render glyphs for (n = 0; n < (int)render_num_glyphs; n++) { error = FT_Glyph_To_Bitmap(&glyphs[n], FT_RENDER_MODE_NORMAL, 0, 1); if (!error) { FT_BitmapGlyph bit = (FT_BitmapGlyph)glyphs[n]; srcLine = bit->bitmap.buffer; dstLine = ovly->buff + ((pos[n].y - bit->top) * ovly->w) + pos[n].x + bit->left + x_margin + y_margin * ovly->w + x_offset; // TODO: fix the problem with offsets if (dstLine < ovly->buff) dstLine = ovly->buff; for (j = 0; j < bit->bitmap.rows; j++) { memcpy(dstLine, srcLine, bit->bitmap.width); srcLine += bit->bitmap.width; dstLine += ovly->w; } } } // cleanup glyphs for (n = 0; n < (int)num_glyphs; n++) { FT_Done_Glyph(glyphs[n]); } } return result; // length of string actually rendered }
FT_Get_Glyph( FT_GlyphSlot slot, FT_Glyph *aglyph ) { FT_Library library; FT_Error error; FT_Glyph glyph; const FT_Glyph_Class* clazz = 0; if ( !slot ) return FT_Err_Invalid_Slot_Handle; library = slot->library; if ( !aglyph ) return FT_Err_Invalid_Argument; /* if it is a bitmap, that's easy :-) */ if ( slot->format == FT_GLYPH_FORMAT_BITMAP ) clazz = &ft_bitmap_glyph_class; /* it it is an outline too */ else if ( slot->format == FT_GLYPH_FORMAT_OUTLINE ) clazz = &ft_outline_glyph_class; else { /* try to find a renderer that supports the glyph image format */ FT_Renderer render = FT_Lookup_Renderer( library, slot->format, 0 ); if ( render ) clazz = &render->glyph_class; } if ( !clazz ) { error = FT_Err_Invalid_Glyph_Format; goto Exit; } /* create FT_Glyph object */ error = ft_new_glyph( library, clazz, &glyph ); if ( error ) goto Exit; /* copy advance while converting it to 16.16 format */ glyph->advance.x = slot->advance.x << 10; glyph->advance.y = slot->advance.y << 10; /* now import the image from the glyph slot */ error = clazz->glyph_init( glyph, slot ); /* if an error occurred, destroy the glyph */ if ( error ) FT_Done_Glyph( glyph ); else *aglyph = glyph; Exit: return error; }
static GF_Glyph *ft_load_glyph(GF_FontReader *dr, u32 glyph_name) { GF_Glyph *glyph; u32 glyph_idx; FT_BBox bbox; FT_OutlineGlyph outline; ft_outliner outl; FT_Outline_Funcs ft_outl_funcs; FTBuilder *ftpriv = (FTBuilder *)dr->udta; if (!ftpriv->active_face || !glyph_name) return NULL; FT_Select_Charmap(ftpriv->active_face, FT_ENCODING_UNICODE); glyph_idx = FT_Get_Char_Index(ftpriv->active_face, glyph_name); /*missing glyph*/ if (!glyph_idx) { GF_LOG(GF_LOG_WARNING, GF_LOG_PARSER, ("[FreeType] Glyph not found for char %d in font %s (style %s)\n", glyph_name, ftpriv->active_face->family_name, ftpriv->active_face->style_name)); return NULL; } /*work in design units*/ FT_Load_Glyph(ftpriv->active_face, glyph_idx, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP); FT_Get_Glyph(ftpriv->active_face->glyph, (FT_Glyph*)&outline); #ifdef FT_GLYPH_FORMAT_OUTLINE /*oops not vectorial...*/ if (outline->root.format != FT_GLYPH_FORMAT_OUTLINE) return NULL; #endif GF_SAFEALLOC(glyph, GF_Glyph); GF_SAFEALLOC(glyph->path, GF_Path); /*setup outliner*/ ft_outl_funcs.shift = 0; ft_outl_funcs.delta = 0; ft_outl_funcs.move_to = ft_move_to; ft_outl_funcs.line_to = ft_line_to; ft_outl_funcs.conic_to = ft_conic_to; ft_outl_funcs.cubic_to = ft_cubic_to; outl.path = glyph->path; outl.ftpriv = ftpriv; /*FreeType is marvelous and gives back the right advance on space char !!!*/ FT_Outline_Decompose(&outline->outline, &ft_outl_funcs, &outl); FT_Glyph_Get_CBox((FT_Glyph) outline, ft_glyph_bbox_unscaled, &bbox); glyph->ID = glyph_name; glyph->utf_name = glyph_name; glyph->horiz_advance = ftpriv->active_face->glyph->metrics.horiAdvance; glyph->vert_advance = ftpriv->active_face->glyph->metrics.vertAdvance; /* glyph->x = bbox.xMin; glyph->y = bbox.yMax; */ glyph->width = ftpriv->active_face->glyph->metrics.width; glyph->height = ftpriv->active_face->glyph->metrics.height; FT_Done_Glyph((FT_Glyph) outline); return glyph; }
~glyph_t () { FT_Done_Glyph(image);}
void EMSCRIPTEN_KEEPALIVE c_Glyph_done(long glyph) { FT_Done_Glyph((FT_Glyph)glyph); }
//------------------------------------------------------------------------------------------------------- StringManager::BChar::~BChar() { FT_Done_Glyph(m_pGlyph); }
Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold) const { // The glyph to return Glyph glyph; // First, transform our ugly void* to a FT_Face FT_Face face = static_cast<FT_Face>(m_face); if (!face) return glyph; // Set the character size if (!setCurrentSize(characterSize)) return glyph; // Load the glyph corresponding to the code point if (FT_Load_Char(face, codePoint, FT_LOAD_TARGET_NORMAL | FT_LOAD_FORCE_AUTOHINT) != 0) return glyph; // Retrieve the glyph FT_Glyph glyphDesc; if (FT_Get_Glyph(face->glyph, &glyphDesc) != 0) return glyph; // Apply bold if necessary -- first technique using outline (highest quality) FT_Pos weight = 1 << 6; bool outline = (glyphDesc->format == FT_GLYPH_FORMAT_OUTLINE); if (bold && outline) { FT_OutlineGlyph outlineGlyph = (FT_OutlineGlyph)glyphDesc; FT_Outline_Embolden(&outlineGlyph->outline, weight); } // Convert the glyph to a bitmap (i.e. rasterize it) FT_Glyph_To_Bitmap(&glyphDesc, FT_RENDER_MODE_NORMAL, 0, 1); FT_Bitmap& bitmap = reinterpret_cast<FT_BitmapGlyph>(glyphDesc)->bitmap; // Apply bold if necessary -- fallback technique using bitmap (lower quality) if (bold && !outline) { FT_Bitmap_Embolden(static_cast<FT_Library>(m_library), &bitmap, weight, weight); } // Compute the glyph's advance offset glyph.advance = static_cast<float>(face->glyph->metrics.horiAdvance) / static_cast<float>(1 << 6); if (bold) glyph.advance += static_cast<float>(weight) / static_cast<float>(1 << 6); int width = bitmap.width; int height = bitmap.rows; if ((width > 0) && (height > 0)) { // Leave a small padding around characters, so that filtering doesn't // pollute them with pixels from neighbors const unsigned int padding = 1; // Get the glyphs page corresponding to the character size Page& page = m_pages[characterSize]; // Find a good position for the new glyph into the texture glyph.textureRect = findGlyphRect(page, width + 2 * padding, height + 2 * padding); // Make sure the texture data is positioned in the center // of the allocated texture rectangle glyph.textureRect.left += padding; glyph.textureRect.top += padding; glyph.textureRect.width -= 2 * padding; glyph.textureRect.height -= 2 * padding; // Compute the glyph's bounding box glyph.bounds.left = static_cast<float>(face->glyph->metrics.horiBearingX) / static_cast<float>(1 << 6); glyph.bounds.top = -static_cast<float>(face->glyph->metrics.horiBearingY) / static_cast<float>(1 << 6); glyph.bounds.width = static_cast<float>(face->glyph->metrics.width) / static_cast<float>(1 << 6); glyph.bounds.height = static_cast<float>(face->glyph->metrics.height) / static_cast<float>(1 << 6); // Extract the glyph's pixels from the bitmap m_pixelBuffer.resize(width * height * 4, 255); const Uint8* pixels = bitmap.buffer; if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { // Pixels are 1 bit monochrome values for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { // The color channels remain white, just fill the alpha channel std::size_t index = (x + y * width) * 4 + 3; m_pixelBuffer[index] = ((pixels[x / 8]) & (1 << (7 - (x % 8)))) ? 255 : 0; } pixels += bitmap.pitch; } } else { // Pixels are 8 bits gray levels for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { // The color channels remain white, just fill the alpha channel std::size_t index = (x + y * width) * 4 + 3; m_pixelBuffer[index] = pixels[x]; } pixels += bitmap.pitch; } } // Write the pixels to the texture unsigned int x = glyph.textureRect.left; unsigned int y = glyph.textureRect.top; unsigned int w = glyph.textureRect.width; unsigned int h = glyph.textureRect.height; page.texture.update(&m_pixelBuffer[0], w, h, x, y); } // Delete the FT glyph FT_Done_Glyph(glyphDesc); // Force an OpenGL flush, so that the font's texture will appear updated // in all contexts immediately (solves problems in multi-threaded apps) glCheck(glFlush()); // Done :) return glyph; }
//////////////////////////////////////////////////////////// /// Create a bitmap font from a font face and a characters set //////////////////////////////////////////////////////////// FT_Error FontLoader::CreateBitmapFont(FT_Face FontFace, unsigned int CharSize, std::wstring Charset, Font& LoadedFont) { // If no characters set has been provided, we take the default one (31 - 255 ASCII codes) if (Charset.empty()) Charset = ourDefaultCharset; // Always add these special characters Charset += L" \n\v\t"; // Clear the font's previous character map LoadedFont.myCharacters.clear(); // Let's find how many characters to put in each row to make them fit into a squared texture GLint MaxSize; GLCheck(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &MaxSize)); int NbChars = static_cast<int>(sqrt(static_cast<double>(Charset.length())) * 0.75); // Clamp the character size to make sure we won't create a texture too big if (NbChars * CharSize >= static_cast<unsigned int>(MaxSize)) CharSize = MaxSize / NbChars; // Initialize the dimensions unsigned int Left = 0; unsigned int Top = 0; unsigned int TexWidth = Image::GetValidTextureSize(CharSize * NbChars); unsigned int TexHeight = Image::GetValidTextureSize(CharSize * NbChars); std::vector<unsigned int> Tops(TexWidth, 0); // Create the bitmap font (all black for now -- we're going to fill it up) LoadedFont.myTexture.Create(TexWidth, TexHeight, Color(0, 0, 0, 0)); // Setup the font size FT_Error Error = FT_Set_Pixel_Sizes(FontFace, CharSize, CharSize); if (Error) return Error; // Select the unicode character map Error = FT_Select_Charmap(FontFace, FT_ENCODING_UNICODE); if (Error) return Error; // Render the characters set to a bitmap std::map<wchar_t, IntRect> Coords; for (std::size_t i = 0; i < Charset.length(); ++i) { // Get the current character to generate wchar_t c = Charset[i]; Font::Character& CurChar = LoadedFont.myCharacters[c]; // Load the glyph corresponding to the current character Error = FT_Load_Char(FontFace, c, FT_LOAD_DEFAULT); if (Error) return Error; // Convert the glyph to a bitmap (ie. rasterize it) FT_Glyph Glyph; Error = FT_Get_Glyph(FontFace->glyph, &Glyph); if (Error) return Error; FT_Glyph_To_Bitmap(&Glyph, ft_render_mode_normal, 0, 1); FT_BitmapGlyph BitmapGlyph = (FT_BitmapGlyph)Glyph; FT_Bitmap& Bitmap = BitmapGlyph->bitmap; // Should we handle other pixel modes ? if (Bitmap.pixel_mode != FT_PIXEL_MODE_GRAY) return FT_Err_Cannot_Render_Glyph; // Make sure we don't go over the texture width if (Left + Bitmap.width + 1 >= TexWidth) Left = 0; // Compute the top coordinate Top = Tops[Left]; for (int x = 0; x < Bitmap.width + 1; ++x) Top = std::max(Top, Tops[Left + x]); Top++; // Make sure we don't go over the texture height -- resize it if we need more space if (Top + Bitmap.rows + 1 >= TexHeight) { TexHeight *= 2; LoadedFont.myTexture.Resize(TexWidth, TexHeight, Color(0, 0, 0, 0)); } // Store the character's position and size CurChar.Rect.Left = BitmapGlyph->left; CurChar.Rect.Top = -BitmapGlyph->top; CurChar.Rect.Right = CurChar.Rect.Left + Bitmap.width; CurChar.Rect.Bottom = Bitmap.rows - BitmapGlyph->top; CurChar.Advance = FontFace->glyph->advance.x / 64; // Texture size may change, so let the texture coordinates be calculated later Coords[c] = IntRect(Left + 1, Top + 1, Left + Bitmap.width + 1, Top + Bitmap.rows + 1); // Draw the glyph into our bitmap font const Uint8* Pixels = Bitmap.buffer; for (int y = 0; y < Bitmap.rows; ++y) { for (int x = 0; x < Bitmap.width; ++x) { LoadedFont.myTexture.SetPixel(x + Left + 1, y + Top + 1, Color(255, 255, 255, Pixels[x])); } Pixels += Bitmap.pitch; } // Update the rendering coordinates for (int x = 0; x < Bitmap.width + 1; ++x) Tops[Left + x] = Top + Bitmap.rows; Left += Bitmap.width + 1; // Delete the glyph FT_Done_Glyph(Glyph); } // Now that the texture has its final size, we can precompute texture coordinates for (std::size_t i = 0; i < Charset.size(); ++i) { wchar_t c = Charset[i]; LoadedFont.myCharacters[c].Coord = LoadedFont.myTexture.GetTexCoords(Coords[c], false); } // Update the character size (it may have been changed by the function) LoadedFont.myCharSize = CharSize; return 0; }
fz_pixmap * fz_renderftstrokedglyph(fz_font *font, int gid, fz_matrix trm, fz_matrix ctm, fz_strokestate *state) { FT_Face face = font->ftface; float expansion = fz_matrixexpansion(ctm); int linewidth = state->linewidth * expansion * 64 / 2; FT_Matrix m; FT_Vector v; FT_Error fterr; FT_Stroker stroker; FT_Glyph glyph; FT_BitmapGlyph bitmap; fz_pixmap *pix; int y; m.xx = trm.a * 64; /* should be 65536 */ m.yx = trm.b * 64; m.xy = trm.c * 64; m.yy = trm.d * 64; v.x = trm.e * 64; v.y = trm.f * 64; fterr = FT_Set_Char_Size(face, 65536, 65536, 72, 72); /* should be 64, 64 */ if (fterr) { fz_warn("FT_Set_Char_Size: %s", ft_errorstring(fterr)); return nil; } FT_Set_Transform(face, &m, &v); fterr = FT_Load_Glyph(face, gid, FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING); if (fterr) { fz_warn("FT_Load_Glyph(gid %d): %s", gid, ft_errorstring(fterr)); return nil; } fterr = FT_Stroker_New(fz_ftlib, &stroker); if (fterr) { fz_warn("FT_Stroker_New: %s", ft_errorstring(fterr)); return nil; } FT_Stroker_Set(stroker, linewidth, state->linecap, state->linejoin, state->miterlimit * 65536); fterr = FT_Get_Glyph(face->glyph, &glyph); if (fterr) { fz_warn("FT_Get_Glyph: %s", ft_errorstring(fterr)); FT_Stroker_Done(stroker); return nil; } fterr = FT_Glyph_Stroke(&glyph, stroker, 1); if (fterr) { fz_warn("FT_Glyph_Stroke: %s", ft_errorstring(fterr)); FT_Done_Glyph(glyph); FT_Stroker_Done(stroker); return nil; } fterr = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1); if (fterr) { fz_warn("FT_Glyph_To_Bitmap: %s", ft_errorstring(fterr)); FT_Done_Glyph(glyph); FT_Stroker_Done(stroker); return nil; } bitmap = (FT_BitmapGlyph)glyph; pix = fz_newpixmap(NULL, bitmap->left, bitmap->top - bitmap->bitmap.rows, bitmap->bitmap.width, bitmap->bitmap.rows); for (y = 0; y < pix->h; y++) { memcpy(pix->samples + y * pix->w, bitmap->bitmap.buffer + (pix->h - y - 1) * bitmap->bitmap.pitch, pix->w); } FT_Done_Glyph(glyph); FT_Stroker_Done(stroker); return pix; }
static say_glyph *say_font_load_glyph(say_font *font, say_font_page *page, uint32_t codepoint, uint8_t bold, size_t size) { uint32_t bold_codepoint = ((bold ? 1 : 0) << 31) | codepoint; say_glyph *glyph = malloc(sizeof(say_glyph)); say_table_set(page->glyphs, bold_codepoint, glyph); glyph->offset = 0; glyph->bounds = say_make_rect(2, 0, 2, 2); glyph->sub_rect = say_make_rect(2, 0, 2, 2); if (!(font->face && say_font_set_size(font, size))) return glyph; if (FT_Load_Char(font->face, codepoint, FT_LOAD_TARGET_NORMAL) != 0) return glyph; FT_Glyph ft_glyph; if (FT_Get_Glyph(font->face->glyph, &ft_glyph) != 0) return glyph; FT_Pos weight = 1 << 6; uint8_t outline = ft_glyph->format == FT_GLYPH_FORMAT_OUTLINE; if (bold && outline) { FT_OutlineGlyph outline_glyph = (FT_OutlineGlyph)ft_glyph; FT_Outline_Embolden(&outline_glyph->outline, weight); } FT_Glyph_To_Bitmap(&ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1); FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)ft_glyph; FT_Bitmap *bitmap = &bitmap_glyph->bitmap; if (bold && !outline) { FT_Bitmap_Embolden(font->library, bitmap, weight, weight); } glyph->offset = ft_glyph->advance.x >> 16; if (bold) glyph->offset += weight >> 6; int width = bitmap->width; int height = bitmap->rows; if (width > 0 && height > 0) { static const int padding = 1; glyph->sub_rect = say_page_find_rect(page, width + (2 * padding), height + (2 * padding)); glyph->bounds.x = +bitmap_glyph->left - padding; glyph->bounds.y = -bitmap_glyph->top - padding; glyph->bounds.w = width + (2 * padding); glyph->bounds.h = height + (2 * padding); say_rect actual_rect = glyph->sub_rect; actual_rect.x += padding; actual_rect.y += padding; actual_rect.w -= 2 * padding; actual_rect.h -= 2 * padding; uint8_t *pixels = bitmap->buffer; if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO) { for (int y = actual_rect.y; y < actual_rect.y + actual_rect.h; y++) { for (int x = actual_rect.x; x < actual_rect.x + actual_rect.w; x++) { int pixel_x = x - actual_rect.x; uint8_t alpha = ((pixels[pixel_x / 8]) & (1 << (7 - (pixel_x % 8)))) ? 255 : 0; say_image_set(page->image, x, y, say_make_color(255, 255, 255, alpha)); } pixels += bitmap->pitch; } } else { for (int y = actual_rect.y; y < actual_rect.y + actual_rect.h; y++) { for (int x = actual_rect.x; x < actual_rect.x + actual_rect.w; x++) { int pixel_x = x - actual_rect.x; say_image_set(page->image, x, y, say_make_color(255, 255, 255, pixels[pixel_x])); } pixels += bitmap->pitch; } } } FT_Done_Glyph(ft_glyph); return glyph; }
static FT_Error Render_Stroke( int num_indices, int first_index ) { int start_x, start_y, step_y, x, y; int i; FT_Size size; FT_Face face; FT_GlyphSlot slot; FT_Fixed radius; error = FTDemo_Get_Size( handle, &size ); if ( error ) { /* probably a non-existent bitmap font size */ return error; } INIT_SIZE( size, start_x, start_y, step_y, x, y ); face = size->face; slot = face->glyph; radius = status.radius * ( status.ptsize * status.res / 72 ); FT_Stroker_Set( handle->stroker, radius, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 ); for ( i = first_index; i < num_indices; i++ ) { int gindex; if ( handle->encoding == FT_ENCODING_NONE ) gindex = i; else gindex = FTDemo_Get_Index( handle, i ); error = FT_Load_Glyph( face, gindex, handle->load_flags | FT_LOAD_NO_BITMAP ); if ( !error && slot->format == FT_GLYPH_FORMAT_OUTLINE ) { FT_Glyph glyph; error = FT_Get_Glyph( slot, &glyph ); if ( error ) goto Next; error = FT_Glyph_Stroke( &glyph, handle->stroker, 1 ); if ( error ) { FT_Done_Glyph( glyph ); goto Next; } error = FTDemo_Draw_Glyph( handle, display, glyph, &x, &y ); if ( !error ) FT_Done_Glyph( glyph ); if ( error ) goto Next; else if ( X_TOO_LONG( x, size, display ) ) { x = start_x; y += step_y; if ( Y_TOO_LONG( y, size, display ) ) break; } } else Next: status.Fail++; } return error; }
bool Font::CreateFromFile( const char* path, const uint32_t font_size, const bool smooth, const uint32_t start_char, const size_t num_chars ) { if( path == NULL || font_size == 0 || num_chars == 0 || _num_glyphs > 0 ) return false; Mojo::Services::Filesystem* filesystem = MOJO_GET_SERVICE(Filesystem); Mojo::Filesystem::File* file = filesystem->Open(path, Mojo::Filesystem::FILE_READ); if( !file ) return false; size_t file_len = filesystem->Length(file); if( file_len == 0 ) { filesystem->Close(file); return false; } // todo: Use FT_Open_Face to read the file instead? uint8_t* buffer = new uint8_t[file_len]; { uint32_t num_bytes_read = 0; while( num_bytes_read < file_len ) { size_t read = filesystem->Read(file, file_len - num_bytes_read, (void*)&buffer[num_bytes_read]); mojo_assertf(read > 0, "Font::CreateFromFile\n -> Services/Filesystem error?\n"); num_bytes_read += read; } } filesystem->Close(file); FT_Face ft_face; if( FT_New_Memory_Face(Mojo::GetFreeTypeLibrary(), (const FT_Byte*)buffer, file_len, 0, &ft_face) ) { delete[] buffer; return false; } FT_Set_Char_Size(ft_face, font_size * 64, font_size * 64, 72, 72); const float line_height = ft_face->size->metrics.height >> 6; // Load the glyphs Font::Glyph* glyphs = new Font::Glyph[num_chars]; FT_Glyph* ft_glyphs = new FT_Glyph[num_chars]; FT_BitmapGlyph* bm_glyphs = (FT_BitmapGlyph*)ft_glyphs; float blo = 0.0f; uint32_t bm_max_width = 0, bm_max_height = 0; for( uint32_t i = 0; i < num_chars; ++i ) { FT_Load_Char(ft_face, start_char + i, FT_LOAD_DEFAULT); FT_Get_Glyph(ft_face->glyph, &ft_glyphs[i]); glyphs[i].x_advance = ft_face->glyph->advance.x >> 6; glyphs[i].y_advance = ft_face->glyph->advance.y >> 6; glyphs[i].x_bearing = (FT_HAS_VERTICAL(ft_face) ? ft_face->glyph->metrics.vertBearingX : ft_face->glyph->metrics.horiBearingX) >> 6; glyphs[i].y_bearing = (FT_HAS_VERTICAL(ft_face) ? ft_face->glyph->metrics.vertBearingY : ft_face->glyph->metrics.horiBearingY) >> 6; FT_Glyph_To_Bitmap(&ft_glyphs[i], smooth ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1); const FT_Bitmap bitmap = bm_glyphs[i]->bitmap; glyphs[i].width = bitmap.width; glyphs[i].height = bitmap.rows; bm_max_width = bm_max_width < bitmap.width ? bitmap.width : bm_max_width; bm_max_height = bm_max_height < bitmap.rows ? bitmap.rows : bm_max_height; const float gbl = -glyphs[i].y_bearing + glyphs[i].height; blo = (blo < gbl) ? gbl : blo; } // Determine the texture atlas size uint32_t atlas_width = 0, atlas_height = 0; { static const struct { uint32_t width, height, area; } atlas_sizes[] = { { 64, 64, 64 * 64 }, { 128, 128, 128 * 128 }, { 256, 256, 256 * 256 }, { 512, 512, 512 * 512 } }; uint32_t min_area = 0; for( uint32_t i = 0; i < num_chars; ++i ) min_area += uint32_t((glyphs[i].width + 2) * (bm_max_height + 2)); for( uint32_t i = 0; i < 4; ++i ) { if( atlas_sizes[i].area < min_area ) continue; const uint32_t index = (i > 3) ? 3 : i; atlas_width = atlas_sizes[index].width; atlas_height = atlas_sizes[index].height; break; } if( atlas_width == 0 || atlas_height == 0 ) { for( uint32_t i = 0; i < num_chars; ++i ) FT_Done_Glyph(ft_glyphs[i]); delete[] ft_glyphs; delete[] glyphs; FT_Done_Face(ft_face); delete[] buffer; return false; } } Mojo::TextureAtlas tex_atlas = Mojo::TextureAtlas(atlas_width, atlas_height, 32); Mojo::BookshelfTexturePacker tex_packer = Mojo::BookshelfTexturePacker(&tex_atlas); uint8_t* bm_buffer = new uint8_t[bm_max_width * bm_max_height * 4]; for( uint32_t i = 0; i < num_chars; ++i ) { const FT_Bitmap bitmap = bm_glyphs[i]->bitmap; if( bitmap.width == 0 || bitmap.rows == 0 ) continue; memset((void*)bm_buffer, 255, bm_max_width * bm_max_height * 4); switch( bitmap.pixel_mode ) { default: { mojo_assertf(0, "Font::CreateFromFile()\n -> Pixel mode not supported.\n"); } break; case FT_PIXEL_MODE_MONO: { const uint8_t* bm_pixels = (const uint8_t*)bitmap.buffer; const uint32_t y_offset = 0; for( uint32_t y = 0; y < bitmap.rows; ++y ) { for( uint32_t x = 0; x < bitmap.width; ++x ) bm_buffer[(x + y * bitmap.width) * 4 + 3] = ((bm_pixels[x / 8]) & (1 << (7 - (x % 8)))) ? 0xFF : 0x00; bm_pixels += bitmap.pitch; } } break; case FT_PIXEL_MODE_GRAY: { const uint8_t* bm_pixels = (const uint8_t*)bitmap.buffer; for( uint32_t y = 0; y < bitmap.rows; ++y ) { for( uint32_t x = 0; x < bitmap.width; ++x ) bm_buffer[(x + y * bitmap.width) * 4 + 3] = bm_pixels[x]; bm_pixels += bitmap.pitch; } } break; } Mojo::Recti packed_rect; if( !tex_packer.Pack(bitmap.width, bitmap.rows, 1, bm_buffer, packed_rect) ) { Mojo::DebugPrintf(DBG_WARNING, "Font::CreateFromFile()\n -> unable to pack character '%c'\n", i + start_char); } glyphs[i].tex_coords[0] = (float)packed_rect.x / atlas_width; glyphs[i].tex_coords[1] = (float)packed_rect.y / atlas_height; glyphs[i].tex_coords[2] = (float)(packed_rect.x + packed_rect.width) / atlas_width; glyphs[i].tex_coords[3] = (float)(packed_rect.y + packed_rect.height) / atlas_height; } delete[] bm_buffer; // Unload the glyphs for( uint32_t i = 0; i < num_chars; ++i ) FT_Done_Glyph(ft_glyphs[i]); delete[] ft_glyphs; FT_Done_Face(ft_face); delete[] buffer; // Update _font_atlas = tex_atlas.Compile(); _start_glyph = start_char; _num_glyphs = num_chars; _glyphs = glyphs; _base_line = bm_max_height - blo; _line_height = line_height; return true; }
static int LayoutLine( filter_t *p_filter, paragraph_t *p_paragraph, int i_start_offset, int i_end_offset, line_desc_t **pp_line ) { if( p_paragraph->i_size <= 0 || p_paragraph->i_runs_count <= 0 || i_start_offset >= i_end_offset || i_start_offset < 0 || i_start_offset >= p_paragraph->i_size || i_end_offset <= 0 || i_end_offset > p_paragraph->i_size ) { msg_Err( p_filter, "LayoutLine() invalid parameters. " "Paragraph size: %d. Runs count: %d. " "Start offset: %d. End offset: %d", p_paragraph->i_size, p_paragraph->i_runs_count, i_start_offset, i_end_offset ); return VLC_EGENERIC; } line_desc_t *p_line = NewLine( i_end_offset - i_start_offset ); if( !p_line ) return VLC_ENOMEM; filter_sys_t *p_sys = p_filter->p_sys; int i_last_run = -1; run_desc_t *p_run = 0; text_style_t *p_style = 0; FT_Face p_face = 0; FT_Vector pen = { .x = 0, .y = 0 }; int i_line_index = 0; int i_font_width = 0; int i_ul_offset = 0; int i_ul_thickness = 0; #ifdef HAVE_FRIBIDI fribidi_reorder_line( 0, p_paragraph->p_types + i_start_offset, i_end_offset - i_start_offset, 0, p_paragraph->paragraph_type, p_paragraph->p_levels + i_start_offset, 0, p_paragraph->pi_reordered_indices + i_start_offset ); #endif for( int i = i_start_offset; i < i_end_offset; ++i, ++i_line_index ) { int i_paragraph_index; #ifdef HAVE_FRIBIDI i_paragraph_index = p_paragraph->pi_reordered_indices[ i ]; #else i_paragraph_index = i; #endif line_character_t *p_ch = p_line->p_character + i_line_index; glyph_bitmaps_t *p_bitmaps = p_paragraph->p_glyph_bitmaps + i_paragraph_index; if( !p_bitmaps->p_glyph ) { --i_line_index; continue; } if( i_last_run != p_paragraph->pi_run_ids[ i_paragraph_index ] ) { i_last_run = p_paragraph->pi_run_ids[ i_paragraph_index ]; p_run = p_paragraph->p_runs + i_last_run; p_style = p_run->p_style; p_face = p_run->p_face; i_font_width = p_style->i_style_flags & STYLE_HALFWIDTH ? p_style->i_font_size / 2 : p_style->i_font_size; } FT_Vector pen_new = { .x = pen.x + p_paragraph->p_glyph_bitmaps[ i_paragraph_index ].i_x_offset, .y = pen.y + p_paragraph->p_glyph_bitmaps[ i_paragraph_index ].i_y_offset }; FT_Vector pen_shadow = { .x = pen_new.x + p_sys->f_shadow_vector_x * ( i_font_width << 6 ), .y = pen_new.y + p_sys->f_shadow_vector_y * ( p_style->i_font_size << 6 ) }; if( p_bitmaps->p_shadow ) { if( FT_Glyph_To_Bitmap( &p_bitmaps->p_shadow, FT_RENDER_MODE_NORMAL, &pen_shadow, 0 ) ) p_bitmaps->p_shadow = 0; else FT_Glyph_Get_CBox( p_bitmaps->p_shadow, ft_glyph_bbox_pixels, &p_bitmaps->shadow_bbox ); } if( p_bitmaps->p_glyph ) { if( FT_Glyph_To_Bitmap( &p_bitmaps->p_glyph, FT_RENDER_MODE_NORMAL, &pen_new, 1 ) ) { FT_Done_Glyph( p_bitmaps->p_glyph ); if( p_bitmaps->p_outline ) FT_Done_Glyph( p_bitmaps->p_outline ); if( p_bitmaps->p_shadow ) FT_Done_Glyph( p_bitmaps->p_shadow ); --i_line_index; continue; } else FT_Glyph_Get_CBox( p_bitmaps->p_glyph, ft_glyph_bbox_pixels, &p_bitmaps->glyph_bbox ); } if( p_bitmaps->p_outline ) { if( FT_Glyph_To_Bitmap( &p_bitmaps->p_outline, FT_RENDER_MODE_NORMAL, &pen_new, 1 ) ) { FT_Done_Glyph( p_bitmaps->p_outline ); p_bitmaps->p_outline = 0; } else FT_Glyph_Get_CBox( p_bitmaps->p_outline, ft_glyph_bbox_pixels, &p_bitmaps->outline_bbox ); } FixGlyph( p_bitmaps->p_glyph, &p_bitmaps->glyph_bbox, p_bitmaps->i_x_advance, p_bitmaps->i_y_advance, &pen_new ); if( p_bitmaps->p_outline ) FixGlyph( p_bitmaps->p_outline, &p_bitmaps->outline_bbox, p_bitmaps->i_x_advance, p_bitmaps->i_y_advance, &pen_new ); if( p_bitmaps->p_shadow ) FixGlyph( p_bitmaps->p_shadow, &p_bitmaps->shadow_bbox, p_bitmaps->i_x_advance, p_bitmaps->i_y_advance, &pen_shadow ); int i_line_offset = 0; int i_line_thickness = 0; text_style_t *p_glyph_style = p_paragraph->pp_styles[ i_paragraph_index ]; if( p_glyph_style->i_style_flags & (STYLE_UNDERLINE | STYLE_STRIKEOUT) ) { i_line_offset = abs( FT_FLOOR( FT_MulFix( p_face->underline_position, p_face->size->metrics.y_scale ) ) ); i_line_thickness = abs( FT_CEIL( FT_MulFix( p_face->underline_thickness, p_face->size->metrics.y_scale ) ) ); if( p_glyph_style->i_style_flags & STYLE_STRIKEOUT ) { /* Move the baseline to make it strikethrough instead of * underline. That means that strikethrough takes precedence */ i_line_offset -= abs( FT_FLOOR( FT_MulFix( p_face->descender * 2, p_face->size->metrics.y_scale ) ) ); } else if( i_line_thickness > 0 ) { p_bitmaps->glyph_bbox.yMin = __MIN( p_bitmaps->glyph_bbox.yMin, - i_line_offset - i_line_thickness ); /* The real underline thickness and position are * updated once the whole line has been parsed */ i_ul_offset = __MAX( i_ul_offset, i_line_offset ); i_ul_thickness = __MAX( i_ul_thickness, i_line_thickness ); i_line_thickness = -1; } } p_ch->p_glyph = ( FT_BitmapGlyph ) p_bitmaps->p_glyph; p_ch->p_outline = ( FT_BitmapGlyph ) p_bitmaps->p_outline; p_ch->p_shadow = ( FT_BitmapGlyph ) p_bitmaps->p_shadow; bool b_karaoke = p_paragraph->pi_karaoke_bar[ i_paragraph_index ] != 0; p_ch->i_color = b_karaoke ? ( uint32_t ) p_glyph_style->i_karaoke_background_color | p_glyph_style->i_karaoke_background_alpha << 24 : ( uint32_t ) p_glyph_style->i_font_color | p_glyph_style->i_font_alpha << 24; p_ch->i_line_thickness = i_line_thickness; p_ch->i_line_offset = i_line_offset; BBoxEnlarge( &p_line->bbox, &p_bitmaps->glyph_bbox ); if( p_bitmaps->p_outline ) BBoxEnlarge( &p_line->bbox, &p_bitmaps->outline_bbox ); if( p_bitmaps->p_shadow ) BBoxEnlarge( &p_line->bbox, &p_bitmaps->shadow_bbox ); pen.x += p_bitmaps->i_x_advance; pen.y += p_bitmaps->i_y_advance; } p_line->i_width = __MAX( 0, p_line->bbox.xMax - p_line->bbox.xMin ); p_line->i_height = __MAX( 0, p_line->bbox.yMax - p_line->bbox.yMin ); p_line->i_character_count = i_line_index; if( i_ul_thickness > 0 ) { for( int i = 0; i < p_line->i_character_count; i++ ) { line_character_t *ch = &p_line->p_character[i]; if( ch->i_line_thickness < 0 ) { ch->i_line_offset = i_ul_offset; ch->i_line_thickness = i_ul_thickness; } } } *pp_line = p_line; return VLC_SUCCESS; } static int LayoutParagraph( filter_t *p_filter, paragraph_t *p_paragraph, int i_max_pixel_width, line_desc_t **pp_lines ) { if( p_paragraph->i_size <= 0 || p_paragraph->i_runs_count <= 0 ) { msg_Err( p_filter, "LayoutParagraph() invalid parameters. " "Paragraph size: %d. Runs count %d", p_paragraph->i_size, p_paragraph->i_runs_count ); return VLC_EGENERIC; } int i_line_start = 0; FT_Pos i_width = 0; FT_Pos i_max_width = i_max_pixel_width << 6; FT_Pos i_preferred_width = 0; FT_Pos i_total_width = 0; FT_Pos i_last_space_width = 0; int i_last_space = -1; line_desc_t *p_first_line = 0; line_desc_t **pp_line = &p_first_line; for( int i = 0; i < p_paragraph->i_size; ++i ) { #ifdef HAVE_FRIBIDI p_paragraph->pi_reordered_indices[ i ] = i; #endif i_total_width += p_paragraph->p_glyph_bitmaps[ i ].i_x_advance; } int i_line_count = i_total_width / i_max_width + 1; i_preferred_width = i_total_width / i_line_count; for( int i = 0; i <= p_paragraph->i_size; ++i ) { if( i == p_paragraph->i_size ) { if( i_line_start < i ) if( LayoutLine( p_filter, p_paragraph, i_line_start, i, pp_line ) ) goto error; break; } if( p_paragraph->p_code_points[ i ] == ' ' #ifdef HAVE_FRIBIDI || p_paragraph->p_types[ i ] == FRIBIDI_TYPE_WS #endif ) { if( i_line_start == i ) { /* * Free orphaned white space glyphs not belonging to any lines. * At this point p_shadow points to either p_glyph or p_outline, * so we should not free it explicitly. */ if( p_paragraph->p_glyph_bitmaps[ i ].p_glyph ) FT_Done_Glyph( p_paragraph->p_glyph_bitmaps[ i ].p_glyph ); if( p_paragraph->p_glyph_bitmaps[ i ].p_outline ) FT_Done_Glyph( p_paragraph->p_glyph_bitmaps[ i ].p_outline ); i_line_start = i + 1; continue; } if( i_last_space == i - 1 ) { p_paragraph->p_glyph_bitmaps[ i - 1 ].i_x_advance = 0; i_last_space = i; continue; } i_last_space = i; i_last_space_width = i_width; } i_width += p_paragraph->p_glyph_bitmaps[ i ].i_x_advance; if( i_last_space_width >= i_preferred_width || i_width >= i_max_width ) { if( i_line_start == i ) { msg_Err( p_filter, "LayoutParagraph(): Width of single glyph exceeds maximum" ); goto error; } int i_end_offset; if( i_last_space > i_line_start ) i_end_offset = i_last_space; else i_end_offset = i; if( LayoutLine( p_filter, p_paragraph, i_line_start, i_end_offset, pp_line ) ) goto error; pp_line = &( *pp_line )->p_next; i_line_start = i_end_offset; i = i_line_start - 1; i_width = 0; i_last_space_width = 0; } } *pp_lines = p_first_line; return VLC_SUCCESS; error: for( int i = i_line_start; i < p_paragraph->i_size; ++i ) { if( p_paragraph->p_glyph_bitmaps[ i ].p_glyph ) FT_Done_Glyph( p_paragraph->p_glyph_bitmaps[ i ].p_glyph ); if( p_paragraph->p_glyph_bitmaps[ i ].p_outline ) FT_Done_Glyph( p_paragraph->p_glyph_bitmaps[ i ].p_outline ); } if( p_first_line ) FreeLines( p_first_line ); return VLC_EGENERIC; } int LayoutText( filter_t *p_filter, line_desc_t **pp_lines, FT_BBox *p_bbox, int *pi_max_face_height, uni_char_t *psz_text, text_style_t **pp_styles, uint32_t *pi_k_dates, int i_len ) { line_desc_t *p_first_line = 0; line_desc_t **pp_line = &p_first_line; paragraph_t *p_paragraph = 0; int i_paragraph_start = 0; int i_max_height = 0; for( int i = 0; i <= i_len; ++i ) { if( i == i_len || psz_text[ i ] == '\n' ) { if( i_paragraph_start == i ) { i_paragraph_start = i + 1; continue; } p_paragraph = NewParagraph( p_filter, i - i_paragraph_start, psz_text + i_paragraph_start, pp_styles + i_paragraph_start, pi_k_dates ? pi_k_dates + i_paragraph_start : 0, 20 ); if( !p_paragraph ) { if( p_first_line ) FreeLines( p_first_line ); return VLC_ENOMEM; } #ifdef HAVE_FRIBIDI if( AnalyzeParagraph( p_paragraph ) ) goto error; #endif if( ItemizeParagraph( p_filter, p_paragraph ) ) goto error; #if defined HAVE_HARFBUZZ if( ShapeParagraphHarfBuzz( p_filter, &p_paragraph ) ) goto error; if( LoadGlyphs( p_filter, p_paragraph, true, false ) ) goto error; #elif defined HAVE_FRIBIDI if( ShapeParagraphFriBidi( p_filter, p_paragraph ) ) goto error; if( LoadGlyphs( p_filter, p_paragraph, false, true ) ) goto error; if( RemoveZeroWidthCharacters( p_paragraph ) ) goto error; if( ZeroNsmAdvance( p_paragraph ) ) goto error; #else if( LoadGlyphs( p_filter, p_paragraph, false, true ) ) goto error; #endif /* * Set max line width to allow for outline and shadow glyphs, * and any extra width caused by visual reordering */ int i_max_width = ( int ) p_filter->fmt_out.video.i_visible_width - 2 * p_filter->p_sys->style.i_font_size; if( LayoutParagraph( p_filter, p_paragraph, i_max_width, pp_line ) ) goto error; FreeParagraph( p_paragraph ); p_paragraph = 0; for( ; *pp_line; pp_line = &( *pp_line )->p_next ) i_max_height = __MAX( i_max_height, ( *pp_line )->i_height ); i_paragraph_start = i + 1; } } int i_base_line = 0; FT_BBox bbox = { .xMin = INT_MAX, .yMin = INT_MAX, .xMax = INT_MIN, .yMax = INT_MIN }; for( line_desc_t *p_line = p_first_line; p_line; p_line = p_line->p_next ) { p_line->i_base_line = i_base_line; p_line->bbox.yMin -= i_base_line; p_line->bbox.yMax -= i_base_line; BBoxEnlarge( &bbox, &p_line->bbox ); i_base_line += i_max_height; } *pp_lines = p_first_line; *p_bbox = bbox; *pi_max_face_height = i_max_height; return VLC_SUCCESS; error: if( p_first_line ) FreeLines( p_first_line ); if( p_paragraph ) FreeParagraph( p_paragraph ); return VLC_EGENERIC; }
// ----------------------------------------------- texture_font_load_glyphs --- size_t texture_font_load_glyphs( texture_font_t * self, const wchar_t * charcodes ) { assert( self ); assert( charcodes ); size_t i, x, y, width, height, depth, w, h; FT_Library library; FT_Error error; FT_Face face; FT_Glyph ft_glyph; FT_GlyphSlot slot; FT_Bitmap ft_bitmap; FT_UInt glyph_index; texture_glyph_t *glyph; ivec4 region; size_t missed = 0; width = self->atlas->width; height = self->atlas->height; depth = self->atlas->depth; if( !texture_font_load_face( &library, self->filename, self->size, &face ) ) { return wcslen(charcodes); } /* Load each glyph */ for( i=0; i<wcslen(charcodes); ++i ) { glyph_index = FT_Get_Char_Index( face, charcodes[i] ); // WARNING: We use texture-atlas depth to guess if user wants // LCD subpixel rendering FT_Int32 flags = 0; if( self->outline_type > 0 ) { flags |= FT_LOAD_NO_BITMAP; } else { flags |= FT_LOAD_RENDER; } if( !self->hinting ) { flags |= FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT; } else { flags |= FT_LOAD_FORCE_AUTOHINT; } if( depth == 3 ) { FT_Library_SetLcdFilter( library, FT_LCD_FILTER_LIGHT ); flags |= FT_LOAD_TARGET_LCD; if( self->filtering ) { FT_Library_SetLcdFilterWeights( library, self->lcd_weights ); } } error = FT_Load_Glyph( face, glyph_index, flags ); if( error ) { fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n", __LINE__, FT_Errors[error].code, FT_Errors[error].message ); FT_Done_FreeType( library ); return wcslen(charcodes)-i; } int ft_bitmap_width = 0; int ft_bitmap_rows = 0; int ft_bitmap_pitch = 0; int ft_glyph_top = 0; int ft_glyph_left = 0; if( self->outline_type == 0 ) { slot = face->glyph; ft_bitmap = slot->bitmap; ft_bitmap_width = slot->bitmap.width; ft_bitmap_rows = slot->bitmap.rows; ft_bitmap_pitch = slot->bitmap.pitch; ft_glyph_top = slot->bitmap_top; ft_glyph_left = slot->bitmap_left; } else { FT_Stroker stroker; error = FT_Stroker_New( library, &stroker ); if( error ) { fprintf(stderr, "FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); return 0; } FT_Stroker_Set( stroker, (int)(self->outline_thickness *64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); error = FT_Get_Glyph( face->glyph, &ft_glyph); if( error ) { fprintf(stderr, "FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); return 0; } if( self->outline_type == 1 ) { error = FT_Glyph_Stroke( &ft_glyph, stroker, 1 ); } else if ( self->outline_type == 2 ) { error = FT_Glyph_StrokeBorder( &ft_glyph, stroker, 0, 1 ); } else if ( self->outline_type == 3 ) { error = FT_Glyph_StrokeBorder( &ft_glyph, stroker, 1, 1 ); } if( error ) { fprintf(stderr, "FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); return 0; } if( depth == 1) { error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1); if( error ) { fprintf(stderr, "FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); return 0; } } else { error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_LCD, 0, 1); if( error ) { fprintf(stderr, "FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); return 0; } } FT_BitmapGlyph ft_bitmap_glyph = (FT_BitmapGlyph) ft_glyph; ft_bitmap = ft_bitmap_glyph->bitmap; ft_bitmap_width = ft_bitmap.width; ft_bitmap_rows = ft_bitmap.rows; ft_bitmap_pitch = ft_bitmap.pitch; ft_glyph_top = ft_bitmap_glyph->top; ft_glyph_left = ft_bitmap_glyph->left; FT_Stroker_Done(stroker); } // We want each glyph to be separated by at least one black pixel // (for example for shader used in demo-subpixel.c) w = ft_bitmap_width/depth + 1; h = ft_bitmap_rows + 1; region = texture_atlas_get_region( self->atlas, w, h ); if ( region.x < 0 ) { missed++; fprintf( stderr, "Texture atlas is full (line %d)\n", __LINE__ ); continue; } w = w - 1; h = h - 1; x = region.x; y = region.y; texture_atlas_set_region( self->atlas, x, y, w, h, ft_bitmap.buffer, ft_bitmap.pitch ); glyph = texture_glyph_new( ); glyph->charcode = charcodes[i]; glyph->width = w; glyph->height = h; glyph->outline_type = self->outline_type; glyph->outline_thickness = self->outline_thickness; glyph->offset_x = ft_glyph_left; glyph->offset_y = ft_glyph_top; glyph->s0 = x/(float)width; glyph->t0 = y/(float)height; glyph->s1 = (x + glyph->width)/(float)width; glyph->t1 = (y + glyph->height)/(float)height; // Discard hinting to get advance FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER | FT_LOAD_NO_HINTING); slot = face->glyph; glyph->advance_x = slot->advance.x/64.0; glyph->advance_y = slot->advance.y/64.0; vector_push_back( self->glyphs, &glyph ); if( self->outline_type > 0 ) { FT_Done_Glyph( ft_glyph ); } } FT_Done_Face( face ); FT_Done_FreeType( library ); texture_atlas_upload( self->atlas ); texture_font_generate_kerning( self ); return missed; }
// Load a character glyph Font::Glyph Font::LoadGlyph( char ch, unsigned int size ) { Glyph glyph; // Ensure we can even retrieve the glyph if ( !_fontFace || !SetCurrentSize( size ) ) { return glyph; } // Attempt to load the font's glyph for the given character if ( 0 != FT_Load_Char( _myFontFace, ch, FT_LOAD_TARGET_NORMAL | FT_LOAD_FORCE_AUTOHINT ) ) { return glyph; } // Get the glyph's description FT_Glyph ftGlyph; if ( 0 != FT_Get_Glyph( _myFontFace->glyph, &ftGlyph ) ) { return glyph; } // Rasterize the glyph FT_Glyph_To_Bitmap( &ftGlyph, FT_RENDER_MODE_NORMAL, 0, 1 ); FT_Bitmap& bitmap = reinterpret_cast<FT_BitmapGlyph>( ftGlyph )->bitmap; // Get the glyph's advance glyph.Advance = static_cast<float>( _myFontFace->glyph->metrics.horiAdvance ) * KerningScale; int width = bitmap.width; int height = bitmap.rows; if ( ( width > 0 ) && ( height > 0 ) ) { // Get the glyph's page GlyphPage& page = _pages[ size ]; // Leave a small padding around characters, so that filtering doesn't // pollute them with pixels from neighbors const unsigned int padding = 1; // Find a good position for the new glyph into the texture UintRect emptyArea = FindGlyphRect( page, width + 2 * padding, height + 2 * padding );; // Ensure the texture data is in the center of the texture rectangle emptyArea.X += padding; emptyArea.Y += padding; emptyArea.Width -= 2 * padding; emptyArea.Height -= 2 * padding; glyph.TextureBounds = emptyArea; // Set the glyph's bounding box glyph.Bounds.X = ( _myFontFace->glyph->metrics.horiBearingX ) * KerningScale; glyph.Bounds.Y = -( _myFontFace->glyph->metrics.horiBearingY ) * KerningScale; glyph.Bounds.Width = ( _myFontFace->glyph->metrics.width ) * KerningScale; glyph.Bounds.Height = ( _myFontFace->glyph->metrics.height ) * KerningScale; // Extract the glyph's pixels from the bitmap _pixelBuffer.resize( width * height * 4, 255 ); const unsigned char* pixels = bitmap.buffer; if ( bitmap.pixel_mode == FT_PIXEL_MODE_MONO ) { // Pixels are 1 bit monochrome values for ( int y = 0; y < height; ++y ) { for ( int x = 0; x < width; ++x ) { // The color channels remain white, just fill the alpha channel std::size_t index = ( x + y * width ) * 4 + 3; _pixelBuffer[ index ] = ( ( pixels[ x / 8 ] ) & ( 1 << ( 7 - ( x % 8 ) ) ) ) ? 255 : 0; } pixels += bitmap.pitch; } } else { // Pixels are 8 bits gray levels for ( int y = 0; y < height; ++y ) { for ( int x = 0; x < width; ++x ) { // The color channels remain white, just fill the alpha channel std::size_t index = ( x + y * width ) * 4 + 3; _pixelBuffer[ index ] = pixels[ x ]; } pixels += bitmap.pitch; } } // Write the pixels to the texture page.Texture->UpdateArea( emptyArea.X, emptyArea.Y, emptyArea.Width, emptyArea.Height, &_pixelBuffer[ 0 ] ); } // Cleanup FT_Done_Glyph( ftGlyph ); return glyph; }
/** 转换FT_GlyphSlot类型数据为LCUI_FontBMP */ static int Convert_FTGlyph( LCUI_FontBMP *bmp, FT_GlyphSlot slot, int mode ) { int error; size_t size; FT_BitmapGlyph bitmap_glyph; FT_Glyph glyph; /* 从字形槽中提取一个字形图像 * 请注意,创建的FT_Glyph对象必须与FT_Done_Glyph成对使用 */ error = FT_Get_Glyph( slot, &glyph ); if(error) { return -1; } /*---------------------- 打印字体信息 -------------------------- printf(" width= %ld, met->height= %ld\n" "horiBearingX = %ld, horiBearingY = %ld, horiAdvance = %ld\n" "vertBearingX = %ld, vertBearingY = %ld, vertAdvance = %ld\n", slot->metrics.width>>6, slot->metrics.height>>6, slot->metrics.horiBearingX>>6, slot->metrics.horiBearingY>>6, slot->metrics.horiAdvance>>6, slot->metrics.vertBearingX>>6, slot->metrics.vertBearingY>>6, slot->metrics.vertAdvance>>6 ); ------------------------------------------------------------*/ if ( glyph->format != FT_GLYPH_FORMAT_BITMAP ) { error = FT_Glyph_To_Bitmap(&glyph, mode, 0 ,1); if(error) { return -1; } } bitmap_glyph = (FT_BitmapGlyph)glyph; /* * FT_Glyph_Metrics结构体中保存字形度量,通过face->glyph->metrics结 * 构访问,可得到字形的宽、高、左边界距、上边界距、水平跨距等等。 * 注意:因为不是所有的字体都包含垂直度量,当FT_HAS_VERTICAL为假时, * vertBearingX,vertBearingY和vertAdvance的值是不可靠的,目前暂不考虑 * 此情况的处理。 * */ bmp->top = bitmap_glyph->top; bmp->left = slot->metrics.horiBearingX>>6; bmp->rows = bitmap_glyph->bitmap.rows; bmp->width = bitmap_glyph->bitmap.width; bmp->advance.x = slot->metrics.horiAdvance>>6; /* 水平跨距 */ bmp->advance.y = slot->metrics.vertAdvance>>6; /* 垂直跨距 */ /* 分配内存,用于保存字体位图 */ size = bmp->rows * bmp->width * sizeof(uchar_t); bmp->buffer = (uchar_t*)malloc( size ); if( !bmp->buffer ) { FT_Done_Glyph(glyph); return -1; } switch( bitmap_glyph->bitmap.pixel_mode ) { /* 8位灰度位图,直接拷贝 */ case FT_PIXEL_MODE_GRAY: memcpy( bmp->buffer, bitmap_glyph->bitmap.buffer, size ); break; /* 单色点阵图,需要转换 */ case FT_PIXEL_MODE_MONO: { FT_Bitmap bitmap; FT_Library lib; FT_Int x, y; uchar_t *t, *s; lib = FontLIB_GetLibrary(); FT_Bitmap_New( &bitmap ); /* 转换位图bitmap_glyph->bitmap至bitmap,1个像素占1个字节 */ FT_Bitmap_Convert( lib, &bitmap_glyph->bitmap, &bitmap, 1); s = bitmap.buffer; t = bmp->buffer; for( y=0; y<bmp->rows; ++y ) { for( x=0; x<bmp->width; ++x ) { *t = *s?255:0; ++t,++s; } } FT_Bitmap_Done( lib, &bitmap ); break; } /* 其它像素模式的位图,暂时先直接填充255,等需要时再完善 */ default: memset( bmp->buffer, 255, size ); break; } FT_Done_Glyph(glyph); return size; }
int main(int argc, char *argv[]) { int i, j, err, size, first=' ', last='~', bitmapOffset = 0, x, y, byte; char *fontName, c, *ptr; FT_Library library; FT_Face face; FT_Glyph glyph; FT_Bitmap *bitmap; FT_BitmapGlyphRec *g; GFXglyph *table; uint8_t bit; // Parse command line. Valid syntaxes are: // fontconvert [filename] [size] // fontconvert [filename] [size] [last char] // fontconvert [filename] [size] [first char] [last char] // Unless overridden, default first and last chars are // ' ' (space) and '~', respectively if(argc < 3) { fprintf(stderr, "Usage: %s fontfile size [first] [last]\n", argv[0]); return 1; } size = atoi(argv[2]); if(argc == 4) { last = atoi(argv[3]); } else if(argc == 5) { first = atoi(argv[3]); last = atoi(argv[4]); } if(last < first) { i = first; first = last; last = i; } ptr = strrchr(argv[1], '/'); // Find last slash in filename if(ptr) ptr++; // First character of filename (path stripped) else ptr = argv[1]; // No path; font in local dir. // Allocate space for font name and glyph table if((!(fontName = malloc(strlen(ptr) + 20))) || (!(table = (GFXglyph *)malloc((last - first + 1) * sizeof(GFXglyph))))) { fprintf(stderr, "Malloc error\n"); return 1; } // Derive font table names from filename. Period (filename // extension) is truncated and replaced with the font size & bits. strcpy(fontName, ptr); ptr = strrchr(fontName, '.'); // Find last period (file ext) if(!ptr) ptr = &fontName[strlen(fontName)]; // If none, append // Insert font size and 7/8 bit. fontName was alloc'd w/extra // space to allow this, we're not sprintfing into Forbidden Zone. sprintf(ptr, "%dpt%db", size, (last > 127) ? 8 : 7); // Space and punctuation chars in name replaced w/ underscores. for(i=0; (c=fontName[i]); i++) { if(isspace(c) || ispunct(c)) fontName[i] = '_'; } // Init FreeType lib, load font if((err = FT_Init_FreeType(&library))) { fprintf(stderr, "FreeType init error: %d", err); return err; } if((err = FT_New_Face(library, argv[1], 0, &face))) { fprintf(stderr, "Font load error: %d", err); FT_Done_FreeType(library); return err; } // << 6 because '26dot6' fixed-point format FT_Set_Char_Size(face, size << 6, 0, DPI, 0); // Currently all symbols from 'first' to 'last' are processed. // Fonts may contain WAY more glyphs than that, but this code // will need to handle encoding stuff to deal with extracting // the right symbols, and that's not done yet. // fprintf(stderr, "%ld glyphs\n", face->num_glyphs); printf("package Giza.Bitmap_Fonts.%s is\n\n", fontName); printf(" %sBitmaps : aliased constant Font_Bitmap := (\n ", fontName); // Process glyphs and output huge bitmap data array for(i=first, j=0; i<=last; i++, j++) { // MONO renderer provides clean image with perfect crop // (no wasted pixels) via bitmap struct. if((err = FT_Load_Char(face, i, FT_LOAD_TARGET_MONO))) { fprintf(stderr, "Error %d loading char '%c'\n", err, i); continue; } if((err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_MONO))) { fprintf(stderr, "Error %d rendering char '%c'\n", err, i); continue; } if((err = FT_Get_Glyph(face->glyph, &glyph))) { fprintf(stderr, "Error %d getting glyph '%c'\n", err, i); continue; } bitmap = &face->glyph->bitmap; g = (FT_BitmapGlyphRec *)glyph; // Minimal font and per-glyph information is stored to // reduce flash space requirements. Glyph bitmaps are // fully bit-packed; no per-scanline pad, though end of // each character may be padded to next byte boundary // when needed. 16-bit offset means 64K max for bitmaps, // code currently doesn't check for overflow. (Doesn't // check that size & offsets are within bounds either for // that matter...please convert fonts responsibly.) table[j].bitmapOffset = bitmapOffset; table[j].width = bitmap->width; table[j].height = bitmap->rows; table[j].xAdvance = face->glyph->advance.x >> 6; table[j].xOffset = g->left; table[j].yOffset = 1 - g->top; for(y=0; y < bitmap->rows; y++) { for(x=0;x < bitmap->width; x++) { byte = x / 8; bit = 0x80 >> (x & 7); enbit(bitmap->buffer[ y * bitmap->pitch + byte] & bit); } } // Pad end of char bitmap to next byte boundary if needed int n = (bitmap->width * bitmap->rows) & 7; if(n) { // Pixel count not an even multiple of 8? n = 8 - n; // # bits to next multiple while(n--) enbit(0); } bitmapOffset += (bitmap->width * bitmap->rows + 7) / 8; FT_Done_Glyph(glyph); } printf(");\n\n"); // End bitmap array // Output glyph attributes table (one per character) printf(" %sGlyphs : aliased constant Glyph_Array := (\n", fontName); for(i=first, j=0; i<=last; i++, j++) { printf(" (%d, %d, %d, %d, %d, %d)", table[j].bitmapOffset, table[j].width, table[j].height, table[j].xAdvance, table[j].xOffset, table[j].yOffset); if(i < last) { printf(", -- 0x%02X", i); if((i >= ' ') && (i <= '~')) { printf(" '%c'", i); } putchar('\n'); } } printf("); -- 0x%02X", last); if((last >= ' ') && (last <= '~')) printf(" '%c'", last); printf("\n\n"); // Output font structure printf(" Font : Bitmap_Font :=\n"); printf(" (%sBitmaps'Access,\n", fontName); printf(" %sGlyphs'Access,\n", fontName); printf(" 16#%02X#,\n", first); printf(" 16#%02X#,\n", last); printf(" %ld);\n", face->size->metrics.height >> 6); printf("end Giza.Bitmap_Fonts.%s;\n", fontName); FT_Done_FreeType(library); return 0; }
Shape2D FontEngine_Freetype::load_glyph_outline(int c, int &out_advance_x) { out_advance_x = 0; FT_UInt glyph_index; glyph_index = FT_Get_Char_Index( face, FT_ULong(c) ); FT_Error error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); if ( error ) { throw Exception("freetype: error loading glyph"); } FT_Glyph glyph; error = FT_Get_Glyph( face->glyph, &glyph ); if ( error ) { throw Exception("freetype: error getting glyph"); } FT_OutlineGlyph ft_outline_glyph_rec = (FT_OutlineGlyph)glyph; FT_Outline ft_outline = ft_outline_glyph_rec->outline; Shape2D outline; // cl_write_console_line(string_format("Num contours: %1", ft_outline.n_contours)); for( int cont = 0; cont < ft_outline.n_contours; cont++ ) { // cl_write_console_line(string_format("Num points in contour %1: %2", cont, ft_outline.contours[0]+1)); Path2D contour; // debug: dump contents of points array to terminal // for( int i = 0; i <= ft_outline.contours[cont]; ++i ) // { // FT_Vector pos = ft_outline.points[i]; // cl_write_console_line(string_format("dump points[%1]: (%2,%3) \t type: %4", i, pos.x, pos.y, ft_outline.tags[i])); // } std::vector<TaggedPoint> points = get_contour_points(cont, &ft_outline); points.push_back(points.front()); // just to simplify, it's removed later. for( unsigned int i = 0; i < points.size()-1; i++ ) { TaggedPoint &tp = points[i]; if( tp.tag == FT_Curve_Tag_On ) { contour.add_line_to(tp.pos); } else if( tp.tag == FT_Curve_Tag_Conic ) { // TODO: i - 1 is safe here because we made sure the contour will start with a Tag_On. BezierCurve curve; curve.add_control_point( points[i-1].pos); curve.add_control_point( tp.pos ); curve.add_control_point( points[i+1].pos ); contour.add_curve(curve); } else if( tp.tag == FT_Curve_Tag_Cubic && points[i-1].tag == FT_Curve_Tag_Cubic ) { BezierCurve curve; curve.add_control_point( points[i-2].pos); curve.add_control_point( points[i-1].pos); curve.add_control_point( tp.pos ); curve.add_control_point( points[i+1].pos ); contour.add_curve(curve); } } outline.add_path(contour); } FT_Done_Glyph(glyph); out_advance_x = get_advance_x( c ); return outline; }
int text_to_overlay(p_info_rec* p_info, overlay* ovly, char* text, int max_width, char* font, const int size, const int aspect_ratio_num, const int aspect_ratio_den) { info_rec* info; BYTE* srcLine; BYTE* dstLine; int j; int error; int result; // initialise our data if (*p_info == NULL) { result = allocate_info(p_info); if (result < 0) return result; } info = *p_info; result = set_font(info, font, size, aspect_ratio_num, aspect_ratio_den); if (result < 0) return result; // render text using freetype2 { #define MAX_GLYPHS 100 FT_GlyphSlot slot = info->face->glyph; /* a small shortcut */ FT_UInt glyph_idx; FT_UInt last_glyph; FT_Vector delta; FT_Bool use_kerning; int pen_x, pen_y, n, margin; FT_Glyph glyphs[MAX_GLYPHS]; /* glyph image */ FT_Vector pos [MAX_GLYPHS]; /* glyph position */ FT_UInt num_glyphs; int vis_last; // index of end of last "word" int vis_width; // width to vis_last use_kerning = FT_HAS_KERNING(info->face); // font metrics are unreliable, so use actual rendered characters // set left margin using a capital M character glyph_idx = FT_Get_Char_Index(info->face, 'M'); FT_Load_Glyph(info->face, glyph_idx, FT_LOAD_DEFAULT); FT_Get_Glyph(info->face->glyph, &glyphs[0]); pen_x = slot->metrics.horiBearingX; margin = pen_x; FT_Done_Glyph(glyphs[0]); // set baseline using a capital A character glyph_idx = FT_Get_Char_Index(info->face, 'A'); FT_Load_Glyph(info->face, glyph_idx, FT_LOAD_DEFAULT); FT_Get_Glyph(info->face->glyph, &glyphs[0]); pen_y = slot->metrics.horiBearingY; FT_Done_Glyph(glyphs[0]); // convert each character to a glyph and set its position num_glyphs = 0; vis_last = -1; vis_width = 0; last_glyph = FT_Get_Char_Index(info->face, ' '); for (n = 0; n < (int)strlen(text); n++) { if (text[n] == '\n') break; if (num_glyphs >= MAX_GLYPHS) break; glyph_idx = FT_Get_Char_Index(info->face, text[n]); if (use_kerning && last_glyph && glyph_idx) { FT_Get_Kerning(info->face, last_glyph, glyph_idx, FT_KERNING_DEFAULT, &delta); pen_x += delta.x; } error = FT_Load_Glyph(info->face, glyph_idx, FT_LOAD_DEFAULT); if (error) continue; /* ignore errors */ error = FT_Get_Glyph(info->face->glyph, &glyphs[num_glyphs]); if (error) continue; /* ignore errors */ if (pen_y - slot->metrics.horiBearingY < 0) { fprintf(stderr, "Character '%c' ascends too high\n", text[n]); pen_y = slot->metrics.horiBearingY; } if (pen_y - slot->metrics.horiBearingY + slot->metrics.height > size * 64) { fprintf(stderr, "Character '%c' descends too low\n", text[n]); exit(1); } pos[num_glyphs].x = pen_x; pos[num_glyphs].y = -pen_y; pen_x += slot->advance.x; last_glyph = glyph_idx; num_glyphs++; if (pen_x + (margin * 2) > max_width * 64) break; if (text[n] != ' ' && (text[n+1] == ' ' || text[n+1] == '\0' || text[n+1] == '\n')) { vis_last = n; vis_width = pen_x; } } // truncate string to end of last word if (vis_last < 0) // haven't found end of first word! { vis_last = num_glyphs - 1; vis_width = pen_x; } num_glyphs = vis_last + 1; // find start of next word result = num_glyphs; while (text[result] == ' ' || text[result] == '\n') result++; // set overlay dimensions ovly->w = (vis_width + (margin * 2)) / 64; ovly->h = size; // fprintf(stderr, "Area of '%s' = (%d x %d)\n", text, ovly->w, ovly->h); ovly->ssx = -1; ovly->ssy = -1; // alloc memory ovly->buff = malloc(ovly->w * ovly->h * 2); if (ovly->buff == NULL) return YUV_no_memory; memset(ovly->buff, 0, ovly->w * ovly->h * 2); ovly->Cbuff = NULL; // render glyphs in pre-calculated positions for (n = 0; n < (int)num_glyphs; n++) { error = FT_Glyph_To_Bitmap(&glyphs[n], FT_RENDER_MODE_NORMAL, &pos[n], 1); if (!error) { FT_BitmapGlyph bit = (FT_BitmapGlyph)glyphs[n]; srcLine = bit->bitmap.buffer; dstLine = ovly->buff - (bit->top * ovly->w) + bit->left; for (j = 0; j < bit->bitmap.rows; j++) { memcpy(dstLine, srcLine, bit->bitmap.width); srcLine += bit->bitmap.width; dstLine += ovly->w; } } FT_Done_Glyph(glyphs[n]); } } return result; // length of string actually rendered }
/* * Renders a glyph immediately, then tries to puts it into cache. * rend and rend->face must be not 0 */ static GLYPH* _gk_rend_workout(GLYPH_REND* const rend,const unsigned unicode) { unsigned glyph_index; int bmp_size; int error; int center_x = 0,center_y = 0; FT_Glyph ft_glyph = 0; GLYPH* glyph; GLYPH_FACE* actual_face; unsigned actual_code; #ifdef GLYPH_LOG if (glyph_log) fprintf(glyph_log,"_gk_rend_workout(%p,%d) begin\n",(void*)rend,unicode); #endif CARE(rend && rend->face); CARE(unicode > 0); CARE(unicode <= GK_MAX_UNICODE); funcname = "_gk_rend_workout()"; _gk_find_mapping(rend->face,unicode,&actual_face,&actual_code); if (!actual_face || !actual_code) return 0; if (!actual_face->face) return 0; glyph_index = FT_Get_Char_Index(actual_face->face,actual_code); if (!glyph_index) { if (unicode==rend->undefined_char || unicode==rend->error_char) return 0; else return _gk_rend_render(rend,rend->undefined_char); } #ifdef GLYPH_LOG if (glyph_log) fprintf(glyph_log," glyph_index is %u\n",glyph_index); #endif /* Preparing for glyph loading: setting size */ /*FT_Activate_Size(size);*/ if (actual_face == rend->face) { error = (rend->size == 0); if (!error) actual_face->face->size = rend->size; } else { error = (actual_face->own_size == 0); if (!error) { actual_face->face->size = actual_face->own_size; error = FT_Set_Char_Size(actual_face->face,rend->hsize,rend->vsize,72,72); } } #ifdef GLYPH_LOG if (error) { if (glyph_log) fprintf(glyph_log," Failed to select size\n"); } else { if (glyph_log) fprintf(glyph_log," Size selected successfully\n"); } #endif /* Loading a glyph with FreeType */ if (!error) { error = FT_Load_Glyph(actual_face->face,glyph_index,rend->load_flags); if (error) _gk_msg("Error: %s: FT_Load_Glyph() bugs on glyph (#%d) for character U+%04X\n",funcname,glyph_index,unicode); else { error = (actual_face->face->glyph == 0); if (error) _gk_msg("Error: %s: Empty glyph slot after FT_Load_Glyph()\n",funcname); } /*#ifdef GLYPH_LOG if (error) { if (glyph_log) fprintf(glyph_log," Could not load a glyph with FT_Load_Glyph()\n"); } else { if (glyph_log) fprintf(glyph_log," Loaded a glyph with FT_Load_Glyph()\n"); } #endif*/ } /* Getting center coordinates (MEGA-HACK) */ /* This whole idea should be re-made in more optimal way */ if (!error) { /*#ifdef GLYPH_LOG if (glyph_log) fprintf(glyph_log," Getting a center point\n"); #endif*/ FT_Glyph g = 0; error = FT_Get_Glyph(actual_face->face->glyph, &g); /*#ifdef GLYPH_LOG if (glyph_log) { if (error) fprintf(glyph_log," FT_Get_Glyph() failed\n"); else fprintf(glyph_log," FT_Get_Glyph() succeeded\n"); if (g) fprintf(glyph_log," FT_Get_Glyph() returned not 0\n"); else fprintf(glyph_log," FT_Get_Glyph() returned 0\n"); } #endif*/ error = error || !g; if (!error && g->format==FT_GLYPH_FORMAT_OUTLINE) { /*#ifdef GLYPH_LOG if (glyph_log) fprintf(glyph_log," Calling FT_Glyph_To_Bitmap()\n"); #endif*/ error = FT_Glyph_To_Bitmap(&g,rend->render_mode,0,1); /*#ifdef GLYPH_LOG if (glyph_log) { if (error) fprintf(glyph_log," FT_Glyph_To_Bitmap() failed\n"); else fprintf(glyph_log," FT_Glyph_To_Bitmap() succeeded\n"); } #endif*/ if (!error) { center_x = 64 * ((FT_BitmapGlyph)g)->left + 64 * ((FT_BitmapGlyph)g)->bitmap.width / 2; center_y = 64 * ((FT_BitmapGlyph)g)->top - 64 * ((FT_BitmapGlyph)g)->bitmap.rows / 2; /*center_x = 64 * ((FT_BitmapGlyph)g)->bitmap.width / 2; center_y = -64 * ((FT_BitmapGlyph)g)->bitmap.rows / 2;*/ } } if (g) { /*#ifdef GLYPH_LOG if (glyph_log) fprintf(glyph_log," Calling FT_Done_Glyph()\n"); #endif*/ FT_Done_Glyph(g); } /*#ifdef GLYPH_LOG if (glyph_log) { if (error) fprintf(glyph_log," Could not find a center point\n"); else fprintf(glyph_log," Computed a center point\n"); } #endif*/ } /* Emboldening the glyph */ if (!error) { if (rend->bold_strength && actual_face->face->glyph->format==FT_GLYPH_FORMAT_OUTLINE) { int xstr, ystr, xstr2, ystr2; int xmin,xmax,ymin,ymax; int center_dx, center_dy; FT_GlyphSlot slot = actual_face->face->glyph; #ifdef GLYPH_LOG if (glyph_log) fprintf(glyph_log," Emboldening the glyph by %d\n",rend->bold_strength); #endif xstr = rend->bold_strength * FT_MulFix( actual_face->face->units_per_EM, actual_face->face->size->metrics.y_scale ) / 2400; ystr = xstr; #ifdef GLYPH_LOG if (glyph_log) fprintf(glyph_log," xstr = %d, ystr = %d\n",xstr,ystr); #endif GK_Outline_Embolden(&slot->outline,xstr,&xmin,&xmax,&ymin,&ymax); #ifdef GLYPH_LOG if (glyph_log) fprintf(glyph_log," xmin = %d, xmax = %d\n",xmin,xmax); if (glyph_log) fprintf(glyph_log," ymin = %d, ymax = %d\n",ymin,ymax); #endif /*xstr = xstr * 2; ystr = xstr;*/ #ifdef GLYPH_LOG if (glyph_log) fprintf(glyph_log," xstr = %d, ystr = %d\n",xstr,ystr); #endif xstr2 = xmin+xmax; ystr2 = ymin+ymax; #ifdef GLYPH_LOG if (glyph_log) fprintf(glyph_log," xstr2 = %d, ystr2 = %d\n",xstr2,ystr2); if (glyph_log) fprintf(glyph_log," xscale = %.5f\n",((double)actual_face->face->size->metrics.x_scale)/65536); if (glyph_log) fprintf(glyph_log," yscale = %.5f\n",((double)actual_face->face->size->metrics.y_scale)/65536); #endif if (slot->advance.x) slot->advance.x += xstr; if (slot->advance.y) slot->advance.y += ystr; slot->metrics.width += xstr * 2; slot->metrics.height += ystr * 2; /*slot->metrics.horiBearingX -= xmin;*/ slot->metrics.horiBearingY += ystr * 2; slot->metrics.horiAdvance += xstr * 2; slot->metrics.vertBearingX -= xstr; slot->metrics.vertBearingY += ystr * 2; slot->metrics.vertAdvance += ystr * 2; /*center_dx = (int)( (double)xstr / 2 * (double)actual_face->face->size->metrics.x_scale / (80000) ); center_dy = (int)( (double)ystr / 2 * (double)actual_face->face->size->metrics.y_scale / (65536) );*/ center_dx = (int)( (double)rend->bold_strength * (double)actual_face->face->size->metrics.x_ppem / 80); center_dy = (int)( (double)rend->bold_strength * (double)actual_face->face->size->metrics.y_ppem / 80); #ifdef GLYPH_LOG if (glyph_log) fprintf(glyph_log," center_x += %.1f\n",((double)center_dx)/64); if (glyph_log) fprintf(glyph_log," center_y += %.1f\n",((double)center_dx)/64); #endif center_x += center_dx; center_y += center_dy; /*center_x += xstr;*/ /*center_y += ymin;*/ /*FT_GlyphSlot_Embolden(slot);*/ } } /* Getting a glyph from FreeType */ if (!error) { error = FT_Get_Glyph(actual_face->face->glyph, &ft_glyph); error = error || !ft_glyph; if (error) _gk_msg("Error: %s: Can't get a glyph with FT_Get_Glyph() call. Glyph (#%d) for character U+%04X\n", funcname,glyph_index,unicode); } /* Transforming the glyph to apply rotation and italics */ if (!error && ft_glyph->format==FT_GLYPH_FORMAT_OUTLINE && rend->do_matrix_transform) { error = FT_Glyph_Transform(ft_glyph,&rend->matrix,0); if (!error) { FT_Vector c; c.x = center_x; c.y = center_y; FT_Vector_Transform(&c,&rend->matrix); center_x = c.x; center_y = c.y; } } /* Converting glyph to bitmap */ if (!error && ft_glyph->format==FT_GLYPH_FORMAT_OUTLINE) { error = FT_Glyph_To_Bitmap(&ft_glyph,rend->render_mode,0,1); if (error) _gk_msg("Error: %s: FT_Glyph_To_Bitmap() bugs on character U+%04X\n",funcname,unicode); } /* Checking if we have bitmap now */ if (!error) { error = (ft_glyph->format!=FT_GLYPH_FORMAT_BITMAP); if (error) _gk_msg("Error: %s: Glyph is not FT_GLYPH_FORMAT_BITMAP after rendering with FT_Glyph_To_Bitmap()\n",funcname); } #ifdef GLYPH_LOG /* { if (glyph_log) { fprintf(glyph_log," We got a bitmap glyph from FreeType!\n"); if (ft_glyph->format==FT_GLYPH_FORMAT_BITMAP) { fprintf(glyph_log," Format: FT_GLYPH_FORMAT_BITMAP\n"); fprintf(glyph_log," Size: %dx%d, pitch: %d\n", ((FT_BitmapGlyph)ft_glyph)->bitmap.width, ((FT_BitmapGlyph)ft_glyph)->bitmap.rows, ((FT_BitmapGlyph)ft_glyph)->bitmap.pitch); if (((FT_BitmapGlyph)ft_glyph)->bitmap.pixel_mode==FT_PIXEL_MODE_MONO) { int x,y; int pitch = ((FT_BitmapGlyph)ft_glyph)->bitmap.pitch; fprintf(glyph_log," Pixel mode: FT_PIXEL_MODE_MONO\n"); fprintf(glyph_log," Buffer:\n"); for (y=0; y < ((FT_BitmapGlyph)ft_glyph)->bitmap.rows; y++) { fprintf(glyph_log," "); for (x=0; x < ((FT_BitmapGlyph)ft_glyph)->bitmap.width; x++) { unsigned char byte = ((unsigned char*)((FT_BitmapGlyph)ft_glyph)->bitmap.buffer)[y*pitch+x]; unsigned char mask = 128 >> (x%8); fprintf(glyph_log,"%s",(byte&mask)?" *":" ."); } fprintf(glyph_log,"\n"); } } else if (((FT_BitmapGlyph)ft_glyph)->bitmap.pixel_mode==FT_PIXEL_MODE_GRAY) fprintf(glyph_log," Pixel mode: FT_PIXEL_MODE_GRAY\n"); else if (((FT_BitmapGlyph)ft_glyph)->bitmap.pixel_mode==FT_PIXEL_MODE_GRAY2) fprintf(glyph_log," Pixel mode: FT_PIXEL_MODE_GRAY2\n"); else if (((FT_BitmapGlyph)ft_glyph)->bitmap.pixel_mode==FT_PIXEL_MODE_GRAY4) fprintf(glyph_log," Pixel mode: FT_PIXEL_MODE_GRAY4\n"); else if (((FT_BitmapGlyph)ft_glyph)->bitmap.pixel_mode==FT_PIXEL_MODE_LCD) fprintf(glyph_log," Pixel mode: FT_PIXEL_MODE_LCD\n"); else if (((FT_BitmapGlyph)ft_glyph)->bitmap.pixel_mode==FT_PIXEL_MODE_LCD_V) fprintf(glyph_log," Pixel mode: FT_PIXEL_MODE_LCD_V\n"); } else if (ft_glyph->format==FT_GLYPH_FORMAT_OUTLINE) { fprintf(glyph_log," Format: FT_GLYPH_FORMAT_OUTLINE\n"); } fprintf(glyph_log," Advance: ( %.1f , %.1f ) pixels\n", ((double)ft_glyph->advance.x)/0x10000,((double)ft_glyph->advance.y)/0x10000); } }*/ #endif if (!error) { error = ( ((FT_BitmapGlyph)ft_glyph)->bitmap.pitch < 0 ); if (error) _gk_msg("Error: rend_workout(): Rendered glyph has negative pitch value, can't handle\n"); /* FIXME */ } if (!error) { glyph = (GLYPH*)_gk_malloc(sizeof(GLYPH)); error = (glyph == 0); } if (error || !ft_glyph) { if (ft_glyph) FT_Done_Glyph(ft_glyph); if (unicode==rend->error_char) return 0; else return _gk_rend_render(rend,rend->error_char); } glyph->unicode = unicode; glyph->width = ((FT_BitmapGlyph)ft_glyph)->bitmap.width; glyph->height = ((FT_BitmapGlyph)ft_glyph)->bitmap.rows; glyph->left = ((FT_BitmapGlyph)ft_glyph)->left; glyph->top = ((FT_BitmapGlyph)ft_glyph)->top; glyph->advance_x = ft_glyph->advance.x >> 10; glyph->advance_y = ft_glyph->advance.y >> 10; glyph->center_x = (center_x + 31) / 64; glyph->center_y = (center_y + 31) / 64; glyph->index = 0; glyph->prev = 0; glyph->next = 0; glyph->bmp = 0; glyph->bmpsize = 0; _gk_msg("rendering character '%c'\n",unicode); bmp_size = glyph->width*glyph->height; if (!bmp_size) /* empty glyph, like space (' ') character */ { if (rend->index) _gk_glyph_index_add_glyph(rend->index,glyph); FT_Done_Glyph(ft_glyph); return glyph; } if (((FT_BitmapGlyph)ft_glyph)->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { #ifdef GLYPH_TARGET_KNOWS_MONO_BITPACK int pitch1 = (glyph->width+7)>>3; int bitpack_size = glyph->height*pitch1; #ifdef GLYPH_TARGET_KNOWS_MONO_RLE7 int rle_size = 0; if (rend->index) rle_size = _gk_make_RLE7( ((FT_BitmapGlyph)ft_glyph)->bitmap.buffer, glyph->width, glyph->height, ((FT_BitmapGlyph)ft_glyph)->bitmap.pitch); if (rle_size>0 && rle_size<=bitpack_size) { glyph->bmp = (unsigned char*)_gk_malloc(rle_size); if (!glyph->bmp) { _gk_free(glyph); return 0; } memcpy(glyph->bmp,rle_buffer,rle_size); glyph->bmpsize = rle_size; } else #endif /* GLYPH_TARGET_KNOWS_MONO_RLE7 */ { glyph->bmp = (unsigned char*)_gk_malloc(bitpack_size+1); if (!glyph->bmp) { _gk_free(glyph); return 0; } glyph->bmp[0] = GLYPH_MONO_BITPACK; if ( ((FT_BitmapGlyph)ft_glyph)->bitmap.pitch == pitch1 ) memcpy( glyph->bmp+1, ((FT_BitmapGlyph)ft_glyph)->bitmap.buffer, bitpack_size ); else { unsigned char *d = glyph->bmp+1; int y = 0; for (; y<glyph->height; y++,d+=pitch1) memcpy( d, ((FT_BitmapGlyph)ft_glyph)->bitmap.buffer + y * ((FT_BitmapGlyph)ft_glyph)->bitmap.pitch, pitch1 ); } glyph->bmpsize = bitpack_size+1; } #else /* GLYPH_TARGET_KNOWS_MONO_BITPACK */ { int y = 0; unsigned char* b; glyph->bmp = (unsigned char*)_gk_malloc(bmp_size+1); if (!glyph->bmp) { _gk_free(glyph); return 0; } glyph->bmp[0] = GLYPH_UNCOMPRESSED; glyph->bmpsize = bmp_size+1; b = glyph->bmp + 1; for (; y<glyph->height; y++) { int x = 0; unsigned char* a = ((FT_BitmapGlyph)ft_glyph)->bitmap.buffer + y * ((FT_BitmapGlyph)ft_glyph)->bitmap.pitch; while (x<glyph->width) { if (x < glyph->width) { *b++ = *a&0x80 ? 255 : 0; x++; } if (x < glyph->width) { *b++ = *a&0x40 ? 255 : 0; x++; } if (x < glyph->width) { *b++ = *a&0x20 ? 255 : 0; x++; } if (x < glyph->width) { *b++ = *a&0x10 ? 255 : 0; x++; } if (x < glyph->width) { *b++ = *a&0x08 ? 255 : 0; x++; } if (x < glyph->width) { *b++ = *a&0x04 ? 255 : 0; x++; } if (x < glyph->width) { *b++ = *a&0x02 ? 255 : 0; x++; } if (x < glyph->width) { *b++ = *a&0x01 ? 255 : 0; x++; } a++; } } } #endif /* GLYPH_TARGET_KNOWS_MONO_BITPACK */ }
static FT_Error Load_Glyph( TTF_Font* font, Uint16 ch, c_glyph* cached, int want ) { FT_Face face; FT_Error error; FT_GlyphSlot glyph; FT_Glyph_Metrics* metrics; FT_Outline* outline; if ( !font || !font->face ) { return FT_Err_Invalid_Handle; } face = font->face; /* Load the glyph */ if ( ! cached->index ) { cached->index = FT_Get_Char_Index( face, ch ); } error = FT_Load_Glyph( face, cached->index, FT_LOAD_DEFAULT | font->hinting); if( error ) { return error; } /* Get our glyph shortcuts */ glyph = face->glyph; metrics = &glyph->metrics; outline = &glyph->outline; /* Get the glyph metrics if desired */ if ( (want & CACHED_METRICS) && !(cached->stored & CACHED_METRICS) ) { if ( FT_IS_SCALABLE( face ) ) { /* Get the bounding box */ cached->minx = FT_FLOOR(metrics->horiBearingX); cached->maxx = cached->minx + FT_CEIL(metrics->width); cached->maxy = FT_FLOOR(metrics->horiBearingY); cached->miny = cached->maxy - FT_CEIL(metrics->height); cached->yoffset = font->ascent - cached->maxy; cached->advance = FT_CEIL(metrics->horiAdvance); } else { /* Get the bounding box for non-scalable format. * Again, freetype2 fills in many of the font metrics * with the value of 0, so some of the values we * need must be calculated differently with certain * assumptions about non-scalable formats. * */ cached->minx = FT_FLOOR(metrics->horiBearingX); cached->maxx = cached->minx + FT_CEIL(metrics->horiAdvance); cached->maxy = FT_FLOOR(metrics->horiBearingY); cached->miny = cached->maxy - FT_CEIL(face->available_sizes[font->font_size_family].height); cached->yoffset = 0; cached->advance = FT_CEIL(metrics->horiAdvance); } /* Adjust for bold and italic text */ if( TTF_HANDLE_STYLE_BOLD(font) ) { cached->maxx += font->glyph_overhang; } if( TTF_HANDLE_STYLE_ITALIC(font) ) { cached->maxx += (int)ceil(font->glyph_italics); } cached->stored |= CACHED_METRICS; } if ( ((want & CACHED_BITMAP) && !(cached->stored & CACHED_BITMAP)) || ((want & CACHED_PIXMAP) && !(cached->stored & CACHED_PIXMAP)) ) { int mono = (want & CACHED_BITMAP); int i; FT_Bitmap* src; FT_Bitmap* dst; FT_Glyph bitmap_glyph = NULL; /* Handle the italic style */ if( TTF_HANDLE_STYLE_ITALIC(font) ) { FT_Matrix shear; shear.xx = 1 << 16; shear.xy = (int) ( font->glyph_italics * ( 1 << 16 ) ) / font->height; shear.yx = 0; shear.yy = 1 << 16; FT_Outline_Transform( outline, &shear ); } /* Render as outline */ if( (font->outline > 0) && glyph->format != FT_GLYPH_FORMAT_BITMAP ) { FT_Stroker stroker; FT_Get_Glyph( glyph, &bitmap_glyph ); error = FT_Stroker_New( library, &stroker ); if( error ) { return error; } FT_Stroker_Set( stroker, font->outline * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0 ); FT_Glyph_Stroke( &bitmap_glyph, stroker, 1 /* delete the original glyph */ ); FT_Stroker_Done( stroker ); /* Render the glyph */ error = FT_Glyph_To_Bitmap( &bitmap_glyph, mono ? ft_render_mode_mono : ft_render_mode_normal, 0, 1 ); if( error ) { FT_Done_Glyph( bitmap_glyph ); return error; } src = &((FT_BitmapGlyph)bitmap_glyph)->bitmap; } else { /* Render the glyph */ error = FT_Render_Glyph( glyph, mono ? ft_render_mode_mono : ft_render_mode_normal ); if( error ) { return error; } src = &glyph->bitmap; } /* Copy over information to cache */ if ( mono ) { dst = &cached->bitmap; } else { dst = &cached->pixmap; } memcpy( dst, src, sizeof( *dst ) ); /* FT_Render_Glyph() and .fon fonts always generate a * two-color (black and white) glyphslot surface, even * when rendered in ft_render_mode_normal. */ /* FT_IS_SCALABLE() means that the font is in outline format, * but does not imply that outline is rendered as 8-bit * grayscale, because embedded bitmap/graymap is preferred * (see FT_LOAD_DEFAULT section of FreeType2 API Reference). * FT_Render_Glyph() canreturn two-color bitmap or 4/16/256- * color graymap according to the format of embedded bitmap/ * graymap. */ if ( src->pixel_mode == FT_PIXEL_MODE_MONO ) { dst->pitch *= 8; } else if ( src->pixel_mode == FT_PIXEL_MODE_GRAY2 ) { dst->pitch *= 4; } else if ( src->pixel_mode == FT_PIXEL_MODE_GRAY4 ) { dst->pitch *= 2; } /* Adjust for bold and italic text */ if( TTF_HANDLE_STYLE_BOLD(font) ) { int bump = font->glyph_overhang; dst->pitch += bump; dst->width += bump; } if( TTF_HANDLE_STYLE_ITALIC(font) ) { int bump = (int)ceil(font->glyph_italics); dst->pitch += bump; dst->width += bump; } if (dst->rows != 0) { dst->buffer = (unsigned char *)malloc( dst->pitch * dst->rows ); if( !dst->buffer ) { return FT_Err_Out_Of_Memory; } memset( dst->buffer, 0, dst->pitch * dst->rows ); for( i = 0; i < src->rows; i++ ) { int soffset = i * src->pitch; int doffset = i * dst->pitch; if ( mono ) { unsigned char *srcp = src->buffer + soffset; unsigned char *dstp = dst->buffer + doffset; int j; if ( src->pixel_mode == FT_PIXEL_MODE_MONO ) { for ( j = 0; j < src->width; j += 8 ) { unsigned char c = *srcp++; *dstp++ = (c&0x80) >> 7; c <<= 1; *dstp++ = (c&0x80) >> 7; c <<= 1; *dstp++ = (c&0x80) >> 7; c <<= 1; *dstp++ = (c&0x80) >> 7; c <<= 1; *dstp++ = (c&0x80) >> 7; c <<= 1; *dstp++ = (c&0x80) >> 7; c <<= 1; *dstp++ = (c&0x80) >> 7; c <<= 1; *dstp++ = (c&0x80) >> 7; } } else if ( src->pixel_mode == FT_PIXEL_MODE_GRAY2 ) { for ( j = 0; j < src->width; j += 4 ) { unsigned char c = *srcp++; *dstp++ = (((c&0xA0) >> 6) >= 0x2) ? 1 : 0; c <<= 2; *dstp++ = (((c&0xA0) >> 6) >= 0x2) ? 1 : 0; c <<= 2; *dstp++ = (((c&0xA0) >> 6) >= 0x2) ? 1 : 0; c <<= 2; *dstp++ = (((c&0xA0) >> 6) >= 0x2) ? 1 : 0; } } else if ( src->pixel_mode == FT_PIXEL_MODE_GRAY4 ) { for ( j = 0; j < src->width; j += 2 ) { unsigned char c = *srcp++; *dstp++ = (((c&0xF0) >> 4) >= 0x8) ? 1 : 0; c <<= 4; *dstp++ = (((c&0xF0) >> 4) >= 0x8) ? 1 : 0; } } else { for ( j = 0; j < src->width; j++ ) {
Glyph::Glyph(FT_Library &library, FontFace &fontFace, int c, int outlineWidth, bool hinting) : mFont(fontFace) { mChar = c; FT_Face &face = fontFace.GetFTFace(); int flags = FT_LOAD_DEFAULT; if (!hinting) { flags = FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING; } FT_Error error = FT_Load_Char(face, c, flags); if (error) { return; } mGlyphIndex = FT_Get_Char_Index(face, c); // Load The Glyph For Our Character. if(FT_Load_Glyph( face, mGlyphIndex, flags )) throw std::runtime_error("FT_Load_Glyph failed"); // Move The Face's Glyph Into A Glyph Object. FT_Glyph glyph; if(FT_Get_Glyph( face->glyph, &glyph )) throw std::runtime_error("FT_Get_Glyph failed"); FT_Stroker stroker; FT_Stroker_New(library, &stroker); FT_Stroker_Set(stroker, outlineWidth * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); FT_Glyph_StrokeBorder(&glyph, stroker, false, true); FT_OutlineGlyph olglyph = reinterpret_cast<FT_OutlineGlyph>(glyph); FT_Outline outline = olglyph->outline; RenderSpans(library, &outline); FT_Stroker_Done(stroker); // Get metrics FT_Glyph_Metrics metrics = face->glyph->metrics; mAdvance.x = metrics.horiAdvance * kOneOver64; mAdvance.y = metrics.vertAdvance * kOneOver64; mBearing.x = metrics.horiBearingX * kOneOver64; mBearing.y = metrics.horiBearingY * kOneOver64; mSize.x = glm::round(metrics.width * kOneOver64); mSize.y = glm::round(metrics.height * kOneOver64); // Adjust for outline? mAdvance.x += outlineWidth; // Draw spans if(mSpans.size() > 0) { GlyphSpan front = mSpans.front(); Rect bounds(front.x, front.y, front.x, front.y); for(int i = 0; i < mSpans.size(); i++) { bounds.Include(mSpans[i].x, mSpans[i].y + 1); bounds.Include(mSpans[i].x + mSpans[i].width, mSpans[i].y); } int width = bounds.GetWidth(); int height = bounds.GetHeight(); mDataSize.x = width; mDataSize.y = height; int size = width * height; mBuffer = new unsigned char[size]; memset(mBuffer, 0, size); for(int i = 0; i < mSpans.size(); i++) { GlyphSpan &span = mSpans[i]; for (int w = 0; w < span.width; ++w) { mBuffer[(int)((height - 1 - (span.y - bounds.top)) * width + span.x - bounds.left + w)] = span.coverage; } } } FT_Done_Glyph(glyph); }
JNIEXPORT jobject JNICALL Java_gnu_java_awt_peer_gtk_FreetypeGlyphVector_getGlyphOutlineNative (JNIEnv *env, jobject obj __attribute__((unused)), jint glyphIndex, jlong fnt) { generalpath *path; jobject gp; FT_Outline_Funcs ftCallbacks = { (FT_Outline_MoveToFunc) _moveTo, (FT_Outline_LineToFunc) _lineTo, (FT_Outline_ConicToFunc) _quadTo, (FT_Outline_CubicToFunc) _curveTo, 0, 0 }; PangoFcFont *font; FT_Face ft_face; FT_Glyph glyph; font = JLONG_TO_PTR(PangoFcFont, fnt); ft_face = pango_fc_font_lock_face( font ); g_assert (ft_face != NULL); path = g_malloc0 (sizeof (generalpath)); g_assert(path != NULL); path->env = env; path->px = path->py = 0.0; path->sx = 1.0/64.0; path->sy = -1.0/64.0; { /* create a GeneralPath instance */ jclass cls; jmethodID method; cls = (*env)->FindClass (env, "java/awt/geom/GeneralPath"); method = (*env)->GetMethodID (env, cls, "<init>", "()V"); gp = path->obj = (*env)->NewObject (env, cls, method); } if(FT_Load_Glyph(ft_face, (FT_UInt)(glyphIndex), FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP) != 0) { pango_fc_font_unlock_face( font ); g_free(path); return NULL; } FT_Get_Glyph( ft_face->glyph, &glyph ); if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) { FT_Outline_Decompose (&(((FT_OutlineGlyph)glyph)->outline), &ftCallbacks, path); } else { char format[5]; format[0] = (glyph->format & 0xFF000000) >> 24; format[1] = (glyph->format & 0x00FF0000) >> 16; format[2] = (glyph->format & 0x0000FF00) >> 8; format[3] = (glyph->format & 0x000000FF); format[4] = '\0'; printf("WARNING: Unable to create outline for font %s %s of format %s\n", ft_face->family_name, ft_face->style_name, format); } FT_Done_Glyph( glyph ); pango_fc_font_unlock_face( font ); g_free(path); return gp; }
/* TODO: Font rendering MUST be redesigned. */ Font* GuiRendererD3D11::MakeFont(const char* pPath, int height) { Common::Timer timer; timer.Start(); FT_Face face; int texWidth = 4096; int texHeight = 4096; //create pixels array unsigned char* pTexData = (unsigned char*)malloc(texWidth * texHeight); ZeroMemory(pTexData, texWidth * texHeight); if (FT_New_Face(mFreeTypeLibrary, pPath, 0, &face) != 0) { free(pTexData); // TODO // LOG_ERROR("Failed to load font '%s'.", pPath); return 0; } //FT_Set_Pixel_Sizes(face, 2*height, 2*height); FT_Set_Char_Size(face, 0, height * 64, 96, 96); Font* pFont = new Font; /* FT_Matrix Transform; Transform.xx = (FT_Fixed)(1.0f * 0x10000L); Transform.xy = (FT_Fixed)(0.0f * 0x10000L); Transform.yx = (FT_Fixed)(0.0f * 0x10000L); Transform.yy = (FT_Fixed)(1.0f * 0x10000L); FT_Set_Transform(face, &Transform, 0); */ int Width; int Height; int OffsetX = 0; int OffsetY = 0; pFont->height = height; pFont->charCount = 65536; pFont->characters = (CharacterInfo*)malloc(sizeof(CharacterInfo) * pFont->charCount); int Index = 0; for (int ChrId = 0; ChrId < (65536); ChrId++) { // Load The Glyph For Our Character. unsigned int GlyphIndex = FT_Get_Char_Index(face, ChrId); if (GlyphIndex == 0) { pFont->characters[ChrId].exists = false; continue; } FT_Load_Glyph(face, GlyphIndex, FT_LOAD_DEFAULT); // Move The Face's Glyph Into A Glyph Object. FT_Glyph glyph; FT_Get_Glyph(face->glyph, &glyph); // Convert The Glyph To A Bitmap. FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1); FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph; // This Reference Will Make Accessing The Bitmap Easier. FT_Bitmap& bitmap = bitmap_glyph->bitmap; Width = bitmap.width; Height = bitmap.rows; //char won't fit to texture if (OffsetX + Width + 2 > texWidth) { OffsetX = 0; OffsetY += 2 * height; } for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { unsigned char Value = bitmap.buffer[x + Width * y]; int PxOffset = (y + OffsetY) * texWidth + x + OffsetX; pTexData[PxOffset] = Value; } } pFont->characters[ChrId].exists = true; pFont->characters[ChrId].top = (short)bitmap_glyph->top; pFont->characters[ChrId].left = (short)bitmap_glyph->left; pFont->characters[ChrId].height = (short)Height; pFont->characters[ChrId].width = (short)Width; pFont->characters[ChrId].u = (short)OffsetX; pFont->characters[ChrId].v = (short)OffsetY; pFont->characters[ChrId].spacing = (short)(face->glyph->advance.x >> 6) + 1; FT_Done_Glyph(glyph); OffsetX += pFont->characters[ChrId].width + 1; Index++; } //close font FT_Done_Face(face); OffsetY += 2 * height; // Used texture height texHeight = CeilToPowerOf2(OffsetY); Common::Image fontImage; fontImage.SetData(pTexData, texWidth, texHeight, Common::ImageFormat::A_UBYTE); // pFont->texture = pRenderer->CreateTexture(&fontImage, false); pFont->texture = new RendererTextureD3D11; pFont->texture->FromImage(fontImage); pFont->texHeight = texHeight; pFont->texWidth = texWidth; free(pTexData); // TODO // LOG_SUCCESS("Font '%s', size %i loaded in %.3lf seconds.", pPath, height, timer.Stop()); return pFont; }
bool TrueTypeFont::bakeGlyphDistance(CodePoint _codePoint, GlyphInfo& _glyphInfo, uint8_t* _outBuffer) { BX_CHECK(m_font != NULL, "TrueTypeFont not initialized"); _glyphInfo.glyphIndex = FT_Get_Char_Index(m_font->face, _codePoint); FT_Int32 loadMode = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING; FT_Render_Mode renderMode = FT_RENDER_MODE_NORMAL; FT_GlyphSlot slot = m_font->face->glyph; FT_Error error = FT_Load_Glyph(m_font->face, _glyphInfo.glyphIndex, loadMode); if (error) { return false; } FT_Glyph glyph; error = FT_Get_Glyph(slot, &glyph); if (error) { return false; } error = FT_Glyph_To_Bitmap(&glyph, renderMode, 0, 1); if (error) { return false; } FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph; int32_t ww = bitmap->bitmap.width; int32_t hh = bitmap->bitmap.rows; glyphInfoInit(_glyphInfo, bitmap, slot, _outBuffer, 1); FT_Done_Glyph(glyph); if (ww * hh > 0) { uint32_t dw = 6; uint32_t dh = 6; uint32_t nw = ww + dw * 2; uint32_t nh = hh + dh * 2; BX_CHECK(nw * nh < 128 * 128, "Buffer overflow (size %d)", nw * nh); uint32_t buffSize = nw * nh * sizeof(uint8_t); uint8_t* alphaImg = (uint8_t*)malloc(buffSize); memset(alphaImg, 0, nw * nh * sizeof(uint8_t) ); //copy the original buffer to the temp one for (uint32_t ii = dh; ii < nh - dh; ++ii) { memcpy(alphaImg + ii * nw + dw, _outBuffer + (ii - dh) * ww, ww); } makeDistanceMap(alphaImg, _outBuffer, nw, nh); free(alphaImg); _glyphInfo.offset_x -= (float)dw; _glyphInfo.offset_y -= (float)dh; _glyphInfo.width = (float)nw; _glyphInfo.height = (float)nh; } return true; }
/* * Function : libaroma_font_glyph_draw * Return Value: byte * Descriptions: draw glyph into canvas */ byte libaroma_font_glyph_draw( LIBAROMA_CANVASP dest, LIBAROMA_GLYPH aglyph_param, int x, int y, word color, byte flags, byte opacity ) { _LIBAROMA_FONT_SLOT_CACHEP aglyph = (_LIBAROMA_FONT_SLOT_CACHEP) aglyph_param; if (!aglyph) { return 0; } if (aglyph->glyph==NULL) { return 0; } if (dest == NULL) { dest = libaroma_fb()->canvas; } /* thread safe lock */ _libaroma_font_lock(1); /* copy & render */ FT_Glyph fglyph=NULL; fglyph=NULL; int cnt=0; if (FT_Glyph_Copy(aglyph->glyph, &fglyph)!=0){ _libaroma_font_lock(0); return 0; } if (cnt>0){ printf("[FT] FC: %i %i\n",cnt,aglyph->codepoint); } /* italic transform */ if (flags & _LIBAROMA_TEXTCHUNK_ITALIC) { FT_Matrix matrix; matrix.xx = 0x10000L; matrix.xy = 0x5000L; matrix.yx = 0; matrix.yy = 0x10000L; FT_Glyph_Transform(fglyph, &matrix, NULL); } /* embolden */ if (flags & _LIBAROMA_TEXTCHUNK_BOLD) { FT_Outline_Embolden( &((FT_OutlineGlyph) fglyph)->outline, aglyph->size * 2 ); } /* convert glyph to bitmap glyph */ if (FT_Glyph_To_Bitmap(&fglyph, _LIBAROMA_FONT_RENDER_FLAG, 0, 1)!=0){ /* release glyph */ printf("[FT] FR:%i\n",aglyph->codepoint); FT_Done_Glyph(fglyph); _libaroma_font_lock(0); return 0; } /* as bitmap glyph */ FT_BitmapGlyph bit = (FT_BitmapGlyph) fglyph; /* prepare locations */ /*int yy;*/ typeof(bit->bitmap.rows) yy; int xpos = x + bit->left; int xstart = 0; if (xpos < 0) { xstart = 0 - xpos; xpos = 0; } /* loop */ #ifndef LIBAROMA_CONFIG_NOFONT_SUBPIXEL int draw_w = (bit->bitmap.width / 3) - xstart; if (draw_w + xpos > dest->w) { draw_w = dest->w - xpos; } if (draw_w > 0) { wordp tmp_dst = NULL; if (opacity != 0xff) { tmp_dst = (wordp) malloc(draw_w * 2); } for (yy = 0; yy < bit->bitmap.rows; yy++) { /* drawing positions */ int yglp = (yy - bit->top); int ypos = (y + yglp) * dest->l; /* check position */ if ((ypos+draw_w>(dest->l*dest->h)) || (ypos<0)) { continue; } /* source * destination pointers */ int ysrc = yy * bit->bitmap.pitch; bytep src_line = bit->bitmap.buffer + ysrc + (xstart * 3); wordp dest_line = dest->data + ypos + xpos; /* draw line */ if (opacity == 0xff) { libaroma_alpha_multi_line( draw_w, dest_line, dest_line, color, src_line); } else { libaroma_alpha_multi_line( draw_w, tmp_dst, dest_line, color, src_line); libaroma_alpha_const( draw_w, dest_line, dest_line, tmp_dst, opacity); } } if (tmp_dst) { free(tmp_dst); } } #else int draw_w = bit->bitmap.width - xstart; if (draw_w + xpos > dest->w) { draw_w = dest->w - xpos; } if (draw_w > 0) { wordp tmp_dst = NULL; if (opacity != 0xff) { tmp_dst = (wordp) malloc(draw_w * 2); } /* line */ for (yy = 0; yy < bit->bitmap.rows; yy++) { /* drawing positions */ int yglp = (yy - bit->top); int ypos = (y + yglp) * dest->l; /* check position */ if ((ypos+draw_w>(dest->l*dest->h)) || (ypos<0)) { continue; } /* source & destination pointers */ int ysrc = yy * bit->bitmap.pitch; bytep src_line = bit->bitmap.buffer + ysrc + xstart; wordp dest_line = dest->data + ypos + xpos; /* draw line */ if (opacity == 0xff) { libaroma_alpha_mono( draw_w, dest_line, dest_line, color, src_line); } else { libaroma_alpha_mono( draw_w, tmp_dst, dest_line, color, src_line); libaroma_alpha_const( draw_w, dest_line, dest_line, tmp_dst, opacity); } } if (tmp_dst) { free(tmp_dst); } } #endif /* release glyph */ FT_Done_Glyph(fglyph); _libaroma_font_lock(0); return 1; } /* End of libaroma_font_glyph_draw */