TextDrawer.cpp 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. #include "TextDrawer.h"
  2. /*
  3. FinalSun/FinalAlert 2 Mission Editor
  4. Copyright (C) 1999-2024 Electronic Arts, Inc.
  5. Authored by Matthias Wagner
  6. This program is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. */
  17. #include "stdafx.h"
  18. #include "TextDrawer.h"
  19. #include <afxwin.h>
  20. #include "Vec2.h"
  21. #include "MissionEditorPackLib.h"
  22. TextDrawer::TextDrawer(IDirectDraw4* pDirectDraw, int fontSizeInPoints, COLORREF col, COLORREF shadowCol): m_fontSizeInPoints(fontSizeInPoints), m_col(col), m_shadowCol(shadowCol)
  23. {
  24. auto dc = CDC::FromHandle(::GetDC(NULL));
  25. auto fontSizeInPixels = -MulDiv(fontSizeInPoints, dc->GetDeviceCaps(LOGPIXELSY), 72);
  26. m_fontSizeInPixels = fontSizeInPixels;
  27. CFont f;
  28. f.CreateFont(fontSizeInPixels, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, NONANTIALIASED_QUALITY, VARIABLE_PITCH, "COURIER NEW");
  29. // Build a string that contains all required characters in order
  30. std::string s;
  31. for (char c = 32; c <= 126; ++c)
  32. s.push_back(c);
  33. // get the extent in pixels of all characters
  34. dc->SelectObject(f);
  35. const auto extent = dc->GetTextExtent(s.c_str(), s.size());
  36. // Now create the DirectDraw surface
  37. DDSURFACEDESC2 desc = { 0 };
  38. desc.dwSize = sizeof(desc);
  39. desc.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
  40. desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
  41. desc.dwWidth = extent.cx;
  42. desc.dwHeight = extent.cy * 2;
  43. m_charExtent.set(extent.cx / s.size(), extent.cy);
  44. auto bkcol = col == RGB(10, 10, 10) ? RGB(11, 11, 11) : RGB(10, 10, 10);
  45. auto pSurface = CComPtr<IDirectDrawSurface4>();
  46. if (pDirectDraw->CreateSurface(&desc, &pSurface, nullptr) != DD_OK)
  47. return;
  48. desc.dwFlags |= DDSD_PIXELFORMAT;
  49. pSurface->GetSurfaceDesc(&desc);
  50. if (pSurface->Lock(NULL, &desc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT | DDLOCK_NOSYSLOCK, NULL) == DD_OK)
  51. {
  52. FSunPackLib::ColorConverter c(desc.ddpfPixelFormat);
  53. std::int32_t backcolor = c.GetColor(bkcol);
  54. auto bytes_per_pixel = (desc.ddpfPixelFormat.dwRGBBitCount + 7) / 8;
  55. BYTE* const pImage = static_cast<BYTE*>(desc.lpSurface);
  56. for (int i=0; i < desc.dwWidth; ++i)
  57. {
  58. for (int e = 0; e < desc.dwHeight; ++e)
  59. {
  60. memcpy(&pImage[e * desc.lPitch + i * bytes_per_pixel], &backcolor, bytes_per_pixel);
  61. }
  62. }
  63. pSurface->Unlock(NULL);
  64. }
  65. HDC hDC;
  66. if (pSurface->GetDC(&hDC) != DD_OK)
  67. return;
  68. // Draw the string with all characters onto the surface
  69. SelectObject(hDC, f);
  70. SetBkMode(hDC, TRANSPARENT);
  71. if (shadowCol != CLR_INVALID)
  72. {
  73. SetTextColor(hDC, shadowCol);
  74. if (!TextOutA(hDC, 0, extent.cy, s.c_str(), s.size()))
  75. return;
  76. }
  77. SetTextColor(hDC, col);
  78. if (!TextOutA(hDC, 0, 0, s.c_str(), s.size()))
  79. return;
  80. if (pSurface->ReleaseDC(hDC) != DD_OK)
  81. return;
  82. // set transparency key to top left
  83. FSunPackLib::SetColorKey(pSurface, CLR_INVALID);
  84. // Everything fine, pass ownership of surface to m_fontSurface
  85. m_fontSurface.Attach(pSurface.Detach());
  86. }
  87. bool TextDrawer::isValid() const
  88. {
  89. return m_fontSurface != nullptr;
  90. }
  91. void TextDrawer::RenderText(IDirectDrawSurface4* target, int x, int y, const std::string& text, bool centered) const
  92. {
  93. if (!isValid())
  94. return;
  95. auto shadowOffset = 1 + m_fontSizeInPixels / 32;
  96. const int lineOffset = m_charExtent.y / 4;
  97. ProjectedVec cur(x, y);
  98. const int cw = m_charExtent.x;
  99. const int ch = m_charExtent.y;
  100. if (centered)
  101. {
  102. cur -= GetExtent(text) / 2;
  103. }
  104. for (const auto c: text)
  105. {
  106. if (c == '\n')
  107. {
  108. cur.set(x, cur.y + ch + lineOffset);
  109. }
  110. else if (c >= 32 && c <= 126)
  111. {
  112. auto i = c - 32;
  113. if (m_shadowCol != CLR_INVALID)
  114. {
  115. RECT s_shadow{ i * cw, ch, i * cw + cw, ch + ch };
  116. target->BltFast(cur.x + 0 * shadowOffset, cur.y + 1 * shadowOffset, m_fontSurface, &s_shadow, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);
  117. target->BltFast(cur.x + 0 * shadowOffset, cur.y - 1 * shadowOffset, m_fontSurface, &s_shadow, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);
  118. target->BltFast(cur.x + 1 * shadowOffset, cur.y + 0 * shadowOffset, m_fontSurface, &s_shadow, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);
  119. target->BltFast(cur.x - 1 * shadowOffset, cur.y + 0 * shadowOffset, m_fontSurface, &s_shadow, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);
  120. target->BltFast(cur.x + 1 * shadowOffset, cur.y + 1 * shadowOffset, m_fontSurface, &s_shadow, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);
  121. target->BltFast(cur.x - 1 * shadowOffset, cur.y + 1 * shadowOffset, m_fontSurface, &s_shadow, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);
  122. target->BltFast(cur.x + 1 * shadowOffset, cur.y - 1 * shadowOffset, m_fontSurface, &s_shadow, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);
  123. target->BltFast(cur.x - 1 * shadowOffset, cur.y - 1 * shadowOffset, m_fontSurface, &s_shadow, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);
  124. }
  125. RECT s{ i * cw, 0, i * cw + cw, ch };
  126. target->BltFast(cur.x, cur.y, m_fontSurface, &s, DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);
  127. cur.x += cw;
  128. }
  129. }
  130. }
  131. ProjectedVec TextDrawer::GetExtent(const std::string& text) const
  132. {
  133. ProjectedVec cur(0, 0);
  134. const int lineOffset = m_charExtent.y / 4;
  135. const int cw = m_charExtent.x;
  136. const int ch = m_charExtent.y;
  137. ProjectedVec maxpos(0, 0);
  138. for (const auto c : text)
  139. {
  140. if (c == '\n')
  141. {
  142. cur.set(0, cur.y + ch + lineOffset);
  143. }
  144. else if (c >= 32 && c <= 126)
  145. {
  146. cur.x += cw;
  147. maxpos.set(max(maxpos.x, cur.x), max(maxpos.y, cur.y + ch));
  148. }
  149. }
  150. return maxpos;
  151. }