From d6d128f09c1d6ddc6392a437712683ea47f75328 Mon Sep 17 00:00:00 2001 From: CodingGorit Date: Mon, 27 Oct 2025 16:01:04 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E8=A7=A3=E5=86=B3=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E7=BC=96=E8=BE=91=EF=BC=8C=E4=BA=AE=E5=BA=A6/=E9=80=8F?= =?UTF-8?q?=E6=98=8E=E5=BA=A6/=E9=A5=B1=E5=92=8C=E5=BA=A6=20=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E5=88=B00=EF=BC=8C=E5=86=8D=E8=BF=98=E5=8E=9F?= =?UTF-8?q?=E5=88=B0100=E5=90=8E=EF=BC=8C=E5=9B=BE=E7=89=87=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=A4=B1=E7=9C=9F=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ets/common/constant/CommonConstants.ets | 5 + .../entry/src/main/ets/utils/AdjustUtil.ts | 208 +++++------------- .../src/main/ets/view/AdjustContentView.ets | 119 +++++++--- 3 files changed, 143 insertions(+), 189 deletions(-) diff --git a/ImageEditTaskPool/entry/src/main/ets/common/constant/CommonConstants.ets b/ImageEditTaskPool/entry/src/main/ets/common/constant/CommonConstants.ets index 70db67e7..0f551cda 100644 --- a/ImageEditTaskPool/entry/src/main/ets/common/constant/CommonConstants.ets +++ b/ImageEditTaskPool/entry/src/main/ets/common/constant/CommonConstants.ets @@ -19,4 +19,9 @@ export class CommonConstants { */ static readonly ADJUST_SLIDER_VALUE: number[] = [100, 100, 100]; static readonly PIXEL_STEP: number; + + /** + * Slider step. + */ + static readonly SLIDER_MAX: number = 100; } \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/ets/utils/AdjustUtil.ts b/ImageEditTaskPool/entry/src/main/ets/utils/AdjustUtil.ts index b952c359..94df1952 100644 --- a/ImageEditTaskPool/entry/src/main/ets/utils/AdjustUtil.ts +++ b/ImageEditTaskPool/entry/src/main/ets/utils/AdjustUtil.ts @@ -13,29 +13,7 @@ * limitations under the License. */ -import { RGBIndex, HSVIndex, AngelRange } from '../viewModel/OptionViewModel'; - -/** - * Saturation adjust. - * - * @param pixelMap. - * @param value saturation's value. - * @return arrayBuffer. - */ -export function adjustSaturation(bufferArray: ArrayBuffer, last: number, cur: number): ArrayBuffer | undefined { - return execColorInfo(bufferArray, last, cur, HSVIndex.SATURATION); -} - -/** - * Image brightness adjust. - * - * @param pixelMap. - * @param value image's brigtness. - * @return arrayBuffer. - */ -export function adjustImageValue(bufferArray: ArrayBuffer, last: number, cur: number): ArrayBuffer | undefined { - return execColorInfo(bufferArray, last, cur, HSVIndex.VALUE); -} +import { AdjustId } from '../viewModel/OptionViewModel'; /** * Exec color transform. @@ -46,141 +24,63 @@ export function adjustImageValue(bufferArray: ArrayBuffer, last: number, cur: nu * @param hsvIndex. * @return arrayBuffer. */ -export function execColorInfo(bufferArray: ArrayBuffer, last: number, cur: number, - hsvIndex: number): ArrayBuffer | undefined { - if (!bufferArray) { - return; +export function execColorInfo(bufferArray: ArrayBuffer, last: number, cur: number, hsvIndex: number) { + if (!bufferArray || bufferArray.byteLength === 0) { + return null; } - const newBufferArr = bufferArray; - let colorInfo = new Uint8Array(newBufferArr); - for (let i = 0; i < colorInfo?.length; i += 4) { - const hsv = rgb2hsv(colorInfo[i + RGBIndex.RED], colorInfo[i + RGBIndex.GREEN], colorInfo[i + RGBIndex.BLUE]); - let rate = cur / last; - hsv[hsvIndex] *= rate; - const rgb = hsv2rgb(hsv[HSVIndex.HUE], hsv[HSVIndex.SATURATION], hsv[HSVIndex.VALUE]); - colorInfo[i + RGBIndex.RED] = rgb[RGBIndex.RED]; - colorInfo[i + RGBIndex.GREEN] = rgb[RGBIndex.GREEN]; - colorInfo[i + RGBIndex.BLUE] = rgb[RGBIndex.BLUE]; + + if (last <= 0 || cur < 0) { + return null; } - return newBufferArr; -} -/** - * Color transform. - * - * @param rgbValue 0 - 255. - * @return 0 - 1. - */ -function colorTransform(rgbValue: number): number { - return Number((rgbValue / 255).toFixed(2)); -} + try { + const pixelData = new Uint8ClampedArray(bufferArray); + const adjustedData = new Uint8ClampedArray(pixelData.length); + const bytesPerPixel = 4; + const factor = cur / 100; // Regulatory factor + for (let i = 0; i < pixelData.length; i += bytesPerPixel) { + // Reserve the alpha channel + adjustedData[i + 3] = pixelData[i + 3]; -/** - * RGB transform to HSV. - * - * @param red 0- 255. - * @param green 0- 255. - * @param blue 0- 255. - * @return h (0 - 360) s(0 - 100) v (0 - 100). - */ -function rgb2hsv(red: number, green: number, blue: number): number[] { - let hsvH: number = 0, hsvS: number = 0, hsvV: number = 0; - const rgbR: number = colorTransform(red); - const rgbG: number = colorTransform(green); - const rgbB: number = colorTransform(blue); - const maxValue = Math.max(rgbR, Math.max(rgbG, rgbB)); - const minValue = Math.min(rgbR, Math.min(rgbG, rgbB)); - hsvV = maxValue * 100; - if (maxValue === 0) { - hsvS = 0; - } else { - hsvS = Number((1 - minValue / maxValue).toFixed(2)) * 100; - } - if (maxValue === minValue) { - hsvH = 0; - } - if (maxValue === rgbR && rgbG >= rgbB) { - hsvH = Math.floor(60 * ((rgbG - rgbB) / (maxValue - minValue))); - } - if (maxValue === rgbR && rgbG < rgbB) { - hsvH = Math.floor(60 * ((rgbG - rgbB) / (maxValue - minValue)) + 360); - } - if (maxValue === rgbG) { - hsvH = Math.floor(60 * ((rgbB - rgbR) / (maxValue - minValue)) + 120); - } - if (maxValue === rgbB) { - hsvH = Math.floor(60 * ((rgbR - rgbG) / (maxValue - minValue)) + 240); - } - return [hsvH, hsvS, hsvV]; -} + // Skip the pixels that are completely transparent + if (pixelData[i + 3] < 1) { + continue; + } -/** - * HSV to RGB conversion formula: - * When 0 <= H <= 360, 0 <= S <= 1 and 0 <= V <= 1: - * C = V * S - * X = C * (1 - Math.abs((H / 60) mod 2 - 1)) - * m = V - C - * | (C, X ,0), 0 <= H < 60 - * | (X, C, 0), 60 <= H < 120 - * | (0, C, X), 120 <= H < 180 - * (R', G', B') = | (0, X, C), 180 <= H < 240 - * | (X, 0, C), 240 <= H < 300 - * | (C, 0, X), 300 <= H < 360 - * - * (R, G, B) = ((R' + m) * 255, (G' + m) * 255, (B' + m) * 255) - * - * @param h hue 0 ~ 360. - * @param s saturation 0 ~ 100. - * @param v value 0 ~ 100. - * @return rgb value. - */ -function hsv2rgb(hue: number, saturation: number, value: number) { - let rgbR: number = 0, rgbG: number = 0, rgbB: number = 0; - if (saturation === 0) { - rgbR = rgbG = rgbB = Math.round((value * 255) / 100); - return { rgbR, rgbG, rgbB }; - } - const cxmC = (value * saturation) / (100 * 100); - const cxmX = cxmC * (1 - Math.abs((hue / 60) % 2 - 1)); - const cxmM = (value - cxmC * 100) / 100; - const hsvHRange = Math.floor(hue / 60); - switch (hsvHRange) { - case AngelRange.ANGEL_0_60: - rgbR = (cxmC + cxmM) * 255; - rgbG = (cxmX + cxmM) * 255; - rgbB = (0 + cxmM) * 255; - break; - case AngelRange.ANGEL_60_120: - rgbR = (cxmX + cxmM) * 255; - rgbG = (cxmC + cxmM) * 255; - rgbB = (0 + cxmM) * 255; - break; - case AngelRange.ANGEL_120_180: - rgbR = (0 + cxmM) * 255; - rgbG = (cxmC + cxmM) * 255; - rgbB = (cxmX + cxmM) * 255; - break; - case AngelRange.ANGEL_180_240: - rgbR = (0 + cxmM) * 255; - rgbG = (cxmX + cxmM) * 255; - rgbB = (cxmC + cxmM) * 255; - break; - case AngelRange.ANGEL_240_300: - rgbR = (cxmX + cxmM) * 255; - rgbG = (0 + cxmM) * 255; - rgbB = (cxmC + cxmM) * 255; - break; - case AngelRange.ANGEL_300_360: - rgbR = (cxmC + cxmM) * 255; - rgbG = (0 + cxmM) * 255; - rgbB = (cxmX + cxmM) * 255; - break; - default: - break; + if (hsvIndex === AdjustId.BRIGHTNESS) { + // Calculate adjusted RGB values (keep relative proportions) + let r = pixelData[i] * factor; + let g = pixelData[i + 1] * factor; + let b = pixelData[i + 2] * factor; + + // Ensure the value is in the valid range (0-255) + adjustedData[i] = Math.max(0, Math.min(255, Math.round(r))); + adjustedData[i + 1] = Math.max(0, Math.min(255, Math.round(g))); + adjustedData[i + 2] = Math.max(0, Math.min(255, Math.round(b))); + } + if (hsvIndex === AdjustId.SATURATION) { + // Extract the RGB value and normalize it + const r = pixelData[i] / 255; + const g = pixelData[i + 1] / 255; + const b = pixelData[i + 2] / 255; + + // Calculate the grayscale value + const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b; + + // Adjust saturation: move closer or further away from the grayscale value + const adjustedR = luminance + (r - luminance) * factor; + const adjustedG = luminance + (g - luminance) * factor; + const adjustedB = luminance + (b - luminance) * factor; + + // Convert back to the 0-255 range and write to the buffer + adjustedData[i] = Math.max(0, Math.min(255, Math.round(adjustedR * 255))); + adjustedData[i + 1] = Math.max(0, Math.min(255, Math.round(adjustedG * 255))); + adjustedData[i + 2] = Math.max(0, Math.min(255, Math.round(adjustedB * 255))); + } + } + return adjustedData.buffer; + } catch (error) { + console.log(`Failed to set adjustedData: ${JSON.stringify(error.message)}`); + return null; } - return [ - Math.round(rgbR), - Math.round(rgbG), - Math.round(rgbB) - ]; } \ No newline at end of file diff --git a/ImageEditTaskPool/entry/src/main/ets/view/AdjustContentView.ets b/ImageEditTaskPool/entry/src/main/ets/view/AdjustContentView.ets index f4c312a9..ee2de148 100644 --- a/ImageEditTaskPool/entry/src/main/ets/view/AdjustContentView.ets +++ b/ImageEditTaskPool/entry/src/main/ets/view/AdjustContentView.ets @@ -16,12 +16,12 @@ import { worker } from '@kit.ArkTS'; import { taskpool } from '@kit.ArkTS'; import { image } from '@kit.ImageKit'; import { adjustIconList, IconStatus } from '../viewModel/IconListViewModel'; -import { adjustImageValue, adjustSaturation } from '../utils/AdjustUtil'; +import { execColorInfo } from '../utils/AdjustUtil'; import { AdjustId } from '../viewModel/OptionViewModel'; -import { adjustOpacity } from '../utils/OpacityUtil'; import { MessageItem } from '../viewModel/MessageItem'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { BusinessError } from '@kit.BasicServicesKit'; +import { CommonConstants } from '../common/constant/CommonConstants'; @Component export default struct AdjustContentView { @@ -122,6 +122,7 @@ struct SliderCustom { @Prop max: number; @Consume('pixelMap') pixelMap?: image.PixelMap; @Consume('isPixelMapChange') isPixelMapChange: boolean; + private startEditPixelMap?: PixelMap; private postState: boolean = true; saturationLastSlider: number = 100; brightnessLastSlider: number = 100; @@ -132,6 +133,10 @@ struct SliderCustom { customStyle: true }); + aboutToAppear(): void { + this.startEditPixelMap = this.pixelMap; + } + build() { Column() { Text(`${this.currentAdjustData[this.currentIndex]}`) @@ -164,30 +169,62 @@ struct SliderCustom { } } - sliderChange(value: number, mode: SliderChangeMode): void { + async sliderChange(value: number, mode: SliderChangeMode) { if ((mode === SliderChangeMode.End) && (value !== this.currentAdjustData[this.currentIndex])) { + if (this.postState) { + this.deviceListDialogController.open(); + } + this.postState = false; + this.currentAdjustData[this.currentIndex] = Math.round(value); - switch (this.currentIndex) { - case AdjustId.BRIGHTNESS: - this.postProcess(AdjustId.BRIGHTNESS, value); - break; - case AdjustId.TRANSPARENCY: - if (this.pixelMap) { - adjustOpacity(this.pixelMap, Math.round(value)) - .then((pixelMap?: image.PixelMap) => { - if (pixelMap) { - this.pixelMap = pixelMap; - this.isPixelMapChange = !this.isPixelMapChange; - } - }); + const px = this.getStartEditPixelMap(); + let buffer = new ArrayBuffer(px.getPixelBytesNumber()); + px.readPixelsToBufferSync(buffer); + + const needBrightness = this.currentAdjustData[AdjustId.BRIGHTNESS] !== CommonConstants.SLIDER_MAX; + const needSaturation = this.currentAdjustData[AdjustId.SATURATION] !== CommonConstants.SLIDER_MAX; + + if (needBrightness || needSaturation) { + try { + if (needBrightness) { + buffer = await this.execImageProcessing(buffer, AdjustId.BRIGHTNESS, this.currentAdjustData[AdjustId.BRIGHTNESS]); + } + if (needSaturation) { + buffer = await this.execImageProcessing(buffer, AdjustId.SATURATION, this.currentAdjustData[AdjustId.SATURATION]); } - break; - case AdjustId.SATURATION: - this.postProcess(AdjustId.SATURATION, value); - break; - default: - break; + px.writeBufferToPixelsSync(buffer); + } catch (err) { + let error = err as BusinessError; + hilog.error(0x0000, 'testTag', `${error.code}, ${error.message}`); + } + } + + if (this.currentAdjustData[AdjustId.TRANSPARENCY] !== CommonConstants.SLIDER_MAX) { + const opacity = this.currentAdjustData[AdjustId.TRANSPARENCY] / CommonConstants.SLIDER_MAX; + try { + px.opacitySync(opacity); + } catch (err) { + let error = err as BusinessError; + hilog.error(0x0000, 'testTag', `${error.code}, ${error.message}`); + } } + + this.pixelMap = px; + this.isPixelMapChange = !this.isPixelMapChange; + this.deviceListDialogController.close(); + this.postState = true; + } + } + + private async execImageProcessing(buffer: ArrayBuffer, type: AdjustId, value: number): Promise { + const processParam: ImageProcessing = { value, buffer, type }; + const task = new taskpool.Task(imageProcessing, processParam); + try { + return await taskpool.execute(task, taskpool.Priority.HIGH) as ArrayBuffer; + } catch (err) { + let error = err as BusinessError; + hilog.error(0x0000, 'AdjustContentView', `${error.code}, ${error.message}`); + return buffer; } } @@ -241,6 +278,28 @@ struct SliderCustom { // [EndExclude postProcess_start] // [End postProcess_start] + + getStartEditPixelMap() { + return this.clonePixelMap(this.startEditPixelMap as image.PixelMap); + } + + clonePixelMap(pixelMap: PixelMap, desiredPixelFormat?: image.PixelMapFormat): PixelMap { + try { + const imageInfo = pixelMap.getImageInfoSync(); + const buffer = new ArrayBuffer(pixelMap.getPixelBytesNumber()); + pixelMap.readPixelsToBufferSync(buffer); + const options: image.InitializationOptions = { + srcPixelFormat: imageInfo.pixelFormat, + pixelFormat: desiredPixelFormat ?? imageInfo.pixelFormat, + size: imageInfo.size + }; + return image.createPixelMapSync(buffer, options); + } catch (err) { + let error = err as BusinessError; + hilog.error(0x0000, 'testTag', `${error.code}, ${error.message}`); + return pixelMap; + } + } } @CustomDialog @@ -285,23 +344,13 @@ function splitTask(buffers: ArrayBuffer[], type: AdjustId, sliderValue: number, @Concurrent async function imageProcessing(args: ImageProcessing) { - const type: AdjustId = args.type; - const bufferArray: ArrayBuffer = args.bufferArray; - const value: number = args.value; - const sliderValue: number = args.sliderValue; - if (type === AdjustId.BRIGHTNESS) { - return adjustImageValue(bufferArray, sliderValue, value); - } else if (type === AdjustId.SATURATION) { - return adjustSaturation(bufferArray, sliderValue, value); - } else { - return undefined; - } + const buf = execColorInfo(args.buffer, CommonConstants.SLIDER_MAX, args.value, args.type) as ArrayBuffer; + return buf; } interface ImageProcessing { type: AdjustId; - bufferArray: ArrayBuffer; - sliderValue: number; + buffer: ArrayBuffer; value: number; } -- Gitee