Como inverter uma imagem para comportar o formato bmp

Olá.
Gostaria de saber como faço para inverter uma imagem.
Estou montando uma imagem, e quero transforma-la em bmp. Já tenho o header, só preciso inverter os dados da imagem, pois em bmp a imagem tem que ser invertida, para que seja impressa corretamente na impressora.
Consegui incrementar um algoritmo que me foi sugerido, mas acho que está faltando alguma coisa, talvez tenha que fazer algum cálculo com o bpp.
Com este algoritmo, consigo inverter verticalmente a imagem, mas não consigo inverter horizontalmente. E o resultado da imagem não fica muito bom.
Alguém tem alguma dica?

void ImageMirror( uchar * img, int width, int height, int lenght, int bpp )
{
uchar aux = 0;
uchar * a = img;
uchar * b = &img[ (lenght) - 1 ];;

for( ; a < b; a++, b-- )
{
    aux = *a;
    *a = *b; 
    *b = aux;
}

}

Não precisa inverter horizontalmente. Você está lendo byte a byte de trás pra frente o que é incorreto.

No formato BMP do Windows, a primeira linha no arquivo é a última linha na tela. Ou seja, só ler linha a linha mas em ordem inversa.

Meu algoritmo está fazendo isso.
Só que ele só consegue inverter verticalmente, não consigo horizontalmente.
Parece simples, mas não é. Manipulação de imagem é mais complexo do que parece.

Eu já implementei tanto leitura quanto gravação de BMP algumas vezes.

Como já falei: não tem que fazer inversão horizontal.

Isso foi um problema que você criou por estar gravando errado os dados. Você tem que manipular linha a linha:

para numeroDaLinha := ultimaLinha ate primeiraLinha:
  deslocamento := numeroDaLinha * numeroTotalDeColunas;
  gravaDadosDaLinha(deslocamento);

Tu não entendeste bem minha questão.
O sistema é uma vm. E quando a aplicação envia a imagem para ser impressa, a vm desmembra a imagem, remove o header, separa os dados da imagem. Depois, tem que remontar o cabeçalho e imprimir. Qualquer formato de imagem pode ser enviado. Entretanto, o dispositivo só aceita imagens no formato bmp. Como bem sabemos, o formato bitmap tem suas informações invertidas. O algoritmo do sistema os corrige invertendo os dados da imagem. Entretanto, o firmware do dispositivo quer um bmp autêntico, e não um que foi alterado. Então eu tenho que inverter a imagem para que ela seja impressa de forma correta.
Me foi apresentada essa solução logo abaixo, entretanto, não entendi que regra que foi aplicada para a inversão da imagem, não encontrei material que ensine.

void PrinterImage(WNMEnv *env, Var stack[])
{

    //OSL_Debug("TESTE IMPRESSORA - nmt4waba.c - PrinterImage");
    uint32 bitmapOffset;
    WObject objImg = stack[0].obj;
    unsigned int width = WOBJ_ImageWidth(env, objImg);
    unsigned int height = WOBJ_ImageHeight(env, objImg);
    uint16 bpp = WOBJ_ImageBPP(env, objImg);
    if (bpp>1)
        env->ThrowException(env, InputOutputException, "bpp deve ser 1");

    WObject objRGBArray = WOBJ_ImageRGB(env, objImg);
    uchar *rgb = (uchar *)WOBJ_arrayStart(env, objRGBArray);//recebendo dados da imagem
	
	//AQUI NÃO ENTENDI ESSA LÓGICA MALUCA, MAS FUNCIONA - NÃO ENCONTREI MATERIAL QUE EXPLIQUE TAL REGRA
    unsigned int bytesPerRow = (width >> 3) + (width & 0x7 ? 1 : 0);
	
    int rgbLen = WOBJ_arrayLen(env, objRGBArray);//tamanho dos dados da imagem
    struct BMPColorTable bmpFormat; //Estrutura bmp a ser montada
    memset(&bmpFormat, 0, sizeof(bmpFormat));
    bitmapOffset= sizeof(bmpFormat); //tamanho da estrutura bmp
    unsigned int fileSize= bitmapOffset+ rgbLen;//tamanho total da imagem

     
    //--------------------------------- BMP FILE FORMAT -----------------------------------
    //***** File Header ***********
    bmpFormat.header.signature = (0x4D << 8) | 0x42;    // 0-1   magic chars 'BM' uint16
    bmpFormat.header.fileSize = fileSize;               // 2-5   uint32 filesize (not reliable) tamanho total em bytes da imagem
    bmpFormat.header.filler1 = 0;                       // 6-9   uint32 0
    bmpFormat.header.bitmapOffset = bitmapOffset;       // 10-13 uint32 bitmapOffset é a posição onde iniciam os dados
    //******************************

    //***** Image Header ***********
    bmpFormat.header.infoSize = 40;                     // 14-17 uint32 info size - Tamanho de Image Header
    bmpFormat.header.width =  width;                    // 18-21 int32  width  tamanho maximo: 384
    bmpFormat.header.height = height;                   // 22-25 int32  height tamanho maximo: 128
    bmpFormat.header.nPlanes = 1;                       // 26-27 uint16 nplanes - sempre sera igual 1
    bmpFormat.header.bpp = bpp;                         // 28-29 uint16 bits per pixel - aqui o bpp deve ser 1, do contrario ocorrerá um exception
    bmpFormat.header.compression = 0;                   // 30-33 uint32 compression flag
    bmpFormat.header.imageSize =0;                      // 34-37 uint32 image size (compressed) in bytes - aqui fica zero se compressão for zero
    bmpFormat.header.biXPelsPerMeter = 0;               // 38-41 int32  biXPelsPerMeter - valores que servem para ajustar a imagem ao tamanho da tela do dispositivo
    bmpFormat.header.biYPelsPerMeter = 0;               // 32-45 int32  biYPelsPerMeter - valores que servem para ajustar a imagem ao tamanho da tela do dispositivo
    bmpFormat.header.colorsUsed = 0;                    // 46-49 uint32 colors used
    bmpFormat.header.importantColorCount = 0;           // 50-53 uint32 important color count
    //******************************

    //***** Color Table ************
    bmpFormat.color1= 0;                                // 54-57 uint32 color cor preta
    bmpFormat.color2= 0x00FFFFFF;                       // 58-61 uint32 color cor branca
    //*******************************
    //---------------------------------------------------------------------------------------

    uchar *bitmapStream = (uchar *)wabaVm->MemAlloc(fileSize);
    memset(bitmapStream,0,fileSize);
    memcpy(bitmapStream, &bmpFormat, sizeof(bmpFormat)); //copiando a estrutura bmp para montar a imagem

    //Inverte os dados da imagem e armazena-os, finalizando toda a estrutura da imagem
    int row;
    for (row = height - 1; row >= 0; row--)
		memcpy(&(bitmapStream[bitmapOffset + row * bytesPerRow]), &rgb[(height - row - 1) * bytesPerRow], bytesPerRow);


    //Imprime imagem
    PrintWithGoal(bitmapStream,fileSize,1);
    wabaVm->MemFree(bitmapStream);

}

Ta ali comentado…

Como eu já te falei que tinha que fazer, ele grava o bitmap de baixo pra cima linha por linha.

Claro que ta ali comentado, fui eu que descobri como funciona e comentei! :roll_eyes:
Continuas não entendo minha questão.
A questão é: Da onde vem a regra do bytesPerRow? Porque procurei em toda a internet e não encontrei tal regra. Sem esse ponto chave o algoritmo não funciona!
Dessa vez não vai ter como tu responder sem ser: ta ali comentado, pois não deixei comentado.

Como eu te disse, não é tão simples. O ponto chave é o bytesPerRow. Sem ele, qualquer tentativa de inversão de imagem é inútil!

Acho contraproducente esse tipo de postura já que você não sabe, esta pedindo ajuda, e ainda estou gastando tempo e tendo paciência tentando te ajudar.

Sobre o tamanho da linha, leia a documentação:

Também acho, só que foste tu que começaste, ao me tratar como se eu fosse uma retardada, explicando coisas que eu mesma comentei. Como se eu não tivesse feito toda uma pesquisa, como se não tivesse sido eu que estruturei o código todo e colocado todos os comentários para um melhor entendimento.
A única coisa que não fiz nesse código aí foi o trecho de linha que envolve inverter a imagem e os bytesPerRow, os quais varri toda a web e não encontrei referências. Lembrando que esse trecho de linha que inverte a imagem funciona apenas com imagens monocromáticas.

Obrigada.
Infelizmente não mostra o porquê daquela fórmula:

Apenas explica isso:

bmWidthBytes

The number of bytes in each scan line. This value must be divisible by 2, because the system assumes that the bit values of a bitmap form an array that is word aligned.

Acredito que provavelmente a pessoa que montou esse código, pegou a fórmula em algum google acadêmico pago.