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