void ScreenPainter::drawGlyphOutline(const GlyphLayout gl, bool fill) { if (fill) drawGlyph(gl); m_painter->save(); bool fr = m_painter->fillRule(); m_painter->setFillRule(false); setupState(false); m_painter->translate(0, -(fontSize() * gl.scaleV)); FPointArray outline = font().glyphOutline(gl.glyph); double scaleHv = gl.scaleH * fontSize() / 10.0; double scaleVv = gl.scaleV * fontSize() / 10.0; QTransform trans; trans.scale(scaleHv, scaleVv); outline.map(trans); m_painter->setupPolygon(&outline, true); if (outline.size() > 3) { m_painter->setLineWidth(strokeWidth()); m_painter->strokePath(); } m_painter->setFillRule(fr); m_painter->restore(); }
void Font::draw (const char* str, float size, bool autoGLState) { glBindTexture(GL_TEXTURE_2D, tex); if (autoGLState) { glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); } glPushMatrix(); glScalef(size, size, 1.0f); float advanceAccum = 0; for (const char* c = str; *c!='\0'; c++) { if (*c == '\n') { glTranslatef(-advanceAccum, 1, 0); advanceAccum = 0; } else { const Glyph& gi = glyphMap.get((unsigned long)*c); drawGlyph(gi); glTranslatef(gi.advance, 0, 0); advanceAccum += gi.advance; } } if (autoGLState) { glDisableClientState(GL_VERTEX_ARRAY); glDisableClientState(GL_TEXTURE_COORD_ARRAY); } glPopMatrix(); }
void Screen::drawGlyphs(u32 x, u32 y, u8 fc, u8 bc, u16 num, u16 *text, bool *dw) { for (; num--; text++, dw++) { drawGlyph(x, y, fc, bc, *text, *dw); x += *dw ? FW(2) : FW(1); } }
void pamd_text(tuple** const tuples, int const cols, int const rows, int const depth, sample const maxval, pamd_point const pos, int const height, int const angle, const char * const sArg, pamd_drawproc drawProc, const void * const clientdata) { /*---------------------------------------------------------------------------- Draw the zero-terminated string 'sArg', with its baseline starting at point 'pos', inclined by 'angle' degrees to the X axis, with letters 'height' tuples high (descenders will extend below the baseline). We pass the supplied drawproc and clientdata to pamd_linep, which performs the actual drawing. There may be multiple lines of text. The baseline of the topmost line starts at 'pos'. -----------------------------------------------------------------------------*/ const struct ppmd_font * const fontP = ppmd_get_font(); long rotsin, rotcos; pamd_point p; const char * s; pamd_validatePoint(pos); p = makePoint(0, 0); rotsin = isin(-angle); rotcos = icos(-angle); for (s = &sArg[0]; *s; ) { unsigned char const ch = *s++; if (ch >= fontP->header.firstCodePoint && ch < fontP->header.firstCodePoint + fontP->header.characterCount) { const struct ppmd_glyph * const glyphP = &fontP->glyphTable[ch - fontP->header.firstCodePoint]; unsigned int cursorAdvance; pamd_validatePoint(p); drawGlyph(glyphP, p, tuples, cols, rows, depth, maxval, height, pos, rotcos, rotsin, drawProc, clientdata, &cursorAdvance); p.x += cursorAdvance; } else if (ch == '\n') { /* Move to the left edge of the next line down */ p.y += Scalef + Descend; p.x = 0; } } }
void DefaultFont::drawString(Graphics* graphics, const std::string& text, int x, int y, bool is_normal) { unsigned int i; for (i = 0; i< text.size(); ++i) { drawGlyph(graphics, text.at(i), x, y); x += getWidth(text); } }
void ImageFont::drawString(Graphics* graphics, const std::string& text, int x, int y) { unsigned int i; for (i = 0; i< text.size(); ++i) { drawGlyph(graphics, text.at(i), x, y); x += getWidth(text.at(i)); } }
void GLText::drawString(vector2d loc, double angle, double size, const char* str, HAlignOptions hAlign, VAlignOptions vAlign) { glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glTranslated(loc.x,loc.y,0.0); glScaled(size, size,1.0); glRotated(angle,0,0,1); switch(hAlign){ case LeftAligned:{ //Normal rendering will achieve this! break; } case RightAligned:{ glTranslated(-getWidth(str),0,0); break; } case CenterAligned:{ glTranslated(-0.5*getWidth(str),0,0); break; } } switch(vAlign){ case BottomAligned:{ glTranslated(0.0,getDescent(str),0.0); break; } case TopAligned:{ glTranslated(0.0,-getAscent(str),0.0); break; } case MedianAligned:{ //Normal rendering will achieve this! break; } case MiddleAligned:{ glTranslated(0.0,-0.5*getHeight(str),0.0); break; } } vector2d textDir; textDir.heading(0); double d = 0.0; while(*str>0){ drawGlyph(*str); d = characterSpacing + 0.5*getWidth(*str); str++; if((*str)>0){ d += 0.5*getWidth(*str); glTranslated(d*textDir.x,d*textDir.y,0.0); } } glPopMatrix(); }
void Font::drawWrap(BITMAP* aBuffer, const std::string& aText, int aX, int aY, int aMaxWidth, int aNumberOfCharacters) { int x = aX; int y = aY; int glyphBitmapMaxHeight = 0; std::string text = aText; int totChar = 0; std::vector<std::string> words = tokenize(text, ' '); for(unsigned int i = 0; i < words.size(); i++) { std::string word = words[i]; if(aMaxWidth != -1 && x + getWidth(word) > (unsigned int)aMaxWidth) { x = aX; y += glyphBitmapMaxHeight; glyphBitmapMaxHeight = 0; } else { if(totChar != 0) { word = " " + word; } } for(unsigned int j = 0; j < word.size(); j++) { if(aNumberOfCharacters != -1 && totChar >= aNumberOfCharacters) { return; } char currChar = word[j]; BITMAP* glyphBitmap = getBitmapForGlyph(currChar); glyphBitmapMaxHeight = max(glyphBitmapMaxHeight, glyphBitmap->h); drawGlyph(aBuffer, currChar, x, y); int glyphWidth = glyphBitmap->w; x += glyphWidth; if(currChar == '\n') { x = aX; y += glyphBitmapMaxHeight; glyphBitmapMaxHeight = 0; } totChar++; } } }
int dispDrawPortal (PORTAL *p) { if (p->type != PORTAL_TEXT) { setError (EDISP_INVAL, "Invalid portal type in dispDrawPortal"); return -1; } if (p->scroll == 1) return scrollPortal (p); GLYPH g[p->content.text.size]; // // Cycle through the letters in the text to be displayed. We have // to resolve justification before rendering the portal. Collect // all the glyphs, add up their widths, and move the start points // to the appropriate column. // for (int i = 0; i < p->content.text.size; ++i) { // get the next letter to render char letter = p->content.text.data[i]; // get the glyph for that letter if (fntGetGlyph (p->content.text.font, &g[i], letter) != 0) { setError (EDISP_FONTERR, "Error getting glyph in dispDrawPortal"); return -1; } } setJustification (p, g, sizeof g / sizeof g[0]); // // Draw the portal // int col = p->startCol; int maxCol = p->startCol + p->width; for (int i = 0; i < p->content.text.size; ++i) drawGlyph (&g[i], &col, p, maxCol); //dumpPortal (p); return 0; }
void Font::printText(char* str, GLfloat Red, GLfloat Green, GLfloat Blue, GLfloat Alpha) { // Initialize OpenGL ES States glDisable(GL_CULL_FACE); glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Use font program glUseProgram(program->ProgramID); // Activate Texture unit 0 and assign the altas texture glActiveTexture (GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, atlasTex); TransformObj->TransformPushMatrix(); GLfloat color[4] = {Red, Green, Blue, Alpha}; glUniform4fv(FRAG_COLOR, 1, color); std::vector< FT_UInt >* codePointsPtr = NULL; int glyph_count = 0; if ( !produceShape(str, codePointsPtr, glyph_count) ) { LOGI("Error in producing font shape"); return; } glyph_count = (int) codePointsPtr->size(); FT_UInt glyph_index = 0; for (int i = 0; i < glyph_count; i++) { glyph_index = codePointsPtr->at(i); const Glyph& gi = glyphs[glyph_index]; TransformObj->TransformTranslate(gi.advanceHor / 2.0, 0.0, 0.0); drawGlyph(gi); } TransformObj->TransformPopMatrix(); return; }
void Renderer::render(Order &order) { //Set background for(int x = 0; x < width_; x++) { for(int y = 0; y < height_; y++) { int pos = x + (y * width_); buffer_[pos] = colorBackground_.ABGR(); } } double width = width_ - marginLeft_ - marginRight_; double height = height_ - marginTop_ - marginBottom_; double ratio = width / height; int w, h; double wasted = grid::findBest(order.size(), ratio, w, h); double contentRatio = double(w) / double(h); double characterWidth = width / double(w); double characterHeight = height / double(h); int fontSize = contentRatio < ratio ? (int) characterHeight : (int) characterWidth; FT_Select_Charmap(face_, ft_encoding_unicode); FT_Set_Pixel_Sizes(face_, 0, fontSize); for(int i = 0; i < order.size(); i++) { int gridX = (i % w) * characterWidth + marginLeft_; int gridY = (i / w) * characterHeight + marginTop_; renderGlyph(face_, order.kanji(i).character()); drawGlyph(buffer_, width_, gridX, gridY, face_->glyph, fontSize, SRSColor(order.kanji(i).SRS())); } }
S32 LLFontGL::render(const LLWString &wstr, const S32 begin_offset, const F32 x, const F32 y, const LLColor4 &color, const HAlign halign, const VAlign valign, U8 style, const S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_embedded, BOOL use_ellipses) const { if(!sDisplayFont) //do not display texts { return wstr.length() ; } if (wstr.empty()) { return 0; } gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX); // Strip off any style bits that are already accounted for by the font. style = style & (~getFontDesc().getStyle()); F32 drop_shadow_strength = 0.f; if (style & (DROP_SHADOW | DROP_SHADOW_SOFT)) { F32 luminance; color.calcHSL(NULL, NULL, &luminance); drop_shadow_strength = clamp_rescale(luminance, 0.35f, 0.6f, 0.f, 1.f); if (luminance < 0.35f) { style = style & ~(DROP_SHADOW | DROP_SHADOW_SOFT); } } gGL.pushMatrix(); glLoadIdentity(); gGL.translatef(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ); // this code snaps the text origin to a pixel grid to start with F32 pixel_offset_x = llround((F32)sCurOrigin.mX) - (sCurOrigin.mX); F32 pixel_offset_y = llround((F32)sCurOrigin.mY) - (sCurOrigin.mY); gGL.translatef(-pixel_offset_x, -pixel_offset_y, 0.f); LLFastTimer t(LLFastTimer::FTM_RENDER_FONTS); gGL.color4fv( color.mV ); S32 chars_drawn = 0; S32 i; S32 length; if (-1 == max_chars) { length = (S32)wstr.length() - begin_offset; } else { length = llmin((S32)wstr.length() - begin_offset, max_chars ); } F32 cur_x, cur_y, cur_render_x, cur_render_y; // Not guaranteed to be set correctly gGL.setSceneBlendType(LLRender::BT_ALPHA); cur_x = ((F32)x * sScaleX); cur_y = ((F32)y * sScaleY); // Offset y by vertical alignment. switch (valign) { case TOP: cur_y -= mAscender; break; case BOTTOM: cur_y += mDescender; break; case VCENTER: cur_y -= ((mAscender - mDescender)/2.f); break; case BASELINE: // Baseline, do nothing. break; default: break; } switch (halign) { case LEFT: break; case RIGHT: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)); break; case HCENTER: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)) / 2; break; default: break; } cur_render_y = cur_y; cur_render_x = cur_x; F32 start_x = cur_x; F32 inv_width = 1.f / mFontBitmapCachep->getBitmapWidth(); F32 inv_height = 1.f / mFontBitmapCachep->getBitmapHeight(); const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL; BOOL draw_ellipses = FALSE; if (use_ellipses && halign == LEFT) { // check for too long of a string if (getWidthF32(wstr.c_str(), 0, max_chars) * sScaleX > scaled_max_pixels) { // use four dots for ellipsis width to generate padding const LLWString dots(utf8str_to_wstring(std::string("...."))); scaled_max_pixels = llmax(0, scaled_max_pixels - llround(getWidthF32(dots.c_str()))); draw_ellipses = TRUE; } } // Remember last-used texture to avoid unnecesssary bind calls. LLImageGL *last_bound_texture = NULL; for (i = begin_offset; i < begin_offset + length; i++) { llwchar wch = wstr[i]; // Handle embedded characters first, if they're enabled. // Embedded characters are a hack for notecards const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; if (ext_data) { LLImageGL* ext_image = ext_data->mImage; const LLWString& label = ext_data->mLabel; F32 ext_height = (F32)ext_image->getHeight() * sScaleY; F32 ext_width = (F32)ext_image->getWidth() * sScaleX; F32 ext_advance = (EXT_X_BEARING * sScaleX) + ext_width; if (!label.empty()) { ext_advance += (EXT_X_BEARING + getFontExtChar()->getWidthF32( label.c_str() )) * sScaleX; } if (start_x + scaled_max_pixels < cur_x + ext_advance) { // Not enough room for this character. break; } if (last_bound_texture != ext_image) { gGL.getTexUnit(0)->bind(ext_image); last_bound_texture = ext_image; } // snap origin to whole screen pixel const F32 ext_x = (F32)llround(cur_render_x + (EXT_X_BEARING * sScaleX)); const F32 ext_y = (F32)llround(cur_render_y + (EXT_Y_BEARING * sScaleY + mAscender - mLineHeight)); LLRectf uv_rect(0.f, 1.f, 1.f, 0.f); LLRectf screen_rect(ext_x, ext_y + ext_height, ext_x + ext_width, ext_y); drawGlyph(screen_rect, uv_rect, LLColor4::white, style, drop_shadow_strength); if (!label.empty()) { gGL.pushMatrix(); //glLoadIdentity(); //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); //glScalef(sScaleX, sScaleY, 1.f); getFontExtChar()->render(label, 0, /*llfloor*/((ext_x + (F32)ext_image->getWidth() + EXT_X_BEARING) / sScaleX), /*llfloor*/(cur_y / sScaleY), color, halign, BASELINE, NORMAL, S32_MAX, S32_MAX, NULL, TRUE ); gGL.popMatrix(); } gGL.color4fv(color.mV); chars_drawn++; cur_x += ext_advance; if (((i + 1) < length) && wstr[i+1]) { cur_x += EXT_KERNING * sScaleX; } cur_render_x = cur_x; } else { if (!hasGlyph(wch)) { addChar(wch); } const LLFontGlyphInfo* fgi= getGlyphInfo(wch); if (!fgi) { llerrs << "Missing Glyph Info" << llendl; break; } // Per-glyph bitmap texture. LLImageGL *image_gl = mFontBitmapCachep->getImageGL(fgi->mBitmapNum); if (last_bound_texture != image_gl) { gGL.getTexUnit(0)->bind(image_gl); last_bound_texture = image_gl; } if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) { // Not enough room for this character. break; } // Draw the text at the appropriate location //Specify vertices and texture coordinates LLRectf uv_rect((fgi->mXBitmapOffset) * inv_width, (fgi->mYBitmapOffset + fgi->mHeight + PAD_UVY) * inv_height, (fgi->mXBitmapOffset + fgi->mWidth) * inv_width, (fgi->mYBitmapOffset - PAD_UVY) * inv_height); // snap glyph origin to whole screen pixel LLRectf screen_rect(llround(cur_render_x + (F32)fgi->mXBearing), llround(cur_render_y + (F32)fgi->mYBearing), llround(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth, llround(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight); drawGlyph(screen_rect, uv_rect, color, style, drop_shadow_strength); chars_drawn++; cur_x += fgi->mXAdvance; cur_y += fgi->mYAdvance; llwchar next_char = wstr[i+1]; if (next_char && (next_char < LAST_CHARACTER)) { // Kern this puppy. if (!hasGlyph(next_char)) { addChar(next_char); } cur_x += getXKerning(wch, next_char); } // Round after kerning. // Must do this to cur_x, not just to cur_render_x, otherwise you // will squish sub-pixel kerned characters too close together. // For example, "CCCCC" looks bad. cur_x = (F32)llfloor(cur_x + 0.5f); //cur_y = (F32)llfloor(cur_y + 0.5f); cur_render_x = cur_x; cur_render_y = cur_y; } } if (right_x) { *right_x = cur_x / sScaleX; } if (style & UNDERLINE) { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.begin(LLRender::LINES); gGL.vertex2f(start_x, cur_y - (mDescender)); gGL.vertex2f(cur_x, cur_y - (mDescender)); gGL.end(); } // *FIX: get this working in all alignment cases, etc. if (draw_ellipses) { // recursively render ellipses at end of string // we've already reserved enough room gGL.pushMatrix(); //glLoadIdentity(); //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); //glScalef(sScaleX, sScaleY, 1.f); renderUTF8(std::string("..."), 0, cur_x / sScaleX, (F32)y, color, LEFT, valign, style, S32_MAX, max_pixels, right_x, FALSE); gGL.popMatrix(); } gGL.popMatrix(); return chars_drawn; }
void EditText::doRender() { if (nullptr == mFont || !mVisible || mEmptyView) return; if (mRenderItem->getCurrentUpdate() || mTextOutDate) updateRawData(); Vertex* vertex = mRenderItem->getCurrentVertexBuffer(); const RenderTargetInfo& renderTargetInfo = mRenderItem->getRenderTarget()->getInfo(); // колличество отрисованных вершин size_t vertexCount = 0; // текущие цвета uint32 colour = mCurrentColourNative; uint32 inverseColour = mInverseColourNative; uint32 selectedColour = mInvertSelect ? inverseColour : mSelectionBgColor; const VectorLineInfo& textViewData = mTextView.getData(); float top = (float)(-mViewOffset.top + mCoord.top); FloatRect vertexRect; const FloatRect& selectedUVRect = mFont->getGlyphInfo(mBackgroundNormal ? FontCodeType::Selected : FontCodeType::SelectedBack)->uvRect; size_t index = 0; for (VectorLineInfo::const_iterator line = textViewData.begin(); line != textViewData.end(); ++line) { float left = (float)(line->offset - mViewOffset.left + mCoord.left); for (VectorCharInfo::const_iterator sim = line->simbols.begin(); sim != line->simbols.end(); ++sim) { if (sim->isColour()) { colour = sim->getColour() | (colour & 0xFF000000); inverseColour = colour ^ 0x00FFFFFF; selectedColour = mInvertSelect ? inverseColour : mSelectionBgColor; continue; } // смещение текстуры для фона bool select = index >= mStartSelect && index < mEndSelect; float fullAdvance = sim->getBearingX() + sim->getAdvance(); // Render the selection, if any, first. if (select) { vertexRect.set(left, top, left + fullAdvance, top + (float)mFontHeight); drawGlyph(renderTargetInfo, vertex, vertexCount, vertexRect, selectedUVRect, selectedColour); } // Render the glyph shadow, if any. if (mShadow) { vertexRect.left = left + sim->getBearingX() + 1.0f; vertexRect.top = top + sim->getBearingY() + 1.0f; vertexRect.right = vertexRect.left + sim->getWidth(); vertexRect.bottom = vertexRect.top + sim->getHeight(); drawGlyph(renderTargetInfo, vertex, vertexCount, vertexRect, sim->getUVRect(), mShadowColourNative); } // Render the glyph itself. vertexRect.left = left + sim->getBearingX(); vertexRect.top = top + sim->getBearingY(); vertexRect.right = vertexRect.left + sim->getWidth(); vertexRect.bottom = vertexRect.top + sim->getHeight(); drawGlyph(renderTargetInfo, vertex, vertexCount, vertexRect, sim->getUVRect(), (!select || !mInvertSelect) ? colour : inverseColour); left += fullAdvance; ++index; } top += mFontHeight; ++index; } // Render the cursor, if any, last. if (mVisibleCursor) { IntPoint point = mTextView.getCursorPoint(mCursorPosition) - mViewOffset + mCoord.point(); GlyphInfo* cursorGlyph = mFont->getGlyphInfo(static_cast<Char>(FontCodeType::Cursor)); vertexRect.set((float)point.left, (float)point.top, (float)point.left + cursorGlyph->width, (float)(point.top + mFontHeight)); drawGlyph(renderTargetInfo, vertex, vertexCount, vertexRect, cursorGlyph->uvRect, mCurrentColourNative | 0x00FFFFFF); } // колличество реально отрисованных вершин mRenderItem->setLastVertexCount(vertexCount); }
void NetHackQtGlyphs::drawCell(QPainter& painter, int glyph, int cellx, int celly) { drawGlyph(painter,glyph,cellx*width(),celly*height()); }
void ONScripter::drawChar( char* text, FontInfo *info, bool flush_flag, bool lookback_flag, SDL_Surface *surface, AnimationInfo *cache_info, SDL_Rect *clip ) { //printf("draw %x-%x[%s] %d, %d\n", text[0], text[1], text, info->xy[0], info->xy[1] ); if ( info->ttf_font == NULL ){ if ( info->openFont( font_file, screen_ratio1, screen_ratio2 ) == NULL ){ fprintf( stderr, "can't open font file: %s\n", font_file ); quit(); exit(-1); } } #if defined(PSP) else info->openFont( font_file, screen_ratio1, screen_ratio2 ); #endif if ( info->isEndOfLine() ){ info->newLine(); for (int i=0 ; i<indent_offset ; i++){ if (lookback_flag){ current_page->add(0x81); current_page->add(0x40); } info->advanceCharInHankaku(2); } } old_xy[0] = info->x(); old_xy[1] = info->y(); char text2[2] = {text[0], 0}; if (IS_TWO_BYTE(text[0])) text2[1] = text[1]; for (int i=0 ; i<2 ; i++){ int xy[2]; xy[0] = info->x() * screen_ratio1 / screen_ratio2; xy[1] = info->y() * screen_ratio1 / screen_ratio2; SDL_Color color; SDL_Rect dst_rect; if ( info->is_shadow ){ color.r = color.g = color.b = 0; drawGlyph(surface, info, color, text2, xy, true, cache_info, clip, dst_rect); } color.r = info->color[0]; color.g = info->color[1]; color.b = info->color[2]; drawGlyph( surface, info, color, text2, xy, false, cache_info, clip, dst_rect ); if ( surface == accumulation_surface && !flush_flag && (!clip || AnimationInfo::doClipping( &dst_rect, clip ) == 0) ){ dirty_rect.add( dst_rect ); } else if ( flush_flag ){ info->addShadeArea(dst_rect, shade_distance); flushDirect( dst_rect, REFRESH_NONE_MODE ); } if (IS_TWO_BYTE(text[0])){ info->advanceCharInHankaku(2); break; } info->advanceCharInHankaku(1); text2[0] = text[1]; if (text2[0] == 0) break; } if ( lookback_flag ){ current_page->add( text[0] ); if (text[1]) current_page->add( text[1] ); } }
int main(int argc, char *argv[]){ KGString *tmp1, *tmp2, *test1, *test2, *filename; FILE *err; char errbuf[errorFileSize]; char *pos, *cur; int dummy; int type; dummy = initDB(); //set default kShotai = kMincho; //kShotai = kGothic; kSize = 200; kType = 0; //png kInput = 0; //ids or direct kResultText = kg_string_new(""); kMode = 0; //confirm request type = 0; //GET request if(type == 0){ tmp1 = kg_string_new((kgchar *)getenv("QUERY_STRING")); if(tmp1->len != 0) type = 2; } //argv(detect after GET request) if(type == 0){ tmp1 = kg_string_new((kgchar *)argv[1]); if(tmp1->len != 0) type = 1; } //redirect if(type == 0){ tmp1 = kg_string_new((kgchar *)getenv("REDIRECT_URL")); if(tmp1->len != 0) type = 3; } //error if(type == 0){ fprintf(stderr, "Request Error.\n"); return 0; } pos = tmp1->str; //separate token if(type == 1 || type == 2){ //argv or GET request while(1){ cur = strchr(pos, '&'); tmp2 = kg_string_new(pos); if(cur != NULL) kg_string_set_size(tmp2, cur - pos); //got request string if(strncmp(tmp2->str, "shotai=mincho", 13) == 0) kShotai = kMincho; else if(strncmp(tmp2->str, "shotai=gothic", 13) == 0) kShotai = kGothic; else if(strncmp(tmp2->str, "shotai=skeleton", 15) == 0) kShotai = kGothic; else if(strncmp(tmp2->str, "type=png", 8) == 0) kType = 0; else if(strncmp(tmp2->str, "type=svg", 8) == 0) kType = 1; else if(strncmp(tmp2->str, "type=eps", 8) == 0) kType = 2; else if(strncmp(tmp2->str, "type=raw", 8) == 0) kType = 3; else if(strncmp(tmp2->str, "input=ids", 9) == 0) kInput = 0; else if(strncmp(tmp2->str, "input=directwithadjust", 22) == 0) kInput = 2; else if(strncmp(tmp2->str, "input=direct", 12) == 0) kInput = 1; else if(strncmp(tmp2->str, "size=24", 7) == 0) kSize = 24; else if(strncmp(tmp2->str, "size=200", 8) == 0) kSize = 200; else test1 = kg_string_new(tmp2->str); if(cur == NULL) break; pos = cur + 1; } } else{ // redirected request kInput = 0; while(1){ cur = strchr(pos, '/'); tmp2 = kg_string_new(pos); if(cur != NULL) kg_string_set_size(tmp2, cur - pos); //got request string if(strncmp(tmp2->str, "mincho", 6) == 0) kShotai = kMincho; else if(strncmp(tmp2->str, "gothic", 6) == 0) kShotai = kGothic; else if(strncmp(tmp2->str, "skeleton", 8) == 0) kShotai = kGothic; else if(strncmp(tmp2->str, "v0.4", 4) == 0); else test1 = kg_string_new(tmp2->str); if(cur == NULL) break; pos = cur + 1; } if(strncmp(test1->str + test1->len - 4, ".png", 4) == 0) kType = 0; if(strncmp(test1->str + test1->len - 4, ".svg", 4) == 0) kType = 1; if(strncmp(test1->str + test1->len - 4, ".eps", 4) == 0) kType = 2; if(strncmp(test1->str + test1->len - 4, ".raw", 4) == 0) kType = 3; kg_string_set_size(test1, test1->len - 4); } //clear result buffer test2 = kg_string_new(""); if(kType == 1){ //svg kg_string_append(kResultText, "<?xml version=\"1.0\"?>\n"); kg_string_append(kResultText, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"); kg_string_append(kResultText, "<svg width=\"1024\" height=\"1024\" viewBox=\"0 0 1024 1024\">"); kg_string_append(kResultText, "<g style=\"fill: black; stroke: black\">"); } else if(kType == 2){ //eps kg_string_append(kResultText, "%!PS-Adobe-3.0 EPSF-3.0\n"); kg_string_append(kResultText, "%%BoundingBox: 0 -208 1024 816\n"); kg_string_append(kResultText, "%%Pages: 0\n"); kg_string_append(kResultText, "%%Title: "); kg_string_append(kResultText, test1->str); kg_string_append(kResultText, "\n"); kg_string_append(kResultText, "%%Creator: KAGE System\n"); kg_string_append(kResultText, "%%CreationDate: 00:00 1-1-2004\n"); kg_string_append(kResultText, "%%EndComments\n"); kg_string_append(kResultText, "%%EndProlog\n"); kg_string_append(kResultText, "%%Page \""); kg_string_append(kResultText, test1->str); kg_string_append(kResultText, "\" 1\n"); kg_string_append(kResultText, "newpath\n"); } kageCanvas = initPng(canvasWidth, canvasHeight); if(kInput == 0) generateGlyph(test1, test2); else{ convert99(test1, test2); // kg_string_append(test2, test1->str); } if(test2->len != 0) test2 = finalAdjustment(test2); if(kType == 0){ //png(image) if(test2->len != 0){ if(kInput != 1){ //0 and 2 test2 = CalcSizes(test2, 1); } DrawBox(); drawGlyph(test2, DRAW_GLYPH_MODE_NORMAL); //output to file filename = kg_string_new(pngFilePath); if(kShotai == kMincho) kg_string_append(filename, "mincho/"); else if(kShotai == kGothic) kg_string_append(filename, "gothic/");//skeleton?? kg_string_append(filename, test1->str); kg_string_append(filename, ".png"); //skip for adjustment mode //fp = fopen(filename->str, "w"); //writePng(pngWidth, pngHeight, kageCanvas, fp); //fclose(fp); //output to stdout if(type != 1) fprintf(stdout, "Content-type: image/png\n\n"); writePng(pngWidth, pngHeight, kageCanvas, stdout); //done closePng(pngWidth, pngHeight, kageCanvas); } else{ err = fopen(errorFileName, "r"); fread(errbuf, sizeof(char), errorFileSize, err); //printf("An error occurred.\r\n"); if(type != 1) fprintf(stdout, "Content-type: image/png\n\n"); fwrite(errbuf, sizeof(char), errorFileSize, stdout); fclose(err); } } else if(kType == 1){ //svg(vector graphics) if(test2->len != 0){ test2 = CalcSizes(test2, 1); kMode = 1; drawGlyph(test2, DRAW_GLYPH_MODE_NORMAL); kg_string_append(kResultText, "</g></svg>\n"); if(type != 1) fprintf(stdout, "Content-type: image/svg-xml\n\n"); fprintf(stdout, "%s", kResultText->str); } else{ if(type != 1) fprintf(stdout, "Content-type: text/plain\n\n"); fprintf(stdout, "An error occurred."); } } else if(kType == 2){ //eps(vector graphics) if(test2->len != 0){ test2 = CalcSizes(test2, 1); kMode = 2; drawGlyph(test2, DRAW_GLYPH_MODE_NORMAL); kg_string_append(kResultText, "fill\n"); kg_string_append(kResultText, "%%EOF\n"); if(type != 1) fprintf(stdout, "Content-type: application/postscript\n\n"); fprintf(stdout, "%s", kResultText->str); } else{ if(type != 1) fprintf(stdout, "Content-type: text/plain\n\n"); fprintf(stdout, "An error occurred."); } } else{ //raw(text) if(test2->len != 0){ test2 = CalcSizes(test2, 1); if(type != 1) fprintf(stdout, "Content-type: text/plain\n\n"); fprintf(stdout, "result=%s", test2->str); } else{ if(type != 1) fprintf(stdout, "Content-type: text/plain\n\n"); fprintf(stdout, "result=nodata"); } } dummy = closeDB(); return 0; }
S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_embedded, BOOL use_ellipses) const { LLFastTimer _(FTM_RENDER_FONTS); if(!sDisplayFont) //do not display texts { return wstr.length() ; } if (wstr.empty() || !max_pixels) { return 0; } if (max_chars == -1) max_chars = S32_MAX; const S32 max_index = llmin(llmax(max_chars, begin_offset + max_chars), S32(wstr.length())); if (max_index <= 0 || begin_offset >= max_index || max_pixels <= 0) return 0; gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX); // Strip off any style bits that are already accounted for by the font. style = style & (~getFontDesc().getStyle()); F32 drop_shadow_strength = 0.f; if (shadow != NO_SHADOW) { F32 luminance; color.calcHSL(NULL, NULL, &luminance); drop_shadow_strength = clamp_rescale(luminance, 0.35f, 0.6f, 0.f, 1.f); if (luminance < 0.35f) { shadow = NO_SHADOW; } } gGL.pushUIMatrix(); gGL.loadUIIdentity(); LLVector2 origin(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY)); // Depth translation, so that floating text appears 'in-world' // and is correctly occluded. gGL.translatef(0.f,0.f,sCurDepth); S32 chars_drawn = 0; S32 i; S32 length = max_index - begin_offset; F32 cur_x, cur_y, cur_render_x, cur_render_y; // Not guaranteed to be set correctly gGL.setSceneBlendType(LLRender::BT_ALPHA); cur_x = ((F32)x * sScaleX) + origin.mV[VX]; cur_y = ((F32)y * sScaleY) + origin.mV[VY]; // Offset y by vertical alignment. // use unscaled font metrics here switch (valign) { case TOP: cur_y -= llceil(mFontFreetype->getAscenderHeight()); break; case BOTTOM: cur_y += llceil(mFontFreetype->getDescenderHeight()); break; case VCENTER: cur_y -= llceil((llceil(mFontFreetype->getAscenderHeight()) - llceil(mFontFreetype->getDescenderHeight())) / 2.f); break; case BASELINE: // Baseline, do nothing. break; default: break; } switch (halign) { case LEFT: break; case RIGHT: cur_x -= llmin(scaled_max_pixels, ll_round(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)); break; case HCENTER: cur_x -= llmin(scaled_max_pixels, ll_round(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)) / 2; break; default: break; } cur_render_y = cur_y; cur_render_x = cur_x; F32 start_x = (F32)ll_round(cur_x); const LLFontBitmapCache* font_bitmap_cache = mFontFreetype->getFontBitmapCache(); F32 inv_width = 1.f / font_bitmap_cache->getBitmapWidth(); F32 inv_height = 1.f / font_bitmap_cache->getBitmapHeight(); const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL; BOOL draw_ellipses = FALSE; if (use_ellipses && halign == LEFT) { // check for too long of a string S32 string_width = ll_round(getWidthF32(wstr, begin_offset, max_chars) * sScaleX); if (string_width > scaled_max_pixels) { // use four dots for ellipsis width to generate padding const LLWString dots(utf8str_to_wstring(std::string("...."))); scaled_max_pixels = llmax(0, scaled_max_pixels - ll_round(getWidthF32(dots.c_str()))); draw_ellipses = TRUE; } } const LLFontGlyphInfo* next_glyph = NULL; const S32 GLYPH_BATCH_SIZE = 30; static LL_ALIGN_16(LLVector4a vertices[GLYPH_BATCH_SIZE * 4]); static LLVector2 uvs[GLYPH_BATCH_SIZE * 4]; static LLColor4U colors[GLYPH_BATCH_SIZE * 4]; LLColor4U text_color(color); S32 bitmap_num = -1; S32 glyph_count = 0; for (i = begin_offset; i < begin_offset + length; i++) { llwchar wch = wstr[i]; // Handle embedded characters first, if they're enabled. // Embedded characters are a hack for notecards const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; if (ext_data) { LLImageGL* ext_image = ext_data->mImage; const LLWString& label = ext_data->mLabel; F32 ext_height = (F32)ext_image->getHeight() * sScaleY; F32 ext_width = (F32)ext_image->getWidth() * sScaleX; F32 ext_advance = (EXT_X_BEARING * sScaleX) + ext_width; if (!label.empty()) { ext_advance += (EXT_X_BEARING + getFontExtChar()->getWidthF32( label.c_str() )) * sScaleX; } if (start_x + scaled_max_pixels < cur_x + ext_advance) { // Not enough room for this character. break; } gGL.getTexUnit(0)->bind(ext_image); // snap origin to whole screen pixel const F32 ext_x = (F32)ll_round(cur_render_x + (EXT_X_BEARING * sScaleX)); const F32 ext_y = (F32)ll_round(cur_render_y + (EXT_Y_BEARING * sScaleY + mFontFreetype->getAscenderHeight() - mFontFreetype->getLineHeight())); LLRectf uv_rect(0.f, 1.f, 1.f, 0.f); LLRectf screen_rect(ext_x, ext_y + ext_height, ext_x + ext_width, ext_y); if (glyph_count > 0) { gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); glyph_count = 0; } renderQuad(vertices, uvs, colors, screen_rect, uv_rect, LLColor4U::white, 0); //No batching here. It will never happen. gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, 4); } gGL.end(); if (!label.empty()) { gGL.pushMatrix(); getFontExtChar()->render(label, 0, /*llfloor*/(ext_x / sScaleX) + ext_image->getWidth() + EXT_X_BEARING - sCurOrigin.mX, /*llfloor*/(cur_render_y / sScaleY) - sCurOrigin.mY, color, halign, BASELINE, UNDERLINE, NO_SHADOW, S32_MAX, S32_MAX, NULL, TRUE ); gGL.popMatrix(); } chars_drawn++; cur_x += ext_advance; if (((i + 1) < length) && wstr[i+1]) { cur_x += EXT_KERNING * sScaleX; } cur_render_x = cur_x; } else { const LLFontGlyphInfo* fgi = next_glyph; next_glyph = NULL; if(!fgi) { fgi = mFontFreetype->getGlyphInfo(wch); } if (!fgi) { LL_ERRS() << "Missing Glyph Info" << LL_ENDL; break; } // Per-glyph bitmap texture. S32 next_bitmap_num = fgi->mBitmapNum; if (next_bitmap_num != bitmap_num) { // Actually draw the queued glyphs before switching their texture; // otherwise the queued glyphs will be taken from wrong textures. if (glyph_count > 0) { gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); glyph_count = 0; } bitmap_num = next_bitmap_num; LLImageGL *font_image = font_bitmap_cache->getImageGL(bitmap_num); gGL.getTexUnit(0)->bind(font_image); } if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) { // Not enough room for this character. break; } // Draw the text at the appropriate location //Specify vertices and texture coordinates LLRectf uv_rect((fgi->mXBitmapOffset) * inv_width, (fgi->mYBitmapOffset + fgi->mHeight + PAD_UVY) * inv_height, (fgi->mXBitmapOffset + fgi->mWidth) * inv_width, (fgi->mYBitmapOffset - PAD_UVY) * inv_height); // snap glyph origin to whole screen pixel LLRectf screen_rect((F32)ll_round(cur_render_x + (F32)fgi->mXBearing), (F32)ll_round(cur_render_y + (F32)fgi->mYBearing), (F32)ll_round(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth, (F32)ll_round(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight); if (glyph_count >= GLYPH_BATCH_SIZE) { gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); glyph_count = 0; } drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, text_color, style, shadow, drop_shadow_strength); chars_drawn++; cur_x += fgi->mXAdvance; cur_y += fgi->mYAdvance; llwchar next_char = wstr[i+1]; if (next_char && (next_char < LAST_CHARACTER)) { // Kern this puppy. next_glyph = mFontFreetype->getGlyphInfo(next_char); cur_x += mFontFreetype->getXKerning(fgi, next_glyph); } // Round after kerning. // Must do this to cur_x, not just to cur_render_x, otherwise you // will squish sub-pixel kerned characters too close together. // For example, "CCCCC" looks bad. cur_x = (F32)ll_round(cur_x); //cur_y = (F32)ll_round(cur_y); cur_render_x = cur_x; cur_render_y = cur_y; } } if(glyph_count) { gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); } if (right_x) { *right_x = (cur_x - origin.mV[VX]) / sScaleX; } if (style & UNDERLINE) { F32 descender = (F32)llfloor(mFontFreetype->getDescenderHeight()); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.begin(LLRender::LINES); gGL.vertex2f(start_x, cur_y - descender); gGL.vertex2f(cur_x, cur_y - descender); gGL.end(); } if (draw_ellipses) { // recursively render ellipses at end of string // we've already reserved enough room gGL.pushUIMatrix(); renderUTF8(std::string("..."), 0, (cur_x - origin.mV[VX]) / sScaleX, (F32)y, color, LEFT, valign, style, shadow, S32_MAX, max_pixels, right_x, FALSE); gGL.popUIMatrix(); } gGL.popUIMatrix(); return chars_drawn; }
S32 LLFontGL::render(const LLWString &wstr, const S32 begin_offset, const F32 x, const F32 y, const LLColor4 &color, const HAlign halign, const VAlign valign, U8 style, const S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_embedded, BOOL use_ellipses) const { if(!sDisplayFont) //do not display texts { return wstr.length() ; } LLGLEnable tex(GL_TEXTURE_2D); if (wstr.empty()) { return 0; } S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX); // HACK for better bolding if (style & BOLD) { if (this == LLFontGL::sSansSerif) { return LLFontGL::sSansSerifBold->render( wstr, begin_offset, x, y, color, halign, valign, (style & ~BOLD), max_chars, max_pixels, right_x, use_embedded); } } F32 drop_shadow_strength = 0.f; if (style & (DROP_SHADOW | DROP_SHADOW_SOFT)) { F32 luminance; color.calcHSL(NULL, NULL, &luminance); drop_shadow_strength = clamp_rescale(luminance, 0.35f, 0.6f, 0.f, 1.f); if (luminance < 0.35f) { style = style & ~(DROP_SHADOW | DROP_SHADOW_SOFT); } } gGL.pushMatrix(); glLoadIdentity(); gGL.translatef(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ); //glScalef(sScaleX, sScaleY, 1.0f); // avoid half pixels // RN: if we're going to this trouble, might as well snap to nearest pixel all the time // but the plan is to get rid of this so that fonts "just work" //F32 half_pixel_distance = llabs(fmodf(sCurOrigin.mX * sScaleX, 1.f) - 0.5f); //if (half_pixel_distance < PIXEL_BORDER_THRESHOLD) //{ gGL.translatef(PIXEL_CORRECTION_DISTANCE*sScaleX, 0.f, 0.f); //} // this code would just snap to pixel grid, although it seems to introduce more jitter //F32 pixel_offset_x = llround(sCurOrigin.mX * sScaleX) - (sCurOrigin.mX * sScaleX); //F32 pixel_offset_y = llround(sCurOrigin.mY * sScaleY) - (sCurOrigin.mY * sScaleY); //gGL.translatef(-pixel_offset_x, -pixel_offset_y, 0.f); // scale back to native pixel size //glScalef(1.f / sScaleX, 1.f / sScaleY, 1.f); //glScaled(1.0 / (F64) sScaleX, 1.0 / (F64) sScaleY, 1.0f); LLFastTimer t(LLFastTimer::FTM_RENDER_FONTS); gGL.color4fv( color.mV ); S32 chars_drawn = 0; S32 i; S32 length; if (-1 == max_chars) { length = (S32)wstr.length() - begin_offset; } else { length = llmin((S32)wstr.length() - begin_offset, max_chars ); } F32 cur_x, cur_y, cur_render_x, cur_render_y; // Bind the font texture mImageGLp->bind(0); // Not guaranteed to be set correctly gGL.setSceneBlendType(LLRender::BT_ALPHA); cur_x = ((F32)x * sScaleX); cur_y = ((F32)y * sScaleY); // Offset y by vertical alignment. switch (valign) { case TOP: cur_y -= mAscender; break; case BOTTOM: cur_y += mDescender; break; case VCENTER: cur_y -= ((mAscender - mDescender)/2.f); break; case BASELINE: // Baseline, do nothing. break; default: break; } switch (halign) { case LEFT: break; case RIGHT: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)); break; case HCENTER: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)) / 2; break; default: break; } // Round properly. //cur_render_y = (F32)llfloor(cur_y/sScaleY + 0.5f)*sScaleY; //cur_render_x = (F32)llfloor(cur_x/sScaleX + 0.5f)*sScaleX; cur_render_y = cur_y; cur_render_x = cur_x; F32 start_x = cur_x; F32 inv_width = 1.f / mImageGLp->getWidth(); F32 inv_height = 1.f / mImageGLp->getHeight(); const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL; BOOL draw_ellipses = FALSE; if (use_ellipses && halign == LEFT) { // check for too long of a string if (getWidthF32(wstr.c_str(), 0, max_chars) * sScaleX > scaled_max_pixels) { // use four dots for ellipsis width to generate padding const LLWString dots(utf8str_to_wstring(std::string("...."))); scaled_max_pixels = llmax(0, scaled_max_pixels - llround(getWidthF32(dots.c_str()))); draw_ellipses = TRUE; } } for (i = begin_offset; i < begin_offset + length; i++) { llwchar wch = wstr[i]; // Handle embedded characters first, if they're enabled. // Embedded characters are a hack for notecards const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; if (ext_data) { LLImageGL* ext_image = ext_data->mImage; const LLWString& label = ext_data->mLabel; F32 ext_height = (F32)ext_image->getHeight() * sScaleY; F32 ext_width = (F32)ext_image->getWidth() * sScaleX; F32 ext_advance = (EXT_X_BEARING * sScaleX) + ext_width; if (!label.empty()) { ext_advance += (EXT_X_BEARING + gExtCharFont->getWidthF32( label.c_str() )) * sScaleX; } if (start_x + scaled_max_pixels < cur_x + ext_advance) { // Not enough room for this character. break; } ext_image->bind(); const F32 ext_x = cur_render_x + (EXT_X_BEARING * sScaleX); const F32 ext_y = cur_render_y + (EXT_Y_BEARING * sScaleY + mAscender - mLineHeight); LLRectf uv_rect(0.f, 1.f, 1.f, 0.f); LLRectf screen_rect(ext_x, ext_y + ext_height, ext_x + ext_width, ext_y); drawGlyph(screen_rect, uv_rect, LLColor4::white, style, drop_shadow_strength); if (!label.empty()) { gGL.pushMatrix(); //glLoadIdentity(); //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); //glScalef(sScaleX, sScaleY, 1.f); gExtCharFont->render(label, 0, /*llfloor*/((ext_x + (F32)ext_image->getWidth() + EXT_X_BEARING) / sScaleX), /*llfloor*/(cur_y / sScaleY), color, halign, BASELINE, NORMAL, S32_MAX, S32_MAX, NULL, TRUE ); gGL.popMatrix(); } gGL.color4fv(color.mV); chars_drawn++; cur_x += ext_advance; if (((i + 1) < length) && wstr[i+1]) { cur_x += EXT_KERNING * sScaleX; } cur_render_x = cur_x; // Bind the font texture mImageGLp->bind(); } else { if (!hasGlyph(wch)) { (const_cast<LLFontGL*>(this))->addChar(wch); } const LLFontGlyphInfo* fgi= getGlyphInfo(wch); if (!fgi) { llerrs << "Missing Glyph Info" << llendl; break; } if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) { // Not enough room for this character. break; } // Draw the text at the appropriate location //Specify vertices and texture coordinates LLRectf uv_rect((fgi->mXBitmapOffset - PAD_AMT) * inv_width, (fgi->mYBitmapOffset + fgi->mHeight + PAD_AMT) * inv_height, (fgi->mXBitmapOffset + fgi->mWidth + PAD_AMT) * inv_width, (fgi->mYBitmapOffset - PAD_AMT) * inv_height); LLRectf screen_rect(cur_render_x + (F32)fgi->mXBearing - PAD_AMT, cur_render_y + (F32)fgi->mYBearing + PAD_AMT, cur_render_x + (F32)fgi->mXBearing + (F32)fgi->mWidth + PAD_AMT, cur_render_y + (F32)fgi->mYBearing - (F32)fgi->mHeight - PAD_AMT); drawGlyph(screen_rect, uv_rect, color, style, drop_shadow_strength); chars_drawn++; cur_x += fgi->mXAdvance; cur_y += fgi->mYAdvance; llwchar next_char = wstr[i+1]; if (next_char && (next_char < LAST_CHARACTER)) { // Kern this puppy. if (!hasGlyph(next_char)) { (const_cast<LLFontGL*>(this))->addChar(next_char); } cur_x += getXKerning(wch, next_char); } // Round after kerning. // Must do this to cur_x, not just to cur_render_x, otherwise you // will squish sub-pixel kerned characters too close together. // For example, "CCCCC" looks bad. cur_x = (F32)llfloor(cur_x + 0.5f); //cur_y = (F32)llfloor(cur_y + 0.5f); cur_render_x = cur_x; cur_render_y = cur_y; } } if (right_x) { *right_x = cur_x / sScaleX; } if (style & UNDERLINE) { LLGLSNoTexture no_texture; gGL.begin(LLVertexBuffer::LINES); gGL.vertex2f(start_x, cur_y - (mDescender)); gGL.vertex2f(cur_x, cur_y - (mDescender)); gGL.end(); } // *FIX: get this working in all alignment cases, etc. if (draw_ellipses) { // recursively render ellipses at end of string // we've already reserved enough room gGL.pushMatrix(); //glLoadIdentity(); //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); //glScalef(sScaleX, sScaleY, 1.f); renderUTF8(std::string("..."), 0, cur_x / sScaleX, (F32)y, color, LEFT, valign, style, S32_MAX, max_pixels, right_x, FALSE); gGL.popMatrix(); } gGL.popMatrix(); return chars_drawn; }
// Returns character bytes. // This is where we process ligatures for display text! int PonscripterLabel::drawChar(const char* text, Fontinfo* info, bool flush_flag, bool lookback_flag, SDL_Surface* surface, AnimationInfo* cache_info, SDL_Rect* clip) { int bytes; wchar unicode = file_encoding->DecodeWithLigatures(text, *info, bytes); bool code = info->processCode(text); bool hidden_language = (current_read_language != -1 && current_read_language != current_language); if (!code && !hidden_language) { // info->doSize() called in GlyphAdvance wchar next = file_encoding->DecodeWithLigatures(text + bytes, *info); float adv = info->GlyphAdvance(unicode, next); if (isNonspacing(unicode)) info->advanceBy(-adv); if (info->isNoRoomFor(adv)) info->newLine(); float x = info->GetX() * screen_ratio1 / screen_ratio2; if (info->getRTL()) x -= adv; int y = info->GetY() * screen_ratio1 / screen_ratio2; SDL_Color color; SDL_Rect dst_rect; if (info->is_shadow) { color.r = color.g = color.b = 0; drawGlyph(surface, info, color, unicode, x, y, true, cache_info, clip, dst_rect); } color.r = info->color.r; color.g = info->color.g; color.b = info->color.b; drawGlyph(surface, info, color, unicode, x, y, false, cache_info, clip, dst_rect); info->addShadeArea(dst_rect, shade_distance); if (surface == accumulation_surface && !flush_flag && (!clip || AnimationInfo::doClipping(&dst_rect, clip) == 0)) { dirty_rect.add(dst_rect); } else if (flush_flag) { if (surface == accumulation_surface) flush(refreshMode()); // hack to fix skip refresh bug flushDirect(dst_rect, REFRESH_NONE_MODE); } /* ---------------------------------------- */ /* Update text buffer */ info->advanceBy(adv); } // textbufferchange int j; for (j = 0; j < 2; j++) { if (current_read_language == j || current_read_language == -1) { if (lookback_flag) { current_text_buffer[j]->addBytes(text, bytes); if (text[0] == '~') current_text_buffer[j]->addBytes(text, bytes); } } } TextBuffer_dumpstate(1); return bytes; }
S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses) const { LLFastTimer _(FTM_RENDER_FONTS); if(!sDisplayFont) //do not display texts { return wstr.length() ; } if (wstr.empty()) { return 0; } gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX); // determine which style flags need to be added programmatically by stripping off the // style bits that are drawn by the underlying Freetype font U8 style_to_add = (style | mFontDescriptor.getStyle()) & ~mFontFreetype->getStyle(); F32 drop_shadow_strength = 0.f; if (shadow != NO_SHADOW) { F32 luminance; color.calcHSL(NULL, NULL, &luminance); drop_shadow_strength = clamp_rescale(luminance, 0.35f, 0.6f, 0.f, 1.f); if (luminance < 0.35f) { shadow = NO_SHADOW; } } gGL.pushUIMatrix(); gGL.loadUIIdentity(); //gGL.translateUI(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ); // this code snaps the text origin to a pixel grid to start with //F32 pixel_offset_x = llround((F32)sCurOrigin.mX) - (sCurOrigin.mX); //F32 pixel_offset_y = llround((F32)sCurOrigin.mY) - (sCurOrigin.mY); //gGL.translateUI(-pixel_offset_x, -pixel_offset_y, 0.f); LLVector2 origin(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY)); // snap the text origin to a pixel grid to start with origin.mV[VX] -= llround((F32)sCurOrigin.mX) - (sCurOrigin.mX); origin.mV[VY] -= llround((F32)sCurOrigin.mY) - (sCurOrigin.mY); S32 chars_drawn = 0; S32 i; S32 length; if (-1 == max_chars) { length = (S32)wstr.length() - begin_offset; } else { length = llmin((S32)wstr.length() - begin_offset, max_chars ); } F32 cur_x, cur_y, cur_render_x, cur_render_y; // Not guaranteed to be set correctly gGL.setSceneBlendType(LLRender::BT_ALPHA); cur_x = ((F32)x * sScaleX) + origin.mV[VX]; cur_y = ((F32)y * sScaleY) + origin.mV[VY]; // Offset y by vertical alignment. switch (valign) { case TOP: cur_y -= mFontFreetype->getAscenderHeight(); break; case BOTTOM: cur_y += mFontFreetype->getDescenderHeight(); break; case VCENTER: cur_y -= (mFontFreetype->getAscenderHeight() - mFontFreetype->getDescenderHeight()) / 2.f; break; case BASELINE: // Baseline, do nothing. break; default: break; } switch (halign) { case LEFT: break; case RIGHT: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)); break; case HCENTER: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)) / 2; break; default: break; } cur_render_y = cur_y; cur_render_x = cur_x; F32 start_x = llround(cur_x); const LLFontBitmapCache* font_bitmap_cache = mFontFreetype->getFontBitmapCache(); F32 inv_width = 1.f / font_bitmap_cache->getBitmapWidth(); F32 inv_height = 1.f / font_bitmap_cache->getBitmapHeight(); const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL; BOOL draw_ellipses = FALSE; if (use_ellipses) { // check for too long of a string S32 string_width = llround(getWidthF32(wstr.c_str(), begin_offset, max_chars) * sScaleX); if (string_width > scaled_max_pixels) { // use four dots for ellipsis width to generate padding const LLWString dots(utf8str_to_wstring(std::string("...."))); scaled_max_pixels = llmax(0, scaled_max_pixels - llround(getWidthF32(dots.c_str()))); draw_ellipses = TRUE; } } const LLFontGlyphInfo* next_glyph = NULL; const S32 GLYPH_BATCH_SIZE = 30; LLVector3 vertices[GLYPH_BATCH_SIZE * 4]; LLVector2 uvs[GLYPH_BATCH_SIZE * 4]; LLColor4U colors[GLYPH_BATCH_SIZE * 4]; LLColor4U text_color(color); S32 bitmap_num = -1; S32 glyph_count = 0; for (i = begin_offset; i < begin_offset + length; i++) { llwchar wch = wstr[i]; const LLFontGlyphInfo* fgi = next_glyph; next_glyph = NULL; if(!fgi) { fgi = mFontFreetype->getGlyphInfo(wch); } if (!fgi) { llerrs << "Missing Glyph Info" << llendl; break; } // Per-glyph bitmap texture. S32 next_bitmap_num = fgi->mBitmapNum; if (next_bitmap_num != bitmap_num) { bitmap_num = next_bitmap_num; LLImageGL *font_image = font_bitmap_cache->getImageGL(bitmap_num); gGL.getTexUnit(0)->bind(font_image); } if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) { // Not enough room for this character. break; } // Draw the text at the appropriate location //Specify vertices and texture coordinates LLRectf uv_rect((fgi->mXBitmapOffset) * inv_width, (fgi->mYBitmapOffset + fgi->mHeight + PAD_UVY) * inv_height, (fgi->mXBitmapOffset + fgi->mWidth) * inv_width, (fgi->mYBitmapOffset - PAD_UVY) * inv_height); // snap glyph origin to whole screen pixel LLRectf screen_rect(llround(cur_render_x + (F32)fgi->mXBearing), llround(cur_render_y + (F32)fgi->mYBearing), llround(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth, llround(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight); if (glyph_count >= GLYPH_BATCH_SIZE) { gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); glyph_count = 0; } drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, text_color, style_to_add, shadow, drop_shadow_strength); chars_drawn++; cur_x += fgi->mXAdvance; cur_y += fgi->mYAdvance; llwchar next_char = wstr[i+1]; if (next_char && (next_char < LAST_CHARACTER)) { // Kern this puppy. next_glyph = mFontFreetype->getGlyphInfo(next_char); cur_x += mFontFreetype->getXKerning(fgi, next_glyph); } // Round after kerning. // Must do this to cur_x, not just to cur_render_x, otherwise you // will squish sub-pixel kerned characters too close together. // For example, "CCCCC" looks bad. cur_x = (F32)llround(cur_x); //cur_y = (F32)llround(cur_y); cur_render_x = cur_x; cur_render_y = cur_y; } gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); if (right_x) { *right_x = (cur_x - origin.mV[VX]) / sScaleX; } //FIXME: add underline as glyph? if (style_to_add & UNDERLINE) { F32 descender = mFontFreetype->getDescenderHeight(); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.begin(LLRender::LINES); gGL.vertex2f(start_x, cur_y - (descender)); gGL.vertex2f(cur_x, cur_y - (descender)); gGL.end(); } if (draw_ellipses) { // recursively render ellipses at end of string // we've already reserved enough room gGL.pushUIMatrix(); renderUTF8(std::string("..."), 0, (cur_x - origin.mV[VX]) / sScaleX, (F32)y, color, LEFT, valign, style_to_add, shadow, S32_MAX, max_pixels, right_x, FALSE); gGL.popUIMatrix(); } gGL.popUIMatrix(); return chars_drawn; }