My Project
TelemetryOverlay.h
1 // Copyright 2023 DreamWorks Animation LLC
2 // SPDX-License-Identifier: Apache-2.0
3 #pragma once
4 
5 #include <scene_rdl2/common/grid_util/Parser.h>
6 #include <scene_rdl2/common/math/BBox.h>
7 #include <scene_rdl2/common/math/Math.h>
8 
9 #include <algorithm> // clamp
10 #include <cctype> // isspace
11 #include <deque>
12 #include <memory>
13 #include <string>
14 #include <unordered_map>
15 #include <vector>
16 
17 #include <freetype2/ft2build.h>
18 #include FT_FREETYPE_H
19 
20 namespace mcrt_dataio {
21 namespace telemetry {
22 
23 using FreeTypeBBox = scene_rdl2::math::BBox<FT_Vector>;
24 
25 class C3
26 //
27 // 8bit RGB Color definition for TelemetryOverlay
28 //
29 {
30 public:
31  C3() = default;
32  C3(unsigned char r, unsigned char g, unsigned char b) : mR(r), mG(g), mB(b) {}
33  C3(const C3& c3) : mR(c3.mR), mG(c3.mG), mB(c3.mB) {}
34 
35  bool isBlack() const { return (mR == 0x0 && mG == 0x0 && mB == 0x0); }
36 
37  unsigned char mR {0};
38  unsigned char mG {0};
39  unsigned char mB {0};
40 };
41 
42 //------------------------------------------------------------------------------------------
43 
45 //
46 // Font bit mask information cache data. We want to avoid multiple times same font bitmap conversion.
47 //
48 {
49 public:
50  FontCacheItem(const char c,
51  const FT_Bitmap& bitmap, unsigned bitmapLeft, unsigned bitmapTop, unsigned advanceX);
52 
53  unsigned char get(int bx, int by) const { return mBuffer[by * mPitch + bx]; }
54 
55  bool isSpace() const { return std::isspace(static_cast<unsigned char>(mC)); }
56 
57  unsigned getRows() const { return mRows; }
58  unsigned getWidth() const { return mWidth; }
59  unsigned getBitmapLeft() const { return mBitmapLeft; }
60  unsigned getBitmapTop() const { return mBitmapTop; }
61  unsigned getAdvanceX() const { return mAdvanceX; }
62 
63 private:
64  char mC {0x0};
65 
66  // FreeType coordinate : Fixed point position value and Y flip
67  // -----> +X
68  // | +<------------------->+ mAdvanceX
69  // | +<------>+ mPitch, mWidth
70  // V +<>+ mBitmapLeft
71  // +Y m +.../..........+ +..............+
72  // B + + : +--------+ : : :
73  // i ^ ^ : |* *| : : :
74  // t | m | : | * * | : : :
75  // m | R | : | * * | : : next :
76  // a | o | : | ** | : : Font :
77  // p | w | : | * | : : :
78  // T | s V : | * | : : :
79  // o V + : +-*---\--+ : : :
80  // p + +.........\....+ +..............+
81  // \ \--- Fontcacheitem
82  // \--- OverlayCharItem : (mFontBasePos.x, mFontBasePos.y)
83  //
84  unsigned mRows {0};
85  unsigned mWidth {0};
86  unsigned mPitch {0};
87  unsigned mBitmapLeft {0};
88  unsigned mBitmapTop {0};
89  unsigned mAdvanceX {0};
90 
91  std::vector<unsigned char> mBuffer;
92 };
93 
94 //------------------------------------------------------------------------------------------
95 
96 class Font
97 //
98 // Single Font data information for FreeType
99 //
100 {
101 public:
102  Font(const std::string& fontTTFFileName, int fontSizePoint)
103  : mFontTTFFileName(fontTTFFileName)
104  , mFontSizePoint(fontSizePoint)
105  {
106  setupFontFace();
107  }
108 
109  const std::string& getFontTTFFileName() const { return mFontTTFFileName; }
110 
111  int getFontSizePoint() const { return mFontSizePoint; }
112  const FT_Face& getFace() const { return mFace; }
113 
114  std::shared_ptr<FontCacheItem> getFontCacheItem(char c);
115 
116  float getBgYAdjustScale() const { return mBgYAdjustScale; }
117 
118  // fixedPoint FreeType coordinate value conversion from/to int
119  static FT_Pos iToftPos(const int v) { return static_cast<FT_Pos>(v * 64); }
120  static int ftPosToi(const FT_Pos v) { return static_cast<int>(v) / 64; }
121 
122 private:
123  void setupFontFace(); // throw scene_rdl2::except::RuntimeError
124 
125  //------------------------------
126 
127  const std::string mFontTTFFileName;
128  int mFontSizePoint {0};
129 
130  FT_Library mFtLibrary;
131  FT_Face mFace;
132 
133  float mBgYAdjustScale {0.0f};
134 
135  std::unordered_map<unsigned, std::shared_ptr<FontCacheItem>> mFontCacheMap;
136 };
137 
138 //------------------------------------------------------------------------------------------
139 
141 //
142 // Single char data for overlay draw action. We do drawing logic in 2 phases. 1st phase creates data sets
143 // of all draw char data (OverlayCharItem) and 2nd phase rasterize all of them and alpha blend.
144 //
145 {
146 public:
147  OverlayCharItem() = default;
148 
149  void set(std::shared_ptr<FontCacheItem> fontCacheItem,
150  const FT_Vector& fontPos,
151  unsigned fontHeight,
152  float bgYAdjustScale,
153  const C3& fgC3,
154  const C3& bgC3)
155  {
156  mFontCacheItem = fontCacheItem;
157 
158  mFontBasePos = fontPos;
159  mFontSize = FT_Vector{fontCacheItem->getAdvanceX(), Font::iToftPos(fontHeight)};
160  mFontDataPos = FT_Vector {mFontBasePos.x + Font::iToftPos(fontCacheItem->getBitmapLeft()),
161  mFontBasePos.y - Font::iToftPos(fontCacheItem->getBitmapTop())};
162  mBgYAdjustScale = bgYAdjustScale;
163  mFgC3 = fgC3;
164  mBgC3 = bgC3;
165  }
166 
167  unsigned getAdvanceX() const { return mFontCacheItem->getAdvanceX(); }
168 
169  std::shared_ptr<FontCacheItem> getFontCacheItem() const { return mFontCacheItem; }
170 
171  unsigned getBaseX() const { return Font::ftPosToi(mFontBasePos.x); }
172  unsigned getBaseY() const { return Font::ftPosToi(mFontBasePos.y); }
173  unsigned getWidth() const {
174  return static_cast<unsigned>(Font::ftPosToi(mFontBasePos.x + mFontSize.x) - getBaseX());
175  }
176  unsigned getHeight() const {
177  return static_cast<unsigned>(Font::ftPosToi(mFontBasePos.y + mFontSize.y) - getBaseY());
178  }
179 
180  unsigned getPosX() const { return Font::ftPosToi(mFontDataPos.x); }
181  unsigned getPosY() const { return Font::ftPosToi(mFontDataPos.y); }
182 
183  unsigned getStepX() const { return Font::ftPosToi(getAdvanceX()); }
184 
185  float getBgYAdjustScale() const { return mBgYAdjustScale; }
186 
187  const C3& getFgC3() const { return mFgC3; }
188  const C3& getBgC3() const { return mBgC3; }
189 
190  FreeTypeBBox getBBox() const;
191 
192  // display framebuffer coord as well if you set non zero winHeight
193  std::string show(unsigned winHeight = 0) const;
194 
195 private:
196  std::shared_ptr<FontCacheItem> mFontCacheItem {nullptr};
197 
198  // FreeType coordinate : Fixed point position value and Y flip
199  // -----> +X
200  // |
201  // | +<------>+ FontCacheItem::getWidth()
202  // V +<------------>+ mFontSize.x
203  // +Y /--- (mFontDataPos.x, mFontDataPos.y) -> getPosX(), getPosY()
204  // +---/----------+ +
205  // | +........+ | ^ +
206  // | :* *: | | ^
207  // | : * * : | | |
208  // | : * * : | |mFontSize.y |
209  // | : ** : | | |FontCacheitem::getRaws()
210  // | : * : | | |
211  // | : * : | | V
212  // | +.*...\..+ | V +
213  // +---------\----+ +
214  // \ \--- FontCacheItem
215  // \--- (mFontBasePos.x, mFontBasePos.y) -> getBaseX(), getBaseY()
216  //
217  FT_Vector mFontBasePos; // char position x y (FreeType coordinate value)
218  FT_Vector mFontSize; // char width and height (FreeType coordinate value)
219  FT_Vector mFontDataPos; // font data position, (FreeType coordinate value)
220 
221  float mBgYAdjustScale {0.0f};
222 
223  C3 mFgC3 {0, 0, 0}; // foreground (font) color 0 ~ 255
224  C3 mBgC3 {0, 0, 0}; // background color 0 ~ 255
225 };
226 
227 class Overlay;
228 
230 //
231 // Single strings that include multiple char items.
232 // All char items are organized as multiple string item data.
233 //
234 {
235 public:
236  OverlayStrItem() = default;
237 
238  void resetCharItemArray(Overlay& overlay);
239 
240  void set(Overlay& overlay,
241  Font& font,
242  const unsigned startX, const unsigned startY, const unsigned overlayHeight,
243  const std::string& str, const C3& c3);
244 
245  void setupAllCharItems(std::vector<std::shared_ptr<OverlayCharItem>>& outCharItemArray);
246 
247  unsigned getFirstCharStepX() const; // return first char's stepX
248 
249  FreeTypeBBox getBBox() const;
250 
251 private:
252 
253  bool entryNewCharItem(Overlay& overlay,
254  Font& font,
255  const FT_Vector& fontPos,
256  char c,
257  const C3& fgC3,
258  const C3& bgC3);
259 
260  //------------------------------
261 
262  std::string mStr;
263  unsigned mStartX {0};
264  unsigned mStartY {0};
265  unsigned mOverlayHeight {0};
266 
267  std::vector<std::shared_ptr<OverlayCharItem>> mCharItemArray;
268 };
269 
271 {
272 public:
273  using BBox2i = scene_rdl2::math::BBox2i;
274 
275  OverlayBoxItem() = default;
276 
277  void set(const BBox2i& bbox, const C3& c, unsigned char alpha)
278  {
279  mBBox = bbox;
280  mC = c;
281  mAlpha = alpha;
282  }
283 
284  const BBox2i& getBBox() const { return mBBox; }
285  const C3& getC() const { return mC; }
286  unsigned char getAlpha() const { return mAlpha; }
287 
288 private:
289  BBox2i mBBox {scene_rdl2::math::Vec2i {0, 0}, scene_rdl2::math::Vec2i {0, 0}};
290  C3 mC {0, 0, 0};
291  unsigned char mAlpha {0};
292 };
293 
294 class Overlay
295 {
296 public:
297  using Arg = scene_rdl2::grid_util::Arg;
298  using Parser = scene_rdl2::grid_util::Parser;
299  using BBox2i = scene_rdl2::math::BBox2i;
300 
301  enum class Align {
302  SMALL, // left or bottom
303  MIDDLE, // center
304  BIG // right or top
305  };
306 
307  Overlay() { parserConfigure(); }
308  Overlay(const unsigned width, const unsigned height)
309  {
310  resize(width, height);
311  clear({0, 0, 0}, 0, true);
312 
313  parserConfigure();
314  }
315 
316  inline void resize(const unsigned width, const unsigned height); // no clear internally just change size
317  void clear(const C3& c3, const unsigned char alpha, bool doParallel);
318  void boxFill(const C3& c3, const unsigned char alpha, const BBox2i& bbox, bool doParallel);
319  void fill(unsigned char r, unsigned char g, unsigned char b, unsigned char a); // for debug
320 
321  unsigned getWidth() const { return mWidth; }
322  unsigned getHeight() const { return mHeight; }
323 
324  unsigned getMaxYLines(const Font& font, unsigned& offsetBottomPixY, unsigned& stepPixY) const;
325 
326  std::shared_ptr<OverlayCharItem> getNewOverlayCharItem() { return getMemOverlayCharItem(); }
327  void restoreOverlayCharItemMem(std::shared_ptr<OverlayCharItem> ptr) { setMemOverlayCharItem(ptr); }
328 
329  void drawStrClear();
330  bool drawStr(Font& font,
331  const unsigned startX,
332  const unsigned startY,
333  const std::string& str,
334  const C3& c3,
335  std::string& error);
336  void drawStrFlush(bool doParallel);
337 
338  void drawBoxClear();
339  void drawBox(const BBox2i box, const C3& c3, const unsigned char alpha);
340  void drawBoxBar(const BBox2i box, const C3& c3, const unsigned char alpha);
341  void drawBoxFlush(bool doParallel);
342 
343  // available after first call of drawStr. (only works properly monospace font)
344  unsigned getFontStepX() const { return mFontStepX; }
345 
346  size_t getDrawStrItemTotal() const { return mDrawStrArray.size(); }
347  BBox2i calcDrawBbox(const size_t startStrItemId, const size_t endStrItemId) const;
348 
349  void finalizeRgb888(std::vector<unsigned char>& rgbFrame,
350  unsigned frameWidth,
351  unsigned frameHeight,
352  bool top2bottomFlag,
353  Align hAlign,
354  Align vAlign,
355  std::vector<unsigned char>* bgArchive,
356  bool doParallel) const;
357 
358  bool savePPM(const std::string& filename) const; // for debugging purposes
359 
360  Parser& getParser() { return mParser; }
361 
362  static size_t msgDisplayLen(const std::string& msg); // skip escape sequence
363  static size_t msgDisplayWidth(const std::string& msg); // skip escape sequence and return max width
364 
365 private:
366  inline unsigned getPixOffset(const unsigned x, const unsigned y) const;
367  inline unsigned getPixDataOffset(const unsigned x, const unsigned y) const;
368  inline unsigned char* getPixDataAddr(const unsigned x, const unsigned y);
369  inline const unsigned char* getPixDataAddr(const unsigned x, const unsigned y) const;
370  inline bool isPixInside(const unsigned x, const unsigned y) const;
371 
372  inline void alphaBlendPixC4(const C3& fgC3, const unsigned char fgAlpha, unsigned char* bgPixC4) const;
373  inline void alphaBlendPixC3(const C3& fgC3, const unsigned char fgAlpha, unsigned char* bgPixC3) const;
374  inline C3 blendCol3(const C3& fgC3, float fgFraction, unsigned char* bgPix) const;
375  inline unsigned char clampCol0255(const float v0255) const;
376  inline void setCol4(const C3& inC3, unsigned char inAlpha, unsigned char* outPix) const;
377  inline void setCol3(const C3& inC3, unsigned char* outPix) const;
378 
379  void overlayDrawFontCache(std::shared_ptr<OverlayCharItem> charItem);
380 
381  inline void resizeRgb888(std::vector<unsigned char>& rgbFrame, unsigned width, unsigned height) const;
382  inline void clearRgb888(std::vector<unsigned char>& rgbFrame) const;
383  void copyRgb888(const std::vector<unsigned char>& in,
384  std::vector<unsigned char>& out,
385  const bool doParallel) const;
386 
387  void bakeOverlayMainRgb888(const std::vector<unsigned char>& fgFrameRGBA,
388  const unsigned fgWidth,
389  const unsigned fgHeight,
390  const Align hAlign,
391  const Align vAlign,
392  std::vector<unsigned char>& bgFrameRGB,
393  const unsigned bgWidth,
394  const unsigned bgHeight,
395  const bool top2bottomFlag,
396  const bool doParallel) const;
397 
398  std::string showPixFrameRGBA(const std::vector<unsigned char>& frameRGBA,
399  const unsigned frameWidth,
400  const unsigned frameHeight,
401  const unsigned pixX,
402  const unsigned pixY) const; // for debug
403 
404  std::shared_ptr<OverlayStrItem> getMemOverlayStrItem();
405  void setMemOverlayStrItem(std::shared_ptr<OverlayStrItem> ptr);
406  std::shared_ptr<OverlayCharItem> getMemOverlayCharItem();
407  void setMemOverlayCharItem(std::shared_ptr<OverlayCharItem> ptr);
408  std::shared_ptr<OverlayBoxItem> getMemOverlayBoxItem();
409  void setMemOverlayBoxItem(std::shared_ptr<OverlayBoxItem> ptr);
410 
411  void parserConfigure();
412  std::string showMemPoolSize() const;
413 
414  //------------------------------
415 
416  std::vector<std::shared_ptr<OverlayStrItem>> mDrawStrArray;
417  std::vector<std::shared_ptr<OverlayBoxItem>> mDrawBoxArray;
418  std::vector<std::shared_ptr<OverlayBoxItem>> mDrawBoxBarArray;
419 
420  unsigned mMaxOverlayStrItemMemPool {0};
421  unsigned mMaxOverlayCharItemMemPool {0};
422  unsigned mMaxOverlayBoxItemMemPool {0};
423  std::deque<std::shared_ptr<OverlayStrItem>> mOverlayStrItemMemPool;
424  std::deque<std::shared_ptr<OverlayCharItem>> mOverlayCharItemMemPool;
425  std::deque<std::shared_ptr<OverlayBoxItem>> mOverlayBoxItemMemPool;
426 
427  unsigned mFontStepX {0};
428 
429  unsigned mWidth {0};
430  unsigned mHeight {0};
431  std::vector<unsigned char> mPixelsRGBA;
432 
433  Parser mParser;
434 };
435 
436 inline void
437 Overlay::resize(const unsigned width, const unsigned height)
438 {
439  if (mWidth == width && mHeight == height) return; // no need to resize
440 
441  mWidth = width;
442  mHeight = height;
443 
444  mPixelsRGBA.resize(mWidth * mHeight * 4);
445 }
446 
447 //------------------------------
448 
449 inline unsigned
450 Overlay::getPixOffset(const unsigned x, const unsigned y) const
451 {
452  return y * mWidth + x;
453 }
454 
455 inline unsigned
456 Overlay::getPixDataOffset(const unsigned x, const unsigned y) const
457 {
458  return getPixOffset(x, y) * 4;
459 }
460 
461 inline unsigned char*
462 Overlay::getPixDataAddr(const unsigned x, const unsigned y)
463 {
464  return &mPixelsRGBA[getPixDataOffset(x, y)];
465 }
466 
467 inline const unsigned char*
468 Overlay::getPixDataAddr(const unsigned x, const unsigned y) const
469 {
470  return &mPixelsRGBA[getPixDataOffset(x, y)];
471 }
472 
473 inline bool
474 Overlay::isPixInside(const unsigned x, const unsigned y) const
475 {
476  return (x < mWidth && y < mHeight);
477 }
478 
479 //------------------------------
480 
481 inline void
482 Overlay::alphaBlendPixC4(const C3& fgC3, const unsigned char fgAlpha, unsigned char* bgPixC4) const
483 //
484 // This is a full alpha blending (fg over bg) operation.
485 //
486 {
487  float currFgA = static_cast<float>(fgAlpha) / 255.0f;
488  float currBgA = static_cast<float>(bgPixC4[3]) / 255.0f;
489  float currOutA = currFgA + currBgA * (1.0f - currFgA);
490  if (currOutA == 0.0f) {
491  setCol4({0, 0, 0}, 0, bgPixC4);
492  } else {
493  auto calcC = [&](const unsigned char fgC, const float fgA,
494  const unsigned char bgC, const float bgA,
495  const float outA) {
496  float outC = (static_cast<float>(fgC) * fgA + static_cast<float>(bgC) * bgA * (1.0f - fgA)) / outA;
497  return static_cast<unsigned char>(clampCol0255(outC));
498  };
499 
500  setCol4({calcC(fgC3.mR, currFgA, bgPixC4[0], currBgA, currOutA),
501  calcC(fgC3.mG, currFgA, bgPixC4[1], currBgA, currOutA),
502  calcC(fgC3.mB, currFgA, bgPixC4[2], currBgA, currOutA)},
503  static_cast<unsigned char>(clampCol0255(currOutA * 255.0f)),
504  bgPixC4);
505  }
506 }
507 
508 inline void
509 Overlay::alphaBlendPixC3(const C3& fgC3, const unsigned char fgAlpha, unsigned char* bgPixC3) const
510 //
511 // We assume bg alpha is 1.0f
512 //
513 {
514  setCol3(blendCol3(fgC3, static_cast<float>(fgAlpha) / 255.0f, bgPixC3),
515  bgPixC3);
516 }
517 
518 inline C3
519 Overlay::blendCol3(const C3& fgC3, float fgFraction, unsigned char* bgPix) const
520 {
521  auto blendCol = [&](const unsigned char fg, const unsigned char bg) {
522  return clampCol0255(static_cast<float>(fg) * fgFraction +
523  static_cast<float>(bg) * (1.0f - fgFraction));
524  };
525  return C3(blendCol(fgC3.mR, bgPix[0]),
526  blendCol(fgC3.mG, bgPix[1]),
527  blendCol(fgC3.mB, bgPix[2]));
528 }
529 
530 inline unsigned char
531 Overlay::clampCol0255(const float v0255) const
532 {
533  return static_cast<unsigned char>(scene_rdl2::math::clamp(v0255, 0.0f, 255.0f));
534 }
535 
536 inline void
537 Overlay::setCol4(const C3& inC3, unsigned char inAlpha, unsigned char* outPix) const
538 {
539  setCol3(inC3, outPix);
540  outPix[3] = inAlpha;
541 }
542 
543 inline void
544 Overlay::setCol3(const C3& inC3, unsigned char* outPix) const
545 {
546  outPix[0] = inC3.mR;
547  outPix[1] = inC3.mG;
548  outPix[2] = inC3.mB;
549 }
550 
551 //------------------------------
552 
553 inline void
554 Overlay::resizeRgb888(std::vector<unsigned char>& rgbFrame, unsigned width, unsigned height) const
555 {
556  unsigned dataSize = width * height * 3;
557  if (rgbFrame.size() != dataSize) {
558  rgbFrame.resize(dataSize);
559  clearRgb888(rgbFrame);
560  }
561 }
562 
563 inline void
564 Overlay::clearRgb888(std::vector<unsigned char>& rgbFrame) const
565 {
566  for (size_t i = 0; i < rgbFrame.size(); ++i) rgbFrame[i] = 0x0;
567 }
568 
569 } // namespace telemetry
570 } // namespace mcrt_dataio
Definition: TelemetryOverlay.h:140
Definition: TelemetryOverlay.h:294
Definition: TelemetryOverlay.h:25
Definition: TelemetryOverlay.h:270
Definition: TelemetryOverlay.h:44
Definition: TelemetryOverlay.h:96
Definition: ClientReceiverConsoleDriver.cc:9
Definition: TelemetryOverlay.h:229