OLD | NEW |
| (Empty) |
1 // Copyright 2014 PDFium Authors. All rights reserved. | |
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/fpdf_render/render_int.h" | |
8 | |
9 #include <algorithm> | |
10 | |
11 #include "core/fpdfapi/fpdf_render/cpdf_rendercontext.h" | |
12 #include "core/fpdfapi/fpdf_render/cpdf_renderoptions.h" | |
13 #include "core/fpdfapi/page/cpdf_form.h" | |
14 #include "core/fpdfapi/page/cpdf_graphicstates.h" | |
15 #include "core/fpdfapi/page/cpdf_meshstream.h" | |
16 #include "core/fpdfapi/page/cpdf_pageobject.h" | |
17 #include "core/fpdfapi/page/cpdf_pathobject.h" | |
18 #include "core/fpdfapi/page/cpdf_shadingobject.h" | |
19 #include "core/fpdfapi/page/cpdf_shadingpattern.h" | |
20 #include "core/fpdfapi/page/cpdf_tilingpattern.h" | |
21 #include "core/fpdfapi/page/pageint.h" | |
22 #include "core/fpdfapi/parser/cpdf_array.h" | |
23 #include "core/fpdfapi/parser/cpdf_dictionary.h" | |
24 #include "core/fxge/cfx_fxgedevice.h" | |
25 #include "core/fxge/cfx_pathdata.h" | |
26 #include "core/fxge/cfx_renderdevice.h" | |
27 #include "core/fxge/ifx_renderdevicedriver.h" | |
28 | |
29 namespace { | |
30 | |
31 uint32_t CountOutputs( | |
32 const std::vector<std::unique_ptr<CPDF_Function>>& funcs) { | |
33 uint32_t total = 0; | |
34 for (const auto& func : funcs) { | |
35 if (func) | |
36 total += func->CountOutputs(); | |
37 } | |
38 return total; | |
39 } | |
40 | |
41 #define SHADING_STEPS 256 | |
42 void DrawAxialShading(CFX_DIBitmap* pBitmap, | |
43 CFX_Matrix* pObject2Bitmap, | |
44 CPDF_Dictionary* pDict, | |
45 const std::vector<std::unique_ptr<CPDF_Function>>& funcs, | |
46 CPDF_ColorSpace* pCS, | |
47 int alpha) { | |
48 ASSERT(pBitmap->GetFormat() == FXDIB_Argb); | |
49 CPDF_Array* pCoords = pDict->GetArrayFor("Coords"); | |
50 if (!pCoords) { | |
51 return; | |
52 } | |
53 FX_FLOAT start_x = pCoords->GetNumberAt(0); | |
54 FX_FLOAT start_y = pCoords->GetNumberAt(1); | |
55 FX_FLOAT end_x = pCoords->GetNumberAt(2); | |
56 FX_FLOAT end_y = pCoords->GetNumberAt(3); | |
57 FX_FLOAT t_min = 0, t_max = 1.0f; | |
58 CPDF_Array* pArray = pDict->GetArrayFor("Domain"); | |
59 if (pArray) { | |
60 t_min = pArray->GetNumberAt(0); | |
61 t_max = pArray->GetNumberAt(1); | |
62 } | |
63 FX_BOOL bStartExtend = FALSE, bEndExtend = FALSE; | |
64 pArray = pDict->GetArrayFor("Extend"); | |
65 if (pArray) { | |
66 bStartExtend = pArray->GetIntegerAt(0); | |
67 bEndExtend = pArray->GetIntegerAt(1); | |
68 } | |
69 int width = pBitmap->GetWidth(); | |
70 int height = pBitmap->GetHeight(); | |
71 FX_FLOAT x_span = end_x - start_x; | |
72 FX_FLOAT y_span = end_y - start_y; | |
73 FX_FLOAT axis_len_square = (x_span * x_span) + (y_span * y_span); | |
74 CFX_Matrix matrix; | |
75 matrix.SetReverse(*pObject2Bitmap); | |
76 uint32_t total_results = | |
77 std::max(CountOutputs(funcs), pCS->CountComponents()); | |
78 CFX_FixedBufGrow<FX_FLOAT, 16> result_array(total_results); | |
79 FX_FLOAT* pResults = result_array; | |
80 FXSYS_memset(pResults, 0, total_results * sizeof(FX_FLOAT)); | |
81 uint32_t rgb_array[SHADING_STEPS]; | |
82 for (int i = 0; i < SHADING_STEPS; i++) { | |
83 FX_FLOAT input = (t_max - t_min) * i / SHADING_STEPS + t_min; | |
84 int offset = 0; | |
85 for (const auto& func : funcs) { | |
86 if (func) { | |
87 int nresults = 0; | |
88 if (func->Call(&input, 1, pResults + offset, nresults)) | |
89 offset += nresults; | |
90 } | |
91 } | |
92 FX_FLOAT R = 0.0f, G = 0.0f, B = 0.0f; | |
93 pCS->GetRGB(pResults, R, G, B); | |
94 rgb_array[i] = | |
95 FXARGB_TODIB(FXARGB_MAKE(alpha, FXSYS_round(R * 255), | |
96 FXSYS_round(G * 255), FXSYS_round(B * 255))); | |
97 } | |
98 int pitch = pBitmap->GetPitch(); | |
99 for (int row = 0; row < height; row++) { | |
100 uint32_t* dib_buf = (uint32_t*)(pBitmap->GetBuffer() + row * pitch); | |
101 for (int column = 0; column < width; column++) { | |
102 FX_FLOAT x = (FX_FLOAT)column, y = (FX_FLOAT)row; | |
103 matrix.Transform(x, y); | |
104 FX_FLOAT scale = (((x - start_x) * x_span) + ((y - start_y) * y_span)) / | |
105 axis_len_square; | |
106 int index = (int32_t)(scale * (SHADING_STEPS - 1)); | |
107 if (index < 0) { | |
108 if (!bStartExtend) { | |
109 continue; | |
110 } | |
111 index = 0; | |
112 } else if (index >= SHADING_STEPS) { | |
113 if (!bEndExtend) { | |
114 continue; | |
115 } | |
116 index = SHADING_STEPS - 1; | |
117 } | |
118 dib_buf[column] = rgb_array[index]; | |
119 } | |
120 } | |
121 } | |
122 | |
123 void DrawRadialShading(CFX_DIBitmap* pBitmap, | |
124 CFX_Matrix* pObject2Bitmap, | |
125 CPDF_Dictionary* pDict, | |
126 const std::vector<std::unique_ptr<CPDF_Function>>& funcs, | |
127 CPDF_ColorSpace* pCS, | |
128 int alpha) { | |
129 ASSERT(pBitmap->GetFormat() == FXDIB_Argb); | |
130 CPDF_Array* pCoords = pDict->GetArrayFor("Coords"); | |
131 if (!pCoords) { | |
132 return; | |
133 } | |
134 FX_FLOAT start_x = pCoords->GetNumberAt(0); | |
135 FX_FLOAT start_y = pCoords->GetNumberAt(1); | |
136 FX_FLOAT start_r = pCoords->GetNumberAt(2); | |
137 FX_FLOAT end_x = pCoords->GetNumberAt(3); | |
138 FX_FLOAT end_y = pCoords->GetNumberAt(4); | |
139 FX_FLOAT end_r = pCoords->GetNumberAt(5); | |
140 CFX_Matrix matrix; | |
141 matrix.SetReverse(*pObject2Bitmap); | |
142 FX_FLOAT t_min = 0, t_max = 1.0f; | |
143 CPDF_Array* pArray = pDict->GetArrayFor("Domain"); | |
144 if (pArray) { | |
145 t_min = pArray->GetNumberAt(0); | |
146 t_max = pArray->GetNumberAt(1); | |
147 } | |
148 FX_BOOL bStartExtend = FALSE, bEndExtend = FALSE; | |
149 pArray = pDict->GetArrayFor("Extend"); | |
150 if (pArray) { | |
151 bStartExtend = pArray->GetIntegerAt(0); | |
152 bEndExtend = pArray->GetIntegerAt(1); | |
153 } | |
154 uint32_t total_results = | |
155 std::max(CountOutputs(funcs), pCS->CountComponents()); | |
156 CFX_FixedBufGrow<FX_FLOAT, 16> result_array(total_results); | |
157 FX_FLOAT* pResults = result_array; | |
158 FXSYS_memset(pResults, 0, total_results * sizeof(FX_FLOAT)); | |
159 uint32_t rgb_array[SHADING_STEPS]; | |
160 for (int i = 0; i < SHADING_STEPS; i++) { | |
161 FX_FLOAT input = (t_max - t_min) * i / SHADING_STEPS + t_min; | |
162 int offset = 0; | |
163 for (const auto& func : funcs) { | |
164 if (func) { | |
165 int nresults; | |
166 if (func->Call(&input, 1, pResults + offset, nresults)) | |
167 offset += nresults; | |
168 } | |
169 } | |
170 FX_FLOAT R = 0.0f, G = 0.0f, B = 0.0f; | |
171 pCS->GetRGB(pResults, R, G, B); | |
172 rgb_array[i] = | |
173 FXARGB_TODIB(FXARGB_MAKE(alpha, FXSYS_round(R * 255), | |
174 FXSYS_round(G * 255), FXSYS_round(B * 255))); | |
175 } | |
176 FX_FLOAT a = ((start_x - end_x) * (start_x - end_x)) + | |
177 ((start_y - end_y) * (start_y - end_y)) - | |
178 ((start_r - end_r) * (start_r - end_r)); | |
179 int width = pBitmap->GetWidth(); | |
180 int height = pBitmap->GetHeight(); | |
181 int pitch = pBitmap->GetPitch(); | |
182 FX_BOOL bDecreasing = FALSE; | |
183 if (start_r > end_r) { | |
184 int length = (int)FXSYS_sqrt((((start_x - end_x) * (start_x - end_x)) + | |
185 ((start_y - end_y) * (start_y - end_y)))); | |
186 if (length < start_r - end_r) { | |
187 bDecreasing = TRUE; | |
188 } | |
189 } | |
190 for (int row = 0; row < height; row++) { | |
191 uint32_t* dib_buf = (uint32_t*)(pBitmap->GetBuffer() + row * pitch); | |
192 for (int column = 0; column < width; column++) { | |
193 FX_FLOAT x = (FX_FLOAT)column, y = (FX_FLOAT)row; | |
194 matrix.Transform(x, y); | |
195 FX_FLOAT b = -2 * (((x - start_x) * (end_x - start_x)) + | |
196 ((y - start_y) * (end_y - start_y)) + | |
197 (start_r * (end_r - start_r))); | |
198 FX_FLOAT c = ((x - start_x) * (x - start_x)) + | |
199 ((y - start_y) * (y - start_y)) - (start_r * start_r); | |
200 FX_FLOAT s; | |
201 if (a == 0) { | |
202 s = -c / b; | |
203 } else { | |
204 FX_FLOAT b2_4ac = (b * b) - 4 * (a * c); | |
205 if (b2_4ac < 0) { | |
206 continue; | |
207 } | |
208 FX_FLOAT root = FXSYS_sqrt(b2_4ac); | |
209 FX_FLOAT s1, s2; | |
210 if (a > 0) { | |
211 s1 = (-b - root) / (2 * a); | |
212 s2 = (-b + root) / (2 * a); | |
213 } else { | |
214 s2 = (-b - root) / (2 * a); | |
215 s1 = (-b + root) / (2 * a); | |
216 } | |
217 if (bDecreasing) { | |
218 if (s1 >= 0 || bStartExtend) { | |
219 s = s1; | |
220 } else { | |
221 s = s2; | |
222 } | |
223 } else { | |
224 if (s2 <= 1.0f || bEndExtend) { | |
225 s = s2; | |
226 } else { | |
227 s = s1; | |
228 } | |
229 } | |
230 if ((start_r + s * (end_r - start_r)) < 0) { | |
231 continue; | |
232 } | |
233 } | |
234 int index = (int32_t)(s * (SHADING_STEPS - 1)); | |
235 if (index < 0) { | |
236 if (!bStartExtend) { | |
237 continue; | |
238 } | |
239 index = 0; | |
240 } | |
241 if (index >= SHADING_STEPS) { | |
242 if (!bEndExtend) { | |
243 continue; | |
244 } | |
245 index = SHADING_STEPS - 1; | |
246 } | |
247 dib_buf[column] = rgb_array[index]; | |
248 } | |
249 } | |
250 } | |
251 | |
252 void DrawFuncShading(CFX_DIBitmap* pBitmap, | |
253 CFX_Matrix* pObject2Bitmap, | |
254 CPDF_Dictionary* pDict, | |
255 const std::vector<std::unique_ptr<CPDF_Function>>& funcs, | |
256 CPDF_ColorSpace* pCS, | |
257 int alpha) { | |
258 ASSERT(pBitmap->GetFormat() == FXDIB_Argb); | |
259 CPDF_Array* pDomain = pDict->GetArrayFor("Domain"); | |
260 FX_FLOAT xmin = 0, ymin = 0, xmax = 1.0f, ymax = 1.0f; | |
261 if (pDomain) { | |
262 xmin = pDomain->GetNumberAt(0); | |
263 xmax = pDomain->GetNumberAt(1); | |
264 ymin = pDomain->GetNumberAt(2); | |
265 ymax = pDomain->GetNumberAt(3); | |
266 } | |
267 CFX_Matrix mtDomain2Target = pDict->GetMatrixFor("Matrix"); | |
268 CFX_Matrix matrix, reverse_matrix; | |
269 matrix.SetReverse(*pObject2Bitmap); | |
270 reverse_matrix.SetReverse(mtDomain2Target); | |
271 matrix.Concat(reverse_matrix); | |
272 int width = pBitmap->GetWidth(); | |
273 int height = pBitmap->GetHeight(); | |
274 int pitch = pBitmap->GetPitch(); | |
275 uint32_t total_results = | |
276 std::max(CountOutputs(funcs), pCS->CountComponents()); | |
277 CFX_FixedBufGrow<FX_FLOAT, 16> result_array(total_results); | |
278 FX_FLOAT* pResults = result_array; | |
279 FXSYS_memset(pResults, 0, total_results * sizeof(FX_FLOAT)); | |
280 for (int row = 0; row < height; row++) { | |
281 uint32_t* dib_buf = (uint32_t*)(pBitmap->GetBuffer() + row * pitch); | |
282 for (int column = 0; column < width; column++) { | |
283 FX_FLOAT x = (FX_FLOAT)column, y = (FX_FLOAT)row; | |
284 matrix.Transform(x, y); | |
285 if (x < xmin || x > xmax || y < ymin || y > ymax) { | |
286 continue; | |
287 } | |
288 FX_FLOAT input[2]; | |
289 int offset = 0; | |
290 input[0] = x; | |
291 input[1] = y; | |
292 for (const auto& func : funcs) { | |
293 if (func) { | |
294 int nresults; | |
295 if (func->Call(input, 2, pResults + offset, nresults)) | |
296 offset += nresults; | |
297 } | |
298 } | |
299 FX_FLOAT R = 0.0f, G = 0.0f, B = 0.0f; | |
300 pCS->GetRGB(pResults, R, G, B); | |
301 dib_buf[column] = FXARGB_TODIB(FXARGB_MAKE( | |
302 alpha, (int32_t)(R * 255), (int32_t)(G * 255), (int32_t)(B * 255))); | |
303 } | |
304 } | |
305 } | |
306 | |
307 bool GetScanlineIntersect(int y, | |
308 FX_FLOAT x1, | |
309 FX_FLOAT y1, | |
310 FX_FLOAT x2, | |
311 FX_FLOAT y2, | |
312 FX_FLOAT* x) { | |
313 if (y1 == y2) | |
314 return FALSE; | |
315 | |
316 if (y1 < y2) { | |
317 if (y < y1 || y > y2) | |
318 return FALSE; | |
319 } else { | |
320 if (y < y2 || y > y1) | |
321 return FALSE; | |
322 } | |
323 *x = x1 + ((x2 - x1) * (y - y1) / (y2 - y1)); | |
324 return TRUE; | |
325 } | |
326 | |
327 void DrawGouraud(CFX_DIBitmap* pBitmap, | |
328 int alpha, | |
329 CPDF_MeshVertex triangle[3]) { | |
330 FX_FLOAT min_y = triangle[0].y, max_y = triangle[0].y; | |
331 for (int i = 1; i < 3; i++) { | |
332 if (min_y > triangle[i].y) { | |
333 min_y = triangle[i].y; | |
334 } | |
335 if (max_y < triangle[i].y) { | |
336 max_y = triangle[i].y; | |
337 } | |
338 } | |
339 if (min_y == max_y) { | |
340 return; | |
341 } | |
342 int min_yi = (int)FXSYS_floor(min_y), max_yi = (int)FXSYS_ceil(max_y); | |
343 if (min_yi < 0) { | |
344 min_yi = 0; | |
345 } | |
346 if (max_yi >= pBitmap->GetHeight()) { | |
347 max_yi = pBitmap->GetHeight() - 1; | |
348 } | |
349 for (int y = min_yi; y <= max_yi; y++) { | |
350 int nIntersects = 0; | |
351 FX_FLOAT inter_x[3], r[3], g[3], b[3]; | |
352 for (int i = 0; i < 3; i++) { | |
353 CPDF_MeshVertex& vertex1 = triangle[i]; | |
354 CPDF_MeshVertex& vertex2 = triangle[(i + 1) % 3]; | |
355 bool bIntersect = GetScanlineIntersect(y, vertex1.x, vertex1.y, vertex2.x, | |
356 vertex2.y, &inter_x[nIntersects]); | |
357 if (!bIntersect) | |
358 continue; | |
359 | |
360 FX_FLOAT y_dist = (y - vertex1.y) / (vertex2.y - vertex1.y); | |
361 r[nIntersects] = vertex1.r + ((vertex2.r - vertex1.r) * y_dist); | |
362 g[nIntersects] = vertex1.g + ((vertex2.g - vertex1.g) * y_dist); | |
363 b[nIntersects] = vertex1.b + ((vertex2.b - vertex1.b) * y_dist); | |
364 nIntersects++; | |
365 } | |
366 if (nIntersects != 2) { | |
367 continue; | |
368 } | |
369 int min_x, max_x, start_index, end_index; | |
370 if (inter_x[0] < inter_x[1]) { | |
371 min_x = (int)FXSYS_floor(inter_x[0]); | |
372 max_x = (int)FXSYS_ceil(inter_x[1]); | |
373 start_index = 0; | |
374 end_index = 1; | |
375 } else { | |
376 min_x = (int)FXSYS_floor(inter_x[1]); | |
377 max_x = (int)FXSYS_ceil(inter_x[0]); | |
378 start_index = 1; | |
379 end_index = 0; | |
380 } | |
381 int start_x = min_x, end_x = max_x; | |
382 if (start_x < 0) { | |
383 start_x = 0; | |
384 } | |
385 if (end_x > pBitmap->GetWidth()) { | |
386 end_x = pBitmap->GetWidth(); | |
387 } | |
388 uint8_t* dib_buf = | |
389 pBitmap->GetBuffer() + y * pBitmap->GetPitch() + start_x * 4; | |
390 FX_FLOAT r_unit = (r[end_index] - r[start_index]) / (max_x - min_x); | |
391 FX_FLOAT g_unit = (g[end_index] - g[start_index]) / (max_x - min_x); | |
392 FX_FLOAT b_unit = (b[end_index] - b[start_index]) / (max_x - min_x); | |
393 FX_FLOAT R = r[start_index] + (start_x - min_x) * r_unit; | |
394 FX_FLOAT G = g[start_index] + (start_x - min_x) * g_unit; | |
395 FX_FLOAT B = b[start_index] + (start_x - min_x) * b_unit; | |
396 for (int x = start_x; x < end_x; x++) { | |
397 R += r_unit; | |
398 G += g_unit; | |
399 B += b_unit; | |
400 FXARGB_SETDIB(dib_buf, | |
401 FXARGB_MAKE(alpha, (int32_t)(R * 255), (int32_t)(G * 255), | |
402 (int32_t)(B * 255))); | |
403 dib_buf += 4; | |
404 } | |
405 } | |
406 } | |
407 | |
408 void DrawFreeGouraudShading( | |
409 CFX_DIBitmap* pBitmap, | |
410 CFX_Matrix* pObject2Bitmap, | |
411 CPDF_Stream* pShadingStream, | |
412 const std::vector<std::unique_ptr<CPDF_Function>>& funcs, | |
413 CPDF_ColorSpace* pCS, | |
414 int alpha) { | |
415 ASSERT(pBitmap->GetFormat() == FXDIB_Argb); | |
416 | |
417 CPDF_MeshStream stream(kFreeFormGouraudTriangleMeshShading, funcs, | |
418 pShadingStream, pCS); | |
419 if (!stream.Load()) | |
420 return; | |
421 | |
422 CPDF_MeshVertex triangle[3]; | |
423 FXSYS_memset(triangle, 0, sizeof(triangle)); | |
424 | |
425 while (!stream.BitStream()->IsEOF()) { | |
426 CPDF_MeshVertex vertex; | |
427 uint32_t flag = stream.GetVertex(vertex, pObject2Bitmap); | |
428 if (flag == 0) { | |
429 triangle[0] = vertex; | |
430 for (int j = 1; j < 3; j++) { | |
431 stream.GetVertex(triangle[j], pObject2Bitmap); | |
432 } | |
433 } else { | |
434 if (flag == 1) { | |
435 triangle[0] = triangle[1]; | |
436 } | |
437 triangle[1] = triangle[2]; | |
438 triangle[2] = vertex; | |
439 } | |
440 DrawGouraud(pBitmap, alpha, triangle); | |
441 } | |
442 } | |
443 | |
444 void DrawLatticeGouraudShading( | |
445 CFX_DIBitmap* pBitmap, | |
446 CFX_Matrix* pObject2Bitmap, | |
447 CPDF_Stream* pShadingStream, | |
448 const std::vector<std::unique_ptr<CPDF_Function>>& funcs, | |
449 CPDF_ColorSpace* pCS, | |
450 int alpha) { | |
451 ASSERT(pBitmap->GetFormat() == FXDIB_Argb); | |
452 | |
453 int row_verts = pShadingStream->GetDict()->GetIntegerFor("VerticesPerRow"); | |
454 if (row_verts < 2) | |
455 return; | |
456 | |
457 CPDF_MeshStream stream(kLatticeFormGouraudTriangleMeshShading, funcs, | |
458 pShadingStream, pCS); | |
459 if (!stream.Load()) | |
460 return; | |
461 | |
462 std::unique_ptr<CPDF_MeshVertex, FxFreeDeleter> vertex( | |
463 FX_Alloc2D(CPDF_MeshVertex, row_verts, 2)); | |
464 if (!stream.GetVertexRow(vertex.get(), row_verts, pObject2Bitmap)) | |
465 return; | |
466 | |
467 int last_index = 0; | |
468 while (1) { | |
469 CPDF_MeshVertex* last_row = vertex.get() + last_index * row_verts; | |
470 CPDF_MeshVertex* this_row = vertex.get() + (1 - last_index) * row_verts; | |
471 if (!stream.GetVertexRow(this_row, row_verts, pObject2Bitmap)) | |
472 return; | |
473 | |
474 CPDF_MeshVertex triangle[3]; | |
475 for (int i = 1; i < row_verts; i++) { | |
476 triangle[0] = last_row[i]; | |
477 triangle[1] = this_row[i - 1]; | |
478 triangle[2] = last_row[i - 1]; | |
479 DrawGouraud(pBitmap, alpha, triangle); | |
480 triangle[2] = this_row[i]; | |
481 DrawGouraud(pBitmap, alpha, triangle); | |
482 } | |
483 last_index = 1 - last_index; | |
484 } | |
485 } | |
486 | |
487 struct Coon_BezierCoeff { | |
488 float a, b, c, d; | |
489 void FromPoints(float p0, float p1, float p2, float p3) { | |
490 a = -p0 + 3 * p1 - 3 * p2 + p3; | |
491 b = 3 * p0 - 6 * p1 + 3 * p2; | |
492 c = -3 * p0 + 3 * p1; | |
493 d = p0; | |
494 } | |
495 Coon_BezierCoeff first_half() { | |
496 Coon_BezierCoeff result; | |
497 result.a = a / 8; | |
498 result.b = b / 4; | |
499 result.c = c / 2; | |
500 result.d = d; | |
501 return result; | |
502 } | |
503 Coon_BezierCoeff second_half() { | |
504 Coon_BezierCoeff result; | |
505 result.a = a / 8; | |
506 result.b = 3 * a / 8 + b / 4; | |
507 result.c = 3 * a / 8 + b / 2 + c / 2; | |
508 result.d = a / 8 + b / 4 + c / 2 + d; | |
509 return result; | |
510 } | |
511 void GetPoints(float p[4]) { | |
512 p[0] = d; | |
513 p[1] = c / 3 + p[0]; | |
514 p[2] = b / 3 - p[0] + 2 * p[1]; | |
515 p[3] = a + p[0] - 3 * p[1] + 3 * p[2]; | |
516 } | |
517 void GetPointsReverse(float p[4]) { | |
518 p[3] = d; | |
519 p[2] = c / 3 + p[3]; | |
520 p[1] = b / 3 - p[3] + 2 * p[2]; | |
521 p[0] = a + p[3] - 3 * p[2] + 3 * p[1]; | |
522 } | |
523 void BezierInterpol(Coon_BezierCoeff& C1, | |
524 Coon_BezierCoeff& C2, | |
525 Coon_BezierCoeff& D1, | |
526 Coon_BezierCoeff& D2) { | |
527 a = (D1.a + D2.a) / 2; | |
528 b = (D1.b + D2.b) / 2; | |
529 c = (D1.c + D2.c) / 2 - (C1.a / 8 + C1.b / 4 + C1.c / 2) + | |
530 (C2.a / 8 + C2.b / 4) + (-C1.d + D2.d) / 2 - (C2.a + C2.b) / 2; | |
531 d = C1.a / 8 + C1.b / 4 + C1.c / 2 + C1.d; | |
532 } | |
533 float Distance() { | |
534 float dis = a + b + c; | |
535 return dis < 0 ? -dis : dis; | |
536 } | |
537 }; | |
538 | |
539 struct Coon_Bezier { | |
540 Coon_BezierCoeff x, y; | |
541 void FromPoints(float x0, | |
542 float y0, | |
543 float x1, | |
544 float y1, | |
545 float x2, | |
546 float y2, | |
547 float x3, | |
548 float y3) { | |
549 x.FromPoints(x0, x1, x2, x3); | |
550 y.FromPoints(y0, y1, y2, y3); | |
551 } | |
552 Coon_Bezier first_half() { | |
553 Coon_Bezier result; | |
554 result.x = x.first_half(); | |
555 result.y = y.first_half(); | |
556 return result; | |
557 } | |
558 Coon_Bezier second_half() { | |
559 Coon_Bezier result; | |
560 result.x = x.second_half(); | |
561 result.y = y.second_half(); | |
562 return result; | |
563 } | |
564 void BezierInterpol(Coon_Bezier& C1, | |
565 Coon_Bezier& C2, | |
566 Coon_Bezier& D1, | |
567 Coon_Bezier& D2) { | |
568 x.BezierInterpol(C1.x, C2.x, D1.x, D2.x); | |
569 y.BezierInterpol(C1.y, C2.y, D1.y, D2.y); | |
570 } | |
571 void GetPoints(FX_PATHPOINT* pPoints) { | |
572 float p[4]; | |
573 int i; | |
574 x.GetPoints(p); | |
575 for (i = 0; i < 4; i++) { | |
576 pPoints[i].m_PointX = p[i]; | |
577 } | |
578 y.GetPoints(p); | |
579 for (i = 0; i < 4; i++) { | |
580 pPoints[i].m_PointY = p[i]; | |
581 } | |
582 } | |
583 void GetPointsReverse(FX_PATHPOINT* pPoints) { | |
584 float p[4]; | |
585 int i; | |
586 x.GetPointsReverse(p); | |
587 for (i = 0; i < 4; i++) { | |
588 pPoints[i].m_PointX = p[i]; | |
589 } | |
590 y.GetPointsReverse(p); | |
591 for (i = 0; i < 4; i++) { | |
592 pPoints[i].m_PointY = p[i]; | |
593 } | |
594 } | |
595 float Distance() { return x.Distance() + y.Distance(); } | |
596 }; | |
597 | |
598 int BiInterpolImpl(int c0, | |
599 int c1, | |
600 int c2, | |
601 int c3, | |
602 int x, | |
603 int y, | |
604 int x_scale, | |
605 int y_scale) { | |
606 int x1 = c0 + (c3 - c0) * x / x_scale; | |
607 int x2 = c1 + (c2 - c1) * x / x_scale; | |
608 return x1 + (x2 - x1) * y / y_scale; | |
609 } | |
610 | |
611 struct Coon_Color { | |
612 Coon_Color() { FXSYS_memset(comp, 0, sizeof(int) * 3); } | |
613 int comp[3]; | |
614 | |
615 void BiInterpol(Coon_Color colors[4], | |
616 int x, | |
617 int y, | |
618 int x_scale, | |
619 int y_scale) { | |
620 for (int i = 0; i < 3; i++) { | |
621 comp[i] = BiInterpolImpl(colors[0].comp[i], colors[1].comp[i], | |
622 colors[2].comp[i], colors[3].comp[i], x, y, | |
623 x_scale, y_scale); | |
624 } | |
625 } | |
626 | |
627 int Distance(Coon_Color& o) { | |
628 return std::max({FXSYS_abs(comp[0] - o.comp[0]), | |
629 FXSYS_abs(comp[1] - o.comp[1]), | |
630 FXSYS_abs(comp[2] - o.comp[2])}); | |
631 } | |
632 }; | |
633 | |
634 struct CPDF_PatchDrawer { | |
635 Coon_Color patch_colors[4]; | |
636 int max_delta; | |
637 CFX_PathData path; | |
638 CFX_RenderDevice* pDevice; | |
639 int fill_mode; | |
640 int alpha; | |
641 void Draw(int x_scale, | |
642 int y_scale, | |
643 int left, | |
644 int bottom, | |
645 Coon_Bezier C1, | |
646 Coon_Bezier C2, | |
647 Coon_Bezier D1, | |
648 Coon_Bezier D2) { | |
649 FX_BOOL bSmall = C1.Distance() < 2 && C2.Distance() < 2 && | |
650 D1.Distance() < 2 && D2.Distance() < 2; | |
651 Coon_Color div_colors[4]; | |
652 int d_bottom = 0; | |
653 int d_left = 0; | |
654 int d_top = 0; | |
655 int d_right = 0; | |
656 div_colors[0].BiInterpol(patch_colors, left, bottom, x_scale, y_scale); | |
657 if (!bSmall) { | |
658 div_colors[1].BiInterpol(patch_colors, left, bottom + 1, x_scale, | |
659 y_scale); | |
660 div_colors[2].BiInterpol(patch_colors, left + 1, bottom + 1, x_scale, | |
661 y_scale); | |
662 div_colors[3].BiInterpol(patch_colors, left + 1, bottom, x_scale, | |
663 y_scale); | |
664 d_bottom = div_colors[3].Distance(div_colors[0]); | |
665 d_left = div_colors[1].Distance(div_colors[0]); | |
666 d_top = div_colors[1].Distance(div_colors[2]); | |
667 d_right = div_colors[2].Distance(div_colors[3]); | |
668 } | |
669 #define COONCOLOR_THRESHOLD 4 | |
670 if (bSmall || | |
671 (d_bottom < COONCOLOR_THRESHOLD && d_left < COONCOLOR_THRESHOLD && | |
672 d_top < COONCOLOR_THRESHOLD && d_right < COONCOLOR_THRESHOLD)) { | |
673 FX_PATHPOINT* pPoints = path.GetPoints(); | |
674 C1.GetPoints(pPoints); | |
675 D2.GetPoints(pPoints + 3); | |
676 C2.GetPointsReverse(pPoints + 6); | |
677 D1.GetPointsReverse(pPoints + 9); | |
678 int fillFlags = FXFILL_WINDING | FXFILL_FULLCOVER; | |
679 if (fill_mode & RENDER_NOPATHSMOOTH) { | |
680 fillFlags |= FXFILL_NOPATHSMOOTH; | |
681 } | |
682 pDevice->DrawPath( | |
683 &path, nullptr, nullptr, | |
684 FXARGB_MAKE(alpha, div_colors[0].comp[0], div_colors[0].comp[1], | |
685 div_colors[0].comp[2]), | |
686 0, fillFlags); | |
687 } else { | |
688 if (d_bottom < COONCOLOR_THRESHOLD && d_top < COONCOLOR_THRESHOLD) { | |
689 Coon_Bezier m1; | |
690 m1.BezierInterpol(D1, D2, C1, C2); | |
691 y_scale *= 2; | |
692 bottom *= 2; | |
693 Draw(x_scale, y_scale, left, bottom, C1, m1, D1.first_half(), | |
694 D2.first_half()); | |
695 Draw(x_scale, y_scale, left, bottom + 1, m1, C2, D1.second_half(), | |
696 D2.second_half()); | |
697 } else if (d_left < COONCOLOR_THRESHOLD && | |
698 d_right < COONCOLOR_THRESHOLD) { | |
699 Coon_Bezier m2; | |
700 m2.BezierInterpol(C1, C2, D1, D2); | |
701 x_scale *= 2; | |
702 left *= 2; | |
703 Draw(x_scale, y_scale, left, bottom, C1.first_half(), C2.first_half(), | |
704 D1, m2); | |
705 Draw(x_scale, y_scale, left + 1, bottom, C1.second_half(), | |
706 C2.second_half(), m2, D2); | |
707 } else { | |
708 Coon_Bezier m1, m2; | |
709 m1.BezierInterpol(D1, D2, C1, C2); | |
710 m2.BezierInterpol(C1, C2, D1, D2); | |
711 Coon_Bezier m1f = m1.first_half(); | |
712 Coon_Bezier m1s = m1.second_half(); | |
713 Coon_Bezier m2f = m2.first_half(); | |
714 Coon_Bezier m2s = m2.second_half(); | |
715 x_scale *= 2; | |
716 y_scale *= 2; | |
717 left *= 2; | |
718 bottom *= 2; | |
719 Draw(x_scale, y_scale, left, bottom, C1.first_half(), m1f, | |
720 D1.first_half(), m2f); | |
721 Draw(x_scale, y_scale, left, bottom + 1, m1f, C2.first_half(), | |
722 D1.second_half(), m2s); | |
723 Draw(x_scale, y_scale, left + 1, bottom, C1.second_half(), m1s, m2f, | |
724 D2.first_half()); | |
725 Draw(x_scale, y_scale, left + 1, bottom + 1, m1s, C2.second_half(), m2s, | |
726 D2.second_half()); | |
727 } | |
728 } | |
729 } | |
730 }; | |
731 | |
732 void DrawCoonPatchMeshes( | |
733 ShadingType type, | |
734 CFX_DIBitmap* pBitmap, | |
735 CFX_Matrix* pObject2Bitmap, | |
736 CPDF_Stream* pShadingStream, | |
737 const std::vector<std::unique_ptr<CPDF_Function>>& funcs, | |
738 CPDF_ColorSpace* pCS, | |
739 int fill_mode, | |
740 int alpha) { | |
741 ASSERT(pBitmap->GetFormat() == FXDIB_Argb); | |
742 ASSERT(type == kCoonsPatchMeshShading || | |
743 type == kTensorProductPatchMeshShading); | |
744 | |
745 CFX_FxgeDevice device; | |
746 device.Attach(pBitmap, false, nullptr, false); | |
747 CPDF_MeshStream stream(type, funcs, pShadingStream, pCS); | |
748 if (!stream.Load()) | |
749 return; | |
750 | |
751 CPDF_PatchDrawer patch; | |
752 patch.alpha = alpha; | |
753 patch.pDevice = &device; | |
754 patch.fill_mode = fill_mode; | |
755 patch.path.SetPointCount(13); | |
756 FX_PATHPOINT* pPoints = patch.path.GetPoints(); | |
757 pPoints[0].m_Flag = FXPT_MOVETO; | |
758 for (int i = 1; i < 13; i++) | |
759 pPoints[i].m_Flag = FXPT_BEZIERTO; | |
760 CFX_PointF coords[16]; | |
761 int point_count = type == kTensorProductPatchMeshShading ? 16 : 12; | |
762 while (!stream.BitStream()->IsEOF()) { | |
763 uint32_t flag = stream.GetFlag(); | |
764 int iStartPoint = 0, iStartColor = 0, i = 0; | |
765 if (flag) { | |
766 iStartPoint = 4; | |
767 iStartColor = 2; | |
768 CFX_PointF tempCoords[4]; | |
769 for (i = 0; i < 4; i++) { | |
770 tempCoords[i] = coords[(flag * 3 + i) % 12]; | |
771 } | |
772 FXSYS_memcpy(coords, tempCoords, sizeof(tempCoords)); | |
773 Coon_Color tempColors[2]; | |
774 tempColors[0] = patch.patch_colors[flag]; | |
775 tempColors[1] = patch.patch_colors[(flag + 1) % 4]; | |
776 FXSYS_memcpy(patch.patch_colors, tempColors, sizeof(Coon_Color) * 2); | |
777 } | |
778 for (i = iStartPoint; i < point_count; i++) { | |
779 stream.GetCoords(coords[i].x, coords[i].y); | |
780 pObject2Bitmap->Transform(coords[i].x, coords[i].y); | |
781 } | |
782 for (i = iStartColor; i < 4; i++) { | |
783 FX_FLOAT r = 0.0f, g = 0.0f, b = 0.0f; | |
784 stream.GetColor(r, g, b); | |
785 patch.patch_colors[i].comp[0] = (int32_t)(r * 255); | |
786 patch.patch_colors[i].comp[1] = (int32_t)(g * 255); | |
787 patch.patch_colors[i].comp[2] = (int32_t)(b * 255); | |
788 } | |
789 CFX_FloatRect bbox = CFX_FloatRect::GetBBox(coords, point_count); | |
790 if (bbox.right <= 0 || bbox.left >= (FX_FLOAT)pBitmap->GetWidth() || | |
791 bbox.top <= 0 || bbox.bottom >= (FX_FLOAT)pBitmap->GetHeight()) { | |
792 continue; | |
793 } | |
794 Coon_Bezier C1, C2, D1, D2; | |
795 C1.FromPoints(coords[0].x, coords[0].y, coords[11].x, coords[11].y, | |
796 coords[10].x, coords[10].y, coords[9].x, coords[9].y); | |
797 C2.FromPoints(coords[3].x, coords[3].y, coords[4].x, coords[4].y, | |
798 coords[5].x, coords[5].y, coords[6].x, coords[6].y); | |
799 D1.FromPoints(coords[0].x, coords[0].y, coords[1].x, coords[1].y, | |
800 coords[2].x, coords[2].y, coords[3].x, coords[3].y); | |
801 D2.FromPoints(coords[9].x, coords[9].y, coords[8].x, coords[8].y, | |
802 coords[7].x, coords[7].y, coords[6].x, coords[6].y); | |
803 patch.Draw(1, 1, 0, 0, C1, C2, D1, D2); | |
804 } | |
805 } | |
806 | |
807 std::unique_ptr<CFX_DIBitmap> DrawPatternBitmap( | |
808 CPDF_Document* pDoc, | |
809 CPDF_PageRenderCache* pCache, | |
810 CPDF_TilingPattern* pPattern, | |
811 const CFX_Matrix* pObject2Device, | |
812 int width, | |
813 int height, | |
814 int flags) { | |
815 std::unique_ptr<CFX_DIBitmap> pBitmap(new CFX_DIBitmap); | |
816 if (!pBitmap->Create(width, height, | |
817 pPattern->colored() ? FXDIB_Argb : FXDIB_8bppMask)) { | |
818 return std::unique_ptr<CFX_DIBitmap>(); | |
819 } | |
820 CFX_FxgeDevice bitmap_device; | |
821 bitmap_device.Attach(pBitmap.get(), false, nullptr, false); | |
822 pBitmap->Clear(0); | |
823 CFX_FloatRect cell_bbox = pPattern->bbox(); | |
824 pPattern->pattern_to_form()->TransformRect(cell_bbox); | |
825 pObject2Device->TransformRect(cell_bbox); | |
826 CFX_FloatRect bitmap_rect(0.0f, 0.0f, (FX_FLOAT)width, (FX_FLOAT)height); | |
827 CFX_Matrix mtAdjust; | |
828 mtAdjust.MatchRect(bitmap_rect, cell_bbox); | |
829 CFX_Matrix mtPattern2Bitmap = *pObject2Device; | |
830 mtPattern2Bitmap.Concat(mtAdjust); | |
831 CPDF_RenderOptions options; | |
832 if (!pPattern->colored()) | |
833 options.m_ColorMode = RENDER_COLOR_ALPHA; | |
834 | |
835 flags |= RENDER_FORCE_HALFTONE; | |
836 options.m_Flags = flags; | |
837 CPDF_RenderContext context(pDoc, pCache); | |
838 context.AppendLayer(pPattern->form(), &mtPattern2Bitmap); | |
839 context.Render(&bitmap_device, &options, nullptr); | |
840 return pBitmap; | |
841 } | |
842 | |
843 } // namespace | |
844 | |
845 void CPDF_RenderStatus::DrawShading(CPDF_ShadingPattern* pPattern, | |
846 CFX_Matrix* pMatrix, | |
847 FX_RECT& clip_rect, | |
848 int alpha, | |
849 FX_BOOL bAlphaMode) { | |
850 const auto& funcs = pPattern->GetFuncs(); | |
851 CPDF_Dictionary* pDict = pPattern->GetShadingObject()->GetDict(); | |
852 CPDF_ColorSpace* pColorSpace = pPattern->GetCS(); | |
853 if (!pColorSpace) | |
854 return; | |
855 | |
856 FX_ARGB background = 0; | |
857 if (!pPattern->IsShadingObject() && pDict->KeyExist("Background")) { | |
858 CPDF_Array* pBackColor = pDict->GetArrayFor("Background"); | |
859 if (pBackColor && | |
860 pBackColor->GetCount() >= pColorSpace->CountComponents()) { | |
861 CFX_FixedBufGrow<FX_FLOAT, 16> comps(pColorSpace->CountComponents()); | |
862 for (uint32_t i = 0; i < pColorSpace->CountComponents(); i++) | |
863 comps[i] = pBackColor->GetNumberAt(i); | |
864 FX_FLOAT R = 0.0f, G = 0.0f, B = 0.0f; | |
865 pColorSpace->GetRGB(comps, R, G, B); | |
866 background = ArgbEncode(255, (int32_t)(R * 255), (int32_t)(G * 255), | |
867 (int32_t)(B * 255)); | |
868 } | |
869 } | |
870 if (pDict->KeyExist("BBox")) { | |
871 CFX_FloatRect rect = pDict->GetRectFor("BBox"); | |
872 rect.Transform(pMatrix); | |
873 clip_rect.Intersect(rect.GetOuterRect()); | |
874 } | |
875 if (m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SHADING && | |
876 m_pDevice->GetDeviceDriver()->DrawShading(pPattern, pMatrix, clip_rect, | |
877 alpha, bAlphaMode)) { | |
878 return; | |
879 } | |
880 CPDF_DeviceBuffer buffer; | |
881 buffer.Initialize(m_pContext, m_pDevice, &clip_rect, m_pCurObj, 150); | |
882 CFX_Matrix FinalMatrix = *pMatrix; | |
883 FinalMatrix.Concat(*buffer.GetMatrix()); | |
884 CFX_DIBitmap* pBitmap = buffer.GetBitmap(); | |
885 if (!pBitmap->GetBuffer()) | |
886 return; | |
887 | |
888 pBitmap->Clear(background); | |
889 int fill_mode = m_Options.m_Flags; | |
890 switch (pPattern->GetShadingType()) { | |
891 case kInvalidShading: | |
892 case kMaxShading: | |
893 return; | |
894 case kFunctionBasedShading: | |
895 DrawFuncShading(pBitmap, &FinalMatrix, pDict, funcs, pColorSpace, alpha); | |
896 break; | |
897 case kAxialShading: | |
898 DrawAxialShading(pBitmap, &FinalMatrix, pDict, funcs, pColorSpace, alpha); | |
899 break; | |
900 case kRadialShading: | |
901 DrawRadialShading(pBitmap, &FinalMatrix, pDict, funcs, pColorSpace, | |
902 alpha); | |
903 break; | |
904 case kFreeFormGouraudTriangleMeshShading: { | |
905 // The shading object can be a stream or a dictionary. We do not handle | |
906 // the case of dictionary at the moment. | |
907 if (CPDF_Stream* pStream = ToStream(pPattern->GetShadingObject())) { | |
908 DrawFreeGouraudShading(pBitmap, &FinalMatrix, pStream, funcs, | |
909 pColorSpace, alpha); | |
910 } | |
911 } break; | |
912 case kLatticeFormGouraudTriangleMeshShading: { | |
913 // The shading object can be a stream or a dictionary. We do not handle | |
914 // the case of dictionary at the moment. | |
915 if (CPDF_Stream* pStream = ToStream(pPattern->GetShadingObject())) { | |
916 DrawLatticeGouraudShading(pBitmap, &FinalMatrix, pStream, funcs, | |
917 pColorSpace, alpha); | |
918 } | |
919 } break; | |
920 case kCoonsPatchMeshShading: | |
921 case kTensorProductPatchMeshShading: { | |
922 // The shading object can be a stream or a dictionary. We do not handle | |
923 // the case of dictionary at the moment. | |
924 if (CPDF_Stream* pStream = ToStream(pPattern->GetShadingObject())) { | |
925 DrawCoonPatchMeshes(pPattern->GetShadingType(), pBitmap, &FinalMatrix, | |
926 pStream, funcs, pColorSpace, fill_mode, alpha); | |
927 } | |
928 } break; | |
929 } | |
930 if (bAlphaMode) | |
931 pBitmap->LoadChannel(FXDIB_Red, pBitmap, FXDIB_Alpha); | |
932 | |
933 if (m_Options.m_ColorMode == RENDER_COLOR_GRAY) | |
934 pBitmap->ConvertColorScale(m_Options.m_ForeColor, m_Options.m_BackColor); | |
935 buffer.OutputToDevice(); | |
936 } | |
937 | |
938 void CPDF_RenderStatus::DrawShadingPattern(CPDF_ShadingPattern* pattern, | |
939 const CPDF_PageObject* pPageObj, | |
940 const CFX_Matrix* pObj2Device, | |
941 FX_BOOL bStroke) { | |
942 if (!pattern->Load()) | |
943 return; | |
944 | |
945 m_pDevice->SaveState(); | |
946 if (pPageObj->IsPath()) { | |
947 if (!SelectClipPath(pPageObj->AsPath(), pObj2Device, bStroke)) { | |
948 m_pDevice->RestoreState(false); | |
949 return; | |
950 } | |
951 } else if (pPageObj->IsImage()) { | |
952 m_pDevice->SetClip_Rect(pPageObj->GetBBox(pObj2Device)); | |
953 } else { | |
954 return; | |
955 } | |
956 FX_RECT rect; | |
957 if (GetObjectClippedRect(pPageObj, pObj2Device, FALSE, rect)) { | |
958 m_pDevice->RestoreState(false); | |
959 return; | |
960 } | |
961 CFX_Matrix matrix = *pattern->pattern_to_form(); | |
962 matrix.Concat(*pObj2Device); | |
963 GetScaledMatrix(matrix); | |
964 int alpha = | |
965 FXSYS_round(255 * (bStroke ? pPageObj->m_GeneralState.GetStrokeAlpha() | |
966 : pPageObj->m_GeneralState.GetFillAlpha())); | |
967 DrawShading(pattern, &matrix, rect, alpha, | |
968 m_Options.m_ColorMode == RENDER_COLOR_ALPHA); | |
969 m_pDevice->RestoreState(false); | |
970 } | |
971 | |
972 void CPDF_RenderStatus::ProcessShading(const CPDF_ShadingObject* pShadingObj, | |
973 const CFX_Matrix* pObj2Device) { | |
974 FX_RECT rect = pShadingObj->GetBBox(pObj2Device); | |
975 FX_RECT clip_box = m_pDevice->GetClipBox(); | |
976 rect.Intersect(clip_box); | |
977 if (rect.IsEmpty()) | |
978 return; | |
979 | |
980 CFX_Matrix matrix = pShadingObj->m_Matrix; | |
981 matrix.Concat(*pObj2Device); | |
982 DrawShading(pShadingObj->m_pShading, &matrix, rect, | |
983 FXSYS_round(255 * pShadingObj->m_GeneralState.GetFillAlpha()), | |
984 m_Options.m_ColorMode == RENDER_COLOR_ALPHA); | |
985 } | |
986 | |
987 void CPDF_RenderStatus::DrawTilingPattern(CPDF_TilingPattern* pPattern, | |
988 CPDF_PageObject* pPageObj, | |
989 const CFX_Matrix* pObj2Device, | |
990 FX_BOOL bStroke) { | |
991 if (!pPattern->Load()) { | |
992 return; | |
993 } | |
994 m_pDevice->SaveState(); | |
995 if (pPageObj->IsPath()) { | |
996 if (!SelectClipPath(pPageObj->AsPath(), pObj2Device, bStroke)) { | |
997 m_pDevice->RestoreState(false); | |
998 return; | |
999 } | |
1000 } else if (pPageObj->IsImage()) { | |
1001 m_pDevice->SetClip_Rect(pPageObj->GetBBox(pObj2Device)); | |
1002 } else { | |
1003 return; | |
1004 } | |
1005 FX_RECT clip_box = m_pDevice->GetClipBox(); | |
1006 if (clip_box.IsEmpty()) { | |
1007 m_pDevice->RestoreState(false); | |
1008 return; | |
1009 } | |
1010 CFX_Matrix dCTM = m_pDevice->GetCTM(); | |
1011 FX_FLOAT sa = FXSYS_fabs(dCTM.a); | |
1012 FX_FLOAT sd = FXSYS_fabs(dCTM.d); | |
1013 clip_box.right = clip_box.left + (int32_t)FXSYS_ceil(clip_box.Width() * sa); | |
1014 clip_box.bottom = clip_box.top + (int32_t)FXSYS_ceil(clip_box.Height() * sd); | |
1015 CFX_Matrix mtPattern2Device = *pPattern->pattern_to_form(); | |
1016 mtPattern2Device.Concat(*pObj2Device); | |
1017 GetScaledMatrix(mtPattern2Device); | |
1018 FX_BOOL bAligned = FALSE; | |
1019 if (pPattern->bbox().left == 0 && pPattern->bbox().bottom == 0 && | |
1020 pPattern->bbox().right == pPattern->x_step() && | |
1021 pPattern->bbox().top == pPattern->y_step() && | |
1022 (mtPattern2Device.IsScaled() || mtPattern2Device.Is90Rotated())) { | |
1023 bAligned = TRUE; | |
1024 } | |
1025 CFX_FloatRect cell_bbox = pPattern->bbox(); | |
1026 mtPattern2Device.TransformRect(cell_bbox); | |
1027 int width = (int)FXSYS_ceil(cell_bbox.Width()); | |
1028 int height = (int)FXSYS_ceil(cell_bbox.Height()); | |
1029 if (width == 0) { | |
1030 width = 1; | |
1031 } | |
1032 if (height == 0) { | |
1033 height = 1; | |
1034 } | |
1035 int min_col, max_col, min_row, max_row; | |
1036 CFX_Matrix mtDevice2Pattern; | |
1037 mtDevice2Pattern.SetReverse(mtPattern2Device); | |
1038 CFX_FloatRect clip_box_p(clip_box); | |
1039 clip_box_p.Transform(&mtDevice2Pattern); | |
1040 | |
1041 min_col = (int)FXSYS_ceil((clip_box_p.left - pPattern->bbox().right) / | |
1042 pPattern->x_step()); | |
1043 max_col = (int)FXSYS_floor((clip_box_p.right - pPattern->bbox().left) / | |
1044 pPattern->x_step()); | |
1045 min_row = (int)FXSYS_ceil((clip_box_p.bottom - pPattern->bbox().top) / | |
1046 pPattern->y_step()); | |
1047 max_row = (int)FXSYS_floor((clip_box_p.top - pPattern->bbox().bottom) / | |
1048 pPattern->y_step()); | |
1049 | |
1050 if (width > clip_box.Width() || height > clip_box.Height() || | |
1051 width * height > clip_box.Width() * clip_box.Height()) { | |
1052 CPDF_GraphicStates* pStates = nullptr; | |
1053 if (!pPattern->colored()) | |
1054 pStates = CloneObjStates(pPageObj, bStroke); | |
1055 | |
1056 CPDF_Dictionary* pFormResource = nullptr; | |
1057 if (pPattern->form()->m_pFormDict) | |
1058 pFormResource = pPattern->form()->m_pFormDict->GetDictFor("Resources"); | |
1059 | |
1060 for (int col = min_col; col <= max_col; col++) | |
1061 for (int row = min_row; row <= max_row; row++) { | |
1062 FX_FLOAT orig_x, orig_y; | |
1063 orig_x = col * pPattern->x_step(); | |
1064 orig_y = row * pPattern->y_step(); | |
1065 mtPattern2Device.Transform(orig_x, orig_y); | |
1066 CFX_Matrix matrix = *pObj2Device; | |
1067 matrix.Translate(orig_x - mtPattern2Device.e, | |
1068 orig_y - mtPattern2Device.f); | |
1069 m_pDevice->SaveState(); | |
1070 CPDF_RenderStatus status; | |
1071 status.Initialize(m_pContext, m_pDevice, nullptr, nullptr, this, | |
1072 pStates, &m_Options, pPattern->form()->m_Transparency, | |
1073 m_bDropObjects, pFormResource); | |
1074 status.RenderObjectList(pPattern->form(), &matrix); | |
1075 m_pDevice->RestoreState(false); | |
1076 } | |
1077 m_pDevice->RestoreState(false); | |
1078 delete pStates; | |
1079 return; | |
1080 } | |
1081 if (bAligned) { | |
1082 int orig_x = FXSYS_round(mtPattern2Device.e); | |
1083 int orig_y = FXSYS_round(mtPattern2Device.f); | |
1084 min_col = (clip_box.left - orig_x) / width; | |
1085 if (clip_box.left < orig_x) { | |
1086 min_col--; | |
1087 } | |
1088 max_col = (clip_box.right - orig_x) / width; | |
1089 if (clip_box.right <= orig_x) { | |
1090 max_col--; | |
1091 } | |
1092 min_row = (clip_box.top - orig_y) / height; | |
1093 if (clip_box.top < orig_y) { | |
1094 min_row--; | |
1095 } | |
1096 max_row = (clip_box.bottom - orig_y) / height; | |
1097 if (clip_box.bottom <= orig_y) { | |
1098 max_row--; | |
1099 } | |
1100 } | |
1101 FX_FLOAT left_offset = cell_bbox.left - mtPattern2Device.e; | |
1102 FX_FLOAT top_offset = cell_bbox.bottom - mtPattern2Device.f; | |
1103 std::unique_ptr<CFX_DIBitmap> pPatternBitmap; | |
1104 if (width * height < 16) { | |
1105 std::unique_ptr<CFX_DIBitmap> pEnlargedBitmap = | |
1106 DrawPatternBitmap(m_pContext->GetDocument(), m_pContext->GetPageCache(), | |
1107 pPattern, pObj2Device, 8, 8, m_Options.m_Flags); | |
1108 pPatternBitmap.reset(pEnlargedBitmap->StretchTo(width, height)); | |
1109 } else { | |
1110 pPatternBitmap = DrawPatternBitmap( | |
1111 m_pContext->GetDocument(), m_pContext->GetPageCache(), pPattern, | |
1112 pObj2Device, width, height, m_Options.m_Flags); | |
1113 } | |
1114 if (!pPatternBitmap) { | |
1115 m_pDevice->RestoreState(false); | |
1116 return; | |
1117 } | |
1118 if (m_Options.m_ColorMode == RENDER_COLOR_GRAY) { | |
1119 pPatternBitmap->ConvertColorScale(m_Options.m_ForeColor, | |
1120 m_Options.m_BackColor); | |
1121 } | |
1122 FX_ARGB fill_argb = GetFillArgb(pPageObj); | |
1123 int clip_width = clip_box.right - clip_box.left; | |
1124 int clip_height = clip_box.bottom - clip_box.top; | |
1125 CFX_DIBitmap screen; | |
1126 if (!screen.Create(clip_width, clip_height, FXDIB_Argb)) { | |
1127 return; | |
1128 } | |
1129 screen.Clear(0); | |
1130 uint32_t* src_buf = (uint32_t*)pPatternBitmap->GetBuffer(); | |
1131 for (int col = min_col; col <= max_col; col++) { | |
1132 for (int row = min_row; row <= max_row; row++) { | |
1133 int start_x, start_y; | |
1134 if (bAligned) { | |
1135 start_x = FXSYS_round(mtPattern2Device.e) + col * width - clip_box.left; | |
1136 start_y = FXSYS_round(mtPattern2Device.f) + row * height - clip_box.top; | |
1137 } else { | |
1138 FX_FLOAT orig_x = col * pPattern->x_step(); | |
1139 FX_FLOAT orig_y = row * pPattern->y_step(); | |
1140 mtPattern2Device.Transform(orig_x, orig_y); | |
1141 | |
1142 pdfium::base::CheckedNumeric<int> safeStartX = | |
1143 FXSYS_round(orig_x + left_offset); | |
1144 pdfium::base::CheckedNumeric<int> safeStartY = | |
1145 FXSYS_round(orig_y + top_offset); | |
1146 | |
1147 safeStartX -= clip_box.left; | |
1148 safeStartY -= clip_box.top; | |
1149 if (!safeStartX.IsValid() || !safeStartY.IsValid()) | |
1150 return; | |
1151 | |
1152 start_x = safeStartX.ValueOrDie(); | |
1153 start_y = safeStartY.ValueOrDie(); | |
1154 } | |
1155 if (width == 1 && height == 1) { | |
1156 if (start_x < 0 || start_x >= clip_box.Width() || start_y < 0 || | |
1157 start_y >= clip_box.Height()) { | |
1158 continue; | |
1159 } | |
1160 uint32_t* dest_buf = | |
1161 (uint32_t*)(screen.GetBuffer() + screen.GetPitch() * start_y + | |
1162 start_x * 4); | |
1163 if (pPattern->colored()) | |
1164 *dest_buf = *src_buf; | |
1165 else | |
1166 *dest_buf = (*(uint8_t*)src_buf << 24) | (fill_argb & 0xffffff); | |
1167 } else { | |
1168 if (pPattern->colored()) { | |
1169 screen.CompositeBitmap(start_x, start_y, width, height, | |
1170 pPatternBitmap.get(), 0, 0); | |
1171 } else { | |
1172 screen.CompositeMask(start_x, start_y, width, height, | |
1173 pPatternBitmap.get(), fill_argb, 0, 0); | |
1174 } | |
1175 } | |
1176 } | |
1177 } | |
1178 CompositeDIBitmap(&screen, clip_box.left, clip_box.top, 0, 255, | |
1179 FXDIB_BLEND_NORMAL, FALSE); | |
1180 m_pDevice->RestoreState(false); | |
1181 } | |
1182 | |
1183 void CPDF_RenderStatus::DrawPathWithPattern(CPDF_PathObject* pPathObj, | |
1184 const CFX_Matrix* pObj2Device, | |
1185 const CPDF_Color* pColor, | |
1186 FX_BOOL bStroke) { | |
1187 CPDF_Pattern* pattern = pColor->GetPattern(); | |
1188 if (!pattern) | |
1189 return; | |
1190 | |
1191 if (CPDF_TilingPattern* pTilingPattern = pattern->AsTilingPattern()) | |
1192 DrawTilingPattern(pTilingPattern, pPathObj, pObj2Device, bStroke); | |
1193 else if (CPDF_ShadingPattern* pShadingPattern = pattern->AsShadingPattern()) | |
1194 DrawShadingPattern(pShadingPattern, pPathObj, pObj2Device, bStroke); | |
1195 } | |
1196 | |
1197 void CPDF_RenderStatus::ProcessPathPattern(CPDF_PathObject* pPathObj, | |
1198 const CFX_Matrix* pObj2Device, | |
1199 int& filltype, | |
1200 FX_BOOL& bStroke) { | |
1201 if (filltype) { | |
1202 const CPDF_Color& FillColor = *pPathObj->m_ColorState.GetFillColor(); | |
1203 if (FillColor.IsPattern()) { | |
1204 DrawPathWithPattern(pPathObj, pObj2Device, &FillColor, FALSE); | |
1205 filltype = 0; | |
1206 } | |
1207 } | |
1208 if (bStroke) { | |
1209 const CPDF_Color& StrokeColor = *pPathObj->m_ColorState.GetStrokeColor(); | |
1210 if (StrokeColor.IsPattern()) { | |
1211 DrawPathWithPattern(pPathObj, pObj2Device, &StrokeColor, TRUE); | |
1212 bStroke = FALSE; | |
1213 } | |
1214 } | |
1215 } | |
OLD | NEW |