1#!/usr/bin/python 2 3''' 4Copyright 2013 Google Inc. 5 6Use of this source code is governed by a BSD-style license that can be 7found in the LICENSE file. 8''' 9 10import math 11import pprint 12 13def withinStdDev(n): 14 """Returns the percent of samples within n std deviations of the normal.""" 15 return math.erf(n / math.sqrt(2)) 16 17def 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. 42samples_per_pixel = 4 43subpxls_per_pixel = 3 44#sample_offsets is (frac, int) in sample units. 45sample_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. 53sample_units_width = 5 54 55#The std deviation at sample_units_width. 56std_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. 60target_sum = 0x110 61 62for 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