That was BGR vs RGB issue, the Blue and Red are swapped.
That's all I know, sorry not much help.
Does anyone know what I need to do to get better results?
Don't manually read the pixel data but use a well-tested library like BGRABitmap. For learning purpose, it is okay to manually read/modify the pixel data but your code won't be cross platform, cross device. BGR or RGB is device and/or OS depended. If you use a library, they will make it uniform.
I ever wrote a demo showing how to read the raw pixel data. Maybe you can learn something from there:
https://forum.lazarus.freepascal.org/index.php/topic,37242.msg252828.html#msg252828Also don't forget to check @User137, @molly, @taazz comments, which added some valuable tips for it.
Even the code works correctly on Linux, I am still doubt it will work correctly on other devices or platforms.
By the way, I only recently discovered that in code like this the Y loop should always appear on the outside and I learned that on this forum. What a shock that was.
That is how the pixel data stored. Most (or maybe all) graphics libraries do like this. Why shock?