/src/skia/src/text/gpu/DistanceFieldAdjustTable.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2015 Google Inc. |
3 | | * |
4 | | * Use of this source code is governed by a BSD-style license that can be |
5 | | * found in the LICENSE file. |
6 | | */ |
7 | | |
8 | | #include "src/text/gpu/DistanceFieldAdjustTable.h" |
9 | | |
10 | | #include "include/core/SkTypes.h" |
11 | | #include "include/private/base/SkDebug.h" |
12 | | #include "include/private/base/SkTemplates.h" |
13 | | #include "src/base/SkNoDestructor.h" |
14 | | #include "src/core/SkScalerContext.h" |
15 | | |
16 | | #include <cstddef> |
17 | | #include <cstdint> |
18 | | |
19 | | using namespace skia_private; |
20 | | |
21 | | namespace sktext::gpu { |
22 | | |
23 | | SkDEBUGCODE(static const int kExpectedDistanceAdjustTableSize = 8;) |
24 | | |
25 | 2 | SkScalar* build_distance_adjust_table(SkScalar paintGamma, SkScalar deviceGamma) { |
26 | | // This is used for an approximation of the mask gamma hack, used by raster and bitmap |
27 | | // text. The mask gamma hack is based off of guessing what the blend color is going to |
28 | | // be, and adjusting the mask so that when run through the linear blend will |
29 | | // produce the value closest to the desired result. However, in practice this means |
30 | | // that the 'adjusted' mask is just increasing or decreasing the coverage of |
31 | | // the mask depending on what it is thought it will blit against. For black (on |
32 | | // assumed white) this means that coverages are decreased (on a curve). For white (on |
33 | | // assumed black) this means that coverages are increased (on a a curve). At |
34 | | // middle (perceptual) gray (which could be blit against anything) the coverages |
35 | | // remain the same. |
36 | | // |
37 | | // The idea here is that instead of determining the initial (real) coverage and |
38 | | // then adjusting that coverage, we determine an adjusted coverage directly by |
39 | | // essentially manipulating the geometry (in this case, the distance to the glyph |
40 | | // edge). So for black (on assumed white) this thins a bit; for white (on |
41 | | // assumed black) this fake bolds the geometry a bit. |
42 | | // |
43 | | // The distance adjustment is calculated by determining the actual coverage value which |
44 | | // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This |
45 | | // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the |
46 | | // actual edge. So by subtracting this distance adjustment and computing without the |
47 | | // the coverage adjustment we should get 0.5 coverage at the same point. |
48 | | // |
49 | | // This has several implications: |
50 | | // For non-gray lcd smoothed text, each subpixel essentially is using a |
51 | | // slightly different geometry. |
52 | | // |
53 | | // For black (on assumed white) this may not cover some pixels which were |
54 | | // previously covered; however those pixels would have been only slightly |
55 | | // covered and that slight coverage would have been decreased anyway. Also, some pixels |
56 | | // which were previously fully covered may no longer be fully covered. |
57 | | // |
58 | | // For white (on assumed black) this may cover some pixels which weren't |
59 | | // previously covered at all. |
60 | | |
61 | 2 | int width, height; |
62 | 2 | size_t size; |
63 | 2 | SkScalar contrast = SK_GAMMA_CONTRAST; |
64 | | |
65 | 2 | size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma, |
66 | 2 | &width, &height); |
67 | | |
68 | 2 | SkASSERT(kExpectedDistanceAdjustTableSize == height); |
69 | 2 | SkScalar* table = new SkScalar[height]; |
70 | | |
71 | 2 | AutoTArray<uint8_t> data((int)size); |
72 | 2 | if (!SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get())) { |
73 | | // if no valid data is available simply do no adjustment |
74 | 0 | for (int row = 0; row < height; ++row) { |
75 | 0 | table[row] = 0; |
76 | 0 | } |
77 | 0 | return table; |
78 | 0 | } |
79 | | |
80 | | // find the inverse points where we cross 0.5 |
81 | | // binsearch might be better, but we only need to do this once on creation |
82 | 18 | for (int row = 0; row < height; ++row) { |
83 | 16 | uint8_t* rowPtr = data.get() + row*width; |
84 | 1.84k | for (int col = 0; col < width - 1; ++col) { |
85 | 1.84k | if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) { |
86 | | // compute point where a mask value will give us a result of 0.5 |
87 | 16 | float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]); |
88 | 16 | float borderAlpha = (col + interp) / 255.f; |
89 | | |
90 | | // compute t value for that alpha |
91 | | // this is an approximate inverse for smoothstep() |
92 | 16 | float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f; |
93 | | |
94 | | // compute distance which gives us that t value |
95 | 16 | const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor |
96 | 16 | float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor; |
97 | | |
98 | 16 | table[row] = d; |
99 | 16 | break; |
100 | 16 | } |
101 | 1.84k | } |
102 | 16 | } |
103 | | |
104 | 2 | return table; |
105 | 2 | } sktext::gpu::build_distance_adjust_table(float, float) Line | Count | Source | 25 | 2 | SkScalar* build_distance_adjust_table(SkScalar paintGamma, SkScalar deviceGamma) { | 26 | | // This is used for an approximation of the mask gamma hack, used by raster and bitmap | 27 | | // text. The mask gamma hack is based off of guessing what the blend color is going to | 28 | | // be, and adjusting the mask so that when run through the linear blend will | 29 | | // produce the value closest to the desired result. However, in practice this means | 30 | | // that the 'adjusted' mask is just increasing or decreasing the coverage of | 31 | | // the mask depending on what it is thought it will blit against. For black (on | 32 | | // assumed white) this means that coverages are decreased (on a curve). For white (on | 33 | | // assumed black) this means that coverages are increased (on a a curve). At | 34 | | // middle (perceptual) gray (which could be blit against anything) the coverages | 35 | | // remain the same. | 36 | | // | 37 | | // The idea here is that instead of determining the initial (real) coverage and | 38 | | // then adjusting that coverage, we determine an adjusted coverage directly by | 39 | | // essentially manipulating the geometry (in this case, the distance to the glyph | 40 | | // edge). So for black (on assumed white) this thins a bit; for white (on | 41 | | // assumed black) this fake bolds the geometry a bit. | 42 | | // | 43 | | // The distance adjustment is calculated by determining the actual coverage value which | 44 | | // when fed into in the mask gamma table gives us an 'adjusted coverage' value of 0.5. This | 45 | | // actual coverage value (assuming it's between 0 and 1) corresponds to a distance from the | 46 | | // actual edge. So by subtracting this distance adjustment and computing without the | 47 | | // the coverage adjustment we should get 0.5 coverage at the same point. | 48 | | // | 49 | | // This has several implications: | 50 | | // For non-gray lcd smoothed text, each subpixel essentially is using a | 51 | | // slightly different geometry. | 52 | | // | 53 | | // For black (on assumed white) this may not cover some pixels which were | 54 | | // previously covered; however those pixels would have been only slightly | 55 | | // covered and that slight coverage would have been decreased anyway. Also, some pixels | 56 | | // which were previously fully covered may no longer be fully covered. | 57 | | // | 58 | | // For white (on assumed black) this may cover some pixels which weren't | 59 | | // previously covered at all. | 60 | | | 61 | 2 | int width, height; | 62 | 2 | size_t size; | 63 | 2 | SkScalar contrast = SK_GAMMA_CONTRAST; | 64 | | | 65 | 2 | size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma, | 66 | 2 | &width, &height); | 67 | | | 68 | 2 | SkASSERT(kExpectedDistanceAdjustTableSize == height); | 69 | 2 | SkScalar* table = new SkScalar[height]; | 70 | | | 71 | 2 | AutoTArray<uint8_t> data((int)size); | 72 | 2 | if (!SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get())) { | 73 | | // if no valid data is available simply do no adjustment | 74 | 0 | for (int row = 0; row < height; ++row) { | 75 | 0 | table[row] = 0; | 76 | 0 | } | 77 | 0 | return table; | 78 | 0 | } | 79 | | | 80 | | // find the inverse points where we cross 0.5 | 81 | | // binsearch might be better, but we only need to do this once on creation | 82 | 18 | for (int row = 0; row < height; ++row) { | 83 | 16 | uint8_t* rowPtr = data.get() + row*width; | 84 | 1.84k | for (int col = 0; col < width - 1; ++col) { | 85 | 1.84k | if (rowPtr[col] <= 127 && rowPtr[col + 1] >= 128) { | 86 | | // compute point where a mask value will give us a result of 0.5 | 87 | 16 | float interp = (127.5f - rowPtr[col]) / (rowPtr[col + 1] - rowPtr[col]); | 88 | 16 | float borderAlpha = (col + interp) / 255.f; | 89 | | | 90 | | // compute t value for that alpha | 91 | | // this is an approximate inverse for smoothstep() | 92 | 16 | float t = borderAlpha*(borderAlpha*(4.0f*borderAlpha - 6.0f) + 5.0f) / 3.0f; | 93 | | | 94 | | // compute distance which gives us that t value | 95 | 16 | const float kDistanceFieldAAFactor = 0.65f; // should match SK_DistanceFieldAAFactor | 96 | 16 | float d = 2.0f*kDistanceFieldAAFactor*t - kDistanceFieldAAFactor; | 97 | | | 98 | 16 | table[row] = d; | 99 | 16 | break; | 100 | 16 | } | 101 | 1.84k | } | 102 | 16 | } | 103 | | | 104 | 2 | return table; | 105 | 2 | } |
Unexecuted instantiation: sktext::gpu::build_distance_adjust_table(float, float) |
106 | | |
107 | 70 | const DistanceFieldAdjustTable* DistanceFieldAdjustTable::Get() { |
108 | 70 | static const SkNoDestructor<DistanceFieldAdjustTable> dfat; |
109 | 70 | return dfat.get(); |
110 | 70 | } |
111 | | |
112 | 1 | DistanceFieldAdjustTable::DistanceFieldAdjustTable() { |
113 | 1 | fTable = build_distance_adjust_table(SK_GAMMA_EXPONENT, SK_GAMMA_EXPONENT); |
114 | 1 | fGammaCorrectTable = build_distance_adjust_table(SK_Scalar1, SK_Scalar1); |
115 | 1 | } |
116 | | |
117 | | } // namespace sktext::gpu |