 Hey guys welcome back skits on episode 12 topic today is raster images and in my opinion the best raster image format is bitmaps Hot take but uh, I don't like these other formats. They seem to either They the compression is not worth it in my opinion you can save you know six bytes here with the PNG Seven bytes here with some other format Meanwhile gta 5 is a hundred gig install. So it's like what's the point? Bitmaps just work just simple header simple rgb array very straightforward format to work with I'm also going to talk about different rasterization techniques for different geometric objects lines circles Triangles things like that and we're going to implement some basic algorithms for that purpose and just as a little preface or I guess Post-fists Here is what our result and it's going to look like All of these geometric elements are generated entirely in x86 assembly No libraries nothing in C. Everything is literally every instruction being executed Was written by us with the exception of the syscalls that are used to write files on Linux, but yeah, that's just what we're doing today. So again line circles other shapes Pixels themselves filling different parts of the domain. That's also also opacity. You kind of see here a Translucent fill all these things will be covered in this video if I interest you stick around if not, I'll see you in the next video so Let's begin So what goes into a bitmap file? And so we're going to stick with this windows bitmap Obviously, there's like other types of bitmaps like on Linux There's different file formats that are used for Programming and things like that and see but it's not very portable in my experience I haven't said anything online that can render even like on Firefox. You can't render these Linux You know specific bitmap formats But anyway a bitmap file has BMP file should say has Two different sections actually maybe three sections. There's actually two headers Just combined into one here for simplicity's sake and then there's the actual RGB data So it's not too dissimilar from the elf files that we're going to be that we've already created in this series In every single video. So those files have a header as well as Instructions in the data section down here. So it's a pretty similar concept and there are many different variants for bitmaps But we're going to stick to this windows bitmap before which is just one type of Combination of parameters that is understood by different software when you open a bitmap and we're going to use 32-bit color in ARGB format. So A is the opacity R is red G is green B is blue and so that's one byte for each of those things and That's good because One thing about bitmaps is you have to have padding in your RGB Data array at the end of every row, I believe but thankfully because 32 bits is four bytes There's no need for padding because you're padding to I think like a four byte boundary. So there's no reason to do that Okay, so with that out of the way Let's talk about the actual header itself And so I ripped this from Wikipedia You can check this out yourself or some other resource, but this basically is kind of the headers required for this Windows bitmap before variant and So basically the first part of the header is just a bunch of garbage nonsense Indicating that it's a bitmap file and then down here this section here I believe this number is how that these are Softwares like gimp and whatever that's how it detects what version you're using. I'm pretty sure that's how it works And so we're gonna use this Windows bitmap before version. So that size is 108 bytes Anyway, there's a bunch of stuff in this DIB header Which is a function of which variant you've picked and some of those things are like the again You can see here the size of this so it's The size of the image Some bunch of information about the number of bits per pixel planes This is if you wanted to print everything out how how much pixels per unit length and color information things like this masks for RGB and alpha channel and Other stuff like that not really too important. In fact only four things are variable in this file as far as we're gonna Use it. We're not gonna change the number of we're not gonna change the variance for this Bitmap file we're not gonna change number of bits per pixel not gonna change number of color planes That's all gonna be fixed. We're only gonna change the size of the image And so all these four things here. So this orange one is the width the width of the bitmap in pixels The light blue one is the height in pixels This down here would be the size of the raw bitmap data. That's just gonna be width times height Times number of bytes per pixel, which is 32 bits or four bytes of 4 wh bytes And as well as this top thing here, and this is the size of the bitmap file itself the whole file header and Pixel array information so again all these four things are just a function of The width and height of your image So yeah professor that is very easy correct Now the actual pixel array data is pretty simple to understand basically You just have you know Bits that indicate different colors and the way that the counting works is that you start at the bottom left of the image and Count to the right and then upwards so your pixel zero is at the bottom left Pixel 7 in this 8 pixel image is at the top Right and you kind of can see in this a RGB format that You know this first pixel here is Blue, but it's not fully opaque so you can see it's light blue and then pixel one is Here it's green and not fully opaque For example, let's say this second glass pixel here pixel 6 that is red and fully opaque. That's this one And so yeah, basically that's how the pixel data is encoded and because of ending this you can see it's kind of backwards here but You can see that familiar a RGB format if you just look at the kind of a double Definition of this 32-bit value so that you kind of can see it's a RGB instead of b g r a Just because of how things flip with ending this Okay Pretty simple stuff, right? That's why I like bitmaps. It's very simple So we're going to make some functions here to help us draw bitmaps The first one is something called write bitmap and that's just going to basically take Your array of pixel data, which is going to be pointed to by register RSI in this case as well as the dimensions of your image data and Write that out to a file descriptor So this is going to write the headers basically going to do everything That you know the professor was talking about was being so easy I'm going to write this header out and a fill in these slots based off image width and height and then it's going to Write the actual pixel array data at the very end So this is going to be a function that we're going to use to write the bitmaps to a file Next We have a set pixel this function is just going to turn a pixel at a certain position to a certain color So you're going to pass in The pixel array data start address in RDI You're going to pass in the color in ESI and here We're going to talk about things in terms of the 32 bit register because everything here is 32 bits The colors themselves are 32 bits. So that's why this register ESI which includes You know inside it the the color of the pixel that you're setting That's why it's 32 bit register but in addition you notice that the Even the width and the height of our image have also been set in a 32 bit register and the reason for that is Because you only have four bytes into which to encode this information So if you're pixel with it only fit in four bytes, you only have four bytes divided in so yeah That's why those values are 32 bits Okay, so yeah, it's a pixel. That's a pixel color get pixel Basically queries a pixel color. That's basically This tool if you have paint you kind of query You know colors of different things I can query the color of this yellow and use yellow. It's like the Color picker tool, I guess it's called Okay That's what get pixel does it just gets the pixel color at a certain location We have set line set line is going to draw a line for us It's gonna rasterize a line between a start point and an endpoint Set circle set circle will do the same thing for a circle It will rasterize a circle with a given center and a radius and then Set fill that will fill a Boundary so you have some kind of boundary shape. It will fill that with a certain color like for example this Fill tool on the top left here. I can just fill this boundary with orange and you can see that whole boundary filled with orange so that's gonna be one function we have and Yeah, so I remember well, I should say that all these things here are using a different positioning scheme So typically when you look at Images you should think about the pixel at the top left to be Pixel zero zero and actually if we open up in Gimp, you can see an example of this So if you if you look here Kind of on the top you can see the rulers here on the top of the image Basically pixel zero zero is at the top left and I can go all the way here and I can actually look at which pixels This is pixels zero zero. This is pixel five two So I kind of can see if you look down here at the bottom left the position within the image. Anyway, if you recall that's not how the Array data was defined the array defines pixels zero at the bottom left so we're going to have to convert between the pixel array definition of pixel zero and the Coordinates that we're used to using to identify Pixels for example pixel six even though it's called pixel six in the image array data. This would actually be pixel zero to or sorry to zero in Inconventional understanding, okay So now to access a pixel That's what I'm just talking about. So basically you can see here We have some image. I guess it's with w w equals one two three four five six pixels and height five pixels h And so based off that understanding the bottom left pixel is that address? offset zero You know zero times four bytes per pixel, but one pixel above pixel zero is W pixels away. So that's w times four bytes Similarly, this pixel here is 2w times four bytes and this pixel here is 2w plus one Times four bytes. That's the offset from the start or address of this array So the question is how can we relate this? addressing to an x and a y position and it's pretty straightforward all you do is If you're measuring from the top left, which is this case we are this pixel, which is zero one two three zero one so pixel three one You can just basically compute the number of Basically image rows between this pixel and the bottom, which is I guess h minus y minus one multiply that by the width of the image data w and then add x which is just the horizontal offset multiply that entire expression by four bytes and that would be able to point to a given address based off a given x and y position in an image that is W by h pixels in dimension a lot of words not very much meaning just a pretty straightforward explanation there okay now talking about some rasterization, so There's one very Common way to enter a good way to rasterize in this case a line and you can look up the press and ham You know algorithm or the midpoint algorithm. They're pretty much one of the same And basically the way it works is in layman's terms You know see you had a line like this line here in red with a it says it well I say a gentle slope downwards You step through all values x Between x zero and x one So this would be x zero and this would be x one Step through all values x Occasionally incrementing y which goes from y zero to y one if the pixel Below is a better approximation in a pixel above. What does that mean? Well basically here? So if this was your start pixel in in blue here You have two options for your Next pixel either this one on top or the one on the bottom and we intuitively as humans can say well one on the top is a much a better pixel to pick but the question is Will it always be not always you can't always pick the pixel to the right because you know that would eventually deviate from Eventually deviate from your line quite a bit as you can see and so you have to be able to pick and know When am I supposed to be moving down? You know is my is my directory supposed to be you know like this Like how am I supposed to be drawing this? You know is this the right pixel or not? Is this the right pixel? How do you know and so that's what we're talking about in this rasterization? slide Okay, let me fix that really quick and so yeah, there's two candidate pixels here the top one in the bottom one And so the question is how do you tell which pixel to choose? Well the way it works is in this midpoint algorithm or Breslin ham algorithm you evaluate based off the relative position of this Line that you're trying to plot and the midpoint of the next pixel and the idea is If the line passes above This midpoint so if the red line is above this green midpoint You color in the top pixel if The line is below the green dot you color in this pixel That's the entire algorithm So if you can do it into to understand that you're well on your way You can skip this part of the video if not there's some math that can go into it And even if you're curious about the math we're going to discuss it right now And so yeah, they basically you're going to increment why based off that so if this was the right pixel to pick You would not be incrementing why right the wide value is the which row you're in basically and If you were below the screen dot you would be incrementing why right from here to here as an increment in why? So yeah, that's how that works So here's the math that goes into this So Basically It's not that hard. It's pretty straightforward. Just take D Then the letter D to be the vertical distance that the line is let's say below and you could inverse this if you wanted But I'm just saying let D be the distance Below the next midpoint. So how far below this red line is relative to this green dot So in this case, we're actually above the the green dot. So our distance is negative D and then take dy and dx which are just the relative Kind of slope components of this line. And so in this case, I've just taken the entire section here. So let's say X zero is here and x1 is here Well, then dy would be to and dx would be six So our slope is one-third right what I say it was dy over dx so that's two over six or yet one-third But we don't really care so much about the actual division. We're going to be tracking things in terms of the effect that these components have on the distance D and So you can manipulate this so basically the green dot represents the slope of one half or yeah one half And so because of that you kind of can break apart this dy over dx idealized value And you know multiply both sides by dx subtract everything to equals zero and your d expression looks like this so D equals dy minus dx over two that is the Equation that represents how far below the red line is from the green dot and again because Everything here is a ratio. We really and we're going to be comparing things relative to zero We don't really need to have a an accurate Scale for this as long as we have the I guess all the matters of the sign. We don't care so much about how Magnified this is and so yeah, you could take the actual dy over dx value Or you could scale it by ten or by a thousand It's not going to make a difference for the algorithm to come And that's going to be important in a second But yeah, so basically the way it works is if D is below zero or let's say equal to zero that means that Your your red line is above the green dot meaning choose the upper pixel which means For the next pixel in your list of pixels that you're drawing Increment X which means go from here to here, but you're not going to increment Y So you're not going to go from here to here and The net last thing that you do is you have to adjust D which D just keeps track of your relative distance between the red line and the Subsequent green dot because you're always going to be looking at the midpoint of the current pixel and comparing relative to that And so if you are at this Future location. Well the midpoint now for this pixel is Let me draw it in orange is here And at that point you're now below that That midpoint and so you would then follow the second Possibility second option here So that's if D is greater than zero meaning that the red line is indeed below the midpoint you choose the lower pixel So in this case you would be Picking this pixel. So that would represent an increment in both X and Y So you're going from here to here that's incrementing X here to here is incrementing Y and At this point you have to again keep track of your relative distance. So in this case Well, I'm really sick of it before I say that above this back here this adjustment that we made for the Relative distance basically because we incremented X the line Overcross that one pixel is expected in our distance evaluation here to decrease by dy That's why we subtracted our guess we added dy to our Track of the distance value because we're measuring distance below the midpoint in this case For the second pixel that we're talking about here. We're both going to be doing that. We're going to be Dropping by dy But we're also going to have to accommodate the fact that we've dropped in dy And so now our next midpoint is actually below and so Let me draw that here if we if we're eventually going to picking this pixel which is the next pixel to go through the Corresponding midpoint is actually here. And so you have to make another Accommodation for that in the distance value long story short. You just increment d by dy minus dx And so things of note to this is that basically this particular algorithm, which again, this is the entire algorithm Basically, you just keep progressing through your image going from your initial X zero Y zero to your Your target X one Y one just incrementing X every iteration you can see here both options involve increment in X and Then you're sometimes incrementing Y Based off whether or not you need to based off what we talked about before So Yeah, that's that process and you should repeat this process for every pixel and clear out of pixels in the x direction Now that works in this particular example for slopes that are less than one Moving from left to right. So you have to be able to either in Pre-processing pre-processing or post-processing you have to be able to either Reverse or reflect that algorithm to handle all cases. So you may not be going from left to right You may be going from right to left. So you may have to invert your or switch your X zero Y zero and your X one Y one just flip them around and You might need to instead of going at a very soft gentle slope of less than one Maybe go in a greater than one in which case Just have to basically switch in this algorithm Everything to do with Y With everything to do with X just flip them around and the algorithm will work. So basically. Yeah, that's how it works the other thing to note is that This factors here this divided by two factor and this one half here and all this different stuff It's not really ideal when it comes to integer math And so you can scale everything here by a factor of two to get rid of this division Which would also mean you have to scale this by two and And this by two etc But that's fine. We can do that Okay so this is a simple algorithm for Rasterizing a line and there's other algorithms for rasterizing other shapes like Circles and conic sections ellipses things like that. I won't talk about them here that though this video would be like two hours long and so just go on Bing and type in press and ham Circle drawing algorithm or midpoint circle drawing algorithm and you'll see it bunch of derivations for those sets of things Or if you're curious look into the code because we do have a circle rasterization function that you can just see how that works in the code Okay, now the last thing I want to talk about was flood fill interestingly enough this tool is Called flood fill and so if you want to you know help blossom here Fill the heart with the color pink Well, we're gonna use flood fill to do just that and the way that works at least I think the simplest way to implement that is using recursion Which may or may not be good, but it's very simple to implement and so let's talk about how this works So here is the entire algorithm so basically you start somewhere inside the heart inside the boundary and Record the initial color of that pixel. So I picked this pixel here initial color of that pixel was white So now it's we want to make it pink want to make this whole heart pink. So our initial color is obviously white So the first thing we do well is we call flood fill on that pixel And here is the flood fill Function, this is the entire algorithm So first thing we do is we check if this pixel is outside the boundaries So let's say we picked the pixel over here. Oh crap. What did I do? Let's see you pick the pixel out here Obviously that pixel is not in the boundaries. So you're gonna get This pixel is actually this pixel here, right? If you think about it, that's the pixel to the left of this one But above and below you have an issue right this pixel down here and this pixel up here You're gonna maybe get a seg fault You're gonna get some memory accessing issue if you try to access these pixels So just if if you're trying to flood fill outside the boundary Don't just don't do it. So it says if outside the boundary return Next thing is if the pixel is a different color from the original also return. So let's say you picked You know your color is supposed to be white if if the pixel that you're on happens to be red or orange or green Stop you're not supposed to fill this pixel You're only supposed to fill pixel pixels of the same color like you know her headband here, you know filling this Fills all the pixels of that red color, but does not fill any of the black pixels in the boundary, right? Okay, that's these first two conditions. So it just says don't don't bother with this pixel if these conditions are true Then all you do is you've colored this pixel. So if you picked this pixel to start with That's the first step you've colored that pixel great now it says a Call this function in the same function on the pixel above the pixel below pixels to the right and pixel pixel to the left and So in this case, we're going to be calling flood fill on the pixel above this one So now we're here. So the question is is this pixel in the boundaries? Yes, is this pixel white? Yes, in that case color this pixel Now at this pixel we're going to flood fill Pixel above pixel below pixel to the right to the left in which case we flood fill this pixel and Then we fill this pixel and then this pixel on this pixel and then when we're here We're going to flood fill this pixel But the question is is this pixel white the answer is no and So we're going to flood fill this pixel right or I guess it would be this this pixel and Then this pixel and then this pixel or whatever it happens to be basically you're just going to keep calling flood fill on All the pixels around the pixel that you started at in this recursive Format until every pixel is exhausted out of your stack Very simple to implement look at this. It's a very short little function like this. Okay, cool Put that out of the way that pretty much covers the basics of rasterization So we'll look at the code so I have two functions actually two examples here one is just the basics of bitmaps That's just gonna be making a simple bitmap from pre-existing binary data that you have in an inner race somewhere already That's just gonna write a bitmap file And then we have a example B here, which is all the basic drawing operations that I explained previously So drawing pixels probing colors drawing lines strong circles custom shapes as well as filling different regions with the flood fill algorithm and So with that Let's see the code So the first thing I want to look at is All the functions so these are all the functions that I talked about before and this is just the implementations And these are all available on the Repository so you can take a look. This is the let's start with the set pixel function This is the most simple of all the functions. This just sets a pixel out of certain position and you can see This implements that kind of mathematics that I talked about before Let me show you Basically this type of accessing mathematics and this is relevant for both the set pixel and the get pixel functions That's all that's going on in this example here As well as the get pixel function here It's the same mathematics Then we have the right bitmap function all this does as you can actually see down here I have Pre-encoded the entire bitmap header and the only thing that you have to Supply into this header is everything where it says reserve So this says you know reserved for bitmap width reserved for bitmap height This says reserved for a size of the map file, which again is a function of width and the height and then down here This is reserved for the size of the bitmap array itself So all those things are just passed into this function and this function writes both the header as well as the entire bitmap pixel data to a File descriptor which you've already opened With some other method Okay, that's right bitmap Only three other functions that we have one is set line So this set line function implements that midpoint line rasterization Discussion and you can see here. It actually uses the set pixel function as a Dependency it calls that to set each pixel and then we have the set circle function Which is a similar algorithm for rasterizing a circle. I didn't discuss the details of that But you can look it up And then the set fill function this implements the flood fill algorithm that we talked about before so you can see here all it does is it kind of saves the initial pixel color and then Jumps or actually calls this loop and then this loop just implements those conditionals I talked about before are we in the boundaries? Are we the right color to fill and then all it does as you kind of Can see here it calls the flood fill recursively on the pixel above on the pixel below On pixel to the left and to the right and that's the entire algorithm. So with that out of the way That's all of the functions that we've implemented for this particular set of examples And now I'm just going to show you What we have so in this directory, which you'll find on the SWHUB suppository We have two examples one is the basics of bitmaps the other is all the drawing operations that we talked about before So let's go into the first example That's just the basics of bitmaps So if I uh If I run this well, let me show you the code first. Let's open the code So all this has here Is just a few includes it has includes to open a file To close a file, which you don't actually need by the way. You can skip this one Um, and then the function that writes bitmaps. In fact these Elf executables don't even have a print buffer. There's nothing to support printing in these Executables because we're not printing all we're doing is writing files using a syscall And so here you can see we just have basic functions to open files close files and in this case Write a bitmap to a file Okay So the first thing is we open And slash create the bitmap file. You can see a previous episode talk about file creation and opening Then we write a bitmap And then we close the file and leave the executable. And so what are we writing? We're writing a Well writing to a file the script that we previously opened and we're writing an image Of a size four by two pixels And down here is the actual image array and this was the same array that I discussed Here And so if we run this Close out of this run this You'll see I now have this uh bitmap Windows bitmap file and I can open that in gimp And we could see how that looks you can see it's a very simple image again It's just four pixels by two pixels if I zoom in you can see I have You know a set of opaque and not so opaque blue pixels green pixels red pixels on white pixels Just as we intended so our right bitmap function works and we understand the inner Meaning of this of the bits that define the colors in this array Okay That example was pretty simple now the more elaborate example. So Let's go into example b In this Well, let me run it first so you can see what we're going for Just to refresh your memory. So if I open this Resulting bitmap file in gimp you can see here Couple things that we've got So this function is going to and I'll explain it, you know when I go through the file The first thing it does is it writes this blue pixel It generates this blue pixel on a completely transparent background Because remember if the entire array is initialized to zeros that means the entire array is black And completely transparent So yeah, so basically it drops a blue pixel right here on a completely transparent background Then the next thing it does is it basically queries this pixel color And it adds 100 green to it And it writes that pixel Diagonally from it So we're going to call basically get pixel on this pixel Add green to it Which would give you a basically a cyan pixel that we draw next to it here Next thing it does is it draws this white line So it plots with this set line function a white line from This pixel down to this pixel Or ultimately ultimately you could draw it from this pixel down here up to this pixel. It would work all the same And the next thing it does is it draws this green Circle here. So that uses the set Circle function and it will take in a center of the circle in terms of x and y position As well as a radius of the circle, which is the distance from that center to the outsides And then it will draw that green circle The next thing the function does is it Fills this region with orange Pretty simple that uses the flood fill algorithm Then I draw this triangle This triangle is just a series of black lines that we've rasterized with the set line function So it draws all three boundaries And then it flood fills the center of this triangle with yellow Then I draw this blue quadrilateral thing here again with four calls to that set line function And then I flood fill the center of that with in this case a transparent red background And then lastly I flood fill everything else around this with pink And you can see it It stopped at the boundaries Otherwise I would have had a segfault or something right so yeah Everything around these images everything around these graphics these shapes Is flood filled with magenta Okay, so that's kind of the visual what we just did and now I'll show you all the calls to functions inside this assembly file So in this case again, I have the The previous functions from the previous assembly file So I have file open file close as well as write bitmap As well as the exit function, but I also have all the other geometric rasterization and other routines that we developed so we have Set pixel set line set circle set fill and get pixel And so here I'm just going to go through the process that we have in this file just so you can Know when you take a look First thing we do is we open and create that file then we set that first blue pixel Then we grab the color of that blue pixel here with the get pixel function Then we set we change that blue to cyan by making it 100 more green as you can see here with this Add an instruction then we are making the pixel diagonal from that cyan Then we are making that white line here. You can see I'm passing in x zero y zero x one and y one And the initial color or guess the target color is All f's which is white Then we make a green circle here. You can see I passed in the You know, I'm passing in every single time the dimensions of the Image as well as the target color in this case. I'm passing in the center of the circle both x and y as well as the radius Then I'm filling that circle with The color in this case orange Now I'm drawing the black triangle in this case. You can see I'm drawing one line from coordinates 40 25 to 38 42 another line here from different coordinates And a third line here in different coordinates and all those coordinates should make a triangle Then I'm filling that triangle with the color and now I'm drawing it in the same way that blue Quadrilateral with four calls to the set line function here And then I'm filling that quadrilateral with Um a transparent red color here. You can see the first value is 7 f. So we're 50 opaque And then lastly I'm filling the background to magenta Then I'm writing the bitmap out closing the file and ending the program so again that generates a Bitmap dot bmp which happens to be Around uh 12k really and then we are uh That bitmap is the one I showed before that's just this graphic here So with that you now understand hopefully the basics of What a bitmap is header the actual data itself understand the basics or at least how to Um understand the basics how to look up the basics what to search for rasterization of Lines and circles and other geometric entities and how we can turn those kind of algorithmic implementations Into actual colors on a screen with the you know the bitmap file itself So with that I think we're done. I want to thanks uh, you guys for your time your attention And your nice comments, and if you want to hang out we have a Top secret discord server check the blast link in the description, and I'll see you there