/* ** v_text.cpp ** Draws text to a canvas. Also has a text line-breaker thingy. ** **--------------------------------------------------------------------------- ** Copyright 1998-2006 Randy Heit ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** */ #include #include #include #include #include "v_text.h" #include "utf8.h" #include "v_draw.h" #include "gstrings.h" #include "vm.h" #include "printf.h" int ListGetInt(VMVa_List &tags); //========================================================================== // // Create a texture from a text in a given font. // //========================================================================== #if 0 FGameTexture * BuildTextTexture(FFont *font, const char *string, int textcolor) { int w; const uint8_t *ch; int cx; int cy; int trans = -1; int kerning; FGameTexture *pic; kerning = font->GetDefaultKerning(); ch = (const uint8_t *)string; cx = 0; cy = 0; IntRect box; while (auto c = GetCharFromString(ch)) { if (c == TEXTCOLOR_ESCAPE) { // Here we only want to measure the texture so just parse over the color. V_ParseFontColor(ch, 0, 0); continue; } if (c == '\n') { cx = 0; cy += font->GetHeight(); continue; } if (nullptr != (pic = font->GetChar(c, CR_UNTRANSLATED, &w, nullptr))) { auto img = pic->GetImage(); auto offsets = img->GetOffsets(); int x = cx - offsets.first; int y = cy - offsets.second; int ww = img->GetWidth(); int h = img->GetHeight(); box.AddToRect(x, y); box.AddToRect(x + ww, y + h); } cx += (w + kerning); } cx = -box.left; cy = -box.top; TArray part(strlen(string)); while (auto c = GetCharFromString(ch)) { if (c == TEXTCOLOR_ESCAPE) { EColorRange newcolor = V_ParseFontColor(ch, textcolor, textcolor); if (newcolor != CR_UNDEFINED) { trans = font->GetColorTranslation(newcolor); textcolor = newcolor; } continue; } if (c == '\n') { cx = 0; cy += font->GetHeight(); continue; } if (nullptr != (pic = font->GetChar(c, textcolor, &w, nullptr))) { auto img = pic->GetImage(); auto offsets = img->GetOffsets(); int x = cx - offsets.first; int y = cy - offsets.second; auto &tp = part[part.Reserve(1)]; tp.OriginX = x; tp.OriginY = y; tp.Image = img; tp.Translation = range; } cx += (w + kerning); } FMultiPatchTexture *image = new FMultiPatchTexture(box.width, box.height, part, false, false); image->SetOffsets(-box.left, -box.top); FImageTexture *tex = new FImageTexture(image, ""); tex->SetUseType(ETextureType::MiscPatch); TexMan.AddTexture(tex); return tex; } #endif //========================================================================== // // DrawChar // // Write a single character using the given font // //========================================================================== void DrawChar(F2DDrawer *drawer, FFont* font, int normalcolor, double x, double y, int character, int tag_first, ...) { if (font == NULL) return; if (normalcolor >= NumTextColors) normalcolor = CR_UNTRANSLATED; FGameTexture* pic; int dummy; if (NULL != (pic = font->GetChar(character, normalcolor, &dummy))) { DrawParms parms; Va_List tags; va_start(tags.list, tag_first); bool res = ParseDrawTextureTags(drawer, pic, x, y, tag_first, tags, &parms, DrawTexture_Normal); va_end(tags.list); if (!res) { return; } bool palettetrans = (normalcolor == CR_NATIVEPAL && parms.TranslationId != NO_TRANSLATION); PalEntry color = 0xffffffff; if (!palettetrans) parms.TranslationId = font->GetColorTranslation((EColorRange)normalcolor, &color); parms.color = PalEntry((color.a * parms.color.a) / 255, (color.r * parms.color.r) / 255, (color.g * parms.color.g) / 255, (color.b * parms.color.b) / 255); drawer->AddTexture(pic, parms); } } void DrawChar(F2DDrawer *drawer, FFont *font, int normalcolor, double x, double y, int character, VMVa_List &args) { if (font == NULL) return; if (normalcolor >= NumTextColors) normalcolor = CR_UNTRANSLATED; FGameTexture *pic; int dummy; if (NULL != (pic = font->GetChar(character, normalcolor, &dummy))) { DrawParms parms; uint32_t tag = ListGetInt(args); bool res = ParseDrawTextureTags(drawer, pic, x, y, tag, args, &parms, DrawTexture_Normal); if (!res) return; bool palettetrans = (normalcolor == CR_NATIVEPAL && parms.TranslationId != NO_TRANSLATION); PalEntry color = 0xffffffff; if (!palettetrans) parms.TranslationId = font->GetColorTranslation((EColorRange)normalcolor, &color); parms.color = PalEntry((color.a * parms.color.a) / 255, (color.r * parms.color.r) / 255, (color.g * parms.color.g) / 255, (color.b * parms.color.b) / 255); drawer->AddTexture(pic, parms); } } DEFINE_ACTION_FUNCTION(_Screen, DrawChar) { PARAM_PROLOGUE; PARAM_POINTER(font, FFont); PARAM_INT(cr); PARAM_FLOAT(x); PARAM_FLOAT(y); PARAM_INT(chr); PARAM_VA_POINTER(va_reginfo) // Get the hidden type information array if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function"); VMVa_List args = { param + 5, 0, numparam - 6, va_reginfo + 5 }; DrawChar(twod, font, cr, x, y, chr, args); return 0; } DEFINE_ACTION_FUNCTION(FCanvas, DrawChar) { PARAM_SELF_PROLOGUE(FCanvas); PARAM_POINTER(font, FFont); PARAM_INT(cr); PARAM_FLOAT(x); PARAM_FLOAT(y); PARAM_INT(chr); PARAM_VA_POINTER(va_reginfo) // Get the hidden type information array VMVa_List args = { param + 6, 0, numparam - 7, va_reginfo + 6 }; DrawChar(&self->Drawer, font, cr, x, y, chr, args); self->Tex->NeedUpdate(); return 0; } //========================================================================== // // DrawText // // Write a string using the given font // //========================================================================== // This is only needed as a dummy. The code using wide strings does not need color control. EColorRange V_ParseFontColor(const char32_t *&color_value, int normalcolor, int boldcolor) { return CR_UNTRANSLATED; } template void DrawTextCommon(F2DDrawer *drawer, FFont *font, int normalcolor, double x, double y, const chartype *string, DrawParms &parms) { int w; const chartype *ch; int c; double cx; double cy; int boldcolor; FTranslationID trans = INVALID_TRANSLATION; int kerning; FGameTexture *pic; double scalex = parms.scalex * parms.patchscalex; double scaley = parms.scaley * parms.patchscaley; if (parms.celly == 0) parms.celly = font->GetHeight() + 1; parms.celly = int (parms.celly * scaley); bool palettetrans = (normalcolor == CR_NATIVEPAL && parms.TranslationId != NO_TRANSLATION); if (normalcolor >= NumTextColors) normalcolor = CR_UNTRANSLATED; boldcolor = normalcolor ? normalcolor - 1 : NumTextColors - 1; PalEntry colorparm = parms.color; PalEntry color = 0xffffffff; trans = palettetrans? INVALID_TRANSLATION : font->GetColorTranslation((EColorRange)normalcolor, &color); parms.color = PalEntry(colorparm.a, (color.r * colorparm.r) / 255, (color.g * colorparm.g) / 255, (color.b * colorparm.b) / 255); kerning = font->GetDefaultKerning(); ch = string; cx = x; cy = y; if (parms.monospace == EMonospacing::CellCenter) cx += parms.spacing / 2; else if (parms.monospace == EMonospacing::CellRight) cx += parms.spacing; auto currentcolor = normalcolor; while (ch - string < parms.maxstrlen) { c = GetCharFromString(ch); if (!c) break; if (c == TEXTCOLOR_ESCAPE) { EColorRange newcolor = V_ParseFontColor(ch, normalcolor, boldcolor); if (newcolor != CR_UNDEFINED) { trans = font->GetColorTranslation(newcolor, &color); parms.color = PalEntry(colorparm.a, (color.r * colorparm.r) / 255, (color.g * colorparm.g) / 255, (color.b * colorparm.b) / 255); currentcolor = newcolor; } continue; } if (c == '\n') { cx = x; cy += parms.celly; continue; } if (NULL != (pic = font->GetChar(c, currentcolor, &w))) { // if palette translation is used, font colors will be ignored. if (!palettetrans) parms.TranslationId = trans; SetTextureParms(drawer, &parms, pic, cx, cy); if (parms.cellx) { w = parms.cellx; parms.destwidth = parms.cellx; parms.destheight = parms.celly; } if (parms.monospace == EMonospacing::CellLeft) parms.left = 0; else if (parms.monospace == EMonospacing::CellCenter) parms.left = w / 2.; else if (parms.monospace == EMonospacing::CellRight) parms.left = w; drawer->AddTexture(pic, parms); } if (parms.monospace == EMonospacing::Off) { cx += (w + kerning + parms.spacing) * scalex; } else { cx += (parms.spacing) * scalex; } } } // For now the 'drawer' parameter is a placeholder - this should be the way to handle it later to allow different drawers. void DrawText(F2DDrawer *drawer, FFont* font, int normalcolor, double x, double y, const char* string, int tag_first, ...) { Va_List tags; DrawParms parms; if (font == NULL || string == NULL) return; va_start(tags.list, tag_first); bool res = ParseDrawTextureTags(drawer, nullptr, 0, 0, tag_first, tags, &parms, DrawTexture_Text); va_end(tags.list); if (!res) { return; } const char *txt = (parms.localize && string[0] == '$') ? GStrings.GetString(&string[1]) : string; DrawTextCommon(drawer, font, normalcolor, x, y, (const uint8_t*)string, parms); } void DrawText(F2DDrawer *drawer, FFont* font, int normalcolor, double x, double y, const char32_t* string, int tag_first, ...) { Va_List tags; DrawParms parms; if (font == NULL || string == NULL) return; va_start(tags.list, tag_first); bool res = ParseDrawTextureTags(drawer, nullptr, 0, 0, tag_first, tags, &parms, DrawTexture_Text); va_end(tags.list); if (!res) { return; } // [Gutawer] right now nothing needs the char32_t version to have localisation support, and i don't know how to do it assert(parms.localize == false); DrawTextCommon(drawer, font, normalcolor, x, y, string, parms); } void DrawText(F2DDrawer *drawer, FFont *font, int normalcolor, double x, double y, const FString& string, VMVa_List &args) { DrawParms parms; if (font == NULL) return; uint32_t tag = ListGetInt(args); bool res = ParseDrawTextureTags(drawer, nullptr, 0, 0, tag, args, &parms, DrawTexture_Text, ~0u, 0.0, true); if (!res) { return; } const char *txt = (parms.localize && string[0] == '$') ? GStrings.GetString(&string[1]) : string.GetChars(); DrawTextCommon(drawer, font, normalcolor, x, y, (uint8_t*)txt, parms); } DEFINE_ACTION_FUNCTION(_Screen, DrawText) { PARAM_PROLOGUE; PARAM_POINTER_NOT_NULL(font, FFont); PARAM_INT(cr); PARAM_FLOAT(x); PARAM_FLOAT(y); PARAM_STRING(chr); PARAM_VA_POINTER(va_reginfo) // Get the hidden type information array if (!twod->HasBegun2D()) ThrowAbortException(X_OTHER, "Attempt to draw to screen outside a draw function"); VMVa_List args = { param + 5, 0, numparam - 6, va_reginfo + 5 }; DrawText(twod, font, cr, x, y, chr, args); return 0; } DEFINE_ACTION_FUNCTION(FCanvas, DrawText) { PARAM_SELF_PROLOGUE(FCanvas); PARAM_POINTER_NOT_NULL(font, FFont); PARAM_INT(cr); PARAM_FLOAT(x); PARAM_FLOAT(y); PARAM_STRING(chr); PARAM_VA_POINTER(va_reginfo) // Get the hidden type information array VMVa_List args = { param + 6, 0, numparam - 7, va_reginfo + 6 }; DrawText(&self->Drawer, font, cr, x, y, chr, args); self->Tex->NeedUpdate(); return 0; }