-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathapproximatecache.go
More file actions
108 lines (101 loc) · 3.34 KB
/
Copy pathapproximatecache.go
File metadata and controls
108 lines (101 loc) · 3.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
package img2ansi
import (
"math"
)
// ApproximateCache is a map of Uint256 to lookupEntry
// that is used to store approximate matches for a given
// block of 4 RGB values. Approximate matches are performed
// by comparing the error of a given match to a threshold
// Value.
//
// The Key of the map is a Uint256, which is a 256-bit
// unsigned integer that is used to represent the foreground
// and background colors of a block of 4 RGB values.
//
// There may be multiple matches for a given Key, so the
// Value of the map is a lookupEntry, which is a struct
// that contains a slice of Match structs.
type ApproximateCache map[Uint256]lookupEntry
// Match is a struct that contains the rune, foreground
// color, background color, and error of a match. The error
// is a float64 Value that represents the difference between
// the actual block of 4 RGB values and the pair of foreground
// and background colors encoded in the Key as an Uint256.
type Match struct {
Rune rune
FG RGB
BG RGB
Error float64
}
type lookupEntry struct {
Matches []Match
}
// addCacheEntry adds a new entry to the renderer's lookup cache. The entry is
// represented by a key, which is a Uint256, and a Match struct that contains
// the rune, foreground color, background color, and error of the match.
func (r *Renderer) addCacheEntry(
k Uint256,
rune rune,
fg RGB,
bg RGB,
block [4]RGB,
isEdge bool,
) {
r.lookupMisses++
// Calculate and store the original error for exact-match detection
quad := getQuadrantsForRune(rune)
originalError := r.calculateBlockError(block, quad, fg, bg, isEdge)
newMatch := Match{
Rune: rune,
FG: fg,
BG: bg,
Error: originalError,
}
if entry, exists := r.lookupTable[k]; exists {
// Create a new slice with the appended match
updatedMatches := append(entry.Matches, newMatch)
// Update the map with the new slice
r.lookupTable[k] = lookupEntry{Matches: updatedMatches}
} else {
r.lookupTable[k] = lookupEntry{Matches: []Match{newMatch}}
}
}
// getCacheEntry retrieves an entry from the renderer's lookup cache. The entry
// is represented by a key, which is a Uint256, and a block of 4 RGB values.
// The function returns the rune, foreground color, background color, and a
// boolean value indicating whether the entry was found in the cache.
//
// There may be multiple matches for a given key, so the function evaluates
// all cached patterns and returns the match with the lowest error below the
// cache threshold.
func (r *Renderer) getCacheEntry(
k Uint256,
block [4]RGB,
isEdge bool,
) (rune, RGB, RGB, bool) {
baseThreshold := r.CacheThreshold
if isEdge {
baseThreshold *= 0.7
}
lowestError := math.MaxFloat64
var bestMatch *Match = nil
if entry, exists := r.lookupTable[k]; exists {
for i := range entry.Matches {
match := &entry.Matches[i]
// Calculate error using the renderer's color method
quad := getQuadrantsForRune(match.Rune)
error := r.calculateBlockError(block, quad, match.FG, match.BG, isEdge)
// Accept if: (1) error below threshold, OR (2) exact match (same error as cached)
isExactMatch := math.Abs(error-match.Error) < 0.001
if error < lowestError && (error < baseThreshold || isExactMatch) {
lowestError = error
bestMatch = match
}
}
if bestMatch != nil {
r.lookupHits++
return bestMatch.Rune, bestMatch.FG, bestMatch.BG, true
}
}
return 0, RGB{}, RGB{}, false
}