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