Home | History | Annotate | Download | only in apple
      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
     20 void* 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 }
     45 void CQuartz2D::destroyGraphics(void* graphics)
     46 {
     47     if (graphics) {
     48         CGContextRelease((CGContextRef) graphics);
     49     }
     50 }
     51 void* 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 }
     61 void CQuartz2D::DestroyFont(void* pFont)
     62 {
     63     CGFontRelease((CGFontRef)pFont);
     64 }
     65 void 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 }
     79 FX_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 }
    135 void CQuartz2D::saveGraphicsState(void * graphics)
    136 {
    137     if (graphics) {
    138         CGContextSaveGState((CGContextRef) graphics);
    139     }
    140 }
    141 void CQuartz2D::restoreGraphicsState(void * graphics)
    142 {
    143     if (graphics) {
    144         CGContextRestoreGState((CGContextRef) graphics);
    145     }
    146 }
    147 static 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 }
    169 CFX_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 }
    210 CFX_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 }
    221 int 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 }
    244 CFX_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 }
    249 void CFX_QuartzDeviceDriver::SaveState()
    250 {
    251     CGContextSaveGState(_context);
    252     m_saveCount++;
    253 }
    254 void CFX_QuartzDeviceDriver::RestoreState(FX_BOOL isKeepSaved )
    255 {
    256     CGContextRestoreGState(_context);
    257     if (isKeepSaved) {
    258         CGContextSaveGState(_context);
    259     } else {
    260         m_saveCount--;
    261     }
    262 }
    263 FX_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 }
    283 FX_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 }
    297 FX_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 }
    316 static 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 }
    374 FX_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 }
    429 FX_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 }
    454 FX_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 }
    488 FX_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 }
    498 FX_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 }
    557 FX_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 }
    688 FX_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 }
    802 FX_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 }
    879 FX_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 }
    941 void 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 }
   1004 void 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 }
   1014 void 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 }
   1040 void 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 }
   1063 void 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 }
   1076 CFX_QuartzDevice::CFX_QuartzDevice()
   1077 {
   1078     m_bOwnedBitmap = FALSE;
   1079     m_pContext = NULL;
   1080 }
   1081 CFX_QuartzDevice::~CFX_QuartzDevice()
   1082 {
   1083     if (m_pContext) {
   1084         CGContextRelease(m_pContext);
   1085     }
   1086     if (GetBitmap() && m_bOwnedBitmap) {
   1087         delete GetBitmap();
   1088     }
   1089 }
   1090 CGContextRef CFX_QuartzDevice::GetContext()
   1091 {
   1092     return m_pContext;
   1093 }
   1094 FX_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 }
   1108 FX_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 }
   1122 FX_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