Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:49:55

0001 #pragma once
0002 /**
0003 STTF.h : Truetype Font wrapper providing text annotation within image rasters
0004 ===============================================================================
0005 
0006 Based on:
0007 
0008 * https://github.com/justinmeiners/stb-truetype-example
0009 * https://github.com/nothings/stb
0010 * https://github.com/nothings/stb/blob/master/stb_truetype.h
0011 * http://nothings.org/stb/stb_truetype.h
0012 
0013 
0014 **/
0015 
0016 #include <cstdio>
0017 #include <cstdlib>
0018 #ifdef OPTICKS_SYSRAP
0019 #include "sconfig.h"
0020 #endif
0021 
0022 struct STTF
0023 {
0024     static constexpr const char* KEY = "OPTICKS_STTF_PATH" ;
0025 
0026     static STTF* Create();
0027     static const char* GetFontPath();
0028     static unsigned char* Load(const char* path);
0029 
0030     const    char* fontPath ;
0031     unsigned char* fontBuffer ;
0032     void* font_ ;   // stbtt_fontinfo*   good to keep foreign types out of definition when easy to do so
0033     bool  valid ;
0034 
0035     STTF() ;
0036     virtual ~STTF();
0037 
0038     void init();
0039     int  render_background( unsigned char* bitmap, int channels, int width, int height,      int* color );
0040     int  render_text(       unsigned char* bitmap, int channels, int width, int line_height, const char* text );
0041 
0042     int   annotate(          unsigned char* bitmap, int channels, int width, int height, int line_height, const char* text, bool bottom );
0043 
0044 };
0045 
0046 
0047 
0048 
0049 #ifdef __clang__
0050 
0051 
0052 #elif defined(__GNUC__) || defined(__GNUG__)
0053 
0054 #pragma GCC diagnostic push
0055 #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
0056 #pragma GCC diagnostic ignored "-Wsign-compare"
0057 
0058 #elif defined(_MSC_VER)
0059 
0060 #endif
0061 
0062 
0063 #ifdef STTF_IMPLEMENTATION
0064 
0065 #define STB_TRUETYPE_IMPLEMENTATION
0066 #include "stb_truetype.h"
0067 
0068 #endif
0069 
0070 #ifdef __clang__
0071 #elif defined(__GNUC__) || defined(__GNUG__)
0072 
0073 #pragma GCC diagnostic pop
0074 
0075 #elif defined(_MSC_VER)
0076 #endif
0077 
0078 
0079 
0080 
0081 inline STTF* STTF::Create() // static
0082 {
0083     STTF* ttf = new STTF ;
0084     if(!ttf->valid)
0085     {
0086         printf("STTF::Create : failed to initialize font \n");
0087         return nullptr ;
0088     }
0089     return ttf ;
0090 }
0091 
0092 
0093 
0094 inline const char* STTF::GetFontPath() // static
0095 {
0096 #ifdef OPTICKS_SYSRAP
0097     const char* dpath = sconfig::DefaultSTTFPath() ;
0098 #else
0099     const char* dpath = nullptr ;
0100 #endif
0101     const char* epath = getenv(KEY) ;
0102     //printf("STTF::GetFontPath dpath %s epath %s \n", ( dpath ? dpath : "" ), ( epath ? epath : "" ) );
0103     return epath ? epath : dpath ;
0104 }
0105 
0106 inline unsigned char* STTF::Load(const char* path) // static
0107 {
0108     if(path == nullptr)
0109     {
0110         printf("STTF::Load : Envvar %s with path to ttf font file, eg Cousine-Regular.ttf, is required \n", KEY);
0111         return nullptr ;
0112     }
0113 
0114 #ifdef DEBUG
0115     printf("STTF::Load font from %s\n", path );
0116 #endif
0117 
0118     long size ;
0119     FILE* fontFile = fopen(path, "rb");
0120 
0121     if( fontFile == nullptr )
0122     {
0123         printf("STTF::Load failed to open %s\n", path);
0124         return nullptr ;
0125     }
0126 
0127     fseek(fontFile, 0, SEEK_END);
0128     size = ftell(fontFile); /* how long is the file ? */
0129     fseek(fontFile, 0, SEEK_SET); /* reset */
0130 
0131     unsigned char* buffer = (unsigned char*)malloc(size);
0132 
0133     fread(buffer, size, 1, fontFile);
0134     fclose(fontFile);
0135 
0136     return buffer ;
0137 }
0138 
0139 
0140 
0141 inline STTF::STTF()
0142     :
0143     fontPath(GetFontPath()),
0144     fontBuffer(Load(fontPath)),
0145     font_(nullptr),
0146     valid(false)
0147 {
0148     init();
0149 }
0150 
0151 
0152 
0153 inline void STTF::init()
0154 {
0155     if(fontBuffer == nullptr)
0156     {
0157         printf("STTF::init failed : no font file has been loaded \n");
0158         return ;
0159     }
0160 
0161     stbtt_fontinfo* font = new stbtt_fontinfo ;
0162     if (!stbtt_InitFont(font, fontBuffer, 0))
0163     {
0164         printf("STTF::init failed : loaded font file is not a valid TTF font ? \n");
0165         return ;
0166     }
0167 
0168     font_ = (void*)font ;
0169     valid = true ;
0170 }
0171 
0172 
0173 inline STTF::~STTF()
0174 {
0175     stbtt_fontinfo* font = (stbtt_fontinfo*)font_ ;
0176     delete font ;
0177     free(fontBuffer);
0178 }
0179 
0180 
0181 inline int STTF::render_background( unsigned char* bitmap, int channels, int width, int line_height, int* color )
0182 {
0183     for(int y=0 ; y < line_height ; y++ )
0184     {
0185         for(int x=0 ; x < width ; x++)
0186         {
0187             for(int c = 0 ; c < channels ; c++ )
0188             {
0189                 bitmap[ (y*width + x)*channels + c] = color[c] ;
0190             }
0191         }
0192     }
0193     return 0 ;
0194 }
0195 
0196 inline int STTF::render_text( unsigned char* bitmap, int channels, int width, int line_height, const char* text )
0197 {
0198     if(!valid) return 1 ;
0199     stbtt_fontinfo* font = (stbtt_fontinfo*)font_ ;
0200 
0201 #ifdef DEBUG
0202     printf("STTF::render_text channels %d \n", channels );
0203 #endif
0204 
0205     float pixels = float(line_height);
0206     float scale = stbtt_ScaleForPixelHeight(font, pixels);
0207     // computes a scale factor to produce a font whose "height" is 'pixels' tall.
0208     // Height is measured as the distance from the highest ascender to the lowest
0209     // descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics
0210     // and computing:
0211     //       scale = pixels / (ascent - descent)
0212     // so if you prefer to measure height by the ascent only, use a similar calculation.
0213 
0214 
0215     int ascent, descent, lineGap;
0216     stbtt_GetFontVMetrics(font, &ascent, &descent, &lineGap);
0217     // ascent: coordinate above the baseline the font extends;
0218     // descent: coordinate below the baseline the font extends (i.e. it is typically negative)
0219     // lineGap: spacing between one row's descent and the next row's ascent...
0220     // so you should advance the vertical position by "*ascent - *descent + *lineGap"
0221     // these are expressed in unscaled coordinates, so you must multiply by
0222     // the scale factor for a given size
0223 
0224     ascent = roundf(ascent * scale);
0225     descent = roundf(descent * scale);
0226 
0227     int x = 0;
0228 
0229     // terminating with "x+pixels < width"  prevents overlong text wrapping around ontop of itself
0230     while(*text && x + pixels < width)
0231     {
0232         int codepoint = *text ;
0233 
0234         int ax;  // advanceWidth is the offset from the current horizontal position to the next horizontal position
0235         int lsb; // leftSideBearing is the offset from the current horizontal position to the left edge of the character
0236         stbtt_GetCodepointHMetrics(font, codepoint, &ax, &lsb);   // expressed in unscaled coordinates
0237         ax  = roundf(ax*scale) ;
0238         lsb = roundf(lsb*scale) ;
0239 
0240 
0241         int ix0,  iy0,  ix1,  iy1;
0242         stbtt_GetCodepointBitmapBox(font, codepoint, scale, scale, &ix0, &iy0, &ix1, &iy1);
0243         // get the bbox of the bitmap centered around the glyph origin; so the
0244         // bitmap width is ix1-ix0, height is iy1-iy0, and location to place
0245         // the bitmap top left is (leftSideBearing*scale,iy0).
0246         // (Note that the bitmap uses y-increases-down, but the shape uses
0247         // y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)
0248 
0249         int y = ascent + iy0 ;
0250         int offset = x + lsb + (y * width);
0251 
0252         unsigned char* output = bitmap + offset*channels ;
0253         int out_w = (ix1-ix0)*channels ;
0254         int out_h = (iy1-iy0) ;   // <-- when multiply by channels get black boxes at y positions below the text
0255         int out_stride = width*channels ;
0256 
0257         float scale_x = scale*channels ;    // adhoc ? why is this needed
0258         float scale_y = scale ;
0259 
0260         stbtt_MakeCodepointBitmap(font, output, out_w, out_h, out_stride, scale_x, scale_y, codepoint );
0261         // the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap
0262         // in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap
0263         // is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the
0264         // width and height and positioning info for it first.
0265 
0266         int kern;
0267         kern = stbtt_GetCodepointKernAdvance(font, *text, *(text+1));
0268         kern = roundf(kern * scale) ;
0269 
0270         x += ax + kern ;
0271 
0272         //printf("[%c]%d\n", *text, x );
0273         text++ ;
0274     }
0275     return 0 ;
0276 }
0277 
0278 
0279 inline int STTF::annotate( unsigned char* bitmap, int channels, int width, int height, int line_height, const char* text, bool bottom )
0280 {
0281     int rc = 1 ;
0282     if(!valid) return rc ;
0283 
0284 
0285     // black band with text annotation at base of image
0286     int black[4] = {0,0,0,0} ;   // any color, so long as its black
0287 
0288     int margin_bkg = 0 ;
0289     int margin_txt = 1 ;
0290 
0291     int offset_bkg = bottom ? width*(height-line_height-margin_bkg)*channels : 0 ;
0292     int offset_txt = bottom ? width*(height-line_height-margin_txt)*channels : 0 ;
0293 
0294     rc = render_background( bitmap+offset_bkg, channels, width, line_height, black ) ;
0295     rc = render_text(       bitmap+offset_txt, channels, width, line_height, text  ) ;
0296     return rc ; 
0297 }
0298 
0299 
0300