2012-04-04 9 views
8

Un amico e io abbiamo passato la parte migliore della scorsa notte quasi strappandoci i capelli cercando di lavorare con alcune immagini in un'app della metropolitana. Abbiamo inserito le immagini nell'app con l'incantesimo di condivisione, quindi ho voluto lavorare con loro, ritagliare le immagini e salvarle nella cartella appdata. Questo si è rivelato estremamente frustrante.Gestione immagini WinRT

La mia domanda, alla fine di tutto questo, sarà "Qual è il modo corretto di farlo, senza sentire come sto martellando insieme un mucchio di pezzi di puzzle non corrispondenti?"

Quando si condividono più immagini con l'app, vengono visualizzate come un elenco di Windows.Storage.StorageFile s. Ecco un codice usato per gestirlo.

var storageItems = await _shareOperation.Data.GetStorageItemsAsync(); 

foreach (StorageFile item in storageItems) 
{ 
    var stream = await item.OpenReadAsync(); 
    var properties = await item.Properties.GetImagePropertiesAsync(); 

    var image = new WriteableBitmap((Int32)properties.Width, (Int32)properties.Height); 
    image.SetSource(stream); 

    images.Add(image); 
} 

qualche ricerca on-line ha indicato che attualmente, un Windows.UI.Xaml.Media.Imaging.WriteableBitmap è l'unica cosa in grado di lasciare che si accede ai dati dei pixel nell'immagine. This question include una risposta utile piena di metodi di estensione per il salvataggio delle immagini in un file, quindi li abbiamo utilizzati.

I nostri problemi sono stati i peggiori quando ho provato ad aprire nuovamente i file in seguito. Ho fatto qualcosa di simile a prima:

var files = await ApplicationData.Current.LocalFolder.GetFilesAsync(); 

foreach (var file in files) 
{ 
    var fileStream = await file.OpenReadAsync(); 
    var properties = await file.Properties.GetImagePropertiesAsync(); 
    var bitmap = new WriteableBitmap((Int32)properties.Width, (Int32)properties.Height); 
    bitmap.SetSource(fileStream); 

    System.IO.Stream stream = bitmap.PixelBuffer.AsStream(); 

Ecco un problema. Quanto è lungo questo flusso, se voglio che i byte escano da esso?

// CRASH! Length isn't supported on an IRandomAccessStream. 
    var pixels = new byte[fileStream.Length]; 

Ok riprovare.

var pixels = new byte[stream.Length]; 

questo funziona, tranne ... se l'immagine viene compressa, il flusso è più breve di quanto si pensi, così si finirà per ottenere un'eccezione di limiti. Per ora fingere che sia una bitmap non compressa.

await _stream.ReadAsync(pixels, 0, pixels.Length); 

Beh, indovinate. Anche se ho detto bitmap.SetSource(fileStream); per leggere i dati, il mio array di byte è ancora pieno di zeri. Non ho idea del perché. Se passo questo stesso bitmap in una mia interfaccia utente attraverso il gruppo di dati di esempio, l'immagine si presenta bene. Quindi ha chiaramente ottenuto i dati dei pixel in quella bitmap da qualche parte, ma non riesco a leggerlo da bitmap.PixelBuffer? Perchè no?

Infine, ecco cosa ha funzionato.

var decoder = await BitmapDecoder.CreateAsync(BitmapDecoder.PngDecoderId, fileStream); 
    var data = await decoder.GetPixelDataAsync(); 
    var bytes = data.DetachPixelData(); 

    /* process my data, finally */ 

} // end of that foreach I started a while ago 

Così ora ho per dati di immagine, ma ho ancora un grosso problema. Per fare qualsiasi cosa con esso, devo fare ipotesi sul suo formato. Non ho idea se sia rgba, rgb, abgr, bgra, qualunque cosa possano essere. Se penso che la mia elaborazione non vada a buon fine, fallisce. Ho avuto dozzine di esecuzioni di test sputando zero byte e immagini danneggiate, immagini capovolte (???), colori sbagliati, ecc. Mi sarei aspettato di trovare alcune di queste informazioni nello properties che ho ricevuto chiamando lo await file.Properties.GetImagePropertiesAsync();, ma senza fortuna. Questo contiene solo la larghezza e l'altezza dell'immagine, oltre ad alcune altre cose inutili. Documentazione minima here.

Quindi, perché questo processo è così doloroso? Sta riflettendo l'immaturità delle biblioteche in questo momento e posso aspettarmi che migliori? O esiste già un modo standard per farlo? Vorrei che fosse facile come in System.Drawing. Questo ti ha dato tutti i dati di cui hai mai avuto bisogno e hai felicemente caricato qualsiasi tipo di immagine senza doverti fare da solo con i flussi.

+0

"Non ho idea se sia rgba, rgb, abgr, bgra, qualunque cosa possano essere.". In realtà sai esattamente cosa è nella tua scelta di formato. Risolvilo una volta e per PNG e puoi applicare la logica a tutti i PNG. – jheriko

+0

... anche tutti implementano le librerie di imaging abbastanza male, sebbene BitmapEncoder/BitmapDecoder siano piuttosto carini. stb_image.c fa un lavoro molto migliore nella maggior parte dei casi e sulla maggior parte delle piattaforme rispetto a qualsiasi libreria nativa disponibile perché fornisce un'interfaccia minima e corretta. (byte, dimensioni, formati, senza cromo e piastra di riscaldamento). – jheriko

risposta

2
  1. Da quello che ho visto - quando si prevede di caricare il WriteableBitmap con un flusso - non c'è bisogno di controllare le dimensioni dell'immagine - basta fare nuova WriteableBitmap (1,1), quindi chiamare SetSource() .
  2. Non sai perché stavi pensando var pixels = new byte [fileStream.Length]; funzionerebbe, dal momento che il file Stream ha i byte dell'immagine compressa e non un array di pixel.
  3. Potrebbe essere necessario cercare l'inizio del flusso per ottenere la matrice pixel:

    var pixelStream = pixelBuffer.AsStream(); 
    var bytes = new byte[this.pixelStream.Length]; 
    this.pixelStream.Seek(0, SeekOrigin.Begin); 
    this.pixelStream.Read(bytes, 0, Bytes.Length); 
    
  4. avevo iniziato a lavorare su una porta di WinRT WriteableBitmapEx - forse potrebbe aiutare: http://bit.ly/WriteableBitmapExWinRT. Non l'ho testato bene e si basa su una versione precedente di WBX, ma è abbastanza completo in termini di supporto delle funzionalità. Potrebbe essere un po 'più lento di quanto sia possibile anche.

+0

Ah, quindi non ho bisogno del decodificatore, ho solo bisogno di cercare l'inizio. Grazie! Darò un colpo. E controllerò la tua biblioteca! – Tesserex

+0

La ricerca dello stream non funzionava, aveva ancora tutti i byte zero. Scusate. – Tesserex

+0

Forse è necessario attendere il caricamento dell'immagine prima di poter accedere al buffer dei pixel? Vorrei iniziare eseguendo Task.Delay (1000) prima di accedere a PixelBuffer. Se questo aiuta - forse c'è un evento da aspettare come bitmap.Loaded. –