1 00:00:00,360 --> 00:00:04,470 The reason that the filter operations still take the long time, 2 00:00:04,470 --> 00:00:08,350 is because our approach does a bunch of unnecessary work. 3 00:00:08,350 --> 00:00:10,130 Now what do I mean by that? 4 00:00:10,130 --> 00:00:12,010 If you took the prerequisite course, 5 00:00:12,010 --> 00:00:16,310 you might remember that when we first go to a photo in this scroll view. 6 00:00:16,310 --> 00:00:20,790 And it's displayed at its full size, that photo is quite large. 7 00:00:20,790 --> 00:00:25,120 With our current approach, we're applying a filter to that entire photo. 8 00:00:25,120 --> 00:00:30,120 And then displaying a 100 by 100 point thumbnail in the collection view. 9 00:00:30,120 --> 00:00:34,420 Essentially, we're using a large amount of resources to carry out an image processing 10 00:00:34,420 --> 00:00:39,640 operation on a large photo only to then shrink that down by a factor of 50. 11 00:00:39,640 --> 00:00:44,220 To avoid this, let's scale the image down and then apply the filter. 12 00:00:44,220 --> 00:00:47,740 When we do this, in addition to using background threads and 13 00:00:47,740 --> 00:00:51,580 operations, we'll have much quicker results. 14 00:00:51,580 --> 00:00:53,480 Now this won't take much code, but 15 00:00:53,480 --> 00:00:56,470 here's an overview of what we're going to implement. 16 00:00:56,470 --> 00:01:00,430 First, we're going to determine the size of the scaled down image. 17 00:01:00,430 --> 00:01:03,830 Then we'll scale this image down and after we do that, 18 00:01:03,830 --> 00:01:07,960 we'll use the scaled image for the filtration process. 19 00:01:07,960 --> 00:01:10,870 All three steps are actually relatively simple. 20 00:01:10,870 --> 00:01:15,880 Since we're creating images in the FiltrationImages lazy property, 21 00:01:15,880 --> 00:01:20,790 up at the top over here inside the PhotoFilter controller class, we can 22 00:01:20,790 --> 00:01:26,630 add the code here to scale down the image that we use before returning instances. 23 00:01:26,630 --> 00:01:30,870 Okay, so in here after we get this image, make some space. 24 00:01:30,870 --> 00:01:34,240 To start, letâ€™s determine the current images height and width. 25 00:01:34,240 --> 00:01:40,670 So say, let imageWidth equal image.size.width. 26 00:01:40,670 --> 00:01:47,012 We'll do that for the imageHeight as well, image.size.height. 27 00:01:47,012 --> 00:01:51,607 Now we know that the CollectionView that displays this image is sized to 28 00:01:51,607 --> 00:01:53,302 100 by 100 points. 29 00:01:53,302 --> 00:01:56,690 So we're gonna scale this image down to be that same size. 30 00:01:56,690 --> 00:02:01,052 But because these are rectangular images, we need to determine the height value, 31 00:02:01,052 --> 00:02:05,354 which we'll do by getting a ratio between the current width and the scaled width, 32 00:02:05,354 --> 00:02:07,928 and then applying that same ratio to the height. 33 00:02:07,928 --> 00:02:11,904 So say, let scaledWidth equal, 34 00:02:11,904 --> 00:02:16,732 now this is a CGFloat is equal to 100. 35 00:02:16,732 --> 00:02:20,140 We'll find out what the ratio is between the scaled width and the actual width. 36 00:02:20,140 --> 00:02:27,038 So scaledRatio equal scaledWidth divided by ImageWidth. 37 00:02:27,038 --> 00:02:30,683 And then to get the scaledHeight, 38 00:02:30,683 --> 00:02:36,320 we'll do scaledRatio times imageHeight. 39 00:02:36,320 --> 00:02:39,880 Okay, so now that we have the height that we want to get to, 40 00:02:39,880 --> 00:02:43,750 let's define a rect that decides to our new dimensions. 41 00:02:43,750 --> 00:02:48,973 So let rect equals CGRect, x and y are 0, 42 00:02:48,973 --> 00:02:53,005 the width is the scaledWidth and 43 00:02:53,005 --> 00:02:56,900 the height is a scaledHeight. 44 00:02:56,900 --> 00:03:01,060 To resize the image, we're going to create a temporary context. 45 00:03:01,060 --> 00:03:04,752 And this is because we're using a high level UIkit API used for 46 00:03:04,752 --> 00:03:06,394 rendering scaled images. 47 00:03:06,394 --> 00:03:11,642 So we'll say UIGraphicsBeginImageContext 48 00:03:11,642 --> 00:03:16,480 with a size, and we'll say rect.size. 49 00:03:16,480 --> 00:03:21,478 Remember, a context defines a drawing destination and we're providing the size 50 00:03:21,478 --> 00:03:24,819 of that destination a passing in that rect we defined. 51 00:03:24,819 --> 00:03:26,900 There are two ways of drawing images. 52 00:03:26,900 --> 00:03:31,440 One is on the screen and the other is in an off-screen buffer. 53 00:03:31,440 --> 00:03:35,970 A buffer is a region of memory used to store physical data. 54 00:03:35,970 --> 00:03:39,540 By calling UIGraphicsBeginImageContext, 55 00:03:39,540 --> 00:03:42,780 we're indicating that we want to draw to an off-screen buffer. 56 00:03:42,780 --> 00:03:47,450 And here we provide a size to indicate how much memory needs to be allocated. 57 00:03:47,450 --> 00:03:51,690 So rather than drawing it to screen, we've created a bitmap context, 58 00:03:51,690 --> 00:03:57,630 bitmap being a representation of the image in bits to draw this image off-screen. 59 00:03:57,630 --> 00:03:59,690 The context created here is temporary and 60 00:03:59,690 --> 00:04:03,315 we'll get rid of it immediately after getting our output image. 61 00:04:03,315 --> 00:04:08,060 Now once we have this context off-screen, we can ask the image to draw itself to fit 62 00:04:08,060 --> 00:04:11,410 the size defined by that buffer, defined by the rect. 63 00:04:11,410 --> 00:04:19,503 So we say image.draw, in rect. 64 00:04:20,692 --> 00:04:25,470 So UIkit is going to handle all the actual scaling logic here for us. 65 00:04:25,470 --> 00:04:29,895 All we need to do to obtain the scaledImage is to convert from the bit map 66 00:04:29,895 --> 00:04:33,571 representation in the context to an instance of UIImage. 67 00:04:33,571 --> 00:04:37,137 And while this is normally expensive, this is a small image, so 68 00:04:37,137 --> 00:04:38,862 we can do that pretty quickly. 69 00:04:38,862 --> 00:04:48,020 We'll say guard let scaledImage equal UIGraphicsGetImageFromCurrentImageContext. 70 00:04:48,020 --> 00:04:50,980 If it doesn't, if we can't scale this image to begin with, 71 00:04:50,980 --> 00:04:55,160 then we're not going to be able to get this array of filtration images. 72 00:04:55,160 --> 00:04:56,490 So we'll return an empty array. 73 00:04:57,510 --> 00:04:59,160 We're done, we've gotten the scaledImage, so 74 00:04:59,160 --> 00:05:02,050 we don't need our temporary context anymore. 75 00:05:02,050 --> 00:05:08,150 So we'll end it with another function called UIGraphicsEndImageContext. 76 00:05:08,150 --> 00:05:12,350 Now instead of applying the filter to the really large image, 77 00:05:12,350 --> 00:05:16,490 we'll apply it to a much smaller image and do a lot less work. 78 00:05:16,490 --> 00:05:21,006 So when initializing our instances of FiltrationImage to use in 79 00:05:21,006 --> 00:05:24,860 surpassing the image here, we'll say scaledImage. 80 00:05:24,860 --> 00:05:28,624 Now you'll see that that came up as error type, but once I build, 81 00:05:28,624 --> 00:05:30,068 this should all be okay. 82 00:05:30,068 --> 00:05:32,224 All right, let's finally run this app. 83 00:05:34,535 --> 00:05:36,100 [INAUDIBLE] Camera. 84 00:05:38,102 --> 00:05:41,930 Going to pick a photo, and there we go. 85 00:05:41,930 --> 00:05:44,332 Everything is blazing fast. 86 00:05:44,332 --> 00:05:48,627 So let's conclude this attempt by taking a snapshot of our code. 87 00:05:48,627 --> 00:05:52,083 So I'm going to hit stop, and from Source Control over here, 88 00:05:52,083 --> 00:05:53,750 I'm going to select Commit. 89 00:05:54,970 --> 00:05:57,280 And for the message down here, we need a message. 90 00:05:57,280 --> 00:06:03,990 We'll type, Finished operations based approach and commit our files.