blob: 8f0f4db6ba6f91b94ee7870121982f5cbfc81722 [file] [log] [blame]
// 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 (GetBitmap() && 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_