2017-09-28 4 views
1

Fond:
Je souhaite copier une image bmp (non compressée 24 RVB) d'un nom de fichier à un autre. J'utilise le compilateur mingw de TDM-GCC (version 4.9.2, 32 bits, SJLJ) fourni avec des blocs de code.Copie d'un bmp dans c

Problème:
Le programme fonctionne pour les images en noir et blanc et les images couleur simples, mais pas les images couleur compliquées. Veuillez revoir les images ci-jointes. Je n'avais pas assez de réputation pour poster les autres images donc j'ai essayé de poster les 2 plus pertinentes. Le programme est incapable de copier l'image de lenna. Quelle est la cause de ce comportement?

code:

#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 
#pragma pack(1) 

/* The following is to access the DIB information 
https://msdn.microsoft.com/en-us/library/cc230309.aspx 
https://msdn.microsoft.com/en-us/library/dd183374(v=vs.85).aspx 
https://msdn.microsoft.com/en-us/library/dd183376(v=vs.85).aspx */ 

typedef uint8_t BYTE; 
typedef uint32_t DWORD; 
typedef int32_t LONG; 
typedef uint16_t WORD; 

typedef struct 
{ 
    WORD bfType; 
    DWORD bfSize; 
    WORD bfReserved1; 
    WORD bfReserved2; 
    DWORD bfOffBits; 
}BITMAPFILEHEADER; 

typedef struct 
{ 
    DWORD biSize; 
    LONG biWidth; 
    LONG biHeight; 
    WORD biPlanes; 
    WORD biBitCount; 
    DWORD biCompression; 
    DWORD biSizeImage; 
    LONG biXPelsPerMeter; 
    LONG biYPelsPerMeter; 
    DWORD biClrUsed; 
    DWORD biClrImportant; 
}BITMAPINFOHEADER; 


typedef struct 
{ 
    BYTE rgbtBlue; 
    BYTE rgbtGreen; 
    BYTE rgbtRed; 
}RGBTRIPLE; 

int main(void) 
{ 
    char *infile = "testin.bmp"; 
    char *outfile = "testout.bmp"; 

    FILE *inptr = fopen(infile, "r"); 
    if (inptr == NULL) 
    { 
     fprintf(stderr, "Could not open %s.\n", infile); 
     return 2; 
    } 

    FILE *outptr = fopen(outfile, "w"); 
    if (outptr == NULL) 
    { 
     fclose(inptr); 
     fprintf(stderr, "Could not create %s.\n", outfile); 
     return 3; 
    } 

    BITMAPFILEHEADER bf; 
    fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr); 

    BITMAPINFOHEADER bi; 
    fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr); 

    fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, outptr); 

    fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, outptr); 

    int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4; 

    int i, j, k, biHeight; 

    for(i = 0, biHeight = abs(bi.biHeight); i < biHeight; i++) 
    { 
     for(j = 0; j < bi.biWidth; j++) 
     { 
     RGBTRIPLE triple; 

     fread(&triple, sizeof(RGBTRIPLE), 1, inptr); 

     fwrite(&triple, sizeof(RGBTRIPLE), 1, outptr); 
     } 
    } 

    fseek(inptr, padding, SEEK_CUR); 

    for(k = 0; k < padding; k++) 
    { 
     fputc(0x00, outptr); 
    } 

fclose(inptr); 

fclose(outptr); 

return 0; 

} 

image d'entrée:

input image

image de sortie:

output image

+0

Ce serait un problème dans MSC - à propos de gcc sur Windows Je ne suis pas sûr. Cependant, s'il vous plaît, essayez 'fopen (infile," rb ")' et 'fopen (outfile," wb ")' et signalez si cela change quelque chose. (Au mieux, cela résout le problème, au moins cela ne change rien.) – Scheff

+0

Il y a de nombreux problèmes avec ce code. Vous ne l'avez obtenu que par une coïncidence. Votre objectif consiste-t-il simplement à copier le bitmap? ou extraire des informations bitmap d'informations? Si vous voulez juste copier, il y a une solution beaucoup plus simple. –

Répondre

0

Je bidouillé un peu avec les données fournies, et je suis sur e ce qui est arrivé:

FILE *inptr = fopen(infile, "r"); 

et

FILE *outptr = fopen(outfile, "w"); 

ouvrir les fichiers en mode texte. Ceci est un comportement spécial que je connais de l'API C de Microsoft et il semble que cela s'applique à mingw TDM-GCC (que j'ai eu du mal à croire jusqu'à ce que les données sous-évaluées m'ont convaincu que mes soupçons étaient corrects).

Le fichier E/S dans l'API C de Microsoft entre les distinguishs mode texte et en mode binaire:

  • fopen(infile, "rt") et fopen(outfile, "wt") ouvrir les fichiers en mode texte.
  • fopen(infile, "rb") et fopen(outfile, "wb") ouvrir les fichiers en mode binaire.
  • fopen(infile, "r") et fopen(outfile, "w") par défaut en mode texte.

En mode texte la lecture de fichiers remplace toutes les fins de ligne Microsoft ("\r\n") par fins de ligne Unix ("\n"), ainsi que l'écriture fait le contraire ("\n" devient "\r\n").

Cela est raisonnable si le contenu est en texte brut mais il corrompt probablement la sortie du contenu binaire où un octet 0x0d est inséré chaque fois qu'un octet 0x0a (avec une signification quelconque) se produit dans le flux de données.

Pour le prouver,

  1. J'ai téléchargé vos fichiers d'exemples (malheureusement téléchargés au format PNG)
  2. converti les fichiers (retour) à 24 bits BMP (en utilisant GIMP)
  3. fait un hexagone -dump pour chacun:

    $ hexdump -C IkW6FbN.bmp >IkW6FbN.bmp.txt 
    
    $ hexdump -C jnxpTwE.bmp >jnxpTwE.bmp.txt 
    
  4. et enfin chargé IkW6FbN.bmp.txt et jnxpTwE.bmp.txt dans WinMerge pour comparaison.

Snapshot of WinMerge showing where wrong contents start in output file

Comme l'illustre instantané, l'entrée et le fichier de sortie ont des contenus identiques pour les premiers (14037) 0x36d5 octets. Ensuite, le fichier d'entrée contient "accidentellement" trois octets 0a 0a 0a où le fichier de sortie a à la place 0d 0a 0d 0a 0d 0a. Ainsi, les pixels originaux respectifs (et tous les suivants) sont corrompus.

(Comme vous pouvez déjà le deviner, 0a est la valeur hexadécimale du caractère de saut de ligne '\n', 0d l'un des retour chariot '\r'.)

btw. le fichier de sortie est probablement un peu plus long que le fichier d'entrée (en raison des octets CR insérés). Cela peut être ignoré par un visualiseur BMP car l'en-tête BMP indique exactement combien d'octets sont nécessaires pour l'image brute (et les octets supplémentaires sont simplement ignorés).

Comme vous déjà avez peut-être reconnu, vous devez changer les fopen() appels à

FILE *inptr = fopen(infile, "rb"); 

et

FILE *outptr = fopen(outfile, "wb"); 

pour résoudre votre problème.

Btw. les API C sur * x OS (par exemple Linux) n'ont pas une telle distinction de texte et de mode binaire. Au lieu de cela, le b est simplement ignoré (ce qui est utile pour écrire du code portable).

Pour en savoir plus: fopen, fopen_s on cppreference.com

+0

Nous vous remercions de votre réponse complète. Comme vous l'avez souligné, le problème a été résolu. – ebmadrio