Why does my algorithm to convert between index and x,y with bitmap buffers result in the image being flipped vertically?

Bitmap usually starts from bottom-left corner and proceeds to top-right corner. But not always.

There is a value biHeight in bitmap header file, if this value is negative, then bitmap is upside down, it starts from bottom-left. And if this value is positive then the bitmap starts from top-left.

If you have access to biHeight then simply flip its value to show the bitmap right side up.

To make the calculations easier, pick any valid X/Y point, then find the corresponding source_index in the buffer, as shown below. Copy that point in to your destination buffer.

Note that you need an additional loop to copy 4 bytes from source to destination (you don't have that in your code, so I am not sure how your code works at all)

for(let i = 0; i < bytes_per_pixel; i++)
    buffer_cropped[dst_index + i] = buffer[source_index + i];

The code below should work for 32-bit images (4 bytes per pixel). Note that 24-bit images will need padding.

if (height < 0) 
    height = -height;
let bytes_per_pixel = 4;
let cropped_x = 10;
let cropped_y = 10;
let cropped_width = width / 2;
let cropped_height = height / 2;

if (new_x < 0 || new_x >= new_width || 
    new_y < 0 || new_y >= new_height) { error... }
if (cropped_width < 1 || cropped_width > width || 
    cropped_height < 1 || cropped_height > height) { error... }

let dst_index = 0;
for(let y = cropped_y; y < cropped_y + cropped_height; y++)
{
    for(let x = cropped_x; x < cropped_x + cropped_width; x++)
    {
        //use for right-side up bitmap:
        //int source_index = (y * width + x) * bytes_per_pixel;
        ////

        //use for upside-down bitmap:
        let source_index = ((height - y - 1)* width + x) * bytes_per_pixel;
        ////

        for(let i = 0; i < bytes_per_pixel; i++)
            buffer_cropped[dst_index + i] = buffer[source_index + i];
        dst_index += bits_per_pixel;
    }
}