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