WKGraphics.mm   [plain text]


/*
 * Copyright (C) 2005, 2006, 2007, 2009 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

#import "config.h"
#import "WKGraphics.h"

#if PLATFORM(IOS)

#import "CoreGraphicsSPI.h"
#import "FontCascade.h"
#import "PlatformScreen.h"
#import "WebCoreThread.h"
#import <ImageIO/ImageIO.h>
#import <wtf/StdLibExtras.h>

using namespace WebCore;

static inline void _FillRectsUsingOperation(CGContextRef context, const CGRect* rects, int count, CGCompositeOperation op)
{
    int i;
    CGRect *integralRects = reinterpret_cast<CGRect *>(alloca(sizeof(CGRect) * count));
    
    assert (integralRects);
    
    for (i = 0; i < count; i++) {
        integralRects[i] = CGRectApplyAffineTransform (rects[i], CGContextGetCTM(context));
        integralRects[i] = CGRectIntegral (integralRects[i]);
        integralRects[i] = CGRectApplyAffineTransform (integralRects[i], CGAffineTransformInvert(CGContextGetCTM(context)));
    }
    CGCompositeOperation oldOp = CGContextGetCompositeOperation(context);
    CGContextSetCompositeOperation(context, op);
    CGContextFillRects(context, integralRects, count);
    CGContextSetCompositeOperation(context, oldOp);
}

static inline void _FillRectUsingOperation(CGContextRef context, CGRect rect, CGCompositeOperation op)
{
    if (rect.size.width > 0 && rect.size.height > 0) {
        _FillRectsUsingOperation (context, &rect, 1, op);
    }
}

void WKRectFill(CGContextRef context, CGRect aRect)
{
    if (aRect.size.width > 0 && aRect.size.height > 0) {
        CGContextSaveGState(context);
        _FillRectUsingOperation(context, aRect, kCGCompositeCopy);
        CGContextRestoreGState(context);
    }
}

void WKRectFillUsingOperation(CGContextRef context, CGRect aRect, WKCompositeOperation compositeOperation)
{
    COMPILE_ASSERT(sizeof(WKCompositeOperation) == sizeof(CGCompositeOperation), "WKCompositeOperation must be the same size as CGCompositeOperation.");
    if (aRect.size.width > 0 && aRect.size.height > 0.0) {
        CGContextSaveGState(context);
        _FillRectUsingOperation(context, aRect, static_cast<CGCompositeOperation>(compositeOperation));
        CGContextRestoreGState(context);
    }
}

void WKSetCurrentGraphicsContext(CGContextRef context)
{
    WebThreadContext* threadContext =  WebThreadCurrentContext();
    threadContext->currentCGContext = context;
}

CGContextRef WKGetCurrentGraphicsContext(void)
{
    WebThreadContext* threadContext =  WebThreadCurrentContext();
    return threadContext->currentCGContext;
}

static NSString *imageResourcePath(const char* imageFile, unsigned scaleFactor)
{
    NSString *fileName = scaleFactor == 1 ? [NSString stringWithUTF8String:imageFile] : [NSString stringWithFormat:@"%s@%dx", imageFile, scaleFactor];
#if PLATFORM(IOS_SIMULATOR)
    NSBundle *bundle = [NSBundle bundleWithIdentifier:@"com.apple.WebCore"];
    return [bundle pathForResource:fileName ofType:@"png"];
#else
    // Workaround for <rdar://problem/7780665> CFBundleCopyResourceURL takes a long time on iPhone 3G.
    NSString *imageDirectory = @"/System/Library/PrivateFrameworks/WebCore.framework";
    return [NSString stringWithFormat:@"%@/%@.png", imageDirectory, fileName];
#endif
}

CGImageRef WKGraphicsCreateImageFromBundleWithName(const char *image_file)
{
    if (!image_file)
        return NULL;

    CGImageRef image = nullptr;
    NSData *imageData = nullptr;
    for (unsigned scaleFactor = screenScaleFactor(); scaleFactor > 0; --scaleFactor) {
        imageData = [NSData dataWithContentsOfFile:imageResourcePath(image_file, scaleFactor)];
        ASSERT(scaleFactor != screenScaleFactor() || imageData);
        if (imageData)
            break;
    }
    
    if (imageData) {
        RetainPtr<CGDataProviderRef> dataProvider = adoptCF(CGDataProviderCreateWithCFData(reinterpret_cast<CFDataRef>(imageData)));
        image = CGImageCreateWithPNGDataProvider(dataProvider.get(), nullptr, NO, kCGRenderingIntentDefault);
    }

    return image;
}

static void WKDrawPatternBitmap(void *info, CGContextRef c) 
{
    CGImageRef image = (CGImageRef)info;
    CGFloat scale = screenScaleFactor();
    CGContextDrawImage(c, CGRectMake(0, 0, CGImageGetWidth(image) / scale, CGImageGetHeight(image) / scale), image);    
}

static void WKReleasePatternBitmap(void *info) 
{
    CGImageRelease(reinterpret_cast<CGImageRef>(info));
}

static const CGPatternCallbacks WKPatternBitmapCallbacks = 
{
    0, WKDrawPatternBitmap, WKReleasePatternBitmap
};

CGPatternRef WKCreatePatternFromCGImage(CGImageRef imageRef)
{
    // retain image since it's freed by our callback
    CGImageRetain(imageRef);

    CGFloat scale = screenScaleFactor();
    return CGPatternCreate((void*)imageRef, CGRectMake(0, 0, CGImageGetWidth(imageRef) / scale, CGImageGetHeight(imageRef) / scale), CGAffineTransformIdentity, CGImageGetWidth(imageRef) / scale, CGImageGetHeight(imageRef) / scale, kCGPatternTilingConstantSpacing, 1 /*isColored*/, &WKPatternBitmapCallbacks);
}

void WKSetPattern(CGContextRef context, CGPatternRef pattern, bool fill, bool stroke) 
{
    if (pattern == NULL)
        return;

    CGFloat patternAlpha = 1;
    CGColorSpaceRef colorspace = CGColorSpaceCreatePattern(NULL);
    if (fill) {
        CGContextSetFillColorSpace(context, colorspace);
        CGContextSetFillPattern(context, pattern, &patternAlpha);
    }
    if (stroke) {
        CGContextSetStrokeColorSpace(context, colorspace);
        CGContextSetStrokePattern(context, pattern, &patternAlpha);
    }
    CGColorSpaceRelease(colorspace);
}

#endif // PLATFORM(IOS)