2010-10-10 24 views
11

J'essaie d'écrire un éditeur bitmap pour un appareil mobile (c'est-à-dire une version limitée de Photoshop). Le document de l'utilisateur se compose de ~ 4 bitmaps d'environ 1000x500 chacun.Annuler/rétablir rapidement pour l'éditeur bitmap lorsque la mémoire est limitée?

Je veux un système d'annulation/restauration robuste et efficace aussi simple que possible. Je vise environ ~ 0.2s pour annuler ou refaire un montage. Je cherche des commentaires sur mon approche actuelle ou pour de nouvelles idées que je peux utiliser. Je pense que ce que j'ai est trop complexe, donc je suis prudent de procéder ainsi juste savoir que c'est sur le meilleur que je pourrais faire serait bien.

J'ai expérimenté des combinaisons d'utilisation du pattern Command et du pattern Memento pour mon système undo/redo. Quelques conclusions que je suis venu à ce jour sont:

  1. Je n'ai pas assez de mémoire et je ne peux pas écrire la mémoire sur le disque assez rapide pour utiliser un mémento pour soutenir une opération « unexecute » sur la commande précédente beaucoup de situations Si l'utilisateur exécute plusieurs traits de peinture très rapidement, je ne pourrai pas stocker de bitmaps représentant ce que l'utilisateur a peint sans que l'utilisateur attende qu'ils soient sauvegardés.

  2. Si je restaure le document dans son état initial et rejoue toutes les commandes à l'exception de la dernière pour implémenter l'annulation, cela est beaucoup trop lent même après un nombre modeste de commandes, par ex. rejouer 10 traits de peinture ou 5 coups de maculage prend ~ 1s qui est trop lent.

  3. Je peux contourner le point précédent en enregistrant périodiquement le document entier en arrière-plan sur le disque et en restaurant ce point de contrôle avant de lire les commandes. Pour annuler plus loin que le dernier point de contrôle, nous rechargerons le point de contrôle avant cela et rejouer les commandes.

Approche 2 avec 3 œuvres OK, sauf l'enregistrement du document entier devient plus lent et plus lent que d'autres couches sont ajoutées et il est déjà lent avec 4 bitmaps (~ 5 - 10 secondes). Attendent J'ai donc besoin de modifier 3 pour ne conserver que ce qui a changé depuis la dernière fois.

Étant donné que de nombreuses commandes fonctionnent sur une seule couche, il est logique de sauvegarder uniquement les couches qui ont été modifiées depuis le dernier point de contrôle. Par exemple, ma pile de commandes pourrait ressembler à ceci si j'ai 3 couches initiales où j'ai indiqué où les points de contrôle pourraient être sauvegardés.

(Checkpoint1: Save layer 1, 2 and 3.) 
Paint on layer 1 
Paint on layer 1 
(Checkpoint2: Save layer 1. Reuse saved layers 2 and 3 from Checkpoint1.) 
Paint on layer 2 
Paint on layer 2 
(Checkpoint3: Save layer 2. Reuse saved layers 1 and 3 from Checkpoint2.) 
Paint on layer 3 
Paint on layer 3 
Flip layer 3 horizontally. 
(Checkpoint4: Save layer 3. Reuse saved layers 1 and 2 from Checkpoint3.) 
Resize layer 1, 2 and 3. 
(Checkpoint5: Save layer 1, 2, 3.) 

Lors de l'édition, je garde la trace des couches qui ont été modifiées depuis le point de contrôle précédent. Lorsque je restaure un point de contrôle, je ne restaure que les calques qui ont été modifiés, par ex. Pour restaurer Checkpoint4 après avoir modifié les couches 2 et 3, je recharge les sauvegardes des couches 2 et 3 à partir du disque. Lorsque j'ajoute un point de contrôle, je ne sauvegarde que la couche qui a été modifiée jusqu'à présent. Je peux rendre tout ceci essentiellement automatique sauf qu'il doit y avoir des endroits dans mon interface où l'utilisateur est forcé d'attendre que des points de contrôle soient sauvegardés parce que je ne peux garder qu'une copie temporaire d'un calque en mémoire à la fois.

Qu'en pensez-vous? C'est beaucoup plus complexe que je ne le voudrais, mais je ne vois pas d'autre moyen. Y a-t-il d'autres modèles utiles que je peux utiliser pour me faciliter la vie?

Répondre

1

Ce qui suit peut être utile pour les couches et défaire des tampons en utilisant des images:

  • Gardez la dernière image comme une image
  • Les versions précédentes sont stockées sous la forme d'un XOR avec la prochaine version, puis (en supposant pas tout changé ou modifié de la même manière) compressé à l'aide d'un algorithme de compression simple (comme la longueur de course encodage)

Cela présente les avantages suivants

  • les versions précédentes pouvaient être facilement fusionnées (x ou ensemble).

Cela pourrait ne pas fonctionner bien avec:

  • ajustements de couleur (teinte, luminosité, etc.)
  • des transformations de coordonnées (cultures, morphing, etc.)
+0

Merci. Cela rendra l'enregistrement des points de contrôle légèrement plus rapide et plus efficace, mais rendra la restauration des points de contrôle légèrement plus lente, car j'ai besoin de charger et de combiner plusieurs points de contrôle pour restaurer un état antérieur. J'apprécierais quelques commentaires sur mon schéma global d'annulation/refaire si et comment il pourrait être rendu plus simple cependant. – memcom

1

Une approche est de garder certaines «images» en tant que cadres complets, et d'autres en tant que commande nécessaire pour créer une image à partir de la précédente. Vous faites allusion à cela dans votre # 2. Il peut être utile de conserver certaines images en mémoire.

Une astuce qui peut aider à équilibrer les performances avec l'espace/temps disponible pour contenir des trames complètes consiste à éliminer une partie des «anciennes» trames, de sorte qu'à tout moment, vous pourriez avoir des états d'annulation par exemple. Il y a 1, 2, 4, 8, 16, 32 et 64 opérations. Annuler une ou deux opérations nécessitera simplement de lire un cadre. Annuler trois nécessitera la lecture d'un point de contrôle et la répétition d'une opération. Annuler cinq nécessitera la lecture d'un point de contrôle et la répétition de trois opérations. Annuler trente-trois nécessitera la lecture d'un point de contrôle et la répétition de 31 opérations.

Pour améliorer le lissage des applications, il peut être utile, dans certains cas, de recalculer les images de point de contrôle en arrière-plan pendant une opération d'annulation. Par exemple, après avoir effacé dix-sept opérations, on pourrait commencer à travailler sur le calcul des états pour 48, 40 et 36 pas depuis le point de départ, de sorte que si l'on veut revenir plus loin, on aura déjà fait quelques l'oeuvre. Notez que l'on peut abandonner les images qui étaient en arrière 1, 2, 4, 8, ou 16 opérations, car on peut les recréer en rejouant les commandes à partir de l'état actuel.

+0

La partie la plus difficile à avoir des intervalles non-constants (f0 = 1, f1 = 2, f2 = 4, f3 = 8, etc.) entre les trames d'annulation maintient ces intervalles. Par exemple, pour vous assurer que f2 est toujours à quatre temps avant le nouveau trait, vous devez passer à f3 et rendre chaque coup entre les deux. La même logique s'applique pour f3, f4, etc. Ouais, vous devriez commencer par fmax (qui est un canevas vide), mais cela signifie que vous devez reconstruire la pile d'annulation entière à chaque fois. – jlukanta

+0

@jlukanta: Il n'est pas nécessaire d'avoir des points de contrôle qui sont ces distances exactes avant la trame actuelle si elles sont ces distances d'une trame dans le futur. Si on numérote toutes les images depuis le début de l'édition à 1, alors on devrait essayer d'avoir la dernière trame impaire, le dernier multiple impaire de 2, le dernier multiple impaire de quatre, etc. C'est un peu difficile à décrire le régime général, mais il finit par utiliser le temps de l'IGN – supercat