/*---------------------------------------------------------------------------* Project: NintendoWare File: font_CharWriter.cpp Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo and/or its licensed developers and are protected by national and international copyright laws. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. The content herein is highly confidential and should be handled accordingly. $Revision: 31311 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include #include #include #include #include #include #include #include #include #include // NW_GL_ASSERT用 namespace nw { namespace font { namespace { /*!--------------------------------------------------------------------------* @brief カラーにアルファ値を掛けます。 *---------------------------------------------------------------------------*/ void MultiplyAlpha( ut::FloatColor* pDst, const ut::Color8 src, u8 alpha ) { const f32 floatAlphaMax = ut::Color8::ALPHA_MAX; pDst->r = src.r / floatAlphaMax; pDst->g = src.g / floatAlphaMax; pDst->b = src.b / floatAlphaMax; pDst->a = (src.a / floatAlphaMax) * (alpha / floatAlphaMax); } } // namespace void CharWriter::SetupGXCommon() { const int *const locations = m_pTextWriterResource->GetUniformLocations(); // フラグメントライティングの無効化 glUniform1i(locations[internal::LOC_FRAGMENTLIGHTING_ENABLED], GL_FALSE); // テクスチャユニット0を2次元テクスチャに設定 glUniform1i(locations[internal::LOC_TEXTURE0_SAMPLERTYPE], GL_TEXTURE_2D); // テクスチャユニット0をアクティブに設定 glActiveTexture(GL_TEXTURE0); // フォグの無効化 glUniform1i(locations[internal::LOC_FOG_MODE], GL_FALSE); // アルファテストの無効化 (常にパス) glUniform1i(locations[internal::LOC_FRAGOPERATION_ENABLEALPHATEST], GL_FALSE); // ソースアルファでブレンド glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendEquation(GL_FUNC_ADD); // 論理演算を無効 glDisable(GL_COLOR_LOGIC_OP); NW_GL_ASSERT(); } /* ======================================================================= public ======================================================================== */ /* ------------------------------------------------------------------------ コンストラクタ/デストラクタ ------------------------------------------------------------------------ */ CharWriter::CharWriter() : m_FixedWidth(0), m_pFont(NULL), m_pTextWriterResource(NULL), m_pDispStringBuffer(NULL), m_IsWidthFixed(false), m_Alpha(ut::Color8::ALPHA_MAX) { ResetColorMapping(); // m_ColorMapping SetGradationMode(GRADMODE_NONE); // m_TextColor, mVertexColor SetTextColor(ut::Color8::WHITE); // m_TextColor, mVertexColor SetScale(1, 1); // m_Scale SetCursor(0, 0, 0); // m_CursorPos #if defined(NW_FONT_PROFILE) m_LoadTextureCount = 0; m_PrintCharCount = 0; m_DispCharCount = 0; #endif } CharWriter::~CharWriter() { } /* ------------------------------------------------------------------------ 描画準備 ------------------------------------------------------------------------ */ void CharWriter::SetupGX() { NN_NULL_ASSERT(m_pTextWriterResource); m_pTextWriterResource->ResetLoadingTexture(); SetupGXCommon(); bool bAlphaTex = false; if (m_pFont) { switch (m_pFont->GetTextureFormat()) { case FONT_SHEET_FORMAT_A4: case FONT_SHEET_FORMAT_A8: bAlphaTex = true; break; } } if ( m_Alpha != ut::Color8::ALPHA_MAX || m_ColorMapping.min != DEFAULT_COLOR_MAPPING_MIN || m_ColorMapping.max != DEFAULT_COLOR_MAPPING_MAX ) { SetupGXWithColorMapping(bAlphaTex); } else { SetupGXDefault(bAlphaTex); } SetupVertexFormat(); } /* ------------------------------------------------------------------------ 文字サイズ ------------------------------------------------------------------------ */ void CharWriter::SetFontSize( f32 width, f32 height ) { NN_POINTER_ASSERT(m_pFont); NW_FONT_MIN_ASSERT(m_pFont->GetWidth(), 1); NW_FONT_MIN_ASSERT(m_pFont->GetHeight(), 1); SetScale( width / m_pFont->GetWidth(), height / m_pFont->GetHeight() ); } void CharWriter::SetFontSize(f32 height) { NN_POINTER_ASSERT(m_pFont); NW_FONT_MIN_ASSERT(m_pFont->GetHeight(), 1); const f32 scale = height / m_pFont->GetHeight(); SetScale(scale); } f32 CharWriter::GetFontWidth() const { NN_POINTER_ASSERT(m_pFont); return m_pFont->GetWidth() * m_Scale.x; } f32 CharWriter::GetFontHeight() const { NN_POINTER_ASSERT(m_pFont); return m_pFont->GetHeight() * m_Scale.y; } f32 CharWriter::GetFontAscent() const { NN_POINTER_ASSERT(m_pFont); return m_pFont->GetAscent() * m_Scale.y; } f32 CharWriter::GetFontDescent() const { NN_POINTER_ASSERT(m_pFont); return m_pFont->GetDescent() * m_Scale.y; } /* ------------------------------------------------------------------------ 文字描画 ------------------------------------------------------------------------ */ f32 CharWriter::Print(CharCode code) { NN_POINTER_ASSERT(m_pFont); NN_ASSERT(code != Font::INVALID_CHARACTER_CODE); NW_FONT_COUNTUP(m_PrintCharCount); NW_FONT_STOPWATCH_START(m_PrintCharSw); Glyph glyph; m_pFont->GetGlyph(&glyph, code); CharWidths &widths = glyph.widths; f32 width; f32 left; if (m_IsWidthFixed) { f32 margin = (m_FixedWidth - widths.charWidth * m_Scale.x) / 2; width = m_FixedWidth; left = margin + widths.left * m_Scale.x; } else { width = widths.charWidth * m_Scale.x; left = widths.left * m_Scale.x; } PrintGlyph(m_CursorPos.x + left, glyph); m_CursorPos.x += width; NW_FONT_STOPWATCH_STOP(m_PrintCharSw); return width; } void CharWriter::DrawGlyph(const Glyph& glyph) { NN_POINTER_ASSERT(&glyph); PrintGlyph(m_CursorPos.x, glyph); m_CursorPos.x += glyph.widths.glyphWidth * m_Scale.x; } /* ======================================================================= private ======================================================================== */ void CharWriter::PrintGlyph( f32 x, const Glyph& glyph ) { NN_POINTER_ASSERT(&glyph); NW_FONT_MIN_ASSERT(glyph.texWidth, 1); NW_FONT_MIN_ASSERT(glyph.texHeight, 1); const f32 y = m_CursorPos.y; const f32 texLeft = 1.0f * glyph.cellX / glyph.texWidth; const f32 texRight = 1.0f * (glyph.cellX + glyph.widths.glyphWidth) / glyph.texWidth; // cellYは左上原点の値が入っているので、これをOpenGLの左下原点の // テクスチャ座標に変換してセットする。 const f32 texTop = 1.0f * (glyph.texHeight - glyph.cellY) / glyph.texHeight; const f32 texBottom = 1.0f * (glyph.texHeight - (glyph.cellY + glyph.height)) / glyph.texHeight; if (NULL != m_pDispStringBuffer) { const f32 width = glyph.widths.glyphWidth * m_Scale.x; const f32 height = - glyph.height * m_Scale.y; const u32 charIdx = m_pDispStringBuffer->charCount; if (charIdx >= m_pDispStringBuffer->charCountMax) { NN_LOG("nw::font::CharWriter : Vertex Buffer Over.\n"); return; } m_pDispStringBuffer->charCount++; internal::CharAttribute* pCharAttrs = &m_pDispStringBuffer->GetCharAttrs()[charIdx]; // ポジションのセット pCharAttrs->pos.Set( width, height, x, y); // カラーのセット for (int i = 0; i < internal::TEXTCOLOR_MAX; ++i) { pCharAttrs->color[i] = m_TextColors[i]; } // テクスチャ座標のセット pCharAttrs->tex.Set( texLeft, texTop, texRight, texBottom); // テクスチャオブジェクトへのポインタのセット NN_NULL_ASSERT(glyph.pTextureObject); pCharAttrs->pTexObj = glyph.pTextureObject; } else { const f32 posLeft = x; const f32 posRight = posLeft + glyph.widths.glyphWidth * m_Scale.x; #if defined(COORDINATE_LT) const f32 posTop = y; const f32 posBottom = y + glyph.height * m_Scale.y; #else const f32 posTop = y + glyph.height * m_Scale.y; const f32 posBottom = y; #endif internal::VertexAttribute *const pVtxAttrs = m_pTextWriterResource->GetVertexAttributeArray(); // ポジションのセット { GLfloat* attrs = pVtxAttrs[internal::TRIFAN_VTX_RT].pos; attrs[internal::POS_X] = posRight; attrs[internal::POS_Y] = posTop; attrs = pVtxAttrs[internal::TRIFAN_VTX_LT].pos; attrs[internal::POS_X] = posLeft; attrs[internal::POS_Y] = posTop; attrs = pVtxAttrs[internal::TRIFAN_VTX_LB].pos; attrs[internal::POS_X] = posLeft; attrs[internal::POS_Y] = posBottom; attrs = pVtxAttrs[internal::TRIFAN_VTX_RB].pos; attrs[internal::POS_X] = posRight; attrs[internal::POS_Y] = posBottom; } // カラーのセット for (int i = 0; i < internal::TRIFAN_VTX_MAX; ++i) { pVtxAttrs[i].color = m_VertexColors[i]; } // テクスチャ座標のセット { GLfloat* attrs = pVtxAttrs[internal::TRIFAN_VTX_RT].tex; attrs[internal::TEXCOORD_X] = texRight; attrs[internal::TEXCOORD_Y] = texTop; attrs = pVtxAttrs[internal::TRIFAN_VTX_LT].tex; attrs[internal::TEXCOORD_X] = texLeft; attrs[internal::TEXCOORD_Y] = texTop; attrs = pVtxAttrs[internal::TRIFAN_VTX_LB].tex; attrs[internal::TEXCOORD_X] = texLeft; attrs[internal::TEXCOORD_Y] = texBottom; attrs = pVtxAttrs[internal::TRIFAN_VTX_RB].tex; attrs[internal::TEXCOORD_X] = texRight; attrs[internal::TEXCOORD_Y] = texBottom; } LoadTexture(glyph); m_pTextWriterResource->UpdatePosZ(m_CursorPos.z); glDrawArrays(GL_TRIANGLE_FAN, 0, internal::TRIFAN_VTX_MAX); NW_GL_ASSERT(); } } void CharWriter::StartPrint() { NN_NULL_ASSERT(m_pDispStringBuffer); m_pDispStringBuffer->charCount = 0; m_pDispStringBuffer->ClearCommand(); } void CharWriter::EndPrint() { NN_NULL_ASSERT(m_pDispStringBuffer); } /* ------------------------------------------------------------------------ 内部処理 ------------------------------------------------------------------------ */ void CharWriter::LoadTexture(const Glyph& glyph) { NN_POINTER_ASSERT(&glyph); bool doLoad = false; GLuint texName = 0; if (NULL == glyph.pTextureObject) { texName = m_pTextWriterResource->GetTextureID(); doLoad = m_pTextWriterResource->SetLoadingTexture(glyph.pTexture); } else { texName = glyph.pTextureObject->GetName(); if (texName == 0) { glGenTextures(1, &texName); const_cast(glyph.pTextureObject)->SetName(texName); doLoad = true; } else { doLoad = 0 != glyph.isSheetUpdated; // 強制ロード } } glBindTexture(GL_TEXTURE_2D, texName); if (doLoad) { NW_FONT_COUNTUP(m_LoadTextureCount); NW_FONT_STOPWATCH_START(m_LoadTextureSw); internal::LoadTexture( glyph.texWidth, glyph.texHeight, glyph.texFormat, glyph.pTexture, m_pFont->IsLinearFilterEnableAtSmall(), m_pFont->IsLinearFilterEnableAtLarge()); NW_FONT_STOPWATCH_STOP(m_LoadTextureSw); } } void CharWriter::UpdateVertexColor() { m_VertexColors[internal::TRIFAN_VTX_LT] = m_TextColors[internal::TEXTCOLOR_START]; m_VertexColors[internal::TRIFAN_VTX_RT] = m_TextColors[ m_GradationMode != GRADMODE_H ? internal::TEXTCOLOR_START: internal::TEXTCOLOR_END]; m_VertexColors[internal::TRIFAN_VTX_LB] = m_TextColors[ m_GradationMode != GRADMODE_V ? internal::TEXTCOLOR_START: internal::TEXTCOLOR_END]; m_VertexColors[internal::TRIFAN_VTX_RB] = m_TextColors[ m_GradationMode == GRADMODE_NONE ? internal::TEXTCOLOR_START: internal::TEXTCOLOR_END]; } /* ------------------------------------------------------------------------ static ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ 描画準備 ------------------------------------------------------------------------ */ void CharWriter::SetupVertexFormat() { NN_ASSERT(NULL == m_pDispStringBuffer); // バッファオブジェクトを無効化 glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); const u8* basePtr = reinterpret_cast(m_pTextWriterResource->GetVertexAttributeArray()[0].pos); const GLsizei stride = sizeof(internal::VertexAttribute); glEnableVertexAttribArray(internal::VERTEX_ATTR_POS); glVertexAttribPointer( internal::VERTEX_ATTR_POS, internal::POS_NUM, GL_FLOAT, GL_FALSE, stride, basePtr); glDisableVertexAttribArray(internal::VERTEX_ATTR_POS_Z); m_pTextWriterResource->SetPosZ(m_CursorPos.z); glEnableVertexAttribArray(internal::VERTEX_ATTR_COLOR); glVertexAttribPointer( internal::VERTEX_ATTR_COLOR, internal::COLOR_NUM, GL_UNSIGNED_BYTE, GL_FALSE, stride, basePtr + offsetof(internal::VertexAttribute, color)); glEnableVertexAttribArray(internal::VERTEX_ATTR_TEXCOORD); glVertexAttribPointer( internal::VERTEX_ATTR_TEXCOORD, internal::TEXCOORD_NUM, GL_FLOAT, GL_FALSE, stride, basePtr + offsetof(internal::VertexAttribute, tex)); NW_GL_ASSERT(); } u32 CharWriter::GetDispStringBufferSize(u32 charNum) { const u32 drawFlagBytes = math::RoundUp(charNum, 8) / 8; return sizeof(DispStringBuffer) + sizeof(internal::CharAttribute) * charNum + math::RoundUp(drawFlagBytes, sizeof(u32)) + sizeof(u32) * DispStringBuffer::CalcCommandBufferCapacity(charNum) ; } DispStringBuffer* CharWriter::InitDispStringBuffer( void* drawBuffer, u32 charNum ) { NN_NULL_ASSERT(drawBuffer); return new (drawBuffer) DispStringBuffer(charNum); } void CharWriter::SetupGXDefault(bool bAlphaTex) { const int (*locations)[internal::TCLOC_MAX] = m_pTextWriterResource->GetTexEnvUniformLocations(); // テクスチャコンバイナ設定 static const GLint src[] = { GL_PRIMARY_COLOR, GL_TEXTURE0, GL_CONSTANT }; static const GLint operandRgb[] = { GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_COLOR }; static const GLint operandAlp[] = { GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA }; const GLint combineMode = bAlphaTex ? GL_REPLACE: GL_MODULATE; glUniform3iv(locations[internal::TEXENV_5][internal::TCLOC_SRCRGB ], 1, src); glUniform3iv(locations[internal::TEXENV_5][internal::TCLOC_SRCALPHA ], 1, src); glUniform3iv(locations[internal::TEXENV_5][internal::TCLOC_OPERANDRGB ], 1, operandRgb); glUniform3iv(locations[internal::TEXENV_5][internal::TCLOC_OPERANDALPHA], 1, operandAlp); glUniform1i (locations[internal::TEXENV_5][internal::TCLOC_COMBINERGB ], combineMode); glUniform1i (locations[internal::TEXENV_5][internal::TCLOC_COMBINEALPHA], GL_MODULATE); glUniform1f (locations[internal::TEXENV_5][internal::TCLOC_SCALERGB ], 1.0); glUniform1f (locations[internal::TEXENV_5][internal::TCLOC_SCALEALPHA ], 1.0); } void CharWriter::SetupGXWithColorMapping(bool bAlphaTex) { const int (*locations)[internal::TCLOC_MAX] = m_pTextWriterResource->GetTexEnvUniformLocations(); // テクスチャコンバイナ設定 static const GLint Src0[] = { GL_TEXTURE0, GL_CONSTANT, GL_CONSTANT }; static const GLint OpRgb0[] = { GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_COLOR }; static const GLint OpRgb1[] = { GL_ONE_MINUS_SRC_COLOR, GL_SRC_COLOR, GL_SRC_COLOR }; static const GLint OpAlp0[] = { GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA }; glUniform3iv(locations[internal::TEXENV_3][internal::TCLOC_SRCRGB ], 1, Src0); glUniform3iv(locations[internal::TEXENV_3][internal::TCLOC_SRCALPHA ], 1, Src0); glUniform3iv(locations[internal::TEXENV_3][internal::TCLOC_OPERANDRGB ], 1, bAlphaTex ? OpRgb1: OpRgb0); glUniform3iv(locations[internal::TEXENV_3][internal::TCLOC_OPERANDALPHA], 1, OpAlp0); glUniform1i (locations[internal::TEXENV_3][internal::TCLOC_COMBINERGB ], GL_MODULATE); glUniform1i (locations[internal::TEXENV_3][internal::TCLOC_COMBINEALPHA], GL_MODULATE); glUniform1f (locations[internal::TEXENV_3][internal::TCLOC_SCALERGB ], 1.0); glUniform1f (locations[internal::TEXENV_3][internal::TCLOC_SCALEALPHA ], 1.0); ut::FloatColor maxCol; MultiplyAlpha(&maxCol, m_ColorMapping.max, m_Alpha); glUniform4fv(locations[internal::TEXENV_3][internal::TCLOC_CONSTRGBA ], 1, maxCol.ToArray()); static const GLint Src1[] = { GL_TEXTURE0, GL_CONSTANT, GL_PREVIOUS }; static const GLint OpAlp1[] = { GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA }; glUniform3iv(locations[internal::TEXENV_4][internal::TCLOC_SRCRGB ], 1, Src1); glUniform3iv(locations[internal::TEXENV_4][internal::TCLOC_SRCALPHA ], 1, Src1); glUniform3iv(locations[internal::TEXENV_4][internal::TCLOC_OPERANDRGB ], 1, bAlphaTex ? OpRgb0: OpRgb1); glUniform3iv(locations[internal::TEXENV_4][internal::TCLOC_OPERANDALPHA], 1, OpAlp1); glUniform1i (locations[internal::TEXENV_4][internal::TCLOC_COMBINERGB ], GL_MULT_ADD_DMP); glUniform1i (locations[internal::TEXENV_4][internal::TCLOC_COMBINEALPHA], GL_MULT_ADD_DMP); glUniform1f (locations[internal::TEXENV_4][internal::TCLOC_SCALERGB ], 1.0); glUniform1f (locations[internal::TEXENV_4][internal::TCLOC_SCALEALPHA ], 1.0); ut::FloatColor minCol; MultiplyAlpha(&minCol, m_ColorMapping.min, m_Alpha); glUniform4fv(locations[internal::TEXENV_4][internal::TCLOC_CONSTRGBA ], 1, minCol.ToArray()); static const GLint Src2[] = { GL_PRIMARY_COLOR, GL_PREVIOUS, GL_PREVIOUS }; glUniform3iv(locations[internal::TEXENV_5][internal::TCLOC_SRCRGB ], 1, Src2); glUniform3iv(locations[internal::TEXENV_5][internal::TCLOC_SRCALPHA ], 1, Src2); glUniform3iv(locations[internal::TEXENV_5][internal::TCLOC_OPERANDRGB ], 1, OpRgb0); glUniform3iv(locations[internal::TEXENV_5][internal::TCLOC_OPERANDALPHA], 1, OpAlp0); glUniform1i (locations[internal::TEXENV_5][internal::TCLOC_COMBINERGB ], GL_MODULATE); glUniform1i (locations[internal::TEXENV_5][internal::TCLOC_COMBINEALPHA], GL_MODULATE); glUniform1f (locations[internal::TEXENV_5][internal::TCLOC_SCALERGB ], 1.0); glUniform1f (locations[internal::TEXENV_5][internal::TCLOC_SCALEALPHA ], 1.0); } #if defined(NW_FONT_PROFILE) void CharWriter::PrintProfile() { using namespace nn::fnd; TimeSpan timeSpan = m_LoadTextureSw.GetElapsedTime(); NN_LOG("CharWriter Load Texture time %d micros, count %d.\n", (int)timeSpan.GetMilliSeconds(), m_LoadTextureCount); timeSpan = m_PrintCharSw.GetElapsedTime(); NN_LOG("CharWriter Print time %d micros, count %d.\n", (int)timeSpan.GetMilliSeconds(), m_PrintCharCount); timeSpan = m_DispCharSw.GetElapsedTime(); NN_LOG("CharWriter Disp time %d micros, count %d.\n", (int)timeSpan.GetMilliSeconds(), m_DispCharCount); } void CharWriter::CopyProfileData(const CharWriter& other) const { m_LoadTextureSw = other.m_LoadTextureSw; m_PrintCharSw = other.m_PrintCharSw; m_LoadTextureCount = other.m_LoadTextureCount; m_PrintCharCount = other.m_PrintCharCount; } #endif } // namespace font } // namespace nw