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