// For proportional fonts, caller MUST already change widths of all chars // (this->TempCharWidth) from ‘default’ to exact values returned by GDI void CVConLine::PolishParts(DWORD* pnXCoords) { if (!TextParts || !PartsCount) { _ASSERTE(TextParts && PartsCount); return; } #ifdef _DEBUG // Validate structure for (unsigned k = 0; k < PartsCount; k++) { _ASSERTE(TextParts[k].Flags != TRF_None); _ASSERTE(TextParts[k].Length > 0); _ASSERTE(TextParts[k].Chars && TextParts[k].Attrs && TextParts[k].CharFlags); } #endif // VirtualConsole has set proper character widths, adopt them TotalLineWidth = 0; for (unsigned k = 0; k < PartsCount; k++) { VConTextPart& part = TextParts[k]; part.Done(); TotalLineWidth += part.TotalWidth; } unsigned PosX, CharIdx; int prevFixedPart; // !SIGNED! // First, we need to check if part do not overlap // with *next* part, which may have *fixed* PosX PosX = 0; prevFixedPart = -1; for (unsigned k = 0; k < PartsCount; k++) { VConTextPart& part = TextParts[k]; if (part.Flags & (TRF_PosFixed|TRF_PosRecommended)) { if (part.Flags & TRF_SizeFixed) { for (unsigned i = 0; i < part.Length; i++) { #define CellWidth FontWidth if (part.CharWidth[i] != CellWidth) { part.TotalWidth += (int)((int)CellWidth - (int)part.CharWidth[i]); part.CharWidth[i] = CellWidth; } } } // Overlaps? Or may be lesser? if (k && (part.PositionX != PosX)) { _ASSERTE(k>0); // We can't get here for k==0 if (k) { DistributeParts(prevFixedPart + 1, k - 1, part.PositionX); } } // Store updated coord prevFixedPart = k; PosX = part.PositionX + part.TotalWidth; } else { //if (part.Flags & TRF_SizeFree) // TextParts[k-1].MinWidth = 1; part.PositionX = PosX; PosX += part.TotalWidth; } } // If last part does not end on screen edge - redistribute if (PosX != (TextWidth * FontWidth)) { DistributeParts(prevFixedPart + 1, PartsCount - 1, (TextWidth * FontWidth)); } /* Fill all X coordinates */ PosX = CharIdx = 0; TotalLineWidth = 0; for (unsigned k = 0; k < PartsCount; k++) { VConTextPart& part = TextParts[k]; _ASSERTE(part.Flags != TRF_None); _ASSERTE(((int)part.TotalWidth > 0) || (part.Flags & TRF_Cropped) || ((part.Length==1) && (part.CharFlags[0]==TCF_WidthZero))); if (part.Flags & (TRF_PosFixed|TRF_PosRecommended)) { _ASSERTE(PosX <= part.PositionX); if (k && PosX && PosX < part.PositionX) ExpandPart(TextParts[k-1], part.PositionX); PosX = part.PositionX; } else { part.PositionX = PosX; } TotalLineWidth += part.TotalWidth; unsigned charPosX = PosX; unsigned* ptrWidth = part.CharWidth; for (unsigned l = part.Length; l--; CharIdx++, ptrWidth++) { charPosX += *ptrWidth; pnXCoords[CharIdx] = charPosX; } PosX += part.TotalWidth; _ASSERTE(PosX >= charPosX); } //TODO: Optimization. Use linked-list for TextParts //TODO: we'll get memory gain and easy part deletion // Now we may combine all parts, which are displayed same style for (unsigned k = 0; k < PartsCount; k++) { VConTextPart& part = TextParts[k]; if (part.Flags & (TRF_PosFixed|TRF_PosRecommended)) continue; const CharAttr attr = ConAttrLine[part.Index]; if (!(part.Flags & TRF_TextSpacing)) { _ASSERTE(TRF_CompareMask & TRF_TextAlternative); for (unsigned k2 = k+1; k2 < PartsCount; k2++) { VConTextPart& part2 = TextParts[k2]; if (part2.Flags & (TRF_PosFixed|TRF_PosRecommended)) break; WARNING("At the moment we do not care about CJK and RTL differences, unless TRF_TextAlternative is specified"); // Let GDI deal with them, seems like it successfully // process strings, containing all types of characters if ((part.Flags & TRF_CompareMask) != (part2.Flags & TRF_CompareMask)) break; if (!(attr == ConAttrLine[part2.Index])) break; part.Length += part2.Length; part.TotalWidth += part2.TotalWidth; // "Hide" this part from list part2.Flags = TRF_None; k = k2; } } } return; }
// For proportional fonts, caller MUST already change widths of all chars // (this->TempCharWidth) from ‘default’ to exact values returned by GDI void CVConLine::PolishParts(DWORD* pnXCoords) { if (!TextParts || !PartsCount) { _ASSERTE(TextParts && PartsCount); return; } #ifdef _DEBUG // Validate structure for (uint k = 0; k < PartsCount; k++) { _ASSERTE(TextParts[k].Flags != TRF_None); _ASSERTE(TextParts[k].Length > 0); _ASSERTE(TextParts[k].Chars && TextParts[k].Attrs && TextParts[k].CharFlags); } #endif uint PosX, CharIdx; int prevFixedPart; // !SIGNED! // First, we need to check if part do not overlap // with *next* part, which may have *fixed* PosX PosX = 0; prevFixedPart = -1; for (uint k = 0; k < PartsCount; k++) { VConTextPart& part = TextParts[k]; if (part.Flags & (TRF_PosFixed|TRF_PosRecommended)) { if (part.Flags & TRF_SizeFixed) { for (uint i = 0; i < part.Length; i++) { #define CellWidth FontWidth if (part.CharWidth[i] != CellWidth) { part.TotalWidth += (int)((int)CellWidth - (int)part.CharWidth[i]); part.CharWidth[i] = CellWidth; } } } // Overlaps? if (k && (part.PositionX != PosX)) { _ASSERTE(k>0); // We can't get here for k==0 if (k) { DistributeParts(prevFixedPart + 1, k - 1, part.PositionX); } } // Store updated coord prevFixedPart = k; PosX = part.PositionX + part.TotalWidth; } else { //if (part.Flags & TRF_SizeFree) // TextParts[k-1].MinWidth = 1; part.PositionX = PosX; PosX += part.TotalWidth; } } // If last part goes out of screen - redistribute if (PosX != (TextWidth * FontWidth)) { DistributeParts(prevFixedPart + 1, PartsCount - 1, (TextWidth * FontWidth)); } /* Fill all X coordinates */ PosX = CharIdx = 0; TotalLineWidth = MinLineWidth = 0; for (uint k = 0; k < PartsCount; k++) { VConTextPart& part = TextParts[k]; _ASSERTE(part.Flags != TRF_None); _ASSERTE(((int)part.TotalWidth > 0) || ((part.Length==1) && (part.CharFlags[0]==TCF_WidthZero))); if (part.Flags & (TRF_PosFixed|TRF_PosRecommended)) { _ASSERTE(PosX <= part.PositionX); if (k && PosX && PosX < part.PositionX) ExpandPart(TextParts[k-1], part.PositionX); PosX = part.PositionX; } else { part.PositionX = PosX; } TotalLineWidth += part.TotalWidth; MinLineWidth += part.MinWidth; uint charPosX = PosX; uint* ptrWidth = part.CharWidth; for (uint l = part.Length; l--; CharIdx++, ptrWidth++) { charPosX += *ptrWidth; pnXCoords[CharIdx] = charPosX; } PosX += part.TotalWidth; _ASSERTE(PosX >= charPosX); } //TODO: Optimization. Use linked-list for TextParts //TODO: we'll get memory gain and easy part deletion // Now we may combine all parts, which are displayed same style for (uint k = 0; k < PartsCount; k++) { VConTextPart& part = TextParts[k]; if (part.Flags & (TRF_PosFixed|TRF_PosRecommended)) continue; const CharAttr attr = ConAttrLine[part.Index]; if (part.Flags & TRF_TextAlternative) { for (uint k2 = k+1; k2 < PartsCount; k2++) { VConTextPart& part2 = TextParts[k2]; if ((part2.Flags & (TRF_PosFixed|TRF_PosRecommended)) || !(part2.Flags & TRF_TextAlternative)) break; if (!(attr == ConAttrLine[part2.Index])) break; part.Length += part2.Length; part.TotalWidth += part2.TotalWidth; part.MinWidth += part2.MinWidth; // "Hide" this part from list part2.Flags = TRF_None; k = k2; } } else if (!(part.Flags & TRF_TextSpacing)) { for (uint k2 = k+1; k2 < PartsCount; k2++) { VConTextPart& part2 = TextParts[k2]; if ((part2.Flags & (TRF_PosFixed|TRF_PosRecommended|TRF_TextAlternative))) break; if (!(attr == ConAttrLine[part2.Index])) break; part.Length += part2.Length; part.TotalWidth += part2.TotalWidth; part.MinWidth += part2.MinWidth; // "Hide" this part from list part2.Flags = TRF_None; k = k2; } } } return; }