1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_JustificationUtils_h_ 8 #define mozilla_JustificationUtils_h_ 9 10 #include "mozilla/Attributes.h" 11 #include "nsCoord.h" 12 13 namespace mozilla { 14 15 /** 16 * Jutification Algorithm 17 * 18 * The justification algorithm is based on expansion opportunities 19 * between justifiable clusters. By this algorithm, there is one 20 * expansion opportunity at each side of a justifiable cluster, and 21 * at most one opportunity between two clusters. For example, if there 22 * is a line in a Chinese document is: "你好世界hello world", then 23 * the expansion opportunities (marked as '*') would be: 24 * 25 * 你*好*世*界*hello*' '*world 26 * 27 * The spacing left in a line will then be distributed equally to each 28 * opportunities. Because we want that, only justifiable clusters get 29 * expanded, and the split point between two justifiable clusters would 30 * be at the middle of the spacing, each expansion opportunities will be 31 * filled by two justification gaps. The example above would be: 32 * 33 * 你 | 好 | 世 | 界 |hello| ' ' |world 34 * 35 * In the algorithm, information about expansion opportunities is stored 36 * in structure JustificationInfo, and the assignment of justification 37 * gaps is in structure JustificationAssignment. 38 */ 39 40 struct JustificationInfo { 41 // Number of expansion opportunities inside a span. It doesn't include 42 // any opportunities between this span and the one before or after. 43 int32_t mInnerOpportunities; 44 // The justifiability of the start and end sides of the span. 45 bool mIsStartJustifiable; 46 bool mIsEndJustifiable; 47 JustificationInfoJustificationInfo48 constexpr JustificationInfo() 49 : mInnerOpportunities(0), 50 mIsStartJustifiable(false), 51 mIsEndJustifiable(false) {} 52 53 // Claim that the last opportunity should be cancelled 54 // because the trailing space just gets trimmed. CancelOpportunityForTrimmedSpaceJustificationInfo55 void CancelOpportunityForTrimmedSpace() { 56 if (mInnerOpportunities > 0) { 57 mInnerOpportunities--; 58 } else { 59 // There is no inner opportunities, hence the whole frame must 60 // contain only the trimmed space, because any content before 61 // space would cause an inner opportunity. The space made each 62 // side justifiable, which should be cancelled now. 63 mIsStartJustifiable = false; 64 mIsEndJustifiable = false; 65 } 66 } 67 }; 68 69 struct JustificationAssignment { 70 // There are at most 2 gaps per end, so it is enough to use 2 bits. 71 uint8_t mGapsAtStart : 2; 72 uint8_t mGapsAtEnd : 2; 73 JustificationAssignmentJustificationAssignment74 constexpr JustificationAssignment() : mGapsAtStart(0), mGapsAtEnd(0) {} 75 TotalGapsJustificationAssignment76 int32_t TotalGaps() const { return mGapsAtStart + mGapsAtEnd; } 77 }; 78 79 struct JustificationApplicationState { 80 struct { 81 // The total number of justification gaps to be processed. 82 int32_t mCount; 83 // The number of justification gaps which have been handled. 84 int32_t mHandled; 85 } mGaps; 86 87 struct { 88 // The total spacing left in a line before justification. 89 nscoord mAvailable; 90 // The spacing has been consumed by handled justification gaps. 91 nscoord mConsumed; 92 } mWidth; 93 JustificationApplicationStateJustificationApplicationState94 JustificationApplicationState(int32_t aGaps, nscoord aWidth) { 95 mGaps.mCount = aGaps; 96 mGaps.mHandled = 0; 97 mWidth.mAvailable = aWidth; 98 mWidth.mConsumed = 0; 99 } 100 IsJustifiableJustificationApplicationState101 bool IsJustifiable() const { 102 return mGaps.mCount > 0 && mWidth.mAvailable > 0; 103 } 104 ConsumeJustificationApplicationState105 nscoord Consume(int32_t aGaps) { 106 mGaps.mHandled += aGaps; 107 nscoord newAllocate = (mWidth.mAvailable * mGaps.mHandled) / mGaps.mCount; 108 nscoord deltaWidth = newAllocate - mWidth.mConsumed; 109 mWidth.mConsumed = newAllocate; 110 return deltaWidth; 111 } 112 }; 113 114 class JustificationUtils { 115 public: 116 // Compute justification gaps should be applied on a unit. CountGaps(const JustificationInfo & aInfo,const JustificationAssignment & aAssign)117 static int32_t CountGaps(const JustificationInfo& aInfo, 118 const JustificationAssignment& aAssign) { 119 // Justification gaps include two gaps for each inner opportunities 120 // and the gaps given assigned to the ends. 121 return aInfo.mInnerOpportunities * 2 + aAssign.TotalGaps(); 122 } 123 }; 124 125 } // namespace mozilla 126 127 #endif /* !defined(mozilla_JustificationUtils_h_) */ 128