void QFontEngineMac::draw(CGContextRef ctx, qreal x, qreal y, const QTextItemInt &ti, int paintDeviceHeight) { QVarLengthArray<QFixedPoint> positions; QVarLengthArray<glyph_t> glyphs; QTransform matrix; matrix.translate(x, y); getGlyphPositions(ti.glyphs, ti.num_glyphs, matrix, ti.flags, glyphs, positions); if (glyphs.size() == 0) return; CGContextSetFontSize(ctx, fontDef.pixelSize); CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx); CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, -1, 0, -paintDeviceHeight); CGAffineTransformConcat(cgMatrix, oldTextMatrix); if (synthesisFlags & QFontEngine::SynthesizedItalic) cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, -tanf(14 * acosf(0) / 90), 1, 0, 0)); cgMatrix = CGAffineTransformConcat(cgMatrix, multiEngine->transform); CGContextSetTextMatrix(ctx, cgMatrix); CGContextSetTextDrawingMode(ctx, kCGTextFill); QVarLengthArray<CGSize> advances(glyphs.size()); QVarLengthArray<CGGlyph> cgGlyphs(glyphs.size()); for (int i = 0; i < glyphs.size() - 1; ++i) { advances[i].width = (positions[i + 1].x - positions[i].x).toReal(); advances[i].height = (positions[i + 1].y - positions[i].y).toReal(); cgGlyphs[i] = glyphs[i]; } advances[glyphs.size() - 1].width = 0; advances[glyphs.size() - 1].height = 0; cgGlyphs[glyphs.size() - 1] = glyphs[glyphs.size() - 1]; CGContextSetFont(ctx, cgFont); CGContextSetTextPosition(ctx, positions[0].x.toReal(), positions[0].y.toReal()); CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size()); if (synthesisFlags & QFontEngine::SynthesizedBold) { CGContextSetTextPosition(ctx, positions[0].x.toReal() + 0.5 * lineThickness().toReal(), positions[0].y.toReal()); CGContextShowGlyphsWithAdvances(ctx, cgGlyphs.data(), advances.data(), glyphs.size()); } CGContextSetTextMatrix(ctx, oldTextMatrix); }
void wkSetPatternPhaseInUserSpace(CGContextRef context, CGPoint phasePoint) { CGAffineTransform userToBase = CGAffineTransformConcat(CGContextGetCTM(context), CGAffineTransformInvert(CGContextGetBaseCTM(context))); CGPoint phase = CGPointApplyAffineTransform(phasePoint, userToBase); CGContextSetPatternPhase(context, CGSizeMake(phase.x, phase.y)); }
FX_BOOL CQuartz2D::drawGraphicsString(void* graphics, void* font, FX_FLOAT fontSize, FX_WORD* glyphIndices, CGPoint* glyphPositions, FX_INT32 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); } FX_INT32 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; }
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; }
void Font::drawGlyphs(GraphicsContext* graphicsContext, const FontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { CGContextRef cgContext = graphicsContext->platformContext(); uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext); const FontPlatformData& platformData = font->platformData(); //NSFont* drawFont; //if ([gContext isDrawingToScreen]) { // drawFont = [platformData.font screenFont]; // if (drawFont != platformData.font) // // We are getting this in too many places (3406411); use ERROR so it only prints on debug versions for now. (We should debug this also, eventually). // LOG_ERROR("Attempting to set non-screen font (%@) when drawing to screen. Using screen font anyway, may result in incorrect metrics.", // [[[platformData.font fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]); //} else { // drawFont = [platformData.font printerFont]; // if (drawFont != platformData.font) // NSLog(@"Attempting to set non-printer font (%@) when printing. Using printer font anyway, may result in incorrect metrics.", // [[[platformData.font fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]); //} CGContextSetFont(cgContext, platformData.cgFont()); CGAffineTransform matrix = CGAffineTransformIdentity; matrix.b = -matrix.b; matrix.d = -matrix.d; if (platformData.syntheticOblique()) { static float skew = -tanf(syntheticObliqueAngle * acosf(0) / 90); matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, skew, 1, 0, 0)); } // Uniscribe gives us offsets to help refine the positioning of combining glyphs. FloatSize translation = glyphBuffer.offsetAt(from); if (translation.width() || translation.height()) CGAffineTransformTranslate(matrix, translation.width(), translation.height()); CGContextSetTextMatrix(cgContext, matrix); //wkSetCGFontRenderingMode(cgContext, drawFont); CGContextSetFontSize(cgContext, platformData.size()); CGContextSetTextPosition(cgContext, point.x(), point.y()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->m_syntheticBoldOffset) { CGContextSetTextPosition(cgContext, point.x() + font->m_syntheticBoldOffset, point.y()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } wkRestoreFontSmoothingStyle(cgContext, oldFontSmoothingStyle); }
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); } } }
JNIEXPORT void JNICALL OS_NATIVE(CGAffineTransformConcat) (JNIEnv *env, jclass that, jfloatArray arg0, jfloatArray arg1, jfloatArray arg2) { jfloat *lparg0=NULL; jfloat *lparg1=NULL; jfloat *lparg2=NULL; OS_NATIVE_ENTER(env, that, CGAffineTransformConcat_FUNC); if (arg0) if ((lparg0 = (*env)->GetFloatArrayElements(env, arg0, NULL)) == NULL) goto fail; if (arg1) if ((lparg1 = (*env)->GetFloatArrayElements(env, arg1, NULL)) == NULL) goto fail; if (arg2) if ((lparg2 = (*env)->GetFloatArrayElements(env, arg2, NULL)) == NULL) goto fail; *(CGAffineTransform *)lparg2 = CGAffineTransformConcat(*(CGAffineTransform *)lparg0, *(CGAffineTransform *)lparg1); fail: if (arg2 && lparg2) (*env)->ReleaseFloatArrayElements(env, arg2, lparg2, 0); if (arg1 && lparg1) (*env)->ReleaseFloatArrayElements(env, arg1, lparg1, JNI_ABORT); if (arg0 && lparg0) (*env)->ReleaseFloatArrayElements(env, arg0, lparg0, JNI_ABORT); OS_NATIVE_EXIT(env, that, CGAffineTransformConcat_FUNC); }
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 cairo_int_status_t _cairo_quartz_init_glyph_path (cairo_quartz_scaled_font_t *font, cairo_scaled_glyph_t *scaled_glyph) { cairo_quartz_font_face_t *font_face = _cairo_quartz_scaled_to_face(font); CGGlyph glyph = _cairo_quartz_scaled_glyph_index (scaled_glyph); CGAffineTransform textMatrix; CGPathRef glyphPath; cairo_path_fixed_t *path; if (glyph == INVALID_GLYPH) { _cairo_scaled_glyph_set_path (scaled_glyph, &font->base, _cairo_path_fixed_create()); return CAIRO_STATUS_SUCCESS; } textMatrix = CGAffineTransformMake (font->base.scale.xx, -font->base.scale.yx, -font->base.scale.xy, font->base.scale.yy, font->base.scale.x0, font->base.scale.y0); textMatrix = CGAffineTransformConcat (textMatrix, CGAffineTransformMake (1.0, 0.0, 0.0, -1.0, 0.0, 0.0)); glyphPath = CGFontGetGlyphPathPtr (font_face->cgFont, &textMatrix, 0, glyph); if (!glyphPath) return CAIRO_INT_STATUS_UNSUPPORTED; path = _cairo_path_fixed_create (); if (!path) { CGPathRelease (glyphPath); return _cairo_error(CAIRO_STATUS_NO_MEMORY); } CGPathApply (glyphPath, path, _cairo_quartz_path_apply_func); CGPathRelease (glyphPath); _cairo_scaled_glyph_set_path (scaled_glyph, &font->base, path); return CAIRO_STATUS_SUCCESS; }
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; }
void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect) { if (!nativeImageForCurrentFrame()) return; ASSERT(patternTransform.isInvertible()); if (!patternTransform.isInvertible()) // Avoid a hang under CGContextDrawTiledImage on release builds. return; CGContextRef context = ctxt->platformContext(); ctxt->save(); CGContextClipToRect(context, destRect); ctxt->setCompositeOperation(op); CGContextTranslateCTM(context, destRect.x(), destRect.y() + destRect.height()); CGContextScaleCTM(context, 1, -1); // Compute the scaled tile size. float scaledTileHeight = tileRect.height() * narrowPrecisionToFloat(patternTransform.d()); // We have to adjust the phase to deal with the fact we're in Cartesian space now (with the bottom left corner of destRect being // the origin). float adjustedX = phase.x() - destRect.x() + tileRect.x() * narrowPrecisionToFloat(patternTransform.a()); // We translated the context so that destRect.x() is the origin, so subtract it out. float adjustedY = destRect.height() - (phase.y() - destRect.y() + tileRect.y() * narrowPrecisionToFloat(patternTransform.d()) + scaledTileHeight); CGImageRef tileImage = nativeImageForCurrentFrame(); float h = CGImageGetHeight(tileImage); RetainPtr<CGImageRef> subImage; if (tileRect.size() == size()) subImage = tileImage; else { // Copying a sub-image out of a partially-decoded image stops the decoding of the original image. It should never happen // because sub-images are only used for border-image, which only renders when the image is fully decoded. ASSERT(h == height()); subImage.adoptCF(CGImageCreateWithImageInRect(tileImage, tileRect)); } // Adjust the color space. subImage = imageWithColorSpace(subImage.get(), styleColorSpace); #ifndef BUILDING_ON_TIGER // Leopard has an optimized call for the tiling of image patterns, but we can only use it if the image has been decoded enough that // its buffer is the same size as the overall image. Because a partially decoded CGImageRef with a smaller width or height than the // overall image buffer needs to tile with "gaps", we can't use the optimized tiling call in that case. // FIXME: Could create WebKitSystemInterface SPI for CGCreatePatternWithImage2 and probably make Tiger tile faster as well. // FIXME: We cannot use CGContextDrawTiledImage with scaled tiles on Leopard, because it suffers from rounding errors. Snow Leopard is ok. float scaledTileWidth = tileRect.width() * narrowPrecisionToFloat(patternTransform.a()); float w = CGImageGetWidth(tileImage); #ifdef BUILDING_ON_LEOPARD if (w == size().width() && h == size().height() && scaledTileWidth == tileRect.width() && scaledTileHeight == tileRect.height()) #else if (w == size().width() && h == size().height()) #endif CGContextDrawTiledImage(context, FloatRect(adjustedX, adjustedY, scaledTileWidth, scaledTileHeight), subImage.get()); else { #endif // On Leopard, this code now only runs for partially decoded images whose buffers do not yet match the overall size of the image. // On Tiger this code runs all the time. This code is suboptimal because the pattern does not reference the image directly, and the // pattern is destroyed before exiting the function. This means any decoding the pattern does doesn't end up cached anywhere, so we // redecode every time we paint. static const CGPatternCallbacks patternCallbacks = { 0, drawPatternCallback, NULL }; CGAffineTransform matrix = CGAffineTransformMake(narrowPrecisionToCGFloat(patternTransform.a()), 0, 0, narrowPrecisionToCGFloat(patternTransform.d()), adjustedX, adjustedY); matrix = CGAffineTransformConcat(matrix, CGContextGetCTM(context)); // The top of a partially-decoded image is drawn at the bottom of the tile. Map it to the top. matrix = CGAffineTransformTranslate(matrix, 0, size().height() - h); RetainPtr<CGPatternRef> pattern(AdoptCF, CGPatternCreate(subImage.get(), CGRectMake(0, 0, tileRect.width(), tileRect.height()), matrix, tileRect.width(), tileRect.height(), kCGPatternTilingConstantSpacing, true, &patternCallbacks)); if (!pattern) { ctxt->restore(); return; } RetainPtr<CGColorSpaceRef> patternSpace(AdoptCF, CGColorSpaceCreatePattern(0)); CGFloat alpha = 1; RetainPtr<CGColorRef> color(AdoptCF, CGColorCreateWithPattern(patternSpace.get(), pattern.get(), &alpha)); CGContextSetFillColorSpace(context, patternSpace.get()); // FIXME: Really want a public API for this. It is just CGContextSetBaseCTM(context, CGAffineTransformIdentiy). wkSetPatternBaseCTM(context, CGAffineTransformIdentity); CGContextSetPatternPhase(context, CGSizeZero); CGContextSetFillColorWithColor(context, color.get()); CGContextFillRect(context, CGContextGetClipBoundingBox(context)); #ifndef BUILDING_ON_TIGER } #endif ctxt->restore(); if (imageObserver()) imageObserver()->didDraw(this); }
void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { CGContextRef cgContext = graphicsContext->platformContext(); bool shouldUseFontSmoothing = WebCoreShouldUseFontSmoothing(); switch(fontDescription().fontSmoothing()) { case Antialiased: { graphicsContext->setShouldAntialias(true); shouldUseFontSmoothing = false; break; } case SubpixelAntialiased: { graphicsContext->setShouldAntialias(true); shouldUseFontSmoothing = true; break; } case NoSmoothing: { graphicsContext->setShouldAntialias(false); shouldUseFontSmoothing = false; break; } case AutoSmoothing: { // For the AutoSmooth case, don't do anything! Keep the default settings. break; } default: ASSERT_NOT_REACHED(); } if (font->platformData().useGDI() && !shouldUseFontSmoothing) { drawGDIGlyphs(graphicsContext, font, glyphBuffer, from, numGlyphs, point); return; } uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext, shouldUseFontSmoothing); const FontPlatformData& platformData = font->platformData(); CGContextSetFont(cgContext, platformData.cgFont()); CGAffineTransform matrix = CGAffineTransformIdentity; matrix.b = -matrix.b; matrix.d = -matrix.d; if (platformData.syntheticOblique()) { static float skew = -tanf(syntheticObliqueAngle * piFloat / 180.0f); matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, skew, 1, 0, 0)); } CGContextSetTextMatrix(cgContext, matrix); // Uniscribe gives us offsets to help refine the positioning of combining glyphs. FloatSize translation = glyphBuffer.offsetAt(from); CGContextSetFontSize(cgContext, platformData.size()); wkSetCGContextFontRenderingStyle(cgContext, font->isSystemFont(), false, font->platformData().useGDI()); FloatSize shadowOffset; float shadowBlur; Color shadowColor; ColorSpace shadowColorSpace; graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace); bool hasSimpleShadow = graphicsContext->textDrawingMode() == TextModeFill && shadowColor.isValid() && !shadowBlur && (!graphicsContext->shadowsIgnoreTransforms() || graphicsContext->getCTM().isIdentityOrTranslationOrFlipped()); if (hasSimpleShadow) { // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing. graphicsContext->clearShadow(); Color fillColor = graphicsContext->fillColor(); Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); graphicsContext->setFillColor(shadowFillColor, ColorSpaceDeviceRGB); float shadowTextX = point.x() + translation.width() + shadowOffset.width(); // If shadows are ignoring transforms, then we haven't applied the Y coordinate flip yet, so down is negative. float shadowTextY = point.y() + translation.height() + shadowOffset.height() * (graphicsContext->shadowsIgnoreTransforms() ? -1 : 1); CGContextSetTextPosition(cgContext, shadowTextX, shadowTextY); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->syntheticBoldOffset()) { CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowOffset.width() + font->syntheticBoldOffset(), point.y() + translation.height() + shadowOffset.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } graphicsContext->setFillColor(fillColor, ColorSpaceDeviceRGB); } CGContextSetTextPosition(cgContext, point.x() + translation.width(), point.y() + translation.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->syntheticBoldOffset()) { CGContextSetTextPosition(cgContext, point.x() + translation.width() + font->syntheticBoldOffset(), point.y() + translation.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } if (hasSimpleShadow) graphicsContext->setShadow(shadowOffset, shadowBlur, shadowColor, ColorSpaceDeviceRGB); wkRestoreFontSmoothingStyle(cgContext, oldFontSmoothingStyle); }
static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) { Color fillColor = graphicsContext->fillColor(); bool drawIntoBitmap = false; TextDrawingModeFlags drawingMode = graphicsContext->textDrawingMode(); if (drawingMode == TextModeFill) { if (!fillColor.alpha()) return; drawIntoBitmap = fillColor.alpha() != 255 || graphicsContext->inTransparencyLayer(); if (!drawIntoBitmap) { FloatSize offset; float blur; Color color; ColorSpace shadowColorSpace; graphicsContext->getShadow(offset, blur, color, shadowColorSpace); drawIntoBitmap = offset.width() || offset.height() || blur; } } // We have to convert CG's two-dimensional floating point advances to just horizontal integer advances. Vector<int, 2048> gdiAdvances; int totalWidth = 0; for (int i = 0; i < numGlyphs; i++) { gdiAdvances.append(lroundf(glyphBuffer.advanceAt(from + i))); totalWidth += gdiAdvances[i]; } HDC hdc = 0; OwnPtr<GraphicsContext::WindowsBitmap> bitmap; IntRect textRect; if (!drawIntoBitmap) hdc = graphicsContext->getWindowsContext(textRect, true, false); if (!hdc) { drawIntoBitmap = true; // We put slop into this rect, since glyphs can overflow the ascent/descent bounds and the left/right edges. // FIXME: Can get glyphs' optical bounds (even from CG) to get this right. const FontMetrics& fontMetrics = font->fontMetrics(); int lineGap = fontMetrics.lineGap(); textRect = IntRect(point.x() - (fontMetrics.ascent() + fontMetrics.descent()) / 2, point.y() - fontMetrics.ascent() - lineGap, totalWidth + fontMetrics.ascent() + fontMetrics.descent(), fontMetrics.lineSpacing()); bitmap = graphicsContext->createWindowsBitmap(textRect.size()); memset(bitmap->buffer(), 255, bitmap->bufferLength()); hdc = bitmap->hdc(); XFORM xform; xform.eM11 = 1.0f; xform.eM12 = 0.0f; xform.eM21 = 0.0f; xform.eM22 = 1.0f; xform.eDx = -textRect.x(); xform.eDy = -textRect.y(); SetWorldTransform(hdc, &xform); } SelectObject(hdc, font->platformData().hfont()); // Set the correct color. if (drawIntoBitmap) SetTextColor(hdc, RGB(0, 0, 0)); else SetTextColor(hdc, RGB(fillColor.red(), fillColor.green(), fillColor.blue())); SetBkMode(hdc, TRANSPARENT); SetTextAlign(hdc, TA_LEFT | TA_BASELINE); // Uniscribe gives us offsets to help refine the positioning of combining glyphs. FloatSize translation = glyphBuffer.offsetAt(from); if (translation.width() || translation.height()) { XFORM xform; xform.eM11 = 1.0; xform.eM12 = 0; xform.eM21 = 0; xform.eM22 = 1.0; xform.eDx = translation.width(); xform.eDy = translation.height(); ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); } if (drawingMode == TextModeFill) { XFORM xform; xform.eM11 = 1.0; xform.eM12 = 0; xform.eM21 = font->platformData().syntheticOblique() ? -tanf(syntheticObliqueAngle * piFloat / 180.0f) : 0; xform.eM22 = 1.0; xform.eDx = point.x(); xform.eDy = point.y(); ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, reinterpret_cast<const WCHAR*>(glyphBuffer.glyphs(from)), numGlyphs, gdiAdvances.data()); if (font->syntheticBoldOffset()) { xform.eM21 = 0; xform.eDx = font->syntheticBoldOffset(); xform.eDy = 0; ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, reinterpret_cast<const WCHAR*>(glyphBuffer.glyphs(from)), numGlyphs, gdiAdvances.data()); } } else { XFORM xform; GetWorldTransform(hdc, &xform); AffineTransform hdcTransform(xform.eM11, xform.eM21, xform.eM12, xform.eM22, xform.eDx, xform.eDy); CGAffineTransform initialGlyphTransform = hdcTransform.isInvertible() ? hdcTransform.inverse() : CGAffineTransformIdentity; if (font->platformData().syntheticOblique()) initialGlyphTransform = CGAffineTransformConcat(initialGlyphTransform, CGAffineTransformMake(1, 0, tanf(syntheticObliqueAngle * piFloat / 180.0f), 1, 0, 0)); initialGlyphTransform.tx = 0; initialGlyphTransform.ty = 0; CGContextRef cgContext = graphicsContext->platformContext(); CGContextSaveGState(cgContext); BOOL fontSmoothingEnabled = false; SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothingEnabled, 0); CGContextSetShouldAntialias(cgContext, fontSmoothingEnabled); CGContextScaleCTM(cgContext, 1.0, -1.0); CGContextTranslateCTM(cgContext, point.x() + glyphBuffer.offsetAt(from).width(), -(point.y() + glyphBuffer.offsetAt(from).height())); for (unsigned i = 0; i < numGlyphs; ++i) { RetainPtr<CGPathRef> glyphPath(AdoptCF, createPathForGlyph(hdc, glyphBuffer.glyphAt(from + i))); CGContextSaveGState(cgContext); CGContextConcatCTM(cgContext, initialGlyphTransform); if (drawingMode & TextModeFill) { CGContextAddPath(cgContext, glyphPath.get()); CGContextFillPath(cgContext); if (font->syntheticBoldOffset()) { CGContextTranslateCTM(cgContext, font->syntheticBoldOffset(), 0); CGContextAddPath(cgContext, glyphPath.get()); CGContextFillPath(cgContext); CGContextTranslateCTM(cgContext, -font->syntheticBoldOffset(), 0); } } if (drawingMode & TextModeStroke) { CGContextAddPath(cgContext, glyphPath.get()); CGContextStrokePath(cgContext); if (font->syntheticBoldOffset()) { CGContextTranslateCTM(cgContext, font->syntheticBoldOffset(), 0); CGContextAddPath(cgContext, glyphPath.get()); CGContextStrokePath(cgContext); CGContextTranslateCTM(cgContext, -font->syntheticBoldOffset(), 0); } } CGContextRestoreGState(cgContext); CGContextTranslateCTM(cgContext, gdiAdvances[i], 0); } CGContextRestoreGState(cgContext); } if (drawIntoBitmap) { UInt8* buffer = bitmap->buffer(); unsigned bufferLength = bitmap->bufferLength(); for (unsigned i = 0; i < bufferLength; i += 4) { // Use green, which is always in the middle. UInt8 alpha = (255 - buffer[i + 1]) * fillColor.alpha() / 255; buffer[i] = fillColor.blue(); buffer[i + 1] = fillColor.green(); buffer[i + 2] = fillColor.red(); buffer[i + 3] = alpha; } graphicsContext->drawWindowsBitmap(bitmap.get(), textRect.location()); } else graphicsContext->releaseWindowsContext(hdc, textRect, true, false); }
Transform2D Transform2D::concat(const Transform2D &other) const { return CGAffineTransformConcat(*this, other); }
void CCSprite::updateTransform(void) { assert(m_bUsesBatchNode); // optimization. Quick return if not dirty if (! m_bDirty) { return; } CGAffineTransform matrix; // Optimization: if it is not visible, then do nothing if (! m_bIsVisible) { m_sQuad.br.vertices = m_sQuad.tl.vertices = m_sQuad.tr.vertices = m_sQuad.bl.vertices = vertex3(0,0,0); m_pobTextureAtlas->updateQuad(&m_sQuad, m_uAtlasIndex); m_bDirty = m_bRecursiveDirty = false; return; } // Optimization: If parent is batchnode, or parent is nil // build Affine transform manually if (! m_pParent || m_pParent == m_pobBatchNode) { float radians = -CC_DEGREES_TO_RADIANS(m_fRotation); float c = cosf(radians); float s = sinf(radians); matrix = CGAffineTransformMake(c * m_fScaleX, s * m_fScaleX, -s * m_fScaleY, c * m_fScaleY, m_tPositionInPixels.x, m_tPositionInPixels.y); matrix = CGAffineTransformTranslate(matrix, -m_tAnchorPointInPixels.x, -m_tAnchorPointInPixels.y); } else // parent_ != batchNode_ { // else do affine transformation according to the HonorParentTransform matrix = CGAffineTransformIdentity; ccHonorParentTransform prevHonor = CC_HONOR_PARENT_TRANSFORM_ALL; for (CCNode *p = this; p && p != m_pobBatchNode; p = p->getParent()) { // Might happen. Issue #1053 // how to implement, we can not use dynamic // NSAssert( [p isKindOfClass:[CCSprite class]], @"CCSprite should be a CCSprite subclass. Probably you initialized an sprite with a batchnode, but you didn't add it to the batch node." ); struct transformValues_ tv; ((CCSprite*)p)->getTransformValues(&tv); // If any of the parents are not visible, then don't draw this node if (! tv.visible) { m_sQuad.br.vertices = m_sQuad.tl.vertices = m_sQuad.tr.vertices = m_sQuad.bl.vertices = vertex3(0,0,0); m_pobTextureAtlas->updateQuad(&m_sQuad, m_uAtlasIndex); m_bDirty = m_bRecursiveDirty = false; return; } CGAffineTransform newMatrix = CGAffineTransformIdentity; // 2nd: Translate, Rotate, Scale if( prevHonor & CC_HONOR_PARENT_TRANSFORM_TRANSLATE ) { newMatrix = CGAffineTransformTranslate(newMatrix, tv.pos.x, tv.pos.y); } if( prevHonor & CC_HONOR_PARENT_TRANSFORM_ROTATE ) { newMatrix = CGAffineTransformRotate(newMatrix, -CC_DEGREES_TO_RADIANS(tv.rotation)); } if( prevHonor & CC_HONOR_PARENT_TRANSFORM_SCALE ) { newMatrix = CGAffineTransformScale(newMatrix, tv.scale.x, tv.scale.y); } // 3rd: Translate anchor point newMatrix = CGAffineTransformTranslate(newMatrix, -tv.ap.x, -tv.ap.y); // 4th: Matrix multiplication matrix = CGAffineTransformConcat( matrix, newMatrix); prevHonor = ((CCSprite*)p)->getHornorParentTransform(); } } // // calculate the Quad based on the Affine Matrix // CGSize size = m_obRectInPixels.size; float x1 = m_obOffsetPositionInPixels.x; float y1 = m_obOffsetPositionInPixels.y; float x2 = x1 + size.width; float y2 = y1 + size.height; float x = matrix.tx; float y = matrix.ty; float cr = matrix.a; float sr = matrix.b; float cr2 = matrix.d; float sr2 = -matrix.c; float ax = x1 * cr - y1 * sr2 + x; float ay = x1 * sr + y1 * cr2 + y; float bx = x2 * cr - y1 * sr2 + x; float by = x2 * sr + y1 * cr2 + y; float cx = x2 * cr - y2 * sr2 + x; float cy = x2 * sr + y2 * cr2 + y; float dx = x1 * cr - y2 * sr2 + x; float dy = x1 * sr + y2 * cr2 + y; m_sQuad.bl.vertices = vertex3((float)RENDER_IN_SUBPIXEL(ax), (float)RENDER_IN_SUBPIXEL(ay), m_fVertexZ); m_sQuad.br.vertices = vertex3((float)RENDER_IN_SUBPIXEL(bx), (float)RENDER_IN_SUBPIXEL(by), m_fVertexZ); m_sQuad.tl.vertices = vertex3((float)RENDER_IN_SUBPIXEL(dx), (float)RENDER_IN_SUBPIXEL(dy), m_fVertexZ); m_sQuad.tr.vertices = vertex3((float)RENDER_IN_SUBPIXEL(cx), (float)RENDER_IN_SUBPIXEL(cy), m_fVertexZ); m_pobTextureAtlas->updateQuad(&m_sQuad, m_uAtlasIndex); m_bDirty = m_bRecursiveDirty = false; }
void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { CGContextRef cgContext = graphicsContext->platformContext(); bool shouldUseFontSmoothing = WebCoreShouldUseFontSmoothing(); if (font->platformData().useGDI()) { static bool canUsePlatformNativeGlyphs = wkCanUsePlatformNativeGlyphs(); if (!canUsePlatformNativeGlyphs || !shouldUseFontSmoothing || (graphicsContext->textDrawingMode() & cTextStroke)) { drawGDIGlyphs(graphicsContext, font, glyphBuffer, from, numGlyphs, point); return; } } uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext, shouldUseFontSmoothing); const FontPlatformData& platformData = font->platformData(); CGContextSetFont(cgContext, platformData.cgFont()); CGAffineTransform matrix = CGAffineTransformIdentity; matrix.b = -matrix.b; matrix.d = -matrix.d; if (platformData.syntheticOblique()) { static float skew = -tanf(syntheticObliqueAngle * piFloat / 180.0f); matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, skew, 1, 0, 0)); } CGContextSetTextMatrix(cgContext, matrix); // Uniscribe gives us offsets to help refine the positioning of combining glyphs. FloatSize translation = glyphBuffer.offsetAt(from); CGContextSetFontSize(cgContext, platformData.size()); wkSetCGContextFontRenderingStyle(cgContext, font->isSystemFont(), false, font->platformData().useGDI()); IntSize shadowSize; int shadowBlur; Color shadowColor; graphicsContext->getShadow(shadowSize, shadowBlur, shadowColor); bool hasSimpleShadow = graphicsContext->textDrawingMode() == cTextFill && shadowColor.isValid() && !shadowBlur; if (hasSimpleShadow) { // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing. graphicsContext->clearShadow(); Color fillColor = graphicsContext->fillColor(); Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); graphicsContext->setFillColor(shadowFillColor); CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowSize.width(), point.y() + translation.height() + shadowSize.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->m_syntheticBoldOffset) { CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowSize.width() + font->m_syntheticBoldOffset, point.y() + translation.height() + shadowSize.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } graphicsContext->setFillColor(fillColor); } CGContextSetTextPosition(cgContext, point.x() + translation.width(), point.y() + translation.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->m_syntheticBoldOffset) { CGContextSetTextPosition(cgContext, point.x() + translation.width() + font->m_syntheticBoldOffset, point.y() + translation.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } if (hasSimpleShadow) graphicsContext->setShadow(shadowSize, shadowBlur, shadowColor); wkRestoreFontSmoothingStyle(cgContext, oldFontSmoothingStyle); }
CGAffineTransform CGAffineTransformRotate(CGAffineTransform T,CGFloat angle) { return CGAffineTransformConcat(T,CGAffineTransformMakeRotation(angle)); }
CGAffineTransform CGAffineTransformScale(CGAffineTransform T,CGFloat sx,CGFloat sy) { return CGAffineTransformConcat(T,CGAffineTransformMakeScale(sx,sy)); }
QImage QFontEngineMac::alphaMapForGlyph(glyph_t glyph) { const glyph_metrics_t br = boundingBox(glyph); QImage im(qRound(br.width)+2, qRound(br.height)+2, QImage::Format_RGB32); im.fill(0); CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) uint cgflags = kCGImageAlphaNoneSkipFirst; #ifdef kCGBitmapByteOrder32Host //only needed because CGImage.h added symbols in the minor version if(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) cgflags |= kCGBitmapByteOrder32Host; #endif #else CGImageAlphaInfo cgflags = kCGImageAlphaNoneSkipFirst; #endif CGContextRef ctx = CGBitmapContextCreate(im.bits(), im.width(), im.height(), 8, im.bytesPerLine(), colorspace, cgflags); CGColorSpaceRelease(colorspace); CGContextSetFontSize(ctx, fontDef.pixelSize); CGContextSetShouldAntialias(ctx, fontDef.pointSize > qt_antialiasing_threshold && !(fontDef.styleStrategy & QFont::NoAntialias)); CGAffineTransform oldTextMatrix = CGContextGetTextMatrix(ctx); CGAffineTransform cgMatrix = CGAffineTransformMake(1, 0, 0, 1, 0, 0); CGAffineTransformConcat(cgMatrix, oldTextMatrix); if (synthesisFlags & QFontEngine::SynthesizedItalic) cgMatrix = CGAffineTransformConcat(cgMatrix, CGAffineTransformMake(1, 0, tanf(14 * acosf(0) / 90), 1, 0, 0)); cgMatrix = CGAffineTransformConcat(cgMatrix, multiEngine->transform); CGContextSetTextMatrix(ctx, cgMatrix); CGContextSetRGBFillColor(ctx, 1, 1, 1, 1); CGContextSetTextDrawingMode(ctx, kCGTextFill); CGContextSetFont(ctx, cgFont); qreal pos_x = -br.x.toReal()+1, pos_y = im.height()+br.y.toReal(); CGContextSetTextPosition(ctx, pos_x, pos_y); CGSize advance; advance.width = 0; advance.height = 0; CGGlyph cgGlyph = glyph; CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1); if (synthesisFlags & QFontEngine::SynthesizedBold) { CGContextSetTextPosition(ctx, pos_x + 0.5 * lineThickness().toReal(), pos_y); CGContextShowGlyphsWithAdvances(ctx, &cgGlyph, &advance, 1); } CGContextRelease(ctx); QImage indexed(im.width(), im.height(), QImage::Format_Indexed8); QVector<QRgb> colors(256); for (int i=0; i<256; ++i) colors[i] = qRgba(0, 0, 0, i); indexed.setColorTable(colors); for (int y=0; y<im.height(); ++y) { uint *src = (uint*) im.scanLine(y); uchar *dst = indexed.scanLine(y); for (int x=0; x<im.width(); ++x) { *dst = qGray(*src); ++dst; ++src; } } return indexed; }
AffineTransform AffineTransform::operator* (const AffineTransform &m2) { return CGAffineTransformConcat(m_transform, CGAffineTransform(m2)); }
AffineTransform &AffineTransform::shear(double sx, double sy) { CGAffineTransform shear = CGAffineTransformMake(1, sy, sx, 1, 0, 0); m_transform = CGAffineTransformConcat(shear,m_transform); return *this; }
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)); FX_INT32 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; }
AffineTransform &AffineTransform::operator*= (const AffineTransform &m2) { m_transform = CGAffineTransformConcat(m_transform, CGAffineTransform(m2)); return *this; }
CGAffineTransform CGAffineTransformTranslate(CGAffineTransform T,CGFloat tx,CGFloat ty) { return CGAffineTransformConcat(T,CGAffineTransformMakeTranslation(tx,ty)); }
void EJCanvasContext::transform(float m11, float m12, float m21, float m22, float dx, float dy) { CGAffineTransform t = CGAffineTransformMake( m11, m12, m21, m22, dx, dy ); state->transform = CGAffineTransformConcat( t, state->transform ); path->transform = state->transform; }
void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect, BlendMode blendMode) { if (!nativeImageForCurrentFrame()) return; if (!patternTransform.isInvertible()) return; CGContextRef context = ctxt->platformContext(); GraphicsContextStateSaver stateSaver(*ctxt); CGContextClipToRect(context, destRect); ctxt->setCompositeOperation(op, blendMode); CGContextTranslateCTM(context, destRect.x(), destRect.y() + destRect.height()); CGContextScaleCTM(context, 1, -1); // Compute the scaled tile size. float scaledTileHeight = tileRect.height() * narrowPrecisionToFloat(patternTransform.d()); // We have to adjust the phase to deal with the fact we're in Cartesian space now (with the bottom left corner of destRect being // the origin). float adjustedX = phase.x() - destRect.x() + tileRect.x() * narrowPrecisionToFloat(patternTransform.a()); // We translated the context so that destRect.x() is the origin, so subtract it out. float adjustedY = destRect.height() - (phase.y() - destRect.y() + tileRect.y() * narrowPrecisionToFloat(patternTransform.d()) + scaledTileHeight); CGImageRef tileImage = nativeImageForCurrentFrame(); float h = CGImageGetHeight(tileImage); RetainPtr<CGImageRef> subImage; if (tileRect.size() == size()) subImage = tileImage; else { // Copying a sub-image out of a partially-decoded image stops the decoding of the original image. It should never happen // because sub-images are only used for border-image, which only renders when the image is fully decoded. ASSERT(h == height()); subImage = adoptCF(CGImageCreateWithImageInRect(tileImage, tileRect)); } // Adjust the color space. subImage = Image::imageWithColorSpace(subImage.get(), styleColorSpace); // Leopard has an optimized call for the tiling of image patterns, but we can only use it if the image has been decoded enough that // its buffer is the same size as the overall image. Because a partially decoded CGImageRef with a smaller width or height than the // overall image buffer needs to tile with "gaps", we can't use the optimized tiling call in that case. // FIXME: We cannot use CGContextDrawTiledImage with scaled tiles on Leopard, because it suffers from rounding errors. Snow Leopard is ok. float scaledTileWidth = tileRect.width() * narrowPrecisionToFloat(patternTransform.a()); float w = CGImageGetWidth(tileImage); if (w == size().width() && h == size().height() && !spaceSize().width() && !spaceSize().height()) CGContextDrawTiledImage(context, FloatRect(adjustedX, adjustedY, scaledTileWidth, scaledTileHeight), subImage.get()); else { // On Leopard and newer, this code now only runs for partially decoded images whose buffers do not yet match the overall size of the image. static const CGPatternCallbacks patternCallbacks = { 0, drawPatternCallback, patternReleaseCallback }; CGAffineTransform matrix = CGAffineTransformMake(narrowPrecisionToCGFloat(patternTransform.a()), 0, 0, narrowPrecisionToCGFloat(patternTransform.d()), adjustedX, adjustedY); matrix = CGAffineTransformConcat(matrix, CGContextGetCTM(context)); // The top of a partially-decoded image is drawn at the bottom of the tile. Map it to the top. matrix = CGAffineTransformTranslate(matrix, 0, size().height() - h); #if PLATFORM(IOS) matrix = CGAffineTransformScale(matrix, 1, -1); matrix = CGAffineTransformTranslate(matrix, 0, -h); #endif CGImageRef platformImage = CGImageRetain(subImage.get()); RetainPtr<CGPatternRef> pattern = adoptCF(CGPatternCreate(platformImage, CGRectMake(0, 0, tileRect.width(), tileRect.height()), matrix, tileRect.width() + spaceSize().width() * (1 / narrowPrecisionToFloat(patternTransform.a())), tileRect.height() + spaceSize().height() * (1 / narrowPrecisionToFloat(patternTransform.d())), kCGPatternTilingConstantSpacing, true, &patternCallbacks)); if (!pattern) return; RetainPtr<CGColorSpaceRef> patternSpace = adoptCF(CGColorSpaceCreatePattern(0)); CGFloat alpha = 1; RetainPtr<CGColorRef> color = adoptCF(CGColorCreateWithPattern(patternSpace.get(), pattern.get(), &alpha)); CGContextSetFillColorSpace(context, patternSpace.get()); // FIXME: Really want a public API for this. It is just CGContextSetBaseCTM(context, CGAffineTransformIdentiy). wkSetBaseCTM(context, CGAffineTransformIdentity); CGContextSetPatternPhase(context, CGSizeZero); CGContextSetFillColorWithColor(context, color.get()); CGContextFillRect(context, CGContextGetClipBoundingBox(context)); } stateSaver.restore(); if (imageObserver()) imageObserver()->didDraw(this); }