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