void FlipContextIfNeeded(CGContextRef inContext, HIThemeOrientation inOrientation, HIRect const *inBounds) { switch (inOrientation) { case kHIThemeOrientationNormal: return; case kHIThemeOrientationInverted: { float height = inBounds->origin.y + inBounds->origin.y + inBounds->size.height; CGContextTranslateCTM(inContext, 0.0, height); CGContextScaleCTM(inContext, 1.0, -1.0); } } } void UnflipContextIfNeeded(CGContextRef inContext, HIThemeOrientation inOrientation, HIRect const *inBounds) { switch (inOrientation) { case kHIThemeOrientationNormal: return; case kHIThemeOrientationInverted: { float height = inBounds->origin.y + inBounds->origin.y + inBounds->size.height; CGContextScaleCTM(inContext, 1.0, -1.0); CGContextTranslateCTM(inContext, 0.0, -height); } } } CGColorSpaceRef CGColorSpaceGetMainDisplay() { // - This is based on Technical Q&A #1396 // http://developer.apple.com/qa/qa2004/qa1396.html static CGColorSpaceRef sDisplayColorSpace = NULL; if (!sDisplayColorSpace) { // - Get the Systems Profile for the main display CMProfileRef sysprof = NULL; if (noErr == CMGetSystemProfile(&sysprof)) { // - Create a colorspace with the systems profile sDisplayColorSpace = CGColorSpaceCreateWithPlatformColorSpace(sysprof); // - Close the profile CMCloseProfile(sysprof); } } return sDisplayColorSpace; } static void MyDrawPattern(void *info, CGContextRef context) { if (info && context) { CGImageRef image = (CGImageRef)info; CGRect rect = { CGPointZero, {1.0, 1.0} }; CGContextDrawImage(context, rect, image); } } static void MyReleasePattern(void *info) { if (info) CGImageRelease((CGImageRef)info); } static CGPatternCallbacks MyCGPatternCallbacks = { 0, // version MyDrawPattern, MyReleasePattern }; static CGPatternRef CreatePatternForImage(CGImageRef tiledImage, size_t *outImageHeight) { if (!tiledImage) return NULL; size_t imageHeight = CGImageGetHeight(tiledImage); if (outImageHeight) *outImageHeight = imageHeight; CGRect patternBounds = { CGPointZero, {1.0, 1.0} }; CGAffineTransform transform = CGAffineTransformMakeScale(CGImageGetWidth (tiledImage), imageHeight); CGPatternRef pattern = NULL; pattern = CGPatternCreate(tiledImage, patternBounds, transform, 1.0, 1.0, kCGPatternTilingConstantSpacingMinimalDistortion, true, &MyCGPatternCallbacks); if (!pattern) return NULL; // the callback will release tiledImage CGImageRetain(tiledImage); return pattern; } static OSStatus DrawPattern(CGRect const *inBounds, CGPatternRef inPattern, size_t inTileHeight, CGContextRef inContext) { if (!inBounds || !inPattern || !inContext) return paramErr; CGColorSpaceRef patternColorspace = CGColorSpaceCreatePattern(NULL); if (!patternColorspace) return notEnoughMemoryErr; int verticalPhase = inTileHeight ? ((int)inBounds->size.height % inTileHeight) : 0; CGSize phase = {0.0, verticalPhase}; CGContextSetPatternPhase(inContext, phase); float const colorComponents[] = {1.0}; CGContextSetFillColorSpace(inContext, patternColorspace); CGContextSetFillPattern(inContext, inPattern, colorComponents); CGContextFillRect(inContext, *inBounds); CGColorSpaceRelease(patternColorspace); return noErr; } OSStatus TileImage(HIRect const *inBounds, CGImageRef inImage, CGContextRef inContext, HIThemeOrientation inOrientation) { OSStatus result = paramErr; size_t tileHeight; CGPatternRef pattern = CreatePatternForImage(inImage, &tileHeight); if (pattern) { CGColorSpaceRef displaySpace = CGColorSpaceGetMainDisplay(); if (displaySpace) { CGContextSaveGState(inContext); FlipContextIfNeeded(inContext, inOrientation, inBounds); CGContextSetFillColorSpace(inContext, displaySpace); result = DrawPattern( (CGRect const *)inBounds, pattern, tileHeight, inContext); UnflipContextIfNeeded( inContext, inOrientation, inBounds ); CGContextRestoreGState(inContext); } CGPatternRelease(pattern); } return result; }