texture reading and writing

When using textures or pictures in a programming context you need to load them, do something with that data that you loaded, then destroy the data, move the data, or write the data.

Loading the data is just the process of getting it into memory, in my case I load it into std::vector because I want the data to be heap allocated. If I used an std::array, which is possible because I know the size of my data before I load it, the program would crash because the cache(stack) would be overloaded. This is uniquely a problem when using large textures, 4k in my case. Further more I build the program in x64 configuration because that extra virtual memory is needed.

whiteboard example of in order texture loading, could also load then write before moving on to the next

Ignoring headers and defines this is the total for reading in a .png then rewriting it to a new file. It is possible to read through the code and understand it but I will go through it anyway.

All I am doing is loading a .png into a data structure, then writing it to a new filepath. This may seems to be a reductive example, but keep in mind that the purpose of textures is to read the data in, then do something with that data. If all you do is write it to another file path, that is still doing something.

Lets go through the parameters.

inputPath: where the texture file is.

data: a pointer to the start of the data structure holding all the texture information.

width: the actual pixel width of the texture

height: the actual pixel height of the texture

channels: the number of components per pixel, for example if a texture has only RGB values, it has 3 channels, if it had RGBA values it would have 4 channels.

pixelData: this is the vector we are going to copy all the texture data, more on this later.

next the load texture function.

stbi_load: takes in our parameters and returns a pointer to the start of the data structure.

notice the parameters that are passed in by reference. These values are uninitialized before entering the function. The stbi_load function also assigns the correct values to these parameters after parsing the texture, which will be useful for this example in the next function.

next we fill out std::vector with the data that was just parsed.

pixelData.assign: takes in 2 parameters, the first is the memory location of the start of the data you want to assign, followed by the last memory location of that data. In this case the last location is equal to the first location plus the total size of the data.

Since the texture data can be visualized as pixels, we can use the logic that a matrix of n rows by m columns has n*n items. But since each pixel contains RGBA (or RGB, or just R) data, that size must be multiplied by this amount of data.

In simpler terms, each pixel has "channels" amount of data, and there are width*height pixels. therefore width*height*channels is the total amount of data.

I cast these int's to int64_t since the numbers are so large I do not want any overflow errors.

the last parameter is 0, this parameter is "desired channels," if it not zero then the output data structure will have the number of channels inputted into that parameter.

So now I have my own std::vector with all the information for the texture. Now, as far as I know, stbi_load returns the pointer locations to the start of a std::vector, so this assign step is not needed, and I do not have it in my own code, but it is useful for explanation purposes.

Last the write function

The stbi_write_png takes in similar parameters to the load function.

The parameters are:

(file output location, image/texture width, image/texture height, channels per pixel, the start of the data, the size of each row of pixels)

and that is everything. This code block will take in a texture, read and load it, then write the identical texture to a separate location.

Next we will look at lambdas which will be helpful in some cases when multithreaded is implemented.