xref: /aosp_15_r20/external/pdfium/core/fpdfapi/render/cpdf_rendershading.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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