Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Portions of this file are copyright Rebirth contributors and licensed as
  3.  * described in COPYING.txt.
  4.  * Portions of this file are copyright Parallax Software and licensed
  5.  * according to the Parallax license below.
  6.  * See COPYING.txt for license details.
  7.  
  8. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
  9. SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
  10. END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
  11. ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
  12. IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
  13. SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
  14. FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
  15. CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
  16. AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
  17. COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  18. */
  19.  
  20. /*
  21.  *
  22.  * Graphical routines for drawing fonts.
  23.  *
  24.  */
  25.  
  26. #include <algorithm>
  27. #include <memory>
  28. #include <stdexcept>
  29. #include <stdarg.h>
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33. #ifndef macintosh
  34. #include <fcntl.h>
  35. #endif
  36.  
  37. #include "u_mem.h"
  38. #include "gr.h"
  39. #include "grdef.h"
  40. #include "dxxerror.h"
  41. #include "common/2d/bitmap.h"
  42. #include "makesig.h"
  43. #include "physfsx.h"
  44. #include "gamefont.h"
  45. #include "byteutil.h"
  46. #include "console.h"
  47. #include "config.h"
  48. #if DXX_USE_OGL
  49. #include "ogl_init.h"
  50. #endif
  51.  
  52. #include "compiler-range_for.h"
  53. #include "partial_range.h"
  54. #include <array>
  55. #include <memory>
  56.  
  57. static font_x_scale_float FONTSCALE_X()
  58. {
  59.         return FNTScaleX.operator float();
  60. }
  61.  
  62. static auto FONTSCALE_Y(const int &y)
  63. {
  64.         return font_scaled_float<'y'>(FNTScaleY * y);
  65. }
  66.  
  67. #define MAX_OPEN_FONTS  50
  68.  
  69. namespace {
  70.  
  71. constexpr std::integral_constant<uint8_t, 255> kerndata_terminator{};
  72.  
  73. }
  74.  
  75. namespace dcx {
  76.  
  77. //list of open fonts, for use (for now) for palette remapping
  78. static std::array<grs_font *, MAX_OPEN_FONTS> open_font;
  79.  
  80. #define BITS_TO_BYTES(x)    (((x)+7)>>3)
  81.  
  82. static int gr_internal_string_clipped(grs_canvas &, const grs_font &cv_font, int x, int y, const char *s);
  83. static int gr_internal_string_clipped_m(grs_canvas &, const grs_font &cv_font, int x, int y, const char *s);
  84.  
  85. static const uint8_t *find_kern_entry(const grs_font &font, const uint8_t first, const uint8_t second)
  86. {
  87.         auto p = font.ft_kerndata;
  88.  
  89.         while (*p != kerndata_terminator)
  90.                 if (p[0]==first && p[1]==second)
  91.                         return p;
  92.                 else p+=3;
  93.         return NULL;
  94. }
  95.  
  96. //takes the character AFTER being offset into font
  97.  
  98. namespace {
  99.  
  100. class font_character_extent
  101. {
  102.         const unsigned r;
  103. public:
  104.         font_character_extent(const grs_font &cv_font) :
  105.                 r(cv_font.ft_maxchar - cv_font.ft_minchar)
  106.         {
  107.         }
  108.         bool operator()(const unsigned c) const
  109.         {
  110.                 return c <= r;
  111.         }
  112. };
  113.  
  114. template <typename T>
  115. struct get_char_width_result
  116. {
  117.         T width, spacing;
  118. };
  119.  
  120. /* Floating form never uses width.  This specialization allows the
  121.  * compiler to recognize width as dead, shortening
  122.  * get_char_width<float>.
  123.  */
  124. template <>
  125. struct get_char_width_result<float>
  126. {
  127.         float spacing;
  128.         get_char_width_result(float, float s) :
  129.                 spacing(s)
  130.         {
  131.         }
  132. };
  133.  
  134. }
  135.  
  136. //takes the character BEFORE being offset into current font
  137. template <typename T>
  138. static get_char_width_result<T> get_char_width(const grs_font &cv_font, const uint8_t c, const uint8_t c2)
  139. {
  140.         const unsigned letter = c - cv_font.ft_minchar;
  141.         const auto ft_flags = cv_font.ft_flags;
  142.         const auto proportional = ft_flags & FT_PROPORTIONAL;
  143.  
  144.         const auto &&fontscale_x = FONTSCALE_X();
  145.         const auto &&INFONT = font_character_extent(cv_font);
  146.         if (!INFONT(letter)) {                          //not in font, draw as space
  147.                 return {0, static_cast<T>(proportional ? fontscale_x(cv_font.ft_w) / 2 : cv_font.ft_w)};
  148.         }
  149.         const T width = proportional ? fontscale_x(cv_font.ft_widths[letter]).operator float() : cv_font.ft_w;
  150.         if (ft_flags & FT_KERNED)
  151.         {
  152.                 if (!(c2==0 || c2=='\n')) {
  153.                         const unsigned letter2 = c2 - cv_font.ft_minchar;
  154.  
  155.                         if (INFONT(letter2)) {
  156.                                 const auto p = find_kern_entry(cv_font, letter, letter2);
  157.                                 if (p)
  158.                                         return {width, static_cast<T>(fontscale_x(p[2]))};
  159.                         }
  160.                 }
  161.         }
  162.         return {width, width};
  163. }
  164.  
  165. static int get_centered_x(const grs_canvas &canvas, const grs_font &cv_font, const char *s)
  166. {
  167.         float w;
  168.         for (w = 0.; const char c = *s; ++s)
  169.         {
  170.                 if (c == '\n')
  171.                         break;
  172.                 if (c <= 0x06)
  173.                 {
  174.                         if (c <= 0x03)
  175.                                 s++;
  176.                         continue;//skip color codes.
  177.                 }
  178.                 w += get_char_width<float>(cv_font, c, s[1]).spacing;
  179.         }
  180.  
  181.         return (canvas.cv_bitmap.bm_w - w) / 2;
  182. }
  183.  
  184. //hack to allow color codes to be embedded in strings -MPM
  185. //note we subtract one from color, since 255 is "transparent" so it'll never be used, and 0 would otherwise end the string.
  186. //function must already have orig_color var set (or they could be passed as args...)
  187. //perhaps some sort of recursive orig_color type thing would be better, but that would be way too much trouble for little gain
  188. constexpr std::integral_constant<int, 1> gr_message_color_level{};
  189. #define CHECK_EMBEDDED_COLORS() if ((*text_ptr >= 0x01) && (*text_ptr <= 0x02)) { \
  190.                 text_ptr++; \
  191.                 if (*text_ptr){ \
  192.                         if (gr_message_color_level >= *(text_ptr-1)) \
  193.                                 canvas.cv_font_fg_color = static_cast<uint8_t>(*text_ptr); \
  194.                         text_ptr++; \
  195.                 } \
  196.         } \
  197.         else if (*text_ptr == 0x03) \
  198.         { \
  199.                 underline = 1; \
  200.                 text_ptr++; \
  201.         } \
  202.         else if ((*text_ptr >= 0x04) && (*text_ptr <= 0x06)){ \
  203.                 if (gr_message_color_level >= *text_ptr - 3) \
  204.                         canvas.cv_font_fg_color= static_cast<uint8_t>(orig_color); \
  205.                 text_ptr++; \
  206.         }
  207.  
  208. template <bool masked_draws_background>
  209. static int gr_internal_string0_template(grs_canvas &canvas, const grs_font &cv_font, const int x, int y, const char *const s)
  210. {
  211.         const auto &&INFONT = font_character_extent(cv_font);
  212.         const auto ft_flags = cv_font.ft_flags;
  213.         const auto proportional = ft_flags & FT_PROPORTIONAL;
  214.         const auto cv_font_bg_color = canvas.cv_font_bg_color;
  215.         int     skip_lines = 0;
  216.  
  217.         unsigned int VideoOffset1;
  218.  
  219.         //to allow easy reseting to default string color with colored strings -MPM
  220.         const auto orig_color = canvas.cv_font_fg_color;
  221.         VideoOffset1 = y * canvas.cv_bitmap.bm_rowsize + x;
  222.         auto next_row = s;
  223.         while (next_row != NULL )
  224.         {
  225.                 const auto text_ptr1 = next_row;
  226.                 next_row = NULL;
  227.  
  228.                 if (x==0x8000) {                        //centered
  229.                         int xx = get_centered_x(canvas, cv_font, text_ptr1);
  230.                         VideoOffset1 = y * canvas.cv_bitmap.bm_rowsize + xx;
  231.                 }
  232.  
  233.                 for (int r=0; r < cv_font.ft_h; ++r)
  234.                 {
  235.                         auto text_ptr = text_ptr1;
  236.  
  237.                         unsigned VideoOffset = VideoOffset1;
  238.  
  239.                         for (; const uint8_t c0 = *text_ptr; ++text_ptr)
  240.                         {
  241.                                 if (c0 == '\n')
  242.                                 {
  243.                                         next_row = &text_ptr[1];
  244.                                         break;
  245.                                 }
  246.  
  247.                                 if (c0 == CC_COLOR)
  248.                                 {
  249.                                         canvas.cv_font_fg_color = static_cast<uint8_t>(*++text_ptr);
  250.                                         continue;
  251.                                 }
  252.  
  253.                                 if (c0 == CC_LSPACING)
  254.                                 {
  255.                                         skip_lines = *++text_ptr - '0';
  256.                                         continue;
  257.                                 }
  258.  
  259.                                 auto underline = unlikely(c0 == CC_UNDERLINE)
  260.                                         ? ++text_ptr, r == cv_font.ft_baseline + 2 || r == cv_font.ft_baseline + 3
  261.                                         : 0;
  262.  
  263.                                 const uint8_t c = *text_ptr;
  264.                                 const auto &result = get_char_width<int>(cv_font, c, text_ptr[1]);
  265.                                 const auto &width = result.width;
  266.                                 const auto &spacing = result.spacing;
  267.  
  268.                                 const unsigned letter = c - cv_font.ft_minchar;
  269.  
  270.                                 if constexpr (masked_draws_background)
  271.                                 {
  272.                                         (void)orig_color;
  273.                                         if (!INFONT(letter)) {  //not in font, draw as space
  274.                                                 VideoOffset += spacing;
  275.                                                 text_ptr++;
  276.                                                 continue;
  277.                                         }
  278.                                 }
  279.                                 else
  280.                                 {
  281.                                         if (!INFONT(letter) || c <= 0x06)       //not in font, draw as space
  282.                                         {
  283.                                                 CHECK_EMBEDDED_COLORS() else{
  284.                                                         VideoOffset += spacing;
  285.                                                         text_ptr++;
  286.                                                 }
  287.                                                 continue;
  288.                                         }
  289.                                 }
  290.  
  291.                                 if (width)
  292.                                 {
  293.                                         auto data = &canvas.cv_bitmap.get_bitmap_data()[VideoOffset];
  294.                                         const auto cv_font_fg_color = canvas.cv_font_fg_color;
  295.                                 if (underline)
  296.                                 {
  297.                                         std::fill_n(data, width, cv_font_fg_color);
  298.                                 }
  299.                                 else
  300.                                 {
  301.                                         auto fp = proportional ? cv_font.ft_chars[letter] : &cv_font.ft_data[letter * BITS_TO_BYTES(width) * cv_font.ft_h];
  302.                                         fp += BITS_TO_BYTES(width)*r;
  303.  
  304.                                         /* Setting bits=0 is a dead store, but is necessary to
  305.                                          * prevent -Og -Wuninitialized from issuing a bogus
  306.                                          * warning.  -Og does not see that bits_remaining=0
  307.                                          * guarantees that bits will be initialized from *fp before
  308.                                          * it is read.
  309.                                          */
  310.                                         uint8_t bits_remaining = 0, bits = 0;
  311.                                         for (uint_fast32_t i = width; i--; ++data, --bits_remaining)
  312.                                         {
  313.                                                 if (!bits_remaining)
  314.                                                 {
  315.                                                         bits = *fp++;
  316.                                                         bits_remaining = 8;
  317.                                                 }
  318.  
  319.                                                 const auto bit_enabled = (bits & 0x80);
  320.                                                 bits <<= 1;
  321.                                                 if constexpr (!masked_draws_background)
  322.                                                 {
  323.                                                         if (!bit_enabled)
  324.                                                                 continue;
  325.                                                 }
  326.                                                 *data = bit_enabled ? cv_font_fg_color : cv_font_bg_color;
  327.                                         }
  328.                                 }
  329.                                 }
  330.                                 VideoOffset += spacing;
  331.                         }
  332.                         VideoOffset1 += canvas.cv_bitmap.bm_rowsize;
  333.                         y++;
  334.                 }
  335.                 y += skip_lines;
  336.                 VideoOffset1 += canvas.cv_bitmap.bm_rowsize * skip_lines;
  337.                 skip_lines = 0;
  338.         }
  339.         return 0;
  340. }
  341.  
  342. static int gr_internal_string0(grs_canvas &canvas, const grs_font &cv_font, const int x, const int y, const char *const s)
  343. {
  344.         return gr_internal_string0_template<true>(canvas, cv_font, x, y, s);
  345. }
  346.  
  347. static int gr_internal_string0m(grs_canvas &canvas, const grs_font &cv_font, const int x, const int y, const char *const s)
  348. {
  349.         return gr_internal_string0_template<false>(canvas, cv_font, x, y, s);
  350. }
  351.  
  352. #if !DXX_USE_OGL
  353. static int gr_internal_color_string(grs_canvas &canvas, const grs_font &cv_font, const int x, const int y, const char *const s)
  354. {
  355. //a bitmap for the character
  356.         grs_bitmap char_bm = {};
  357.         char_bm.set_type(bm_mode::linear);
  358.         char_bm.set_flags(BM_FLAG_TRANSPARENT);
  359.         const char *text_ptr, *next_row, *text_ptr1;
  360.         int letter;
  361.         int xx,yy;
  362.  
  363.         const auto &&INFONT = font_character_extent(cv_font);
  364.         char_bm.bm_h = cv_font.ft_h;            //set height for chars of this font
  365.  
  366.         next_row = s;
  367.  
  368.         yy = y;
  369.  
  370.         const auto &&fspacy1 = FSPACY(1);
  371.         while (next_row != NULL)
  372.         {
  373.                 text_ptr1 = next_row;
  374.                 next_row = NULL;
  375.  
  376.                 text_ptr = text_ptr1;
  377.  
  378.                 xx = x;
  379.  
  380.                 if (xx==0x8000)                 //centered
  381.                         xx = get_centered_x(canvas, cv_font, text_ptr);
  382.  
  383.                 while (*text_ptr)
  384.                 {
  385.                         if (*text_ptr == '\n' )
  386.                         {
  387.                                 next_row = &text_ptr[1];
  388.                                 yy += cv_font.ft_h + fspacy1;
  389.                                 break;
  390.                         }
  391.  
  392.                         letter = static_cast<uint8_t>(*text_ptr) - cv_font.ft_minchar;
  393.  
  394.                         const auto &result = get_char_width<int>(cv_font, text_ptr[0], text_ptr[1]);
  395.                         const auto &width = result.width;
  396.                         const auto &spacing = result.spacing;
  397.  
  398.                         if (!INFONT(letter)) {  //not in font, draw as space
  399.                                 xx += spacing;
  400.                                 text_ptr++;
  401.                                 continue;
  402.                         }
  403.  
  404.                         const auto fp = (cv_font.ft_flags & FT_PROPORTIONAL)
  405.                                 ? cv_font.ft_chars[letter]
  406.                                 : &cv_font.ft_data[letter * BITS_TO_BYTES(width) * cv_font.ft_h];
  407.  
  408.                         gr_init_bitmap(char_bm, bm_mode::linear, 0, 0, width, cv_font.ft_h, width, fp);
  409.                         gr_bitmapm(canvas, xx, yy, char_bm);
  410.  
  411.                         xx += spacing;
  412.  
  413.                         text_ptr++;
  414.                 }
  415.  
  416.         }
  417.         return 0;
  418. }
  419.  
  420. #else //OGL
  421.  
  422. static int get_font_total_width(const grs_font &font)
  423. {
  424.         if (font.ft_flags & FT_PROPORTIONAL)
  425.         {
  426.                 int w=0;
  427.                 range_for (const auto v, unchecked_partial_range(font.ft_widths, static_cast<unsigned>(font.ft_maxchar - font.ft_minchar) + 1))
  428.                 {
  429.                         if (v < 0)
  430.                                 throw std::underflow_error("negative width");
  431.                         w += v;
  432.                 }
  433.                 return w;
  434.         }else{
  435.                 return font.ft_w*(font.ft_maxchar-font.ft_minchar+1);
  436.         }
  437. }
  438.  
  439. static void ogl_font_choose_size(grs_font * font,int gap,int *rw,int *rh){
  440.         int     nchars = font->ft_maxchar-font->ft_minchar+1;
  441.         int r,x,y,nc=0,smallest=999999,smallr=-1,tries;
  442.         int smallprop=10000;
  443.         int w;
  444.         for (int h=32;h<=256;h*=2){
  445. //              h=pow2ize(font->ft_h*rows+gap*(rows-1));
  446.                 if (font->ft_h>h)continue;
  447.                 r=(h/(font->ft_h+gap));
  448.                 w=pow2ize((get_font_total_width(*font)+(nchars-r)*gap)/r);
  449.                 tries=0;
  450.                 do {
  451.                         if (tries)
  452.                                 w=pow2ize(w+1);
  453.                         if(tries>3){
  454.                                 break;
  455.                         }
  456.                         nc=0;
  457.                         y=0;
  458.                         while(y+font->ft_h<=h){
  459.                                 x=0;
  460.                                 while (x<w){
  461.                                         if (nc==nchars)
  462.                                                 break;
  463.                                         if (font->ft_flags & FT_PROPORTIONAL){
  464.                                                 if (x+font->ft_widths[nc]+gap>w)break;
  465.                                                 x+=font->ft_widths[nc++]+gap;
  466.                                         }else{
  467.                                                 if (x+font->ft_w+gap>w)break;
  468.                                                 x+=font->ft_w+gap;
  469.                                                 nc++;
  470.                                         }
  471.                                 }
  472.                                 if (nc==nchars)
  473.                                         break;
  474.                                 y+=font->ft_h+gap;
  475.                         }
  476.                        
  477.                         tries++;
  478.                 }while(nc!=nchars);
  479.                 if (nc!=nchars)
  480.                         continue;
  481.  
  482.                 if (w*h==smallest){//this gives squarer sizes priority (ie, 128x128 would be better than 512*32)
  483.                         if (w>=h){
  484.                                 if (w/h<smallprop){
  485.                                         smallprop=w/h;
  486.                                         smallest++;//hack
  487.                                 }
  488.                         }else{
  489.                                 if (h/w<smallprop){
  490.                                         smallprop=h/w;
  491.                                         smallest++;//hack
  492.                                 }
  493.                         }
  494.                 }
  495.                 if (w*h<smallest){
  496.                         smallr=1;
  497.                         smallest=w*h;
  498.                         *rw=w;
  499.                         *rh=h;
  500.                 }
  501.         }
  502.         if (smallr<=0)
  503.                 Error("Could not fit font?\n");
  504. }
  505.  
  506. static void ogl_init_font(grs_font * font)
  507. {
  508.         int oglflags = OGL_FLAG_ALPHA;
  509.         int     nchars = font->ft_maxchar-font->ft_minchar+1;
  510.         int w,h,tw,th,curx=0,cury=0;
  511.         int gap=1; // x/y offset between the chars so we can filter
  512.  
  513.         th = tw = 0xffff;
  514.  
  515.         ogl_font_choose_size(font,gap,&tw,&th);
  516.         {
  517.                 RAIIdmem<uint8_t[]> data;
  518.                 const unsigned length = tw * th;
  519.                 MALLOC(data, uint8_t, length);
  520.                 std::fill_n(data.get(), length, TRANSPARENCY_COLOR); // map the whole data with transparency so we won't have borders if using gap
  521.                 gr_init_main_bitmap(font->ft_parent_bitmap, bm_mode::linear, 0, 0, tw, th, tw, std::move(data));
  522.         }
  523.         gr_set_transparent(font->ft_parent_bitmap, 1);
  524.  
  525.         if (!(font->ft_flags & FT_COLOR))
  526.                 oglflags |= OGL_FLAG_NOCOLOR;
  527.         ogl_init_texture(*(font->ft_parent_bitmap.gltexture = ogl_get_free_texture()), tw, th, oglflags); // have to init the gltexture here so the subbitmaps will find it.
  528.  
  529.         font->ft_bitmaps = std::make_unique<grs_bitmap[]>(nchars);
  530.         h=font->ft_h;
  531.  
  532.         for(int i=0;i<nchars;i++)
  533.         {
  534.                 if (font->ft_flags & FT_PROPORTIONAL)
  535.                         w=font->ft_widths[i];
  536.                 else
  537.                         w=font->ft_w;
  538.  
  539.                 if (w<1 || w>256)
  540.                         continue;
  541.  
  542.                 if (curx+w+gap>tw)
  543.                 {
  544.                         cury+=h+gap;
  545.                         curx=0;
  546.                 }
  547.  
  548.                 if (cury+h>th)
  549.                         Error("font doesn't really fit (%i/%i)?\n",i,nchars);
  550.  
  551.                 if (font->ft_flags & FT_COLOR)
  552.                 {
  553.                         const auto fp = (font->ft_flags & FT_PROPORTIONAL)
  554.                                 ? font->ft_chars[i]
  555.                                 : font->ft_data + i * w*h;
  556.                         for (int y=0;y<h;y++)
  557.                         {
  558.                                 for (int x=0;x<w;x++)
  559.                                 {
  560.                                         font->ft_parent_bitmap.get_bitmap_data()[curx+x+(cury+y)*tw] = fp[x+y*w];
  561.                                         // Let's call this a HACK:
  562.                                         // If we filter the fonts, the sliders will be messed up as the border pixels will have an
  563.                                         // alpha value while filtering. So the slider bitmaps will not look "connected".
  564.                                         // To prevent this, duplicate the first/last pixel-row with a 1-pixel offset.
  565.                                         if (gap && i >= 99 && i <= 102)
  566.                                         {
  567.                                                 // See which bitmaps need left/right shifts:
  568.                                                 // 99  = SLIDER_LEFT - shift RIGHT
  569.                                                 // 100 = SLIDER_RIGHT - shift LEFT
  570.                                                 // 101 = SLIDER_MIDDLE - shift LEFT+RIGHT
  571.                                                 // 102 = SLIDER_MARKER - shift RIGHT
  572.  
  573.                                                 // shift left border
  574.                                                 if (x==0 && i != 99 && i != 102)
  575.                                                         font->ft_parent_bitmap.get_bitmap_data()[(curx+x+(cury+y)*tw)-1] = fp[x+y*w];
  576.  
  577.                                                 // shift right border
  578.                                                 if (x==w-1 && i != 100)
  579.                                                         font->ft_parent_bitmap.get_bitmap_data()[(curx+x+(cury+y)*tw)+1] = fp[x+y*w];
  580.                                         }
  581.                                 }
  582.                         }
  583.                 }
  584.                 else
  585.                 {
  586.                         int BitMask, bits = 0;
  587.                         auto white = gr_find_closest_color(63, 63, 63);
  588.                         auto fp = (font->ft_flags & FT_PROPORTIONAL)
  589.                                 ? font->ft_chars[i]
  590.                                 : font->ft_data + i * BITS_TO_BYTES(w)*h;
  591.                         for (int y=0;y<h;y++){
  592.                                 BitMask=0;
  593.                                 for (int x=0; x< w; x++ )
  594.                                 {
  595.                                         if (BitMask==0) {
  596.                                                 bits = *fp++;
  597.                                                 BitMask = 0x80;
  598.                                         }
  599.  
  600.                                         if (bits & BitMask)
  601.                                                 font->ft_parent_bitmap.get_bitmap_data()[curx+x+(cury+y)*tw] = white;
  602.                                         else
  603.                                                 font->ft_parent_bitmap.get_bitmap_data()[curx+x+(cury+y)*tw] = 255;
  604.                                         BitMask >>= 1;
  605.                                 }
  606.                         }
  607.                 }
  608.                 gr_init_sub_bitmap(font->ft_bitmaps[i],font->ft_parent_bitmap,curx,cury,w,h);
  609.                 curx+=w+gap;
  610.         }
  611.         ogl_loadbmtexture_f(font->ft_parent_bitmap, CGameCfg.TexFilt, 0, 0);
  612. }
  613.  
  614. static int ogl_internal_string(grs_canvas &canvas, const grs_font &cv_font, const int x, const int y, const char *const s)
  615. {
  616.         const char * text_ptr, * next_row, * text_ptr1;
  617.         int letter;
  618.         int xx,yy;
  619.         int orig_color = canvas.cv_font_fg_color;//to allow easy reseting to default string color with colored strings -MPM
  620.         int underline;
  621.  
  622.         next_row = s;
  623.  
  624.         yy = y;
  625.  
  626.         if (grd_curscreen->sc_canvas.cv_bitmap.get_type() != bm_mode::ogl)
  627.                 Error("carp.\n");
  628.         const auto &&fspacy1 = FSPACY(1);
  629.         const auto &&INFONT = font_character_extent(cv_font);
  630.         const auto &&fontscale_x = FONTSCALE_X();
  631.         const auto &&FONTSCALE_Y_ft_h = FONTSCALE_Y(cv_font.ft_h);
  632.         ogl_colors colors;
  633.         while (next_row != NULL)
  634.         {
  635.                 text_ptr1 = next_row;
  636.                 next_row = NULL;
  637.  
  638.                 text_ptr = text_ptr1;
  639.  
  640.                 xx = x;
  641.  
  642.                 if (xx==0x8000)                 //centered
  643.                         xx = get_centered_x(canvas, cv_font, text_ptr);
  644.  
  645.                 while (*text_ptr)
  646.                 {
  647.  
  648.                         if (*text_ptr == '\n' )
  649.                         {
  650.                                 next_row = &text_ptr[1];
  651.                                 yy += FONTSCALE_Y_ft_h + fspacy1;
  652.                                 break;
  653.                         }
  654.  
  655.                         letter = static_cast<uint8_t>(*text_ptr) - cv_font.ft_minchar;
  656.  
  657.                         const auto &result = get_char_width<int>(cv_font, text_ptr[0], text_ptr[1]);
  658.                         const auto &spacing = result.spacing;
  659.  
  660.                         underline = 0;
  661.                         if (!INFONT(letter) || static_cast<uint8_t>(*text_ptr) <= 0x06) //not in font, draw as space
  662.                         {
  663.                                 CHECK_EMBEDDED_COLORS() else{
  664.                                         xx += spacing;
  665.                                         text_ptr++;
  666.                                 }
  667.                                
  668.                                 if (underline)
  669.                                 {
  670.                                         const auto color = canvas.cv_font_fg_color;
  671.                                         gr_rect(canvas, xx, yy + cv_font.ft_baseline + 2, xx + cv_font.ft_w, yy + cv_font.ft_baseline + 3, color);
  672.                                 }
  673.  
  674.                                 continue;
  675.                         }
  676.                        
  677.                         const auto ft_w = (cv_font.ft_flags & FT_PROPORTIONAL)
  678.                                 ? cv_font.ft_widths[letter]
  679.                                 : cv_font.ft_w;
  680.  
  681.                         ogl_ubitmapm_cs(canvas, xx, yy, fontscale_x(ft_w), FONTSCALE_Y_ft_h, cv_font.ft_bitmaps[letter], (cv_font.ft_flags & FT_COLOR) ? colors.white : (canvas.cv_bitmap.get_type() == bm_mode::ogl) ? colors.init(canvas.cv_font_fg_color) : throw std::runtime_error("non-color string to non-ogl dest"), F1_0);
  682.  
  683.                         xx += spacing;
  684.  
  685.                         text_ptr++;
  686.                 }
  687.         }
  688.         return 0;
  689. }
  690.  
  691. #define gr_internal_color_string ogl_internal_string
  692. #endif //OGL
  693.  
  694. void gr_string(grs_canvas &canvas, const grs_font &cv_font, const int x, const int y, const char *const s)
  695. {
  696.         int w, h;
  697.         gr_get_string_size(cv_font, s, &w, &h, nullptr);
  698.         gr_string(canvas, cv_font, x, y, s, w, h);
  699. }
  700.  
  701. static void gr_ustring_mono(grs_canvas &canvas, const grs_font &cv_font, const int x, const int y, const char *const s)
  702. {
  703.         switch (canvas.cv_bitmap.get_type())
  704.         {
  705.                 case bm_mode::linear:
  706.                         if (canvas.cv_font_bg_color == -1)
  707.                                 gr_internal_string0m(canvas, cv_font, x, y, s);
  708.                         else
  709.                                 gr_internal_string0(canvas, cv_font, x, y, s);
  710.         }
  711. }
  712.  
  713. void gr_string(grs_canvas &canvas, const grs_font &cv_font, const int x, const int y, const char *const s, const int w, const int h)
  714. {
  715.         if (y + h < 0)
  716.                 return;
  717.         const auto bm_h = canvas.cv_bitmap.bm_h;
  718.         if (y > bm_h)
  719.                 return;
  720.         const auto bm_w = canvas.cv_bitmap.bm_w;
  721.         int xw = w;
  722.         if ( x == 0x8000 )      {
  723.                 // for x, since this will be centered, only look at
  724.                 // width.
  725.         } else {
  726.                 if (x > bm_w)
  727.                         return;
  728.                 xw += x;
  729.                 if (xw < 0)
  730.                         return;
  731.         }
  732.         if (
  733. #if DXX_USE_OGL
  734.                 canvas.cv_bitmap.get_type() == bm_mode::ogl ||
  735. #endif
  736.                 cv_font.ft_flags & FT_COLOR)
  737.         {
  738.                 gr_internal_color_string(canvas, cv_font, x, y, s);
  739.                 return;
  740.         }
  741.         // Partially clipped...
  742.         if (!(y < 0 ||
  743.                 x < 0 ||
  744.                 xw > bm_w ||
  745.                 y + h > bm_h))
  746.         {
  747.                 gr_ustring_mono(canvas, cv_font, x, y, s);
  748.                 return;
  749.         }
  750.  
  751.         if (canvas.cv_font_bg_color == -1)
  752.         {
  753.                 gr_internal_string_clipped_m(canvas, cv_font, x, y, s);
  754.                 return;
  755.         }
  756.         gr_internal_string_clipped(canvas, cv_font, x, y, s);
  757. }
  758.  
  759. void gr_ustring(grs_canvas &canvas, const grs_font &cv_font, const int x, const int y, const char *const s)
  760. {
  761. #if DXX_USE_OGL
  762.         if (canvas.cv_bitmap.get_type() == bm_mode::ogl)
  763.         {
  764.                 ogl_internal_string(canvas, cv_font, x, y, s);
  765.                 return;
  766.         }
  767. #endif
  768.        
  769.         if (canvas.cv_font->ft_flags & FT_COLOR) {
  770.                 gr_internal_color_string(canvas, cv_font, x, y, s);
  771.         }
  772.         else
  773.                 gr_ustring_mono(canvas, cv_font, x, y, s);
  774. }
  775.  
  776. int gr_get_string_height(const grs_font &cv_font, const unsigned lines)
  777. {
  778.         const auto fontscale_y = FONTSCALE_Y(cv_font.ft_h);
  779.         return static_cast<int>(fontscale_y + (lines * (fontscale_y + FSPACY(1))));
  780. }
  781.  
  782. void gr_get_string_size(const grs_font &cv_font, const char *s, int *const string_width, int *const string_height, int *const average_width)
  783. {
  784.         gr_get_string_size(cv_font, s, string_width, string_height, average_width, UINT_MAX);
  785. }
  786.  
  787. void gr_get_string_size(const grs_font &cv_font, const char *s, int *const string_width, int *const string_height, int *const average_width, const unsigned max_chars_per_line)
  788. {
  789.         float longest_width=0.0,string_width_f=0.0;
  790.         unsigned lines = 0;
  791.         if (average_width)
  792.                 *average_width = cv_font.ft_w;
  793.         if (!string_width && !string_height)
  794.                 return;
  795.         if (s)
  796.         {
  797.                 unsigned remaining_chars_this_line = max_chars_per_line;
  798.                 while (*s)
  799.                 {
  800.                         if (*s == '\n')
  801.                         {
  802.                                 if (longest_width < string_width_f)
  803.                                         longest_width = string_width_f;
  804.                                 string_width_f = 0;
  805.                                 const auto os = s;
  806.                                 while (*++s == '\n')
  807.                                 {
  808.                                 }
  809.                                 lines += s - os;
  810.                                 if (!*s)
  811.                                         break;
  812.                                 remaining_chars_this_line = max_chars_per_line;
  813.                         }
  814.  
  815.                         const auto &result = get_char_width<float>(cv_font, s[0], s[1]);
  816.                         const auto &spacing = result.spacing;
  817.                         string_width_f += spacing;
  818.                         s++;
  819.                         if (!--remaining_chars_this_line)
  820.                                 break;
  821.                 }
  822.         }
  823.         if (string_width)
  824.                 *string_width = std::max(longest_width, string_width_f);
  825.         if (string_height)
  826.                 *string_height = gr_get_string_height(cv_font, lines);
  827. }
  828.  
  829. std::pair<const char *, unsigned> gr_get_string_wrap(const grs_font &cv_font, const char *s, unsigned limit)
  830. {
  831.         assert(s);
  832.         float string_width_f=0.0;
  833.         const float limit_f = limit;
  834.         for (uint8_t c; (c = *s); ++s)
  835.         {
  836.                 const auto &&spacing = get_char_width<float>(cv_font, c, s[1]).spacing;
  837.                 const float next_string_width = string_width_f + spacing;
  838.                 if (next_string_width >= limit_f)
  839.                         break;
  840.                 string_width_f = next_string_width;
  841.         }
  842.         return {s, static_cast<unsigned>(string_width_f)};
  843. }
  844.  
  845. template <void (&F)(grs_canvas &, const grs_font &, int, int, const char *)>
  846. void (gr_printt)(grs_canvas &canvas, const grs_font &cv_font, const int x, const int y, const char *const format, ...)
  847. {
  848.         char buffer[1000];
  849.         va_list args;
  850.  
  851.         va_start(args, format );
  852.         vsnprintf(buffer, sizeof(buffer), format, args);
  853.         va_end(args);
  854.         F(canvas, cv_font, x, y, buffer);
  855. }
  856.  
  857. template void gr_printt<gr_string>(grs_canvas &, const grs_font &, int, int, const char *, ...);
  858. template void gr_printt<gr_ustring>(grs_canvas &, const grs_font &, int, int, const char *, ...);
  859.  
  860. void gr_close_font(std::unique_ptr<grs_font> font)
  861. {
  862.         if (font)
  863.         {
  864.                 //find font in list
  865.                 if (!(font->ft_flags & FT_COLOR))
  866.                         return;
  867.                 auto e = end(open_font);
  868.                 auto i = std::find(begin(open_font), e, font.get());
  869.                 if (i == e)
  870.                         throw std::logic_error("closing non-open font");
  871.                 *i = nullptr;
  872.         }
  873. }
  874.  
  875. //remap a font, re-reading its data & palette
  876. static void gr_remap_font(grs_font *font, const char *fontname);
  877.  
  878. //remap (by re-reading) all the color fonts
  879. void gr_remap_color_fonts()
  880. {
  881.         range_for (auto font, open_font)
  882.         {
  883.                 if (font)
  884.                         gr_remap_font(font, &font->ft_filename[0]);
  885.         }
  886. }
  887.  
  888. /*
  889.  * reads a grs_font structure from a PHYSFS_File
  890.  */
  891. static void grs_font_read(grs_font *gf, PHYSFS_File *fp)
  892. {
  893.         gf->ft_w = PHYSFSX_readShort(fp);
  894.         gf->ft_h = PHYSFSX_readShort(fp);
  895.         gf->ft_flags = PHYSFSX_readShort(fp);
  896.         gf->ft_baseline = PHYSFSX_readShort(fp);
  897.         gf->ft_minchar = PHYSFSX_readByte(fp);
  898.         gf->ft_maxchar = PHYSFSX_readByte(fp);
  899.         PHYSFSX_readShort(fp);
  900.         gf->ft_data = reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(PHYSFSX_readInt(fp)) - GRS_FONT_SIZE);
  901.         PHYSFSX_readInt(fp);
  902.         gf->ft_widths = reinterpret_cast<int16_t *>(static_cast<uintptr_t>(PHYSFSX_readInt(fp)) - GRS_FONT_SIZE);
  903.         gf->ft_kerndata = reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(PHYSFSX_readInt(fp)) - GRS_FONT_SIZE);
  904. }
  905.  
  906. static std::unique_ptr<grs_font> gr_internal_init_font(const char *fontname)
  907. {
  908.         const uint8_t *ptr;
  909.         color_palette_index *ft_data;
  910.         struct {
  911.                 std::array<char, 4> magic;
  912.                 unsigned datasize;      //size up to (but not including) palette
  913.         } file_header;
  914.  
  915.         auto fontfile = PHYSFSX_openReadBuffered(fontname);
  916.  
  917.         if (!fontfile) {
  918.                 con_printf(CON_VERBOSE, "Can't open font file %s", fontname);
  919.                 return {};
  920.         }
  921.  
  922.         static_assert(sizeof(file_header) == 8, "file header size error");
  923.         if (PHYSFS_read(fontfile, &file_header, sizeof(file_header), 1) != 1 ||
  924.                 memcmp(file_header.magic.data(), "PSFN", 4) ||
  925.                 (file_header.datasize = INTEL_INT(file_header.datasize)) < GRS_FONT_SIZE)
  926.         {
  927.                 con_printf(CON_NORMAL, "Invalid header in font file %s", fontname);
  928.                 return {};
  929.         }
  930.  
  931.         file_header.datasize -= GRS_FONT_SIZE; // subtract the size of the header.
  932.         const auto &datasize = file_header.datasize;
  933.  
  934.         auto font = std::make_unique<grs_font>();
  935.         grs_font_read(font.get(), fontfile);
  936.  
  937.         const unsigned nchars = font->ft_maxchar - font->ft_minchar + 1;
  938.         std::size_t ft_chars_storage = (font->ft_flags & FT_PROPORTIONAL)
  939.                 ? sizeof(uint8_t *) * nchars
  940.                 : 0;
  941.  
  942.         auto ft_allocdata = std::make_unique<color_palette_index[]>(datasize + ft_chars_storage);
  943.         const auto font_data = &ft_allocdata[ft_chars_storage];
  944.         if (PHYSFS_read(fontfile, font_data, 1, datasize) != datasize)
  945.         {
  946.                 con_printf(CON_URGENT, "Insufficient data in font file %s", fontname);
  947.                 return {};
  948.         }
  949.  
  950.         if (font->ft_flags & FT_PROPORTIONAL) {
  951.                 const auto offset_widths = reinterpret_cast<uintptr_t>(font->ft_widths);
  952.                 auto w = reinterpret_cast<short *>(&font_data[offset_widths]);
  953.                 if (offset_widths >= datasize || offset_widths + (nchars * sizeof(*w)) >= datasize)
  954.                 {
  955.                         con_printf(CON_URGENT, "Missing widths in font file %s", fontname);
  956.                         return {};
  957.                 }
  958.                 font->ft_widths = w;
  959.                 const auto offset_data = reinterpret_cast<uintptr_t>(font->ft_data);
  960.                 if (offset_data >= datasize)
  961.                 {
  962.                         con_printf(CON_URGENT, "Missing data in font file %s", fontname);
  963.                         return {};
  964.                 }
  965.                 font->ft_data = ptr = ft_data = &font_data[offset_data];
  966.                 const auto ft_chars = reinterpret_cast<const uint8_t **>(ft_allocdata.get());
  967.                 font->ft_chars = reinterpret_cast<const uint8_t *const *>(ft_chars);
  968.  
  969.                 const unsigned is_color = font->ft_flags & FT_COLOR;
  970.                 const unsigned ft_h = font->ft_h;
  971.                 std::generate_n(ft_chars, nchars, [is_color, ft_h, &w, &ptr]{
  972.                         const unsigned s = INTEL_SHORT(*w);
  973.                         if constexpr (words_bigendian)
  974.                                 *w = static_cast<uint16_t>(s);
  975.                         ++w;
  976.                         const auto r = ptr;
  977.                         ptr += ft_h * (is_color ? s : BITS_TO_BYTES(s));
  978.                         return r;
  979.                 });
  980.         } else  {
  981.  
  982.                 font->ft_data   = ft_data = font_data;
  983.                 font->ft_widths = NULL;
  984.  
  985.                 ptr = font->ft_data + (nchars * font->ft_w * font->ft_h);
  986.         }
  987.  
  988.         if (font->ft_flags & FT_KERNED)
  989.         {
  990.                 const auto offset_kerndata = reinterpret_cast<uintptr_t>(font->ft_kerndata);
  991.                 if (datasize <= offset_kerndata)
  992.                 {
  993.                         con_printf(CON_URGENT, "Missing kerndata in font file %s", fontname);
  994.                         return {};
  995.                 }
  996.                 const auto begin_kerndata = reinterpret_cast<const unsigned char *>(&font_data[offset_kerndata]);
  997.                 const auto end_font_data = &font_data[datasize - ((datasize - offset_kerndata + 2) % 3)];
  998.                 for (auto cur_kerndata = begin_kerndata;; cur_kerndata += 3)
  999.                 {
  1000.                         if (cur_kerndata == end_font_data)
  1001.                         {
  1002.                                 con_printf(CON_URGENT, "Unterminated kerndata in font file %s", fontname);
  1003.                                 return {};
  1004.                         }
  1005.                         if (*cur_kerndata == kerndata_terminator)
  1006.                                 break;
  1007.                 }
  1008.                 font->ft_kerndata = begin_kerndata;
  1009.         }
  1010.         else
  1011.                 font->ft_kerndata = nullptr;
  1012.  
  1013.         if (font->ft_flags & FT_COLOR) {                //remap palette
  1014.                 palette_array_t palette;
  1015.                 std::array<color_palette_index, 256> colormap;
  1016.                 /* `freq` exists so that decode_data can write to it, but it is
  1017.                  * otherwise unused.  `decode_data` is not guaranteed to write
  1018.                  * to every element, but the bitset constructor will initialize
  1019.                  * the storage, so reading unwritten fields will always return
  1020.                  * false.
  1021.                  */
  1022.                 std::bitset<256> freq;
  1023.  
  1024.                 PHYSFS_read(fontfile,&palette[0],sizeof(palette[0]),palette.size());            //read the palette
  1025.  
  1026.                 build_colormap_good(palette, colormap);
  1027.  
  1028.                 colormap[TRANSPARENCY_COLOR] = TRANSPARENCY_COLOR;              // changed from colormap[255] = 255 to this for macintosh
  1029.  
  1030.                 decode_data(ft_data, ptr - font->ft_data, colormap, freq );
  1031.         }
  1032.         fontfile.reset();
  1033.         //set curcanv vars
  1034.  
  1035.         auto &ft_filename = font->ft_filename;
  1036.         font->ft_allocdata = move(ft_allocdata);
  1037.         strncpy(&ft_filename[0], fontname, ft_filename.size());
  1038.         return font;
  1039. }
  1040.  
  1041. grs_font_ptr gr_init_font(grs_canvas &canvas, const char *fontname)
  1042. {
  1043.         auto font = gr_internal_init_font(fontname);
  1044.         if (!font)
  1045.                 return {};
  1046.  
  1047.         canvas.cv_font        = font.get();
  1048.         canvas.cv_font_fg_color    = 0;
  1049.         canvas.cv_font_bg_color    = 0;
  1050.         if (font->ft_flags & FT_COLOR)
  1051.         {
  1052.                 auto e = end(open_font);
  1053.                 auto i = std::find(begin(open_font), e, static_cast<grs_font *>(nullptr));
  1054.                 if (i == e)
  1055.                         throw std::logic_error("too many open fonts");
  1056.                 *i = font.get();
  1057.         }
  1058. #if DXX_USE_OGL
  1059.         ogl_init_font(font.get());
  1060. #endif
  1061.         return grs_font_ptr(font.release());
  1062. }
  1063.  
  1064. //remap a font by re-reading its data & palette
  1065. void gr_remap_font(grs_font *font, const char *fontname)
  1066. {
  1067.         auto n = gr_internal_init_font(fontname);
  1068.         if (!n)
  1069.                 return;
  1070.         if (!(n->ft_flags & FT_COLOR))
  1071.                 return;
  1072.         *font = std::move(*n.get());
  1073. #if DXX_USE_OGL
  1074.         ogl_init_font(font);
  1075. #endif
  1076. }
  1077.  
  1078. void gr_set_curfont(grs_canvas &canvas, const grs_font *n)
  1079. {
  1080.         canvas.cv_font = n;
  1081. }
  1082.  
  1083. template <bool masked_draws_background>
  1084. static int gr_internal_string_clipped_template(grs_canvas &canvas, const grs_font &cv_font, int x, int y, const char *const s)
  1085. {
  1086.         int letter;
  1087.         int x1 = x, last_x;
  1088.  
  1089.         const auto &&INFONT = font_character_extent(cv_font);
  1090.         const auto ft_flags = cv_font.ft_flags;
  1091.         const auto proportional = ft_flags & FT_PROPORTIONAL;
  1092.         const auto cv_font_bg_color = canvas.cv_font_bg_color;
  1093.  
  1094.         for (auto next_row = s; next_row;)
  1095.         {
  1096.                 const auto text_ptr1 = next_row;
  1097.                 next_row = NULL;
  1098.  
  1099.                 x = x1;
  1100.                 if (x==0x8000)                  //centered
  1101.                         x = get_centered_x(canvas, cv_font, text_ptr1);
  1102.  
  1103.                 last_x = x;
  1104.  
  1105.                 for (int r=0; r < cv_font.ft_h; r++) {
  1106.                         auto text_ptr = text_ptr1;
  1107.                         x = last_x;
  1108.  
  1109.                         for (; const uint8_t c0 = *text_ptr; ++text_ptr)
  1110.                         {
  1111.                                 if (c0 == '\n')
  1112.                                 {
  1113.                                         next_row = &text_ptr[1];
  1114.                                         break;
  1115.                                 }
  1116.                                 if (c0 == CC_COLOR)
  1117.                                 {
  1118.                                         canvas.cv_font_fg_color = static_cast<uint8_t>(*++text_ptr);
  1119.                                         continue;
  1120.                                 }
  1121.  
  1122.                                 if (c0 == CC_LSPACING)
  1123.                                 {
  1124.                                         Int3(); //      Warning: skip lines not supported for clipped strings.
  1125.                                         text_ptr += 1;
  1126.                                         continue;
  1127.                                 }
  1128.  
  1129.                                 const auto underline = unlikely(c0 == CC_UNDERLINE)
  1130.                                         ? ++text_ptr, r == cv_font.ft_baseline + 2 || r == cv_font.ft_baseline + 3
  1131.                                         : 0;
  1132.                                 const uint8_t c = *text_ptr;
  1133.                                 const auto &result = get_char_width<int>(cv_font, c, text_ptr[1]);
  1134.                                 const auto &width = result.width;
  1135.                                 const auto &spacing = result.spacing;
  1136.  
  1137.                                 letter = c - cv_font.ft_minchar;
  1138.  
  1139.                                 if (!INFONT(letter)) {  //not in font, draw as space
  1140.                                         x += spacing;
  1141.                                         continue;
  1142.                                 }
  1143.                                 const auto cv_font_fg_color = canvas.cv_font_fg_color;
  1144.                                 auto color = cv_font_fg_color;
  1145.                                 if (width)
  1146.                                 {
  1147.                                 if (underline)  {
  1148.                                         for (uint_fast32_t i = width; i--;)
  1149.                                         {
  1150.                                                 gr_pixel(canvas.cv_bitmap, x++, y, color);
  1151.                                         }
  1152.                                 } else {
  1153.                                         auto fp = proportional ? cv_font.ft_chars[letter] : cv_font.ft_data + letter * BITS_TO_BYTES(width) * cv_font.ft_h;
  1154.                                         fp += BITS_TO_BYTES(width)*r;
  1155.  
  1156.                                         /* Setting bits=0 is a dead store, but is necessary to
  1157.                                          * prevent -Og -Wuninitialized from issuing a bogus
  1158.                                          * warning.  -Og does not see that bits_remaining=0
  1159.                                          * guarantees that bits will be initialized from *fp before
  1160.                                          * it is read.
  1161.                                          */
  1162.                                         uint8_t bits_remaining = 0, bits = 0;
  1163.  
  1164.                                         for (uint_fast32_t i = width; i--; ++x, --bits_remaining)
  1165.                                         {
  1166.                                                 if (!bits_remaining)
  1167.                                                 {
  1168.                                                         bits = *fp++;
  1169.                                                         bits_remaining = 8;
  1170.                                                 }
  1171.                                                 const auto bit_enabled = (bits & 0x80);
  1172.                                                 bits <<= 1;
  1173.                                                 if constexpr (masked_draws_background)
  1174.                                                         color = bit_enabled ? cv_font_fg_color : cv_font_bg_color;
  1175.                                                 else
  1176.                                                 {
  1177.                                                         if (!bit_enabled)
  1178.                                                                 continue;
  1179.                                                 }
  1180.                                                 gr_pixel(canvas.cv_bitmap, x, y, color);
  1181.                                         }
  1182.                                 }
  1183.                                 }
  1184.                                 x += spacing-width;             //for kerning
  1185.                         }
  1186.                         y++;
  1187.                 }
  1188.         }
  1189.         return 0;
  1190. }
  1191.  
  1192. static int gr_internal_string_clipped_m(grs_canvas &canvas, const grs_font &cv_font, const int x, const int y, const char *const s)
  1193. {
  1194.         return gr_internal_string_clipped_template<true>(canvas, cv_font, x, y, s);
  1195. }
  1196.  
  1197. static int gr_internal_string_clipped(grs_canvas &canvas, const grs_font &cv_font, const int x, const int y, const char *const s)
  1198. {
  1199.         return gr_internal_string_clipped_template<false>(canvas, cv_font, x, y, s);
  1200. }
  1201.  
  1202. }
  1203.