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