2017-02-20 20 views
12

В моем приложении я создаю отображение изображений с плавающей точкой в ​​пиксельное значение и использую его как оверлей на Картах Google, но навсегда нужно делать то же самое в Android почти мгновенно. Мой код выглядит так:Рисование изображения в Swift берет навсегда

private func imageFromPixels(pixels: [PixelData], width: Int, height: Int) -> UIImage? { 

    let bitsPerComponent = 8 
    let bitsPerPixel = bitsPerComponent * 4 
    let bytesPerRow = bitsPerPixel * width/8 

    let providerRef = CGDataProvider(
     data: NSData(bytes: pixels, length: height * width * 4) 
    ) 

    let cgimage = CGImage(
     width: width, 
     height: height, 
     bitsPerComponent: bitsPerComponent, 
     bitsPerPixel: bitsPerPixel, 
     bytesPerRow: bytesPerRow, 
     space: CGColorSpaceCreateDeviceRGB(), 
     bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue), 
     provider: providerRef!, 
     decode: nil, 
     shouldInterpolate: true, 
     intent: .defaultIntent 
    ) 

    if cgimage == nil { 
     print("CGImage is not supposed to be nil") 
     return nil 
    } 
    return UIImage(cgImage: cgimage!) 
} 

Любые предложения о том, как это может занять столько времени? Я вижу, что он использует около 96% мощности процессора.

func fromData(pair: AllocationPair) -> UIImage? { 

    let table = pair.table 
    let data = pair.data 

    prepareColors(allocations: table.allocations) 

    let height = data.count 
    let width = data[0].count 

    var colors = [PixelData]() 

    for row in data { 
     for val in row { 

      if (val == 0.0) { 
       colors.append(PixelData(a: 0, r: 0, g: 0, b: 0)) 
       continue 
      } 

      if let interval = findInterval(table: table, value: val) { 
       if let color = intervalColorDict[interval] { 
        colors.append(PixelData(a: color.a, r: color.r, g: color.g, b: color.b)) 
       } 
      } 
     } 
    } 

    return imageFromPixels(pixels: colors, width: width, height: height) 
} 

Я пробовал время профилировать его, и это результат, на который требуется время.

enter image description here

+0

В основном я просто показываю картинку с либо зелеными, красными или желтыми пикселями вперемешку – Recusiwe

+0

Вы уверены, что проблема здесь, а не в создании '[PixelData]' массив? В отладочных сборках управление большими массивами происходит очень медленно. Если вы делаете сборку релизов с включенными оптимизациями, использование больших массивов происходит намного быстрее. Удостоверьтесь, что проблема здесь, а не в создании этого массива. И тогда, конечно, убедитесь, что когда вы показываете изображение, которое вы делаете в главной очереди. – Rob

+1

@Recusiwe, вы пробовали использовать инструменты, чтобы определить, где расходуется весь процессор? Вы можете попробовать начать с этого учебника: https://www.raywenderlich.com/97886/instruments-tutorial-with-swift-getting-started –

ответ

8

Я попробовал ваш код, и я обнаружил, что проблема не связана с вашей функцией.

Я думаю, что вы должны использовать структуру пикселей на основе UInt8, а не структуру на основе CGFloat.

Я перевел свой код для приложения какао, и это результат:

public struct PixelData { 
    var a: UInt8 
    var r: UInt8 
    var g: UInt8 
    var b: UInt8 
} 

func imageFromPixels(pixels: [PixelData], width: Int, height: Int) -> NSImage? { 

    let bitsPerComponent = 8 
    let bitsPerPixel = bitsPerComponent * 4 
    let bytesPerRow = bitsPerPixel * width/8 

    let providerRef = CGDataProvider(
     data: NSData(bytes: pixels, length: height * width * 4) 
    ) 

    let cgimage = CGImage(
     width: width, 
     height: height, 
     bitsPerComponent: bitsPerComponent, 
     bitsPerPixel: bitsPerPixel, 
     bytesPerRow: bytesPerRow, 
     space: CGColorSpaceCreateDeviceRGB(), 
     bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue), 
     provider: providerRef!, 
     decode: nil, 
     shouldInterpolate: true, 
     intent: .defaultIntent 
    ) 

    if cgimage == nil { 
     print("CGImage is not supposed to be nil") 
     return nil 
    } 
    return NSImage(cgImage: cgimage!, size: NSSize(width: width, height: height)) 
} 

var img = [PixelData]() 

for i: UInt8 in 0 ..< 20 { 
    for j: UInt8 in 0 ..< 20 { 
     // Creating a red 20x20 image. 
     img.append(PixelData(a: 255, r: 255, g: 0, b: 0)) 
    } 
} 

var ns = imageFromPixels(pixels: img, width: 20, height: 20) 

Этот код быстро и легко, вот значения воздействия отладки системы:

enter image description here

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

+0

Я не буду брать NSImage и NSSize? Как это будет выглядеть, если он использует UIKit вместо AppKit? – Recusiwe

+0

@Recusiwe вы можете использовать 'UIImage (cgImage: cgimage!)' И возвращать 'UIImage' вместо' NSImage', а затем изменить 'NSData' на' Data', если вы используете UIKit. Вот [документация] (https://developer.apple.com/reference/uikit/uiimage/1624090-init). Другие инструкции почти одинаковы ... – Cristian

1

Если вам нужно получить доступ к фактическим растровым данным и изменить их, вы можете использовать CGImage. В других случаях CIImage использует объекты. Поэтому работайте с UIImage, конвертируйте UIImage в CIImage, делайте с ним какие-либо манипуляции, а затем конвертируйте его обратно в UIImage (как и в CGImage в коде).

Вот после объяснения того, что это то, что: UIImage vs. CIImage vs. CGImage

Ядро изображения фактически не делают изображение, пока он не сказал, чтобы сделать это. Этот метод «ленивой оценки» позволяет Core Image работать максимально эффективно.

CIImage page in Apple Developer Documentation