Metting === <i class="fa fa-calendar fa-lg"></i> 2023-06-08 <br/> ![](https://imgur.com/77oLka4.png) <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] ![](https://i.imgur.com/AgW1HzR.png) #### [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] ![](https://i.imgur.com/Fn5z6zx.png) ### [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 ``` ![](https://imgur.com/mjMxWBJ.png) ![](https://imgur.com/M60V0wL.png) ::: <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** ![](https://imgur.com/CddhFmL.png) #### [91705]Debug : **Issue can NOT reproduce** ![](https://imgur.com/NDuTDMR.png) ### Relative changeset [[91706]](http://clt-svn:8090/trac/powerdirector/changeset/91706) #### [91706]Release : **Issue can NOT reproduce** ![](https://imgur.com/imcJble.png) #### [91706]Debug : **Issue can NOT reproduce** ![](https://imgur.com/1zixKsa.png) ### [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 ![](https://imgur.com/zpXl83w.png) ### [2D]Produced ![](https://imgur.com/gVK5xIM.png) ### [Online]Producing ![](https://imgur.com/C3nWj6z.png) ### [Online]Produced ![](https://imgur.com/7CPsKOb.png) ### [Colud]Producing ![](https://imgur.com/HyT2l9E.png) ### [Colud]Produced ![](https://imgur.com/128Ed3q.png)