why you have to call "changed" manually
Because:
bmp.Canvas is a memory canvas
its handle is a memory DC,
====> any direct changes through a memory DC have no visual effect.
When Graphics32 draws ImgView32.Bitmap onto bmp, it is copying its contents to bmp's memory representation. It uses The handle bmp.Canvas.handle that is provided by the system (Windows in my case) and LCL has no way of knowing of any changes without a call to "changed".
Only after a call to "changed", the memory representation gets copied to the visual one.
Maybe bmp.assign should have called "changed", unless this design maintains compatibility with Delphi?