Как я могу многократно (со временем) рисовать в одном и том же UIImage? (Или какой магазин поддержки мне нужно сделать?) - PullRequest
2 голосов
/ 21 февраля 2010

Я почти сошел с ума по этой проблеме, из-за того, что перебрал добрую дюжину других веб-сайтов (а также этот) в поисках прямого ответа на мою проблему ...

Во-первых, я пытаюсь найти способ, учитывая начальное изображение, созданное с помощью [UIImage imageNamed:], чтобы добавить украшения, основанные на обработке во времени. (Подумайте о тепловой карте.) Я рано осознал, что мне нужно скопировать изображение, поскольку изображения, вызываемые с помощью imageNamed, находятся в специальном кэше, поэтому я копирую необработанные данные и создаю новые изображения и контексты через Базовый графический API. Несмотря на то, что я смог скопировать изображение и связать его с UIView, я не могу понять, как впоследствии изменить его содержимое ... Вот что я сейчас делаю в своем коде ...

@interface HeatMapTestViewController : UIViewController {

    CFDataRef imageData;
    CGColorSpaceRef colorSpace;
    CGContextRef context;
    CGColorRef paintColor;
    CGImageRef image;

}

- (IBAction) imageButtonTapped:(UIButton*)sender forEvent:(UIEvent*)event;

@end

@implementation HeatMapTestViewController

- (void) viewDidLoad {

    [super viewDidLoad];

    UIImage* backImage = [UIImage imageNamed:@"Grey Checkerboard.png"];
    UIColor* backdrop = [UIColor colorWithPatternImage:backImage];
    [[self view] setBackgroundColor:backdrop];

    UIImage* startImage = [UIImage imageNamed:@"Starting Image.png"];
    CGImageRef pixelmap = [startImage CGImage];
    // http://www.iphonedevsdk.com/forum/iphone-sdk-development/34247-cgimage-pixel-array.html
    CGDataProviderRef dataProvider = CGImageGetDataProvider(pixelmap);
    imageData = CGDataProviderCopyData(dataProvider);
    void* rawPixels = (void*)CFDataGetBytePtr(imageData);
    colorSpace = CGColorSpaceCreateDeviceRGB();
    // http://developer.apple.com/mac/library/qa/qa2001/qa1037.html
    CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(pixelmap);
    if ( ( bitmapInfo & kCGBitmapAlphaInfoMask ) == kCGImageAlphaLast )
        bitmapInfo = ( bitmapInfo & kCGBitmapByteOrderMask ) | kCGImageAlphaPremultipliedLast;
    if ( ( bitmapInfo & kCGBitmapAlphaInfoMask ) == kCGImageAlphaFirst )
        bitmapInfo = ( bitmapInfo & kCGBitmapByteOrderMask ) | kCGImageAlphaPremultipliedFirst;
    context = CGBitmapContextCreate(
           rawPixels,
           CGImageGetWidth(pixelmap),
           CGImageGetHeight(pixelmap),
           CGImageGetBitsPerComponent(pixelmap),
           CGImageGetBytesPerRow(pixelmap),
           colorSpace,
           bitmapInfo
        );
    CGFloat components[] = {1.0, 0.0, 0.0, 1.0};
    paintColor = CGColorCreate(colorSpace, components);

    image = CGBitmapContextCreateImage(context);    
    UIImage* newImage = [UIImage imageWithCGImage:image];
    UIButton* button = (id)[[self view] viewWithTag:327];
    [button setBackgroundImage:newImage forState:UIControlStateNormal];

}

- (IBAction) imageButtonTapped:(UIButton*)sender forEvent:(UIEvent*)event {

    UITouch* touch;
    CGPoint touchPoint;

    touch = [[event touchesForView:sender] anyObject];
        // Assuming we always just get one due to IB setting...
    touchPoint = [touch locationInView:sender];

    CGRect touchRect = CGRectZero;
    touchRect.origin = touchPoint;
    touchRect = CGRectInset(touchRect, -11.0, -11.0);

    UIGraphicsPushContext(context);
    CGContextSetFillColorWithColor(context, paintColor);
    CGContextFillEllipseInRect(context, touchRect);
    UIGraphicsPopContext();

    UIImage* newImage = [UIImage imageWithCGImage:image];
    [sender setBackgroundImage:newImage forState:UIControlStateNormal];
    [sender setNeedsDisplayInRect:touchRect];

} // imageButtonTapped:forEvent:

@end

Обратная связь в режиме реального времени предназначена только для демонстрационных целей ... Что я действительно планирую сделать в конечном продукте, так это показать измененное изображение по запросу пользователя за экраном, на который он / она в данный момент смотрит. Мой Google-fu меня подводил, я не могу найти пример кода в документации Apple. Я даже пытался найти примеры кода для «рисования» приложений для iPhone, так как приложения такого типа, кажется, разделяют основную функциональность того, чего я пытаюсь достичь. Нет кости. / -:

РЕДАКТИРОВАТЬ: После гораздо большего количества экспериментов и исследований, я получил что-то вроде этого, приняв другой подход ...

@interface HeatMapTestViewController : UIViewController {

    CGColorSpaceRef colorSpace;
    CGColorRef paintColor;

}

- (IBAction) imageButtonTapped:(UIButton*)sender forEvent:(UIEvent*)event;

@end

@implementation HeatMapTestViewController

- (void) viewDidLoad {

    [super viewDidLoad];

    UIImage* backImage = [UIImage imageNamed:@"Grey Checkerboard.png"];
    UIColor* backdrop = [UIColor colorWithPatternImage:backImage];
    [[self view] setBackgroundColor:backdrop];

    UIImage* startImage = [UIImage imageNamed:@"Starting Image.png"];
    UIButton* button = (id)[[self view] viewWithTag:327];
    [button setBackgroundImage:startImage forState:UIControlStateNormal];

    colorSpace = CGColorSpaceCreateDeviceRGB();
    CGFloat components[] = {1.0, 0.0, 0.0, 1.0};
    paintColor = CGColorCreate(colorSpace, components);

}

- (IBAction) imageButtonTapped:(UIButton*)sender forEvent:(UIEvent*)event {

    UITouch* touch;
    CGPoint touchPoint;

    touch = [[event touchesForView:sender] anyObject];
        // Assuming we always just get one due to IB setting...
    touchPoint = [touch locationInView:sender];

    CGRect touchRect = CGRectZero;
    touchRect.origin = touchPoint;
    touchRect = CGRectInset(touchRect, -11.0, -11.0);

    UIImage* image = [sender backgroundImageForState:UIControlStateNormal];

    // http://www.ipodtouchfans.com/forums/showthread.php?t=132024
    UIGraphicsBeginImageContext([image size]);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [image drawInRect:CGRectMake(0.0, 0.0, [image size].width, [image size].height)];
        // This repaints the entire image! Boo hiss!
    CGContextSetFillColorWithColor(context, paintColor);
    CGContextFillEllipseInRect(context, touchRect);
    [sender setBackgroundImage:UIGraphicsGetImageFromCurrentImageContext()
            forState:UIControlStateNormal];
    UIGraphicsEndImageContext();

} // imageButtonTapped:forEvent:

@end

Большая проблема с этим сигналом отмечена в комментарии. Если я правильно понимаю вышеизложенное, я перерисовываю все содержимое изображения в новое, просто чтобы обновить его небольшую часть. Это не похоже на то, что я могу делать с 20-30 раз в секунду.

1 Ответ

1 голос
/ 22 февраля 2010

Это то, что я наконец-то начал работать ... но я все еще не уверен, что все делаю правильно ...

@interface HeatMapTestViewController : UIViewController {

    void* imageData;
    CGContextRef context;

}

- (IBAction) imageButtonTapped:(UIButton*)sender forEvent:(UIEvent*)event;

@end

@implementation HeatMapTestViewController

- (void) viewDidLoad {

    [super viewDidLoad];

    UIImage* backImage = [UIImage imageNamed:@"Grey Checkerboard.png"];
    UIColor* backdrop = [UIColor colorWithPatternImage:backImage];
    [[self view] setBackgroundColor:backdrop];

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGFloat components[] = {1.0, 0.0, 0.0, 0.2};
    CGColorRef paintColor = CGColorCreate(colorSpace, components);

    UIImage* startImage = [UIImage imageNamed:@"Starting Image.png"];
    imageData = NSZoneMalloc(NSDefaultMallocZone(), [startImage size].width * [startImage size].height * 4);
    // http://developer.apple.com/mac/library/qa/qa2001/qa1037.html
    context = CGBitmapContextCreate(
           imageData,
           [startImage size].width,
           [startImage size].height,
           8,
           [startImage size].height * 4,
           colorSpace,
           kCGImageAlphaPremultipliedLast
        );
    // Why is this needed? I'm wrapping the final product in a UIImage!
    // http://developer.apple.com/iphone/library/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-SW9
    CGContextTranslateCTM(context, 0.0, [startImage size].height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextSetFillColorWithColor(context, paintColor);
    CGColorRelease(paintColor);
    CGColorSpaceRelease(colorSpace);

    UIGraphicsPushContext(context);
    [startImage drawInRect:CGRectMake(0.0, 0.0, [startImage size].width, [startImage size].height)];
    CGContextFlush(context);
    UIGraphicsPopContext();

    CGImageRef image = CGBitmapContextCreateImage(context); // Copy! Boo!
    UIImage* newImage = [UIImage imageWithCGImage:image];
    CGImageRelease(image);
    UIButton* button = (id)[[self view] viewWithTag:327];
    [button setBackgroundImage:newImage forState:UIControlStateNormal];

}

- (IBAction) imageButtonTapped:(UIButton*)sender forEvent:(UIEvent*)event {

    UITouch* touch;
    CGPoint touchPoint;

    touch = [[event touchesForView:sender] anyObject];
        // Assuming we always just get one due to IB setting...
    touchPoint = [touch locationInView:sender];

    CGRect touchRect = CGRectZero;
    touchRect.origin = touchPoint;
    touchRect = CGRectInset(touchRect, -11.0, -11.0);

    UIGraphicsPushContext(context);
    CGContextFillEllipseInRect(context, touchRect);
    CGContextFlush(context);
    UIGraphicsPopContext();

    CGImageRef image = CGBitmapContextCreateImage(context); // Copy! Boo!
    UIImage* newImage = [UIImage imageWithCGImage:image];
    CGImageRelease(image);
    [sender setBackgroundImage:newImage forState:UIControlStateNormal];

} // imageButtonTapped:forEvent:

@end
...