Initial commit.
[pdfium.git] / core / src / fxge / apple / fx_quartz_device.cpp
1 // Copyright 2014 PDFium Authors. All rights reserved.\r
2 // Use of this source code is governed by a BSD-style license that can be\r
3 // found in the LICENSE file.\r
4  \r
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com\r
6 \r
7 #include "../../../include/fxcrt/fx_ext.h"\r
8 #include "../../../include/fxge/fx_ge.h"\r
9 #include "../agg/include/fxfx_agg_clip_liang_barsky.h"\r
10 #include "../ge/text_int.h"\r
11 #include "../dib/dib_int.h"\r
12 #include "../agg/include/fx_agg_driver.h"\r
13 #include "../../../include/fxge/fx_freetype.h"\r
14 #if _FXM_PLATFORM_  == _FXM_PLATFORM_APPLE_\r
15 #include "apple_int.h"\r
16 #include "../../../include/fxge/fx_ge_apple.h"\r
17 #ifndef CGFLOAT_IS_DOUBLE\r
18 #error Expected CGFLOAT_IS_DOUBLE to be defined by CoreGraphics headers\r
19 #endif\r
20 void* CQuartz2D::createGraphics(CFX_DIBitmap* pBitmap)\r
21 {\r
22     if (!pBitmap) {\r
23         return NULL;\r
24     }\r
25     CGBitmapInfo bmpInfo = kCGBitmapByteOrder32Little;\r
26     switch (pBitmap->GetFormat()) {\r
27         case FXDIB_Rgb32:\r
28             bmpInfo |= kCGImageAlphaNoneSkipFirst;\r
29             break;\r
30         case FXDIB_Argb:\r
31         default:\r
32             return NULL;\r
33     }\r
34     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();\r
35     CGContextRef context = CGBitmapContextCreate(pBitmap->GetBuffer(),\r
36                            pBitmap->GetWidth(),\r
37                            pBitmap->GetHeight(),\r
38                            8,\r
39                            pBitmap->GetPitch(),\r
40                            colorSpace,\r
41                            bmpInfo);\r
42     CGColorSpaceRelease(colorSpace);\r
43     return context;\r
44 }\r
45 void CQuartz2D::destroyGraphics(void* graphics)\r
46 {\r
47     if (graphics) {\r
48         CGContextRelease((CGContextRef) graphics);\r
49     }\r
50 }\r
51 void* CQuartz2D::CreateFont(FX_LPCBYTE pFontData, FX_DWORD dwFontSize)\r
52 {\r
53     CGDataProviderRef pDataProvider = CGDataProviderCreateWithData(NULL, pFontData, (size_t)dwFontSize, NULL);\r
54     if (NULL == pDataProvider) {\r
55         return NULL;\r
56     }\r
57     CGFontRef pCGFont = CGFontCreateWithDataProvider(pDataProvider);\r
58     CGDataProviderRelease(pDataProvider);\r
59     return pCGFont;\r
60 }\r
61 void CQuartz2D::DestroyFont(void* pFont)\r
62 {\r
63     CGFontRelease((CGFontRef)pFont);\r
64 }\r
65 void CQuartz2D::setGraphicsTextMatrix(void* graphics, CFX_AffineMatrix* matrix)\r
66 {\r
67     if (!graphics || !matrix) {\r
68         return;\r
69     }\r
70     CGContextRef context = (CGContextRef) graphics;\r
71     CGFloat ty = CGBitmapContextGetHeight(context) - matrix->f;\r
72     CGContextSetTextMatrix(context, CGAffineTransformMake(matrix->a,\r
73                            matrix->b,\r
74                            matrix->c,\r
75                            matrix->d,\r
76                            matrix->e,\r
77                            ty));\r
78 }\r
79 FX_BOOL CQuartz2D::drawGraphicsString(void*                 graphics,\r
80                                       void*                 font,\r
81                                       FX_FLOAT              fontSize,\r
82                                       FX_WORD*              glyphIndices,\r
83                                       CGPoint*           glyphPositions,\r
84                                       FX_INT32              charsCount,\r
85                                       FX_ARGB               argb,\r
86                                       CFX_AffineMatrix*     matrix )\r
87 {\r
88     if (!graphics) {\r
89         return FALSE;\r
90     }\r
91     CGContextRef context = (CGContextRef) graphics;\r
92     CGContextSetFont(context, (CGFontRef)font);\r
93     CGContextSetFontSize(context, fontSize);\r
94     if (matrix) {\r
95         CGAffineTransform m = CGContextGetTextMatrix(context);\r
96         m = CGAffineTransformConcat(m,\r
97                                     CGAffineTransformMake(matrix->a,\r
98                                             matrix->b,\r
99                                             matrix->c,\r
100                                             matrix->d,\r
101                                             matrix->e,\r
102                                             matrix->f));\r
103         CGContextSetTextMatrix(context, m);\r
104     }\r
105     FX_INT32 a, r, g, b;\r
106     ArgbDecode(argb, a, r, g, b);\r
107     CGContextSetRGBFillColor(context,\r
108                              r / 255.f,\r
109                              g / 255.f,\r
110                              b / 255.f,\r
111                              a / 255.f);\r
112     CGContextSaveGState(context);\r
113 #if CGFLOAT_IS_DOUBLE\r
114     CGPoint* glyphPositionsCG = new CGPoint[charsCount];\r
115     if (!glyphPositionsCG) {\r
116         return FALSE;\r
117     }\r
118     for (int index = 0; index < charsCount; ++index) {\r
119         glyphPositionsCG[index].x = glyphPositions[index].x;\r
120         glyphPositionsCG[index].y = glyphPositions[index].y;\r
121     }\r
122 #else\r
123     CGPoint* glyphPositionsCG = (CGPoint*)glyphPositions;\r
124 #endif\r
125     CGContextShowGlyphsAtPositions(context,\r
126                                    (CGGlyph *) glyphIndices,\r
127                                    glyphPositionsCG,\r
128                                    charsCount);\r
129 #if CGFLOAT_IS_DOUBLE\r
130     delete[] glyphPositionsCG;\r
131 #endif\r
132     CGContextRestoreGState(context);\r
133     return TRUE;\r
134 }\r
135 void CQuartz2D::saveGraphicsState(void * graphics)\r
136 {\r
137     if (graphics) {\r
138         CGContextSaveGState((CGContextRef) graphics);\r
139     }\r
140 }\r
141 void CQuartz2D::restoreGraphicsState(void * graphics)\r
142 {\r
143     if (graphics) {\r
144         CGContextRestoreGState((CGContextRef) graphics);\r
145     }\r
146 }\r
147 static CGContextRef createContextWithBitmap(CFX_DIBitmap* pBitmap)\r
148 {\r
149     if (!pBitmap || pBitmap->IsCmykImage() || pBitmap->GetBPP() < 32) {\r
150         return NULL;\r
151     }\r
152     CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little;\r
153     if (pBitmap->HasAlpha()) {\r
154         bitmapInfo |= kCGImageAlphaPremultipliedFirst;\r
155     } else {\r
156         bitmapInfo |= kCGImageAlphaNoneSkipFirst;\r
157     }\r
158     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();\r
159     CGContextRef context = CGBitmapContextCreate(pBitmap->GetBuffer(),\r
160                            pBitmap->GetWidth(),\r
161                            pBitmap->GetHeight(),\r
162                            8,\r
163                            pBitmap->GetPitch(),\r
164                            colorSpace,\r
165                            bitmapInfo);\r
166     CGColorSpaceRelease(colorSpace);\r
167     return context;\r
168 }\r
169 CFX_QuartzDeviceDriver::CFX_QuartzDeviceDriver(CGContextRef context, FX_INT32 deviceClass)\r
170 {\r
171     m_saveCount = 0;\r
172     _context            = context;\r
173     _deviceClass        = deviceClass;\r
174     CGContextRetain(_context);\r
175     CGRect r = CGContextGetClipBoundingBox(context);\r
176     _width      = FXSYS_round(r.size.width);\r
177     _height     = FXSYS_round(r.size.height);\r
178     _renderCaps = FXRC_SOFT_CLIP | FXRC_BLEND_MODE |\r
179                   FXRC_ALPHA_PATH | FXRC_ALPHA_IMAGE |\r
180                   FXRC_BIT_MASK | FXRC_ALPHA_MASK;\r
181     if (_deviceClass != FXDC_DISPLAY) {\r
182     } else {\r
183         CGImageRef image = CGBitmapContextCreateImage(_context);\r
184         if (image) {\r
185             _renderCaps |= FXRC_GET_BITS;\r
186             _width = CGImageGetWidth(image);\r
187             _height = CGImageGetHeight(image);\r
188             CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(image);\r
189             if (kCGImageAlphaPremultipliedFirst == alphaInfo ||\r
190                     kCGImageAlphaPremultipliedLast == alphaInfo ||\r
191                     kCGImageAlphaOnly == alphaInfo) {\r
192                 _renderCaps |= FXRC_ALPHA_OUTPUT;\r
193             }\r
194         }\r
195         CGImageRelease(image);\r
196     }\r
197     CGAffineTransform ctm = CGContextGetCTM(_context);\r
198     CGContextSaveGState(_context);\r
199     m_saveCount++;\r
200     if (ctm.d >= 0) {\r
201         CGFloat offset_x, offset_y;\r
202         offset_x = ctm.tx;\r
203         offset_y = ctm.ty;\r
204         CGContextTranslateCTM(_context, -offset_x, -offset_y);\r
205         CGContextConcatCTM(_context, CGAffineTransformMake(1, 0, 0, -1, offset_x, _height + offset_y));\r
206     }\r
207     _foxitDevice2User = CGAffineTransformIdentity;\r
208     _user2FoxitDevice = CGAffineTransformInvert(_foxitDevice2User);\r
209 }\r
210 CFX_QuartzDeviceDriver::~CFX_QuartzDeviceDriver()\r
211 {\r
212     CGContextRestoreGState(_context);\r
213     m_saveCount--;\r
214     for (int i = 0; i < m_saveCount; ++i) {\r
215         CGContextRestoreGState(_context);\r
216     }\r
217     if (_context) {\r
218         CGContextRelease(_context);\r
219     }\r
220 }\r
221 int CFX_QuartzDeviceDriver::GetDeviceCaps(int capsID)\r
222 {\r
223     switch (capsID) {\r
224         case FXDC_DEVICE_CLASS: {\r
225                 return _deviceClass;\r
226             }\r
227         case FXDC_PIXEL_WIDTH: {\r
228                 return _width;\r
229             }\r
230         case FXDC_PIXEL_HEIGHT: {\r
231                 return _height;\r
232             }\r
233         case FXDC_BITS_PIXEL: {\r
234                 return 32;\r
235             }\r
236         case FXDC_RENDER_CAPS: {\r
237                 return _renderCaps;\r
238             }\r
239         default: {\r
240                 return 0;\r
241             }\r
242     }\r
243 }\r
244 CFX_Matrix CFX_QuartzDeviceDriver::GetCTM() const\r
245 {\r
246     CGAffineTransform ctm = CGContextGetCTM(_context);\r
247     return CFX_Matrix(ctm.a, ctm.b, ctm.c, ctm.d, ctm.tx, ctm.ty);\r
248 }\r
249 void CFX_QuartzDeviceDriver::SaveState()\r
250 {\r
251     CGContextSaveGState(_context);\r
252     m_saveCount++;\r
253 }\r
254 void CFX_QuartzDeviceDriver::RestoreState(FX_BOOL isKeepSaved )\r
255 {\r
256     CGContextRestoreGState(_context);\r
257     if (isKeepSaved) {\r
258         CGContextSaveGState(_context);\r
259     } else {\r
260         m_saveCount--;\r
261     }\r
262 }\r
263 FX_BOOL CFX_QuartzDeviceDriver::SetClip_PathFill(const CFX_PathData*    pathData,\r
264         const CFX_AffineMatrix*   matrix,\r
265         int                       fillMode )\r
266 {\r
267     SaveState();\r
268     CGAffineTransform m = CGAffineTransformIdentity;\r
269     if (matrix) {\r
270         m = CGAffineTransformMake(matrix->GetA(), matrix->GetB(), matrix->GetC(), matrix->GetD(), matrix->GetE(), matrix->GetF());\r
271     }\r
272     m = CGAffineTransformConcat(m, _foxitDevice2User);\r
273     CGContextConcatCTM(_context, m);\r
274     setPathToContext(pathData);\r
275     RestoreState(FALSE);\r
276     if ((fillMode & 3) == FXFILL_WINDING) {\r
277         CGContextClip(_context);\r
278     } else {\r
279         CGContextEOClip(_context);\r
280     }\r
281     return TRUE;\r
282 }\r
283 FX_FLOAT CFX_QuartzDeviceDriver::getLineWidth(const CFX_GraphStateData * graphState, CGAffineTransform ctm)\r
284 {\r
285     FX_FLOAT lineWidth = graphState->m_LineWidth;\r
286     if (graphState->m_LineWidth <= 0.f) {\r
287         CGSize size;\r
288         size.width = 1;\r
289         size.height = 1;\r
290         CGSize temp = CGSizeApplyAffineTransform(size, ctm);\r
291         CGFloat x = 1 / temp.width;\r
292         CGFloat y = 1 / temp.height;\r
293         lineWidth = x > y ? x : y;\r
294     }\r
295     return lineWidth;\r
296 }\r
297 FX_BOOL CFX_QuartzDeviceDriver::SetClip_PathStroke(const CFX_PathData*      pathData,\r
298         const CFX_AffineMatrix*     matrix,\r
299         const CFX_GraphStateData*   graphState )\r
300 {\r
301     SaveState();\r
302     CGAffineTransform m = CGAffineTransformIdentity;\r
303     if (matrix) {\r
304         m = CGAffineTransformMake(matrix->GetA(), matrix->GetB(), matrix->GetC(), matrix->GetD(), matrix->GetE(), matrix->GetF());\r
305     }\r
306     m = CGAffineTransformConcat(m, _foxitDevice2User);\r
307     CGContextConcatCTM(_context, m);\r
308     FX_FLOAT lineWidth = getLineWidth(graphState, m);\r
309     setStrokeInfo(graphState, 0xFF000000, lineWidth);\r
310     setPathToContext(pathData);\r
311     CGContextReplacePathWithStrokedPath(_context);\r
312     RestoreState(FALSE);\r
313     CGContextClip(_context);\r
314     return TRUE;\r
315 }\r
316 static CGBlendMode GetCGBlendMode(int blend_type)\r
317 {\r
318     CGBlendMode mode = kCGBlendModeNormal;\r
319     switch (blend_type) {\r
320         case FXDIB_BLEND_NORMAL:\r
321             mode = kCGBlendModeNormal;\r
322             break;\r
323         case FXDIB_BLEND_MULTIPLY:\r
324             mode = kCGBlendModeMultiply;\r
325             break;\r
326         case FXDIB_BLEND_SCREEN:\r
327             mode = kCGBlendModeScreen;\r
328             break;\r
329         case FXDIB_BLEND_OVERLAY:\r
330             mode = kCGBlendModeOverlay;\r
331             break;\r
332         case FXDIB_BLEND_DARKEN:\r
333             mode = kCGBlendModeDarken;\r
334             break;\r
335         case FXDIB_BLEND_LIGHTEN:\r
336             mode = kCGBlendModeLighten;\r
337             break;\r
338         case FXDIB_BLEND_COLORDODGE:\r
339             mode = kCGBlendModeColorDodge;\r
340             break;\r
341         case FXDIB_BLEND_COLORBURN:\r
342             mode = kCGBlendModeColorBurn;\r
343             break;\r
344         case FXDIB_BLEND_HARDLIGHT:\r
345             mode = kCGBlendModeHardLight;\r
346             break;\r
347         case FXDIB_BLEND_SOFTLIGHT:\r
348             mode = kCGBlendModeSoftLight;\r
349             break;\r
350         case FXDIB_BLEND_DIFFERENCE:\r
351             mode = kCGBlendModeDifference;\r
352             break;\r
353         case FXDIB_BLEND_EXCLUSION:\r
354             mode = kCGBlendModeExclusion;\r
355             break;\r
356         case FXDIB_BLEND_HUE:\r
357             mode = kCGBlendModeHue;\r
358             break;\r
359         case FXDIB_BLEND_SATURATION:\r
360             mode = kCGBlendModeSaturation;\r
361             break;\r
362         case FXDIB_BLEND_COLOR:\r
363             mode = kCGBlendModeColor;\r
364             break;\r
365         case FXDIB_BLEND_LUMINOSITY:\r
366             mode = kCGBlendModeLuminosity;\r
367             break;\r
368         default:\r
369             mode = kCGBlendModeNormal;\r
370             break;\r
371     }\r
372     return mode;\r
373 }\r
374 FX_BOOL CFX_QuartzDeviceDriver::DrawPath(const CFX_PathData*        pathData,\r
375         const CFX_AffineMatrix*       matrix,\r
376         const CFX_GraphStateData*     graphState,\r
377         FX_DWORD                      fillArgb,\r
378         FX_DWORD                      strokeArgb,\r
379         int                           fillMode,\r
380         int                           alpha_flag,\r
381         void*                         pIccTransform,\r
382         int                                                     blend_type\r
383                                         )\r
384 {\r
385     SaveState();\r
386     CGBlendMode mode = GetCGBlendMode(blend_type);\r
387     if (mode != kCGBlendModeNormal) {\r
388         CGContextSetBlendMode(_context, mode);\r
389     }\r
390     CGAffineTransform m = CGAffineTransformIdentity;\r
391     if (matrix) {\r
392         m = CGAffineTransformMake(matrix->GetA(), matrix->GetB(), matrix->GetC(), matrix->GetD(), matrix->GetE(), matrix->GetF());\r
393     }\r
394     m = CGAffineTransformConcat(m, _foxitDevice2User);\r
395     CGContextConcatCTM(_context, m);\r
396     int pathMode = 0;\r
397     if (graphState && strokeArgb) {\r
398         CGContextSetMiterLimit(_context, graphState->m_MiterLimit);\r
399         FX_FLOAT lineWidth = getLineWidth(graphState, m);\r
400         setStrokeInfo(graphState, strokeArgb, lineWidth);\r
401         pathMode |= 4;\r
402     }\r
403     if (fillMode && fillArgb) {\r
404         setFillInfo(fillArgb);\r
405         if ((fillMode & 3) == FXFILL_WINDING) {\r
406             pathMode |= 1;\r
407         } else if ((fillMode & 3) == FXFILL_ALTERNATE) {\r
408             pathMode |= 2;\r
409         }\r
410     }\r
411     setPathToContext(pathData);\r
412     if (fillMode & FXFILL_FULLCOVER) {\r
413         CGContextSetShouldAntialias(_context, false);\r
414     }\r
415     if (pathMode == 4) {\r
416         CGContextStrokePath(_context);\r
417     } else if (pathMode == 1) {\r
418         CGContextFillPath(_context);\r
419     } else if (pathMode == 2) {\r
420         CGContextEOFillPath(_context);\r
421     } else if (pathMode == 5) {\r
422         CGContextDrawPath(_context, kCGPathFillStroke);\r
423     } else if (pathMode == 6) {\r
424         CGContextDrawPath(_context, kCGPathEOFillStroke);\r
425     }\r
426     RestoreState(FALSE);\r
427     return TRUE;\r
428 }\r
429 FX_BOOL CFX_QuartzDeviceDriver::FillRect(const FX_RECT*         rect,\r
430         FX_ARGB                   fillArgb,\r
431         int                       alphaFlag        ,\r
432         void*                     iccTransform ,\r
433         int                                             blend_type )\r
434 {\r
435     CGBlendMode mode = GetCGBlendMode(blend_type);\r
436     if (mode != kCGBlendModeNormal) {\r
437         CGContextSetBlendMode(_context, mode);\r
438     }\r
439     CGRect rect_fx = CGRectMake(rect->left, rect->top, rect->Width(), rect->Height());\r
440     CGRect rect_usr = CGRectApplyAffineTransform(rect_fx, _foxitDevice2User);\r
441     FX_INT32 a, r, g, b;\r
442     ArgbDecode(fillArgb, a, r, g, b);\r
443     CGContextSetRGBFillColor(_context,\r
444                              r / 255.f,\r
445                              g / 255.f,\r
446                              b / 255.f,\r
447                              a / 255.f);\r
448     CGContextFillRect(_context, rect_usr);\r
449     if (mode != kCGBlendModeNormal) {\r
450         CGContextSetBlendMode(_context, kCGBlendModeNormal);\r
451     }\r
452     return TRUE;\r
453 }\r
454 FX_BOOL CFX_QuartzDeviceDriver::DrawCosmeticLine(FX_FLOAT           x1,\r
455         FX_FLOAT              y1,\r
456         FX_FLOAT              x2,\r
457         FX_FLOAT              y2,\r
458         FX_DWORD              argb,\r
459         int                   alphaFlag       ,\r
460         void*                 iccTransform    ,\r
461         int                                     blend_type )\r
462 {\r
463     CGBlendMode mode = GetCGBlendMode(blend_type);\r
464     if (mode != kCGBlendModeNormal) {\r
465         CGContextSetBlendMode(_context, mode);\r
466     }\r
467     CGPoint pt = CGPointApplyAffineTransform(CGPointMake(x1, y1), _foxitDevice2User);\r
468     x1 = pt.x;\r
469     y1 = pt.y;\r
470     pt = CGPointApplyAffineTransform(CGPointMake(x2, y2), _foxitDevice2User);\r
471     x2 = pt.x;\r
472     y2 = pt.y;\r
473     FX_INT32 a, r, g, b;\r
474     ArgbDecode(argb, a, r, g, b);\r
475     CGContextSetRGBStrokeColor(_context,\r
476                                r / 255.f,\r
477                                g / 255.f,\r
478                                b / 255.f,\r
479                                a / 255.f);\r
480     CGContextMoveToPoint(_context, x1, y1);\r
481     CGContextAddLineToPoint(_context, x2, y2);\r
482     CGContextStrokePath(_context);\r
483     if (mode != kCGBlendModeNormal) {\r
484         CGContextSetBlendMode(_context, kCGBlendModeNormal);\r
485     }\r
486     return TRUE;\r
487 }\r
488 FX_BOOL CFX_QuartzDeviceDriver::GetClipBox(FX_RECT* rect)\r
489 {\r
490     CGRect r = CGContextGetClipBoundingBox(_context);\r
491     r = CGRectApplyAffineTransform(r, _user2FoxitDevice);\r
492     rect->left          = FXSYS_floor(r.origin.x);\r
493     rect->top           = FXSYS_floor(r.origin.y);\r
494     rect->right         = FXSYS_ceil(r.origin.x + r.size.width);\r
495     rect->bottom        = FXSYS_ceil(r.origin.y + r.size.height);\r
496     return TRUE;\r
497 }\r
498 FX_BOOL CFX_QuartzDeviceDriver::GetDIBits(CFX_DIBitmap*     bitmap,\r
499         FX_INT32            left,\r
500         FX_INT32            top,\r
501         void* pIccTransform,\r
502         FX_BOOL bDEdge)\r
503 {\r
504     if (FXDC_PRINTER == _deviceClass) {\r
505         return FALSE;\r
506     }\r
507     if (bitmap->GetBPP() < 32) {\r
508         return FALSE;\r
509     }\r
510     if (!(_renderCaps | FXRC_GET_BITS)) {\r
511         return FALSE;\r
512     }\r
513     CGPoint pt = CGPointMake(left, top);\r
514     pt = CGPointApplyAffineTransform(pt, _foxitDevice2User);\r
515     CGAffineTransform ctm = CGContextGetCTM(_context);\r
516     pt.x *= FXSYS_fabs(ctm.a);\r
517     pt.y *= FXSYS_fabs(ctm.d);\r
518     CGImageRef image = CGBitmapContextCreateImage(_context);\r
519     if (NULL == image) {\r
520         return FALSE;\r
521     }\r
522     CGFloat width       = (CGFloat) bitmap->GetWidth();\r
523     CGFloat height      = (CGFloat) bitmap->GetHeight();\r
524     if (width + pt.x > _width) {\r
525         width -= (width + pt.x - _width);\r
526     }\r
527     if (height + pt.y > _height) {\r
528         height -= (height + pt.y - _height);\r
529     }\r
530     CGImageRef subImage = CGImageCreateWithImageInRect(image,\r
531                           CGRectMake(pt.x,\r
532                                      pt.y,\r
533                                      width,\r
534                                      height));\r
535     CGContextRef context = createContextWithBitmap(bitmap);\r
536     CGRect rect = CGContextGetClipBoundingBox(context);\r
537     CGContextClearRect(context, rect);\r
538     CGContextDrawImage(context, rect, subImage);\r
539     CGContextRelease(context);\r
540     CGImageRelease(subImage);\r
541     CGImageRelease(image);\r
542     if (bitmap->HasAlpha()) {\r
543         for (int row = 0; row < bitmap->GetHeight(); row ++) {\r
544             FX_LPBYTE pScanline = (FX_LPBYTE)bitmap->GetScanline(row);\r
545             for (int col = 0; col < bitmap->GetWidth(); col ++) {\r
546                 if (pScanline[3] > 0) {\r
547                     pScanline[0] = (pScanline[0] * 255.f / pScanline[3] + .5f);\r
548                     pScanline[1] = (pScanline[1] * 255.f / pScanline[3] + .5f);\r
549                     pScanline[2] = (pScanline[2] * 255.f / pScanline[3] + .5f);\r
550                 }\r
551                 pScanline += 4;\r
552             }\r
553         }\r
554     }\r
555     return TRUE;\r
556 }\r
557 FX_BOOL CFX_QuartzDeviceDriver::SetDIBits(const CFX_DIBSource*      pBitmap,\r
558         FX_ARGB                     argb,\r
559         const FX_RECT*              srcRect,\r
560         int                         dest_left,\r
561         int                         dest_top,\r
562         int                         blendType,\r
563         int                         alphaFlag       ,\r
564         void*                       iccTransform    )\r
565 {\r
566     SaveState();\r
567     CGFloat src_left, src_top, src_width, src_height;\r
568     if (srcRect) {\r
569         src_left = srcRect->left;\r
570         src_top = srcRect->top;\r
571         src_width = srcRect->Width();\r
572         src_height = srcRect->Height();\r
573     } else {\r
574         src_left = src_top = 0;\r
575         src_width = pBitmap->GetWidth();\r
576         src_height = pBitmap->GetHeight();\r
577     }\r
578     CGAffineTransform ctm = CGContextGetCTM(_context);\r
579     CGFloat scale_x = FXSYS_fabs(ctm.a);\r
580     CGFloat scale_y = FXSYS_fabs(ctm.d);\r
581     src_left /= scale_x;\r
582     src_top /= scale_y;\r
583     src_width /= scale_x;\r
584     src_height /= scale_y;\r
585     CGRect rect_fx = CGRectMake(dest_left, dest_top, src_width, src_height);\r
586     CGRect rect_usr = CGRectApplyAffineTransform(rect_fx, _foxitDevice2User);\r
587     CGContextBeginPath(_context);\r
588     CGContextAddRect(_context, rect_usr);\r
589     CGContextClip(_context);\r
590     rect_usr.size = CGSizeMake(pBitmap->GetWidth() / scale_x, pBitmap->GetHeight() / scale_y);\r
591     rect_usr = CGRectOffset(rect_usr, -src_left, -src_top);\r
592     CG_SetImageTransform(dest_left, dest_top, src_width, src_height, &rect_usr);\r
593     CFX_DIBitmap* pBitmap1 = NULL;\r
594     if (pBitmap->IsAlphaMask()) {\r
595         if (pBitmap->GetBuffer()) {\r
596             pBitmap1 = (CFX_DIBitmap*)pBitmap;\r
597         } else {\r
598             pBitmap1 = pBitmap->Clone();\r
599         }\r
600         if (NULL == pBitmap1) {\r
601             RestoreState(FALSE);\r
602             return FALSE;\r
603         }\r
604         CGDataProviderRef pBitmapProvider = CGDataProviderCreateWithData(NULL,\r
605                                             pBitmap1->GetBuffer(),\r
606                                             pBitmap1->GetPitch() * pBitmap1->GetHeight(),\r
607                                             NULL);\r
608         CGColorSpaceRef pColorSpace = CGColorSpaceCreateDeviceGray();\r
609         CGBitmapInfo bitmapInfo = kCGImageAlphaNone | kCGBitmapByteOrderDefault;\r
610         CGImageRef pImage = CGImageCreate(pBitmap1->GetWidth(),\r
611                                           pBitmap1->GetHeight(),\r
612                                           pBitmap1->GetBPP(),\r
613                                           pBitmap1->GetBPP(),\r
614                                           pBitmap1->GetPitch(),\r
615                                           pColorSpace,\r
616                                           bitmapInfo,\r
617                                           pBitmapProvider, NULL, true,\r
618                                           kCGRenderingIntentDefault);\r
619         CGContextClipToMask(_context, rect_usr, pImage);\r
620         CGContextSetRGBFillColor(_context,\r
621                                  FXARGB_R(argb) / 255.f,\r
622                                  FXARGB_G(argb) / 255.f,\r
623                                  FXARGB_B(argb) / 255.f,\r
624                                  FXARGB_A(argb) / 255.f);\r
625         CGContextFillRect(_context, rect_usr);\r
626         CGImageRelease(pImage);\r
627         CGColorSpaceRelease(pColorSpace);\r
628         CGDataProviderRelease(pBitmapProvider);\r
629         if (pBitmap1 != pBitmap) {\r
630             delete pBitmap1;\r
631         }\r
632         RestoreState(FALSE);\r
633         return TRUE;\r
634     }\r
635     if (pBitmap->GetBPP() < 32) {\r
636         pBitmap1 = pBitmap->CloneConvert(FXDIB_Rgb32);\r
637     } else {\r
638         if (pBitmap->GetBuffer()) {\r
639             pBitmap1 = (CFX_DIBitmap*)pBitmap;\r
640         } else {\r
641             pBitmap1 = pBitmap->Clone();\r
642         }\r
643     }\r
644     if (NULL == pBitmap1) {\r
645         RestoreState(FALSE);\r
646         return FALSE;\r
647     }\r
648     if (pBitmap1->HasAlpha()) {\r
649         if (pBitmap1 == pBitmap) {\r
650             pBitmap1 = pBitmap->Clone();\r
651             if (!pBitmap1) {\r
652                 RestoreState(FALSE);\r
653                 return FALSE;\r
654             }\r
655         }\r
656         for (int row = 0; row < pBitmap1->GetHeight(); row ++) {\r
657             FX_LPBYTE pScanline = (FX_LPBYTE)pBitmap1->GetScanline(row);\r
658             for (int col = 0; col < pBitmap1->GetWidth(); col ++) {\r
659                 pScanline[0] = (FX_BYTE)(pScanline[0] * pScanline[3] / 255.f + .5f);\r
660                 pScanline[1] = (FX_BYTE)(pScanline[1] * pScanline[3] / 255.f + .5f);\r
661                 pScanline[2] = (FX_BYTE)(pScanline[2] * pScanline[3] / 255.f + .5f);\r
662                 pScanline += 4;\r
663             }\r
664         }\r
665     }\r
666     CGContextRef ctx = createContextWithBitmap(pBitmap1);\r
667     CGImageRef image = CGBitmapContextCreateImage(ctx);\r
668     int blend_mode = blendType;\r
669     if (FXDIB_BLEND_HARDLIGHT == blendType) {\r
670         blend_mode = kCGBlendModeSoftLight;\r
671     } else if (FXDIB_BLEND_SOFTLIGHT == blendType) {\r
672         blend_mode = kCGBlendModeHardLight;\r
673     } else if (blendType >= FXDIB_BLEND_NONSEPARABLE && blendType <= FXDIB_BLEND_LUMINOSITY) {\r
674         blend_mode = blendType - 9;\r
675     } else if (blendType > FXDIB_BLEND_LUMINOSITY || blendType < 0) {\r
676         blend_mode = kCGBlendModeNormal;\r
677     }\r
678     CGContextSetBlendMode(_context, (CGBlendMode)blend_mode);\r
679     CGContextDrawImage(_context, rect_usr, image);\r
680     CGImageRelease(image);\r
681     CGContextRelease(ctx);\r
682     if (pBitmap1 != pBitmap) {\r
683         delete pBitmap1;\r
684     }\r
685     RestoreState(FALSE);\r
686     return TRUE;\r
687 }\r
688 FX_BOOL CFX_QuartzDeviceDriver::StretchDIBits(const CFX_DIBSource*      pBitmap,\r
689         FX_ARGB                     argb,\r
690         int                         dest_left,\r
691         int                         dest_top,\r
692         int                         dest_width,\r
693         int                         dest_height,\r
694         const FX_RECT*              clipRect,\r
695         FX_DWORD                    flags,\r
696         int                         alphaFlag      ,\r
697         void*                       iccTransform ,\r
698         int                                                     blend_type)\r
699 {\r
700     SaveState();\r
701     if (clipRect) {\r
702         CGContextBeginPath(_context);\r
703         CGRect rect_clip = CGRectMake(clipRect->left, clipRect->top, clipRect->Width(), clipRect->Height());\r
704         rect_clip = CGRectApplyAffineTransform(rect_clip, _foxitDevice2User);\r
705         CGContextAddRect(_context, rect_clip);\r
706         CGContextClip(_context);\r
707     }\r
708     CGRect rect = CGRectMake(dest_left, dest_top, dest_width, dest_height);\r
709     rect = CGRectApplyAffineTransform(rect, _foxitDevice2User);\r
710     if (FXDIB_BICUBIC_INTERPOL == flags) {\r
711         CGContextSetInterpolationQuality(_context, kCGInterpolationHigh);\r
712     } else if (FXDIB_DOWNSAMPLE == flags) {\r
713         CGContextSetInterpolationQuality(_context, kCGInterpolationNone);\r
714     } else {\r
715         CGContextSetInterpolationQuality(_context, kCGInterpolationMedium);\r
716     }\r
717     CG_SetImageTransform(dest_left, dest_top, dest_width, dest_height);\r
718     CFX_DIBitmap* pBitmap1 = NULL;\r
719     if (pBitmap->IsAlphaMask()) {\r
720         if (pBitmap->GetBuffer()) {\r
721             pBitmap1 = (CFX_DIBitmap*)pBitmap;\r
722         } else {\r
723             pBitmap1 = pBitmap->Clone();\r
724         }\r
725         if (NULL == pBitmap1) {\r
726             RestoreState(FALSE);\r
727             return FALSE;\r
728         }\r
729         CGDataProviderRef pBitmapProvider = CGDataProviderCreateWithData(NULL,\r
730                                             pBitmap1->GetBuffer(),\r
731                                             pBitmap1->GetPitch() * pBitmap1->GetHeight(),\r
732                                             NULL);\r
733         CGColorSpaceRef pColorSpace = CGColorSpaceCreateDeviceGray();\r
734         CGBitmapInfo bitmapInfo = kCGImageAlphaNone | kCGBitmapByteOrderDefault;\r
735         CGImageRef pImage = CGImageCreate(pBitmap1->GetWidth(),\r
736                                           pBitmap1->GetHeight(),\r
737                                           pBitmap1->GetBPP(),\r
738                                           pBitmap1->GetBPP(),\r
739                                           pBitmap1->GetPitch(),\r
740                                           pColorSpace,\r
741                                           bitmapInfo,\r
742                                           pBitmapProvider, NULL, true,\r
743                                           kCGRenderingIntentDefault);\r
744         CGContextClipToMask(_context, rect, pImage);\r
745         CGContextSetRGBFillColor(_context,\r
746                                  FXARGB_R(argb) / 255.f,\r
747                                  FXARGB_G(argb) / 255.f,\r
748                                  FXARGB_B(argb) / 255.f,\r
749                                  FXARGB_A(argb) / 255.f);\r
750         CGContextFillRect(_context, rect);\r
751         CGImageRelease(pImage);\r
752         CGColorSpaceRelease(pColorSpace);\r
753         CGDataProviderRelease(pBitmapProvider);\r
754         if (pBitmap1 != pBitmap) {\r
755             delete pBitmap1;\r
756         }\r
757         RestoreState(FALSE);\r
758         return TRUE;\r
759     }\r
760     if (pBitmap->GetBPP() < 32) {\r
761         pBitmap1 = pBitmap->CloneConvert(FXDIB_Rgb32);\r
762     } else {\r
763         if (pBitmap->GetBuffer()) {\r
764             pBitmap1 = (CFX_DIBitmap*)pBitmap;\r
765         } else {\r
766             pBitmap1 = pBitmap->Clone();\r
767         }\r
768     }\r
769     if (NULL == pBitmap1) {\r
770         RestoreState(FALSE);\r
771         return FALSE;\r
772     }\r
773     if (pBitmap1->HasAlpha()) {\r
774         if (pBitmap1 == pBitmap) {\r
775             pBitmap1 = pBitmap->Clone();\r
776             if (!pBitmap1) {\r
777                 RestoreState(FALSE);\r
778                 return FALSE;\r
779             }\r
780         }\r
781         for (int row = 0; row < pBitmap1->GetHeight(); row ++) {\r
782             FX_LPBYTE pScanline = (FX_LPBYTE)pBitmap1->GetScanline(row);\r
783             for (int col = 0; col < pBitmap1->GetWidth(); col ++) {\r
784                 pScanline[0] = (FX_BYTE)(pScanline[0] * pScanline[3] / 255.f + .5f);\r
785                 pScanline[1] = (FX_BYTE)(pScanline[1] * pScanline[3] / 255.f + .5f);\r
786                 pScanline[2] = (FX_BYTE)(pScanline[2] * pScanline[3] / 255.f + .5f);\r
787                 pScanline += 4;\r
788             }\r
789         }\r
790     }\r
791     CGContextRef ctx = createContextWithBitmap(pBitmap1);\r
792     CGImageRef image = CGBitmapContextCreateImage(ctx);\r
793     CGContextDrawImage(_context, rect, image);\r
794     CGImageRelease(image);\r
795     CGContextRelease(ctx);\r
796     if (pBitmap1 != pBitmap) {\r
797         delete pBitmap1;\r
798     }\r
799     RestoreState(FALSE);\r
800     return TRUE;\r
801 }\r
802 FX_BOOL CFX_QuartzDeviceDriver::CG_DrawGlypRun(int                        nChars,\r
803         const FXTEXT_CHARPOS*      pCharPos,\r
804         CFX_Font*                  pFont,\r
805         CFX_FontCache*             pCache,\r
806         const CFX_AffineMatrix*    pGlyphMatrix,\r
807         const CFX_AffineMatrix*    pObject2Device,\r
808         FX_FLOAT                   font_size,\r
809         FX_DWORD                   argb,\r
810         int                        alpha_flag,\r
811         void*                      pIccTransform)\r
812 {\r
813     if (nChars == 0) {\r
814         return TRUE;\r
815     }\r
816     CQuartz2D& quartz2d = ((CApplePlatform *) CFX_GEModule::Get()->GetPlatformData())->_quartz2d;\r
817     if (!pFont->m_pPlatformFont) {\r
818         if (pFont->GetPsName() == CFX_WideString::FromLocal("DFHeiStd-W5")) {\r
819             return FALSE;\r
820         }\r
821         pFont->m_pPlatformFont = quartz2d.CreateFont(pFont->m_pFontData, pFont->m_dwSize);\r
822         if (NULL == pFont->m_pPlatformFont) {\r
823             return FALSE;\r
824         }\r
825     }\r
826     CFX_FixedBufGrow<FX_WORD, 32> glyph_indices(nChars);\r
827     CFX_FixedBufGrow<CGPoint, 32> glyph_positions(nChars);\r
828     for (int i = 0; i < nChars; i++ ) {\r
829         glyph_indices[i] = pCharPos[i].m_ExtGID;\r
830         glyph_positions[i].x = pCharPos[i].m_OriginX;\r
831         glyph_positions[i].y = pCharPos[i].m_OriginY;\r
832     }\r
833     CFX_AffineMatrix text_matrix;\r
834     if (pObject2Device) {\r
835         text_matrix.Concat(*pObject2Device);\r
836     }\r
837     CGAffineTransform matrix_cg = CGAffineTransformMake(text_matrix.a,\r
838                                   text_matrix.b,\r
839                                   text_matrix.c,\r
840                                   text_matrix.d,\r
841                                   text_matrix.e,\r
842                                   text_matrix.f);\r
843     matrix_cg = CGAffineTransformConcat(matrix_cg, _foxitDevice2User);\r
844     CGContextSetTextMatrix(_context, matrix_cg);\r
845     CGContextSetFont(_context, (CGFontRef)pFont->m_pPlatformFont);\r
846     CGContextSetFontSize(_context, FXSYS_fabs(font_size));\r
847     FX_INT32 a, r, g, b;\r
848     ArgbDecode(argb, a, r, g, b);\r
849     CGContextSetRGBFillColor(_context,\r
850                              r / 255.f,\r
851                              g / 255.f,\r
852                              b / 255.f,\r
853                              a / 255.f);\r
854     SaveState();\r
855     if (pGlyphMatrix) {\r
856         CGAffineTransform ctm = CGContextGetCTM(_context);\r
857         CGPoint origin = CGPointMake( glyph_positions[0].x,  glyph_positions[0].y);\r
858         origin = CGPointApplyAffineTransform(origin, matrix_cg);\r
859         CGContextTranslateCTM(_context, origin.x, origin.y);\r
860         CGAffineTransform glyph_matrix = CGAffineTransformMake(pGlyphMatrix->a,\r
861                                          pGlyphMatrix->b,\r
862                                          pGlyphMatrix->c,\r
863                                          pGlyphMatrix->d,\r
864                                          pGlyphMatrix->e,\r
865                                          pGlyphMatrix->f);\r
866         if (_foxitDevice2User.d < 0) {\r
867             glyph_matrix = CGAffineTransformInvert(glyph_matrix);\r
868         }\r
869         CGContextConcatCTM(_context, glyph_matrix);\r
870         CGContextTranslateCTM(_context, -origin.x, -origin.y);\r
871     }\r
872     CGContextShowGlyphsAtPositions(_context,\r
873                                    (CGGlyph*)glyph_indices,\r
874                                    glyph_positions,\r
875                                    nChars);\r
876     RestoreState(FALSE);\r
877     return TRUE;\r
878 }\r
879 FX_BOOL CFX_QuartzDeviceDriver::DrawDeviceText(int                      nChars,\r
880         const FXTEXT_CHARPOS*    pCharPos,\r
881         CFX_Font*                pFont,\r
882         CFX_FontCache*           pCache,\r
883         const CFX_AffineMatrix*  pObject2Device,\r
884         FX_FLOAT                 font_size,\r
885         FX_DWORD                 color,\r
886         int                      alpha_flag       ,\r
887         void*                    pIccTransform)\r
888 {\r
889     if (NULL == pFont || NULL == _context) {\r
890         return FALSE;\r
891     }\r
892     FX_BOOL bBold = pFont->IsBold();\r
893     if (!bBold && pFont->GetSubstFont() &&\r
894             pFont->GetSubstFont()->m_Weight >= 500 &&\r
895             pFont->GetSubstFont()->m_Weight <= 600) {\r
896         return FALSE;\r
897     }\r
898     SaveState();\r
899     CGContextSetTextDrawingMode(_context, kCGTextFillClip);\r
900     FX_BOOL ret = FALSE;\r
901     FX_INT32 i = 0;\r
902     while (i < nChars) {\r
903         if (pCharPos[i].m_bGlyphAdjust || font_size < 0) {\r
904             if (i > 0) {\r
905                 ret = CG_DrawGlypRun(i, pCharPos, pFont, pCache, NULL, pObject2Device, font_size, color, alpha_flag, pIccTransform);\r
906                 if (!ret) {\r
907                     RestoreState(FALSE);\r
908                     return ret;\r
909                 }\r
910             }\r
911             const FXTEXT_CHARPOS* char_pos = pCharPos + i;\r
912             CFX_AffineMatrix glphy_matrix;\r
913             if (font_size < 0) {\r
914                 glphy_matrix.Concat(-1, 0, 0, -1, 0, 0);\r
915             }\r
916             if (char_pos->m_bGlyphAdjust) {\r
917                 glphy_matrix.Concat(char_pos->m_AdjustMatrix[0],\r
918                                     char_pos->m_AdjustMatrix[1],\r
919                                     char_pos->m_AdjustMatrix[2],\r
920                                     char_pos->m_AdjustMatrix[3], 0, 0);\r
921             }\r
922             ret = CG_DrawGlypRun(1, char_pos, pFont, pCache, &glphy_matrix, pObject2Device, font_size, color, alpha_flag, pIccTransform);\r
923             if (!ret) {\r
924                 RestoreState(FALSE);\r
925                 return ret;\r
926             }\r
927             i ++;\r
928             pCharPos += i;\r
929             nChars -= i;\r
930             i = 0;\r
931         } else {\r
932             i ++;\r
933         }\r
934     }\r
935     if (i > 0) {\r
936         ret = CG_DrawGlypRun(i, pCharPos, pFont, pCache, NULL, pObject2Device, font_size, color, alpha_flag, pIccTransform);\r
937     }\r
938     RestoreState(FALSE);\r
939     return ret;\r
940 }\r
941 void CFX_QuartzDeviceDriver::setStrokeInfo(const CFX_GraphStateData* graphState, FX_ARGB argb, FX_FLOAT lineWidth)\r
942 {\r
943     if (NULL == graphState) {\r
944         return;\r
945     }\r
946     CGContextSetLineWidth(_context, lineWidth);\r
947     CGLineCap cap;\r
948     switch (graphState->m_LineCap) {\r
949         case CFX_GraphStateData::LineCapRound: {\r
950                 cap = kCGLineCapRound;\r
951                 break;\r
952             }\r
953         case CFX_GraphStateData::LineCapSquare: {\r
954                 cap = kCGLineCapSquare;\r
955                 break;\r
956             }\r
957         case CFX_GraphStateData::LineCapButt:\r
958         default: {\r
959                 cap = kCGLineCapButt;\r
960             }\r
961     }\r
962     CGContextSetLineCap(_context, cap);\r
963     CGLineJoin join;\r
964     switch (graphState->m_LineJoin) {\r
965         case CFX_GraphStateData::LineJoinRound: {\r
966                 join = kCGLineJoinRound;\r
967                 break;\r
968             }\r
969         case CFX_GraphStateData::LineJoinBevel: {\r
970                 join = kCGLineJoinBevel;\r
971                 break;\r
972             }\r
973         case CFX_GraphStateData::LineJoinMiter:\r
974         default: {\r
975                 join = kCGLineJoinMiter;\r
976             }\r
977     }\r
978     CGContextSetLineJoin(_context, join);\r
979     if (graphState->m_DashCount) {\r
980 #if CGFLOAT_IS_DOUBLE\r
981         CGFloat* dashArray = new CGFloat[graphState->m_DashCount];\r
982         if (!dashArray) {\r
983             return;\r
984         }\r
985         for (int index = 0; index < graphState->m_DashCount; ++index) {\r
986             dashArray[index] = graphState->m_DashArray[index];\r
987         }\r
988 #else\r
989         CGFloat* dashArray = (CGFloat*)graphState->m_DashArray;\r
990 #endif\r
991         CGContextSetLineDash(_context, graphState->m_DashPhase, dashArray, graphState->m_DashCount);\r
992 #if CGFLOAT_IS_DOUBLE\r
993         delete[] dashArray;\r
994 #endif\r
995     }\r
996     FX_INT32 a, r, g, b;\r
997     ArgbDecode(argb, a, r, g, b);\r
998     CGContextSetRGBStrokeColor(_context,\r
999                                r / 255.f,\r
1000                                g / 255.f,\r
1001                                b / 255.f,\r
1002                                a / 255.f);\r
1003 }\r
1004 void CFX_QuartzDeviceDriver::setFillInfo(FX_ARGB argb)\r
1005 {\r
1006     FX_INT32 a, r, g, b;\r
1007     ArgbDecode(argb, a, r, g, b);\r
1008     CGContextSetRGBFillColor(_context,\r
1009                              r / 255.f,\r
1010                              g / 255.f,\r
1011                              b / 255.f,\r
1012                              a / 255.f);\r
1013 }\r
1014 void CFX_QuartzDeviceDriver::setPathToContext(const CFX_PathData* pathData)\r
1015 {\r
1016     FX_INT32 count = pathData->GetPointCount();\r
1017     FX_PATHPOINT* points = pathData->GetPoints();\r
1018     CGContextBeginPath(_context);\r
1019     for (FX_INT32 i = 0; i < count; i ++) {\r
1020         switch (points[i].m_Flag & FXPT_TYPE) {\r
1021             case FXPT_MOVETO:\r
1022                 CGContextMoveToPoint(_context, points[i].m_PointX, points[i].m_PointY);\r
1023                 break;\r
1024             case FXPT_LINETO:\r
1025                 CGContextAddLineToPoint(_context, points[i].m_PointX, points[i].m_PointY);\r
1026                 break;\r
1027             case FXPT_BEZIERTO: {\r
1028                     CGContextAddCurveToPoint(_context,\r
1029                                              points[i].m_PointX, points[i].m_PointY,\r
1030                                              points[i + 1].m_PointX, points[i + 1].m_PointY,\r
1031                                              points[i + 2].m_PointX, points[i + 2].m_PointY);\r
1032                     i += 2;\r
1033                 }\r
1034         }\r
1035         if (points[i].m_Flag & FXPT_CLOSEFIGURE) {\r
1036             CGContextClosePath(_context);\r
1037         }\r
1038     }\r
1039 }\r
1040 void CFX_QuartzDeviceDriver::CG_SetImageTransform(int dest_left, int dest_top, int dest_width, int dest_height,\r
1041         CGRect* rect )\r
1042 {\r
1043     int flip_y = _foxitDevice2User.d * dest_height < 0 ? 1 : -1;\r
1044     int flip_x = _foxitDevice2User.a * dest_width > 0 ? 1 : -1;\r
1045     if (flip_y < 0 || flip_x < 0) {\r
1046         if (dest_height < 0) {\r
1047             dest_height = -dest_height;\r
1048             dest_top -= dest_height;\r
1049         }\r
1050         CGRect rt = CGRectApplyAffineTransform(CGRectMake(dest_left, dest_top, dest_width, dest_height), _foxitDevice2User);\r
1051         CGFloat offset_x = (rt.origin.x) + rt.size.width / 2.f,\r
1052                 offset_y = (rt.origin.y) + rt.size.height / 2.f;\r
1053         CGAffineTransform transform = CGAffineTransformIdentity;\r
1054         transform = CGAffineTransformConcat(transform, CGAffineTransformMake(1, 0, 0, 1, -offset_x, -offset_y));\r
1055         transform = CGAffineTransformConcat(transform, CGAffineTransformMake(flip_x, 0, 0, flip_y, 0, 0));\r
1056         transform = CGAffineTransformConcat(transform, CGAffineTransformMake(1, 0, 0, 1, offset_x, offset_y));\r
1057         CGContextConcatCTM(_context, transform);\r
1058         if (rect) {\r
1059             *rect = CGRectApplyAffineTransform(*rect, transform);\r
1060         }\r
1061     }\r
1062 }\r
1063 void CFX_QuartzDeviceDriver::ClearDriver()\r
1064 {\r
1065     if (NULL == _context) {\r
1066         return;\r
1067     }\r
1068     for (int i = 0; i < m_saveCount; ++i) {\r
1069         CGContextRestoreGState(_context);\r
1070     }\r
1071     m_saveCount = 0;\r
1072     if (_context) {\r
1073         CGContextRelease(_context);\r
1074     }\r
1075 }\r
1076 CFX_QuartzDevice::CFX_QuartzDevice()\r
1077 {\r
1078     m_bOwnedBitmap = FALSE;\r
1079     m_pContext = NULL;\r
1080 }\r
1081 CFX_QuartzDevice::~CFX_QuartzDevice()\r
1082 {\r
1083     if (m_pContext) {\r
1084         CGContextRelease(m_pContext);\r
1085     }\r
1086     if (GetBitmap() && m_bOwnedBitmap) {\r
1087         delete GetBitmap();\r
1088     }\r
1089 }\r
1090 CGContextRef CFX_QuartzDevice::GetContext()\r
1091 {\r
1092     return m_pContext;\r
1093 }\r
1094 FX_BOOL CFX_QuartzDevice::Attach(CGContextRef context, FX_INT32 nDeviceClass)\r
1095 {\r
1096     if (m_pContext) {\r
1097         CGContextRelease(m_pContext);\r
1098     }\r
1099     m_pContext = context;\r
1100     CGContextRetain(m_pContext);\r
1101     IFX_RenderDeviceDriver* pDriver = FX_NEW CFX_QuartzDeviceDriver(m_pContext, nDeviceClass);\r
1102     if (!pDriver) {\r
1103         return FALSE;\r
1104     }\r
1105     SetDeviceDriver(pDriver);\r
1106     return TRUE;\r
1107 }\r
1108 FX_BOOL CFX_QuartzDevice::Attach(CFX_DIBitmap* pBitmap)\r
1109 {\r
1110     SetBitmap(pBitmap);\r
1111     m_pContext = createContextWithBitmap(pBitmap);\r
1112     if (NULL == m_pContext) {\r
1113         return FALSE;\r
1114     }\r
1115     IFX_RenderDeviceDriver* pDriver = FX_NEW CFX_QuartzDeviceDriver(m_pContext, FXDC_DISPLAY);\r
1116     if (!pDriver) {\r
1117         return FALSE;\r
1118     }\r
1119     SetDeviceDriver(pDriver);\r
1120     return TRUE;\r
1121 }\r
1122 FX_BOOL CFX_QuartzDevice::Create(FX_INT32 width, FX_INT32 height, FXDIB_Format format)\r
1123 {\r
1124     if ((FX_BYTE)format < 32) {\r
1125         return FALSE;\r
1126     }\r
1127     CFX_DIBitmap* pBitmap = FX_NEW CFX_DIBitmap;\r
1128     if (!pBitmap) {\r
1129         return FALSE;\r
1130     }\r
1131     if (!pBitmap->Create(width, height, format)) {\r
1132         delete pBitmap;\r
1133         return FALSE;\r
1134     }\r
1135     m_bOwnedBitmap = TRUE;\r
1136     return Attach(pBitmap);\r
1137 }\r
1138 #endif\r