Glyph Font::glyph(char code) const { if (size_t(code) < glyph_.size()) { return glyph_[code]; } else { return Glyph(); } }
//----------------------------------------------------------------------------------------------- BitmapFont::BitmapFont( const std::string& glyphSheetFileName, const std::string& xmlMetaDataFileName ) { m_glyphSheet = Texture::CreateOrGetTexture( glyphSheetFileName ); XMLDocument fontInfo( xmlMetaDataFileName ); fontInfo.SetCurrentNodeToChild( "FontDefinition" ); fontInfo.SetCurrentNodeToChild( "FontInfo" ); m_fontName = GetStringXMLAttribute( fontInfo, "name", "null" ); while( !fontInfo.GetCurrentNode().IsNull() ) { fontInfo.SetCurrentNodeToNextSibling(); unsigned int ucsIndex = GetUnsignedIntXMLAttribute( fontInfo, "ucsIndex", 0 ); unsigned int sheetNumber = GetUnsignedIntXMLAttribute( fontInfo, "sheet", 0 ); float ttfA = GetFloatXMLAttribute( fontInfo, "ttfA", 0.f ); float ttfB = GetFloatXMLAttribute( fontInfo, "ttfB", 0.f ); float ttfC = GetFloatXMLAttribute( fontInfo, "ttfC", 0.f ); Vector2 texCoordMins = GetVector2XMLAttribute( fontInfo, "texCoordMins", Vector2( 0.f, 0.f ) ); Vector2 texCoordMaxs = GetVector2XMLAttribute( fontInfo, "texCoordMaxs", Vector2( 0.f, 0.f ) ); m_glyphData[ ucsIndex ] = Glyph( ucsIndex, sheetNumber, texCoordMins, texCoordMaxs, ttfA, ttfB, ttfC ); } }
float FTGlyphContainer::Advance(const unsigned int charCode, const unsigned int nextCharCode) { unsigned int left = charMap->FontIndex(charCode); unsigned int right = charMap->FontIndex(nextCharCode); return face->KernAdvance(left, right).Xf() + Glyph(charCode)->Advance(); }
void reset() { this->glyph_stamp = 0; for (auto & array : this->glyphs) { for (auto & glyph : array) { glyph = Glyph(); } } }
Font::Font(const String &filename) : Image(filename, 16, 16) { m_x = 0; m_y = 0; m_speedX = 0; m_speedY = 0; for (unsigned short int f = 0; f < GetNumFrames(); f++) glyphs.Add(Glyph(0, 0, GetWidth(), GetHeight())); int width32 = 0; int height32 = 0; int *ptrComp = nullptr; uint8 * const buffer = stbi_load(filename.ToCString(), &width32, &height32, ptrComp, 4); //this pointer should never be missing if (buffer) { uint8 *ptrBuffer = buffer; //0x<A><B><G><R> const uint32 red = 0xff0000ff; const uint32 yellow = 0xff00ffff; const uint32 black = 0xff000000; const uint32 transparent = 0x00000000; unsigned short int i, j; uint16 row = 0; uint16 col = 0; for (unsigned short int frame = 0; frame < GetNumFrames(); frame++) { row = frame / GetHFrames(); col = frame % GetHFrames(); ptrBuffer = buffer + ((GetWidth() * col) * 4) + (row * GetHeight() * GetWidth() * GetHFrames() * 4); for (j = 0; j < GetHeight(); j++) { for (i = 0; i < GetWidth(); i++) { //inside each frame if (*((uint32 *)(ptrBuffer)) == black) memset(ptrBuffer, transparent, 4); else if (*((uint32 *)(ptrBuffer)) == yellow) { glyphs[frame].SetOrigX(i); glyphs[frame].SetOrigY(j); memset(ptrBuffer, transparent, 4); } else if (*((uint32 *)(ptrBuffer)) == red) { glyphs[frame].SetEndX(i); glyphs[frame].SetEndY(j); memset(ptrBuffer, transparent, 4); } ptrBuffer += 4; } ptrBuffer += ((GetHFrames() * GetWidth()) - GetWidth()) * 4; } } Renderer::Instance().GenFontImage(buffer, width32, height32); delete buffer; } }
float FTGlyphContainer::Advance(const unsigned int charCode, const unsigned int nextCharCode) { unsigned int left = FontIndex(charCode); unsigned int right = FontIndex(nextCharCode); const FTGlyph *glyph = Glyph(charCode); if (!glyph) return 0.0f; return font->KernAdvance(left, right).Xf() + glyph->Advance(); }
void Font::Render(Bitmap& bmp, int const x, int const y, Bitmap const& sys, int color, char32_t code) { if(color != ColorShadow) { BitmapRef system = Cache::System(); Render(bmp, x + 1, y + 1, system->GetShadowColor(), code); } BitmapRef bm = Glyph(code); unsigned const src_x = color == ColorShadow? 16 : color % 10 * 16 + 2, src_y = color == ColorShadow? 32 : color / 10 * 16 + 48 + 16 - bm->height(); bmp.MaskedBlit(Rect(x, y, bm->width(), bm->height()), *bm, 0, 0, sys, src_x, src_y); }
void insertCharacter(char character) { FT_UInt glyphIndex = FT_Get_Char_Index(_face, character); if(glyphIndex == 0) { printError("Undefined character."); return; } FT_Error error = FT_Load_Glyph(_face, glyphIndex, FT_LOAD_DEFAULT); if(error) { printError("Could not load glyph."); return; } FT_GlyphSlot slot = _face->glyph; if(slot->format != FT_GLYPH_FORMAT_BITMAP) { error = FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL); if(error) { printError("Could not render glyph."); return; } } if(_insert.X() + slot->bitmap.width > static_cast<size_t>(_pitch)) { _insert = vec2<size_t>(_insert.X(), _insert.Y() + _lineHeight + 1); _lineHeight = 0; } byte * bufferPos = _buffer + _pitch * _insert.Y() + _insert.X(); byte * const bitmapEnd = abs(slot->bitmap.pitch) * slot->bitmap.rows + slot->bitmap.buffer; size_t const bitmapPitch = abs(slot->bitmap.pitch); for(byte * bitmapPos = slot->bitmap.buffer; bitmapPos < bitmapEnd; bitmapPos += bitmapPitch) { memcpy(bufferPos, bitmapPos, slot->bitmap.width); bufferPos += _pitch; } vec2<size_t> size(slot->bitmap.width, slot->bitmap.rows); _cachedChars[character] = Glyph(_insert, size); _insert = vec2<size_t>(_insert.X() + size.X() + 1, _insert.Y()); if(size.Y() > _lineHeight) { _lineHeight = size.Y(); } }
FTPoint FTGlyphContainer::Render(const unsigned int charCode, const unsigned int nextCharCode, FTPoint penPosition) { unsigned int left = FontIndex(charCode); unsigned int right = FontIndex(nextCharCode); FTPoint kernAdvance = font->KernAdvance(left, right); FTGlyph * glyph = Glyph(charCode); if (glyph != NULL) kernAdvance += glyph->Render(penPosition); return kernAdvance; }
//-------------------------------------------------------------- void testApp::setup() { ofSetVerticalSync(true); ofSetFrameRate(60); keyPressed('3'); bDebug = false; // Create an alphabet. for (int i = 0; i < 26; i++) { alphabet.push_back(Glyph()); } alphaScale = 8; VF.setupField(60, 40, ofGetWidth(), ofGetHeight()); }
const Font::Glyph* Font::addGlyph(uint32 codepoint) { const int index = m_face->indexForCodePoint(codepoint); if (!index) return nullptr; m_glyphs.push_back(Glyph()); Glyph& glyph = m_glyphs.back(); glyph.codepoint = codepoint; glyph.advance = ceil(m_face->advance(index, m_scale)); glyph.bearing = ceil(m_face->bearing(index, m_scale)); if (Ref<Image> image = m_face->glyph(index, m_scale)) { if (m_position.x + image->width() + 1 > m_texture->width()) { m_position.x = 1; m_position.y += int(m_height) + 1; if (m_position.y + image->height() + 1 > m_texture->height()) { if (!addGlyphTextureRow()) return nullptr; } } if (!m_texture->copyFrom(0, *image, m_position.x, m_position.y)) { logError("Failed to copy glyph image data for font %s", name().c_str()); return nullptr; } glyph.offset = vec2(m_position); glyph.size = vec2(image->width(), image->height()); m_position.x += image->width() + 1; } return &glyph; }
void SCR_DrawConsoleFontUnichar( float x, float y, int ch ) { if ( cls.useLegacyConsoleFont ) { SCR_DrawSmallUnichar( ( int ) x, ( int ) y, ch ); return; } if ( ch != ' ' ) { glyphInfo_t *glyph = Glyph( ch ); float yadj = glyph->top; float xadj = ( SCR_ConsoleFontUnicharWidth( ch ) - glyph->xSkip ) / 2.0; re.DrawStretchPic( x + xadj, y - yadj, glyph->imageWidth, glyph->imageHeight, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph ); } }
////////////////////////////////////////////////////////// // Protected member functions ///////////////////////////////////////////////////////// Font::Glyph Font::loadGlyph( XMLParser& parser, XMLNode* node ) { unsigned int ucsIndex = 0; unsigned int sheet = 0; ucsIndex = (unsigned int)parser.getXMLAttributeAsInt( node, "ucsIndex", 0 ); sheet = (unsigned int)parser.getXMLAttributeAsInt( node, "sheet", 0 ); vec2f texCoordMin = parser.getXMLAttributeAsVec2( node, "texCoordMins", vec2f( -1.0f ) ); vec2f texCoordMax = parser.getXMLAttributeAsVec2( node, "texCoordMaxs", vec2f( -1.0f ) ); assertion( texCoordMin.x != -1.0f, "Min texture coordinate invalid" ); assertion( texCoordMax.x != 1.0f, "Max texture coordinate invalid" ); float A = parser.getXMLAttributeAsFloat( node, "ttfA", -1000.0f ); float B = parser.getXMLAttributeAsFloat( node, "ttfB", -1000.0f); float C = parser.getXMLAttributeAsFloat( node, "ttfC", -1000.0f ); assertion( A != -1000.0f, "Error loading A value for font: %s", m_fontName.c_str() ); assertion( B != -1000.0f, "Error loading B value for font: %s", m_fontName.c_str() ); assertion( C != -1000.0f, "Error loading C value for font: %s", m_fontName.c_str() ); return Glyph( ( unsigned char )ucsIndex, ( unsigned char )sheet, texCoordMin, texCoordMax, A, B, C ); }
FTBBox FTGlyphContainer::BBox(const unsigned int charCode) const { return Glyph(charCode)->BBox(); }
row = ch>>4; col = ch&15; frow = row*0.0625; fcol = col*0.0625; size = 0.0625; // adjust for baseline re.DrawStretchPic( x, y - (int)( SMALLCHAR_HEIGHT / ( CONSOLE_FONT_VPADDING + 1 ) ), SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT, fcol, frow, fcol + size, frow + size, cls.charSetShader ); } else { glyphInfo_t *glyph = Glyph( ch ); re.DrawStretchPic( x, y, SMALLCHAR_WIDTH, glyph->imageHeight, glyph->s, glyph->t, glyph->s2, glyph->t2, glyph->glyph ); } } /* ================== SCR_DrawSmallString[Color] Draws a multi-colored string with a drop shadow, optionally forcing
const Glyph& TextRenderer::getGlyph(char c) { Glyph& glyph = _glyphs[c]; if (glyph.isValid()) { return glyph; } // we use 'J' as a representative size for the solid block character QChar ch = (c == SOLID_BLOCK_CHAR) ? QChar('J') : QChar(c); QRect bounds = _metrics.boundingRect(ch); if (bounds.isEmpty()) { glyph = Glyph(0, QPoint(), QRect(), _metrics.width(ch)); return glyph; } // grow the bounds to account for effect, if any if (_effectType == SHADOW_EFFECT) { bounds.adjust(-_effectThickness, 0, 0, _effectThickness); } else if (_effectType == OUTLINE_EFFECT) { bounds.adjust(-_effectThickness, -_effectThickness, _effectThickness, _effectThickness); } // grow the bounds to account for antialiasing bounds.adjust(-1, -1, 1, 1); if (_x + bounds.width() > IMAGE_SIZE) { // we can't fit it on the current row; move to next _y += _rowHeight; _x = _rowHeight = 0; } if (_y + bounds.height() > IMAGE_SIZE) { // can't fit it on current texture; make a new one glGenTextures(1, &_currentTextureID); _x = _y = _rowHeight = 0; glBindTexture(GL_TEXTURE_2D, _currentTextureID); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, IMAGE_SIZE, IMAGE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); _allTextureIDs.append(_currentTextureID); } else { glBindTexture(GL_TEXTURE_2D, _currentTextureID); } // render the glyph into an image and copy it into the texture QImage image(bounds.width(), bounds.height(), QImage::Format_ARGB32); if (c == SOLID_BLOCK_CHAR) { image.fill(QColor(255, 255, 255)); } else { image.fill(0); QPainter painter(&image); painter.setFont(_font); if (_effectType == SHADOW_EFFECT) { for (int i = 0; i < _effectThickness; i++) { painter.drawText(-bounds.x() - 1 - i, -bounds.y() + 1 + i, ch); } } else if (_effectType == OUTLINE_EFFECT) { QPainterPath path; QFont font = _font; font.setStyleStrategy(QFont::ForceOutline); path.addText(-bounds.x() - 0.5, -bounds.y() + 0.5, font, ch); QPen pen; pen.setWidth(_effectThickness); pen.setJoinStyle(Qt::RoundJoin); pen.setCapStyle(Qt::RoundCap); painter.setPen(pen); painter.setRenderHint(QPainter::Antialiasing); painter.drawPath(path); } painter.setPen(QColor(255, 255, 255)); painter.drawText(-bounds.x(), -bounds.y(), ch); } glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, bounds.width(), bounds.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.constBits()); glyph = Glyph(_currentTextureID, QPoint(_x, _y), bounds, _metrics.width(ch)); _x += bounds.width(); _rowHeight = qMax(_rowHeight, bounds.height()); glBindTexture(GL_TEXTURE_2D, 0); return glyph; }
RenderedSubtitle* Renderer::Lookup(const Subtitle* s, const CSize& vs, const CRect& vr) { m_sra.UpdateTarget(vs, vr); if(s->m_text.IsEmpty()) { return NULL; } CRect spdrc = s->m_frame.reference == _T("video") ? vr : CRect(CPoint(0, 0), vs); if(spdrc.IsRectEmpty()) { return NULL; } RenderedSubtitle* rs = NULL; if(m_rsc.Lookup(s->m_name, rs)) { if(!s->m_animated && rs->m_spdrc == spdrc) { return rs; } m_rsc.Invalidate(s->m_name); } const Style& style = s->m_text.GetHead().style; Size scale; scale.cx = (float)spdrc.Width() / s->m_frame.resolution.cx; scale.cy = (float)spdrc.Height() / s->m_frame.resolution.cy; CRect frame; frame.left = (int)(64.0f * (spdrc.left + style.placement.margin.l * scale.cx) + 0.5); frame.top = (int)(64.0f * (spdrc.top + style.placement.margin.t * scale.cy) + 0.5); frame.right = (int)(64.0f * (spdrc.right - style.placement.margin.r * scale.cx) + 0.5); frame.bottom = (int)(64.0f * (spdrc.bottom - style.placement.margin.b * scale.cy) + 0.5); CRect clip; if(style.placement.clip.l == -1) { clip.left = 0; } else { clip.left = (int)(spdrc.left + style.placement.clip.l * scale.cx); } if(style.placement.clip.t == -1) { clip.top = 0; } else { clip.top = (int)(spdrc.top + style.placement.clip.t * scale.cy); } if(style.placement.clip.r == -1) { clip.right = vs.cx; } else { clip.right = (int)(spdrc.left + style.placement.clip.r * scale.cx); } if(style.placement.clip.b == -1) { clip.bottom = vs.cy; } else { clip.bottom = (int)(spdrc.top + style.placement.clip.b * scale.cy); } clip.left = max(clip.left, 0); clip.top = max(clip.top, 0); clip.right = min(clip.right, vs.cx); clip.bottom = min(clip.bottom, vs.cy); scale.cx *= 64; scale.cy *= 64; bool vertical = s->m_direction.primary == _T("down") || s->m_direction.primary == _T("up"); // create glyph paths WCHAR c_prev = 0, c_next; CAutoPtrList<Glyph> glyphs; POSITION pos = s->m_text.GetHeadPosition(); while(pos) { const Text& t = s->m_text.GetNext(pos); LOGFONT lf; memset(&lf, 0, sizeof(lf)); lf.lfCharSet = DEFAULT_CHARSET; _tcscpy_s(lf.lfFaceName, CString(t.style.font.face)); lf.lfHeight = (LONG)(t.style.font.size * scale.cy + 0.5); lf.lfWeight = (LONG)(t.style.font.weight + 0.5); lf.lfItalic = !!t.style.font.italic; lf.lfUnderline = !!t.style.font.underline; lf.lfStrikeOut = !!t.style.font.strikethrough; lf.lfOutPrecision = OUT_TT_PRECIS; lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; lf.lfQuality = ANTIALIASED_QUALITY; lf.lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE; FontWrapper* font = m_fc.Create(m_hDC, lf); if(!font) { _tcscpy_s(lf.lfFaceName, _T("Arial")); font = m_fc.Create(m_hDC, lf); if(!font) { ASSERT(0); continue; } } HFONT hOldFont = SelectFont(m_hDC, *font); const TEXTMETRIC& tm = font->GetTextMetric(); for(LPCWSTR c = t.str; *c; c++) { CAutoPtr<Glyph> g(DNew Glyph()); g->c = *c; g->style = t.style; g->scale = scale; g->vertical = vertical; g->font = font; c_next = !c[1] && pos ? c_next = s->m_text.GetAt(pos).str[0] : c[1]; Arabic::Replace(g->c, c_prev, c_next); c_prev = c[0]; CSize extent; GetTextExtentPoint32W(m_hDC, &g->c, 1, &extent); ASSERT(extent.cx >= 0 && extent.cy >= 0); if(vertical) { g->spacing = (int)(t.style.font.spacing * scale.cy + 0.5); g->ascent = extent.cx / 2; g->descent = extent.cx - g->ascent; g->width = extent.cy; // TESTME if(g->c == Text::SP) { g->width /= 2; } } else { g->spacing = (int)(t.style.font.spacing * scale.cx + 0.5); g->ascent = tm.tmAscent; g->descent = tm.tmDescent; g->width = extent.cx; } if(g->c == Text::LSEP) { g->spacing = 0; g->width = 0; g->ascent /= 2; g->descent /= 2; } else { GlyphPath* path = m_gpc.Create(m_hDC, font, g->c); if(!path) { ASSERT(0); continue; } g->path = *path; } glyphs.AddTail(g); } SelectFont(m_hDC, hOldFont); } // break glyphs into rows CAutoPtrList<Row> rows; CAutoPtr<Row> row; pos = glyphs.GetHeadPosition(); while(pos) { CAutoPtr<Glyph> g = glyphs.GetNext(pos); if(!row) { row.Attach(DNew Row()); } WCHAR c = g->c; row->AddTail(g); if(c == Text::LSEP || !pos) { rows.AddTail(row); } } // kerning if(s->m_direction.primary == _T("right")) { // || s->m_direction.primary == _T("left") for(POSITION rpos = rows.GetHeadPosition(); rpos; rows.GetNext(rpos)) { Row* r = rows.GetAt(rpos); POSITION gpos = r->GetHeadPosition(); while(gpos) { Glyph* g1 = r->GetNext(gpos); if(!gpos) { break; } Glyph* g2 = r->GetAt(gpos); if(g1->font != g2->font || !g1->style.font.kerning || !g2->style.font.kerning) { continue; } if(int size = g1->font->GetKernAmount(g1->c, g2->c)) { g2->path.MovePoints(CPoint(size, 0)); g2->width += size; } } } } // wrap rows if(s->m_wrap == _T("normal") || s->m_wrap == _T("even")) { int maxwidth = abs((int)(vertical ? frame.Height() : frame.Width())); int minwidth = 0; for(POSITION rpos = rows.GetHeadPosition(); rpos; rows.GetNext(rpos)) { Row* r = rows.GetAt(rpos); POSITION brpos = NULL; if(s->m_wrap == _T("even")) { int fullwidth = 0; for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos)) { const Glyph* g = r->GetAt(gpos); fullwidth += g->width + g->spacing; } fullwidth = abs(fullwidth); if(fullwidth > maxwidth) { maxwidth = fullwidth / ((fullwidth / maxwidth) + 1); minwidth = maxwidth; } } int width = 0; for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos)) { const Glyph* g = r->GetAt(gpos); width += g->width + g->spacing; if(brpos && abs(width) > maxwidth && g->c != Text::SP) { row.Attach(DNew Row()); POSITION next = brpos; r->GetNext(next); do { row->AddHead(r->GetPrev(brpos)); } while(brpos); rows.InsertBefore(rpos, row); while(!r->IsEmpty() && r->GetHeadPosition() != next) { r->RemoveHeadNoReturn(); } g = r->GetAt(gpos = next); width = g->width + g->spacing; } if(abs(width) >= minwidth) { if(g->style.linebreak == _T("char") || g->style.linebreak == _T("word") && g->c == Text::SP) { brpos = gpos; } } } } } // trim rows for(POSITION pos = rows.GetHeadPosition(); pos; rows.GetNext(pos)) { Row* r = rows.GetAt(pos); while(!r->IsEmpty() && r->GetHead()->c == Text::SP) { r->RemoveHead(); } while(!r->IsEmpty() && r->GetTail()->c == Text::SP) { r->RemoveTail(); } } // calc fill width for each glyph CAtlList<Glyph*> glypsh2fill; int fill_id = 0; int fill_width = 0; for(POSITION pos = rows.GetHeadPosition(); pos; rows.GetNext(pos)) { Row* r = rows.GetAt(pos); POSITION gpos = r->GetHeadPosition(); while(gpos) { Glyph* g = r->GetNext(gpos); if(!glypsh2fill.IsEmpty() && fill_id && (g->style.fill.id != fill_id || !pos && !gpos)) { int w = (int)(g->style.fill.width * fill_width + 0.5); while(!glypsh2fill.IsEmpty()) { Glyph* g = glypsh2fill.RemoveTail(); fill_width -= g->width; g->fill = w - fill_width; } ASSERT(glypsh2fill.IsEmpty()); ASSERT(fill_width == 0); glypsh2fill.RemoveAll(); fill_width = 0; } fill_id = g->style.fill.id; if(g->style.fill.id) { glypsh2fill.AddTail(g); fill_width += g->width; } } } // calc row sizes and total subtitle size CSize size(0, 0); if(s->m_direction.secondary == _T("left") || s->m_direction.secondary == _T("up")) { ReverseList(rows); } for(POSITION pos = rows.GetHeadPosition(); pos; rows.GetNext(pos)) { Row* r = rows.GetAt(pos); if(s->m_direction.primary == _T("left") || s->m_direction.primary == _T("up")) { ReverseList(*r); } int w = 0, h = 0; r->width = 0; for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos)) { const Glyph* g = r->GetAt(gpos); w += g->width; if(gpos) { w += g->spacing; } h = max(h, g->ascent + g->descent); r->width += g->width; if(gpos) { r->width += g->spacing; } r->ascent = max(r->ascent, g->ascent); r->descent = max(r->descent, g->descent); r->border = max(r->border, g->GetBackgroundSize()); } for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos)) { Glyph* g = r->GetAt(gpos); g->row_ascent = r->ascent; g->row_descent = r->descent; } if(vertical) { size.cx += h; size.cy = max(size.cy, w); } else { size.cx = max(size.cx, w); size.cy += h; } } // align rows and calc glyph positions rs = DNew RenderedSubtitle(spdrc, clip); CPoint p = GetAlignPoint(style.placement, scale, frame, size); CPoint org = GetAlignPoint(style.placement, scale, frame); // collision detection if(!s->m_animated) { int tlb = !rows.IsEmpty() ? rows.GetHead()->border : 0; int brb = !rows.IsEmpty() ? rows.GetTail()->border : 0; CRect r(p, size); m_sra.GetRect(r, s, style.placement.align, tlb, brb); org += r.TopLeft() - p; p = r.TopLeft(); } CRect subrect(p, size); // continue positioning for(POSITION pos = rows.GetHeadPosition(); pos; rows.GetNext(pos)) { Row* r = rows.GetAt(pos); CSize rsize; rsize.cx = rsize.cy = r->width; if(vertical) { p.y = GetAlignPoint(style.placement, scale, frame, rsize).y; for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos)) { CAutoPtr<Glyph> g = r->GetAt(gpos); g->tl.x = p.x + (int)(g->style.placement.offset.x * scale.cx + 0.5) + r->ascent - g->ascent; g->tl.y = p.y + (int)(g->style.placement.offset.y * scale.cy + 0.5); p.y += g->width + g->spacing; rs->m_glyphs.AddTail(g); } p.x += r->ascent + r->descent; } else { p.x = GetAlignPoint(style.placement, scale, frame, rsize).x; for(POSITION gpos = r->GetHeadPosition(); gpos; r->GetNext(gpos)) { CAutoPtr<Glyph> g = r->GetAt(gpos); g->tl.x = p.x + (int)(g->style.placement.offset.x * scale.cx + 0.5); g->tl.y = p.y + (int)(g->style.placement.offset.y * scale.cy + 0.5) + r->ascent - g->ascent; p.x += g->width + g->spacing; rs->m_glyphs.AddTail(g); } p.y += r->ascent + r->descent; } } // bkg, precalc style.placement.path, transform pos = rs->m_glyphs.GetHeadPosition(); while(pos) { Glyph* g = rs->m_glyphs.GetNext(pos); g->CreateBkg(); g->CreateSplineCoeffs(spdrc); g->Transform(org, subrect); } // merge glyphs (TODO: merge 'fill' too) Glyph* g0 = NULL; pos = rs->m_glyphs.GetHeadPosition(); while(pos) { POSITION cur = pos; Glyph* g = rs->m_glyphs.GetNext(pos); CRect r = g->bbox + g->tl; int size = (int)(g->GetBackgroundSize() + 0.5); int depth = (int)(g->GetShadowDepth() + 0.5); r.InflateRect(size, size); r.InflateRect(depth, depth); r.left >>= 6; r.top >>= 6; r.right = (r.right + 32) >> 6; r.bottom = (r.bottom + 32) >> 6; if((r & clip).IsRectEmpty()) { // clip rs->m_glyphs.RemoveAt(cur); } else if(g0 && g0->style.IsSimilar(g->style)) { // append CPoint o = g->tl - g0->tl; g->path.MovePoints(o); g0->path.types.Append(g->path.types); g0->path.points.Append(g->path.points); g->path_bkg.MovePoints(o); g0->path_bkg.types.Append(g->path_bkg.types); g0->path_bkg.points.Append(g->path_bkg.points); g0->bbox |= g->bbox + o; rs->m_glyphs.RemoveAt(cur); } else { // leave alone g0 = g; } } // rasterize pos = rs->m_glyphs.GetHeadPosition(); while(pos) { rs->m_glyphs.GetNext(pos)->Rasterize(); } // cache m_rsc.Add(s->m_name, rs); m_fc.Flush(); return rs; }
Font::Font(const String& filename) : Image(filename, 16, 16) { _glyphs = Array<Glyph>(); Glyph myGlyph = Glyph(); // Cargamos el buffer con la imagen int iWidth; int iHeight; unsigned char* buffer = stbi_load(filename.ToCString(), &iWidth, &iHeight, NULL, nElemPixel); // Calculamos las dimensiones de cada frame bool dimX = false; for (int i = 1; i < iWidth / nElemPixel && !dimX; i++) { uint8 r = buffer[i * nElemPixel]; uint8 g = buffer[i * nElemPixel + 1]; uint8 b = buffer[i * nElemPixel + 2]; if (IsColor(r, g, b, 255, 255, 0)) { _dimFrameX = i; dimX = true; } } bool dimY = false; for (int i = 1; i < iHeight / nElemPixel && !dimY; i++) { uint8 r = buffer[i * iWidth * nElemPixel]; uint8 g = buffer[i * iWidth * nElemPixel + 1]; uint8 b = buffer[i * iWidth * nElemPixel + 2]; if (IsColor(r, g, b, 255, 255, 0)) { _dimFrameY = i; dimY = true; } } // Tratamos los pixeles dentro de cada frame if ( buffer ) { if (dimX && dimY) { for (int j = 0; j < nFrameY; j++) { for (int i = 0; i < nFrameX; i++) { // Accedemos al frame[i,j] for (int k = 0; k < _dimFrameX; k++) { for (int l = 0; l < _dimFrameY; l++) { // Accedemos al pixel[k,l] del frame[i,j] uint8 r = buffer[IndexFrom(i, j, k, l, iWidth, 0)]; uint8 g = buffer[IndexFrom(i, j, k, l, iWidth, 1)]; uint8 b = buffer[IndexFrom(i, j, k, l, iWidth, 2)]; if (IsColor(r, g, b, 255, 255, 0)) { myGlyph.SetIni(i * _dimFrameX + k, j * _dimFrameY + l); buffer[IndexFrom(i, j, k, l, iWidth, 3)] = 0; } else if (IsColor(r, g, b, 255, 0, 0)) { myGlyph.SetFin(i * _dimFrameX + k, j * _dimFrameY + l); buffer[IndexFrom(i, j, k, l, iWidth, 3)] = 0; } else if (IsColor(r, g, b, 0, 0, 0)) { buffer[IndexFrom(i, j, k, l, iWidth, 3)] = 0; } } } if (myGlyph.GetIniX() != 0 || myGlyph.GetIniY() != 0 || myGlyph.GetFinX() != 0 || myGlyph.GetFinY() != 0) _glyphs.Add(myGlyph); } } glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, iWidth, iHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); } free(buffer); } }
void Font::Render(Bitmap& bmp, int x, int y, Color const& color, char32_t code) { BitmapRef bm = Glyph(code); bmp.MaskedBlit(Rect(x, y, bm->width(), bm->height()), *bm, 0, 0, color); }