//----------------------------------------------------------------// void MOAITextFrame::Parse () { float xScaleAdvance = this->mRightToLeft ? -1.0f : 1.0f; this->mLineBottom = 0; this->mLineTop = 0; this->mLineCount = 0; this->mLineXMax = 0.0f; this->mTokenXMin = 0.0f; this->mTokenXMax = 0.0f; float points = this->mPoints; float lineHeight = this->mFont->GetLineSpacing () * this->mPoints * this->mLineSpacing; this->mTotalLines = ( u32 )floorf ( this->mFrame.Height () / lineHeight ); if ( !this->mTotalLines ) return; this->mPen.Init ( 0.0f, this->mFrame.mYMin ); u32 colorSize = 0; u8 color [ COLOR_MAX ]; u32 c = 0; this->mGlyph = 0; bool inToken = false; int resetIdx = 0; u32 state = META_START; while ( state != META_FINISH ) { switch ( state ) { //================================================================// // META //----------------------------------------------------------------// //----------------------------------------------------------------// // check to see if we've encountered a style escape case META_START: { resetIdx = this->mIdx; c = this->DecodeChar (); if ( c == 0 ) { this->FlushToken (); if ( this->mLineCount >= this->mTotalLines ) { TRANSITION ( META_FINISH ); } // save the cursor just before the EOF this->mCursor->mIndex = resetIdx; this->mCursor->mRGBA = this->mRGBA; this->FlushLine (); TRANSITION ( META_FINISH ); } TRANSITION_ON_MATCH ( '<', STYLE_START ); TRANSITION ( TEXT_START ); } //================================================================// // TEXT //----------------------------------------------------------------// //----------------------------------------------------------------// // process a single char as text (ignore style escape) case TEXT_START: { if ( c == '\n' ) { this->FlushToken (); inToken = false; if ( this->mLineCount >= this->mTotalLines ) { TRANSITION ( META_FINISH ); } this->mCursor->mIndex = resetIdx; this->mCursor->mRGBA = this->mRGBA; this->FlushLine (); this->mPen.mX = 0.0f; this->mTokenXMax = 0.0f; this->mTokenXMin = 0.0f; this->mGlyph = 0; TRANSITION ( META_START ); } const MOAIGlyph* prevGlyph = this->mGlyph; const MOAIGlyph* glyph = &this->mFont->GetGlyphForChar ( c ); this->mGlyph = glyph; assert ( glyph ); bool hasWidth = ( glyph->mWidth > 0.0f ); // apply kerning if ( prevGlyph ) { MOAIKernVec kernVec = prevGlyph->GetKerning ( glyph->mCode ); this->mPen.mX += kernVec.mX * points * xScaleAdvance; } if ( hasWidth ) { if ( inToken == false ) { // save the cursor just before the new token this->mCursor->mIndex = resetIdx; this->mCursor->mRGBA = this->mRGBA; this->mTokenXMin = this->mPen.mX; this->mTokenXMax = this->mPen.mX; inToken = true; } // push the glyph float penX = this->mPen.mX + (( glyph->mWidth + glyph->mBearingX ) * points * xScaleAdvance ); float glyphX = this->mRightToLeft ? penX : this->mPen.mX; this->mLayout->PushGlyph ( glyph, resetIdx, glyphX, 0.0f, points, this->mRGBA ); this->mTokenXMax = penX; } else if ( inToken ) { this->FlushToken (); // bail if line count overflow if ( this->mLineCount >= this->mTotalLines ) { TRANSITION ( META_FINISH ); } inToken = false; } // advance the pen this->mPen.mX += glyph->mAdvanceX * points * xScaleAdvance; // back to start TRANSITION ( META_START ); } //================================================================// // STYLE //----------------------------------------------------------------// //----------------------------------------------------------------// // see which style we're parsing case STYLE_START: { c = this->DecodeChar (); TRANSITION_ON_MATCH ( 'c', COLOR_START ); TRANSITION ( STYLE_ABORT ); } // reset the cursor and go directly to text mode case STYLE_ABORT: { this->mIdx = resetIdx; c = this->DecodeChar (); TRANSITION ( TEXT_START ); } //================================================================// // COLOR //----------------------------------------------------------------// //----------------------------------------------------------------// case COLOR_START: { colorSize = 0; c = this->DecodeChar (); if ( IsWhitespace ( c )) continue; if ( c == ':' ) { state = COLOR_BODY; break; } TRANSITION_ON_MATCH ( ':', COLOR_BODY ); TRANSITION_ON_MATCH ( '>', COLOR_FINISH ); TRANSITION ( STYLE_ABORT ); } //----------------------------------------------------------------// case COLOR_BODY: { c = this->DecodeChar (); if ( IsWhitespace ( c )) continue; TRANSITION_ON_MATCH ( '>', COLOR_FINISH ); u8 hex = HexToByte ( c ); if (( hex != 0xff ) && ( colorSize < COLOR_MAX )) { color [ colorSize++ ] = hex; break; } TRANSITION ( STYLE_ABORT ); } //----------------------------------------------------------------// case COLOR_FINISH: { this->mRGBA = this->PackColor ( color, colorSize ); TRANSITION ( META_START ); } } } // discard overflow this->mLayout->SetTop ( this->mLineTop ); }
//----------------------------------------------------------------// bool MOAITextStyler::ParseStyle () { if ( this->mStr [ this->mIdx ] != '<' ) return false; u32 colorSize = 0; u8 color [ COLOR_MAX ]; u32 c = 0; int startIdx = this->mIdx; u32 state = STYLE_START; while ( state != DONE ) { switch ( state ) { //================================================================// // STYLE //----------------------------------------------------------------// //----------------------------------------------------------------// // see which style we're parsing case STYLE_START: { c = this->GetChar (); TRANSITION_ON_MATCH ( '<', STYLE_BODY ); TRANSITION ( STYLE_ABORT ); } //----------------------------------------------------------------// case STYLE_BODY: { c = this->GetChar (); if ( c == '<' ) { this->mIdx = startIdx + 1; this->FinishToken (); startIdx = this->mIdx; TRANSITION ( DONE ); } TRANSITION_ON_MATCH ( '/', STYLE_POP_START ); TRANSITION_ON_MATCH ( '>', STYLE_POP_FINISH ); TRANSITION_ON_MATCH ( 'c', COLOR_START ); TRANSITION ( STYLE_NAME_START ); } //----------------------------------------------------------------// case STYLE_ABORT: { this->mIdx = startIdx; TRANSITION ( DONE ); } //================================================================// // COLOR //----------------------------------------------------------------// //----------------------------------------------------------------// case COLOR_START: { colorSize = 0; c = this->GetChar (); TRANSITION_ON_MATCH ( ':', COLOR_BODY ); // reset and try to parse style name instead this->mIdx = startIdx; TRANSITION ( STYLE_NAME_START ); } //----------------------------------------------------------------// case COLOR_BODY: { c = this->GetChar (); TRANSITION_ON_MATCH ( '>', COLOR_FINISH ); u8 hex = HexToByte ( c ); if (( hex != 0xff ) && ( colorSize < COLOR_MAX )) { color [ colorSize++ ] = hex; TRANSITION ( COLOR_BODY ); } TRANSITION ( STYLE_ABORT ); } //----------------------------------------------------------------// case COLOR_FINISH: { this->FinishToken (); MOAITextStyle* style = this->mTextBox->AddAnonymousStyle ( this->mCurrentStyle ); style->mColor = this->PackColor ( color, colorSize ); this->PushStyle ( style ); TRANSITION ( DONE ); } //================================================================// // STYLE_NAME //----------------------------------------------------------------// //----------------------------------------------------------------// case STYLE_NAME_START: { c = this->GetChar (); if ( MOAIFont::IsControl ( c ) || MOAIFont::IsWhitespace ( c )) { TRANSITION ( STYLE_ABORT ); } TRANSITION_ON_MATCH ( '>', STYLE_NAME_FINISH ); TRANSITION ( STYLE_NAME_START ); } //----------------------------------------------------------------// case STYLE_NAME_FINISH: { this->FinishToken (); int namesize = this->mIdx - startIdx - 2; assert ( namesize > 0 ); char* name = ( char* )alloca ( namesize + 1 ); memcpy ( name, &this->mStr [ startIdx + 1 ], namesize ); name [ namesize ] = 0; MOAITextStyle* style = this->mTextBox->GetStyle ( name ); this->PushStyle ( style ); TRANSITION ( DONE ); } //================================================================// // STYLE_POP //----------------------------------------------------------------// //----------------------------------------------------------------// case STYLE_POP_START: { c = this->GetChar (); if ( MOAIFont::IsControl ( c ) || MOAIFont::IsWhitespace ( c )) { TRANSITION ( STYLE_ABORT ); } TRANSITION_ON_MATCH ( '>', STYLE_POP_FINISH ); TRANSITION ( STYLE_POP_START ); } //----------------------------------------------------------------// case STYLE_POP_FINISH: { this->FinishToken (); this->PopStyle (); TRANSITION ( DONE ); } } } return ( this->mIdx > startIdx ); }