From 15990b01a269a7f5af9eddeb98a70417f8e59197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20A=C3=9Fmus?= Date: Thu, 5 Sep 2013 10:59:39 +0200 Subject: [PATCH] HaikuDepot: Text stuff: ParagraphLayout compiles and maybe works --- src/apps/haiku-depot/Jamfile | 1 + .../haiku-depot/textview/ParagraphLayout.cpp | 413 ++++++++---------- .../haiku-depot/textview/ParagraphLayout.h | 137 ++++-- 3 files changed, 285 insertions(+), 266 deletions(-) diff --git a/src/apps/haiku-depot/Jamfile b/src/apps/haiku-depot/Jamfile index b9ed0aa76f..22335f5c30 100644 --- a/src/apps/haiku-depot/Jamfile +++ b/src/apps/haiku-depot/Jamfile @@ -30,6 +30,7 @@ Application HaikuDepot : CharacterStyle.cpp CharacterStyleData.cpp Paragraph.cpp + ParagraphLayout.cpp ParagraphStyle.cpp ParagraphStyleData.cpp TextDocument.cpp diff --git a/src/apps/haiku-depot/textview/ParagraphLayout.cpp b/src/apps/haiku-depot/textview/ParagraphLayout.cpp index c0ff872114..ef8336c638 100644 --- a/src/apps/haiku-depot/textview/ParagraphLayout.cpp +++ b/src/apps/haiku-depot/textview/ParagraphLayout.cpp @@ -19,10 +19,6 @@ #include #include -#include "GlyphInfo.h" -#include "List.h" -#include "TextStyle.h" - enum { CHAR_CLASS_DEFAULT, @@ -98,22 +94,24 @@ get_char_classification(uint32 charCode) inline bool -can_end_line(const GlyphInfo* buffer, int offset, int count) +can_end_line(const GlyphInfoList& glyphInfos, int offset) { + int count = glyphInfos.CountItems(); + if (offset == count - 1) return true; if (offset < 0 || offset > count) return false; - uint32 charCode = buffer[offset].charCode; + uint32 charCode = glyphInfos.ItemAtFast(offset).charCode; uint32 classification = get_char_classification(charCode); // wrapping is always allowed at end of text and at newlines if (classification == CHAR_CLASS_END_OF_TEXT || charCode == '\n') return true; - uint32 nextCharCode = buffer[offset + 1].charCode; + uint32 nextCharCode = glyphInfos.ItemAtFast(offset + 1).charCode; uint32 nextClassification = get_char_classification(nextCharCode); // never separate a punctuation char from its preceding word @@ -166,15 +164,13 @@ can_end_line(const GlyphInfo* buffer, int offset, int count) ParagraphLayout::ParagraphLayout() : fTextSpans(), - fWidth(0.0f), + fParagraphStyle(), + fWidth(0.0f), fLayoutValid(false), - fGlyphInfoBuffer(NULL), - fGlpyhInfoBufferSize(0), - fGlyphInfoCount(0), - - fLayoutLines() + fGlyphInfos(), + fLineInfos() { } @@ -182,15 +178,13 @@ ParagraphLayout::ParagraphLayout() ParagraphLayout::ParagraphLayout(const Paragraph& paragraph) : fTextSpans(paragraph.TextSpans()), - fWidth(other.fWidth), + fParagraphStyle(paragraph.Style()), + fWidth(0.0f), fLayoutValid(false), - fGlyphInfoBuffer(NULL), - fGlpyhInfoBufferSize(0), - fGlyphInfoCount(0), - - fLayoutLines() + fGlyphInfos(), + fLineInfos() { _Init(); } @@ -199,15 +193,13 @@ ParagraphLayout::ParagraphLayout(const Paragraph& paragraph) ParagraphLayout::ParagraphLayout(const ParagraphLayout& other) : fTextSpans(other.fTextSpans), + fParagraphStyle(other.fParagraphStyle), + fWidth(other.fWidth), - fLayoutValid(false), - fGlyphInfoBuffer(NULL), - fGlpyhInfoBufferSize(0), - fGlyphInfoCount(0), - - fLayoutLines() + fGlyphInfos(), + fLineInfos() { _Init(); } @@ -215,7 +207,18 @@ ParagraphLayout::ParagraphLayout(const ParagraphLayout& other) ParagraphLayout::~ParagraphLayout() { - delete[] fGlyphInfoBuffer; +} + + +void +ParagraphLayout::SetParagraph(const Paragraph& paragraph) +{ + fTextSpans = paragraph.TextSpans(); + fParagraphStyle = paragraph.Style(); + + _Init(); + + fLayoutValid = false; } @@ -236,8 +239,8 @@ ParagraphLayout::Height() float height = 0.0f; - if (fLayoutLines.CountItems() > 0) { - const LayoutLine& lastLine = fLayoutLines.LastItem(); + if (fLineInfos.CountItems() > 0) { + const LineInfo& lastLine = fLineInfos.LastItem(); height = lastLine.y + lastLine.height; } @@ -250,9 +253,9 @@ ParagraphLayout::Draw(BView* view, const BPoint& offset) { _ValidateLayout(); - int lineCount = fLayoutLines.CountItems(); + int lineCount = fLineInfos.CountItems(); for (int i = 0; i < lineCount; i++) { - const LayoutLine& line = fLayoutLines.ItemAtFast(i); + const LineInfo& line = fLineInfos.ItemAtFast(i); _DrawLine(view, line); } } @@ -272,51 +275,26 @@ ParagraphLayout::_ValidateLayout() void ParagraphLayout::_Layout() { - fLayoutLines.Clear(); + fLineInfos.Clear(); -// int charCount = fText.CountChars(); -// if (charCount == 0) -// return; -// -// // Allocate arrays -// float* escapementArray = new (std::nothrow) float[charCount]; -// if (escapementArray == NULL) -// return; -// ArrayDeleter escapementDeleter(escapementArray); -// -// // Fetch glyph spacing information -// fDefaultFont.GetEscapements(fText.String(), charCount, escapementArray); -// -// // Convert to glyph buffer -// fGlyphInfoCount = charCount; -// fGlyphInfoBuffer = new (std::nothrow) GlyphInfo[fGlyphInfoCount]; -// -// // Init glyph buffer and convert escapement scale -// float size = fDefaultFont.Size(); -// const char* c = fText.String(); -// for (int i = 0; i < fGlyphInfoCount; i++) { -// fGlyphInfoBuffer[i].charCode = UTF8ToCharCode(&c); -// escapementArray[i] *= size; -// } -// -// -// float x = fFirstLineInset; -// float y = 0.0f; -// int lineIndex = 0; -// int lineStart = 0; -// -// for (int i = 0; i < fGlyphInfoCount; i++) { -// uint32 charClassification = get_char_classification( -// fGlyphInfoBuffer[i].charCode); -// -// float advanceX = 0.0f; -// float advanceY = 0.0f; -// _GetGlyphAdvance(i, advanceX, advanceY, escapementArray); -// -// bool nextLine = false; -// bool lineBreak = false; -// -// if (fGlyphInfoBuffer[i].charCode == '\t') { + float x = fParagraphStyle.FirstLineInset(); + float y = 0.0f; + int lineIndex = 0; + int lineStart = 0; + + int glyphCount = fGlyphInfos.CountItems(); + for (int i = 0; i < glyphCount; i++) { + GlyphInfo glyph = fGlyphInfos.ItemAtFast(i); + + uint32 charClassification = get_char_classification(glyph.charCode); + + float advanceX = glyph.width; + float advanceY = 0.0f; + + bool nextLine = false; + bool lineBreak = false; + +// if (glyph.charCode == '\t') { // // Figure out tab width, it's the width between the last two tab // // stops. // float tabWidth = 0.0f; @@ -342,92 +320,90 @@ ParagraphLayout::_Layout() // if (tabOffset - x > 0.0) // advanceX = tabOffset - x; // } -// -// fGlyphInfoBuffer[i].advanceX = advanceX; -// -// if (fGlyphInfoBuffer[i].charCode == '\n') { -// nextLine = true; -// lineBreak = true; -// fGlyphInfoBuffer[i].x = x; -// fGlyphInfoBuffer[i].y = y; -// } else if (fWidth > 0.0f && x + advanceX > fWidth) { -// if (charClassification == CHAR_CLASS_WHITESPACE) { -// advanceX = 0.0f; -// } else if (i > lineStart) { -// nextLine = true; -// // The current glyph extends outside the width, we need to wrap -// // to the next line. See what previous offset can be the end -// // of the line. -// int lineEnd = i - 1; -// while (lineEnd > lineStart -// && !can_end_line(fGlyphInfoBuffer, lineEnd, -// fGlyphInfoCount)) { -// lineEnd--; -// } -// -// if (lineEnd > lineStart) { -// // Found a place to perform a line break. -// i = lineEnd + 1; -// // Adjust the glyph info to point at the changed buffer -// // position -// _GetGlyphAdvance(i, advanceX, advanceY, escapementArray); -// } else { -// // Just break where we are. -// } -// } -// } -// -// if (nextLine) { -// // * Initialize the max ascent/descent of all preceding glyph infos -// // on the current/last line -// // * Adjust the baseline offset according to the max ascent -// // * Fill in the line index. -// unsigned lineEnd; -// if (lineBreak) -// lineEnd = i; -// else -// lineEnd = i - 1; -// -// float lineHeight = 0.0; -// _FinalizeLine(lineStart, lineEnd, lineIndex, y, -// lineHeight); -// -// // Start position of the next line -// x = fLineInset; -// y += lineHeight + fLineSpacing; -// -// if (lineBreak) -// lineStart = i + 1; -// else -// lineStart = i; -// -// lineIndex++; -// } -// -// if (!lineBreak && i < fGlyphInfoCount) { -// fGlyphInfoBuffer[i].x = x; -// fGlyphInfoBuffer[i].y = y; -// } -// -// x += advanceX; -// y += advanceY; -// } -// -// // The last line may not have been appended and initialized yet. -// if (lineStart <= fGlyphInfoCount - 1) { -// float lineHeight; -// _FinalizeLine(lineStart, fGlyphInfoCount - 1, lineIndex, y, lineHeight); -// } + + glyph.advanceX = advanceX; + + if (glyph.charCode == '\n') { + nextLine = true; + lineBreak = true; + glyph.x = x; + fGlyphInfos.Replace(i, glyph); + } else if (fWidth > 0.0f && x + advanceX > fWidth) { + fGlyphInfos.Replace(i, glyph); + if (charClassification == CHAR_CLASS_WHITESPACE) { + advanceX = 0.0f; + } else if (i > lineStart) { + nextLine = true; + // The current glyph extends outside the width, we need to wrap + // to the next line. See what previous offset can be the end + // of the line. + int lineEnd = i - 1; + while (lineEnd > lineStart + && !can_end_line(fGlyphInfos, lineEnd)) { + lineEnd--; + } + + if (lineEnd > lineStart) { + // Found a place to perform a line break. + i = lineEnd + 1; + + // Adjust the glyph info to point at the changed buffer + // position + glyph = fGlyphInfos.ItemAtFast(i); + advanceX = glyph.width; + } else { + // Just break where we are. + } + } + } + + if (nextLine) { + // * Initialize the max ascent/descent of all preceding glyph infos + // on the current/last line + // * Adjust the baseline offset according to the max ascent + // * Fill in the line index. + unsigned lineEnd; + if (lineBreak) + lineEnd = i; + else + lineEnd = i - 1; + + float lineHeight = 0.0; + _FinalizeLine(lineStart, lineEnd, lineIndex, y, lineHeight); + + // Start position of the next line + x = fParagraphStyle.LineInset(); + y += lineHeight + fParagraphStyle.LineSpacing(); + + if (lineBreak) + lineStart = i + 1; + else + lineStart = i; + + lineIndex++; + } + + if (!lineBreak && i < glyphCount) { + glyph.x = x; + fGlyphInfos.Replace(i, glyph); + } + + x += advanceX; + y += advanceY; + } + + // The last line may not have been appended and initialized yet. + if (lineStart <= glyphCount - 1) { + float lineHeight; + _FinalizeLine(lineStart, glyphCount - 1, lineIndex, y, lineHeight); + } } void ParagraphLayout::_Init() { - delete[] fGlyphInfoBuffer; - fGlyphInfoBuffer = NULL; - fGlpyhInfoBufferSize = 0; - fGlyphInfoCount = 0; + fGlyphInfos.Clear(); int spanCount = fTextSpans.CountItems(); for (int i = 0; i < spanCount; i++) { @@ -449,7 +425,7 @@ ParagraphLayout::_AppendGlyphInfos(const TextSpan& span) return true; const BString& text = span.Text(); - const BFont& font = span.Style().font; + const BFont& font = span.Style().Font(); // Allocate arrays float* escapementArray = new (std::nothrow) float[charCount]; @@ -464,112 +440,68 @@ ParagraphLayout::_AppendGlyphInfos(const TextSpan& span) float size = font.Size(); const char* c = text.String(); for (int i = 0; i < charCount; i++) { - _AppendGlyphInfo(UTF8ToCharCode(&c), ); - fGlyphInfoBuffer[i].charCode = ; - escapementArray[i] *= size; - } - -} - - -bool -ParagraphLayout::_AppendGlyphInfo(uint32 charCode, const TextStyleRef& style) -{ - // Enlarge buffer if necessary - if (fGlyphInfoCount == fGlyphInfoBufferSize) { - int size = fGlyphInfoBufferSize + 64; - - GlyphInfo* buffer = (GlyphInfo*) realloc(fGlyphInfoBuffer, - size * sizeof(GlyphInfo)); - if (buffer == NULL) + if (!_AppendGlyphInfo(UTF8ToCharCode(&c), escapementArray[i] * size, + span.Style())) { return false; - - fGlyphInfoBufferSize = size; - fGlyphInfoBuffer = buffer; + } } - // Store given information - fGlyphInfoBuffer[fGlyphInfoCount].charCode = charCode; - fGlyphInfoBuffer[fGlyphInfoCount].glyph = glyph; - fGlyphInfoBuffer[fGlyphInfoCount].x = 0; - fGlyphInfoBuffer[fGlyphInfoCount].y = 0; - fGlyphInfoBuffer[fGlyphInfoCount].advanceX = 0; - fGlyphInfoBuffer[fGlyphInfoCount].maxAscend = 0; - fGlyphInfoBuffer[fGlyphInfoCount].maxDescend = 0; - fGlyphInfoBuffer[fGlyphInfoCount].lineIndex = 0; - fGlyphInfoBuffer[fGlyphInfoCount].styleRun = styleRun; - - fGlyphInfoCount++; - return true; } -void -ParagraphLayout::_GetGlyphAdvance(int offset, float& advanceX, float& advanceY, - float escapementArray[]) const +bool +ParagraphLayout::_AppendGlyphInfo(uint32 charCode, float width, + const CharacterStyle& style) { - float additionalGlyphSpacing = 0.0f; - if (fGlyphInfoBuffer[offset].style.Get() != NULL) - additionalGlyphSpacing = fGlyphInfoBuffer[offset].style->glyphSpacing; - else - additionalGlyphSpacing = fGlyphSpacing; - - if (fGlyphInfoBuffer[offset].style.Get() != NULL - && fGlyphInfoBuffer[offset].style->width > 0.0f) { - // Use the metrics provided by the TextStyle - advanceX = fGlyphInfoBuffer[offset].style->width - + additionalGlyphSpacing; - } else { - advanceX = escapementArray[offset] + additionalGlyphSpacing; - advanceY = 0.0f; + if (style.Width() >= 0.0f) { + // Use the metrics provided by the CharacterStyle and override + // the font provided metrics passed in "width" + width = style.Width(); } + + width += style.GlyphSpacing(); + + GlyphInfo glyph(charCode, 0.0f, width, width, 0, style); + return fGlyphInfos.Add(glyph); } void -ParagraphLayout::_FinalizeLine(int lineStart, int lineEnd, int lineIndex, float y, - float& lineHeight) +ParagraphLayout::_FinalizeLine(int lineStart, int lineEnd, int lineIndex, + float y, float& lineHeight) { lineHeight = 0.0f; float maxAscent = 0.0f; float maxDescent = 0.0f; for (int i = lineStart; i <= lineEnd; i++) { - if (fGlyphInfoBuffer[i].style.Get() != NULL) { - if (fGlyphInfoBuffer[i].style->font.Size() > lineHeight) - lineHeight = fGlyphInfoBuffer[i].style->font.Size(); - if (fGlyphInfoBuffer[i].style->ascent > maxAscent) - maxAscent = fGlyphInfoBuffer[i].style->ascent; - if (fGlyphInfoBuffer[i].style->descent > maxDescent) - maxDescent = fGlyphInfoBuffer[i].style->descent; - } else { - if (fDefaultFont.Size() > lineHeight) - lineHeight = fDefaultFont.Size(); - if (fAscent > maxAscent) - maxAscent = fAscent; - if (fDescent > maxDescent) - maxDescent = fDescent; - } + GlyphInfo glyph = fGlyphInfos.ItemAtFast(i); + glyph.lineIndex = lineIndex; + fGlyphInfos.Replace(i, glyph); + + const CharacterStyle& style = glyph.style; + + if (style.Font().Size() > lineHeight) + lineHeight = style.Font().Size(); + + if (style.Ascent() > maxAscent) + maxAscent = style.Ascent(); + + if (style.Descent() > maxDescent) + maxDescent = style.Descent(); } - fLayoutLines.Add(LayoutLine(lineStart, y, lineHeight, maxAscent, maxDescent)); - - for (int i = lineStart; i <= lineEnd; i++) { - fGlyphInfoBuffer[i].maxAscent = maxAscent; - fGlyphInfoBuffer[i].maxDescent = maxDescent; - fGlyphInfoBuffer[i].lineIndex = lineIndex; - fGlyphInfoBuffer[i].y += maxAscent; - } + fLineInfos.Add(LineInfo(lineStart, y, lineHeight, maxAscent, maxDescent)); } void -ParagraphLayout::_DrawLine(BView* view, const LayoutLine& line) const +ParagraphLayout::_DrawLine(BView* view, const LineInfo& line) const { int textOffset = line.textOffset; int spanCount = line.layoutedSpans.CountItems(); - for (int i = 0; i < spanCount) { + for (int i = 0; i < spanCount; i++) { const TextSpan& span = line.layoutedSpans.ItemAtFast(i); _DrawSpan(view, span, textOffset); textOffset += span.CharCount(); @@ -579,15 +511,18 @@ ParagraphLayout::_DrawLine(BView* view, const LayoutLine& line) const void ParagraphLayout::_DrawSpan(BView* view, const TextSpan& span, - int textOffset) const + int32 textOffset) const { - float x = fGlyphInfoBuffer[textOffset].x; - float y = fGlyphInfoBuffer[textOffset].y; + const GlyphInfo& glyph = fGlyphInfos.ItemAtFast(textOffset); + const LineInfo& line = fLineInfos.ItemAtFast(glyph.lineIndex); + + float x = glyph.x; + float y = line.y + line.maxAscent; - const TextStyle& style = span.Style(); + const CharacterStyle& style = span.Style(); - view->SetFont(&style.font); - view->SetHighColor(style.fgColor); + view->SetFont(&style.Font()); + view->SetHighColor(style.ForegroundColor()); // TODO: Implement other style properties // TODO: Implement correct glyph spacing diff --git a/src/apps/haiku-depot/textview/ParagraphLayout.h b/src/apps/haiku-depot/textview/ParagraphLayout.h index 0ff8599c73..be838e55a8 100644 --- a/src/apps/haiku-depot/textview/ParagraphLayout.h +++ b/src/apps/haiku-depot/textview/ParagraphLayout.h @@ -9,26 +9,117 @@ #include #include -#include "GlyphInfo.h" #include "Paragraph.h" +#include "CharacterStyle.h" class BView; -class LayoutLine { +class GlyphInfo { public: - LayoutLine() + GlyphInfo() + : + charCode(0), + x(0.0f), + width(0.0f), + advanceX(0.0f), + lineIndex(0), + style() + { + } + + GlyphInfo(uint32 charCode, float x, float width, float advanceX, + int32 lineIndex, const CharacterStyle& style) + : + charCode(charCode), + x(x), + width(width), + advanceX(advanceX), + lineIndex(lineIndex), + style(style) + { + } + + GlyphInfo(const GlyphInfo& other) + : + charCode(other.charCode), + x(other.x), + width(other.width), + advanceX(other.advanceX), + lineIndex(other.lineIndex), + style(other.style) + { + } + + GlyphInfo& operator=(const GlyphInfo& other) + { + charCode = other.charCode; + x = other.x; + width = other.width; + advanceX = other.advanceX; + lineIndex = other.lineIndex; + style = other.style; + return *this; + } + + bool operator==(const GlyphInfo& other) const + { + return charCode == other.charCode + && x == other.x + && width == other.width + && advanceX == other.advanceX + && lineIndex == other.lineIndex + && style == other.style; + } + + bool operator!=(const GlyphInfo& other) const + { + return !(*this == other); + } + +public: + uint32 charCode; + + float x; + float width; + float advanceX; + + int32 lineIndex; + + CharacterStyle style; +}; + + +typedef List GlyphInfoList; + + +class LineInfo { +public: + LineInfo() : textOffset(0), y(0.0f), height(0.0f), maxAscent(0.0f), - maxDescent(0.0f) + maxDescent(0.0f), + layoutedSpans() { } - LayoutLine(const LayoutLine& other) + LineInfo(int32 textOffset, float y, float height, float maxAscent, + float maxDescent) + : + textOffset(textOffset), + y(y), + height(height), + maxAscent(maxAscent), + maxDescent(maxDescent), + layoutedSpans() + { + } + + LineInfo(const LineInfo& other) : textOffset(other.textOffset), y(other.y), @@ -39,7 +130,7 @@ public: { } - LayoutLine& operator=(const LayoutLine& other) + LineInfo& operator=(const LineInfo& other) { textOffset = other.textOffset; y = other.y; @@ -50,23 +141,23 @@ public: return *this; } - bool operator==(const LayoutLine& other) const + bool operator==(const LineInfo& other) const { return textOffset == other.textOffset && y == other.y && height == other.height && maxAscent == other.maxAscent && maxDescent == other.maxDescent - && layoutedSpans == otner.layoutedSpans; + && layoutedSpans == other.layoutedSpans; } - bool operator!=(const LayoutLine& other) const + bool operator!=(const LineInfo& other) const { return !(*this == other); } public: - int textOffset; + int32 textOffset; float y; float height; float maxAscent; @@ -76,7 +167,7 @@ public: }; -typedef List LayoutLineList; +typedef List LineInfoList; class ParagraphLayout : public BReferenceable { @@ -86,9 +177,7 @@ public: ParagraphLayout(const ParagraphLayout& other); virtual ~ParagraphLayout(); - void SetParagraph(const ::Paragraph& paragraph); - const ::Paragraph& Paragraph() const - { return fParagraph; } + void SetParagraph(const Paragraph& paragraph); void SetWidth(float width); float Width() const @@ -104,32 +193,26 @@ private: void _Init(); bool _AppendGlyphInfos(const TextSpan& span); bool _AppendGlyphInfo(uint32 charCode, - const TextStyleRef& span); + float advanceX, + const CharacterStyle& style); - void _GetGlyphAdvance(int offset, - float& advanceX, float& advanceY, - float escapementArray[]) const; - void _FinalizeLine(int lineStart, int lineEnd, int lineIndex, float y, float& lineHeight); void _DrawLine(BView* view, - const LayoutLine& line) const; + const LineInfo& line) const; void _DrawSpan(BView* view, const TextSpan& span, - int textOffset) const; + int32 textOffset) const; private: TextSpanList fTextSpans; + ParagraphStyle fParagraphStyle; float fWidth; - bool fLayoutValid; - GlyphInfo* fGlyphInfoBuffer; - int fGlpyhInfoBufferSize; - int fGlyphInfoCount; - - LayoutLineList fLayoutLines; + GlyphInfoList fGlyphInfos; + LineInfoList fLineInfos; }; #endif // PARAGRAPH_LAYOUT_H