Metting
===
<i class="fa fa-calendar fa-lg"></i> 2023-06-08
<br/>

<br/>
<i class="fa fa-calendar fa-lg"></i> 2023-05-18
:::info
### [BubbleInfo]
```cpp=
let info = BubbleInfo(targetView: view,
anchorPosition: .right,
bubbleController: TutorialBodyEffectController())
bodyEffectBubble = TutorialBubble(info: info)
```
### [TutorialBubbleController]
```cpp=
class TutorialBubbleController: NSObject, TutorialBubbleControllerProtocol {
var isHiddenPreview:Bool { return false }
var isHiddenTitle:Bool { return false }
var isHiddenDescription:Bool { return false }
var isHiddenHyperlink:Bool { return false }
var isHiddenTryItButton:Bool { return false }
var titleText:String { return "" }
var descriptionText:String { return "" }
var canShowBubble:Bool { return false }
var demoVideoFileName:String? { return nil }
func bubbleWillShow() {}
func bubbleWillClose() {}
}
final class TutorialBodyEffectController: TutorialBubbleController {
override var isHiddenTryItButton:Bool { return true }
override var titleText: String {
return NSLocalizedString("Body Effect", comment: "")
}
override var descriptionText:String {
return NSLocalizedString("Apply background and wraparound video effects around moving objects.", comment: "")
}
override var canShowBubble:Bool {
return !StatusCenter.shared.isBubbleShowing()
}
override var demoVideoFileName:String? {
return "Tutorial_BodyEffect"
}
override func bubbleWillClose() {
sendCEIPBubbleTried()
}
private func sendCEIPBubbleTried() {
let ceip = CEIPBubbleTried(.bodyEffect, .close)
ceip.send()
}
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2023-05-04
:::info
### [ObjectMaskDetector]
```cpp=
class ObjectMaskDetector {
init(sourceImage:NSBitmapImageRep?) {/**/}
init(sourcePathURL:URL?) {/**/}
func detect()->Bool {/**/}
func getMask()->MTLTexture? {/**/}
func getMask()->NSImage? {/**/}
func getMask()->CGImage? {/**/}
}
```
### [how to use]
#### [BrushMaskDesignerController]
```cpp=
func applyAutoObjectSelectionMask() -> Bool {
//ignore...
guard let pictureBIR = getPictureBitmapImageRep() else { return false }
let detector = ObjectMaskDetector(sourceImage: pictureBIR)
guard detector.detect() else { return false }
guard let newTex:MTLTexture = detector.getMask(),
var maskAttr = picture.getValue(forKey: PictureParaKey.kMaskAttr) as? PictureDefinition.MaskAttr else { return false }
//ignore...
maskAttr.buffer = newTex
_ = picture.setValue(maskAttr, forKey: PictureParaKey.kMaskAttr)
//ignore...
return true
}
```
#### [TextToStickerViewController]
```cpp=
private func executeObjectDetection() {
let detector = ObjectMaskDetector(sourcePathURL: sourceImagePathURL)
guard detector.detect() else { return }
if let texture:MTLTexture = detector.getMask() {
maskImagePathURL = MaskUtility.saveMaskToFile(texture: texture)
}
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2023-04-06
:::info
### [TutorialTargetView]

#### [how to use]
```cpp=
tutorialTargetView.initView(superView: titleRoomButton.superview,
regionViews: [titleRoomButton,
transitionRoomButton,
effectRoomButton,
pipRoomButton,
particleRoomButton])
```
:::
<i class="fa fa-calendar fa-lg"></i> 2023-03-30
:::info
### [TutorialBubble]
#### [class]
```cpp=
class TutorialBubble: NSObject {
init(info:BubbleInfo) {/*ignore*/}
func show() {/*ignore*/}
func close() {/*ignore*/}
}
```
#### [bubbleInfo]
```cpp=
struct BubbleInfo {
var targetView:NSView
var anchorPosition:AnchorPosition
var bubbleStoryboardName:String
var bubbleStoryboardID:String? = nil
var targetViewHighlightColor:CGColor? = nil
var bubbleBackgroundColor:CGColor? = nil
}
```
#### [protocol]
```cpp=
@objc protocol TutorialBubbleDelegate: AnyObject {
@objc optional func bubbleWillShow()
@objc optional func bubbleWillClose()
}
```
#### [how to use]
```cpp=
let info = BubbleInfo(targetView: view,
anchorPosition: .right,
bubbleStoryboardName: "BodyEffectBlueBubble")
bodyEffectBubble = TutorialBubble(info: info)
if let bodyEffectBubble = bodyEffectBubble {
bodyEffectBubble.show()
}
```
:::
:::info
### [TutorialBubbleViewController]
#### [class]
```cpp=
class TutorialBubbleViewController: NSViewController, TutorialBubbleDelegate {
func getTutorialBubble()->TutorialBubble? {
return tutorialBubble
}
func canShowBubble()->Bool { assert(false); return true }/*need to override by derived class*/
}
```
#### [how to use]
```cpp=
class BodyEffectBlueBubbleViewController: TutorialBubbleViewController {
override func canShowBubble()->Bool {
return isWebViewLoadFinish && !isBlueBubbleShown
}
}
extension BodyEffectBlueBubbleViewController {
func bubbleWillShow() {
isBlueBubbleShown = true
NotificationCenter.default.post(name: .bubbleWillShow, object: self)
}
func bubbleWillClose() {
sendCEIPBubbleTried()
}
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2023-03-23
:::info
### [CEIPTimelineEnumerator]
#### [protocol]
```cpp=
protocol CEIPEnumerateTimelineProtocol {
func enumerating(tlObject:TLObject?)
func execute()
}
final class CEIPTimelineEnumerator {
//
}
```
#### [need to confirm protocol]
```cpp=
extension CEIPProduceUseContent : CEIPEnumerateTimelineProtocol {
func enumerating(tlObject:TLObject?) {
guard let tlObject = tlObject else { return }
let newInfos = createContentInfo(tlObject: tlObject)
if !newInfos.isEmpty {
newInfos.forEach { newInfo in
if let info = infos.first(where: {$0 == newInfo}) {
info.count += 1
} else {
infos.append(newInfo)
}
}
}
}
func execute() {
send()
}
}
```
#### [how to use]
```cpp=
let enumerator = CEIPTimelineEnumerator(senders: [CEIPProduceTimelineEdit(), CEIPProduceUseContent()])
enumerator.execute()
```
:::
<i class="fa fa-calendar fa-lg"></i> 2023-03-09
:::info
### [current]
```cpp=
class Entity: EditBaseObject {
var title: String?
}
class IADEntity: Entity {
var uniqueGUID: String? // Different with each aspect ratio
var sharedGUID: String? // Same with each aspect ratio
var categories: [CMSTemplateListResponseSubField_Categories] = []
}
class BaseEffectEntity: IADEntity {
var effectId = ""
var categoryID = ""
var category = ""
}
class TemplateEntity: IADEntity {
var categoryID: String?
var category: String?
var directorZoneTemplateID: String = ""
var DZParentTemplateID: String = ""
}
```
### [CEIPContentData]
```cpp=
struct CEIPContentData : Codable {
var contentID:String? = nil
var contentName:String? = nil
var categoryID:String? = nil
var categoryName:String? = nil
}
```
<i class="fa fa-calendar fa-lg"></i> 2023-03-02
:::info
### [CEIPMgr]
```cpp=
struct ShutterStockIDList {
var VideoIDList: [String]
var PhotoIDList: [String]
var AudioIDList: [String]
}
@objc class CEIPMgr: NSObject {
@objc static let shared = CEIPMgr()
private let CL_VENDOR_ID = "D8D760AC-ACA2-493e-9623-61E9D47DE89C"
private let exec: String = "PowerDirector Mac \(MiscUtility.getVersionString(isMajorOnly: false))"
private let crashEventName = "BOOMERANG_SESSION_SUCCESS"
private var strLaunchMode = ""
override init() {
/*do something*/
}
func updateMemberIdAndRatePlan() -> Int {
/*do something*/
}
func deinitCEIPMgr() {
/*do something*/
}
func sendCEIPUOpen() {
/*do something*/
}
func getUUID() -> String {
/*do something*/
}
// Only used by app store build currently
@objc func sendCrashMsg() {
/*do something*/
}
func sendCEIPFirstSignInResult(_ isSuccess: Bool) {
/*do something*/
}
func sendCEIPSession() {
/*do something*/
}
func sendCEIPTimelineInfo() {
/*do something*/
}
func sendCEIPStockUsage(_ source: StockUsage.Source, _ stockId: String, _ stockType: StockUsage.StockType) {
/*do something*/
}
func sendCEIPProduceUseIntroTemplate(_ list: [Int64]) {
/*do something*/
}
func sendIntroCEIP(info: IntroCEIP.BaseInfo) {
/*do something*/
}
func sendSTTCEIP(info: STTCEIP.BaseInfo) {
/*do something*/
}
func sendClickShoppingCart() {
/*do something*/
}
func sendRatingDialogInfo(eventName: String, content: [String: Any] = [:]) {
/*do something*/
}
func setLaunchMode(_ mode: LaunchMode) {
/*do something*/
}
private func licenseForShutterStockContent() {
/*do something*/
}
private func getShutterStockContent(TLObjectList: [TLObject]) -> ShutterStockIDList {
/*do something*/
}
func sendTitleDesignerUsage(hasMultipleBorder: Bool, hasColorGradient: Bool) {
/*do something*/
}
func sendPipDesignerUsage(_ content: [String: Any]) {
/*do something*/
}
func sendCEIPInfo(eventName: String,
version: String = "1",
content: [String: Any] = [:]) {
/*do something*/
}
private func sendCEIPInfo(_ eventName: String, _ version: String, _ content: [String: Any], _ forceSend: Bool = false) {
/*do something*/
}
private func sendCEIPInfo(_ content: String, _ eventName: String = "") {
/*do something*/
}
private func sendCEIPUClose() {
/*do something*/
}
private func sendOpenMsg() {
/*do something*/
}
private func sendCEIPOnOff() {
/*do something*/
}
}
```
### [Client side]
#### [LibraryViewController]
```cpp=
@IBAction func switchRoom(_ sender: StatusButton) {
let controller = currentRoomController
let ip = lastSelectedIpPage
onSwitchRoom(sender)
if controller != currentRoomController {
switch currentRoomController {
case is MediaRoomController:
CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_MEDIA_ROOM)
case is TitleRoomController:
CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_TITLE_ROOM)
case is TransitionRoomController:
CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_TRANSITION_ROOM)
case is EffectRoomController:
CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_EFFECT_ROOM)
case is PipRoomController:
CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_PIP_ROOM)
case is ParticleRoomController:
CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_PARTICLE_ROOM)
default:
break
}
}
if currentRoomController == nil,
ip != lastSelectedIpPage {
switch lastSelectedIpPage {
case .IP_MIXER:
CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_AUDIO_MIX_ROOM)
case .IP_VOICEOVER:
CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_VOICE_RECORD_ROOM)
case .IP_SUBTITLE:
CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_ENTER_SUBTITLE_ROOM)
CEIPMgr.shared.sendCEIPInfo(eventName: CEGOAL_SUBTITLE)
default:
break
}
}
}
```
#### [StockMediaController]
```cpp=
private func sendCEIPStockUsage(_ entity: ShutterstockEntity) {
guard let stockId = entity.id else { return }
guard let mediaType = entity.media_type else { return }
guard [.Shutterstock, .GettyImages, .GettyImagesPay].contains(entity.poolType) else { return }
var source = StockUsage.Source.ShutterStock
if entity.poolType == .Shutterstock {
source = .ShutterStock
} else if entity.poolType == .GettyImages {
source = .GettyImagesFree
} else if entity.poolType == .GettyImagesPay {
source = .GettyImagesPremium
}
var stockType = StockUsage.StockType.ImageStock
if mediaType == .Photo {
stockType = StockUsage.StockType.ImageStock
} else if mediaType == .Video {
stockType = StockUsage.StockType.VideoStock
} else if mediaType == .Music {
stockType = StockUsage.StockType.MusicStock
}
CEIPMgr.shared.sendCEIPStockUsage(source, stockId, stockType)
}
//CEIPMgr
func sendCEIPStockUsage(_ source: StockUsage.Source, _ stockId: String, _ stockType: StockUsage.StockType) {
let event = "Stock_Usage"
let version = "1"
let dateformat = DateFormatter()
dateformat.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
let usage_date = dateformat.string(from: Date())
let content: [String: Any] = [
"source": source.rawValue,
"stock_id": stockId,
"stock_type": stockType.rawValue,
"usage_date": usage_date
]
sendCEIPInfo(event, version, content, true)
}
```
:::
:::info
### [Design]
#### [CEIPAnalyzer][Interface]
```cpp=
protocol CEIPAnalyzer {
func send()
}
```
#### [CEIPStockUsage][derived class]
```cpp=
class CEIPStockUsage : CEIPAnalyzer {
private var entity:ShutterstockEntity?
init(entity: ShutterstockEntity) {
self.entity = entity
/*do something*/
}
func send() {
/*send info via CEIPMgr*/
CEIPMgr.shared.sendCEIPInfo(event, version, content, true)
}
}
```
#### [Client side][StockMediaController]
```cpp=
private func sendCEIPStockUsage(_ entity: ShutterstockEntity) {
let ceip = CEIPStockUsage(entity: entity)
ceip.send()
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2023-01-05
:::info
### [layout]

### [AlertIconView]
```cpp=
/* APPKit.NSAlert
extension NSAlert {
public enum Style : UInt, @unchecked Sendable {
case warning = 0
case informational = 1
case critical = 2
}
}
*/
class AccountDeletionViewController: CloudViewController {
@IBOutlet weak var iconView: AlertIconView!
override func viewDidLoad() {
super.viewDidLoad()
//
iconView.initIcon(alertStyle: .critical)
//
}
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2022-02-17
### [custom folder path]
/Users/{user name}/Library/Group Containers/{id}.com.cyberlink.powerdirector/Library/Application Support/MyIntros/**Custom**/
### [download folder path]
/Users/{user name}/Library/Group Containers/{id}.com.cyberlink.powerdirector/Library/Application Support/MyIntros/**Preset**/
:::info
### [IntroUploadHandler]
```cpp=
protocol IntroUploadHandlerProtocol : class {
func exportProgress(progress:Double)
func exportComplete(isSucceed:Bool, isUserAbort:Bool)
}
class IntroUploadHandler {
weak var delegate:IntroUploadHandlerProtocol? = nil
func exportUniversalTemplate(packedFolderPathURL:URL) {/**/}
func rollbackToOldIntroMovie() {/**/}
func cancelExportUniversalTemplate() {/**/}
func removeUploadedFiles() {/**/}
func getUniversalTemplateUUID()->String? {/**/}
}
```
### [sample code]
```cpp=
class XXXClass {
private var uploader:IntroUploadHandler? = nil
func initDialog(controller:IntroController) {
uploader = IntroUploadHandler(controller: controller)
uploader?.delegate = self
}
func onUploadClicked(_ sender: Any) {
//...
uploader?.exportUniversalTemplate(packedFolderPathURL: packedFolderPathURL)
//...
}
func onCancelUploadClicked(_ sender: Any) {
//...
uploader?.cancelExportUniversalTemplate()
//...
}
}
extension XXXClass : IntroUploadHandlerProtocol {
//run on work thread
func exportProgress(progress:Double) {//0.0-1.0
DispatchQueue.main.async {
print("[upload]progress=\(progress)")
}
}
//run on main thread
func exportComplete(isSucceed:Bool, isUserAbort:Bool) {
print("[upload]exportComplete")
if isUserAbort {
uploader?.removeUploadedFiles()
}
}
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2021-10-05
:::info
### [AVIProfileTable]
```cpp=
{AVI_BEGIN_INDEX+5,_T("AUDIO"), _T(""),_T(""),_T(""),IDS_AVI_HIGH_QUALITY,IDS_PRODUCE_WAV_1024K,IDS_MPEG1_K_LBR_FAST_RECOM,
IDS_MPEG1_K_LBR_FAST_RECOM_DV , BASIC_GROUP,FVFC_WAV_AUDIO, SF_AUDIO, FIELD_FRAME,
0X00000082,
_T("\0"), // Compressor Name
_T("PCM"), // AudioCompressor Name
0, // KeyFrameRate
30.0, // FramePerSecond
0, // Quality
0, // Data rate
640, // Width
480, // Height
24, // Color bits
&(AVIProfileExt[4]),
NULL,
0
}
```
### [AVIProfileExt]
```cpp=
{//AVIProfileExt[4]
WAVE_FORMAT_PCM, // Wave format
2, // channel
32000, // HZ
128000, // Average bytes per second
4, // nBlockAlign
16, // bits per sample
0,
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2021-02-18
:::info
### [SpinTimeEdit.swift]
```cpp=
@objc protocol SpinTimeEditTextFieldDelegate : class {
@objc optional func pressOK()
@objc optional func pressCancel()
}
```
### [CloseWindowHandlerViewController.swift]
```cpp=
class CloseWindowHandlerViewController: NSViewController {
private var response:NSApplication.ModalResponse = .cancel
func getResponse()->NSApplication.ModalResponse {
return response
}
func setResponse(response:NSApplication.ModalResponse) {
self.response = response
}
}
```
### [DurationSettingDlg.swift]
```cpp=
class DurationSettingDlg: CloseWindowHandlerViewController {
@IBOutlet weak var spinTimeEdit: SpinTimeEdit!
private func initSpinTimeEdit() {
//...
spinTimeEdit.delegate = self
}
@IBAction func onOK(_ sender: Any) {
setResponse(response: .OK)
self.view.window?.performClose(self)
}
private func onCancel() {
setResponse(response: .cancel)
self.view.window?.performClose(self)
}
}
extension DurationSettingDlg: SpinTimeEditTextFieldDelegate {
func pressOK() {
onOK(self)
}
func pressCancel() {
onCancel()
}
}
extension DurationSettingDlg: NSWindowDelegate {
func windowWillClose(_ notification: Notification) {
NSApplication.shared.stopModal(withCode: getResponse())
}
}
```
<i class="fa fa-calendar fa-lg"></i> 2020-11-19
:::info
### [DeviceChecker.swift]
```cpp=
class DeviceChecker {
enum VendorID {
case intel
case amd
case nvKepler
}
static let shared = DeviceChecker()
private init() {}
private let cpuChecker = CPUChecker()
private let gpuChecker = GPUChecker()
//MARK:public
func isIntelCPU()->Bool {
return cpuChecker.isIntelCPU()
}
func isARMCPU()->Bool {
return cpuChecker.isARMCPU()
}
func getGPUVendorIDs()->[VendorID] {
return gpuChecker.getVendorIDs()
}
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2020-10-29
:::info
### [PerformanceTuning.swift]
```cpp=
typealias PT = PerformanceTuning
typealias AutoPT = AutoPerformanceTuning
class PerformanceTuning {
//...
}
class AutoPerformanceTuning : PerformanceTuning {
//...
}
```
### [Example - OSLog]
```cpp=
//import os
func reconstruectObjects() {
guard let layerTemplateScript = layerTemplateScript else { return }
//let log = OSLog(subsystem:subSystem, category:.pointsOfInterest)
//os_signpost(.begin, log:log, name:"reconstruectObjects")
if controller.getLayerTemplate().loadTemplateFromBuffer(xmlBuffer: layerTemplateScript, encode: true) {
controller.reconstruectObjects()
for (index, object) in controller.getObjectArray().enumerated() {
object.selectObject(_isSelected: objectSelectedStatus[index])
}
controller.updateTrackExpandStatus(trackExpandStatus: trackExpandStatus)
}
//os_signpost(.end, log:log, name:"reconstruectObjects")
}
```
### [Example - PerformanceTuning]
```cpp=
func reconstruectObjects() {
guard let layerTemplateScript = layerTemplateScript else { return }
//let pt = PT()
//pt.begin(pointName: "reconstruectObjects")
if controller.getLayerTemplate().loadTemplateFromBuffer(xmlBuffer: layerTemplateScript, encode: true) {
controller.reconstruectObjects()
for (index, object) in controller.getObjectArray().enumerated() {
object.selectObject(_isSelected: objectSelectedStatus[index])
}
controller.updateTrackExpandStatus(trackExpandStatus: trackExpandStatus)
}
//pt.end(pointName: "reconstruectObjects")
}
```
### [Example - AutoPerformanceTuning]
```cpp=
func reconstruectObjects() {
guard let layerTemplateScript = layerTemplateScript else { return }
//let pt = AutoPT(pointName: "reconstruectObjects")
if controller.getLayerTemplate().loadTemplateFromBuffer(xmlBuffer: layerTemplateScript, encode: true) {
controller.reconstruectObjects()
for (index, object) in controller.getObjectArray().enumerated() {
object.selectObject(_isSelected: objectSelectedStatus[index])
}
controller.updateTrackExpandStatus(trackExpandStatus: trackExpandStatus)
}
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2020-05-28
:::info
### [SelectSlider.swift]
```cpp=
/*
enum SliderType:Int {
case normal = 0
case tickMark
}
*/
@IBInspectable var sliderType:Int = 1
```


:::
<i class="fa fa-calendar fa-lg"></i> 2020-04-30
:::info
### [extension CIImage]
```cpp=
//extension in CIImage+extension.swift
extension CIImage {
var nsImage: NSImage? {
let rep = NSCIImageRep(ciImage: self)
let _nsImage = NSImage(size: rep.size)
_nsImage.addRepresentation(rep)
return _nsImage
}
}
//sample code in TitleEffectImporter.swift
private func getThumbnail(effectID:String)->NSImage? {
if TitleNoEffect.isNoEffect(effectID: effectID) {
return thumbGenerator.getNoEffectImage()?.nsImage
}
//check Assets
if let image = NSImage(named: NSImage.Name(effectID)) {
return ImageUtility.createCIImage(from: image)?.nsImage
}
//check cache folder
let cacheFolderURL:URL = PathUtility.getCachesDirectory().appendingPathComponent("DSPCache/Title")
let imagePathURL:URL = cacheFolderURL.appendingPathComponent(effectID).appendingPathExtension("jpg")
if PathUtility.checkIfPathExists(path: imagePathURL.path) {
return CIImage(contentsOf: imagePathURL)?.nsImage
}
//generate thumbnail to cache folder
let startPeriod:TitleDefinition.PathPeriod = TitleDefinition.PathPeriod(start: 0.0, end: 1.0)
let endPeriod:TitleDefinition.PathPeriod = TitleDefinition.PathPeriod()
title?.setupPath(effectID, startPeriod, "", endPeriod)
if let thumbnail = thumbGenerator.createThumbnail(title: title, progress: 0.3) {
_ = writeThumbnailToFile(cacheFolderPath: cacheFolderURL.path, exportFilePath: imagePathURL.path, _thumbnail: thumbnail)
return thumbnail.nsImage
}
return nil
}
```
### [RepeatingTimer]
```cpp=
//RepeatingTimer.swift
class RepeatingTimer {
init(seconds timeInterval: TimeInterval) {
self.timeInterval = timeInterval
}
deinit {
timer.setEventHandler {}
timer.cancel()
/*
If the timer is suspended, calling cancel without resuming
triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902
*/
resume()
eventHandler = nil
}
//MARK:public
var eventHandler: (() -> Void)?
func resume() {
if state == .resumed {
return
}
state = .resumed
timer.resume()
}
func suspend() {
if state == .suspended {
return
}
state = .suspended
timer.suspend()
}
//MARK:private
private enum State {
case suspended
case resumed
}
private let timeInterval: TimeInterval
private var state: State = .suspended
private lazy var timer: DispatchSourceTimer = {
let t = DispatchSource.makeTimerSource()
t.schedule(deadline: .now() + self.timeInterval, repeating: self.timeInterval)
t.setEventHandler(handler: { [weak self] in
self?.eventHandler?()
})
return t
}()
}
//sample code in TitleStartEndEffectViewer.swift
class TitleStartEndEffectViewer {
private var title:CES_Title? = nil
private var effect:TitleEffect? = nil
private weak var mtkView:MTKView! = nil
private let timeInterval = 0.1
private var timer:RepeatingTimer? = nil
private var thumbGenerator:TitleEffectThumbGenerator = TitleEffectThumbGenerator()
private var position:Double = 0
init(_mtkView:MTKView, _title:CES_Title, _effect:TitleEffect, pageType:TitleEffectPageType) {
title = _title
effect = _effect
mtkView = _mtkView
timer = RepeatingTimer(seconds: timeInterval)
configureTitle(pageType: pageType)
if let _timer = timer {
_timer.eventHandler = {[weak self] in
DispatchQueue.main.async(execute: {
if let weakself = self {
weakself.mtkView.setNeedsDisplay(weakself.mtkView.bounds)
}
})
}
}
}
deinit {
pause()
}
func pause() {
guard let t = timer else { return }
t.suspend()
}
func unpause() {
guard let t = timer else { return }
t.resume()
}
func updateView(view: MTKView) {
guard let title = title,
let thumbnail = thumbGenerator.createThumbnail(title: title, progress: getProgress()) else { return }
if let pixelBuffer = thumbnail.pixelBuffer {
ImageUtility.blendWithEditingContext(inputBuffer: pixelBuffer,
outputView: view,
clearColor: bkClearColor)
}
}
//ignore some code...
}
//sample code in TitleEffectBaseViewController.swift
extension TitleEffectBaseViewController : TitleEffectCollectionViewItemDelegate {
func onEnterPreview(imageView: NSView?, titleEffect: TitleEffect?) {
guard let view = imageView,
let title = importer.getTitle(),
let effect = titleEffect else { return }
if TitleNoEffect.isNoEffect(effectID: effect.effectID) { return }
if effectViewer != nil { return }
mtkView = MTKView(frame: view.bounds, device: EditingContext.shared.device)
mtkView?.framebufferOnly = true
mtkView?.enableSetNeedsDisplay = true
mtkView?.delegate = self
view.addSubview(mtkView!)
effectViewer = TitleStartEndEffectViewer(_mtkView: mtkView!, _title: title, _effect: effect, pageType: getPageType())
effectViewer?.unpause()
}
func onLeavePreview(titleEffect:TitleEffect?) {
guard let effect = titleEffect else { return }
if TitleNoEffect.isNoEffect(effectID: effect.effectID) { return }
effectViewer?.pause()
effectViewer = nil
mtkView?.removeFromSuperview()
}
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2020-03-12
:::info
### [presentAsModalWindow]
```cpp=
@objc func popupTitleDesigner(noti: Notification) {
guard let userInfo = noti.userInfo else {return}
guard let trackIdx: Int = userInfo["trackIndex"] as? Int,
let clipIdx : Int = userInfo["clipIndex"] as? Int else {return}
guard let tlObj = EditingManager.shared.getTLObject(trackIdx, clipIdx) as? TLTitleObject else { return }
let storyboard = NSStoryboard(name: NSStoryboard.Name("TitleDesigner"), bundle: nil)
if let viewController = storyboard.instantiateInitialController() as? TitleDesignerViewController {
let clipInfo = TemplateClipInfo(trackIndex: trackIdx,
clipIndex: clipIdx,
tlBegin: tlObj.getTLBegin(),
tlDuration: tlObj.getTLDuration(),
tlIndicator: EditingManager.shared.position,
refScriptPath: tlObj.getUserData().scriptPath,
scriptBuffEnc: tlObj.getScriptBuffer())
viewController.initDialog(clipInfo: clipInfo)
presentAsModalWindow(viewController)
}
}
/*
[enter designer]
storyboard.instantiateInitialController()
initDialog
presentAsModalWindow
viewDidLoad
prepare
viewDidAppear
Leave function scope
[leave designer]
windowWillClose
deinit
*/
```
### [NSApplication.shared.runModal]
```cpp=
@IBAction func clickAddParticleButton(_ sender: Any) {
let storyboard = NSStoryboard(name: NSStoryboard.Name("ImportParticleDlg"), bundle: nil)
guard let windowController = storyboard.instantiateInitialController() as? NSWindowController,
let window = windowController.window,
let viewController = windowController.contentViewController as? ImportParticleDlgViewController else { return }
if NSApplication.shared.runModal(for: window) == .OK {
if let _controller = controller as? TitleController {
if viewController.getScriptPath().isEmpty == false {
_controller.addParticle(filePath: viewController.getScriptPath(), templateAlias: viewController.getAlias())
}
}
}
}
/*
[enter designer]
viewDidLoad (instantiate window controller)
prepare (instantiate window controller)
initDialog
NSApplication.shared.runModal
viewDidAppear
[leave designer]
windowWillClose
NSApplication.shared.stopModal
Leave function scope
deinit
* /
```
:::
<i class="fa fa-calendar fa-lg"></i> 2019-09-20
:::info
### [NSSlider+extension]
```cpp=
//in NSControl
//open var intValue: Int32
//open var integerValue: Int
//open var floatValue: Float
//open var doubleValue: Double
extension NSSlider {
var uint64Value: UInt64 {
get {
return UInt64(self.integerValue)
}
set {
self.integerValue = Int(newValue)
}
}
}
```
### [Before]
```cpp=
guard let position = controller?.position() else { assert(false); return }
sliderPlayback.integerValue = Int(position)
```
### [After]
```cpp=
guard let position = controller?.position() else { assert(false); return }
sliderPlayback.uint64Value = position
```
:::
:::warning
### [Custom NotificationCenter : post part]
### [Before]
```cpp=
extension Notification.Name {
static let displayPanelPlayback = NSNotification.Name("displayPanelPlayback")
}
//key
struct DPKey {
static let keyPlayStatus = "playStatus"
}
//post
var userInfo: Dictionary<String, Any> = Dictionary<String, Any>()
userInfo[DPKey.keyPlayStatus] = DPPlayStatus.play
NotificationCenter.default.post(name: .displayPanelSeekPreviewSlider, object: nil, userInfo: userInfo)
//receive
@objc private func displayPanelPlayback(_ notification:Notification){
if let userInfo = notification.userInfo {
if let playStatus = userInfo[DPKey.keyPlayStatus] as? DPPlayStatus{
updatePlayback(playStatus: playStatus)
}
}
}
```
### [After]
```cpp=
struct dpPlaybackInfo : NotificationRepresentable{//NotificationCenter+extension.swift
var playStatus : DPPlayStatus
}
extension Notification.Name {
static let displayPanelPlayback = dpPlaybackInfo.notificationName //PowerDirector.dpPlaybackInfo
}
//post
NotificationCenter.default.post(dpPlaybackInfo(playStatus: DPPlayStatus.play))
//receive
@objc private func displayPanelPlayback(_ notification:Notification){
guard let info = dpPlaybackInfo(notification: notification) else { return }
updatePlayback(playStatus: info.playStatus)
}
```
### [NotificationCenter+extension]
```cpp=
protocol NotificationRepresentable { }
extension NotificationRepresentable {
static var notificationName: Notification.Name {
return Notification.Name(String(reflecting: Self.self))
}
static var userInfoKey: String {
return "UserInfo"
}
init?(notification: Notification) {
guard let value = notification.userInfo?[Self.userInfoKey] as? Self else { return nil }
self = value
}
}
extension NotificationCenter {
func post<T>(_ notificationRepresentable: T, object: Any? = nil) where T: NotificationRepresentable {
post(name: T.notificationName, object: object, userInfo: [T.userInfoKey : notificationRepresentable])
}
}
```
:::
<i class="fa fa-calendar fa-lg"></i> 2019-07-04
### [Memory leak case]
```cpp=
void CTitleElegantFontSettingPage::InitTitleFontNameCombo()
{
std::vector<std::pair<CString, BYTE>> vecFontName;
m_pITitleModeController->EnumElegantTitleFontName(vecFontName);
for (int nIdx=0 ; nIdx<(int)vecFontName.size() ; ++nIdx)
{
const CString strFontName = vecFontName[nIdx].first;
const BYTE byCharSet = vecFontName[nIdx].second;
const CString strFontFaceName = strFontName.Left(LF_FACESIZE-1);
m_pcboFontName->AddString(strFontFaceName);
//m_pcboFontName->InsertString(nIdx, strFontFaceName);
m_mapCharSet[strFontFaceName] = byCharSet;
std::pair<BYTE, CString> * pPairItemData = new std::pair<BYTE, CString>(byCharSet, strFontName);
m_pcboFontName->SetItemDataPtr(nIdx, pPairItemData);
}
}
```
### [Resolve]
```cpp=
void CTitleElegantFontSettingPage::InitTitleFontNameCombo()
{
std::vector<std::pair<CString, BYTE>> vecFontName;
m_pITitleModeController->EnumElegantTitleFontName(vecFontName);
for (int nIdx=0 ; nIdx<(int)vecFontName.size() ; ++nIdx)
{
const CString strFontName = vecFontName[nIdx].first;
const BYTE byCharSet = vecFontName[nIdx].second;
const CString strFontFaceName = strFontName.Left(LF_FACESIZE-1);
const int nInsertIdx = m_pcboFontName->AddString(strFontFaceName);
m_mapCharSet[strFontFaceName] = byCharSet;
std::pair<BYTE, CString> * pPairItemData = new std::pair<BYTE, CString>(byCharSet, strFontName);
m_pcboFontName->SetItemDataPtr(nInsertIdx, pPairItemData);
}
}
```
### [note1]
```htmlembedded=
/*skin_Title_Elegant_FontSetting.xml*/
<COMBOBOX id="IDC_ELEGANT_TITLE_COMBOBOX_FONT_TYPE" SORT="TRUE">
//ignore
</COMBOBOX>
```
### [note2]
```cpp=
/*TitleElegantFontSettingPage.cpp*/
if(m_pcboFontName = (CGComboBox*)GetDlgItem(IDC_ELEGANT_TITLE_COMBOBOX_FONT_TYPE))
{
/*
enum COMBOBOX_STYLE
{
CS_NORMAL,
CS_FONT,
CS_FONTSTYLE // Must insert 4 items [Ref : EFONTSTYLE]
};
*/
m_pcboFontName->SetStyle(CGComboBox::CS_FONT);
m_pcboFontName->SetItemHeight(0, nItemHeight);
}
/*CGComboBox.cpp*/
void CGComboListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
//...
if(style==CGComboBox::CS_FONT)
{
dcBuf.SelectObject(pOldFont);
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT));
ASSERT(m_pFont!=NULL);
CFont* pFont=m_pFont;
pFont->GetLogFont(&lf);
if(szText.Left(1) == _T("@"))// && IsFontExist(szText.Mid(1))) //ignore font exist check for performance concern
_tcscpy_s(lf.lfFaceName,szText.Mid(1));
else
_tcscpy_s(lf.lfFaceName,szText);
lf.lfCharSet=(BYTE)GetItemDataPtr(lpDrawItemStruct->itemID);
lf.lfHeight = (LONG) (int) CDpiTrans(-14,FALSE);
font.CreateFontIndirect(&lf);
bCreateFont=TRUE;
pOldFont=dcBuf.SelectObject(&font);
}
//...
}
```
---
<i class="fa fa-calendar fa-lg"></i> 2019-05-16
### eBug : [VDE191610-0022](https://ecl.cyberlink.com/Ebug/EbugHandle/HandleMainEbug.asp?BugCode=VDE191610-0022)
### [Root Cause]
```cpp=
void CTitleModeBaseController::ExecuteUndoPackage(TITLE_UNDO_PACKAGE* ptupPackage)
{
//do something...
int nZOrder = 0;
std::vector<TitleTimelineTrackSetting> vTrackSettings;
if(ptupPackage->plistObject && ptupPackage->plistObject->GetCount() > 0)
{
SortUndoObjListByZOrder(ptupPackage->plistObject);
POSITION pos = ptupPackage->plistObject->GetHeadPosition();
while(pos)
{
//execute undo package
}
}
//do something...
}
```
### Relative changeset [[82710]](http://clt-svn:8090/trac/powerdirector/changeset/82710)
```cpp=
void CTitleModeBaseController::SortUndoObjListByZOrder(CList<TITLE_UNDO_OBJECT*, TITLE_UNDO_OBJECT*>* pUndoObjList)
{
if(!pUndoObjList)
return;
for(INT_PTR i = 0; i < pUndoObjList->GetCount(); i++)
{
for(INT_PTR j = 0; j < pUndoObjList->GetCount() - 1; j++)
{
TITLE_UNDO_OBJECT *pUndoObjFirst = pUndoObjList->GetAt(pUndoObjList->FindIndex(j));
CTitleBase* pTitleBaseFirst = pUndoObjFirst->pString;
TITLE_UNDO_OBJECT *pUndoObjSecond = pUndoObjList->GetAt(pUndoObjList->FindIndex(j + 1));
CTitleBase* pTitleBaseSecond = pUndoObjSecond->pString;
if(pTitleBaseFirst->GetZOrder() > pTitleBaseSecond->GetZOrder())
{
pUndoObjList->SetAt(pUndoObjList->FindIndex(j), pUndoObjSecond);
pUndoObjList->SetAt(pUndoObjList->FindIndex(j + 1), pUndoObjFirst);
}
}
}
}
```
```cpp=
struct TITLE_UNDO_OBJECT
{
enum TITLE_UNDO_OBJECT_TYPE
{
TUOT_STRING,
TUOT_PICTURE,
TUOT_PARTICLE,
TUOT_ELEGANT_TITLE,
};
TITLE_UNDO_OBJECT_TYPE tuotObjectType;
union{
CTitleTitle *pString;
CTitlePicture* pPicture;
CTitleParticleEditable* pParticle;
CTitleUndoElegantTitle* pElegantTitle;
};
};
```
#### [91705]Release : **Issue can reproduce**

#### [91705]Debug : **Issue can NOT reproduce**

### Relative changeset [[91706]](http://clt-svn:8090/trac/powerdirector/changeset/91706)
#### [91706]Release : **Issue can NOT reproduce**

#### [91706]Debug : **Issue can NOT reproduce**

### [Resolve]
### changeset [[92375]](http://clt-svn:8090/trac/powerdirector/changeset/92357)
---
<i class="fa fa-calendar fa-lg"></i> 2018-11-22
## [BB improve]
### [2D]Producing

### [2D]Produced

### [Online]Producing

### [Online]Produced

### [Colud]Producing

### [Colud]Produced
