使用转换为 CALayer 渲染 UIImageView 以进行视频编辑

Ben*_*nez 3 objective-c avfoundation ios quartz-core

我正在制作一个应用程序,为在后台视图中播放的视频添加“贴纸”。贴纸被添加为 imageView,它响应平移、捏合和旋转手势,然后缩放和旋转作为变换应用于 UIImageview

要将 ImageViews 添加到视频中,我必须将它们渲染为 CALayer,如下面的代码

- (void)applyVideoEffectsToComposition:(AVMutableVideoComposition *)composition size:(CGSize)videoSize {

    CGSize screenSize = self.view.frame.size;
    CGFloat scale = videoSize.width / screenSize.width; //get scale between video recorded and the screen used to edit

    //Creates parentLayer that contains videoLayer and each image layer

    for (UIImageView *imageView in [self.stickerContainer subviews]) {
        CALayer *layer = [CALayer layer];
        [layer setContents:(id)[imageView.image CGImage]];

        //Set layer frame
        CGRect imageFrame = imageView.frame;
        CGRect imageBounds = imageView.bounds;
        CGRect frame = CGRectMake(imageFrame.origin.x * scale, imageFrame.origin.y * scale, imageBounds.size.width * scale, imageBounds.size.height * scale);
        layer.frame = frame;

        //Second set transform
        CGAffineTransform transform = imageView.transform;
        layer.transform = CATransform3DMakeAffineTransform(transform);

        [layer setMasksToBounds:YES];
        [parentLayer addSublayer:layer];
    }

    composition.animationTool = [AVVideoCompositionCoreAnimationTool
                                 videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
}
Run Code Online (Sandbox Code Playgroud)

基本上创建一个与 imageView 具有相同框架和转换的图层。但这会对每个手势产生奇怪的结果

  1. 平移手势结果似乎使 Y 轴反转(如果我在左上角有图像,它会在左下角呈现)
  2. 旋转手势结果似乎颠倒了 te b 和 c 值(来自 CGAffineTransform)
  3. 缩放手势结果螺旋渲染原点(因为图层原点是相同的..也许锚点必须做些什么?)

无论如何.. 知道为什么会发生这种情况吗?.. 如果我的图像没有任何变换并且完全居中,它看起来应该如此。

我的代码基于以下教程,因此生成新视频的代码基本相同

Ben*_*nez 5

所以我终于找到了一个有点“hacky”的解决方案..我会留下代码并解释它

- (void)applyVideoEffectsToComposition:(AVMutableVideoComposition *)composition videoSize:(CGSize)videoSize screenSize:(CGSize)screenSize stickersArray:(NSArray<UIImageView *>*)stickers{

    CGFloat videoScale = videoSize.width / screenSize.width; //get scale between video recorded and the screen used to edit

    CALayer *parentLayer = [CALayer layer];
    CALayer *videoLayer = [CALayer layer];
    parentLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height);
    videoLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height);
    //Add video layer first and then stickers
    [parentLayer addSublayer:videoLayer];
    for (UIImageView *imageView in stickers) {
        CALayer *layer = [CALayer layer];
        [layer setContents:(id)[imageView.image CGImage]];

        //First set image size with bounds of original image and center (not affected by transform)
        CGSize imageSize   = imageView.bounds.size;
        CGPoint center = imageView.center;

       //Fix offset issue by getting bottom point of imageView to a screen size proportion
        CGFloat yOffset = (center.y + imageSize.height/2) / screenSize.height;
        yOffset = 1 - yOffset; // invert screen proportion
        CGFloat scaledOriginX = (center.x - imageSize.width/2)* videoScale;
        CGRect frame = CGRectMake(scaledOriginX, videoSize.height * yOffset, imageSize.width * videoScale , imageSize.height * videoScale);
        layer.frame = frame;


        //Apply transforms but to original imageView
        CGAffineTransform transform = imageView.transform;
        //Invert original transform b value with c value
        layer.transform = CATransform3DMakeAffineTransform(CGAffineTransformMake(transform.a, transform.c, transform.b, transform.d, 0, 0));



        [layer setMasksToBounds:YES];
        [parentLayer addSublayer:layer];


    }

    composition.animationTool = [AVVideoCompositionCoreAnimationTool
                                 videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
}
Run Code Online (Sandbox Code Playgroud)

最初我的代码有缺陷,因为我使用了imageView.frame(sizeorigin),它们都受变换矩阵的影响,结果为CALayer. 相反,我应该使用 imageViewboundscenter,它们不受变换矩阵的影响或改变,本答案所示

这修复了缩放和原点问题,但是平移和旋转问题仍然存在

为了解决平移手势问题,我必须手动“反转”y 轴,如代码的这一部分所示

    //Fix offset issue by getting bottom point of imageView to a screen size propotion
    CGFloat yOffset = (center.y + imageSize.height/2) / screenSize.height;
    yOffset = 1 - yOffset;
    CGFloat scaledOriginX = (center.x - imageSize.width/2)* videoScale;
    CGRect frame = CGRectMake(scaledOriginX, videoSize.height * yOffset, imageSize.width * videoScale , imageSize.height * videoScale);
    layer.frame = frame;
Run Code Online (Sandbox Code Playgroud)

为了修复反向旋转,我必须手动反转变换 b 和 c 值

 layer.transform = CATransform3DMakeAffineTransform(CGAffineTransformMake(transform.a, transform.c, transform.b, transform.d, 0, 0)); 
Run Code Online (Sandbox Code Playgroud)

基本上就是这样......不知道为什么会发生旋转和平移问题,但这应该可以解决它。我真的希望有人有比我更好的解决方案,并且知道为什么会发生最后两个问题。