bitmap editor
undo functionality
fast undo
graphic design
software development

Fast undo facility for bitmap editor application

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A fast undo system for a bitmap editor should feel instantaneous even when the canvas is large. The main design decision is whether to store full image snapshots, per-operation pixel deltas, or a hybrid of both, because that choice controls memory usage, latency, and implementation complexity.

Why Full Snapshots Alone Do Not Scale Well

The most obvious undo design is to copy the entire bitmap after every operation. That is easy to implement, but it becomes expensive quickly.

If a canvas is 4000 x 4000 pixels with four bytes per pixel, one full snapshot is already large. Copying that buffer after every brush stroke or smudge action burns both memory and time.

For a bitmap editor, most edits only change a small region. That means full snapshots waste work.

The Usual Fast Design: Store Deltas Per Operation

A better design records only the region and pixels that changed during one command. For example, if the user paints a brush stroke across a small rectangle, store:

  • the bounding rectangle of the change
  • the old pixel data for that rectangle
  • enough command metadata to reapply or redo the change if needed

A simplified Python sketch looks like this:

python
1import numpy as np
2
3class PatchUndo:
4    def __init__(self, x, y, old_pixels, new_pixels):
5        self.x = x
6        self.y = y
7        self.old_pixels = old_pixels.copy()
8        self.new_pixels = new_pixels.copy()
9
10    def undo(self, canvas):
11        h, w, _ = self.old_pixels.shape
12        canvas[self.y:self.y + h, self.x:self.x + w] = self.old_pixels
13
14    def redo(self, canvas):
15        h, w, _ = self.new_pixels.shape
16        canvas[self.y:self.y + h, self.x:self.x + w] = self.new_pixels
17
18
19canvas = np.zeros((100, 100, 4), dtype=np.uint8)
20old = canvas[10:20, 10:20].copy()
21canvas[10:20, 10:20] = 255
22new = canvas[10:20, 10:20].copy()
23entry = PatchUndo(10, 10, old, new)
24entry.undo(canvas)
25entry.redo(canvas)

This pattern keeps undo cost proportional to the edited area rather than the full image size.

Use Commands to Group User Intent

Users think in actions, not in low-level pixel writes. One brush stroke should usually be one undo step even if it touched the canvas many times internally.

That is why bitmap editors often combine the command pattern with patch storage:

  • the command defines the logical user action
  • the patch stores the minimal pixel changes needed for undo and redo

This produces an undo stack that matches user expectations and remains efficient.

Add Periodic Keyframes

Pure delta history can become expensive to replay after a long editing session, especially if you need to reconstruct very old states or recover after a crash.

A common compromise is:

  • store deltas for most edits
  • store a full snapshot every N operations or after large filters

Those full snapshots act like keyframes. They limit how far the editor must replay history and reduce worst-case reconstruction time.

This hybrid design is often the practical sweet spot.

Filters and Large Operations Need Special Handling

A tiny brush stroke is easy. A blur, resize, or color-correction filter may touch the whole image.

For those operations, a full-image delta may be almost as large as a full snapshot anyway. In that case, storing one snapshot before the filter can be simpler than building a more complicated patch structure.

A fast undo facility is not one rigid data structure. It is a policy that chooses the cheapest safe representation for each class of edit.

Memory Management Rules

Undo systems must have limits. Otherwise, a long editing session consumes unbounded memory.

Common controls include:

  • maximum number of undo steps
  • maximum memory budget for undo history
  • adaptive eviction of the oldest entries
  • optional compression of stored patches

If the history uses a budget rather than only a step count, the editor handles both tiny edits and giant image-wide changes more gracefully.

Common Pitfalls

Storing only full snapshots is simple but often too slow or memory-heavy for real bitmap workloads.

Storing every mouse-move event as a separate undo entry creates an unusable history. Group edits by user intent.

Trying to force one storage strategy for every tool is another mistake. Small brush edits and full-image filters have different economics.

Finally, do not forget redo. An undo system that cannot reapply changes cleanly is only half designed.

Summary

  • fast bitmap undo usually relies on storing per-operation pixel deltas instead of full snapshots every time
  • grouping low-level edits into one logical command makes the history match user expectations
  • periodic full snapshots or keyframes keep long-session recovery practical
  • large filters may justify snapshot-based undo even when brush tools use patch-based undo
  • the best undo system is usually a hybrid that balances speed, memory, and implementation simplicity

Course illustration
Course illustration

All Rights Reserved.