<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

HarmonyOS開發案例:【圖片編輯】

jf_46214456 ? 來源:jf_46214456 ? 作者:jf_46214456 ? 2024-04-22 16:42 ? 次閱讀

介紹

基于canvas組件、圖片編解碼,介紹了圖片編輯實現過程。主要包含以下功能:

  1. 圖片的解碼和繪制。
  2. 使用PixelMap進行圖片編輯,如裁剪、旋轉、亮度調節、透明度調節、飽和度調節等操作。

相關概念

  • [canvas組件]:提供畫布組件。用于自定義繪制圖形。
  • [圖片處理]:提供圖片處理效果,包括通過屬性創建PixelMap、讀取圖像像素數據、讀取區域內的圖片數據等。

相關權限

本篇Codelab使用了媒體文件存儲能力,需要在配置文件config.json里添加媒體文件讀寫權限:

  • ohos.permission.MEDIA_LOCATION
  • ohos.permission.READ_MEDIA
  • ohos.permission.WRITE_MEDIA

環境搭建

鴻蒙開發指導文檔:[gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md]

搜狗高速瀏覽器截圖20240326151344.png

軟件要求

  • [DevEco Studio]版本:DevEco Studio 3.1 Release及以上版本。
  • OpenHarmony SDK版本:API version 9及以上版本。

硬件要求

  • 開發板類型:[潤和RK3568開發板]。
  • OpenHarmony系統:3.2 Release及以上版本。

環境搭建

完成本篇Codelab我們首先要完成開發環境的搭建,本示例以RK3568開發板為例,參照以下步驟進行:

  1. [獲取OpenHarmony系統版本]:標準系統解決方案(二進制)。以3.2 Release版本為例:
  2. 搭建燒錄環境。
    1. [完成DevEco Device Tool的安裝]
    2. [完成RK3568開發板的燒錄]
  3. 搭建開發環境。
    1. 開始前請參考[工具準備],完成DevEco Studio的安裝和開發環境配置。
    2. 開發環境配置完成后,請參考[使用工程向導]創建工程(模板選擇“Empty Ability”)。
    3. 工程創建完成后,選擇使用[真機進行調測]。

代碼結構解讀

本篇Codelab只對核心代碼進行講解,對于完整代碼,我們會在gitee中提供。

├──entry/src/main/js	              // 代碼區
│  └──MainAbility
│     ├──common
│     │  ├──bean
│     │  │  └──messageItem.js         // 多線程封裝消息
│     │  ├──constant
│     │  │  └──commonConstants.js     // 常量
│     │  ├──images                    // 圖片資源
│     │  └──utils
│     │     ├──adjustUtil.js          // 飽和度、亮度調節工具
│     │     ├──imageUtil.js           // 圖片獲取、打包工具
│     │     ├──logger.js              // 日志工具             
│     │     ├──opacityUtil.js         // 透明度調節工具
│     │     └──rotateUtil.js          // 旋轉工具
│     ├──i18n		                  // 國際化中英文
│     │  ├──en-US.json			
│     │  └──zh-CN.json		
│     ├──model
│     │  └──cropModel.js              // 裁剪數據處理
│     ├──pages
│     │  └──index
│     │     ├──index.css              // 首頁樣式文件	
│     │     ├──index.hml              // 首頁布局文件
│     │     └──index.js               // 首頁業務處理文件
│     ├──workers
│     │  ├──adjustBrightnessWork.js   // 亮度異步調節
│     │  └──adjustSaturationWork.js   // 飽和度異步調節
│     └──app.js                       // 程序入口
└──entry/src/main/resources           // 應用資源目錄

圖片解碼

本章節將介紹如何將圖片解碼,并顯示在canvas組件上。需要完成以下功能:

  1. 獲取圖片的PixelMap對象。
  2. 在canvas組件上繪制第一步獲取的PixelMap對象。

在index.js文件的onInit生命周期中初始化canvas畫布,具體有以下步驟:

  1. 調用imageUtil工具類的getImageFd方法,根據資源文件獲取圖片fd。
  2. 調用imageUtil工具類的getImagePixelMap方法,將獲取的fd創建成圖片實例,通過實例獲取其PixelMap。
  3. 通過canvas id獲取CanvasRenderingContext2D對象。
  4. 將獲取的PixelMap繪制到canvas組件上。
// index.js
export default {
  onInit() {
    ...
    this.initCanvas().then(() = > {
      this.postState = false;
    });
  },
  async initCanvas() {
    ...
    // 獲取圖片fd
    this.imageFd = await getImageFd(CommonConstants.IMAGE_NAME);
    // 獲取圖片pixelMap
    this.imagePixelMapAdjust = await getImagePixelMap(this.imageFd);
    ...
    // 獲取canvas對象
    const canvasOne = this.$element('canvasOne');
    this.canvasContext = canvasOne.getContext('2d');
    ...
    // 在canvas組件上繪圖
    this.drawToCanvas(this.imagePixelMapAdjust, this.drawImageLeft, this.drawImageTop,
      this.drawWidth, this.drawHeight);
  }
}

// imageUtil.js
export async function getImageFd(imageName) {
  let mResourceManager = await resourceManager.getResourceManager();
  let rawImageDescriptor = await mResourceManager.getRawFd(imageName);
  let fd = rawImageDescriptor?.fd;
  return fd;
}
export async function getImagePixelMap(fd) {
  let imageSource = image.createImageSource(fd);
  if (!imageSource) {
    return;
  }
  let pixelMap = await imageSource.createPixelMap({
    editable: true,
    desiredPixelFormat: CommonConstants.PIXEL_FORMAT
  });
  return pixelMap;
}

圖片裁剪

本篇Codelab提供四種裁剪比例,全圖裁剪、1:1裁剪、16:9裁剪、4:3裁剪。需要完成以下步驟實現裁剪功能:

  1. 根據裁剪比例,獲取canvas畫布上需要繪制的裁剪框寬高。
  2. 根據裁剪比例,獲取原圖需要裁剪的寬高。
  3. 根據第二步獲取的原圖需要裁剪的寬高,對圖片進行裁剪。
  4. 根據裁剪后原圖寬高,適配屏幕大小,重新繪制。

// index.js
export default {
  // 任意點擊四種裁剪比例
  cropClick(clickIndex) {
    this.cropClickIndex = clickIndex;
    switch (clickIndex) {
      // 全圖裁剪
      case CommonConstants.CropType.ORIGINAL:
        cropOriginal(this);
        break;
      // 1:1裁剪
      case CommonConstants.CropType.ONE_TO_ONE:
        cropSquareImage(this);
        break;
      // 16:9裁剪
      case CommonConstants.CropType.SIXTEEN_TO_NINE:
        cropRectangleImage(this);
        break;
      // 4:3裁剪
      case CommonConstants.CropType.FOUR_TO_THREE:
        cropBannerImage(this);
        break;
      default:
        break;
    }
    drawScreenSelection(this, this.canvasCropContext);
  }
}

以1:1裁剪為例,調用cropSquareImage方法,獲取原圖需要裁剪的寬高以及裁剪框的寬高。點擊切換編輯類型或保存,調用cropDrawImage方法裁剪圖片,最后適配屏幕重新繪制。

// cropModel.js
export function cropSquareImage(context) {
  ...
  let length = Math.min(context.originalImage.width, context.originalImage.height);
  // 原圖需要裁剪的寬高
  context.cropWidth = length;
  context.cropHeight = length;
  let drawLength = Math.min(context.drawWidth, context.drawHeight);
  // 裁剪框寬高
  context.cropDrawWidth = drawLength;
  context.cropDrawHeight = drawLength;
}
export async function cropDrawImage(context) {
  ...
  let imagePixel = context.imagePixelMapAdjust;
  let diffX = (context.originalImage.width - context.cropWidth) / CommonConstants.HALF;
  let diffY = (context.originalImage.height - context.cropHeight) / CommonConstants.HALF;
  context.cropLeft = Math.floor(diffX * accuracy) / accuracy;
  context.cropTop = Math.floor(diffY * accuracy) / accuracy;
  // 裁剪圖片
  await imagePixel.crop({ x: context.cropLeft, y: context.cropTop,
    size: {
      height: context.cropHeight,
      width: context.cropWidth
    }
  });
  // 裁剪后原圖寬高
  context.originalImage.width = context.cropWidth;
  context.originalImage.height = context.cropHeight;
  context.imagePixelMapAdjust = imagePixel;
}

// index.js
// 選擇裁剪框后,裁剪并適應屏幕顯示
async crop() {
  await cropDrawImage(this);
  // 適配屏幕
  this.adjustSize();
  this.canvasCropContext.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
  // 重新繪制
  this.drawToCanvas(this.imagePixelMapAdjust, this.drawImageLeft, this.drawImageTop, this.drawWidth,
    this.drawHeight);
  cropOriginal(this);
}

圖片旋轉

本篇Codelab提供逆時針旋轉、順時針旋轉兩種方式,每次旋轉角度為90度。在index.html文件中,使用兩個image組件實現逆時針旋轉、順時針旋轉按鈕。點擊對應圖片時,觸發onclick事件并回調onRotate方法。

< !-- index.html -- >
< div class="space-around-row adjust-width crop-height" >
    < !-- 逆時針旋轉 -- >
    < image src="http://www.qd573.com/images/chaijie_default.png" class="edit-image" onclick="onRotate(-90)" >< /image >
    < !-- 順時針旋轉 -- >
    < image src="http://www.qd573.com/images/chaijie_default.png" class="edit-image" onclick="onRotate(90)" >< /image >
< /div >

在index.js文件中,實現onRotate方法。根據方法入參angle,調用PixelMap接口提供的rotate方法,完成圖片旋轉功能。

// index.js
export default {
  // 點擊逆時針旋轉
  onRotate(angle) {
    let that = this;
    this.postState = true;
    rotate(this.imagePixelMapAdjust, angle, () = > {
      that.exchange();
    });
  }
}
// rotateUtil.js
export async function rotate(pixelMap, angle, callback) {
  if (!pixelMap) {
    return;
  }
  await pixelMap.rotate(angle);
  callback();
}

圖片色域調節

本篇Codelab的色域調節是使用色域模型RGB-HSV來實現的。

  • RGB:是我們接觸最多的顏色空間,分別為紅色(R)、綠色(G)和藍色(B)。
  • HSV:是用色相H,飽和度S,明亮度V來描述顏色的變化
    • H:色相H取值范圍為0°~360°,從紅色開始按逆時針方向計算,紅色為0°,綠色為120°,藍色為240°。
    • S:飽和度S越高,顏色則深而艷。光譜色的白光成分為0,飽和度達到最高。通常取值范圍為0%~100%,值越大,顏色越飽和。
    • V:明度V表示顏色明亮的程度,對于光源色,明度值與發光體的光亮度有關;對于物體色,此值和物體的透射比或反射比有關。通常取值范圍為0%(黑)到100%(白)。

亮度調節

完成以下步驟實現亮度調節:

  1. 將PixelMap轉換成ArrayBuffer。
  2. 將生成好的ArrayBuffer發送到worker線程。
  3. 對每一個像素點的亮度值按倍率計算。
  4. 將計算好的ArrayBuffer發送回主線程。
  5. 將ArrayBuffer寫入PixelMap,重新繪圖。

說明: 當前亮度調節是在UI層面實現的,未實現細節優化算法,只做簡單示例。調節后的圖片會有色彩上的失真。

// index.js
export default {
  // pixelMap轉換ArrayBuffer及發送ArrayBuffer到worker,
  postToWorker(type, value, workerName) {
    let sliderValue = type === CommonConstants.AdjustId.BRIGHTNESS ? this.brightnessValue : this.saturationValue;
    this.workerInstance = new worker.ThreadWorker(workerName);
    const bufferArray = new ArrayBuffer(this.imagePixelMapAdjust.getPixelBytesNumber());
    this.imagePixelMapAdjust.readPixelsToBuffer(bufferArray).then(() = > {
      let message = new MessageItem(bufferArray, sliderValue, value);
      this.workerInstance.postMessage(message);
      this.postState = true;
      // 收到worker線程完成的消息
      this.workerInstance.onmessage = this.updatePixelMap.bind(this);
      this.workerInstance.onexit = () = > {
        if (type === CommonConstants.AdjustId.BRIGHTNESS) {
          this.brightnessValue = Math.floor(value);
        } else {
          this.saturationValue = Math.floor(value);
        }
      }
    });
  }
}
// AdjustBrightnessWork.js
// worker線程處理部分
workerPort.onmessage = function (event) {
  let bufferArray = event.data.buffer;
  let lastValue = event.data.lastValue;
  let currentValue = event.data.currentValue;
  let buffer = adjustImageValue(bufferArray, lastValue, currentValue);
  workerPort.postMessage(buffer);
}
// adjustUtil.js
// 倍率計算部分
export function adjustImageValue(bufferArray, last, cur) {
  return execColorInfo(bufferArray, last, cur, CommonConstants.HSVIndex.VALUE);
}

透明度調節

PixelMap接口提供了圖片透明度調節的功能。拖動滑塊調節透明度,回調setOpacityValue方法,獲取需要調節的透明度,最后調用opacity方法完成透明度調節。

// index.js
export default {
  setOpacityValue(event) {
    let slidingOpacityValue = event.value;
    let slidingMode = event.mode;
    if (slidingMode === CommonConstants.SLIDER_MODE_END || slidingMode === CommonConstants.SLIDER_MODE_CLICK) {
      adjustOpacity(this.imagePixelMapAdjust, slidingOpacityValue).then(pixelMap = > {
        this.imagePixelMapAdjust = pixelMap;
        this.drawToCanvas(this.imagePixelMapAdjust, this.drawImageLeft, this.drawImageTop,
          this.drawWidth, this.drawHeight);
        this.opacityValue = Math.floor(slidingOpacityValue);
      });
    }
  }
}
// opacityUtil.js
export async function adjustOpacity(pixelMap, value) {
  if (!pixelMap) {
    return;
  }
  pixelMap.opacity(parseInt(value) / CommonConstants.SLIDER_MAX_VALUE).catch(err = > {
    Logger.error(`opacity err ${JSON.stringify(err)}`);
  });
  return pixelMap;
}

飽和度調節

飽和度調節與亮度調節步驟類似:

  1. 將PixelMap轉換成ArrayBuffer。
  2. 將生成好的ArrayBuffer發送到worker線程。
  3. 對每一個像素點的飽和度值按倍率計算。
  4. 將計算好的ArrayBuffer發送回主線程。
  5. 將ArrayBuffer寫入PixelMap,重新繪圖。

說明: 當前飽和度調節是在UI層面實現的,未實現細節優化算法,只做簡單示例。調節后的圖片會有色彩上的失真。

// index.js
export default {
  // pixelMap轉換ArrayBuffer及發送ArrayBuffer到worker,
  postToWorker(type, value, workerName) {
    let sliderValue = type === CommonConstants.AdjustId.BRIGHTNESS ? this.brightnessValue : this.saturationValue;
    this.workerInstance = new worker.ThreadWorker(workerName);
    const bufferArray = new ArrayBuffer(this.imagePixelMapAdjust.getPixelBytesNumber());
    this.imagePixelMapAdjust.readPixelsToBuffer(bufferArray).then(() = > {
      let message = new MessageItem(bufferArray, sliderValue, value);
      this.workerInstance.postMessage(message);
      this.postState = true;
      // 收到worker線程完成的消息
      this.workerInstance.onmessage = this.updatePixelMap.bind(this);
      this.workerInstance.onexit = () = > {
        if (type === CommonConstants.AdjustId.BRIGHTNESS) {
          this.brightnessValue = Math.floor(value);
        } else {
          this.saturationValue = Math.floor(value);
        }
      }
    });
  }
}
// adjustSaturationWork.js
// worker線程處理部分
workerPort.onmessage = function (event) {
  let bufferArray = event.data.buffer;
  let lastValue = event.data.lastValue;
  let currentValue = event.data.currentValue;
  let buffer = adjustSaturation(bufferArray, lastValue, currentValue)
  workerPort.postMessage(buffer);
}
// adjustUtil.js
// 倍率計算部分
export function adjustSaturation(bufferArray, last, cur) {
  return execColorInfo(bufferArray, last, cur, CommonConstants.HSVIndex.SATURATION);
}

審核編輯 黃宇

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Canvas
    +關注

    關注

    0

    文章

    15

    瀏覽量

    10952
  • 鴻蒙
    +關注

    關注

    55

    文章

    1760

    瀏覽量

    42153
  • HarmonyOS
    +關注

    關注

    79

    文章

    1876

    瀏覽量

    29331
  • OpenHarmony
    +關注

    關注

    23

    文章

    3375

    瀏覽量

    15192
收藏 人收藏

    評論

    相關推薦

    TL3588-視頻開發案

    TL3588-視頻開發案
    的頭像 發表于 01-24 16:29 ?412次閱讀
    TL3588-視頻<b class='flag-5'>開發案</b>例

    51單片機應用開發案例精選(代碼及圖片

    《51單片機應用開發案例精選》分為3部分。第1部分(第1章)講解單片機開發的預備知識,簡要介紹了單片機的開發流程、開發工具和最小系統;第2部分(第2章~第3章)講解單片機
    發表于 01-09 16:16

    HarmonyOS IoT 硬件開發案例分享

    ``許思維老師HiSpark Wi-Fi IoT 開發案例分享:案例一:AHT20溫濕度傳感器開發、調試;案例二:oled屏驅動庫移植,調試;案例三:用OLED屏播放視頻,Wi-Fi 和 TCP/IP 綜合應用。 ``
    發表于 10-27 17:30

    HarmonyOS HiSpark Wi-Fi IoT套件】HarmonyOS IoT 硬件開發案例分享

    HiSpark Wi-Fi IoT 開發案例分享:案例一:AHT20溫濕度傳感器開發、調試;案例二:oled屏驅動庫移植,調試;案例三:用OLED屏播放視頻,Wi-Fi 和 TCP/IP 綜合應用。
    發表于 10-27 19:12

    許思維老師HarmonyOS IoT 硬件開發案例分享

    許思維老師HiSpark Wi-Fi IoT 開發案例分享:案例一:AHT20溫濕度傳感器開發、調試;案例二:oled屏驅動庫移植,調試;案例三:用OLED屏播放視頻,Wi-Fi 和 TCP/IP 綜合應用。
    發表于 10-28 13:40

    【潤和直播課預告@華為開發者學院】HarmonyOS設備開發基礎課程|HiSpark WiFi-IoT 智能小車套件開發案

    `【潤和直播課預告@華為開發者學院】HarmonyOS設備開發基礎課程|HiSparkWiFi-IoT 智能小車套件開發案例,3月18日(周四) 19:00-21:00,讓你的
    發表于 03-16 15:01

    HarmonyOS教程—基于圖片處理能力,實現一個圖片編輯模板

    :界面UI和圖片編輯器。模板界面UI部分主要為開發者提供了:圖片編輯界面的設計參考,以及HarmonyO
    發表于 08-31 10:13

    51單片機應用開發案例精選-源代碼

    本內容提供了51單片機應用開發案例精選-源代碼及開發圖片
    發表于 08-10 09:40 ?535次下載
    51單片機應用<b class='flag-5'>開發案</b>例精選-源代碼

    許思維老師HarmonyOS IoT硬件開發案例分享

    許思維老師HiSpark Wi-Fi IoT 開發案例分享:案例一:AHT20溫濕度傳感器開發、調試;案例二:oled屏驅動庫移植,調試;案例三:用OLED屏播放視頻,Wi-Fi 和 TCP/IP 綜合應用。
    發表于 10-29 10:39 ?39次下載
    許思維老師<b class='flag-5'>HarmonyOS</b> IoT硬件<b class='flag-5'>開發案</b>例分享

    數碼播放器的開發案

    數碼播放器的開發案例說明。
    發表于 05-19 11:07 ?6次下載

    華為開發者分論壇HarmonyOS學生公開課-OpenHarmony Codelabs開發案

    2021華為開發者分論壇HarmonyOS學生公開課-OpenHarmony Codelabs開發案
    的頭像 發表于 10-24 11:25 ?1635次閱讀
    華為<b class='flag-5'>開發</b>者分論壇<b class='flag-5'>HarmonyOS</b>學生公開課-OpenHarmony Codelabs<b class='flag-5'>開發案</b>例

    OpenHarmony上實現圖片編輯功能

    圖片編輯是在應用中經常用到的功能,比如相機拍完照片后可以對照片進行編輯;截圖后可以對截圖進行編輯;可以對圖庫中的圖片進行
    的頭像 發表于 06-25 15:17 ?809次閱讀
    OpenHarmony上實現<b class='flag-5'>圖片</b><b class='flag-5'>編輯</b>功能

    RK3568---NPU開發案

    RK3568---NPU開發案
    的頭像 發表于 01-19 13:50 ?357次閱讀
    RK3568---NPU<b class='flag-5'>開發案</b>例

    HarmonyOS開發實例:【圖片編輯應用】

    通過動態設置元素樣式的方式,實現幾種常見的圖片操作,包括裁剪、旋轉、縮放和鏡像。
    的頭像 發表于 04-23 09:42 ?154次閱讀
    <b class='flag-5'>HarmonyOS</b><b class='flag-5'>開發</b>實例:【<b class='flag-5'>圖片</b><b class='flag-5'>編輯</b>應用】

    HarmonyOS開發案例:【圖片編輯

    基于ArkTS的聲明式開發范式的樣例,主要介紹了圖片編輯實現過程。
    的頭像 發表于 04-23 20:54 ?90次閱讀
    <b class='flag-5'>HarmonyOS</b><b class='flag-5'>開發案</b>例:【<b class='flag-5'>圖片</b><b class='flag-5'>編輯</b>】
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>