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 <windows.h>
8
9#include <algorithm>
10#include <memory>
11#include <sstream>
12
13#include "core/fxcrt/fx_system.h"
14#include "core/fxge/cfx_gemodule.h"
15#include "core/fxge/cfx_graphstatedata.h"
16#include "core/fxge/cfx_pathdata.h"
17#include "core/fxge/win32/cfx_windowsdib.h"
18#include "core/fxge/win32/win32_int.h"
19#include "third_party/base/ptr_util.h"
20
21// Has to come before gdiplus.h
22namespace Gdiplus {
23using std::min;
24using std::max;
25}  // namespace Gdiplus
26
27#include <gdiplus.h>  // NOLINT
28
29// TODO(thestig): Remove the infrequently used ones.
30using Gdiplus::CombineMode;
31using Gdiplus::DashCap;
32using Gdiplus::DashCapFlat;
33using Gdiplus::DashCapRound;
34using Gdiplus::FillModeAlternate;
35using Gdiplus::FillModeWinding;
36using Gdiplus::GdiplusStartupInput;
37using Gdiplus::GdiplusStartupOutput;
38using Gdiplus::GpBitmap;
39using Gdiplus::GpBrush;
40using Gdiplus::GpDashCap;
41using Gdiplus::GpDashStyle;
42using Gdiplus::GpFillMode;
43using Gdiplus::GpFont;
44using Gdiplus::GpFontCollection;
45using Gdiplus::GpFontFamily;
46using Gdiplus::GpGraphics;
47using Gdiplus::GpImage;
48using Gdiplus::GpLineCap;
49using Gdiplus::GpLineJoin;
50using Gdiplus::GpMatrix;
51using Gdiplus::GpPath;
52using Gdiplus::GpPen;
53using Gdiplus::GpPoint;
54using Gdiplus::GpPointF;
55using Gdiplus::GpRect;
56using Gdiplus::GpRegion;
57using Gdiplus::GpSolidFill;
58using Gdiplus::GpStatus;
59using Gdiplus::GpStringFormat;
60using Gdiplus::GpUnit;
61using Gdiplus::ImageLockModeRead;
62using Gdiplus::InterpolationMode;
63using Gdiplus::InterpolationModeBilinear;
64using Gdiplus::InterpolationModeHighQuality;
65using Gdiplus::InterpolationModeNearestNeighbor;
66using Gdiplus::LineCap;
67using Gdiplus::LineCapFlat;
68using Gdiplus::LineCapRound;
69using Gdiplus::LineCapSquare;
70using Gdiplus::LineJoin;
71using Gdiplus::LineJoinBevel;
72using Gdiplus::LineJoinMiterClipped;
73using Gdiplus::LineJoinRound;
74using Gdiplus::PaletteFlagsHasAlpha;
75using Gdiplus::PathPointTypeBezier;
76using Gdiplus::PathPointTypeCloseSubpath;
77using Gdiplus::PathPointTypeLine;
78using Gdiplus::PathPointTypeStart;
79using Gdiplus::PixelOffsetMode;
80using Gdiplus::PixelOffsetModeHalf;
81using Gdiplus::REAL;
82using Gdiplus::SmoothingMode;
83using Gdiplus::SmoothingModeAntiAlias;
84using Gdiplus::SmoothingModeNone;
85using Gdiplus::TextRenderingHint;
86using Gdiplus::UnitPixel;
87using Gdiplus::UnitWorld;
88
89#define GdiFillType2Gdip(fill_type) \
90  (fill_type == ALTERNATE ? FillModeAlternate : FillModeWinding)
91
92enum {
93  FuncId_GdipCreatePath2,
94  FuncId_GdipSetPenDashStyle,
95  FuncId_GdipSetPenDashArray,
96  FuncId_GdipSetPenDashCap197819,
97  FuncId_GdipSetPenLineJoin,
98  FuncId_GdipSetPenWidth,
99  FuncId_GdipCreateFromHDC,
100  FuncId_GdipSetPageUnit,
101  FuncId_GdipSetSmoothingMode,
102  FuncId_GdipCreateSolidFill,
103  FuncId_GdipFillPath,
104  FuncId_GdipDeleteBrush,
105  FuncId_GdipCreatePen1,
106  FuncId_GdipSetPenMiterLimit,
107  FuncId_GdipDrawPath,
108  FuncId_GdipDeletePen,
109  FuncId_GdipDeletePath,
110  FuncId_GdipDeleteGraphics,
111  FuncId_GdipCreateBitmapFromFileICM,
112  FuncId_GdipCreateBitmapFromStreamICM,
113  FuncId_GdipGetImageHeight,
114  FuncId_GdipGetImageWidth,
115  FuncId_GdipGetImagePixelFormat,
116  FuncId_GdipBitmapLockBits,
117  FuncId_GdipGetImagePaletteSize,
118  FuncId_GdipGetImagePalette,
119  FuncId_GdipBitmapUnlockBits,
120  FuncId_GdipDisposeImage,
121  FuncId_GdipFillRectangle,
122  FuncId_GdipCreateBitmapFromScan0,
123  FuncId_GdipSetImagePalette,
124  FuncId_GdipSetInterpolationMode,
125  FuncId_GdipDrawImagePointsI,
126  FuncId_GdipCreateBitmapFromGdiDib,
127  FuncId_GdiplusStartup,
128  FuncId_GdipDrawLineI,
129  FuncId_GdipResetClip,
130  FuncId_GdipCreatePath,
131  FuncId_GdipAddPathPath,
132  FuncId_GdipSetPathFillMode,
133  FuncId_GdipSetClipPath,
134  FuncId_GdipGetClip,
135  FuncId_GdipCreateRegion,
136  FuncId_GdipGetClipBoundsI,
137  FuncId_GdipSetClipRegion,
138  FuncId_GdipWidenPath,
139  FuncId_GdipAddPathLine,
140  FuncId_GdipAddPathRectangle,
141  FuncId_GdipDeleteRegion,
142  FuncId_GdipGetDC,
143  FuncId_GdipReleaseDC,
144  FuncId_GdipSetPenLineCap197819,
145  FuncId_GdipSetPenDashOffset,
146  FuncId_GdipResetPath,
147  FuncId_GdipCreateRegionPath,
148  FuncId_GdipCreateFont,
149  FuncId_GdipGetFontSize,
150  FuncId_GdipCreateFontFamilyFromName,
151  FuncId_GdipSetTextRenderingHint,
152  FuncId_GdipDrawDriverString,
153  FuncId_GdipCreateMatrix2,
154  FuncId_GdipDeleteMatrix,
155  FuncId_GdipSetWorldTransform,
156  FuncId_GdipResetWorldTransform,
157  FuncId_GdipDeleteFontFamily,
158  FuncId_GdipDeleteFont,
159  FuncId_GdipNewPrivateFontCollection,
160  FuncId_GdipDeletePrivateFontCollection,
161  FuncId_GdipPrivateAddMemoryFont,
162  FuncId_GdipGetFontCollectionFamilyList,
163  FuncId_GdipGetFontCollectionFamilyCount,
164  FuncId_GdipSetTextContrast,
165  FuncId_GdipSetPixelOffsetMode,
166  FuncId_GdipGetImageGraphicsContext,
167  FuncId_GdipDrawImageI,
168  FuncId_GdipDrawImageRectI,
169  FuncId_GdipDrawString,
170  FuncId_GdipSetPenTransform,
171};
172
173static LPCSTR g_GdipFuncNames[] = {
174    "GdipCreatePath2",
175    "GdipSetPenDashStyle",
176    "GdipSetPenDashArray",
177    "GdipSetPenDashCap197819",
178    "GdipSetPenLineJoin",
179    "GdipSetPenWidth",
180    "GdipCreateFromHDC",
181    "GdipSetPageUnit",
182    "GdipSetSmoothingMode",
183    "GdipCreateSolidFill",
184    "GdipFillPath",
185    "GdipDeleteBrush",
186    "GdipCreatePen1",
187    "GdipSetPenMiterLimit",
188    "GdipDrawPath",
189    "GdipDeletePen",
190    "GdipDeletePath",
191    "GdipDeleteGraphics",
192    "GdipCreateBitmapFromFileICM",
193    "GdipCreateBitmapFromStreamICM",
194    "GdipGetImageHeight",
195    "GdipGetImageWidth",
196    "GdipGetImagePixelFormat",
197    "GdipBitmapLockBits",
198    "GdipGetImagePaletteSize",
199    "GdipGetImagePalette",
200    "GdipBitmapUnlockBits",
201    "GdipDisposeImage",
202    "GdipFillRectangle",
203    "GdipCreateBitmapFromScan0",
204    "GdipSetImagePalette",
205    "GdipSetInterpolationMode",
206    "GdipDrawImagePointsI",
207    "GdipCreateBitmapFromGdiDib",
208    "GdiplusStartup",
209    "GdipDrawLineI",
210    "GdipResetClip",
211    "GdipCreatePath",
212    "GdipAddPathPath",
213    "GdipSetPathFillMode",
214    "GdipSetClipPath",
215    "GdipGetClip",
216    "GdipCreateRegion",
217    "GdipGetClipBoundsI",
218    "GdipSetClipRegion",
219    "GdipWidenPath",
220    "GdipAddPathLine",
221    "GdipAddPathRectangle",
222    "GdipDeleteRegion",
223    "GdipGetDC",
224    "GdipReleaseDC",
225    "GdipSetPenLineCap197819",
226    "GdipSetPenDashOffset",
227    "GdipResetPath",
228    "GdipCreateRegionPath",
229    "GdipCreateFont",
230    "GdipGetFontSize",
231    "GdipCreateFontFamilyFromName",
232    "GdipSetTextRenderingHint",
233    "GdipDrawDriverString",
234    "GdipCreateMatrix2",
235    "GdipDeleteMatrix",
236    "GdipSetWorldTransform",
237    "GdipResetWorldTransform",
238    "GdipDeleteFontFamily",
239    "GdipDeleteFont",
240    "GdipNewPrivateFontCollection",
241    "GdipDeletePrivateFontCollection",
242    "GdipPrivateAddMemoryFont",
243    "GdipGetFontCollectionFamilyList",
244    "GdipGetFontCollectionFamilyCount",
245    "GdipSetTextContrast",
246    "GdipSetPixelOffsetMode",
247    "GdipGetImageGraphicsContext",
248    "GdipDrawImageI",
249    "GdipDrawImageRectI",
250    "GdipDrawString",
251    "GdipSetPenTransform",
252};
253static_assert(FX_ArraySize(g_GdipFuncNames) ==
254                  static_cast<size_t>(FuncId_GdipSetPenTransform) + 1,
255              "g_GdipFuncNames has wrong size");
256
257typedef GpStatus(WINGDIPAPI* FuncType_GdipCreatePath2)(GDIPCONST GpPointF*,
258                                                       GDIPCONST BYTE*,
259                                                       INT,
260                                                       GpFillMode,
261                                                       GpPath** path);
262typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashStyle)(
263    GpPen* pen,
264    GpDashStyle dashstyle);
265typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashArray)(GpPen* pen,
266                                                           GDIPCONST REAL* dash,
267                                                           INT count);
268typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashCap197819)(
269    GpPen* pen,
270    GpDashCap dashCap);
271typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenLineJoin)(GpPen* pen,
272                                                          GpLineJoin lineJoin);
273typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenWidth)(GpPen* pen, REAL width);
274typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateFromHDC)(HDC hdc,
275                                                         GpGraphics** graphics);
276typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPageUnit)(GpGraphics* graphics,
277                                                       GpUnit unit);
278typedef GpStatus(WINGDIPAPI* FuncType_GdipSetSmoothingMode)(
279    GpGraphics* graphics,
280    SmoothingMode smoothingMode);
281typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateSolidFill)(Gdiplus::ARGB color,
282                                                           GpSolidFill** brush);
283typedef GpStatus(WINGDIPAPI* FuncType_GdipFillPath)(GpGraphics* graphics,
284                                                    GpBrush* brush,
285                                                    GpPath* path);
286typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteBrush)(GpBrush* brush);
287typedef GpStatus(WINGDIPAPI* FuncType_GdipCreatePen1)(Gdiplus::ARGB color,
288                                                      REAL width,
289                                                      GpUnit unit,
290                                                      GpPen** pen);
291typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenMiterLimit)(GpPen* pen,
292                                                            REAL miterLimit);
293typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawPath)(GpGraphics* graphics,
294                                                    GpPen* pen,
295                                                    GpPath* path);
296typedef GpStatus(WINGDIPAPI* FuncType_GdipDeletePen)(GpPen* pen);
297typedef GpStatus(WINGDIPAPI* FuncType_GdipDeletePath)(GpPath* path);
298typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteGraphics)(GpGraphics* graphics);
299typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromFileICM)(
300    GDIPCONST WCHAR* filename,
301    GpBitmap** bitmap);
302typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromStreamICM)(
303    IStream* stream,
304    GpBitmap** bitmap);
305typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImageWidth)(GpImage* image,
306                                                         UINT* width);
307typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImageHeight)(GpImage* image,
308                                                          UINT* height);
309typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImagePixelFormat)(
310    GpImage* image,
311    Gdiplus::PixelFormat* format);
312typedef GpStatus(WINGDIPAPI* FuncType_GdipBitmapLockBits)(
313    GpBitmap* bitmap,
314    GDIPCONST GpRect* rect,
315    UINT flags,
316    Gdiplus::PixelFormat format,
317    Gdiplus::BitmapData* lockedBitmapData);
318typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImagePalette)(
319    GpImage* image,
320    Gdiplus::ColorPalette* palette,
321    INT size);
322typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImagePaletteSize)(GpImage* image,
323                                                               INT* size);
324typedef GpStatus(WINGDIPAPI* FuncType_GdipBitmapUnlockBits)(
325    GpBitmap* bitmap,
326    Gdiplus::BitmapData* lockedBitmapData);
327typedef GpStatus(WINGDIPAPI* FuncType_GdipDisposeImage)(GpImage* image);
328typedef GpStatus(WINGDIPAPI* FuncType_GdipFillRectangle)(GpGraphics* graphics,
329                                                         GpBrush* brush,
330                                                         REAL x,
331                                                         REAL y,
332                                                         REAL width,
333                                                         REAL height);
334typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromScan0)(
335    INT width,
336    INT height,
337    INT stride,
338    Gdiplus::PixelFormat format,
339    BYTE* scan0,
340    GpBitmap** bitmap);
341typedef GpStatus(WINGDIPAPI* FuncType_GdipSetImagePalette)(
342    GpImage* image,
343    GDIPCONST Gdiplus::ColorPalette* palette);
344typedef GpStatus(WINGDIPAPI* FuncType_GdipSetInterpolationMode)(
345    GpGraphics* graphics,
346    InterpolationMode interpolationMode);
347typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawImagePointsI)(
348    GpGraphics* graphics,
349    GpImage* image,
350    GDIPCONST GpPoint* dstpoints,
351    INT count);
352typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromGdiDib)(
353    GDIPCONST BITMAPINFO* gdiBitmapInfo,
354    VOID* gdiBitmapData,
355    GpBitmap** bitmap);
356typedef Gdiplus::Status(WINAPI* FuncType_GdiplusStartup)(
357    OUT uintptr_t* token,
358    const GdiplusStartupInput* input,
359    OUT GdiplusStartupOutput* output);
360typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawLineI)(GpGraphics* graphics,
361                                                     GpPen* pen,
362                                                     int x1,
363                                                     int y1,
364                                                     int x2,
365                                                     int y2);
366typedef GpStatus(WINGDIPAPI* FuncType_GdipResetClip)(GpGraphics* graphics);
367typedef GpStatus(WINGDIPAPI* FuncType_GdipCreatePath)(GpFillMode brushMode,
368                                                      GpPath** path);
369typedef GpStatus(WINGDIPAPI* FuncType_GdipAddPathPath)(
370    GpPath* path,
371    GDIPCONST GpPath* addingPath,
372    BOOL connect);
373typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPathFillMode)(GpPath* path,
374                                                           GpFillMode fillmode);
375typedef GpStatus(WINGDIPAPI* FuncType_GdipSetClipPath)(GpGraphics* graphics,
376                                                       GpPath* path,
377                                                       CombineMode combineMode);
378typedef GpStatus(WINGDIPAPI* FuncType_GdipGetClip)(GpGraphics* graphics,
379                                                   GpRegion* region);
380typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateRegion)(GpRegion** region);
381typedef GpStatus(WINGDIPAPI* FuncType_GdipGetClipBoundsI)(GpGraphics* graphics,
382                                                          GpRect* rect);
383typedef GpStatus(WINGDIPAPI* FuncType_GdipSetClipRegion)(
384    GpGraphics* graphics,
385    GpRegion* region,
386    CombineMode combineMode);
387typedef GpStatus(WINGDIPAPI* FuncType_GdipWidenPath)(GpPath* nativePath,
388                                                     GpPen* pen,
389                                                     GpMatrix* matrix,
390                                                     REAL flatness);
391typedef GpStatus(WINGDIPAPI* FuncType_GdipAddPathLine)(GpPath* path,
392                                                       REAL x1,
393                                                       REAL y1,
394                                                       REAL x2,
395                                                       REAL y2);
396typedef GpStatus(WINGDIPAPI* FuncType_GdipAddPathRectangle)(GpPath* path,
397                                                            REAL x,
398                                                            REAL y,
399                                                            REAL width,
400                                                            REAL height);
401typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteRegion)(GpRegion* region);
402typedef GpStatus(WINGDIPAPI* FuncType_GdipGetDC)(GpGraphics* graphics,
403                                                 HDC* hdc);
404typedef GpStatus(WINGDIPAPI* FuncType_GdipReleaseDC)(GpGraphics* graphics,
405                                                     HDC hdc);
406typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenLineCap197819)(
407    GpPen* pen,
408    GpLineCap startCap,
409    GpLineCap endCap,
410    GpDashCap dashCap);
411typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashOffset)(GpPen* pen,
412                                                            REAL offset);
413typedef GpStatus(WINGDIPAPI* FuncType_GdipResetPath)(GpPath* path);
414typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateRegionPath)(GpPath* path,
415                                                            GpRegion** region);
416typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateFont)(
417    GDIPCONST GpFontFamily* fontFamily,
418    REAL emSize,
419    INT style,
420    Gdiplus::Unit unit,
421    GpFont** font);
422typedef GpStatus(WINGDIPAPI* FuncType_GdipGetFontSize)(GpFont* font,
423                                                       REAL* size);
424typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateFontFamilyFromName)(
425    GDIPCONST WCHAR* name,
426    GpFontCollection* fontCollection,
427    GpFontFamily** FontFamily);
428typedef GpStatus(WINGDIPAPI* FuncType_GdipSetTextRenderingHint)(
429    GpGraphics* graphics,
430    TextRenderingHint mode);
431typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawDriverString)(
432    GpGraphics* graphics,
433    GDIPCONST UINT16* text,
434    INT length,
435    GDIPCONST GpFont* font,
436    GDIPCONST GpBrush* brush,
437    GDIPCONST Gdiplus::PointF* positions,
438    INT flags,
439    GDIPCONST GpMatrix* matrix);
440typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateMatrix2)(REAL m11,
441                                                         REAL m12,
442                                                         REAL m21,
443                                                         REAL m22,
444                                                         REAL dx,
445                                                         REAL dy,
446                                                         GpMatrix** matrix);
447typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteMatrix)(GpMatrix* matrix);
448typedef GpStatus(WINGDIPAPI* FuncType_GdipSetWorldTransform)(
449    GpGraphics* graphics,
450    GpMatrix* matrix);
451typedef GpStatus(WINGDIPAPI* FuncType_GdipResetWorldTransform)(
452    GpGraphics* graphics);
453typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteFontFamily)(
454    GpFontFamily* FontFamily);
455typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteFont)(GpFont* font);
456typedef GpStatus(WINGDIPAPI* FuncType_GdipNewPrivateFontCollection)(
457    GpFontCollection** fontCollection);
458typedef GpStatus(WINGDIPAPI* FuncType_GdipDeletePrivateFontCollection)(
459    GpFontCollection** fontCollection);
460typedef GpStatus(WINGDIPAPI* FuncType_GdipPrivateAddMemoryFont)(
461    GpFontCollection* fontCollection,
462    GDIPCONST void* memory,
463    INT length);
464typedef GpStatus(WINGDIPAPI* FuncType_GdipGetFontCollectionFamilyList)(
465    GpFontCollection* fontCollection,
466    INT numSought,
467    GpFontFamily* gpfamilies[],
468    INT* numFound);
469typedef GpStatus(WINGDIPAPI* FuncType_GdipGetFontCollectionFamilyCount)(
470    GpFontCollection* fontCollection,
471    INT* numFound);
472typedef GpStatus(WINGDIPAPI* FuncType_GdipSetTextContrast)(GpGraphics* graphics,
473                                                           UINT contrast);
474typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPixelOffsetMode)(
475    GpGraphics* graphics,
476    PixelOffsetMode pixelOffsetMode);
477typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImageGraphicsContext)(
478    GpImage* image,
479    GpGraphics** graphics);
480typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawImageI)(GpGraphics* graphics,
481                                                      GpImage* image,
482                                                      INT x,
483                                                      INT y);
484typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawImageRectI)(GpGraphics* graphics,
485                                                          GpImage* image,
486                                                          INT x,
487                                                          INT y,
488                                                          INT width,
489                                                          INT height);
490typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawString)(
491    GpGraphics* graphics,
492    GDIPCONST WCHAR* str,
493    INT length,
494    GDIPCONST GpFont* font,
495    GDIPCONST Gdiplus::RectF* layoutRect,
496    GDIPCONST GpStringFormat* stringFormat,
497    GDIPCONST GpBrush* brush);
498typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenTransform)(GpPen* pen,
499                                                           GpMatrix* matrix);
500#define CallFunc(funcname) \
501  ((FuncType_##funcname)GdiplusExt.m_Functions[FuncId_##funcname])
502
503void* CGdiplusExt::GdiAddFontMemResourceEx(void* pFontdata,
504                                           uint32_t size,
505                                           void* pdv,
506                                           uint32_t* num_face) {
507  if (!m_pGdiAddFontMemResourceEx)
508    return nullptr;
509
510  return m_pGdiAddFontMemResourceEx((PVOID)pFontdata, (DWORD)size, (PVOID)pdv,
511                                    (DWORD*)num_face);
512}
513
514bool CGdiplusExt::GdiRemoveFontMemResourceEx(void* handle) {
515  return m_pGdiRemoveFontMemResourseEx &&
516         m_pGdiRemoveFontMemResourseEx((HANDLE)handle);
517}
518
519static GpBrush* _GdipCreateBrush(DWORD argb) {
520  CGdiplusExt& GdiplusExt =
521      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
522  GpSolidFill* solidBrush = nullptr;
523  CallFunc(GdipCreateSolidFill)((Gdiplus::ARGB)argb, &solidBrush);
524  return solidBrush;
525}
526
527static RetainPtr<CFX_DIBitmap> StretchMonoToGray(
528    int dest_width,
529    int dest_height,
530    const RetainPtr<CFX_DIBitmap>& pSource,
531    FX_RECT* pClipRect) {
532  bool bFlipX = dest_width < 0;
533  if (bFlipX)
534    dest_width = -dest_width;
535
536  bool bFlipY = dest_height < 0;
537  if (bFlipY)
538    dest_height = -dest_height;
539
540  int result_width = pClipRect->Width();
541  int result_height = pClipRect->Height();
542  int result_pitch = (result_width + 3) / 4 * 4;
543  auto pStretched = pdfium::MakeRetain<CFX_DIBitmap>();
544  if (!pStretched->Create(result_width, result_height, FXDIB_8bppRgb))
545    return nullptr;
546
547  LPBYTE dest_buf = pStretched->GetBuffer();
548  int src_width = pSource->GetWidth();
549  int src_height = pSource->GetHeight();
550  int y_unit = src_height / dest_height;
551  int x_unit = src_width / dest_width;
552  int area_unit = y_unit * x_unit;
553  LPBYTE src_buf = pSource->GetBuffer();
554  int src_pitch = pSource->GetPitch();
555  for (int dest_y = 0; dest_y < result_height; dest_y++) {
556    LPBYTE dest_scan = dest_buf + dest_y * result_pitch;
557    int src_y_start = bFlipY ? (dest_height - 1 - dest_y - pClipRect->top)
558                             : (dest_y + pClipRect->top);
559    src_y_start = src_y_start * src_height / dest_height;
560    LPBYTE src_scan = src_buf + src_y_start * src_pitch;
561    for (int dest_x = 0; dest_x < result_width; dest_x++) {
562      int sum = 0;
563      int src_x_start = bFlipX ? (dest_width - 1 - dest_x - pClipRect->left)
564                               : (dest_x + pClipRect->left);
565      src_x_start = src_x_start * src_width / dest_width;
566      int src_x_end = src_x_start + x_unit;
567      LPBYTE src_line = src_scan;
568      for (int src_y = 0; src_y < y_unit; src_y++) {
569        for (int src_x = src_x_start; src_x < src_x_end; src_x++) {
570          if (!(src_line[src_x / 8] & (1 << (7 - src_x % 8)))) {
571            sum += 255;
572          }
573        }
574        src_line += src_pitch;
575      }
576      dest_scan[dest_x] = 255 - sum / area_unit;
577    }
578  }
579  return pStretched;
580}
581
582static void OutputImageMask(GpGraphics* pGraphics,
583                            BOOL bMonoDevice,
584                            const RetainPtr<CFX_DIBitmap>& pBitmap,
585                            int dest_left,
586                            int dest_top,
587                            int dest_width,
588                            int dest_height,
589                            FX_ARGB argb,
590                            const FX_RECT* pClipRect) {
591  ASSERT(pBitmap->GetBPP() == 1);
592  CGdiplusExt& GdiplusExt =
593      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
594  int src_width = pBitmap->GetWidth(), src_height = pBitmap->GetHeight();
595  int src_pitch = pBitmap->GetPitch();
596  uint8_t* scan0 = pBitmap->GetBuffer();
597  if (src_width == 1 && src_height == 1) {
598    if ((scan0[0] & 0x80) == 0)
599      return;
600
601    GpSolidFill* solidBrush;
602    CallFunc(GdipCreateSolidFill)((Gdiplus::ARGB)argb, &solidBrush);
603    if (dest_width < 0) {
604      dest_width = -dest_width;
605      dest_left -= dest_width;
606    }
607    if (dest_height < 0) {
608      dest_height = -dest_height;
609      dest_top -= dest_height;
610    }
611    CallFunc(GdipFillRectangle)(pGraphics, solidBrush, (float)dest_left,
612                                (float)dest_top, (float)dest_width,
613                                (float)dest_height);
614    CallFunc(GdipDeleteBrush)(solidBrush);
615    return;
616  }
617  if (!bMonoDevice && abs(dest_width) < src_width &&
618      abs(dest_height) < src_height) {
619    FX_RECT image_rect(dest_left, dest_top, dest_left + dest_width,
620                       dest_top + dest_height);
621    image_rect.Normalize();
622    FX_RECT image_clip = image_rect;
623    image_clip.Intersect(*pClipRect);
624    if (image_clip.IsEmpty())
625      return;
626
627    image_clip.Offset(-image_rect.left, -image_rect.top);
628    RetainPtr<CFX_DIBitmap> pStretched;
629    if (src_width * src_height > 10000) {
630      pStretched =
631          StretchMonoToGray(dest_width, dest_height, pBitmap, &image_clip);
632    } else {
633      pStretched =
634          pBitmap->StretchTo(dest_width, dest_height, false, &image_clip);
635    }
636    GpBitmap* bitmap;
637    CallFunc(GdipCreateBitmapFromScan0)(image_clip.Width(), image_clip.Height(),
638                                        (image_clip.Width() + 3) / 4 * 4,
639                                        PixelFormat8bppIndexed,
640                                        pStretched->GetBuffer(), &bitmap);
641    int a;
642    int r;
643    int g;
644    int b;
645    std::tie(a, r, g, b) = ArgbDecode(argb);
646    UINT pal[258];
647    pal[0] = 0;
648    pal[1] = 256;
649    for (int i = 0; i < 256; i++) {
650      pal[i + 2] = ArgbEncode(i * a / 255, r, g, b);
651    }
652    CallFunc(GdipSetImagePalette)(bitmap, (Gdiplus::ColorPalette*)pal);
653    CallFunc(GdipDrawImageI)(pGraphics, bitmap,
654                             image_rect.left + image_clip.left,
655                             image_rect.top + image_clip.top);
656    CallFunc(GdipDisposeImage)(bitmap);
657    return;
658  }
659  GpBitmap* bitmap;
660  CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
661                                      PixelFormat1bppIndexed, scan0, &bitmap);
662  UINT palette[4] = {PaletteFlagsHasAlpha, 2, 0, argb};
663  CallFunc(GdipSetImagePalette)(bitmap, (Gdiplus::ColorPalette*)palette);
664  Gdiplus::Point destinationPoints[] = {
665      Gdiplus::Point(dest_left, dest_top),
666      Gdiplus::Point(dest_left + dest_width, dest_top),
667      Gdiplus::Point(dest_left, dest_top + dest_height)};
668  CallFunc(GdipDrawImagePointsI)(pGraphics, bitmap, destinationPoints, 3);
669  CallFunc(GdipDisposeImage)(bitmap);
670}
671static void OutputImage(GpGraphics* pGraphics,
672                        const RetainPtr<CFX_DIBitmap>& pBitmap,
673                        const FX_RECT* pSrcRect,
674                        int dest_left,
675                        int dest_top,
676                        int dest_width,
677                        int dest_height) {
678  int src_width = pSrcRect->Width(), src_height = pSrcRect->Height();
679  CGdiplusExt& GdiplusExt =
680      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
681  if (pBitmap->GetBPP() == 1 && (pSrcRect->left % 8)) {
682    FX_RECT new_rect(0, 0, src_width, src_height);
683    RetainPtr<CFX_DIBitmap> pCloned = pBitmap->Clone(pSrcRect);
684    if (!pCloned)
685      return;
686    OutputImage(pGraphics, pCloned, &new_rect, dest_left, dest_top, dest_width,
687                dest_height);
688    return;
689  }
690  int src_pitch = pBitmap->GetPitch();
691  uint8_t* scan0 = pBitmap->GetBuffer() + pSrcRect->top * src_pitch +
692                   pBitmap->GetBPP() * pSrcRect->left / 8;
693  GpBitmap* bitmap = nullptr;
694  switch (pBitmap->GetFormat()) {
695    case FXDIB_Argb:
696      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
697                                          PixelFormat32bppARGB, scan0, &bitmap);
698      break;
699    case FXDIB_Rgb32:
700      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
701                                          PixelFormat32bppRGB, scan0, &bitmap);
702      break;
703    case FXDIB_Rgb:
704      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
705                                          PixelFormat24bppRGB, scan0, &bitmap);
706      break;
707    case FXDIB_8bppRgb: {
708      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
709                                          PixelFormat8bppIndexed, scan0,
710                                          &bitmap);
711      UINT pal[258];
712      pal[0] = 0;
713      pal[1] = 256;
714      for (int i = 0; i < 256; i++)
715        pal[i + 2] = pBitmap->GetPaletteArgb(i);
716      CallFunc(GdipSetImagePalette)(bitmap, (Gdiplus::ColorPalette*)pal);
717      break;
718    }
719    case FXDIB_1bppRgb: {
720      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
721                                          PixelFormat1bppIndexed, scan0,
722                                          &bitmap);
723      break;
724    }
725  }
726  if (dest_height < 0) {
727    dest_height--;
728  }
729  if (dest_width < 0) {
730    dest_width--;
731  }
732  Gdiplus::Point destinationPoints[] = {
733      Gdiplus::Point(dest_left, dest_top),
734      Gdiplus::Point(dest_left + dest_width, dest_top),
735      Gdiplus::Point(dest_left, dest_top + dest_height)};
736  CallFunc(GdipDrawImagePointsI)(pGraphics, bitmap, destinationPoints, 3);
737  CallFunc(GdipDisposeImage)(bitmap);
738}
739
740CGdiplusExt::CGdiplusExt() {}
741
742CGdiplusExt::~CGdiplusExt() {
743  FreeLibrary(m_GdiModule);
744  FreeLibrary(m_hModule);
745}
746
747void CGdiplusExt::Load() {
748  char buf[MAX_PATH];
749  GetSystemDirectoryA(buf, MAX_PATH);
750  ByteString dllpath = buf;
751  dllpath += "\\GDIPLUS.DLL";
752  m_hModule = LoadLibraryA(dllpath.c_str());
753  if (!m_hModule)
754    return;
755
756  m_Functions.resize(FX_ArraySize(g_GdipFuncNames));
757  for (size_t i = 0; i < FX_ArraySize(g_GdipFuncNames); ++i) {
758    m_Functions[i] = GetProcAddress(m_hModule, g_GdipFuncNames[i]);
759    if (!m_Functions[i]) {
760      m_hModule = nullptr;
761      return;
762    }
763  }
764
765  uintptr_t gdiplusToken;
766  GdiplusStartupInput gdiplusStartupInput;
767  ((FuncType_GdiplusStartup)m_Functions[FuncId_GdiplusStartup])(
768      &gdiplusToken, &gdiplusStartupInput, nullptr);
769  m_GdiModule = LoadLibraryA("GDI32.DLL");
770  if (!m_GdiModule)
771    return;
772
773  m_pGdiAddFontMemResourceEx =
774      reinterpret_cast<FuncType_GdiAddFontMemResourceEx>(
775          GetProcAddress(m_GdiModule, "AddFontMemResourceEx"));
776  m_pGdiRemoveFontMemResourseEx =
777      reinterpret_cast<FuncType_GdiRemoveFontMemResourceEx>(
778          GetProcAddress(m_GdiModule, "RemoveFontMemResourceEx"));
779}
780
781LPVOID CGdiplusExt::LoadMemFont(LPBYTE pData, uint32_t size) {
782  GpFontCollection* pCollection = nullptr;
783  CGdiplusExt& GdiplusExt =
784      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
785  CallFunc(GdipNewPrivateFontCollection)(&pCollection);
786  GpStatus status =
787      CallFunc(GdipPrivateAddMemoryFont)(pCollection, pData, size);
788  if (status == Gdiplus::Ok)
789    return pCollection;
790
791  CallFunc(GdipDeletePrivateFontCollection)(&pCollection);
792  return nullptr;
793}
794void CGdiplusExt::DeleteMemFont(LPVOID pCollection) {
795  CGdiplusExt& GdiplusExt =
796      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
797  CallFunc(GdipDeletePrivateFontCollection)((GpFontCollection**)&pCollection);
798}
799bool CGdiplusExt::GdipCreateBitmap(const RetainPtr<CFX_DIBitmap>& pBitmap,
800                                   void** bitmap) {
801  CGdiplusExt& GdiplusExt =
802      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
803  Gdiplus::PixelFormat format;
804  switch (pBitmap->GetFormat()) {
805    case FXDIB_Rgb:
806      format = PixelFormat24bppRGB;
807      break;
808    case FXDIB_Rgb32:
809      format = PixelFormat32bppRGB;
810      break;
811    case FXDIB_Argb:
812      format = PixelFormat32bppARGB;
813      break;
814    default:
815      return false;
816  }
817  GpStatus status = CallFunc(GdipCreateBitmapFromScan0)(
818      pBitmap->GetWidth(), pBitmap->GetHeight(), pBitmap->GetPitch(), format,
819      pBitmap->GetBuffer(), (GpBitmap**)bitmap);
820  return status == Gdiplus::Ok;
821}
822bool CGdiplusExt::GdipCreateFromImage(void* bitmap, void** graphics) {
823  CGdiplusExt& GdiplusExt =
824      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
825  GpStatus status = CallFunc(GdipGetImageGraphicsContext)(
826      (GpBitmap*)bitmap, (GpGraphics**)graphics);
827  return status == Gdiplus::Ok;
828}
829bool CGdiplusExt::GdipCreateFontFamilyFromName(const wchar_t* name,
830                                               void* pFontCollection,
831                                               void** pFamily) {
832  CGdiplusExt& GdiplusExt =
833      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
834  GpStatus status = CallFunc(GdipCreateFontFamilyFromName)(
835      (GDIPCONST WCHAR*)name, (GpFontCollection*)pFontCollection,
836      (GpFontFamily**)pFamily);
837  return status == Gdiplus::Ok;
838}
839bool CGdiplusExt::GdipCreateFontFromFamily(void* pFamily,
840                                           float font_size,
841                                           int fontstyle,
842                                           int flag,
843                                           void** pFont) {
844  CGdiplusExt& GdiplusExt =
845      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
846  GpStatus status =
847      CallFunc(GdipCreateFont)((GpFontFamily*)pFamily, font_size, fontstyle,
848                               Gdiplus::Unit(flag), (GpFont**)pFont);
849  return status == Gdiplus::Ok;
850}
851void CGdiplusExt::GdipGetFontSize(void* pFont, float* size) {
852  REAL get_size;
853  CGdiplusExt& GdiplusExt =
854      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
855  GpStatus status = CallFunc(GdipGetFontSize)((GpFont*)pFont, &get_size);
856  *size = (status == Gdiplus::Ok) ? static_cast<float>(get_size) : 0;
857}
858void CGdiplusExt::GdipSetTextRenderingHint(void* graphics, int mode) {
859  CGdiplusExt& GdiplusExt =
860      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
861  CallFunc(GdipSetTextRenderingHint)((GpGraphics*)graphics,
862                                     (TextRenderingHint)mode);
863}
864void CGdiplusExt::GdipSetPageUnit(void* graphics, uint32_t unit) {
865  CGdiplusExt& GdiplusExt =
866      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
867  CallFunc(GdipSetPageUnit)((GpGraphics*)graphics, (GpUnit)unit);
868}
869bool CGdiplusExt::GdipDrawDriverString(void* graphics,
870                                       unsigned short* text,
871                                       int length,
872                                       void* font,
873                                       void* brush,
874                                       void* positions,
875                                       int flags,
876                                       const void* matrix) {
877  CGdiplusExt& GdiplusExt =
878      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
879  GpStatus status = CallFunc(GdipDrawDriverString)(
880      (GpGraphics*)graphics, (GDIPCONST UINT16*)text, (INT)length,
881      (GDIPCONST GpFont*)font, (GDIPCONST GpBrush*)brush,
882      (GDIPCONST Gdiplus::PointF*)positions, (INT)flags,
883      (GDIPCONST GpMatrix*)matrix);
884  return status == Gdiplus::Ok;
885}
886void CGdiplusExt::GdipCreateBrush(uint32_t fill_argb, void** pBrush) {
887  CGdiplusExt& GdiplusExt =
888      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
889  CallFunc(GdipCreateSolidFill)((Gdiplus::ARGB)fill_argb,
890                                (GpSolidFill**)pBrush);
891}
892void CGdiplusExt::GdipDeleteBrush(void* pBrush) {
893  CGdiplusExt& GdiplusExt =
894      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
895  CallFunc(GdipDeleteBrush)((GpSolidFill*)pBrush);
896}
897void* CGdiplusExt::GdipCreateFontFromCollection(void* pFontCollection,
898                                                float font_size,
899                                                int fontstyle) {
900  CGdiplusExt& GdiplusExt =
901      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
902  int numFamilies = 0;
903  GpStatus status = CallFunc(GdipGetFontCollectionFamilyCount)(
904      (GpFontCollection*)pFontCollection, &numFamilies);
905  if (status != Gdiplus::Ok)
906    return nullptr;
907
908  GpFontFamily* family_list[1];
909  status = CallFunc(GdipGetFontCollectionFamilyList)(
910      (GpFontCollection*)pFontCollection, 1, family_list, &numFamilies);
911  if (status != Gdiplus::Ok)
912    return nullptr;
913
914  GpFont* pFont = nullptr;
915  status = CallFunc(GdipCreateFont)(family_list[0], font_size, fontstyle,
916                                    UnitPixel, &pFont);
917  if (status != Gdiplus::Ok)
918    return nullptr;
919
920  return pFont;
921}
922void CGdiplusExt::GdipCreateMatrix(float a,
923                                   float b,
924                                   float c,
925                                   float d,
926                                   float e,
927                                   float f,
928                                   void** matrix) {
929  CGdiplusExt& GdiplusExt =
930      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
931  CallFunc(GdipCreateMatrix2)(a, b, c, d, e, f, (GpMatrix**)matrix);
932}
933void CGdiplusExt::GdipDeleteMatrix(void* matrix) {
934  CGdiplusExt& GdiplusExt =
935      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
936  CallFunc(GdipDeleteMatrix)((GpMatrix*)matrix);
937}
938void CGdiplusExt::GdipDeleteFontFamily(void* pFamily) {
939  CGdiplusExt& GdiplusExt =
940      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
941  CallFunc(GdipDeleteFontFamily)((GpFontFamily*)pFamily);
942}
943void CGdiplusExt::GdipDeleteFont(void* pFont) {
944  CGdiplusExt& GdiplusExt =
945      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
946  CallFunc(GdipDeleteFont)((GpFont*)pFont);
947}
948void CGdiplusExt::GdipSetWorldTransform(void* graphics, void* pMatrix) {
949  CGdiplusExt& GdiplusExt =
950      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
951  CallFunc(GdipSetWorldTransform)((GpGraphics*)graphics, (GpMatrix*)pMatrix);
952}
953void CGdiplusExt::GdipDisposeImage(void* bitmap) {
954  CGdiplusExt& GdiplusExt =
955      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
956  CallFunc(GdipDisposeImage)((GpBitmap*)bitmap);
957}
958void CGdiplusExt::GdipDeleteGraphics(void* graphics) {
959  CGdiplusExt& GdiplusExt =
960      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
961  CallFunc(GdipDeleteGraphics)((GpGraphics*)graphics);
962}
963bool CGdiplusExt::StretchBitMask(HDC hDC,
964                                 BOOL bMonoDevice,
965                                 const RetainPtr<CFX_DIBitmap>& pBitmap,
966                                 int dest_left,
967                                 int dest_top,
968                                 int dest_width,
969                                 int dest_height,
970                                 uint32_t argb,
971                                 const FX_RECT* pClipRect,
972                                 int flags) {
973  ASSERT(pBitmap->GetBPP() == 1);
974  CGdiplusExt& GdiplusExt =
975      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
976  GpGraphics* pGraphics = nullptr;
977  CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
978  CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
979  if (flags & FXDIB_NOSMOOTH) {
980    CallFunc(GdipSetInterpolationMode)(pGraphics,
981                                       InterpolationModeNearestNeighbor);
982  } else {
983    CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeHighQuality);
984  }
985  OutputImageMask(pGraphics, bMonoDevice, pBitmap, dest_left, dest_top,
986                  dest_width, dest_height, argb, pClipRect);
987  CallFunc(GdipDeleteGraphics)(pGraphics);
988  return true;
989}
990bool CGdiplusExt::StretchDIBits(HDC hDC,
991                                const RetainPtr<CFX_DIBitmap>& pBitmap,
992                                int dest_left,
993                                int dest_top,
994                                int dest_width,
995                                int dest_height,
996                                const FX_RECT* pClipRect,
997                                int flags) {
998  GpGraphics* pGraphics;
999  CGdiplusExt& GdiplusExt =
1000      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
1001  CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
1002  CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
1003  if (flags & FXDIB_NOSMOOTH) {
1004    CallFunc(GdipSetInterpolationMode)(pGraphics,
1005                                       InterpolationModeNearestNeighbor);
1006  } else if (pBitmap->GetWidth() > abs(dest_width) / 2 ||
1007             pBitmap->GetHeight() > abs(dest_height) / 2) {
1008    CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeHighQuality);
1009  } else {
1010    CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeBilinear);
1011  }
1012  FX_RECT src_rect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight());
1013  OutputImage(pGraphics, pBitmap, &src_rect, dest_left, dest_top, dest_width,
1014              dest_height);
1015  CallFunc(GdipDeleteGraphics)(pGraphics);
1016  CallFunc(GdipDeleteGraphics)(pGraphics);
1017  return true;
1018}
1019static GpPen* _GdipCreatePen(const CFX_GraphStateData* pGraphState,
1020                             const CFX_Matrix* pMatrix,
1021                             DWORD argb,
1022                             bool bTextMode = false) {
1023  CGdiplusExt& GdiplusExt =
1024      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
1025  float width = pGraphState ? pGraphState->m_LineWidth : 1.0f;
1026  if (!bTextMode) {
1027    float unit = pMatrix
1028                     ? 1.0f / ((pMatrix->GetXUnit() + pMatrix->GetYUnit()) / 2)
1029                     : 1.0f;
1030    if (width < unit)
1031      width = unit;
1032  }
1033  GpPen* pPen = nullptr;
1034  CallFunc(GdipCreatePen1)((Gdiplus::ARGB)argb, width, UnitWorld, &pPen);
1035  LineCap lineCap = LineCapFlat;
1036  DashCap dashCap = DashCapFlat;
1037  bool bDashExtend = false;
1038  switch (pGraphState->m_LineCap) {
1039    case CFX_GraphStateData::LineCapButt:
1040      lineCap = LineCapFlat;
1041      break;
1042    case CFX_GraphStateData::LineCapRound:
1043      lineCap = LineCapRound;
1044      dashCap = DashCapRound;
1045      bDashExtend = true;
1046      break;
1047    case CFX_GraphStateData::LineCapSquare:
1048      lineCap = LineCapSquare;
1049      bDashExtend = true;
1050      break;
1051  }
1052  CallFunc(GdipSetPenLineCap197819)(pPen, lineCap, lineCap, dashCap);
1053  LineJoin lineJoin = LineJoinMiterClipped;
1054  switch (pGraphState->m_LineJoin) {
1055    case CFX_GraphStateData::LineJoinMiter:
1056      lineJoin = LineJoinMiterClipped;
1057      break;
1058    case CFX_GraphStateData::LineJoinRound:
1059      lineJoin = LineJoinRound;
1060      break;
1061    case CFX_GraphStateData::LineJoinBevel:
1062      lineJoin = LineJoinBevel;
1063      break;
1064  }
1065  CallFunc(GdipSetPenLineJoin)(pPen, lineJoin);
1066  if (pGraphState->m_DashCount) {
1067    float* pDashArray = FX_Alloc(
1068        float, pGraphState->m_DashCount + pGraphState->m_DashCount % 2);
1069    int nCount = 0;
1070    float on_leftover = 0, off_leftover = 0;
1071    for (int i = 0; i < pGraphState->m_DashCount; i += 2) {
1072      float on_phase = pGraphState->m_DashArray[i];
1073      float off_phase;
1074      if (i == pGraphState->m_DashCount - 1)
1075        off_phase = on_phase;
1076      else
1077        off_phase = pGraphState->m_DashArray[i + 1];
1078      on_phase /= width;
1079      off_phase /= width;
1080      if (on_phase + off_phase <= 0.00002f) {
1081        on_phase = 1.0f / 10;
1082        off_phase = 1.0f / 10;
1083      }
1084      if (bDashExtend) {
1085        if (off_phase < 1)
1086          off_phase = 0;
1087        else
1088          off_phase -= 1;
1089        on_phase += 1;
1090      }
1091      if (on_phase == 0 || off_phase == 0) {
1092        if (nCount == 0) {
1093          on_leftover += on_phase;
1094          off_leftover += off_phase;
1095        } else {
1096          pDashArray[nCount - 2] += on_phase;
1097          pDashArray[nCount - 1] += off_phase;
1098        }
1099      } else {
1100        pDashArray[nCount++] = on_phase + on_leftover;
1101        on_leftover = 0;
1102        pDashArray[nCount++] = off_phase + off_leftover;
1103        off_leftover = 0;
1104      }
1105    }
1106    CallFunc(GdipSetPenDashArray)(pPen, pDashArray, nCount);
1107    float phase = pGraphState->m_DashPhase;
1108    if (bDashExtend) {
1109      if (phase < 0.5f)
1110        phase = 0;
1111      else
1112        phase -= 0.5f;
1113    }
1114    CallFunc(GdipSetPenDashOffset)(pPen, phase);
1115    FX_Free(pDashArray);
1116    pDashArray = nullptr;
1117  }
1118  CallFunc(GdipSetPenMiterLimit)(pPen, pGraphState->m_MiterLimit);
1119  return pPen;
1120}
1121static bool IsSmallTriangle(Gdiplus::PointF* points,
1122                            const CFX_Matrix* pMatrix,
1123                            int& v1,
1124                            int& v2) {
1125  int pairs[] = {1, 2, 0, 2, 0, 1};
1126  for (int i = 0; i < 3; i++) {
1127    int pair1 = pairs[i * 2];
1128    int pair2 = pairs[i * 2 + 1];
1129
1130    CFX_PointF p1(points[pair1].X, points[pair1].Y);
1131    CFX_PointF p2(points[pair2].X, points[pair2].Y);
1132    if (pMatrix) {
1133      p1 = pMatrix->Transform(p1);
1134      p2 = pMatrix->Transform(p2);
1135    }
1136
1137    CFX_PointF diff = p1 - p2;
1138    float distance_square = (diff.x * diff.x) + (diff.y * diff.y);
1139    if (distance_square < (1.0f * 2 + 1.0f / 4)) {
1140      v1 = i;
1141      v2 = pair1;
1142      return true;
1143    }
1144  }
1145  return false;
1146}
1147bool CGdiplusExt::DrawPath(HDC hDC,
1148                           const CFX_PathData* pPathData,
1149                           const CFX_Matrix* pObject2Device,
1150                           const CFX_GraphStateData* pGraphState,
1151                           uint32_t fill_argb,
1152                           uint32_t stroke_argb,
1153                           int fill_mode) {
1154  auto& pPoints = pPathData->GetPoints();
1155  if (pPoints.empty())
1156    return true;
1157
1158  GpGraphics* pGraphics = nullptr;
1159  CGdiplusExt& GdiplusExt =
1160      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
1161  CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
1162  CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
1163  CallFunc(GdipSetPixelOffsetMode)(pGraphics, PixelOffsetModeHalf);
1164  GpMatrix* pMatrix = nullptr;
1165  if (pObject2Device) {
1166    CallFunc(GdipCreateMatrix2)(pObject2Device->a, pObject2Device->b,
1167                                pObject2Device->c, pObject2Device->d,
1168                                pObject2Device->e, pObject2Device->f, &pMatrix);
1169    CallFunc(GdipSetWorldTransform)(pGraphics, pMatrix);
1170  }
1171  Gdiplus::PointF* points = FX_Alloc(Gdiplus::PointF, pPoints.size());
1172  BYTE* types = FX_Alloc(BYTE, pPoints.size());
1173  int nSubPathes = 0;
1174  bool bSubClose = false;
1175  int pos_subclose = 0;
1176  bool bSmooth = false;
1177  int startpoint = 0;
1178  for (size_t i = 0; i < pPoints.size(); i++) {
1179    points[i].X = pPoints[i].m_Point.x;
1180    points[i].Y = pPoints[i].m_Point.y;
1181
1182    CFX_PointF pos = pPoints[i].m_Point;
1183    if (pObject2Device)
1184      pos = pObject2Device->Transform(pos);
1185
1186    if (pos.x > 50000 * 1.0f)
1187      points[i].X = 50000 * 1.0f;
1188    if (pos.x < -50000 * 1.0f)
1189      points[i].X = -50000 * 1.0f;
1190    if (pos.y > 50000 * 1.0f)
1191      points[i].Y = 50000 * 1.0f;
1192    if (pos.y < -50000 * 1.0f)
1193      points[i].Y = -50000 * 1.0f;
1194
1195    FXPT_TYPE point_type = pPoints[i].m_Type;
1196    if (point_type == FXPT_TYPE::MoveTo) {
1197      types[i] = PathPointTypeStart;
1198      nSubPathes++;
1199      bSubClose = false;
1200      startpoint = i;
1201    } else if (point_type == FXPT_TYPE::LineTo) {
1202      types[i] = PathPointTypeLine;
1203      if (pPoints[i - 1].IsTypeAndOpen(FXPT_TYPE::MoveTo) &&
1204          (i == pPoints.size() - 1 ||
1205           pPoints[i + 1].IsTypeAndOpen(FXPT_TYPE::MoveTo)) &&
1206          points[i].Y == points[i - 1].Y && points[i].X == points[i - 1].X) {
1207        points[i].X += 0.01f;
1208        continue;
1209      }
1210      if (!bSmooth && points[i].X != points[i - 1].X &&
1211          points[i].Y != points[i - 1].Y)
1212        bSmooth = true;
1213    } else if (point_type == FXPT_TYPE::BezierTo) {
1214      types[i] = PathPointTypeBezier;
1215      bSmooth = true;
1216    }
1217    if (pPoints[i].m_CloseFigure) {
1218      if (bSubClose)
1219        types[pos_subclose] &= ~PathPointTypeCloseSubpath;
1220      else
1221        bSubClose = true;
1222      pos_subclose = i;
1223      types[i] |= PathPointTypeCloseSubpath;
1224      if (!bSmooth && points[i].X != points[startpoint].X &&
1225          points[i].Y != points[startpoint].Y)
1226        bSmooth = true;
1227    }
1228  }
1229  if (fill_mode & FXFILL_NOPATHSMOOTH) {
1230    bSmooth = false;
1231    CallFunc(GdipSetSmoothingMode)(pGraphics, SmoothingModeNone);
1232  } else if (!(fill_mode & FXFILL_FULLCOVER)) {
1233    if (!bSmooth && (fill_mode & 3))
1234      bSmooth = true;
1235
1236    if (bSmooth || (pGraphState && pGraphState->m_LineWidth > 2))
1237      CallFunc(GdipSetSmoothingMode)(pGraphics, SmoothingModeAntiAlias);
1238  }
1239  int new_fill_mode = fill_mode & 3;
1240  if (pPoints.size() == 4 && !pGraphState) {
1241    int v1, v2;
1242    if (IsSmallTriangle(points, pObject2Device, v1, v2)) {
1243      GpPen* pPen = nullptr;
1244      CallFunc(GdipCreatePen1)(fill_argb, 1.0f, UnitPixel, &pPen);
1245      CallFunc(GdipDrawLineI)(
1246          pGraphics, pPen, FXSYS_round(points[v1].X), FXSYS_round(points[v1].Y),
1247          FXSYS_round(points[v2].X), FXSYS_round(points[v2].Y));
1248      CallFunc(GdipDeletePen)(pPen);
1249      return true;
1250    }
1251  }
1252  GpPath* pGpPath = nullptr;
1253  CallFunc(GdipCreatePath2)(points, types, pPoints.size(),
1254                            GdiFillType2Gdip(new_fill_mode), &pGpPath);
1255  if (!pGpPath) {
1256    if (pMatrix)
1257      CallFunc(GdipDeleteMatrix)(pMatrix);
1258
1259    FX_Free(points);
1260    FX_Free(types);
1261    CallFunc(GdipDeleteGraphics)(pGraphics);
1262    return false;
1263  }
1264  if (new_fill_mode) {
1265    GpBrush* pBrush = _GdipCreateBrush(fill_argb);
1266    CallFunc(GdipSetPathFillMode)(pGpPath, GdiFillType2Gdip(new_fill_mode));
1267    CallFunc(GdipFillPath)(pGraphics, pBrush, pGpPath);
1268    CallFunc(GdipDeleteBrush)(pBrush);
1269  }
1270  if (pGraphState && stroke_argb) {
1271    GpPen* pPen = _GdipCreatePen(pGraphState, pObject2Device, stroke_argb,
1272                                 !!(fill_mode & FX_STROKE_TEXT_MODE));
1273    if (nSubPathes == 1) {
1274      CallFunc(GdipDrawPath)(pGraphics, pPen, pGpPath);
1275    } else {
1276      int iStart = 0;
1277      for (size_t i = 0; i < pPoints.size(); i++) {
1278        if (i == pPoints.size() - 1 || types[i + 1] == PathPointTypeStart) {
1279          GpPath* pSubPath;
1280          CallFunc(GdipCreatePath2)(points + iStart, types + iStart,
1281                                    i - iStart + 1,
1282                                    GdiFillType2Gdip(new_fill_mode), &pSubPath);
1283          iStart = i + 1;
1284          CallFunc(GdipDrawPath)(pGraphics, pPen, pSubPath);
1285          CallFunc(GdipDeletePath)(pSubPath);
1286        }
1287      }
1288    }
1289    CallFunc(GdipDeletePen)(pPen);
1290  }
1291  if (pMatrix)
1292    CallFunc(GdipDeleteMatrix)(pMatrix);
1293  FX_Free(points);
1294  FX_Free(types);
1295  CallFunc(GdipDeletePath)(pGpPath);
1296  CallFunc(GdipDeleteGraphics)(pGraphics);
1297  return true;
1298}
1299
1300class GpStream final : public IStream {
1301 public:
1302  GpStream() : m_RefCount(1), m_ReadPos(0) {}
1303
1304  // IUnknown
1305  HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
1306                                           void** ppvObject) override {
1307    if (iid == __uuidof(IUnknown) || iid == __uuidof(IStream) ||
1308        iid == __uuidof(ISequentialStream)) {
1309      *ppvObject = static_cast<IStream*>(this);
1310      AddRef();
1311      return S_OK;
1312    }
1313    return E_NOINTERFACE;
1314  }
1315  ULONG STDMETHODCALLTYPE AddRef() override {
1316    return (ULONG)InterlockedIncrement(&m_RefCount);
1317  }
1318  ULONG STDMETHODCALLTYPE Release() override {
1319    ULONG res = (ULONG)InterlockedDecrement(&m_RefCount);
1320    if (res == 0) {
1321      delete this;
1322    }
1323    return res;
1324  }
1325
1326  // ISequentialStream
1327  HRESULT STDMETHODCALLTYPE Read(void* output,
1328                                 ULONG cb,
1329                                 ULONG* pcbRead) override {
1330    if (pcbRead)
1331      *pcbRead = 0;
1332
1333    if (m_ReadPos >= m_InterStream.tellp())
1334      return HRESULT_FROM_WIN32(ERROR_END_OF_MEDIA);
1335
1336    size_t bytes_left = m_InterStream.tellp() - m_ReadPos;
1337    size_t bytes_out =
1338        std::min(pdfium::base::checked_cast<size_t>(cb), bytes_left);
1339    memcpy(output, m_InterStream.str().c_str() + m_ReadPos, bytes_out);
1340    m_ReadPos += bytes_out;
1341    if (pcbRead)
1342      *pcbRead = (ULONG)bytes_out;
1343
1344    return S_OK;
1345  }
1346  HRESULT STDMETHODCALLTYPE Write(const void* input,
1347                                  ULONG cb,
1348                                  ULONG* pcbWritten) override {
1349    if (cb <= 0) {
1350      if (pcbWritten)
1351        *pcbWritten = 0;
1352      return S_OK;
1353    }
1354    m_InterStream.write(reinterpret_cast<const char*>(input), cb);
1355    if (pcbWritten)
1356      *pcbWritten = cb;
1357    return S_OK;
1358  }
1359
1360  // IStream
1361  HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER) override {
1362    return E_NOTIMPL;
1363  }
1364  HRESULT STDMETHODCALLTYPE CopyTo(IStream*,
1365                                   ULARGE_INTEGER,
1366                                   ULARGE_INTEGER*,
1367                                   ULARGE_INTEGER*) override {
1368    return E_NOTIMPL;
1369  }
1370  HRESULT STDMETHODCALLTYPE Commit(DWORD) override { return E_NOTIMPL; }
1371  HRESULT STDMETHODCALLTYPE Revert() override { return E_NOTIMPL; }
1372  HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER,
1373                                       ULARGE_INTEGER,
1374                                       DWORD) override {
1375    return E_NOTIMPL;
1376  }
1377  HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER,
1378                                         ULARGE_INTEGER,
1379                                         DWORD) override {
1380    return E_NOTIMPL;
1381  }
1382  HRESULT STDMETHODCALLTYPE Clone(IStream** stream) override {
1383    return E_NOTIMPL;
1384  }
1385  HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove,
1386                                 DWORD dwOrigin,
1387                                 ULARGE_INTEGER* lpNewFilePointer) override {
1388    std::streamoff start;
1389    std::streamoff new_read_position;
1390    switch (dwOrigin) {
1391      case STREAM_SEEK_SET:
1392        start = 0;
1393        break;
1394      case STREAM_SEEK_CUR:
1395        start = m_ReadPos;
1396        break;
1397      case STREAM_SEEK_END:
1398        start = m_InterStream.tellp();
1399        break;
1400      default:
1401        return STG_E_INVALIDFUNCTION;
1402    }
1403    new_read_position = start + liDistanceToMove.QuadPart;
1404    if (new_read_position > m_InterStream.tellp())
1405      return STG_E_SEEKERROR;
1406
1407    m_ReadPos = new_read_position;
1408    if (lpNewFilePointer)
1409      lpNewFilePointer->QuadPart = m_ReadPos;
1410
1411    return S_OK;
1412  }
1413  HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg,
1414                                 DWORD grfStatFlag) override {
1415    if (!pStatstg)
1416      return STG_E_INVALIDFUNCTION;
1417
1418    ZeroMemory(pStatstg, sizeof(STATSTG));
1419    pStatstg->cbSize.QuadPart = m_InterStream.tellp();
1420    return S_OK;
1421  }
1422
1423 private:
1424  LONG m_RefCount;
1425  std::streamoff m_ReadPos;
1426  std::ostringstream m_InterStream;
1427};
1428
1429typedef struct {
1430  BITMAPINFO* pbmi;
1431  int Stride;
1432  LPBYTE pScan0;
1433  GpBitmap* pBitmap;
1434  Gdiplus::BitmapData* pBitmapData;
1435  GpStream* pStream;
1436} PREVIEW3_DIBITMAP;
1437
1438static PREVIEW3_DIBITMAP* LoadDIBitmap(WINDIB_Open_Args_ args) {
1439  GpBitmap* pBitmap;
1440  GpStream* pStream = nullptr;
1441  CGdiplusExt& GdiplusExt =
1442      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
1443  Gdiplus::Status status = Gdiplus::Ok;
1444  if (args.flags == WINDIB_OPEN_PATHNAME) {
1445    status = CallFunc(GdipCreateBitmapFromFileICM)((wchar_t*)args.path_name,
1446                                                   &pBitmap);
1447  } else {
1448    if (args.memory_size == 0 || !args.memory_base)
1449      return nullptr;
1450
1451    pStream = new GpStream;
1452    pStream->Write(args.memory_base, (ULONG)args.memory_size, nullptr);
1453    status = CallFunc(GdipCreateBitmapFromStreamICM)(pStream, &pBitmap);
1454  }
1455  if (status != Gdiplus::Ok) {
1456    if (pStream)
1457      pStream->Release();
1458
1459    return nullptr;
1460  }
1461  UINT height, width;
1462  CallFunc(GdipGetImageHeight)(pBitmap, &height);
1463  CallFunc(GdipGetImageWidth)(pBitmap, &width);
1464  Gdiplus::PixelFormat pixel_format;
1465  CallFunc(GdipGetImagePixelFormat)(pBitmap, &pixel_format);
1466  int info_size = sizeof(BITMAPINFOHEADER);
1467  int bpp = 24;
1468  int dest_pixel_format = PixelFormat24bppRGB;
1469  if (pixel_format == PixelFormat1bppIndexed) {
1470    info_size += 8;
1471    bpp = 1;
1472    dest_pixel_format = PixelFormat1bppIndexed;
1473  } else if (pixel_format == PixelFormat8bppIndexed) {
1474    info_size += 1024;
1475    bpp = 8;
1476    dest_pixel_format = PixelFormat8bppIndexed;
1477  } else if (pixel_format == PixelFormat32bppARGB) {
1478    bpp = 32;
1479    dest_pixel_format = PixelFormat32bppARGB;
1480  }
1481  LPBYTE buf = FX_Alloc(BYTE, info_size);
1482  BITMAPINFOHEADER* pbmih = (BITMAPINFOHEADER*)buf;
1483  pbmih->biBitCount = bpp;
1484  pbmih->biCompression = BI_RGB;
1485  pbmih->biHeight = -(int)height;
1486  pbmih->biPlanes = 1;
1487  pbmih->biWidth = width;
1488  Gdiplus::Rect rect(0, 0, width, height);
1489  Gdiplus::BitmapData* pBitmapData = FX_Alloc(Gdiplus::BitmapData, 1);
1490  CallFunc(GdipBitmapLockBits)(pBitmap, &rect, ImageLockModeRead,
1491                               dest_pixel_format, pBitmapData);
1492  if (pixel_format == PixelFormat1bppIndexed ||
1493      pixel_format == PixelFormat8bppIndexed) {
1494    DWORD* ppal = (DWORD*)(buf + sizeof(BITMAPINFOHEADER));
1495    struct {
1496      UINT flags;
1497      UINT Count;
1498      DWORD Entries[256];
1499    } pal;
1500    int size = 0;
1501    CallFunc(GdipGetImagePaletteSize)(pBitmap, &size);
1502    CallFunc(GdipGetImagePalette)(pBitmap, (Gdiplus::ColorPalette*)&pal, size);
1503    int entries = pixel_format == PixelFormat1bppIndexed ? 2 : 256;
1504    for (int i = 0; i < entries; i++) {
1505      ppal[i] = pal.Entries[i] & 0x00ffffff;
1506    }
1507  }
1508  PREVIEW3_DIBITMAP* pInfo = FX_Alloc(PREVIEW3_DIBITMAP, 1);
1509  pInfo->pbmi = (BITMAPINFO*)buf;
1510  pInfo->pScan0 = (LPBYTE)pBitmapData->Scan0;
1511  pInfo->Stride = pBitmapData->Stride;
1512  pInfo->pBitmap = pBitmap;
1513  pInfo->pBitmapData = pBitmapData;
1514  pInfo->pStream = pStream;
1515  return pInfo;
1516}
1517
1518static void FreeDIBitmap(PREVIEW3_DIBITMAP* pInfo) {
1519  CGdiplusExt& GdiplusExt =
1520      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
1521  CallFunc(GdipBitmapUnlockBits)(pInfo->pBitmap, pInfo->pBitmapData);
1522  CallFunc(GdipDisposeImage)(pInfo->pBitmap);
1523  FX_Free(pInfo->pBitmapData);
1524  FX_Free((LPBYTE)pInfo->pbmi);
1525  if (pInfo->pStream)
1526    pInfo->pStream->Release();
1527  FX_Free(pInfo);
1528}
1529
1530// TODO(tsepez): Really? Really? Move to header.
1531RetainPtr<CFX_DIBitmap> _FX_WindowsDIB_LoadFromBuf(BITMAPINFO* pbmi,
1532                                                   LPVOID pData,
1533                                                   bool bAlpha);
1534
1535RetainPtr<CFX_DIBitmap> CGdiplusExt::LoadDIBitmap(WINDIB_Open_Args_ args) {
1536  PREVIEW3_DIBITMAP* pInfo = ::LoadDIBitmap(args);
1537  if (!pInfo)
1538    return nullptr;
1539
1540  int height = abs(pInfo->pbmi->bmiHeader.biHeight);
1541  int width = pInfo->pbmi->bmiHeader.biWidth;
1542  int dest_pitch = (width * pInfo->pbmi->bmiHeader.biBitCount + 31) / 32 * 4;
1543  LPBYTE pData = FX_Alloc2D(BYTE, dest_pitch, height);
1544  if (dest_pitch == pInfo->Stride) {
1545    memcpy(pData, pInfo->pScan0, dest_pitch * height);
1546  } else {
1547    for (int i = 0; i < height; i++) {
1548      memcpy(pData + dest_pitch * i, pInfo->pScan0 + pInfo->Stride * i,
1549             dest_pitch);
1550    }
1551  }
1552  RetainPtr<CFX_DIBitmap> pDIBitmap = _FX_WindowsDIB_LoadFromBuf(
1553      pInfo->pbmi, pData, pInfo->pbmi->bmiHeader.biBitCount == 32);
1554  FX_Free(pData);
1555  FreeDIBitmap(pInfo);
1556  return pDIBitmap;
1557}
1558