void Bitmap::drawText(const IntRect &rect, const char *str, int align) { GUARD_DISPOSED; GUARD_MEGA; if (*str == '\0') return; if (str[0] == ' ' && str[1] == '\0') return; TTF_Font *font = p->font->getSdlFont(); Color *fontColor = p->font->getColor(); SDL_Color c; fontColor->toSDLColor(c); float txtAlpha = fontColor->norm.w; SDL_Surface *txtSurf; if (shState->rtData().config.solidFonts) txtSurf = TTF_RenderUTF8_Solid(font, str, c); else txtSurf = TTF_RenderUTF8_Blended(font, str, c); p->ensureFormat(txtSurf, SDL_PIXELFORMAT_ARGB8888); int alignX = rect.x; switch (align) { default: case Left : break; case Center : alignX += (rect.w - txtSurf->w) / 2; break; case Right : alignX += rect.w - txtSurf->w; break; } if (alignX < rect.x) alignX = rect.x; int alignY = rect.y + (rect.h - txtSurf->h) / 2; float squeeze = (float) rect.w / txtSurf->w; if (squeeze > 1) squeeze = 1; FloatRect posRect(alignX, alignY, txtSurf->w * squeeze, txtSurf->h); Vec2i gpTexSize; shState->ensureTexSize(txtSurf->w, txtSurf->h, gpTexSize); bool fastBlit = !p->touchesTaintedArea(posRect) && txtAlpha == 1.0; if (fastBlit) { if (squeeze == 1.0) { /* Even faster: upload directly to bitmap texture. * We have to make sure the posRect lies within the texture * boundaries or texSubImage will generate errors. * If it partly lies outside bounds we have to upload * the clipped visible part of it. */ SDL_Rect btmRect; btmRect.x = btmRect.y = 0; btmRect.w = width(); btmRect.h = height(); SDL_Rect txtRect; txtRect.x = posRect.x; txtRect.y = posRect.y; txtRect.w = posRect.w; txtRect.h = posRect.h; SDL_Rect inters; /* If we have no intersection at all, * there's nothing to upload to begin with */ if (SDL_IntersectRect(&btmRect, &txtRect, &inters)) { if (inters.w != txtRect.w || inters.h != txtRect.h) { /* Clip the text surface */ SDL_Rect clipSrc = inters; clipSrc.x -= txtRect.x; clipSrc.y -= txtRect.y; posRect.x = inters.x; posRect.y = inters.y; posRect.w = inters.w; posRect.h = inters.h; PixelStore::setupSubImage(txtSurf->w, clipSrc.x, clipSrc.y); } TEX::bind(p->gl.tex); TEX::uploadSubImage(posRect.x, posRect.y, posRect.w, posRect.h, txtSurf->pixels, GL_BGRA); PixelStore::reset(); } } else { /* Squeezing involved: need to use intermediary TexFBO */ TEXFBO &gpTF = shState->gpTexFBO(txtSurf->w, txtSurf->h); TEX::bind(gpTF.tex); TEX::uploadSubImage(0, 0, txtSurf->w, txtSurf->h, txtSurf->pixels, GL_BGRA); FBO::bind(gpTF.fbo, FBO::Read); p->bindFBO(); FBO::blit(0, 0, txtSurf->w, txtSurf->h, posRect.x, posRect.y, posRect.w, posRect.h, FBO::Linear); } } else { /* Aquire a partial copy of the destination * buffer we're about to render to */ TEXFBO &gpTex2 = shState->gpTexFBO(posRect.w, posRect.h); FBO::bind(gpTex2.fbo, FBO::Draw); FBO::bind(p->gl.fbo, FBO::Read); FBO::blit(posRect.x, posRect.y, 0, 0, posRect.w, posRect.h); FloatRect bltRect(0, 0, (float) gpTexSize.x / gpTex2.width, (float) gpTexSize.y / gpTex2.height); BltShader &shader = shState->shaders().blt; shader.bind(); shader.setTexSize(gpTexSize); shader.setSource(); shader.setDestination(gpTex2.tex); shader.setSubRect(bltRect); shader.setOpacity(txtAlpha); shState->bindTex(); TEX::uploadSubImage(0, 0, txtSurf->w, txtSurf->h, txtSurf->pixels, GL_BGRA); TEX::setSmooth(true); Quad &quad = shState->gpQuad(); quad.setTexRect(FloatRect(0, 0, txtSurf->w, txtSurf->h)); quad.setPosRect(posRect); p->bindFBO(); p->pushSetViewport(shader); glState.blendMode.pushSet(BlendNone); quad.draw(); glState.blendMode.pop(); p->popViewport(); } SDL_FreeSurface(txtSurf); p->addTaintedArea(posRect); modified(); }
void Bitmap::drawText(const IntRect &rect, const char *str, int align) { guardDisposed(); GUARD_MEGA; std::string fixed = fixupString(str); str = fixed.c_str(); if (*str == '\0') return; if (str[0] == ' ' && str[1] == '\0') return; TTF_Font *font = p->font->getSdlFont(); const Color &fontColor = p->font->getColor(); const Color &outColor = p->font->getOutColor(); SDL_Color c = fontColor.toSDLColor(); c.a = 255; float txtAlpha = fontColor.norm.w; SDL_Surface *txtSurf; if (shState->rtData().config.solidFonts) txtSurf = TTF_RenderUTF8_Solid(font, str, c); else txtSurf = TTF_RenderUTF8_Blended(font, str, c); p->ensureFormat(txtSurf, SDL_PIXELFORMAT_ABGR8888); int rawTxtSurfH = txtSurf->h; if (p->font->getShadow()) applyShadow(txtSurf, *p->format, c); /* outline using TTF_Outline and blending it together with SDL_BlitSurface * FIXME: outline is forced to have the same opacity as the font color */ if (p->font->getOutline()) { SDL_Color co = outColor.toSDLColor(); co.a = 255; SDL_Surface *outline; /* set the next font render to render the outline */ TTF_SetFontOutline(font, OUTLINE_SIZE); if (shState->rtData().config.solidFonts) outline = TTF_RenderUTF8_Solid(font, str, co); else outline = TTF_RenderUTF8_Blended(font, str, co); p->ensureFormat(outline, SDL_PIXELFORMAT_ABGR8888); SDL_Rect outRect = {OUTLINE_SIZE, OUTLINE_SIZE, txtSurf->w, txtSurf->h}; SDL_SetSurfaceBlendMode(txtSurf, SDL_BLENDMODE_BLEND); SDL_BlitSurface(txtSurf, NULL, outline, &outRect); SDL_FreeSurface(txtSurf); txtSurf = outline; /* reset outline to 0 */ TTF_SetFontOutline(font, 0); } int alignX = rect.x; switch (align) { default: case Left : break; case Center : alignX += (rect.w - txtSurf->w) / 2; break; case Right : alignX += rect.w - txtSurf->w; break; } if (alignX < rect.x) alignX = rect.x; int alignY = rect.y + (rect.h - rawTxtSurfH) / 2; float squeeze = (float) rect.w / txtSurf->w; if (squeeze > 1) squeeze = 1; FloatRect posRect(alignX, alignY, txtSurf->w * squeeze, txtSurf->h); Vec2i gpTexSize; shState->ensureTexSize(txtSurf->w, txtSurf->h, gpTexSize); bool fastBlit = !p->touchesTaintedArea(posRect) && txtAlpha == 1.0f; if (fastBlit) { if (squeeze == 1.0f && !shState->config().subImageFix) { /* Even faster: upload directly to bitmap texture. * We have to make sure the posRect lies within the texture * boundaries or texSubImage will generate errors. * If it partly lies outside bounds we have to upload * the clipped visible part of it. */ SDL_Rect btmRect; btmRect.x = btmRect.y = 0; btmRect.w = width(); btmRect.h = height(); SDL_Rect txtRect; txtRect.x = posRect.x; txtRect.y = posRect.y; txtRect.w = posRect.w; txtRect.h = posRect.h; SDL_Rect inters; /* If we have no intersection at all, * there's nothing to upload to begin with */ if (SDL_IntersectRect(&btmRect, &txtRect, &inters)) { bool subImage = false; int subSrcX = 0, subSrcY = 0; if (inters.w != txtRect.w || inters.h != txtRect.h) { /* Clip the text surface */ subSrcX = inters.x - txtRect.x; subSrcY = inters.y - txtRect.y; subImage = true; posRect.x = inters.x; posRect.y = inters.y; posRect.w = inters.w; posRect.h = inters.h; } TEX::bind(p->gl.tex); if (!subImage) { TEX::uploadSubImage(posRect.x, posRect.y, posRect.w, posRect.h, txtSurf->pixels, GL_RGBA); } else { GLMeta::subRectImageUpload(txtSurf->w, subSrcX, subSrcY, posRect.x, posRect.y, posRect.w, posRect.h, txtSurf, GL_RGBA); GLMeta::subRectImageEnd(); } } } else { /* Squeezing involved: need to use intermediary TexFBO */ TEXFBO &gpTF = shState->gpTexFBO(txtSurf->w, txtSurf->h); TEX::bind(gpTF.tex); TEX::uploadSubImage(0, 0, txtSurf->w, txtSurf->h, txtSurf->pixels, GL_RGBA); GLMeta::blitBegin(p->gl); GLMeta::blitSource(gpTF); GLMeta::blitRectangle(IntRect(0, 0, txtSurf->w, txtSurf->h), posRect, true); GLMeta::blitEnd(); } } else { /* Aquire a partial copy of the destination * buffer we're about to render to */ TEXFBO &gpTex2 = shState->gpTexFBO(posRect.w, posRect.h); GLMeta::blitBegin(gpTex2); GLMeta::blitSource(p->gl); GLMeta::blitRectangle(posRect, Vec2i()); GLMeta::blitEnd(); FloatRect bltRect(0, 0, (float) (gpTexSize.x * squeeze) / gpTex2.width, (float) gpTexSize.y / gpTex2.height); BltShader &shader = shState->shaders().blt; shader.bind(); shader.setTexSize(gpTexSize); shader.setSource(); shader.setDestination(gpTex2.tex); shader.setSubRect(bltRect); shader.setOpacity(txtAlpha); shState->bindTex(); TEX::uploadSubImage(0, 0, txtSurf->w, txtSurf->h, txtSurf->pixels, GL_RGBA); TEX::setSmooth(true); Quad &quad = shState->gpQuad(); quad.setTexRect(FloatRect(0, 0, txtSurf->w, txtSurf->h)); quad.setPosRect(posRect); p->bindFBO(); p->pushSetViewport(shader); p->blitQuad(quad); p->popViewport(); } SDL_FreeSurface(txtSurf); p->addTaintedArea(posRect); p->onModified(); }