1 // Copyright 2019 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "core/fpdfapi/render/cpdf_rendershading.h"
8
9 #include <math.h>
10
11 #include <algorithm>
12 #include <array>
13 #include <memory>
14 #include <utility>
15 #include <vector>
16
17 #include "core/fpdfapi/page/cpdf_colorspace.h"
18 #include "core/fpdfapi/page/cpdf_dib.h"
19 #include "core/fpdfapi/page/cpdf_function.h"
20 #include "core/fpdfapi/page/cpdf_meshstream.h"
21 #include "core/fpdfapi/parser/cpdf_array.h"
22 #include "core/fpdfapi/parser/cpdf_dictionary.h"
23 #include "core/fpdfapi/parser/cpdf_stream.h"
24 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
25 #include "core/fpdfapi/render/cpdf_devicebuffer.h"
26 #include "core/fpdfapi/render/cpdf_renderoptions.h"
27 #include "core/fxcrt/fx_safe_types.h"
28 #include "core/fxcrt/fx_system.h"
29 #include "core/fxcrt/span_util.h"
30 #include "core/fxcrt/unowned_ptr.h"
31 #include "core/fxge/cfx_defaultrenderdevice.h"
32 #include "core/fxge/cfx_fillrenderoptions.h"
33 #include "core/fxge/cfx_path.h"
34 #include "core/fxge/dib/cfx_dibitmap.h"
35 #include "core/fxge/dib/fx_dib.h"
36 #include "third_party/base/check.h"
37 #include "third_party/base/check_op.h"
38 #include "third_party/base/containers/span.h"
39
40 namespace {
41
42 constexpr int kShadingSteps = 256;
43
CountOutputsFromFunctions(const std::vector<std::unique_ptr<CPDF_Function>> & funcs)44 uint32_t CountOutputsFromFunctions(
45 const std::vector<std::unique_ptr<CPDF_Function>>& funcs) {
46 FX_SAFE_UINT32 total = 0;
47 for (const auto& func : funcs) {
48 if (func)
49 total += func->CountOutputs();
50 }
51 return total.ValueOrDefault(0);
52 }
53
GetValidatedOutputsCount(const std::vector<std::unique_ptr<CPDF_Function>> & funcs,const RetainPtr<CPDF_ColorSpace> & pCS)54 uint32_t GetValidatedOutputsCount(
55 const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
56 const RetainPtr<CPDF_ColorSpace>& pCS) {
57 uint32_t funcs_outputs = CountOutputsFromFunctions(funcs);
58 return funcs_outputs ? std::max(funcs_outputs, pCS->CountComponents()) : 0;
59 }
60
GetShadingSteps(float t_min,float t_max,const std::vector<std::unique_ptr<CPDF_Function>> & funcs,const RetainPtr<CPDF_ColorSpace> & pCS,int alpha,size_t results_count)61 std::array<FX_ARGB, kShadingSteps> GetShadingSteps(
62 float t_min,
63 float t_max,
64 const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
65 const RetainPtr<CPDF_ColorSpace>& pCS,
66 int alpha,
67 size_t results_count) {
68 DCHECK(results_count >= CountOutputsFromFunctions(funcs));
69 DCHECK(results_count >= pCS->CountComponents());
70 std::array<FX_ARGB, kShadingSteps> shading_steps;
71 std::vector<float> result_array(results_count);
72 float diff = t_max - t_min;
73 for (int i = 0; i < kShadingSteps; ++i) {
74 float input = diff * i / kShadingSteps + t_min;
75 pdfium::span<float> result_span = pdfium::make_span(result_array);
76 for (const auto& func : funcs) {
77 if (!func)
78 continue;
79 absl::optional<uint32_t> nresults =
80 func->Call(pdfium::make_span(&input, 1), result_span);
81 if (nresults.has_value())
82 result_span = result_span.subspan(nresults.value());
83 }
84 float R = 0.0f;
85 float G = 0.0f;
86 float B = 0.0f;
87 pCS->GetRGB(result_array, &R, &G, &B);
88 shading_steps[i] = ArgbEncode(alpha, FXSYS_roundf(R * 255),
89 FXSYS_roundf(G * 255), FXSYS_roundf(B * 255));
90 }
91 return shading_steps;
92 }
93
DrawAxialShading(const RetainPtr<CFX_DIBitmap> & pBitmap,const CFX_Matrix & mtObject2Bitmap,const CPDF_Dictionary * pDict,const std::vector<std::unique_ptr<CPDF_Function>> & funcs,const RetainPtr<CPDF_ColorSpace> & pCS,int alpha)94 void DrawAxialShading(const RetainPtr<CFX_DIBitmap>& pBitmap,
95 const CFX_Matrix& mtObject2Bitmap,
96 const CPDF_Dictionary* pDict,
97 const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
98 const RetainPtr<CPDF_ColorSpace>& pCS,
99 int alpha) {
100 DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb);
101
102 const uint32_t total_results = GetValidatedOutputsCount(funcs, pCS);
103 if (total_results == 0)
104 return;
105
106 RetainPtr<const CPDF_Array> pCoords = pDict->GetArrayFor("Coords");
107 if (!pCoords)
108 return;
109
110 float start_x = pCoords->GetFloatAt(0);
111 float start_y = pCoords->GetFloatAt(1);
112 float end_x = pCoords->GetFloatAt(2);
113 float end_y = pCoords->GetFloatAt(3);
114 float t_min = 0;
115 float t_max = 1.0f;
116 RetainPtr<const CPDF_Array> pArray = pDict->GetArrayFor("Domain");
117 if (pArray) {
118 t_min = pArray->GetFloatAt(0);
119 t_max = pArray->GetFloatAt(1);
120 }
121 pArray = pDict->GetArrayFor("Extend");
122 const bool bStartExtend = pArray && pArray->GetBooleanAt(0, false);
123 const bool bEndExtend = pArray && pArray->GetBooleanAt(1, false);
124
125 int width = pBitmap->GetWidth();
126 int height = pBitmap->GetHeight();
127 float x_span = end_x - start_x;
128 float y_span = end_y - start_y;
129 float axis_len_square = (x_span * x_span) + (y_span * y_span);
130
131 std::array<FX_ARGB, kShadingSteps> shading_steps =
132 GetShadingSteps(t_min, t_max, funcs, pCS, alpha, total_results);
133
134 CFX_Matrix matrix = mtObject2Bitmap.GetInverse();
135 for (int row = 0; row < height; row++) {
136 uint32_t* dib_buf =
137 reinterpret_cast<uint32_t*>(pBitmap->GetWritableScanline(row).data());
138 for (int column = 0; column < width; column++) {
139 CFX_PointF pos = matrix.Transform(
140 CFX_PointF(static_cast<float>(column), static_cast<float>(row)));
141 float scale =
142 (((pos.x - start_x) * x_span) + ((pos.y - start_y) * y_span)) /
143 axis_len_square;
144 int index = static_cast<int32_t>(scale * (kShadingSteps - 1));
145 if (index < 0) {
146 if (!bStartExtend)
147 continue;
148
149 index = 0;
150 } else if (index >= kShadingSteps) {
151 if (!bEndExtend)
152 continue;
153
154 index = kShadingSteps - 1;
155 }
156 dib_buf[column] = shading_steps[index];
157 }
158 }
159 }
160
DrawRadialShading(const RetainPtr<CFX_DIBitmap> & pBitmap,const CFX_Matrix & mtObject2Bitmap,const CPDF_Dictionary * pDict,const std::vector<std::unique_ptr<CPDF_Function>> & funcs,const RetainPtr<CPDF_ColorSpace> & pCS,int alpha)161 void DrawRadialShading(const RetainPtr<CFX_DIBitmap>& pBitmap,
162 const CFX_Matrix& mtObject2Bitmap,
163 const CPDF_Dictionary* pDict,
164 const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
165 const RetainPtr<CPDF_ColorSpace>& pCS,
166 int alpha) {
167 DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb);
168
169 const uint32_t total_results = GetValidatedOutputsCount(funcs, pCS);
170 if (total_results == 0)
171 return;
172
173 RetainPtr<const CPDF_Array> pCoords = pDict->GetArrayFor("Coords");
174 if (!pCoords)
175 return;
176
177 float start_x = pCoords->GetFloatAt(0);
178 float start_y = pCoords->GetFloatAt(1);
179 float start_r = pCoords->GetFloatAt(2);
180 float end_x = pCoords->GetFloatAt(3);
181 float end_y = pCoords->GetFloatAt(4);
182 float end_r = pCoords->GetFloatAt(5);
183 float t_min = 0;
184 float t_max = 1.0f;
185 RetainPtr<const CPDF_Array> pArray = pDict->GetArrayFor("Domain");
186 if (pArray) {
187 t_min = pArray->GetFloatAt(0);
188 t_max = pArray->GetFloatAt(1);
189 }
190 pArray = pDict->GetArrayFor("Extend");
191 const bool bStartExtend = pArray && pArray->GetBooleanAt(0, false);
192 const bool bEndExtend = pArray && pArray->GetBooleanAt(1, false);
193
194 std::array<FX_ARGB, kShadingSteps> shading_steps =
195 GetShadingSteps(t_min, t_max, funcs, pCS, alpha, total_results);
196
197 const float dx = end_x - start_x;
198 const float dy = end_y - start_y;
199 const float dr = end_r - start_r;
200 const float a = dx * dx + dy * dy - dr * dr;
201 const bool a_is_float_zero = FXSYS_IsFloatZero(a);
202
203 int width = pBitmap->GetWidth();
204 int height = pBitmap->GetHeight();
205 bool bDecreasing = dr < 0 && static_cast<int>(FXSYS_sqrt2(dx, dy)) < -dr;
206
207 CFX_Matrix matrix = mtObject2Bitmap.GetInverse();
208 for (int row = 0; row < height; row++) {
209 uint32_t* dib_buf =
210 reinterpret_cast<uint32_t*>(pBitmap->GetWritableScanline(row).data());
211 for (int column = 0; column < width; column++) {
212 CFX_PointF pos = matrix.Transform(
213 CFX_PointF(static_cast<float>(column), static_cast<float>(row)));
214 float pos_dx = pos.x - start_x;
215 float pos_dy = pos.y - start_y;
216 float b = -2 * (pos_dx * dx + pos_dy * dy + start_r * dr);
217 float c = pos_dx * pos_dx + pos_dy * pos_dy - start_r * start_r;
218 float s;
219 if (FXSYS_IsFloatZero(b)) {
220 s = sqrt(-c / a);
221 } else if (a_is_float_zero) {
222 s = -c / b;
223 } else {
224 float b2_4ac = (b * b) - 4 * (a * c);
225 if (b2_4ac < 0)
226 continue;
227
228 float root = sqrt(b2_4ac);
229 float s1 = (-b - root) / (2 * a);
230 float s2 = (-b + root) / (2 * a);
231 if (a <= 0)
232 std::swap(s1, s2);
233 if (bDecreasing)
234 s = (s1 >= 0 || bStartExtend) ? s1 : s2;
235 else
236 s = (s2 <= 1.0f || bEndExtend) ? s2 : s1;
237
238 if (start_r + s * dr < 0)
239 continue;
240 }
241
242 int index = static_cast<int32_t>(s * (kShadingSteps - 1));
243 if (index < 0) {
244 if (!bStartExtend)
245 continue;
246 index = 0;
247 } else if (index >= kShadingSteps) {
248 if (!bEndExtend)
249 continue;
250 index = kShadingSteps - 1;
251 }
252 dib_buf[column] = shading_steps[index];
253 }
254 }
255 }
256
DrawFuncShading(const RetainPtr<CFX_DIBitmap> & pBitmap,const CFX_Matrix & mtObject2Bitmap,const CPDF_Dictionary * pDict,const std::vector<std::unique_ptr<CPDF_Function>> & funcs,const RetainPtr<CPDF_ColorSpace> & pCS,int alpha)257 void DrawFuncShading(const RetainPtr<CFX_DIBitmap>& pBitmap,
258 const CFX_Matrix& mtObject2Bitmap,
259 const CPDF_Dictionary* pDict,
260 const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
261 const RetainPtr<CPDF_ColorSpace>& pCS,
262 int alpha) {
263 DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb);
264
265 const uint32_t total_results = GetValidatedOutputsCount(funcs, pCS);
266 if (total_results == 0)
267 return;
268
269 RetainPtr<const CPDF_Array> pDomain = pDict->GetArrayFor("Domain");
270 float xmin = 0.0f;
271 float ymin = 0.0f;
272 float xmax = 1.0f;
273 float ymax = 1.0f;
274 if (pDomain) {
275 xmin = pDomain->GetFloatAt(0);
276 xmax = pDomain->GetFloatAt(1);
277 ymin = pDomain->GetFloatAt(2);
278 ymax = pDomain->GetFloatAt(3);
279 }
280 CFX_Matrix mtDomain2Target = pDict->GetMatrixFor("Matrix");
281 CFX_Matrix matrix =
282 mtObject2Bitmap.GetInverse() * mtDomain2Target.GetInverse();
283 int width = pBitmap->GetWidth();
284 int height = pBitmap->GetHeight();
285
286 DCHECK(total_results >= CountOutputsFromFunctions(funcs));
287 DCHECK(total_results >= pCS->CountComponents());
288 std::vector<float> result_array(total_results);
289 for (int row = 0; row < height; ++row) {
290 uint32_t* dib_buf =
291 reinterpret_cast<uint32_t*>(pBitmap->GetWritableScanline(row).data());
292 for (int column = 0; column < width; column++) {
293 CFX_PointF pos = matrix.Transform(
294 CFX_PointF(static_cast<float>(column), static_cast<float>(row)));
295 if (pos.x < xmin || pos.x > xmax || pos.y < ymin || pos.y > ymax)
296 continue;
297
298 float input[2] = {pos.x, pos.y};
299 pdfium::span<float> result_span = pdfium::make_span(result_array);
300 for (const auto& func : funcs) {
301 if (!func)
302 continue;
303 absl::optional<uint32_t> nresults = func->Call(input, result_span);
304 if (nresults.has_value())
305 result_span = result_span.subspan(nresults.value());
306 }
307 float R = 0.0f;
308 float G = 0.0f;
309 float B = 0.0f;
310 pCS->GetRGB(result_array, &R, &G, &B);
311 dib_buf[column] = ArgbEncode(alpha, static_cast<int32_t>(R * 255),
312 static_cast<int32_t>(G * 255),
313 static_cast<int32_t>(B * 255));
314 }
315 }
316 }
317
GetScanlineIntersect(int y,const CFX_PointF & first,const CFX_PointF & second,float * x)318 bool GetScanlineIntersect(int y,
319 const CFX_PointF& first,
320 const CFX_PointF& second,
321 float* x) {
322 if (first.y == second.y)
323 return false;
324
325 if (first.y < second.y) {
326 if (y < first.y || y > second.y)
327 return false;
328 } else if (y < second.y || y > first.y) {
329 return false;
330 }
331 *x = first.x + ((second.x - first.x) * (y - first.y) / (second.y - first.y));
332 return true;
333 }
334
DrawGouraud(const RetainPtr<CFX_DIBitmap> & pBitmap,int alpha,CPDF_MeshVertex triangle[3])335 void DrawGouraud(const RetainPtr<CFX_DIBitmap>& pBitmap,
336 int alpha,
337 CPDF_MeshVertex triangle[3]) {
338 float min_y = triangle[0].position.y;
339 float max_y = triangle[0].position.y;
340 for (int i = 1; i < 3; i++) {
341 min_y = std::min(min_y, triangle[i].position.y);
342 max_y = std::max(max_y, triangle[i].position.y);
343 }
344 if (min_y == max_y)
345 return;
346
347 int min_yi = std::max(static_cast<int>(floorf(min_y)), 0);
348 int max_yi = static_cast<int>(ceilf(max_y));
349 if (max_yi >= pBitmap->GetHeight())
350 max_yi = pBitmap->GetHeight() - 1;
351
352 for (int y = min_yi; y <= max_yi; y++) {
353 int nIntersects = 0;
354 float inter_x[3];
355 float r[3];
356 float g[3];
357 float b[3];
358 for (int i = 0; i < 3; i++) {
359 CPDF_MeshVertex& vertex1 = triangle[i];
360 CPDF_MeshVertex& vertex2 = triangle[(i + 1) % 3];
361 CFX_PointF& position1 = vertex1.position;
362 CFX_PointF& position2 = vertex2.position;
363 bool bIntersect =
364 GetScanlineIntersect(y, position1, position2, &inter_x[nIntersects]);
365 if (!bIntersect)
366 continue;
367
368 float y_dist = (y - position1.y) / (position2.y - position1.y);
369 r[nIntersects] = vertex1.r + ((vertex2.r - vertex1.r) * y_dist);
370 g[nIntersects] = vertex1.g + ((vertex2.g - vertex1.g) * y_dist);
371 b[nIntersects] = vertex1.b + ((vertex2.b - vertex1.b) * y_dist);
372 nIntersects++;
373 }
374 if (nIntersects != 2)
375 continue;
376
377 int min_x;
378 int max_x;
379 int start_index;
380 int end_index;
381 if (inter_x[0] < inter_x[1]) {
382 min_x = static_cast<int>(floorf(inter_x[0]));
383 max_x = static_cast<int>(ceilf(inter_x[1]));
384 start_index = 0;
385 end_index = 1;
386 } else {
387 min_x = static_cast<int>(floorf(inter_x[1]));
388 max_x = static_cast<int>(ceilf(inter_x[0]));
389 start_index = 1;
390 end_index = 0;
391 }
392
393 int start_x = std::clamp(min_x, 0, pBitmap->GetWidth());
394 int end_x = std::clamp(max_x, 0, pBitmap->GetWidth());
395 float r_unit = (r[end_index] - r[start_index]) / (max_x - min_x);
396 float g_unit = (g[end_index] - g[start_index]) / (max_x - min_x);
397 float b_unit = (b[end_index] - b[start_index]) / (max_x - min_x);
398 float r_result = r[start_index] + (start_x - min_x) * r_unit;
399 float g_result = g[start_index] + (start_x - min_x) * g_unit;
400 float b_result = b[start_index] + (start_x - min_x) * b_unit;
401 pdfium::span<uint8_t> dib_span =
402 pBitmap->GetWritableScanline(y).subspan(start_x * 4);
403
404 for (int x = start_x; x < end_x; x++) {
405 uint8_t* dib_buf = dib_span.data();
406 r_result += r_unit;
407 g_result += g_unit;
408 b_result += b_unit;
409 FXARGB_SETDIB(dib_buf, ArgbEncode(alpha, static_cast<int>(r_result * 255),
410 static_cast<int>(g_result * 255),
411 static_cast<int>(b_result * 255)));
412 dib_span = dib_span.subspan(4);
413 }
414 }
415 }
416
DrawFreeGouraudShading(const RetainPtr<CFX_DIBitmap> & pBitmap,const CFX_Matrix & mtObject2Bitmap,RetainPtr<const CPDF_Stream> pShadingStream,const std::vector<std::unique_ptr<CPDF_Function>> & funcs,RetainPtr<CPDF_ColorSpace> pCS,int alpha)417 void DrawFreeGouraudShading(
418 const RetainPtr<CFX_DIBitmap>& pBitmap,
419 const CFX_Matrix& mtObject2Bitmap,
420 RetainPtr<const CPDF_Stream> pShadingStream,
421 const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
422 RetainPtr<CPDF_ColorSpace> pCS,
423 int alpha) {
424 DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb);
425
426 CPDF_MeshStream stream(kFreeFormGouraudTriangleMeshShading, funcs,
427 std::move(pShadingStream), std::move(pCS));
428 if (!stream.Load())
429 return;
430
431 CPDF_MeshVertex triangle[3];
432 while (!stream.IsEOF()) {
433 CPDF_MeshVertex vertex;
434 uint32_t flag;
435 if (!stream.ReadVertex(mtObject2Bitmap, &vertex, &flag))
436 return;
437
438 if (flag == 0) {
439 triangle[0] = vertex;
440 for (int i = 1; i < 3; ++i) {
441 uint32_t dummy_flag;
442 if (!stream.ReadVertex(mtObject2Bitmap, &triangle[i], &dummy_flag))
443 return;
444 }
445 } else {
446 if (flag == 1)
447 triangle[0] = triangle[1];
448
449 triangle[1] = triangle[2];
450 triangle[2] = vertex;
451 }
452 DrawGouraud(pBitmap, alpha, triangle);
453 }
454 }
455
DrawLatticeGouraudShading(const RetainPtr<CFX_DIBitmap> & pBitmap,const CFX_Matrix & mtObject2Bitmap,RetainPtr<const CPDF_Stream> pShadingStream,const std::vector<std::unique_ptr<CPDF_Function>> & funcs,RetainPtr<CPDF_ColorSpace> pCS,int alpha)456 void DrawLatticeGouraudShading(
457 const RetainPtr<CFX_DIBitmap>& pBitmap,
458 const CFX_Matrix& mtObject2Bitmap,
459 RetainPtr<const CPDF_Stream> pShadingStream,
460 const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
461 RetainPtr<CPDF_ColorSpace> pCS,
462 int alpha) {
463 DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb);
464
465 int row_verts = pShadingStream->GetDict()->GetIntegerFor("VerticesPerRow");
466 if (row_verts < 2)
467 return;
468
469 CPDF_MeshStream stream(kLatticeFormGouraudTriangleMeshShading, funcs,
470 std::move(pShadingStream), std::move(pCS));
471 if (!stream.Load())
472 return;
473
474 std::vector<CPDF_MeshVertex> vertices[2];
475 vertices[0] = stream.ReadVertexRow(mtObject2Bitmap, row_verts);
476 if (vertices[0].empty())
477 return;
478
479 int last_index = 0;
480 while (true) {
481 vertices[1 - last_index] = stream.ReadVertexRow(mtObject2Bitmap, row_verts);
482 if (vertices[1 - last_index].empty())
483 return;
484
485 CPDF_MeshVertex triangle[3];
486 for (int i = 1; i < row_verts; ++i) {
487 triangle[0] = vertices[last_index][i];
488 triangle[1] = vertices[1 - last_index][i - 1];
489 triangle[2] = vertices[last_index][i - 1];
490 DrawGouraud(pBitmap, alpha, triangle);
491 triangle[2] = vertices[1 - last_index][i];
492 DrawGouraud(pBitmap, alpha, triangle);
493 }
494 last_index = 1 - last_index;
495 }
496 }
497
498 struct CoonBezierCoeff {
InitFromPoints__anonf1719f160111::CoonBezierCoeff499 void InitFromPoints(float p0, float p1, float p2, float p3) {
500 a = -p0 + 3 * p1 - 3 * p2 + p3;
501 b = 3 * p0 - 6 * p1 + 3 * p2;
502 c = -3 * p0 + 3 * p1;
503 d = p0;
504 }
505
InitFromBezierInterpolation__anonf1719f160111::CoonBezierCoeff506 void InitFromBezierInterpolation(const CoonBezierCoeff& C1,
507 const CoonBezierCoeff& C2,
508 const CoonBezierCoeff& D1,
509 const CoonBezierCoeff& D2) {
510 a = (D1.a + D2.a) / 2;
511 b = (D1.b + D2.b) / 2;
512 c = (D1.c + D2.c) / 2 - (C1.a / 8 + C1.b / 4 + C1.c / 2) +
513 (C2.a / 8 + C2.b / 4) + (-C1.d + D2.d) / 2 - (C2.a + C2.b) / 2;
514 d = C1.a / 8 + C1.b / 4 + C1.c / 2 + C1.d;
515 }
516
first_half__anonf1719f160111::CoonBezierCoeff517 CoonBezierCoeff first_half() const {
518 CoonBezierCoeff result;
519 result.a = a / 8;
520 result.b = b / 4;
521 result.c = c / 2;
522 result.d = d;
523 return result;
524 }
525
second_half__anonf1719f160111::CoonBezierCoeff526 CoonBezierCoeff second_half() const {
527 CoonBezierCoeff result;
528 result.a = a / 8;
529 result.b = 3 * a / 8 + b / 4;
530 result.c = 3 * a / 8 + b / 2 + c / 2;
531 result.d = a / 8 + b / 4 + c / 2 + d;
532 return result;
533 }
534
GetPoints__anonf1719f160111::CoonBezierCoeff535 void GetPoints(float p[4]) const {
536 p[0] = d;
537 p[1] = c / 3 + p[0];
538 p[2] = b / 3 - p[0] + 2 * p[1];
539 p[3] = a + p[0] - 3 * p[1] + 3 * p[2];
540 }
541
Distance__anonf1719f160111::CoonBezierCoeff542 float Distance() const {
543 float dis = a + b + c;
544 return dis < 0 ? -dis : dis;
545 }
546
547 float a;
548 float b;
549 float c;
550 float d;
551 };
552
553 struct CoonBezier {
InitFromPoints__anonf1719f160111::CoonBezier554 void InitFromPoints(float x0,
555 float y0,
556 float x1,
557 float y1,
558 float x2,
559 float y2,
560 float x3,
561 float y3) {
562 x.InitFromPoints(x0, x1, x2, x3);
563 y.InitFromPoints(y0, y1, y2, y3);
564 }
565
InitFromBezierInterpolation__anonf1719f160111::CoonBezier566 void InitFromBezierInterpolation(const CoonBezier& C1,
567 const CoonBezier& C2,
568 const CoonBezier& D1,
569 const CoonBezier& D2) {
570 x.InitFromBezierInterpolation(C1.x, C2.x, D1.x, D2.x);
571 y.InitFromBezierInterpolation(C1.y, C2.y, D1.y, D2.y);
572 }
573
first_half__anonf1719f160111::CoonBezier574 CoonBezier first_half() const {
575 CoonBezier result;
576 result.x = x.first_half();
577 result.y = y.first_half();
578 return result;
579 }
580
second_half__anonf1719f160111::CoonBezier581 CoonBezier second_half() const {
582 CoonBezier result;
583 result.x = x.second_half();
584 result.y = y.second_half();
585 return result;
586 }
587
GetPoints__anonf1719f160111::CoonBezier588 void GetPoints(pdfium::span<CFX_Path::Point> path_points) const {
589 constexpr size_t kPointsCount = 4;
590 float points_x[kPointsCount];
591 float points_y[kPointsCount];
592 x.GetPoints(points_x);
593 y.GetPoints(points_y);
594 for (size_t i = 0; i < kPointsCount; ++i)
595 path_points[i].m_Point = {points_x[i], points_y[i]};
596 }
597
GetPointsReverse__anonf1719f160111::CoonBezier598 void GetPointsReverse(pdfium::span<CFX_Path::Point> path_points) const {
599 constexpr size_t kPointsCount = 4;
600 float points_x[kPointsCount];
601 float points_y[kPointsCount];
602 x.GetPoints(points_x);
603 y.GetPoints(points_y);
604 for (size_t i = 0; i < kPointsCount; ++i) {
605 size_t reverse_index = kPointsCount - i - 1;
606 path_points[i].m_Point = {points_x[reverse_index],
607 points_y[reverse_index]};
608 }
609 }
610
Distance__anonf1719f160111::CoonBezier611 float Distance() const { return x.Distance() + y.Distance(); }
612
613 CoonBezierCoeff x;
614 CoonBezierCoeff y;
615 };
616
Interpolate(int p1,int p2,int delta1,int delta2,bool * overflow)617 int Interpolate(int p1, int p2, int delta1, int delta2, bool* overflow) {
618 FX_SAFE_INT32 p = p2;
619 p -= p1;
620 p *= delta1;
621 p /= delta2;
622 p += p1;
623 if (!p.IsValid())
624 *overflow = true;
625 return p.ValueOrDefault(0);
626 }
627
BiInterpolImpl(int c0,int c1,int c2,int c3,int x,int y,int x_scale,int y_scale,bool * overflow)628 int BiInterpolImpl(int c0,
629 int c1,
630 int c2,
631 int c3,
632 int x,
633 int y,
634 int x_scale,
635 int y_scale,
636 bool* overflow) {
637 int x1 = Interpolate(c0, c3, x, x_scale, overflow);
638 int x2 = Interpolate(c1, c2, x, x_scale, overflow);
639 return Interpolate(x1, x2, y, y_scale, overflow);
640 }
641
642 struct CoonColor {
643 CoonColor() = default;
644
645 // Returns true if successful, false if overflow detected.
BiInterpol__anonf1719f160111::CoonColor646 bool BiInterpol(CoonColor colors[4], int x, int y, int x_scale, int y_scale) {
647 bool overflow = false;
648 for (int i = 0; i < 3; i++) {
649 comp[i] = BiInterpolImpl(colors[0].comp[i], colors[1].comp[i],
650 colors[2].comp[i], colors[3].comp[i], x, y,
651 x_scale, y_scale, &overflow);
652 }
653 return !overflow;
654 }
655
Distance__anonf1719f160111::CoonColor656 int Distance(const CoonColor& o) const {
657 return std::max({abs(comp[0] - o.comp[0]), abs(comp[1] - o.comp[1]),
658 abs(comp[2] - o.comp[2])});
659 }
660
661 int comp[3] = {};
662 };
663
664 struct PatchDrawer {
665 static constexpr int kCoonColorThreshold = 4;
666
Draw__anonf1719f160111::PatchDrawer667 void Draw(int x_scale,
668 int y_scale,
669 int left,
670 int bottom,
671 CoonBezier C1,
672 CoonBezier C2,
673 CoonBezier D1,
674 CoonBezier D2) {
675 bool bSmall = C1.Distance() < 2 && C2.Distance() < 2 && D1.Distance() < 2 &&
676 D2.Distance() < 2;
677 CoonColor div_colors[4];
678 int d_bottom = 0;
679 int d_left = 0;
680 int d_top = 0;
681 int d_right = 0;
682 if (!div_colors[0].BiInterpol(patch_colors, left, bottom, x_scale,
683 y_scale)) {
684 return;
685 }
686 if (!bSmall) {
687 if (!div_colors[1].BiInterpol(patch_colors, left, bottom + 1, x_scale,
688 y_scale)) {
689 return;
690 }
691 if (!div_colors[2].BiInterpol(patch_colors, left + 1, bottom + 1, x_scale,
692 y_scale)) {
693 return;
694 }
695 if (!div_colors[3].BiInterpol(patch_colors, left + 1, bottom, x_scale,
696 y_scale)) {
697 return;
698 }
699 d_bottom = div_colors[3].Distance(div_colors[0]);
700 d_left = div_colors[1].Distance(div_colors[0]);
701 d_top = div_colors[1].Distance(div_colors[2]);
702 d_right = div_colors[2].Distance(div_colors[3]);
703 }
704
705 if (bSmall ||
706 (d_bottom < kCoonColorThreshold && d_left < kCoonColorThreshold &&
707 d_top < kCoonColorThreshold && d_right < kCoonColorThreshold)) {
708 pdfium::span<CFX_Path::Point> points = path.GetPoints();
709 C1.GetPoints(points.subspan(0, 4));
710 D2.GetPoints(points.subspan(3, 4));
711 C2.GetPointsReverse(points.subspan(6, 4));
712 D1.GetPointsReverse(points.subspan(9, 4));
713 CFX_FillRenderOptions fill_options(
714 CFX_FillRenderOptions::WindingOptions());
715 fill_options.full_cover = true;
716 if (bNoPathSmooth)
717 fill_options.aliased_path = true;
718 pDevice->DrawPath(
719 path, nullptr, nullptr,
720 ArgbEncode(alpha, div_colors[0].comp[0], div_colors[0].comp[1],
721 div_colors[0].comp[2]),
722 0, fill_options);
723 } else {
724 if (d_bottom < kCoonColorThreshold && d_top < kCoonColorThreshold) {
725 CoonBezier m1;
726 m1.InitFromBezierInterpolation(D1, D2, C1, C2);
727 y_scale *= 2;
728 bottom *= 2;
729 Draw(x_scale, y_scale, left, bottom, C1, m1, D1.first_half(),
730 D2.first_half());
731 Draw(x_scale, y_scale, left, bottom + 1, m1, C2, D1.second_half(),
732 D2.second_half());
733 } else if (d_left < kCoonColorThreshold &&
734 d_right < kCoonColorThreshold) {
735 CoonBezier m2;
736 m2.InitFromBezierInterpolation(C1, C2, D1, D2);
737 x_scale *= 2;
738 left *= 2;
739 Draw(x_scale, y_scale, left, bottom, C1.first_half(), C2.first_half(),
740 D1, m2);
741 Draw(x_scale, y_scale, left + 1, bottom, C1.second_half(),
742 C2.second_half(), m2, D2);
743 } else {
744 CoonBezier m1;
745 CoonBezier m2;
746 m1.InitFromBezierInterpolation(D1, D2, C1, C2);
747 m2.InitFromBezierInterpolation(C1, C2, D1, D2);
748 CoonBezier m1f = m1.first_half();
749 CoonBezier m1s = m1.second_half();
750 CoonBezier m2f = m2.first_half();
751 CoonBezier m2s = m2.second_half();
752 x_scale *= 2;
753 y_scale *= 2;
754 left *= 2;
755 bottom *= 2;
756 Draw(x_scale, y_scale, left, bottom, C1.first_half(), m1f,
757 D1.first_half(), m2f);
758 Draw(x_scale, y_scale, left, bottom + 1, m1f, C2.first_half(),
759 D1.second_half(), m2s);
760 Draw(x_scale, y_scale, left + 1, bottom, C1.second_half(), m1s, m2f,
761 D2.first_half());
762 Draw(x_scale, y_scale, left + 1, bottom + 1, m1s, C2.second_half(), m2s,
763 D2.second_half());
764 }
765 }
766 }
767
768 int max_delta;
769 CFX_Path path;
770 UnownedPtr<CFX_RenderDevice> pDevice;
771 int bNoPathSmooth;
772 int alpha;
773 CoonColor patch_colors[4];
774 };
775
DrawCoonPatchMeshes(ShadingType type,const RetainPtr<CFX_DIBitmap> & pBitmap,const CFX_Matrix & mtObject2Bitmap,RetainPtr<const CPDF_Stream> pShadingStream,const std::vector<std::unique_ptr<CPDF_Function>> & funcs,RetainPtr<CPDF_ColorSpace> pCS,bool bNoPathSmooth,int alpha)776 void DrawCoonPatchMeshes(
777 ShadingType type,
778 const RetainPtr<CFX_DIBitmap>& pBitmap,
779 const CFX_Matrix& mtObject2Bitmap,
780 RetainPtr<const CPDF_Stream> pShadingStream,
781 const std::vector<std::unique_ptr<CPDF_Function>>& funcs,
782 RetainPtr<CPDF_ColorSpace> pCS,
783 bool bNoPathSmooth,
784 int alpha) {
785 DCHECK_EQ(pBitmap->GetFormat(), FXDIB_Format::kArgb);
786 DCHECK(type == kCoonsPatchMeshShading ||
787 type == kTensorProductPatchMeshShading);
788
789 CFX_DefaultRenderDevice device;
790 device.Attach(pBitmap);
791
792 CPDF_MeshStream stream(type, funcs, std::move(pShadingStream),
793 std::move(pCS));
794 if (!stream.Load())
795 return;
796
797 PatchDrawer patch;
798 patch.alpha = alpha;
799 patch.pDevice = &device;
800 patch.bNoPathSmooth = bNoPathSmooth;
801
802 for (int i = 0; i < 13; i++) {
803 patch.path.AppendPoint(CFX_PointF(), i == 0
804 ? CFX_Path::Point::Type::kMove
805 : CFX_Path::Point::Type::kBezier);
806 }
807
808 CFX_PointF coords[16];
809 int point_count = type == kTensorProductPatchMeshShading ? 16 : 12;
810 while (!stream.IsEOF()) {
811 if (!stream.CanReadFlag())
812 break;
813 uint32_t flag = stream.ReadFlag();
814 int iStartPoint = 0;
815 int iStartColor = 0;
816 int i = 0;
817 if (flag) {
818 iStartPoint = 4;
819 iStartColor = 2;
820 CFX_PointF tempCoords[4];
821 for (i = 0; i < 4; i++) {
822 tempCoords[i] = coords[(flag * 3 + i) % 12];
823 }
824 fxcrt::spancpy(pdfium::make_span(coords), pdfium::make_span(tempCoords));
825 CoonColor tempColors[2] = {
826 tempColors[0] = patch.patch_colors[flag],
827 tempColors[1] = patch.patch_colors[(flag + 1) % 4]};
828 fxcrt::spancpy(pdfium::make_span(patch.patch_colors),
829 pdfium::make_span(tempColors));
830 }
831 for (i = iStartPoint; i < point_count; i++) {
832 if (!stream.CanReadCoords())
833 break;
834 coords[i] = mtObject2Bitmap.Transform(stream.ReadCoords());
835 }
836
837 for (i = iStartColor; i < 4; i++) {
838 if (!stream.CanReadColor())
839 break;
840
841 float r;
842 float g;
843 float b;
844 std::tie(r, g, b) = stream.ReadColor();
845
846 patch.patch_colors[i].comp[0] = static_cast<int32_t>(r * 255);
847 patch.patch_colors[i].comp[1] = static_cast<int32_t>(g * 255);
848 patch.patch_colors[i].comp[2] = static_cast<int32_t>(b * 255);
849 }
850 CFX_FloatRect bbox =
851 CFX_FloatRect::GetBBox(pdfium::make_span(coords).first(point_count));
852 if (bbox.right <= 0 || bbox.left >= (float)pBitmap->GetWidth() ||
853 bbox.top <= 0 || bbox.bottom >= (float)pBitmap->GetHeight()) {
854 continue;
855 }
856 CoonBezier C1;
857 CoonBezier C2;
858 CoonBezier D1;
859 CoonBezier D2;
860 C1.InitFromPoints(coords[0].x, coords[0].y, coords[11].x, coords[11].y,
861 coords[10].x, coords[10].y, coords[9].x, coords[9].y);
862 C2.InitFromPoints(coords[3].x, coords[3].y, coords[4].x, coords[4].y,
863 coords[5].x, coords[5].y, coords[6].x, coords[6].y);
864 D1.InitFromPoints(coords[0].x, coords[0].y, coords[1].x, coords[1].y,
865 coords[2].x, coords[2].y, coords[3].x, coords[3].y);
866 D2.InitFromPoints(coords[9].x, coords[9].y, coords[8].x, coords[8].y,
867 coords[7].x, coords[7].y, coords[6].x, coords[6].y);
868 patch.Draw(1, 1, 0, 0, C1, C2, D1, D2);
869 }
870 }
871
872 } // namespace
873
874 // static
Draw(CFX_RenderDevice * pDevice,CPDF_RenderContext * pContext,const CPDF_PageObject * pCurObj,const CPDF_ShadingPattern * pPattern,const CFX_Matrix & mtMatrix,const FX_RECT & clip_rect,int alpha,const CPDF_RenderOptions & options)875 void CPDF_RenderShading::Draw(CFX_RenderDevice* pDevice,
876 CPDF_RenderContext* pContext,
877 const CPDF_PageObject* pCurObj,
878 const CPDF_ShadingPattern* pPattern,
879 const CFX_Matrix& mtMatrix,
880 const FX_RECT& clip_rect,
881 int alpha,
882 const CPDF_RenderOptions& options) {
883 RetainPtr<CPDF_ColorSpace> pColorSpace = pPattern->GetCS();
884 if (!pColorSpace)
885 return;
886
887 FX_ARGB background = 0;
888 RetainPtr<const CPDF_Dictionary> pDict =
889 pPattern->GetShadingObject()->GetDict();
890 if (!pPattern->IsShadingObject() && pDict->KeyExist("Background")) {
891 RetainPtr<const CPDF_Array> pBackColor = pDict->GetArrayFor("Background");
892 if (pBackColor && pBackColor->size() >= pColorSpace->CountComponents()) {
893 std::vector<float> comps = ReadArrayElementsToVector(
894 pBackColor.Get(), pColorSpace->CountComponents());
895
896 float R = 0.0f;
897 float G = 0.0f;
898 float B = 0.0f;
899 pColorSpace->GetRGB(comps, &R, &G, &B);
900 background = ArgbEncode(255, static_cast<int32_t>(R * 255),
901 static_cast<int32_t>(G * 255),
902 static_cast<int32_t>(B * 255));
903 }
904 }
905 FX_RECT clip_rect_bbox = clip_rect;
906 if (pDict->KeyExist("BBox")) {
907 clip_rect_bbox.Intersect(
908 mtMatrix.TransformRect(pDict->GetRectFor("BBox")).GetOuterRect());
909 }
910 bool bAlphaMode = options.ColorModeIs(CPDF_RenderOptions::kAlpha);
911 if (pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SHADING &&
912 pDevice->DrawShading(pPattern, &mtMatrix, clip_rect_bbox, alpha,
913 bAlphaMode)) {
914 return;
915 }
916 CPDF_DeviceBuffer buffer(pContext, pDevice, clip_rect_bbox, pCurObj, 150);
917 if (!buffer.Initialize())
918 return;
919
920 RetainPtr<CFX_DIBitmap> pBitmap = buffer.GetBitmap();
921 if (pBitmap->GetBuffer().empty())
922 return;
923
924 if (background != 0) {
925 pBitmap->Clear(background);
926 }
927 const CFX_Matrix final_matrix = mtMatrix * buffer.GetMatrix();
928 const auto& funcs = pPattern->GetFuncs();
929 switch (pPattern->GetShadingType()) {
930 case kInvalidShading:
931 case kMaxShading:
932 return;
933 case kFunctionBasedShading:
934 DrawFuncShading(pBitmap, final_matrix, pDict.Get(), funcs, pColorSpace,
935 alpha);
936 break;
937 case kAxialShading:
938 DrawAxialShading(pBitmap, final_matrix, pDict.Get(), funcs, pColorSpace,
939 alpha);
940 break;
941 case kRadialShading:
942 DrawRadialShading(pBitmap, final_matrix, pDict.Get(), funcs, pColorSpace,
943 alpha);
944 break;
945 case kFreeFormGouraudTriangleMeshShading: {
946 // The shading object can be a stream or a dictionary. We do not handle
947 // the case of dictionary at the moment.
948 RetainPtr<const CPDF_Stream> pStream =
949 ToStream(pPattern->GetShadingObject());
950 if (pStream) {
951 DrawFreeGouraudShading(pBitmap, final_matrix, std::move(pStream), funcs,
952 pColorSpace, alpha);
953 }
954 break;
955 }
956 case kLatticeFormGouraudTriangleMeshShading: {
957 // The shading object can be a stream or a dictionary. We do not handle
958 // the case of dictionary at the moment.
959 RetainPtr<const CPDF_Stream> pStream =
960 ToStream(pPattern->GetShadingObject());
961 if (pStream) {
962 DrawLatticeGouraudShading(pBitmap, final_matrix, std::move(pStream),
963 funcs, pColorSpace, alpha);
964 }
965 break;
966 }
967 case kCoonsPatchMeshShading:
968 case kTensorProductPatchMeshShading: {
969 // The shading object can be a stream or a dictionary. We do not handle
970 // the case of dictionary at the moment.
971 RetainPtr<const CPDF_Stream> pStream =
972 ToStream(pPattern->GetShadingObject());
973 if (pStream) {
974 DrawCoonPatchMeshes(pPattern->GetShadingType(), pBitmap, final_matrix,
975 std::move(pStream), funcs, pColorSpace,
976 options.GetOptions().bNoPathSmooth, alpha);
977 }
978 break;
979 }
980 }
981 if (bAlphaMode)
982 pBitmap->SetRedFromBitmap(pBitmap);
983
984 if (options.ColorModeIs(CPDF_RenderOptions::kGray))
985 pBitmap->ConvertColorScale(0, 0xffffff);
986
987 buffer.OutputToDevice();
988 }
989