HarmonyOS APP实战-画图APP - 第10篇:色彩管理功能

HarmonyOS APP实战-画图APP - 第10篇:色彩空间转换与HDR支持

1. 开篇

在上一篇中,我们实现了智能取色与色板生成功能------通过 @ohos.effectKitColorPicker 提取图像主色,并动态生成五色色板供用户快速选择。用户点击画布任意区域即可获取该区域主色,色板同步更新,大大提升了取色效率。

然而,显示设备的色彩能力千差万别。目前 APP 绘制的颜色均基于 sRGB 色域,而越来越多的设备支持 Display P3、BT.2020 等更广色域,甚至支持 HDR(高动态范围)。为了充分利用硬件能力,让画作色彩更加鲜艳、明暗对比更真实,我们需要集成色彩空间管理能力。本篇将 集成 @ohos.graphics.colorSpaceManager 实现色彩空间转换 ,并利用 @ohos.graphics.hdrCapability 检测设备 HDR 能力,在 APP 中添加 HDR 模式开关,使画布能够输出 HDR 色彩。

2. 核心实现

2.1 基础配置 -- 导入模块与权限声明

ets/entryability/EntryAbility.ets 中或工具模块中导入必要的 API。由于色彩空间管理不涉及敏感权限,无需额外授权,但需要确保 API 版本兼容(推荐 API 12+)。

typescript 复制代码
// utils/ColorManager.ets
import { colorSpaceManager } from '@kit.ArkGraphics2D';        // @ohos.graphics.colorSpaceManager
import { hdrCapability } from '@kit.ArkGraphics2D';           // @ohos.graphics.hdrCapability
import { BusinessError } from '@kit.BasicServicesKit';

关键点说明

  • colorSpaceManager 提供 create(colorSpaceName: colorSpace.ColorSpace) 等方法创建色域对象,并可获取色域属性。
  • hdrCapability 提供 isHdrSupported() 方法检测当前设备是否支持 HDR 显示。
  • 所有导入需在 module.json5 中确保已依赖 @kit.ArkGraphics2D(默认包含)。

2.2 核心逻辑 -- ColorManager 类与 HDR 检测

编写 ColorManager 工具类,封装色彩空间创建、颜色转换、HDR 能力判断逻辑。该类将被上层页面调用,实现画布着色空间切换。

typescript 复制代码
// utils/ColorManager.ets

export class ColorManager {
  // 当前使用的色域对象
  private currentColorSpace: colorSpaceManager.ColorSpace | null = null;
  // 是否支持 HDR
  private _isHdrSupported: boolean = false;

  constructor() {
    this.initHdrCheck();
  }

  /**
   * 检测设备 HDR 能力
   */
  private initHdrCheck(): void {
    try {
      this._isHdrSupported = hdrCapability.isHdrSupported();
      console.info('HDR 支持情况:', this._isHdrSupported);
    } catch (err) {
      console.error('HDR 能力检测失败:', (err as BusinessError).message);
      this._isHdrSupported = false;
    }
  }

  get isHdrSupported(): boolean {
    return this._isHdrSupported;
  }

  /**
   * 获取指定色域名称对应的 ColorSpace 对象
   * @param name 色域名称,如 'DISPLAY_P3', 'SRGB', 'BT2020_HLG'
   */
  getColorSpace(name: string): colorSpaceManager.ColorSpace | null {
    const colorSpaceNames = colorSpaceManager.ColorSpace;
    // 根据字符串映射到枚举值(若 API 支持)
    const enumValue = (colorSpaceNames as Record<string, number>)[name];
    if (enumValue === undefined) {
      console.warn('不支持的色域:', name);
      return null;
    }
    try {
      return colorSpaceManager.create(enumValue as colorSpaceManager.ColorSpace);
    } catch (err) {
      console.error('创建色域失败:', (err as BusinessError).message);
      return null;
    }
  }

  /**
   * 将 RGBA 颜色值从源色域转换到目标色域
   * @param rgba [r,g,b,a] 0-255
   * @param srcSpace 源色域名称
   * @param dstSpace 目标色域名称
   */
  convertColor(rgba: number[], srcSpace: string, dstSpace: string): number[] {
    const src = this.getColorSpace(srcSpace);
    const dst = this.getColorSpace(dstSpace);
    if (!src || !dst) {
      console.warn('色域转换失败:无法创建源或目标色域');
      return rgba;
    }
    try {
      // 假设 colorSpaceManager 提供了 convert 方法(文档示例 API)
      // 实际 API 为 colorSpaceManager.convert(color: number[], src: ColorSpace, dst: ColorSpace)
      const converted = colorSpaceManager.convert(rgba, src, dst);
      return converted;
    } catch (err) {
      console.error('色域转换失败:', (err as BusinessError).message);
      return rgba;
    }
  }
}

关键点说明

  • hdrCapability.isHdrSupported() 返回布尔值,需在 try-catch 中调用以防不支持。
  • colorSpaceManager.create() 参数为枚举值,枚举定义在 colorSpaceManager.ColorSpace 中,包含 SRGBDISPLAY_P3BT2020_HLG 等。
  • 颜色转换方法 colorSpaceManager.convert()(此处为假设的 API 名称,实际需以官方文档为准;若文档未提供 convert,则需另寻途径。根据 ArkGraphics 2D 简介,colorSpaceManager 主要提供色域创建和属性获取,颜色转换可能通过 colorSpaceManager.transform() 或手动矩阵计算。此处为演示逻辑,实际实现时应参照官方 API 参考。为确保文章准确性,我们将在下文使用文档明确支持的 API 进行色域切换,而非转换单个颜色值。)

真实 API 说明 :截至 HarmonyOS NEXT,@ohos.graphics.colorSpaceManager 的核心能力是色域对象的创建与元数据获取,并不直接提供颜色转换 API。颜色的实际转换需要结合 Canvas 的 colorSpace 属性或通过 offlineImageProcessing 实现。因此,我们的 ColorManager 将主要管理画布色域设置,而非单独转换颜色。

修改 ColorManager 以使代码符合文档实际能力:

typescript 复制代码
// utils/ColorManager.ets (修正版)
import { colorSpaceManager } from '@kit.ArkGraphics2D';
import { hdrCapability } from '@kit.ArkGraphics2D';

export class ColorManager {
  private currentColorSpace: colorSpaceManager.ColorSpace | null = null;
  private _isHdrSupported: boolean = false;

  constructor() {
    this.checkHdr();
  }

  private checkHdr(): void {
    try {
      this._isHdrSupported = hdrCapability.isHdrSupported();
    } catch (e) {
      this._isHdrSupported = false;
    }
  }

  get isHdrSupported(): boolean {
    return this._isHdrSupported;
  }

  /**
   * 根据色域名称获取 ColorSpace 对象
   */
  getColorSpace(name: string): colorSpaceManager.ColorSpace | null {
    try {
      // 枚举映射
      const enumMap: Record<string, number> = {
        'SRGB': colorSpaceManager.ColorSpace.SRGB,
        'DISPLAY_P3': colorSpaceManager.ColorSpace.DISPLAY_P3,
        'BT2020_HLG': colorSpaceManager.ColorSpace.BT2020_HLG,
        'BT2020_PQ': colorSpaceManager.ColorSpace.BT2020_PQ,
      };
      const enumVal = enumMap[name];
      if (enumVal === undefined) return null;
      return colorSpaceManager.create(enumVal);
    } catch (err) {
      console.error('getColorSpace error:', err);
      return null;
    }
  }

  /**
   * 设置当前画布使用的色域(实际使用时需要将 ColorSpace 传给 Canvas 的 colorSpace 属性)
   */
  setCanvasColorSpace(ctx: CanvasRenderingContext2D, colorSpaceName: string): void {
    const cs = this.getColorSpace(colorSpaceName);
    if (cs) {
      // CanvasRenderingContext2D 的 colorSpace 属性可赋值 ColorSpace 对象
      ctx.colorSpace = cs;
      console.info('Canvas 色域切换为:', colorSpaceName);
    }
  }
}

关键点说明

  • Canvas 的 colorSpace 属性可以直接接受 colorSpaceManager.ColorSpace 对象,从而影响后续绘制的颜色渲染。
  • hdrCapability.isHdrSupported()@ohos.graphics.hdrCapability 的核心方法,调用前需确保设备支持 HDR 显示(如华为 Mate 60 Pro+)。
  • 色域枚举值随 API 版本可能变化,需参考官方文档

2.3 完整模块 -- HDRToggle 组件与画布页面集成

创建 HDRToggle 组件,作为工具栏上的一个开关按钮。点击后切换画布的色域(sRGB ↔ Display P3),并展示 HDR 标识。同时,将 ColorManager 注入到画布页面(DrawingBoard)中,实现色彩空间切换。

typescript 复制代码
// pages/HDRToggle.ets
import { ColorManager } from '../utils/ColorManager';
import { hdrCapability } from '@kit.ArkGraphics2D';

@Component
export struct HDRToggle {
  // 从父组件传入的 Canvas 上下文
  private canvasContext: CanvasRenderingContext2D;
  private colorManager: ColorManager = new ColorManager();

  @State isHdrMode: boolean = false;
  @State deviceSupportHdr: boolean = this.colorManager.isHdrSupported;

  build() {
    Row({ space: 8 }) {
      // HDR 模式开关
      Toggle({ type: ToggleType.Switch, isOn: this.isHdrMode })
        .onChange((isOn: boolean) => {
          this.isHdrMode = isOn;
          if (isOn && this.deviceSupportHdr) {
            // 切换至 Display P3 色域(宽色域)
            this.colorManager.setCanvasColorSpace(this.canvasContext, 'DISPLAY_P3');
          } else {
            // 切回 sRGB 标准色域
            this.colorManager.setCanvasColorSpace(this.canvasContext, 'SRGB');
          }
        })
        .enabled(this.deviceSupportHdr)   // 不支持 HDR 的设备禁用开关
      Text(this.isHdrMode ? 'HDR 模式' : '标准模式')
        .fontSize(14)
      // HDR 图标
      if (this.deviceSupportHdr) {
        Image($r('app.media.ic_hdr'))
          .width(20)
          .height(20)
          .objectFit(ImageFit.Contain)
      } else {
        Text('不支持HDR')
          .fontSize(12)
          .fontColor(Color.Gray)
      }
    }
    .padding(8)
    .backgroundColor('#f0f0f0')
    .borderRadius(8)
  }
}

在主画布页面 pages/DrawingBoard.ets 中引用 HDRToggle,并将 Canvas 上下文传入:

typescript 复制代码
// pages/DrawingBoard.ets
import { HDRToggle } from './HDRToggle';
// ...其他导入

@Entry
@Component
struct DrawingBoard {
  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);

  build() {
    Column() {
      // 顶部工具栏
      Row() {
        // ...其他工具按钮(画笔、形状等)
        HDRToggle({ canvasContext: this.context })
          .width('auto')
      }
      .width('100%')
      .height(60)
      .padding({ left: 8, right: 8 })

      // 画布区域
      Canvas(this.context)
        .width('100%')
        .layoutWeight(1)
        .backgroundColor(Color.White)
        .onReady(() => {
          // 默认使用 sRGB
          this.context.colorSpace = this.colorManager.getColorSpace('SRGB');
        })
    }
    .width('100%')
    .height('100%')
  }
}

关键点说明

  • HDRToggle 组件通过 @State 响应式管理 HDR 模式状态,切换时调用 ColorManager.setCanvasColorSpace() 直接修改 Canvas 的 colorSpace 属性。
  • 若设备不支持 HDR(hdrCapability.isHdrSupported() 返回 false),开关被禁用,并显示"不支持HDR"文本。
  • 需在 resources/base/element 中添加 ic_hdr.svg 图标,或使用系统图标替代(如 $media('startIcon'))。

3. 运行验证

编译运行 APP 到支持 HDR 的真机(如 Mate 60 Pro 系列或平板)。预期效果:

  1. 顶部工具栏出现 HDR 开关(开关默认关闭,显示"标准模式")。
  2. 点击开关,画布背景色无明显变化,但后续绘制的图形(如矩形、圆形、手绘笔画)颜色会变得更鲜艳(Display P3 色域比 sRGB 多覆盖约 25% 的颜色)。
  3. 在画布上绘制一笔红色(原本 sRGB 下接近 #FF0000),开启 HDR 模式后,红色可能更明亮、更饱和。您可以通过之前第9篇的智能取色功能提取红色区域,对比 RGB 值变化。
  4. 若在不支持 HDR 的模拟器或旧设备上运行,开关将变灰,旁边显示"不支持HDR"。

4. 小结与预告

本篇实现了色彩空间转换与 HDR 支持,核心产出:

  • ColorManager 封装了 @ohos.graphics.colorSpaceManager@ohos.graphics.hdrCapability,提供色域创建、Canvas 色域设置、HDR 能力检测。
  • HDRToggle 组件集成到工具栏,可一键切换画布色域,让画作在 HDR 设备上呈现更丰富的色彩。

至此,画图 APP 的图形效果处理能力从 sRGB 扩展到宽色域/HDR,为专业绘画用户提供更大创作空间。

下一篇将聚焦 操作历史管理,利用命令模式(Command Pattern)实现绘制操作的撤销与重做。我们将设计操作栈,记录每一步绘制命令,支持无限次撤销(受内存限制),并配套 UI 按钮。敬请期待!

相关推荐
见山是山-见水是水7 小时前
鸿蒙Divider 分割线组件完全指南:内容分组、视觉分区与自定义样式
华为·harmonyos
贾伟康8 小时前
【知律|18】HarmonyOS ArkTS 权限与隐私实战:让 module.json5、功能说明和拒绝路径一致
harmonyos·arkts·隐私合规·appgallery·应用权限
Kevin Coding18 小时前
JsonConvert:适用于 Android、鸿蒙与 Flutter 的 JSON 转 Model 插件
android·flutter·harmonyos
m0_7496902321 小时前
【寻迹校园 HarmonyOS NEXT 实战 28】不交换手机号也能交接:固定校内交接点的隐私设计
华为·harmonyos·arkts·产品设计·隐私设计·安全交接
Magic-ZYJ21 小时前
HarmonyOS Stage 模型实战:UIAbility 生命周期如何驱动页面安全状态
安全·华为·harmonyos·鸿蒙·移动端开发·独立开发者·心晴手记
贾伟康21 小时前
【中国方言题库|09】HarmonyOS ArkTS 方言搜索实战:实现词语检索和无结果反馈
harmonyos·arkts·状态管理·arkui·本地搜索
Dovis(誓平步青云)21 小时前
从手机单栏到平板分栏:任务清单的筛选、选中态与不可变更新
华为·harmonyos
梦想不只是梦与想21 小时前
鸿蒙 AppGallery Connect:查看应用信息(三)
harmonyos·appgallery·client id·app id·developer id
贾伟康1 天前
【中国方言题库|03】HarmonyOS ArkTS 四川话分库实战:复用题库组件并保持地区参数清晰
harmonyos·arkts·arkui·路由传参·组件复用
贾伟康1 天前
【中国方言题库|10】HarmonyOS ArkTS 语音播放实战:管理读音播放与页面生命周期
生命周期·harmonyos·arkts·语音合成·texttospeech