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;
}