如何给图片添加底色
=========

#### 问题现象

1.  已经获取到一个PixelMap图片，如何给这个PixelMap添加底色（渐变色或纯色）？
2.  应用存在大量多次创建纯色图片的场景，为避免线程阻塞，该如何解决？

#### 背景知识

*   [createPixelMapSync](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-image-f#imagecreatepixelmapsync12)：通过属性创建PixelMap，默认采用BGRA\_8888格式处理数据，同步返回结果。
*   [OffscreenCanvasRenderingContext2D](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-offscreencanvasrenderingcontext2d)：绘制对象可以是矩形、文本、图片等。离屏绘制是指将需要绘制的内容先绘制在缓存区，然后将其转换成图片，一次性绘制到Canvas上。离屏绘制使用CPU进行绘制，绘制速度较慢，对绘制速度有要求的场景应避免使用离屏绘制。
*   [Canvas](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-graphics-drawing-canvas)承载绘制内容与绘制状态的载体。
*   [Brush](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-graphics-drawing-brush)画刷对象，描述所绘制图形的填充信息。
*   [ShaderEffect](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-graphics-drawing-shadereffect)着色器。画刷和画笔设置着色器后，会使用着色器效果而不是颜色属性去绘制，但此时画笔和画刷的透明度属性仍然生效。
*   [readPixelsToBufferSync](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-image-pixelmap#readpixelstobuffersync12)：访问PixelMap的像素数据，通过对像素的处理来达到图片处理的效果。
*   [TaskPool](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/taskpool-introduction)：为应用程序提供多线程环境，降低资源消耗并提高系统性能。

#### 解决方案

**场景一**：为图片创建纯色背景。

*   **方案一**：使用Canvas实现纯色背景。
    
    先创建一个空的PixelMap作为Canvas的底图，绘制一个矩形渲染到Canvas并填充底色，再绘制图片到Canvas，返回叠加后的图片PixelMap。
    
    import { image } from '@kit.ImageKit';
    import { resourceManager } from '@kit.LocalizationKit';
    import { common2D, drawing } from '@kit.ArkGraphics2D';
    
    _// colors数组中的值为32位无符号整数（ARGB）_
    function drawCanvasBackgroundImage(pixelMap: PixelMap, colors: string) {
      let imageInfo = pixelMap.getImageInfoSync();
      let size: Size = imageInfo.size;
      const pixelMapColor: ArrayBuffer = new ArrayBuffer(size.width \* size.height \* 4);
      const options: image.InitializationOptions = {
        editable: true,
        pixelFormat: image.PixelMapFormat.RGBA\_8888,
        size: size
      };
      const canvasPixelMap = image.createPixelMapSync(pixelMapColor, options);
      const colorArray: number\[\] = colorAnalysis(colors);
      const color: common2D.Color = {
        alpha: colorArray\[0\],
        red: colorArray\[1\],
        green: colorArray\[2\],
        blue: colorArray\[3\]
    
      };
      const canvas = new drawing.Canvas(canvasPixelMap);
      canvas.drawColor(color, drawing.BlendMode.SRC); _// 使用源像素替换目标像素_
      canvas.drawImage(pixelMap, 0, 0);
      return canvasPixelMap;
    }
    
    function colorAnalysis(hexColor: string) {
    
      _// 创建一个数组来存储解析后的颜色值_
      let hexArr: number\[\] = \[\];
      let length = hexColor.length;
    
     _ // 如果十六进制颜色值长度为奇数，则去掉前面的一个字符_
      if (length % 2 === 1) {
        hexColor = hexColor.slice(1);
      }
    
     _ // 解析十六进制颜色值_
      for (let i = 0; i < hexColor.length / 2; i++) {
     _   // 获取当前两位的十六进制字符_
        let charCode1 = Number.parseInt(hexColor\[i \* 2\], 16);
        let charCode2 = Number.parseInt(hexColor\[i \* 2 + 1\], 16);
      _  // 计算十进制值_
        let hexNumber = charCode1 \* 16 + charCode2;
      _  // 将十进制值归一化到0-255的范围，并存储在数组中_
        hexArr.push(hexNumber);
      }
      return hexArr;
    }
    
    @Entry
    @Component
    struct ImageAddBackColor {
      @State pixelMap: PixelMap | undefined = undefined;
      @State resImage: PixelMap | undefined = undefined;
    
      getPixelMapByImage(fileName: string): image.PixelMap {
        const context: Context = this.getUIContext().getHostContext() as Context;
     _   // 获取resourceManager资源管理器_
        const resourceMgr: resourceManager.ResourceManager = context.resourceManager;
        let rawFileDescriptor = resourceMgr.getRawFdSync(fileName);
        const imageSource: image.ImageSource = image.createImageSource(rawFileDescriptor);
        let decodingOptions: image.DecodingOptions = {
          editable: true,
          desiredPixelFormat: 3,
        };
        const pixelMap: image.PixelMap = imageSource.createPixelMapSync(decodingOptions);
        resourceMgr.closeRawFdSync(fileName);
        return pixelMap;
      }
    
      aboutToAppear() {
        this.pixelMap = this.getPixelMapByImage('test.png'); _// test.png放在resources/rawfile下请根据实际情况替换_
      }
    
      @Styles
      ButtonStyle(){
        .backgroundColor('#0A59F7')
        .width(120)
        .height(35)
        .margin({
          top: 5,
          bottom: 5,
          left: 5,
          right: 5
        });
      }
    
      build() {
        Column({ space: 5 }) {
          Row({ space: 20 }) {
            Column() {
              Image(this.pixelMap)
                .objectFit(ImageFit.Contain)
                .margin({ right: 5 })
                .width(150)
                .height(150)
            }
            .borderRadius(8)
            .backgroundColor(Color.Gray) _// 图片为透明白色为展示效果明显添加灰色背景_
    
            Image(this.resImage)
              .objectFit(ImageFit.Contain)
              .width(150)
              .height(150)
              .borderRadius(8);
          }
    
          Button('添加背景色').ButtonStyle()
            .onClick(() => {
              this.resImage = drawCanvasBackgroundImage(this.pixelMap as PixelMap, '#FFD1D1D6');
            });
        }
        .justifyContent(FlexAlign.Center)
        .width('100%')
        .height('100%');
      }
    }
    
    效果预览：
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/f2/v3/C1YHY5GhSAmWWfLCwHV6xA/zh-cn_image_0000002628553242.png?HW-CC-KV=V1&HW-CC-Date=20260709T134302Z&HW-CC-Expire=86400&HW-CC-Sign=1FCCB83E91D68A91E6069C7B82B12C481B208A813477D4B586B6D2BD43C73F1D "点击放大")
    
    也可改为：
    
    import { image } from '@kit.ImageKit';
    import { resourceManager } from '@kit.LocalizationKit';
    import { common2D, drawing } from '@kit.ArkGraphics2D';
    
    _// colors数组中的值为32位无符号整数（ARGB）_
    function drawCanvasBackgroundImage(pixelMap: PixelMap, colors: string) {
      let imageInfo = pixelMap.getImageInfoSync();
      let size: Size = imageInfo.size;
      const pixelMapColor: ArrayBuffer = new ArrayBuffer(size.width \* size.height \* 4);
      const options: image.InitializationOptions = {
        editable: true,
        pixelFormat: image.PixelMapFormat.RGBA\_8888,
        size: size
      };
      const canvasPixelMap = image.createPixelMapSync(pixelMapColor, options);
      const colorArray: number\[\] = colorAnalysis(colors);
      const color: common2D.Color = {
        alpha: colorArray\[0\],
        red: colorArray\[1\],
        green: colorArray\[2\],
        blue: colorArray\[3\]
    
      };
      const canvas = new drawing.Canvas(canvasPixelMap);
      canvas.drawImage(pixelMap, 0, 0);
      canvas.drawColor(color, drawing.BlendMode.DST\_OVER); _// 源像素上方绘制目标像素，考虑目标像素的透明度。_
      return canvasPixelMap;
    }
    
    function colorAnalysis(hexColor: string) {
      _// 创建一个数组来存储解析后的颜色值_
      let hexArr: number\[\] = \[\];
      let length = hexColor.length;
    
     _ // 如果十六进制颜色值长度为奇数，则去掉前面的一个字符_
      if (length % 2 === 1) {
        hexColor = hexColor.slice(1);
      }
    
     _ // 解析十六进制颜色值_
      for (let i = 0; i < hexColor.length / 2; i++) {
     _   // 获取当前两位的十六进制字符_
        let charCode1 = Number.parseInt(hexColor\[i \* 2\], 16);
        let charCode2 = Number.parseInt(hexColor\[i \* 2 + 1\], 16);
     _   // 计算十进制值_
        let hexNumber = charCode1 \* 16 + charCode2;
     _   // 将十进制值归一化到0-255的范围，并存储在数组中_
        hexArr.push(hexNumber);
      }
      return hexArr;
    }
    
    @Entry
    @Component
    struct ImageAddBackColor {
      @State pixelMap: PixelMap | undefined = undefined;
      @State resImage: PixelMap | undefined = undefined;
    
      getPixelMapByImage(fileName: string): image.PixelMap {
        const context: Context = this.getUIContext().getHostContext() as Context;
      _  // 获取resourceManager资源管理器_
        const resourceMgr: resourceManager.ResourceManager = context.resourceManager;
        let rawFileDescriptor = resourceMgr.getRawFdSync(fileName);
        const imageSource: image.ImageSource = image.createImageSource(rawFileDescriptor);
        let decodingOptions: image.DecodingOptions = {
          editable: true,
          desiredPixelFormat: 3,
        };
        const pixelMap: image.PixelMap = imageSource.createPixelMapSync(decodingOptions);
        resourceMgr.closeRawFdSync(fileName);
        return pixelMap;
      }
    
      aboutToAppear() {
        this.pixelMap = this.getPixelMapByImage('test.png'); _// test.png放在resources/rawfile下请根据实际情况替换_
      }
    
      @Styles
      ButtonStyle(){
        .backgroundColor('#0A59F7')
        .width(120)
        .height(35)
        .margin({
          top: 5,
          bottom: 5,
          left: 5,
          right: 5
        });
      }
    
      build() {
        Column({ space: 5 }) {
          Row({ space: 20 }) {
            Column() {
              Image(this.pixelMap)
                .objectFit(ImageFit.Contain)
                .margin({ right: 5 })
                .width(150)
                .height(150)
            }
            .borderRadius(8)
            .backgroundColor(Color.Gray) _// 图片为透明白色为展示效果明显添加灰色背景_
    
            Image(this.resImage)
              .objectFit(ImageFit.Contain)
              .width(150)
              .height(150)
              .borderRadius(8);
          }
    
          Button('添加背景色').ButtonStyle()
            .onClick(() => {
              this.resImage = drawCanvasBackgroundImage(this.pixelMap as PixelMap, '#FFD1D1D6');
            });
        }
        .justifyContent(FlexAlign.Center)
        .width('100%')
        .height('100%');
      }
    }
    
    效果预览：
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/16/v3/7vKkTtMfT2iKttY3IzqaTw/zh-cn_image_0000002658912557.png?HW-CC-KV=V1&HW-CC-Date=20260709T134302Z&HW-CC-Expire=86400&HW-CC-Sign=790F71A856FD17CE74C433C229271B34675794E123E19AC21349FD3FF91D4ED3 "点击放大")
    
*   **方案二**：使用createPixelMap实现纯色背景。
    *   对一张蒙版图片创建纯色背景：
        
        通过接口createPixelMapSync创建纯色图片，作为背景图。通过OffscreenCanvasRenderingContext2D进行离屏渲染，先绘制背景图，再把原图绘制在背景图上面，达到底色效果。
        
        import { image } from '@kit.ImageKit';
        import { resourceManager } from '@kit.LocalizationKit';
        
        function getPixelMapByImage(fileName: string, context: Context): image.PixelMap {
         _ // 获取resourceManager资源管理器_
          const resourceMgr: resourceManager.ResourceManager = context.resourceManager;
          let rawFileDescriptor = resourceMgr.getRawFdSync(fileName);
          const imageSource: image.ImageSource = image.createImageSource(rawFileDescriptor);
          let decodingOptions: image.DecodingOptions = {
            editable: true,
            desiredPixelFormat: 3,
          };
          const pixelMap: image.PixelMap = imageSource.createPixelMapSync(decodingOptions);
          resourceMgr.closeRawFdSync(fileName);
          return pixelMap;
        }
        
        function pixelMapAddBackGroundColor(pixleMap: PixelMap, hexColor: string): PixelMap {
          let imageInfo = pixleMap.getImageInfoSync();
          let width = imageInfo.size.width;
          let height = imageInfo.size.height;
        
          const offScreenCanvas = new OffscreenCanvas(width, height);
          const offScreenContext: OffscreenCanvasRenderingContext2D = offScreenCanvas.getContext('2d');
        
          const colorBuf: ArrayBuffer = new ArrayBuffer(offScreenCanvas.width \* offScreenCanvas.height \*
            4);
          const colorInfo: Uint8Array = new Uint8Array(colorBuf);
         _ // 初始化RGB和Alpha值_
          let r = 0;
          let g = 0;
          let b = 0;
          let a = 255;
        
         _ // 创建一个数组来存储解析后的颜色值_
          let hexArr: number\[\] = \[\];
          let length = hexColor.length;
        
          _// 如果十六进制颜色值长度为奇数，则去掉前面的一个字符_
          if (length % 2 === 1) {
            hexColor = hexColor.slice(1);
          }
        
          _// 解析十六进制颜色值_
          for (let i = 0; i < hexColor.length / 2; i++) {
          _  // 获取当前两位的十六进制字符_
            let charCode1 = Number.parseInt(hexColor\[i \* 2\], 16);
            let charCode2 = Number.parseInt(hexColor\[i \* 2 + 1\], 16);
          _  // 计算十进制值_
            let hexNumber = charCode1 \* 16 + charCode2;
         _   // 将十进制值归一化到0-255的范围，并存储在数组中_
            hexArr.push(hexNumber);
          }
        
         _ // 判断是否有透明度_
          if (hexArr.length > 3) {
         _   // 有透明度时，从数组中读取Alpha、R、G、B值_
            a = hexArr\[0\];
            r = hexArr\[1\];
            g = hexArr\[2\];
            b = hexArr\[3\];
          } else {
          _  // 没有透明度时，从数组中读取R、G、B值_
            r = hexArr\[0\];
            g = hexArr\[1\];
            b = hexArr\[2\];
          }
          for (let i = 0; i < colorInfo.length; i += 4) {
            colorInfo\[i\] = b; _// Blue_
            colorInfo\[i + 1\] = g; _// Green_
            colorInfo\[i + 2\] = r; _// Red_
            colorInfo\[i + 3\] = a; _// Alpha_
          }
        
          let opts: image.InitializationOptions = {
            editable: true,
            pixelFormat: image.PixelMapFormat.RGBA\_8888,
            size: { height: offScreenCanvas.height, width: offScreenCanvas.width }
          };
          const bg = image.createPixelMapSync(colorInfo.buffer, opts);
          offScreenContext.drawImage(bg, 0, 0, offScreenCanvas.width, offScreenCanvas.height);
          offScreenContext.drawImage(pixleMap, 0, 0, offScreenCanvas.width, offScreenCanvas.height);
          let retPixelMap = offScreenContext.getPixelMap(0, 0, offScreenCanvas.width, offScreenCanvas.height);
          return retPixelMap;
        }
        
        @Entry
        @Component
        struct ImageAddBackColor {
          @State pixelMap: PixelMap | undefined = undefined;
          @State resImage: PixelMap | undefined = undefined;
        
          aboutToAppear() {
            let context = this.getUIContext().getHostContext() as Context;
            this.pixelMap = getPixelMapByImage('test.png', context); _// test.png放在resources/rawfile下请根据实际情况替换_
          }
        
          @Styles
          ButtonStyle(){
            .backgroundColor('#0A59F7')
            .width(120)
            .height(35)
            .margin({
              top: 5,
              bottom: 5,
              left: 5,
              right: 5
            });
          }
        
          build() {
            Column({ space: 5 }) {
              Row({ space: 20 }) {
                Column() {
                  Image(this.pixelMap)
                    .objectFit(ImageFit.Contain)
                    .margin({ right: 5 })
                    .width(150)
                    .height(150)
                }
                .borderRadius(8)
                .backgroundColor(Color.Gray) _// 图片为透明白色为展示效果明显添加灰色背景_
        
                Image(this.resImage)
                  .objectFit(ImageFit.Contain)
                  .width(150)
                  .height(150)
                  .borderRadius(8);
              }
        
              Button('添加背景色').ButtonStyle()
                .onClick(() => {
                  this.resImage = pixelMapAddBackGroundColor(this.pixelMap as PixelMap, '#FFD1D1D6');
                });
            }
            .justifyContent(FlexAlign.Center)
            .width('100%')
            .height('100%');
          }
        }
        
        效果预览：
        
        ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/12/v3/vAIQFLT5S5e42NYK-EJTPg/zh-cn_image_0000002658792619.png?HW-CC-KV=V1&HW-CC-Date=20260709T134302Z&HW-CC-Expire=86400&HW-CC-Sign=A22F1E1586EFAD7CE9030DE3ABB84B709366FD245F8E9C1E67CAC1A8FC147FF1 "点击放大")
        
    *   对蒙版图片多次创建纯色背景：
        
        为避免批量处理图片导致线程阻塞，可采用异步接口[createPixelMap](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-image-f#imagecreatepixelmap8)或多线程[TaskPool](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/taskpool-introduction)。
        
        使用异步方法适用于轻量级任务，但无法并行处理任务，且若多个异步任务间存在时序依赖，可能导致回调嵌套问题。
        
        使用TaskPool可以发挥多核优势，对于批量数据处理时实现多核并行计算，提高性能。通过[createPixelMapSync](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-image-f#imagecreatepixelmapsync12)多次创建纯色图片的耗时任务，分配给后台线程并行执行，主线程可以继续处理其他任务，保持应用的流畅性和响应性。
        
        多线程创建纯色图。
        
        import image from '@ohos.multimedia.image';
        import { drawing } from '@kit.ArkGraphics2D';
        import { floodFillScanLineWithStack } from './FloodFillAlgorithm';
        import { taskpool } from '@kit.ArkTS';
        import { BusinessError } from '@kit.BasicServicesKit';
        
        _/\*\*_
         _\*_
         _\* @param paramX - 起始填充点的 X 坐标_
         _\* @param paramY - 起始填充点的 Y 坐标_
         _\* @param newColor - 新的颜色值（ARGB 格式）_
         _\* @param pixels - 原始像素数据（ArrayBuffer）_
         _\* @param width - 图像宽度_
         _\* @param height - 图像高度_
         _\* @returns 处理后的像素数据（ArrayBuffer）_
         _\*/_
        @Concurrent
        function executeFloodFillTask(
          paramX: number,
          paramY: number,
          newColor: number,
          pixels: ArrayBuffer,
          width: number,
          height: number
        ) {
          console.info('填色开始');
          let pixelArray = new Int32Array(pixels);
          floodFillScanLineWithStack(paramX, paramY, newColor, pixelArray, width, height);
          console.info('填色结束');
          return pixels;
        }
        
        @Entry
        @ComponentV2
        struct FillColorComponent {
          @Local currPixelMap: PixelMap | null = null;
          _// 存储图像的宽高（单位：像素）_
          private imgWidth: number = 0;
          private imgHeight: number = 0;
          _// 当前填充颜色值（ARGB 格式，默认黑色）_
          private currColor: number = 0xff000000;
          _// 标记是否正在进行填充_
          private isFill: boolean = false;
        
          build() {
            RelativeContainer() {
              Image(this.currPixelMap)
                .width(200)
                .height(200)
                .objectFit(ImageFit.Fill)
                .onAreaChange((oldArea, newArea) => {
                  console.info(\`原前宽为:${oldArea.width}, 原前高为：${oldArea.height}\`);
                _  // 将视口单位（vp）转换为真实像素值_
                  this.imgWidth = this.getUIContext().vp2px(newArea.width as number);
                  this.imgHeight = this.getUIContext().vp2px(newArea.height as number);
                  console.info(\`当前宽为:${this.imgWidth}, 当前高为：${this.imgHeight}\`);
                  this.initFillColor();
                });
              Button('开始填色', { type: ButtonType.Normal })
                .width(100)
                .height(40)
                .fontColor(Color.White)
                .fontSize(16)
                .backgroundColor('#0A59F7')
                .alignRules({
                  center: { anchor: '\_\_container\_\_', align: VerticalAlign.Center },
                  middle: { anchor: '\_\_container\_\_', align: HorizontalAlign.Center }
                })
                .onClick(() => {
                  this.startFillColor().catch((error: BusinessError) => {
                    console.error(\`填充失败, error is ${error}\`);
                  });
                });
            }
            .padding(30)
            .height('100%')
            .width('100%');
          }
        
         _ /\*\*_
           _\* 初始化图像填充环境_
           _\* 创建一个空白的可编辑像素图（PixelMap），用于后续绘制和填充_
           _\*/_
          private initFillColor() {
          _  // 创建一个用于存储像素数据的 ArrayBuffer 大小为：宽 × 高 × 4 字节（RGBA 每像素 4 字节）_
            const colorArr = new ArrayBuffer(this.imgWidth \* this.imgHeight \* 4);
            let tempPixelMap = image.createPixelMapSync(colorArr, {
              editable: true,
              pixelFormat: image.PixelMapFormat.RGBA\_8888,
              size: { height: this.imgHeight, width: this.imgWidth }
            });
            const canvas = new drawing.Canvas(tempPixelMap);
            canvas.drawColor(255, 0, 0, 0);
            this.currPixelMap = tempPixelMap;
          }
        
         _ /\*\*_
           _\* 开始执行填充操作_
           _\* 包括检查状态、准备数据、提交异步任务等_
           _\*/_
        
          private async startFillColor() {
            if (this.currPixelMap == null) {
              return;
            }
        
            if (this.isFill) {
              return;
            }
        
            this.isFill = true;
            this.currColor = this.currColor === 0xff000000 ? 0xFFD1D1D6 : 0xff000000;
        
           _ // 创建一个缓冲区，用于读取当前 PixelMap 的像素数据_
            const colorBuffer = new ArrayBuffer(this.currPixelMap.getPixelBytesNumber());
            this.currPixelMap.readPixelsToBufferSync(colorBuffer);
        
          _  // 获取图像信息（如尺寸）_
            const imgSize = this.currPixelMap.getImageInfoSync().size;
            const imgWidth = imgSize.width;
            const imgHeight = imgSize.height;
        
           _ // 从当前颜色值中解析出 ARGB 各分量_
            const alpha = (this.currColor >> 24) & 0xff;
            const red = (this.currColor >> 16) & 0xff;
            const green = (this.currColor >> 8) & 0xff;
            const blue = this.currColor & 0xff;
            const replaceColor = red | green << 8 | blue << 16 | alpha << 24;
            console.info('被替换的颜色为:', replaceColor);
            const task = new taskpool.Task(
              executeFloodFillTask,
              100, 100, replaceColor, colorBuffer, imgWidth, imgHeight
            );
            taskpool.execute(task)
              .then((data) => {
                this.currPixelMap = image.createPixelMapSync(data as ArrayBuffer, {
                  editable: true,
                  srcPixelFormat: image.PixelMapFormat.RGBA\_8888,
                  pixelFormat: image.PixelMapFormat.RGBA\_8888,
                  size: { height: imgHeight, width: imgWidth }
                });
                this.isFill = false;
              })
              .catch((error: BusinessError) => {
                console.error(\`填充失败, error  is ${error}\`);
                this.isFill = false;
              });
          }
        }
        
        FloodFillAlgorithm.ets需要放置在Pages目录下，完整示例参考如下：
        
        _/\*\*_
         _\* 扫描填充算法（Scanline Flood Fill）_
         _\* @param startX 起始 x 坐标（number）_
         _\* @param startY 起始 y 坐标（number）_
         _\* @param newColor 新颜色值（Int32，RGBA 格式）_
         _\* @param pixels 像素数据数组（Int32Array）_
         _\* @param width 图像宽度（number）_
         _\* @param height 图像高度（number）_
         _\* @returns void_
         _\*/_
        export function floodFillScanLineWithStack(
          startX: number,
          startY: number,
          newColor: number,
          pixels: Int32Array,
          width: number,
          height: number
        ): void {
         _ // 检查起始坐标是否越界_
          if (
            startX < 0 || startX >= width ||
              startY < 0 || startY >= height
          ) {
            console.warn(
              '起始坐标超出图像范围：x=%d, y=%d',
              startX,
              startY
            );
            return;
          }
        
         _ // 获取起始点颜色_
          const initialPixelIndex = startY \* width + startX;
          const oldColor = pixels\[initialPixelIndex\];
        
          _// 如果新旧颜色相同，直接返回_
          if (oldColor === newColor) {
            console.info(
              '新旧颜色相同：起始点(%d, %d)，颜色=%s',
              startX,
              startY,
              int32ToRgba(newColor)
            );
            return;
          }
        
          interface StackCoordinate {
            x: number;
            y: number;
          }
        
          const processingStack: StackCoordinate\[\] = \[\];
          const visitedCoordinates = new Set<string>();
        
         _ // 用于快速获取像素索引_
          const getIndex = (x: number, y: number): number => {
            return y \* width + x;
          };
        
          _// 将起始点入栈_
          processingStack.push({ x: startX, y: startY });
        
          _// 打印开始信息（使用 rgba 格式）_
          console.info(
            '填充开始：起始点(%d, %d)，旧颜色=%s，新颜色=%s',
            startX,
            startY,
            int32ToRgba(oldColor),
            int32ToRgba(newColor)
          );
        
          while (processingStack.length > 0) {
            const currentCoordinate = processingStack.pop();
            if (!currentCoordinate) {
              continue;
            }
        
            const x = currentCoordinate.x;
            const y = currentCoordinate.y;
            const pixelIndex = getIndex(x, y);
        
          _  // 越界、颜色不匹配、已访问_
            if (
              x < 0 ||
                x >= width ||
                y < 0 ||
                y >= height ||
                pixels\[pixelIndex\] !== oldColor ||
              visitedCoordinates.has(\`${x},${y}\`)
            ) {
              continue;
            }
        
         _   // 标记为已访问_
            visitedCoordinates.add(\`${x},${y}\`);
        
         _   // 填充当前像素为新颜色_
            pixels\[pixelIndex\] = newColor;
        
           _ // 向上扫描：找到最上端的旧颜色像素_
            let upperEdgeY = y - 1;
            while (
              upperEdgeY >= 0 &&
                pixels\[getIndex(x, upperEdgeY)\] === oldColor
            ) {
              upperEdgeY--;
            }
            upperEdgeY++; _// 起始行_
        
          _  // 向下扫描：找到最下端的旧颜色像素_
            let lowerEdgeY = y + 1;
            while (
              lowerEdgeY < height &&
                pixels\[getIndex(x, lowerEdgeY)\] === oldColor
            ) {
              lowerEdgeY++;
            }
            lowerEdgeY--; _// 结束行_
        
         _   // 填充当前列（upperEdgeY 到 lowerEdgeY）_
            for (let currentY = upperEdgeY; currentY <= lowerEdgeY; currentY++) {
              const columnPixelIndex = getIndex(x, currentY);
              if (pixels\[columnPixelIndex\] === oldColor) {
                pixels\[columnPixelIndex\] = newColor;
              }
            }
        
          _  // 扫描左右方向的连续区域（水平扫描）_
            let leftEdgeX = x - 1;
            let rightEdgeX = x + 1;
        
          _  // 向左扫描连续的旧颜色_
            while (
              leftEdgeX >= 0 &&
                pixels\[getIndex(leftEdgeX, y)\] === oldColor
            ) {
              leftEdgeX--;
            }
            leftEdgeX++; _// 起始点_
        
          _  // 向右扫描连续的旧颜色_
            while (
              rightEdgeX < width &&
                pixels\[getIndex(rightEdgeX, y)\] === oldColor
            ) {
              rightEdgeX++;
            }
            rightEdgeX--; _// 结束点_
        
          _  // 将左右行的相邻点入栈（仅在有新区域时）_
            for (let currentX = leftEdgeX; currentX <= rightEdgeX; currentX++) {
              const rowPixelIndex = getIndex(currentX, y);
              if (pixels\[rowPixelIndex\] === oldColor) {
              _  // 检查上下相邻点是否为旧颜色，避免重复入栈_
                if (y > 0 && pixels\[getIndex(currentX, y - 1)\] === oldColor) {
                  processingStack.push({ x: currentX, y: y - 1 });
                }
                if (y < height - 1 && pixels\[getIndex(currentX, y + 1)\] === oldColor) {
                  processingStack.push({ x: currentX, y: y + 1 });
                }
              }
            }
          }
        
          _// 输出完成信息_
          console.info(
            '填充完成：起始点(%d, %d)，新颜色=%s',
            startX,
            startY,
            int32ToRgba(newColor)
          );
        }
        
        _// 工具函数：Int32 颜色 → rgba(R, G, B, A) 字符串_
        function int32ToRgba(color: number): string {
          const alpha = (color >> 24) & 0xFF;
          const red = (color >> 16) & 0xFF;
          const green = (color >> 8) & 0xFF;
          const blue = color & 0xFF;
        
          return \`rgba(${red}, ${green}, ${blue}, ${alpha / 255})\`;
        }
        
        效果预览：
        
        ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/3b/v3/3lQwnlKeSy6r6S59ptDxhQ/zh-cn_image_0000002628393360.png?HW-CC-KV=V1&HW-CC-Date=20260709T134302Z&HW-CC-Expire=86400&HW-CC-Sign=ED87544481563081536A7C50D9395E755E0E01B6E3C91B67ABDCE2BBDE5651F7 "点击放大")
        
*   **方案三**：使用readPixelsToBufferSync遍历像素中的透明像素实现纯色背景。
    
    通过接口[readPixelsToBufferSync](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-image-pixelmap#readpixelstobuffersync12)访问PixelMap的像素数据，对透明部分的像素点添加为底色。
    
    import { image } from '@kit.ImageKit';
    import { resourceManager } from '@kit.LocalizationKit';
    
    function getPixelMapByImage(fileName: string, context: Context): image.PixelMap {
     _ // 获取resourceManager资源管理器_
      const resourceMgr: resourceManager.ResourceManager = context.resourceManager;
      let rawFileDescriptor = resourceMgr.getRawFdSync(fileName);
      const imageSource: image.ImageSource = image.createImageSource(rawFileDescriptor);
      let decodingOptions: image.DecodingOptions = {
        editable: true,
        desiredPixelFormat: 3,
      };
      const pixelMap: image.PixelMap = imageSource.createPixelMapSync(decodingOptions);
      resourceMgr.closeRawFdSync(fileName);
      return pixelMap;
    }
    
    function hexColorToRGBA(hexColor: string) {
     _ // 初始化RGB和Alpha值_
      let r = 0;
      let g = 0;
      let b = 0;
      let a = 255;
    
      _// 创建一个数组来存储解析后的颜色值_
      let hexArr: number\[\] = \[\];
      let length = hexColor.length;
    
     _ // 如果十六进制颜色值长度为奇数，则去掉前面的一个字符_
      if (length % 2 === 1) {
        hexColor = hexColor.slice(1);
      }
    
      _// 解析十六进制颜色值_
      for (let i = 0; i < hexColor.length / 2; i++) {
       _ // 获取当前两位的十六进制字符_
        let charCode1 = Number.parseInt(hexColor\[i \* 2\], 16);
        let charCode2 = Number.parseInt(hexColor\[i \* 2 + 1\], 16);
     _   // 计算十进制值_
        let hexNumber = charCode1 \* 16 + charCode2;
       _ // 将十进制值归一化到0-255的范围，并存储在数组中_
        hexArr.push(hexNumber);
      }
    
      _// 判断是否有透明度_
      if (hexArr.length > 3) {
      _  // 有透明度时，从数组中读取Alpha、R、G、B值_
        a = hexArr\[0\];
        r = hexArr\[1\];
        g = hexArr\[2\];
        b = hexArr\[3\];
      } else {
     _   // 没有透明度时，从数组中读取R、G、B值_
        r = hexArr\[0\];
        g = hexArr\[1\];
        b = hexArr\[2\];
      }
    
      return \[r, g, b, a\];
    }
    
    function pixelMapAddBackGroundColorByRgb(pixelMap: PixelMap, hexColor: string): PixelMap {
      let imageInfo = pixelMap.getImageInfoSync();
      let width = imageInfo.size.width;
      let height = imageInfo.size.height;
    
     _ // 获取图片像素值_
      let readBuffer: ArrayBuffer = new ArrayBuffer(width \* height \* 4);
      pixelMap.readPixelsToBufferSync(readBuffer);
      let byteBuffer: Uint32Array = new Uint32Array(readBuffer);
      let finalBinary: Uint8Array = new Uint8Array(new ArrayBuffer(width \* height \* 4));
    
      _// 初始化RGB和Alpha值_
      let rgba = hexColorToRGBA(hexColor);
    
      for (let i = 0; i < (width \* height); i++) {
        let rgb = byteBuffer\[i\];
        let red = (rgb >> 0) & 0x000000ff;
        let green = (rgb >> 8) & 0x000000ff;
        let blue = (rgb >> 16) & 0x000000ff;
        let alpha = (rgb >> 24) & 0x000000ff;
        if (alpha === 0) {
          finalBinary\[i \* 4\] = rgba\[2\]; _// Blue_
          finalBinary\[i \* 4 + 1\] = rgba\[1\]; _// Green_
          finalBinary\[i \* 4 + 2\] = rgba\[0\]; _// Red_
          finalBinary\[i \* 4 + 3\] = rgba\[3\]; _// Alpha_
        } else {
       _   // RGBA转ARGB数据_
          finalBinary\[i \* 4\] = blue;
          finalBinary\[i \* 4 + 1\] = green;
          finalBinary\[i \* 4 + 2\] = red;
          finalBinary\[i \* 4 + 3\] = alpha;
        }
    
      }
    
      let color: ArrayBuffer = finalBinary.buffer as ArrayBuffer;
      let opts: image.InitializationOptions = { editable: true, pixelFormat: 3, size: { height: height, width: width } };
      let pixelMapTemp: image.PixelMap = image.createPixelMapSync(color, opts);
      return pixelMapTemp;
    }
    
    @Entry
    @Component
    struct ImageAddBackColor {
      @State pixelMap: PixelMap | undefined = undefined;
      @State resImage: PixelMap | undefined = undefined;
    
      aboutToAppear() {
        let context = this.getUIContext().getHostContext() as Context;
        this.pixelMap = getPixelMapByImage('test.png', context); _// test.png放在resources/rawfile下请根据实际情况替换_
      }
    
      @Styles
      ButtonStyle(){
        .backgroundColor('#0A59F7')
        .width(120)
        .height(35)
        .margin({
          top: 5,
          bottom: 5,
          left: 5,
          right: 5
        });
      }
    
      build() {
        Column({ space: 5 }) {
          Row({ space: 20 }) {
            Column() {
              Image(this.pixelMap)
                .objectFit(ImageFit.Contain)
                .margin({ right: 5 })
                .width(150)
                .height(150)
            }
            .borderRadius(8)
            .backgroundColor(Color.Gray) _// 图片为透明白色为展示效果明显添加灰色背景_
    
            Image(this.resImage)
              .objectFit(ImageFit.Contain)
              .width(150)
              .height(150)
              .borderRadius(8);
          }
    
          Button('添加背景色').ButtonStyle()
            .onClick(() => {
              this.resImage = pixelMapAddBackGroundColorByRgb(this.pixelMap as PixelMap, '#FFD1D1D6');
            });
        }
        .justifyContent(FlexAlign.Center)
        .width('100%')
        .height('100%');
      }
    }
    
    效果预览：
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/9f/v3/4DUuBEHhSVCJu-2gsqiTEQ/zh-cn_image_0000002628553246.png?HW-CC-KV=V1&HW-CC-Date=20260709T134302Z&HW-CC-Expire=86400&HW-CC-Sign=CC8BD949704312F46E59AC0F345A532A9CF75D9994B4225338C9A1106D89ED3C "点击放大")
    

**场景二**：为图片创建渐变色背景。

*   使用ShaderEffect实现渐变色背景:
    
    先创建一个空的PixelMap作为Canvas的底图，渲染到Canvas，通过[ShaderEffect](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-graphics-drawing-shadereffect)设置着色，Brush画刷绘制铺满PixelMap的图形，再绘制图片到Canvas，返回叠加后的图片PixelMap。
    
    为PixelMap图片添加两种颜色渐变的底色：
    
    import { image } from '@kit.ImageKit';
    import { resourceManager } from '@kit.LocalizationKit';
    import { common2D, drawing } from '@kit.ArkGraphics2D';
    
    _// colors数组中的值为32位无符号整数（ARGB）_
    function drawCanvasBackgroundImage(pixelMap: PixelMap, colors: number\[\]) {
      let imageInfo = pixelMap.getImageInfoSync();
      let size: Size = imageInfo.size;
      let canvasRect: common2D.Rect = {
        left: 0,
        top: 0,
        right: size.width,
        bottom: size.height
      };
      _// 通过设置startPt和endPt可以改变渐变方向_
      let startPt: common2D.Point = { x: 0, y: 0 };
      let endPt: common2D.Point = { x: 0, y: size.height };
     _ // pos表示每种对应颜色在颜色数组中的相对位置。_
      const pos: number\[\] = \[0, 1\];
      const pixelMapColor: ArrayBuffer = new ArrayBuffer(size.width \* size.height \* 4);
      const options: image.InitializationOptions = {
        editable: true,
        pixelFormat: image.PixelMapFormat.RGBA\_8888,
        size: size
      };
      const canvasPixelMap = image.createPixelMapSync(pixelMapColor, options);
      const canvas = new drawing.Canvas(canvasPixelMap);
      _// 创建渐变色着色器_
      let shaderEffect =
        drawing.ShaderEffect.createLinearGradient(startPt, endPt, colors, drawing.TileMode.CLAMP, pos);
     _ // pos数组长度需和colors保持一致，数组的首个元素应当是0.0，末尾元素应当是1.0，中间的元素应当在0与1之间并且逐下标递增，表示colors中每个对应颜色的相对位置。_
      _// pos默认为null，表示颜色均匀分布在起点和终点之间。_
      let brush = new drawing.Brush();
      brush.setShaderEffect(shaderEffect);
      canvas.attachBrush(brush);
      canvas.drawRect(canvasRect);
      canvas.detachBrush();
      canvas.drawImage(pixelMap, 0, 0);
      return canvasPixelMap;
    }
    
    @Entry
    @Component
    struct ImageWithGradientBackground {
      @State pixelMap: PixelMap | undefined = undefined;
      @State resImage: PixelMap | undefined = undefined;
    
      getPixelMapByImage(fileName: string): image.PixelMap {
        const context: Context = this.getUIContext().getHostContext() as Context;
      _  // 获取resourceManager资源管理器_
        const resourceMgr: resourceManager.ResourceManager = context.resourceManager;
        let rawFileDescriptor = resourceMgr.getRawFdSync(fileName);
        const imageSource: image.ImageSource = image.createImageSource(rawFileDescriptor);
        let decodingOptions: image.DecodingOptions = {
          editable: true,
          desiredPixelFormat: 3,
        };
        const pixelMap: image.PixelMap = imageSource.createPixelMapSync(decodingOptions);
        resourceMgr.closeRawFdSync(fileName);
        return pixelMap;
      }
    
      aboutToAppear() {
        this.pixelMap = this.getPixelMapByImage('test.png'); _// test.png放在resources/rawfile下请根据实际情况替换_
      }
    
      @Styles
      ButtonStyle(){
        .backgroundColor('#0A59F7')
        .width(120)
        .height(35)
        .margin({
          top: 5,
          bottom: 5,
          left: 5,
          right: 5
        });
      }
    
      build() {
        Column({ space: 5 }) {
          Row({ space: 20 }) {
            Column() {
              Image(this.pixelMap)
                .objectFit(ImageFit.Contain)
                .margin({ right: 5 })
                .width(150)
                .height(150)
            }
            .borderRadius(8)
            .backgroundColor(Color.Gray) _// 图片为透明白色为展示效果明显添加灰色背景_
    
            Image(this.resImage)
              .objectFit(ImageFit.Contain)
              .width(150)
              .height(150)
              .borderRadius(8);
          }
    
          Button('添加背景色').ButtonStyle()
            .onClick(() => {
              this.resImage = drawCanvasBackgroundImage(this.pixelMap as PixelMap, \[0x330A59F7, 0xFF61CFBE\]);
            });
        }
        .justifyContent(FlexAlign.Center)
        .width('100%')
        .height('100%');
      }
    }
    
    效果预览：
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/e4/v3/C4OJTXJcQW--sKn1tEuHqg/zh-cn_image_0000002658912559.png?HW-CC-KV=V1&HW-CC-Date=20260709T134302Z&HW-CC-Expire=86400&HW-CC-Sign=8CE378BC4ACD16CB206B6941B3F05B33181B9CF63F3B7A36B530F825E0609B9A "点击放大")
    

#### 总结

| 
场景

 | 

使用方法

 |
| :-- | :-- |
| 

纯色背景

 | 

[Canvas](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-graphics-drawing-canvas)绘制一个矩形并填充底色，再绘制图片到Canvas，返回叠加后的图片。

[createPixelMapSync](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-image-f#imagecreatepixelmapsync12)创建纯色图片，通过[OffscreenCanvasRenderingContext2D](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-offscreencanvasrenderingcontext2d)进行离屏渲染达到底色效果。

[readPixelsToBufferSync](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-image-pixelmap#readpixelstobuffersync12)遍历像素中的透明像素实现纯色背景。

 |
| 

渐变色背景

 | 

使用[ShaderEffect](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-graphics-drawing-shadereffect)实现渐变色背景。

 |