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