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 <memory>
8
9#include "fx_path_generator.h"
10#include "pre.h"
11
12class CAGG_Graphics {
13 public:
14  CAGG_Graphics();
15  FX_ERR Create(CFX_Graphics* owner,
16                int32_t width,
17                int32_t height,
18                FXDIB_Format format);
19  virtual ~CAGG_Graphics();
20
21 private:
22  CFX_Graphics* _owner;
23};
24CFX_Graphics::CFX_Graphics() {
25  _type = FX_CONTEXT_None;
26  _info._graphState.SetDashCount(0);
27  _info._isAntialiasing = TRUE;
28  _info._strokeAlignment = FX_STROKEALIGNMENT_Center;
29  _info._CTM.SetIdentity();
30  _info._isActOnDash = FALSE;
31  _info._strokeColor = NULL;
32  _info._fillColor = NULL;
33  _info._font = NULL;
34  _info._fontSize = 40.0;
35  _info._fontHScale = 1.0;
36  _info._fontSpacing = 0.0;
37  _renderDevice = NULL;
38  _aggGraphics = NULL;
39}
40FX_ERR CFX_Graphics::Create(CFX_RenderDevice* renderDevice,
41                            FX_BOOL isAntialiasing) {
42  _FX_RETURN_VALUE_IF_FAIL(renderDevice, FX_ERR_Parameter_Invalid);
43  if (_type != FX_CONTEXT_None) {
44    return FX_ERR_Property_Invalid;
45  }
46  _type = FX_CONTEXT_Device;
47  _info._isAntialiasing = isAntialiasing;
48  _renderDevice = renderDevice;
49  if (_renderDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SOFT_CLIP) {
50    return FX_ERR_Succeeded;
51  }
52  return FX_ERR_Indefinite;
53}
54FX_ERR CFX_Graphics::Create(int32_t width,
55                            int32_t height,
56                            FXDIB_Format format,
57                            FX_BOOL isNative,
58                            FX_BOOL isAntialiasing) {
59  if (_type != FX_CONTEXT_None) {
60    return FX_ERR_Property_Invalid;
61  }
62  _type = FX_CONTEXT_Device;
63  _info._isAntialiasing = isAntialiasing;
64  {
65    _aggGraphics = new CAGG_Graphics;
66    return _aggGraphics->Create(this, width, height, format);
67  }
68}
69CFX_Graphics::~CFX_Graphics() {
70  if (_aggGraphics) {
71    delete _aggGraphics;
72    _aggGraphics = NULL;
73  }
74  _renderDevice = NULL;
75  _info._graphState.SetDashCount(0);
76  _type = FX_CONTEXT_None;
77}
78FX_ERR CFX_Graphics::GetDeviceCap(const int32_t capID, FX_DeviceCap& capVal) {
79  switch (_type) {
80    case FX_CONTEXT_Device: {
81      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
82      capVal = _renderDevice->GetDeviceCaps(capID);
83      return FX_ERR_Succeeded;
84    }
85    default: { return FX_ERR_Property_Invalid; }
86  }
87}
88FX_ERR CFX_Graphics::IsPrinterDevice(FX_BOOL& isPrinter) {
89  switch (_type) {
90    case FX_CONTEXT_Device: {
91      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
92      int32_t deviceClass = _renderDevice->GetDeviceClass();
93      if (deviceClass == FXDC_PRINTER) {
94        isPrinter = TRUE;
95      } else {
96        isPrinter = FALSE;
97      }
98      return FX_ERR_Succeeded;
99    }
100    default: { return FX_ERR_Property_Invalid; }
101  }
102}
103FX_ERR CFX_Graphics::EnableAntialiasing(FX_BOOL isAntialiasing) {
104  switch (_type) {
105    case FX_CONTEXT_Device: {
106      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
107      _info._isAntialiasing = isAntialiasing;
108      return FX_ERR_Succeeded;
109    }
110    default: { return FX_ERR_Property_Invalid; }
111  }
112}
113FX_ERR CFX_Graphics::SaveGraphState() {
114  switch (_type) {
115    case FX_CONTEXT_Device: {
116      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
117      _renderDevice->SaveState();
118      TInfo* info = new TInfo;
119      info->_graphState.Copy(_info._graphState);
120      info->_isAntialiasing = _info._isAntialiasing;
121      info->_strokeAlignment = _info._strokeAlignment;
122      info->_CTM = _info._CTM;
123      info->_isActOnDash = _info._isActOnDash;
124      info->_strokeColor = _info._strokeColor;
125      info->_fillColor = _info._fillColor;
126      info->_font = _info._font;
127      info->_fontSize = _info._fontSize;
128      info->_fontHScale = _info._fontHScale;
129      info->_fontSpacing = _info._fontSpacing;
130      _infoStack.Add(info);
131      return FX_ERR_Succeeded;
132    }
133    default: { return FX_ERR_Property_Invalid; }
134  }
135}
136FX_ERR CFX_Graphics::RestoreGraphState() {
137  switch (_type) {
138    case FX_CONTEXT_Device: {
139      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
140      _renderDevice->RestoreState();
141      int32_t size = _infoStack.GetSize();
142      if (size <= 0) {
143        return FX_ERR_Intermediate_Value_Invalid;
144      }
145      int32_t topIndex = size - 1;
146      TInfo* info = (TInfo*)_infoStack.GetAt(topIndex);
147      _FX_RETURN_VALUE_IF_FAIL(info, FX_ERR_Intermediate_Value_Invalid);
148      _info._graphState.Copy(info->_graphState);
149      _info._isAntialiasing = info->_isAntialiasing;
150      _info._strokeAlignment = info->_strokeAlignment;
151      _info._CTM = info->_CTM;
152      _info._isActOnDash = info->_isActOnDash;
153      _info._strokeColor = info->_strokeColor;
154      _info._fillColor = info->_fillColor;
155      _info._font = info->_font;
156      _info._fontSize = info->_fontSize;
157      _info._fontHScale = info->_fontHScale;
158      _info._fontSpacing = info->_fontSpacing;
159      delete info;
160      info = NULL;
161      _infoStack.RemoveAt(topIndex);
162      return FX_ERR_Succeeded;
163    }
164    default: { return FX_ERR_Property_Invalid; }
165  }
166}
167FX_ERR CFX_Graphics::GetLineCap(CFX_GraphStateData::LineCap& lineCap) {
168  switch (_type) {
169    case FX_CONTEXT_Device: {
170      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
171      lineCap = _info._graphState.m_LineCap;
172      return FX_ERR_Succeeded;
173    }
174    default: { return FX_ERR_Property_Invalid; }
175  }
176}
177FX_ERR CFX_Graphics::SetLineCap(CFX_GraphStateData::LineCap lineCap) {
178  switch (_type) {
179    case FX_CONTEXT_Device: {
180      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
181      _info._graphState.m_LineCap = lineCap;
182      return FX_ERR_Succeeded;
183    }
184    default: { return FX_ERR_Property_Invalid; }
185  }
186}
187FX_ERR CFX_Graphics::GetDashCount(int32_t& dashCount) {
188  switch (_type) {
189    case FX_CONTEXT_Device: {
190      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
191      dashCount = _info._graphState.m_DashCount;
192      return FX_ERR_Succeeded;
193    }
194    default: { return FX_ERR_Property_Invalid; }
195  }
196}
197FX_ERR CFX_Graphics::GetLineDash(FX_FLOAT& dashPhase, FX_FLOAT* dashArray) {
198  _FX_RETURN_VALUE_IF_FAIL(dashArray, FX_ERR_Parameter_Invalid);
199  switch (_type) {
200    case FX_CONTEXT_Device: {
201      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
202      dashPhase = _info._graphState.m_DashPhase;
203      FXSYS_memcpy(dashArray, _info._graphState.m_DashArray,
204                   _info._graphState.m_DashCount * sizeof(FX_FLOAT));
205      return FX_ERR_Succeeded;
206    }
207    default: { return FX_ERR_Property_Invalid; }
208  }
209}
210FX_ERR CFX_Graphics::SetLineDash(FX_FLOAT dashPhase,
211                                 FX_FLOAT* dashArray,
212                                 int32_t dashCount) {
213  if (dashCount > 0 && !dashArray) {
214    return FX_ERR_Parameter_Invalid;
215  }
216  dashCount = dashCount < 0 ? 0 : dashCount;
217  switch (_type) {
218    case FX_CONTEXT_Device: {
219      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
220      FX_FLOAT scale = 1.0;
221      if (_info._isActOnDash) {
222        scale = _info._graphState.m_LineWidth;
223      }
224      _info._graphState.m_DashPhase = dashPhase;
225      _info._graphState.SetDashCount(dashCount);
226      for (int32_t i = 0; i < dashCount; i++) {
227        _info._graphState.m_DashArray[i] = dashArray[i] * scale;
228      }
229      return FX_ERR_Succeeded;
230    }
231    default: { return FX_ERR_Property_Invalid; }
232  }
233}
234FX_ERR CFX_Graphics::SetLineDash(FX_DashStyle dashStyle) {
235  switch (_type) {
236    case FX_CONTEXT_Device: {
237      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
238      return RenderDeviceSetLineDash(dashStyle);
239    }
240    default: { return FX_ERR_Property_Invalid; }
241  }
242}
243FX_ERR CFX_Graphics::GetLineJoin(CFX_GraphStateData::LineJoin& lineJoin) {
244  switch (_type) {
245    case FX_CONTEXT_Device: {
246      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
247      lineJoin = _info._graphState.m_LineJoin;
248      return FX_ERR_Succeeded;
249    }
250    default: { return FX_ERR_Property_Invalid; }
251  }
252}
253FX_ERR CFX_Graphics::SetLineJoin(CFX_GraphStateData::LineJoin lineJoin) {
254  switch (_type) {
255    case FX_CONTEXT_Device: {
256      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
257      _info._graphState.m_LineJoin = lineJoin;
258      return FX_ERR_Succeeded;
259    }
260    default: { return FX_ERR_Property_Invalid; }
261  }
262}
263FX_ERR CFX_Graphics::GetMiterLimit(FX_FLOAT& miterLimit) {
264  switch (_type) {
265    case FX_CONTEXT_Device: {
266      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
267      miterLimit = _info._graphState.m_MiterLimit;
268      return FX_ERR_Succeeded;
269    }
270    default: { return FX_ERR_Property_Invalid; }
271  }
272}
273FX_ERR CFX_Graphics::SetMiterLimit(FX_FLOAT miterLimit) {
274  switch (_type) {
275    case FX_CONTEXT_Device: {
276      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
277      _info._graphState.m_MiterLimit = miterLimit;
278      return FX_ERR_Succeeded;
279    }
280    default: { return FX_ERR_Property_Invalid; }
281  }
282}
283FX_ERR CFX_Graphics::GetLineWidth(FX_FLOAT& lineWidth) {
284  switch (_type) {
285    case FX_CONTEXT_Device: {
286      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
287      lineWidth = _info._graphState.m_LineWidth;
288      return FX_ERR_Succeeded;
289    }
290    default: { return FX_ERR_Property_Invalid; }
291  }
292}
293FX_ERR CFX_Graphics::SetLineWidth(FX_FLOAT lineWidth, FX_BOOL isActOnDash) {
294  switch (_type) {
295    case FX_CONTEXT_Device: {
296      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
297      _info._graphState.m_LineWidth = lineWidth;
298      _info._isActOnDash = isActOnDash;
299      return FX_ERR_Succeeded;
300    }
301    default: { return FX_ERR_Property_Invalid; }
302  }
303}
304FX_ERR CFX_Graphics::GetStrokeAlignment(FX_StrokeAlignment& strokeAlignment) {
305  switch (_type) {
306    case FX_CONTEXT_Device: {
307      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
308      strokeAlignment = _info._strokeAlignment;
309      return FX_ERR_Succeeded;
310    }
311    default: { return FX_ERR_Property_Invalid; }
312  }
313}
314FX_ERR CFX_Graphics::SetStrokeAlignment(FX_StrokeAlignment strokeAlignment) {
315  switch (_type) {
316    case FX_CONTEXT_Device: {
317      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
318      _info._strokeAlignment = strokeAlignment;
319      return FX_ERR_Succeeded;
320    }
321    default: { return FX_ERR_Property_Invalid; }
322  }
323}
324FX_ERR CFX_Graphics::SetStrokeColor(CFX_Color* color) {
325  _FX_RETURN_VALUE_IF_FAIL(color, FX_ERR_Parameter_Invalid);
326  switch (_type) {
327    case FX_CONTEXT_Device: {
328      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
329      _info._strokeColor = color;
330      return FX_ERR_Succeeded;
331    }
332    default: { return FX_ERR_Property_Invalid; }
333  }
334}
335FX_ERR CFX_Graphics::SetFillColor(CFX_Color* color) {
336  _FX_RETURN_VALUE_IF_FAIL(color, FX_ERR_Parameter_Invalid);
337  switch (_type) {
338    case FX_CONTEXT_Device: {
339      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
340      _info._fillColor = color;
341      return FX_ERR_Succeeded;
342    }
343    default: { return FX_ERR_Property_Invalid; }
344  }
345}
346FX_ERR CFX_Graphics::StrokePath(CFX_Path* path, CFX_Matrix* matrix) {
347  _FX_RETURN_VALUE_IF_FAIL(path, FX_ERR_Parameter_Invalid);
348  switch (_type) {
349    case FX_CONTEXT_Device: {
350      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
351      return RenderDeviceStrokePath(path, matrix);
352    }
353    default: { return FX_ERR_Property_Invalid; }
354  }
355}
356FX_ERR CFX_Graphics::FillPath(CFX_Path* path,
357                              FX_FillMode fillMode,
358                              CFX_Matrix* matrix) {
359  _FX_RETURN_VALUE_IF_FAIL(path, FX_ERR_Parameter_Invalid);
360  switch (_type) {
361    case FX_CONTEXT_Device: {
362      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
363      return RenderDeviceFillPath(path, fillMode, matrix);
364    }
365    default: { return FX_ERR_Property_Invalid; }
366  }
367}
368FX_ERR CFX_Graphics::ClipPath(CFX_Path* path,
369                              FX_FillMode fillMode,
370                              CFX_Matrix* matrix) {
371  _FX_RETURN_VALUE_IF_FAIL(path, FX_ERR_Parameter_Invalid);
372  switch (_type) {
373    case FX_CONTEXT_Device: {
374      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
375      FX_BOOL result = _renderDevice->SetClip_PathFill(
376          path->GetPathData(), (CFX_Matrix*)matrix, fillMode);
377      _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Indefinite);
378      return FX_ERR_Succeeded;
379    }
380    default: { return FX_ERR_Property_Invalid; }
381  }
382}
383FX_ERR CFX_Graphics::DrawImage(CFX_DIBSource* source,
384                               const CFX_PointF& point,
385                               CFX_Matrix* matrix) {
386  _FX_RETURN_VALUE_IF_FAIL(source, FX_ERR_Parameter_Invalid);
387  switch (_type) {
388    case FX_CONTEXT_Device: {
389      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
390      return RenderDeviceDrawImage(source, point, matrix);
391    }
392    default: { return FX_ERR_Property_Invalid; }
393  }
394}
395FX_ERR CFX_Graphics::StretchImage(CFX_DIBSource* source,
396                                  const CFX_RectF& rect,
397                                  CFX_Matrix* matrix) {
398  _FX_RETURN_VALUE_IF_FAIL(source, FX_ERR_Parameter_Invalid);
399  switch (_type) {
400    case FX_CONTEXT_Device: {
401      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
402      return RenderDeviceStretchImage(source, rect, matrix);
403    }
404    default: { return FX_ERR_Property_Invalid; }
405  }
406}
407FX_ERR CFX_Graphics::ConcatMatrix(const CFX_Matrix* matrix) {
408  _FX_RETURN_VALUE_IF_FAIL(matrix, FX_ERR_Parameter_Invalid);
409  switch (_type) {
410    case FX_CONTEXT_Device: {
411      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
412      _info._CTM.Concat(*matrix);
413      return FX_ERR_Succeeded;
414    }
415    default: { return FX_ERR_Property_Invalid; }
416  }
417}
418CFX_Matrix* CFX_Graphics::GetMatrix() {
419  switch (_type) {
420    case FX_CONTEXT_Device: {
421      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, NULL);
422      return &_info._CTM;
423    }
424    default: { return NULL; }
425  }
426}
427FX_ERR CFX_Graphics::GetClipRect(CFX_RectF& rect) {
428  switch (_type) {
429    case FX_CONTEXT_Device: {
430      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
431      FX_RECT r = _renderDevice->GetClipBox();
432      rect.left = (FX_FLOAT)r.left;
433      rect.top = (FX_FLOAT)r.top;
434      rect.width = (FX_FLOAT)r.Width();
435      rect.height = (FX_FLOAT)r.Height();
436      return FX_ERR_Succeeded;
437    }
438    default: { return FX_ERR_Property_Invalid; }
439  }
440}
441FX_ERR CFX_Graphics::SetClipRect(const CFX_RectF& rect) {
442  switch (_type) {
443    case FX_CONTEXT_Device: {
444      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
445      FX_RECT r(FXSYS_round(rect.left), FXSYS_round(rect.top),
446                FXSYS_round(rect.right()), FXSYS_round(rect.bottom()));
447      FX_BOOL result = _renderDevice->SetClip_Rect(&r);
448      _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Method_Not_Supported);
449      return FX_ERR_Succeeded;
450    }
451    default: { return FX_ERR_Property_Invalid; }
452  }
453}
454FX_ERR CFX_Graphics::ClearClip() {
455  switch (_type) {
456    case FX_CONTEXT_Device: {
457      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
458      FX_BOOL result = FX_ERR_Succeeded;
459      _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Method_Not_Supported);
460      return FX_ERR_Succeeded;
461    }
462    default: { return FX_ERR_Property_Invalid; }
463  }
464}
465FX_ERR CFX_Graphics::SetFont(CFX_Font* font) {
466  _FX_RETURN_VALUE_IF_FAIL(font, FX_ERR_Parameter_Invalid);
467  switch (_type) {
468    case FX_CONTEXT_Device: {
469      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
470      _info._font = font;
471      return FX_ERR_Succeeded;
472    }
473    default: { return FX_ERR_Property_Invalid; }
474  }
475}
476FX_ERR CFX_Graphics::SetFontSize(const FX_FLOAT size) {
477  FX_FLOAT fontSize = size <= 0 ? 1.0f : size;
478  switch (_type) {
479    case FX_CONTEXT_Device: {
480      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
481      _info._fontSize = fontSize;
482      return FX_ERR_Succeeded;
483    }
484    default: { return FX_ERR_Property_Invalid; }
485  }
486}
487FX_ERR CFX_Graphics::SetFontHScale(const FX_FLOAT scale) {
488  FX_FLOAT fontHScale = scale <= 0 ? 1.0f : scale;
489  switch (_type) {
490    case FX_CONTEXT_Device: {
491      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
492      _info._fontHScale = fontHScale;
493      return FX_ERR_Succeeded;
494    }
495    default: { return FX_ERR_Property_Invalid; }
496  }
497}
498FX_ERR CFX_Graphics::SetCharSpacing(const FX_FLOAT spacing) {
499  FX_FLOAT fontSpacing = spacing < 0 ? 0 : spacing;
500  switch (_type) {
501    case FX_CONTEXT_Device: {
502      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
503      _info._fontSpacing = fontSpacing;
504      return FX_ERR_Succeeded;
505    }
506    default: { return FX_ERR_Property_Invalid; }
507  }
508}
509FX_ERR CFX_Graphics::SetTextDrawingMode(const int32_t mode) {
510  switch (_type) {
511    case FX_CONTEXT_Device: {
512      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
513      return FX_ERR_Succeeded;
514    }
515    default: { return FX_ERR_Property_Invalid; }
516  }
517}
518FX_ERR CFX_Graphics::ShowText(const CFX_PointF& point,
519                              const CFX_WideString& text,
520                              CFX_Matrix* matrix) {
521  switch (_type) {
522    case FX_CONTEXT_Device: {
523      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
524      return RenderDeviceShowText(point, text, matrix);
525    }
526    default: { return FX_ERR_Property_Invalid; }
527  }
528}
529FX_ERR CFX_Graphics::CalcTextRect(CFX_RectF& rect,
530                                  const CFX_WideString& text,
531                                  FX_BOOL isMultiline,
532                                  CFX_Matrix* matrix) {
533  switch (_type) {
534    case FX_CONTEXT_Device: {
535      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
536      int32_t length = text.GetLength();
537      FX_DWORD* charCodes = FX_Alloc(FX_DWORD, length);
538      FXTEXT_CHARPOS* charPos = FX_Alloc(FXTEXT_CHARPOS, length);
539      CalcTextInfo(text, charCodes, charPos, rect);
540      FX_Free(charPos);
541      FX_Free(charCodes);
542      return FX_ERR_Succeeded;
543    }
544    default: { return FX_ERR_Property_Invalid; }
545  }
546}
547FX_ERR CFX_Graphics::Transfer(CFX_Graphics* graphics,
548                              const CFX_Matrix* matrix) {
549  _FX_RETURN_VALUE_IF_FAIL(graphics, FX_ERR_Parameter_Invalid);
550  CFX_Matrix m;
551  m.Set(_info._CTM.a, _info._CTM.b, _info._CTM.c, _info._CTM.d, _info._CTM.e,
552        _info._CTM.f);
553  if (matrix) {
554    m.Concat(*matrix);
555  }
556  switch (_type) {
557    case FX_CONTEXT_Device: {
558      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
559      {
560        _FX_RETURN_VALUE_IF_FAIL(graphics->_renderDevice,
561                                 FX_ERR_Parameter_Invalid);
562        CFX_DIBitmap* bitmap = graphics->_renderDevice->GetBitmap();
563        FX_BOOL result = _renderDevice->SetDIBits(bitmap, 0, 0);
564        _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Method_Not_Supported);
565      }
566    }
567    default: { return FX_ERR_Property_Invalid; }
568  }
569}
570FX_ERR CFX_Graphics::Transfer(CFX_Graphics* graphics,
571                              FX_FLOAT srcLeft,
572                              FX_FLOAT srcTop,
573                              const CFX_RectF& dstRect,
574                              const CFX_Matrix* matrix) {
575  _FX_RETURN_VALUE_IF_FAIL(graphics, FX_ERR_Parameter_Invalid);
576  CFX_Matrix m;
577  m.Set(_info._CTM.a, _info._CTM.b, _info._CTM.c, _info._CTM.d, _info._CTM.e,
578        _info._CTM.f);
579  if (matrix) {
580    m.Concat(*matrix);
581  }
582  switch (_type) {
583    case FX_CONTEXT_Device: {
584      _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
585      {
586        _FX_RETURN_VALUE_IF_FAIL(graphics->_renderDevice,
587                                 FX_ERR_Parameter_Invalid);
588        CFX_DIBitmap* bitmap = graphics->_renderDevice->GetBitmap();
589        FX_BOOL result = FX_ERR_Indefinite;
590        CFX_DIBitmap bmp;
591        result = bmp.Create((int32_t)dstRect.width, (int32_t)dstRect.height,
592                            bitmap->GetFormat());
593        _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Intermediate_Value_Invalid);
594        result = graphics->_renderDevice->GetDIBits(&bmp, (int32_t)srcLeft,
595                                                    (int32_t)srcTop);
596        _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Method_Not_Supported);
597        result = _renderDevice->SetDIBits(&bmp, (int32_t)dstRect.left,
598                                          (int32_t)dstRect.top);
599        _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Method_Not_Supported);
600        return FX_ERR_Succeeded;
601      }
602    }
603    default: { return FX_ERR_Property_Invalid; }
604  }
605}
606CFX_RenderDevice* CFX_Graphics::GetRenderDevice() {
607  return _renderDevice;
608}
609FX_ERR CFX_Graphics::InverseRect(const CFX_RectF& rect) {
610  _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
611  CFX_DIBitmap* bitmap = _renderDevice->GetBitmap();
612  _FX_RETURN_VALUE_IF_FAIL(bitmap, FX_ERR_Property_Invalid);
613  CFX_RectF temp(rect);
614  _info._CTM.TransformRect(temp);
615  CFX_RectF r;
616  r.Set(0, 0, (FX_FLOAT)bitmap->GetWidth(), (FX_FLOAT)bitmap->GetWidth());
617  r.Intersect(temp);
618  if (r.IsEmpty()) {
619    return FX_ERR_Parameter_Invalid;
620  }
621  FX_ARGB* pBuf =
622      (FX_ARGB*)(bitmap->GetBuffer() + int32_t(r.top) * bitmap->GetPitch());
623  int32_t bottom = (int32_t)r.bottom();
624  int32_t right = (int32_t)r.right();
625  for (int32_t i = (int32_t)r.top; i < bottom; i++) {
626    FX_ARGB* pLine = pBuf + (int32_t)r.left;
627    for (int32_t j = (int32_t)r.left; j < right; j++) {
628      FX_ARGB c = *pLine;
629      *pLine++ = (c & 0xFF000000) | (0xFFFFFF - (c & 0x00FFFFFF));
630    }
631    pBuf = (FX_ARGB*)((uint8_t*)pBuf + bitmap->GetPitch());
632  }
633  return FX_ERR_Succeeded;
634}
635FX_ERR CFX_Graphics::XorDIBitmap(const CFX_DIBitmap* srcBitmap,
636                                 const CFX_RectF& rect) {
637  _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
638  CFX_DIBitmap* dst = _renderDevice->GetBitmap();
639  _FX_RETURN_VALUE_IF_FAIL(dst, FX_ERR_Property_Invalid);
640  CFX_RectF temp(rect);
641  _info._CTM.TransformRect(temp);
642  CFX_RectF r;
643  r.Set(0, 0, (FX_FLOAT)dst->GetWidth(), (FX_FLOAT)dst->GetWidth());
644  r.Intersect(temp);
645  if (r.IsEmpty()) {
646    return FX_ERR_Parameter_Invalid;
647  }
648  FX_ARGB* pSrcBuf = (FX_ARGB*)(srcBitmap->GetBuffer() +
649                                int32_t(r.top) * srcBitmap->GetPitch());
650  FX_ARGB* pDstBuf =
651      (FX_ARGB*)(dst->GetBuffer() + int32_t(r.top) * dst->GetPitch());
652  int32_t bottom = (int32_t)r.bottom();
653  int32_t right = (int32_t)r.right();
654  for (int32_t i = (int32_t)r.top; i < bottom; i++) {
655    FX_ARGB* pSrcLine = pSrcBuf + (int32_t)r.left;
656    FX_ARGB* pDstLine = pDstBuf + (int32_t)r.left;
657    for (int32_t j = (int32_t)r.left; j < right; j++) {
658      FX_ARGB c = *pDstLine;
659      *pDstLine++ =
660          ArgbEncode(FXARGB_A(c), (c & 0xFFFFFF) ^ (*pSrcLine & 0xFFFFFF));
661      pSrcLine++;
662    }
663    pSrcBuf = (FX_ARGB*)((uint8_t*)pSrcBuf + srcBitmap->GetPitch());
664    pDstBuf = (FX_ARGB*)((uint8_t*)pDstBuf + dst->GetPitch());
665  }
666  return FX_ERR_Succeeded;
667}
668FX_ERR CFX_Graphics::EqvDIBitmap(const CFX_DIBitmap* srcBitmap,
669                                 const CFX_RectF& rect) {
670  _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
671  CFX_DIBitmap* dst = _renderDevice->GetBitmap();
672  _FX_RETURN_VALUE_IF_FAIL(dst, FX_ERR_Property_Invalid);
673  CFX_RectF temp(rect);
674  _info._CTM.TransformRect(temp);
675  CFX_RectF r;
676  r.Set(0, 0, (FX_FLOAT)dst->GetWidth(), (FX_FLOAT)dst->GetWidth());
677  r.Intersect(temp);
678  if (r.IsEmpty()) {
679    return FX_ERR_Parameter_Invalid;
680  }
681  FX_ARGB* pSrcBuf = (FX_ARGB*)(srcBitmap->GetBuffer() +
682                                int32_t(r.top) * srcBitmap->GetPitch());
683  FX_ARGB* pDstBuf =
684      (FX_ARGB*)(dst->GetBuffer() + int32_t(r.top) * dst->GetPitch());
685  int32_t bottom = (int32_t)r.bottom();
686  int32_t right = (int32_t)r.right();
687  for (int32_t i = (int32_t)r.top; i < bottom; i++) {
688    FX_ARGB* pSrcLine = pSrcBuf + (int32_t)r.left;
689    FX_ARGB* pDstLine = pDstBuf + (int32_t)r.left;
690    for (int32_t j = (int32_t)r.left; j < right; j++) {
691      FX_ARGB c = *pDstLine;
692      *pDstLine++ =
693          ArgbEncode(FXARGB_A(c), ~((c & 0xFFFFFF) ^ (*pSrcLine & 0xFFFFFF)));
694      pSrcLine++;
695    }
696    pSrcBuf = (FX_ARGB*)((uint8_t*)pSrcBuf + srcBitmap->GetPitch());
697    pDstBuf = (FX_ARGB*)((uint8_t*)pDstBuf + dst->GetPitch());
698  }
699  return FX_ERR_Succeeded;
700}
701FX_ERR CFX_Graphics::RenderDeviceSetLineDash(FX_DashStyle dashStyle) {
702  switch (dashStyle) {
703    case FX_DASHSTYLE_Solid: {
704      _info._graphState.SetDashCount(0);
705      return FX_ERR_Succeeded;
706    }
707    case FX_DASHSTYLE_Dash: {
708      FX_FLOAT dashArray[] = {3, 1};
709      SetLineDash(0, dashArray, 2);
710      return FX_ERR_Succeeded;
711    }
712    case FX_DASHSTYLE_Dot: {
713      FX_FLOAT dashArray[] = {1, 1};
714      SetLineDash(0, dashArray, 2);
715      return FX_ERR_Succeeded;
716    }
717    case FX_DASHSTYLE_DashDot: {
718      FX_FLOAT dashArray[] = {3, 1, 1, 1};
719      SetLineDash(0, dashArray, 4);
720      return FX_ERR_Succeeded;
721    }
722    case FX_DASHSTYLE_DashDotDot: {
723      FX_FLOAT dashArray[] = {4, 1, 2, 1, 2, 1};
724      SetLineDash(0, dashArray, 6);
725      return FX_ERR_Succeeded;
726    }
727    default: { return FX_ERR_Parameter_Invalid; }
728  }
729}
730FX_ERR CFX_Graphics::RenderDeviceStrokePath(CFX_Path* path,
731                                            CFX_Matrix* matrix) {
732  _FX_RETURN_VALUE_IF_FAIL(_info._strokeColor, FX_ERR_Property_Invalid);
733  CFX_Matrix m;
734  m.Set(_info._CTM.a, _info._CTM.b, _info._CTM.c, _info._CTM.d, _info._CTM.e,
735        _info._CTM.f);
736  if (matrix) {
737    m.Concat(*matrix);
738  }
739  switch (_info._strokeColor->_type) {
740    case FX_COLOR_Solid: {
741      FX_BOOL result = _renderDevice->DrawPath(
742          path->GetPathData(), (CFX_Matrix*)&m, &_info._graphState, 0x0,
743          _info._strokeColor->_argb, 0);
744      _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Indefinite);
745      return FX_ERR_Succeeded;
746    }
747    case FX_COLOR_Pattern: {
748      return StrokePathWithPattern(path, &m);
749    }
750    case FX_COLOR_Shading: {
751      return StrokePathWithShading(path, &m);
752    }
753    default: { return FX_ERR_Property_Invalid; }
754  }
755}
756FX_ERR CFX_Graphics::RenderDeviceFillPath(CFX_Path* path,
757                                          FX_FillMode fillMode,
758                                          CFX_Matrix* matrix) {
759  _FX_RETURN_VALUE_IF_FAIL(_info._fillColor, FX_ERR_Property_Invalid);
760  CFX_Matrix m;
761  m.Set(_info._CTM.a, _info._CTM.b, _info._CTM.c, _info._CTM.d, _info._CTM.e,
762        _info._CTM.f);
763  if (matrix) {
764    m.Concat(*matrix);
765  }
766  switch (_info._fillColor->_type) {
767    case FX_COLOR_Solid: {
768      FX_BOOL result = _renderDevice->DrawPath(
769          path->GetPathData(), (CFX_Matrix*)&m, &_info._graphState,
770          _info._fillColor->_argb, 0x0, fillMode);
771      _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Indefinite);
772      return FX_ERR_Succeeded;
773    }
774    case FX_COLOR_Pattern: {
775      { return FillPathWithPattern(path, fillMode, &m); }
776    }
777    case FX_COLOR_Shading: {
778      { return FillPathWithShading(path, fillMode, &m); }
779    }
780    default: { return FX_ERR_Property_Invalid; }
781  }
782}
783FX_ERR CFX_Graphics::RenderDeviceDrawImage(CFX_DIBSource* source,
784                                           const CFX_PointF& point,
785                                           CFX_Matrix* matrix) {
786  CFX_Matrix m1;
787  m1.Set(_info._CTM.a, _info._CTM.b, _info._CTM.c, _info._CTM.d, _info._CTM.e,
788         _info._CTM.f);
789  if (matrix) {
790    m1.Concat(*matrix);
791  }
792  CFX_Matrix m2;
793  m2.Set((FX_FLOAT)source->GetWidth(), 0.0, 0.0, (FX_FLOAT)source->GetHeight(),
794         point.x, point.y);
795  m2.Concat(m1);
796  int32_t left, top;
797  CFX_DIBitmap* bmp1 = source->FlipImage(FALSE, TRUE);
798  CFX_DIBitmap* bmp2 = bmp1->TransformTo((CFX_Matrix*)&m2, left, top);
799  CFX_RectF r;
800  GetClipRect(r);
801  FX_ERR result = FX_ERR_Indefinite;
802  {
803    CFX_DIBitmap* bitmap = _renderDevice->GetBitmap();
804    CFX_DIBitmap bmp;
805    bmp.Create(bitmap->GetWidth(), bitmap->GetHeight(), FXDIB_Argb);
806    _renderDevice->GetDIBits(&bmp, 0, 0);
807    bmp.TransferBitmap(FXSYS_round(r.left), FXSYS_round(r.top),
808                       FXSYS_round(r.Width()), FXSYS_round(r.Height()), bmp2,
809                       FXSYS_round(r.left - left), FXSYS_round(r.top - top));
810    _renderDevice->SetDIBits(&bmp, 0, 0);
811    result = FX_ERR_Succeeded;
812  }
813  if (bmp2) {
814    delete bmp2;
815    bmp2 = NULL;
816  }
817  if (bmp1) {
818    delete bmp1;
819    bmp1 = NULL;
820  }
821  return result;
822}
823FX_ERR CFX_Graphics::RenderDeviceStretchImage(CFX_DIBSource* source,
824                                              const CFX_RectF& rect,
825                                              CFX_Matrix* matrix) {
826  CFX_Matrix m1;
827  m1.Set(_info._CTM.a, _info._CTM.b, _info._CTM.c, _info._CTM.d, _info._CTM.e,
828         _info._CTM.f);
829  if (matrix) {
830    m1.Concat(*matrix);
831  }
832  CFX_DIBitmap* bmp1 =
833      source->StretchTo((int32_t)rect.Width(), (int32_t)rect.Height());
834  CFX_Matrix m2;
835  m2.Set(rect.Width(), 0.0, 0.0, rect.Height(), rect.left, rect.top);
836  m2.Concat(m1);
837  int32_t left, top;
838  CFX_DIBitmap* bmp2 = bmp1->FlipImage(FALSE, TRUE);
839  CFX_DIBitmap* bmp3 = bmp2->TransformTo((CFX_Matrix*)&m2, left, top);
840  CFX_RectF r;
841  GetClipRect(r);
842  FX_ERR result = FX_ERR_Indefinite;
843  {
844    CFX_DIBitmap* bitmap = _renderDevice->GetBitmap();
845    bitmap->CompositeBitmap(FXSYS_round(r.left), FXSYS_round(r.top),
846                            FXSYS_round(r.Width()), FXSYS_round(r.Height()),
847                            bmp3, FXSYS_round(r.left - left),
848                            FXSYS_round(r.top - top));
849    result = FX_ERR_Succeeded;
850  }
851  if (bmp3) {
852    delete bmp3;
853    bmp3 = NULL;
854  }
855  if (bmp2) {
856    delete bmp2;
857    bmp2 = NULL;
858  }
859  if (bmp1) {
860    delete bmp1;
861    bmp1 = NULL;
862  }
863  return result;
864}
865FX_ERR CFX_Graphics::RenderDeviceShowText(const CFX_PointF& point,
866                                          const CFX_WideString& text,
867                                          CFX_Matrix* matrix) {
868  int32_t length = text.GetLength();
869  FX_DWORD* charCodes = FX_Alloc(FX_DWORD, length);
870  FXTEXT_CHARPOS* charPos = FX_Alloc(FXTEXT_CHARPOS, length);
871  CFX_RectF rect;
872  rect.Set(point.x, point.y, 0, 0);
873  CalcTextInfo(text, charCodes, charPos, rect);
874  CFX_Matrix m;
875  m.Set(_info._CTM.a, _info._CTM.b, _info._CTM.c, _info._CTM.d, _info._CTM.e,
876        _info._CTM.f);
877  m.Translate(0, _info._fontSize * _info._fontHScale);
878  if (matrix) {
879    m.Concat(*matrix);
880  }
881  FX_BOOL result = _renderDevice->DrawNormalText(
882      length, charPos, _info._font, CFX_GEModule::Get()->GetFontCache(),
883      -_info._fontSize * _info._fontHScale, (CFX_Matrix*)&m,
884      _info._fillColor->_argb, FXTEXT_CLEARTYPE);
885  _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Indefinite);
886  FX_Free(charPos);
887  FX_Free(charCodes);
888  return FX_ERR_Succeeded;
889}
890FX_ERR CFX_Graphics::StrokePathWithPattern(CFX_Path* path, CFX_Matrix* matrix) {
891  return FX_ERR_Method_Not_Supported;
892}
893FX_ERR CFX_Graphics::StrokePathWithShading(CFX_Path* path, CFX_Matrix* matrix) {
894  return FX_ERR_Method_Not_Supported;
895}
896FX_ERR CFX_Graphics::FillPathWithPattern(CFX_Path* path,
897                                         FX_FillMode fillMode,
898                                         CFX_Matrix* matrix) {
899  CFX_Pattern* pattern = _info._fillColor->_pattern;
900  CFX_DIBitmap* bitmap = _renderDevice->GetBitmap();
901  int32_t width = bitmap->GetWidth();
902  int32_t height = bitmap->GetHeight();
903  CFX_DIBitmap bmp;
904  bmp.Create(width, height, FXDIB_Argb);
905  _renderDevice->GetDIBits(&bmp, 0, 0);
906  switch (pattern->_type) {
907    case FX_PATTERN_Bitmap: {
908      int32_t xStep = FXSYS_round(pattern->_x1Step);
909      int32_t yStep = FXSYS_round(pattern->_y1Step);
910      int32_t xCount = width / xStep + 1;
911      int32_t yCount = height / yStep + 1;
912      for (int32_t i = 0; i <= yCount; i++) {
913        for (int32_t j = 0; j <= xCount; j++) {
914          bmp.TransferBitmap(j * xStep, i * yStep, xStep, yStep,
915                             pattern->_bitmap, 0, 0);
916        }
917      }
918      break;
919    }
920    case FX_PATTERN_Hatch: {
921      FX_HatchStyle hatchStyle = _info._fillColor->_pattern->_hatchStyle;
922      if (hatchStyle < FX_HATCHSTYLE_Horizontal ||
923          hatchStyle > FX_HATCHSTYLE_SolidDiamond) {
924        return FX_ERR_Intermediate_Value_Invalid;
925      }
926      const FX_HATCHDATA& data = hatchBitmapData[hatchStyle];
927      CFX_DIBitmap mask;
928      mask.Create(data.width, data.height, FXDIB_1bppMask);
929      FXSYS_memcpy(mask.GetBuffer(), data.maskBits,
930                   mask.GetPitch() * data.height);
931      CFX_FloatRect rectf = path->GetPathData()->GetBoundingBox();
932      if (matrix) {
933        rectf.Transform((const CFX_Matrix*)matrix);
934      }
935      FX_RECT rect(FXSYS_round(rectf.left), FXSYS_round(rectf.top),
936                   FXSYS_round(rectf.right), FXSYS_round(rectf.bottom));
937      CFX_FxgeDevice device;
938      device.Attach(&bmp);
939      device.FillRect(&rect, _info._fillColor->_pattern->_backArgb);
940      for (int32_t j = rect.bottom; j < rect.top; j += mask.GetHeight()) {
941        for (int32_t i = rect.left; i < rect.right; i += mask.GetWidth()) {
942          device.SetBitMask(&mask, i, j, _info._fillColor->_pattern->_foreArgb);
943        }
944      }
945      break;
946    }
947  }
948  _renderDevice->SaveState();
949  _renderDevice->SetClip_PathFill(path->GetPathData(), (CFX_Matrix*)matrix,
950                                  fillMode);
951  SetDIBitsWithMatrix(&bmp, &pattern->_matrix);
952  _renderDevice->RestoreState();
953  return FX_ERR_Succeeded;
954}
955FX_ERR CFX_Graphics::FillPathWithShading(CFX_Path* path,
956                                         FX_FillMode fillMode,
957                                         CFX_Matrix* matrix) {
958  CFX_DIBitmap* bitmap = _renderDevice->GetBitmap();
959  int32_t width = bitmap->GetWidth();
960  int32_t height = bitmap->GetHeight();
961  FX_FLOAT start_x = _info._fillColor->_shading->_beginPoint.x;
962  FX_FLOAT start_y = _info._fillColor->_shading->_beginPoint.y;
963  FX_FLOAT end_x = _info._fillColor->_shading->_endPoint.x;
964  FX_FLOAT end_y = _info._fillColor->_shading->_endPoint.y;
965  CFX_DIBitmap bmp;
966  bmp.Create(width, height, FXDIB_Argb);
967  _renderDevice->GetDIBits(&bmp, 0, 0);
968  int32_t pitch = bmp.GetPitch();
969  FX_BOOL result = FALSE;
970  switch (_info._fillColor->_shading->_type) {
971    case FX_SHADING_Axial: {
972      FX_FLOAT x_span = end_x - start_x;
973      FX_FLOAT y_span = end_y - start_y;
974      FX_FLOAT axis_len_square =
975          FXSYS_Mul(x_span, x_span) + FXSYS_Mul(y_span, y_span);
976      for (int32_t row = 0; row < height; row++) {
977        FX_DWORD* dib_buf = (FX_DWORD*)(bmp.GetBuffer() + row * pitch);
978        for (int32_t column = 0; column < width; column++) {
979          FX_FLOAT x = (FX_FLOAT)(column);
980          FX_FLOAT y = (FX_FLOAT)(row);
981          FX_FLOAT scale = FXSYS_Div(
982              FXSYS_Mul(x - start_x, x_span) + FXSYS_Mul(y - start_y, y_span),
983              axis_len_square);
984          if (scale < 0) {
985            if (!_info._fillColor->_shading->_isExtendedBegin) {
986              continue;
987            }
988            scale = 0;
989          } else if (scale > 1.0f) {
990            if (!_info._fillColor->_shading->_isExtendedEnd) {
991              continue;
992            }
993            scale = 1.0f;
994          }
995          int32_t index = (int32_t)(scale * (FX_SHADING_Steps - 1));
996          dib_buf[column] = _info._fillColor->_shading->_argbArray[index];
997        }
998      }
999      result = TRUE;
1000      break;
1001    }
1002    case FX_SHADING_Radial: {
1003      FX_FLOAT start_r = _info._fillColor->_shading->_beginRadius;
1004      FX_FLOAT end_r = _info._fillColor->_shading->_endRadius;
1005      FX_FLOAT a = FXSYS_Mul(start_x - end_x, start_x - end_x) +
1006                   FXSYS_Mul(start_y - end_y, start_y - end_y) -
1007                   FXSYS_Mul(start_r - end_r, start_r - end_r);
1008      for (int32_t row = 0; row < height; row++) {
1009        FX_DWORD* dib_buf = (FX_DWORD*)(bmp.GetBuffer() + row * pitch);
1010        for (int32_t column = 0; column < width; column++) {
1011          FX_FLOAT x = (FX_FLOAT)(column);
1012          FX_FLOAT y = (FX_FLOAT)(row);
1013          FX_FLOAT b = -2 * (FXSYS_Mul(x - start_x, end_x - start_x) +
1014                             FXSYS_Mul(y - start_y, end_y - start_y) +
1015                             FXSYS_Mul(start_r, end_r - start_r));
1016          FX_FLOAT c = FXSYS_Mul(x - start_x, x - start_x) +
1017                       FXSYS_Mul(y - start_y, y - start_y) -
1018                       FXSYS_Mul(start_r, start_r);
1019          FX_FLOAT s;
1020          if (a == 0) {
1021            s = (FXSYS_Div(-c, b));
1022          } else {
1023            FX_FLOAT b2_4ac = FXSYS_Mul(b, b) - 4 * FXSYS_Mul(a, c);
1024            if (b2_4ac < 0) {
1025              continue;
1026            }
1027            FX_FLOAT root = (FXSYS_sqrt(b2_4ac));
1028            FX_FLOAT s1, s2;
1029            if (a > 0) {
1030              s1 = FXSYS_Div(-b - root, 2 * a);
1031              s2 = FXSYS_Div(-b + root, 2 * a);
1032            } else {
1033              s2 = FXSYS_Div(-b - root, 2 * a);
1034              s1 = FXSYS_Div(-b + root, 2 * a);
1035            }
1036            if (s2 <= 1.0f || _info._fillColor->_shading->_isExtendedEnd) {
1037              s = (s2);
1038            } else {
1039              s = (s1);
1040            }
1041            if ((start_r) + s * (end_r - start_r) < 0) {
1042              continue;
1043            }
1044          }
1045          if (s < 0) {
1046            if (!_info._fillColor->_shading->_isExtendedBegin) {
1047              continue;
1048            }
1049            s = 0;
1050          }
1051          if (s > 1.0f) {
1052            if (!_info._fillColor->_shading->_isExtendedEnd) {
1053              continue;
1054            }
1055            s = 1.0f;
1056          }
1057          int index = (int32_t)(s * (FX_SHADING_Steps - 1));
1058          dib_buf[column] = _info._fillColor->_shading->_argbArray[index];
1059        }
1060      }
1061      result = TRUE;
1062      break;
1063    }
1064    default: { result = FALSE; }
1065  }
1066  if (result) {
1067    _renderDevice->SaveState();
1068    _renderDevice->SetClip_PathFill(path->GetPathData(), (CFX_Matrix*)matrix,
1069                                    fillMode);
1070    SetDIBitsWithMatrix(&bmp, matrix);
1071    _renderDevice->RestoreState();
1072  }
1073  return result;
1074}
1075FX_ERR CFX_Graphics::SetDIBitsWithMatrix(CFX_DIBSource* source,
1076                                         CFX_Matrix* matrix) {
1077  if (matrix->IsIdentity()) {
1078    _renderDevice->SetDIBits(source, 0, 0);
1079  } else {
1080    CFX_Matrix m;
1081    m.Set((FX_FLOAT)source->GetWidth(), 0, 0, (FX_FLOAT)source->GetHeight(), 0,
1082          0);
1083    m.Concat(*matrix);
1084    int32_t left, top;
1085    CFX_DIBitmap* bmp1 = source->FlipImage(FALSE, TRUE);
1086    CFX_DIBitmap* bmp2 = bmp1->TransformTo((CFX_Matrix*)&m, left, top);
1087    _renderDevice->SetDIBits(bmp2, left, top);
1088    if (bmp2) {
1089      delete bmp2;
1090      bmp2 = NULL;
1091    }
1092    if (bmp1) {
1093      delete bmp1;
1094      bmp1 = NULL;
1095    }
1096  }
1097  return FX_ERR_Succeeded;
1098}
1099FX_ERR CFX_Graphics::CalcTextInfo(const CFX_WideString& text,
1100                                  FX_DWORD* charCodes,
1101                                  FXTEXT_CHARPOS* charPos,
1102                                  CFX_RectF& rect) {
1103  std::unique_ptr<CFX_UnicodeEncoding> encoding(
1104      new CFX_UnicodeEncoding(_info._font));
1105  int32_t length = text.GetLength();
1106  FX_FLOAT penX = (FX_FLOAT)rect.left;
1107  FX_FLOAT penY = (FX_FLOAT)rect.top;
1108  FX_FLOAT left = (FX_FLOAT)(0);
1109  FX_FLOAT top = (FX_FLOAT)(0);
1110  charCodes[0] = text.GetAt(0);
1111  charPos[0].m_OriginX = penX + left;
1112  charPos[0].m_OriginY = penY + top;
1113  charPos[0].m_GlyphIndex = encoding->GlyphFromCharCode(charCodes[0]);
1114  charPos[0].m_FontCharWidth = FXSYS_round(
1115      _info._font->GetGlyphWidth(charPos[0].m_GlyphIndex) * _info._fontHScale);
1116  charPos[0].m_bGlyphAdjust = TRUE;
1117  charPos[0].m_AdjustMatrix[0] = -1;
1118  charPos[0].m_AdjustMatrix[1] = 0;
1119  charPos[0].m_AdjustMatrix[2] = 0;
1120  charPos[0].m_AdjustMatrix[3] = 1;
1121  penX += (FX_FLOAT)(charPos[0].m_FontCharWidth) * _info._fontSize / 1000 +
1122          _info._fontSpacing;
1123  for (int32_t i = 1; i < length; i++) {
1124    charCodes[i] = text.GetAt(i);
1125    charPos[i].m_OriginX = penX + left;
1126    charPos[i].m_OriginY = penY + top;
1127    charPos[i].m_GlyphIndex = encoding->GlyphFromCharCode(charCodes[i]);
1128    charPos[i].m_FontCharWidth =
1129        FXSYS_round(_info._font->GetGlyphWidth(charPos[i].m_GlyphIndex) *
1130                    _info._fontHScale);
1131    charPos[i].m_bGlyphAdjust = TRUE;
1132    charPos[i].m_AdjustMatrix[0] = -1;
1133    charPos[i].m_AdjustMatrix[1] = 0;
1134    charPos[i].m_AdjustMatrix[2] = 0;
1135    charPos[i].m_AdjustMatrix[3] = 1;
1136    penX += (FX_FLOAT)(charPos[i].m_FontCharWidth) * _info._fontSize / 1000 +
1137            _info._fontSpacing;
1138  }
1139  rect.width = (FX_FLOAT)penX - rect.left;
1140  rect.height = rect.top + _info._fontSize * _info._fontHScale - rect.top;
1141  return FX_ERR_Succeeded;
1142}
1143CAGG_Graphics::CAGG_Graphics() {
1144  _owner = NULL;
1145}
1146FX_ERR CAGG_Graphics::Create(CFX_Graphics* owner,
1147                             int32_t width,
1148                             int32_t height,
1149                             FXDIB_Format format) {
1150  if (owner->_renderDevice) {
1151    return FX_ERR_Parameter_Invalid;
1152  }
1153  if (_owner) {
1154    return FX_ERR_Property_Invalid;
1155  }
1156  CFX_FxgeDevice* device = new CFX_FxgeDevice;
1157  device->Create(width, height, format);
1158  _owner = owner;
1159  _owner->_renderDevice = device;
1160  _owner->_renderDevice->GetBitmap()->Clear(0xFFFFFFFF);
1161  return FX_ERR_Succeeded;
1162}
1163CAGG_Graphics::~CAGG_Graphics() {
1164  if (_owner->_renderDevice) {
1165    delete (CFX_FxgeDevice*)_owner->_renderDevice;
1166  }
1167  _owner = NULL;
1168}
1169CFX_Path::CFX_Path() {
1170  _generator = NULL;
1171}
1172FX_ERR CFX_Path::Create() {
1173  if (_generator) {
1174    return FX_ERR_Property_Invalid;
1175  }
1176  _generator = new CFX_PathGenerator;
1177  _generator->Create();
1178  return FX_ERR_Succeeded;
1179}
1180CFX_Path::~CFX_Path() {
1181  if (_generator) {
1182    delete _generator;
1183    _generator = NULL;
1184  }
1185}
1186FX_ERR CFX_Path::MoveTo(FX_FLOAT x, FX_FLOAT y) {
1187  _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
1188  _generator->MoveTo(x, y);
1189  return FX_ERR_Succeeded;
1190}
1191FX_ERR CFX_Path::LineTo(FX_FLOAT x, FX_FLOAT y) {
1192  _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
1193  _generator->LineTo(x, y);
1194  return FX_ERR_Succeeded;
1195}
1196FX_ERR CFX_Path::BezierTo(FX_FLOAT ctrlX1,
1197                          FX_FLOAT ctrlY1,
1198                          FX_FLOAT ctrlX2,
1199                          FX_FLOAT ctrlY2,
1200                          FX_FLOAT toX,
1201                          FX_FLOAT toY) {
1202  _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
1203  _generator->BezierTo(ctrlX1, ctrlY1, ctrlX2, ctrlY2, toX, toY);
1204  return FX_ERR_Succeeded;
1205}
1206FX_ERR CFX_Path::ArcTo(FX_FLOAT left,
1207                       FX_FLOAT top,
1208                       FX_FLOAT width,
1209                       FX_FLOAT height,
1210                       FX_FLOAT startAngle,
1211                       FX_FLOAT sweepAngle) {
1212  _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
1213  _generator->ArcTo(left + width / 2, top + height / 2, width / 2, height / 2,
1214                    startAngle, sweepAngle);
1215  return FX_ERR_Succeeded;
1216}
1217FX_ERR CFX_Path::Close() {
1218  _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
1219  _generator->Close();
1220  return FX_ERR_Succeeded;
1221}
1222FX_ERR CFX_Path::AddLine(FX_FLOAT x1, FX_FLOAT y1, FX_FLOAT x2, FX_FLOAT y2) {
1223  _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
1224  _generator->AddLine(x1, y1, x2, y2);
1225  return FX_ERR_Succeeded;
1226}
1227FX_ERR CFX_Path::AddBezier(FX_FLOAT startX,
1228                           FX_FLOAT startY,
1229                           FX_FLOAT ctrlX1,
1230                           FX_FLOAT ctrlY1,
1231                           FX_FLOAT ctrlX2,
1232                           FX_FLOAT ctrlY2,
1233                           FX_FLOAT endX,
1234                           FX_FLOAT endY) {
1235  _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
1236  _generator->AddBezier(startX, startY, ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX,
1237                        endY);
1238  return FX_ERR_Succeeded;
1239}
1240FX_ERR CFX_Path::AddRectangle(FX_FLOAT left,
1241                              FX_FLOAT top,
1242                              FX_FLOAT width,
1243                              FX_FLOAT height) {
1244  _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
1245  _generator->AddRectangle(left, top, left + width, top + height);
1246  return FX_ERR_Succeeded;
1247}
1248FX_ERR CFX_Path::AddEllipse(FX_FLOAT left,
1249                            FX_FLOAT top,
1250                            FX_FLOAT width,
1251                            FX_FLOAT height) {
1252  _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
1253  _generator->AddEllipse(left + width / 2, top + height / 2, width / 2,
1254                         height / 2);
1255  return FX_ERR_Succeeded;
1256}
1257FX_ERR CFX_Path::AddEllipse(const CFX_RectF& rect) {
1258  _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
1259  _generator->AddEllipse(rect.left + rect.Width() / 2,
1260                         rect.top + rect.Height() / 2, rect.Width() / 2,
1261                         rect.Height() / 2);
1262  return FX_ERR_Succeeded;
1263}
1264FX_ERR CFX_Path::AddArc(FX_FLOAT left,
1265                        FX_FLOAT top,
1266                        FX_FLOAT width,
1267                        FX_FLOAT height,
1268                        FX_FLOAT startAngle,
1269                        FX_FLOAT sweepAngle) {
1270  _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
1271  _generator->AddArc(left + width / 2, top + height / 2, width / 2, height / 2,
1272                     startAngle, sweepAngle);
1273  return FX_ERR_Succeeded;
1274}
1275FX_ERR CFX_Path::AddPie(FX_FLOAT left,
1276                        FX_FLOAT top,
1277                        FX_FLOAT width,
1278                        FX_FLOAT height,
1279                        FX_FLOAT startAngle,
1280                        FX_FLOAT sweepAngle) {
1281  _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
1282  _generator->AddPie(left + width / 2, top + height / 2, width / 2, height / 2,
1283                     startAngle, sweepAngle);
1284  return FX_ERR_Succeeded;
1285}
1286FX_ERR CFX_Path::AddSubpath(CFX_Path* path) {
1287  _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
1288  _generator->AddPathData(path->GetPathData());
1289  return FX_ERR_Succeeded;
1290}
1291FX_ERR CFX_Path::Clear() {
1292  _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
1293  _generator->GetPathData()->SetPointCount(0);
1294  return FX_ERR_Succeeded;
1295}
1296FX_BOOL CFX_Path::IsEmpty() {
1297  _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
1298  if (_generator->GetPathData()->GetPointCount() == 0) {
1299    return TRUE;
1300  }
1301  return FALSE;
1302}
1303CFX_PathData* CFX_Path::GetPathData() {
1304  _FX_RETURN_VALUE_IF_FAIL(_generator, NULL);
1305  return _generator->GetPathData();
1306}
1307CFX_Color::CFX_Color() {
1308  _type = FX_COLOR_None;
1309}
1310CFX_Color::CFX_Color(const FX_ARGB argb) {
1311  _type = FX_COLOR_None;
1312  Set(argb);
1313}
1314CFX_Color::CFX_Color(CFX_Pattern* pattern, const FX_ARGB argb) {
1315  _type = FX_COLOR_None;
1316  Set(pattern, argb);
1317}
1318CFX_Color::CFX_Color(CFX_Shading* shading) {
1319  _type = FX_COLOR_None;
1320  Set(shading);
1321}
1322CFX_Color::~CFX_Color() {
1323  _type = FX_COLOR_None;
1324}
1325FX_ERR CFX_Color::Set(const FX_ARGB argb) {
1326  _type = FX_COLOR_Solid;
1327  _argb = argb;
1328  _pattern = NULL;
1329  return FX_ERR_Succeeded;
1330}
1331FX_ERR CFX_Color::Set(CFX_Pattern* pattern, const FX_ARGB argb) {
1332  _FX_RETURN_VALUE_IF_FAIL(pattern, FX_ERR_Parameter_Invalid);
1333  _type = FX_COLOR_Pattern;
1334  _argb = argb;
1335  _pattern = pattern;
1336  return FX_ERR_Succeeded;
1337}
1338FX_ERR CFX_Color::Set(CFX_Shading* shading) {
1339  _FX_RETURN_VALUE_IF_FAIL(shading, FX_ERR_Parameter_Invalid);
1340  _type = FX_COLOR_Shading;
1341  _shading = shading;
1342  return FX_ERR_Succeeded;
1343}
1344CFX_Pattern::CFX_Pattern() {
1345  _type = FX_PATTERN_None;
1346  _matrix.SetIdentity();
1347}
1348FX_ERR CFX_Pattern::Create(CFX_DIBitmap* bitmap,
1349                           const FX_FLOAT xStep,
1350                           const FX_FLOAT yStep,
1351                           CFX_Matrix* matrix) {
1352  _FX_RETURN_VALUE_IF_FAIL(bitmap, FX_ERR_Parameter_Invalid);
1353  if (_type != FX_PATTERN_None) {
1354    return FX_ERR_Property_Invalid;
1355  }
1356  _type = FX_PATTERN_Bitmap;
1357  _bitmap = bitmap;
1358  _x1Step = xStep;
1359  _y1Step = yStep;
1360  if (matrix) {
1361    _matrix.Set(matrix->a, matrix->b, matrix->c, matrix->d, matrix->e,
1362                matrix->f);
1363  }
1364  return FX_ERR_Succeeded;
1365}
1366FX_ERR CFX_Pattern::Create(FX_HatchStyle hatchStyle,
1367                           const FX_ARGB foreArgb,
1368                           const FX_ARGB backArgb,
1369                           CFX_Matrix* matrix) {
1370  if (hatchStyle < FX_HATCHSTYLE_Horizontal ||
1371      hatchStyle > FX_HATCHSTYLE_SolidDiamond) {
1372    return FX_ERR_Parameter_Invalid;
1373  }
1374  if (_type != FX_PATTERN_None) {
1375    return FX_ERR_Property_Invalid;
1376  }
1377  _type = FX_PATTERN_Hatch;
1378  _hatchStyle = hatchStyle;
1379  _foreArgb = foreArgb;
1380  _backArgb = backArgb;
1381  if (matrix) {
1382    _matrix.Set(matrix->a, matrix->b, matrix->c, matrix->d, matrix->e,
1383                matrix->f);
1384  }
1385  return FX_ERR_Succeeded;
1386}
1387CFX_Pattern::~CFX_Pattern() {
1388  _type = FX_PATTERN_None;
1389}
1390CFX_Shading::CFX_Shading() {
1391  _type = FX_SHADING_None;
1392}
1393FX_ERR CFX_Shading::CreateAxial(const CFX_PointF& beginPoint,
1394                                const CFX_PointF& endPoint,
1395                                FX_BOOL isExtendedBegin,
1396                                FX_BOOL isExtendedEnd,
1397                                const FX_ARGB beginArgb,
1398                                const FX_ARGB endArgb) {
1399  if (_type != FX_SHADING_None) {
1400    return FX_ERR_Property_Invalid;
1401  }
1402  _type = FX_SHADING_Axial;
1403  _beginPoint = beginPoint;
1404  _endPoint = endPoint;
1405  _isExtendedBegin = isExtendedBegin;
1406  _isExtendedEnd = isExtendedEnd;
1407  _beginArgb = beginArgb;
1408  _endArgb = endArgb;
1409  return InitArgbArray();
1410}
1411FX_ERR CFX_Shading::CreateRadial(const CFX_PointF& beginPoint,
1412                                 const CFX_PointF& endPoint,
1413                                 const FX_FLOAT beginRadius,
1414                                 const FX_FLOAT endRadius,
1415                                 FX_BOOL isExtendedBegin,
1416                                 FX_BOOL isExtendedEnd,
1417                                 const FX_ARGB beginArgb,
1418                                 const FX_ARGB endArgb) {
1419  if (_type != FX_SHADING_None) {
1420    return FX_ERR_Property_Invalid;
1421  }
1422  _type = FX_SHADING_Radial;
1423  _beginPoint = beginPoint;
1424  _endPoint = endPoint;
1425  _beginRadius = beginRadius;
1426  _endRadius = endRadius;
1427  _isExtendedBegin = isExtendedBegin;
1428  _isExtendedEnd = isExtendedEnd;
1429  _beginArgb = beginArgb;
1430  _endArgb = endArgb;
1431  return InitArgbArray();
1432}
1433CFX_Shading::~CFX_Shading() {
1434  _type = FX_SHADING_None;
1435}
1436FX_ERR CFX_Shading::InitArgbArray() {
1437  int32_t a1, r1, g1, b1;
1438  ArgbDecode(_beginArgb, a1, r1, g1, b1);
1439  int32_t a2, r2, g2, b2;
1440  ArgbDecode(_endArgb, a2, r2, g2, b2);
1441  FX_FLOAT f = (FX_FLOAT)(FX_SHADING_Steps - 1);
1442  FX_FLOAT aScale = (FX_FLOAT)(1.0 * (a2 - a1) / f);
1443  FX_FLOAT rScale = (FX_FLOAT)(1.0 * (r2 - r1) / f);
1444  FX_FLOAT gScale = (FX_FLOAT)(1.0 * (g2 - g1) / f);
1445  FX_FLOAT bScale = (FX_FLOAT)(1.0 * (b2 - b1) / f);
1446  int32_t a3, r3, g3, b3;
1447  for (int32_t i = 0; i < FX_SHADING_Steps; i++) {
1448    a3 = (int32_t)(i * aScale);
1449    r3 = (int32_t)(i * rScale);
1450    g3 = (int32_t)(i * gScale);
1451    b3 = (int32_t)(i * bScale);
1452    _argbArray[i] =
1453        FXARGB_TODIB(FXARGB_MAKE((a1 + a3), (r1 + r3), (g1 + g3), (b1 + b3)));
1454  }
1455  return FX_ERR_Succeeded;
1456}
1457class CFX_Pause : public IFX_Pause {
1458 public:
1459  virtual FX_BOOL NeedToPauseNow() { return TRUE; }
1460};
1461