1 // Copyright 2016 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/fpdfdoc/cpdf_annot.h"
8
9 #include <algorithm>
10 #include <utility>
11
12 #include "constants/annotation_common.h"
13 #include "constants/annotation_flags.h"
14 #include "core/fpdfapi/page/cpdf_form.h"
15 #include "core/fpdfapi/page/cpdf_page.h"
16 #include "core/fpdfapi/parser/cpdf_array.h"
17 #include "core/fpdfapi/parser/cpdf_boolean.h"
18 #include "core/fpdfapi/parser/cpdf_dictionary.h"
19 #include "core/fpdfapi/parser/cpdf_document.h"
20 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
21 #include "core/fpdfapi/render/cpdf_pagerendercache.h"
22 #include "core/fpdfapi/render/cpdf_rendercontext.h"
23 #include "core/fpdfapi/render/cpdf_renderoptions.h"
24 #include "core/fpdfdoc/cpvt_generateap.h"
25 #include "core/fxge/cfx_graphstatedata.h"
26 #include "core/fxge/cfx_pathdata.h"
27 #include "core/fxge/cfx_renderdevice.h"
28 #include "third_party/base/ptr_util.h"
29
30 namespace {
31
32 const char kPDFiumKey_HasGeneratedAP[] = "PDFIUM_HasGeneratedAP";
33
IsTextMarkupAnnotation(CPDF_Annot::Subtype type)34 bool IsTextMarkupAnnotation(CPDF_Annot::Subtype type) {
35 return type == CPDF_Annot::Subtype::HIGHLIGHT ||
36 type == CPDF_Annot::Subtype::SQUIGGLY ||
37 type == CPDF_Annot::Subtype::STRIKEOUT ||
38 type == CPDF_Annot::Subtype::UNDERLINE;
39 }
40
AnnotGetMatrix(const CPDF_Page * pPage,CPDF_Annot * pAnnot,CPDF_Annot::AppearanceMode mode,const CFX_Matrix * pUser2Device,CFX_Matrix * matrix)41 CPDF_Form* AnnotGetMatrix(const CPDF_Page* pPage,
42 CPDF_Annot* pAnnot,
43 CPDF_Annot::AppearanceMode mode,
44 const CFX_Matrix* pUser2Device,
45 CFX_Matrix* matrix) {
46 CPDF_Form* pForm = pAnnot->GetAPForm(pPage, mode);
47 if (!pForm)
48 return nullptr;
49
50 CFX_Matrix form_matrix = pForm->GetDict()->GetMatrixFor("Matrix");
51 CFX_FloatRect form_bbox =
52 form_matrix.TransformRect(pForm->GetDict()->GetRectFor("BBox"));
53 matrix->MatchRect(pAnnot->GetRect(), form_bbox);
54 matrix->Concat(*pUser2Device);
55 return pForm;
56 }
57
GetAnnotAPInternal(CPDF_Dictionary * pAnnotDict,CPDF_Annot::AppearanceMode eMode,bool bFallbackToNormal)58 CPDF_Stream* GetAnnotAPInternal(CPDF_Dictionary* pAnnotDict,
59 CPDF_Annot::AppearanceMode eMode,
60 bool bFallbackToNormal) {
61 CPDF_Dictionary* pAP = pAnnotDict->GetDictFor(pdfium::annotation::kAP);
62 if (!pAP)
63 return nullptr;
64
65 const char* ap_entry = "N";
66 if (eMode == CPDF_Annot::Down)
67 ap_entry = "D";
68 else if (eMode == CPDF_Annot::Rollover)
69 ap_entry = "R";
70 if (bFallbackToNormal && !pAP->KeyExist(ap_entry))
71 ap_entry = "N";
72
73 CPDF_Object* psub = pAP->GetDirectObjectFor(ap_entry);
74 if (!psub)
75 return nullptr;
76 if (CPDF_Stream* pStream = psub->AsStream())
77 return pStream;
78
79 CPDF_Dictionary* pDict = psub->AsDictionary();
80 if (!pDict)
81 return nullptr;
82
83 ByteString as = pAnnotDict->GetStringFor(pdfium::annotation::kAS);
84 if (as.IsEmpty()) {
85 ByteString value = pAnnotDict->GetStringFor("V");
86 if (value.IsEmpty()) {
87 const CPDF_Dictionary* pParentDict = pAnnotDict->GetDictFor("Parent");
88 value = pParentDict ? pParentDict->GetStringFor("V") : ByteString();
89 }
90 as = (!value.IsEmpty() && pDict->KeyExist(value)) ? value : "Off";
91 }
92 return pDict->GetStreamFor(as);
93 }
94
95 } // namespace
96
CPDF_Annot(RetainPtr<CPDF_Dictionary> pDict,CPDF_Document * pDocument)97 CPDF_Annot::CPDF_Annot(RetainPtr<CPDF_Dictionary> pDict,
98 CPDF_Document* pDocument)
99 : m_pAnnotDict(std::move(pDict)), m_pDocument(pDocument) {
100 Init();
101 }
102
CPDF_Annot(CPDF_Dictionary * pDict,CPDF_Document * pDocument)103 CPDF_Annot::CPDF_Annot(CPDF_Dictionary* pDict, CPDF_Document* pDocument)
104 : m_pAnnotDict(pDict), m_pDocument(pDocument) {
105 Init();
106 }
107
~CPDF_Annot()108 CPDF_Annot::~CPDF_Annot() {
109 ClearCachedAP();
110 }
111
Init()112 void CPDF_Annot::Init() {
113 m_nSubtype = StringToAnnotSubtype(
114 m_pAnnotDict->GetStringFor(pdfium::annotation::kSubtype));
115 m_bIsTextMarkupAnnotation = IsTextMarkupAnnotation(m_nSubtype);
116 m_bHasGeneratedAP =
117 m_pAnnotDict->GetBooleanFor(kPDFiumKey_HasGeneratedAP, false);
118 GenerateAPIfNeeded();
119 }
120
GenerateAPIfNeeded()121 void CPDF_Annot::GenerateAPIfNeeded() {
122 if (!ShouldGenerateAP())
123 return;
124 if (!CPVT_GenerateAP::GenerateAnnotAP(m_pDocument.Get(), m_pAnnotDict.Get(),
125 m_nSubtype)) {
126 return;
127 }
128
129 m_pAnnotDict->SetNewFor<CPDF_Boolean>(kPDFiumKey_HasGeneratedAP, true);
130 m_bHasGeneratedAP = true;
131 }
132
ShouldGenerateAP() const133 bool CPDF_Annot::ShouldGenerateAP() const {
134 // If AP dictionary exists and defines an appearance for normal mode, we use
135 // the appearance defined in the existing AP dictionary.
136 const CPDF_Dictionary* pAP =
137 m_pAnnotDict->GetDictFor(pdfium::annotation::kAP);
138 if (pAP && pAP->GetDictFor("N"))
139 return false;
140
141 return !IsHidden();
142 }
143
ShouldDrawAnnotation() const144 bool CPDF_Annot::ShouldDrawAnnotation() const {
145 if (IsHidden())
146 return false;
147 return m_bOpenState || m_nSubtype != CPDF_Annot::Subtype::POPUP;
148 }
149
ClearCachedAP()150 void CPDF_Annot::ClearCachedAP() {
151 m_APMap.clear();
152 }
153
GetSubtype() const154 CPDF_Annot::Subtype CPDF_Annot::GetSubtype() const {
155 return m_nSubtype;
156 }
157
RectForDrawing() const158 CFX_FloatRect CPDF_Annot::RectForDrawing() const {
159 bool bShouldUseQuadPointsCoords =
160 m_bIsTextMarkupAnnotation && m_bHasGeneratedAP;
161 if (bShouldUseQuadPointsCoords)
162 return BoundingRectFromQuadPoints(m_pAnnotDict.Get());
163 return m_pAnnotDict->GetRectFor(pdfium::annotation::kRect);
164 }
165
GetRect() const166 CFX_FloatRect CPDF_Annot::GetRect() const {
167 CFX_FloatRect rect = RectForDrawing();
168 rect.Normalize();
169 return rect;
170 }
171
GetFlags() const172 uint32_t CPDF_Annot::GetFlags() const {
173 return m_pAnnotDict->GetIntegerFor(pdfium::annotation::kF);
174 }
175
IsHidden() const176 bool CPDF_Annot::IsHidden() const {
177 return !!(GetFlags() & pdfium::annotation_flags::kHidden);
178 }
179
GetAnnotAP(CPDF_Dictionary * pAnnotDict,CPDF_Annot::AppearanceMode eMode)180 CPDF_Stream* GetAnnotAP(CPDF_Dictionary* pAnnotDict,
181 CPDF_Annot::AppearanceMode eMode) {
182 ASSERT(pAnnotDict);
183 return GetAnnotAPInternal(pAnnotDict, eMode, true);
184 }
185
GetAnnotAPNoFallback(CPDF_Dictionary * pAnnotDict,CPDF_Annot::AppearanceMode eMode)186 CPDF_Stream* GetAnnotAPNoFallback(CPDF_Dictionary* pAnnotDict,
187 CPDF_Annot::AppearanceMode eMode) {
188 ASSERT(pAnnotDict);
189 return GetAnnotAPInternal(pAnnotDict, eMode, false);
190 }
191
GetAPForm(const CPDF_Page * pPage,AppearanceMode mode)192 CPDF_Form* CPDF_Annot::GetAPForm(const CPDF_Page* pPage, AppearanceMode mode) {
193 CPDF_Stream* pStream = GetAnnotAP(m_pAnnotDict.Get(), mode);
194 if (!pStream)
195 return nullptr;
196
197 auto it = m_APMap.find(pStream);
198 if (it != m_APMap.end())
199 return it->second.get();
200
201 auto pNewForm = pdfium::MakeUnique<CPDF_Form>(
202 m_pDocument.Get(), pPage->m_pResources.Get(), pStream);
203 pNewForm->ParseContent();
204
205 CPDF_Form* pResult = pNewForm.get();
206 m_APMap[pStream] = std::move(pNewForm);
207 return pResult;
208 }
209
210 // static
RectFromQuadPointsArray(const CPDF_Array * pArray,size_t nIndex)211 CFX_FloatRect CPDF_Annot::RectFromQuadPointsArray(const CPDF_Array* pArray,
212 size_t nIndex) {
213 ASSERT(pArray);
214 ASSERT(nIndex < pArray->size() / 8);
215
216 // QuadPoints are defined with 4 pairs of numbers
217 // ([ pair0, pair1, pair2, pair3 ]), where
218 // pair0 = top_left
219 // pair1 = top_right
220 // pair2 = bottom_left
221 // pair3 = bottom_right
222 //
223 // On the other hand, /Rect is defined as 2 pairs [pair0, pair1] where:
224 // pair0 = bottom_left
225 // pair1 = top_right.
226
227 return CFX_FloatRect(
228 pArray->GetNumberAt(4 + nIndex * 8), pArray->GetNumberAt(5 + nIndex * 8),
229 pArray->GetNumberAt(2 + nIndex * 8), pArray->GetNumberAt(3 + nIndex * 8));
230 }
231
232 // static
BoundingRectFromQuadPoints(const CPDF_Dictionary * pAnnotDict)233 CFX_FloatRect CPDF_Annot::BoundingRectFromQuadPoints(
234 const CPDF_Dictionary* pAnnotDict) {
235 CFX_FloatRect ret;
236 const CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
237 size_t nQuadPointCount = pArray ? QuadPointCount(pArray) : 0;
238 if (nQuadPointCount == 0)
239 return ret;
240
241 ret = RectFromQuadPointsArray(pArray, 0);
242 for (size_t i = 1; i < nQuadPointCount; ++i) {
243 CFX_FloatRect rect = RectFromQuadPointsArray(pArray, i);
244 ret.Union(rect);
245 }
246 return ret;
247 }
248
249 // static
RectFromQuadPoints(const CPDF_Dictionary * pAnnotDict,size_t nIndex)250 CFX_FloatRect CPDF_Annot::RectFromQuadPoints(const CPDF_Dictionary* pAnnotDict,
251 size_t nIndex) {
252 const CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
253 size_t nQuadPointCount = pArray ? QuadPointCount(pArray) : 0;
254 if (nIndex >= nQuadPointCount)
255 return CFX_FloatRect();
256 return RectFromQuadPointsArray(pArray, nIndex);
257 }
258
259 // static
StringToAnnotSubtype(const ByteString & sSubtype)260 CPDF_Annot::Subtype CPDF_Annot::StringToAnnotSubtype(
261 const ByteString& sSubtype) {
262 if (sSubtype == "Text")
263 return CPDF_Annot::Subtype::TEXT;
264 if (sSubtype == "Link")
265 return CPDF_Annot::Subtype::LINK;
266 if (sSubtype == "FreeText")
267 return CPDF_Annot::Subtype::FREETEXT;
268 if (sSubtype == "Line")
269 return CPDF_Annot::Subtype::LINE;
270 if (sSubtype == "Square")
271 return CPDF_Annot::Subtype::SQUARE;
272 if (sSubtype == "Circle")
273 return CPDF_Annot::Subtype::CIRCLE;
274 if (sSubtype == "Polygon")
275 return CPDF_Annot::Subtype::POLYGON;
276 if (sSubtype == "PolyLine")
277 return CPDF_Annot::Subtype::POLYLINE;
278 if (sSubtype == "Highlight")
279 return CPDF_Annot::Subtype::HIGHLIGHT;
280 if (sSubtype == "Underline")
281 return CPDF_Annot::Subtype::UNDERLINE;
282 if (sSubtype == "Squiggly")
283 return CPDF_Annot::Subtype::SQUIGGLY;
284 if (sSubtype == "StrikeOut")
285 return CPDF_Annot::Subtype::STRIKEOUT;
286 if (sSubtype == "Stamp")
287 return CPDF_Annot::Subtype::STAMP;
288 if (sSubtype == "Caret")
289 return CPDF_Annot::Subtype::CARET;
290 if (sSubtype == "Ink")
291 return CPDF_Annot::Subtype::INK;
292 if (sSubtype == "Popup")
293 return CPDF_Annot::Subtype::POPUP;
294 if (sSubtype == "FileAttachment")
295 return CPDF_Annot::Subtype::FILEATTACHMENT;
296 if (sSubtype == "Sound")
297 return CPDF_Annot::Subtype::SOUND;
298 if (sSubtype == "Movie")
299 return CPDF_Annot::Subtype::MOVIE;
300 if (sSubtype == "Widget")
301 return CPDF_Annot::Subtype::WIDGET;
302 if (sSubtype == "Screen")
303 return CPDF_Annot::Subtype::SCREEN;
304 if (sSubtype == "PrinterMark")
305 return CPDF_Annot::Subtype::PRINTERMARK;
306 if (sSubtype == "TrapNet")
307 return CPDF_Annot::Subtype::TRAPNET;
308 if (sSubtype == "Watermark")
309 return CPDF_Annot::Subtype::WATERMARK;
310 if (sSubtype == "3D")
311 return CPDF_Annot::Subtype::THREED;
312 if (sSubtype == "RichMedia")
313 return CPDF_Annot::Subtype::RICHMEDIA;
314 if (sSubtype == "XFAWidget")
315 return CPDF_Annot::Subtype::XFAWIDGET;
316 return CPDF_Annot::Subtype::UNKNOWN;
317 }
318
319 // static
AnnotSubtypeToString(CPDF_Annot::Subtype nSubtype)320 ByteString CPDF_Annot::AnnotSubtypeToString(CPDF_Annot::Subtype nSubtype) {
321 if (nSubtype == CPDF_Annot::Subtype::TEXT)
322 return "Text";
323 if (nSubtype == CPDF_Annot::Subtype::LINK)
324 return "Link";
325 if (nSubtype == CPDF_Annot::Subtype::FREETEXT)
326 return "FreeText";
327 if (nSubtype == CPDF_Annot::Subtype::LINE)
328 return "Line";
329 if (nSubtype == CPDF_Annot::Subtype::SQUARE)
330 return "Square";
331 if (nSubtype == CPDF_Annot::Subtype::CIRCLE)
332 return "Circle";
333 if (nSubtype == CPDF_Annot::Subtype::POLYGON)
334 return "Polygon";
335 if (nSubtype == CPDF_Annot::Subtype::POLYLINE)
336 return "PolyLine";
337 if (nSubtype == CPDF_Annot::Subtype::HIGHLIGHT)
338 return "Highlight";
339 if (nSubtype == CPDF_Annot::Subtype::UNDERLINE)
340 return "Underline";
341 if (nSubtype == CPDF_Annot::Subtype::SQUIGGLY)
342 return "Squiggly";
343 if (nSubtype == CPDF_Annot::Subtype::STRIKEOUT)
344 return "StrikeOut";
345 if (nSubtype == CPDF_Annot::Subtype::STAMP)
346 return "Stamp";
347 if (nSubtype == CPDF_Annot::Subtype::CARET)
348 return "Caret";
349 if (nSubtype == CPDF_Annot::Subtype::INK)
350 return "Ink";
351 if (nSubtype == CPDF_Annot::Subtype::POPUP)
352 return "Popup";
353 if (nSubtype == CPDF_Annot::Subtype::FILEATTACHMENT)
354 return "FileAttachment";
355 if (nSubtype == CPDF_Annot::Subtype::SOUND)
356 return "Sound";
357 if (nSubtype == CPDF_Annot::Subtype::MOVIE)
358 return "Movie";
359 if (nSubtype == CPDF_Annot::Subtype::WIDGET)
360 return "Widget";
361 if (nSubtype == CPDF_Annot::Subtype::SCREEN)
362 return "Screen";
363 if (nSubtype == CPDF_Annot::Subtype::PRINTERMARK)
364 return "PrinterMark";
365 if (nSubtype == CPDF_Annot::Subtype::TRAPNET)
366 return "TrapNet";
367 if (nSubtype == CPDF_Annot::Subtype::WATERMARK)
368 return "Watermark";
369 if (nSubtype == CPDF_Annot::Subtype::THREED)
370 return "3D";
371 if (nSubtype == CPDF_Annot::Subtype::RICHMEDIA)
372 return "RichMedia";
373 if (nSubtype == CPDF_Annot::Subtype::XFAWIDGET)
374 return "XFAWidget";
375 return ByteString();
376 }
377
378 // static
QuadPointCount(const CPDF_Array * pArray)379 size_t CPDF_Annot::QuadPointCount(const CPDF_Array* pArray) {
380 return pArray->size() / 8;
381 }
382
DrawAppearance(CPDF_Page * pPage,CFX_RenderDevice * pDevice,const CFX_Matrix & mtUser2Device,AppearanceMode mode,const CPDF_RenderOptions * pOptions)383 bool CPDF_Annot::DrawAppearance(CPDF_Page* pPage,
384 CFX_RenderDevice* pDevice,
385 const CFX_Matrix& mtUser2Device,
386 AppearanceMode mode,
387 const CPDF_RenderOptions* pOptions) {
388 if (!ShouldDrawAnnotation())
389 return false;
390
391 // It might happen that by the time this annotation instance was created,
392 // it was flagged as "hidden" (e.g. /F 2), and hence CPVT_GenerateAP decided
393 // to not "generate" its AP.
394 // If for a reason the object is no longer hidden, but still does not have
395 // its "AP" generated, generate it now.
396 GenerateAPIfNeeded();
397
398 CFX_Matrix matrix;
399 CPDF_Form* pForm = AnnotGetMatrix(pPage, this, mode, &mtUser2Device, &matrix);
400 if (!pForm)
401 return false;
402
403 CPDF_RenderContext context(
404 pPage->GetDocument(), pPage->m_pPageResources.Get(),
405 static_cast<CPDF_PageRenderCache*>(pPage->GetRenderCache()));
406 context.AppendLayer(pForm, &matrix);
407 context.Render(pDevice, pOptions, nullptr);
408 return true;
409 }
410
DrawInContext(const CPDF_Page * pPage,CPDF_RenderContext * pContext,const CFX_Matrix * pUser2Device,AppearanceMode mode)411 bool CPDF_Annot::DrawInContext(const CPDF_Page* pPage,
412 CPDF_RenderContext* pContext,
413 const CFX_Matrix* pUser2Device,
414 AppearanceMode mode) {
415 if (!ShouldDrawAnnotation())
416 return false;
417
418 // It might happen that by the time this annotation instance was created,
419 // it was flagged as "hidden" (e.g. /F 2), and hence CPVT_GenerateAP decided
420 // to not "generate" its AP.
421 // If for a reason the object is no longer hidden, but still does not have
422 // its "AP" generated, generate it now.
423 GenerateAPIfNeeded();
424
425 CFX_Matrix matrix;
426 CPDF_Form* pForm = AnnotGetMatrix(pPage, this, mode, pUser2Device, &matrix);
427 if (!pForm)
428 return false;
429
430 pContext->AppendLayer(pForm, &matrix);
431 return true;
432 }
433
DrawBorder(CFX_RenderDevice * pDevice,const CFX_Matrix * pUser2Device,const CPDF_RenderOptions * pOptions)434 void CPDF_Annot::DrawBorder(CFX_RenderDevice* pDevice,
435 const CFX_Matrix* pUser2Device,
436 const CPDF_RenderOptions* pOptions) {
437 if (GetSubtype() == CPDF_Annot::Subtype::POPUP)
438 return;
439
440 uint32_t annot_flags = GetFlags();
441 if (annot_flags & pdfium::annotation_flags::kHidden)
442 return;
443
444 bool bPrinting = pDevice->GetDeviceType() == DeviceType::kPrinter ||
445 (pOptions && pOptions->GetOptions().bPrintPreview);
446 if (bPrinting && (annot_flags & pdfium::annotation_flags::kPrint) == 0) {
447 return;
448 }
449 if (!bPrinting && (annot_flags & pdfium::annotation_flags::kNoView)) {
450 return;
451 }
452 CPDF_Dictionary* pBS = m_pAnnotDict->GetDictFor("BS");
453 char style_char;
454 float width;
455 CPDF_Array* pDashArray = nullptr;
456 if (!pBS) {
457 CPDF_Array* pBorderArray =
458 m_pAnnotDict->GetArrayFor(pdfium::annotation::kBorder);
459 style_char = 'S';
460 if (pBorderArray) {
461 width = pBorderArray->GetNumberAt(2);
462 if (pBorderArray->size() == 4) {
463 pDashArray = pBorderArray->GetArrayAt(3);
464 if (!pDashArray) {
465 return;
466 }
467 size_t nLen = pDashArray->size();
468 size_t i = 0;
469 for (; i < nLen; ++i) {
470 CPDF_Object* pObj = pDashArray->GetDirectObjectAt(i);
471 if (pObj && pObj->GetInteger()) {
472 break;
473 }
474 }
475 if (i == nLen) {
476 return;
477 }
478 style_char = 'D';
479 }
480 } else {
481 width = 1;
482 }
483 } else {
484 ByteString style = pBS->GetStringFor("S");
485 pDashArray = pBS->GetArrayFor("D");
486 style_char = style[1];
487 width = pBS->GetNumberFor("W");
488 }
489 if (width <= 0) {
490 return;
491 }
492 CPDF_Array* pColor = m_pAnnotDict->GetArrayFor(pdfium::annotation::kC);
493 uint32_t argb = 0xff000000;
494 if (pColor) {
495 int R = (int32_t)(pColor->GetNumberAt(0) * 255);
496 int G = (int32_t)(pColor->GetNumberAt(1) * 255);
497 int B = (int32_t)(pColor->GetNumberAt(2) * 255);
498 argb = ArgbEncode(0xff, R, G, B);
499 }
500 CFX_GraphStateData graph_state;
501 graph_state.m_LineWidth = width;
502 if (style_char == 'D') {
503 if (pDashArray) {
504 graph_state.m_DashArray =
505 ReadArrayElementsToVector(pDashArray, pDashArray->size());
506 if (graph_state.m_DashArray.size() % 2)
507 graph_state.m_DashArray.push_back(graph_state.m_DashArray.back());
508 } else {
509 graph_state.m_DashArray = {3.0f, 3.0f};
510 }
511 }
512
513 CFX_FloatRect rect = GetRect();
514 rect.Deflate(width / 2, width / 2);
515 CFX_PathData path;
516 path.AppendFloatRect(rect);
517
518 int fill_type = 0;
519 if (pOptions && pOptions->GetOptions().bNoPathSmooth)
520 fill_type |= FXFILL_NOPATHSMOOTH;
521
522 pDevice->DrawPath(&path, pUser2Device, &graph_state, argb, argb, fill_type);
523 }
524