iOS: CGImageCreateWith[PNG или JPEG]DataProvider вызывает ошибку сегментации

У меня странная проблема. Я разрабатываю утилиту для сканирования штрих-кода командной строки iOS с использованием libzbar (да, это для взломанных устройств). Все идет хорошо, кроме случаев, когда я пытаюсь использовать CGImageCreateWithPNGDataProvider() или же CGImageCreateWithJPEGDataProvider() методы получения CGImageRef из файла - потому что эти две функции генерируют ошибку на моем iPad 5.1.1. Проблема не в моем обычном классе, ZBarScanner потому что, если я использую UIImage для получения данных изображения, используя что-то вроде

UIImage *uiImage = [UIImage imageWithContentsOfFile:fname];
CGImageRef image = uiImage.CGImage;

затем он работает нормально и печатает данные, хранящиеся в штрих-коде. Кроме того, изображения в формате PNG и JPEG хорошо отформатированы - я могу просматривать их с помощью файлового браузера на самом устройстве, а также попробовал несколько других изображений. Я даже пытался опустить все CFRelease() вызовы функций и release сообщения, чтобы избежать висящих указателей. Вот мой код:

#define LOG() NSLog(@"Reached line %d", __LINE__)

int main(int argc, char **argv)
{
    if (argc != 2)
            return 1;

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    LOG(); // line 21

    NSString *fname = [[NSString stringWithUTF8String:argv[1]] retain]; // added an extra retain just in case

    LOG(); // line 25

    CFDataRef data = (CFDataRef)[NSData dataWithContentsOfFile:fname];
    CGDataProviderRef dProv = CGDataProviderCreateWithCFData(data);
    // I also tried using
    // dProv = CGDataProviderCreateWithFilename(argv[1]);
    // that made no difference either. The data and data provider are
    // valid, but the CGImage constructors always segfault.
    if (dProv == NULL) {
        fprintf(stderr, "Invalid CGDataProvider\n");
        abort();
    }

    LOG(); // line 34

    CGImageRef image = NULL;

    if ([[fname pathExtension] isEqualToString:@"png"]) {
        LOG(); // line 39
        NSLog(@"Function pointer: %p", CGImageCreateWithPNGDataProvider);
        image = CGImageCreateWithPNGDataProvider(dProv, NULL, false, kCGRenderingIntentDefault); // This function segfaults, or...
        LOG();
    } else if ([[fname pathExtension] isEqualToString:@"jpg"]
        || [[fname pathExtension] isEqualToString:@"jpeg"]) {
        LOG();
        image = CGImageCreateWithJPEGDataProvider(dProv, NULL, true, kCGRenderingIntentDefault); // ... or this one.
        LOG();
    } else {
        fprintf(stderr, "File '%s' is neither a PNG nor a JPEG file!\n", argv[1]);
        LOG();
        abort();
    }

    LOG();
    // CFRelease(dProv);
    LOG();
    ZBarScanner *scanner = [ZBarScanner zbarScannerWithCGImage:image];
    // CFRelease(image);
    LOG();
    NSArray *arr = [scanner scan];
    NSLog(@"The result of the scanning is:\n%@", arr);
    LOG();
    [pool drain];

    return 0;
}

Если я запускаю его в отладчике (для ясности удален беспорядок в GDB и NSLog):

gdb ./scanner
(gdb) run ./barcode1.png
Reached line 21
Reached line 25
Reached line 34
Reached line 39
Function pointer: 0x37c5b535

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x00000000
0x00000000 in ?? ()
(gdb) backtrace
#0 0x00000000 in ?? ()
(gdb)

Так что даже обратная трассировка не показывает ничего явно неправильного / полезного... Кажется, что- то где- то NULL. Я даже подозревал, что из-за того, что мой набор инструментов является неофициальной сборкой на основе 4.0, эти функции могут быть недоступны в iOS 5.1.1, поэтому сборка завершается успешно, так как CGImageCreateWith[...]DataProvider символы находятся внутри sysroot разработки, но не среди реальных динамических библиотек iOS, но если бы это было так, указатель функции, который я выбрал NSL, был бы NULL, право? Однако ни NS, ни CG-объекты, ни функции не кажутся NULL - единственным NULL, который я передаю конструкторам CGImage, является decodeArray параметр, но в документации Apple явно указано, что он может иметь значение NULL... (Обновление: я попытался передать допустимый ненулевой массив, чтобы выяснить, не является ли документация неправильной, но я все еще получил ту же ошибку).

Не могли бы вы дать мне несколько советов (каламбур) об этой аварии? Что мне здесь не хватает? Все учебники и ссылки, которые я нашел до сих пор, предлагают использовать CGDataProvider и CGImage, вот так.

1 ответ

Решение

При использовании имени файла в примере кода из документации Apple используется комбинация того, что вы упомянули в своих комментариях, плюс значение kCGRenderingIntentPerceptual а не по умолчанию:

    CGDataProviderRef pngDP = CGDataProviderCreateWithFilename([filePath fileSystemRepresentation]);
    if (pngDP) {
        CGImageRef img = CGImageCreateWithPNGDataProvider(pngDP, NULL, true, kCGRenderingIntentPerceptual); // true for interpolate, false for not-interpolate

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

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

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