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