Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Как я могу создать эффект, похожий на размытие в iOS 7?

Автор вопроса: SnowmanОтветов: 12

Я пытаюсь воспроизвести этот размытый фон с публично выпущенного экрана примера iOS 7 от Apple:

В этом вопросе предлагается применить фильтр CI к содержимому ниже, но это совершенно другой подход. . Очевидно, что iOS 7 не захватывает содержимое представленных ниже представлений по многим причинам:

  1. Выполнение грубого тестирования, создание снимка экрана представлений ниже и применение фильтра CIGaussianBlur с достаточно большим радиусом, чтобы имитировать стиль размытия iOS 7, занимает 1-2 секунды, даже на симуляторе.
  2. Размытие в iOS 7 позволяет размывать динамические представления, такие как видео или анимация, без заметных задержек.

Может ли кто-нибудь предположить, какие фреймворки они могли бы использовать для создания этого эффекта, и возможно ли создать аналогичный эффект с текущими общедоступными API-интерфейсами?

Изменить: (из комментария) Мы точно не знаем, как Apple это делает, но можем ли мы сделать какие-то основные предположения? Мы можем предположить, что они используют оборудование, верно?

Является ли эффект самодостаточным в каждом представлении, так что эффект на самом деле не знает, что за ним стоит? Или необходимо, в зависимости от того, как работает размытие, учитывать содержимое размытия?

Если содержимое, стоящее за эффектом, имеет значение, можем ли мы предположить, что Apple получает "поток" содержимого ниже и непрерывно отображает его с размытием?

  • (Я думаю, мы можем предположить, что Apple в любом случае использует чистый GL для рендеринга домашних экранов. Я сомневаюсь, что они абстрагируют его с помощью UIViews и других вещей, которые могут снизить производительность, поскольку это такая ключевая часть ОС) Dave 12.06.2013
  • Как я указал в комментариях к моему ответу здесь: stackoverflow.com/a/17048668/19679 они написали ОС, поэтому конечно, у них будет ускоренный доступ к содержимому слоев, составленных под текущим представлением. Мы можем увидеть некоторые из того, что они могут использовать в частном API IOSurface: stackoverflow.com/questions/14135215/. Размытие по Гауссу может быть выполнено намного быстрее, чем случаи обобщенного размытия по Гауссу, если они имеют фиксированный радиус или даже с использованием интересных оптимизаций, таких как интегральные изображения. Brad Larson 13.06.2013
  • @BradLarson - Перефразируя Джессику Симпсон ... Я понятия не имею, что все это значит, но звучит чертовски круто! А если серьезно, вы говорите, что можете использовать частично прозрачный вид с фильтром размытия и наложить его поверх другого вида для достижения этого эффекта? sangony 17.06.2013
  • stackoverflow.com/a/25706250/2308190 отлично работал у меня в первый раз, когда я его попробовал, и был кратким Ben Wheeler 25.06.2015

iostransparencygraphics

Ответы

Ответ 1 · рейтинг 134 · user2342340

Зачем копировать эффект? Просто нарисуйте UIToolbar позади вашего представления.

myView.backgroundColor = [UIColor clearColor];
UIToolbar* bgToolbar = [[UIToolbar alloc] initWithFrame:myView.frame];
bgToolbar.barStyle = UIBarStyleDefault;
[myView.superview insertSubview:bgToolbar belowSubview:myView];

Ответ 2 · рейтинг 64 · FreaknBigPanda

Apple выпустила код на WWDC в качестве категории на UIImage, которая включает эту функцию. Если у вас есть учетная запись разработчика, вы можете получить категорию UIImage (и остальную часть примера кода), перейдя по этой ссылке: https://developer.apple.com/wwdc/schedule/, просматривая раздел 226 и нажимая на подробности. Я еще не играл с ним, но думаю, что эффект будет намного медленнее в iOS 6, есть некоторые улучшения в iOS 7, которые делают захват начального снимка экрана, который используется в качестве входных данных для размытия, намного быстрее.

Прямая ссылка: https://developer.apple.com/downloads/download.action?path=wwdc_2013/wwdc_2013_sample_code/ios_uiimageeffects.zip

Ответ 3 · рейтинг 37 · Jeremy Fox

На самом деле я готов поспорить, что этого будет довольно просто достичь. Вероятно, он не будет работать или выглядеть точно так же, как Apple, но может быть очень близок.

Прежде всего, вам нужно определить CGRect UIView, который вы будете представлять. Как только вы определите, что вам просто нужно захватить изображение части пользовательского интерфейса, чтобы его можно было размыть. Что-то вроде этого...

- (UIImage*)getBlurredImage {
    // You will want to calculate this in code based on the view you will be presenting.
    CGSize size = CGSizeMake(200,200);

    UIGraphicsBeginImageContext(size);
    [view drawViewHierarchyInRect:(CGRect){CGPointZero, w, h} afterScreenUpdates:YES]; // view is the view you are grabbing the screen shot of. The view that is to be blurred.
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    // Gaussian Blur
    image = [image applyLightEffect];

    // Box Blur
    // image = [image boxblurImageWithBlur:0.2f];

    return image;
}

Размытие по Гауссу - рекомендуется

Используя UIImage+ImageEffects категорию, предоставленную Apple здесь, вы получите гауссовский размытие, которое очень похоже на размытие в iOS 7.

Размытие по рамке

Вы также можете использовать размытие рамки, используя следующую категорию boxBlurImageWithBlur: UIImage. Это основано на алгоритме, который вы можете найти в здесь.

@implementation UIImage (Blur)

-(UIImage *)boxblurImageWithBlur:(CGFloat)blur {
    if (blur < 0.f || blur > 1.f) {
        blur = 0.5f;
    }
    int boxSize = (int)(blur * 50);
    boxSize = boxSize - (boxSize % 2) + 1;

    CGImageRef img = self.CGImage;

    vImage_Buffer inBuffer, outBuffer;

    vImage_Error error;

    void *pixelBuffer;

    CGDataProviderRef inProvider = CGImageGetDataProvider(img);
    CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);

    inBuffer.width = CGImageGetWidth(img);
    inBuffer.height = CGImageGetHeight(img);
    inBuffer.rowBytes = CGImageGetBytesPerRow(img);

    inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData);

    pixelBuffer = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));

    if(pixelBuffer == NULL)
        NSLog(@"No pixelbuffer");

    outBuffer.data = pixelBuffer;
    outBuffer.width = CGImageGetWidth(img);
    outBuffer.height = CGImageGetHeight(img);
    outBuffer.rowBytes = CGImageGetBytesPerRow(img);

    error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);

    if (error) {
        NSLog(@"JFDepthView: error from convolution %ld", error);
    }

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef ctx = CGBitmapContextCreate(outBuffer.data,
                                         outBuffer.width,
                                         outBuffer.height,
                                         8,
                                         outBuffer.rowBytes,
                                         colorSpace,
                                         kCGImageAlphaNoneSkipLast);
    CGImageRef imageRef = CGBitmapContextCreateImage (ctx);
    UIImage *returnImage = [UIImage imageWithCGImage:imageRef];

    //clean up
    CGContextRelease(ctx);
    CGColorSpaceRelease(colorSpace);

    free(pixelBuffer);
    CFRelease(inBitmapData);

    CGImageRelease(imageRef);

    return returnImage;
}

@end

Теперь, когда вы вычисляете область экрана для размытия, передаете ее в категорию размытия и получаете обратно размытый UIImage, теперь остается только установить это размытое изображение в качестве фона для представления, которое вы будете представлять. Как я уже сказал, это не будет идеально соответствовать тому, что делает Apple, но все равно должно выглядеть довольно круто.

Надеюсь, это поможет.

Ответ 4 · рейтинг 25 · Adam Waite

iOS8 ответила на эти вопросы.

- (instancetype)initWithEffect:(UIVisualEffect *)effect

или Swift:

init(effect effect: UIVisualEffect)

Ответ 5 · рейтинг 20 · Ivo Leko

Я только что написал свой небольшой подкласс UIView, который может создавать собственное размытие iOS 7 в любом пользовательском представлении. Он использует UIToolbar, но безопасным способом для изменения его кадра, границ, цвета и альфа-канала с анимацией в реальном времени.

Пожалуйста, дайте мне знать, если заметите какие-либо проблемы.

https://github.com/ivoleko/ILTranslucentView

Ответ 6 · рейтинг 10 · Cody C

Ходят слухи, что инженеры Apple утверждают, что для обеспечения такой производительности они считывают данные непосредственно из буфера графического процессора, что вызывает проблемы с безопасностью, поэтому пока нет общедоступного API для этого.

Ответ 7 · рейтинг 7 · Stornu2

Это решение, которое вы можете увидеть на видеороликах WWDC. Вам нужно сделать размытие по Гауссу, поэтому первое, что вам нужно сделать, это добавить новый файл .m и .h с кодом, который я пишу здесь, затем вы должны сделать снимок экрана, использовать желаемый эффект и добавьте его в свое представление, затем ваш UITable UIView или что-то еще должно быть прозрачным, вы можете поиграть с applyBlurWithRadius, чтобы заархивировать желаемый эффект, этот вызов работает с любым UIImage.

В конце размытое изображение будет фоном, а остальные элементы управления должны быть прозрачными.

Чтобы это работало, вам нужно добавить следующие библиотеки:

Acelerate.framework, UIKit.framework, CoreGraphics.framework

Я надеюсь тебе это понравится.

Удачного кодирования.

    //Screen capture.
    UIGraphicsBeginImageContext(self.view.bounds.size);

    CGContextRef c = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(c, 0, 0);
    [self.view.layer renderInContext:c];

    UIImage* viewImage = UIGraphicsGetImageFromCurrentImageContext();
    viewImage = [viewImage applyLightEffect];

    UIGraphicsEndImageContext();

    //.h FILE
    #import <UIKit/UIKit.h>

    @interface UIImage (ImageEffects)

   - (UIImage *)applyLightEffect;
   - (UIImage *)applyExtraLightEffect;
   - (UIImage *)applyDarkEffect;
   - (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor;

   - (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage;

   @end

    //.m FILE
    #import "cGaussianEffect.h"
    #import <Accelerate/Accelerate.h>
    #import <float.h>


     @implementation UIImage (ImageEffects)


    - (UIImage *)applyLightEffect
    {
        UIColor *tintColor = [UIColor colorWithWhite:1.0 alpha:0.3];
        return [self applyBlurWithRadius:1 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
    }


    - (UIImage *)applyExtraLightEffect
    {
        UIColor *tintColor = [UIColor colorWithWhite:0.97 alpha:0.82];
        return [self applyBlurWithRadius:1 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
    }


    - (UIImage *)applyDarkEffect
    {
        UIColor *tintColor = [UIColor colorWithWhite:0.11 alpha:0.73];
        return [self applyBlurWithRadius:1 tintColor:tintColor saturationDeltaFactor:1.8 maskImage:nil];
    }


    - (UIImage *)applyTintEffectWithColor:(UIColor *)tintColor
    {
        const CGFloat EffectColorAlpha = 0.6;
        UIColor *effectColor = tintColor;
        int componentCount = CGColorGetNumberOfComponents(tintColor.CGColor);
        if (componentCount == 2) {
            CGFloat b;
            if ([tintColor getWhite:&b alpha:NULL]) {
                effectColor = [UIColor colorWithWhite:b alpha:EffectColorAlpha];
            }
        }
        else {
            CGFloat r, g, b;
            if ([tintColor getRed:&r green:&g blue:&b alpha:NULL]) {
                effectColor = [UIColor colorWithRed:r green:g blue:b alpha:EffectColorAlpha];
            }
        }
        return [self applyBlurWithRadius:10 tintColor:effectColor saturationDeltaFactor:-1.0 maskImage:nil];
    }


    - (UIImage *)applyBlurWithRadius:(CGFloat)blurRadius tintColor:(UIColor *)tintColor saturationDeltaFactor:(CGFloat)saturationDeltaFactor maskImage:(UIImage *)maskImage
    {
        if (self.size.width < 1 || self.size.height < 1) {
            NSLog (@"*** error: invalid size: (%.2f x %.2f). Both dimensions must be >= 1: %@", self.size.width, self.size.height, self);
            return nil;
        }
        if (!self.CGImage) {
            NSLog (@"*** error: image must be backed by a CGImage: %@", self);
            return nil;
        }
        if (maskImage && !maskImage.CGImage) {
            NSLog (@"*** error: maskImage must be backed by a CGImage: %@", maskImage);
            return nil;
        }

        CGRect imageRect = { CGPointZero, self.size };
        UIImage *effectImage = self;

        BOOL hasBlur = blurRadius > __FLT_EPSILON__;
        BOOL hasSaturationChange = fabs(saturationDeltaFactor - 1.) > __FLT_EPSILON__;
        if (hasBlur || hasSaturationChange) {
            UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
            CGContextRef effectInContext = UIGraphicsGetCurrentContext();
            CGContextScaleCTM(effectInContext, 1.0, -1.0);
            CGContextTranslateCTM(effectInContext, 0, -self.size.height);
            CGContextDrawImage(effectInContext, imageRect, self.CGImage);

            vImage_Buffer effectInBuffer;
            effectInBuffer.data     = CGBitmapContextGetData(effectInContext);
            effectInBuffer.width    = CGBitmapContextGetWidth(effectInContext);
            effectInBuffer.height   = CGBitmapContextGetHeight(effectInContext);
            effectInBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectInContext);

            UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
            CGContextRef effectOutContext = UIGraphicsGetCurrentContext();
            vImage_Buffer effectOutBuffer;
            effectOutBuffer.data     = CGBitmapContextGetData(effectOutContext);
            effectOutBuffer.width    = CGBitmapContextGetWidth(effectOutContext);
            effectOutBuffer.height   = CGBitmapContextGetHeight(effectOutContext);
            effectOutBuffer.rowBytes = CGBitmapContextGetBytesPerRow(effectOutContext);

            if (hasBlur) {
                CGFloat inputRadius = blurRadius * [[UIScreen mainScreen] scale];
                NSUInteger radius = floor(inputRadius * 3. * sqrt(2 * M_PI) / 4 + 0.5);
                if (radius % 2 != 1) {
                    radius += 1;
                }
                vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
                vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
                vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, NULL, 0, 0, radius, radius, 0, kvImageEdgeExtend);
            }
            BOOL effectImageBuffersAreSwapped = NO;
            if (hasSaturationChange) {
                CGFloat s = saturationDeltaFactor;
                CGFloat floatingPointSaturationMatrix[] = {
                    0.0722 + 0.9278 * s,  0.0722 - 0.0722 * s,  0.0722 - 0.0722 * s,  0,
                    0.7152 - 0.7152 * s,  0.7152 + 0.2848 * s,  0.7152 - 0.7152 * s,  0,
                    0.2126 - 0.2126 * s,  0.2126 - 0.2126 * s,  0.2126 + 0.7873 * s,  0,
                                  0,                    0,                    0,  1,
                };
                const int32_t divisor = 256;
                NSUInteger matrixSize = sizeof(floatingPointSaturationMatrix)/sizeof(floatingPointSaturationMatrix[0]);
                int16_t saturationMatrix[matrixSize];
                for (NSUInteger i = 0; i < matrixSize; ++i) {
                    saturationMatrix[i] = (int16_t)roundf(floatingPointSaturationMatrix[i] * divisor);
                }
                if (hasBlur) {
                    vImageMatrixMultiply_ARGB8888(&effectOutBuffer, &effectInBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags);
                    effectImageBuffersAreSwapped = YES;
                }
                else {
                    vImageMatrixMultiply_ARGB8888(&effectInBuffer, &effectOutBuffer, saturationMatrix, divisor, NULL, NULL, kvImageNoFlags);
                }
            }
            if (!effectImageBuffersAreSwapped)
                effectImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();

            if (effectImageBuffersAreSwapped)
                effectImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
        }

        UIGraphicsBeginImageContextWithOptions(self.size, NO, [[UIScreen mainScreen] scale]);
        CGContextRef outputContext = UIGraphicsGetCurrentContext();
        CGContextScaleCTM(outputContext, 1.0, -1.0);
        CGContextTranslateCTM(outputContext, 0, -self.size.height);

        CGContextDrawImage(outputContext, imageRect, self.CGImage);

        if (hasBlur) {
            CGContextSaveGState(outputContext);
            if (maskImage) {
                CGContextClipToMask(outputContext, imageRect, maskImage.CGImage);
            }
            CGContextDrawImage(outputContext, imageRect, effectImage.CGImage);
            CGContextRestoreGState(outputContext);
        }

        if (tintColor) {
            CGContextSaveGState(outputContext);
            CGContextSetFillColorWithColor(outputContext, tintColor.CGColor);
            CGContextFillRect(outputContext, imageRect);
            CGContextRestoreGState(outputContext);
        }

        UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();

        return outputImage;
    }

Ответ 8 · рейтинг 7 · Senry

Вы можете найти свое решение в демонстрационной версии Apple на этой странице: WWDC 2013, найдите и скачайте образец кода UIImageEffects.

Затем с кодом @Jeremy Fox. Я изменил это на

- (UIImage*)getDarkBlurredImageWithTargetView:(UIView *)targetView
{
    CGSize size = targetView.frame.size;

    UIGraphicsBeginImageContext(size);
    CGContextRef c = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(c, 0, 0);
    [targetView.layer renderInContext:c]; // view is the view you are grabbing the screen shot of. The view that is to be blurred.
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return [image applyDarkEffect];
}

Надеюсь, что это поможет вам.

Ответ 9 · рейтинг 7 · Simon

Вот действительно простой способ сделать это: https://github.com/JagCesar/iOS-blur < / а>

Просто скопируйте слой UIToolbar, и все готово, AMBlurView сделает это за вас. Ладно, он не такой размытый, как центр управления, но достаточно размытый.

Помните, что iOS7 находится под соглашением о неразглашении.

Ответ 10 · рейтинг 6 · D33pN16h7

Каждый ответ здесь использует vImageBoxConvolve_ARGB8888, эта функция действительно очень медленная, это нормально, если производительность не является требованием высокого приоритета, но если вы используете ее для перехода между двумя контроллерами представления (например, ) этот подход означает время, превышающее 1 секунду или, может быть, больше, что очень плохо для пользовательского опыта вашего приложения.

Если вы предпочитаете оставить всю эту обработку изображений на GPU (и вы должны), вы можете получить гораздо лучший эффект, а также отличное время, округляющее 50 мс (предположим, что у вас есть время в 1 секунду в первом подходе), так что давайте сделаем это .

Сначала загрузите GPUImage Framework (под лицензией BSD) здесь.

Затем добавьте следующие классы (.m и .h) из GPUImage (я не уверен, что это минимум, необходимый только для эффекта размытия)

  • GPUImage.h
  • GPUImageAlphaBlendFilter
  • GPUImageFilter
  • GPUImageFilterGroup
  • GPUImageGaussianBlurPositionFilter
  • GPUImageGaussianSelectiveBlurFilter
  • GPUImageLuminanceRangeFilter
  • GPUImageOutput
  • GPUImageTwoInputFilter
  • GLProgram
  • GPUImageBoxBlurFilter
  • GPUImageGaussianBlurFilter
  • GPUImageiOSBlurFilter
  • GPUImageSaturationFilter
  • GPUImageSolidColorGenerator
  • GPUImageTwoPassFilter
  • GPUImageTwoPassTextureSamplingFilter

  • iOS / GPUImage-Prefix.pch

  • iOS / GPUImageContext
  • iOS / GPUImageMovieWriter
  • iOS / GPUImagePicture
  • iOS / GPUImageView

Затем создайте категорию в UIImage, которая добавит эффект размытия к существующему UIImage:

#import "UIImage+Utils.h"

#import "GPUImagePicture.h"
#import "GPUImageSolidColorGenerator.h"
#import "GPUImageAlphaBlendFilter.h"
#import "GPUImageBoxBlurFilter.h"

@implementation UIImage (Utils)

- (UIImage*) GPUBlurredImage
{
    GPUImagePicture *source =[[GPUImagePicture alloc] initWithImage:self];

    CGSize size = CGSizeMake(self.size.width * self.scale, self.size.height * self.scale);

    GPUImageBoxBlurFilter *blur = [[GPUImageBoxBlurFilter alloc] init];
    [blur setBlurRadiusInPixels:4.0f];
    [blur setBlurPasses:2.0f];
    [blur forceProcessingAtSize:size];
    [source addTarget:blur];

    GPUImageSolidColorGenerator * white = [[GPUImageSolidColorGenerator alloc] init];

    [white setColorRed:1.0f green:1.0f blue:1.0f alpha:0.1f];
    [white forceProcessingAtSize:size];

    GPUImageAlphaBlendFilter * blend = [[GPUImageAlphaBlendFilter alloc] init];
    blend.mix = 0.9f;

    [blur addTarget:blend];
    [white addTarget:blend];

    [blend forceProcessingAtSize:size];
    [source processImage];

    return [blend imageFromCurrentlyProcessedOutput];
}

@end

И наконец, добавьте в свой проект следующие фреймворки:

AVFoundation CoreMedia CoreVideo OpenGLES

Да, получил удовольствие от этого гораздо более быстрого подхода;)

Ответ 11 · рейтинг 4 · Byte

Вы можете попробовать использовать мой пользовательский вид, в котором есть возможность размывать фон. Он делает это, имитируя снимок фона и размывая его, как в коде WWDC от Apple. Очень просто использовать.

Я также сделал некоторые улучшения, чтобы имитировать динамическое размытие без потери производительности. Фоном моего представления является scrollView, который прокручивается вместе с представлением, тем самым обеспечивая эффект размытия для остальной части супервизора.

См. Пример и код на моем GitHub

Ответ 12 · рейтинг 2 · Justin Fischer

Core Background реализует желаемый эффект iOS 7.

https://github.com/justinmfischer/core-background

Отказ от ответственности: я являюсь автором этого проекта

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы