aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/fonts/generate_fir_coeff.py
blob: f5cc5e55a07fcd4c8a6d4e8a87ca81c200fb92cb (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
#!/usr/bin/python

'''
Copyright 2013 Google Inc.

Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
'''

import math
import pprint

def withinStdDev(n):
  """Returns the percent of samples within n std deviations of the normal."""
  return math.erf(n / math.sqrt(2))

def withinStdDevRange(a, b):
  """Returns the percent of samples within the std deviation range a, b"""
  if b < a:
    return 0;

  if a < 0:
    if b < 0:
      return (withinStdDev(-a) - withinStdDev(-b)) / 2;
    else:
      return (withinStdDev(-a) + withinStdDev(b)) / 2;
  else:
    return (withinStdDev(b) - withinStdDev(a)) / 2;


# We have some smudged samples which represent the average coverage of a range.
# We have a 'center' which may not line up with those samples.
# From center make a normal where 5 sample widths out is at 3 std deviations.
# The first and last samples may not be fully covered.

# This is the sub-sample shift for each set of FIR coefficients
#   (the centers of the lcds in the samples)
# Each subpxl takes up 1/3 of a pixel,
#   so they are centered at x=(i/n+1/2n), or 1/6, 3/6, 5/6 of a pixel.
# Each sample takes up 1/4 of a pixel,
#   so the results fall at (x*4)%1, or 2/3, 0, 1/3 of a sample.
samples_per_pixel = 4
subpxls_per_pixel = 3
#sample_offsets is (frac, int) in sample units.
sample_offsets = [
  math.modf(
    (float(subpxl_index)/subpxls_per_pixel + 1.0/(2.0*subpxls_per_pixel))
    * samples_per_pixel
  ) for subpxl_index in range(subpxls_per_pixel)
]

#How many samples to consider to the left and right of the subpxl center.
sample_units_width = 5

#The std deviation at sample_units_width.
std_dev_max = 3

#The target sum is in some fixed point representation.
#Values larger the 1 in fixed point simulate ink spread.
target_sum = 0x110

for sample_offset, sample_align in sample_offsets:
  coeffs = []
  coeffs_rounded = []

  #We start at sample_offset - sample_units_width
  current_sample_left = sample_offset - sample_units_width
  current_std_dev_left = -std_dev_max

  done = False
  while not done:
    current_sample_right = math.floor(current_sample_left + 1)
    if current_sample_right > sample_offset + sample_units_width:
      done = True
      current_sample_right = sample_offset + sample_units_width
    current_std_dev_right = current_std_dev_left + (
      (current_sample_right - current_sample_left) / sample_units_width
    ) * std_dev_max

    coverage = withinStdDevRange(current_std_dev_left, current_std_dev_right)
    coeffs.append(coverage * target_sum)
    coeffs_rounded.append(int(round(coverage * target_sum)))

    current_sample_left = current_sample_right
    current_std_dev_left = current_std_dev_right

  # Have the numbers, but rounding needs to add up to target_sum.
  delta = 0
  coeffs_rounded_sum = sum(coeffs_rounded)
  if coeffs_rounded_sum > target_sum:
    # The coeffs add up to too much.
    # Subtract 1 from the ones which were rounded up the most.
    delta = -1

  if coeffs_rounded_sum < target_sum:
    # The coeffs add up to too little.
    # Add 1 to the ones which were rounded down the most.
    delta = 1

  if delta:
    print "Initial sum is 0x%0.2X, adjusting." % (coeffs_rounded_sum,)
    coeff_diff = [(coeff_rounded - coeff) * delta
                  for coeff, coeff_rounded in zip(coeffs, coeffs_rounded)]

    class IndexTracker:
      def __init__(self, index, item):
        self.index = index
        self.item = item
      def __lt__(self, other):
        return self.item < other.item
      def __repr__(self):
        return "arr[%d] == %s" % (self.index, repr(self.item))

    coeff_pkg = [IndexTracker(i, diff) for i, diff in enumerate(coeff_diff)]
    coeff_pkg.sort()

    # num_elements_to_force_round better be < (2 * sample_units_width + 1) or
    # * our math was wildy wrong
    # * an awful lot of the curve is out side our sample
    # either is pretty bad, and probably means the results will not be useful.
    num_elements_to_force_round = abs(coeffs_rounded_sum - target_sum)
    for i in xrange(num_elements_to_force_round):
      print "Adding %d to index %d to force round %f." % (
          delta, coeff_pkg[i].index, coeffs[coeff_pkg[i].index])
      coeffs_rounded[coeff_pkg[i].index] += delta

  print "Prepending %d 0x00 for allignment." % (sample_align,)
  coeffs_rounded_aligned = ([0] * int(sample_align)) + coeffs_rounded

  print ', '.join(["0x%0.2X" % coeff_rounded
                   for coeff_rounded in coeffs_rounded_aligned])
  print sum(coeffs), hex(sum(coeffs_rounded))
  print