static inline cairo_status_t _cairo_matrix_to_unit_quartz_matrix (const cairo_matrix_t *m, CGAffineTransform *txout, double *xout, double *yout) { CGAffineTransform transform; double xscale, yscale; cairo_status_t status; status = _cairo_matrix_compute_basis_scale_factors (m, &xscale, &yscale, 1); if (status) return status; transform = CGAffineTransformMake (m->xx, - m->yx, - m->xy, m->yy, 0.0f, 0.0f); if (xout) *xout = xscale; if (yout) *yout = yscale; if (xscale) xscale = 1.0 / xscale; if (yscale) yscale = 1.0 / yscale; *txout = CGAffineTransformScale (transform, xscale, yscale); return CAIRO_STATUS_SUCCESS; }
void doRotatedEllipses(CGContextRef context) { int i, totreps = 144; float tint = 1.0, tintIncrement = 1.0/totreps; // Create a new transform consisting of a 45 degrees rotation. CGAffineTransform theTransform = CGAffineTransformMakeRotation(M_PI/4); // Apply a scale to the transform just created. theTransform = CGAffineTransformScale(theTransform, 1, 2); // Place the first ellipse at a good location. CGContextTranslateCTM(context, 100., 100.); for(i=0 ; i < totreps ; i++){ // Make a snapshot the coordinate system. CGContextSaveGState(context); // Set up the coordinate system for the rotated ellipse. CGContextConcatCTM(context, theTransform); CGContextBeginPath(context); CGContextAddArc(context, 0., 0., 45., 0., 2*M_PI, 0); // Set the fill color for this instance of the ellipse. CGContextSetRGBFillColor(context, tint, 0., 0., 1.0); CGContextDrawPath(context, kCGPathFill); // Restore the coordinate system to that of the snapshot. CGContextRestoreGState(context); // Compute the next tint color. tint -= tintIncrement; // Move over by 1 unit in x for the next ellipse. CGContextTranslateCTM(context, 1.0, 0.0); } }
void drawTextWithFeature(CGContextRef context, CTFontDescriptorRef fontDescriptor, CFStringRef feature, int value, CGPoint location) { CGFloat fontSize = 25; CGContextSetTextMatrix(context, CGAffineTransformScale(CGAffineTransformIdentity, 1, 1)); CGContextSetTextPosition(context, location.x, location.y); CFNumberRef featureValue = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &value); CFTypeRef featureDictionaryKeys[] = { kCTFontOpenTypeFeatureTag, kCTFontOpenTypeFeatureValue }; CFTypeRef featureDictionaryValues[] = { feature, featureValue }; CFDictionaryRef featureDictionary = CFDictionaryCreate(kCFAllocatorDefault, featureDictionaryKeys, featureDictionaryValues, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFRelease(featureValue); CFTypeRef featureSettingsValues[] = { featureDictionary }; CFArrayRef fontFeatureSettings = CFArrayCreate(kCFAllocatorDefault, featureSettingsValues, 1, &kCFTypeArrayCallBacks); CFRelease(featureDictionary); CFTypeRef fontDescriptorKeys[] = { kCTFontFeatureSettingsAttribute }; CFTypeRef fontDescriptorValues[] = { fontFeatureSettings }; CFDictionaryRef fontDescriptorAttributes = CFDictionaryCreate(kCFAllocatorDefault, fontDescriptorKeys, fontDescriptorValues, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFRelease(fontFeatureSettings); CTFontDescriptorRef modifiedFontDescriptor = CTFontDescriptorCreateCopyWithAttributes(fontDescriptor, fontDescriptorAttributes); CFRelease(fontDescriptorAttributes); CTFontRef font = CTFontCreateWithFontDescriptor(modifiedFontDescriptor, fontSize, nullptr); CFRelease(modifiedFontDescriptor); CFMutableStringRef string = CFStringCreateMutable(kCFAllocatorDefault, 0); CFStringAppend(string, feature); CFStringAppend(string, value ? CFSTR(" (on)") : CFSTR(" (off)")); CFStringAppend(string, CFSTR(": ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")); CGColorRef red = CGColorCreateGenericRGB(1, 0, 0, 1); CFTypeRef lineKeys[] = { kCTForegroundColorAttributeName }; CFTypeRef lineValues[] = { red }; CFDictionaryRef lineAttributes = CFDictionaryCreate(kCFAllocatorDefault, lineKeys, lineValues, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CGColorRelease(red); CFAttributedStringRef attributedString = CFAttributedStringCreate(kCFAllocatorDefault, string, lineAttributes); CFRelease(lineAttributes); CFRelease(string); CFMutableAttributedStringRef mutableAttributedString = CFAttributedStringCreateMutableCopy(kCFAllocatorDefault, 0, attributedString); CFRelease(attributedString); CTFontRef monospaceFont = CTFontCreateWithName(CFSTR("Courier"), fontSize, nullptr); CFAttributedStringSetAttribute(mutableAttributedString, CFRangeMake(0, 12), kCTFontAttributeName, monospaceFont); CFRelease(monospaceFont); CFAttributedStringSetAttribute(mutableAttributedString, CFRangeMake(12, 52), kCTFontAttributeName, font); CFRelease(font); CTLineRef line = CTLineCreateWithAttributedString(mutableAttributedString); CFRelease(mutableAttributedString); CTLineDraw(line, context); CFRelease(line); }
JNIEXPORT void JNICALL OS_NATIVE(CGAffineTransformScale) (JNIEnv *env, jclass that, jfloatArray arg0, jfloat arg1, jfloat arg2, jfloatArray arg3) { jfloat *lparg0=NULL; jfloat *lparg3=NULL; OS_NATIVE_ENTER(env, that, CGAffineTransformScale_FUNC); if (arg0) if ((lparg0 = (*env)->GetFloatArrayElements(env, arg0, NULL)) == NULL) goto fail; if (arg3) if ((lparg3 = (*env)->GetFloatArrayElements(env, arg3, NULL)) == NULL) goto fail; *(CGAffineTransform *)lparg3 = CGAffineTransformScale(*(CGAffineTransform *)lparg0, arg1, arg2); fail: if (arg3 && lparg3) (*env)->ReleaseFloatArrayElements(env, arg3, lparg3, 0); if (arg0 && lparg0) (*env)->ReleaseFloatArrayElements(env, arg0, lparg0, JNI_ABORT); OS_NATIVE_EXIT(env, that, CGAffineTransformScale_FUNC); }
void doRotatedEllipsesWithCGPath(CGContextRef context) { int i, totreps = 144.; CGMutablePathRef path = NULL; float tint = 1., tintIncrement = 1./totreps; // Create a new transform consisting of a 45 degree rotation. CGAffineTransform theTransform = CGAffineTransformMakeRotation(M_PI/4); // Apply a scaling transformation to the transform just created. theTransform = CGAffineTransformScale(theTransform, 1, 2); // Create a mutable CGPath object. path = CGPathCreateMutable(); if(!path){ fprintf(stderr, "Couldn't create path!\n"); return; } // Add a circular arc to the CGPath object, transformed // by an affine transform. CGPathAddArc(path, &theTransform, 0., 0., 45., 0., 2*M_PI, false); // Close the CGPath object. CGPathCloseSubpath(path); // Place the first ellipse at a good location. CGContextTranslateCTM(context, 100., 100.); for (i = 0 ; i < totreps ; i++){ CGContextBeginPath(context); // Add the CGPath object to the current path in the context. CGContextAddPath(context, path); // Set the fill color for this instance of the ellipse. CGContextSetRGBFillColor(context, tint, 0., 0., 1.); // Filling the path implicitly closes it. CGContextFillPath(context); // Compute the next tint color. tint -= tintIncrement; // Move over for the next ellipse. CGContextTranslateCTM(context, 1, 0.); } // Release the path when done with it. CGPathRelease(path); }
void EJCanvasContext::scale(float x, float y) { state->transform = CGAffineTransformScale( state->transform, x, y ); path->transform = state->transform; }
AffineTransform &AffineTransform::scale(double sx, double sy) { m_transform = CGAffineTransformScale(m_transform, sx, sy); return *this; }
Transform2D Transform2D::scale(Float scaleX, Float scaleY) const { return CGAffineTransformScale(*this, scaleX, scaleY); }
/*----------------------------------------------------------------------------------------------------------*/ pascal OSStatus ScrollingTextViewHandler(EventHandlerCallRef inCaller, EventRef inEvent, void* inRefcon) { OSStatus result = eventNotHandledErr; ScrollingTextBoxData* myData = (ScrollingTextBoxData*)inRefcon; switch (GetEventClass(inEvent)) { case kEventClassHIObject: switch (GetEventKind(inEvent)) { case kEventHIObjectConstruct: { // allocate some instance data myData = (ScrollingTextBoxData*) calloc(1, sizeof(ScrollingTextBoxData)); // get our superclass instance HIViewRef epView; GetEventParameter(inEvent, kEventParamHIObjectInstance, typeHIObjectRef, NULL, sizeof(epView), NULL, &epView); // remember our superclass in our instance data and initialize other fields myData->view = epView; // set the control ID so that we can find it later with HIViewFindByID result = SetControlID(myData->view, &kScrollingTextBoxViewID); // store our instance data into the event result = SetEventParameter(inEvent, kEventParamHIObjectInstance, typeVoidPtr, sizeof(myData), &myData); break; } case kEventHIObjectDestruct: { if (myData->theTimer != NULL) RemoveEventLoopTimer(myData->theTimer); CFRelease(myData->theText); free(myData); result = noErr; break; } case kEventHIObjectInitialize: { // always begin kEventHIObjectInitialize by calling through to the previous handler result = CallNextEventHandler(inCaller, inEvent); // if that succeeded, do our own initialization if (result == noErr) { GetEventParameter(inEvent, kEventParamScrollingText, typeCFStringRef, NULL, sizeof(myData->theText), NULL, &myData->theText); CFRetain(myData->theText); GetEventParameter(inEvent, kEventParamAutoScroll, typeBoolean, NULL, sizeof(myData->autoScroll), NULL, &myData->autoScroll); GetEventParameter(inEvent, kEventParamDelayBeforeAutoScroll, typeUInt32, NULL, sizeof(myData->delayBeforeAutoScroll), NULL, &myData->delayBeforeAutoScroll); GetEventParameter(inEvent, kEventParamDelayBetweenAutoScroll, typeUInt32, NULL, sizeof(myData->delayBetweenAutoScroll), NULL, &myData->delayBetweenAutoScroll); GetEventParameter(inEvent, kEventParamAutoScrollAmount, typeSInt16, NULL, sizeof(myData->autoScrollAmount), NULL, &myData->autoScrollAmount); myData->theTimer = NULL; } break; } default: break; } break; case kEventClassScrollable: switch (GetEventKind(inEvent)) { case kEventScrollableGetInfo: { // we're being asked to return information about the scrolled view that we set as Event Parameters HISize imageSize = {50.0, myData->height}; SetEventParameter(inEvent, kEventParamImageSize, typeHISize, sizeof(imageSize), &imageSize); HISize lineSize = {50.0, 20.0}; SetEventParameter(inEvent, kEventParamLineSize, typeHISize, sizeof(lineSize), &lineSize); HIRect bounds; HIViewGetBounds(myData->view, &bounds); SetEventParameter(inEvent, kEventParamViewSize, typeHISize, sizeof(bounds.size), &bounds.size); SetEventParameter(inEvent, kEventParamOrigin, typeHIPoint, sizeof(myData->originPoint), &myData->originPoint); result = noErr; break; } case kEventScrollableScrollTo: { // we're being asked to scroll, we just do a sanity check and ask for a redraw HIPoint where; GetEventParameter(inEvent, kEventParamOrigin, typeHIPoint, NULL, sizeof(where), NULL, &where); HIViewSetNeedsDisplay(myData->view, true); myData->originPoint.y = (where.y < 0.0)?0.0:where.y; HIViewSetBoundsOrigin(myData->view, 0, myData->originPoint.y); break; } default: break; } break; case kEventClassControl: switch (GetEventKind(inEvent)) { // sets the feature of the view. case kEventControlInitialize: { result = CallNextEventHandler(inCaller, inEvent); if (result != noErr) break; UInt32 features = 0; result = GetEventParameter(inEvent, kEventParamControlFeatures, typeUInt32, NULL, sizeof(features), NULL, &features); if (result == noErr) features |= kControlSupportsEmbedding; else features = kControlSupportsEmbedding; result = SetEventParameter(inEvent, kEventParamControlFeatures, typeUInt32, sizeof features, &features); break; } // Our parent view just changed dimensions, so we determined our new height. case kEventControlSetData: CFRelease(myData->theText); CFStringRef *p; GetEventParameter(inEvent, kEventParamControlDataBuffer, typePtr, NULL, sizeof(p), NULL, &p); myData->theText = *p; CFRetain(myData->theText); // fallthrough case kEventControlBoundsChanged: { HIRect bounds; HIViewGetBounds(myData->view, &bounds); // // If we're building on Panther (or later) then HIThemeGetTextDimensions is available, else we use GetThemeTextDimensions // #if PANTHER_BUILD // // Furthermore, if we're running on Panther then we can call HIThemeGetTextDimensions else we call GetThemeTextDimensions // if (GetHIToolboxVersion() >= Panther_HIToolbox_Version) { HIThemeTextInfo textInfo = {0, kThemeStateActive, kScrollingTextBoxFontID, kHIThemeTextHorizontalFlushLeft, kHIThemeTextVerticalFlushTop, kHIThemeTextBoxOptionStronglyVertical, kHIThemeTextTruncationNone, 0, false}; HIThemeGetTextDimensions(myData->theText, bounds.size.width - kMargin - kMargin, &textInfo, NULL, &myData->height, NULL); } else #endif { Point pointBounds; pointBounds.h = (int)(bounds.size.width - kMargin - kMargin); GetThemeTextDimensions(myData->theText, kScrollingTextBoxFontID, kThemeStateActive, true, &pointBounds, NULL); myData->height = pointBounds.v; } myData->height += 2.0 * kMargin; HIViewSetNeedsDisplay(myData->view, true); result = eventNotHandledErr; break; } // Draw the view. case kEventControlDraw: { CGContextRef context; result = GetEventParameter(inEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof(context), NULL, &context); HIRect bounds; HIViewGetBounds(myData->view, &bounds); CGContextSaveGState(context); CGAffineTransform transform = CGAffineTransformIdentity; // adjust the transform so the text doesn't draw upside down transform = CGAffineTransformScale(transform, 1, -1); CGContextSetTextMatrix(context, transform); // now that the proper parameters and configurations have been dealt with, let's draw result = ScrollingTextBoxDraw(context, &bounds, myData); CGContextRestoreGState(context); if (myData->autoScroll) CGContextStrokeRect(context, bounds); // we postpone starting the autoscroll timer until after we do our first drawing if ( (myData->autoScroll) && (myData->theTimer == NULL) ) InstallEventLoopTimer(GetCurrentEventLoop(), TicksToEventTime(myData->delayBeforeAutoScroll), TicksToEventTime(myData->delayBetweenAutoScroll), myScrollingTextTimeProc, myData, &myData->theTimer); result = noErr; break; } default: break; } break; default: break; } return result; }
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; }
static cairo_status_t _cairo_atsui_font_show_glyphs(void *abstract_font, cairo_operator_t operator, cairo_pattern_t *pattern, cairo_surface_t *generic_surface, int source_x, int source_y, int dest_x, int dest_y, unsigned int width, unsigned int height, const cairo_glyph_t *glyphs, int num_glyphs) { cairo_atsui_font_t *font = abstract_font; CGContextRef myBitmapContext; CGColorSpaceRef colorSpace; cairo_image_surface_t *destImageSurface; int i; void *extra = NULL; cairo_rectangle_t rect = {dest_x, dest_y, width, height}; _cairo_surface_acquire_dest_image(generic_surface, &rect, &destImageSurface, &rect, &extra); // Create a CGBitmapContext for the dest surface for drawing into colorSpace = CGColorSpaceCreateDeviceRGB(); myBitmapContext = CGBitmapContextCreate(destImageSurface->data, destImageSurface->width, destImageSurface->height, destImageSurface->depth / 4, destImageSurface->stride, colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host); CGContextTranslateCTM(myBitmapContext, 0, destImageSurface->height); CGContextScaleCTM(myBitmapContext, 1.0f, -1.0f); ATSFontRef atsFont = FMGetATSFontRefFromFont(font->fontID); CGFontRef cgFont = CGFontCreateWithPlatformFont(&atsFont); CGContextSetFont(myBitmapContext, cgFont); CGAffineTransform textTransform = CGAffineTransformMakeWithCairoFontScale(&font->scale); textTransform = CGAffineTransformScale(textTransform, 1.0f, -1.0f); CGContextSetFontSize(myBitmapContext, 1.0); CGContextSetTextMatrix(myBitmapContext, textTransform); if (pattern->type == CAIRO_PATTERN_SOLID && _cairo_pattern_is_opaque_solid(pattern)) { cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *)pattern; CGContextSetRGBFillColor(myBitmapContext, solid->color.red, solid->color.green, solid->color.blue, 1.0f); } else { CGContextSetRGBFillColor(myBitmapContext, 0.0f, 0.0f, 0.0f, 0.0f); } // TODO - bold and italic text // // We could draw the text using ATSUI and get bold, italics // etc. for free, but ATSUI does a lot of text layout work // that we don't really need... for (i = 0; i < num_glyphs; i++) { CGGlyph theGlyph = glyphs[i].index; CGContextShowGlyphsAtPoint(myBitmapContext, glyphs[i].x, glyphs[i].y, &theGlyph, 1); } CGColorSpaceRelease(colorSpace); CGContextRelease(myBitmapContext); _cairo_surface_release_dest_image(generic_surface, &rect, destImageSurface, &rect, extra); return CAIRO_STATUS_SUCCESS; }
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); }