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