diff --git a/CHANGELOG.md b/CHANGELOG.md index b9fa439..b869b5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 3.0.0-rc.6 +- 支持多种组合变换 + ## 3.0.0-rc.5 - 图片加载事件增加请求开始的回调,以及修复有缓存时,没有回调的bug - 修复对已销毁组件不再下发请求的逻辑 diff --git a/README.md b/README.md index dbe3f5a..f0d4350 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,26 @@ ImageKnifeComponent({ ImageKnifeOption: } }).width(100).height(100) ``` +多种组合变换用法 + +``` +let transformations: collections.Array = new collections.Array(); +transformations.push(new BlurTransformation(5)); +transformations.push(new BrightnessTransformation(0.2)); +ImageKnifeComponent({ + { + loadSrc: $r('app.media.pngSample'), + placeholderSrc: $r("app.media.loading"), + errorholderSrc: $r("app.media.app_icon"), + objectFit: ImageFit.Contain, + border: { radius: { topLeft: 50, bottomRight: 50 } }, // 圆角设置 + transformation: transformations.length > 0 ? new MultiTransTransformation(transformations) : undefined // 图形变换组 +} +}).width(300) + .height(300) + .rotate({ angle: 90 }) // 旋转90度 + .contrast(12) // 对比度滤波器 +``` #### 8.监听图片加载成功与失败 @@ -210,7 +230,38 @@ ImageKnifeComponent({ ImageKnifeOption: | putCacheImage | url: string, pixelMap: PixelMap, cacheType: CacheStrategy = CacheStrategy.Default, signature?: string | 写入内存磁盘缓存 | | removeMemoryCache| url: string | ImageKnifeOption | 清理指定内存缓存 | | removeFileCache | url: string | ImageKnifeOption | 清理指定磁盘缓存 | +### 图形变换类型(需要为GPUImage添加依赖项) +| 类型 | 相关描述 | +| ---------------------------------- | ----------------------------- | +| BlurTransformation | 模糊处理 | +| BrightnessTransformation | 亮度滤波器 | +| CropCircleTransformation | 圆形剪裁显示 | +| CropCircleWithBorderTransformation | 圆环展示 | +| CropSquareTransformation | 正方形剪裁 | +| CropTransformation | 自定义矩形剪裁 | +| GrayScaleTransformation | 灰度级滤波器 | +| InvertTransformation | 反转滤波器 | +| KuwaharaTransformation | 桑原滤波器(使用GPUIImage) | +| MaskTransformation | 遮罩 | +| PixelationTransformation | 像素化滤波器(使用GPUIImage) | +| SepiaTransformation | 乌墨色滤波器(使用GPUIImage) | +| SketchTransformation | 素描滤波器(使用GPUIImage) | +| SwirlTransformation | 扭曲滤波器(使用GPUIImage) | +| ToonTransformation | 动画滤波器(使用GPUIImage) | +| VignetterTransformation | 装饰滤波器(使用GPUIImage) | + +## 下载安装GPUImage依赖 +方法一:在Terminal窗口中,执行如下命令安装三方包,DevEco Studio会自动在工程的oh-package.json5中自动添加三方包依赖。 +``` + ohpm install @ohos/gpu_transform +``` +方法二: 在工程的oh-package.json5中设置三方包依赖,配置示例如下: +``` + "dependencies": { + "@ohos/gpu_transform": "^1.0.2" + } +``` ## 约束与限制 在下述版本验证通过: diff --git a/entry/src/main/ets/pages/ImageTransformation.ets b/entry/src/main/ets/pages/ImageTransformation.ets index ed863ca..32da2e7 100644 --- a/entry/src/main/ets/pages/ImageTransformation.ets +++ b/entry/src/main/ets/pages/ImageTransformation.ets @@ -1,82 +1,417 @@ -import { BlurTransformation, BrightnessTransformation, ImageKnifeComponent, ImageKnifeOption, +import { + BlurTransformation, + BrightnessTransformation, + CropCircleTransformation, + CropCircleWithBorderTransformation, + CropSquareTransformation, + CropTransformation, + GrayScaleTransformation, + ImageKnifeComponent, + ImageKnifeOption, + InvertTransformation, + KuwaharaTransformation, + MaskTransformation, MultiTransTransformation, - PixelMapTransformation } from '@ohos/libraryimageknife' + PixelationTransformation, + PixelMapTransformation, + SepiaTransformation, + SketchTransformation, + SwirlTransformation, + ToonTransformation, + VignetterTransformation +} from '@ohos/imageknife'; import { collections } from '@kit.ArkTS' @Entry @Component struct ImageTransformation { @State imageKnifeOption: ImageKnifeOption = { - loadSrc: "http://g.hiphotos.baidu.com/image/pic/item/6d81800a19d8bc3e770bd00d868ba61ea9d345f2.jpg", + loadSrc: $r('app.media.pngSample'), placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Contain } + @State isRound: boolean = false; + @State isContrast: boolean = false; + @State isRotate: boolean = false; isBlur: boolean = false isBrightness: boolean = false + isGrayScale: boolean = false; + isInvert: boolean = false; + isToon: boolean = false; + isCropCircle: boolean = false; + isCropCircleWithBorder: boolean = false; + isKuwahara: boolean = false; + isPixelation: boolean = false; + isSketch: boolean = false; + isSwirl: boolean = false; + isVignetter: boolean = false; + isCropSquare: boolean = false; + isCropTop: boolean = false; + isCropCenter: boolean = false; + isCropBottom: boolean = false; + isMask: boolean = false; + isSepia: boolean = false; build() { - Column() { - Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { - Checkbox({ name: 'checkbox1', group: 'checkboxGroup' }) - .selectedColor(0x39a2db) - .shape(CheckBoxShape.ROUNDED_SQUARE) - .onChange((value: boolean) => { - this.isBlur = value - this.upedateImageKnifeOption() - }) - .width(30) - .height(30) - Text('模糊效果').fontSize(20) - } + Scroll() { + Column() { + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox1', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isBlur = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('模糊效果').fontSize(20) + } - Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { - Checkbox({ name: 'checkbox2', group: 'checkboxGroup' }) - .selectedColor(0x39a2db) - .shape(CheckBoxShape.ROUNDED_SQUARE) - .onChange((value: boolean) => { - this.isBrightness = value - this.upedateImageKnifeOption() - }) - .width(30) - .height(30) - Text('高亮效果').fontSize(20) - } + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox2', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isBrightness = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('高亮效果').fontSize(20) + } - Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { - Checkbox({ name: 'checkbox3', group: 'checkboxGroup' }) - .selectedColor(0x39a2db) - .shape(CheckBoxShape.ROUNDED_SQUARE) - .onChange((value: boolean) => { - console.info('Checkbox3 change is' + value) - }) - .width(30) - .height(30) - Text('Checkbox3').fontSize(20) - } + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox3', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isGrayScale = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('灰化效果').fontSize(20) + } - ImageKnifeComponent({ - imageKnifeOption: this.imageKnifeOption - }).width(300).height(300) + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox4', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isInvert = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('反转效果').fontSize(20) + } + + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox5', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isToon = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('动画滤镜效果').fontSize(20) + } + + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox6', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isCropCircle = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('裁剪圆形效果').fontSize(20) + } + + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox7', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isCropCircleWithBorder = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('裁剪圆形带边框效果').fontSize(20) + } + + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox8', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isContrast = value; + }) + .width(30) + .height(30) + Text('对比度效果').fontSize(20) + } + + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox9', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isSepia = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('乌墨色滤波效果').fontSize(20) + } + + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox10', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isRotate = value; + }) + .width(30) + .height(30) + Text('旋转效果').fontSize(20) + } + + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox11', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isRound = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('圆角效果').fontSize(20) + } + + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox12', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isKuwahara = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('桑原滤波效果').fontSize(20) + } + + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox13', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isPixelation = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('像素化滤波效果').fontSize(20) + } + + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox14', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isSketch = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('素描滤波效果').fontSize(20) + } + + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox15', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isSwirl = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('扭曲滤波效果').fontSize(20) + } + + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox16', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isVignetter = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('装饰滤波效果').fontSize(20) + } + + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox17', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isCropSquare = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('正方形裁剪效果').fontSize(20) + } + + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox18', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isCropTop = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('上方裁剪效果').fontSize(20) + } + + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox19', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isCropCenter = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('中间裁剪效果').fontSize(20) + } + + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox20', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isCropBottom = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('底下裁剪效果').fontSize(20) + } + + Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { + Checkbox({ name: 'checkbox21', group: 'checkboxGroup' }) + .selectedColor(0x39a2db) + .shape(CheckBoxShape.ROUNDED_SQUARE) + .onChange((value: boolean) => { + this.isMask = value; + this.updateImageKnifeOption(); + }) + .width(30) + .height(30) + Text('遮罩效果').fontSize(20) + } + + if (this.isContrast) { + ImageKnifeComponent({ + imageKnifeOption: this.imageKnifeOption + }) + .width(300) + .height(300) + .rotate({ angle: this.isRotate ? 90 : 0 }) + .contrast(12) + .backgroundColor(Color.Pink) + } else { + ImageKnifeComponent({ + imageKnifeOption: this.imageKnifeOption + }).width(300) + .height(300) + .rotate({ angle: this.isRotate ? 90 : 0 }) + .backgroundColor(Color.Pink) + } + } } .height('100%') .width('100%') } - upedateImageKnifeOption() { + updateImageKnifeOption() { let transformations: collections.Array = new collections.Array() - if (this.isBlur){ - transformations.push(new BlurTransformation(5)) + if (this.isBlur) { + transformations.push(new BlurTransformation(5)); } - if (this.isBrightness){ - transformations.push(new BrightnessTransformation(0.2)) + if (this.isBrightness) { + transformations.push(new BrightnessTransformation(0.2)); + } + if (this.isGrayScale) { + transformations.push(new GrayScaleTransformation()); + } + if (this.isInvert) { + transformations.push(new InvertTransformation()); + } + if (this.isToon) { + transformations.push(new ToonTransformation(0.3, 10.0)); + } + if (this.isCropCircle) { + transformations.push(new CropCircleTransformation()); + } + if (this.isCropCircleWithBorder) { + transformations.push(new CropCircleWithBorderTransformation(16, { r_color: 100, g_color: 100, b_color: 0 })); + } + if (this.isKuwahara) { + transformations.push(new KuwaharaTransformation(10)); + } + if (this.isPixelation) { + transformations.push(new PixelationTransformation(5.0)); + } + if (this.isSketch) { + transformations.push(new SketchTransformation()); + } + if (this.isSwirl) { + transformations.push(new SwirlTransformation(200, 1.0, [0.5, 0.5])); + } + if (this.isVignetter) { + transformations.push(new VignetterTransformation([0.5, 0.5], [0.0, 0.0, 0.0], [0.3, 0.75])); + } + if (this.isCropSquare) { + transformations.push(new CropSquareTransformation()); + } + if (this.isCropTop) { + transformations.push(new CropTransformation(25, 25, 0)); + } + if (this.isCropCenter) { + transformations.push(new CropTransformation(25, 25, 1)); + } + if (this.isCropBottom) { + transformations.push(new CropTransformation(25, 25, 2)); + } + if (this.isSepia) { + transformations.push(new SepiaTransformation()); + } + if (this.isMask) { + transformations.push(new MaskTransformation($r('app.media.mask_starfish'))); } - this.imageKnifeOption = { - loadSrc: "http://g.hiphotos.baidu.com/image/pic/item/6d81800a19d8bc3e770bd00d868ba61ea9d345f2.jpg", + loadSrc: $r('app.media.pngSample'), placeholderSrc: $r("app.media.loading"), errorholderSrc: $r("app.media.app_icon"), objectFit: ImageFit.Contain, + border: { radius: this.isRound ? { topLeft: 50, bottomRight: 50 } : 0 }, transformation: transformations.length > 0 ? new MultiTransTransformation(transformations) : undefined } } diff --git a/entry/src/main/resources/base/media/mask_starfish.png b/entry/src/main/resources/base/media/mask_starfish.png new file mode 100644 index 0000000..34f4a7f Binary files /dev/null and b/entry/src/main/resources/base/media/mask_starfish.png differ diff --git a/entry/src/main/resources/base/media/pngSample.png b/entry/src/main/resources/base/media/pngSample.png new file mode 100644 index 0000000..df4a140 Binary files /dev/null and b/entry/src/main/resources/base/media/pngSample.png differ diff --git a/entry/src/ohosTest/ets/test/List.test.ets b/entry/src/ohosTest/ets/test/List.test.ets index a730b72..8c04309 100644 --- a/entry/src/ohosTest/ets/test/List.test.ets +++ b/entry/src/ohosTest/ets/test/List.test.ets @@ -17,6 +17,7 @@ import FileLruCacheTest from './FileLruCache.test'; import ImageKnifeOptionTest from './ImageKnifeOption.test'; import MemoryLruCacheTest from './MemoryLruCache.test'; import ImageKnifeTest from './ImageKnife.test'; +import Transform from './transform.test'; export default function testsuite() { MemoryLruCacheTest(); @@ -24,4 +25,5 @@ export default function testsuite() { DefaultJobQueueTest(); ImageKnifeOptionTest(); ImageKnifeTest(); + Transform(); } \ No newline at end of file diff --git a/entry/src/ohosTest/ets/test/transform.test.ets b/entry/src/ohosTest/ets/test/transform.test.ets new file mode 100644 index 0000000..cec2fe7 --- /dev/null +++ b/entry/src/ohosTest/ets/test/transform.test.ets @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' +import { + BlurTransformation, + BrightnessTransformation, + CropCircleTransformation, + CropCircleWithBorderTransformation, + CropSquareTransformation, + CropTransformation, + GrayScaleTransformation, + InvertTransformation, + KuwaharaTransformation, + MaskTransformation, + PixelationTransformation, + SepiaTransformation, + SketchTransformation, + SwirlTransformation, + ToonTransformation, + VignetterTransformation +} from '@ohos/imageknife' + +const BASE_COUNT: number = 1000; + +export default function Transform() { + describe('Transform', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + + }) + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('TestBlurTransformation', 0, () => { + let startTime = new Date().getTime(); + for (let index = 0; index < BASE_COUNT; index++) { + new BlurTransformation(15); + } + endTime(startTime, 'TestBlurTransformation'); + let blur = new BlurTransformation(15); + expect(blur.getName()).assertEqual('BlurTransformation;radius:15'); + }) + it('TestBrightnessTransformation', 1, () => { + let startTime = new Date().getTime(); + for (let index = 0; index < BASE_COUNT; index++) { + new BrightnessTransformation(20); + } + endTime(startTime, 'BrightnessTransformation'); + let bright = new BrightnessTransformation(20); + expect(bright.getName()).assertEqual("BrightnessTransformation;bright:20"); + }) + it('TestCropCircleTransformation', 3, () => { + let startTime = new Date().getTime(); + for (let index = 0; index < BASE_COUNT; index++) { + new CropCircleTransformation(); + } + endTime(startTime, 'TestCropCircleTransformation'); + let cropCircle = new CropCircleTransformation(); + expect(cropCircle.getName()).assertContain("CropCircleTransformation"); + expect(cropCircle.getName()).assertContain(";mCenterX:"); + expect(cropCircle.getName()).assertContain(";mCenterY:"); + expect(cropCircle.getName()).assertContain(";mRadius:"); + }) + it('TestCropCircleWithBorderTransformation', 4, () => { + let startTime = new Date().getTime(); + for (let index = 0; index < BASE_COUNT; index++) { + new CropCircleWithBorderTransformation(10, { + r_color: 100, g_color: 100, b_color: 100 + }); + } + endTime(startTime, 'TestCropCircleWithBorderTransformation'); + let CropCircleWithBorder = new CropCircleWithBorderTransformation(10, { + r_color: 100, g_color: 100, b_color: 100 + }); + expect(CropCircleWithBorder.getName()).assertContain("CropCircleWithBorderTransformation") + expect(CropCircleWithBorder.getName()).assertContain(";mCenterX:"); + expect(CropCircleWithBorder.getName()).assertContain(";mCenterY:"); + expect(CropCircleWithBorder.getName()).assertContain(";mRadius:"); + expect(CropCircleWithBorder.getName()).assertContain(";mBorderSize:"); + expect(CropCircleWithBorder.getName()).assertContain(";mRColor:"); + expect(CropCircleWithBorder.getName()).assertContain(";mGColor:"); + expect(CropCircleWithBorder.getName()).assertContain(";mBColor:"); + }) + it('TestCropSquareTransformation', 5, () => { + let startTime = new Date().getTime(); + for (let index = 0; index < BASE_COUNT; index++) { + new CropSquareTransformation(); + } + endTime(startTime, 'TestCropSquareTransformation'); + let CropSquare = new CropSquareTransformation(); + expect(CropSquare.getName()).assertContain("CropSquareTransformation"); + }) + it('TestCropTransformation', 6, () => { + let startTime = new Date().getTime(); + for (let index = 0; index < BASE_COUNT; index++) { + new CropTransformation(10, 10, 1); + } + endTime(startTime, 'TestCropTransformation'); + let crop = new CropTransformation(10, 10, 1); + expect(crop.getName()).assertContain("CropTransformation" + ";mWidth:10" + ";mHeight:10" + ";mCropType:1") + }) + it('TestGrayScaleTransformation', 7, () => { + let startTime = new Date().getTime(); + for (let index = 0; index < BASE_COUNT; index++) { + new GrayScaleTransformation(); + } + endTime(startTime, 'GrayScaleTransformation'); + let grayscale = new GrayScaleTransformation(); + expect(grayscale.getName()).assertContain("GrayScaleTransformation") + }) + it('TestInvertTransformation', 8, () => { + let startTime = new Date().getTime(); + for (let index = 0; index < BASE_COUNT; index++) { + new InvertTransformation(); + } + endTime(startTime, 'TestInvertFilterTransformation'); + let invert = new InvertTransformation(); + expect(invert.getName()).assertContain("InvertTransformation"); + }) + it('TestPixelationTransformation', 9, () => { + let startTime = new Date().getTime(); + for (let index = 0; index < BASE_COUNT; index++) { + new PixelationTransformation(); + } + endTime(startTime, 'TestPixelationTransformation'); + let pixelation = new PixelationTransformation(); + expect(pixelation.getName()).assertContain("PixelationTransformation"); + }) + it('TestSepiaTransformation', 12, () => { + let startTime = new Date().getTime(); + for (let index = 0; index < BASE_COUNT; index++) { + new SepiaTransformation(); + } + endTime(startTime, 'SepiaTransformation'); + let speia = new SepiaTransformation(); + expect(speia.getName()).assertContain("SepiaTransformation"); + }) + it('TestSketchTransformation', 13, () => { + let startTime = new Date().getTime(); + for (let index = 0; index < BASE_COUNT; index++) { + new SketchTransformation(); + } + endTime(startTime, 'TestSketchTransformation'); + let Sketch = new SketchTransformation(); + expect(Sketch.getName()).assertContain("SketchTransformation"); + }) + it('TestMaskTransformation', 14, () => { + let startTime = new Date().getTime(); + for (let index = 0; index < BASE_COUNT; index++) { + new MaskTransformation($r('app.media.icon')); + } + endTime(startTime, 'TestMaskTransformation'); + let mask = new MaskTransformation($r('app.media.icon')); + expect(mask.getName()).assertContain("MaskTransformation"); + }) + it('TestSwirlTransformation', 15, () => { + let startTime = new Date().getTime(); + for (let index = 0; index < BASE_COUNT; index++) { + new SwirlTransformation(10, 180, [10, 10]); + } + endTime(startTime, 'TestSwirlTransformation'); + let swirl = new SwirlTransformation(10, 180, [10, 10]); + expect(swirl.getName()).assertContain("SwirlTransformation"); + }) + it('TestKuwaharaTransformation', 16, () => { + let startTime = new Date().getTime(); + for (let index = 0; index < BASE_COUNT; index++) { + new KuwaharaTransformation(10); + } + endTime(startTime, 'TestKuwaharaTransformation'); + let kuwahara = new KuwaharaTransformation(10); + expect(kuwahara.getName()).assertContain("KuwaharaTransformation;radius:10"); + }) + it('TestToonTransformation', 17, () => { + let startTime = new Date().getTime(); + for (let index = 0; index < BASE_COUNT; index++) { + new ToonTransformation(10); + } + endTime(startTime, 'TestToonTransformation'); + let toon = new ToonTransformation(10); + expect(toon.getName()).assertContain("ToonTransformation;threshold:10"); + }) + it('TestVignetterTransformation', 18, () => { + let startTime = new Date().getTime(); + for (let index = 0; index < BASE_COUNT; index++) { + new VignetterTransformation([0.5, 0.5], [0.0, 0.0, 0.0], [0.3, 0.75]); + } + endTime(startTime, 'TestVignetterTransformation'); + let vignette = new VignetterTransformation([0.5, 0.5], [0.0, 0.0, 0.0], [0.3, 0.75]); + expect(vignette.getName()).assertContain("VignetterTransformation"); + }) + }) +} + +function endTime(startTime: number, tag: string) { + let endTime: number = new Date().getTime(); + let averageTime = ((endTime - startTime) * 1000 / BASE_COUNT); + console.info(tag + " startTime: " + endTime); + console.info(tag + " endTime: " + endTime); + console.log(tag + " averageTime: " + averageTime + "μs"); +} \ No newline at end of file diff --git a/library/index.ets b/library/index.ets index bbeed44..1b690d5 100644 --- a/library/index.ets +++ b/library/index.ets @@ -24,4 +24,30 @@ export { BlurTransformation } from './src/main/ets/transform/BlurTransformation' export { SparkMD5 } from "./src/main/ets/3rd_party/sparkmd5/spark-md5" +export { GrayScaleTransformation } from './src/main/ets/transform/GrayScaleTransformation' +export { InvertTransformation } from './src/main/ets/transform/InvertTransformation' + +export { ToonTransformation } from './src/main/ets/transform/ToonTransformation' + +export { CropCircleTransformation } from './src/main/ets/transform/CropCircleTransformation' + +export { CropCircleWithBorderTransformation } from './src/main/ets/transform/CropCircleWithBorderTransformation' + +export { KuwaharaTransformation } from './src/main/ets/transform/KuwaharaTransformation' + +export { PixelationTransformation } from './src/main/ets/transform/PixelationTransformation' + +export { SketchTransformation } from './src/main/ets/transform/SketchTransformation' + +export { SwirlTransformation } from './src/main/ets/transform/SwirlTransformation' + +export { VignetterTransformation } from './src/main/ets/transform/VignetterTransformation' + +export { CropSquareTransformation } from './src/main/ets/transform/CropSquareTransformation' + +export { CropTransformation } from './src/main/ets/transform/CropTransformation' + +export { MaskTransformation } from './src/main/ets/transform/MaskTransformation' + +export { SepiaTransformation } from './src/main/ets/transform/SepiaTransformation' \ No newline at end of file diff --git a/library/oh-package.json5 b/library/oh-package.json5 index 4de491c..e7309db 100644 --- a/library/oh-package.json5 +++ b/library/oh-package.json5 @@ -16,7 +16,7 @@ "type": "module", "version": "3.0.0-rc.4", "dependencies": { - + "@ohos/gpu_transform": "^1.0.2" }, "tags": [ "ImageCache", diff --git a/library/src/main/ets/ImageKnifeDispatcher.ets b/library/src/main/ets/ImageKnifeDispatcher.ets index 0e099ea..6937419 100644 --- a/library/src/main/ets/ImageKnifeDispatcher.ets +++ b/library/src/main/ets/ImageKnifeDispatcher.ets @@ -146,6 +146,8 @@ export class ImageKnifeDispatcher { src: imageSrc, headers: currentRequest.imageKnifeOption.headerOption, allHeaders: currentRequest.headers, + componentWidth:currentRequest.componentWidth, + componentHeight:currentRequest.componentHeight, customGetImage: currentRequest.imageKnifeOption.customGetImage, onlyRetrieveFromCache: currentRequest.imageKnifeOption.onlyRetrieveFromCache, transformation: currentRequest.imageKnifeOption.transformation, @@ -470,7 +472,7 @@ async function requestJob(request: RequestJobRequest): Promise, allHeaders: Map, + componentWidth: number, + componentHeight: number, customGetImage?: (context: Context, src: string | PixelMap | Resource) => Promise, onlyRetrieveFromCache?: boolean requestSource: ImageKnifeRequestSource diff --git a/library/src/main/ets/transform/BlurTransformation.ets b/library/src/main/ets/transform/BlurTransformation.ets index 07de719..10af8cd 100644 --- a/library/src/main/ets/transform/BlurTransformation.ets +++ b/library/src/main/ets/transform/BlurTransformation.ets @@ -27,6 +27,10 @@ export class BlurTransformation extends PixelMapTransformation { this.radius = radius } + getName(): string { + return this.constructor.name + ';radius:' + this.radius; + } + async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { let headFilter = effectKit.createEffect(toTransform); if (headFilter != null) { diff --git a/library/src/main/ets/transform/BrightnessTransformation.ets b/library/src/main/ets/transform/BrightnessTransformation.ets index 1b68426..a263ec9 100644 --- a/library/src/main/ets/transform/BrightnessTransformation.ets +++ b/library/src/main/ets/transform/BrightnessTransformation.ets @@ -27,6 +27,10 @@ export class BrightnessTransformation extends PixelMapTransformation { this.bright = bright } + getName(): string { + return this.constructor.name + ';bright:' + this.bright; + } + async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { let headFilter = effectKit.createEffect(toTransform); if (headFilter != null) { diff --git a/library/src/main/ets/transform/CropCircleTransformation.ets b/library/src/main/ets/transform/CropCircleTransformation.ets new file mode 100644 index 0000000..707ecbf --- /dev/null +++ b/library/src/main/ets/transform/CropCircleTransformation.ets @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 { PixelMapTransformation } from './PixelMapTransformation'; +import { Size } from '@kit.ArkUI'; +import { image } from '@kit.ImageKit'; + +/** + * 图片变换:圆形裁剪效果 + */ +@Sendable +export class CropCircleTransformation extends PixelMapTransformation { + private mCenterX: number = 0; + private mCenterY: number = 0; + private mRadius: number = 0; + + constructor() { + super(); + } + + getName(): string { + return this.constructor.name + ';mCenterX:' + this.mCenterX + ';mCenterY:' + this.mCenterY + ';mRadius:' + this.mRadius; + } + + async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { + return await this.transformCircle(toTransform); + } + + private async transformCircle(data: PixelMap): Promise { + let imageInfo: image.ImageInfo = await data.getImageInfo(); + let size: Size = { + width: imageInfo.size.width, + height: imageInfo.size.height + }; + if (!size) { + console.error("CropCircleTransformation The image size does not exist."); + return data; + } + let height: number = size.height; + let width: number = size.width; + this.mRadius = 0; + if (width > height) { + this.mRadius = height / 2; + } else { + this.mRadius = width / 2; + } + this.mCenterX = width / 2; + this.mCenterY = height / 2; + + let bufferData: ArrayBuffer = new ArrayBuffer(data.getPixelBytesNumber()); + await data.readPixelsToBuffer(bufferData); + + let dataArray = new Uint8Array(bufferData); + + for (let h = 0; h <= height; h++) { + for (let w = 0; w <= width; w++) { + if (this.isContainsCircle(w, h)) { + continue; + } + // 针对的点 + let index = (h * width + w) * 4; + dataArray[index] = 0; + dataArray[index+1] = 0; + dataArray[index+2] = 0; + dataArray[index+3] = 0; + } + } + await data.writeBufferToPixels(bufferData); + return data; + } + + isContainsCircle(x: number, y: number): boolean { + let a = Math.pow((this.mCenterX - x), 2); + let b = Math.pow((this.mCenterY - y), 2); + let c = Math.sqrt((a + b)); + return c <= this.mRadius; + } +} \ No newline at end of file diff --git a/library/src/main/ets/transform/CropCircleWithBorderTransformation.ets b/library/src/main/ets/transform/CropCircleWithBorderTransformation.ets new file mode 100644 index 0000000..bad7336 --- /dev/null +++ b/library/src/main/ets/transform/CropCircleWithBorderTransformation.ets @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 { PixelMapTransformation } from './PixelMapTransformation'; +import { Size } from '@kit.ArkUI'; +import { image } from '@kit.ImageKit'; + +export interface rgbColor { + r_color: number, + g_color: number, + b_color: number, +} + +/** + * 图片变换:圆环裁剪效果 + */ +@Sendable +export class CropCircleWithBorderTransformation extends PixelMapTransformation { + private mBorderSize: number = 5; + private mCenterX: number = 0; + private mCenterY: number = 0; + private mRadius: number = 0; + private mRColor: number = 0; + private mGColor: number = 0; + private mBColor: number = 0; + + constructor(borderSize: number, value: rgbColor) { + super(); + this.mRColor = value.g_color; + this.mGColor = value.g_color; + this.mBColor = value.b_color; + this.mBorderSize = borderSize; + } + + getConstructorParams() { + return JSON.stringify([this.mBorderSize, { + r_color: this.mRColor, + g_color: this.mGColor, + b_color: this.mBColor + }]); + } + + getName(): string { + return this.constructor.name + ';mBorderSize:' + this.mBorderSize + ';mCenterX:' + this.mCenterX + ';mCenterY:' + + this.mCenterY + ';mRadius:' + this.mRadius + ';mRColor:' + this.mRColor + ';mGColor:' + this.mGColor + ';mBColor:' + this.mBColor; + } + + async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { + return await this.transformPixelMap(toTransform); + } + + private async transformPixelMap(pixelMap: PixelMap): Promise { + let imageInfo: image.ImageInfo = await pixelMap.getImageInfo(); + let size: Size = { + width: imageInfo.size.width, + height: imageInfo.size.height + }; + if (!size) { + console.error("CropCircleWithBorderTransformation The image size does not exist."); + return pixelMap; + } + let height: number = size.height; + let width: number = size.width; + this.mRadius = 0; + if (width > height) { + this.mRadius = height / 2; + } else { + this.mRadius = width / 2; + } + this.mCenterX = width / 2; + this.mCenterY = height / 2; + + + let bufferData = new ArrayBuffer(pixelMap.getPixelBytesNumber()); + await pixelMap.readPixelsToBuffer(bufferData); + + let dataArray = new Uint8Array(bufferData); + + for (let h = 0; h <= height; h++) { + for (let w = 0; w <= width; w++) { + // 不在大圆之内的设置透明 + // 在大圆与小圆之间的 设置rgb值 + // 小圆之内的不变 + let isSmallCircle: boolean = this.isContainsSmallCircle(w, h); + let isBigCircle: boolean = this.isContainsCircle(w, h); + if (isSmallCircle) { + continue; + } + + let index = (h * width + w) * 4; + if (!isBigCircle) { + // 设置透明 + dataArray[index] = 0; + dataArray[index+1] = 0; + dataArray[index+2] = 0; + dataArray[index+3] = 0; + } else { + // 设置broke + dataArray[index] = this.mRColor; + dataArray[index+1] = this.mGColor; + dataArray[index+2] = this.mBColor; + } + } + } + await pixelMap.writeBufferToPixels(bufferData); + return pixelMap; + } + + isContainsCircle(x: number, y: number): boolean { + let a: number = Math.pow((this.mCenterX - x), 2); + let b: number = Math.pow((this.mCenterY - y), 2); + let c: number = Math.sqrt((a + b)); + return c <= this.mRadius; + } + + isContainsSmallCircle(x: number, y: number): boolean { + let a: number = Math.pow((this.mCenterX - x), 2); + let b: number = Math.pow((this.mCenterY - y), 2); + let c: number = Math.sqrt((a + b)); + return c <= (this.mRadius - this.mBorderSize); + } +} \ No newline at end of file diff --git a/library/src/main/ets/transform/CropSquareTransformation.ets b/library/src/main/ets/transform/CropSquareTransformation.ets new file mode 100644 index 0000000..f939845 --- /dev/null +++ b/library/src/main/ets/transform/CropSquareTransformation.ets @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 { PixelMapTransformation } from './PixelMapTransformation'; +import { Size } from '@kit.ArkUI'; +import { image } from '@kit.ImageKit'; + +/** + * 图片变换:正方形裁剪效果 + */ +@Sendable +export class CropSquareTransformation extends PixelMapTransformation { + constructor() { + super(); + } + + async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { + let imageInfo: image.ImageInfo = await toTransform.getImageInfo(); + let size: Size = { + width: imageInfo.size.width, + height: imageInfo.size.height + }; + if (!size) { + console.error("CropSquareTransformation The image size does not exist."); + return toTransform; + } + let pixelMapWidth: number = size.width; + let pixelMapHeight: number = size.height; + let targetSize: number = pixelMapWidth > pixelMapHeight ? pixelMapHeight : pixelMapWidth; + let region: image.Region = { + size: { width: targetSize, height: targetSize }, + x: pixelMapWidth / 2 - targetSize / 2, + y: pixelMapHeight / 2 - targetSize / 2 + }; + await toTransform.crop(region); + return toTransform; + } +} \ No newline at end of file diff --git a/library/src/main/ets/transform/CropTransformation.ets b/library/src/main/ets/transform/CropTransformation.ets new file mode 100644 index 0000000..1d63651 --- /dev/null +++ b/library/src/main/ets/transform/CropTransformation.ets @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 { PixelMapTransformation } from './PixelMapTransformation'; +import { Size } from '@kit.ArkUI'; +import { image } from '@kit.ImageKit'; + +/** + * 图片变换:自定义裁剪效果 + */ +@Sendable +export class CropTransformation extends PixelMapTransformation { + private mWidth: number = 0; + private mHeight: number = 0; + private mCropType: number = 0; + + constructor(width: number, height: number, cropType: number) { + super(); + this.mWidth = width; + this.mHeight = height; + this.mCropType = cropType; + } + + getName(): string { + return this.constructor.name + ";mWidth:" + this.mWidth + ";mHeight:" + this.mHeight + ";mCropType:" + this.mCropType; + } + + async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { + let imageInfo: image.ImageInfo = await toTransform.getImageInfo(); + let size: Size = { + width: imageInfo.size.width, + height: imageInfo.size.height + }; + if (!size) { + console.error("CropTransformation The image size does not exist."); + return toTransform; + } + let pixelMapWidth: number = size.width; + let pixelMapHeight: number = size.height; + this.mWidth = this.mWidth == 0 ? pixelMapWidth : this.mWidth; + this.mHeight = this.mHeight == 0 ? pixelMapHeight : this.mHeight; + let scaleX: number = this.mWidth / pixelMapWidth; + let scaleY: number = this.mHeight / pixelMapHeight; + let scale: number = Math.max(scaleX, scaleY); + let scaledWidth: number = scale * pixelMapWidth; + let scaledHeight: number = scale * pixelMapHeight; + let left: number = (this.mWidth - scaledWidth) / 2; + let top: number = Math.abs(this.getTop(pixelMapHeight)); + let region: image.Region = { + size: { + width: scaledWidth > pixelMapWidth ? pixelMapWidth : scaledWidth, + height: scaledHeight > pixelMapHeight ? pixelMapHeight : scaledHeight + }, + x: left < 0 ? 0 : left, + y: top < 0 ? 0 : top + }; + toTransform.cropSync(region); + return toTransform; + } + + private getTop(scaledHeight: number): number { + switch (this.mCropType) { + case 0: + return 0; + case 1: + return (this.mHeight - scaledHeight) / 2; + case 2: + return this.mHeight - scaledHeight; + default: + return 0; + } + } +} \ No newline at end of file diff --git a/library/src/main/ets/transform/GrayScaleTransformation.ets b/library/src/main/ets/transform/GrayScaleTransformation.ets new file mode 100644 index 0000000..eb13e48 --- /dev/null +++ b/library/src/main/ets/transform/GrayScaleTransformation.ets @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 { PixelMapTransformation } from './PixelMapTransformation'; +import { effectKit } from '@kit.ArkGraphics2D'; + +/** + * 图片变换:灰化效果 + */ +@Sendable +export class GrayScaleTransformation extends PixelMapTransformation { + constructor() { + super(); + } + + async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { + let headFilter = effectKit.createEffect(toTransform); + if (headFilter != null) { + return await headFilter.grayscale().getEffectPixelMap(); + } + return toTransform; + } +} \ No newline at end of file diff --git a/library/src/main/ets/transform/InvertTransformation.ets b/library/src/main/ets/transform/InvertTransformation.ets new file mode 100644 index 0000000..40e5e32 --- /dev/null +++ b/library/src/main/ets/transform/InvertTransformation.ets @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 { PixelMapTransformation } from './PixelMapTransformation'; +import { effectKit } from '@kit.ArkGraphics2D'; + +/** + * 图片变换:反转效果 + */ +@Sendable +export class InvertTransformation extends PixelMapTransformation { + constructor() { + super(); + } + + async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { + let headFilter = effectKit.createEffect(toTransform); + if (headFilter != null) { + return await headFilter.invert().getEffectPixelMap(); + } + return toTransform; + } +} \ No newline at end of file diff --git a/library/src/main/ets/transform/KuwaharaTransformation.ets b/library/src/main/ets/transform/KuwaharaTransformation.ets new file mode 100644 index 0000000..657af09 --- /dev/null +++ b/library/src/main/ets/transform/KuwaharaTransformation.ets @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 { GPUImageKuwaharaFilter } from '@ohos/gpu_transform'; +import { PixelMapTransformation } from './PixelMapTransformation'; +import { image } from '@kit.ImageKit'; + +/** + * 图片变换:桑原滤波效果 + */ +@Sendable +export class KuwaharaTransformation extends PixelMapTransformation { + private radius: number; + + constructor(radius: number) { + super(); + this.radius = radius; + } + + getName(): string { + return this.constructor.name + ';radius:' + this.radius; + } + + async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { + let imageInfo: image.ImageInfo = await toTransform.getImageInfo(); + if (!imageInfo.size) { + console.error("KuwaharaTransformation The image size does not exist."); + return toTransform; + } + return await this.kuwaharaGpu(toTransform, imageInfo.size.width, imageInfo.size.height); + } + + private async kuwaharaGpu(bitmap: PixelMap, targetWidth: number, targetHeight: number): Promise { + let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber()); + await bitmap.readPixelsToBuffer(bufferData); + let filter = new GPUImageKuwaharaFilter(); + filter.setImageData(bufferData, targetWidth, targetHeight); + filter.setRadius(this.radius); + let buf = await filter.getPixelMapBuf(0, 0, targetWidth, targetHeight); + bitmap.writeBufferToPixels(buf); + return bitmap; + } +} \ No newline at end of file diff --git a/library/src/main/ets/transform/MaskTransformation.ets b/library/src/main/ets/transform/MaskTransformation.ets new file mode 100644 index 0000000..a715cf1 --- /dev/null +++ b/library/src/main/ets/transform/MaskTransformation.ets @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 { PixelMapTransformation } from './PixelMapTransformation'; +import { Size } from '@kit.ArkUI'; +import { image } from '@kit.ImageKit'; +import { resourceManager } from '@kit.LocalizationKit'; +import { CalculatePixelUtils } from '../utils/CalculatePixelUtils'; +import { ColorUtils } from '../utils/ColorUtils'; +import { PixelEntry } from './entry/PixelEntry'; + +/** + * 图片变换:遮罩效果 + */ +@Sendable +export class MaskTransformation extends PixelMapTransformation { + private mResourceId: number; + private mResourceModuleName: string; + + constructor(resource: Resource) { + super(); + this.mResourceId = resource.id; + this.mResourceModuleName = resource.moduleName; + } + + getName(): string { + return this.constructor.name + ';resourceId:' + this.mResourceId + ';resourceModuleName:' + this.mResourceModuleName; + } + + async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { + let imageInfo = await toTransform.getImageInfo(); + let size: Size = { + width: imageInfo.size.width, + height: imageInfo.size.height + }; + if (!size) { + console.error("MaskTransformation The image size does not exist."); + return toTransform; + } + let pixelMapWidth: number = size.width; + let pixelMapHeight: number = size.height; + let targetWidth: number = width; + let targetHeight: number = height; + if (pixelMapWidth > targetWidth && pixelMapHeight > targetHeight) { + let scale = Math.max(targetWidth / pixelMapWidth, targetHeight / pixelMapHeight); + await toTransform.scale(scale, scale); + return await this.openInternal(context, toTransform, scale * pixelMapWidth, scale * pixelMapHeight); + } + return await this.openInternal(context, toTransform, size.width, size.height); + } + + private async openInternal(context: Context, bitmap: PixelMap, width: number, height: number): Promise { + if (context == undefined) { + console.error("MaskTransformation openInternal the context is undefined."); + return bitmap; + } + let moduleContext = context.createModuleContext(this.mResourceModuleName); + if (moduleContext == undefined) { + console.error("MaskTransformation openInternal the moduleContext is undefined."); + return bitmap; + } + let resourceManager = moduleContext.resourceManager as resourceManager.ResourceManager; + if (resourceManager == undefined) { + console.error("MaskTransformation openInternal the resourceManager is undefined."); + return bitmap; + } + let array: Uint8Array = await resourceManager.getMediaContent(this.mResourceId); + let buffer = array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset); + let imageSource: image.ImageSource = image.createImageSource(buffer); + let options: image.DecodingOptions = { + editable: true, + desiredSize: { + width: width, + height: height + } + }; + let maskBitmap: PixelMap = await imageSource.createPixelMap(options); + return await this.mask(bitmap, maskBitmap); + } + + async mask(bitmap: PixelMap, maskBitmap: PixelMap): Promise { + let imageInfo = await bitmap.getImageInfo(); + let size: Size = { + width: imageInfo.size.width, + height: imageInfo.size.height + }; + if (!size) { + console.error("MaskTransformation mask the image size does not exist."); + return bitmap; + } + let width = size.width; + let height = size.height; + let rgbData = CalculatePixelUtils.createInt2DArray(height, width); + let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber()); + await bitmap.readPixelsToBuffer(bufferData); + let dataArray = new Uint8Array(bufferData); + let ph = 0; + let pw = 0; + for (let index = 0; index < dataArray.length; index += 4) { + const r = dataArray[index]; + const g = dataArray[index+1]; + const b = dataArray[index+2]; + const f = dataArray[index+3]; + let entry = new PixelEntry(); + entry.a = 0; + entry.b = b; + entry.g = g; + entry.r = r; + entry.f = f; + entry.pixel = ColorUtils.rgb(entry.r, entry.g, entry.b); + rgbData[ph][pw] = ColorUtils.rgb(entry.r, entry.g, entry.b); + if (pw == width - 1) { + pw = 0; + ph++; + } else { + pw++; + } + } + let imageInfoMask = await maskBitmap.getImageInfo(); + let sizeMask: Size = { + width: imageInfoMask.size.width, + height: imageInfoMask.size.height + }; + if (!sizeMask) { + console.error("MaskTransformation mask the sizeMask size does not exist."); + return bitmap; + } + let widthMask = sizeMask.width; + let heightMask = sizeMask.height; + let rgbDataMask = CalculatePixelUtils.createInt2DArray(heightMask, widthMask); + let pixEntry: Array = new Array(); + let bufferDataM = new ArrayBuffer(maskBitmap.getPixelBytesNumber()); + await maskBitmap.readPixelsToBuffer(bufferDataM); + let dataArrayM = new Uint8Array(bufferDataM); + let phM = 0; + let pwM = 0; + for (let index = 0; index < dataArrayM.length; index += 4) { + const r = dataArrayM[index]; + const g = dataArrayM[index+1]; + const b = dataArrayM[index+2]; + const f = dataArrayM[index+3]; + let entry = new PixelEntry(); + entry.a = 0; + entry.b = b; + entry.g = g; + entry.r = r; + entry.f = f; + entry.pixel = ColorUtils.rgb(entry.r, entry.g, entry.b); + pixEntry.push(entry); + if (entry.r == 0 && entry.g == 0 && entry.b == 0) { + rgbDataMask[phM][pwM] = rgbData[phM][pwM]; + } else { + rgbDataMask[phM][pwM] = ColorUtils.rgb(entry.r, entry.g, entry.b); + } + if (pwM == widthMask - 1) { + pwM = 0; + phM++; + } else { + pwM++; + } + } + let bufferNewData = new ArrayBuffer(maskBitmap.getPixelBytesNumber()); + let dataNewArray = new Uint8Array(bufferNewData); + let index = 0; + let mh = 0; + let nw = 0; + for (let i = 0; i < dataNewArray.length; i += 4) { + let pixel1 = rgbDataMask[mh][nw]; + if (nw == widthMask - 1) { + nw = 0; + mh++; + } else { + nw++; + } + let pR = ColorUtils.red(pixel1); + let pG = ColorUtils.green(pixel1); + let pB = ColorUtils.blue(pixel1); + dataNewArray[i] = pR; + dataNewArray[i+1] = pG; + dataNewArray[i+2] = pB; + dataNewArray[i+3] = pixEntry[index].f; + index++; + } + await maskBitmap.writeBufferToPixels(bufferNewData); + return maskBitmap; + } +} \ No newline at end of file diff --git a/library/src/main/ets/transform/PixelationTransformation.ets b/library/src/main/ets/transform/PixelationTransformation.ets new file mode 100644 index 0000000..2a32d78 --- /dev/null +++ b/library/src/main/ets/transform/PixelationTransformation.ets @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 { GPUImagePixelationFilter } from '@ohos/gpu_transform'; +import { PixelMapTransformation } from './PixelMapTransformation'; +import { image } from '@kit.ImageKit'; + +/** + * Applies a Pixelation effect to the image. + * The pixel with a default of 10.0. + */ +@Sendable +export class PixelationTransformation extends PixelMapTransformation { + private mPixel: number = 10.0; + + constructor(pixel?: number) { + super(); + if (pixel) { + this.mPixel = pixel; + } + } + + getName(): string { + return this.constructor.name + ';pixel:' + this.mPixel; + } + + + async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { + let imageInfo: image.ImageInfo = await toTransform.getImageInfo(); + if (!imageInfo.size) { + console.error("PixelationTransformation The image size does not exist."); + return toTransform; + } + return await this.pixelGPU(toTransform, imageInfo.size.width, imageInfo.size.height); + } + + private async pixelGPU(bitmap: PixelMap, targetWidth: number, targetHeight: number): Promise { + let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber()); + await bitmap.readPixelsToBuffer(bufferData); + let filter = new GPUImagePixelationFilter(); + filter.setImageData(bufferData, targetWidth, targetHeight); + filter.setPixel(this.mPixel); + let buf = await filter.getPixelMapBuf(0, 0, targetWidth, targetHeight); + await bitmap.writeBufferToPixels(buf); + return bitmap; + } +} \ No newline at end of file diff --git a/library/src/main/ets/transform/SepiaTransformation.ets b/library/src/main/ets/transform/SepiaTransformation.ets new file mode 100644 index 0000000..d9eaf0d --- /dev/null +++ b/library/src/main/ets/transform/SepiaTransformation.ets @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 { GPUImageSepiaToneFilter } from '@ohos/gpu_transform'; +import { PixelMapTransformation } from './PixelMapTransformation'; +import { image } from '@kit.ImageKit'; + +/** + * 图片变换:乌墨色滤波效果 + */ +@Sendable +export class SepiaTransformation extends PixelMapTransformation { + constructor() { + super(); + } + + async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { + let imageInfo: image.ImageInfo = await toTransform.getImageInfo(); + if (!imageInfo.size) { + console.error("SepiaTransformation The image size does not exist."); + return toTransform; + } + return await this.sepiaGPU(toTransform, imageInfo.size.width, imageInfo.size.height); + } + + private async sepiaGPU(bitmap: PixelMap, targetWidth: number, targetHeight: number): Promise { + let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber()); + await bitmap.readPixelsToBuffer(bufferData); + let filter = new GPUImageSepiaToneFilter(); + filter.setImageData(bufferData, targetWidth, targetHeight); + let buf = await filter.getPixelMapBuf(0, 0, targetWidth, targetHeight); + bitmap.writeBufferToPixels(buf); + return bitmap; + } +} \ No newline at end of file diff --git a/library/src/main/ets/transform/SketchTransformation.ets b/library/src/main/ets/transform/SketchTransformation.ets new file mode 100644 index 0000000..421bc9a --- /dev/null +++ b/library/src/main/ets/transform/SketchTransformation.ets @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 { GPUImageSketchFilter } from '@ohos/gpu_transform'; +import { PixelMapTransformation } from './PixelMapTransformation'; +import { image } from '@kit.ImageKit'; + +/** + * 图片变换:素描效果 + */ +@Sendable +export class SketchTransformation extends PixelMapTransformation { + constructor() { + super(); + } + + async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { + let imageInfo: image.ImageInfo = await toTransform.getImageInfo(); + if (!imageInfo.size) { + console.error("SketchTransformation The image size does not exist."); + return toTransform; + } + return await this.sketchGPU(toTransform, imageInfo.size.width, imageInfo.size.height); + } + + private async sketchGPU(bitmap: PixelMap, targetWidth: number, targetHeight: number): Promise { + let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber()); + await bitmap.readPixelsToBuffer(bufferData); + let filter = new GPUImageSketchFilter(); + filter.setImageData(bufferData, targetWidth, targetHeight); + let buf = await filter.getPixelMapBuf(0, 0, targetWidth, targetHeight); + bitmap.writeBufferToPixels(buf); + return bitmap; + } +} \ No newline at end of file diff --git a/library/src/main/ets/transform/SwirlTransformation.ets b/library/src/main/ets/transform/SwirlTransformation.ets new file mode 100644 index 0000000..1cebc74 --- /dev/null +++ b/library/src/main/ets/transform/SwirlTransformation.ets @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 { GPUImageSwirlFilter } from '@ohos/gpu_transform'; +import { PixelMapTransformation } from './PixelMapTransformation'; +import { image } from '@kit.ImageKit'; + +/** + * 图片变换:扭曲效果 + */ +@Sendable +export class SwirlTransformation extends PixelMapTransformation { + // degree:值越大范围越大 + private mDegree: number = 0; + // 取值范围 0.0 ~ 1.0 + private mAngle: number = 0.9; + // 在图中的位置 取值范围 0.0 ~ 1.0 + private mXCenter: number = 0.5; + // 在图中的位置 取值范围 0.0 ~ 1.0 + private mYCenter: number = 0.5; + + constructor(degree: number, angle?: number, centerPoint?: Array) { + super(); + this.mDegree = degree; + if (angle) { + this.mAngle = angle; + } + if (centerPoint && centerPoint.length === 2) { + this.mXCenter = centerPoint[0]; + this.mYCenter = centerPoint[1]; + } + } + + getName(): string { + return this.constructor.name + ';degree:' + this.mDegree + ';angle:' + this.mAngle + ';XCenter:' + this.mXCenter + + ';YCenter:' + this.mYCenter; + } + + async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { + let imageInfo: image.ImageInfo = await toTransform.getImageInfo(); + if (!imageInfo.size) { + console.error("SwirlTransformation The image size does not exist."); + return toTransform; + } + return await this.swirlGPU(toTransform, imageInfo.size.width, imageInfo.size.height); + } + + private async swirlGPU(bitmap: image.PixelMap, targetWidth: number, targetHeight: number): Promise { + let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber()); + await bitmap.readPixelsToBuffer(bufferData); + let filter = new GPUImageSwirlFilter(); + filter.setImageData(bufferData, targetWidth, targetHeight); + filter.setRadius(this.mDegree); + filter.setAngle(this.mAngle); + filter.setCenter(this.mXCenter, this.mYCenter); + let buf = await filter.getPixelMapBuf(0, 0, targetWidth, targetHeight); + await bitmap.writeBufferToPixels(buf); + return bitmap; + } +} \ No newline at end of file diff --git a/library/src/main/ets/transform/ToonTransformation.ets b/library/src/main/ets/transform/ToonTransformation.ets new file mode 100644 index 0000000..4f5628d --- /dev/null +++ b/library/src/main/ets/transform/ToonTransformation.ets @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 { GPUImageToonFilter } from '@ohos/gpu_transform'; +import { PixelMapTransformation } from './PixelMapTransformation'; +import { image } from '@kit.ImageKit'; + +/** + * 图片变换:动画滤镜效果 + */ +@Sendable +export class ToonTransformation extends PixelMapTransformation { + private threshold: number = 0.2; + private quantizationLevels: number = 10.0; + + constructor(threshold?: number, quantizationLevels?: number) { + super(); + if (threshold) { + this.threshold = threshold; + } + if (quantizationLevels) { + this.quantizationLevels = quantizationLevels; + } + } + + getName(): string { + return this.constructor.name + ';threshold:' + this.threshold + ';quantizationLevels:' + this.quantizationLevels; + } + + async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { + let imageInfo: image.ImageInfo = await toTransform.getImageInfo(); + if (!imageInfo.size) { + console.error("ToonTransformation The image size does not exist."); + return toTransform; + } + return await this.toonGPU(toTransform, imageInfo.size.width, imageInfo.size.height); + } + + private async toonGPU(bitmap: image.PixelMap, targetWidth: number, targetHeight: number) { + let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber()); + await bitmap.readPixelsToBuffer(bufferData); + let filter = new GPUImageToonFilter(); + filter.setImageData(bufferData, targetWidth, targetHeight); + filter.setThreshold(this.threshold); + filter.setQuantizationLevels(this.quantizationLevels); + let buf = await filter.getPixelMapBuf(0, 0, targetWidth, targetHeight); + bitmap.writeBufferToPixels(buf); + return bitmap; + } +} \ No newline at end of file diff --git a/library/src/main/ets/transform/VignetterTransformation.ets b/library/src/main/ets/transform/VignetterTransformation.ets new file mode 100644 index 0000000..a8c7691 --- /dev/null +++ b/library/src/main/ets/transform/VignetterTransformation.ets @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 { GPUImageVignetterFilter } from '@ohos/gpu_transform'; +import { PixelMapTransformation } from './PixelMapTransformation'; +import { image } from '@kit.ImageKit'; + +/** + * 图片变换:装饰效果 + */ +@Sendable +export class VignetterTransformation extends PixelMapTransformation { + private mXCenter: number = 0.5; + private mYCenter: number = 0.5; + private mRed: number = 0.0; + private mGreen: number = 0.0; + private mBlue: number = 0.0; + private mStart: number = 0.3; + private mEnd: number = 0.75; + + constructor(centerPoint: Array, vignetteColor: Array, vignetteSpace: Array) { + super(); + if (centerPoint.length === 2) { + this.mXCenter = centerPoint[0]; + this.mYCenter = centerPoint[1]; + } + if (vignetteColor.length === 3) { + this.mRed = vignetteColor[0]; + this.mGreen = vignetteColor[1]; + this.mBlue = vignetteColor[2]; + } + if (vignetteSpace.length === 2) { + this.mStart = vignetteSpace[0]; + this.mEnd = vignetteSpace[1]; + } + } + + getName(): string { + return this.constructor.name + ';XCenter:' + this.mXCenter + ';YCenter:' + this.mYCenter + ';Red:' + + this.mRed + ';Green:' + this.mGreen + ';Blue:' + this.mBlue + ';Start:' + this.mStart + ';End:' + this.mEnd; + } + + async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { + let imageInfo: image.ImageInfo = await toTransform.getImageInfo(); + if (!imageInfo.size) { + console.error("VignetterTransformation The image size does not exist."); + return toTransform; + } + return await this.swirlGPU(toTransform, imageInfo.size.width, imageInfo.size.height); + } + + private async swirlGPU(bitmap: image.PixelMap, targetWidth: number, targetHeight: number) { + let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber()); + await bitmap.readPixelsToBuffer(bufferData); + let filter = new GPUImageVignetterFilter(); + filter.setImageData(bufferData, targetWidth, targetHeight); + filter.setVignetteCenter([this.mXCenter, this.mYCenter]); + filter.setVignetteColor([this.mRed, this.mGreen, this.mBlue]); + filter.setVignetteStart(this.mStart); + filter.setVignetteEnd(this.mEnd); + let buf = await filter.getPixelMapBuf(0, 0, targetWidth, targetHeight); + await bitmap.writeBufferToPixels(buf); + return bitmap; + } +} \ No newline at end of file diff --git a/library/src/main/ets/transform/entry/PixelEntry.ets b/library/src/main/ets/transform/entry/PixelEntry.ets new file mode 100644 index 0000000..4a0205a --- /dev/null +++ b/library/src/main/ets/transform/entry/PixelEntry.ets @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 class PixelEntry { + a: number = 0; + b: number = 0; + r: number = 0; + g: number = 0; + f: number = 0; + pixel: number = 0; + + public toString(): string { + return "PixelEntry a:" + this.a + ";b:" + this.b + ";r:" + this.r + ";g:" + this.g + ";f:" + this.f; + } +} \ No newline at end of file diff --git a/library/src/main/ets/utils/CalculatePixelUtils.ets b/library/src/main/ets/utils/CalculatePixelUtils.ets new file mode 100644 index 0000000..3bf1568 --- /dev/null +++ b/library/src/main/ets/utils/CalculatePixelUtils.ets @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 namespace CalculatePixelUtils { + + export function createInt2DArray(first_len: number, second_len: number): Array> { + let array = new Array>(); + for (let f = 0; f < first_len; f++) { + let s1 = new Array(); + for (let s = 0; s < second_len; s++) { + s1.push(0); + } + array.push(s1); + } + return array; + } +} \ No newline at end of file diff --git a/library/src/main/ets/utils/ColorUtils.ets b/library/src/main/ets/utils/ColorUtils.ets new file mode 100644 index 0000000..5c1ee2e --- /dev/null +++ b/library/src/main/ets/utils/ColorUtils.ets @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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 namespace ColorUtils { + + export function red(color: number): number { + return (color >> 16) & 0xFF; + } + + export function green(color: number): number { + return (color >> 8) & 0xFF; + } + + export function blue(color: number): number { + return color & 0xFF; + } + + export function alpha(color: number): number { + return color >>> 24; + } + + export function rgb(red: number, green: number, blue: number): number { + return 0xff000000 | (red << 16) | (green << 8) | blue; + } +} \ No newline at end of file diff --git a/oh-package.json5 b/oh-package.json5 index 14e9dcc..34e1875 100644 --- a/oh-package.json5 +++ b/oh-package.json5 @@ -7,8 +7,10 @@ "author": "", "license": "", "dependencies": { + "@ohos/gpu_transform": "^1.0.2" }, "devDependencies": { "@ohos/hypium": "1.0.16" - } + }, + "dynamicDependencies": {} } \ No newline at end of file diff --git a/sharedlibrary/Index.ets b/sharedlibrary/Index.ets index b25707a..468219f 100644 --- a/sharedlibrary/Index.ets +++ b/sharedlibrary/Index.ets @@ -30,4 +30,32 @@ export { BlurTransformation } from '@ohos/imageknife' export { SparkMD5 } from "@ohos/imageknife" -export { ImageKnifeRequestSource } from "@ohos/imageknife" \ No newline at end of file +export { ImageKnifeRequestSource } from "@ohos/imageknife" + +export { CropCircleTransformation } from '@ohos/imageknife' + +export { CropCircleWithBorderTransformation } from '@ohos/imageknife' + +export { CropSquareTransformation } from '@ohos/imageknife' + +export { CropTransformation } from '@ohos/imageknife' + +export { GrayScaleTransformation } from '@ohos/imageknife' + +export { InvertTransformation } from '@ohos/imageknife' + +export { KuwaharaTransformation } from '@ohos/imageknife' + +export { MaskTransformation } from '@ohos/imageknife' + +export { PixelationTransformation } from '@ohos/imageknife' + +export { SepiaTransformation } from '@ohos/imageknife' + +export { SketchTransformation } from '@ohos/imageknife' + +export { SwirlTransformation } from '@ohos/imageknife' + +export { ToonTransformation } from '@ohos/imageknife' + +export { VignetterTransformation } from '@ohos/imageknife' \ No newline at end of file diff --git a/sharedlibrary/src/main/ets/pages/IndexPage.ets b/sharedlibrary/src/main/ets/pages/IndexPage.ets new file mode 100644 index 0000000..86424e3 --- /dev/null +++ b/sharedlibrary/src/main/ets/pages/IndexPage.ets @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * 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. + */ + +@Entry +@Component +export struct IndexPage { + build() { + } +} \ No newline at end of file diff --git a/sharedlibrary/src/main/resources/base/profile/main_pages.json b/sharedlibrary/src/main/resources/base/profile/main_pages.json index 1898d94..3187f74 100644 --- a/sharedlibrary/src/main/resources/base/profile/main_pages.json +++ b/sharedlibrary/src/main/resources/base/profile/main_pages.json @@ -1,5 +1,5 @@ { "src": [ - "pages/Index" + "pages/IndexPage" ] -} +} \ No newline at end of file