Home | History | Annotate | Download | only in fonts
      1 #!/usr/bin/python
      2 
      3 '''
      4 Copyright 2013 Google Inc.
      5 
      6 Use of this source code is governed by a BSD-style license that can be
      7 found in the LICENSE file.
      8 '''
      9 
     10 import math
     11 import pprint
     12 
     13 def withinStdDev(n):
     14   """Returns the percent of samples within n std deviations of the normal."""
     15   return math.erf(n / math.sqrt(2))
     16 
     17 def withinStdDevRange(a, b):
     18   """Returns the percent of samples within the std deviation range a, b"""
     19   if b < a:
     20     return 0;
     21 
     22   if a < 0:
     23     if b < 0:
     24       return (withinStdDev(-a) - withinStdDev(-b)) / 2;
     25     else:
     26       return (withinStdDev(-a) + withinStdDev(b)) / 2;
     27   else:
     28     return (withinStdDev(b) - withinStdDev(a)) / 2;
     29 
     30 
     31 # We have some smudged samples which represent the average coverage of a range.
     32 # We have a 'center' which may not line up with those samples.
     33 # From center make a normal where 5 sample widths out is at 3 std deviations.
     34 # The first and last samples may not be fully covered.
     35 
     36 # This is the sub-sample shift for each set of FIR coefficients
     37 #   (the centers of the lcds in the samples)
     38 # Each subpxl takes up 1/3 of a pixel,
     39 #   so they are centered at x=(i/n+1/2n), or 1/6, 3/6, 5/6 of a pixel.
     40 # Each sample takes up 1/4 of a pixel,
     41 #   so the results fall at (x*4)%1, or 2/3, 0, 1/3 of a sample.
     42 samples_per_pixel = 4
     43 subpxls_per_pixel = 3
     44 #sample_offsets is (frac, int) in sample units.
     45 sample_offsets = [
     46   math.modf(
     47     (float(subpxl_index)/subpxls_per_pixel + 1.0/(2.0*subpxls_per_pixel))
     48     * samples_per_pixel
     49   ) for subpxl_index in range(subpxls_per_pixel)
     50 ]
     51 
     52 #How many samples to consider to the left and right of the subpxl center.
     53 sample_units_width = 5
     54 
     55 #The std deviation at sample_units_width.
     56 std_dev_max = 3
     57 
     58 #The target sum is in some fixed point representation.
     59 #Values larger the 1 in fixed point simulate ink spread.
     60 target_sum = 0x110
     61 
     62 for sample_offset, sample_align in sample_offsets:
     63   coeffs = []
     64   coeffs_rounded = []
     65 
     66   #We start at sample_offset - sample_units_width
     67   current_sample_left = sample_offset - sample_units_width
     68   current_std_dev_left = -std_dev_max
     69 
     70   done = False
     71   while not done:
     72     current_sample_right = math.floor(current_sample_left + 1)
     73     if current_sample_right > sample_offset + sample_units_width:
     74       done = True
     75       current_sample_right = sample_offset + sample_units_width
     76     current_std_dev_right = current_std_dev_left + (
     77       (current_sample_right - current_sample_left) / sample_units_width
     78     ) * std_dev_max
     79 
     80     coverage = withinStdDevRange(current_std_dev_left, current_std_dev_right)
     81     coeffs.append(coverage * target_sum)
     82     coeffs_rounded.append(int(round(coverage * target_sum)))
     83 
     84     current_sample_left = current_sample_right
     85     current_std_dev_left = current_std_dev_right
     86 
     87   # Have the numbers, but rounding needs to add up to target_sum.
     88   delta = 0
     89   coeffs_rounded_sum = sum(coeffs_rounded)
     90   if coeffs_rounded_sum > target_sum:
     91     # The coeffs add up to too much.
     92     # Subtract 1 from the ones which were rounded up the most.
     93     delta = -1
     94 
     95   if coeffs_rounded_sum < target_sum:
     96     # The coeffs add up to too little.
     97     # Add 1 to the ones which were rounded down the most.
     98     delta = 1
     99 
    100   if delta:
    101     print "Initial sum is 0x%0.2X, adjusting." % (coeffs_rounded_sum,)
    102     coeff_diff = [(coeff_rounded - coeff) * delta
    103                   for coeff, coeff_rounded in zip(coeffs, coeffs_rounded)]
    104 
    105     class IndexTracker:
    106       def __init__(self, index, item):
    107         self.index = index
    108         self.item = item
    109       def __lt__(self, other):
    110         return self.item < other.item
    111       def __repr__(self):
    112         return "arr[%d] == %s" % (self.index, repr(self.item))
    113 
    114     coeff_pkg = [IndexTracker(i, diff) for i, diff in enumerate(coeff_diff)]
    115     coeff_pkg.sort()
    116 
    117     # num_elements_to_force_round better be < (2 * sample_units_width + 1) or
    118     # * our math was wildy wrong
    119     # * an awful lot of the curve is out side our sample
    120     # either is pretty bad, and probably means the results will not be useful.
    121     num_elements_to_force_round = abs(coeffs_rounded_sum - target_sum)
    122     for i in xrange(num_elements_to_force_round):
    123       print "Adding %d to index %d to force round %f." % (
    124           delta, coeff_pkg[i].index, coeffs[coeff_pkg[i].index])
    125       coeffs_rounded[coeff_pkg[i].index] += delta
    126 
    127   print "Prepending %d 0x00 for allignment." % (sample_align,)
    128   coeffs_rounded_aligned = ([0] * int(sample_align)) + coeffs_rounded
    129 
    130   print ', '.join(["0x%0.2X" % coeff_rounded
    131                    for coeff_rounded in coeffs_rounded_aligned])
    132   print sum(coeffs), hex(sum(coeffs_rounded))
    133   print
    134