How do I create BMP files using c++? - c++

I'm trying to create a class that saves BMP image given the pixels.
This is my .h:
#ifndef _IMAGE_SAVER_
#define _IMAGE_SAVER_
#include <vector>
#include <fstream>
#include <sstream>
#include <string>
#include <bitset>
//#define _DEBUG_
#ifdef _DEBUG_
#include <iostream>
#endif
#include <stdint.h>
typedef struct BMFileHeader {
uint16_t _bfType = 19778;
uint32_t _bfSize;
uint16_t _bfReserved1 = 0;
uint16_t _bfReserved2 = 0;
uint32_t _bfOffBits = 54;
} BMFileHeader;
typedef struct BMInfoHeader {
uint32_t _biSize = 40;
uint32_t _biWidth;
uint32_t _biHeight;
uint16_t _biPlanes = 1;
uint16_t _biBitCount = 24;
uint32_t _biCompression = 0;
uint32_t _biSizeImage = 0;
uint32_t _biXPelsPerMeter = 3780;
uint32_t _biYPelsPerMeter = 3780;
uint32_t _biClrUser = 0;
uint32_t _biClrImportant = 0;
} BMInfoHeader;
typedef struct RGBQuad {
uint8_t _blue;
uint8_t _green;
uint8_t _red;
} RGBQuad;
class ImageSaver
{
public:
ImageSaver() = delete;
ImageSaver(const uint32_t& height, const uint32_t& width, const std::vector<RGBQuad>& pixels) : _RGBQuad_Vector(pixels) {
this->_Info_Header._biHeight = height;
this->_Info_Header._biWidth = width;
};
void saveImage(const std::string& fileName);
protected:
void setFileSize();
void createImageFile(const std::string& fileName);
private:
BMFileHeader _File_Header;
BMInfoHeader _Info_Header;
std::vector<RGBQuad> _RGBQuad_Vector;
};
#endif
this is my .cpp:
#include "ImageSaver.h"
void ImageSaver::saveImage(const std::string& fileName)
{
this->setFileSize();
this->createImageFile(fileName);
}
void ImageSaver::setFileSize()
{
uint32_t height = this->_Info_Header._biHeight;
uint32_t width = this->_Info_Header._biWidth;
this->_File_Header._bfSize = 3 * (height * width) + 54;
}
void ImageSaver::createImageFile(const std::string& fileName)
{
std::ofstream imageFile(fileName + ".bmp", std::ios::binary);
imageFile.write((char*)&this->_File_Header, 14);
imageFile.write((char*)&this->_Info_Header, 40);
size_t numberOfPixels = this->_Info_Header._biHeight * this->_Info_Header._biWidth;
for (int i = 0; i < numberOfPixels; i++) {
imageFile.write((char*)&(this->_RGBQuad_Vector[i]), 3);
}
imageFile.close();
}
and this is my main.cpp:
#include "ImageSaver.h"
#include <vector>
#include <iostream>
int main() {
std::vector<RGBQuad> pixels;
for (unsigned i = 0; i < 512 * 512; i++) {
RGBQuad pixel;
pixel._blue = 0;
pixel._green = 255;
pixel._red = 255;
pixels.push_back(pixel);
}
ImageSaver im(512, 512, pixels);
im.saveImage("scene1");
std::cout << "ESTOP0" << std::endl;
return 0;
}
Whenever I try to create an image file, it says that that image file is corrupt, even though it seems to me that I've been following BMP format properly. I've analyzed raw binary data and data seems (to me) to be correct. GIMP can open the picture, but it gives a black 512x512 picture which is not what I'm going for here.

Certainly you miss #pragma pack. Wrap your structures with #pragma pack(push,1)/#pragma pack(pop)
When structures represent memory layout and should have no padding, even if having unaligned data, pack structures.
It is very helpful to have static_assert on expected sizeof value for such cases.
Additionally, when you pack them, the RGBQuad will become only 3 bytes. If you really want triple, suggest renaming it to triple. Otherwise add dummy or alpha byte to complete it to quad.

Related

How to create a bitmap/image from Scan0?

I'm having trouble porting the following C# code to C++:
protected override void OnPaint(CefBrowser browser, CefPaintElementType type, CefRectangle[] dirtyRects
, System.IntPtr buffer, int width, int height)
{
if (isPainting == true)
return;
isPainting = true;
// Save the provided buffer (a bitmap image) as a PNG.
using (System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(width, height, width * 4, System.Drawing.Imaging.PixelFormat.Format32bppRgb, buffer))
{
bitmap.Save(#"LastOnPaint.png", System.Drawing.Imaging.ImageFormat.Png);
} // End Using bitmap
}
What it does:
Create an image from a WebSite/SVG as rendered by the latest version of Chromium embedded and save it as a file.
So this is the corresponding render-handler in C++:
void RenderHandler::OnPaint(
CefRefPtr<CefBrowser> browser,
CefRenderHandler::PaintElementType type,
const CefRenderHandler::RectList& dirtyRects,
const void* buffer, int width, int height
) {
// size_t len = sizeof(buffer) / sizeof(void*);
// printf("buffer length: %zu\n", len); // 1...
// Array size is probably: width*height * 4;
}
So I was looking into what C# does in the bitmap-constructor, which is the following:
public Bitmap(int width, int height, int stride, PixelFormat format, IntPtr scan0)
{
IntPtr bitmap = IntPtr.Zero;
int status = Gdip.GdipCreateBitmapFromScan0(width, height, stride, unchecked((int)format), new HandleRef(null, scan0), out bitmap);
Gdip.CheckStatus(status);
SetNativeImage(bitmap);
}
internal void SetNativeImage(IntPtr handle) {
if (handle == IntPtr.Zero)
throw new ArgumentException(SR.GetString(SR.NativeHandle0), "handle");
nativeImage = handle;
}
Which traces to
internal const string Gdiplus = "gdiplus.dll";
[DllImport(ExternDll.Gdiplus, SetLastError=true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Unicode)] // 3 = Unicode
[ResourceExposure(ResourceScope.Machine)]
internal static extern int GdipCreateBitmapFromScan0(int width, int height, int stride, int format, HandleRef scan0, out IntPtr bitmap);
So I thought I could just call GdipCreateBitmapFromScan0 in gdibitmapflat and be almost finished
GpStatus WINGDIPAPI GdipCreateBitmapFromScan0(INT width
, INT height, INT stride, PixelFormat format
, BYTE* scan0, GpBitmap** bitmap)
So I gathered the necessary header-files for GDI, which was a horrible experience
#ifndef __BITMAPHELPER_H__
#define __BITMAPHELPER_H__
// #define WIN32_LEAN_AND_MEAN
#pragma warning(disable:4458)
#include <Windows.h>
#include <ObjIdl.h>
#include <minmax.h>
#include <gdiplus.h>
#include <wingdi.h>
#include <gdiplusbitmap.h>
#include <gdiplusflat.h>
using namespace Gdiplus;
#pragma comment (lib,"gdiplus.lib")
#pragma warning(default:4458)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdint>
#include <cstdbool>
#include <algorithm>
#include <memory>
And thought this would about do it
#include "BitmapHelper.h"
static void Test()
{
GpBitmap *bitmap = NULL;
GdipCreateBitmapFromScan0(100, 100, 0, PixelFormat32bppARGB, NULL, &bitmap); // create a bitmap object with specified width/height/color
// GpGraphics *graph;
// Image * syntaxTest = NULL;
//syntaxTest->FromFile(TEXT("d:\\abc.jpg"), true); // create an image object
// Bitmap::FromBITMAPINFO
// GpImage *image = NULL;
// Gdiplus::Image()
Bitmap *bmp = NULL;
// GdipLoadImageFromFile(TEXT("d:\\abc.jpg"), &image); // create an image object
// GdipGetImageGraphicsContext(bitmap, &graph); // create a graphic object via bitmap object
// GdipDrawImageI(graph, image, 100, 100); // draw image to this graphic object, it can be done
}
However, it turns out the compiler doesn't know GdipCreateBitmapFromScan0, although it's definitely inside #include <gdiplusflat.h>...
How to create a bitmap/image from Scan0 ?
Note:
While I am at it, I don't want to resort to C++.NET, and ideally not to the WinAPI either; because i'd like it to work on Linux too. And not to a monstrous dependency like SDL either.
So far, it looks like my possible alternatives are using this code:
https://codereview.stackexchange.com/questions/196084/read-and-write-bmp-file-in-c
which means I have to create the bitmap header myselfs.
Or I could use some code from ImageIO.
I can't quite belive that creating a simple bitmap on even a single operating-system is that hard...
Is there really no better (and portable) way to create a simple bitmap from a trivial array of pixel colors ?
And why does the compiler not find GdipCreateBitmapFromScan0 ?
If I had used LoadLibrary and GetProcAddress to invoke it instead of f*ing windows header files, I'd be about finished by now...
And why does #include <gdiplus.h> not include its own dependencies ?
Your looking at the internals of .NET have led you toward using a function that's not part of the documented, public interface of GDI+. It looks to me like that's the real cause of most of your problems.
What I think you probably want to do is start by creating a GdiPlus::Bitmap object from your pixels. It has a constructor that looks like it'll directly accept your data.
Once you've created the Bitmap object, you call its Save member function. Bitmap is publicly derived from Image, so you're basically dealing with the normal Image::Save to generate a PNG.
If you want to eliminate the dependency on Windows code, you might consider using (for one obvious possibility) libpng instead. This gives you quite a lot more control over the process, at the expense of being quite a bit more work to use (depending on what you want to do, probably on the order of a half dozen to a dozen lines of code rather than one or two).
So, after having done this in both GDI+ and raw C, I can safely say that it's actually faster, and not to mention considerably less problematic and less google-intensive just doing the image-handling without GDI/GDI+. Whoever implemented GDI+ has a major brain damage.
Since I haven't yet handled transparency properly, and not yet incorporated lodepng, I've added GDI+ as an optional extra option, for the time being.
// A program to read, write, and crop BMP image files.
#include "Bmp.h"
// Make a copy of a string on the heap.
// - Postcondition: the caller is responsible to free
// the memory for the string.
char *_string_duplicate(const char *string)
{
char *copy = (char*)malloc(sizeof(*copy) * (strlen(string) + 1));
if (copy == NULL)
{
// return "Not enough memory for error message";
const char* error_message = "Not enough memory for error message";
size_t len = strlen(error_message);
char* error = (char*)malloc(len * sizeof(char) + 1);
strcpy(error, error_message);
return error;
}
strcpy(copy, string);
return copy;
}
// Check condition and set error message.
bool _check(bool condition, char **error, const char *error_message)
{
bool is_valid = true;
if (!condition)
{
is_valid = false;
if (*error == NULL) // to avoid memory leaks
{
*error = _string_duplicate(error_message);
}
}
return is_valid;
}
// Write an image to an already open file.
// - Postcondition: it is the caller's responsibility to free the memory
// for the error message.
// - Return: true if and only if the operation succeeded.
bool write_bmp(FILE *fp, BMPImage *image, char **error)
{
// Write header
rewind(fp);
size_t num_read = fwrite(&image->header, sizeof(image->header), 1, fp);
if (!_check(num_read == 1, error, "Cannot write image"))
{
return false;
}
// Write image data
num_read = fwrite(image->data, image->header.image_size_bytes, 1, fp);
if (!_check(num_read == 1, error, "Cannot write image"))
{
return false;
}
return true;
}
// Free all memory referred to by the given BMPImage.
void free_bmp(BMPImage *image)
{
free(image->data);
free(image);
}
// Open file. In case of error, print message and exit.
FILE *_open_file(const char *filename, const char *mode)
{
FILE *fp = fopen(filename, mode);
if (fp == NULL)
{
fprintf(stderr, "Could not open file %s\n", filename);
exit(EXIT_FAILURE);
}
return fp;
}
// Close file and release memory.void _clean_up(FILE *fp, BMPImage *image, char **error)
void _clean_up(FILE *fp, BMPImage *image, char **error)
{
if (fp != NULL)
{
fclose(fp);
}
free_bmp(image);
free(*error);
}
// Print error message and clean up resources.
void _handle_error(char **error, FILE *fp, BMPImage *image)
{
fprintf(stderr, "ERROR: %s\n", *error);
_clean_up(fp, image, error);
exit(EXIT_FAILURE);
}
void write_image(const char *filename, BMPImage *image, char **error)
{
FILE *output_ptr = _open_file(filename, "wb");
if (!write_bmp(output_ptr, image, error))
{
_handle_error(error, output_ptr, image);
}
fflush(output_ptr);
fclose(output_ptr);
_clean_up(output_ptr, image, error);
}
// Return the size of an image row in bytes.
// - Precondition: the header must have the width of the image in pixels.
uint32_t computeImageSize(BMPHeader *bmp_header)
{
uint32_t bytes_per_pixel = bmp_header->bits_per_pixel / BITS_PER_BYTE;
uint32_t bytes_per_row_without_padding = bmp_header->width_px * bytes_per_pixel;
uint32_t padding = (4 - (bmp_header->width_px * bytes_per_pixel) % 4) % 4;
uint32_t row_size_bytes = bytes_per_row_without_padding + padding;
return row_size_bytes * bmp_header->height_px;
}
#ifdef USE_GDI
#pragma warning(disable:4189)
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;
Gdiplus::GetImageEncodersSize(&num, &size);
if (size == 0)
return -1; // Failure
pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL)
return -1; // Failure
Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
} // if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
} // Next j
free(pImageCodecInfo);
return -1; // Failure
}
// https://github.com/lvandeve/lodepng
static bool notInitialized = true;
void WriteBitmapToFile(const char *filename, int width, int height, const void* buffer)
{
// HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (notInitialized)
{
// https://learn.microsoft.com/en-us/windows/desktop/api/gdiplusinit/nf-gdiplusinit-gdiplusstartup
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
Gdiplus::Status isOk = Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
if (isOk != Gdiplus::Status::Ok)
{
printf("Failed on GdiplusStartup\n");
}
notInitialized = false;
// defer
// GdiplusShutdown(gdiplusToken);
} // End if (notInitialized)
// https://learn.microsoft.com/en-us/windows/desktop/gdiplus/-gdiplus-constant-image-pixel-format-constants
Gdiplus::Bitmap* myBitmap = new Gdiplus::Bitmap(width, height, width*4, PixelFormat32bppARGB, (BYTE*)buffer);
// myBitmap->RotateFlip(Gdiplus::Rotate180FlipY);
CLSID pngClsid;
// int result = GetEncoderClsid(L"image/tiff", &tiffClsid);
int result = GetEncoderClsid(L"image/png", &pngClsid);
printf("End GetEncoderClsid:\n");
if (result == -1)
printf("Error: GetEncoderClsid\n");
// throw std::runtime_error("Bitmap::Save");
// if (Ok != myBitmap->Save(L"D\foobartest.png", &pngClsid)) printf("Error: Bitmap::Save");
// WTF ? I guess a standard C/C++-stream would have been too simple ?
IStream* oStream = nullptr;
if (CreateStreamOnHGlobal(NULL, TRUE, (LPSTREAM*)&oStream) != S_OK)
printf("Error on creating an empty IStream\n");
Gdiplus::EncoderParameters encoderParameters;
encoderParameters.Count = 1;
encoderParameters.Parameter[0].Guid = Gdiplus::EncoderQuality;
encoderParameters.Parameter[0].Type = Gdiplus::EncoderParameterValueTypeLong;
encoderParameters.Parameter[0].NumberOfValues = 1;
ULONG quality = 100;
encoderParameters.Parameter[0].Value = &quality;
// https://learn.microsoft.com/en-us/windows/desktop/api/gdiplusheaders/nf-gdiplusheaders-image-save(inistream_inconstclsid_inconstencoderparameters)
if (Gdiplus::Status::Ok != myBitmap->Save(oStream, &pngClsid, &encoderParameters))
printf("Error: Bitmap::Save\n");
// throw std::runtime_error("Bitmap::Save");
ULARGE_INTEGER ulnSize;
LARGE_INTEGER lnOffset;
lnOffset.QuadPart = 0;
oStream->Seek(lnOffset, STREAM_SEEK_END, &ulnSize);
oStream->Seek(lnOffset, STREAM_SEEK_SET, NULL);
uint8_t *pBuff = new uint8_t[(unsigned int)ulnSize.QuadPart];
ULONG ulBytesRead;
oStream->Read(pBuff, (ULONG)ulnSize.QuadPart, &ulBytesRead);
FILE *output_ptr = _open_file(filename, "wb");
fwrite((void*)pBuff, sizeof(uint8_t), (unsigned int)ulnSize.QuadPart, output_ptr);
fflush(output_ptr);
fclose(output_ptr);
oStream->Release();
delete pBuff;
delete myBitmap;
// https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp
// std::string rotated_string = base64_encode((const unsigned char*)pBuff, ulnSize.QuadPart);
}
#pragma warning(default:4189)
#else
// TODO: PNG-Encoder
// https://github.com/lvandeve/lodepng
// https://lodev.org/lodepng/
BMPImage * CreateBitmapFromScan0(int32_t w, int32_t h, uint8_t* scan0)
{
BMPImage *new_image = (BMPImage *)malloc(sizeof(*new_image));
BMPHeader *header = (BMPHeader *)malloc(sizeof(*header));
new_image->header = *header;
new_image->header.type = MAGIC_VALUE;
new_image->header.bits_per_pixel = BITS_PER_PIXEL;
new_image->header.width_px = w;
new_image->header.height_px = h;
new_image->header.image_size_bytes = computeImageSize(&new_image->header);
new_image->header.size = BMP_HEADER_SIZE + new_image->header.image_size_bytes;
new_image->header.dib_header_size = DIB_HEADER_SIZE;
new_image->header.offset = (uint32_t) sizeof(BMPHeader);
new_image->header.num_planes = 1;
new_image->header.compression = 0;
new_image->header.reserved1 = 0;
new_image->header.reserved2 = 0;
new_image->header.num_colors = 0;
new_image->header.important_colors = 0;
new_image->header.x_resolution_ppm = 3780; // image->header.x_resolution_ppm;
new_image->header.y_resolution_ppm = 3780; // image->header.y_resolution_ppm;
new_image->data = (uint8_t*)malloc(sizeof(*new_image->data) * new_image->header.image_size_bytes);
memcpy(new_image->data, scan0, new_image->header.image_size_bytes);
return new_image;
}
void WriteBitmapToFile(const char *filename, int width, int height, const void* buffer)
{
BMPImage * image = CreateBitmapFromScan0((int32_t)width, (int32_t)height, (uint8_t*)buffer);
char *error = NULL;
write_image(filename, image, &error);
}
#endif
Header:
#ifndef BITMAPLION_BITMAPINFORMATION_H
#define BITMAPLION_BITMAPINFORMATION_H
#ifdef __cplusplus
// #include <iostream>
// #include <fstream>
#include <cstdio>
#include <cstdlib>
#include <cstdint>
#include <cstring>
#else
#include <stdio.h>
#include <stdlib.h> // for malloc
#include <stdint.h>
#include <stdbool.h>
#include <string.h> // for strlen, strcopy
#endif
#ifdef __linux__
//linux specific code goes here
#elif _WIN32
// windows specific code goes here
#pragma warning(disable:4458)
#include <Windows.h>
#include <ObjIdl.h>
#include <minmax.h>
#include <gdiplus.h>
// #include <gdiplusheaders.h>
// #include <wingdi.h>
// #include <gdiplusbitmap.h>
// #include <gdiplusflat.h>
// #include <Gdipluspixelformats.h>
#pragma comment (lib,"gdiplus.lib")
// using namespace Gdiplus;
#pragma warning(default:4458)
#else
#endif
#define BMP_HEADER_SIZE 54
#define DIB_HEADER_SIZE 40
// Correct values for the header
#define MAGIC_VALUE 0x4D42
#define NUM_PLANE 1
#define COMPRESSION 0
#define NUM_COLORS 0
#define IMPORTANT_COLORS 0
#define BITS_PER_BYTE 8
// #define BITS_PER_PIXEL 24
#define BITS_PER_PIXEL 32
#ifdef _MSC_VER
#pragma pack(push) // save the original data alignment
#pragma pack(1) // Set data alignment to 1 byte boundary
#endif
typedef struct
#ifndef _MSC_VER
__attribute__((packed))
#endif
{
uint16_t type; // Magic identifier: 0x4d42
uint32_t size; // File size in bytes
uint16_t reserved1; // Not used
uint16_t reserved2; // Not used
uint32_t offset; // Offset to image data in bytes from beginning of file
uint32_t dib_header_size; // DIB Header size in bytes
int32_t width_px; // Width of the image
int32_t height_px; // Height of image
uint16_t num_planes; // Number of color planes
uint16_t bits_per_pixel; // Bits per pixel
uint32_t compression; // Compression type
uint32_t image_size_bytes; // Image size in bytes
int32_t x_resolution_ppm; // Pixels per meter
int32_t y_resolution_ppm; // Pixels per meter
uint32_t num_colors; // Number of colors
uint32_t important_colors; // Important colors
} BMPHeader;
#ifdef _MSC_VER
#pragma pack(pop) // restore the previous pack setting
#endif
typedef struct {
BMPHeader header;
// unsigned char* data;
// It is more informative and will force a necessary compiler error
// on a rare machine with 16-bit char.
uint8_t* data;
} BMPImage;
// #define USE_GDI true
#ifndef USE_GDI
BMPImage * CreateBitmapFromScan0(int32_t w, int32_t h, uint8_t* scan0);
#endif
void WriteBitmapToFile(const char *filename, int width, int height, const void* buffer);
#endif //BITMAPLION_BITMAPINFORMATION_H

How do I properly use the OpenH264 Usage Code Example for Encoding?

I have an image and want to encode it with OpenH264.
So far this is the code I derived from their wiki:
#include <fstream>
#include <iterator>
#include <iostream>
#include <codec_api.h> //standard api for openh264
//additional libaries used by sample code
#include <codec_app_def.h>
#include <codec_def.h>
#include <codec_ver.h>
#include <assert.h>
#include <vector>
#include <cstring>
int main()
{
//parameter values
int width = 1920;
int height = 1080;
int framerate = 60;
int bitrate = 5000000;
int total_num = 500; //what does this value do?
//end parameter values
//Read in the File from bmp
std::vector<char> buf; //to store the image information
std::basic_ifstream<char> file("/home/megamol/Git/h264_sample/build/test.bmp", std::ios::binary); //opens bitstream to source
buf = std::vector<char>((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>()); // reads in data to the vector
std::cout << "sizeof buf: " << buf.size() << std::endl;
//Step 1: set up Encoder
ISVCEncoder* encoder_; //declaration of encoder pointer
int rv = WelsCreateSVCEncoder (&encoder_);
//Step 2: initialize with basic parameter
SEncParamBase param;
memset(&param, 0, sizeof (SEncParamBase));
param.iUsageType = EUsageType::SCREEN_CONTENT_REAL_TIME;
param.fMaxFrameRate = framerate;
param.iPicWidth = width;
param.iPicHeight = height;
param.iTargetBitrate = bitrate; //default value of example
encoder_->Initialize(&param);
//Step 3: set video format
int videoFormat = videoFormatI420;
encoder_->SetOption (ENCODER_OPTION_DATAFORMAT, &videoFormat);
//Step 4: encocode and store output bitstream
int frameSize = width * height * 3 / 2;
buf.resize(frameSize);
SFrameBSInfo info;
std::vector<char> compressedData;
memset (&info, 0, sizeof (SFrameBSInfo));
SSourcePicture pic;
memset (&pic, 0, sizeof (SSourcePicture));
pic.iPicWidth = width;
pic.iPicHeight = height;
pic.iColorFormat = videoFormatI420;
pic.iStride[0] = pic.iPicWidth;
pic.iStride[1] = pic.iStride[2] = pic.iPicWidth >> 1;
pic.pData[0] = reinterpret_cast<unsigned char*>(&buf[0]);
pic.pData[1] = pic.pData[0] + width * height;
pic.pData[2] = pic.pData[1] + (width * height >> 2);
//encodes the frame
rv = encoder_->EncodeFrame (&pic, &info); // encodes the Frame
//encoding done encoded Frame should be stored in &info
//begin decoding block
ISVCDecoder *pSvcDecoder;
unsigned char *pBuf= &info;
return 0;
}
I'm not entirely sure whether this is the correct usage of OpenH264 but I'm also not sure how to test it properly.
Now the code example is kind of poorly documented.
What is BufferedData buf; for example? I get that that's supposed to be the input but what is that type? Like how do I load my test.bmp as BufferedData? I don't think that I'm doing that correctly yet.
Another thing I'm pretty confused about is how do I access the output after the encoding? In the example it just says //output bitstream and nothing about saving this output anywhere. I thought the output was info like it says in the codec_api.h header file:
/**
* #brief Encode one frame
* #param kpSrcPic the pointer to the source luminance plane
* chrominance data:
* CbData = kpSrc + m_iMaxPicWidth * m_iMaxPicHeight;
* CrData = CbData + (m_iMaxPicWidth * m_iMaxPicHeight)/4;
* the application calling this interface needs to ensure the data validation between the location
* #param pBsInfo output bit stream
* #return 0 - success; otherwise -failed;
*/
virtual int EXTAPI EncodeFrame (const SSourcePicture* kpSrcPic, SFrameBSInfo* pBsInfo) = 0;
But apparently it only saves informations about the output. I'm just really confused about all of this.
Based on https://github.com/cisco/openh264/blob/master/codec/console/enc/src/welsenc.cpp
#include <codec_api.h>
#include <cassert>
#include <cstring>
#include <vector>
#include <fstream>
#include <iostream>
//Tested with OpenCV 3.3
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
using namespace std;
using namespace cv;
int main()
{
ISVCEncoder *encoder_ = nullptr;
int rv = WelsCreateSVCEncoder (&encoder_);
assert (0==rv);
assert (encoder_ != nullptr);
int width = 640;
int height = 480;
int total_num = 100;
SEncParamBase param;
memset (&param, 0, sizeof (SEncParamBase));
param.iUsageType = CAMERA_VIDEO_REAL_TIME;
param.fMaxFrameRate = 30;
param.iPicWidth = width;
param.iPicHeight = height;
param.iTargetBitrate = 5000000;
encoder_->Initialize (&param);
Mat image = imread("test.jpg", IMREAD_COLOR );
Mat imageResized, imageYuv, imageYuvMini;
resize(image, imageResized, Size(width, height));
Mat imageYuvCh[3], imageYuvMiniCh[3];
cvtColor(imageResized, imageYuv, cv::COLOR_BGR2YUV);
split(imageYuv, imageYuvCh);
resize(imageYuv, imageYuvMini, Size(width/2, height/2));
split(imageYuvMini, imageYuvMiniCh);
SFrameBSInfo info;
memset (&info, 0, sizeof (SFrameBSInfo));
SSourcePicture pic;
memset (&pic, 0, sizeof (SSourcePicture));
pic.iPicWidth = width;
pic.iPicHeight = height;
pic.iColorFormat = videoFormatI420;
pic.iStride[0] = imageYuvCh[0].step;
pic.iStride[1] = imageYuvMiniCh[1].step;
pic.iStride[2] = imageYuvMiniCh[2].step;
pic.pData[0] = imageYuvCh[0].data;
pic.pData[1] = imageYuvMiniCh[1].data;
pic.pData[2] = imageYuvMiniCh[2].data;
ofstream outFi;
outFi.open ("test.264", ios::out | ios::binary);
for(int num = 0; num<total_num; num++)
{
//prepare input data
rv = encoder_->EncodeFrame (&pic, &info);
assert (rv == cmResultSuccess);
if (info.eFrameType != videoFrameTypeSkip /*&& cbk != nullptr*/)
{
//output bitstream
for (int iLayer=0; iLayer < info.iLayerNum; iLayer++)
{
SLayerBSInfo* pLayerBsInfo = &info.sLayerInfo[iLayer];
int iLayerSize = 0;
int iNalIdx = pLayerBsInfo->iNalCount - 1;
do {
iLayerSize += pLayerBsInfo->pNalLengthInByte[iNalIdx];
--iNalIdx;
} while (iNalIdx >= 0);
unsigned char *outBuf = pLayerBsInfo->pBsBuf;
outFi.write((char *)outBuf, iLayerSize);
}
}
}
if (encoder_) {
encoder_->Uninitialize();
WelsDestroySVCEncoder (encoder_);
}
outFi.close();
}

Quake 3 BSP Renderer Drawing Random Vertices

Recently I've been writing a game engine in OpenGL and have a question. When I generate my BSP file (Quake 3), it generates very odd vertices!! I've checked the vertex vector, heres a screenshot of what it contains (it repeats itself)
I've checked glGetError, and it returns nothing (zero)! I'm really stumped here, and I have no idea what to do.
Here is my code:
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/vec3.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <vector>
#include "map.h"
#include <fstream>
#include <memory>
#include "game_manager.h"
std::vector<BSPVerts> vertexes;
bool KikoBSP::load_map(std::string file_name)
{
this->file.open(file_name.c_str(), std::ios::in | std::ios::binary);
if (this->file.is_open())
{
this->file.read(reinterpret_cast<char*>(&this->header), sizeof(this->header));
BSPEntities* ents = new BSPEntities;
ents->ents_array = new char[this->header.lumps[BSPLUMPS::ENTITIES].length];
this->num_textures = this->header.lumps[BSPLUMPS::TEXTURES].length / sizeof(BSPTexture);
this->num_planes = this->header.lumps[BSPLUMPS::PLANES].length / sizeof(BSPPlane);
this->num_textures = this->header.lumps[BSPLUMPS::TEXTURES].length / sizeof(BSPTexture);
this->num_nodes = this->header.lumps[BSPLUMPS::NODES].length / sizeof(BSPNode);
this->num_leafs = this->header.lumps[BSPLUMPS::LEAFS].length / sizeof(BSPLeaf);
this->num_leaf_faces = this->header.lumps[BSPLUMPS::LEAF_FACES].length / sizeof(BSPLeafFace);
this->num_leaf_brushes = this->header.lumps[BSPLUMPS::LEAF_BRUSHES].length / sizeof(BSPLeafBrush);
this->num_models = this->header.lumps[BSPLUMPS::MODELS].length / sizeof(BSPModel);
this->num_brushes = this->header.lumps[BSPLUMPS::BRUSHES].length / sizeof(BSPBrush);
this->num_brush_sides = this->header.lumps[BSPLUMPS::BRUSHSIDES].length / sizeof(BSPBrushSides);
this->num_vertexes = this->header.lumps[BSPLUMPS::VERTEXES].length / sizeof(BSPVerts);
this->num_meshverts = this->header.lumps[BSPLUMPS::MESHVERTS].length / sizeof(BSPMeshVerts);
this->num_effects = this->header.lumps[BSPLUMPS::EFFECTS].length / sizeof(BSPEffects);
this->num_faces = this->header.lumps[BSPLUMPS::FACES].length / sizeof(BSPFaces);
for (int32_t x = 0; x < this->num_vertexes; x++)
{
vertexes.push_back(*reinterpret_cast<BSPVerts*>(&x));
}
for (int32_t x = 0; x < this->num_textures; x++)
{
this->textures.push_back(*reinterpret_cast<BSPTexture*>(&x));
}
for (int32_t x = 0; x < this->num_meshverts; x++)
{
this->mesh_verts.push_back(*reinterpret_cast<BSPMeshVerts*>(&x));
}
for (int32_t x = 0; x < this->num_planes; x++)
{
this->planes.push_back(*reinterpret_cast<BSPPlane*>(&x));
}
for (int32_t x = 0; x < this->num_nodes; x++)
{
this->nodes.push_back(*reinterpret_cast<BSPNode*>(&x));
}
for (int32_t x = 0; x < this->num_leafs; x++)
{
this->leaf.push_back(*reinterpret_cast<BSPLeaf*>(&x));
}
for (int32_t x = 0; x < this->num_faces; x++)
{
this->faces.push_back(*reinterpret_cast<BSPFaces*>(&x));
}
for (int32_t x = 0; x < this->num_leaf_faces; x++)
{
this->leaf_faces.push_back(*reinterpret_cast<BSPLeafFace*>(&x));
}
for (int32_t x = 0; x < this->num_leaf_brushes; x++)
{
this->leaf_brush.push_back(*reinterpret_cast<BSPLeafBrush*>(&x));
}
for (int32_t x = 0; x < this->num_models; x++)
{
this->model.push_back(*reinterpret_cast<BSPModel*>(&x));
}
for (int32_t x = 0; x < this->num_brushes; x++)
{
this->brushes.push_back(*reinterpret_cast<BSPBrush*>(&x));
}
for (int32_t x = 0; x < this->num_brush_sides; x++)
{
this->brush_sides.push_back(*reinterpret_cast<BSPBrushSides*>(&x));
}
for (int32_t x = 0; x < this->num_effects; x++)
{
this->effect.push_back(*reinterpret_cast<BSPEffects*>(&x));
}
this->file.seekg(this->header.lumps[BSPLUMPS::ENTITIES].offset);
this->file.read(reinterpret_cast<char*>(ents->ents_array), this->header.lumps[BSPLUMPS::ENTITIES].length);
this->file.seekg(this->header.lumps[BSPLUMPS::TEXTURES].offset);
this->file.read(reinterpret_cast<char*>(textures.data()), this->header.lumps[BSPLUMPS::TEXTURES].length);
this->file.seekg(this->header.lumps[BSPLUMPS::PLANES].offset);
this->file.read(reinterpret_cast<char*>(this->planes.data()), this->header.lumps[BSPLUMPS::PLANES].length);
this->file.seekg(this->header.lumps[BSPLUMPS::NODES].offset);
this->file.read(reinterpret_cast<char*>(this->nodes.data()), this->header.lumps[BSPLUMPS::NODES].length);
this->file.seekg(this->header.lumps[BSPLUMPS::LEAFS].offset);
this->file.read(reinterpret_cast<char*>(this->leaf.data()), this->header.lumps[BSPLUMPS::LEAFS].length);
this->file.seekg(this->header.lumps[BSPLUMPS::LEAF_FACES].offset);
this->file.read(reinterpret_cast<char*>(this->leaf_faces.data()), this->header.lumps[BSPLUMPS::LEAF_FACES].length);
this->file.seekg(this->header.lumps[BSPLUMPS::LEAF_BRUSHES].offset);
this->file.read(reinterpret_cast<char*>(this->leaf_brush.data()), this->header.lumps[BSPLUMPS::LEAF_BRUSHES].length);
this->file.seekg(this->header.lumps[BSPLUMPS::MODELS].offset);
this->file.read(reinterpret_cast<char*>(this->model.data()), this->header.lumps[BSPLUMPS::MODELS].length);
this->file.seekg(this->header.lumps[BSPLUMPS::BRUSHES].offset);
this->file.read(reinterpret_cast<char*>(this->brushes.data()), this->header.lumps[BSPLUMPS::BRUSHES].length);
this->file.seekg(this->header.lumps[BSPLUMPS::BRUSHSIDES].offset);
this->file.read(reinterpret_cast<char*>(this->brush_sides.data()), this->header.lumps[BSPLUMPS::BRUSHSIDES].length);
this->file.seekg(this->header.lumps[BSPLUMPS::VERTEXES].offset);
for (int32_t x = 0; x < this->num_vertexes; x++)
{
this->file.read(reinterpret_cast<char*>(&vertexes.data()[x]), this->header.lumps[BSPLUMPS::VERTEXES].length);
}
this->file.seekg(this->header.lumps[BSPLUMPS::MESHVERTS].offset);
this->file.read(reinterpret_cast<char*>(this->mesh_verts.data()), this->header.lumps[BSPLUMPS::MESHVERTS].length);
this->file.seekg(this->header.lumps[BSPLUMPS::EFFECTS].offset);
this->file.read(reinterpret_cast<char*>(this->effect.data()), this->header.lumps[BSPLUMPS::EFFECTS].length);
this->file.seekg(this->header.lumps[BSPLUMPS::FACES].offset);
this->file.read(reinterpret_cast<char*>(this->faces.data()), this->header.lumps[BSPLUMPS::FACES].length);
std::printf("BSP VERSION: '%s'\n", this->header.magic);
if (std::strncmp(this->header.magic, "IBSP", 4) == 0)
{
std::printf("SUCCESS: VALID BSP FORMAT!\n");
}
else
{
std::printf("ERROR: INVALID BSP FORMAT!\n");
return false;
}
this->shader.load_shader("shaders/bsp.vs", "shaders/bsp.fs");
/* heres where I try to store the data for the faces vertices */
for (int32_t x = 0; x < this->num_faces; x++)
{
BSPFaces& face = this->faces[x];
for (int32_t vertices = 0; vertices < this->num_vertexes; vertices++)
{
BSPVerts* vert = reinterpret_cast<BSPVerts*>(&vertexes[x]);
this->vertices_vector.push_back(vert[x].position.x);
this->vertices_vector.push_back(vert[x].position.y);
this->vertices_vector.push_back(vert[x].position.z);
this->colors.push_back(vert[x].position.x / 0xFF);
this->colors.push_back(vert[x].position.y / 0xFF);
this->colors.push_back(vert[x].position.z / 0xFF);
this->indices.push_back(vert[x].position.x);
this->indices.push_back(vert[x].position.y);
this->indices.push_back(vert[x].position.z);
}
}
glGenVertexArrays(1, &this->vao);
glBindVertexArray(this->vao);
glGenBuffers(1, &this->vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, this->vertices_vector.size() * sizeof(float), &this->vertices_vector.front(), GL_STATIC_DRAW);
glGenBuffers(1, &this->color_vbo);
glBindBuffer(GL_ARRAY_BUFFER, this->color_vbo);
glBufferData(GL_ARRAY_BUFFER, this->colors.size() * sizeof(float), &this->colors.front(), GL_STATIC_DRAW);
this->coord3d = glGetAttribLocation(this->shader.program, "coord3d");
this->mvp = glGetUniformLocation(this->shader.program, "mvp");
this->attrib_color = glGetAttribLocation(this->shader.program, "v_color");
glBindBuffer(GL_ARRAY_BUFFER, this->vbo);
glVertexAttribPointer(this->coord3d, // attribute
3, // number of elements per vertex, here (R,G,B)
GL_FLOAT, // the currentBlock of each element
GL_FALSE, // take our values as-is
0, // no extra data between each position
nullptr // offset of first element
);
glBindBuffer(GL_ARRAY_BUFFER, this->color_vbo);
glVertexAttribPointer(this->attrib_color,
3,
GL_FLOAT,
GL_FALSE,
0,
nullptr
);
glBindVertexArray(0);
glVertexAttrib3fv(this->attrib_color, reinterpret_cast<float*>(this->colors.data()));
return true;
}
else
{
std::printf("ERROR: COULDN'T OPEN FILE!\n");
return false;
}
return false;
}
void KikoBSP::render(glm::vec3 position)
{
glBindVertexArray(this->vao);
glEnableVertexAttribArray(this->coord3d);
glEnableVertexAttribArray(this->attrib_color);
glm::mat4 model = glm::translate(glm::mat4(1.0), glm::vec3(position.x, position.y, position.z));
glm::mat4 mvp = game_manager->projection * game_manager->view * model;
glUniformMatrix4fv(this->mvp, 1, GL_FALSE, glm::value_ptr(mvp));
glLineWidth(3.0);
glDrawArrays(GL_LINES, 0, this->vertices_vector.size());
glDisableVertexAttribArray(this->coord3d);
glDisableVertexAttribArray(this->attrib_color);
glBindVertexArray(0);
}
void KikoBSP::cleanup_map()
{
/* delete[] textures;
delete[] planes;
delete[] this->leafs;
delete[] this->nodes;
delete[] this->leaf_this->faces;
delete[] this->models;
delete[] this->brushes;
delete[] this->brush_sides;
delete[] this->vertexes;
delete[] this->mesh_verts;
delete[] this->effects;
delete[] this->this->faces;
(use maybe later?) */
this->file.close();
}
There are no errors in the code, it just draws this:
Which is obviously not the map!
Also, heres my header file if you need that to:
#pragma once
#pragma pack(2)
#include <iostream>
#include <cstdint>
#include <string>
#include <vector>
#include <glm/vec3.hpp>
#include <fstream>
#include "shader.h"
#include <memory>
#include <array>
#define FACE_POLYGON 1
enum BSPLUMPS
{
ENTITIES,
TEXTURES,
PLANES,
NODES,
LEAFS,
LEAF_FACES,
LEAF_BRUSHES,
MODELS,
BRUSHES,
BRUSHSIDES,
VERTEXES,
MESHVERTS,
EFFECTS,
FACES,
LIGHTMAPS,
LIGHTVOLS,
VISDATA,
MAX_LUMPS
};
struct BSPLump
{
int32_t offset; /* offset to start of lump */
int32_t length; /* length of lump, always multiple of 4 */
};
struct BSPHeader
{
char magic[4]; /* ALWAYS IBSP */
int32_t version; /* 0x2E for Quake 3 */
BSPLump lumps[BSPLUMPS::MAX_LUMPS]; /* direntries */
};
struct BSPEntities
{
char* ents_array;
};
struct BSPTexture
{
char name[64];
int32_t flags;
int32_t contents;
};
struct BSPPlane
{
glm::vec3 normal;
float distance;
};
struct BSPNode
{
int32_t plane;
glm::ivec2 children;
glm::ivec3 mins;
glm::ivec3 maxs;
};
struct BSPLeaf
{
int32_t cluster;
int32_t area;
glm::ivec3 mins;
glm::ivec3 maxs;
int32_t leafface;
int32_t num_leaffaces;
int32_t leaf_brush;
int32_t num_leaf_brushes;
};
struct BSPLeafFace
{
int32_t face;
};
struct BSPLeafBrush
{
int32_t brush;
};
struct BSPModel
{
glm::fvec3 mins;
glm::fvec3 maxs;
int32_t face;
int32_t num_faces;
int32_t brush;
int32_t num_brushes;
};
struct BSPBrush
{
int32_t brush_side;
int32_t num_brush_sides;
int32_t texture;
};
struct BSPBrushSides
{
int32_t plane;
int32_t texture;
};
struct BSPVerts
{
glm::vec3 position;
glm::vec2 tex_coord; /* same as float tex_coord[2][2] */
glm::vec2 lm_coord; /* same as float tex_coord[2][2] */
glm::vec3 normal;
char color[4];
};
struct BSPMeshVerts
{
int32_t offset;
};
struct BSPEffects
{
char name[64];
int32_t brush;
int32_t unk; /* unknown */
};
struct BSPFaces
{
int32_t texture;
int32_t effect;
int32_t type;
int32_t vertex;
int32_t num_vertexes;
int32_t meshvert; /* start */
int32_t num_of_meshverts;
int32_t lm_index;
glm::ivec2 lm_start;
glm::ivec2 lm_size;
glm::vec3 lm_origin;
float lm_vecs[2][3];
glm::fvec3 normal;
glm::ivec2 size;
};
class KikoBSP
{
public:
bool load_map(std::string);
void render(glm::vec3);
void draw_level();
int32_t get_max();
int32_t get_min();
void get_vert();
void cleanup_map();
Shader shader;
BSPHeader header;
int32_t num_vertexes;
private:
std::ifstream file;
uint32_t vbo;
uint32_t vao;
uint32_t color_vbo;
uint32_t ebo;
int32_t coord3d;
int32_t mvp;
int32_t attrib_color;
BSPFaces* cur_face;
std::vector<float> vertices_vector;
std::vector<float> colors;
std::vector<float> indices;
std::vector<BSPFaces> faces;
std::vector<BSPTexture> textures;
std::vector<BSPPlane> planes;
std::vector<BSPNode> nodes;
std::vector<BSPMeshVerts> mesh_verts;
std::vector<BSPLeaf> leaf;
std::vector<BSPLeafFace> leaf_faces;
std::vector<BSPLeafBrush> leaf_brush;
std::vector<BSPModel> model;
std::vector<BSPBrush> brushes;
std::vector<BSPBrushSides> brush_sides;
std::vector<BSPEffects> effect;
int32_t num_textures;
int32_t num_planes;
int32_t num_nodes;
int32_t num_leafs;
int32_t num_leaf_faces;
int32_t num_leaf_brushes;
int32_t num_models;
int32_t num_brushes;
int32_t num_brush_sides;
int32_t num_meshverts;
int32_t num_effects;
int32_t num_faces;
int32_t num_lightmaps;
int32_t num_lightvols;
};
I think the problem is the indices or vertices. I believe the problem lies with how I STORE the data for the vertices. Its really buggy.
Thanks! All helps appreciated! :)
Whenever someone says "There are no errors in the code", you know something has already gone terribly wrong!
For example, what is this supposed to be doing?
for (int32_t x = 0; x < this->num_vertexes; x++)
{
vertexes.push_back(*reinterpret_cast<BSPVerts*>(&x));
}
It looks like it's invoking some undefined behavior here. If all you want to do is fill the array up, just do this:
vertexes.resize(num_vertices);
Secondly, the code that reads in the vertexes from the file looks like it has a logic error:
for (int32_t x = 0; x < this->num_vertexes; x++)
{
this->file.read(reinterpret_cast<char*>(&vertexes.data()[x]), this->header.lumps[BSPLUMPS::VERTEXES].length);
}
This looks like it's going to read in the entirety of the vertex lump for each vertex, probably resulting in bizarre behavior. You probably just want to do this:
file.read(reinterpret_cast<char*>(vertexes.data()), header.lumps[BSPLUMPS::VERTEXES].length);
Also, a note on your code that's unrelated to your problem: using this-> everywhere hurts readability and adds nothing to the code, remove it.
I did the same BSP renderer a while back, I can't remember what state I left it in, but you can have a look at the code here.

How to create an image in c++ with an 2d rgb array?

Well I'm using a restAPI to get a 2d numpy rgb-array from python and I want to create and image from it using c++ .Do you have any idea how to do it? or Do you know a similar library of numpy fro c++?
Its pretty simple to create a bitmap (.bmp extensions).
You can either use a library, like this one:
https://github.com/ArashPartow/bitmap. A library with a narrow use case is usually pretty readable. The logic is all contained in a single hpp file in this case. Looking at it, its a little complex because it handles a ton of different cases.
Or you can just do it yourself. Look up how to write a binary bitmap header and use fstream to write it out to a file. You'll want to use the binary options when opening the file for writing ios::out | ios::binary. You can find the details of the bitmap header and file format here: https://en.wikipedia.org/wiki/BMP_file_format
By the way, Wikipedia has many decent references on binary file formats with bit-by-bit tables.
For an extremely naive implementation that does not handle a bunch of different formats, I've done this in the past.
BitmapFileHeader.h
#pragma once
#include <cstdint>
#pragma pack(push, 2)
struct BitmapFileHeader
{
char header[2]{'B', 'M'};
uint32_t fileSize;
uint32_t reserved{};
uint32_t dataOffset;
};
#pragma pack(pop)
BitmapInfoHeader.h
#pragma once
#include <cstdint>
#pragma pack(push, 2)
struct BitmapInfoHeader
{
uint32_t headerSize{ 40 };
uint32_t width{ 0 };
uint32_t height{ 0 };
uint16_t planes{ 1 };
uint16_t bitsPerPixel{ 24 };
uint32_t compression{ 0 };
uint32_t dataSize{ 0 };
uint32_t horizontalResolution{ 2400 };
uint32_t verticalResolution{ 2400 };
uint32_t colors{ 0 };
uint32_t importantColors{ 0 };
};
#pragma pack(pop)
Bitmap.h
#include <string>
#include <iostream>
#include <cstdint>
#include <memory>
#include <fstream>
#include "BitmapFileHeader.h"
#include "BitmapInfoHeader.h"
using namespace std;
class Bitmap
{
private:
int m_width{ 0 };
int m_height{ 0 };
unique_ptr<uint8_t[]> m_pixels{ nullptr };
public:
struct RBG {
uint8_t r;
uint8_t b;
uint8_t g;
};
Bitmap(int width, int height) : m_width(width),
m_height(height), m_pixels(new uint8_t[width * height * sizeof(RBG)]{}) {};
void setPixel(int, int, RBG);
void setDimensions(int, int);
int getSize();
bool write(string);
~Bitmap();
};
Bitmap.cpp
#include "stdafx.h"
#include "Bitmap.h"
using namespace std;
void Bitmap::setPixel(int x, int y, RBG color)
{
uint8_t *pixel = m_pixels.get();
pixel = pixel + ((y * sizeof(RBG)) * m_width) + (x * sizeof(RBG));
// little endian
pixel[0] = color.b;
pixel[1] = color.g;
pixel[2] = color.r;
}
void Bitmap::setDimensions(int w, int h)
{
m_width = w;
m_height = h;
}
int Bitmap::getSize()
{
return m_width * m_height * sizeof(RBG);
}
bool Bitmap::write(string filename)
{
BitmapFileHeader fileHeader;
BitmapInfoHeader infoHeader;
fileHeader.fileSize = sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + getSize();
fileHeader.dataOffset = sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader);
infoHeader.width = m_width;
infoHeader.height = m_height;
ofstream file;
file.open(filename, ios::out | ios::binary);
if (!file)
{
return false;
}
file.write(reinterpret_cast<char *>(&fileHeader), sizeof(fileHeader));
file.write(reinterpret_cast<char *>(&infoHeader), sizeof(infoHeader));
file.write(reinterpret_cast<char *>(m_pixels.get()), getSize());
file.close();
return true;
}
Bitmap::~Bitmap()
{
cout << "bitmap destroyed" << endl;
}

Flip and crop a bitmap

I'm getting a bytearray (32 bit or 16 bit) from a source.
If the size width is odd, the last pixel in each row needs to be dropped.
If the height is odd, the last row needs to be dropped.
If the height is negative the bitmap needs to be flipped vertically.
Here is my code so far:
m_pbmiLast = new BITMAPINFO(*m_pbmi);
m_pbmiLast->bmiHeader.biWidth = abs(m_pbmiLast->bmiHeader.biWidth) - (abs(m_pbmiLast->bmiHeader.biWidth) % 2);
m_pbmiLast->bmiHeader.biHeight = abs(m_pbmiLast->bmiHeader.biHeight) - (abs(m_pbmiLast->bmiHeader.biHeight) % 2);
int biWidth = m_pbmiLast->bmiHeader.biWidth;
int biHeight = m_pbmiLast->bmiHeader.biHeight;
int iAdjustedStride = ((((biWidth * m_pbmiLast->bmiHeader.biBitCount) + 31) & ~31) >> 3);
int iRealStride = ((((m_pbmi->bmiHeader.biWidth * m_pbmi->bmiHeader.biBitCount) + 31) & ~31) >> 3);
if (m_pbmi->bmiHeader.biHeight < 0) {
/* Copy the actual data */
int iLineOffsetSource = 0;
int iLineOffsetDest = (biHeight - 1) * iRealStride;
for (int i = 0; i < biHeight; ++i) {
memcpy(&pData[iLineOffsetDest], &m_inputBuffer[iLineOffsetSource], iAdjustedStride);
iLineOffsetSource += iRealStride;
iLineOffsetDest -= iRealStride;
}
} else {
int iLineOffset = 0;
for (int i = 0; i < biHeight; ++i) {
memcpy(&pData[iLineOffset], &m_inputBuffer[iLineOffset], iAdjustedStride);
iLineOffset += iRealStride;
}
}
It doesn't flip the bitmap, and when the bitmap is an odd width, it slants the bitmap.
Can be done like so.. I include the reading and writing just to make it an SSCCE. It has little to no error.
As for my comment about new BITMAPINFO. I was saying that you don't have to allocate such a small structure on the HEAP. Ditch the new part. The only allocation you need for a bitmap is the pixels. The header and other info does not need an allocation at all.
See the Flip function below.
#include <iostream>
#include <fstream>
#include <cstring>
#include <windows.h>
typedef struct
{
BITMAPFILEHEADER Header;
BITMAPINFO Info;
unsigned char* Pixels;
} BITMAPDATA;
void LoadBmp(const char* path, BITMAPDATA* Data)
{
std::ifstream hFile(path, std::ios::in | std::ios::binary);
if(hFile.is_open())
{
hFile.read((char*)&Data->Header, sizeof(Data->Header));
hFile.read((char*)&Data->Info, sizeof(Data->Info));
hFile.seekg(Data->Header.bfOffBits, std::ios::beg);
Data->Pixels = new unsigned char[Data->Info.bmiHeader.biSizeImage];
hFile.read((char*)Data->Pixels, Data->Info.bmiHeader.biSizeImage);
hFile.close();
}
}
void SaveBmp(const char* path, BITMAPDATA* Data)
{
std::ofstream hFile(path, std::ios::out | std::ios::binary);
if (hFile.is_open())
{
hFile.write((char*)&Data->Header, sizeof(Data->Header));
hFile.write((char*)&Data->Info, sizeof(Data->Info));
hFile.seekp(Data->Header.bfOffBits, std::ios::beg);
hFile.write((char*)Data->Pixels, Data->Info.bmiHeader.biSizeImage);
hFile.close();
}
}
void Flip(BITMAPDATA* Data)
{
unsigned short bpp = Data->Info.bmiHeader.biBitCount;
unsigned int width = std::abs(Data->Info.bmiHeader.biWidth);
unsigned int height = std::abs(Data->Info.bmiHeader.biHeight);
unsigned char* out = new unsigned char[Data->Info.bmiHeader.biSizeImage];
unsigned long chunk = (bpp > 24 ? width * 4 : width * 3 + width % 4);
unsigned char* dst = out;
unsigned char* src = Data->Pixels + chunk * (height - 1);
while(src != Data->Pixels)
{
std::memcpy(dst, src, chunk);
dst += chunk;
src -= chunk;
}
std::memcpy(dst, src, chunk); //for 24-bit.
std::swap(Data->Pixels, out);
delete[] out;
}
int main()
{
BITMAPDATA Data;
LoadBmp("C:/Users/Brandon/Desktop/Bar.bmp", &Data);
Flip(&Data);
SaveBmp("C:/Users/Brandon/Desktop/Foo.bmp", &Data);
delete[] Data.Pixels;
return 0;
}