Skip to content

Commit d99586a

Browse files
committed
Code extraction.
1 parent 408525c commit d99586a

File tree

6 files changed

+207
-22
lines changed

6 files changed

+207
-22
lines changed

FanOfAscii.xcodeproj/project.pbxproj

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
5E551EC52371FC3F00784365 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E551EC42371FC3F00784365 /* Assets.xcassets */; };
1212
5E6F7E592367B529008CC191 /* UserModules in Copy Book Contents */ = {isa = PBXBuildFile; fileRef = 5E6F7E582367B51E008CC191 /* UserModules */; };
1313
5E6F7E6C2368FC01008CC191 /* BookAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E6F7E5C2367B64A008CC191 /* BookAPI.swift */; };
14-
5E6F7E7D2368FF29008CC191 /* SharedCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7067A323678E220094BDEF /* SharedCode.swift */; };
14+
5E6F7E7D2368FF29008CC191 /* Image.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E7067A323678E220094BDEF /* Image.swift */; };
1515
5E70679F23678DC30094BDEF /* Modules in Copy Book Contents */ = {isa = PBXBuildFile; fileRef = 5E70679E23678DC00094BDEF /* Modules */; };
1616
5EA2E3CA2056F35B00416A35 /* LiveViewTestAppLaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5EA2E3C82056F35B00416A35 /* LiveViewTestAppLaunchScreen.storyboard */; };
1717
5EA2E3D42056F36800416A35 /* libBookCore.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5EF2F9AB2054BBF300191409 /* libBookCore.a */; };
@@ -20,6 +20,7 @@
2020
5EBEC2F0205B399300975D3F /* LiveViewHost.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5EBEC2EF205B396500975D3F /* LiveViewHost.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
2121
5EF2F97C2054B6E400191409 /* ManifestPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5EF2F97E2054B6E400191409 /* ManifestPlist.strings */; };
2222
5EF2F9B72054E6FB00191409 /* Chapters in Copy Book Contents */ = {isa = PBXBuildFile; fileRef = 5EF2F9B62054E6F900191409 /* Chapters */; };
23+
71371DA023F819BE009DC4C6 /* EventListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71DEC1A823F79FB10091A700 /* EventListener.swift */; };
2324
7174D39523F7CB5500A09FFB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7174D39423F7CB5500A09FFB /* AppDelegate.swift */; };
2425
7174D39723F7CBCF00A09FFB /* LiveView.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7174D39623F7CBCF00A09FFB /* LiveView.storyboard */; };
2526
7174D3B223F7D0D100A09FFB /* cat-thumb.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 7174D3A623F7D0D000A09FFB /* cat-thumb.jpg */; };
@@ -42,7 +43,6 @@
4243
71DD3E7223F79BA7001510FB /* ShowcaseImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71DD3E7123F79BA6001510FB /* ShowcaseImageView.swift */; };
4344
71DD3E7523F79BCC001510FB /* UIColor+StateColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71DD3E7423F79BCC001510FB /* UIColor+StateColors.swift */; };
4445
71DD3E7823F79C12001510FB /* ImagePickerDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71DD3E7723F79C12001510FB /* ImagePickerDataSource.swift */; };
45-
71DEC1AB23F79FB20091A700 /* EventListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71DEC1A823F79FB10091A700 /* EventListener.swift */; };
4646
71DEC1AC23F79FB20091A700 /* EventMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71DEC1AA23F79FB20091A700 /* EventMessage.swift */; };
4747
71DEC1B023F7A21E0091A700 /* IntroductionLiveViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71DEC1AF23F7A21E0091A700 /* IntroductionLiveViewController.swift */; };
4848
71DEC1CB23F7A2650091A700 /* GrayscaleHistogramEqualization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71DEC1B523F7A2650091A700 /* GrayscaleHistogramEqualization.swift */; };
@@ -173,7 +173,7 @@
173173
5E6F7E6D2368FC9A008CC191 /* ModuleOverridingBuildSettings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ModuleOverridingBuildSettings.xcconfig; sourceTree = "<group>"; };
174174
5E6F7E742368FF15008CC191 /* libUserModule.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libUserModule.a; sourceTree = BUILT_PRODUCTS_DIR; };
175175
5E70679E23678DC00094BDEF /* Modules */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Modules; sourceTree = "<group>"; };
176-
5E7067A323678E220094BDEF /* SharedCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedCode.swift; sourceTree = "<group>"; };
176+
5E7067A323678E220094BDEF /* Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = "<group>"; };
177177
5E80DF292342971E00595EB4 /* LiveViewTestApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LiveViewTestApp.entitlements; sourceTree = "<group>"; };
178178
5EA2E3BD2056F35A00416A35 /* LiveViewTestApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LiveViewTestApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
179179
5EA2E3C92056F35B00416A35 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LiveViewTestAppLaunchScreen.storyboard; sourceTree = "<group>"; };
@@ -327,6 +327,7 @@
327327
5E6F7E5B2367B632008CC191 /* Sources */ = {
328328
isa = PBXGroup;
329329
children = (
330+
71DEC1A823F79FB10091A700 /* EventListener.swift */,
330331
5E6F7E5C2367B64A008CC191 /* BookAPI.swift */,
331332
);
332333
path = Sources;
@@ -363,23 +364,23 @@
363364
5E7067A023678DED0094BDEF /* UserModules */ = {
364365
isa = PBXGroup;
365366
children = (
366-
5E7067A123678E010094BDEF /* UserModule.playgroundmodule */,
367+
5E7067A123678E010094BDEF /* FanOfAscii.playgroundmodule */,
367368
);
368369
path = UserModules;
369370
sourceTree = "<group>";
370371
};
371-
5E7067A123678E010094BDEF /* UserModule.playgroundmodule */ = {
372+
5E7067A123678E010094BDEF /* FanOfAscii.playgroundmodule */ = {
372373
isa = PBXGroup;
373374
children = (
374375
5E7067A223678E0D0094BDEF /* Sources */,
375376
);
376-
path = UserModule.playgroundmodule;
377+
path = FanOfAscii.playgroundmodule;
377378
sourceTree = "<group>";
378379
};
379380
5E7067A223678E0D0094BDEF /* Sources */ = {
380381
isa = PBXGroup;
381382
children = (
382-
5E7067A323678E220094BDEF /* SharedCode.swift */,
383+
5E7067A323678E220094BDEF /* Image.swift */,
383384
);
384385
path = Sources;
385386
sourceTree = "<group>";
@@ -517,7 +518,6 @@
517518
isa = PBXGroup;
518519
children = (
519520
71DD3E6223F7991F001510FB /* LiveViewSupport.swift */,
520-
71DEC1A823F79FB10091A700 /* EventListener.swift */,
521521
71DEC1AA23F79FB20091A700 /* EventMessage.swift */,
522522
);
523523
path = Core;
@@ -796,6 +796,7 @@
796796
isa = PBXSourcesBuildPhase;
797797
buildActionMask = 2147483647;
798798
files = (
799+
71371DA023F819BE009DC4C6 /* EventListener.swift in Sources */,
799800
5E6F7E6C2368FC01008CC191 /* BookAPI.swift in Sources */,
800801
);
801802
runOnlyForDeploymentPostprocessing = 0;
@@ -804,7 +805,7 @@
804805
isa = PBXSourcesBuildPhase;
805806
buildActionMask = 2147483647;
806807
files = (
807-
5E6F7E7D2368FF29008CC191 /* SharedCode.swift in Sources */,
808+
5E6F7E7D2368FF29008CC191 /* Image.swift in Sources */,
808809
);
809810
runOnlyForDeploymentPostprocessing = 0;
810811
};
@@ -831,7 +832,6 @@
831832
71DD3E6723F79B42001510FB /* ImagePickerViewController.swift in Sources */,
832833
71DEC1AC23F79FB20091A700 /* EventMessage.swift in Sources */,
833834
71DEC1D023F7A2650091A700 /* MagnifierView.swift in Sources */,
834-
71DEC1AB23F79FB20091A700 /* EventListener.swift in Sources */,
835835
71DEC1CB23F7A2650091A700 /* GrayscaleHistogramEqualization.swift in Sources */,
836836
71DD3E7523F79BCC001510FB /* UIColor+StateColors.swift in Sources */,
837837
71DD3E6323F7991F001510FB /* LiveViewSupport.swift in Sources */,

FanOfAscii/Chapters/01-FanOfAscii.playgroundchapter/Pages/02-HowImagesComposed.playgroundpage/main.swift

Lines changed: 106 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,110 @@
11
//#-hidden-code
22
//
3-
// See LICENSE folder for this template’s licensing information.
4-
//
5-
// Abstract:
6-
// The Swift file containing the source code edited by the user of this playground book.
3+
// Copyright © 2020 Bunny Wong
4+
// Created on 2019/12/18.
75
//
6+
7+
import UIKit
8+
import PlaygroundSupport
9+
import Accelerate
10+
11+
import BookCore
12+
13+
PlaygroundPage.current.needsIndefiniteExecution = true
14+
15+
//#-end-hidden-code
16+
/*:
17+
# How Images Composed
18+
19+
## Pixels
20+
21+
Images are made up of **pixels**. Think of them as tiny square blocks with a single, solid color.
22+
23+
### 🔬Pixel Discovery
24+
25+
* Experiment:
26+
Choose an image and then tap the 🔎 icon in the bottom right corner to bring up the magnifier. Drag it around to
27+
examine how pixels compose a whole image.
28+
29+
## Red, Green & Blue
30+
31+
Each pixel has a color. To store a color, we have to use a **color model** to measure them first. The **RGB Color model**
32+
is mostly used to represent colors in digital world.
33+
34+
In **RGB color model**, all colors are mixed from lights three main colors: **red, green and blue** as well as an
35+
**alpha** attribute to describe how *opaque* the color is.
36+
37+
![RGB Color Model](rgb-model.png)
38+
39+
This model is close to the way how screens on our devices works. For old monitors with low resolution, you can
40+
even see lighting units in these three colors with a close-up look.
41+
42+
![Close-up Look of LCD Screen](lcd-screen-closeup.jpg)
43+
44+
## Filters
45+
46+
**Filters** are used as the general technique for image processing. Mysterious it seems to be, a filter is more like a
47+
mathematical function, receiving colors per pixel, recalculates them, producing a new image as output.
48+
49+
### 🔨Build Your First Image Filter
50+
51+
* Experiment:
52+
* In this experiment, we'll build a simple filter which takes red, green or blue component out of the source
53+
image.
54+
* Try to read and complete the following code snippet. When you finish it, run your code and tap the **R, G
55+
and B** button below the image to see whether it works.
56+
*/
57+
58+
//#-code-completion(everything, hide)
59+
//#-code-completion(literal, show, float, integer)
60+
//#-code-completion(identifier, show, coefficientRed, coefficientGreen, coefficientBlue)
61+
func applyRGBFilter(redEnabled: Bool,
62+
greenEnabled: Bool,
63+
blueEnabled: Bool,
64+
image: Image) {
65+
let coefficientRed, coefficientGreen, coefficientBlue: Float
66+
coefficientRed = (redEnabled ? 1 : 0)
67+
coefficientGreen = (greenEnabled ? 1 : 0)
68+
coefficientBlue = (blueEnabled ? 1 : 0)
69+
var filterMatrix: [Float] = [
70+
/*#-editable-code*/<#T##Red##Float#>/*#-end-editable-code*/, 0, 0, 0,
71+
0, /*#-editable-code*/<#T##Green##Float#>/*#-end-editable-code*/, 0, 0,
72+
0, 0, /*#-editable-code*/<#T##Blue##Float#>/*#-end-editable-code*/, 0,
73+
0, 0, 0, /*#-editable-code*/<#T##Alpha##Float#>/*#-end-editable-code*/
74+
]
75+
image.multiplyByMatrix(matrix4x4: filterMatrix)
76+
}
77+
78+
/*:
79+
* Note:
80+
In this code snippet, we transform the image by multiplying it with a custom filter matrix. If you're not familiar
81+
with limier algebra, the following figure will explain how this transform matrix works.
82+
*/
83+
84+
//#-hidden-code
85+
let remoteView = remoteViewAsLiveViewProxy()
86+
let eventListener = EventListener(proxy: remoteView) { message in
87+
switch message {
88+
case .rgbFilterRequest(let redEnabled,
89+
let greenEnabled,
90+
let blueEnabled,
91+
let uiImage):
92+
guard let uiImage = uiImage,
93+
let image = Image(uiImage: uiImage) else {
94+
return
95+
}
96+
applyRGBFilter(redEnabled: redEnabled,
97+
greenEnabled: greenEnabled,
98+
blueEnabled: blueEnabled,
99+
image: image);
100+
101+
let destinationBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)
102+
if let destCGImage = image.cgImage(bitmapInfo: destinationBitmapInfo),
103+
let destImage = try? UIImage(cgImage: destCGImage) {
104+
remoteView?.send(EventMessage.imageProcessingResponse(image: destImage).playgroundValue)
105+
}
106+
default:
107+
break
108+
}
109+
}
8110
//#-end-hidden-code
9-
let str = "Hello, playground"

FanOfAscii/Modules/BookCore.playgroundmodule/Sources/Core/EventListener.swift renamed to FanOfAscii/Modules/BookAPI.playgroundmodule/Sources/EventListener.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import Foundation
77
import PlaygroundSupport
88

9+
import BookCore
10+
911
public class EventListener: PlaygroundRemoteLiveViewProxyDelegate {
1012

1113
public typealias EventHandler = (EventMessage) -> Void

FanOfAscii/Modules/BookCore.playgroundmodule/Sources/UserInterface/Chapters/01-FanOfAscii/02-HowImagesComposed/HowImagesComposedLiveViewController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,13 @@ extension HowImagesComposedViewController: PlaygroundLiveViewMessageHandler {
103103
redFilterButton.state = .selected
104104
greenFilterButton.state = .selected
105105
blueFilterButton.state = .selected
106+
requestImageFiltering()
106107
}
107108

108109
public func liveViewMessageConnectionClosed() {
109110
redFilterButton.state = .disabled
110111
greenFilterButton.state = .disabled
111112
blueFilterButton.state = .disabled
112-
requestImageFiltering()
113113
}
114114

115115
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
Copyright © 2020 Bunny Wong
3+
Created on 2019/12/18.
4+
5+
Abstract:
6+
* This file defines a class `Image` for holding raw image data and performs
7+
general processing operations, since `vImage` is not available prior to iOS13.
8+
This class is used throughout this book to abstract away conversion logic and
9+
help you focus on main concepts.
10+
* The class utilizes `Accelerate`, a framework that performs large-scale
11+
mathematical computations, utilizing hardware capabilities and optimized for
12+
high performance.
13+
*/
14+
15+
import UIKit
16+
import Accelerate
17+
18+
public class ImageFormat {
19+
20+
let width: Int
21+
let height: Int
22+
let byteForRow: Int
23+
let bitmapInfo: CGBitmapInfo
24+
25+
public init(cgImage: CGImage) {
26+
self.width = cgImage.width
27+
self.height = cgImage.height
28+
self.byteForRow = cgImage.bytesPerRow
29+
self.bitmapInfo = cgImage.bitmapInfo
30+
}
31+
32+
}
33+
34+
public class Image {
35+
36+
private let format: ImageFormat
37+
private let data: CFData
38+
39+
public func cgImage(bitmapInfo: CGBitmapInfo?) -> CGImage? {
40+
let colorSpace = CGColorSpaceCreateDeviceRGB()
41+
let finalBitmapInfo = bitmapInfo ?? format.bitmapInfo
42+
guard let context = CGContext.init(data: UnsafeMutablePointer<UInt8>(mutating: CFDataGetBytePtr(data)),
43+
width: format.width,
44+
height: format.height,
45+
bitsPerComponent: 8,
46+
bytesPerRow: format.byteForRow,
47+
space: colorSpace,
48+
bitmapInfo: finalBitmapInfo.rawValue) else {
49+
return nil
50+
}
51+
return context.makeImage()
52+
}
53+
54+
public init?(uiImage: UIImage) {
55+
guard let cgImage = uiImage.cgImage,
56+
let bitmapData = cgImage.dataProvider?.data else {
57+
return nil
58+
}
59+
self.data = bitmapData
60+
self.format = ImageFormat(cgImage: cgImage)
61+
}
62+
63+
private func getBuffer() -> vImage_Buffer {
64+
let bitmapPointer =
65+
UnsafeMutablePointer<UInt8>(mutating: CFDataGetBytePtr(data))
66+
return vImage_Buffer(data: bitmapPointer,
67+
height: UInt(format.height),
68+
width: UInt(format.width),
69+
rowBytes: format.byteForRow)
70+
}
71+
72+
public func multiplyByMatrix(matrix4x4: [Float]) {
73+
let divisor: Int32 = 0x1000
74+
let fDivisor = Float(divisor)
75+
var matrixInt16 = matrix4x4.map {
76+
Int16($0 * fDivisor)
77+
}
78+
var buffer = getBuffer()
79+
vImageMatrixMultiply_ARGB8888(&buffer,
80+
&buffer,
81+
&matrixInt16,
82+
divisor,
83+
nil,
84+
nil,
85+
vImage_Flags(kvImageNoFlags))
86+
}
87+
88+
}

FanOfAscii/UserModules/UserModule.playgroundmodule/Sources/SharedCode.swift

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)