1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     lyt_TextBox.cpp
4 
5   Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Revision: 25594 $
14  *---------------------------------------------------------------------------*/
15 
16 #include "precompiled.h"
17 
18 #include <nw/lyt/lyt_DrawInfo.h>
19 #include <nw/lyt/lyt_GraphicsResource.h>
20 #include <nw/lyt/lyt_Layout.h>
21 #include <nw/lyt/lyt_TextBox.h>
22 #include <nw/lyt/lyt_Material.h>
23 #include <nw/lyt/lyt_Animation.h>
24 #include <nw/lyt/lyt_Common.h>
25 #include <nw/lyt/lyt_ResourceAccessor.h>
26 #include <nw/lyt/lyt_Stopwatch.h>
27 #include <nw/font/font_DispStringBuffer.h>
28 
29 namespace nw
30 {
31 namespace lyt
32 {
33 namespace internal
34 {
35 namespace
36 {
37 
38 inline u8
ClampColor(s16 colVal)39 ClampColor(s16 colVal)
40 {
41     return u8(colVal < 0 ? 0: (colVal > 255 ? 255: colVal));
42 }
43 
44 /*!--------------------------------------------------------------------------*
45   @brief        基点が中央であるときのカーソル位置の値を求めます。
46 
47   @param[in]    value   考慮する幅/高さ
48   @param[in]    isCeil  半分の値が浮動小数点にならないように整数値に
49                         切り上げる場合は真。
50 
51   @return       基点が中央であるときのカーソル位置補正の値を返します。
52  *---------------------------------------------------------------------------*/
53 inline
54 f32
AdjustCenterValue(f32 value,bool isCeil)55 AdjustCenterValue(
56     f32 value,
57     bool isCeil
58 )
59 {
60     f32 ret = value / 2;
61     return isCeil ? math::FCeil(ret): ret;
62 }
63 
64 } // namespace nw::lyt::internal::{anonymous}
65 
66 } // namespace nw::lyt::internal
67 
68 using namespace math;
69 
70 NW_UT_RUNTIME_TYPEINFO_DEFINITION(TextBox, TextBox::Base);    // 実行時型情報の実体を定義
71 
TextBox(u16 allocStrLen)72 TextBox::TextBox(u16 allocStrLen)
73 {
74     Init(allocStrLen);
75     InitMaterial();
76 }
77 
TextBox(u16 allocStrLen,const wchar_t * str,const font::Font * pFont)78 TextBox::TextBox(
79     u16 allocStrLen,
80     const wchar_t* str,
81     const font::Font* pFont
82 )
83 {
84     Init(allocStrLen);
85     SetFont(pFont);
86     SetString(str);
87     InitMaterial();
88 }
89 
TextBox(u16 allocStrLen,const wchar_t * str,u16 strLen,const font::Font * pFont)90 TextBox::TextBox(
91     u16 allocStrLen,
92     const wchar_t* str,
93     u16 strLen,
94     const font::Font* pFont
95 )
96 {
97     Init(allocStrLen);
98     SetFont(pFont);
99     SetString(str, 0, strLen);
100     InitMaterial();
101 }
102 
TextBox(const res::TextBox * pBlock,const ResBlockSet & resBlockSet)103 TextBox::TextBox(
104     const res::TextBox* pBlock,
105     const ResBlockSet&  resBlockSet
106 )
107 :   Pane(pBlock)
108 {
109     u16 allocStrBufLen = static_cast<u16>(pBlock->textBufBytes / sizeof(wchar_t));
110     if (allocStrBufLen > 0)
111     {
112         allocStrBufLen -= 1;
113     }
114 
115     Init(allocStrBufLen);
116 
117     for (int i = 0; i < TEXTCOLOR_MAX; ++i)
118     {
119         m_TextColors[i] = pBlock->textCols[i];
120     }
121 
122     m_FontSize = pBlock->fontSize;
123     m_TextPosition = pBlock->textPosition;
124     m_Bits.textAlignment = pBlock->textAlignment;
125     m_CharSpace = pBlock->charSpace;
126     m_LineSpace = pBlock->lineSpace;
127 
128     // Fontの構築
129     NW_NULL_ASSERT(resBlockSet.pFontList);
130     NW_ASSERT(pBlock->fontIdx < resBlockSet.pFontList->fontNum);
131     const res::Font *const fonts = internal::ConvertOffsToPtr<res::Font>(resBlockSet.pFontList, sizeof(*resBlockSet.pFontList));
132     const char *const fontName = internal::ConvertOffsToPtr<char>(fonts, fonts[pBlock->fontIdx].nameStrOffset);
133 
134     m_pFont = resBlockSet.pResAccessor->GetFont(fontName);
135 
136     // 初期文字列のコピー
137     if (pBlock->textStrBytes >= sizeof(wchar_t) && m_TextBuf)
138     {
139         const wchar_t *const pBlockText = internal::ConvertOffsToPtr<wchar_t>(pBlock, pBlock->textStrOffset);
140         const u16 resStrLen = static_cast<u16>(pBlock->textStrBytes / sizeof(wchar_t) - 1);
141         SetString(pBlockText, 0, resStrLen);
142     }
143 
144     // マテリアルの作成
145     {
146         NW_NULL_ASSERT(resBlockSet.pMaterialList);
147         const u32 *const matOffsTbl = internal::ConvertOffsToPtr<u32>(resBlockSet.pMaterialList, sizeof(*resBlockSet.pMaterialList));
148         const res::Material *const pResMaterial = internal::ConvertOffsToPtr<res::Material>(resBlockSet.pMaterialList, matOffsTbl[pBlock->materialIdx]);
149         m_pMaterial = Layout::NewObj<Material>(pResMaterial, resBlockSet);
150     }
151 }
152 
153 void
Init(u16 allocStrLen)154 TextBox::Init(u16 allocStrLen)
155 {
156     m_TextBuf = 0;
157     m_TextBufBytes = 0;
158     m_TextLen = 0;
159     m_pFont = 0;
160     m_FontSize = Size(0, 0);
161     SetTextPositionH(HORIZONTALPOSITION_CENTER);
162     SetTextPositionV(VERTICALPOSITION_CENTER);
163     m_LineSpace = 0;
164     m_CharSpace = 0;
165     m_pTagProcessor = 0;
166     m_pDispStringBuf = 0;
167     std::memset(&m_Bits, 0, sizeof(m_Bits));
168 
169     if (allocStrLen > 0)
170     {
171         AllocStringBuffer(allocStrLen);
172     }
173 }
174 
175 void
InitMaterial()176 TextBox::InitMaterial()
177 {
178     // マテリアルの作成
179     m_pMaterial = Layout::NewObj<Material>();
180     if (m_pMaterial)
181     {
182         m_pMaterial->ReserveMem(0, 0, 0);
183     }
184 }
185 
~TextBox()186 TextBox::~TextBox()
187 {
188     // OSReport("TextBox::~TextBox()\n");
189 
190     if (m_pMaterial && !m_pMaterial->IsUserAllocated())
191     {
192         Layout::DeleteObj(m_pMaterial);
193         m_pMaterial = 0;
194     }
195 
196     FreeStringBuffer();
197 }
198 
199 u8
GetMaterialNum() const200 TextBox::GetMaterialNum() const
201 {
202     return m_pMaterial? 1 : 0;
203 }
204 
205 Material*
GetMaterial(u32 idx) const206 TextBox::GetMaterial(u32 idx) const
207 {
208     NW_WARNING(idx < GetMaterialNum(), "idx >= GetMaterialNum() : %d >= %d", idx, GetMaterialNum());
209 
210     return idx == 0 ? m_pMaterial : 0;
211 }
212 
SetMaterial(Material * pMaterial)213 void TextBox::SetMaterial(Material* pMaterial)
214 {
215     if (m_pMaterial && !m_pMaterial->IsUserAllocated())
216     {
217         Layout::DeleteObj(m_pMaterial);
218     }
219     m_pMaterial = pMaterial;
220 }
221 
222 const ut::Color8
GetVtxColor(u32 idx) const223 TextBox::GetVtxColor(u32 idx) const
224 {
225     NW_ASSERT(idx < VERTEXCOLOR_MAX);
226 
227     return GetTextColor(idx / 2);
228 }
229 
230 void
SetVtxColor(u32 idx,ut::Color8 value)231 TextBox::SetVtxColor(
232     u32         idx,
233     ut::Color8 value
234 )
235 {
236     NW_ASSERT(idx < VERTEXCOLOR_MAX);
237 
238     SetTextColor(idx / 2, value);
239 }
240 
241 u8
GetVtxColorElement(u32 idx) const242 TextBox::GetVtxColorElement(u32 idx) const
243 {
244     NW_ASSERT(idx < ANIMTARGET_VERTEXCOLOR_MAX);
245 
246     return reinterpret_cast<const u8*>(&m_TextColors[idx / (2 * sizeof(ut::Color8))])[idx % sizeof(ut::Color8)];
247 }
248 
249 void
SetVtxColorElement(u32 idx,u8 value)250 TextBox::SetVtxColorElement(u32 idx, u8 value)
251 {
252     NW_ASSERT(idx < ANIMTARGET_VERTEXCOLOR_MAX);
253 
254     u8& elm =
255         reinterpret_cast<u8*>(&m_TextColors[idx / (2 * sizeof(ut::Color8))])[idx % sizeof(ut::Color8)];
256 
257     elm = value;
258 }
259 
260 const ut::Rect
GetTextDrawRect() const261 TextBox::GetTextDrawRect() const
262 {
263     if (m_pFont == NULL)
264     {
265         NW_WARNING(false, "m_pFont is NULL");
266         return ut::Rect();
267     }
268 
269     font::WideTextWriter writer;
270     writer.SetCursor(0, 0);
271     SetFontInfo(&writer);
272 
273     ut::Rect textRect;
274     writer.CalcStringRect(&textRect, m_TextBuf, m_TextLen);
275 
276     const Size textSize(textRect.GetWidth(), textRect.GetHeight());
277 
278     VEC2 ltPos = GetVtxPos();
279 
280     const VEC2 curPos  = AdjustTextPos(GetSize(), false);
281     const VEC2 textPos = AdjustTextPos(textSize, true);
282 
283     ltPos.x += curPos.x - textPos.x;
284     ltPos.y -= curPos.y - textPos.y;
285 
286     textRect.left   = ltPos.x;
287     textRect.top    = ltPos.y;
288     textRect.right  = ltPos.x + textSize.width;
289     textRect.bottom = ltPos.y - textSize.height;
290 
291     return textRect;
292 }
293 
294 #ifdef NW_LYT_DMPGL_ENABLED
295 void
DrawSelf(const DrawInfo & drawInfo)296 TextBox::DrawSelf(const DrawInfo& drawInfo)
297 {
298     NW_LYT_STOPWATCH_MEASURE(-400, "nw::lyt::TextBox::DrawSelf");
299 
300     if (m_TextLen <= 0 || !m_pFont || !m_pMaterial)
301     {
302         return;
303     }
304 
305     // lytの描画設定がfontに影響しないように。
306     internal::FinalizeGraphics();
307 
308     GraphicsResource& graphicsResource = *drawInfo.GetGraphicsResource();
309     graphicsResource.ResetGlState();
310 
311     font::TextWriterResource& writerResource = graphicsResource.GetTextWriterResource();
312     font::WideTextWriter writer;
313 
314     writer.SetTextWriterResource(&writerResource);
315     SetupTextWriter(&writer);
316 
317     writerResource.ActiveGlProgram();
318     NW_GL_ASSERT();
319 
320     // 行列
321     LoadMtx(drawInfo);
322 
323     ut::Color8 minCol = m_pMaterial->GetColor(INTERPOLATECOLOR_BLACK);
324     ut::Color8 maxCol = m_pMaterial->GetColor(INTERPOLATECOLOR_WHITE);
325 
326     writer.SetColorMapping(minCol, maxCol);
327     writer.SetAlpha(GetGlobalAlpha());
328 
329     writer.SetupGX();
330 
331     (void)writer.Print(m_TextBuf, m_TextLen);
332 
333     // fontの描画設定がlytに影響しないように。
334     writer.FinalizeGX();
335 }
336 #endif // NW_LYT_DMPGL_ENABLED
337 
338 u16
GetStringBufferLength() const339 TextBox::GetStringBufferLength() const
340 {
341     if (m_TextBufBytes == 0)
342     {
343         return 0;
344     }
345 
346     NW_ASSERT(m_TextBufBytes >= sizeof(wchar_t));
347 
348     return static_cast<u16>(m_TextBufBytes / sizeof(wchar_t) - 1);
349 }
350 
351 void
AllocStringBuffer(u16 minLen)352 TextBox::AllocStringBuffer(u16 minLen)
353 {
354     if (minLen == 0)
355     {
356         return;
357     }
358 
359     u32 allocLen = minLen;
360     ++allocLen; // 終端文字分インクリメント
361 
362     const u32 textBufBytes = allocLen * sizeof(wchar_t);
363     if (textBufBytes >= 0x10000)
364     {
365         NW_WARNING(false, "minLen is too large. - %d\n", minLen);
366     }
367 
368     // 要求する長さのバッファを既に確保している場合は何もしない
369     if (textBufBytes <= m_TextBufBytes)
370     {
371         return;
372     }
373 
374     FreeStringBuffer();
375 
376     // 指定した文字数分VBOバッファを確保
377     const u32 drawBufSize = font::CharWriter::GetDispStringBufferSize(minLen);
378 
379     wchar_t* textBuf     = Layout::NewArray<wchar_t>(allocLen);
380     void* pDispStringBuf = Layout::AllocMemory(drawBufSize);
381     if (NULL == textBuf || NULL == pDispStringBuf)
382     {
383         if (NULL != textBuf)
384         {
385             Layout::DeletePrimArray(textBuf);
386         }
387         if (NULL != pDispStringBuf)
388         {
389             Layout::FreeMemory(pDispStringBuf);
390         }
391         return;
392     }
393 
394     m_TextBuf = textBuf;
395     m_TextBufBytes = static_cast<u16>(textBufBytes);
396     // 表示文字列バッファを初期化
397     m_pDispStringBuf = font::CharWriter::InitDispStringBuffer(
398         pDispStringBuf,
399         minLen);
400 }
401 
402 void
FreeStringBuffer()403 TextBox::FreeStringBuffer()
404 {
405     if (m_TextBuf)
406     {
407         Layout::FreeMemory(m_pDispStringBuf);
408         Layout::DeletePrimArray(m_TextBuf);
409         m_pDispStringBuf = 0;
410         m_TextBuf = 0;
411         m_TextBufBytes = 0;
412         m_TextLen = 0;
413     }
414 }
415 
416 u16
SetString(const wchar_t * str,u16 dstIdx)417 TextBox::SetString(
418     const wchar_t* str,
419     u16 dstIdx
420 )
421 {
422     return SetStringImpl(str, dstIdx, std::wcslen(str));
423 }
424 
425 u16
SetString(const wchar_t * str,u16 dstIdx,u16 strLen)426 TextBox::SetString(
427     const wchar_t* str,
428     u16 dstIdx,
429     u16 strLen
430 )
431 {
432     return SetStringImpl(str, dstIdx, strLen);
433 }
434 
435 u16
SetStringImpl(const wchar_t * str,u16 dstIdx,u32 strLen)436 TextBox::SetStringImpl(
437     const wchar_t*  str,
438     u16             dstIdx,
439     u32             strLen
440 )
441 {
442     if (m_pFont == 0)
443     {
444         NW_WARNING(false, "m_pFont is NULL.\n");
445         return 0;
446     }
447 
448     if (m_TextBuf == 0)
449     {
450         NW_WARNING(false, "m_TextBuf is NULL.\n");
451         return 0;
452     }
453 
454     const u16 bufLen = GetStringBufferLength();
455 
456     if (dstIdx >= bufLen)   // バッファを超えているためコピーしない
457     {
458         NW_WARNING(false, "dstIdx is out of range.\n");
459         return 0;
460     }
461 
462     u32 cpLen = bufLen;
463     cpLen -= dstIdx;        // コピー可能な文字数
464 
465     cpLen = ut::Min(strLen, cpLen);
466     NW_WARNING(cpLen >= strLen, "%d character(s) droped.\n", strLen - cpLen);
467 
468     std::memcpy(m_TextBuf + dstIdx, str, cpLen * sizeof(wchar_t));
469 
470     // m_TextLen にセットする値は 16bitの bufLen 以下の値。
471     m_TextLen = static_cast<u16>(dstIdx + cpLen);
472     m_TextBuf[m_TextLen] = 0;
473 
474     UpdatePTDirty(true);
475 
476     return static_cast<u16>(cpLen);
477 }
478 
479 const font::Font*
GetFont() const480 TextBox::GetFont() const
481 {
482     return m_pFont;
483 }
484 
485 void
SetFont(const font::Font * pFont)486 TextBox::SetFont(const font::Font* pFont)
487 {
488     if (pFont && pFont->GetCharacterCode() != font::CHARACTER_CODE_UNICODE)
489     {
490         NW_WARNING(false, "pFont is not uincode encoding.");
491         return;
492     }
493 
494     if (UpdatePTDirty(m_pFont != pFont))
495     {
496         m_pFont = pFont;
497 
498         if (m_pFont)
499         {
500             SetFontSize(Size(static_cast<f32>(m_pFont->GetWidth()), static_cast<f32>(m_pFont->GetHeight())));
501         }
502         else
503         {
504             SetFontSize(Size(0.f, 0.f));
505         }
506     }
507 }
508 
509 #ifdef NW_LYT_DMPGL_ENABLED
510 void
LoadMtx(const DrawInfo & drawInfo)511 TextBox::LoadMtx(const DrawInfo& drawInfo)
512 {
513     MTX34 mtx;
514 
515     GetTextGlobalMtx(&mtx);
516 
517     drawInfo.GetGraphicsResource()->GetTextWriterResource().SetViewMtx(mtx.a);
518     NW_GL_ASSERT();
519 }
520 #endif
521 
522 void
SetFontInfo(font::WideTextWriter * pWriter) const523 TextBox::SetFontInfo(font::WideTextWriter* pWriter) const
524 {
525     pWriter->SetFont(m_pFont);
526     if (m_pFont != NULL)
527     {
528         pWriter->SetFontSize(m_FontSize.width, m_FontSize.height);
529         pWriter->SetLineSpace(m_LineSpace);
530         pWriter->SetCharSpace(m_CharSpace);
531         pWriter->SetWidthLimit(GetSize().width);
532     }
533 
534     if (m_pTagProcessor)
535     {
536         pWriter->SetTagProcessor(m_pTagProcessor);
537     }
538 }
539 
540 void
SetTextPos(font::WideTextWriter * pWriter) const541 TextBox::SetTextPos(font::WideTextWriter* pWriter) const
542 {
543     u32 value = 0;
544 
545     switch (GetTextAlignment())
546     {
547     case TEXTALIGNMENT_SYNCHRONOUS:
548     default:
549         switch (GetTextPositionH())
550         {
551         case HORIZONTALPOSITION_LEFT:
552         default:                        value = font::WideTextWriter::HORIZONTAL_ALIGN_LEFT;   break;
553         case HORIZONTALPOSITION_CENTER: value = font::WideTextWriter::HORIZONTAL_ALIGN_CENTER; break;
554         case HORIZONTALPOSITION_RIGHT:  value = font::WideTextWriter::HORIZONTAL_ALIGN_RIGHT;  break;
555         }
556         break;
557 
558     case TEXTALIGNMENT_LEFT:        value = font::WideTextWriter::HORIZONTAL_ALIGN_LEFT;    break;
559     case TEXTALIGNMENT_CENTER:      value = font::WideTextWriter::HORIZONTAL_ALIGN_CENTER;  break;
560     case TEXTALIGNMENT_RIGHT:       value = font::WideTextWriter::HORIZONTAL_ALIGN_RIGHT;   break;
561     }
562 
563     switch (GetTextPositionH())
564     {
565     case HORIZONTALPOSITION_LEFT:
566     default:
567         value |= font::WideTextWriter::HORIZONTAL_ORIGIN_LEFT;
568         break;
569     case HORIZONTALPOSITION_CENTER:
570         value |= font::WideTextWriter::HORIZONTAL_ORIGIN_CENTER;
571         break;
572     case HORIZONTALPOSITION_RIGHT:
573         value |= font::WideTextWriter::HORIZONTAL_ORIGIN_RIGHT;
574         break;
575     }
576 
577     switch (GetTextPositionV())
578     {
579     case VERTICALPOSITION_TOP:
580     default:
581         value |= font::WideTextWriter::VERTICAL_ORIGIN_TOP;
582         break;
583     case VERTICALPOSITION_CENTER:
584         value |= font::WideTextWriter::VERTICAL_ORIGIN_MIDDLE;
585         break;
586     case VERTICALPOSITION_BOTTOM:
587         value |= font::WideTextWriter::VERTICAL_ORIGIN_BOTTOM;
588         break;
589     }
590 
591     pWriter->SetDrawFlag(value);
592 }
593 
594 VEC2
AdjustTextPos(const Size & size,bool isCeil) const595 TextBox::AdjustTextPos(
596     const Size&     size,
597     bool            isCeil
598 ) const
599 {
600     VEC2 pos;
601 
602     switch (GetTextPositionH())
603     {
604     case HORIZONTALPOSITION_LEFT:
605     default:
606         pos.x = 0.f;
607         break;
608     case HORIZONTALPOSITION_CENTER:
609         pos.x = internal::AdjustCenterValue(size.width, isCeil);
610         break;
611     case HORIZONTALPOSITION_RIGHT:
612         pos.x = size.width;
613         break;
614     }
615 
616     switch (GetTextPositionV())
617     {
618     case VERTICALPOSITION_TOP:
619     default:
620         pos.y = 0.f;
621         break;
622     case VERTICALPOSITION_CENTER:
623         pos.y = internal::AdjustCenterValue(size.height, isCeil);
624         break;
625     case VERTICALPOSITION_BOTTOM:
626         pos.y = size.height;
627         break;
628     }
629 
630     return pos;
631 }
632 
633 void
GetTextGlobalMtx(nw::math::MTX34 * pMtx) const634 TextBox::GetTextGlobalMtx(nw::math::MTX34* pMtx) const
635 {
636     MTX34Copy(pMtx, &GetGlobalMtx());
637 
638     // テキストの描画開始位置の調整をViewModel行列で行う。
639     VEC2 pos = GetVtxPos();
640     const VEC2 txtPos = AdjustTextPos(GetSize(), false);
641 
642     pos.x += txtPos.x;
643     pos.y -= txtPos.y;
644 
645     pMtx->m[0][3] += pMtx->m[0][0] * pos.x + pMtx->m[0][1] * pos.y;
646     pMtx->m[1][3] += pMtx->m[1][0] * pos.x + pMtx->m[1][1] * pos.y;
647     pMtx->m[2][3] += pMtx->m[2][0] * pos.x + pMtx->m[2][1] * pos.y;
648 
649     // TextWriterはY軸正を下にしているので、YとZを反転させる。
650     //     Yの反転。
651     pMtx->m[0][1] = - pMtx->m[0][1];
652     pMtx->m[1][1] = - pMtx->m[1][1];
653     pMtx->m[2][1] = - pMtx->m[2][1];
654 
655     //     Zの反転。
656     //     Zは影響が無いので計算を省略。
657     /*
658     pMtx->m[0][2] = - pMtx->m[0][2];
659     pMtx->m[1][2] = - pMtx->m[1][2];
660     pMtx->m[2][2] = - pMtx->m[2][2];
661     */
662 }
663 
664 #ifdef NW_LYT_DRAWER_ENABLE
665 void
SetupDrawCharData(Drawer * pDrawer)666 TextBox::SetupDrawCharData(Drawer* pDrawer)
667 {
668     font::WideTextWriter writer;
669 
670     writer.SetDispStringBuffer(m_pDispStringBuf);
671     SetupTextWriter(&writer);
672 
673     if (m_Bits.isPTDirty)
674     {
675         writer.StartPrint();
676         (void)writer.Print(m_TextBuf, m_TextLen);
677         writer.EndPrint();
678 
679         m_Bits.isPTDirty = false;
680     }
681 
682     // CharWriter::StartPrint() 後は、DispStringBuf::IsGeneratedCommand() は false になる。
683     if (! m_pDispStringBuf->IsGeneratedCommand() && pDrawer)
684     {
685         // 文字列のコマンドキャッシュを作成
686         pDrawer->BuildTextCommand(&writer);
687     }
688 }
689 #endif
690 
691 void
SetupTextWriter(font::WideTextWriter * pWriter)692 TextBox::SetupTextWriter(font::WideTextWriter* pWriter)
693 {
694     SetFontInfo(pWriter);
695     SetTextPos(pWriter);
696 
697     ut::Color8 topCol = m_TextColors[TEXTCOLOR_TOP];
698     ut::Color8 btmCol = m_TextColors[TEXTCOLOR_BOTTOM];
699     pWriter->SetGradationMode(topCol != btmCol ? font::CharWriter::GRADMODE_V: font::CharWriter::GRADMODE_NONE);
700     pWriter->SetTextColor(topCol, btmCol);
701 }
702 
703 } // namespace nw::lyt
704 } // namespace nw
705