Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(152)

Side by Side Diff: core/src/fxge/win32/fx_win32_gdipext.cpp

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

Powered by Google App Engine
This is Rietveld 408576698