Как рисовать данные EPS в NSView

Я борюсь с проблемой рисования файла eps на NSView. Когда я впервые загружаю файл eps из файла и рисую его с помощью drawInRect:, изображение отображается правильно. Однако изображение не будет нарисовано, когда я загружу его из файла архива.

Я подготовил грязный небольшой пример, который вы можете скопировать/вставить и попробовать. Создайте новый проект Cocoa App и добавьте его в метод делегата.

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
  // Just a sample eps file
  NSURL *url = [NSURL URLWithString: @"http://embedded.eecs.berkeley.edu/concurrency/latex/figure.eps"];
  NSImage *epsImage = [[[NSImage alloc] initWithContentsOfURL: url] autorelease];

  // Encode data
  NSMutableData *mutableData = [[[NSMutableData alloc] init] autorelease];
  NSKeyedArchiver *coder = [[[NSKeyedArchiver alloc] initForWritingWithMutableData: mutableData] autorelease];
  [coder encodeObject: epsImage forKey: @"image"];
  [coder finishEncoding];
  NSString *dataFile = [@"~/Desktop/image.data" stringByExpandingTildeInPath];
  [mutableData writeToFile: dataFile atomically: YES];

  // Decode data
  NSData *data = [NSData dataWithContentsOfFile: dataFile];
  NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData: data];
  NSImage *loadedImage = [decoder decodeObjectForKey: @"image"];

  // Draw image
  NSRect rect;
  rect.origin = NSZeroPoint;
  rect.size = loadedImage.size;

  NSView *view = [[NSApp mainWindow] contentView];
  [view lockFocus];
  [loadedImage drawInRect: rect fromRect: rect operation: NSCompositeSourceOver fraction: 1.0];
  [view unlockFocus];
}

Чтобы убедиться, что первое загруженное изображение отображается правильно, просто измените строку [loadedImage drawInRect:...] на [epsImage drawInRect:...].

Здесь я использую NSKeyedArchiver и NSKeyedUnarchiver для имитации encodeWithCoder: и initWithCoder:. Поэтому, пожалуйста, обратите внимание на тот факт, что NSImage с представлением NSEPSImageRep, которое не содержит предварительного просмотра (из ветки ресурсов?) и загружается исключительно как команды eps, не отображается на NSView правильно.

Любая помощь приветствуется.


person cocoafan    schedule 10.06.2013    source источник
comment
После того, как вы загрузите loadedImage, какие NSImageRep будут к нему прикреплены? Вы должны быть в состоянии NSLog описать как loadedImage, так и его изображения, чтобы увидеть, что там есть.   -  person gaige    schedule 10.06.2013
comment
Привет @gaige, это точно такой же NSImageRep (NSEPSImageRep) с точно таким же содержимым данных. Извините, что не упомянул об этом раньше. Оба изображения содержат только одно представление, NSEPSImageRep.   -  person cocoafan    schedule 10.06.2013
comment
Это определенно странно. У нас есть приложение, которое сохраняет/загружает EPS из NSKeyedArchiver, и у нас не было проблем с ним. Тем не менее, мы разрываем NSImage и сохраняем NSEPSImageRep напрямую, чтобы убедиться, что мы получаем наивысшую точность (после того, как было задействовано кэширование, у нас возникли проблемы с получением лучшей версии, а не самой маленькой версии). Некоторые из этих проблем могли быть в более старых версиях OSX, так как этому коду на данный момент 3-4 года. Вы знаете, что файл будет EPS? В таком случае, вы можете просто сохранить версию NSData вместо NSImage?   -  person gaige    schedule 10.06.2013
comment
Я тоже это пробовал. Данный код является полным образцом для его воспроизведения. Он точно не будет рисовать при загрузке из NSKeyedArchiver. Я попытался заархивировать NSEPSImageRep вместо NSImage и снова сконструировал NSImage с загруженным NSEPSImageRep, но безуспешно. Я тоже раньше с такой проблемой не сталкивался. Фактический код довольно старый и работал раньше. Я предполагаю, что он начал не работать после установки Base SDK на 10.8 в Xcode (но не уверен в этом, изменение его на 10.7 SDK тоже не помогло).   -  person cocoafan    schedule 10.06.2013
comment
Чтобы уточнить, я на самом деле не сохранял NSEPSImageRep, я сохранял данные EPS, используя [(NSEPSImageRep*)rep EPSRepresentation], что в основном эквивалентно сохранению исходных данных EPS (теоретически). Это продолжает работать для меня.   -  person gaige    schedule 10.06.2013
comment
@gaige. ты мой герой. Спасибо. Это помогло. Я приму ваш ответ, когда вы его напишете. Также My NSImage может содержать любое представление NSImageRep. Есть ли элегантный и более общий способ (не относящийся к EPS) для сохранения и загрузки представлений?   -  person cocoafan    schedule 10.06.2013
comment
Я добавил ответ вместе с кодом, который мы используем для предпочтительного сохранения на основе некоторых использованных нами эвристик.   -  person gaige    schedule 10.06.2013


Ответы (1)


Из-за того, как кэширование работает с NSImage, я часто находил более эффективным получение NSImageRep, если я знаю, что это за тип.

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

К счастью, загрузка проста, так как NSImage::initWithData: определит тип на основе данных.

Вот наш давний код для этого. По сути, он предпочитает PDF, а не EPS, а затем создает TIFF из всего, что не понимает.

+ (NSData*) dataWithImage:(NSImage*)image kindString:( NSString**)kindStringPtr
{
    if (!image)
        return nil;

    NSData *pdfData=nil, *newData=nil, *epsData=nil, *imageData=nil;;
    NSString *kindString=nil;
    NSArray *reps = [image representations];
    for (NSImageRep *rep in reps) {
        if ([rep isKindOfClass: [NSPDFImageRep class]]) {
            // we have a PDF, so save that
            pdfData = [(NSPDFImageRep*)rep PDFRepresentation];
            PDFDocument *doc = [[PDFDocument alloc] initWithData:pdfData];
            newData = [doc dataRepresentation];
            if (newData && ([newData length]<[pdfData length])) {
                pdfData = newData;
            }
            break;
        }
        if ([rep isKindOfClass: [NSEPSImageRep class]]) {
            epsData = [(NSEPSImageRep*)rep EPSRepresentation];
            break;
        }
    }

    if (pdfData) {
        imageData=pdfData;
        kindString= @"pdfImage";
    } else if (epsData) {
        imageData=epsData;
        kindString=@"epsImage";
    } else {
        // make a big copy
        NSBitmapImageRep *rep0 = [reps objectAtIndex:0];
        if ([rep0 isKindOfClass: [NSBitmapImageRep class]]) {
            [image setSize: NSMakeSize( [rep0 pixelsWide], [rep0 pixelsHigh])];
        }

        imageData = [image TIFFRepresentation];
        kindString=@"tiffImage";
    }

    if (kindStringPtr)
        *kindStringPtr=kindString;
    return imageData;
}

Когда у нас есть NSData*, его можно сохранить в архиве с ключами, записать на диск или что-то еще.

На обратном пути загрузите NSData*, а затем

NSImage *image = [[NSImage alloc] initWithData: savedData];

и вы должны быть готовы.

person gaige    schedule 10.06.2013
comment
Большое спасибо. Это именно то, что мне нужно. - person cocoafan; 10.06.2013