Skip to content

Commit

Permalink
histogram bitmap
Browse files Browse the repository at this point in the history
the changes
  • Loading branch information
GDBobby committed Sep 24, 2023
1 parent 630dce4 commit 828ec20
Show file tree
Hide file tree
Showing 6 changed files with 502 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ out/*
screenshots
.cache
.DS_Store
*.log
*.bak

CMakeSettings.json
compile_commands.json
Expand Down
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
add_subdirectory(histogram)
add_subdirectory(HistogramBitmap)
12 changes: 12 additions & 0 deletions examples/HistogramBitmap/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
project(noiz-HistogramBitmap)

add_executable(${PROJECT_NAME})

target_link_libraries(${PROJECT_NAME} PRIVATE
noiz::noiz-lib
noiz::noiz-compile-options
)

target_sources(${PROJECT_NAME} PRIVATE
HistogramBitmap.cpp
)
245 changes: 245 additions & 0 deletions examples/HistogramBitmap/HistogramBitmap.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
#include <noiz/noise2.hpp>


#include <charconv>
#include <filesystem>
#include <format>
#include <iostream>
#include <vector>
#include <fstream>

struct BmpHeader {
char bitmapSignatureBytes[2] = {'B', 'M'};
uint32_t sizeOfBitmapFile = 54;
uint32_t reservedBytes = 0;
uint32_t pixelDataOffset = 54;

BmpHeader(size_t imageSize) {
sizeOfBitmapFile += imageSize * imageSize * 3; //rgb, 1 byte per color
}
void writeToFile(std::ofstream& outFile){
//writing like this to avoid padding issues
outFile.write(bitmapSignatureBytes, 2);
outFile.write((char*) &sizeOfBitmapFile, sizeof(uint32_t));
outFile.write((char*) &reservedBytes, sizeof(uint32_t));
outFile.write((char*) &pixelDataOffset, sizeof(uint32_t));
}
};


struct BmpInfoHeader {
uint32_t sizeOfThisHeader = 40;
int32_t width = 256; // in pixels
int32_t height = 256;
uint16_t numberOfColorPlanes = 1; // must be 1
uint16_t colorDepth = 24;
uint32_t compressionMethod = 0;
uint32_t rawBitmapDataSize = 0;
int32_t horizontalResolution = 3780; // in pixel per meter //do i even need this?
int32_t verticalResolution = 3780; // in pixel per meter
uint32_t colorTableEntries = 0;
uint32_t importantColors = 0;

BmpInfoHeader(size_t imageSize){
width = imageSize;
height = imageSize;
}
void writeToFile(std::ofstream& outFile){
//writing like this to avoid padding issues
outFile.write((char*) &sizeOfThisHeader, sizeof(uint32_t));
outFile.write((char*) &width, sizeof(int32_t));
outFile.write((char*) &height, sizeof(int32_t));
outFile.write((char*) &numberOfColorPlanes, sizeof(uint16_t));
outFile.write((char*) &colorDepth, sizeof(uint16_t));
outFile.write((char*) &compressionMethod, sizeof(uint32_t));
outFile.write((char*) &rawBitmapDataSize, sizeof(uint32_t));
outFile.write((char*) &horizontalResolution, sizeof(int32_t));
outFile.write((char*) &verticalResolution, sizeof(int32_t));
outFile.write((char*) &colorTableEntries, sizeof(uint32_t));
outFile.write((char*) &importantColors, sizeof(uint32_t));
}
};


namespace {
template <typename Type>
concept NumberT = std::integral<Type> || std::floating_point<Type>;

template <NumberT Type>
auto parse_as(Type& out, std::string_view const value) -> bool {
if (value.empty()) { return false; }

auto const* end = value.data() + value.size();
auto const [ptr, ec] = std::from_chars(value.data(), end, out);

return ec == std::errc{} && ptr == end;
}

struct Args {
std::span<char const* const> args{};

[[nodiscard]] constexpr auto next() -> std::string_view {
if (args.empty()) { return {}; }
auto const* ret = args.front();
args = args.subspan(1);
return ret;
}

template <NumberT Type>
auto next_as(Type& out, std::string_view const key) -> bool {
auto value = next();
if (value.empty()) { return true; }

if (!parse_as(out, value)) {
std::cerr << std::format("invalid argument: '{}' for '{}'\n", value, key);
return false;
}

return true;
}
};

struct Config {
noiz::Seed seed{noiz::detail::Generator::make_random_seed()};
noiz::GridExtent2 grid_extent{50, 1}; // NOLINT
int count{100}; // NOLINT
float step{0.1f}; // NOLINT
int imageSize{1};

// syntax: [count] [step]
auto parse_args(Args args) -> bool {
if (!args.next_as(count, "count")) { return false; }
if (!args.next_as(step, "step")) { return false; }
if (!args.next_as(imageSize, "imageSize")) { return false; }
assert(imageSize > 0 && imageSize < 16);
if (!args.args.empty()) {
std::cerr << std::format("unrecognized argument: '{}'\n", args.next());
return false;
}
return true;
}
};

void writeBitmapFileHeader(size_t imageSize, std::ofstream& outFile) {

assert(outFile.is_open());
BmpHeader bmpHeader{imageSize};
bmpHeader.writeToFile(outFile);
BmpInfoHeader bmpInfoHeader{imageSize};
bmpInfoHeader.writeToFile(outFile);
}

// contains logic to store data points and draw them
class HistogramBitmap {
public:
struct Color{
uint8_t red = 0;
uint8_t green = 0;
uint8_t blue = 0;

void writeToFile(std::ofstream& outFile) {
//writing like this to avoid padding issues
outFile.write((char*) &red, sizeof(uint8_t));
outFile.write((char*) &green, sizeof(uint8_t));
outFile.write((char*) &blue, sizeof(uint8_t));
}

};

explicit HistogramBitmap(int imageSizeFactor, int dataCount) {
imageSize *= imageSizeFactor;
heightValues.reserve(dataCount);
}


auto add(float const value) -> HistogramBitmap& {
heightValues.push_back(static_cast<std::size_t>((value + 1.0f) * 0.5f * static_cast<float>(imageSize)));

return *this;
}

void writeImage() {
std::vector<uint16_t> interpolatedHeight;
interpolatedHeight.resize(imageSize);
float stepSizeOfBaseGraph = static_cast<float>(imageSize) / static_cast<float>(heightValues.size() - 1);

std::ofstream outFile{"histogram.bmp", std::ios::binary};

#if false
//leaving this for future cubic interpolation
//i need to learn more about it

for(int i = 1; i < imageSize - 1; i++) {
interpolatedHeight[i] = cubicInterpolation();
}

#else
interpolatedHeight[0] = heightValues[0];
interpolatedHeight.back() = heightValues.back();

for(int i = 1; i < imageSize - 1; i++) {
float exactPoint = static_cast<float>(i) / stepSizeOfBaseGraph;
uint16_t lastPoint = std::floor(exactPoint);
uint16_t nextPoint = std::ceil(exactPoint);
float weighting = exactPoint - (float)lastPoint;
interpolatedHeight[i] = std::lerp(heightValues[lastPoint], heightValues[nextPoint], weighting);
}

writeBitmapFileHeader(imageSize, outFile);
Color outColor;
for(int y = 0; y < imageSize; y++) {
for(int x = 0; x < imageSize; x++) {
if(interpolatedHeight[x] >= y) {
histogramColor.writeToFile(outFile);
}
else{
backgroundColor.writeToFile(outFile);
}
}
}

#endif
outFile.close();
}


private:
uint16_t imageSize = 256;
Color backgroundColor{.red = 0, .green = 0, .blue = 0};
Color histogramColor{.red = 0, .green = 127, .blue = 0};
std::vector<size_t> heightValues;
};
} // namespace

auto main(int argc, char** argv) -> int {
auto config = Config{};
auto noise = noiz::Noise2f{noiz::Seed{config.seed}, config.grid_extent};

// skip exe name (argv[0])
auto const args = Args{std::span{argv, static_cast<std::size_t>(argc)}.subspan(1)};

// handle --help
if (!args.args.empty() && args.args.front() == std::string_view{"--help"}) {
std::cout << std::format("Usage: {} [count(=100)] [step(=0.1)] [imageSize(=1)]\n", std::filesystem::path{*argv}.stem().string());
std::cout << "\t output image resolution is 256x256, with each dimension multiplied by imageSize, maximum imageSize is 16[4096]" << std::endl;
return EXIT_SUCCESS;
}

// parse args, if any
if (!config.parse_args(args)) {
std::cout << args.args.front() << std::endl;
return EXIT_FAILURE;
}

// build histogram
auto histogramBitmap = HistogramBitmap{config.imageSize, config.count};
for (int i = 0; i < config.count; ++i) {
// build point on line (y = 0)
auto const point = noiz::Vec2f{.x = static_cast<float>(i) * config.step};
// add noise at point
histogramBitmap.add(noise.at(point));
}

//write histogram to bmp file in same folder as execution
histogramBitmap.writeImage();
}
12 changes: 12 additions & 0 deletions examples/Texture2D/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
project(noiz-Texture2D)

add_executable(${PROJECT_NAME})

target_link_libraries(${PROJECT_NAME} PRIVATE
noiz::noiz-lib
noiz::noiz-compile-options
)

target_sources(${PROJECT_NAME} PRIVATE
Texture2D.cpp
)
Loading

0 comments on commit 828ec20

Please sign in to comment.