Невозможно правильно прочитать цветовые данные UIImage, созданные из UIView - PullRequest
0 голосов
/ 09 апреля 2019

Что должно произойти:

Пользователь рисует (используя, например, Apple Pencil) в UIView. Когда он закончил, из этого представления создается UIImage, данные ARGB читаются по одному и, наконец, сохраняются в NSData.

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

Что на самом деле происходит:

Пользователь рисует в представлении, UIImage берется, но когда код обращается к данным изображения, происходит что-то проводное: данные, кажется, не соответствуют изображению, что становится очевидным, когда они используются для восстановления изображения ,

А вот и код.

Из UIView

* 1015 инициализации *

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.layer.allowsEdgeAntialiasing=YES;
        self.backgroundColor=[UIColor clearColor];
    }
    return self;
}

Рисунок

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [[event allTouches]anyObject];
    CGPoint touchLocation = [touch locationInView:self];
    if (myImage!=nil) {
        UIImageView * iV = [[UIImageView alloc] initWithFrame:self.bounds];
        iV.image = myImage;
        [self insertSubview:iV atIndex:0];

    }
    myPath = [UIBezierPath bezierPath];
    [myPath moveToPoint:touchLocation];
}

- (void) touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [[event allTouches]anyObject];
    CGPoint touchLocation = [touch locationInView:self];
    [myPath addLineToPoint:touchLocation];
    [self setNeedsDisplay];
}

-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        myImage = self.imageRepresentation;
}


- (void) drawRect:(CGRect)rect {

    self.context = UIGraphicsGetCurrentContext();
    [[UIColor blackColor]setStroke];
    myPath.lineWidth=3.0;
    [myPath stroke];

}

Получение UIImage из UIView

-(UIImage *)imageRepresentation{

    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 1.0);
    [self.layer renderInContext:UIGraphicsGetCurrentContext()];
    myImage= UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return myImage;

}

Чтение и преобразование данных изображения

-(NSData *) pixel{

    int sidelength = (int) self.myImage.size.width;
    int numberData = sidelength * sidelength * 4;

    CFDataRef pixelData = CGDataProviderCopyData(CGImageGetDataProvider(self.myImage.CGImage));
    const UInt8 * data = CFDataGetBytePtr(pixelData);

    UInt8 pixel[numberData];
    for (int y=0; y<sidelength; y++) {
        for (int x=0; x<sidelength; x++) {
            int components = sidelength * y + x * 4;
            UInt8 a = data[components];
            UInt8 r = data[components+1];
            UInt8 g = data[components+2];
            UInt8 b = data[components+3];

            pixel[components]=a;
            pixel[components+1]=r;
            pixel[components+2]=g;
            pixel[components+3]=b;

        }
    }
    CFRelease(pixelData);
    return [NSData dataWithBytes:pixel length:sidelength*sidelength*4];;

из другого класса

Получение изображения с использованием NSData

-(UIImage *) getImage{

    UInt8 * pixel=malloc(sizeof(UInt8)*self.numberBytes);
    pixel = (UInt8 *) booleanData.bytes;

    const int Width = self.size.width;
    const int Height = self.size.height;
    const int ComponentsPerPixel = 4;

    const size_t BitsPerComponent = 8;
    const size_t BytesPerRow=((BitsPerComponent * Width) / 8) * ComponentsPerPixel;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef gtx = CGBitmapContextCreate(&pixel[0], Width, Height, BitsPerComponent, BytesPerRow, colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);

    CGImageRef toCGImage = CGBitmapContextCreateImage(gtx);
    UIImage * image = [[UIImage alloc] initWithCGImage:toCGImage];
    return image;


}

Вот пример результатов:

Исходный рисунок, выполненный с использованием «imageRepresentation»:

enter image description here

Полученное изображение с использованием «pixel» и «getImage»:

enter image description here

Я ожидаю, что виновником будет метод «пикселей», потому что я проверил его результаты и обнаружил, что они уже довольно ошибочны. Кроме того, я протестировал getImage в другой настройке, и он работал просто отлично. Но странно то, что это верно и для «пикселей». Так возможно ли, что UIView визуализируется не так, как, скажем, png или jpg? Я проверил документацию для UIGraphicsBeginImageContextWithOptions, но это не дало мне полезную подсказку.

1 Ответ

1 голос
/ 11 апреля 2019

Есть две проблемы. Во-первых, вы не вставляете все данные изображения в массив pixel в методе pixel.

Если ваше изображение имеет размер 100x100, то общее количество байтов равно 10000, умноженному на 4. В вашем цикле for самый большой пиксель, который вы получите, будет 100x100 + 100x4, что составляет 10400. Остальные пиксели содержат мусор.

Этот цикл сделает работу:

for (unsigned long i = 0; i < numberData; i += 4) {
    pixel[i] = data[i];
    pixel[i + 1] = data[i + 1];
    pixel[i + 2] = data[i + 2];
    pixel[i + 3] = data[i + 3];
}

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

Я запустил ваш код (с новым циклом), и он отлично работает с UIView размером 128x128, но возвращает искаженное изображение при размере 100x100.

...