| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
from hq_gen import ( |
| 8 |
edges, expandQuadrant, genExpr2, genExpr3, genExpr4, |
| 9 |
permuteCase, simplifyWeights |
| 10 |
) |
| 11 |
|
| 12 |
from collections import defaultdict |
| 13 |
from itertools import count |
| 14 |
from math import sqrt |
| 15 |
import sys |
| 16 |
|
| 17 |
|
| 18 |
|
| 19 |
class Variant(object): |
| 20 |
|
| 21 |
def __init__(self, pixelExpr, lite, table, narrow = None): |
| 22 |
self.lite = lite |
| 23 |
self.table = table |
| 24 |
if lite: |
| 25 |
pixelExpr = makeLite(pixelExpr, table) |
| 26 |
if narrow is not None: |
| 27 |
pixelExpr = narrow(pixelExpr) |
| 28 |
|
| 29 |
|
| 30 |
|
| 31 |
|
| 32 |
|
| 33 |
|
| 34 |
|
| 35 |
|
| 36 |
|
| 37 |
|
| 38 |
pixelExpr = permuteCases( |
| 39 |
(9, 2, 7, 3, 4, 10, 5, 1, 11, 8, 6, 0) |
| 40 |
if table else |
| 41 |
(2, 9, 7, 4, 3, 10, 11, 1, 8, 0, 6, 5), |
| 42 |
pixelExpr |
| 43 |
) |
| 44 |
self.pixelExpr = pixelExpr |
| 45 |
|
| 46 |
def writeSwitch(self, fileName): |
| 47 |
writeTextFile(fileName, genSwitch(self.pixelExpr)) |
| 48 |
|
| 49 |
|
| 50 |
|
| 51 |
def printText(contents): |
| 52 |
for text in contents: |
| 53 |
sys.stdout.write(text) |
| 54 |
|
| 55 |
def writeTextFile(fileName, contents): |
| 56 |
with open(fileName, 'w') as out: |
| 57 |
for content in contents: |
| 58 |
out.write(content) |
| 59 |
|
| 60 |
def writeBinaryFile(fileName, data): |
| 61 |
with open(fileName, 'wb') as out: |
| 62 |
out.write(bytes(data)) |
| 63 |
|
| 64 |
|
| 65 |
|
| 66 |
def genSwitch(pixelExpr): |
| 67 |
|
| 68 |
|
| 69 |
|
| 70 |
subExprForSubPixel = tuple( |
| 71 |
{expr[subPixel] for expr in pixelExpr if expr[subPixel] is not None} |
| 72 |
for subPixel in range(len(pixelExpr[0])) |
| 73 |
) |
| 74 |
isIndependentSubPixel = tuple( |
| 75 |
len(subExprs) == 1 |
| 76 |
for subExprs in subExprForSubPixel |
| 77 |
) |
| 78 |
|
| 79 |
exprToCases = defaultdict(list) |
| 80 |
for case, expr in enumerate(pixelExpr): |
| 81 |
if expr[0] is None: |
| 82 |
assert all(subExpr is None for subExpr in expr) |
| 83 |
key = None |
| 84 |
else: |
| 85 |
key = tuple(tuple(subExpr) for subExpr in expr) |
| 86 |
exprToCases[key].append(case) |
| 87 |
yield 'switch (pattern) {\n' |
| 88 |
for cases, expr in sorted( |
| 89 |
( sorted(cases), expr ) |
| 90 |
for expr, cases in exprToCases.items() |
| 91 |
): |
| 92 |
if expr is not None: |
| 93 |
for case in cases: |
| 94 |
yield 'case %d:\n' % case |
| 95 |
for subPixel, subExpr in enumerate(expr): |
| 96 |
if not isIndependentSubPixel[subPixel]: |
| 97 |
yield '\tpixel%s = %s;\n' % ( |
| 98 |
hex(subPixel)[2 : ], getBlendCode(subExpr) |
| 99 |
) |
| 100 |
yield '\tbreak;\n' |
| 101 |
for case in sorted(exprToCases[None]): |
| 102 |
yield 'case %d:\n' % case |
| 103 |
yield 'default:\n' |
| 104 |
yield '\tUNREACHABLE;\n' |
| 105 |
yield '\t%s = 0; // avoid warning\n' % ' = '.join( |
| 106 |
'pixel%d' % subPixel |
| 107 |
for subPixel, independent_ in enumerate(isIndependentSubPixel) |
| 108 |
) |
| 109 |
yield '}\n' |
| 110 |
|
| 111 |
for subPixel, independent in enumerate(isIndependentSubPixel): |
| 112 |
if independent: |
| 113 |
subExpr, = subExprForSubPixel[subPixel] |
| 114 |
yield 'pixel%s = %s;\n' % ( |
| 115 |
hex(subPixel)[2 : ], getBlendCode(subExpr) |
| 116 |
) |
| 117 |
|
| 118 |
def getBlendCode(weights): |
| 119 |
wSum = sum(weights) |
| 120 |
assert isPow2(wSum) |
| 121 |
if wSum == 1: |
| 122 |
index, = (index for index, weight in enumerate(weights) if weight != 0) |
| 123 |
return 'c%d' % (index + 1) |
| 124 |
elif wSum <= 8: |
| 125 |
|
| 126 |
|
| 127 |
|
| 128 |
return ' + '.join( |
| 129 |
'(c%d / %d) * %d' % (index + 1, wSum, weight) |
| 130 |
for index, weight in enumerate(weights) |
| 131 |
if weight != 0 |
| 132 |
) |
| 133 |
else: |
| 134 |
return '((%s) & 0xFF00FF00) | (((%s) / %d) & 0x00FF00FF)' % ( |
| 135 |
' + '.join( |
| 136 |
'((c%d & 0xFF00FF00) / %d) * %d' % (index + 1, wSum, weight) |
| 137 |
for index, weight in enumerate(weights) |
| 138 |
if weight != 0 |
| 139 |
), |
| 140 |
' + '.join( |
| 141 |
'(c%d & 0x00FF00FF) * %d' % (index + 1, weight) |
| 142 |
for index, weight in enumerate(weights) |
| 143 |
if weight != 0 |
| 144 |
), |
| 145 |
wSum |
| 146 |
) |
| 147 |
|
| 148 |
|
| 149 |
|
| 150 |
def formatOffsetsTable(pixelExpr): |
| 151 |
for case, expr in enumerate(pixelExpr): |
| 152 |
yield '// %d\n' % case |
| 153 |
for weights in expr: |
| 154 |
for x, y in transformOffsets(weights): |
| 155 |
yield ' %3d, %3d,' % (x, y) |
| 156 |
yield '\n' |
| 157 |
|
| 158 |
def formatWeightsTable(pixelExpr, cellFunc): |
| 159 |
for case, expr in enumerate(pixelExpr): |
| 160 |
yield '// %d\n' % case |
| 161 |
for weights in expr: |
| 162 |
for weight in transformWeights(weights, cellFunc): |
| 163 |
yield ' %3d,' % weight |
| 164 |
yield '\n' |
| 165 |
|
| 166 |
|
| 167 |
|
| 168 |
def getZoom(pixelExpr): |
| 169 |
zoom = int(sqrt(len(pixelExpr[0]))) |
| 170 |
assert zoom * zoom == len(pixelExpr[0]) |
| 171 |
return zoom |
| 172 |
|
| 173 |
def isPow2(num): |
| 174 |
while num & 1 == 0: |
| 175 |
num >>= 1 |
| 176 |
return num == 1 |
| 177 |
|
| 178 |
def permuteCases(permutation, pixelExpr): |
| 179 |
return [ |
| 180 |
pixelExpr[permuteCase(case, permutation)] |
| 181 |
for case in range(len(pixelExpr)) |
| 182 |
] |
| 183 |
|
| 184 |
def computeNeighbours(weights): |
| 185 |
neighbours = [ i for i in range(9) if i != 4 and weights[i] != 0 ] |
| 186 |
assert len(neighbours) <= 2 |
| 187 |
neighbours += [ None ] * (2 - len(neighbours)) |
| 188 |
return neighbours |
| 189 |
|
| 190 |
def transformOffsets(weights): |
| 191 |
for neighbour in computeNeighbours(weights): |
| 192 |
yield ( |
| 193 |
min(255, (1 if neighbour is None else neighbour % 3) * 128), |
| 194 |
min(255, (1 if neighbour is None else neighbour // 3) * 128) |
| 195 |
) |
| 196 |
|
| 197 |
def transformWeights(weights, cellFunc): |
| 198 |
scaledWeights = scaleWeights(weights, 256) |
| 199 |
for cell in cellFunc(scaledWeights): |
| 200 |
yield min(255, 0 if cell is None else scaledWeights[cell]) |
| 201 |
|
| 202 |
def computeOffsets(pixelExpr): |
| 203 |
|
| 204 |
|
| 205 |
|
| 206 |
zoom = getZoom(pixelExpr) |
| 207 |
for caseMajor in range(0, len(pixelExpr), 64): |
| 208 |
for subY in range(0, zoom ** 2, zoom): |
| 209 |
for caseMinor in range(64): |
| 210 |
for subX in range(zoom): |
| 211 |
weights = pixelExpr[caseMajor + caseMinor][subY + subX] |
| 212 |
for x, y in transformOffsets(weights): |
| 213 |
yield x |
| 214 |
yield y |
| 215 |
|
| 216 |
def computeWeights(pixelExpr, cellFunc): |
| 217 |
|
| 218 |
|
| 219 |
|
| 220 |
zoom = getZoom(pixelExpr) |
| 221 |
for caseMajor in range(0, len(pixelExpr), 64): |
| 222 |
for subY in range(0, zoom ** 2, zoom): |
| 223 |
for caseMinor in range(64): |
| 224 |
for subX in range(zoom): |
| 225 |
weights = pixelExpr[caseMajor + caseMinor][subY + subX] |
| 226 |
for tw in transformWeights(weights, cellFunc): |
| 227 |
yield tw |
| 228 |
|
| 229 |
def computeWeightCells(weights): |
| 230 |
neighbours = computeNeighbours(weights) |
| 231 |
return (neighbours[0], neighbours[1], 4) |
| 232 |
|
| 233 |
def computeLiteWeightCells(weights_): |
| 234 |
return (3, 4, 5) |
| 235 |
|
| 236 |
def isContradiction(case): |
| 237 |
inv = case ^ 0xFFF |
| 238 |
return ( |
| 239 |
(inv & 0x121) in [0x120, 0x101, 0x021] or |
| 240 |
(inv & 0x502) in [0x500, 0x402, 0x102] or |
| 241 |
(inv & 0x484) in [0x480, 0x404, 0x084] or |
| 242 |
(inv & 0x0A8) in [0x0A0, 0x088, 0x028] or |
| 243 |
(inv & 0x00F) in [0x00E, 0x00D, 0x00B, 0x007] |
| 244 |
) |
| 245 |
|
| 246 |
def scaleWeights(weights, newSum): |
| 247 |
|
| 248 |
|
| 249 |
oldSum = sum(weights) |
| 250 |
factor, remainder = divmod(newSum, oldSum) |
| 251 |
assert remainder == 0 |
| 252 |
return tuple(weight * factor for weight in weights) |
| 253 |
|
| 254 |
def blendWeights(weights1, weights2, factor1 = 1, factor2 = 1): |
| 255 |
factor1 *= sum(weights2) |
| 256 |
factor2 *= sum(weights1) |
| 257 |
return simplifyWeights( |
| 258 |
factor1 * w1 + factor2 * w2 |
| 259 |
for w1, w2 in zip(weights1, weights2) |
| 260 |
) |
| 261 |
|
| 262 |
def calcNeighbourToSet(): |
| 263 |
|
| 264 |
ret = [] |
| 265 |
for case in range(1 << len(edges)): |
| 266 |
if isContradiction(case): |
| 267 |
ret.append(None) |
| 268 |
else: |
| 269 |
neighbourToSet = [{neighbour} for neighbour in range(9)] |
| 270 |
for edge, (neighbour1, neighbour2) in enumerate(edges): |
| 271 |
if (case & (1 << edge)) == 0: |
| 272 |
|
| 273 |
|
| 274 |
set1 = neighbourToSet[neighbour1] |
| 275 |
set2 = neighbourToSet[neighbour2] |
| 276 |
set1 |= set2 |
| 277 |
for n in set2: |
| 278 |
neighbourToSet[n] = set1 |
| 279 |
ret.append(neighbourToSet) |
| 280 |
return ret |
| 281 |
|
| 282 |
neighbourToSet = calcNeighbourToSet() |
| 283 |
|
| 284 |
def lighten(case, weights, neighbourPreference): |
| 285 |
equalNeighboursOf = neighbourToSet[case] |
| 286 |
if equalNeighboursOf is None: |
| 287 |
return None |
| 288 |
else: |
| 289 |
done = set() |
| 290 |
newWeights = [ 0 ] * 9 |
| 291 |
for neighbour in neighbourPreference: |
| 292 |
if neighbour not in done: |
| 293 |
equalNeighbours = equalNeighboursOf[neighbour] |
| 294 |
newWeights[neighbour] = sum( |
| 295 |
weights[n] for n in equalNeighbours |
| 296 |
) |
| 297 |
done |= equalNeighbours |
| 298 |
for c in (0, 1, 2, 6, 7, 8): |
| 299 |
|
| 300 |
assert newWeights[c] == 0 |
| 301 |
return simplifyWeights(newWeights) |
| 302 |
|
| 303 |
def makeLite(pixelExpr, biased): |
| 304 |
zoom = getZoom(pixelExpr) |
| 305 |
biasLeft = (4, 3, 5, 1, 7, 0, 2, 6, 8) |
| 306 |
biasRight = (4, 5, 3, 1, 7, 2, 0, 8, 6) |
| 307 |
if biased: |
| 308 |
center = (zoom - 1) / 2 |
| 309 |
neighbourPreferences = [ |
| 310 |
biasRight if subPixel % zoom > center else biasLeft |
| 311 |
for subPixel in range(zoom ** 2) |
| 312 |
] |
| 313 |
else: |
| 314 |
neighbourPreferences = [ biasLeft ] * (zoom ** 2) |
| 315 |
return [ |
| 316 |
[ lighten(case, weights, pref) |
| 317 |
for weights, pref in zip(expr, neighbourPreferences) ] |
| 318 |
for case, expr in enumerate(pixelExpr) |
| 319 |
] |
| 320 |
|
| 321 |
def makeNarrow2to1(pixelExpr): |
| 322 |
return tuple( |
| 323 |
(None, None) if a is None else (blendWeights(a, b), blendWeights(c, d)) |
| 324 |
for a, b, c, d in pixelExpr |
| 325 |
) |
| 326 |
|
| 327 |
def makeNarrow3to2(pixelExpr): |
| 328 |
return [ |
| 329 |
[ blendWeights(expr[0], expr[1], 2, 1), |
| 330 |
blendWeights(expr[2], expr[1], 2, 1), |
| 331 |
blendWeights(expr[3], expr[4], 2, 1), |
| 332 |
blendWeights(expr[5], expr[4], 2, 1), |
| 333 |
blendWeights(expr[6], expr[7], 2, 1), |
| 334 |
blendWeights(expr[8], expr[7], 2, 1), |
| 335 |
] |
| 336 |
for expr in pixelExpr |
| 337 |
] |
| 338 |
|
| 339 |
def genHQLiteOffsetsTable(pixelExpr): |
| 340 |
|
| 341 |
|
| 342 |
|
| 343 |
|
| 344 |
|
| 345 |
zoom = getZoom(pixelExpr) |
| 346 |
for caseMajor in range(0, len(pixelExpr), 64): |
| 347 |
for subY in range(zoom): |
| 348 |
for caseMinor in range(64): |
| 349 |
for subX in range(zoom): |
| 350 |
subPixel = zoom * subY + subX |
| 351 |
weights = pixelExpr[caseMajor + caseMinor][subPixel] |
| 352 |
if weights is None: |
| 353 |
neighbour = None |
| 354 |
else: |
| 355 |
neighbours = computeNeighbours(weights) |
| 356 |
assert neighbours[1] is None, neighbours |
| 357 |
neighbour = neighbours[0] |
| 358 |
factor = sum(weights) |
| 359 |
|
| 360 |
x = int(192.5 - 128 * (0.5 + subX) / zoom) |
| 361 |
y = int(192.5 - 128 * (0.5 + subY) / zoom) |
| 362 |
if neighbour == 3: |
| 363 |
x -= 128 * weights[3] // factor |
| 364 |
elif neighbour == 5: |
| 365 |
x += 128 * weights[5] // factor |
| 366 |
else: |
| 367 |
assert neighbour is None, neighbour |
| 368 |
assert 0 <= x < 256, x |
| 369 |
assert 0 <= y < 256, y |
| 370 |
yield x |
| 371 |
yield y |
| 372 |
|
| 373 |
|
| 374 |
|
| 375 |
def process2x(): |
| 376 |
pixelExpr = expandQuadrant(genExpr2(), 2) |
| 377 |
|
| 378 |
fullTableVariant = Variant(pixelExpr, lite = False, table = True) |
| 379 |
liteTableVariant = Variant(pixelExpr, lite = True, table = True) |
| 380 |
|
| 381 |
|
| 382 |
|
| 383 |
|
| 384 |
|
| 385 |
|
| 386 |
|
| 387 |
|
| 388 |
|
| 389 |
|
| 390 |
writeBinaryFile( |
| 391 |
'HQ2xOffsets.dat', |
| 392 |
computeOffsets(fullTableVariant.pixelExpr) |
| 393 |
) |
| 394 |
writeBinaryFile( |
| 395 |
'HQ2xWeights.dat', |
| 396 |
computeWeights(fullTableVariant.pixelExpr, computeWeightCells) |
| 397 |
) |
| 398 |
writeBinaryFile( |
| 399 |
'HQ2xLiteOffsets.dat', |
| 400 |
genHQLiteOffsetsTable(liteTableVariant.pixelExpr) |
| 401 |
) |
| 402 |
|
| 403 |
|
| 404 |
|
| 405 |
Variant(pixelExpr, lite = False, table = False).writeSwitch( |
| 406 |
'HQ2xScaler-1x1to2x2.nn' |
| 407 |
) |
| 408 |
Variant(pixelExpr, lite = False, table = False, narrow = makeNarrow2to1).writeSwitch( |
| 409 |
'HQ2xScaler-1x1to1x2.nn' |
| 410 |
) |
| 411 |
Variant(pixelExpr, lite = True, table = False).writeSwitch( |
| 412 |
'HQ2xLiteScaler-1x1to2x2.nn' |
| 413 |
) |
| 414 |
Variant(pixelExpr, lite = True, table = False, narrow = makeNarrow2to1).writeSwitch( |
| 415 |
'HQ2xLiteScaler-1x1to1x2.nn' |
| 416 |
) |
| 417 |
|
| 418 |
def process3x(): |
| 419 |
pixelExpr = expandQuadrant(genExpr3(), 3) |
| 420 |
|
| 421 |
fullTableVariant = Variant(pixelExpr, lite = False, table = True) |
| 422 |
liteTableVariant = Variant(pixelExpr, lite = True, table = True) |
| 423 |
|
| 424 |
|
| 425 |
|
| 426 |
|
| 427 |
|
| 428 |
|
| 429 |
|
| 430 |
|
| 431 |
|
| 432 |
|
| 433 |
writeBinaryFile( |
| 434 |
'HQ3xOffsets.dat', |
| 435 |
computeOffsets(fullTableVariant.pixelExpr) |
| 436 |
) |
| 437 |
writeBinaryFile( |
| 438 |
'HQ3xWeights.dat', |
| 439 |
computeWeights(fullTableVariant.pixelExpr, computeWeightCells) |
| 440 |
) |
| 441 |
writeBinaryFile( |
| 442 |
'HQ3xLiteOffsets.dat', |
| 443 |
genHQLiteOffsetsTable(liteTableVariant.pixelExpr) |
| 444 |
) |
| 445 |
|
| 446 |
|
| 447 |
|
| 448 |
Variant(pixelExpr, lite = False, table = False).writeSwitch( |
| 449 |
'HQ3xScaler-1x1to3x3.nn' |
| 450 |
) |
| 451 |
Variant(pixelExpr, lite = True, table = False).writeSwitch( |
| 452 |
'HQ3xLiteScaler-1x1to3x3.nn' |
| 453 |
) |
| 454 |
|
| 455 |
def process4x(): |
| 456 |
pixelExpr = expandQuadrant(genExpr4(), 4) |
| 457 |
|
| 458 |
fullTableVariant = Variant(pixelExpr, lite = False, table = True) |
| 459 |
liteTableVariant = Variant(pixelExpr, lite = True, table = True) |
| 460 |
|
| 461 |
|
| 462 |
|
| 463 |
|
| 464 |
|
| 465 |
writeBinaryFile( |
| 466 |
'HQ4xOffsets.dat', |
| 467 |
computeOffsets(fullTableVariant.pixelExpr) |
| 468 |
) |
| 469 |
writeBinaryFile( |
| 470 |
'HQ4xWeights.dat', |
| 471 |
computeWeights(fullTableVariant.pixelExpr, computeWeightCells) |
| 472 |
) |
| 473 |
writeBinaryFile( |
| 474 |
'HQ4xLiteOffsets.dat', |
| 475 |
genHQLiteOffsetsTable(liteTableVariant.pixelExpr) |
| 476 |
) |
| 477 |
|
| 478 |
|
| 479 |
|
| 480 |
|
| 481 |
|
| 482 |
|
| 483 |
|
| 484 |
if __name__ == '__main__': |
| 485 |
process2x() |
| 486 |
process3x() |
| 487 |
process4x() |