Преобразование CGContext, чтобы извлечь повернутый прямоугольник

Мое намерение состоит в том, чтобы написать функцию, которая может извлечь повернутую прямоугольную часть большего изображения. Эта повернутая прямоугольная часть описывается с центральной точкой (от 0,0 до полной ширины / высоты изображения), размером и углом поворота.

Я волнуюсь, что я не понимаю что-то о CGContexts. Я знаю, что после вращения мое координатное пространство вращается.

Эта функция всегда работает, если угол равен 0, и кажется правильной, если угол небольшой. Однако с увеличением угла все смещается неправильно.

Я вычисляю верхний левый угол с помощью аффинного преобразования, аналогичного преобразованию изображения, чтобы избежать любой неточности. Я также попытался изменить верхний левый угол, применив к нему преобразование изображения, а также обратное преобразование изображения (как случайную мысль).

Будет ли этот подход работать? Я где-то пропускаю космическое преобразование?

Примечание. Было очень легко заштриховать область изображения, нарисовав полное изображение, а затем нарисовав черный прямоугольник в правильном положении с правильным размером и вращением. Учитывая это, я знаю, что мои данные верны.

Зрительные:

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

Это изображение представляет желаемый результат этой функции:Это изображение представляет желаемый результат этой функции

- (UIImage*) croppedImage:(UIImage*) image center:(CGPoint) rotationCenter size:(CGSize) size rotation:(CGFloat) rotation
{
    CGFloat fullWidth = image.size.width;
    CGFloat fullHeight = image.size.height;
    CGRect imageRect = CGRectMake(0, 0, fullWidth, fullHeight);

    //must convert UI space to CG space, in this case that just means adjusting the y coordinate
    rotationCenter.y = fullHeight - rotationCenter.y;

    CGFloat width = size.width;
    CGFloat height = size.height;

    int flags = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast;
    CGContextRef btx = CGBitmapContextCreate(NULL, width, height, 8, 0, CGColorSpaceCreateDeviceRGB(), flags);
    CGContextSetAllowsAntialiasing(btx, YES);
    CGContextSetShouldAntialias(btx, YES);

    //use a transformation to rotate at the center of the request area
    CGAffineTransform imageTransform = CGAffineTransformIdentity;
    imageTransform = CGAffineTransformTranslate(imageTransform, rotationCenter.x, rotationCenter.y);
    imageTransform = CGAffineTransformRotate(imageTransform, rotation);
    imageTransform = CGAffineTransformTranslate(imageTransform, -rotationCenter.x, -rotationCenter.y);

    //use a transformation to determine the top-left coordinate of the rect
    CGAffineTransform ptTransform = CGAffineTransformIdentity;
    ptTransform = CGAffineTransformTranslate(ptTransform, rotationCenter.x, rotationCenter.y);
    ptTransform = CGAffineTransformRotate(ptTransform, rotation);
    ptTransform = CGAffineTransformTranslate(ptTransform, -width/2.0, -height/2.0);

    //this gets me the position of the top-left corner of the image no matter the rotation
    CGPoint topleft  = CGPointApplyAffineTransform(CGPointZero, ptTransform);

    CGContextConcatCTM(btx, imageTransform);

    //move the image away from the origin, to align origin with where i want my image sampled from
    CGContextTranslateCTM(btx, -topleft.x,
                                -topleft.y);

    //Close... but wrong
    CGContextDrawImage(btx, imageRect, image.CGImage);

    CGImageRef img = CGBitmapContextCreateImage(btx);
    UIImage* uiimage =  [UIImage imageWithCGImage:img scale:image.scale orientation:image.imageOrientation];

    CGContextRelease(btx);
    CGImageRelease(img);

    return uiimage;
}

1 ответ

Решение

Я полагаю, что вы описываете такие вещи:

Это действительно запущенное приложение для iOS, так что вы можете увидеть, что я сделал: на первом рисунке я нарисовал синим прямоугольник, который я хочу захватить, и показал результат в виде изображения; на втором снимке я его запечатлел и показал, что результат в виде изображения.

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

Итак, теперь я полагаю, вы хотите знать, как я это сделал? Синий прямоугольник был нарисован с использованием трех частей информации: верхний левый угол прямоугольника topLeft, размер прямоугольника sizeи вращение rot, Чтобы упростить вещи, я проигнорировал ваш разговор о "центре"; Я повернулся вокруг верхнего левого.

Затем второе изображение было получено путем создания графического контекста размера sizeвращая это негатив rotи рисование полного исходного изображения в негативе topLeft, Фактическая часть рисования - это всего две строки кода (извините, я пишу и думаю в Свифт в наши дни, но я уверен, что вы можете перевести):

CGContextRotateCTM(context, -rot)
im.drawAtPoint(CGPointMake(-topLeft.x, -topLeft.y))

Поэтому я думаю, что мой ответ в конечном итоге таков: это очень просто и работает так, как вы ожидаете, если вы превратите свое первоначальное описание в то, что вы хотите сделать, чтобы topLeft вместо центра.

Другие вопросы по тегам