Merge pull request #16106 from bungeman/improve_color_stop_detection

Better approximate gradient color stops
This commit is contained in:
calixteman 2023-05-04 19:48:57 +02:00 committed by GitHub
commit a24e11a91c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 797 additions and 7 deletions

View File

@ -157,10 +157,9 @@ class RadialAxialShading extends BaseShading {
const fnObj = dict.getRaw("Function"); const fnObj = dict.getRaw("Function");
const fn = pdfFunctionFactory.createFromArray(fnObj); const fn = pdfFunctionFactory.createFromArray(fnObj);
// 10 samples seems good enough for now, but probably won't work // Use lcm(1,2,3,4,5,6,7,8,10) = 840 (including 9 increases this to 2520)
// if there are sharp color changes. Ideally, we would implement // to catch evenly spaced stops. oeis.org/A003418
// the spec faithfully and add lossless optimizations. const NUMBER_OF_SAMPLES = 840;
const NUMBER_OF_SAMPLES = 10;
const step = (t1 - t0) / NUMBER_OF_SAMPLES; const step = (t1 - t0) / NUMBER_OF_SAMPLES;
const colorStops = (this.colorStops = []); const colorStops = (this.colorStops = []);
@ -176,13 +175,80 @@ class RadialAxialShading extends BaseShading {
const color = new Float32Array(cs.numComps), const color = new Float32Array(cs.numComps),
ratio = new Float32Array(1); ratio = new Float32Array(1);
let rgbColor; let rgbColor;
for (let i = 0; i <= NUMBER_OF_SAMPLES; i++) {
let iBase = 0;
ratio[0] = t0;
fn(ratio, 0, color, 0);
let rgbBase = cs.getRgb(color, 0);
const cssColorBase = Util.makeHexColor(rgbBase[0], rgbBase[1], rgbBase[2]);
colorStops.push([0, cssColorBase]);
let iPrev = 1;
ratio[0] = t0 + step;
fn(ratio, 0, color, 0);
let rgbPrev = cs.getRgb(color, 0);
// Slopes are rise / run.
// A max slope is from the least value the base component could have been
// to the greatest value the current component could have been.
// A min slope is from the greatest value the base component could have been
// to the least value the current component could have been.
// Each component could have been rounded up to .5 from its original value
// so the conservative deltas are +-1 (+-.5 for base and -+.5 for current).
// The run is iPrev - iBase = 1, so omitted.
let maxSlopeR = rgbPrev[0] - rgbBase[0] + 1;
let maxSlopeG = rgbPrev[1] - rgbBase[1] + 1;
let maxSlopeB = rgbPrev[2] - rgbBase[2] + 1;
let minSlopeR = rgbPrev[0] - rgbBase[0] - 1;
let minSlopeG = rgbPrev[1] - rgbBase[1] - 1;
let minSlopeB = rgbPrev[2] - rgbBase[2] - 1;
for (let i = 2; i < NUMBER_OF_SAMPLES; i++) {
ratio[0] = t0 + i * step; ratio[0] = t0 + i * step;
fn(ratio, 0, color, 0); fn(ratio, 0, color, 0);
rgbColor = cs.getRgb(color, 0); rgbColor = cs.getRgb(color, 0);
const cssColor = Util.makeHexColor(rgbColor[0], rgbColor[1], rgbColor[2]);
colorStops.push([i / NUMBER_OF_SAMPLES, cssColor]); // Keep going if the maximum minimum slope <= the minimum maximum slope.
// Otherwise add a rgbPrev color stop and make it the new base.
const run = i - iBase;
maxSlopeR = Math.min(maxSlopeR, (rgbColor[0] - rgbBase[0] + 1) / run);
maxSlopeG = Math.min(maxSlopeG, (rgbColor[1] - rgbBase[1] + 1) / run);
maxSlopeB = Math.min(maxSlopeB, (rgbColor[2] - rgbBase[2] + 1) / run);
minSlopeR = Math.max(minSlopeR, (rgbColor[0] - rgbBase[0] - 1) / run);
minSlopeG = Math.max(minSlopeG, (rgbColor[1] - rgbBase[1] - 1) / run);
minSlopeB = Math.max(minSlopeB, (rgbColor[2] - rgbBase[2] - 1) / run);
const slopesExist =
minSlopeR <= maxSlopeR &&
minSlopeG <= maxSlopeG &&
minSlopeB <= maxSlopeB;
if (!slopesExist) {
const cssColor = Util.makeHexColor(rgbPrev[0], rgbPrev[1], rgbPrev[2]);
colorStops.push([iPrev / NUMBER_OF_SAMPLES, cssColor]);
// TODO: When fn frequency is high (iPrev - iBase === 1 twice in a row),
// send the color space and function to do the sampling display side.
// The run is i - iPrev = 1, so omitted.
maxSlopeR = rgbColor[0] - rgbPrev[0] + 1;
maxSlopeG = rgbColor[1] - rgbPrev[1] + 1;
maxSlopeB = rgbColor[2] - rgbPrev[2] + 1;
minSlopeR = rgbColor[0] - rgbPrev[0] - 1;
minSlopeG = rgbColor[1] - rgbPrev[1] - 1;
minSlopeB = rgbColor[2] - rgbPrev[2] - 1;
iBase = iPrev;
rgbBase = rgbPrev;
}
iPrev = i;
rgbPrev = rgbColor;
} }
const cssColor = Util.makeHexColor(rgbPrev[0], rgbPrev[1], rgbPrev[2]);
colorStops.push([1, cssColor]);
let background = "transparent"; let background = "transparent";
if (dict.has("Background")) { if (dict.has("Background")) {

View File

@ -345,6 +345,7 @@
!issue12810.pdf !issue12810.pdf
!bug866395.pdf !bug866395.pdf
!issue12010_reduced.pdf !issue12010_reduced.pdf
!issue10572.pdf
!issue11718_reduced.pdf !issue11718_reduced.pdf
!bug1027533.pdf !bug1027533.pdf
!bug1028735.pdf !bug1028735.pdf
@ -534,6 +535,7 @@
!issue15012.pdf !issue15012.pdf
!issue15150.pdf !issue15150.pdf
!poppler-395-0-fuzzed.pdf !poppler-395-0-fuzzed.pdf
!issue14165.pdf
!GHOSTSCRIPT-698804-1-fuzzed.pdf !GHOSTSCRIPT-698804-1-fuzzed.pdf
!issue14814.pdf !issue14814.pdf
!poppler-91414-0-53.pdf !poppler-91414-0-53.pdf

374
test/pdfs/issue10572.pdf Normal file
View File

@ -0,0 +1,374 @@
%PDF-1.5
%¿÷¢þ
%QDF-1.0
%% Original object ID: 20 0
1 0 obj
<<
/Pages 3 0 R
/Type /Catalog
>>
endobj
%% Original object ID: 19 0
2 0 obj
<<
/Creator (cairo 1.9.5 \(http://cairographics.org\))
/Producer (cairo 1.9.5 \(http://cairographics.org\))
>>
endobj
%% Original object ID: 1 0
3 0 obj
<<
/Count 1
/Kids [
4 0 R
]
/Type /Pages
>>
endobj
%% Page 1
%% Original object ID: 7 0
4 0 obj
<<
/Contents 5 0 R
/Group <<
/CS /DeviceRGB
/S /Transparency
/Type /Group
>>
/MediaBox [
0
0
612
792
]
/Parent 3 0 R
/Resources 7 0 R
/Type /Page
>>
endobj
%% Contents for page 1
%% Original object ID: 3 0
5 0 obj
<<
/Length 6 0 R
>>
stream
q
Q q
54 738 504 -661.699 re W n
1 1 1 rg /a0 gs
54 738 504 -661.699 re f
Q q
61 716 225 -450 re W n
q /Pattern cs /p5 scn /a0 gs
61 716 225 -450 re f
Q
Q q
54 738 504 -661.699 re W n
0 0 0 RG /a0 gs
1 w
0 J
0 j
[] 0.0 d
10 M 60.5 716.5 226 -451 re S
56 736 424 -23 re W n
Q q
Q
endstream
endobj
6 0 obj
279
endobj
%% Original object ID: 2 0
7 0 obj
<<
/ExtGState <<
/a0 <<
/CA 1
/ca 1
>>
>>
/Pattern <<
/p5 9 0 R
>>
>>
endobj
%% Original object ID: 6 0
8 0 obj
<<
>>
endobj
%% Original object ID: 5 0
9 0 obj
<<
/Matrix [
1
0
0
-1
61
716
]
/PatternType 2
/Shading <<
/ColorSpace /DeviceRGB
/Coords [
112.5
-900
112.5
900
]
/Domain [
-6
6
]
/Extend [
false
false
]
/Function 13 0 R
/ShadingType 2
>>
/Type /Pattern
>>
endobj
%% Original object ID: 18 0
10 0 obj
<<
>>
endobj
%% Original object ID: 15 0
11 0 obj
<<
/Length 12 0 R
>>
stream
endstream
endobj
12 0 obj
0
endobj
%% Original object ID: 12 0
13 0 obj
<<
/Bounds [
-5
-4
-3
-2
-1
0
1
2
3
4
5
]
/Domain [
-6
6
]
/Encode [
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
]
/FunctionType 3
/Functions [
15 0 R
15 0 R
15 0 R
15 0 R
15 0 R
15 0 R
15 0 R
15 0 R
15 0 R
15 0 R
15 0 R
15 0 R
]
>>
endobj
%% Original object ID: 17 0
14 0 obj
<<
>>
endobj
%% Original object ID: 11 0
15 0 obj
<<
/Bounds [
0.5
0.5
]
/Domain [
0
1
]
/Encode [
0
1
0
1
0
1
]
/FunctionType 3
/Functions [
18 0 R
19 0 R
20 0 R
]
>>
endobj
%% Original object ID: 13 0
16 0 obj
<<
/Length1 9948
/Length 17 0 R
>>
stream
endstream
endobj
%QDF: ignore_newline
17 0 obj
0
endobj
%% Original object ID: 8 0
18 0 obj
<<
/C0 [
0
1
0
]
/C1 [
0
1
0
]
/Domain [
0
1
]
/FunctionType 2
/N 1
>>
endobj
%% Original object ID: 9 0
19 0 obj
<<
/C0 [
0
1
0
]
/C1 [
0
0
1
]
/Domain [
0
1
]
/FunctionType 2
/N 1
>>
endobj
%% Original object ID: 10 0
20 0 obj
<<
/C0 [
0
0
1
]
/C1 [
0
0
1
]
/Domain [
0
1
]
/FunctionType 2
/N 1
>>
endobj
xref
0 21
0000000000 65535 f
0000000053 00000 n
0000000135 00000 n
0000000293 00000 n
0000000402 00000 n
0000000661 00000 n
0000000995 00000 n
0000001042 00000 n
0000001187 00000 n
0000001236 00000 n
0000001608 00000 n
0000001659 00000 n
0000001716 00000 n
0000001763 00000 n
0000002259 00000 n
0000002310 00000 n
0000002543 00000 n
0000002637 00000 n
0000002683 00000 n
0000002846 00000 n
0000003010 00000 n
trailer <<
/Info 2 0 R
/Root 1 0 R
/Size 21
/ID [<68de518de1319b4dc1c1bfbdee95b02c><68de518de1319b4dc1c1bfbdee95b02c>]
>>
startxref
3146
%%EOF

336
test/pdfs/issue14165.pdf Normal file

File diff suppressed because one or more lines are too long

View File

@ -5407,6 +5407,12 @@
"enableXfa": true, "enableXfa": true,
"type": "eq" "type": "eq"
}, },
{ "id": "issue14165",
"file": "pdfs/issue14165.pdf",
"md5": "917850af2a387475b34b00010200897d",
"rounds": 1,
"type": "eq"
},
{ "id": "scorecard_reduced", { "id": "scorecard_reduced",
"file": "pdfs/scorecard_reduced.pdf", "file": "pdfs/scorecard_reduced.pdf",
"md5": "aa8ed0827092c963eea64adb718a3806", "md5": "aa8ed0827092c963eea64adb718a3806",
@ -6282,6 +6288,12 @@
} }
} }
}, },
{ "id": "issue10572",
"file": "pdfs/issue10572.pdf",
"md5": "48ad69ed106338b3c6845fc7101488b2",
"rounds": 1,
"type": "eq"
},
{ "id": "issue11931", { "id": "issue11931",
"file": "pdfs/issue11931.pdf", "file": "pdfs/issue11931.pdf",
"md5": "9ea233037992e1f10280420a49e72845", "md5": "9ea233037992e1f10280420a49e72845",