diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 8829b7587..9260c0e25 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -636,6 +636,7 @@ file( GLOB HEADER_FILES mact/include/*.h mact/src/*.h common/*.h + common/2d/*.h common/utility/*.h common/console/*.h @@ -788,6 +789,11 @@ set (PCH_SOURCES common/rts.cpp common/gameconfigfile.cpp common/gamecvars.cpp + + common/2d/v_2ddrawer.cpp + common/2d/v_draw.cpp + common/2d/v_drawtext.cpp + common/2d/renderstyle.cpp common/console/c_cvars.cpp diff --git a/source/common/2d/drawparms.h b/source/common/2d/drawparms.h new file mode 100644 index 000000000..cd1498ab5 --- /dev/null +++ b/source/common/2d/drawparms.h @@ -0,0 +1,196 @@ +#pragma once +/* +** v_video.h +** +**--------------------------------------------------------------------------- +** Copyright 1998-2008 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 "palentry.h" +#include "renderstyle.h" +#include "c_cvars.h" +#include "v_2ddrawer.h" + +#define TAG_DONE (0) /* Used to indicate the end of the Tag list */ +#define TAG_END (0) /* Ditto */ + /* list pointed to in ti_Data */ + +enum +{ + DTA_Base = 1, + DTA_DestWidth, // width of area to draw to + DTA_DestHeight, // height of area to draw to + DTA_Alpha, // alpha value for translucency + DTA_FillColor, // color to stencil onto the destination + DTA_TranslationIndex, // translation table to recolor the source + DTA_AlphaChannel, // bool: the source is an alpha channel; used with DTA_FillColor + DTA_Clean, // bool: scale texture size and position by CleanXfac and CleanYfac + DTA_320x200, // bool: scale texture size and position to fit on a virtual 320x200 screen + DTA_Bottom320x200, // bool: same as DTA_320x200 but centers virtual screen on bottom for 1280x1024 targets + DTA_CleanNoMove, // bool: like DTA_Clean but does not reposition output position + DTA_CleanNoMove_1, // bool: like DTA_CleanNoMove, but uses Clean[XY]fac_1 instead + DTA_FlipX, // bool: flip image horizontally //FIXME: Does not work with DTA_Window(Left|Right) + DTA_ShadowColor, // color of shadow + DTA_ShadowAlpha, // alpha of shadow + DTA_Shadow, // set shadow color and alphas to defaults + DTA_VirtualWidth, // pretend the canvas is this wide + DTA_VirtualHeight, // pretend the canvas is this tall + DTA_TopOffset, // override texture's top offset + DTA_LeftOffset, // override texture's left offset + DTA_CenterOffset, // bool: override texture's left and top offsets and set them for the texture's middle + DTA_CenterBottomOffset,// bool: override texture's left and top offsets and set them for the texture's bottom middle + DTA_WindowLeft, // don't draw anything left of this column (on source, not dest) + DTA_WindowRight, // don't draw anything at or to the right of this column (on source, not dest) + DTA_ClipTop, // don't draw anything above this row (on dest, not source) + DTA_ClipBottom, // don't draw anything at or below this row (on dest, not source) + DTA_ClipLeft, // don't draw anything to the left of this column (on dest, not source) + DTA_ClipRight, // don't draw anything at or to the right of this column (on dest, not source) + DTA_Masked, // true(default)=use masks from texture, false=ignore masks + DTA_HUDRules, // use fullscreen HUD rules to position and size textures + DTA_HUDRulesC, // only used internally for marking HUD_HorizCenter + DTA_KeepRatio, // doesn't adjust screen size for DTA_Virtual* if the aspect ratio is not 4:3 + DTA_RenderStyle, // same as render style for actors + DTA_ColorOverlay, // uint32_t: ARGB to overlay on top of image; limited to black for software + DTA_BilinearFilter, // bool: apply bilinear filtering to the image + DTA_SpecialColormap,// pointer to FSpecialColormapParameters + DTA_Desaturate, // explicit desaturation factor (does not do anything in Legacy OpenGL) + DTA_Fullscreen, // Draw image fullscreen (same as DTA_VirtualWidth/Height with graphics size.) + + // floating point duplicates of some of the above: + DTA_DestWidthF, + DTA_DestHeightF, + DTA_TopOffsetF, + DTA_LeftOffsetF, + DTA_VirtualWidthF, + DTA_VirtualHeightF, + DTA_WindowLeftF, + DTA_WindowRightF, + + // For DrawText calls: + DTA_TextLen, // stop after this many characters, even if \0 not hit + DTA_CellX, // horizontal size of character cell + DTA_CellY, // vertical size of character cell + + // New additions. + DTA_Color, + DTA_FlipY, // bool: flip image vertically + DTA_SrcX, // specify a source rectangle (this supersedes the poorly implemented DTA_WindowLeft/Right + DTA_SrcY, + DTA_SrcWidth, + DTA_SrcHeight, + DTA_LegacyRenderStyle, // takes an old-style STYLE_* constant instead of an FRenderStyle + DTA_Burn, // activates the burn shader for this element + DTA_Spacing, // Strings only: Additional spacing between characters + DTA_Monospace, // Fonts only: Use a fixed distance between characters. +}; + +enum EMonospacing : int +{ + Off = 0, + CellLeft = 1, + CellCenter = 2, + CellRight = 3 +}; + +enum +{ + HUD_Normal, + HUD_HorizCenter +}; + + +class FFont; + +struct DrawParms +{ + double x, y; + double texwidth; + double texheight; + double destwidth; + double destheight; + double virtWidth; + double virtHeight; + double windowleft; + double windowright; + int cleanmode; + int dclip; + int uclip; + int lclip; + int rclip; + double top; + double left; + float Alpha; + int remap; + PalEntry fillcolor; + PalEntry colorOverlay; + PalEntry color; + int alphaChannel; + int flipX; + int flipY; + //float shadowAlpha; + int shadowColor; + int keepratio; + int masked; + int bilinear; + FRenderStyle style; + int desaturate; + int scalex, scaley; + int cellx, celly; + int monospace; + int spacing; + int maxstrlen; + bool fortext; + bool virtBottom; + double srcx, srcy; + double srcwidth, srcheight; + bool burn; +}; + +struct Va_List +{ + va_list list; +}; + +int CheckRatio (int width, int height, int *trueratio=NULL); +static inline int CheckRatio (double width, double height) { return CheckRatio(int(width), int(height)); } +inline bool IsRatioWidescreen(int ratio) { return (ratio & 3) != 0; } + +float ActiveRatio (int width, int height, float *trueratio = NULL); +static inline double ActiveRatio (double width, double height) { return ActiveRatio(int(width), int(height)); } + +int AspectBaseWidth(float aspect); +int AspectBaseHeight(float aspect); +double AspectPspriteOffset(float aspect); +int AspectMultiplier(float aspect); +bool AspectTallerThanWide(float aspect); +void ScaleWithAspect(int &w, int &h, int Width, int Height); + +int GetUIScale(int altval); +int GetConScale(int altval); diff --git a/source/common/2d/renderstyle.cpp b/source/common/2d/renderstyle.cpp new file mode 100644 index 000000000..3b491535d --- /dev/null +++ b/source/common/2d/renderstyle.cpp @@ -0,0 +1,158 @@ +/* +** r_data.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2008-2011 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 "templates.h" +#include "renderstyle.h" +#include "c_cvars.h" + +CVAR (Bool, r_drawtrans, true, 0) +CVAR (Int, r_drawfuzz, 1, CVAR_ARCHIVE) + +// Convert legacy render styles to flexible render styles. + +FRenderStyle LegacyRenderStyles[STYLE_Count] = +{ + { { STYLEOP_None, STYLEALPHA_Zero, STYLEALPHA_Zero, 0 } }, /* STYLE_None */ + { { STYLEOP_Add, STYLEALPHA_Src, STYLEALPHA_InvSrc, STYLEF_Alpha1 } }, /* STYLE_Normal */ + { { STYLEOP_Fuzz, STYLEALPHA_Src, STYLEALPHA_InvSrc, 0 } }, /* STYLE_Fuzzy */ + { { STYLEOP_FuzzOrAdd, STYLEALPHA_Src, STYLEALPHA_InvSrc, 0 } }, /* STYLE_OptFuzzy */ + { { STYLEOP_Add, STYLEALPHA_Src, STYLEALPHA_InvSrc, STYLEF_Alpha1 | STYLEF_ColorIsFixed } }, /* STYLE_Stencil */ + { { STYLEOP_Add, STYLEALPHA_Src, STYLEALPHA_InvSrc, 0 } }, /* STYLE_Translucent */ + { { STYLEOP_Add, STYLEALPHA_Src, STYLEALPHA_One, 0 } }, /* STYLE_Add */ + { { STYLEOP_Add, STYLEALPHA_Src, STYLEALPHA_InvSrc, STYLEF_RedIsAlpha | STYLEF_ColorIsFixed } }, /* STYLE_Shaded */ + { { STYLEOP_Add, STYLEALPHA_Src, STYLEALPHA_InvSrc, STYLEF_ColorIsFixed } }, /* STYLE_TranslucentStencil */ + { { STYLEOP_Shadow, 0, 0, 0 } }, /* STYLE_Shadow */ + { { STYLEOP_RevSub, STYLEALPHA_Src, STYLEALPHA_One, 0 } }, /* STYLE_Subtract*/ + { { STYLEOP_Add, STYLEALPHA_Src, STYLEALPHA_One, STYLEF_ColorIsFixed } }, /* STYLE_AddStencil */ + { { STYLEOP_Add, STYLEALPHA_Src, STYLEALPHA_One, STYLEF_RedIsAlpha | STYLEF_ColorIsFixed } }, /* STYLE_AddShaded */ + { { STYLEOP_Add, STYLEALPHA_DstCol, STYLEALPHA_Zero, 0 } }, /* STYLE_Multiply */ + { { STYLEOP_Add, STYLEALPHA_InvDstCol, STYLEALPHA_Zero, 0 } }, /* STYLE_InverseMultiply */ + { { STYLEOP_Add, STYLEALPHA_SrcCol, STYLEALPHA_InvSrcCol, 0 } }, /* STYLE_ColorBlend */ + { { STYLEOP_Add, STYLEALPHA_One, STYLEALPHA_Zero, 0 } }, /* STYLE_Source */ + { { STYLEOP_Add, STYLEALPHA_SrcCol, STYLEALPHA_One, 0 } }, /* STYLE_ColorAdd */ +}; + +double GetAlpha(int type, double alpha) +{ + switch (type) + { + case STYLEALPHA_Zero: return 0; + case STYLEALPHA_One: return 1.; + case STYLEALPHA_Src: return alpha; + case STYLEALPHA_InvSrc: return 1. - alpha; + default: return 0.5; // undeterminable + } +} + +//========================================================================== +// +// FRenderStyle :: IsVisible +// +// Coupled with the given alpha, will this render style produce something +// visible on-screen? +// +//========================================================================== + +bool FRenderStyle::IsVisible(double alpha) const throw() +{ + if (BlendOp == STYLEOP_None) + { + return false; + } + if (BlendOp == STYLEOP_Add || BlendOp == STYLEOP_RevSub) + { + if (Flags & STYLEF_Alpha1) + { + alpha = 1.; + } + else + { + alpha = clamp(alpha, 0., 1.); + } + return GetAlpha(SrcAlpha, alpha) != 0 || GetAlpha(DestAlpha, alpha) != 1; + } + // Treat anything else as visible. + return true; +} + + +//========================================================================== +// +// FRenderStyle :: CheckFuzz +// +// Adjusts settings based on r_drawfuzz CVAR +// +//========================================================================== + +void FRenderStyle::CheckFuzz() +{ + switch (BlendOp) + { + default: + return; + + case STYLEOP_FuzzOrAdd: + if (r_drawtrans && r_drawfuzz == 0) + { + BlendOp = STYLEOP_Add; + return; + } + break; + + case STYLEOP_FuzzOrSub: + if (r_drawtrans && r_drawfuzz == 0) + { + BlendOp = STYLEOP_Sub; + return; + } + break; + + case STYLEOP_FuzzOrRevSub: + if (r_drawtrans && r_drawfuzz == 0) + { + BlendOp = STYLEOP_RevSub; + return; + } + break; + } + + if (r_drawfuzz == 2) + { + BlendOp = STYLEOP_Shadow; + } + else + { + BlendOp = STYLEOP_Fuzz; + } +} diff --git a/source/common/2d/renderstyle.h b/source/common/2d/renderstyle.h new file mode 100644 index 000000000..ac4419ee1 --- /dev/null +++ b/source/common/2d/renderstyle.h @@ -0,0 +1,182 @@ +#pragma once +/* +** r_blend.h +** Constants and types for specifying texture blending. +** +**--------------------------------------------------------------------------- +** Copyright 2008 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 + +// also #defines OPAQUE +#ifdef OPAQUE +#undef OPAQUE +#endif + +enum +{ + OPAQUE = 65536, +}; + +enum ETexMode +{ + TM_NORMAL = 0, // (r, g, b, a) + TM_STENCIL, // (1, 1, 1, a) + TM_OPAQUE, // (r, g, b, 1) + TM_INVERSE, // (1-r, 1-g, 1-b, a) + TM_ALPHATEXTURE, // (1, 1, 1, r) + TM_CLAMPY, // (r, g, b, (t >= 0.0 && t <= 1.0)? a:0) + TM_INVERTOPAQUE, // (1-r, 1-g, 1-b, 1) + TM_FOGLAYER, // (renders a fog layer in the shape of the active texture) + TM_FIXEDCOLORMAP = TM_FOGLAYER, // repurposes the objectcolor uniforms to render a fixed colormap range. (Same constant because they cannot be used in the same context. +}; + +// Legacy render styles +enum ERenderStyle +{ + STYLE_None, // Do not draw + STYLE_Normal, // Normal; just copy the image to the screen + STYLE_Fuzzy, // Draw silhouette using "fuzz" effect + STYLE_OptFuzzy, // Draw as fuzzy or translucent, based on user preference + STYLE_Stencil, // Fill image interior with alphacolor + STYLE_Translucent, // Draw translucent + STYLE_Add, // Draw additive + STYLE_Shaded, // Treat patch data as alpha values for alphacolor + STYLE_TranslucentStencil, + STYLE_Shadow, + STYLE_Subtract, // Actually this is 'reverse subtract' but this is what normal people would expect by 'subtract'. + STYLE_AddStencil, // Fill image interior with alphacolor + STYLE_AddShaded, // Treat patch data as alpha values for alphacolor + STYLE_Multiply, // Multiply source with destination (HW renderer only.) + STYLE_InverseMultiply, // Multiply source with inverse of destination (HW renderer only.) + STYLE_ColorBlend, // Use color intensity as transparency factor + STYLE_Source, // No blending (only used internally) + STYLE_ColorAdd, // Use color intensity as transparency factor and blend additively. + + STYLE_Count +}; + +// Flexible render styles (most possible combinations are supported in software) +enum ERenderOp +{ + STYLEOP_None, // Do not draw + STYLEOP_Add, // Add source to destination + STYLEOP_Sub, // Subtract source from destination + STYLEOP_RevSub, // Subtract destination from source + STYLEOP_Fuzz, // Draw fuzzy on top of destination - ignores alpha and color + STYLEOP_FuzzOrAdd, // Draw fuzzy or add, based on user preference + STYLEOP_FuzzOrSub, // Draw fuzzy or subtract, based on user preference + STYLEOP_FuzzOrRevSub, // Draw fuzzy or reverse subtract, based on user preference + + // special styles + STYLEOP_Shadow, +}; + +enum ERenderAlpha +{ + STYLEALPHA_Zero, // Blend factor is 0.0 + STYLEALPHA_One, // Blend factor is 1.0 + STYLEALPHA_Src, // Blend factor is alpha + STYLEALPHA_InvSrc, // Blend factor is 1.0 - alpha + STYLEALPHA_SrcCol, // Blend factor is color (HWR only) + STYLEALPHA_InvSrcCol, // Blend factor is 1.0 - color (HWR only) + STYLEALPHA_DstCol, // Blend factor is dest. color (HWR only) + STYLEALPHA_InvDstCol, // Blend factor is 1.0 - dest. color (HWR only) + STYLEALPHA_MAX +}; + +enum ERenderFlags +{ + // Force alpha to 1. Not the same as STYLEALPHA_One, since that also + // ignores alpha from the texture. + STYLEF_Alpha1 = 2, + + // Use red component from grayscale/RGB texture as alpha. If the texture + // is paletted, the palette is ignored and it is treated as grayscale. + // This should generally be combined with STYLEF_ColorIsFixed, since that's + // all the software renderer supports, but hardware acceleration can do + // them separately should you want to do that for some reason. + STYLEF_RedIsAlpha = 4, + + // Ignore texture for RGB output. Color comes from fillcolor for actors + // or DTA_FillColor for DrawTexture(). + STYLEF_ColorIsFixed = 8, + + // Invert source color, either the texture color or the fixed color. + STYLEF_InvertSource = 16, + + // Invert overlay color. This is the fade for actors and DTA_ColorOverlay + // for DrawTexture(). + STYLEF_InvertOverlay = 32, + + // Actors only: Ignore sector fade and fade to black. To fade to white, + // combine this with STYLEF_InvertOverlay. + STYLEF_FadeToBlack = 64, +}; + +union FRenderStyle +{ + struct + { + uint8_t BlendOp; // Of ERenderOp type + uint8_t SrcAlpha; // Of ERenderAlpha type + uint8_t DestAlpha; // Of ERenderAlpha type + uint8_t Flags; + }; + uint32_t AsDWORD; + + inline FRenderStyle &operator= (ERenderStyle legacy); + bool operator==(const FRenderStyle &o) const { return AsDWORD == o.AsDWORD; } + bool operator!=(const FRenderStyle &o) const { return AsDWORD != o.AsDWORD; } + void CheckFuzz(); + bool IsVisible(double alpha) const throw(); +private: + // Code that compares an actor's render style with a legacy render + // style value should be updated. + operator ERenderStyle() = delete; + operator int() const = delete; +}; + +extern FRenderStyle LegacyRenderStyles[STYLE_Count]; + +inline FRenderStyle DefaultRenderStyle() +{ + return LegacyRenderStyles[STYLE_Normal]; +} + +inline FRenderStyle &FRenderStyle::operator= (ERenderStyle legacy) +{ + if (legacy < STYLE_None || legacy >= STYLE_Count) + { + legacy = STYLE_None; + } + *this = LegacyRenderStyles[legacy]; + return *this; +} + diff --git a/source/common/2d/v_2ddrawer.cpp b/source/common/2d/v_2ddrawer.cpp new file mode 100644 index 000000000..3bd9eae47 --- /dev/null +++ b/source/common/2d/v_2ddrawer.cpp @@ -0,0 +1,554 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2016-2018 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** v_2ddrawer.h +** Device independent 2D draw list +** +**/ + +#include +#include "c_cvars.h" +#include "v_2ddrawer.h" +#include "renderstyle.h" +#include "drawparms.h" +#include "vectors.h" +//#include "doomtype.h" +#include "templates.h" +//#include "r_utility.h" +//#include "v_video.h" +//#include "g_levellocals.h" +//#include "vm.h" + +//========================================================================== +// +// +// +//========================================================================== + +int F2DDrawer::AddCommand(const RenderCommand *data) +{ + if (mData.Size() > 0 && data->isCompatible(mData.Last())) + { + // Merge with the last command. + mData.Last().mIndexCount += data->mIndexCount; + mData.Last().mVertCount += data->mVertCount; + return mData.Size(); + } + else + { + return mData.Push(*data); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void F2DDrawer::AddIndices(int firstvert, int count, ...) +{ + va_list ap; + va_start(ap, count); + int addr = mIndices.Reserve(count); + for (int i = 0; i < count; i++) + { + mIndices[addr + i] = firstvert + va_arg(ap, int); + } +} + +//========================================================================== +// +// SetStyle +// +// Patterned after R_SetPatchStyle. +// +//========================================================================== + +bool F2DDrawer::SetStyle(FTexture *tex, DrawParms &parms, PalEntry &vertexcolor, RenderCommand &quad) +{ + FRenderStyle style = parms.style; + float alpha; + bool stencilling; + + if (style.Flags & STYLEF_Alpha1) + { + alpha = 1; + } + else + { + alpha = clamp(parms.Alpha, 0.f, 1.f); + } + + style.CheckFuzz(); + if (style.BlendOp == STYLEOP_Shadow || style.BlendOp == STYLEOP_Fuzz) + { + style = LegacyRenderStyles[STYLE_TranslucentStencil]; + alpha = 0.3f; + parms.fillcolor = 0; + } + else if (style.BlendOp == STYLEOP_FuzzOrAdd) + { + style.BlendOp = STYLEOP_Add; + } + else if (style.BlendOp == STYLEOP_FuzzOrSub) + { + style.BlendOp = STYLEOP_Sub; + } + else if (style.BlendOp == STYLEOP_FuzzOrRevSub) + { + style.BlendOp = STYLEOP_RevSub; + } + + stencilling = false; + + if (style.Flags & STYLEF_InvertOverlay) + { + // Only the overlay color is inverted, not the overlay alpha. + parms.colorOverlay.r = 255 - parms.colorOverlay.r; + parms.colorOverlay.g = 255 - parms.colorOverlay.g; + parms.colorOverlay.b = 255 - parms.colorOverlay.b; + } + + SetColorOverlay(parms.colorOverlay, alpha, vertexcolor, quad.mColor1); + + if (style.Flags & STYLEF_ColorIsFixed) + { + if (style.Flags & STYLEF_InvertSource) + { // Since the source color is a constant, we can invert it now + // without spending time doing it in the shader. + parms.fillcolor.r = 255 - parms.fillcolor.r; + parms.fillcolor.g = 255 - parms.fillcolor.g; + parms.fillcolor.b = 255 - parms.fillcolor.b; + style.Flags &= ~STYLEF_InvertSource; + } + if (parms.desaturate > 0) + { + // Desaturation can also be computed here without having to do it in the shader. + auto gray = parms.fillcolor.Luminance(); + auto notgray = 255 - gray; + parms.fillcolor.r = uint8_t((parms.fillcolor.r * notgray + gray * 255) / 255); + parms.fillcolor.g = uint8_t((parms.fillcolor.g * notgray + gray * 255) / 255); + parms.fillcolor.b = uint8_t((parms.fillcolor.b * notgray + gray * 255) / 255); + parms.desaturate = 0; + } + + // Set up the color mod to replace the color from the image data. + vertexcolor.r = parms.fillcolor.r; + vertexcolor.g = parms.fillcolor.g; + vertexcolor.b = parms.fillcolor.b; + + if (style.Flags & STYLEF_RedIsAlpha) + { + quad.mDrawMode = TM_ALPHATEXTURE; + } + else + { + quad.mDrawMode = TM_STENCIL; + } + } + else + { + if (style.Flags & STYLEF_RedIsAlpha) + { + quad.mDrawMode = TM_ALPHATEXTURE; + } + else if (style.Flags & STYLEF_InvertSource) + { + quad.mDrawMode = TM_INVERSE; + } + quad.mDesaturate = parms.desaturate; + } + // apply the element's own color. This is being blended with anything that came before. + vertexcolor = PalEntry((vertexcolor.a * parms.color.a) / 255, (vertexcolor.r * parms.color.r) / 255, (vertexcolor.g * parms.color.g) / 255, (vertexcolor.b * parms.color.b) / 255); + + if (!parms.masked) + { + // For TM_ALPHATEXTURE and TM_STENCIL the mask cannot be turned off because it would not yield a usable result. + if (quad.mDrawMode == TM_NORMAL) quad.mDrawMode = TM_OPAQUE; + else if (quad.mDrawMode == TM_INVERSE) quad.mDrawMode = TM_INVERTOPAQUE; + } + quad.mRenderStyle = parms.style; // this contains the blend mode and blend equation settings. + if (parms.burn) quad.mFlags |= DTF_Burn; + return true; +} + +//========================================================================== +// +// Draws a texture +// +//========================================================================== + +void F2DDrawer::SetColorOverlay(PalEntry color, float alpha, PalEntry &vertexcolor, PalEntry &overlaycolor) +{ + if (color.a != 0 && (color & 0xffffff) != 0) + { + // overlay color uses premultiplied alpha. + int a = color.a * 256 / 255; + overlaycolor.r = (color.r * a) >> 8; + overlaycolor.g = (color.g * a) >> 8; + overlaycolor.b = (color.b * a) >> 8; + overlaycolor.a = 0; // The overlay gets added on top of the texture data so to preserve the pixel's alpha this must be 0. + } + else + { + overlaycolor = 0; + } + // Vertex intensity is the inverse of the overlay so that the shader can do a simple addition to combine them. + uint8_t light = 255 - color.a; + vertexcolor = PalEntry(int(alpha * 255), light, light, light); + + // The real color gets multiplied into vertexcolor later. +} + +//========================================================================== +// +// Draws a texture +// +//========================================================================== + +void F2DDrawer::AddTexture(FTexture *img, DrawParms &parms) +{ + if (parms.style.BlendOp == STYLEOP_None) return; // not supposed to be drawn. + + double xscale = parms.destwidth / parms.texwidth; + double yscale = parms.destheight / parms.texheight; + double x = parms.x - parms.left * xscale; + double y = parms.y - parms.top * yscale; + double w = parms.destwidth; + double h = parms.destheight; + double u1, v1, u2, v2; + PalEntry vertexcolor; + + RenderCommand dg; + + dg.mType = DrawTypeTriangles; + dg.mVertCount = 4; + dg.mTexture = img; + + dg.mTranslation = 0; + SetStyle(img, parms, vertexcolor, dg); + + u1 = parms.srcx; + v1 = parms.srcy; + u2 = parms.srcx + parms.srcwidth; + v2 = parms.srcy + parms.srcheight; + + if (parms.flipX) + std::swap(u1, u2); + + if (parms.flipY) + std::swap(v1, v2); + + // This is crap. Only kept for backwards compatibility with scripts that may have used it. + // Note that this only works for unflipped full textures. + if (parms.windowleft > 0 || parms.windowright < parms.texwidth) + { + double wi = std::min(parms.windowright, parms.texwidth); + x += parms.windowleft * xscale; + w -= (parms.texwidth - wi + parms.windowleft) * xscale; + + u1 = float(u1 + parms.windowleft / parms.texwidth); + u2 = float(u2 - (parms.texwidth - wi) / parms.texwidth); + } + + if (x < (double)parms.lclip || y < (double)parms.uclip || x + w >(double)parms.rclip || y + h >(double)parms.dclip) + { + dg.mScissor[0] = parms.lclip; + dg.mScissor[1] = parms.uclip; + dg.mScissor[2] = parms.rclip; + dg.mScissor[3] = parms.dclip; + dg.mFlags |= DTF_Scissor; + } + else + { + memset(dg.mScissor, 0, sizeof(dg.mScissor)); + } + + dg.mVertCount = 4; + dg.mVertIndex = (int)mVertices.Reserve(4); + TwoDVertex *ptr = &mVertices[dg.mVertIndex]; + ptr->Set(x, y, 0, u1, v1, vertexcolor); ptr++; + ptr->Set(x, y + h, 0, u1, v2, vertexcolor); ptr++; + ptr->Set(x + w, y, 0, u2, v1, vertexcolor); ptr++; + ptr->Set(x + w, y + h, 0, u2, v2, vertexcolor); ptr++; + dg.mIndexIndex = mIndices.Size(); + dg.mIndexCount += 6; + AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2); + AddCommand(&dg); +} + +//========================================================================== +// +// +// +//========================================================================== +void F2DDrawer::AddPoly(FTexture *texture, FVector2 *points, int npoints, + double originx, double originy, double scalex, double scaley, + DAngle rotation, int colormap, PalEntry flatcolor, int lightlevel, + uint32_t *indices, size_t indexcount) +{ + // Use an equation similar to player sprites to determine shade + + // Convert a light level into an unbounded colormap index (shade). + // Why the +12? I wish I knew, but experimentation indicates it + // is necessary in order to best reproduce Doom's original lighting. + double fadelevel; + + // The hardware renderer's light modes 0, 1 and 4 use a linear light scale which must be used here as well. Otherwise the automap gets too dark. + fadelevel = 1. - clamp(lightlevel, 0, 255) / 255.f; + + RenderCommand poly; + + poly.mType = DrawTypeTriangles; + poly.mTexture = texture; + poly.mRenderStyle = DefaultRenderStyle(); + poly.mFlags |= DTF_Wrap; + poly.mDesaturate = 0; + + PalEntry color0; + double invfade = 1. - fadelevel; + + color0.r = uint8_t(flatcolor.r * invfade); + color0.g = uint8_t(flatcolor.g * invfade); + color0.b = uint8_t(flatcolor.b * invfade); + color0.a = 255; + + poly.mColor1 = 0; + + bool dorotate = rotation != 0; + + float cosrot = (float)cos(rotation.Radians()); + float sinrot = (float)sin(rotation.Radians()); + + float uscale = float(1.f / (texture->GetWidth() * scalex)); + float vscale = float(1.f / (texture->GetHeight() * scaley)); + float ox = float(originx); + float oy = float(originy); + + poly.mVertCount = npoints; + poly.mVertIndex = (int)mVertices.Reserve(npoints); + for (int i = 0; i < npoints; ++i) + { + float u = points[i].X - 0.5f - ox; + float v = points[i].Y - 0.5f - oy; + if (dorotate) + { + float t = u; + u = t * cosrot - v * sinrot; + v = v * cosrot + t * sinrot; + } + mVertices[poly.mVertIndex+i].Set(points[i].X, points[i].Y, 0, u*uscale, v*vscale, color0); + } + poly.mIndexIndex = mIndices.Size(); + + if (indices == nullptr || indexcount == 0) + { + poly.mIndexCount += (npoints - 2) * 3; + for (int i = 2; i < npoints; ++i) + { + AddIndices(poly.mVertIndex, 3, 0, i - 1, i); + } + } + else + { + poly.mIndexCount += (int)indexcount; + int addr = mIndices.Reserve(indexcount); + for (size_t i = 0; i < indexcount; i++) + { + mIndices[addr + i] = poly.mVertIndex + indices[i]; + } + } + + AddCommand(&poly); +} + +//========================================================================== +// +// +// +//========================================================================== + +void F2DDrawer::AddFlatFill(int left, int top, int right, int bottom, FTexture *src, bool local_origin) +{ + float fU1, fU2, fV1, fV2; + + RenderCommand dg; + + dg.mType = DrawTypeTriangles; + dg.mRenderStyle = DefaultRenderStyle(); + dg.mTexture = src; + dg.mVertCount = 4; + dg.mTexture = src; + dg.mFlags = DTF_Wrap; + + // scaling is not used here. + if (!local_origin) + { + fU1 = float(left) / src->GetWidth(); + fV1 = float(top) / src->GetHeight(); + fU2 = float(right) / src->GetWidth(); + fV2 = float(bottom) / src->GetHeight(); + } + else + { + fU1 = 0; + fV1 = 0; + fU2 = float(right - left) / src->GetWidth(); + fV2 = float(bottom - top) / src->GetHeight(); + } + dg.mVertIndex = (int)mVertices.Reserve(4); + auto ptr = &mVertices[dg.mVertIndex]; + + ptr->Set(left, top, 0, fU1, fV1, 0xffffffff); ptr++; + ptr->Set(left, bottom, 0, fU1, fV2, 0xffffffff); ptr++; + ptr->Set(right, top, 0, fU2, fV1, 0xffffffff); ptr++; + ptr->Set(right, bottom, 0, fU2, fV2, 0xffffffff); ptr++; + dg.mIndexIndex = mIndices.Size(); + dg.mIndexCount += 6; + AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2); + AddCommand(&dg); +} + + +//=========================================================================== +// +// +// +//=========================================================================== + +void F2DDrawer::AddColorOnlyQuad(int x1, int y1, int w, int h, PalEntry color, FRenderStyle *style) +{ + RenderCommand dg; + + dg.mType = DrawTypeTriangles; + dg.mVertCount = 4; + dg.mVertIndex = (int)mVertices.Reserve(4); + dg.mRenderStyle = style? *style : LegacyRenderStyles[STYLE_Translucent]; + auto ptr = &mVertices[dg.mVertIndex]; + ptr->Set(x1, y1, 0, 0, 0, color); ptr++; + ptr->Set(x1, y1 + h, 0, 0, 0, color); ptr++; + ptr->Set(x1 + w, y1, 0, 0, 0, color); ptr++; + ptr->Set(x1 + w, y1 + h, 0, 0, 0, color); ptr++; + dg.mIndexIndex = mIndices.Size(); + dg.mIndexCount += 6; + AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2); + AddCommand(&dg); +} + +//========================================================================== +// +// +// +//========================================================================== + +void F2DDrawer::AddLine(int x1, int y1, int x2, int y2, uint32_t color, uint8_t alpha) +{ + PalEntry p = (PalEntry)color; + p.a = alpha; + + RenderCommand dg; + + dg.mType = DrawTypeLines; + dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent]; + dg.mVertCount = 2; + dg.mVertIndex = (int)mVertices.Reserve(2); + mVertices[dg.mVertIndex].Set(x1, y1, 0, 0, 0, p); + mVertices[dg.mVertIndex+1].Set(x2, y2, 0, 0, 0, p); + AddCommand(&dg); +} + +//========================================================================== +// +// +// +//========================================================================== + +void F2DDrawer::AddThickLine(int x1, int y1, int x2, int y2, double thickness, uint32_t color, uint8_t alpha) +{ + PalEntry p = (PalEntry)color; + p.a = alpha; + + DVector2 point0(x1, y1); + DVector2 point1(x2, y2); + + DVector2 delta = point1 - point0; + DVector2 perp(-delta.Y, delta.X); + perp.MakeUnit(); + perp *= thickness / 2; + + DVector2 corner0 = point0 + perp; + DVector2 corner1 = point0 - perp; + DVector2 corner2 = point1 + perp; + DVector2 corner3 = point1 - perp; + + RenderCommand dg; + + dg.mType = DrawTypeTriangles; + dg.mVertCount = 4; + dg.mVertIndex = (int)mVertices.Reserve(4); + dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent]; + auto ptr = &mVertices[dg.mVertIndex]; + ptr->Set(corner0.X, corner0.Y, 0, 0, 0, p); ptr++; + ptr->Set(corner1.X, corner1.Y, 0, 0, 0, p); ptr++; + ptr->Set(corner2.X, corner2.Y, 0, 0, 0, p); ptr++; + ptr->Set(corner3.X, corner3.Y, 0, 0, 0, p); ptr++; + dg.mIndexIndex = mIndices.Size(); + dg.mIndexCount += 6; + AddIndices(dg.mVertIndex, 6, 0, 1, 2, 1, 3, 2); + AddCommand(&dg); +} + +//========================================================================== +// +// +// +//========================================================================== + +void F2DDrawer::AddPixel(int x1, int y1, uint32_t color) +{ + PalEntry p = (PalEntry)color; + p.a = 255; + + RenderCommand dg; + + dg.mType = DrawTypePoints; + dg.mRenderStyle = LegacyRenderStyles[STYLE_Translucent]; + dg.mVertCount = 1; + dg.mVertIndex = (int)mVertices.Reserve(1); + mVertices[dg.mVertIndex].Set(x1, y1, 0, 0, 0, p); + AddCommand(&dg); +} + +//========================================================================== +// +// +// +//========================================================================== + +void F2DDrawer::Clear() +{ + mVertices.Clear(); + mIndices.Clear(); + mData.Clear(); + mIsFirstPass = true; +} diff --git a/source/common/2d/v_2ddrawer.h b/source/common/2d/v_2ddrawer.h new file mode 100644 index 000000000..96d76b9f9 --- /dev/null +++ b/source/common/2d/v_2ddrawer.h @@ -0,0 +1,134 @@ +#ifndef __2DDRAWER_H +#define __2DDRAWER_H + +#include "tarray.h" +#include "vectors.h" +#include "textures.h" +#include "renderstyle.h" + +struct DrawParms; + + + +class F2DDrawer +{ +public: + + enum EDrawType : uint8_t + { + DrawTypeTriangles, + DrawTypeLines, + DrawTypePoints, + }; + + enum ETextureFlags : uint8_t + { + DTF_Wrap = 1, + DTF_Scissor = 2, + DTF_Burn = 4, + }; + + + // This vertex type is hardware independent and needs conversion when put into a buffer. + struct TwoDVertex + { + float x, y, z; + float u, v; + PalEntry color0; + + void Set(float xx, float yy, float zz) + { + x = xx; + z = zz; + y = yy; + u = 0; + v = 0; + color0 = 0; + } + + void Set(double xx, double yy, double zz, double uu, double vv, PalEntry col) + { + x = (float)xx; + z = (float)zz; + y = (float)yy; + u = (float)uu; + v = (float)vv; + color0 = col; + } + + }; + + struct RenderCommand + { + EDrawType mType; + int mVertIndex; + int mVertCount; + int mIndexIndex; + int mIndexCount; + + FTexture *mTexture; + FRemapTable *mTranslation; + PalEntry mSpecialColormap[2]; + int mScissor[4]; + int mDesaturate; + FRenderStyle mRenderStyle; + PalEntry mColor1; // Overlay color + ETexMode mDrawMode; + uint8_t mFlags; + + RenderCommand() + { + memset(this, 0, sizeof(*this)); + } + + // If these fields match, two draw commands can be batched. + bool isCompatible(const RenderCommand &other) const + { + return mTexture == other.mTexture && + mType == other.mType && + mTranslation == other.mTranslation && + mSpecialColormap[0].d == other.mSpecialColormap[0].d && + mSpecialColormap[1].d == other.mSpecialColormap[1].d && + !memcmp(mScissor, other.mScissor, sizeof(mScissor)) && + mDesaturate == other.mDesaturate && + mRenderStyle == other.mRenderStyle && + mDrawMode == other.mDrawMode && + mFlags == other.mFlags && + mColor1.d == other.mColor1.d; + + } + }; + + TArray mIndices; + TArray mVertices; + TArray mData; + + int AddCommand(const RenderCommand *data); + void AddIndices(int firstvert, int count, ...); + bool SetStyle(FTexture *tex, DrawParms &parms, PalEntry &color0, RenderCommand &quad); + void SetColorOverlay(PalEntry color, float alpha, PalEntry &vertexcolor, PalEntry &overlaycolor); + +public: + void AddTexture(FTexture *img, DrawParms &parms); + void AddPoly(FTexture *texture, FVector2 *points, int npoints, + double originx, double originy, double scalex, double scaley, + DAngle rotation, int colormap, PalEntry flatcolor, int lightlevel, uint32_t *indices, size_t indexcount); + void AddFlatFill(int left, int top, int right, int bottom, FTexture *src, bool local_origin); + + void AddColorOnlyQuad(int left, int top, int width, int height, PalEntry color, FRenderStyle *style); + + void AddDim(PalEntry color, float damount, int x1, int y1, int w, int h); + void AddClear(int left, int top, int right, int bottom, int palcolor, uint32_t color); + + + void AddLine(int x1, int y1, int x2, int y2, uint32_t color, uint8_t alpha = 255); + void AddThickLine(int x1, int y1, int x2, int y2, double thickness, uint32_t color, uint8_t alpha = 255); + void AddPixel(int x1, int y1, uint32_t color); + + void Clear(); + + bool mIsFirstPass = true; +}; + + +#endif diff --git a/source/common/2d/v_draw.cpp b/source/common/2d/v_draw.cpp new file mode 100644 index 000000000..e2057f91e --- /dev/null +++ b/source/common/2d/v_draw.cpp @@ -0,0 +1,873 @@ +/* +** v_draw.cpp +** Draw patches and blocks to a canvas +** +**--------------------------------------------------------------------------- +** Copyright 1998-2008 Randy Heit +** Copyright 2005-2019 Christoph Oelckers +** 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 "c_cvars.h" +#include "drawparms.h" +#include "templates.h" +#include "v_draw.h" + +ScreenDummy* screen; + +CUSTOM_CVAR(Int, uiscale, 0, CVAR_ARCHIVE | CVAR_NOINITCALL) +{ + if (self < 0) + { + self = 0; + return; + } + //setsizeneeded = true; +} + +CVAR(Int, vid_aspect, 0, 0) + +int GetUIScale(int altval) +{ + int scaleval; + if (altval > 0) scaleval = altval; + else if (uiscale == 0) + { + // Default should try to scale to 640x400 + int vscale = screen->GetHeight() / 400; + int hscale = screen->GetWidth() / 640; + scaleval = clamp(vscale, 1, hscale); + } + else scaleval = uiscale; + + // block scales that result in something larger than the current screen. + int vmax = screen->GetHeight() / 200; + int hmax = screen->GetWidth() / 320; + int max = std::max(vmax, hmax); + return std::max(1,std::min(scaleval, max)); +} + +// The new console font is twice as high, so the scaling calculation must factor that in. +int GetConScale(int altval) +{ + int scaleval; + if (altval > 0) scaleval = (altval+1) / 2; + else if (uiscale == 0) + { + // Default should try to scale to 640x400 + int vscale = screen->GetHeight() / 800; + int hscale = screen->GetWidth() / 1280; + scaleval = clamp(vscale, 1, hscale); + } + else scaleval = (uiscale+1) / 2; + + // block scales that result in something larger than the current screen. + int vmax = screen->GetHeight() / 400; + int hmax = screen->GetWidth() / 640; + int max = std::max(vmax, hmax); + return std::max(1, std::min(scaleval, max)); +} + + +// [RH] Stretch values to make a 320x200 image best fit the screen +// without using fractional steppings +int CleanXfac, CleanYfac; + +// [RH] Effective screen sizes that the above scale values give you +int CleanWidth, CleanHeight; + +// Above minus 1 (or 1, if they are already 1) +int CleanXfac_1, CleanYfac_1, CleanWidth_1, CleanHeight_1; + + +//========================================================================== +// +// Draw parameter parsing +// +//========================================================================== + +bool SetTextureParms(DrawParms *parms, FTexture *img, double xx, double yy) +{ + if (img != NULL) + { + parms->x = xx; + parms->y = yy; + parms->texwidth = img->GetWidth(); + parms->texheight = img->GetHeight(); + if (parms->top == INT_MAX || parms->fortext) + { + parms->top = img->GetTopOffset(); + } + if (parms->left == INT_MAX || parms->fortext) + { + parms->left = img->GetLeftOffset(); + } + if (parms->destwidth == INT_MAX || parms->fortext) + { + parms->destwidth = img->GetWidth(); + } + if (parms->destheight == INT_MAX || parms->fortext) + { + parms->destheight = img->GetHeight(); + } + + switch (parms->cleanmode) + { + default: + break; + + case DTA_Clean: + parms->x = (parms->x - 160.0) * CleanXfac + (screen->GetWidth() * 0.5); + parms->y = (parms->y - 100.0) * CleanYfac + (screen->GetHeight() * 0.5); + parms->destwidth = parms->texwidth * CleanXfac; + parms->destheight = parms->texheight * CleanYfac; + break; + + case DTA_CleanNoMove: + parms->destwidth = parms->texwidth * CleanXfac; + parms->destheight = parms->texheight * CleanYfac; + break; + + case DTA_CleanNoMove_1: + parms->destwidth = parms->texwidth * CleanXfac_1; + parms->destheight = parms->texheight * CleanYfac_1; + break; + + case DTA_Fullscreen: + parms->x = parms->y = 0; + break; + + } + if (parms->virtWidth != screen->GetWidth() || parms->virtHeight != screen->GetHeight()) + { + VirtualToRealCoords(parms->x, parms->y, parms->destwidth, parms->destheight, + parms->virtWidth, parms->virtHeight, parms->virtBottom, !parms->keepratio); + } + } + + return false; +} + +//========================================================================== +// +// template helpers +// +//========================================================================== + +static void ListEnd(Va_List &tags) +{ + va_end(tags.list); +} + +static int ListGetInt(Va_List &tags) +{ + return va_arg(tags.list, int); +} + +static inline double ListGetDouble(Va_List &tags) +{ + return va_arg(tags.list, double); +} + + +//========================================================================== +// +// Main taglist parsing +// +//========================================================================== + +bool ParseDrawTextureTags(FTexture *img, double x, double y, uint32_t tag, Va_List& tags, DrawParms *parms, bool fortext) +{ + int boolval; + int intval; + bool translationset = false; + bool fillcolorset = false; + + if (!fortext) + { + if (img == NULL) + { + ListEnd(tags); + return false; + } + } + + // Do some sanity checks on the coordinates. + if (x < -16383 || x > 16383 || y < -16383 || y > 16383) + { + ListEnd(tags); + return false; + } + + parms->fortext = fortext; + parms->windowleft = 0; + parms->windowright = INT_MAX; + parms->dclip = screen->GetHeight(); + parms->uclip = 0; + parms->lclip = 0; + parms->rclip = screen->GetWidth(); + parms->left = INT_MAX; + parms->top = INT_MAX; + parms->destwidth = INT_MAX; + parms->destheight = INT_MAX; + parms->Alpha = 1.f; + parms->fillcolor = -1; + parms->colorOverlay = 0; + parms->alphaChannel = false; + parms->flipX = false; + parms->flipY = false; + parms->color = 0xffffffff; + //parms->shadowAlpha = 0; + parms->shadowColor = 0; + parms->virtWidth = screen->GetWidth(); + parms->virtHeight = screen->GetHeight(); + parms->keepratio = false; + parms->style.BlendOp = 255; // Dummy "not set" value + parms->masked = true; + parms->bilinear = false; + parms->desaturate = 0; + parms->cleanmode = DTA_Base; + parms->scalex = parms->scaley = 1; + parms->cellx = parms->celly = 0; + parms->maxstrlen = INT_MAX; + parms->virtBottom = false; + parms->srcx = 0.; + parms->srcy = 0.; + parms->srcwidth = 1.; + parms->srcheight = 1.; + parms->burn = false; + parms->monospace = EMonospacing::Off; + parms->spacing = 0; + parms->remap = 0; + + // Parse the tag list for attributes. (For floating point attributes, + // consider that the C ABI dictates that all floats be promoted to + // doubles when passed as function arguments.) + while (tag != TAG_DONE) + { + switch (tag) + { + default: + ListGetInt(tags); + break; + + case DTA_DestWidth: + assert(fortext == false); + if (fortext) return false; + parms->cleanmode = DTA_Base; + parms->destwidth = ListGetInt(tags); + break; + + case DTA_DestWidthF: + assert(fortext == false); + if (fortext) return false; + parms->cleanmode = DTA_Base; + parms->destwidth = ListGetDouble(tags); + break; + + case DTA_DestHeight: + assert(fortext == false); + if (fortext) return false; + parms->cleanmode = DTA_Base; + parms->destheight = ListGetInt(tags); + break; + + case DTA_DestHeightF: + assert(fortext == false); + if (fortext) return false; + parms->cleanmode = DTA_Base; + parms->destheight = ListGetDouble(tags); + break; + + case DTA_Clean: + boolval = ListGetInt(tags); + if (boolval) + { + parms->scalex = 1; + parms->scaley = 1; + parms->cleanmode = tag; + } + break; + + case DTA_CleanNoMove: + boolval = ListGetInt(tags); + if (boolval) + { + parms->scalex = CleanXfac; + parms->scaley = CleanYfac; + parms->cleanmode = tag; + } + break; + + case DTA_CleanNoMove_1: + boolval = ListGetInt(tags); + if (boolval) + { + parms->scalex = CleanXfac_1; + parms->scaley = CleanYfac_1; + parms->cleanmode = tag; + } + break; + + case DTA_320x200: + boolval = ListGetInt(tags); + if (boolval) + { + parms->cleanmode = DTA_Base; + parms->scalex = 1; + parms->scaley = 1; + parms->virtWidth = 320; + parms->virtHeight = 200; + } + break; + + case DTA_Bottom320x200: + boolval = ListGetInt(tags); + if (boolval) + { + parms->cleanmode = DTA_Base; + parms->scalex = 1; + parms->scaley = 1; + parms->virtWidth = 320; + parms->virtHeight = 200; + } + parms->virtBottom = true; + break; + + case DTA_HUDRules: + intval = ListGetInt(tags); + parms->cleanmode = intval == HUD_HorizCenter ? DTA_HUDRulesC : DTA_HUDRules; + break; + + case DTA_VirtualWidth: + parms->cleanmode = DTA_Base; + parms->virtWidth = ListGetInt(tags); + break; + + case DTA_VirtualWidthF: + parms->cleanmode = DTA_Base; + parms->virtWidth = ListGetDouble(tags); + break; + + case DTA_VirtualHeight: + parms->cleanmode = DTA_Base; + parms->virtHeight = ListGetInt(tags); + break; + + case DTA_VirtualHeightF: + parms->cleanmode = DTA_Base; + parms->virtHeight = ListGetDouble(tags); + break; + + case DTA_Fullscreen: + boolval = ListGetInt(tags); + if (boolval) + { + assert(fortext == false); + if (img == NULL) return false; + parms->cleanmode = DTA_Fullscreen; + parms->virtWidth = img->GetWidth(); + parms->virtHeight = img->GetHeight(); + } + break; + + case DTA_Alpha: + parms->Alpha = (float)(std::min(1., ListGetDouble(tags))); + break; + + case DTA_AlphaChannel: + parms->alphaChannel = ListGetInt(tags); + break; + + case DTA_FillColor: + parms->fillcolor = ListGetInt(tags); + if (parms->fillcolor != ~0u) + { + fillcolorset = true; + } + break; + + case DTA_TranslationIndex: + parms->remap = ListGetInt(tags); + break; + + case DTA_ColorOverlay: + parms->colorOverlay = ListGetInt(tags); + break; + + case DTA_Color: + parms->color = ListGetInt(tags); + break; + + case DTA_FlipX: + parms->flipX = ListGetInt(tags); + break; + + case DTA_FlipY: + parms->flipY = ListGetInt(tags); + break; + + case DTA_SrcX: + parms->srcx = ListGetDouble(tags) / img->GetWidth(); + break; + + case DTA_SrcY: + parms->srcy = ListGetDouble(tags) / img->GetHeight(); + break; + + case DTA_SrcWidth: + parms->srcwidth = ListGetDouble(tags) / img->GetWidth(); + break; + + case DTA_SrcHeight: + parms->srcheight = ListGetDouble(tags) / img->GetHeight(); + break; + + case DTA_TopOffset: + assert(fortext == false); + if (fortext) return false; + parms->top = ListGetInt(tags); + break; + + case DTA_TopOffsetF: + assert(fortext == false); + if (fortext) return false; + parms->top = ListGetDouble(tags); + break; + + case DTA_LeftOffset: + assert(fortext == false); + if (fortext) return false; + parms->left = ListGetInt(tags); + break; + + case DTA_LeftOffsetF: + assert(fortext == false); + if (fortext) return false; + parms->left = ListGetDouble(tags); + break; + + case DTA_CenterOffset: + assert(fortext == false); + if (fortext) return false; + if (ListGetInt(tags)) + { + parms->left = img->GetWidth() * 0.5; + parms->top = img->GetHeight() * 0.5; + } + break; + + case DTA_CenterBottomOffset: + assert(fortext == false); + if (fortext) return false; + if (ListGetInt(tags)) + { + parms->left = img->GetWidth() * 0.5; + parms->top = img->GetHeight(); + } + break; + + case DTA_WindowLeft: + assert(fortext == false); + if (fortext) return false; + parms->windowleft = ListGetInt(tags); + break; + + case DTA_WindowLeftF: + assert(fortext == false); + if (fortext) return false; + parms->windowleft = ListGetDouble(tags); + break; + + case DTA_WindowRight: + assert(fortext == false); + if (fortext) return false; + parms->windowright = ListGetInt(tags); + break; + + case DTA_WindowRightF: + assert(fortext == false); + if (fortext) return false; + parms->windowright = ListGetDouble(tags); + break; + + case DTA_ClipTop: + parms->uclip = ListGetInt(tags); + if (parms->uclip < 0) + { + parms->uclip = 0; + } + break; + + case DTA_ClipBottom: + parms->dclip = ListGetInt(tags); + if (parms->dclip > screen->GetHeight()) + { + parms->dclip = screen->GetHeight(); + } + break; + + case DTA_ClipLeft: + parms->lclip = ListGetInt(tags); + if (parms->lclip < 0) + { + parms->lclip = 0; + } + break; + + case DTA_ClipRight: + parms->rclip = ListGetInt(tags); + if (parms->rclip > screen->GetWidth()) + { + parms->rclip = screen->GetWidth(); + } + break; + + case DTA_ShadowAlpha: + //parms->shadowAlpha = (float)std::min(1., ListGetDouble(tags)); + break; + + case DTA_ShadowColor: + parms->shadowColor = ListGetInt(tags); + break; + + case DTA_Shadow: + boolval = ListGetInt(tags); + if (boolval) + { + //parms->shadowAlpha = 0.5; + parms->shadowColor = 0; + } + else + { + //parms->shadowAlpha = 0; + } + break; + + case DTA_Masked: + parms->masked = ListGetInt(tags); + break; + + case DTA_BilinearFilter: + parms->bilinear = ListGetInt(tags); + break; + + case DTA_KeepRatio: + // I think this is a terribly misleading name, since it actually turns + // *off* aspect ratio correction. + parms->keepratio = ListGetInt(tags); + break; + + case DTA_RenderStyle: + parms->style.AsDWORD = ListGetInt(tags); + break; + + case DTA_LegacyRenderStyle: // mainly for ZScript which does not handle FRenderStyle that well. + parms->style = (ERenderStyle)ListGetInt(tags); + break; + + case DTA_Desaturate: + parms->desaturate = ListGetInt(tags); + break; + + case DTA_TextLen: + parms->maxstrlen = ListGetInt(tags); + break; + + case DTA_CellX: + parms->cellx = ListGetInt(tags); + break; + + case DTA_CellY: + parms->celly = ListGetInt(tags); + break; + + case DTA_Monospace: + parms->monospace = ListGetInt(tags); + break; + + case DTA_Spacing: + parms->spacing = ListGetInt(tags); + break; + + case DTA_Burn: + parms->burn = true; + break; + + } + tag = ListGetInt(tags); + } + ListEnd(tags); + + if (parms->uclip >= parms->dclip || parms->lclip >= parms->rclip) + { + return false; + } + + if (img != NULL) + { + SetTextureParms(parms, img, x, y); + + if (parms->destwidth <= 0 || parms->destheight <= 0) + { + return false; + } + } + + if (parms->style.BlendOp == 255) + { + if (fillcolorset) + { + if (parms->alphaChannel) + { + parms->style = STYLE_Shaded; + } + else if (parms->Alpha < 1.f) + { + parms->style = STYLE_TranslucentStencil; + } + else + { + parms->style = STYLE_Stencil; + } + } + else if (parms->Alpha < 1.f) + { + parms->style = STYLE_Translucent; + } + else + { + parms->style = STYLE_Normal; + } + } + return true; +} + +//========================================================================== +// +// Coordinate conversion +// +//========================================================================== + +void VirtualToRealCoords(double &x, double &y, double &w, double &h, + double vwidth, double vheight, bool vbottom, bool handleaspect) +{ + float myratio = handleaspect ? ActiveRatio (screen->GetWidth(), screen->GetHeight()) : (4.0f / 3.0f); + + // if 21:9 AR, map to 16:9 for all callers. + // this allows for black bars and stops the stretching of fullscreen images + if (myratio > 1.7f) { + myratio = 16.0f / 9.0f; + } + + double right = x + w; + double bottom = y + h; + + if (myratio > 1.334f) + { // The target surface is either 16:9 or 16:10, so expand the + // specified virtual size to avoid undesired stretching of the + // image. Does not handle non-4:3 virtual sizes. I'll worry about + // those if somebody expresses a desire to use them. + x = (x - vwidth * 0.5) * screen->GetWidth() * 960 / (vwidth * AspectBaseWidth(myratio)) + screen->GetWidth() * 0.5; + w = (right - vwidth * 0.5) * screen->GetWidth() * 960 / (vwidth * AspectBaseWidth(myratio)) + screen->GetWidth() * 0.5 - x; + } + else + { + x = x * screen->GetWidth() / vwidth; + w = right * screen->GetWidth() / vwidth - x; + } + if (AspectTallerThanWide(myratio)) + { // The target surface is 5:4 + y = (y - vheight * 0.5) * screen->GetHeight() * 600 / (vheight * AspectBaseHeight(myratio)) + screen->GetHeight() * 0.5; + h = (bottom - vheight * 0.5) * screen->GetHeight() * 600 / (vheight * AspectBaseHeight(myratio)) + screen->GetHeight() * 0.5 - y; + if (vbottom) + { + y += (screen->GetHeight() - screen->GetHeight() * AspectMultiplier(myratio) / 48.0) * 0.5; + } + } + else + { + y = y * screen->GetHeight() / vheight; + h = bottom * screen->GetHeight() / vheight - y; + } +} + + +// Helper for ActiveRatio and CheckRatio. Returns the forced ratio type, or -1 if none. +int ActiveFakeRatio(int width, int height) +{ + int fakeratio = -1; + if ((vid_aspect >= 1) && (vid_aspect <= 6)) + { + // [SP] User wants to force aspect ratio; let them. + fakeratio = int(vid_aspect); + if (fakeratio == 3) + { + fakeratio = 0; + } + else if (fakeratio == 5) + { + fakeratio = 3; + } + } + else if (vid_aspect == 0)// && ViewportIsScaled43()) + { + fakeratio = 0; + } + return fakeratio; +} + +// Active screen ratio based on cvars and size +float ActiveRatio(int width, int height, float* trueratio) +{ + static float forcedRatioTypes[] = + { + 4 / 3.0f, + 16 / 9.0f, + 16 / 10.0f, + 17 / 10.0f, + 5 / 4.0f, + 17 / 10.0f, + 21 / 9.0f + }; + + float ratio = width / (float)height; + int fakeratio = ActiveFakeRatio(width, height); + + if (trueratio) + *trueratio = ratio; + return (fakeratio != -1) ? forcedRatioTypes[fakeratio] : ratio; +} + +// Tries to guess the physical dimensions of the screen based on the +// screen's pixel dimensions. Can return: +// 0: 4:3 +// 1: 16:9 +// 2: 16:10 +// 3: 17:10 +// 4: 5:4 +// 5: 17:10 (redundant, never returned) +// 6: 21:9 +int CheckRatio(int width, int height, int* trueratio) +{ + float aspect = width / (float)height; + + static std::pair ratioTypes[] = + { + { 21 / 9.0f , 6 }, + { 16 / 9.0f , 1 }, + { 17 / 10.0f , 3 }, + { 16 / 10.0f , 2 }, + { 4 / 3.0f , 0 }, + { 5 / 4.0f , 4 }, + { 0.0f, 0 } + }; + + int ratio = ratioTypes[0].second; + float distance = fabs(ratioTypes[0].first - aspect); + for (int i = 1; ratioTypes[i].first != 0.0f; i++) + { + float d = fabs(ratioTypes[i].first - aspect); + if (d < distance) + { + ratio = ratioTypes[i].second; + distance = d; + } + } + + int fakeratio = ActiveFakeRatio(width, height); + if (fakeratio == -1) + fakeratio = ratio; + + if (trueratio) + *trueratio = ratio; + return fakeratio; +} + +int AspectBaseWidth(float aspect) +{ + return (int)round(240.0f * aspect * 3.0f); +} + +int AspectBaseHeight(float aspect) +{ + if (!AspectTallerThanWide(aspect)) + return (int)round(200.0f * (320.0f / (AspectBaseWidth(aspect) / 3.0f)) * 3.0f); + else + return (int)round((200.0f * (4.0f / 3.0f)) / aspect * 3.0f); +} + +double AspectPspriteOffset(float aspect) +{ + if (!AspectTallerThanWide(aspect)) + return 0.0; + else + return ((4.0 / 3.0) / aspect - 1.0) * 97.5; +} + +int AspectMultiplier(float aspect) +{ + if (!AspectTallerThanWide(aspect)) + return (int)round(320.0f / (AspectBaseWidth(aspect) / 3.0f) * 48.0f); + else + return (int)round(200.0f / (AspectBaseHeight(aspect) / 3.0f) * 48.0f); +} + +bool AspectTallerThanWide(float aspect) +{ + return aspect < 1.333f; +} + +void ScaleWithAspect(int& w, int& h, int Width, int Height) +{ + int resRatio = CheckRatio(Width, Height); + int screenRatio; + CheckRatio(w, h, &screenRatio); + if (resRatio == screenRatio) + return; + + double yratio; + switch (resRatio) + { + case 0: yratio = 4. / 3.; break; + case 1: yratio = 16. / 9.; break; + case 2: yratio = 16. / 10.; break; + case 3: yratio = 17. / 10.; break; + case 4: yratio = 5. / 4.; break; + case 6: yratio = 21. / 9.; break; + default: return; + } + double y = w / yratio; + if (y > h) + w = static_cast(h * yratio); + else + h = static_cast(y); +} + diff --git a/source/common/2d/v_draw.h b/source/common/2d/v_draw.h new file mode 100644 index 000000000..c8f79461c --- /dev/null +++ b/source/common/2d/v_draw.h @@ -0,0 +1,35 @@ +#pragma once + +struct ScreenDummy +{ + static int GetWidth() { return 1360; } + static int GetHeight() { return 768; } +}; +extern ScreenDummy* screen; + +int GetUIScale(int altval); +int GetConScale(int altval); + +// [RH] Stretch values to make a 320x200 image best fit the screen +// without using fractional steppings +extern int CleanXfac, CleanYfac; + +// [RH] Effective screen sizes that the above scale values give you +extern int CleanWidth, CleanHeight; + +// Above minus 1 (or 1, if they are already 1) +extern int CleanXfac_1, CleanYfac_1, CleanWidth_1, CleanHeight_1; + + +bool SetTextureParms(DrawParms *parms, FTexture *img, double xx, double yy); +bool ParseDrawTextureTags(FTexture *img, double x, double y, uint32_t tag, Va_List& tags, DrawParms *parms, bool fortext); +void VirtualToRealCoords(double &x, double &y, double &w, double &h, double vwidth, double vheight, bool vbottom, bool handleaspect); +int ActiveFakeRatio(int width, int height); +float ActiveRatio(int width, int height, float* trueratio); +int CheckRatio(int width, int height, int* trueratio); +int AspectBaseWidth(float aspect);; +int AspectBaseHeight(float aspect); +double AspectPspriteOffset(float aspect); +int AspectMultiplier(float aspect); +bool AspectTallerThanWide(float aspect); +void ScaleWithAspect(int& w, int& h, int Width, int Height); diff --git a/source/common/2d/v_drawtext.cpp b/source/common/2d/v_drawtext.cpp new file mode 100644 index 000000000..213914941 --- /dev/null +++ b/source/common/2d/v_drawtext.cpp @@ -0,0 +1,254 @@ +/* +** 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 "utf8.h" +#include "v_text.h" + +#include "drawparms.h" +#include "v_draw.h" +#include "image.h" +#include "v_2ddrawer.h" + +class FFont; +int NumTextColors; +//========================================================================== +// +// Internal texture drawing function +// +//========================================================================== + +void DrawTexture(F2DDrawer *drawer, FTexture* img, double x, double y, int tags_first, ...) +{ + Va_List tags; + va_start(tags.list, tags_first); + DrawParms parms; + + bool res = ParseDrawTextureTags(img, x, y, tags_first, tags, &parms, false); + va_end(tags.list); + if (!res) + { + return; + } + drawer->AddTexture(img, parms); +} + +//========================================================================== +// +// 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; + + FTexture *pic; + int dummy; + bool redirected; + +#if 0 + if (NULL != (pic = font->GetChar (character, normalcolor, &dummy, &redirected))) + { + DrawParms parms; + Va_List tags; + va_start(tags.list, tag_first); + bool res = ParseDrawTextureTags(pic, x, y, tag_first, tags, &parms, false); + va_end(tags.list); + if (!res) + { + return; + } + PalEntry color = 0xffffffff; + parms.remap = redirected? nullptr : 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); + } +#endif +} + +//========================================================================== +// +// 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) +{ +#if 0 + int w; + const chartype *ch; + int c; + double cx; + double cy; + int boldcolor; + FRemapTable *range; + int kerning; + FTexture *pic; + + if (parms.celly == 0) parms.celly = font->GetHeight() + 1; + parms.celly *= parms.scaley; + + if (normalcolor >= NumTextColors) + normalcolor = CR_UNTRANSLATED; + boldcolor = normalcolor ? normalcolor - 1 : NumTextColors - 1; + + PalEntry colorparm = parms.color; + PalEntry color = 0xffffffff; + range = 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) + { + range = 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; + } + + bool redirected = false; + if (NULL != (pic = font->GetChar(c, currentcolor, &w, &redirected))) + { + parms.remap = redirected? nullptr : range; + SetTextureParms(&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) * parms.scalex; + } + else + { + cx += (parms.spacing) * parms.scalex; + } + + } +#endif +} + +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(nullptr, 0, 0, tag_first, tags, &parms, true); + va_end(tags.list); + if (!res) + { + return; + } + 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(nullptr, 0, 0, tag_first, tags, &parms, true); + va_end(tags.list); + if (!res) + { + return; + } + DrawTextCommon(drawer, font, normalcolor, x, y, string, parms); +} + diff --git a/source/common/2d/v_text.h b/source/common/2d/v_text.h new file mode 100644 index 000000000..90e5401d5 --- /dev/null +++ b/source/common/2d/v_text.h @@ -0,0 +1,124 @@ +/* +** v_text.h +** +**--------------------------------------------------------------------------- +** 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. +**--------------------------------------------------------------------------- +** +*/ + +#ifndef __V_TEXT_H__ +#define __V_TEXT_H__ + +#include "zstring.h" +class FFont; + +struct FBrokenLines +{ + unsigned Width; + FString Text; +}; + +#define TEXTCOLOR_ESCAPE '\034' +#define TEXTCOLOR_ESCAPESTR "\034" + +#define TEXTCOLOR_BRICK "\034A" +#define TEXTCOLOR_TAN "\034B" +#define TEXTCOLOR_GRAY "\034C" +#define TEXTCOLOR_GREY "\034C" +#define TEXTCOLOR_GREEN "\034D" +#define TEXTCOLOR_BROWN "\034E" +#define TEXTCOLOR_GOLD "\034F" +#define TEXTCOLOR_RED "\034G" +#define TEXTCOLOR_BLUE "\034H" +#define TEXTCOLOR_ORANGE "\034I" +#define TEXTCOLOR_WHITE "\034J" +#define TEXTCOLOR_YELLOW "\034K" +#define TEXTCOLOR_UNTRANSLATED "\034L" +#define TEXTCOLOR_BLACK "\034M" +#define TEXTCOLOR_LIGHTBLUE "\034N" +#define TEXTCOLOR_CREAM "\034O" +#define TEXTCOLOR_OLIVE "\034P" +#define TEXTCOLOR_DARKGREEN "\034Q" +#define TEXTCOLOR_DARKRED "\034R" +#define TEXTCOLOR_DARKBROWN "\034S" +#define TEXTCOLOR_PURPLE "\034T" +#define TEXTCOLOR_DARKGRAY "\034U" +#define TEXTCOLOR_CYAN "\034V" +#define TEXTCOLOR_ICE "\034W" +#define TEXTCOLOR_FIRE "\034X" +#define TEXTCOLOR_SAPPHIRE "\034Y" +#define TEXTCOLOR_TEAL "\034Z" + +#define TEXTCOLOR_NORMAL "\034-" +#define TEXTCOLOR_BOLD "\034+" + +#define TEXTCOLOR_CHAT "\034*" +#define TEXTCOLOR_TEAMCHAT "\034!" + +enum EColorRange : int +{ + CR_UNDEFINED = -1, + CR_BRICK, + CR_TAN, + CR_GRAY, + CR_GREY = CR_GRAY, + CR_GREEN, + CR_BROWN, + CR_GOLD, + CR_RED, + CR_BLUE, + CR_ORANGE, + CR_WHITE, + CR_YELLOW, + CR_UNTRANSLATED, + CR_BLACK, + CR_LIGHTBLUE, + CR_CREAM, + CR_OLIVE, + CR_DARKGREEN, + CR_DARKRED, + CR_DARKBROWN, + CR_PURPLE, + CR_DARKGRAY, + CR_CYAN, + CR_ICE, + CR_FIRE, + CR_SAPPHIRE, + CR_TEAL, + NUM_TEXT_COLORS, +}; + +extern int NumTextColors; + +TArray V_BreakLines (FFont *font, int maxwidth, const uint8_t *str, bool preservecolor = false); +inline TArray V_BreakLines (FFont *font, int maxwidth, const char *str, bool preservecolor = false) + { return V_BreakLines (font, maxwidth, (const uint8_t *)str, preservecolor); } +inline TArray V_BreakLines (FFont *font, int maxwidth, const FString &str, bool preservecolor = false) + { return V_BreakLines (font, maxwidth, (const uint8_t *)str.GetChars(), preservecolor); } + +#endif //__V_TEXT_H__