4#include "Stroika/Frameworks/StroikaPreComp.h"
9#include "Stroika/Foundation/Containers/Support/ReserveTweaks.h"
11#include "BiDiLayoutEngine.h"
17using namespace Stroika::Frameworks;
18using namespace Stroika::Frameworks::Led;
28#ifndef qDebugHack_ReverseDirections_
29#define qDebugHack_ReverseDirections_ 0
37#ifndef qDebugHack_UpperCaseCharsTratedAsRTL_
38#define qDebugHack_UpperCaseCharsTratedAsRTL_ 0
50TextDirection TextLayoutBlock::GetCharacterDirection (
size_t realCharOffset)
const
52 vector<ScriptRunElt> runs = GetScriptRuns ();
53 for (
auto i = runs.begin (); i != runs.end (); ++i) {
54 if ((*i).fRealStart <= realCharOffset and realCharOffset < (*i).fRealEnd) {
55 return (*i).fDirection;
66size_t TextLayoutBlock::MapRealOffsetToVirtual (
size_t )
const
76size_t TextLayoutBlock::MapVirtualOffsetToReal (
size_t i)
const
78 Require (i < GetTextLength ());
79 vector<ScriptRunElt> runs = GetScriptRuns ();
80 for (
auto runIt = runs.begin (); runIt != runs.end (); ++runIt) {
81 const ScriptRunElt& se = *runIt;
82 if (se.fVirtualStart <= i and i < se.fVirtualEnd) {
83 if (se.fDirection == eLeftToRight) {
84 size_t result = (i - se.fVirtualStart) + se.fRealStart;
85 Ensure (result < GetTextLength ());
89 size_t segLen = se.fRealEnd - se.fRealStart;
90 size_t result = (segLen - (i - se.fVirtualStart)) - 1 + se.fRealStart;
91 Ensure (result < GetTextLength ());
100void TextLayoutBlock::CopyOutRealText (
const ScriptRunElt& scriptRunElt, Led_tChar* buf)
const
102 const Led_tChar* s =
nullptr;
103 const Led_tChar* e =
nullptr;
104 PeekAtRealText (scriptRunElt, &s, &e);
108void TextLayoutBlock::CopyOutVirtualText (
const ScriptRunElt& scriptRunElt, Led_tChar* buf)
const
110 const Led_tChar* s =
nullptr;
111 const Led_tChar* e =
nullptr;
112 PeekAtVirtualText (scriptRunElt, &s, &e);
116void TextLayoutBlock::PeekAtRealText (
const ScriptRunElt& scriptRunElt,
const Led_tChar** startText,
const Led_tChar** endText)
const
120 PeekAtRealText_ (startText, endText);
121 *startText += scriptRunElt.fRealStart;
122 size_t len = scriptRunElt.fRealEnd - scriptRunElt.fRealStart;
123 Assert (*endText - *startText >=
static_cast<ptrdiff_t
> (len));
124 *endText = *startText + len;
127Led_tString TextLayoutBlock::GetRealText ()
const
129 const Led_tChar* s =
nullptr;
130 const Led_tChar* e =
nullptr;
131 PeekAtRealText_ (&s, &e);
132 return Led_tString{s, e};
135Led_tString TextLayoutBlock::GetRealText (
const ScriptRunElt& scriptRunElt)
const
137 const Led_tChar* s =
nullptr;
138 const Led_tChar* e =
nullptr;
139 PeekAtRealText (scriptRunElt, &s, &e);
140 return Led_tString{s, e};
143void TextLayoutBlock::PeekAtVirtualText (
const ScriptRunElt& scriptRunElt,
const Led_tChar** startText,
const Led_tChar** endText)
const
147 PeekAtVirtualText_ (startText, endText);
148 *startText += scriptRunElt.fVirtualStart;
149 size_t len = scriptRunElt.fVirtualEnd - scriptRunElt.fVirtualStart;
150 Assert (*endText - *startText >=
static_cast<ptrdiff_t
> (len));
151 *endText = *startText + len;
154Led_tString TextLayoutBlock::GetVirtualText ()
const
156 const Led_tChar* s =
nullptr;
157 const Led_tChar* e =
nullptr;
158 PeekAtVirtualText_ (&s, &e);
159 return Led_tString{s, e};
162Led_tString TextLayoutBlock::GetVirtualText (
const ScriptRunElt& scriptRunElt)
const
164 const Led_tChar* s =
nullptr;
165 const Led_tChar* e =
nullptr;
166 PeekAtVirtualText (scriptRunElt, &s, &e);
167 return Led_tString{s, e};
170#if qStroika_Foundation_Debug_AssertionsChecked
171void TextLayoutBlock::Invariant_ ()
const
173 size_t len = GetTextLength ();
175 vector<ScriptRunElt> scriptRuns = GetScriptRuns ();
179 for (
auto j = scriptRuns.begin (); j != scriptRuns.end (); ++j) {
180 Assert ((*j).fRealStart < len);
181 Assert ((*j).fRealEnd <= len);
182 Assert ((*j).fVirtualStart < len);
183 Assert ((*j).fVirtualEnd <= len);
184 Assert ((*j).fRealStart < (*j).fRealEnd);
194 for (
size_t i = 0; i < len; ++i) {
196 bool foundReal =
false;
197 bool foundVirtual =
false;
198 size_t nRealFound = 0;
199 size_t nVirtualFound = 0;
200 for (
auto j = scriptRuns.begin (); j != scriptRuns.end (); ++j) {
201 if ((*j).fRealStart <= i and i < (*j).fRealEnd) {
202 Assert (not foundReal);
205 Assert ((*j).fVirtualStart < (*j).fVirtualEnd);
206 if ((*j).fVirtualStart <= i and i < (*j).fVirtualEnd) {
207 Assert (not foundVirtual);
210 nRealFound += (*j).fRealEnd - (*j).fRealStart;
211 nVirtualFound += (*j).fVirtualEnd - (*j).fVirtualStart;
214 Assert (foundVirtual);
215 Assert (nRealFound == len);
216 Assert (nVirtualFound == len);
222bool TextLayoutBlock::operator== (
const TextLayoutBlock& rhs)
const
224 if (this->GetTextLength () != rhs.GetTextLength ()) {
227 if (this->GetRealText () != rhs.GetRealText ()) {
230 if (this->GetVirtualText () != rhs.GetVirtualText ()) {
233 vector<TextLayoutBlock::ScriptRunElt> lhsSR = this->GetScriptRuns ();
234 vector<TextLayoutBlock::ScriptRunElt> rhsSR = rhs.GetScriptRuns ();
235 if (lhsSR.size () != rhsSR.size ()) {
238 for (
size_t i = 0; i < lhsSR.size (); ++i) {
239 if (lhsSR[i] != rhsSR[i]) {
251TextLayoutBlock_Basic::TextLayoutBlock_Basic (
const Led_tChar* realText,
const Led_tChar* realTextEnd)
257 Construct (realText, realTextEnd,
nullptr);
260TextLayoutBlock_Basic::TextLayoutBlock_Basic (
const Led_tChar* realText,
const Led_tChar* realTextEnd, TextDirection initialDirection)
266 Construct (realText, realTextEnd, &initialDirection);
269void TextLayoutBlock_Basic::Construct (
const Led_tChar* realText,
const Led_tChar* realTextEnd, [[maybe_unused]]
const TextDirection* initialDirection)
271 size_t textLength = realTextEnd - realText;
272 fTextLength = textLength;
273 fRealText.GrowToSize (textLength);
274 fVirtualText.GrowToSize (textLength);
275 copy (realText, realText + textLength,
static_cast<Led_tChar*
> (fRealText));
277#if qDebugHack_UpperCaseCharsTratedAsRTL_
278 for (
size_t i = 0; i < textLength; ++i) {
279 if (
'A' <= fRealText[i] and fRealText[i] <=
'Z') {
280 fRealText[i] = 0xfe7d;
286 Construct_Default ();
288 if constexpr (qDebugHack_ReverseDirections_) {
289 for (
auto i = fScriptRuns.begin (); i != fScriptRuns.end (); ++i) {
290 ScriptRunElt& se = *i;
291 TextDirection newDir = (se.fDirection == eLeftToRight) ? eRightToLeft : eLeftToRight;
292 se.fDirection = newDir;
294 size_t runLen = se.fVirtualEnd - se.fVirtualStart;
296 for (
size_t j = se.fVirtualStart; j < se.fVirtualEnd; ++j) {
297 reverseBuf[runLen - 1 - (j - se.fVirtualStart)] = fVirtualText[j];
299 copy (
static_cast<Led_tChar*
> (reverseBuf),
static_cast<Led_tChar*
> (reverseBuf) + runLen,
300 static_cast<Led_tChar*
> (fVirtualText) + se.fVirtualStart);
307void TextLayoutBlock_Basic::Construct_Default ()
310 copy (
static_cast<Led_tChar*
> (fRealText),
static_cast<Led_tChar*
> (fRealText) + fTextLength,
static_cast<Led_tChar*
> (fVirtualText));
311 if (fTextLength != 0) {
312 ScriptRunElt thisCharElt;
313 thisCharElt.fDirection = eLeftToRight;
314 thisCharElt.fRealStart = 0;
315 thisCharElt.fRealEnd = fTextLength;
316 thisCharElt.fVirtualStart = 0;
317 thisCharElt.fVirtualEnd = fTextLength;
318 fScriptRuns.push_back (thisCharElt);
322void TextLayoutBlock_Basic::PeekAtRealText_ (
const Led_tChar** startText,
const Led_tChar** endText)
const
326 *startText = fRealText.data ();
327 *endText = fRealText.data () + fTextLength;
330void TextLayoutBlock_Basic::PeekAtVirtualText_ (
const Led_tChar** startText,
const Led_tChar** endText)
const
334 *startText = fVirtualText.data ();
335 *endText = fVirtualText.data () + fTextLength;
338vector<TextLayoutBlock::ScriptRunElt> TextLayoutBlock_Basic::GetScriptRuns ()
const
348TextLayoutBlock_Copy::TextLayoutBlock_Copy (
const TextLayoutBlock& from)
353 size_t strLength = from.GetTextLength ();
354 vector<ScriptRunElt> scriptRuns = from.GetScriptRuns ();
357 size_t neededSize =
sizeof (BlockRep);
358 neededSize +=
sizeof (Led_tChar) * strLength;
359 neededSize +=
sizeof (Led_tChar) * strLength;
360 neededSize +=
sizeof (ScriptRunElt) * scriptRuns.size ();
361 auto deleter = [] (BlockRep* br) {
delete[] (
reinterpret_cast<byte*
> (br)); };
362 fRep = shared_ptr<BlockRep>{
reinterpret_cast<BlockRep*
> (
new byte[neededSize]), deleter};
364 fRep->fTextLength = strLength;
366 fRep->fRealText =
reinterpret_cast<Led_tChar*
> (fRep.get () + 1);
368 const Led_tChar* s =
nullptr;
369 const Led_tChar* e =
nullptr;
370 from.PeekAtRealText_ (&s, &e);
371 copy (s, e,
const_cast<Led_tChar*
> (fRep->fRealText));
374 fRep->fVirtualText = fRep->fRealText + strLength;
376 const Led_tChar* s =
nullptr;
377 const Led_tChar* e =
nullptr;
378 from.PeekAtVirtualText_ (&s, &e);
379 copy (s, e,
const_cast<Led_tChar*
> (fRep->fVirtualText));
382 fRep->fScriptRuns =
reinterpret_cast<const ScriptRunElt*
> (fRep->fVirtualText + strLength);
383 fRep->fScriptRunsEnd = fRep->fScriptRuns + scriptRuns.size ();
384 copy (scriptRuns.begin (), scriptRuns.end (),
const_cast<ScriptRunElt*
> (fRep->fScriptRuns));
387TextLayoutBlock_Copy::TextLayoutBlock_Copy (
const TextLayoutBlock_Copy& from)
393void TextLayoutBlock_Copy::PeekAtRealText_ (
const Led_tChar** startText,
const Led_tChar** endText)
const
397 *startText = fRep->fRealText;
398 *endText = fRep->fRealText + fRep->fTextLength;
401void TextLayoutBlock_Copy::PeekAtVirtualText_ (
const Led_tChar** startText,
const Led_tChar** endText)
const
405 *startText = fRep->fVirtualText;
406 *endText = fRep->fVirtualText + fRep->fTextLength;
409vector<TextLayoutBlock_Copy::ScriptRunElt> TextLayoutBlock_Copy::GetScriptRuns ()
const
411 return vector<ScriptRunElt> (fRep->fScriptRuns, fRep->fScriptRunsEnd);
419void TextLayoutBlock_Copy::BlockRep::operator
delete (
void* p)
422 delete[] (
reinterpret_cast<char*
> (p));
430TextLayoutBlock_VirtualSubset::TextLayoutBlock_VirtualSubset (
const TextLayoutBlock& subsetOf,
size_t start,
size_t end)
431 : fSubsetOf{subsetOf}
434 , fRealText{end - start}
436 vector<ScriptRunElt> origRuns = fSubsetOf.GetScriptRuns ();
437 size_t offsetSoFar = 0;
438 const Led_tChar* fullRealText = fSubsetOf.PeekAtRealText ();
439 for (
auto i = origRuns.begin (); i != origRuns.end (); ++i) {
440 size_t offsetStart = (*i).fVirtualStart;
441 size_t offsetEnd = (*i).fVirtualEnd;
443 size_t runRelStart = max (offsetStart,
static_cast<size_t> (fStart));
444 size_t runRelEnd = min (offsetEnd,
static_cast<size_t> (fEnd));
445 if (runRelStart < runRelEnd) {
448 Assert (runRelStart >= fStart);
449 Assert (runRelEnd >= fStart);
450 s.fVirtualStart = runRelStart - fStart;
451 s.fVirtualEnd = runRelEnd - fStart;
461 Assert (runRelEnd >= runRelStart);
462 size_t runEltLen = runRelEnd - runRelStart;
463 copy (fullRealText + s.fRealStart, fullRealText + s.fRealStart + runEltLen, fRealText.data () + offsetSoFar);
464 s.fRealStart = offsetSoFar;
465 offsetSoFar += runEltLen;
466 s.fRealEnd = offsetSoFar;
467 Containers::Support::ReserveTweaks::Reserve4Add1 (fScriptRuns);
468 fScriptRuns.push_back (s);
475void TextLayoutBlock_VirtualSubset::PeekAtRealText_ (
const Led_tChar** startText,
const Led_tChar** endText)
const
479 *startText = fRealText.data ();
480 *endText = *startText + (fEnd - fStart);
483void TextLayoutBlock_VirtualSubset::PeekAtVirtualText_ (
const Led_tChar** startText,
const Led_tChar** endText)
const
487 fSubsetOf.PeekAtVirtualText_ (startText, endText);
488 *startText += fStart;
489 Assert (fStart <= fEnd);
490 Assert (*endText - *startText >=
static_cast<int> (fEnd - fStart));
491 *endText = *startText + (fEnd - fStart);
494vector<TextLayoutBlock::ScriptRunElt> TextLayoutBlock_VirtualSubset::GetScriptRuns ()
const
#define RequireNotNull(p)
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...