If you need to be able to perform a single Undo after a series of changes, saving the whole image is good enough. It can even be the best solution if changes are numerous. (A list of changed pixels is less compact as you need to store the coordinates as well as the old argb).
If you wand to be able to Undo every atomic change, saving every intermediate bitmap is probably overkill and accumulating the differences becomes more attractive. A one-size-fits-all solution can be to alternate full image saving (when most of the image is modified) and differences saving.
Most of the time, changes are irreversible. When you overwrite a pixel, you can't retrieve its previous value. For instance, if you apply a smoothing filter, you can't invert it to restore the exact original. That seems to imply that in case of large changes you need to keep a lot of information.
But there is a workaround trick: you can keep spaced restore points plus the list of individual transforms between them. Then, rather than working backward from the current state, you will work forward from the previous restore point, and re-apply the direct transform. This way, you trade storage for running-time (the transform list being extremely compact).
Example:
Original > Smooth > Fill a Rectangle > Invert
Successive Undo's are obtained by recomputing:
Original > Smooth > Fill a Rectangle
Original > Smooth
Original
While recomputing, you can cache all intermediate images so that all Undo's and Redo's between two restore points are fast.