首页 > 其他分享 >[TinyRenderer] Chapter1 p1 Output Image

[TinyRenderer] Chapter1 p1 Output Image

时间:2024-06-10 11:44:28浏览次数:24  
标签:std int infoHeader Image TinyRenderer BMPImage rowSize Output void

由于本文章是对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,里面仅是一些头文件。

输出结果

img

你能看到那个白点吗?那是我们的起点。

标签:std,int,infoHeader,Image,TinyRenderer,BMPImage,rowSize,Output,void
From: https://www.cnblogs.com/qiyuewuyi/p/18240538

相关文章

  • View->可拖拽滑动的ImageView + Fling惯性滑动效果 + 回弹效果
    XML文件<?xmlversion="1.0"encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent&quo......
  • ArcGIS和ArcGIS Pro快速加载ArcGIS历史影像World Imagery Wayback
    ArcGIS在线历史影像网站WorldImagery Wayback(网址:https://livingatlas.arcgis.com/wayback/)提供了数期历史影像在线浏览服务,之前不少自媒体作者在文中宣称其能代表GoogleEarth历史影像。1、一点对比(1)同一级别下的版本覆盖面以下述区域为例,自2014年2月20日至2022年5月18......
  • WPF Image zoomin zoomout move
    //xaml<Windowx:Class="WpfApp145.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.mi......
  • 【MATLAB】去除imagesc()白边
    目的:在MATLAB中去除imagesc()白边,去除图片的白边,可以将图片复制到word中的表格中显得更加紧凑。示例代码如下:figure;imagesc(sarImageNormalization);colormapjet;axisxy;set(gca,'Position',[0011]);%消除白边实验结果:未去除白边的效果:去除白边的效果:最终将多个图......
  • Django上传图片时ImageField的max_length报错
    我使用的版本是Django4.2,有一个模型里定义了ImageField,以下面这个为例:classExample(models.Model)image=models.ImageField(blank=True,upload_to=my_image_path,)当我上传图片的时候,django返回了这样一个错误:Ensurethisfilenam......
  • BLIP-2: Bootstrapping Language-Image Pre-training with Frozen Image Encoders and
    Motivation&Abs端到端大规模视觉语言预训练的开销极大。为此,本文提出了BLIP2,利用现成的冻住的imageencoder以及LLM引导视觉语言预训练。模态差距:通过两阶段训练的轻量级的QueryTransformer(Q-Former)弥补。第一阶段:从冻结的imageencoder引导VL学习;第二阶段:从冻结的LLM引导视......
  • 界面控件Telerik UI for WPF中文教程 - 用RadSvgImage升级应用程序UI
    TelerikUIforWPF拥有超过100个控件来创建美观、高性能的桌面应用程序,同时还能快速构建企业级办公WPF应用程序。UIforWPF支持MVVM、触摸等,创建的应用程序可靠且结构良好,非常容易维护,其直观的API将无缝地集成VisualStudio工具箱中。TelerikUIforWPF中的RadSvgImage组件使......
  • 【实用技巧】Unity中的Image组件
    Unity中的Image组件是UI系统的核心部分,用于显示图像和纹理。以下是一些关于UnityImage组件的实用技巧:使用Sprite作为Image源:将Sprite直接拖拽到Image组件的SourceImage字段中,可以快速设置显示的图像。调整颜色和透明度:通过修改Image组件的Color属性,可以改变显示图像的颜......
  • CLIP(Contrastive Language-Image Pre-training)
    CLIP(ContrastiveLanguage-ImagePre-training)是一种多模态预训练神经网络模型,由OpenAI在2021年初发布469。CLIP的核心创新在于其能够将图像和文本映射到一个共享的向量空间中,使得模型能够理解图像和文本之间的语义关系1。CLIP模型的架构非常简洁,但在zero-shot文本-图像检索、z......
  • vs中怎么设置统一的output路径
    背景:1、一个sln下有多个csproj项目,让所有csproj生成的dll路径在sln根目录下的ouput文件夹解决办法:1、在sln目录下新建Directory.Build.props文件,文件内容如下:<Project><PropertyGroupCondition="'$(Configuration)'=='Debug'"><CustomOutputPath&g......