skia2/tools/generate_fir_coeff.py
bungeman@google.com 0abbff9987 Fix metrics on Windows.
With this change, Skia's metrics are much better in general, and
specifically can be made to match the metrics produced by current
Blink code. This allows Blink to use Skia's metrics.

This change will require a number of rebaselines in Skia, since
previous metrics were quite different. This will require about five
rebaslines in Blink, as the new code may cause GDI's matrix to differ
in the low bits.

Review URL: https://codereview.chromium.org/20585004

git-svn-id: http://skia.googlecode.com/svn/trunk@10399 2bbb7eff-a529-9590-31e7-b0007b416f81
2013-07-27 20:37:56 +00:00

120 lines
4.4 KiB
Python

#!/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 a bunch of smudged samples which represent the average coverage of a range.
#We have a 'center' which may not line up with those samples.
#From the 'center' we want to make a normal approximation where '5' sample width out we're 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
# Now we have the numbers we want, but our 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 had 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