GDAL, like almost all image libraries, places the origin coordinate for an image in the top-left corner. GDAL just has an extra property of working in a geographic space, and keeps that convention. The first row (line) is at the top and successive rows (lines) are below it. In a North-up image (or in a coordinate system with X-right and Y-up and the image aligned to the grid), this means that successive rows are positioned at successively-less Y-values.
Pixel size (x: 5 ; y: -5)
Row 0: Y 20
Row 1: Y 15
Row 2: Y 10
It is very common to have a negative Y pixel-size, because it allows for a simple calculation of the geographic position of any pixel (i, j) in the image.
pix_x = origin_x + j * res_x // for example
pix_y = origin_y + i * res_y // for example
As to why the image flipped after passing it through
gdalwarp: The original image you supplied must have placed the geographic origin in the bottom-left corner, effectively mirroring the image across a horizontal axis. The georeferencing used, however, had the data positioned correctly.
Read more about the spatial model, particularly the geotransform in the GDAL docs