1 00:00:00,000 --> 00:00:09,500 >> [MUSIC PLAYING] 2 00:00:09,500 --> 00:00:12,350 >> ZAMYLA CHAN: It was Miss Scarlett with the candlestick. 3 00:00:12,350 --> 00:00:13,560 Whodunit? 4 00:00:13,560 --> 00:00:15,030 Well, we're going to find out. 5 00:00:15,030 --> 00:00:20,870 In the board game Clue, you might be given a physical red image. 6 00:00:20,870 --> 00:00:24,120 And that image is very red and spotty, and your job is to 7 00:00:24,120 --> 00:00:25,490 reveal the hidden message. 8 00:00:25,490 --> 00:00:29,740 And usually you're provided with a red magnifying glass, or a red screen to 9 00:00:29,740 --> 00:00:31,410 reveal that hidden message. 10 00:00:31,410 --> 00:00:33,340 Well, we're going to mimic that. 11 00:00:33,340 --> 00:00:37,960 >> In whodunit, you're given a bitmap image that looks very spotty and red, 12 00:00:37,960 --> 00:00:43,430 and then run the whodunit program to reveal a hidden message. 13 00:00:43,430 --> 00:00:45,650 >> So let's break this into steps. 14 00:00:45,650 --> 00:00:50,390 First, you want to open the file-- the clue that you've been given. 15 00:00:50,390 --> 00:00:53,880 And then also create a verdict bitmap file. 16 00:00:53,880 --> 00:00:58,240 Then you want to update the bitmap header info for the verdict outfile. 17 00:00:58,240 --> 00:00:59,920 More on that later. 18 00:00:59,920 --> 00:01:04,319 And then you're going to read into the clue, scanline, pixel by pixel, 19 00:01:04,319 --> 00:01:07,320 changing the pixel colors as necessary, and writing 20 00:01:07,320 --> 00:01:08,960 those into the verdict-- 21 00:01:08,960 --> 00:01:12,000 pixel by pixel into the verdict scanline. 22 00:01:12,000 --> 00:01:13,780 >> How do we start going about this? 23 00:01:13,780 --> 00:01:16,940 Well, luckily, we have copy.c in the distribution code. 24 00:01:16,940 --> 00:01:21,240 And this is going to prove quite useful to us. 25 00:01:21,240 --> 00:01:29,700 Copy.c opens a file, reads in that infile's header, and then updates the 26 00:01:29,700 --> 00:01:31,070 outfile's header. 27 00:01:31,070 --> 00:01:37,010 And then it reads each pixel in the scanline, pixel by pixel, and then 28 00:01:37,010 --> 00:01:42,390 writes that pixel into the outfile. 29 00:01:42,390 --> 00:01:45,020 >> So, your first step might be to run the following 30 00:01:45,020 --> 00:01:46,420 command in the terminal-- 31 00:01:46,420 --> 00:01:50,270 cp copy.c whodunit.c. 32 00:01:50,270 --> 00:01:55,320 This will create a copy of copy.c named whodunit.c. 33 00:01:55,320 --> 00:01:58,320 So our first step to open the file, well, there's an exact 34 00:01:58,320 --> 00:02:00,070 replica of that in copy.c. 35 00:02:00,070 --> 00:02:03,360 So I'll leave you to look at that. 36 00:02:03,360 --> 00:02:07,860 >> What we're dealing with in this PSET is file I/O, basically taking files, 37 00:02:07,860 --> 00:02:10,229 reading, writing, editing them. 38 00:02:10,229 --> 00:02:12,650 How do you first open a file? 39 00:02:12,650 --> 00:02:16,800 Well, you're going to declare a file pointer, and then you call the 40 00:02:16,800 --> 00:02:18,670 function fopen. 41 00:02:18,670 --> 00:02:23,150 Pass in the path, or the name of that file, and then the mode that you want 42 00:02:23,150 --> 00:02:24,700 to open that file in. 43 00:02:24,700 --> 00:02:28,620 Passing in an r will open foo.bmp for reading. 44 00:02:28,620 --> 00:02:35,670 Whereas fopen with passing in a w will open bar.bmp, for writing the file and 45 00:02:35,670 --> 00:02:37,020 actually editing it. 46 00:02:37,020 --> 00:02:41,970 >> So now that we've opened the file, our next step is to update the header info 47 00:02:41,970 --> 00:02:43,230 for the outfile. 48 00:02:43,230 --> 00:02:44,610 What's a header info? 49 00:02:44,610 --> 00:02:48,160 Well, first we need to know what a bitmap is. 50 00:02:48,160 --> 00:02:51,000 A bitmap is just a simple arrangement of bytes. 51 00:02:51,000 --> 00:02:55,480 And they're declared in this file here, bmp.h, with a bunch of 52 00:02:55,480 --> 00:02:58,610 information of what a bitmap is actually made out of. 53 00:02:58,610 --> 00:03:05,730 But what we really care about is the bitmap file header, right here, and 54 00:03:05,730 --> 00:03:08,460 the bitmap info header, over here. 55 00:03:08,460 --> 00:03:13,170 The header is composed of a couple of variables that will prove very useful. 56 00:03:13,170 --> 00:03:18,400 There is biSizeImage, which is the total size of the image in bytes. 57 00:03:18,400 --> 00:03:20,890 And this includes pixels and padding. 58 00:03:20,890 --> 00:03:24,210 Padding is very important, but we'll get to that later. 59 00:03:24,210 --> 00:03:30,000 >> BiWidth represents the width of the image in pixels minus the padding. 60 00:03:30,000 --> 00:03:34,220 BiHeight is then also the height of the image in pixels. 61 00:03:34,220 --> 00:03:38,240 And then the BITMAPFILEHEADER and the BITMAPINFOHEADER, as I mentioned 62 00:03:38,240 --> 00:03:40,900 earlier, those are represented as structs. 63 00:03:40,900 --> 00:03:45,410 So, you can't access the file header itself, but you'll want to get to 64 00:03:45,410 --> 00:03:47,370 those variables inside. 65 00:03:47,370 --> 00:03:48,170 >> OK. 66 00:03:48,170 --> 00:03:50,600 So how do we update the header info? 67 00:03:50,600 --> 00:03:54,020 Well, first we have to see whether we need to change any information from 68 00:03:54,020 --> 00:03:58,480 the infile, the clue, to the outfile, the verdict. 69 00:03:58,480 --> 00:04:00,250 Is anything changing in this case? 70 00:04:00,250 --> 00:04:04,320 Well, not actually, because we're going to be just changing the colors. 71 00:04:04,320 --> 00:04:07,550 We're not going to be changing the file size, the image size, the width, 72 00:04:07,550 --> 00:04:08,310 or the height. 73 00:04:08,310 --> 00:04:14,010 So you're all right for now by just copying each pixel. 74 00:04:14,010 --> 00:04:14,840 >> OK. 75 00:04:14,840 --> 00:04:20,720 So now let's look at how we actually can read every pixel from the file. 76 00:04:20,720 --> 00:04:23,640 Another file I/O function will come into play-- 77 00:04:23,640 --> 00:04:24,700 fread. 78 00:04:24,700 --> 00:04:28,440 It takes in a pointer to the struct that will contain the bytes that 79 00:04:28,440 --> 00:04:30,110 you're reading. 80 00:04:30,110 --> 00:04:31,890 So you're reading into that. 81 00:04:31,890 --> 00:04:36,090 And then you pass in a size, which is the size of every element that you 82 00:04:36,090 --> 00:04:37,360 want to read. 83 00:04:37,360 --> 00:04:40,640 Here, the function sizeof will come in handy. 84 00:04:40,640 --> 00:04:45,570 Then you pass in number, which represents the number of elements of 85 00:04:45,570 --> 00:04:47,480 size to read. 86 00:04:47,480 --> 00:04:51,180 And then finally, inptr, which is the file pointer that you're 87 00:04:51,180 --> 00:04:52,530 going to read from. 88 00:04:52,530 --> 00:04:58,650 So all of those elements are inside inptr and they're going to data. 89 00:04:58,650 --> 00:05:01,660 >> Let's look at a little example. 90 00:05:01,660 --> 00:05:07,590 If I want to read into data two dogs, well, I can do it one of two ways. 91 00:05:07,590 --> 00:05:15,250 I can either read in two objects of size dog from my inptr, or I can read 92 00:05:15,250 --> 00:05:19,280 in one object the size of two dogs. 93 00:05:19,280 --> 00:05:23,580 So you see that depending on the way that you arrange size and number, you 94 00:05:23,580 --> 00:05:25,840 can read in the same number of bytes. 95 00:05:25,840 --> 00:05:28,720 96 00:05:28,720 --> 00:05:33,020 >> So now, let's change the pixel color as we need. 97 00:05:33,020 --> 00:05:37,320 If you look at bmp.h again, then you'll see that at the bottom 98 00:05:37,320 --> 00:05:42,920 RGBTRIPLEs are another struct, where they are comprised of three bytes. 99 00:05:42,920 --> 00:05:49,220 One, rgbtBlue, rgbtGreen, and rgbtRed. 100 00:05:49,220 --> 00:05:52,480 So each of these represents the amount of blue, the amount of green, and the 101 00:05:52,480 --> 00:05:57,250 amount of red inside this pixel, where each amount is represented by a 102 00:05:57,250 --> 00:05:58,670 hexadecimal number. 103 00:05:58,670 --> 00:06:04,370 >> So ff0000 will be a blue color, because it goes from blue, 104 00:06:04,370 --> 00:06:05,850 to green, to red. 105 00:06:05,850 --> 00:06:09,300 And then all f's will be white. 106 00:06:09,300 --> 00:06:13,440 Let's take a look at smiley.bmp, which you have in your distribution code. 107 00:06:13,440 --> 00:06:15,690 If you open it in just an image viewer, then you'll 108 00:06:15,690 --> 00:06:17,080 just see a red smiley. 109 00:06:17,080 --> 00:06:20,380 But taking a deeper dive in, we'll see that the structure 110 00:06:20,380 --> 00:06:22,340 of it is just pixels. 111 00:06:22,340 --> 00:06:25,880 We have white pixels, and then red pixels. 112 00:06:25,880 --> 00:06:31,000 The white, ffffff, and then all of the red pixels I've colored in for you 113 00:06:31,000 --> 00:06:35,440 here, and you see that they're 0000ff. 114 00:06:35,440 --> 00:06:39,760 Zero blue, zero green, and full red. 115 00:06:39,760 --> 00:06:45,350 And since smiley is eight pixels wide, we don't have any padding. 116 00:06:45,350 --> 00:06:47,360 All right. 117 00:06:47,360 --> 00:06:53,310 >> So if I were to assign different values to an RGBTRIPLE and I wanted to 118 00:06:53,310 --> 00:06:58,350 make it green, then what I would do is I would declare an RGBTRIPLE, named 119 00:06:58,350 --> 00:07:02,660 triple, and then to access every byte within that struct I 120 00:07:02,660 --> 00:07:04,030 would use the dot operator. 121 00:07:04,030 --> 00:07:08,430 So triple.rgbtBlue, I can assign that to 0. 122 00:07:08,430 --> 00:07:13,460 Green I can assign it to full-- any number, really, between 0 and ff. 123 00:07:13,460 --> 00:07:15,470 And then red, I'm also going to say 0. 124 00:07:15,470 --> 00:07:19,160 So then that gives me a green pixel. 125 00:07:19,160 --> 00:07:23,030 >> Next, what if I want to check the value of something? 126 00:07:23,030 --> 00:07:27,250 I could have something that checks whether the triple's rgbtBlue value is 127 00:07:27,250 --> 00:07:31,080 ff and then print, "I'm feeling blue!", as a result. 128 00:07:31,080 --> 00:07:35,640 Now, that doesn't necessarily mean that the pixel is blue, right? 129 00:07:35,640 --> 00:07:40,060 Because the pixel's green and red values could also have non-0 values. 130 00:07:40,060 --> 00:07:43,470 All that this means, and all that this is checking for is 131 00:07:43,470 --> 00:07:45,610 for a full blue color. 132 00:07:45,610 --> 00:07:50,050 But all pixels could also have partial color values, like this 133 00:07:50,050 --> 00:07:52,180 next example here. 134 00:07:52,180 --> 00:07:55,400 >> It's a little harder to see what this image is now. 135 00:07:55,400 --> 00:08:00,320 This looks a little bit more like the clue.bmp that you'll be given. 136 00:08:00,320 --> 00:08:03,600 Now, physically, you might solve this, because there's a lot of red, by 137 00:08:03,600 --> 00:08:07,040 holding up a red screen to the image so that the other colors can appear. 138 00:08:07,040 --> 00:08:10,968 So how do we mimic this with c? 139 00:08:10,968 --> 00:08:15,640 Well, we might remove all red from the image entirely. 140 00:08:15,640 --> 00:08:21,870 And so to do that we'd set every pixel's red value to 0. 141 00:08:21,870 --> 00:08:25,020 And so the image would look a little bit like this, where we have no red 142 00:08:25,020 --> 00:08:26,300 whatsoever. 143 00:08:26,300 --> 00:08:29,390 >> We can see the hidden message a little bit more clearly now. 144 00:08:29,390 --> 00:08:31,730 It's another smiley face. 145 00:08:31,730 --> 00:08:33,870 Or maybe we could use another method. 146 00:08:33,870 --> 00:08:36,480 Maybe, we could identify all of the red pixels-- 147 00:08:36,480 --> 00:08:41,100 that is, all of the pixels with 0 blue, 0 green, and 0 red-- 148 00:08:41,100 --> 00:08:43,169 and change those to white. 149 00:08:43,169 --> 00:08:45,470 And our image might look something like this. 150 00:08:45,470 --> 00:08:48,250 A little bit easier to see. 151 00:08:48,250 --> 00:08:51,170 >> There are lots of other ways to uncover the secret message as well, 152 00:08:51,170 --> 00:08:53,730 dealing with the color manipulation. 153 00:08:53,730 --> 00:08:57,050 Maybe you might use one of the methods that I mentioned above. 154 00:08:57,050 --> 00:08:59,600 And additionally, you might want to enhance some colors 155 00:08:59,600 --> 00:09:02,620 and bring those out. 156 00:09:02,620 --> 00:09:06,190 >> So now that we've changed the pixel color, next we just need to write them 157 00:09:06,190 --> 00:09:08,500 in to the scanline, pixel by pixel. 158 00:09:08,500 --> 00:09:11,860 And yet again, you'll want to look back to copy.c, if you haven't copied 159 00:09:11,860 --> 00:09:18,170 it already, and look at the fwrite function, which takes data, a pointer 160 00:09:18,170 --> 00:09:23,230 to the struct that contains the bytes that you're reading from, the size of 161 00:09:23,230 --> 00:09:26,610 the items, the number of items, and then the outptr-- 162 00:09:26,610 --> 00:09:29,450 the destination of those files. 163 00:09:29,450 --> 00:09:34,010 >> After you write in the pixels, you'll also have to write in the padding. 164 00:09:34,010 --> 00:09:34,970 What is padding? 165 00:09:34,970 --> 00:09:38,670 Well, every rgbt pixel is three bytes long. 166 00:09:38,670 --> 00:09:43,670 But, the scanline for a bitmap image has to be a multiple of four bytes. 167 00:09:43,670 --> 00:09:47,650 And if the number of pixels isn't a multiple of four, then we need to add 168 00:09:47,650 --> 00:09:48,880 this padding. 169 00:09:48,880 --> 00:09:51,420 Padding is just represented by 0s. 170 00:09:51,420 --> 00:09:54,380 So, how do we write, or read this? 171 00:09:54,380 --> 00:09:59,280 Well, it turns out that you can't actually fread padding, but you can 172 00:09:59,280 --> 00:10:00,970 calculate it. 173 00:10:00,970 --> 00:10:04,400 >> In this case, the clue and the verdict have the same width, so the 174 00:10:04,400 --> 00:10:05,910 padding is the same. 175 00:10:05,910 --> 00:10:09,370 And the padding, as you'll see in copy.c, is calculated 176 00:10:09,370 --> 00:10:11,790 with the below formula-- 177 00:10:11,790 --> 00:10:16,690 bi.biWidth times sizeof(RGBTRIPLE) will give us how many bytes the bmp 178 00:10:16,690 --> 00:10:18,280 has in every row. 179 00:10:18,280 --> 00:10:21,890 From there, the modulos and subtractions with 4 can calculate how 180 00:10:21,890 --> 00:10:25,610 many bytes must be added so that the multiple of bytes on 181 00:10:25,610 --> 00:10:27,250 every row is four. 182 00:10:27,250 --> 00:10:30,490 >> Now that we have the formula for how much padding we need, now 183 00:10:30,490 --> 00:10:31,610 we can write it. 184 00:10:31,610 --> 00:10:34,080 Now, I mentioned before, padding is just 0s. 185 00:10:34,080 --> 00:10:39,730 So in that case, we're just putting a char, in this case a 0, into our 186 00:10:39,730 --> 00:10:41,710 outptr-- our outfile. 187 00:10:41,710 --> 00:10:47,530 So that can just be fputc 0, comma outptr. 188 00:10:47,530 --> 00:10:52,400 >> So, while we've been reading into our file, file I/O has kept track of our 189 00:10:52,400 --> 00:10:57,440 position in those files with something called the file position indicator. 190 00:10:57,440 --> 00:10:59,350 Think of it as a cursor. 191 00:10:59,350 --> 00:11:03,550 Basically, it advances every time that we fread, but we have 192 00:11:03,550 --> 00:11:05,671 control over it, too. 193 00:11:05,671 --> 00:11:11,030 >> To move the file position indicator, you can use the function fseek. 194 00:11:11,030 --> 00:11:15,600 Where the inptr represents the file pointer that you're seeking in, the 195 00:11:15,600 --> 00:11:20,370 amount is the number of bytes that you want to move the cursor, and then from 196 00:11:20,370 --> 00:11:23,470 relates to the reference point from where your cursor is. 197 00:11:23,470 --> 00:11:26,770 If you pass in SEEK_CUR, that represents the current 198 00:11:26,770 --> 00:11:28,100 position in the file. 199 00:11:28,100 --> 00:11:31,020 Or you can use some other parameters. 200 00:11:31,020 --> 00:11:35,400 So, we might want to use fseek to skip over the padding of the in file. 201 00:11:35,400 --> 00:11:39,410 And again, if you're stuck, there's an example of that in copy.c. 202 00:11:39,410 --> 00:11:43,260 >> So now we've opened the file, the clue, and the verdict. 203 00:11:43,260 --> 00:11:46,450 We've updated the header info for our verdict, because every 204 00:11:46,450 --> 00:11:48,730 bitmap needs a header. 205 00:11:48,730 --> 00:11:52,280 We've then read into the clue's scanline, pixel by pixel, changing 206 00:11:52,280 --> 00:11:55,210 every color as necessary, and writing those into the 207 00:11:55,210 --> 00:11:57,340 verdict, pixel by pixel. 208 00:11:57,340 --> 00:12:01,550 Once you open verdict, you can see who the culprit, or what the secret 209 00:12:01,550 --> 00:12:02,850 message is. 210 00:12:02,850 --> 00:12:05,550 My name is Zamyla, and this was whodunit. 211 00:12:05,550 --> 00:12:12,864