summaryrefslogtreecommitdiffstats
path: root/layout/generic/JustificationUtils.h
blob: 1be36956b03bb3fc01e763ca348ae36839cf819a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_JustificationUtils_h_
#define mozilla_JustificationUtils_h_

#include "mozilla/Attributes.h"
#include "nsCoord.h"

namespace mozilla {

/**
 * Jutification Algorithm
 *
 * The justification algorithm is based on expansion opportunities
 * between justifiable clusters.  By this algorithm, there is one
 * expansion opportunity at each side of a justifiable cluster, and
 * at most one opportunity between two clusters. For example, if there
 * is a line in a Chinese document is: "你好世界hello world", then
 * the expansion opportunities (marked as '*') would be:
 *
 *                    你*好*世*界*hello*' '*world
 *
 * The spacing left in a line will then be distributed equally to each
 * opportunities. Because we want that, only justifiable clusters get
 * expanded, and the split point between two justifiable clusters would
 * be at the middle of the spacing, each expansion opportunities will be
 * filled by two justification gaps. The example above would be:
 *
 *              你 | 好 | 世 | 界  |hello|  ' '  |world
 *
 * In the algorithm, information about expansion opportunities is stored
 * in structure JustificationInfo, and the assignment of justification
 * gaps is in structure JustificationAssignment.
 */

struct JustificationInfo
{
  // Number of expansion opportunities inside a span. It doesn't include
  // any opportunities between this span and the one before or after.
  int32_t mInnerOpportunities;
  // The justifiability of the start and end sides of the span.
  bool mIsStartJustifiable;
  bool mIsEndJustifiable;

  constexpr JustificationInfo()
    : mInnerOpportunities(0)
    , mIsStartJustifiable(false)
    , mIsEndJustifiable(false)
  {
  }

  // Claim that the last opportunity should be cancelled
  // because the trailing space just gets trimmed.
  void CancelOpportunityForTrimmedSpace()
  {
    if (mInnerOpportunities > 0) {
      mInnerOpportunities--;
    } else {
      // There is no inner opportunities, hence the whole frame must
      // contain only the trimmed space, because any content before
      // space would cause an inner opportunity. The space made each
      // side justifiable, which should be cancelled now.
      mIsStartJustifiable = false;
      mIsEndJustifiable = false;
    }
  }
};

struct JustificationAssignment
{
  // There are at most 2 gaps per end, so it is enough to use 2 bits.
  uint8_t mGapsAtStart : 2;
  uint8_t mGapsAtEnd : 2;

  constexpr JustificationAssignment()
    : mGapsAtStart(0)
    , mGapsAtEnd(0)
  {
  }

  int32_t TotalGaps() const { return mGapsAtStart + mGapsAtEnd; }
};

struct JustificationApplicationState
{
  struct
  {
    // The total number of justification gaps to be processed.
    int32_t mCount;
    // The number of justification gaps which have been handled.
    int32_t mHandled;
  } mGaps;

  struct
  {
    // The total spacing left in a line before justification.
    nscoord mAvailable;
    // The spacing has been consumed by handled justification gaps.
    nscoord mConsumed;
  } mWidth;

  JustificationApplicationState(int32_t aGaps, nscoord aWidth)
  {
    mGaps.mCount = aGaps;
    mGaps.mHandled = 0;
    mWidth.mAvailable = aWidth;
    mWidth.mConsumed = 0;
  }

  bool IsJustifiable() const
  {
    return mGaps.mCount > 0 && mWidth.mAvailable > 0;
  }

  nscoord Consume(int32_t aGaps)
  {
    mGaps.mHandled += aGaps;
    nscoord newAllocate = (mWidth.mAvailable * mGaps.mHandled) / mGaps.mCount;
    nscoord deltaWidth = newAllocate - mWidth.mConsumed;
    mWidth.mConsumed = newAllocate;
    return deltaWidth;
  }
};

class JustificationUtils
{
public:
  // Compute justification gaps should be applied on a unit.
  static int32_t CountGaps(const JustificationInfo& aInfo,
                           const JustificationAssignment& aAssign)
  {
    // Justification gaps include two gaps for each inner opportunities
    // and the gaps given assigned to the ends.
    return aInfo.mInnerOpportunities * 2 + aAssign.TotalGaps();
  }
};

} // namespace mozilla

#endif /* !defined(mozilla_JustificationUtils_h_) */