diff --git a/CHANGELOG.md b/CHANGELOG.md index d74fa1b..cc7b23a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - 图片加载事件增加请求开始的回调,以及修复有缓存时,没有回调的bug - 修复对已销毁组件不再下发请求的逻辑 - 提供清理缓存能力 +- 支持多种组合变换 ## 3.0.0-rc.4 - 支持hsp多包图片资源 diff --git a/README.md b/README.md index 64cd223..6e21eb4 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,28 @@ 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 } }, // 圆角设置 + gpuEnabled: true, + transformation: transformations.length > 0 ? new MultiTransTransformation(transformations) : undefined // 图形变换组 +} +}).width(300) + .height(300) + .rotate({ angle: 90 }) // 旋转90度 + .contrast(12) // 对比度滤波器 +``` + ## 接口说明 ### ImageKnifeOption参数列表 @@ -188,6 +210,27 @@ ImageKnifeComponent({ ImageKnifeOption: | removeMemoryCache| url: string | ImageKnifeOption | 清理指定内存缓存 | | removeFileCache | url: string | ImageKnifeOption | 清理指定磁盘缓存 | +#### 图形变换类型 + +| 类型 | 相关描述 | +| ---------------------------------- | -------------- | +| BlurTransformation | 模糊处理 | +| BrightnessTransformation | 亮度滤波器 | +| CropCircleTransformation | 圆形剪裁显示 | +| CropCircleWithBorderTransformation | 圆环展示 | +| CropSquareTransformation | 正方形剪裁 | +| CropTransformation | 自定义矩形剪裁 | +| GrayScaleTransformation | 灰度级滤波器 | +| InvertTransformation | 反转滤波器 | +| KuwaharaTransformation | 桑原滤波器 | +| MaskTransformation | 遮罩 | +| PixelationTransformation | 像素化滤波器 | +| SepiaTransformation | 乌墨色滤波器 | +| SketchTransformation | 素描滤波器 | +| SwirlTransformation | 扭曲滤波器 | +| ToonTransformation | 动画滤波器 | +| VignetterTransformation | 装饰滤波器 | + ## 约束与限制 在下述版本验证通过: diff --git a/entry/src/main/ets/pages/ImageTransformation.ets b/entry/src/main/ets/pages/ImageTransformation.ets index 4cbbcba..de79ed5 100644 --- a/entry/src/main/ets/pages/ImageTransformation.ets +++ b/entry/src/main/ets/pages/ImageTransformation.ets @@ -1,82 +1,419 @@ -import { BlurTransformation, BrightnessTransformation, ImageKnifeComponent, ImageKnifeOption, +import { + BlurTransformation, + BrightnessTransformation, + CropCircleTransformation, + CropCircleWithBorderTransformation, + CropSquareTransformation, + CropTransformation, + GrayScaleTransformation, + ImageKnifeComponent, + ImageKnifeOption, + InvertTransformation, + KuwaharaTransformation, + MaskTransformation, MultiTransTransformation, - PixelMapTransformation } from '@ohos/imageknife' + PixelationTransformation, + PixelMapTransformation, + SepiaTransformation, + SketchTransformation, + SwirlTransformation, + ToonTransformation, + VignetterTransformation +} from '@ohos/imageknife' import { collections } from '@kit.ArkTS' +import { CircleShape, PathShape } from '@ohos.arkui.shape'; @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 }, + gpuEnabled: true, 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..6c75ca3 100644 --- a/entry/src/ohosTest/ets/test/List.test.ets +++ b/entry/src/ohosTest/ets/test/List.test.ets @@ -24,4 +24,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..9d502cb --- /dev/null +++ b/entry/src/ohosTest/ets/test/transform.test.ets @@ -0,0 +1,227 @@ +/* + * 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 hilog from '@ohos.hilog'; +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 d0f552e..42f6904 100644 --- a/library/index.ets +++ b/library/index.ets @@ -22,4 +22,32 @@ export { BrightnessTransformation } from './src/main/ets/transform/BrightnessTra export { BlurTransformation } from './src/main/ets/transform/BlurTransformation' +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' + diff --git a/library/oh-package.json5 b/library/oh-package.json5 index 4de491c..c00ff82 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.0" }, "tags": [ "ImageCache", diff --git a/library/src/main/ets/ImageKnifeDispatcher.ets b/library/src/main/ets/ImageKnifeDispatcher.ets index 34d5810..ce524d2 100644 --- a/library/src/main/ets/ImageKnifeDispatcher.ets +++ b/library/src/main/ets/ImageKnifeDispatcher.ets @@ -137,6 +137,9 @@ export class ImageKnifeDispatcher { src: imageSrc, headers: currentRequest.imageKnifeOption.headerOption, allHeaders: currentRequest.headers, + componentWidth:currentRequest.componentWidth, + componentHeight:currentRequest.componentHeight, + isGpuEnabled:currentRequest.imageKnifeOption.gpuEnabled === true, customGetImage: currentRequest.imageKnifeOption.customGetImage, onlyRetrieveFromCache: currentRequest.imageKnifeOption.onlyRetrieveFromCache, transformation: currentRequest.imageKnifeOption.transformation, @@ -441,7 +444,7 @@ async function requestJob(request: RequestJobRequest): Promise Promise border?: BorderOptions + // 开启GPU变换绘制 + gpuEnabled?: boolean = false; // 缓存策略 writeCacheStrategy?: CacheStrategy // 仅使用缓存加载数据 diff --git a/library/src/main/ets/interface/DataCallBack.ets b/library/src/main/ets/interface/DataCallBack.ets new file mode 100644 index 0000000..424f89a --- /dev/null +++ b/library/src/main/ets/interface/DataCallBack.ets @@ -0,0 +1,18 @@ +/* + * 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 interface DataCallBack { + callback: (data: T) => void; +} \ No newline at end of file diff --git a/library/src/main/ets/model/ImageKnifeData.ets b/library/src/main/ets/model/ImageKnifeData.ets index f2abd25..db16a53 100644 --- a/library/src/main/ets/model/ImageKnifeData.ets +++ b/library/src/main/ets/model/ImageKnifeData.ets @@ -69,6 +69,9 @@ export interface RequestJobRequest { src: string | PixelMap | Resource, headers?: Array, allHeaders: Map, + componentWidth: number, + componentHeight: number, + isGpuEnabled: boolean, customGetImage?: (context: Context, src: string | PixelMap | Resource) => Promise, onlyRetrieveFromCache?: boolean requestSource: ImageKnifeRequestSource diff --git a/library/src/main/ets/transform/BaseTransformation.ets b/library/src/main/ets/transform/BaseTransformation.ets index ced2f06..654d7b1 100644 --- a/library/src/main/ets/transform/BaseTransformation.ets +++ b/library/src/main/ets/transform/BaseTransformation.ets @@ -18,7 +18,7 @@ */ export interface BaseTransformation { - transform(context: Context, toTransform: T, width: number, height: number): Promise; + transform(context: Context, toTransform: T, width: number, height: number, gpuEnable: boolean): Promise; getName(): string } \ No newline at end of file diff --git a/library/src/main/ets/transform/BlurTransformation.ets b/library/src/main/ets/transform/BlurTransformation.ets index 07de719..e3a044d 100644 --- a/library/src/main/ets/transform/BlurTransformation.ets +++ b/library/src/main/ets/transform/BlurTransformation.ets @@ -24,15 +24,18 @@ export class BlurTransformation extends PixelMapTransformation { constructor(radius: number) { super() - this.radius = radius + this.radius = radius; } - async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { + getName(): string { + return this.constructor.name + ';radius:' + this.radius; + } + + async transform(context: Context, toTransform: PixelMap, width: number, height: number, gpuEnable: boolean): Promise { let headFilter = effectKit.createEffect(toTransform); if (headFilter != null) { return await headFilter.blur(this.radius).getEffectPixelMap() } return toTransform } -} - +} \ No newline at end of file diff --git a/library/src/main/ets/transform/BrightnessTransformation.ets b/library/src/main/ets/transform/BrightnessTransformation.ets index 1b68426..8992ba0 100644 --- a/library/src/main/ets/transform/BrightnessTransformation.ets +++ b/library/src/main/ets/transform/BrightnessTransformation.ets @@ -24,10 +24,14 @@ export class BrightnessTransformation extends PixelMapTransformation { constructor(bright: number) { super() - this.bright = bright + this.bright = bright; } - async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { + getName(): string { + return this.constructor.name + ';bright:' + this.bright; + } + + async transform(context: Context, toTransform: PixelMap, width: number, height: number, gpuEnable: boolean): Promise { let headFilter = effectKit.createEffect(toTransform); if (headFilter != null) { return await headFilter.brightness(this.bright).getEffectPixelMap() diff --git a/library/src/main/ets/transform/CropCircleTransformation.ets b/library/src/main/ets/transform/CropCircleTransformation.ets new file mode 100644 index 0000000..98470b3 --- /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 { image } from '@kit.ImageKit'; + +/** + * 图片变换:圆形裁剪效果 + */ +@Sendable +export class CropCircleTransformation extends PixelMapTransformation { + private static TAG: string = "CropCircleTransformation"; + 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, gpuEnable: boolean): Promise { + return await this.transformCircle(toTransform, width, height); + } + + private async transformCircle(data: PixelMap, outWidth: number, outHeight: number): 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..1779811 --- /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 { image } from '@kit.ImageKit'; + +export interface rgbColor { + r_color: number, + g_color: number, + b_color: number, +} + +/** + * 图片变换:圆环裁剪效果 + */ +@Sendable +export class CropCircleWithBorderTransformation extends PixelMapTransformation { + private static TAG: string = "CropCircleWithBorderTransformation"; + 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, gpuEnable: boolean): 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..79676af --- /dev/null +++ b/library/src/main/ets/transform/CropSquareTransformation.ets @@ -0,0 +1,50 @@ +/* + * 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 { image } from '@kit.ImageKit'; + +/** + * 图片变换:正方形裁剪效果 + */ +@Sendable +export class CropSquareTransformation extends PixelMapTransformation { + private static TAG: string = "CropSquareTransformation"; + + constructor() { + super(); + } + + async transform(context: Context, toTransform: PixelMap, width: number, height: number, gpuEnable: boolean): 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..ee398d7 --- /dev/null +++ b/library/src/main/ets/transform/CropTransformation.ets @@ -0,0 +1,86 @@ +/* + * 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 { image } from '@kit.ImageKit'; + +/** + * 图片变换:自定义裁剪效果 + */ +@Sendable +export class CropTransformation extends PixelMapTransformation { + private static TAG: string = "CropTransformation"; + 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, gpuEnable: boolean): 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..d3a7ac9 --- /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, gpuEnable: boolean): 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..d272a71 --- /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, gpuEnable: boolean): 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..f4c3c2a --- /dev/null +++ b/library/src/main/ets/transform/KuwaharaTransformation.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 { 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, gpuEnable: boolean): Promise { + if (!gpuEnable) { + console.error("the KuwaharaFilterTransform supported only in GPU mode"); + return toTransform; + } + 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..821b7ab --- /dev/null +++ b/library/src/main/ets/transform/MaskTransformation.ets @@ -0,0 +1,197 @@ +/* + * 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 { 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, gpuEnable: boolean): 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/MultiTransTransformation.ets b/library/src/main/ets/transform/MultiTransTransformation.ets index 459afbc..92067e1 100644 --- a/library/src/main/ets/transform/MultiTransTransformation.ets +++ b/library/src/main/ets/transform/MultiTransTransformation.ets @@ -28,10 +28,10 @@ export class MultiTransTransformation extends PixelMapTransformation { this.transformations = transformations } - async transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { + async transform(context: Context, toTransform: PixelMap, width: number, height: number, gpuEnable: boolean): Promise { let res = toTransform for (let i = 0; i < this.transformations.length; i++) { - res = await this.transformations[i].transform(context, res, width, height) + res = await this.transformations[i].transform(context, res, width, height, gpuEnable); } return res } diff --git a/library/src/main/ets/transform/PixelMapTransformation.ets b/library/src/main/ets/transform/PixelMapTransformation.ets index ca002a6..78a749a 100644 --- a/library/src/main/ets/transform/PixelMapTransformation.ets +++ b/library/src/main/ets/transform/PixelMapTransformation.ets @@ -18,8 +18,8 @@ import { BaseTransformation } from './BaseTransformation'; * 基于PixelMap的图片变换 */ @Sendable -export abstract class PixelMapTransformation implements BaseTransformation{ - transform(context: Context, toTransform: PixelMap, width: number, height: number): Promise { +export abstract class PixelMapTransformation implements BaseTransformation { + transform(context: Context, toTransform: PixelMap, width: number, height: number, gpuEnable: boolean): Promise { throw new Error('Method not implemented.'); } diff --git a/library/src/main/ets/transform/PixelationTransformation.ets b/library/src/main/ets/transform/PixelationTransformation.ets new file mode 100644 index 0000000..af74994 --- /dev/null +++ b/library/src/main/ets/transform/PixelationTransformation.ets @@ -0,0 +1,141 @@ +/* + * 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'; +import { CalculatePixelUtils } from '../utils/CalculatePixelUtils'; +import { PixelEntry } from './entry/PixelEntry'; +import { ColorUtils } from '../utils/ColorUtils'; + +/** + * 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, gpuEnable: boolean): Promise { + let imageInfo: image.ImageInfo = await toTransform.getImageInfo(); + if (!imageInfo.size) { + console.error("PixelationTransformation The image size does not exist."); + return toTransform; + } + if (!gpuEnable) { + return await this.pixel(toTransform, imageInfo.size.width, imageInfo.size.height); + } + return await this.pixelGPU(toTransform, imageInfo.size.width, imageInfo.size.height); + } + + private async pixel(bitmap: PixelMap, targetWidth: number, targetHeight: number): Promise { + let pixEntry: Array = new Array() + let inPixels: Array> = CalculatePixelUtils.createInt2DArray(targetHeight, targetWidth); + let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber()); + await bitmap.readPixelsToBuffer(bufferData); + let dataArray = new Uint8Array(bufferData); + let ph: number = 0; + let pw: number = 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); + pixEntry.push(entry); + inPixels[ph][pw] = ColorUtils.rgb(entry.r, entry.g, entry.b); + if (pw == targetWidth - 1) { + pw = 0; + ph++; + } else { + pw++; + } + } + let realPixel_W = this.mPixel > targetWidth ? targetWidth : this.mPixel; + let realPixel_H = this.mPixel > targetHeight ? targetHeight : this.mPixel; + // 横排的正方形个数 + let x_index = Math.floor(targetWidth / realPixel_W); + // 纵排的正方形个数 + let y_index = Math.floor(targetHeight / realPixel_H); + for (let ch = 0; ch < y_index; ch++) { + for (let cw = 0; cw < x_index; cw++) { + let max_x = (cw + 1) * realPixel_W > targetWidth ? targetWidth : (cw + 1) * realPixel_W; + let max_y = (ch + 1) * realPixel_H > targetHeight ? targetHeight : (ch + 1) * realPixel_H; + let min_x = cw * realPixel_W; + let min_y = ch * realPixel_H; + // 取左上角的像素值 + let center_p = inPixels[min_y+1][min_x+1]; + // 设置该正方形里的像素统一 + for (let zh = min_y; zh < max_y; zh++) { + for (let zw = min_x; zw < max_x; zw++) { + inPixels[zh][zw] = center_p; + } + } + } + } + let bufferNewData = new ArrayBuffer(bitmap.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 pixel_1 = inPixels[mh][nw]; + if (nw == targetWidth - 1) { + nw = 0; + mh++; + } else { + nw++; + } + let p_r = ColorUtils.red(pixel_1); + let p_g = ColorUtils.green(pixel_1); + let p_b = ColorUtils.blue(pixel_1); + dataNewArray[i] = p_r; + dataNewArray[i+1] = p_g; + dataNewArray[i+2] = p_b; + dataNewArray[i+3] = pixEntry[index].f; + index++; + } + await bitmap.writeBufferToPixels(bufferNewData); + return bitmap; + } + + 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..b72e2bc --- /dev/null +++ b/library/src/main/ets/transform/SepiaTransformation.ets @@ -0,0 +1,93 @@ +/* + * 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, gpuEnable: boolean): Promise { + let imageInfo: image.ImageInfo = await toTransform.getImageInfo(); + if (!imageInfo.size) { + console.error("SepiaTransformation The image size does not exist."); + return toTransform; + } + if (!gpuEnable) { + return await this.sepia(toTransform, imageInfo.size.width, imageInfo.size.height); + } + return await this.sepiaGPU(toTransform, imageInfo.size.width, imageInfo.size.height); + } + + private async sepia(bitmap: PixelMap, targetWidth: number, targetHeight: number): Promise { + let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber()); + await bitmap.readPixelsToBuffer(bufferData); + let bufferNewData = new ArrayBuffer(bitmap.getPixelBytesNumber()); + let dataArray = new Uint8Array(bufferData); + let dataNewArray = new Uint8Array(bufferNewData); + for (let index = 0; index < dataArray.length; index += 4) { + const r: number = dataArray[index]; + const g: number = dataArray[index+1]; + const b: number = dataArray[index+2]; + const f: number = dataArray[index+3]; + dataNewArray[index+2] = this.checkVisAble(this.colorBlend(this.noise() + , (r * 0.272) + (g * 0.534) + (b * 0.131) + , b)); + dataNewArray[index+1] = this.checkVisAble(this.colorBlend(this.noise() + , (r * 0.349) + (g * 0.686) + (b * 0.168) + , g)); + dataNewArray[index] = this.checkVisAble(this.colorBlend(this.noise() + , (r * 0.393) + (g * 0.769) + (b * 0.189) + , r)); + dataNewArray[index+3] = f; + } + await bitmap.writeBufferToPixels(bufferNewData); + return bitmap; + } + + private checkVisAble(input: number) { + if (input > 255) { + input = 255; + } + if (input <= 0) { + input = 0; + } + return input; + } + + private colorBlend(scale: number, dest: number, src: number): number { + return (scale * dest + (1.0 - scale) * src); + } + + private noise(): number { + return Math.random() * 0.5 + 0.5; + } + + 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..03a9ceb --- /dev/null +++ b/library/src/main/ets/transform/SketchTransformation.ets @@ -0,0 +1,120 @@ +/* + * 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'; +import { PixelEntry } from './entry/PixelEntry'; +import { ColorUtils } from '../utils/ColorUtils'; +import { CalculatePixelUtils } from '../utils/CalculatePixelUtils'; + +/** + * 图片变换:素描效果 + */ +@Sendable +export class SketchTransformation extends PixelMapTransformation { + constructor() { + super(); + } + + async transform(context: Context, toTransform: PixelMap, width: number, height: number, gpuEnable: boolean): Promise { + let imageInfo: image.ImageInfo = await toTransform.getImageInfo(); + if (!imageInfo.size) { + console.error("SketchTransformation The image size does not exist."); + return toTransform; + } + if (!gpuEnable) { + return await this.sketch(toTransform, imageInfo.size.width, imageInfo.size.height); + } + return await this.sketchGPU(toTransform, imageInfo.size.width, imageInfo.size.height); + } + + private async sketch(bitmap: PixelMap, targetWidth: number, targetHeight: number): Promise { + let pos: number = 0 + let row: number = 0 + let col: number = 0 + let clr: number = 0 + let width: number = targetWidth; + let height: number = targetHeight; + let pixEntry: Array = new Array() + let pixSrc: Array = new Array() + let pixNvt: Array = new Array() + + let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber()); + await bitmap.readPixelsToBuffer(bufferData); + let dataArray = new Uint8Array(bufferData); + + 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); + pixEntry.push(entry); + pixSrc.push(ColorUtils.rgb(entry.r, entry.g, entry.b)); + pixNvt.push(0); + } + + for (row = 0; row < height; row++) { + for (col = 0; col < width; col++) { + pos = row * width + col; + pixSrc[pos] = (pixEntry[pos].r + pixEntry[pos].g + pixEntry[pos].b) / 3; + pixNvt[pos] = 255 - pixSrc[pos]; + } + } + // 对取反的像素进行高斯模糊, 强度可以设置,暂定为5.0 + CalculatePixelUtils.gaussGray(pixNvt, 5.0, 5.0, width, height); + // 灰度颜色和模糊后像素进行差值运算 + for (row = 0; row < height; row++) { + for (col = 0; col < width; col++) { + pos = row * width + col; + clr = pixSrc[pos] << 8; + clr /= 256 - pixNvt[pos]; + clr = Math.min(clr, 255); + pixSrc[pos] = ColorUtils.rgb(clr, clr, clr); + } + } + + let bufferNewData = new ArrayBuffer(bitmap.getPixelBytesNumber()); + let dataNewArray = new Uint8Array(bufferNewData); + let index = 0; + + for (let i = 0; i < dataNewArray.length; i += 4) { + dataNewArray[i] = ColorUtils.red(pixSrc[index]); + dataNewArray[i+1] = ColorUtils.green(pixSrc[index]); + dataNewArray[i+2] = ColorUtils.blue(pixSrc[index]); + dataNewArray[i+3] = pixEntry[index].f; + index++; + } + await bitmap.writeBufferToPixels(bufferNewData); + return bitmap; + } + + 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..ae8cc30 --- /dev/null +++ b/library/src/main/ets/transform/SwirlTransformation.ets @@ -0,0 +1,149 @@ +/* + * 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'; +import { PixelEntry } from './entry/PixelEntry'; +import { CalculatePixelUtils } from '../utils/CalculatePixelUtils'; +import { ColorUtils } from '../utils/ColorUtils'; + +/** + * 图片变换:扭曲效果 + */ +@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, gpuEnable: boolean): Promise { + let imageInfo: image.ImageInfo = await toTransform.getImageInfo(); + if (!imageInfo.size) { + console.error("SwirlTransformation The image size does not exist."); + return toTransform; + } + if (!gpuEnable) { + return await this.swirl(toTransform, imageInfo.size.width, imageInfo.size.height); + } + return await this.swirlGPU(toTransform, imageInfo.size.width, imageInfo.size.height); + } + + private async swirl(bitmap: image.PixelMap, targetWidth: number, targetHeight: number): Promise { + let bufferData = new ArrayBuffer(bitmap.getPixelBytesNumber()); + await bitmap.readPixelsToBuffer(bufferData); + let pixEntry: Array = new Array(); + let rgbData = CalculatePixelUtils.createInt2DArray(targetHeight, targetWidth); + let dataArray = new Uint8Array(bufferData); + let ph: number = 0; + let pw: number = 0; + for (let index = 0; index < dataArray.length; index += 4) { + const r: number = dataArray[index]; + const g: number = dataArray[index+1]; + const b: number = dataArray[index+2]; + const f: number = 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); + pixEntry.push(entry); + rgbData[ph][pw] = ColorUtils.rgb(entry.r, entry.g, entry.b); + if (pw == targetWidth - 1) { + pw = 0; + ph++; + } else { + pw++; + } + } + let k = this.mDegree / 3600; + let cenX = targetWidth / 2; + let cenY = targetHeight / 2; + let radius = 0; + for (let h = 0; h < targetWidth; h++) { + for (let w = 0; w < targetHeight; w++) { + let offerX = w - cenX; + let offerY = h - cenY; + let radian = Math.atan2(offerY, offerX) + radius = Math.floor(Math.sqrt(offerX * offerX + offerY * offerY)); + let newX = Math.floor(radius * Math.cos(radian + k * radius)) + cenX; + let newY = Math.floor(radius * Math.sin(radian + k * radius)) + cenY; + newX = Math.floor(Math.min(targetWidth - 1, Math.max(0, newX))); + newY = Math.floor(Math.min(targetHeight - 1, Math.max(0, newY))); + rgbData[h][w] = rgbData[newY][newX]; + } + } + let bufferNewData = new ArrayBuffer(bitmap.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 pixel_1 = rgbData[mh][nw]; + if (nw == targetWidth - 1) { + nw = 0; + mh++; + } else { + nw++; + } + let p_r = ColorUtils.red(pixel_1); + let p_g = ColorUtils.green(pixel_1); + let p_b = ColorUtils.blue(pixel_1); + dataNewArray[i] = p_r; + dataNewArray[i+1] = p_g; + dataNewArray[i+2] = p_b; + dataNewArray[i+3] = pixEntry[index].f; + index++; + } + await bitmap.writeBufferToPixels(bufferNewData); + return bitmap; + } + + 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..36c3460 --- /dev/null +++ b/library/src/main/ets/transform/ToonTransformation.ets @@ -0,0 +1,65 @@ +/* + * 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, gpuEnable: boolean): Promise { + if (!gpuEnable) { + console.error("the ToonTransformation supported only in GPU mode"); + return toTransform; + } + 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..eb8b13d --- /dev/null +++ b/library/src/main/ets/transform/VignetterTransformation.ets @@ -0,0 +1,80 @@ +/* + * 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, gpuEnable: boolean): Promise { + if (!gpuEnable) { + console.error("the VignetterTransformation supported only in GPU mode"); + return toTransform; + } + 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..06c6416 --- /dev/null +++ b/library/src/main/ets/utils/CalculatePixelUtils.ets @@ -0,0 +1,250 @@ +/* + * 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 gaussGray(psrc: Array, horz: number, vert: number, + width: number, height: number): number { + let dst: Array; + let src: Array; + let n_p: Array; + let n_m: Array; + let d_p: Array; + let d_m: Array; + let bd_p: Array; + let bd_m: Array; + let val_p: Array; + let val_m: Array; + let initial_p: Array; + let initial_m: Array; + let i: number; + let j: number; + let t: number; + let k: number; + let row: number; + let col: number; + let terms: number; + let std_dev: number; + let sp_p_idx: number; + let sp_m_idx: number; + let vp_idx: number; + let vm_idx: number; + let row_stride = width; + let max_len = Math.max(width, height); + val_p = createIntArray(max_len); + val_m = createIntArray(max_len); + n_p = createIntArray(5); + n_m = createIntArray(5); + d_p = createIntArray(5); + d_m = createIntArray(5); + bd_p = createIntArray(5); + bd_m = createIntArray(5); + src = createIntArray(max_len); + dst = createIntArray(max_len); + initial_p = createIntArray(4); + initial_m = createIntArray(4); + // 垂直方向 + if (vert > 0.0) { + vert = Math.abs(vert) + 1.0; + std_dev = Math.sqrt(-(vert * vert) / (2 * Math.log(1.0 / 255.0))); + // 初试化常量 + findConstants(n_p, n_m, d_p, d_m, bd_p, bd_m, std_dev); + for (col = 0; col < width; col++) { + for (k = 0; k < max_len; k++) { + val_m[k] = val_p[k] = 0; + } + for (t = 0; t < height; t++) { + src[t] = psrc[t * row_stride + col]; + } + sp_p_idx = 0; + sp_m_idx = height - 1; + vp_idx = 0; + vm_idx = height - 1; + initial_p[0] = src[0]; + initial_m[0] = src[height - 1]; + for (row = 0; row < height; row++) { + terms = (row < 4) ? row : 4; + for (i = 0; i <= terms; i++) { + val_p[vp_idx] += n_p[i] * src[sp_p_idx - i] - d_p[i] + * val_p[vp_idx - i]; + val_m[vm_idx] += n_m[i] * src[sp_m_idx + i] - d_m[i] + * val_m[vm_idx + i]; + } + for (j = i; j <= 4; j++) { + val_p[vp_idx] += (n_p[j] - bd_p[j]) * initial_p[0]; + val_m[vm_idx] += (n_m[j] - bd_m[j]) * initial_m[0]; + } + sp_p_idx++; + sp_m_idx--; + vp_idx++; + vm_idx--; + } + transferGaussPixels(val_p, val_m, dst, 1, height); + for (t = 0; t < height; t++) { + psrc[t * row_stride + col] = dst[t]; + } + } + } + // 水平方向 + if (horz > 0.0) { + horz = Math.abs(horz) + 1.0; + if (horz != vert) { + std_dev = Math.sqrt(-(horz * horz) + / (2 * Math.log(1.0 / 255.0))); + // 初试化常量 + findConstants(n_p, n_m, d_p, d_m, bd_p, bd_m, std_dev); + } + for (row = 0; row < height; row++) { + for (k = 0; k < max_len; k++) { + val_m[k] = val_p[k] = 0; + } + for (t = 0; t < width; t++) { + src[t] = psrc[row * row_stride + t]; + } + sp_p_idx = 0; + sp_m_idx = width - 1; + vp_idx = 0; + vm_idx = width - 1; + initial_p[0] = src[0]; + initial_m[0] = src[width - 1]; + for (col = 0; col < width; col++) { + terms = (col < 4) ? col : 4; + for (i = 0; i <= terms; i++) { + val_p[vp_idx] += n_p[i] * src[sp_p_idx - i] - d_p[i] + * val_p[vp_idx - i]; + val_m[vm_idx] += n_m[i] * src[sp_m_idx + i] - d_m[i] + * val_m[vm_idx + i]; + } + for (j = i; j <= 4; j++) { + val_p[vp_idx] += (n_p[j] - bd_p[j]) * initial_p[0]; + val_m[vm_idx] += (n_m[j] - bd_m[j]) * initial_m[0]; + } + sp_p_idx++; + sp_m_idx--; + vp_idx++; + vm_idx--; + } + transferGaussPixels(val_p, val_m, dst, 1, width); + for (t = 0; t < width; t++) { + psrc[row * row_stride + t] = dst[t]; + } + } + } + return 0; + } + + let findConstants = (n_p: Array, n_m: Array, d_p: Array, + d_m: Array, bd_p: Array + , bd_m: Array, std_dev: number) => { + let div = Math.sqrt(2 * 3.141593) * std_dev; + let x0 = -1.783 / std_dev; + let x1 = -1.723 / std_dev; + let x2 = 0.6318 / std_dev; + let x3 = 1.997 / std_dev; + let x4 = 1.6803 / div; + let x5 = 3.735 / div; + let x6 = -0.6803 / div; + let x7 = -0.2598 / div; + let i: number; + n_p[0] = x4 + x6; + n_p[1] = (Math.exp(x1) + * (x7 * Math.sin(x3) - (x6 + 2 * x4) * Math.cos(x3)) + Math + .exp(x0) * (x5 * Math.sin(x2) - (2 * x6 + x4) * Math.cos(x2))); + n_p[2] = (2 + * Math.exp(x0 + x1) + * ((x4 + x6) * Math.cos(x3) * Math.cos(x2) - x5 * Math.cos(x3) + * Math.sin(x2) - x7 * Math.cos(x2) * Math.sin(x3)) + x6 + * Math.exp(2 * x0) + x4 * Math.exp(2 * x1)); + n_p[3] = (Math.exp(x1 + 2 * x0) + * (x7 * Math.sin(x3) - x6 * Math.cos(x3)) + Math.exp(x0 + 2 + * x1) + * (x5 * Math.sin(x2) - x4 * Math.cos(x2))); + n_p[4] = 0.0; + d_p[0] = 0.0; + d_p[1] = -2 * Math.exp(x1) * Math.cos(x3) - 2 * Math.exp(x0) + * Math.cos(x2); + d_p[2] = 4 * Math.cos(x3) * Math.cos(x2) * Math.exp(x0 + x1) + + Math.exp(2 * x1) + Math.exp(2 * x0); + d_p[3] = -2 * Math.cos(x2) * Math.exp(x0 + 2 * x1) - 2 * Math.cos(x3) + * Math.exp(x1 + 2 * x0); + d_p[4] = Math.exp(2 * x0 + 2 * x1); + for (i = 0; i <= 4; i++) { + d_m[i] = d_p[i]; + } + n_m[0] = 0.0; + for (i = 1; i <= 4; i++) { + n_m[i] = n_p[i] - d_p[i] * n_p[0]; + } + let sum_n_p: number; + let sum_n_m: number; + let sum_d: number; + let a: number; + let b: number; + sum_n_p = 0.0; + sum_n_m = 0.0; + sum_d = 0.0; + for (i = 0; i <= 4; i++) { + sum_n_p += n_p[i]; + sum_n_m += n_m[i]; + sum_d += d_p[i]; + } + a = sum_n_p / (1.0 + sum_d); + b = sum_n_m / (1.0 + sum_d); + for (i = 0; i <= 4; i++) { + bd_p[i] = d_p[i] * a; + bd_m[i] = d_m[i] * b; + } + } + + let transferGaussPixels = (src1: Array, src2: Array, + dest: Array, bytes: number, width: number) => { + let i: number; + let j: number; + let k: number; + let b: number; + let sum: number; + let bend = bytes * width; + i = j = k = 0; + for (b = 0; b < bend; b++) { + sum = src1[i++] + src2[j++]; + if (sum > 255) + sum = 255; + else if (sum < 0) + sum = 0; + dest[k++] = sum; + } + } + + export function createIntArray(len: number): Array { + let array = new Array(); + for (let index = 0; index < len; index++) { + array.push(0); + } + return array; + } + + 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..2ce5188 --- /dev/null +++ b/library/src/main/ets/utils/ColorUtils.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 resourceManager from '@ohos.resourceManager'; +import common from '@ohos.app.ability.common'; +import { DataCallBack } from '../interface/DataCallBack'; + + +export namespace ColorUtils { + + /** + * 本地颜色值解析 + * ColorUtils.parseColor("color_name",callback) + */ + export function parseColor(context: Context, c: string, callback: DataCallBack) { + + let reColor = 'sys.color.' + c; + (context.resourceManager as resourceManager.ResourceManager).getString($r(reColor) + .id, (err, color) => { + if (!err) { + let cos = JSON.stringify(color); + callback.callback(cos) + } else { + callback.callback(err.message) + } + }); + + + } + + 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