2010-07-13 8 views
11

Sto cercando di creare un'applicazione per l'editor bitmap per l'iPhone che sarebbe simile a Pennelli o Livelli o una versione ridotta di Photoshop. Mi piacerebbe essere in grado di supportare immagini con risoluzione 1000x1000 con circa 4 livelli, se possibile.Funzione di annullamento rapido per l'applicazione di editor bitmap

Sto provando a progettare il mio sistema di annullamento/ripristino prima di scrivere troppo codice e sto avendo problemi reali con una buona soluzione a causa dei limiti di un dispositivo mobile e del fatto che le operazioni dell'editor di bitmap sono di solito distruttivo. I disegni di annullamento/ripetizione più comuni che conosco sono:

  1. Utilizzare il modello di comando. Si memorizza lo stato iniziale e i comandi utilizzati per trasformarlo nello stato corrente. Per annullare, si ricarica lo stato iniziale e si riproducono tutti i comandi tranne l'ultimo.

  2. Utilizzare il modello di memento. Dopo ogni operazione, si memorizzano informazioni sufficienti per essere in grado di ripristinare tale operazione.

I problemi prevedo sono: modello

  1. Comando: Cosa faccio dopo 500 operazioni di modifica e voglio annullare l'ultimo? Il caricamento dello stato iniziale e l'applicazione di 499 potrebbero richiedere molto tempo, soprattutto se alcune di queste sono costose come ad es. applicando i filtri sfocatura. Non mi piace il modo in cui l'annullamento richiede una diversa quantità di tempo in diversi scenari.

  2. Schema di memoria: il salvataggio delle parti della bitmap che è stata modificata richiede molta memoria. Anche la memorizzazione nella cache di questi bitmap su disco può essere lenta (quindi potrei avere problemi a memorizzare nella cache i bitmap se l'utente sta apportando molte modifiche veloci) e non sono sicuro delle implicazioni sull'utilizzo della batteria.

Le uniche soluzioni che in mente sono:

  1. Utilizzare il modello di comando e Memento Pattern dove ogni 10 comandi o così o dopo un'operazione costosa, l'intero stato è anche salvati (che ti offre una funzione di salvataggio automatico gratis). Per annullare, ricarico lo snapshot più vicino e poi rigiro i comandi. Preferirei comunque evitare questa complessità.

  2. Utilizzare il modello di memento e forzare l'utente ad attendere che i bitmap vengano memorizzati nella cache. Questo non è male se costruisco questa volta in es. in attesa che venga applicato un filtro ma non funziona bene tra una pennellata e l'altra.

Sono un consiglio? Sarei interessato a sapere come fanno alcune app esistenti.

Posso pensare a tutti i tipi di strani ibridi di cui sopra ma hanno tutti problemi evidenti. Tutto quello che posso pensare di fare è vivere con alcuni di questi problemi o compromettere l'app per rendere il problema più semplice (ad esempio, ridurre le dimensioni della dimensione massima della bitmap). Ho notato che diverse app hanno dimensioni di bitmap e limiti di strato al massimo piuttosto bassi.

+2

+1: è un piacere vedere domande così ben studiate (e scritte)! E benvenuti a StackOverflow, Radent :-) –

+0

Grazie, probabilmente puoi dire che questo problema mi sta facendo impazzire in questo momento. :-) – Radent

+0

Hai misurato il tempo necessario per memorizzare nella cache le bitmap? –

risposta

0

C'è una terza opzione che viene in mente: ogni azione viene eseguita sul proprio livello e Annulla elimina quel livello. Ha bisogno di un meccanismo di rendering veloce e di una rappresentazione intelligente di "livelli di azione" in cui non vengono memorizzati pixel "trasparenti" (non modificati).

Se si assume u livelli di annullamento, è possibile unire i livelli di azione più vecchio di u passi in secondo piano.

Si potrebbe anche avere un approccio ibrido. Se l'azione è "piccola", rappresentala come un livello. Se è grande, come un'azione registrata, deve essere riprodotto. Come dici tu, hai bisogno di un euristico per decidere tra i due casi. È possibile testare il rendering/salvare le prestazioni al primo avvio dell'applicazione dopo l'installazione e decidere alcuni valori dei parametri per l'euristica.

+0

Grazie. L'ho considerato, ma la memoria è un problema. In realtà il rendering dei livelli va bene, ma lo scenario peggiore è qualcuno che scarabocchia su tutta la tela in un'unica operazione e quindi è necessario memorizzare una bitmap 1000x1000 per quella azione. Poiché ogni bitmap sarà ~ 2Mb è la dimensione, esaurirai la memoria rapidamente. – Radent

+0

@Radent: Capisco. Immagino tu abbia lo stesso problema per ogni rappresentazione 'bitmap' della tua catena di annullamento. Potresti avere un approccio ibrido. Se l'azione è "piccola", rappresentala come un livello. Se è grande, come un'azione registrata, deve essere riprodotto. – Mau

+0

Sì, ho avuto pensieri simili. Vorresti davvero un euristico che fornisse una metrica per quanto fosse lenta la ripetizione di un'operazione (ad es.le pennellate sono veloci ma sfocando l'intera immagine è lenta) e quindi la usano per determinare quando memorizzare il livello nella cache. Ho ancora il problema di cosa fare quando ho bisogno di memorizzare un altro livello nella cache quando l'ultimo non ha ancora finito di salvare. – Radent

0

Per me, lo schema del momento sembra il migliore. Ciò significa che probabilmente proverei a pensare a modi per memorizzare in modo ottimale tali informazioni. Hai detto che può essere una bitmap 1000x1000 potenzialmente per un'azione: potresti, ad esempio, rappresentare la bitmap come bitmap a 1 bit con un campo colore separato memorizzato altrove? Ora invece di 2 MB hai 125KB da archiviare.

Un'altra cosa che probabilmente esaminerei è la creazione delle informazioni mentre l'utente sta eseguendo l'azione - mentre, mentre scrivono, è possibile scrivere quei bit nella struttura dei dati nel momento in cui avviene. E quando hanno finito, puoi commettere l'azione nella cronologia degli annullamenti.

Dovresti essere in grado di applicare una logica algoritmica a queste strutture di dati di annullamento per ridurre l'impatto della memoria nascondendo l'utilizzo della CPU durante i periodi di attesa dell'utente.

0

Mi piacerebbe un approccio ibrido. Oltre a ciò, organizza i tuoi dati in parti (ad es. Tessere) in modo che tu debba solo memorizzare gli stati delle parti modificate (usando un meccanismo di copia ritardata).

1

Checkpoint ogni bitmap con la mappatura della memoria La memoria flash sui dispositivi iOS è abbastanza veloce per supportare le operazioni di undo/redo su grandi bitmap. Usando la chiamata di sistema mmap, I memory mappava i bitmap AB24 a 1024x768 con facilità, risparmiando il mio programma dall'uso di preziose DRAM. Non so come vorreste astrarre i pattern di annullamento/ripetizione, ma il modo per evitare operazioni di copia con overhead elevato è di avere i puntatori per annullare e ripetere i bitmap scambiando ogni annullamento/ripetizione. Hai specificato più di un livello di annullamento, ma scommetto che puoi scappare con lo scambio di puntatori (sto soffrendo di qualche insonnia in questo momento e sto cercando di dimostrare che lo scambio dei puntatori è risultato eccessivo - era un bel pozzetto di psuedocode).

Inoltre, non è consigliabile contrassegnare le pagine di mmap come F_NOCACHE. E 'preferibile avere la cache iOS scrive al bitmap in DRAM perché:

  • E' più veloce se non a filo a lampeggiare
  • memoria flash è stato progettato per un numero fisso di scritture - non è bello masterizzare il flash degli utenti (penso che occorra da qualche parte nell'ordine di 5 milioni di scritture con il flash di qualità nei dispositivi iOS)
  • Credo che iOS sia abbastanza aggressivo nella gestione della memoria che cancellerà le tue scritture memorizzate nella cache e le svuoterà durante un crisi della memoria (non mi è mai importato abbastanza di controllare, però)

Grida a John Carmack per la soffiata che la memoria Flash di iDevice sia abbastanza veloce (usa F_NOCACHE, comunque, per ottenere prestazioni di lettura prevedibili).

Nota che ho dovuto rendere il file la dimensione della bitmap effettiva prima di chiamare mmap sul file fd. Non impazzire la mappatura della memoria 100 bitmap, però (scherzando - DO IT!) Voglio dire, quanti annullamenti possono eseguire un utente? Sono deboli e le loro dita si stancano dopo pochi secondi di schiacciamento dei tasti.

+1

Questo è stato chiesto nel luglio 2010 ?? !! StackOverflow AGGGG! – MrAnonymous

0

Quello che stai affrontando mi sembra una limitazione di risorse, quindi questo è qualcosa che riguarda la tua UI/Application Design. Non ci sono trucchi per ottenere più memoria, più veloce accesso al "disco" ecc.

Vorrei un approccio ibrido utilizzando memeton in un buffer e processando ogni filtro nel successivo slot preallocato libero. Questo ti darà un annullamento/ripristino rapido limitato senza danneggiamento dell'heap. Per i passi illimitati di annullamento/ripristino, si ha il registro dei comandi. Se hai davvero bisogno di 400 operazioni di annullamento, puoi anche salvare come ogni 10 operazioni un'istantanea con i comandi e comprimere 10 di quelle istantanee in un'istantanea più grande.

La cosa più importante è chiarire all'utente che sta operando ai limiti del dispositivo, come un segnale di avvertimento quando viene annullato l'annullamento rapido.

Inoltre potrebbe essere utile preparare il buffer di annullamento/ripristino veloce in background, così quando l'utente annulla 2 volte si prepara già il terzo annullamento. La maggior parte delle volte l'utente guarda l'immagine per alcuni ms (attorno al 200-400) prima di eseguire il successivo annullamento.

Inoltre è possibile implementare diverse strategie di annullamento/ripetizione per operazioni reversibili che utilizzano memoria minima. Un algoritmo di compressione senza perdita rapida (lzma, ecc.) In background può ridurre ulteriormente il consumo di memoria.

Ma poiché questo sembra essere un limite di risorse (IO rispetto alla memoria rispetto alla CPU) tutte le opzioni sono per conviverci e cercare di rendere l'esperienza dell'utente il migliore possibile. E questo a volte diventa un compito difficile.

Problemi correlati