diff --git a/ohos/entry/src/main/ets/pages/Index.ets b/ohos/entry/src/main/ets/pages/Index.ets index 9ea6e15ad1..bc0d1733eb 100755 --- a/ohos/entry/src/main/ets/pages/Index.ets +++ b/ohos/entry/src/main/ets/pages/Index.ets @@ -23,31 +23,32 @@ import * as pag from 'libpag'; struct Index { @State message: string = ""; @State @Watch("updateMessage") stateString: string = ""; - @State composition: pag.PAGComposition | null = null; @State @Watch("updateMessage") progress: number = 0; - @State isPlaying: boolean = false; - @State repeatCount: number = 1; + @State viewController: pag.PAGViewController = new pag.PAGViewController(); aboutToAppear(): void { let manager = getContext(this).resourceManager; let file = pag.PAGFile.LoadFromAssets(manager, "PAG_LOGO.pag"); - this.composition = file as pag.PAGComposition; - this.isPlaying = true; + this.viewController.setComposition(file); + this.viewController.setRepeatCount(1); + this.viewController.addListener(new WeakRef(this)); + this.viewController.play(); } - onAnimationStart = (view: pag.PAGView) => { - this.stateString = 'PAG start'; + onAnimationStart = (viewController: pag.PAGViewController) => { + this.stateString = viewController.uniqueID() + ` PAG start`; } - onAnimationEnd = (view: pag.PAGView) => { - this.stateString = `PAG end`; + onAnimationEnd = (viewController: pag.PAGViewController) => { + this.stateString = viewController.uniqueID() + ` PAG end`; } - onAnimationRepeat = (view: pag.PAGView) => { - this.stateString = `PAG repeat`; + onAnimationRepeat = (viewController: pag.PAGViewController) => { + this.stateString = viewController.uniqueID() + ` PAG repeat`; } - onAnimationCancel = (view: pag.PAGView) => { - this.stateString = `PAG cancel`; + onAnimationCancel = (viewController: pag.PAGViewController) => { + this.stateString = viewController.uniqueID() + ` PAG cancel`; } - onAnimationUpdate = (view: pag.PAGView) => { + onAnimationUpdate = (viewController: pag.PAGViewController) => { + this.progress = viewController.getProgress(); } updateMessage() { @@ -58,23 +59,23 @@ struct Index { Row() { Column() { pag.PAGView({ - composition: this.composition, - progress: this.progress, - isPlaying: this.isPlaying, - repeatCount: this.repeatCount, - listeners: [new WeakRef(this)] + controller: this.viewController }) .height('50%') .onClick(() => { - this.isPlaying = !this.isPlaying + if (this.viewController.isPlaying()) { + this.viewController.pause(); + } else { + this.viewController.play(); + } }) Text(this.message) .fontSize(50) .fontWeight(FontWeight.Bold) .onClick(() => { - this.progress = 0.5; - this.repeatCount = 0; + this.viewController.setProgress(0.5); + this.viewController.setRepeatCount(0); }) .height('50%') } diff --git a/ohos/libpag/Index.ets b/ohos/libpag/Index.ets index 64225187c7..c62daca4d0 100644 --- a/ohos/libpag/Index.ets +++ b/ohos/libpag/Index.ets @@ -16,7 +16,7 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// -export { PAGView, PAGViewListener } from './src/main/ets/PAGView' +export { PAGView, PAGViewListener, PAGViewController } from './src/main/ets/PAGView' export { PAGPlayer } from './src/main/ets/PAGPlayer' diff --git a/ohos/libpag/src/main/cpp/types/libpag/Index.d.ts b/ohos/libpag/src/main/cpp/types/libpag/Index.d.ts index 70adfa3672..b8ee7d51e6 100644 --- a/ohos/libpag/src/main/cpp/types/libpag/Index.d.ts +++ b/ohos/libpag/src/main/cpp/types/libpag/Index.d.ts @@ -1,3 +1,21 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + import resourceManager from "@ohos.resourceManager"; import { image } from "@kit.ImageKit"; @@ -284,46 +302,74 @@ export declare class JPAGSurface { } export declare class JPAGView { - flush(): void; + flush(): boolean; + + update(): void; setProgress(progress: number): void; + getProgress(): number; + setComposition(composition: JPAGComposition | null): void; setRepeatCount(repeatCount: number): void; + repeatCount(): number; + play(): void; pause(): void; + isPlaying(): boolean; + setStateChangeCallback(callback: (number) => void): void; - setProgressUpdateCallback(callback: (double) => void): void; + setProgressUpdateCallback(callback: () => void): void; uniqueID(): string; setSync(isSync: boolean): void; + isSync(): boolean; + setVideoEnabled(videoEnabled: boolean): void; + videoEnabled(): boolean; + setCacheEnabled(cacheEnabled: boolean): void; + cacheEnabled(): boolean; + setCacheScale(cacheScale: number): void; + cacheScale(): number; + setMaxFrameRate(maxFrameRate: number): void; + maxFrameRate(): number; + setScaleMode(scaleMode: number): void; + scaleMode(): number; + setMatrix(matrix: Array); + matrix(): Array; + currentFrame(): number; getLayersUnderPoint(x: number, y: number): Array; + getBounds(pagLayer: JPAGLayer): Array; + freeCache(); makeSnapshot(): image.PixelMap | null; + useDiskCache(): boolean; + + setUseDiskCache(value: boolean); + release(); } diff --git a/ohos/libpag/src/main/ets/PAGView.ets b/ohos/libpag/src/main/ets/PAGView.ets index 3445556e76..7c2a631d33 100644 --- a/ohos/libpag/src/main/ets/PAGView.ets +++ b/ohos/libpag/src/main/ets/PAGView.ets @@ -23,184 +23,296 @@ import { PAGLayer } from './PAGLayer'; import { image } from '@kit.ImageKit'; import { PAGUtils } from './private/PAGUtils'; import { Matrix4 } from '@ohos.arkui.node'; +import { PAGAnimatorState } from './private/PAGAnimatorState'; import { PAGFile } from './PAGFile'; +import { common2D } from '@kit.ArkGraphics2D'; export interface PAGViewListener { /** * Notifies the beginning of the animation. It can be called from either the UI thread or the thread * that calls the play method. */ - onAnimationStart?: (view: PAGView) => void; + onAnimationStart?: (viewController: PAGViewController) => void; /** * Notifies the end of the animation. It can only be called from the UI thread. */ - onAnimationEnd?: (view: PAGView) => void; + onAnimationEnd?: (viewController: PAGViewController) => void; /** * Notifies the repetition of the animation. It can only be called from the UI thread. */ - onAnimationRepeat?: (view: PAGView) => void; + onAnimationRepeat?: (viewController: PAGViewController) => void; /** * Notifies the cancellation of the animation. It can be called from either the UI thread or the * thread that calls the stop method. */ - onAnimationCancel?: (view: PAGView) => void; + onAnimationCancel?: (viewController: PAGViewController) => void; /** * Notifies another frame of the animation has occurred. It may be called from an arbitrary * thread if the animation is running asynchronously. */ - onAnimationUpdate?: (view: PAGView) => void; + onAnimationUpdate?: (viewController: PAGViewController) => void; } -@Component -export struct PAGView { +@Observed +export class PAGViewController { + /** + * Sets a new PAGComposition for PAGView to render as content. Note that if the composition is + * already added to another PAGView, it will be removed from the previous PAGView. + */ + setComposition(composition: PAGComposition | null) { + this.filePath = null; + this.composition = composition; + this.jView.setComposition(composition?.getNativeComposition()); + } + + /** + * Returns the current PAGComposition for PAGView to render as content. + */ + getComposition(): PAGComposition | null { + return this.composition; + } + + /** + * The path string of a pag file set by setPath() or setPathAsync(). + * @returns + */ + getPath(): string | null { + return this.filePath; + } + + /** + * Loads a pag file from the local path, returns false if the file does not exist or the data is not a pag file. + * @param path + * @returns + */ + setPath(path: string): boolean { + this.filePath = path; + let file = PAGFile.LoadFromPath(path); + this.composition = file; + this.jView.setComposition(file?.getNativeComposition()); + return file != null; + } + + /** + * Asynchronously load a pag file from the specific path which can be a network path or a local path. + * @param path + * @returns + */ + setPathAsync(path: string): Promise { + this.filePath = path; + return new Promise((resolve) => { + PAGFile.LoadFromPathAsync(path).then((pagFile) => { + this.composition = pagFile; + this.jView.setComposition(pagFile?.getNativeComposition()); + resolve(pagFile) + }) + }) + } + + /** + * Starts to play the animation from the current position. Calling the play() method when the + * animation is already playing has no effect. The play() method does not alter the animation's + * current position. However, if the animation previously reached its end, it will restart from + * the beginning. + */ + play() { + this.jView?.play(); + } + + /** + * Cancels the animation at the current position. Calling the play() method can resume the + * animation from the last paused position. + */ + pause() { + this.jView.pause(); + } + /** - * PAGComposition is the content of PAGView. - * Note: If the composition is already added to another PAGView, it will be removed from the - * previous PAGView. + * Indicates whether the animation is playing. */ - @Link @Watch("onCompositionChange") composition: PAGComposition | null; + isPlaying(): boolean { + return this.jView.isPlaying(); + } + + /** + * Returns true if PAGView is playing in the main thread. The default value is false. + */ + isSync(): boolean { + return this.jView.isSync(); + } + /** - * Indicates whether this pag view is playing. + * Sets the sync flag for the PAGView. */ - @Link @Watch("onPlayStateChange") isPlaying: boolean; + setSync(isSync: boolean) { + this.jView.setSync(isSync); + } + /** - * The current progress of play position, the value is from 0.0 to 1.0. It is applied only - * when the composition is not null. + * Set the number of times the animation to play. */ - @Link @Watch("onProgressChange") progress: number; + setRepeatCount(repeatCount: number) { + this.jView.setRepeatCount(repeatCount); + } + /** * The total number of times the animation is set to play. The default is 1, which means the * animation will play only once. If the repeat count is set to 0 or a negative value, the * animation will play infinity times. */ - @Prop @Watch("onRepeatCountChange") repeatCount: number = 0; + repeatCount(): number { + return this.jView.repeatCount(); + } + /** - * Set true to make PAGView playing in the main thread. The default value is false. + * If set to false, PAGView skips rendering for video composition. */ - @Prop @Watch("OnSyncChange") isSync: boolean = false; + videoEnabled(): boolean { + return this.jView.videoEnabled(); + } + /** - * If set to false, PAGView skips rendering for video composition. + * Sets the value of videoEnabled property. */ - @Prop @Watch("OnVideoEnabledChange") videoEnabled: boolean = true; + setVideoEnabled(enable: boolean) { + this.jView.setVideoEnabled(enable); + } + /** * If set to true, PAG renderer caches an internal bitmap representation of the static content * for each layer. This caching can increase performance for layers that contain complex vector * content. The execution speed can be significantly faster depending on the complexity of the * content, but it requires extra graphics memory. The default value is true. */ - @Prop @Watch("OnCacheEnabledChange") cacheEnabled: boolean = true; + cacheEnabled(): boolean { + return this.jView.cacheEnabled(); + } + + /** + * Sets the value of cacheEnabled property. + */ + setCacheEnabled(value: boolean) { + this.jView.setCacheEnabled(value); + } + + /** + * If set to true, PAG will cache the associated rendering data into a disk file, such as the + * decoded image frames of video compositions. This can help reduce memory usage and improve + * rendering performance. + */ + useDiskCache(): boolean { + return this.jView.useDiskCache(); + } + + /** + * Set the value of useDiskCache property. + */ + setUseDiskCache(value: boolean) { + this.jView.setUseDiskCache(value); + } + /** * This value defines the scale factor for internal graphics caches, ranges from 0.0 to 1.0. The * scale factors less than 1.0 may result in blurred output, but it can reduce the usage of * graphics memory which leads to better performance. The default value is 1.0. */ - @Prop @Watch("OnCacheScaleChange") cacheScale: number = 1.0; + cacheScale(): number { + return this.jView.cacheScale(); + } + /** - * The maximum frame rate for rendering. If set to a value less than the actual frame rate from - * PAGFile, it drops frames but increases performance. Otherwise, it has no effect. The default - * value is 60. + * Sets the value of cacheScale property. */ - @Prop @Watch("OnMaxFrameRateChange") maxFrameRate: number = 60.0; + setCacheScale(value: number) { + this.jView.setCacheScale(value); + } + /** - * Specifies the rule of how to scale the pag content to fit the surface size. The matrix - * changes when this method is called. + * Sets the maximum frame rate for rendering. */ - @Prop @Watch("OnScaleModeChange") scaleMode: PAGScaleMode = PAGScaleMode.LetterBox; + setMaxFrameRate(maxFrameRate: number) { + this.jView.setMaxFrameRate(maxFrameRate); + } + /** - * Sets the transformation which will be applied to the composition. The scaleMode property - * will be set to PAGScaleMode::None when this method is called. + * The maximum frame rate for rendering. If set to a value less than the actual frame rate from + * PAGFile, it drops frames but increases performance. Otherwise, it has no effect. The default + * value is 60. */ - @Prop @Watch("OnMatrixChange") matrix: Matrix4 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + maxFrameRate(): number { + return this.jView.maxFrameRate(); + } + /** - * listener will be sent events through the life of an animation, - * such as start, repeat, and end. PAGView only holds a weak reference to the listener. + * Returns the current scale mode. */ - @State listeners: Array> = []; - - private filePath: string = ""; - - build() { - XComponent({ - id: this.controller?.uniqueID(), - type: XComponentType.SURFACE, - libraryname: "pag", - }) - .onLoad(() => { - if (this.isPlaying) { - this.controller?.play(); - } else { - this.controller?.flush(); - } - }) - .backgroundColor(Color.Transparent) + scaleMode(): PAGScaleMode { + return this.jView.scaleMode(); } /** - * The path string of a pag file set by setPath() or setPathAsync(). - * @returns + * Specifies the rule of how to scale the pag content to fit the surface size. The matrix + * changes when this method is called. */ - getPath():string { - return this.filePath; + setScaleMode(mode: PAGScaleMode) { + this.jView.setScaleMode(mode); } /** - * Loads a pag file from the local path, returns false if the file does not exist or the data is not a pag file. - * @param path - * @returns + * Returns a copy of current matrix. */ - setPath(path: string): boolean { - this.filePath = path; - let file = PAGFile.LoadFromPath(path); - this.composition = file; - return file != null; - } + matrix(): Matrix4 { + return PAGUtils.ToTsMatrix(this.jView.matrix()); + } - /** - * Asynchronously load a pag file from the specific path which can be a network path or a local path. - * @param path - * @returns - */ - setPathAsync(path: string): Promise { - this.filePath = path; - return new Promise((resolve) => { - PAGFile.LoadFromPathAsync(path).then((pagFile) => { - this.composition = pagFile; - resolve(pagFile) - }) - }) + /** + * Sets the transformation which will be applied to the composition. The scaleMode property + * will be set to PAGScaleMode::None when this method is called. + */ + setMatrix(matrix: Matrix4) { + this.jView.setMatrix(PAGUtils.ToNativeMatrix(matrix)); } - flush(): void { - this.controller?.flush(); + /** + * The duration of current composition in microseconds. + */ + duration(): number { + return this.composition ? this.composition.duration() : 0; } /** - * Adds a listener to the set of listeners that are sent events through the life of an - * animation, such as start, repeat, and end. + * Returns the current progress of play position, the value is from 0.0 to 1.0. */ - addListener(listener: WeakRef) { - this.listeners.push(listener); + getProgress(): number { + return this.jView.getProgress(); } /** - * Removes a listener from the set listening to this animation. + * Sets the progress of play position, the valid value is from 0.0 to 1.0. */ - removeListener(listener: WeakRef) { - const index = this.listeners.indexOf(listener); - if (index > -1) { - this.listeners.splice(index, 1); - } + setProgress(progress: number) { + this.jView?.setProgress(progress); } /** * Returns the current frame. */ currentFrame(): number { - return this.composition ? this.composition.currentTime() : 0; + return this.jView.currentFrame(); + } + + /** + * Call this method to render current position immediately. Note that all the changes previously + * made to the PAGView will only take effect after this method is called. If the play() method + * is already called, there is no need to call it manually since it will be automatically called + * every frame. Returns true if the content has changed. + */ + flush(): boolean { + return this.jView.flush(); } /** @@ -208,14 +320,14 @@ export struct PAGView { * The point is in pixels not dp. */ getLayersUnderPoint(x: number, y: number): Array { - return this.controller ? PAGUtils.WarpJPAGLayers(this.controller.getLayersUnderPoint(x, y)) : []; + return PAGUtils.WarpJPAGLayers(this.jView.getLayersUnderPoint(x, y)); } /** * Free the cache created by the pag view immediately. Can be called to reduce memory pressure. */ - freeCache(): void { - this.controller?.freeCache(); + freeCache() { + this.jView.freeCache(); } /** @@ -223,102 +335,60 @@ export struct PAGView { * PAGView will not be captured. Returns null if the PAGView hasn't been presented yet. */ makeSnapshot(): image.PixelMap | null { - return this.controller ? this.controller.makeSnapshot() : null; - } - - onPageShow(): void { - if (super.onPageShow) { - super.onPageShow(); - } - this.flush(); - } - - aboutToAppear(): void { - this.initView(); + return this.jView.makeSnapshot(); } - aboutToDisappear(): void { - this.controller?.release(); - this.controller = null; - } - - private initView() { - this.controller?.setStateChangeCallback(this.onAnimatorStateChange); - this.controller?.setProgressUpdateCallback(this.onAnimatorProgressUpdate); - this.controller?.setComposition(this.composition?.getNativeComposition()); - this.controller?.setProgress(this.progress); - this.controller?.setRepeatCount(this.repeatCount) - this.controller?.setSync(this.isSync); - this.controller?.setVideoEnabled(this.videoEnabled); - this.controller?.setCacheEnabled(this.cacheEnabled); - this.controller?.setCacheScale(this.cacheScale); - this.controller?.setMaxFrameRate(this.maxFrameRate); - if (PAGUtils.IsEmptyMatrix(this.matrix)) { - this.controller?.setScaleMode(this.scaleMode); - } else { - this.controller?.setMatrix(PAGUtils.ToNativeMatrix(this.matrix)); - } + /** + * Returns a rectangle in pixels that defines the displaying area of the specified layer, which + * is in the coordinate of the PAGView. + */ + getBounds(pagLayer: PAGLayer): common2D.Rect { + return PAGUtils.ToTsRect(this.jView.getBounds(pagLayer.nativeLayer)); } - private onCompositionChange() { - this.controller?.setComposition(this.composition?.getNativeComposition()); - if (!this.isPlaying) { - this.flush() - } + /** + * Adds a listener to the set of listeners that are sent events through the life of an + * animation, such as start, repeat, and end. + */ + addListener(listener: WeakRef) { + this.listeners.push(listener); } - private onPlayStateChange() { - if (this.isPlaying) { - this.controller?.play(); - } else { - this.controller?.pause(); + /** + * Removes a listener from the set listening to this animation. + */ + removeListener(listener: WeakRef) { + let index = this.listeners.indexOf(listener); + if (index != -1) { + this.listeners.splice(index); } } - private onRepeatCountChange() { - this.controller?.setRepeatCount(this.repeatCount); - } - - private onProgressChange() { - if (this.progress == this.nativeProgress) { - return; + attachToView(view: WeakRef) { + if (this.view != null && this.view != view) { + console.error("controller has attached to view"); } - this.controller?.setProgress(this.progress); - if (!this.isPlaying) { - this.flush() - } - } - - private OnSyncChange() { - this.controller?.setSync(this.isSync); + this.view = view; } - private OnVideoEnabledChange() { - this.controller?.setVideoEnabled(this.videoEnabled); + detachFromView() { + this.view = null; } - private OnCacheEnabledChange() { - this.controller?.setCacheEnabled(this.cacheEnabled); + uniqueID() { + return this.jView.uniqueID(); } - private OnCacheScaleChange() { - this.controller?.setCacheScale(this.cacheScale); + update() { + return this.jView.update(); } - private OnMaxFrameRateChange() { - this.controller?.setMaxFrameRate(this.maxFrameRate); - } - - private OnScaleModeChange() { - this.controller?.setScaleMode(this.scaleMode); - } - - private OnMatrixChange() { - this.controller?.setMatrix(PAGUtils.ToNativeMatrix(this.matrix)) + constructor() { + this.jView.setStateChangeCallback(this.onAnimatorStateChange); + this.jView.setProgressUpdateCallback(this.onAnimatorProgressUpdate); } private onAnimationStart() { - this.isPlaying = true; for (const weakListener of this.listeners) { const listener = weakListener.deref(); if (listener && listener.onAnimationStart) { @@ -328,7 +398,6 @@ export struct PAGView { } private onAnimationEnd() { - this.isPlaying = false; for (const weakListener of this.listeners) { const listener = weakListener.deref(); if (listener && listener.onAnimationEnd) { @@ -338,7 +407,6 @@ export struct PAGView { } private onAnimationCancel() { - this.isPlaying = false; for (const weakListener of this.listeners) { const listener = weakListener.deref(); if (listener && listener.onAnimationCancel) { @@ -356,8 +424,6 @@ export struct PAGView { } } - private nativeProgress: number = 0.0; - private controller: JPAGView | null = new JPAGView(); private onAnimatorStateChange = (state: number): void => { switch (state) { case PAGAnimatorState.Start: @@ -370,9 +436,7 @@ export struct PAGView { return this.onAnimationRepeat(); } } - private onAnimatorProgressUpdate = (progress: number): void => { - this.nativeProgress = progress; - this.progress = progress; + private onAnimatorProgressUpdate = (): void => { for (const weakListener of this.listeners) { const listener = weakListener.deref(); if (listener && listener.onAnimationUpdate) { @@ -380,11 +444,35 @@ export struct PAGView { } } } + private composition: PAGComposition | null = null; + private filePath: string | null = null; + private listeners: Array> = []; + private jView: JPAGView = new JPAGView(); + private view: WeakRef | null = null; } -enum PAGAnimatorState { - Start = 0, - Cancel = 1, - End = 2, - Repeat = 3 +@Component +export struct PAGView { + @ObjectLink controller: PAGViewController; + + build() { + XComponent({ + id: this.controller.uniqueID(), + type: XComponentType.SURFACE, + libraryname: "pag", + }) + .backgroundColor(Color.Transparent) + } + + onPageShow(): void { + this.controller.update(); + } + + aboutToAppear(): void { + this.controller.attachToView(new WeakRef(this)); + } + + aboutToDisappear(): void { + this.controller.detachFromView(); + } } \ No newline at end of file diff --git a/ohos/libpag/src/main/ets/private/PAGAnimatorState.ets b/ohos/libpag/src/main/ets/private/PAGAnimatorState.ets new file mode 100644 index 0000000000..baa47ca8af --- /dev/null +++ b/ohos/libpag/src/main/ets/private/PAGAnimatorState.ets @@ -0,0 +1,24 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making libpag available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file +// except in compliance with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +export enum PAGAnimatorState { + Start = 0, + Cancel = 1, + End = 2, + Repeat = 3 +} \ No newline at end of file diff --git a/src/platform/ohos/JPAGView.cpp b/src/platform/ohos/JPAGView.cpp index dce167be44..047a429124 100644 --- a/src/platform/ohos/JPAGView.cpp +++ b/src/platform/ohos/JPAGView.cpp @@ -127,6 +127,27 @@ static napi_value Flush(napi_env env, napi_callback_info info) { JPAGView* view = nullptr; napi_unwrap(env, jsView, reinterpret_cast(&view)); + bool flushResult = false; + if (view != nullptr) { + auto player = view->getPlayer(); + if (player != nullptr) { + flushResult = player->flush(); + } + } + napi_value result; + napi_get_boolean(env, flushResult, &result); + return result; +} + +static napi_value Update(napi_env env, napi_callback_info info) { + napi_value jsView = nullptr; + size_t argc = 0; + napi_value args[1] = {0}; + napi_get_cb_info(env, info, &argc, args, &jsView, nullptr); + + JPAGView* view = nullptr; + napi_unwrap(env, jsView, reinterpret_cast(&view)); + if (view != nullptr) { auto animator = view->getAnimator(); if (animator != nullptr) { @@ -150,13 +171,36 @@ static napi_value SetProgress(napi_env env, napi_callback_info info) { napi_unwrap(env, jsView, reinterpret_cast(&view)); if (view != nullptr) { auto animator = view->getAnimator(); - if (animator != nullptr) { + auto player = view->getPlayer(); + if (player != nullptr && animator != nullptr) { + player->setProgress(progress); animator->setProgress(progress); + animator->update(); } } return nullptr; } +static napi_value GetProgress(napi_env env, napi_callback_info info) { + napi_value jsView = nullptr; + size_t argc = 0; + napi_value args[1] = {0}; + napi_get_cb_info(env, info, &argc, args, &jsView, nullptr); + + double progress = 0; + JPAGView* view = nullptr; + napi_unwrap(env, jsView, reinterpret_cast(&view)); + if (view != nullptr) { + auto animator = view->getAnimator(); + if (animator != nullptr) { + progress = animator->progress(); + } + } + napi_value result; + napi_create_double(env, progress, &result); + return result; +} + static napi_value SetComposition(napi_env env, napi_callback_info info) { napi_value jsView = nullptr; size_t argc = 1; @@ -177,6 +221,7 @@ static napi_value SetComposition(napi_env env, napi_callback_info info) { if (layer != nullptr) { if (layer->layerType() == LayerType::PreCompose) { player->setComposition(std::static_pointer_cast(layer)); + animator->setProgress(player->getProgress()); animator->setDuration(layer->duration()); } } else { @@ -208,6 +253,26 @@ static napi_value SetRepeatCount(napi_env env, napi_callback_info info) { return nullptr; } +static napi_value RepeatCount(napi_env env, napi_callback_info info) { + napi_value jsView = nullptr; + size_t argc = 0; + napi_value args[1] = {0}; + napi_get_cb_info(env, info, &argc, args, &jsView, nullptr); + + int repeatCount = 0; + JPAGView* view = nullptr; + napi_unwrap(env, jsView, reinterpret_cast(&view)); + if (view != nullptr) { + auto animator = view->getAnimator(); + if (animator != nullptr) { + repeatCount = animator->repeatCount(); + } + } + napi_value result; + napi_create_int32(env, repeatCount, &result); + return result; +} + static napi_value Play(napi_env env, napi_callback_info info) { napi_value jsView = nullptr; size_t argc = 0; @@ -244,6 +309,26 @@ static napi_value Pause(napi_env env, napi_callback_info info) { return nullptr; } +static napi_value IsPlaying(napi_env env, napi_callback_info info) { + napi_value jsView = nullptr; + size_t argc = 0; + napi_value args[1] = {0}; + napi_get_cb_info(env, info, &argc, args, &jsView, nullptr); + + bool isPlaying = false; + JPAGView* view = nullptr; + napi_unwrap(env, jsView, reinterpret_cast(&view)); + if (view != nullptr) { + auto animator = view->getAnimator(); + if (animator != nullptr) { + isPlaying = animator->isRunning(); + } + } + napi_value result; + napi_get_boolean(env, isPlaying, &result); + return result; +} + static napi_value UniqueID(napi_env env, napi_callback_info info) { napi_value jsView = nullptr; size_t argc = 0; @@ -285,15 +370,10 @@ static napi_value SetStateChangeCallback(napi_env env, napi_callback_info info) return nullptr; } -static void ProgressCallback(napi_env env, napi_value callback, void*, void* data) { - double* progressPtr = static_cast(data); - size_t argc = 1; - napi_value argv[1] = {0}; - napi_create_double(env, *progressPtr, &argv[0]); +static void ProgressCallback(napi_env env, napi_value callback, void*, void*) { napi_value undefined; napi_get_undefined(env, &undefined); - napi_call_function(env, undefined, callback, argc, argv, nullptr); - delete progressPtr; + napi_call_function(env, undefined, callback, 0, nullptr, nullptr); } static napi_value SetProgressUpdateCallback(napi_env env, napi_callback_info info) { @@ -334,6 +414,26 @@ static napi_value SetSync(napi_env env, napi_callback_info info) { return nullptr; } +static napi_value IsSync(napi_env env, napi_callback_info info) { + napi_value jsView = nullptr; + size_t argc = 0; + napi_value args[1] = {0}; + napi_get_cb_info(env, info, &argc, args, &jsView, nullptr); + + bool isSync = false; + JPAGView* view = nullptr; + napi_unwrap(env, jsView, reinterpret_cast(&view)); + if (view != nullptr) { + auto animator = view->getAnimator(); + if (animator != nullptr) { + isSync = animator->isSync(); + } + } + napi_value result; + napi_get_boolean(env, isSync, &result); + return result; +} + static napi_value SetVideoEnabled(napi_env env, napi_callback_info info) { napi_value jsView = nullptr; size_t argc = 1; @@ -355,6 +455,26 @@ static napi_value SetVideoEnabled(napi_env env, napi_callback_info info) { return nullptr; } +static napi_value VideoEnabled(napi_env env, napi_callback_info info) { + napi_value jsView = nullptr; + size_t argc = 0; + napi_value args[1] = {0}; + napi_get_cb_info(env, info, &argc, args, &jsView, nullptr); + + bool videoEnabled = false; + JPAGView* view = nullptr; + napi_unwrap(env, jsView, reinterpret_cast(&view)); + if (view != nullptr) { + auto player = view->getPlayer(); + if (player != nullptr) { + videoEnabled = player->videoEnabled(); + } + } + napi_value result; + napi_get_boolean(env, videoEnabled, &result); + return result; +} + static napi_value SetCacheEnabled(napi_env env, napi_callback_info info) { napi_value jsView = nullptr; size_t argc = 1; @@ -376,6 +496,26 @@ static napi_value SetCacheEnabled(napi_env env, napi_callback_info info) { return nullptr; } +static napi_value CacheEnabled(napi_env env, napi_callback_info info) { + napi_value jsView = nullptr; + size_t argc = 0; + napi_value args[1] = {0}; + napi_get_cb_info(env, info, &argc, args, &jsView, nullptr); + + bool cacheEnabled = false; + JPAGView* view = nullptr; + napi_unwrap(env, jsView, reinterpret_cast(&view)); + if (view != nullptr) { + auto player = view->getPlayer(); + if (player != nullptr) { + cacheEnabled = player->cacheEnabled(); + } + } + napi_value result; + napi_get_boolean(env, cacheEnabled, &result); + return result; +} + static napi_value SetCacheScale(napi_env env, napi_callback_info info) { napi_value jsView = nullptr; size_t argc = 1; @@ -397,6 +537,26 @@ static napi_value SetCacheScale(napi_env env, napi_callback_info info) { return nullptr; } +static napi_value CacheScale(napi_env env, napi_callback_info info) { + napi_value jsView = nullptr; + size_t argc = 0; + napi_value args[1] = {0}; + napi_get_cb_info(env, info, &argc, args, &jsView, nullptr); + + float cacheScale = 1.0; + JPAGView* view = nullptr; + napi_unwrap(env, jsView, reinterpret_cast(&view)); + if (view != nullptr) { + auto player = view->getPlayer(); + if (player != nullptr) { + cacheScale = player->cacheScale(); + } + } + napi_value result; + napi_create_double(env, cacheScale, &result); + return result; +} + static napi_value SetMaxFrameRate(napi_env env, napi_callback_info info) { napi_value jsView = nullptr; size_t argc = 1; @@ -418,6 +578,26 @@ static napi_value SetMaxFrameRate(napi_env env, napi_callback_info info) { return nullptr; } +static napi_value MaxFrameRate(napi_env env, napi_callback_info info) { + napi_value jsView = nullptr; + size_t argc = 0; + napi_value args[1] = {0}; + napi_get_cb_info(env, info, &argc, args, &jsView, nullptr); + + float maxFrameRate = 60.0; + JPAGView* view = nullptr; + napi_unwrap(env, jsView, reinterpret_cast(&view)); + if (view != nullptr) { + auto player = view->getPlayer(); + if (player != nullptr) { + maxFrameRate = player->maxFrameRate(); + } + } + napi_value result; + napi_create_double(env, maxFrameRate, &result); + return result; +} + static napi_value SetScaleMode(napi_env env, napi_callback_info info) { napi_value jsView = nullptr; size_t argc = 1; @@ -439,6 +619,26 @@ static napi_value SetScaleMode(napi_env env, napi_callback_info info) { return nullptr; } +static napi_value ScaleMode(napi_env env, napi_callback_info info) { + napi_value jsView = nullptr; + size_t argc = 0; + napi_value args[1] = {0}; + napi_get_cb_info(env, info, &argc, args, &jsView, nullptr); + + int scaleMode = PAGScaleMode::LetterBox; + JPAGView* view = nullptr; + napi_unwrap(env, jsView, reinterpret_cast(&view)); + if (view != nullptr) { + auto player = view->getPlayer(); + if (player != nullptr) { + scaleMode = player->scaleMode(); + } + } + napi_value result; + napi_create_int32(env, scaleMode, &result); + return result; +} + static napi_value SetMatrix(napi_env env, napi_callback_info info) { napi_value jsView = nullptr; size_t argc = 1; @@ -458,6 +658,24 @@ static napi_value SetMatrix(napi_env env, napi_callback_info info) { return nullptr; } +static napi_value Matrix(napi_env env, napi_callback_info info) { + napi_value jsView = nullptr; + size_t argc = 0; + napi_value args[1] = {0}; + napi_get_cb_info(env, info, &argc, args, &jsView, nullptr); + + auto matrix = Matrix::I(); + JPAGView* view = nullptr; + napi_unwrap(env, jsView, reinterpret_cast(&view)); + if (view != nullptr) { + auto player = view->getPlayer(); + if (player != nullptr) { + matrix = player->matrix(); + } + } + return CreateMatrix(env, matrix); +} + static napi_value CurrentFrame(napi_env env, napi_callback_info info) { napi_value jsView = nullptr; size_t argc = 0; @@ -508,6 +726,28 @@ static napi_value GetLayersUnderPoint(napi_env env, napi_callback_info info) { return result; } +static napi_value GetBounds(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value args[1] = {nullptr}; + napi_value jsView = nullptr; + napi_get_cb_info(env, info, &argc, args, &jsView, nullptr); + JPAGView* view = nullptr; + napi_unwrap(env, jsView, reinterpret_cast(&view)); + Rect rect = Rect::MakeEmpty(); + if (!view) { + return CreateRect(env, rect); + } + auto layer = JPAGLayerHandle::FromJs(env, args[0]); + if (!layer) { + return CreateRect(env, rect); + } + auto player = view->getPlayer(); + if (player) { + rect = player->getBounds(layer); + } + return CreateRect(env, rect); +} + static napi_value FreeCache(napi_env env, napi_callback_info info) { napi_value jsView = nullptr; size_t argc = 0; @@ -547,11 +787,50 @@ static napi_value MakeSnapshot(napi_env env, napi_callback_info info) { return MakeSnapshot(env, surface.get()); } -static napi_value Release(napi_env env, napi_callback_info info) { +static napi_value UseDiskCache(napi_env env, napi_callback_info info) { napi_value jsView = nullptr; size_t argc = 0; napi_value args[1] = {0}; napi_get_cb_info(env, info, &argc, args, &jsView, nullptr); + + bool useDiskCache = false; + JPAGView* view = nullptr; + napi_unwrap(env, jsView, reinterpret_cast(&view)); + if (view != nullptr) { + auto player = view->getPlayer(); + if (player != nullptr) { + useDiskCache = player->useDiskCache(); + } + } + napi_value result; + napi_get_boolean(env, useDiskCache, &result); + return result; +} + +static napi_value SetUseDiskCache(napi_env env, napi_callback_info info) { + napi_value jsView = nullptr; + size_t argc = 1; + napi_value args[1] = {0}; + napi_get_cb_info(env, info, &argc, args, &jsView, nullptr); + JPAGView* view = nullptr; + napi_unwrap(env, jsView, reinterpret_cast(&view)); + if (view == nullptr) { + return nullptr; + } + auto player = view->getPlayer(); + if (player) { + bool useDiskCache = false; + napi_get_value_bool(env, args[0], &useDiskCache); + player->setUseDiskCache(useDiskCache); + } + return nullptr; +} + +static napi_value Release(napi_env env, napi_callback_info info) { + napi_value jsView = nullptr; + size_t argc = 1; + napi_value args[1] = {0}; + napi_get_cb_info(env, info, &argc, args, &jsView, nullptr); JPAGView* view = nullptr; napi_unwrap(env, jsView, reinterpret_cast(&view)); if (view == nullptr) { @@ -583,25 +862,39 @@ napi_value JPAGView::Constructor(napi_env env, napi_callback_info info) { bool JPAGView::Init(napi_env env, napi_value exports) { napi_property_descriptor classProp[] = { PAG_DEFAULT_METHOD_ENTRY(flush, Flush), + PAG_DEFAULT_METHOD_ENTRY(update, Update), PAG_DEFAULT_METHOD_ENTRY(setProgress, SetProgress), + PAG_DEFAULT_METHOD_ENTRY(getProgress, GetProgress), PAG_DEFAULT_METHOD_ENTRY(setComposition, SetComposition), PAG_DEFAULT_METHOD_ENTRY(setRepeatCount, SetRepeatCount), + PAG_DEFAULT_METHOD_ENTRY(repeatCount, RepeatCount), PAG_DEFAULT_METHOD_ENTRY(play, Play), PAG_DEFAULT_METHOD_ENTRY(pause, Pause), + PAG_DEFAULT_METHOD_ENTRY(isPlaying, IsPlaying), PAG_DEFAULT_METHOD_ENTRY(setStateChangeCallback, SetStateChangeCallback), PAG_DEFAULT_METHOD_ENTRY(setProgressUpdateCallback, SetProgressUpdateCallback), PAG_DEFAULT_METHOD_ENTRY(uniqueID, UniqueID), PAG_DEFAULT_METHOD_ENTRY(setSync, SetSync), + PAG_DEFAULT_METHOD_ENTRY(isSync, IsSync), PAG_DEFAULT_METHOD_ENTRY(setVideoEnabled, SetVideoEnabled), + PAG_DEFAULT_METHOD_ENTRY(videoEnabled, VideoEnabled), PAG_DEFAULT_METHOD_ENTRY(setCacheEnabled, SetCacheEnabled), + PAG_DEFAULT_METHOD_ENTRY(cacheEnabled, CacheEnabled), PAG_DEFAULT_METHOD_ENTRY(setCacheScale, SetCacheScale), + PAG_DEFAULT_METHOD_ENTRY(cacheScale, CacheScale), PAG_DEFAULT_METHOD_ENTRY(setMaxFrameRate, SetMaxFrameRate), + PAG_DEFAULT_METHOD_ENTRY(maxFrameRate, MaxFrameRate), PAG_DEFAULT_METHOD_ENTRY(setScaleMode, SetScaleMode), + PAG_DEFAULT_METHOD_ENTRY(scaleMode, ScaleMode), PAG_DEFAULT_METHOD_ENTRY(setMatrix, SetMatrix), + PAG_DEFAULT_METHOD_ENTRY(matrix, Matrix), PAG_DEFAULT_METHOD_ENTRY(currentFrame, CurrentFrame), PAG_DEFAULT_METHOD_ENTRY(getLayersUnderPoint, GetLayersUnderPoint), + PAG_DEFAULT_METHOD_ENTRY(getBounds, GetBounds), PAG_DEFAULT_METHOD_ENTRY(freeCache, FreeCache), PAG_DEFAULT_METHOD_ENTRY(makeSnapshot, MakeSnapshot), + PAG_DEFAULT_METHOD_ENTRY(useDiskCache, UseDiskCache), + PAG_DEFAULT_METHOD_ENTRY(setUseDiskCache, SetUseDiskCache), PAG_DEFAULT_METHOD_ENTRY(release, Release)}; auto status = DefineClass(env, exports, ClassName(), sizeof(classProp) / sizeof(classProp[0]), classProp, Constructor, ""); @@ -668,7 +961,7 @@ void JPAGView::onAnimationRepeat(PAGAnimator*) { void JPAGView::onAnimationUpdate(PAGAnimator* animator) { std::lock_guard lock_guard(locker); if (progressCallback) { - napi_call_threadsafe_function(progressCallback, new double(animator->progress()), + napi_call_threadsafe_function(progressCallback, nullptr, napi_threadsafe_function_call_mode::napi_tsfn_nonblocking); }