Chapter1 p1 Output Image
由于本文章是对TinyRenderer的模仿,所以并不打算引入外部库。
那么我们第一步需要解决的就是图形输出的问题,毕竟,如果连渲染的结果都看不到,那还叫什么Renderer嘛。
由于不引入外部库,所以选择输出的图片格式应该越简单越好,各种位图就成为了我们的首选。
这里我们选择了生态较好的bmp位图。
技术上,由于只使用C++,所以各种文件流就成了我们构建图片的唯一工具。
本章目标
输出一张保存了我们渲染结果的bmp位图
需求:
- 大小可以控制,也就是位图的尺寸可控
- 控制某个像素点的颜色,精准更改set()
- 对位图进行上下反转
实现
BMPImage.h
#ifndef BMP_IMAGE_H
#define BMP_IMAGE_H
#include <string>
#include <vector>
#pragma pack(push, 1)
struct BMPFileHeader
{
uint16_t bfType; // BMP文件的类型,必须为"B"然后是"M"
uint32_t bfSize; // 文件大小
uint16_t bfReserved1; // 保留字,必须为0
uint16_t bfReserved2; // 从文件头到实际位图数据的偏移字节数
uint32_t bfOffBits; // 信息头的大小
};
struct BMPInfoHeader
{
uint32_t biSize; // info head size
int32_t biWidth; // 图像宽度
int32_t biHeight; // 图像高度
uint16_t biPlanes; // 图像的位面数
uint16_t biBitCount; // 每个像素的位数
uint32_t biCompression; // 压缩类型
uint32_t biSizeImage; // 图像的大小,以字节为单位
int32_t biXPelsPerMeter; // 水平分辨率
int32_t biYPelsPerMeter; // 垂直分辨率
uint32_t biClrUsed; // 位图实际使用的颜色表中的颜色数
uint32_t biClrImportant; // 位图显示过程中重要的颜色数
};
#pragma pack(pop)
/**
* \brief custom the color format used
*/
enum ColorFormat
{
RGB,
CMYK
};
struct RGBPixel
{
uint8_t red;
uint8_t green;
uint8_t blue;
RGBPixel() : red(0), green(0), blue(0)
{
}
RGBPixel(uint8_t red, uint8_t green, uint8_t blue) : red(red), green(green), blue(blue)
{
}
};
class BMPImage
{
public:
BMPImage() = delete;
BMPImage(unsigned int width, unsigned int height, ColorFormat colorFormat = ColorFormat::RGB);
void loadData(std::vector<char>&& userData);
void generate(const std::string& fileName);
void loadDataAndGenerate(std::vector<char>&& userData, const std::string& fileName);
void set(int x, int y, RGBPixel pixel);
void flipVertically();
private:
BMPFileHeader fileHeader;
BMPInfoHeader infoHeader;
ColorFormat colorFormat;
std::vector<unsigned char> pixelData;
};
#endif
Important:
- 在组织bmp文件头的部分,一定要使用预处理宏
#pragma pack(push, 1)和#pragma pack(pop),控制内存对齐方式为单字节,否则会由于编译器控制的内存对齐而导致文件格式错误,从而不能正确输出
BMPImage.cpp
#include "TinyRenderer/BMPImage.h"
#include <fstream>
#include <iostream>
BMPImage::BMPImage(unsigned width, unsigned height, ColorFormat colorFormat)
{
int rowSize = (width * 3 + 3) & (~3); // Ensure row size is a multiple of 4 bytes
int fileSize = rowSize * height + sizeof(BMPFileHeader) + sizeof(BMPInfoHeader);
// Set BMP file header
fileHeader.bfType = 0x4D42; // 'BM'
fileHeader.bfSize = fileSize;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader);
// Set BMP info header
infoHeader.biSize = sizeof(BMPInfoHeader);
infoHeader.biWidth = width;
infoHeader.biHeight = height;
infoHeader.biPlanes = 1;
infoHeader.biBitCount = 24;
infoHeader.biCompression = 0;
infoHeader.biSizeImage = rowSize * height;
infoHeader.biXPelsPerMeter = 0;
infoHeader.biYPelsPerMeter = 0;
infoHeader.biClrUsed = 0;
infoHeader.biClrImportant = 0;
// Initialize pixel data
pixelData.resize(rowSize * height, 0);
}
// not important now
void BMPImage::loadData(std::vector<char>&& userData)
{
// TODO: load image
}
void BMPImage::generate(const std::string& fileName)
{
std::ofstream file(fileName, std::ios::out | std::ios::binary);
if (!file)
{
std::cerr << "Error: Unable to open file for writing." << std::endl;
return;
}
// Write headers
file.write(reinterpret_cast<const char*>(&fileHeader), sizeof(fileHeader));
file.write(reinterpret_cast<const char*>(&infoHeader), sizeof(infoHeader));
// Write pixel data
file.write(reinterpret_cast<const char*>(pixelData.data()), pixelData.size());
file.close();
}
void BMPImage::loadDataAndGenerate(std::vector<char>&& userData, const std::string& fileName)
{
}
void BMPImage::set(int x, int y, RGBPixel pixel)
{
if (x < 0 || y < 0 || x >= infoHeader.biWidth || y >= infoHeader.biHeight)
{
throw std::out_of_range("Pixel coordinates are out of bounds");
}
int rowSize = (infoHeader.biWidth * 3 + 3) & (~3);
int index = (infoHeader.biHeight - 1 - y) * rowSize + x * 3;
pixelData[index] = pixel.blue;
pixelData[index + 1] = pixel.green;
pixelData[index + 2] = pixel.red;
}
void BMPImage::flipVertically()
{
int width = infoHeader.biWidth;
int height = infoHeader.biHeight;
int rowSize = (width * 3 + 3) & (~3);
for (int y = 0; y < height / 2; ++y)
{
int topIndex = y * rowSize;
int bottomIndex = (height - 1 - y) * rowSize;
for (int x = 0; x < rowSize; ++x)
{
std::swap(pixelData[topIndex + x], pixelData[bottomIndex + x]);
}
}
}
测试
main.cpp
#include "TinyRenderer/TinyRenderer.h"
#include "TinyRenderer/BMPImage.h"
int main()
{
BMPImage image(100, 100, ColorFormat::RGB);
RGBPixel white(255, 255, 255);
image.set(22, 77, white);
image.flipVertically();
image.generate("test.bmp");
std::cout << "Image Generated." << std::endl;
return 0;
}
请忽略TinyRenderer/TinyRenderer.h,里面仅是一些头文件。
输出结果

你能看到那个白点吗?那是我们的起点。
Chapter1 p1 Output Image的更多相关文章
- sql分页操作
看到了网上关于分页的讲解 对最快的分页语句做了测试 还别说速度真快 总共6w条数据 速度确实so 快 前提是id是主键 或者是索引 declare @page int;--页数 declare @P ...
- C#解析json文件的方法
C# 解析 json JSON(全称为JavaScript Object Notation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集. JSON采用完全独立于语言的 ...
- 设计一个用于人事管理的People(人员)类
#include <iostream> #include <string> using namespace std; class Date //日期类 { private: i ...
- JSON学习
1.JSON 语法是 JavaScript 对象表示语法的子集. l 数据在名称/值对中 l 数据由逗号分隔 l 花括号保存对象 l 方括号保存数组 JSON 值可以是: l 数字(整数或浮 ...
- js的this和面向对象编程
很奇怪的是很多书或资料没有把这个事情讲清楚. 关键就是在于没有一个整体的思维技术模式,问题被隔离了所以反而不容易理解. 我们先看this,这是js的关键字,指示函数的上下文对象. 这里问题就来了,比如 ...
- oracle,mysql,SqlServer三种数据库的分页查询的实例。
MySql: MySQL数据库实现分页比较简单,提供了 LIMIT函数.一般只需要直接写到sql语句后面就行了.LIMIT子 句可以用来限制由SELECT语句返回过来的数据数量,它有一个或两个参数,如 ...
- C# 解析JSON格式数据
JSON简介 JSON(全称为JavaScript ObjectNotation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集.JSON采用完全独立于语言的文本格式,可 ...
- C# 解析 Json数据
JSON(全称为JavaScript Object Notation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集. JSON采用完全独立于语言的文本格式,可以很容易在 ...
- C# 解析 Json
C# 解析 json JSON(全称为JavaScript Object Notation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集. JSON采用完全独立于语言的 ...
- sql server 2000数据库 最近经常出现某进程一直占用资源,阻塞?死锁?
OA的数据库最近多次出现某进程一直占用资源,导致其他进程无法执行.使用sp_who2 和 sql server profiler跟踪查询,发现有以下几个语句常常占用资源: 1.declare @P1 ...
随机推荐
- 鸿蒙HarmonyOS实战-ArkUI动画(弹簧曲线动画)
前言 弹簧曲线动画是一种模拟弹簧运动的动画效果,通过改变弹簧的拉伸或压缩来表现不同的运动状态.以下是制作弹簧曲线动画的步骤: 创建一个弹簧的模型,可以使用圆形或者曲线来代表弹簧的形状. 将弹簧固定在一 ...
- [ARC174B] Bought Review 题解
[题目描述] 你开了一家店,有 \(A_i\) 个 \(i\) 星级评论,你可以花费 \(P_i\) 元买到一个 \(i\) 星评论,问使得这家店评论的星星平均值不小于 \(3\),最少要花多少钱. ...
- Apsara Stack 技术百科 | 联结良性生态,筑千行百业的数字基石
简介:作为现今IT领域最重要的课题:基础设施云化,离不开与伙伴的携手合作,如何让云上解决方案能充分释放价值的同时形成一个相互依存的自循环生态系统,混合云君来跟你聊聊! 生态系统这个词在维基百科上 ...
- 知乎的 Flink 数据集成平台建设实践
简介: 本文由知乎技术平台负责人孙晓光分享,主要介绍知乎 Flink 数据集成平台建设实践.内容如下: 1. 业务场景 : 2. 历史设计 : 3. 全面转向 Flink 后的设计 : 4. 未来 F ...
- [FE] iframe 相关选项 x-frame-options: 设置 meta 标签无效 & helmet
The X-Frame-Options HTTP 响应头是用来给浏览器 指示允许一个页面 可否在 <frame>, <iframe>, <embed> 或者 < ...
- [PHP] Laravel cast array 数据库存 json 时的 unicode 编码问题
在模型上设置 accessor 和 mutator,将数组转为 json,并设置 json 选项. class User extends Model { public function setOpti ...
- dotnet SemanticKernel 入门 注入日志
使用 SemanticKernel 框架在对接 AI 时,由于使用到了大量的魔法,需要有日志的帮助才好更方便定位问题,本文将告诉大家如何在 SemanticKernel 注入日志 本文属于 Seman ...
- dotnet 6 精细控制 HttpClient 网络请求超时
本文告诉大家如何在 dotnet 6 下使用 HttpClient 更加精细的控制网络请求的超时,实现 HttpWebRequest 的 ReadWriteTimeout 功能 本文将介绍如何在 Ht ...
- 2018-7-15-WPF-在-DrawingContext-的-push-如何使用
title author date CreateTime categories WPF 在 DrawingContext 的 push 如何使用 lindexi 2018-7-15 15:51:0 + ...
- RT-Thead移植时缺少SystemCoreClockUpdate函数
一.问题 在移植 RT-Thead 时,需要获取mcu当前的 HCLK 时钟频率来设置滴答时钟,而我使用的库函数中刚好缺少SystemCoreClockUpdate函数. 我之前在RT-Thread移 ...