Fix fix of mac build breakage at 2a2a6aa
[pdfium.git] / core / src / fxge / apple / fx_quartz_device.cpp
index bef4516..61a0cef 100644 (file)
-// Copyright 2014 PDFium Authors. All rights reserved.\r
-// Use of this source code is governed by a BSD-style license that can be\r
-// found in the LICENSE file.\r
\r
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com\r
-\r
-#include "../../../include/fxcrt/fx_ext.h"\r
-#include "../../../include/fxge/fx_ge.h"\r
-#include "../agg/include/fxfx_agg_clip_liang_barsky.h"\r
-#include "../ge/text_int.h"\r
-#include "../dib/dib_int.h"\r
-#include "../agg/include/fx_agg_driver.h"\r
-#include "../../../include/fxge/fx_freetype.h"\r
-#if _FXM_PLATFORM_  == _FXM_PLATFORM_APPLE_\r
-#include "apple_int.h"\r
-#include "../../../include/fxge/fx_ge_apple.h"\r
-#ifndef CGFLOAT_IS_DOUBLE\r
-#error Expected CGFLOAT_IS_DOUBLE to be defined by CoreGraphics headers\r
-#endif\r
-void* CQuartz2D::createGraphics(CFX_DIBitmap* pBitmap)\r
-{\r
-    if (!pBitmap) {\r
-        return NULL;\r
-    }\r
-    CGBitmapInfo bmpInfo = kCGBitmapByteOrder32Little;\r
-    switch (pBitmap->GetFormat()) {\r
-        case FXDIB_Rgb32:\r
-            bmpInfo |= kCGImageAlphaNoneSkipFirst;\r
-            break;\r
-        case FXDIB_Argb:\r
-        default:\r
-            return NULL;\r
-    }\r
-    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();\r
-    CGContextRef context = CGBitmapContextCreate(pBitmap->GetBuffer(),\r
-                           pBitmap->GetWidth(),\r
-                           pBitmap->GetHeight(),\r
-                           8,\r
-                           pBitmap->GetPitch(),\r
-                           colorSpace,\r
-                           bmpInfo);\r
-    CGColorSpaceRelease(colorSpace);\r
-    return context;\r
-}\r
-void CQuartz2D::destroyGraphics(void* graphics)\r
-{\r
-    if (graphics) {\r
-        CGContextRelease((CGContextRef) graphics);\r
-    }\r
-}\r
-void* CQuartz2D::CreateFont(FX_LPCBYTE pFontData, FX_DWORD dwFontSize)\r
-{\r
-    CGDataProviderRef pDataProvider = CGDataProviderCreateWithData(NULL, pFontData, (size_t)dwFontSize, NULL);\r
-    if (NULL == pDataProvider) {\r
-        return NULL;\r
-    }\r
-    CGFontRef pCGFont = CGFontCreateWithDataProvider(pDataProvider);\r
-    CGDataProviderRelease(pDataProvider);\r
-    return pCGFont;\r
-}\r
-void CQuartz2D::DestroyFont(void* pFont)\r
-{\r
-    CGFontRelease((CGFontRef)pFont);\r
-}\r
-void CQuartz2D::setGraphicsTextMatrix(void* graphics, CFX_AffineMatrix* matrix)\r
-{\r
-    if (!graphics || !matrix) {\r
-        return;\r
-    }\r
-    CGContextRef context = (CGContextRef) graphics;\r
-    CGFloat ty = CGBitmapContextGetHeight(context) - matrix->f;\r
-    CGContextSetTextMatrix(context, CGAffineTransformMake(matrix->a,\r
-                           matrix->b,\r
-                           matrix->c,\r
-                           matrix->d,\r
-                           matrix->e,\r
-                           ty));\r
-}\r
-FX_BOOL CQuartz2D::drawGraphicsString(void*                 graphics,\r
-                                      void*                 font,\r
-                                      FX_FLOAT              fontSize,\r
-                                      FX_WORD*              glyphIndices,\r
-                                      CGPoint*           glyphPositions,\r
-                                      FX_INT32              charsCount,\r
-                                      FX_ARGB               argb,\r
-                                      CFX_AffineMatrix*     matrix )\r
-{\r
-    if (!graphics) {\r
-        return FALSE;\r
-    }\r
-    CGContextRef context = (CGContextRef) graphics;\r
-    CGContextSetFont(context, (CGFontRef)font);\r
-    CGContextSetFontSize(context, fontSize);\r
-    if (matrix) {\r
-        CGAffineTransform m = CGContextGetTextMatrix(context);\r
-        m = CGAffineTransformConcat(m,\r
-                                    CGAffineTransformMake(matrix->a,\r
-                                            matrix->b,\r
-                                            matrix->c,\r
-                                            matrix->d,\r
-                                            matrix->e,\r
-                                            matrix->f));\r
-        CGContextSetTextMatrix(context, m);\r
-    }\r
-    FX_INT32 a, r, g, b;\r
-    ArgbDecode(argb, a, r, g, b);\r
-    CGContextSetRGBFillColor(context,\r
-                             r / 255.f,\r
-                             g / 255.f,\r
-                             b / 255.f,\r
-                             a / 255.f);\r
-    CGContextSaveGState(context);\r
-#if CGFLOAT_IS_DOUBLE\r
-    CGPoint* glyphPositionsCG = new CGPoint[charsCount];\r
-    if (!glyphPositionsCG) {\r
-        return FALSE;\r
-    }\r
-    for (int index = 0; index < charsCount; ++index) {\r
-        glyphPositionsCG[index].x = glyphPositions[index].x;\r
-        glyphPositionsCG[index].y = glyphPositions[index].y;\r
-    }\r
-#else\r
-    CGPoint* glyphPositionsCG = (CGPoint*)glyphPositions;\r
-#endif\r
-    CGContextShowGlyphsAtPositions(context,\r
-                                   (CGGlyph *) glyphIndices,\r
-                                   glyphPositionsCG,\r
-                                   charsCount);\r
-#if CGFLOAT_IS_DOUBLE\r
-    delete[] glyphPositionsCG;\r
-#endif\r
-    CGContextRestoreGState(context);\r
-    return TRUE;\r
-}\r
-void CQuartz2D::saveGraphicsState(void * graphics)\r
-{\r
-    if (graphics) {\r
-        CGContextSaveGState((CGContextRef) graphics);\r
-    }\r
-}\r
-void CQuartz2D::restoreGraphicsState(void * graphics)\r
-{\r
-    if (graphics) {\r
-        CGContextRestoreGState((CGContextRef) graphics);\r
-    }\r
-}\r
-static CGContextRef createContextWithBitmap(CFX_DIBitmap* pBitmap)\r
-{\r
-    if (!pBitmap || pBitmap->IsCmykImage() || pBitmap->GetBPP() < 32) {\r
-        return NULL;\r
-    }\r
-    CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little;\r
-    if (pBitmap->HasAlpha()) {\r
-        bitmapInfo |= kCGImageAlphaPremultipliedFirst;\r
-    } else {\r
-        bitmapInfo |= kCGImageAlphaNoneSkipFirst;\r
-    }\r
-    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();\r
-    CGContextRef context = CGBitmapContextCreate(pBitmap->GetBuffer(),\r
-                           pBitmap->GetWidth(),\r
-                           pBitmap->GetHeight(),\r
-                           8,\r
-                           pBitmap->GetPitch(),\r
-                           colorSpace,\r
-                           bitmapInfo);\r
-    CGColorSpaceRelease(colorSpace);\r
-    return context;\r
-}\r
-CFX_QuartzDeviceDriver::CFX_QuartzDeviceDriver(CGContextRef context, FX_INT32 deviceClass)\r
-{\r
-    m_saveCount = 0;\r
-    _context           = context;\r
-    _deviceClass       = deviceClass;\r
-    CGContextRetain(_context);\r
-    CGRect r = CGContextGetClipBoundingBox(context);\r
-    _width     = FXSYS_round(r.size.width);\r
-    _height    = FXSYS_round(r.size.height);\r
-    _renderCaps = FXRC_SOFT_CLIP | FXRC_BLEND_MODE |\r
-                  FXRC_ALPHA_PATH | FXRC_ALPHA_IMAGE |\r
-                  FXRC_BIT_MASK | FXRC_ALPHA_MASK;\r
-    if (_deviceClass != FXDC_DISPLAY) {\r
-    } else {\r
-        CGImageRef image = CGBitmapContextCreateImage(_context);\r
-        if (image) {\r
-            _renderCaps |= FXRC_GET_BITS;\r
-            _width = CGImageGetWidth(image);\r
-            _height = CGImageGetHeight(image);\r
-            CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(image);\r
-            if (kCGImageAlphaPremultipliedFirst == alphaInfo ||\r
-                    kCGImageAlphaPremultipliedLast == alphaInfo ||\r
-                    kCGImageAlphaOnly == alphaInfo) {\r
-                _renderCaps |= FXRC_ALPHA_OUTPUT;\r
-            }\r
-        }\r
-        CGImageRelease(image);\r
-    }\r
-    CGAffineTransform ctm = CGContextGetCTM(_context);\r
-    CGContextSaveGState(_context);\r
-    m_saveCount++;\r
-    if (ctm.d >= 0) {\r
-        CGFloat offset_x, offset_y;\r
-        offset_x = ctm.tx;\r
-        offset_y = ctm.ty;\r
-        CGContextTranslateCTM(_context, -offset_x, -offset_y);\r
-        CGContextConcatCTM(_context, CGAffineTransformMake(1, 0, 0, -1, offset_x, _height + offset_y));\r
-    }\r
-    _foxitDevice2User = CGAffineTransformIdentity;\r
-    _user2FoxitDevice = CGAffineTransformInvert(_foxitDevice2User);\r
-}\r
-CFX_QuartzDeviceDriver::~CFX_QuartzDeviceDriver()\r
-{\r
-    CGContextRestoreGState(_context);\r
-    m_saveCount--;\r
-    for (int i = 0; i < m_saveCount; ++i) {\r
-        CGContextRestoreGState(_context);\r
-    }\r
-    if (_context) {\r
-        CGContextRelease(_context);\r
-    }\r
-}\r
-int CFX_QuartzDeviceDriver::GetDeviceCaps(int capsID)\r
-{\r
-    switch (capsID) {\r
-        case FXDC_DEVICE_CLASS: {\r
-                return _deviceClass;\r
-            }\r
-        case FXDC_PIXEL_WIDTH: {\r
-                return _width;\r
-            }\r
-        case FXDC_PIXEL_HEIGHT: {\r
-                return _height;\r
-            }\r
-        case FXDC_BITS_PIXEL: {\r
-                return 32;\r
-            }\r
-        case FXDC_RENDER_CAPS: {\r
-                return _renderCaps;\r
-            }\r
-        default: {\r
-                return 0;\r
-            }\r
-    }\r
-}\r
-CFX_Matrix CFX_QuartzDeviceDriver::GetCTM() const\r
-{\r
-    CGAffineTransform ctm = CGContextGetCTM(_context);\r
-    return CFX_Matrix(ctm.a, ctm.b, ctm.c, ctm.d, ctm.tx, ctm.ty);\r
-}\r
-void CFX_QuartzDeviceDriver::SaveState()\r
-{\r
-    CGContextSaveGState(_context);\r
-    m_saveCount++;\r
-}\r
-void CFX_QuartzDeviceDriver::RestoreState(FX_BOOL isKeepSaved )\r
-{\r
-    CGContextRestoreGState(_context);\r
-    if (isKeepSaved) {\r
-        CGContextSaveGState(_context);\r
-    } else {\r
-        m_saveCount--;\r
-    }\r
-}\r
-FX_BOOL CFX_QuartzDeviceDriver::SetClip_PathFill(const CFX_PathData*    pathData,\r
-        const CFX_AffineMatrix*   matrix,\r
-        int                       fillMode )\r
-{\r
-    SaveState();\r
-    CGAffineTransform m = CGAffineTransformIdentity;\r
-    if (matrix) {\r
-        m = CGAffineTransformMake(matrix->GetA(), matrix->GetB(), matrix->GetC(), matrix->GetD(), matrix->GetE(), matrix->GetF());\r
-    }\r
-    m = CGAffineTransformConcat(m, _foxitDevice2User);\r
-    CGContextConcatCTM(_context, m);\r
-    setPathToContext(pathData);\r
-    RestoreState(FALSE);\r
-    if ((fillMode & 3) == FXFILL_WINDING) {\r
-        CGContextClip(_context);\r
-    } else {\r
-        CGContextEOClip(_context);\r
-    }\r
-    return TRUE;\r
-}\r
-FX_FLOAT CFX_QuartzDeviceDriver::getLineWidth(const CFX_GraphStateData * graphState, CGAffineTransform ctm)\r
-{\r
-    FX_FLOAT lineWidth = graphState->m_LineWidth;\r
-    if (graphState->m_LineWidth <= 0.f) {\r
-        CGSize size;\r
-        size.width = 1;\r
-        size.height = 1;\r
-        CGSize temp = CGSizeApplyAffineTransform(size, ctm);\r
-        CGFloat x = 1 / temp.width;\r
-        CGFloat y = 1 / temp.height;\r
-        lineWidth = x > y ? x : y;\r
-    }\r
-    return lineWidth;\r
-}\r
-FX_BOOL CFX_QuartzDeviceDriver::SetClip_PathStroke(const CFX_PathData*      pathData,\r
-        const CFX_AffineMatrix*     matrix,\r
-        const CFX_GraphStateData*   graphState )\r
-{\r
-    SaveState();\r
-    CGAffineTransform m = CGAffineTransformIdentity;\r
-    if (matrix) {\r
-        m = CGAffineTransformMake(matrix->GetA(), matrix->GetB(), matrix->GetC(), matrix->GetD(), matrix->GetE(), matrix->GetF());\r
-    }\r
-    m = CGAffineTransformConcat(m, _foxitDevice2User);\r
-    CGContextConcatCTM(_context, m);\r
-    FX_FLOAT lineWidth = getLineWidth(graphState, m);\r
-    setStrokeInfo(graphState, 0xFF000000, lineWidth);\r
-    setPathToContext(pathData);\r
-    CGContextReplacePathWithStrokedPath(_context);\r
-    RestoreState(FALSE);\r
-    CGContextClip(_context);\r
-    return TRUE;\r
-}\r
-static CGBlendMode GetCGBlendMode(int blend_type)\r
-{\r
-    CGBlendMode mode = kCGBlendModeNormal;\r
-    switch (blend_type) {\r
-        case FXDIB_BLEND_NORMAL:\r
-            mode = kCGBlendModeNormal;\r
-            break;\r
-        case FXDIB_BLEND_MULTIPLY:\r
-            mode = kCGBlendModeMultiply;\r
-            break;\r
-        case FXDIB_BLEND_SCREEN:\r
-            mode = kCGBlendModeScreen;\r
-            break;\r
-        case FXDIB_BLEND_OVERLAY:\r
-            mode = kCGBlendModeOverlay;\r
-            break;\r
-        case FXDIB_BLEND_DARKEN:\r
-            mode = kCGBlendModeDarken;\r
-            break;\r
-        case FXDIB_BLEND_LIGHTEN:\r
-            mode = kCGBlendModeLighten;\r
-            break;\r
-        case FXDIB_BLEND_COLORDODGE:\r
-            mode = kCGBlendModeColorDodge;\r
-            break;\r
-        case FXDIB_BLEND_COLORBURN:\r
-            mode = kCGBlendModeColorBurn;\r
-            break;\r
-        case FXDIB_BLEND_HARDLIGHT:\r
-            mode = kCGBlendModeHardLight;\r
-            break;\r
-        case FXDIB_BLEND_SOFTLIGHT:\r
-            mode = kCGBlendModeSoftLight;\r
-            break;\r
-        case FXDIB_BLEND_DIFFERENCE:\r
-            mode = kCGBlendModeDifference;\r
-            break;\r
-        case FXDIB_BLEND_EXCLUSION:\r
-            mode = kCGBlendModeExclusion;\r
-            break;\r
-        case FXDIB_BLEND_HUE:\r
-            mode = kCGBlendModeHue;\r
-            break;\r
-        case FXDIB_BLEND_SATURATION:\r
-            mode = kCGBlendModeSaturation;\r
-            break;\r
-        case FXDIB_BLEND_COLOR:\r
-            mode = kCGBlendModeColor;\r
-            break;\r
-        case FXDIB_BLEND_LUMINOSITY:\r
-            mode = kCGBlendModeLuminosity;\r
-            break;\r
-        default:\r
-            mode = kCGBlendModeNormal;\r
-            break;\r
-    }\r
-    return mode;\r
-}\r
-FX_BOOL CFX_QuartzDeviceDriver::DrawPath(const CFX_PathData*        pathData,\r
-        const CFX_AffineMatrix*       matrix,\r
-        const CFX_GraphStateData*     graphState,\r
-        FX_DWORD                      fillArgb,\r
-        FX_DWORD                      strokeArgb,\r
-        int                           fillMode,\r
-        int                           alpha_flag,\r
-        void*                         pIccTransform,\r
-        int                                                    blend_type\r
-                                        )\r
-{\r
-    SaveState();\r
-    CGBlendMode mode = GetCGBlendMode(blend_type);\r
-    if (mode != kCGBlendModeNormal) {\r
-        CGContextSetBlendMode(_context, mode);\r
-    }\r
-    CGAffineTransform m = CGAffineTransformIdentity;\r
-    if (matrix) {\r
-        m = CGAffineTransformMake(matrix->GetA(), matrix->GetB(), matrix->GetC(), matrix->GetD(), matrix->GetE(), matrix->GetF());\r
-    }\r
-    m = CGAffineTransformConcat(m, _foxitDevice2User);\r
-    CGContextConcatCTM(_context, m);\r
-    int pathMode = 0;\r
-    if (graphState && strokeArgb) {\r
-        CGContextSetMiterLimit(_context, graphState->m_MiterLimit);\r
-        FX_FLOAT lineWidth = getLineWidth(graphState, m);\r
-        setStrokeInfo(graphState, strokeArgb, lineWidth);\r
-        pathMode |= 4;\r
-    }\r
-    if (fillMode && fillArgb) {\r
-        setFillInfo(fillArgb);\r
-        if ((fillMode & 3) == FXFILL_WINDING) {\r
-            pathMode |= 1;\r
-        } else if ((fillMode & 3) == FXFILL_ALTERNATE) {\r
-            pathMode |= 2;\r
-        }\r
-    }\r
-    setPathToContext(pathData);\r
-    if (fillMode & FXFILL_FULLCOVER) {\r
-        CGContextSetShouldAntialias(_context, false);\r
-    }\r
-    if (pathMode == 4) {\r
-        CGContextStrokePath(_context);\r
-    } else if (pathMode == 1) {\r
-        CGContextFillPath(_context);\r
-    } else if (pathMode == 2) {\r
-        CGContextEOFillPath(_context);\r
-    } else if (pathMode == 5) {\r
-        CGContextDrawPath(_context, kCGPathFillStroke);\r
-    } else if (pathMode == 6) {\r
-        CGContextDrawPath(_context, kCGPathEOFillStroke);\r
-    }\r
-    RestoreState(FALSE);\r
-    return TRUE;\r
-}\r
-FX_BOOL CFX_QuartzDeviceDriver::FillRect(const FX_RECT*         rect,\r
-        FX_ARGB                   fillArgb,\r
-        int                       alphaFlag       ,\r
-        void*                     iccTransform ,\r
-        int                                            blend_type )\r
-{\r
-    CGBlendMode mode = GetCGBlendMode(blend_type);\r
-    if (mode != kCGBlendModeNormal) {\r
-        CGContextSetBlendMode(_context, mode);\r
-    }\r
-    CGRect rect_fx = CGRectMake(rect->left, rect->top, rect->Width(), rect->Height());\r
-    CGRect rect_usr = CGRectApplyAffineTransform(rect_fx, _foxitDevice2User);\r
-    FX_INT32 a, r, g, b;\r
-    ArgbDecode(fillArgb, a, r, g, b);\r
-    CGContextSetRGBFillColor(_context,\r
-                             r / 255.f,\r
-                             g / 255.f,\r
-                             b / 255.f,\r
-                             a / 255.f);\r
-    CGContextFillRect(_context, rect_usr);\r
-    if (mode != kCGBlendModeNormal) {\r
-        CGContextSetBlendMode(_context, kCGBlendModeNormal);\r
-    }\r
-    return TRUE;\r
-}\r
-FX_BOOL CFX_QuartzDeviceDriver::DrawCosmeticLine(FX_FLOAT           x1,\r
-        FX_FLOAT              y1,\r
-        FX_FLOAT              x2,\r
-        FX_FLOAT              y2,\r
-        FX_DWORD              argb,\r
-        int                   alphaFlag       ,\r
-        void*                 iccTransform    ,\r
-        int                                    blend_type )\r
-{\r
-    CGBlendMode mode = GetCGBlendMode(blend_type);\r
-    if (mode != kCGBlendModeNormal) {\r
-        CGContextSetBlendMode(_context, mode);\r
-    }\r
-    CGPoint pt = CGPointApplyAffineTransform(CGPointMake(x1, y1), _foxitDevice2User);\r
-    x1 = pt.x;\r
-    y1 = pt.y;\r
-    pt = CGPointApplyAffineTransform(CGPointMake(x2, y2), _foxitDevice2User);\r
-    x2 = pt.x;\r
-    y2 = pt.y;\r
-    FX_INT32 a, r, g, b;\r
-    ArgbDecode(argb, a, r, g, b);\r
-    CGContextSetRGBStrokeColor(_context,\r
-                               r / 255.f,\r
-                               g / 255.f,\r
-                               b / 255.f,\r
-                               a / 255.f);\r
-    CGContextMoveToPoint(_context, x1, y1);\r
-    CGContextAddLineToPoint(_context, x2, y2);\r
-    CGContextStrokePath(_context);\r
-    if (mode != kCGBlendModeNormal) {\r
-        CGContextSetBlendMode(_context, kCGBlendModeNormal);\r
-    }\r
-    return TRUE;\r
-}\r
-FX_BOOL CFX_QuartzDeviceDriver::GetClipBox(FX_RECT* rect)\r
-{\r
-    CGRect r = CGContextGetClipBoundingBox(_context);\r
-    r = CGRectApplyAffineTransform(r, _user2FoxitDevice);\r
-    rect->left         = FXSYS_floor(r.origin.x);\r
-    rect->top          = FXSYS_floor(r.origin.y);\r
-    rect->right                = FXSYS_ceil(r.origin.x + r.size.width);\r
-    rect->bottom       = FXSYS_ceil(r.origin.y + r.size.height);\r
-    return TRUE;\r
-}\r
-FX_BOOL CFX_QuartzDeviceDriver::GetDIBits(CFX_DIBitmap*     bitmap,\r
-        FX_INT32            left,\r
-        FX_INT32            top,\r
-        void* pIccTransform,\r
-        FX_BOOL bDEdge)\r
-{\r
-    if (FXDC_PRINTER == _deviceClass) {\r
-        return FALSE;\r
-    }\r
-    if (bitmap->GetBPP() < 32) {\r
-        return FALSE;\r
-    }\r
-    if (!(_renderCaps | FXRC_GET_BITS)) {\r
-        return FALSE;\r
-    }\r
-    CGPoint pt = CGPointMake(left, top);\r
-    pt = CGPointApplyAffineTransform(pt, _foxitDevice2User);\r
-    CGAffineTransform ctm = CGContextGetCTM(_context);\r
-    pt.x *= FXSYS_fabs(ctm.a);\r
-    pt.y *= FXSYS_fabs(ctm.d);\r
-    CGImageRef image = CGBitmapContextCreateImage(_context);\r
-    if (NULL == image) {\r
-        return FALSE;\r
-    }\r
-    CGFloat width      = (CGFloat) bitmap->GetWidth();\r
-    CGFloat height     = (CGFloat) bitmap->GetHeight();\r
-    if (width + pt.x > _width) {\r
-        width -= (width + pt.x - _width);\r
-    }\r
-    if (height + pt.y > _height) {\r
-        height -= (height + pt.y - _height);\r
-    }\r
-    CGImageRef subImage = CGImageCreateWithImageInRect(image,\r
-                          CGRectMake(pt.x,\r
-                                     pt.y,\r
-                                     width,\r
-                                     height));\r
-    CGContextRef context = createContextWithBitmap(bitmap);\r
-    CGRect rect = CGContextGetClipBoundingBox(context);\r
-    CGContextClearRect(context, rect);\r
-    CGContextDrawImage(context, rect, subImage);\r
-    CGContextRelease(context);\r
-    CGImageRelease(subImage);\r
-    CGImageRelease(image);\r
-    if (bitmap->HasAlpha()) {\r
-        for (int row = 0; row < bitmap->GetHeight(); row ++) {\r
-            FX_LPBYTE pScanline = (FX_LPBYTE)bitmap->GetScanline(row);\r
-            for (int col = 0; col < bitmap->GetWidth(); col ++) {\r
-                if (pScanline[3] > 0) {\r
-                    pScanline[0] = (pScanline[0] * 255.f / pScanline[3] + .5f);\r
-                    pScanline[1] = (pScanline[1] * 255.f / pScanline[3] + .5f);\r
-                    pScanline[2] = (pScanline[2] * 255.f / pScanline[3] + .5f);\r
-                }\r
-                pScanline += 4;\r
-            }\r
-        }\r
-    }\r
-    return TRUE;\r
-}\r
-FX_BOOL CFX_QuartzDeviceDriver::SetDIBits(const CFX_DIBSource*      pBitmap,\r
-        FX_ARGB                     argb,\r
-        const FX_RECT*              srcRect,\r
-        int                         dest_left,\r
-        int                         dest_top,\r
-        int                         blendType,\r
-        int                         alphaFlag       ,\r
-        void*                       iccTransform    )\r
-{\r
-    SaveState();\r
-    CGFloat src_left, src_top, src_width, src_height;\r
-    if (srcRect) {\r
-        src_left = srcRect->left;\r
-        src_top = srcRect->top;\r
-        src_width = srcRect->Width();\r
-        src_height = srcRect->Height();\r
-    } else {\r
-        src_left = src_top = 0;\r
-        src_width = pBitmap->GetWidth();\r
-        src_height = pBitmap->GetHeight();\r
-    }\r
-    CGAffineTransform ctm = CGContextGetCTM(_context);\r
-    CGFloat scale_x = FXSYS_fabs(ctm.a);\r
-    CGFloat scale_y = FXSYS_fabs(ctm.d);\r
-    src_left /= scale_x;\r
-    src_top /= scale_y;\r
-    src_width /= scale_x;\r
-    src_height /= scale_y;\r
-    CGRect rect_fx = CGRectMake(dest_left, dest_top, src_width, src_height);\r
-    CGRect rect_usr = CGRectApplyAffineTransform(rect_fx, _foxitDevice2User);\r
-    CGContextBeginPath(_context);\r
-    CGContextAddRect(_context, rect_usr);\r
-    CGContextClip(_context);\r
-    rect_usr.size = CGSizeMake(pBitmap->GetWidth() / scale_x, pBitmap->GetHeight() / scale_y);\r
-    rect_usr = CGRectOffset(rect_usr, -src_left, -src_top);\r
-    CG_SetImageTransform(dest_left, dest_top, src_width, src_height, &rect_usr);\r
-    CFX_DIBitmap* pBitmap1 = NULL;\r
-    if (pBitmap->IsAlphaMask()) {\r
-        if (pBitmap->GetBuffer()) {\r
-            pBitmap1 = (CFX_DIBitmap*)pBitmap;\r
-        } else {\r
-            pBitmap1 = pBitmap->Clone();\r
-        }\r
-        if (NULL == pBitmap1) {\r
-            RestoreState(FALSE);\r
-            return FALSE;\r
-        }\r
-        CGDataProviderRef pBitmapProvider = CGDataProviderCreateWithData(NULL,\r
-                                            pBitmap1->GetBuffer(),\r
-                                            pBitmap1->GetPitch() * pBitmap1->GetHeight(),\r
-                                            NULL);\r
-        CGColorSpaceRef pColorSpace = CGColorSpaceCreateDeviceGray();\r
-        CGBitmapInfo bitmapInfo = kCGImageAlphaNone | kCGBitmapByteOrderDefault;\r
-        CGImageRef pImage = CGImageCreate(pBitmap1->GetWidth(),\r
-                                          pBitmap1->GetHeight(),\r
-                                          pBitmap1->GetBPP(),\r
-                                          pBitmap1->GetBPP(),\r
-                                          pBitmap1->GetPitch(),\r
-                                          pColorSpace,\r
-                                          bitmapInfo,\r
-                                          pBitmapProvider, NULL, true,\r
-                                          kCGRenderingIntentDefault);\r
-        CGContextClipToMask(_context, rect_usr, pImage);\r
-        CGContextSetRGBFillColor(_context,\r
-                                 FXARGB_R(argb) / 255.f,\r
-                                 FXARGB_G(argb) / 255.f,\r
-                                 FXARGB_B(argb) / 255.f,\r
-                                 FXARGB_A(argb) / 255.f);\r
-        CGContextFillRect(_context, rect_usr);\r
-        CGImageRelease(pImage);\r
-        CGColorSpaceRelease(pColorSpace);\r
-        CGDataProviderRelease(pBitmapProvider);\r
-        if (pBitmap1 != pBitmap) {\r
-            delete pBitmap1;\r
-        }\r
-        RestoreState(FALSE);\r
-        return TRUE;\r
-    }\r
-    if (pBitmap->GetBPP() < 32) {\r
-        pBitmap1 = pBitmap->CloneConvert(FXDIB_Rgb32);\r
-    } else {\r
-        if (pBitmap->GetBuffer()) {\r
-            pBitmap1 = (CFX_DIBitmap*)pBitmap;\r
-        } else {\r
-            pBitmap1 = pBitmap->Clone();\r
-        }\r
-    }\r
-    if (NULL == pBitmap1) {\r
-        RestoreState(FALSE);\r
-        return FALSE;\r
-    }\r
-    if (pBitmap1->HasAlpha()) {\r
-        if (pBitmap1 == pBitmap) {\r
-            pBitmap1 = pBitmap->Clone();\r
-            if (!pBitmap1) {\r
-                RestoreState(FALSE);\r
-                return FALSE;\r
-            }\r
-        }\r
-        for (int row = 0; row < pBitmap1->GetHeight(); row ++) {\r
-            FX_LPBYTE pScanline = (FX_LPBYTE)pBitmap1->GetScanline(row);\r
-            for (int col = 0; col < pBitmap1->GetWidth(); col ++) {\r
-                pScanline[0] = (FX_BYTE)(pScanline[0] * pScanline[3] / 255.f + .5f);\r
-                pScanline[1] = (FX_BYTE)(pScanline[1] * pScanline[3] / 255.f + .5f);\r
-                pScanline[2] = (FX_BYTE)(pScanline[2] * pScanline[3] / 255.f + .5f);\r
-                pScanline += 4;\r
-            }\r
-        }\r
-    }\r
-    CGContextRef ctx = createContextWithBitmap(pBitmap1);\r
-    CGImageRef image = CGBitmapContextCreateImage(ctx);\r
-    int blend_mode = blendType;\r
-    if (FXDIB_BLEND_HARDLIGHT == blendType) {\r
-        blend_mode = kCGBlendModeSoftLight;\r
-    } else if (FXDIB_BLEND_SOFTLIGHT == blendType) {\r
-        blend_mode = kCGBlendModeHardLight;\r
-    } else if (blendType >= FXDIB_BLEND_NONSEPARABLE && blendType <= FXDIB_BLEND_LUMINOSITY) {\r
-        blend_mode = blendType - 9;\r
-    } else if (blendType > FXDIB_BLEND_LUMINOSITY || blendType < 0) {\r
-        blend_mode = kCGBlendModeNormal;\r
-    }\r
-    CGContextSetBlendMode(_context, (CGBlendMode)blend_mode);\r
-    CGContextDrawImage(_context, rect_usr, image);\r
-    CGImageRelease(image);\r
-    CGContextRelease(ctx);\r
-    if (pBitmap1 != pBitmap) {\r
-        delete pBitmap1;\r
-    }\r
-    RestoreState(FALSE);\r
-    return TRUE;\r
-}\r
-FX_BOOL CFX_QuartzDeviceDriver::StretchDIBits(const CFX_DIBSource*      pBitmap,\r
-        FX_ARGB                     argb,\r
-        int                         dest_left,\r
-        int                         dest_top,\r
-        int                         dest_width,\r
-        int                         dest_height,\r
-        const FX_RECT*              clipRect,\r
-        FX_DWORD                    flags,\r
-        int                         alphaFlag     ,\r
-        void*                       iccTransform ,\r
-        int                                                    blend_type)\r
-{\r
-    SaveState();\r
-    if (clipRect) {\r
-        CGContextBeginPath(_context);\r
-        CGRect rect_clip = CGRectMake(clipRect->left, clipRect->top, clipRect->Width(), clipRect->Height());\r
-        rect_clip = CGRectApplyAffineTransform(rect_clip, _foxitDevice2User);\r
-        CGContextAddRect(_context, rect_clip);\r
-        CGContextClip(_context);\r
-    }\r
-    CGRect rect = CGRectMake(dest_left, dest_top, dest_width, dest_height);\r
-    rect = CGRectApplyAffineTransform(rect, _foxitDevice2User);\r
-    if (FXDIB_BICUBIC_INTERPOL == flags) {\r
-        CGContextSetInterpolationQuality(_context, kCGInterpolationHigh);\r
-    } else if (FXDIB_DOWNSAMPLE == flags) {\r
-        CGContextSetInterpolationQuality(_context, kCGInterpolationNone);\r
-    } else {\r
-        CGContextSetInterpolationQuality(_context, kCGInterpolationMedium);\r
-    }\r
-    CG_SetImageTransform(dest_left, dest_top, dest_width, dest_height);\r
-    CFX_DIBitmap* pBitmap1 = NULL;\r
-    if (pBitmap->IsAlphaMask()) {\r
-        if (pBitmap->GetBuffer()) {\r
-            pBitmap1 = (CFX_DIBitmap*)pBitmap;\r
-        } else {\r
-            pBitmap1 = pBitmap->Clone();\r
-        }\r
-        if (NULL == pBitmap1) {\r
-            RestoreState(FALSE);\r
-            return FALSE;\r
-        }\r
-        CGDataProviderRef pBitmapProvider = CGDataProviderCreateWithData(NULL,\r
-                                            pBitmap1->GetBuffer(),\r
-                                            pBitmap1->GetPitch() * pBitmap1->GetHeight(),\r
-                                            NULL);\r
-        CGColorSpaceRef pColorSpace = CGColorSpaceCreateDeviceGray();\r
-        CGBitmapInfo bitmapInfo = kCGImageAlphaNone | kCGBitmapByteOrderDefault;\r
-        CGImageRef pImage = CGImageCreate(pBitmap1->GetWidth(),\r
-                                          pBitmap1->GetHeight(),\r
-                                          pBitmap1->GetBPP(),\r
-                                          pBitmap1->GetBPP(),\r
-                                          pBitmap1->GetPitch(),\r
-                                          pColorSpace,\r
-                                          bitmapInfo,\r
-                                          pBitmapProvider, NULL, true,\r
-                                          kCGRenderingIntentDefault);\r
-        CGContextClipToMask(_context, rect, pImage);\r
-        CGContextSetRGBFillColor(_context,\r
-                                 FXARGB_R(argb) / 255.f,\r
-                                 FXARGB_G(argb) / 255.f,\r
-                                 FXARGB_B(argb) / 255.f,\r
-                                 FXARGB_A(argb) / 255.f);\r
-        CGContextFillRect(_context, rect);\r
-        CGImageRelease(pImage);\r
-        CGColorSpaceRelease(pColorSpace);\r
-        CGDataProviderRelease(pBitmapProvider);\r
-        if (pBitmap1 != pBitmap) {\r
-            delete pBitmap1;\r
-        }\r
-        RestoreState(FALSE);\r
-        return TRUE;\r
-    }\r
-    if (pBitmap->GetBPP() < 32) {\r
-        pBitmap1 = pBitmap->CloneConvert(FXDIB_Rgb32);\r
-    } else {\r
-        if (pBitmap->GetBuffer()) {\r
-            pBitmap1 = (CFX_DIBitmap*)pBitmap;\r
-        } else {\r
-            pBitmap1 = pBitmap->Clone();\r
-        }\r
-    }\r
-    if (NULL == pBitmap1) {\r
-        RestoreState(FALSE);\r
-        return FALSE;\r
-    }\r
-    if (pBitmap1->HasAlpha()) {\r
-        if (pBitmap1 == pBitmap) {\r
-            pBitmap1 = pBitmap->Clone();\r
-            if (!pBitmap1) {\r
-                RestoreState(FALSE);\r
-                return FALSE;\r
-            }\r
-        }\r
-        for (int row = 0; row < pBitmap1->GetHeight(); row ++) {\r
-            FX_LPBYTE pScanline = (FX_LPBYTE)pBitmap1->GetScanline(row);\r
-            for (int col = 0; col < pBitmap1->GetWidth(); col ++) {\r
-                pScanline[0] = (FX_BYTE)(pScanline[0] * pScanline[3] / 255.f + .5f);\r
-                pScanline[1] = (FX_BYTE)(pScanline[1] * pScanline[3] / 255.f + .5f);\r
-                pScanline[2] = (FX_BYTE)(pScanline[2] * pScanline[3] / 255.f + .5f);\r
-                pScanline += 4;\r
-            }\r
-        }\r
-    }\r
-    CGContextRef ctx = createContextWithBitmap(pBitmap1);\r
-    CGImageRef image = CGBitmapContextCreateImage(ctx);\r
-    CGContextDrawImage(_context, rect, image);\r
-    CGImageRelease(image);\r
-    CGContextRelease(ctx);\r
-    if (pBitmap1 != pBitmap) {\r
-        delete pBitmap1;\r
-    }\r
-    RestoreState(FALSE);\r
-    return TRUE;\r
-}\r
-FX_BOOL CFX_QuartzDeviceDriver::CG_DrawGlypRun(int                        nChars,\r
-        const FXTEXT_CHARPOS*      pCharPos,\r
-        CFX_Font*                  pFont,\r
-        CFX_FontCache*             pCache,\r
-        const CFX_AffineMatrix*    pGlyphMatrix,\r
-        const CFX_AffineMatrix*    pObject2Device,\r
-        FX_FLOAT                   font_size,\r
-        FX_DWORD                   argb,\r
-        int                        alpha_flag,\r
-        void*                      pIccTransform)\r
-{\r
-    if (nChars == 0) {\r
-        return TRUE;\r
-    }\r
-    CQuartz2D& quartz2d = ((CApplePlatform *) CFX_GEModule::Get()->GetPlatformData())->_quartz2d;\r
-    if (!pFont->m_pPlatformFont) {\r
-        if (pFont->GetPsName() == CFX_WideString::FromLocal("DFHeiStd-W5")) {\r
-            return FALSE;\r
-        }\r
-        pFont->m_pPlatformFont = quartz2d.CreateFont(pFont->m_pFontData, pFont->m_dwSize);\r
-        if (NULL == pFont->m_pPlatformFont) {\r
-            return FALSE;\r
-        }\r
-    }\r
-    CFX_FixedBufGrow<FX_WORD, 32> glyph_indices(nChars);\r
-    CFX_FixedBufGrow<CGPoint, 32> glyph_positions(nChars);\r
-    for (int i = 0; i < nChars; i++ ) {\r
-        glyph_indices[i] = pCharPos[i].m_ExtGID;\r
-        glyph_positions[i].x = pCharPos[i].m_OriginX;\r
-        glyph_positions[i].y = pCharPos[i].m_OriginY;\r
-    }\r
-    CFX_AffineMatrix text_matrix;\r
-    if (pObject2Device) {\r
-        text_matrix.Concat(*pObject2Device);\r
-    }\r
-    CGAffineTransform matrix_cg = CGAffineTransformMake(text_matrix.a,\r
-                                  text_matrix.b,\r
-                                  text_matrix.c,\r
-                                  text_matrix.d,\r
-                                  text_matrix.e,\r
-                                  text_matrix.f);\r
-    matrix_cg = CGAffineTransformConcat(matrix_cg, _foxitDevice2User);\r
-    CGContextSetTextMatrix(_context, matrix_cg);\r
-    CGContextSetFont(_context, (CGFontRef)pFont->m_pPlatformFont);\r
-    CGContextSetFontSize(_context, FXSYS_fabs(font_size));\r
-    FX_INT32 a, r, g, b;\r
-    ArgbDecode(argb, a, r, g, b);\r
-    CGContextSetRGBFillColor(_context,\r
-                             r / 255.f,\r
-                             g / 255.f,\r
-                             b / 255.f,\r
-                             a / 255.f);\r
-    SaveState();\r
-    if (pGlyphMatrix) {\r
-        CGAffineTransform ctm = CGContextGetCTM(_context);\r
-        CGPoint origin = CGPointMake( glyph_positions[0].x,  glyph_positions[0].y);\r
-        origin = CGPointApplyAffineTransform(origin, matrix_cg);\r
-        CGContextTranslateCTM(_context, origin.x, origin.y);\r
-        CGAffineTransform glyph_matrix = CGAffineTransformMake(pGlyphMatrix->a,\r
-                                         pGlyphMatrix->b,\r
-                                         pGlyphMatrix->c,\r
-                                         pGlyphMatrix->d,\r
-                                         pGlyphMatrix->e,\r
-                                         pGlyphMatrix->f);\r
-        if (_foxitDevice2User.d < 0) {\r
-            glyph_matrix = CGAffineTransformInvert(glyph_matrix);\r
-        }\r
-        CGContextConcatCTM(_context, glyph_matrix);\r
-        CGContextTranslateCTM(_context, -origin.x, -origin.y);\r
-    }\r
-    CGContextShowGlyphsAtPositions(_context,\r
-                                   (CGGlyph*)glyph_indices,\r
-                                   glyph_positions,\r
-                                   nChars);\r
-    RestoreState(FALSE);\r
-    return TRUE;\r
-}\r
-FX_BOOL CFX_QuartzDeviceDriver::DrawDeviceText(int                      nChars,\r
-        const FXTEXT_CHARPOS*    pCharPos,\r
-        CFX_Font*                pFont,\r
-        CFX_FontCache*           pCache,\r
-        const CFX_AffineMatrix*  pObject2Device,\r
-        FX_FLOAT                 font_size,\r
-        FX_DWORD                 color,\r
-        int                      alpha_flag       ,\r
-        void*                    pIccTransform)\r
-{\r
-    if (NULL == pFont || NULL == _context) {\r
-        return FALSE;\r
-    }\r
-    FX_BOOL bBold = pFont->IsBold();\r
-    if (!bBold && pFont->GetSubstFont() &&\r
-            pFont->GetSubstFont()->m_Weight >= 500 &&\r
-            pFont->GetSubstFont()->m_Weight <= 600) {\r
-        return FALSE;\r
-    }\r
-    SaveState();\r
-    CGContextSetTextDrawingMode(_context, kCGTextFillClip);\r
-    FX_BOOL ret = FALSE;\r
-    FX_INT32 i = 0;\r
-    while (i < nChars) {\r
-        if (pCharPos[i].m_bGlyphAdjust || font_size < 0) {\r
-            if (i > 0) {\r
-                ret = CG_DrawGlypRun(i, pCharPos, pFont, pCache, NULL, pObject2Device, font_size, color, alpha_flag, pIccTransform);\r
-                if (!ret) {\r
-                    RestoreState(FALSE);\r
-                    return ret;\r
-                }\r
-            }\r
-            const FXTEXT_CHARPOS* char_pos = pCharPos + i;\r
-            CFX_AffineMatrix glphy_matrix;\r
-            if (font_size < 0) {\r
-                glphy_matrix.Concat(-1, 0, 0, -1, 0, 0);\r
-            }\r
-            if (char_pos->m_bGlyphAdjust) {\r
-                glphy_matrix.Concat(char_pos->m_AdjustMatrix[0],\r
-                                    char_pos->m_AdjustMatrix[1],\r
-                                    char_pos->m_AdjustMatrix[2],\r
-                                    char_pos->m_AdjustMatrix[3], 0, 0);\r
-            }\r
-            ret = CG_DrawGlypRun(1, char_pos, pFont, pCache, &glphy_matrix, pObject2Device, font_size, color, alpha_flag, pIccTransform);\r
-            if (!ret) {\r
-                RestoreState(FALSE);\r
-                return ret;\r
-            }\r
-            i ++;\r
-            pCharPos += i;\r
-            nChars -= i;\r
-            i = 0;\r
-        } else {\r
-            i ++;\r
-        }\r
-    }\r
-    if (i > 0) {\r
-        ret = CG_DrawGlypRun(i, pCharPos, pFont, pCache, NULL, pObject2Device, font_size, color, alpha_flag, pIccTransform);\r
-    }\r
-    RestoreState(FALSE);\r
-    return ret;\r
-}\r
-void CFX_QuartzDeviceDriver::setStrokeInfo(const CFX_GraphStateData* graphState, FX_ARGB argb, FX_FLOAT lineWidth)\r
-{\r
-    if (NULL == graphState) {\r
-        return;\r
-    }\r
-    CGContextSetLineWidth(_context, lineWidth);\r
-    CGLineCap cap;\r
-    switch (graphState->m_LineCap) {\r
-        case CFX_GraphStateData::LineCapRound: {\r
-                cap = kCGLineCapRound;\r
-                break;\r
-            }\r
-        case CFX_GraphStateData::LineCapSquare: {\r
-                cap = kCGLineCapSquare;\r
-                break;\r
-            }\r
-        case CFX_GraphStateData::LineCapButt:\r
-        default: {\r
-                cap = kCGLineCapButt;\r
-            }\r
-    }\r
-    CGContextSetLineCap(_context, cap);\r
-    CGLineJoin join;\r
-    switch (graphState->m_LineJoin) {\r
-        case CFX_GraphStateData::LineJoinRound: {\r
-                join = kCGLineJoinRound;\r
-                break;\r
-            }\r
-        case CFX_GraphStateData::LineJoinBevel: {\r
-                join = kCGLineJoinBevel;\r
-                break;\r
-            }\r
-        case CFX_GraphStateData::LineJoinMiter:\r
-        default: {\r
-                join = kCGLineJoinMiter;\r
-            }\r
-    }\r
-    CGContextSetLineJoin(_context, join);\r
-    if (graphState->m_DashCount) {\r
-#if CGFLOAT_IS_DOUBLE\r
-        CGFloat* dashArray = new CGFloat[graphState->m_DashCount];\r
-        if (!dashArray) {\r
-            return;\r
-        }\r
-        for (int index = 0; index < graphState->m_DashCount; ++index) {\r
-            dashArray[index] = graphState->m_DashArray[index];\r
-        }\r
-#else\r
-        CGFloat* dashArray = (CGFloat*)graphState->m_DashArray;\r
-#endif\r
-        CGContextSetLineDash(_context, graphState->m_DashPhase, dashArray, graphState->m_DashCount);\r
-#if CGFLOAT_IS_DOUBLE\r
-        delete[] dashArray;\r
-#endif\r
-    }\r
-    FX_INT32 a, r, g, b;\r
-    ArgbDecode(argb, a, r, g, b);\r
-    CGContextSetRGBStrokeColor(_context,\r
-                               r / 255.f,\r
-                               g / 255.f,\r
-                               b / 255.f,\r
-                               a / 255.f);\r
-}\r
-void CFX_QuartzDeviceDriver::setFillInfo(FX_ARGB argb)\r
-{\r
-    FX_INT32 a, r, g, b;\r
-    ArgbDecode(argb, a, r, g, b);\r
-    CGContextSetRGBFillColor(_context,\r
-                             r / 255.f,\r
-                             g / 255.f,\r
-                             b / 255.f,\r
-                             a / 255.f);\r
-}\r
-void CFX_QuartzDeviceDriver::setPathToContext(const CFX_PathData* pathData)\r
-{\r
-    FX_INT32 count = pathData->GetPointCount();\r
-    FX_PATHPOINT* points = pathData->GetPoints();\r
-    CGContextBeginPath(_context);\r
-    for (FX_INT32 i = 0; i < count; i ++) {\r
-        switch (points[i].m_Flag & FXPT_TYPE) {\r
-            case FXPT_MOVETO:\r
-                CGContextMoveToPoint(_context, points[i].m_PointX, points[i].m_PointY);\r
-                break;\r
-            case FXPT_LINETO:\r
-                CGContextAddLineToPoint(_context, points[i].m_PointX, points[i].m_PointY);\r
-                break;\r
-            case FXPT_BEZIERTO: {\r
-                    CGContextAddCurveToPoint(_context,\r
-                                             points[i].m_PointX, points[i].m_PointY,\r
-                                             points[i + 1].m_PointX, points[i + 1].m_PointY,\r
-                                             points[i + 2].m_PointX, points[i + 2].m_PointY);\r
-                    i += 2;\r
-                }\r
-        }\r
-        if (points[i].m_Flag & FXPT_CLOSEFIGURE) {\r
-            CGContextClosePath(_context);\r
-        }\r
-    }\r
-}\r
-void CFX_QuartzDeviceDriver::CG_SetImageTransform(int dest_left, int dest_top, int dest_width, int dest_height,\r
-        CGRect* rect )\r
-{\r
-    int flip_y = _foxitDevice2User.d * dest_height < 0 ? 1 : -1;\r
-    int flip_x = _foxitDevice2User.a * dest_width > 0 ? 1 : -1;\r
-    if (flip_y < 0 || flip_x < 0) {\r
-        if (dest_height < 0) {\r
-            dest_height = -dest_height;\r
-            dest_top -= dest_height;\r
-        }\r
-        CGRect rt = CGRectApplyAffineTransform(CGRectMake(dest_left, dest_top, dest_width, dest_height), _foxitDevice2User);\r
-        CGFloat offset_x = (rt.origin.x) + rt.size.width / 2.f,\r
-                offset_y = (rt.origin.y) + rt.size.height / 2.f;\r
-        CGAffineTransform transform = CGAffineTransformIdentity;\r
-        transform = CGAffineTransformConcat(transform, CGAffineTransformMake(1, 0, 0, 1, -offset_x, -offset_y));\r
-        transform = CGAffineTransformConcat(transform, CGAffineTransformMake(flip_x, 0, 0, flip_y, 0, 0));\r
-        transform = CGAffineTransformConcat(transform, CGAffineTransformMake(1, 0, 0, 1, offset_x, offset_y));\r
-        CGContextConcatCTM(_context, transform);\r
-        if (rect) {\r
-            *rect = CGRectApplyAffineTransform(*rect, transform);\r
-        }\r
-    }\r
-}\r
-void CFX_QuartzDeviceDriver::ClearDriver()\r
-{\r
-    if (NULL == _context) {\r
-        return;\r
-    }\r
-    for (int i = 0; i < m_saveCount; ++i) {\r
-        CGContextRestoreGState(_context);\r
-    }\r
-    m_saveCount = 0;\r
-    if (_context) {\r
-        CGContextRelease(_context);\r
-    }\r
-}\r
-CFX_QuartzDevice::CFX_QuartzDevice()\r
-{\r
-    m_bOwnedBitmap = FALSE;\r
-    m_pContext = NULL;\r
-}\r
-CFX_QuartzDevice::~CFX_QuartzDevice()\r
-{\r
-    if (m_pContext) {\r
-        CGContextRelease(m_pContext);\r
-    }\r
-    if (GetBitmap() && m_bOwnedBitmap) {\r
-        delete GetBitmap();\r
-    }\r
-}\r
-CGContextRef CFX_QuartzDevice::GetContext()\r
-{\r
-    return m_pContext;\r
-}\r
-FX_BOOL CFX_QuartzDevice::Attach(CGContextRef context, FX_INT32 nDeviceClass)\r
-{\r
-    if (m_pContext) {\r
-        CGContextRelease(m_pContext);\r
-    }\r
-    m_pContext = context;\r
-    CGContextRetain(m_pContext);\r
-    IFX_RenderDeviceDriver* pDriver = FX_NEW CFX_QuartzDeviceDriver(m_pContext, nDeviceClass);\r
-    if (!pDriver) {\r
-        return FALSE;\r
-    }\r
-    SetDeviceDriver(pDriver);\r
-    return TRUE;\r
-}\r
-FX_BOOL CFX_QuartzDevice::Attach(CFX_DIBitmap* pBitmap)\r
-{\r
-    SetBitmap(pBitmap);\r
-    m_pContext = createContextWithBitmap(pBitmap);\r
-    if (NULL == m_pContext) {\r
-        return FALSE;\r
-    }\r
-    IFX_RenderDeviceDriver* pDriver = FX_NEW CFX_QuartzDeviceDriver(m_pContext, FXDC_DISPLAY);\r
-    if (!pDriver) {\r
-        return FALSE;\r
-    }\r
-    SetDeviceDriver(pDriver);\r
-    return TRUE;\r
-}\r
-FX_BOOL CFX_QuartzDevice::Create(FX_INT32 width, FX_INT32 height, FXDIB_Format format)\r
-{\r
-    if ((FX_BYTE)format < 32) {\r
-        return FALSE;\r
-    }\r
-    CFX_DIBitmap* pBitmap = FX_NEW CFX_DIBitmap;\r
-    if (!pBitmap) {\r
-        return FALSE;\r
-    }\r
-    if (!pBitmap->Create(width, height, format)) {\r
-        delete pBitmap;\r
-        return FALSE;\r
-    }\r
-    m_bOwnedBitmap = TRUE;\r
-    return Attach(pBitmap);\r
-}\r
-#endif\r
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "../../../include/fxcrt/fx_ext.h"
+#include "../../../include/fxge/fx_freetype.h"
+#include "../../../include/fxge/fx_ge.h"
+#include "../agg/include/fx_agg_driver.h"
+#include "../dib/dib_int.h"
+#include "../ge/text_int.h"
+
+#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
+#include "apple_int.h"
+#include "../../../include/fxge/fx_ge_apple.h"
+#ifndef CGFLOAT_IS_DOUBLE
+#error Expected CGFLOAT_IS_DOUBLE to be defined by CoreGraphics headers
+#endif
+void* CQuartz2D::createGraphics(CFX_DIBitmap* pBitmap) {
+  if (!pBitmap) {
+    return NULL;
+  }
+  CGBitmapInfo bmpInfo = kCGBitmapByteOrder32Little;
+  switch (pBitmap->GetFormat()) {
+    case FXDIB_Rgb32:
+      bmpInfo |= kCGImageAlphaNoneSkipFirst;
+      break;
+    case FXDIB_Argb:
+    default:
+      return NULL;
+  }
+  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+  CGContextRef context = CGBitmapContextCreate(
+      pBitmap->GetBuffer(), pBitmap->GetWidth(), pBitmap->GetHeight(), 8,
+      pBitmap->GetPitch(), colorSpace, bmpInfo);
+  CGColorSpaceRelease(colorSpace);
+  return context;
+}
+void CQuartz2D::destroyGraphics(void* graphics) {
+  if (graphics) {
+    CGContextRelease((CGContextRef)graphics);
+  }
+}
+void* CQuartz2D::CreateFont(const uint8_t* pFontData, FX_DWORD dwFontSize) {
+  CGDataProviderRef pDataProvider =
+      CGDataProviderCreateWithData(NULL, pFontData, (size_t)dwFontSize, NULL);
+  if (NULL == pDataProvider) {
+    return NULL;
+  }
+  CGFontRef pCGFont = CGFontCreateWithDataProvider(pDataProvider);
+  CGDataProviderRelease(pDataProvider);
+  return pCGFont;
+}
+void CQuartz2D::DestroyFont(void* pFont) {
+  CGFontRelease((CGFontRef)pFont);
+}
+void CQuartz2D::setGraphicsTextMatrix(void* graphics,
+                                      CFX_AffineMatrix* matrix) {
+  if (!graphics || !matrix) {
+    return;
+  }
+  CGContextRef context = (CGContextRef)graphics;
+  CGFloat ty = CGBitmapContextGetHeight(context) - matrix->f;
+  CGContextSetTextMatrix(
+      context, CGAffineTransformMake(matrix->a, matrix->b, matrix->c, matrix->d,
+                                     matrix->e, ty));
+}
+FX_BOOL CQuartz2D::drawGraphicsString(void* graphics,
+                                      void* font,
+                                      FX_FLOAT fontSize,
+                                      FX_WORD* glyphIndices,
+                                      CGPoint* glyphPositions,
+                                      int32_t charsCount,
+                                      FX_ARGB argb,
+                                      CFX_AffineMatrix* matrix) {
+  if (!graphics) {
+    return FALSE;
+  }
+  CGContextRef context = (CGContextRef)graphics;
+  CGContextSetFont(context, (CGFontRef)font);
+  CGContextSetFontSize(context, fontSize);
+  if (matrix) {
+    CGAffineTransform m = CGContextGetTextMatrix(context);
+    m = CGAffineTransformConcat(
+        m, CGAffineTransformMake(matrix->a, matrix->b, matrix->c, matrix->d,
+                                 matrix->e, matrix->f));
+    CGContextSetTextMatrix(context, m);
+  }
+  int32_t a, r, g, b;
+  ArgbDecode(argb, a, r, g, b);
+  CGContextSetRGBFillColor(context, r / 255.f, g / 255.f, b / 255.f, a / 255.f);
+  CGContextSaveGState(context);
+#if CGFLOAT_IS_DOUBLE
+  CGPoint* glyphPositionsCG = new CGPoint[charsCount];
+  for (int index = 0; index < charsCount; ++index) {
+    glyphPositionsCG[index].x = glyphPositions[index].x;
+    glyphPositionsCG[index].y = glyphPositions[index].y;
+  }
+#else
+  CGPoint* glyphPositionsCG = (CGPoint*)glyphPositions;
+#endif
+  CGContextShowGlyphsAtPositions(context, (CGGlyph*)glyphIndices,
+                                 glyphPositionsCG, charsCount);
+#if CGFLOAT_IS_DOUBLE
+  delete[] glyphPositionsCG;
+#endif
+  CGContextRestoreGState(context);
+  return TRUE;
+}
+void CQuartz2D::saveGraphicsState(void* graphics) {
+  if (graphics) {
+    CGContextSaveGState((CGContextRef)graphics);
+  }
+}
+void CQuartz2D::restoreGraphicsState(void* graphics) {
+  if (graphics) {
+    CGContextRestoreGState((CGContextRef)graphics);
+  }
+}
+static CGContextRef createContextWithBitmap(CFX_DIBitmap* pBitmap) {
+  if (!pBitmap || pBitmap->IsCmykImage() || pBitmap->GetBPP() < 32) {
+    return NULL;
+  }
+  CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Little;
+  if (pBitmap->HasAlpha()) {
+    bitmapInfo |= kCGImageAlphaPremultipliedFirst;
+  } else {
+    bitmapInfo |= kCGImageAlphaNoneSkipFirst;
+  }
+  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
+  CGContextRef context = CGBitmapContextCreate(
+      pBitmap->GetBuffer(), pBitmap->GetWidth(), pBitmap->GetHeight(), 8,
+      pBitmap->GetPitch(), colorSpace, bitmapInfo);
+  CGColorSpaceRelease(colorSpace);
+  return context;
+}
+CFX_QuartzDeviceDriver::CFX_QuartzDeviceDriver(CGContextRef context,
+                                               int32_t deviceClass) {
+  m_saveCount = 0;
+  _context = context;
+  _deviceClass = deviceClass;
+  CGContextRetain(_context);
+  CGRect r = CGContextGetClipBoundingBox(context);
+  _width = FXSYS_round(r.size.width);
+  _height = FXSYS_round(r.size.height);
+  _renderCaps = FXRC_SOFT_CLIP | FXRC_BLEND_MODE | FXRC_ALPHA_PATH |
+                FXRC_ALPHA_IMAGE | FXRC_BIT_MASK | FXRC_ALPHA_MASK;
+  if (_deviceClass != FXDC_DISPLAY) {
+  } else {
+    CGImageRef image = CGBitmapContextCreateImage(_context);
+    if (image) {
+      _renderCaps |= FXRC_GET_BITS;
+      _width = CGImageGetWidth(image);
+      _height = CGImageGetHeight(image);
+      CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(image);
+      if (kCGImageAlphaPremultipliedFirst == alphaInfo ||
+          kCGImageAlphaPremultipliedLast == alphaInfo ||
+          kCGImageAlphaOnly == alphaInfo) {
+        _renderCaps |= FXRC_ALPHA_OUTPUT;
+      }
+    }
+    CGImageRelease(image);
+  }
+  CGAffineTransform ctm = CGContextGetCTM(_context);
+  CGContextSaveGState(_context);
+  m_saveCount++;
+  if (ctm.d >= 0) {
+    CGFloat offset_x, offset_y;
+    offset_x = ctm.tx;
+    offset_y = ctm.ty;
+    CGContextTranslateCTM(_context, -offset_x, -offset_y);
+    CGContextConcatCTM(_context, CGAffineTransformMake(1, 0, 0, -1, offset_x,
+                                                       _height + offset_y));
+  }
+  _foxitDevice2User = CGAffineTransformIdentity;
+  _user2FoxitDevice = CGAffineTransformInvert(_foxitDevice2User);
+}
+CFX_QuartzDeviceDriver::~CFX_QuartzDeviceDriver() {
+  CGContextRestoreGState(_context);
+  m_saveCount--;
+  for (int i = 0; i < m_saveCount; ++i) {
+    CGContextRestoreGState(_context);
+  }
+  if (_context) {
+    CGContextRelease(_context);
+  }
+}
+int CFX_QuartzDeviceDriver::GetDeviceCaps(int capsID) {
+  switch (capsID) {
+    case FXDC_DEVICE_CLASS: {
+      return _deviceClass;
+    }
+    case FXDC_PIXEL_WIDTH: {
+      return _width;
+    }
+    case FXDC_PIXEL_HEIGHT: {
+      return _height;
+    }
+    case FXDC_BITS_PIXEL: {
+      return 32;
+    }
+    case FXDC_RENDER_CAPS: {
+      return _renderCaps;
+    }
+    default: { return 0; }
+  }
+}
+CFX_Matrix CFX_QuartzDeviceDriver::GetCTM() const {
+  CGAffineTransform ctm = CGContextGetCTM(_context);
+  return CFX_Matrix(ctm.a, ctm.b, ctm.c, ctm.d, ctm.tx, ctm.ty);
+}
+void CFX_QuartzDeviceDriver::SaveState() {
+  CGContextSaveGState(_context);
+  m_saveCount++;
+}
+void CFX_QuartzDeviceDriver::RestoreState(FX_BOOL isKeepSaved) {
+  CGContextRestoreGState(_context);
+  if (isKeepSaved) {
+    CGContextSaveGState(_context);
+  } else {
+    m_saveCount--;
+  }
+}
+FX_BOOL CFX_QuartzDeviceDriver::SetClip_PathFill(const CFX_PathData* pathData,
+                                                 const CFX_AffineMatrix* matrix,
+                                                 int fillMode) {
+  SaveState();
+  CGAffineTransform m = CGAffineTransformIdentity;
+  if (matrix) {
+    m = CGAffineTransformMake(matrix->GetA(), matrix->GetB(), matrix->GetC(),
+                              matrix->GetD(), matrix->GetE(), matrix->GetF());
+  }
+  m = CGAffineTransformConcat(m, _foxitDevice2User);
+  CGContextConcatCTM(_context, m);
+  setPathToContext(pathData);
+  RestoreState(FALSE);
+  if ((fillMode & 3) == FXFILL_WINDING) {
+    CGContextClip(_context);
+  } else {
+    CGContextEOClip(_context);
+  }
+  return TRUE;
+}
+FX_FLOAT CFX_QuartzDeviceDriver::getLineWidth(
+    const CFX_GraphStateData* graphState,
+    CGAffineTransform ctm) {
+  FX_FLOAT lineWidth = graphState->m_LineWidth;
+  if (graphState->m_LineWidth <= 0.f) {
+    CGSize size;
+    size.width = 1;
+    size.height = 1;
+    CGSize temp = CGSizeApplyAffineTransform(size, ctm);
+    CGFloat x = 1 / temp.width;
+    CGFloat y = 1 / temp.height;
+    lineWidth = x > y ? x : y;
+  }
+  return lineWidth;
+}
+FX_BOOL CFX_QuartzDeviceDriver::SetClip_PathStroke(
+    const CFX_PathData* pathData,
+    const CFX_AffineMatrix* matrix,
+    const CFX_GraphStateData* graphState) {
+  SaveState();
+  CGAffineTransform m = CGAffineTransformIdentity;
+  if (matrix) {
+    m = CGAffineTransformMake(matrix->GetA(), matrix->GetB(), matrix->GetC(),
+                              matrix->GetD(), matrix->GetE(), matrix->GetF());
+  }
+  m = CGAffineTransformConcat(m, _foxitDevice2User);
+  CGContextConcatCTM(_context, m);
+  FX_FLOAT lineWidth = getLineWidth(graphState, m);
+  setStrokeInfo(graphState, 0xFF000000, lineWidth);
+  setPathToContext(pathData);
+  CGContextReplacePathWithStrokedPath(_context);
+  RestoreState(FALSE);
+  CGContextClip(_context);
+  return TRUE;
+}
+static CGBlendMode GetCGBlendMode(int blend_type) {
+  CGBlendMode mode = kCGBlendModeNormal;
+  switch (blend_type) {
+    case FXDIB_BLEND_NORMAL:
+      mode = kCGBlendModeNormal;
+      break;
+    case FXDIB_BLEND_MULTIPLY:
+      mode = kCGBlendModeMultiply;
+      break;
+    case FXDIB_BLEND_SCREEN:
+      mode = kCGBlendModeScreen;
+      break;
+    case FXDIB_BLEND_OVERLAY:
+      mode = kCGBlendModeOverlay;
+      break;
+    case FXDIB_BLEND_DARKEN:
+      mode = kCGBlendModeDarken;
+      break;
+    case FXDIB_BLEND_LIGHTEN:
+      mode = kCGBlendModeLighten;
+      break;
+    case FXDIB_BLEND_COLORDODGE:
+      mode = kCGBlendModeColorDodge;
+      break;
+    case FXDIB_BLEND_COLORBURN:
+      mode = kCGBlendModeColorBurn;
+      break;
+    case FXDIB_BLEND_HARDLIGHT:
+      mode = kCGBlendModeHardLight;
+      break;
+    case FXDIB_BLEND_SOFTLIGHT:
+      mode = kCGBlendModeSoftLight;
+      break;
+    case FXDIB_BLEND_DIFFERENCE:
+      mode = kCGBlendModeDifference;
+      break;
+    case FXDIB_BLEND_EXCLUSION:
+      mode = kCGBlendModeExclusion;
+      break;
+    case FXDIB_BLEND_HUE:
+      mode = kCGBlendModeHue;
+      break;
+    case FXDIB_BLEND_SATURATION:
+      mode = kCGBlendModeSaturation;
+      break;
+    case FXDIB_BLEND_COLOR:
+      mode = kCGBlendModeColor;
+      break;
+    case FXDIB_BLEND_LUMINOSITY:
+      mode = kCGBlendModeLuminosity;
+      break;
+    default:
+      mode = kCGBlendModeNormal;
+      break;
+  }
+  return mode;
+}
+FX_BOOL CFX_QuartzDeviceDriver::DrawPath(const CFX_PathData* pathData,
+                                         const CFX_AffineMatrix* matrix,
+                                         const CFX_GraphStateData* graphState,
+                                         FX_DWORD fillArgb,
+                                         FX_DWORD strokeArgb,
+                                         int fillMode,
+                                         int alpha_flag,
+                                         void* pIccTransform,
+                                         int blend_type) {
+  SaveState();
+  CGBlendMode mode = GetCGBlendMode(blend_type);
+  if (mode != kCGBlendModeNormal) {
+    CGContextSetBlendMode(_context, mode);
+  }
+  CGAffineTransform m = CGAffineTransformIdentity;
+  if (matrix) {
+    m = CGAffineTransformMake(matrix->GetA(), matrix->GetB(), matrix->GetC(),
+                              matrix->GetD(), matrix->GetE(), matrix->GetF());
+  }
+  m = CGAffineTransformConcat(m, _foxitDevice2User);
+  CGContextConcatCTM(_context, m);
+  int pathMode = 0;
+  if (graphState && strokeArgb) {
+    CGContextSetMiterLimit(_context, graphState->m_MiterLimit);
+    FX_FLOAT lineWidth = getLineWidth(graphState, m);
+    setStrokeInfo(graphState, strokeArgb, lineWidth);
+    pathMode |= 4;
+  }
+  if (fillMode && fillArgb) {
+    setFillInfo(fillArgb);
+    if ((fillMode & 3) == FXFILL_WINDING) {
+      pathMode |= 1;
+    } else if ((fillMode & 3) == FXFILL_ALTERNATE) {
+      pathMode |= 2;
+    }
+  }
+  setPathToContext(pathData);
+  if (fillMode & FXFILL_FULLCOVER) {
+    CGContextSetShouldAntialias(_context, false);
+  }
+  if (pathMode == 4) {
+    CGContextStrokePath(_context);
+  } else if (pathMode == 1) {
+    CGContextFillPath(_context);
+  } else if (pathMode == 2) {
+    CGContextEOFillPath(_context);
+  } else if (pathMode == 5) {
+    CGContextDrawPath(_context, kCGPathFillStroke);
+  } else if (pathMode == 6) {
+    CGContextDrawPath(_context, kCGPathEOFillStroke);
+  }
+  RestoreState(FALSE);
+  return TRUE;
+}
+FX_BOOL CFX_QuartzDeviceDriver::FillRect(const FX_RECT* rect,
+                                         FX_ARGB fillArgb,
+                                         int alphaFlag,
+                                         void* iccTransform,
+                                         int blend_type) {
+  CGBlendMode mode = GetCGBlendMode(blend_type);
+  if (mode != kCGBlendModeNormal) {
+    CGContextSetBlendMode(_context, mode);
+  }
+  CGRect rect_fx =
+      CGRectMake(rect->left, rect->top, rect->Width(), rect->Height());
+  CGRect rect_usr = CGRectApplyAffineTransform(rect_fx, _foxitDevice2User);
+  int32_t a, r, g, b;
+  ArgbDecode(fillArgb, a, r, g, b);
+  CGContextSetRGBFillColor(_context, r / 255.f, g / 255.f, b / 255.f,
+                           a / 255.f);
+  CGContextFillRect(_context, rect_usr);
+  if (mode != kCGBlendModeNormal) {
+    CGContextSetBlendMode(_context, kCGBlendModeNormal);
+  }
+  return TRUE;
+}
+FX_BOOL CFX_QuartzDeviceDriver::DrawCosmeticLine(FX_FLOAT x1,
+                                                 FX_FLOAT y1,
+                                                 FX_FLOAT x2,
+                                                 FX_FLOAT y2,
+                                                 FX_DWORD argb,
+                                                 int alphaFlag,
+                                                 void* iccTransform,
+                                                 int blend_type) {
+  CGBlendMode mode = GetCGBlendMode(blend_type);
+  if (mode != kCGBlendModeNormal) {
+    CGContextSetBlendMode(_context, mode);
+  }
+  CGPoint pt =
+      CGPointApplyAffineTransform(CGPointMake(x1, y1), _foxitDevice2User);
+  x1 = pt.x;
+  y1 = pt.y;
+  pt = CGPointApplyAffineTransform(CGPointMake(x2, y2), _foxitDevice2User);
+  x2 = pt.x;
+  y2 = pt.y;
+  int32_t a, r, g, b;
+  ArgbDecode(argb, a, r, g, b);
+  CGContextSetRGBStrokeColor(_context, r / 255.f, g / 255.f, b / 255.f,
+                             a / 255.f);
+  CGContextMoveToPoint(_context, x1, y1);
+  CGContextAddLineToPoint(_context, x2, y2);
+  CGContextStrokePath(_context);
+  if (mode != kCGBlendModeNormal) {
+    CGContextSetBlendMode(_context, kCGBlendModeNormal);
+  }
+  return TRUE;
+}
+FX_BOOL CFX_QuartzDeviceDriver::GetClipBox(FX_RECT* rect) {
+  CGRect r = CGContextGetClipBoundingBox(_context);
+  r = CGRectApplyAffineTransform(r, _user2FoxitDevice);
+  rect->left = FXSYS_floor(r.origin.x);
+  rect->top = FXSYS_floor(r.origin.y);
+  rect->right = FXSYS_ceil(r.origin.x + r.size.width);
+  rect->bottom = FXSYS_ceil(r.origin.y + r.size.height);
+  return TRUE;
+}
+FX_BOOL CFX_QuartzDeviceDriver::GetDIBits(CFX_DIBitmap* bitmap,
+                                          int32_t left,
+                                          int32_t top,
+                                          void* pIccTransform,
+                                          FX_BOOL bDEdge) {
+  if (FXDC_PRINTER == _deviceClass) {
+    return FALSE;
+  }
+  if (bitmap->GetBPP() < 32) {
+    return FALSE;
+  }
+  if (!(_renderCaps | FXRC_GET_BITS)) {
+    return FALSE;
+  }
+  CGPoint pt = CGPointMake(left, top);
+  pt = CGPointApplyAffineTransform(pt, _foxitDevice2User);
+  CGAffineTransform ctm = CGContextGetCTM(_context);
+  pt.x *= FXSYS_fabs(ctm.a);
+  pt.y *= FXSYS_fabs(ctm.d);
+  CGImageRef image = CGBitmapContextCreateImage(_context);
+  if (NULL == image) {
+    return FALSE;
+  }
+  CGFloat width = (CGFloat)bitmap->GetWidth();
+  CGFloat height = (CGFloat)bitmap->GetHeight();
+  if (width + pt.x > _width) {
+    width -= (width + pt.x - _width);
+  }
+  if (height + pt.y > _height) {
+    height -= (height + pt.y - _height);
+  }
+  CGImageRef subImage = CGImageCreateWithImageInRect(
+      image, CGRectMake(pt.x, pt.y, width, height));
+  CGContextRef context = createContextWithBitmap(bitmap);
+  CGRect rect = CGContextGetClipBoundingBox(context);
+  CGContextClearRect(context, rect);
+  CGContextDrawImage(context, rect, subImage);
+  CGContextRelease(context);
+  CGImageRelease(subImage);
+  CGImageRelease(image);
+  if (bitmap->HasAlpha()) {
+    for (int row = 0; row < bitmap->GetHeight(); row++) {
+      uint8_t* pScanline = (uint8_t*)bitmap->GetScanline(row);
+      for (int col = 0; col < bitmap->GetWidth(); col++) {
+        if (pScanline[3] > 0) {
+          pScanline[0] = (pScanline[0] * 255.f / pScanline[3] + .5f);
+          pScanline[1] = (pScanline[1] * 255.f / pScanline[3] + .5f);
+          pScanline[2] = (pScanline[2] * 255.f / pScanline[3] + .5f);
+        }
+        pScanline += 4;
+      }
+    }
+  }
+  return TRUE;
+}
+FX_BOOL CFX_QuartzDeviceDriver::SetDIBits(const CFX_DIBSource* pBitmap,
+                                          FX_ARGB argb,
+                                          const FX_RECT* srcRect,
+                                          int dest_left,
+                                          int dest_top,
+                                          int blendType,
+                                          int alphaFlag,
+                                          void* iccTransform) {
+  SaveState();
+  CGFloat src_left, src_top, src_width, src_height;
+  if (srcRect) {
+    src_left = srcRect->left;
+    src_top = srcRect->top;
+    src_width = srcRect->Width();
+    src_height = srcRect->Height();
+  } else {
+    src_left = src_top = 0;
+    src_width = pBitmap->GetWidth();
+    src_height = pBitmap->GetHeight();
+  }
+  CGAffineTransform ctm = CGContextGetCTM(_context);
+  CGFloat scale_x = FXSYS_fabs(ctm.a);
+  CGFloat scale_y = FXSYS_fabs(ctm.d);
+  src_left /= scale_x;
+  src_top /= scale_y;
+  src_width /= scale_x;
+  src_height /= scale_y;
+  CGRect rect_fx = CGRectMake(dest_left, dest_top, src_width, src_height);
+  CGRect rect_usr = CGRectApplyAffineTransform(rect_fx, _foxitDevice2User);
+  CGContextBeginPath(_context);
+  CGContextAddRect(_context, rect_usr);
+  CGContextClip(_context);
+  rect_usr.size =
+      CGSizeMake(pBitmap->GetWidth() / scale_x, pBitmap->GetHeight() / scale_y);
+  rect_usr = CGRectOffset(rect_usr, -src_left, -src_top);
+  CG_SetImageTransform(dest_left, dest_top, src_width, src_height, &rect_usr);
+  CFX_DIBitmap* pBitmap1 = NULL;
+  if (pBitmap->IsAlphaMask()) {
+    if (pBitmap->GetBuffer()) {
+      pBitmap1 = (CFX_DIBitmap*)pBitmap;
+    } else {
+      pBitmap1 = pBitmap->Clone();
+    }
+    if (NULL == pBitmap1) {
+      RestoreState(FALSE);
+      return FALSE;
+    }
+    CGDataProviderRef pBitmapProvider = CGDataProviderCreateWithData(
+        NULL, pBitmap1->GetBuffer(),
+        pBitmap1->GetPitch() * pBitmap1->GetHeight(), NULL);
+    CGColorSpaceRef pColorSpace = CGColorSpaceCreateDeviceGray();
+    CGBitmapInfo bitmapInfo = kCGImageAlphaNone | kCGBitmapByteOrderDefault;
+    CGImageRef pImage = CGImageCreate(
+        pBitmap1->GetWidth(), pBitmap1->GetHeight(), pBitmap1->GetBPP(),
+        pBitmap1->GetBPP(), pBitmap1->GetPitch(), pColorSpace, bitmapInfo,
+        pBitmapProvider, NULL, true, kCGRenderingIntentDefault);
+    CGContextClipToMask(_context, rect_usr, pImage);
+    CGContextSetRGBFillColor(_context, FXARGB_R(argb) / 255.f,
+                             FXARGB_G(argb) / 255.f, FXARGB_B(argb) / 255.f,
+                             FXARGB_A(argb) / 255.f);
+    CGContextFillRect(_context, rect_usr);
+    CGImageRelease(pImage);
+    CGColorSpaceRelease(pColorSpace);
+    CGDataProviderRelease(pBitmapProvider);
+    if (pBitmap1 != pBitmap) {
+      delete pBitmap1;
+    }
+    RestoreState(FALSE);
+    return TRUE;
+  }
+  if (pBitmap->GetBPP() < 32) {
+    pBitmap1 = pBitmap->CloneConvert(FXDIB_Rgb32);
+  } else {
+    if (pBitmap->GetBuffer()) {
+      pBitmap1 = (CFX_DIBitmap*)pBitmap;
+    } else {
+      pBitmap1 = pBitmap->Clone();
+    }
+  }
+  if (NULL == pBitmap1) {
+    RestoreState(FALSE);
+    return FALSE;
+  }
+  if (pBitmap1->HasAlpha()) {
+    if (pBitmap1 == pBitmap) {
+      pBitmap1 = pBitmap->Clone();
+      if (!pBitmap1) {
+        RestoreState(FALSE);
+        return FALSE;
+      }
+    }
+    for (int row = 0; row < pBitmap1->GetHeight(); row++) {
+      uint8_t* pScanline = (uint8_t*)pBitmap1->GetScanline(row);
+      for (int col = 0; col < pBitmap1->GetWidth(); col++) {
+        pScanline[0] = (uint8_t)(pScanline[0] * pScanline[3] / 255.f + .5f);
+        pScanline[1] = (uint8_t)(pScanline[1] * pScanline[3] / 255.f + .5f);
+        pScanline[2] = (uint8_t)(pScanline[2] * pScanline[3] / 255.f + .5f);
+        pScanline += 4;
+      }
+    }
+  }
+  CGContextRef ctx = createContextWithBitmap(pBitmap1);
+  CGImageRef image = CGBitmapContextCreateImage(ctx);
+  int blend_mode = blendType;
+  if (FXDIB_BLEND_HARDLIGHT == blendType) {
+    blend_mode = kCGBlendModeSoftLight;
+  } else if (FXDIB_BLEND_SOFTLIGHT == blendType) {
+    blend_mode = kCGBlendModeHardLight;
+  } else if (blendType >= FXDIB_BLEND_NONSEPARABLE &&
+             blendType <= FXDIB_BLEND_LUMINOSITY) {
+    blend_mode = blendType - 9;
+  } else if (blendType > FXDIB_BLEND_LUMINOSITY || blendType < 0) {
+    blend_mode = kCGBlendModeNormal;
+  }
+  CGContextSetBlendMode(_context, (CGBlendMode)blend_mode);
+  CGContextDrawImage(_context, rect_usr, image);
+  CGImageRelease(image);
+  CGContextRelease(ctx);
+  if (pBitmap1 != pBitmap) {
+    delete pBitmap1;
+  }
+  RestoreState(FALSE);
+  return TRUE;
+}
+FX_BOOL CFX_QuartzDeviceDriver::StretchDIBits(const CFX_DIBSource* pBitmap,
+                                              FX_ARGB argb,
+                                              int dest_left,
+                                              int dest_top,
+                                              int dest_width,
+                                              int dest_height,
+                                              const FX_RECT* clipRect,
+                                              FX_DWORD flags,
+                                              int alphaFlag,
+                                              void* iccTransform,
+                                              int blend_type) {
+  SaveState();
+  if (clipRect) {
+    CGContextBeginPath(_context);
+    CGRect rect_clip = CGRectMake(clipRect->left, clipRect->top,
+                                  clipRect->Width(), clipRect->Height());
+    rect_clip = CGRectApplyAffineTransform(rect_clip, _foxitDevice2User);
+    CGContextAddRect(_context, rect_clip);
+    CGContextClip(_context);
+  }
+  CGRect rect = CGRectMake(dest_left, dest_top, dest_width, dest_height);
+  rect = CGRectApplyAffineTransform(rect, _foxitDevice2User);
+  if (FXDIB_BICUBIC_INTERPOL == flags) {
+    CGContextSetInterpolationQuality(_context, kCGInterpolationHigh);
+  } else if (FXDIB_DOWNSAMPLE == flags) {
+    CGContextSetInterpolationQuality(_context, kCGInterpolationNone);
+  } else {
+    CGContextSetInterpolationQuality(_context, kCGInterpolationMedium);
+  }
+  CG_SetImageTransform(dest_left, dest_top, dest_width, dest_height);
+  CFX_DIBitmap* pBitmap1 = NULL;
+  if (pBitmap->IsAlphaMask()) {
+    if (pBitmap->GetBuffer()) {
+      pBitmap1 = (CFX_DIBitmap*)pBitmap;
+    } else {
+      pBitmap1 = pBitmap->Clone();
+    }
+    if (NULL == pBitmap1) {
+      RestoreState(FALSE);
+      return FALSE;
+    }
+    CGDataProviderRef pBitmapProvider = CGDataProviderCreateWithData(
+        NULL, pBitmap1->GetBuffer(),
+        pBitmap1->GetPitch() * pBitmap1->GetHeight(), NULL);
+    CGColorSpaceRef pColorSpace = CGColorSpaceCreateDeviceGray();
+    CGBitmapInfo bitmapInfo = kCGImageAlphaNone | kCGBitmapByteOrderDefault;
+    CGImageRef pImage = CGImageCreate(
+        pBitmap1->GetWidth(), pBitmap1->GetHeight(), pBitmap1->GetBPP(),
+        pBitmap1->GetBPP(), pBitmap1->GetPitch(), pColorSpace, bitmapInfo,
+        pBitmapProvider, NULL, true, kCGRenderingIntentDefault);
+    CGContextClipToMask(_context, rect, pImage);
+    CGContextSetRGBFillColor(_context, FXARGB_R(argb) / 255.f,
+                             FXARGB_G(argb) / 255.f, FXARGB_B(argb) / 255.f,
+                             FXARGB_A(argb) / 255.f);
+    CGContextFillRect(_context, rect);
+    CGImageRelease(pImage);
+    CGColorSpaceRelease(pColorSpace);
+    CGDataProviderRelease(pBitmapProvider);
+    if (pBitmap1 != pBitmap) {
+      delete pBitmap1;
+    }
+    RestoreState(FALSE);
+    return TRUE;
+  }
+  if (pBitmap->GetBPP() < 32) {
+    pBitmap1 = pBitmap->CloneConvert(FXDIB_Rgb32);
+  } else {
+    if (pBitmap->GetBuffer()) {
+      pBitmap1 = (CFX_DIBitmap*)pBitmap;
+    } else {
+      pBitmap1 = pBitmap->Clone();
+    }
+  }
+  if (NULL == pBitmap1) {
+    RestoreState(FALSE);
+    return FALSE;
+  }
+  if (pBitmap1->HasAlpha()) {
+    if (pBitmap1 == pBitmap) {
+      pBitmap1 = pBitmap->Clone();
+      if (!pBitmap1) {
+        RestoreState(FALSE);
+        return FALSE;
+      }
+    }
+    for (int row = 0; row < pBitmap1->GetHeight(); row++) {
+      uint8_t* pScanline = (uint8_t*)pBitmap1->GetScanline(row);
+      for (int col = 0; col < pBitmap1->GetWidth(); col++) {
+        pScanline[0] = (uint8_t)(pScanline[0] * pScanline[3] / 255.f + .5f);
+        pScanline[1] = (uint8_t)(pScanline[1] * pScanline[3] / 255.f + .5f);
+        pScanline[2] = (uint8_t)(pScanline[2] * pScanline[3] / 255.f + .5f);
+        pScanline += 4;
+      }
+    }
+  }
+  CGContextRef ctx = createContextWithBitmap(pBitmap1);
+  CGImageRef image = CGBitmapContextCreateImage(ctx);
+  CGContextDrawImage(_context, rect, image);
+  CGImageRelease(image);
+  CGContextRelease(ctx);
+  if (pBitmap1 != pBitmap) {
+    delete pBitmap1;
+  }
+  RestoreState(FALSE);
+  return TRUE;
+}
+FX_BOOL CFX_QuartzDeviceDriver::CG_DrawGlypRun(
+    int nChars,
+    const FXTEXT_CHARPOS* pCharPos,
+    CFX_Font* pFont,
+    CFX_FontCache* pCache,
+    const CFX_AffineMatrix* pGlyphMatrix,
+    const CFX_AffineMatrix* pObject2Device,
+    FX_FLOAT font_size,
+    FX_DWORD argb,
+    int alpha_flag,
+    void* pIccTransform) {
+  if (nChars == 0) {
+    return TRUE;
+  }
+  CQuartz2D& quartz2d =
+      ((CApplePlatform*)CFX_GEModule::Get()->GetPlatformData())->_quartz2d;
+  if (!pFont->m_pPlatformFont) {
+    if (pFont->GetPsName() == CFX_WideString::FromLocal("DFHeiStd-W5")) {
+      return FALSE;
+    }
+    pFont->m_pPlatformFont =
+        quartz2d.CreateFont(pFont->m_pFontData, pFont->m_dwSize);
+    if (NULL == pFont->m_pPlatformFont) {
+      return FALSE;
+    }
+  }
+  CFX_FixedBufGrow<FX_WORD, 32> glyph_indices(nChars);
+  CFX_FixedBufGrow<CGPoint, 32> glyph_positions(nChars);
+  for (int i = 0; i < nChars; i++) {
+    glyph_indices[i] = pCharPos[i].m_ExtGID;
+    glyph_positions[i].x = pCharPos[i].m_OriginX;
+    glyph_positions[i].y = pCharPos[i].m_OriginY;
+  }
+  CFX_AffineMatrix text_matrix;
+  if (pObject2Device) {
+    text_matrix.Concat(*pObject2Device);
+  }
+  CGAffineTransform matrix_cg =
+      CGAffineTransformMake(text_matrix.a, text_matrix.b, text_matrix.c,
+                            text_matrix.d, text_matrix.e, text_matrix.f);
+  matrix_cg = CGAffineTransformConcat(matrix_cg, _foxitDevice2User);
+  CGContextSetTextMatrix(_context, matrix_cg);
+  CGContextSetFont(_context, (CGFontRef)pFont->m_pPlatformFont);
+  CGContextSetFontSize(_context, FXSYS_fabs(font_size));
+  int32_t a, r, g, b;
+  ArgbDecode(argb, a, r, g, b);
+  CGContextSetRGBFillColor(_context, r / 255.f, g / 255.f, b / 255.f,
+                           a / 255.f);
+  SaveState();
+  if (pGlyphMatrix) {
+    CGPoint origin = CGPointMake(glyph_positions[0].x, glyph_positions[0].y);
+    origin = CGPointApplyAffineTransform(origin, matrix_cg);
+    CGContextTranslateCTM(_context, origin.x, origin.y);
+    CGAffineTransform glyph_matrix = CGAffineTransformMake(
+        pGlyphMatrix->a, pGlyphMatrix->b, pGlyphMatrix->c, pGlyphMatrix->d,
+        pGlyphMatrix->e, pGlyphMatrix->f);
+    if (_foxitDevice2User.d < 0) {
+      glyph_matrix = CGAffineTransformInvert(glyph_matrix);
+    }
+    CGContextConcatCTM(_context, glyph_matrix);
+    CGContextTranslateCTM(_context, -origin.x, -origin.y);
+  }
+  CGContextShowGlyphsAtPositions(_context, (CGGlyph*)glyph_indices,
+                                 glyph_positions, nChars);
+  RestoreState(FALSE);
+  return TRUE;
+}
+FX_BOOL CFX_QuartzDeviceDriver::DrawDeviceText(
+    int nChars,
+    const FXTEXT_CHARPOS* pCharPos,
+    CFX_Font* pFont,
+    CFX_FontCache* pCache,
+    const CFX_AffineMatrix* pObject2Device,
+    FX_FLOAT font_size,
+    FX_DWORD color,
+    int alpha_flag,
+    void* pIccTransform) {
+  if (NULL == pFont || NULL == _context) {
+    return FALSE;
+  }
+  FX_BOOL bBold = pFont->IsBold();
+  if (!bBold && pFont->GetSubstFont() &&
+      pFont->GetSubstFont()->m_Weight >= 500 &&
+      pFont->GetSubstFont()->m_Weight <= 600) {
+    return FALSE;
+  }
+  SaveState();
+  CGContextSetTextDrawingMode(_context, kCGTextFillClip);
+  FX_BOOL ret = FALSE;
+  int32_t i = 0;
+  while (i < nChars) {
+    if (pCharPos[i].m_bGlyphAdjust || font_size < 0) {
+      if (i > 0) {
+        ret = CG_DrawGlypRun(i, pCharPos, pFont, pCache, NULL, pObject2Device,
+                             font_size, color, alpha_flag, pIccTransform);
+        if (!ret) {
+          RestoreState(FALSE);
+          return ret;
+        }
+      }
+      const FXTEXT_CHARPOS* char_pos = pCharPos + i;
+      CFX_AffineMatrix glphy_matrix;
+      if (font_size < 0) {
+        glphy_matrix.Concat(-1, 0, 0, -1, 0, 0);
+      }
+      if (char_pos->m_bGlyphAdjust) {
+        glphy_matrix.Concat(
+            char_pos->m_AdjustMatrix[0], char_pos->m_AdjustMatrix[1],
+            char_pos->m_AdjustMatrix[2], char_pos->m_AdjustMatrix[3], 0, 0);
+      }
+      ret = CG_DrawGlypRun(1, char_pos, pFont, pCache, &glphy_matrix,
+                           pObject2Device, font_size, color, alpha_flag,
+                           pIccTransform);
+      if (!ret) {
+        RestoreState(FALSE);
+        return ret;
+      }
+      i++;
+      pCharPos += i;
+      nChars -= i;
+      i = 0;
+    } else {
+      i++;
+    }
+  }
+  if (i > 0) {
+    ret = CG_DrawGlypRun(i, pCharPos, pFont, pCache, NULL, pObject2Device,
+                         font_size, color, alpha_flag, pIccTransform);
+  }
+  RestoreState(FALSE);
+  return ret;
+}
+void CFX_QuartzDeviceDriver::setStrokeInfo(const CFX_GraphStateData* graphState,
+                                           FX_ARGB argb,
+                                           FX_FLOAT lineWidth) {
+  if (NULL == graphState) {
+    return;
+  }
+  CGContextSetLineWidth(_context, lineWidth);
+  CGLineCap cap;
+  switch (graphState->m_LineCap) {
+    case CFX_GraphStateData::LineCapRound: {
+      cap = kCGLineCapRound;
+      break;
+    }
+    case CFX_GraphStateData::LineCapSquare: {
+      cap = kCGLineCapSquare;
+      break;
+    }
+    case CFX_GraphStateData::LineCapButt:
+    default: { cap = kCGLineCapButt; }
+  }
+  CGContextSetLineCap(_context, cap);
+  CGLineJoin join;
+  switch (graphState->m_LineJoin) {
+    case CFX_GraphStateData::LineJoinRound: {
+      join = kCGLineJoinRound;
+      break;
+    }
+    case CFX_GraphStateData::LineJoinBevel: {
+      join = kCGLineJoinBevel;
+      break;
+    }
+    case CFX_GraphStateData::LineJoinMiter:
+    default: { join = kCGLineJoinMiter; }
+  }
+  CGContextSetLineJoin(_context, join);
+  if (graphState->m_DashCount) {
+#if CGFLOAT_IS_DOUBLE
+    CGFloat* dashArray = new CGFloat[graphState->m_DashCount];
+    for (int index = 0; index < graphState->m_DashCount; ++index) {
+      dashArray[index] = graphState->m_DashArray[index];
+    }
+#else
+    CGFloat* dashArray = (CGFloat*)graphState->m_DashArray;
+#endif
+    CGContextSetLineDash(_context, graphState->m_DashPhase, dashArray,
+                         graphState->m_DashCount);
+#if CGFLOAT_IS_DOUBLE
+    delete[] dashArray;
+#endif
+  }
+  int32_t a, r, g, b;
+  ArgbDecode(argb, a, r, g, b);
+  CGContextSetRGBStrokeColor(_context, r / 255.f, g / 255.f, b / 255.f,
+                             a / 255.f);
+}
+void CFX_QuartzDeviceDriver::setFillInfo(FX_ARGB argb) {
+  int32_t a, r, g, b;
+  ArgbDecode(argb, a, r, g, b);
+  CGContextSetRGBFillColor(_context, r / 255.f, g / 255.f, b / 255.f,
+                           a / 255.f);
+}
+void CFX_QuartzDeviceDriver::setPathToContext(const CFX_PathData* pathData) {
+  int32_t count = pathData->GetPointCount();
+  FX_PATHPOINT* points = pathData->GetPoints();
+  CGContextBeginPath(_context);
+  for (int32_t i = 0; i < count; i++) {
+    switch (points[i].m_Flag & FXPT_TYPE) {
+      case FXPT_MOVETO:
+        CGContextMoveToPoint(_context, points[i].m_PointX, points[i].m_PointY);
+        break;
+      case FXPT_LINETO:
+        CGContextAddLineToPoint(_context, points[i].m_PointX,
+                                points[i].m_PointY);
+        break;
+      case FXPT_BEZIERTO: {
+        CGContextAddCurveToPoint(_context, points[i].m_PointX,
+                                 points[i].m_PointY, points[i + 1].m_PointX,
+                                 points[i + 1].m_PointY, points[i + 2].m_PointX,
+                                 points[i + 2].m_PointY);
+        i += 2;
+      }
+    }
+    if (points[i].m_Flag & FXPT_CLOSEFIGURE) {
+      CGContextClosePath(_context);
+    }
+  }
+}
+void CFX_QuartzDeviceDriver::CG_SetImageTransform(int dest_left,
+                                                  int dest_top,
+                                                  int dest_width,
+                                                  int dest_height,
+                                                  CGRect* rect) {
+  int flip_y = _foxitDevice2User.d * dest_height < 0 ? 1 : -1;
+  int flip_x = _foxitDevice2User.a * dest_width > 0 ? 1 : -1;
+  if (flip_y < 0 || flip_x < 0) {
+    if (dest_height < 0) {
+      dest_height = -dest_height;
+      dest_top -= dest_height;
+    }
+    CGRect rt = CGRectApplyAffineTransform(
+        CGRectMake(dest_left, dest_top, dest_width, dest_height),
+        _foxitDevice2User);
+    CGFloat offset_x = (rt.origin.x) + rt.size.width / 2.f,
+            offset_y = (rt.origin.y) + rt.size.height / 2.f;
+    CGAffineTransform transform = CGAffineTransformIdentity;
+    transform = CGAffineTransformConcat(
+        transform, CGAffineTransformMake(1, 0, 0, 1, -offset_x, -offset_y));
+    transform = CGAffineTransformConcat(
+        transform, CGAffineTransformMake(flip_x, 0, 0, flip_y, 0, 0));
+    transform = CGAffineTransformConcat(
+        transform, CGAffineTransformMake(1, 0, 0, 1, offset_x, offset_y));
+    CGContextConcatCTM(_context, transform);
+    if (rect) {
+      *rect = CGRectApplyAffineTransform(*rect, transform);
+    }
+  }
+}
+void CFX_QuartzDeviceDriver::ClearDriver() {
+  if (NULL == _context) {
+    return;
+  }
+  for (int i = 0; i < m_saveCount; ++i) {
+    CGContextRestoreGState(_context);
+  }
+  m_saveCount = 0;
+  if (_context) {
+    CGContextRelease(_context);
+  }
+}
+CFX_QuartzDevice::CFX_QuartzDevice() {
+  m_bOwnedBitmap = FALSE;
+  m_pContext = NULL;
+}
+CFX_QuartzDevice::~CFX_QuartzDevice() {
+  if (m_pContext) {
+    CGContextRelease(m_pContext);
+  }
+  if (m_bOwnedBitmap) {
+    delete GetBitmap();
+  }
+}
+CGContextRef CFX_QuartzDevice::GetContext() {
+  return m_pContext;
+}
+FX_BOOL CFX_QuartzDevice::Attach(CGContextRef context, int32_t nDeviceClass) {
+  if (m_pContext) {
+    CGContextRelease(m_pContext);
+  }
+  m_pContext = context;
+  CGContextRetain(m_pContext);
+  IFX_RenderDeviceDriver* pDriver =
+      new CFX_QuartzDeviceDriver(m_pContext, nDeviceClass);
+  SetDeviceDriver(pDriver);
+  return TRUE;
+}
+FX_BOOL CFX_QuartzDevice::Attach(CFX_DIBitmap* pBitmap) {
+  SetBitmap(pBitmap);
+  m_pContext = createContextWithBitmap(pBitmap);
+  if (NULL == m_pContext) {
+    return FALSE;
+  }
+  IFX_RenderDeviceDriver* pDriver =
+      new CFX_QuartzDeviceDriver(m_pContext, FXDC_DISPLAY);
+  SetDeviceDriver(pDriver);
+  return TRUE;
+}
+FX_BOOL CFX_QuartzDevice::Create(int32_t width,
+                                 int32_t height,
+                                 FXDIB_Format format) {
+  if ((uint8_t)format < 32) {
+    return FALSE;
+  }
+  CFX_DIBitmap* pBitmap = new CFX_DIBitmap;
+  if (!pBitmap->Create(width, height, format)) {
+    delete pBitmap;
+    return FALSE;
+  }
+  m_bOwnedBitmap = TRUE;
+  return Attach(pBitmap);
+}
+#endif  // _FXM_PLATFORM_  == _FXM_PLATFORM_APPLE_