0

I'm trying to read a standard 24-bit BMP file into a byte array so that I can send that byte array to libpng to be saved as a png. My code, which compiles:

#include <string>
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <Windows.h>
#include "png.h"

using namespace std;

namespace BMP2PNG {
long getFileSize(FILE *file)
    {
        long lCurPos, lEndPos;
        lCurPos = ftell(file);
        fseek(file, 0, 2);
        lEndPos = ftell(file);
        fseek(file, lCurPos, 0);
        return lEndPos;
    }


private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) 
         {
             std::string filenamePNG = "D:\\TEST.png";
             FILE *fp = fopen(filenamePNG.c_str(), "wb");

             png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);

             png_info *info_ptr = png_create_info_struct(png_ptr);

             png_init_io(png_ptr, fp);

             png_set_IHDR(png_ptr,info_ptr,1920,1080,16,PNG_COLOR_TYPE_RGB,PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_BASE,PNG_FILTER_TYPE_BASE);

             png_write_info(png_ptr,info_ptr);
             png_set_swap(png_ptr);

             const char *inputImage = "G:\\R-000.bmp";
             BYTE *fileBuf;
             BYTE *noHeaderBuf;
             FILE *inFile = NULL;

             inFile = fopen(inputImage, "rb");

             long fileSize = getFileSize(inFile);

             fileBuf = new BYTE[fileSize];
             noHeaderBuf = new BYTE[fileSize - 54];

             fread(fileBuf,fileSize,1,inFile);

             for(int i = 54; i < fileSize; i++) //gets rid of 54-byte bmp header
             {
                noHeaderBuf[i-54] = fileBuf[i];
             }

             fclose(inFile);

             png_write_rows(png_ptr, (png_bytep*)&noHeaderBuf, 1);

             png_write_end(png_ptr, NULL);

             fclose(fp);
         }
};

Unfortunately, when I click the button that runs the code, I get an error "Attempted to read or write protected memory...". I'm very new to C++, but I thought I was reading in the file correctly. Why does this happen and how do I fix it?

Also, my end goal is to read a BMP one pixel row at a time so I don't use much memory. If the BMP is 1920x1080, I just need to read 1920 x 3 bytes for each row. How would I go about reading a file into a byte array n bytes at a time?

Aeon2058
  • 527
  • 6
  • 22
  • 1
    There is no error checking - are the `fopen` calls successful? – suspectus Mar 31 '13 at 09:57
  • Instead of "wild numbers" in fseek(file, 0, 2), you should use fseek(file, 0, SEEK_END). Same goes for SEEK_SET and the third one SEKK_. Its more readable and not code is less messy. – Martin Perry Mar 31 '13 at 12:10
  • 1
    You're obviously using C++/CLI. I'm not sure if you're using WinForms or WPF. If you're new to C++, this is probably not the best place to start. You would be better off using C# and using the facilities of the .NET framework to convert your images. Take a look at http://stackoverflow.com/questions/1060442/png-to-bmp. I know you're converting the other way, but its just as easy. – Ferruccio Mar 31 '13 at 12:27
  • @suspectus- yes the fopen calls are successful, I just can't read in the file. @ ferruccio- I'm just using Visual C++ Express with a Windows Form App. I would absolutely use C# if I could, as I've got a lot more experience with that, but libpng is written for C and C++ only (unless you can tell me how to use the .lib and .h files in C# of course). I've tried the .NET BMPEncoder stuff but it doesn't save PNG files properly, AND I need to be able to do this one row at a time because of memory limits. – Aeon2058 Mar 31 '13 at 12:36
  • If you get error, have you tried to run code in debugger, to see where error occurs ? Your definition of error is little vague. – Martin Perry Apr 01 '13 at 08:51

3 Answers3

0

Your getFileSize() method is not actually returning the file size. You're basically moving to the correct position in the BMP header but instead of actually reading the next 4 bytes that represent the size, you're returning the position in the file (which will be always 2). Then in the caller function you don't have any error checking and you have code that assumes the file size is always greater than 54 (the allocations for the read buffers for example).

Also keep in mind that the file size field in the BMP header might not always be correct, you should also take into account the actual file size.

Ionut
  • 6,436
  • 1
  • 17
  • 17
  • getFileSize() is imho correct, he is not reading size from header, but get "real" file size on disk. – Martin Perry Mar 31 '13 at 12:07
  • Martin is correct. I'm not trying to read the file size from the BMP header itself- that's never reliable. – Aeon2058 Mar 31 '13 at 12:31
  • You're right, I completely misread the first `fseek()` call in that function. `getFileSize()` is indeed correct, I'm sorry about that. – Ionut Mar 31 '13 at 15:39
0

You are reading filee size of your *.bmp file, but "real" data can be larger. BMP can have compression (RLE). After that when you write decompressed PNG to that array, you can have overflow size of image, because you previsouly obtained size of compressed BMP file.

Martin Perry
  • 9,232
  • 8
  • 46
  • 114
  • No compression here- I would expect a uncompressed bmp to be exactly width*height*3 + 54 bytes in size, and these files are. – Aeon2058 Mar 31 '13 at 23:06
0

In function

png_set_IHDR(png_ptr,info_ptr,1920,1080,16,PNG_COLOR_TYPE_RGB,PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_BASE,PNG_FILTER_TYPE_BASE);

Why do you have bit depth set to 16 ? Shouldn´t it be 8, because each RGB channel from BMP is 8bit.

Also for PNG handling, I am using this library: http://lodev.org/lodepng/. It works fine.

Martin Perry
  • 9,232
  • 8
  • 46
  • 114