<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-23 20:54 ? 次閱讀

介紹

是基于ArkTS的聲明式開發范式的樣例,主要介紹了圖片編輯實現過程。樣例主要包含以下功能:

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

相關概念

  • [圖片解碼]:讀取不同格式的圖片文件,無壓縮的解碼為位圖格式。
  • [PixelMap]:圖片解碼后的狀態,用于對圖片像素進行處理。
  • [圖片編碼]:圖片經過像素處理完成之后,需要重新進行編碼打包,生成需要的圖片格式。

環境搭建

軟件要求

  • [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. 工程創建完成后,選擇使用[真機進行調測]。
    4. HarmonyOS與OpenHarmony鴻蒙文檔籽料:mau123789是v直接拿

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

代碼結構解讀

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

├──entry/src/main/ets                            // 代碼區
│  ├──common                         
│  │  └──constant
│  │     └──CommonConstant.ets                   // 常量類
│  ├──entryability
│  │  └──EntryAbility.ts                         // 本地啟動ability           
│  ├──pages
│  │  └──HomePage.ets                            // 本地主頁面    
│  ├──utils
│  │  ├──AdjustUtil.ets                          // 調節工具類
│  │  ├──CropUtil.ets                            // 裁剪工具類
│  │  ├──DecodeUtil.ets                          // 解碼工具類
│  │  ├──DrawingUtils.ets                        // Canvas畫圖工具類
│  │  ├──EncodeUtil.ets                          // 編碼工具類
│  │  ├──LoggerUtil.ets                          // 日志工具類
│  │  ├──MathUtils.ets                           // 坐標轉換工具類
│  │  └──OpacityUtil.ets                         // 透明度調節工具類
│  ├──view
│  │  ├──AdjustContentView.ets                   // 色域調整視圖     
│  │  └──ImageSelect.ets                         // Canvas選擇框實現類   
│  ├──viewmodel
│  │  ├──CropShow.ets                            // 選擇框顯示控制類
│  │  ├──CropType.ets                            // 按比例選取圖片
│  │  ├──IconListViewModel.ets                   // icon數據
│  │  ├──ImageEditCrop.ets                       // 圖片編輯操作類
│  │  ├──ImageFilterCrop.ets                     // 圖片操作收集類
│  │  ├──ImageSizeItem.ets                       // 圖片尺寸
│  │  ├──Line.ets                                // 線封裝類
│  │  ├──MessageItem.ets                         // 多線程封裝消息
│  │  ├──OptionViewModel.ets                     // 圖片處理封裝類
│  │  ├──PixelMapWrapper.ets                     // PixelMap封裝類
│  │  ├──Point.ets                               // 點封裝類
│  │  ├──Ratio.ets                               // 比例封裝類
│  │  ├──Rect.ets                                // 矩形封裝類
│  │  ├──RegionItem.ets                          // 區域封裝類
│  │  └──ScreenManager.ts                        // 屏幕尺寸計算工具類
│  └──workers
│     ├──AdjustBrightnessWork.ts                 // 亮度異步調節
│     └──AdjustSaturationWork.ts                 // 飽和度異步調節
└──entry/src/main/resources                      // 資源文件目錄

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

圖片解碼

在這個章節中,需要完成圖片解碼的操作,并將解碼后的圖片展示。效果如圖所示:

在進行圖片編輯前需要先加載圖片,當前文檔是在生命周期aboutToAppear開始加載。具體實現步驟。

  1. 讀取資源文件。
  2. 將獲取的fd創建成圖片實例,通過實例獲取其pixelMap。
  3. 將解析好的pixelMap通過Image組件加載顯示。
// HomePage.ets
aboutToAppear() {
  this.pixelInit();
  ...
}

build() {
  Column() {
    ...
    Column() {
      if (this.isCrop && this.showCanvas && this.statusBar > 0) {
        if (this.isSaveFresh) {
          ImageSelect({
            statusBar: this.statusBar
          })
        }
        ...
      } else {
        if (this.isPixelMapChange) {
          Image(this.pixelMap)
            .scale({ x: this.imageScale, y: this.imageScale, z: 1 })
            .objectFit(ImageFit.None)
        }
        ...
      }
    }
    ...
  }
  ...
}

async getResourceFd(filename: string) {
  const resourceMgr = getContext(this).resourceManager;
  const context = getContext(this);
  if (filename === CommonConstants.RAW_FILE_NAME) {
    let imageBuffer = await resourceMgr.getMediaContent($r("app.media.ic_low"))
    let filePath = context.cacheDir + '/' + filename;
    let file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
    let writeLen = fs.writeSync(file.fd, imageBuffer.buffer);
    fs.copyFileSync(filePath, context.cacheDir + '/' + CommonConstants.RAW_FILE_NAME_TEST);
    return file.fd;
  } else {
    let filePath = context.cacheDir + '/' + filename;
    let file = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
    return file.fd;
  }
}

async getPixelMap(fileName: string) {
  const fd = await this.getResourceFd(fileName);
  const imageSourceApi = image.createImageSource(fd);
  if (!imageSourceApi) {
    Logger.error(TAG, 'imageSourceAPI created failed!');
    return;
  }
  const pixelMap = await imageSourceApi.createPixelMap({
    editable: true
  });
  return pixelMap;
}

圖片處理

當前章節需要完成圖片的裁剪、旋轉、色域調節(本章只介紹亮度、透明度、飽和度)等功能。

  • 裁剪:選取圖片中的部分進行裁剪生成新的圖片。
  • 旋轉:將圖片按照不同的角度進行旋轉,生成新的圖片。
  • 色域調節:當前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%(白)。
// AdjustUtil.ets
// rgb轉hsv
function rgb2hsv(red: number, green: number, blue: 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 * CommonConstants.CONVERT_INT;
  if (maxValue === 0) {
    hsvS = 0;
  } else {
    hsvS = Number((1 - minValue / maxValue).toFixed(CommonConstants.DECIMAL_TWO)) * CommonConstants.CONVERT_INT;
  }
  if (maxValue === minValue) {
    hsvH = 0;
  }
  if (maxValue === rgbR && rgbG >= rgbB) {
    hsvH = Math.floor(CommonConstants.ANGLE_60 * ((rgbG - rgbB) / (maxValue - minValue)));
  }
  if (maxValue === rgbR && rgbG < rgbB) {
    hsvH = Math.floor(CommonConstants.ANGLE_60 * ((rgbG - rgbB) / (maxValue - minValue)) + CommonConstants.ANGLE_360);
  }
  if (maxValue === rgbG) {
    hsvH = Math.floor(CommonConstants.ANGLE_60 * ((rgbB - rgbR) / (maxValue - minValue)) + CommonConstants.ANGLE_120);
  }
  if (maxValue === rgbB) {
    hsvH = Math.floor(CommonConstants.ANGLE_60 * ((rgbR - rgbG) / (maxValue - minValue)) + CommonConstants.ANGLE_240);
  }
  return [hsvH, hsvS, hsvV];
}
// hsv轉rgb
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 * CommonConstants.COLOR_LEVEL_MAX) / CommonConstants.CONVERT_INT);
    return { rgbR, rgbG, rgbB };
  }
  const cxmC = (value * saturation) / (CommonConstants.CONVERT_INT * CommonConstants.CONVERT_INT);
  const cxmX = cxmC * (1 - Math.abs((hue / CommonConstants.ANGLE_60) % CommonConstants.MOD_2 - 1));
  const cxmM = (value - cxmC * CommonConstants.CONVERT_INT) / CommonConstants.CONVERT_INT;
  const hsvHRange = Math.floor(hue / CommonConstants.ANGLE_60);
  switch (hsvHRange) {
    case AngelRange.ANGEL_0_60:
      rgbR = (cxmC + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      rgbG = (cxmX + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      rgbB = (0 + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      break;
    case AngelRange.ANGEL_60_120:
      rgbR = (cxmX + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      rgbG = (cxmC + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      rgbB = (0 + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      break;
    case AngelRange.ANGEL_120_180:
      rgbR = (0 + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      rgbG = (cxmC + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      rgbB = (cxmX + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      break;
    case AngelRange.ANGEL_180_240:
      rgbR = (0 + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      rgbG = (cxmX + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      rgbB = (cxmC + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      break;
    case AngelRange.ANGEL_240_300:
      rgbR = (cxmX + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      rgbG = (0 + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      rgbB = (cxmC + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      break;
    case AngelRange.ANGEL_300_360:
      rgbR = (cxmC + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      rgbG = (0 + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      rgbB = (cxmX + cxmM) * CommonConstants.COLOR_LEVEL_MAX;
      break;
    default:
      break;
  }
  return [
    Math.round(rgbR),
    Math.round(rgbG),
    Math.round(rgbB)
  ];
}

圖片裁剪

  1. 通過pixelMap獲取圖片尺寸,為后續裁剪做準備。
  2. 確定裁剪的方式,當前裁剪默認有自由選取、1:1選取、4:3選取、16:9選取。
  3. 通過pixelMap調用接口crop()進行裁剪操作。

http://www.qd573.com/images/chaijie_default.png說明: 當前裁剪功能采用pixelMap裁剪能力直接做切割,會有疊加效果,后續會通過增加選取框對當前功能進行優化。

// HomePage.ets
cropImage(index: CropType) {
  this.currentCropIndex = index;
  switch (this.currentCropIndex) {
    case CropType.ORIGINAL_IMAGE:
      this.cropRatio = CropRatioType.RATIO_TYPE_FREE;
      break;
    case CropType.SQUARE:
      this.cropRatio = CropRatioType.RATIO_TYPE_1_1;
      break;
    case CropType.BANNER:
      this.cropRatio = CropRatioType.RATIO_TYPE_4_3;
      break;
    case CropType.RECTANGLE:
      this.cropRatio = CropRatioType.RATIO_TYPE_16_9;
      break;
    default:
      this.cropRatio = CropRatioType.RATIO_TYPE_FREE;
      break;
  }
}

// ImageFilterCrop.ets
cropImage(pixelMap: PixelMapWrapper, realCropRect: RectF, callback: () = > void) {
  let offWidth = realCropRect.getWidth();
  let offHeight = realCropRect.getHeight();
  if (pixelMap.pixelMap!== undefined) {
    pixelMap.pixelMap.crop({
      size:{ height: vp2px(offHeight), width: vp2px(offWidth) },
      x: vp2px(realCropRect.left),
      y: vp2px(realCropRect.top)
    }, callback);
  }
}

圖片旋轉

  1. 確定旋轉方向,當前支持順時針和逆時針旋轉。
  2. 通過pixelMap調用接口rotate()進行旋轉操作。

// HomePage.ets
rotateImage(rotateType: RotateType) {
  if (rotateType === RotateType.CLOCKWISE) {
    try {
      if (this.pixelMap !== undefined) {
        this.pixelMap.rotate(CommonConstants.CLOCK_WISE)
          .then(() = > {
            this.flushPixelMapNew();
          })
      }
    } catch (error) {
      Logger.error(TAG, `there is a error in rotate process with ${error?.code}`);
    }
  }
  if (rotateType === RotateType.ANTI_CLOCK) {
    try {
      if (this.pixelMap !== undefined) {
        this.pixelMap.rotate(CommonConstants.ANTI_CLOCK)
          .then(() = > {
            this.flushPixelMapNew();
          })
      }
    } catch (error) {
      Logger.error(TAG, `there is a error in rotate process with ${error?.code}`);
    }
  }
}

亮度調節

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

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

// AdjustContentView.ets
// 轉化成pixelMap及發送buffer到worker,返回數據刷新ui
postToWorker(type: AdjustId, value: number, workerName: string) {
  let sliderValue = type === AdjustId.BRIGHTNESS ? this.brightnessLastSlider : this.saturationLastSlider;
  try {
    let workerInstance = new worker.ThreadWorker(workerName);
    const bufferArray = new ArrayBuffer(this.pixelMap.getPixelBytesNumber());
    this.pixelMap.readPixelsToBuffer(bufferArray).then(() = > {
      let message = new MessageItem(bufferArray, sliderValue, value);
      workerInstance.postMessage(message);
      if (this.postState) {
        this.deviceListDialogController.open();
      }
      this.postState = false;
      workerInstance.onmessage = (event: MessageEvents) = > {
        this.updatePixelMap(event)
      };
      if (type === AdjustId.BRIGHTNESS) {
        this.brightnessLastSlider = Math.round(value);
      } else {
        this.saturationLastSlider = Math.round(value);
      }
      workerInstance.onexit = () = > {
        if (workerInstance !== undefined) {
          workerInstance.terminate();
        }
      }
    });
  } catch (error) {
    Logger.error(`Create work instance fail, error message: ${JSON.stringify(error)}`)
  }
}

// AdjustBrightnessWork.ts
// worker線程處理部分
workerPort.onmessage = function(event : MessageEvents) {
  let bufferArray = event.data.buf;
  let last = event.data.last;
  let cur = event.data.cur;
  let buffer = adjustImageValue(bufferArray, last, cur);
  workerPort.postMessage(buffer);
  workerPort.close();
}

// AdjustUtil.ets
// 倍率計算部分
export function adjustImageValue(bufferArray: ArrayBuffer, last: number, cur: number) {
  return execColorInfo(bufferArray, last, cur, HSVIndex.VALUE);
}

透明度調節

  1. 獲取pixelMap。
  2. 調用接口opacity()進行透明度調節。

// OpacityUtil.ets
export async function adjustOpacity(pixelMap: PixelMap, value: number) {
  if (!pixelMap) {
    return;
  }
  const newPixelMap = pixelMap;
  await newPixelMap.opacity(value / CommonConstants.SLIDER_MAX);
  return newPixelMap;
}

飽和度調節

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

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

// AdjustContentView.ets
// 轉化成pixelMap及發送buffer到worker,返回數據刷新ui
postToWorker(type: AdjustId, value: number, workerName: string) {
  let sliderValue = type === AdjustId.BRIGHTNESS ? this.brightnessLastSlider : this.saturationLastSlider;
  try {
    let workerInstance = new worker.ThreadWorker(workerName);
    const bufferArray = new ArrayBuffer(this.pixelMap.getPixelBytesNumber());
    this.pixelMap.readPixelsToBuffer(bufferArray).then(() = > {
      let message = new MessageItem(bufferArray, sliderValue, value);
      workerInstance.postMessage(message);
      if (this.postState) {
        this.deviceListDialogController.open();
      }
      this.postState = false;
      workerInstance.onmessage = (event: MessageEvents) = > {
        this.updatePixelMap(event)
      };
      if (type === AdjustId.BRIGHTNESS) {
        this.brightnessLastSlider = Math.round(value);
      } else {
        this.saturationLastSlider = Math.round(value);
      }
      workerInstance.onexit = () = > {
        if (workerInstance !== undefined) {
          workerInstance.terminate();
        }
      }
    });
  } catch (error) {
    Logger.error(`Create work instance fail, error message: ${JSON.stringify(error)}`);
  }
}

// AdjustSaturationWork.ts
// worker線程處理部分
workerPort.onmessage = function(event : MessageEvents) {
  let bufferArray = event.data.buf;
  let last = event.data.last;
  let cur = event.data.cur;
  let buffer = adjustSaturation(bufferArray, last, cur)
  workerPort.postMessage(buffer);
  workerPort.close();
}

// AdjustUtil.ets
// 倍率計算部分
export function adjustSaturation(bufferArray: ArrayBuffer, last: number, cur: number) {
  return execColorInfo(bufferArray, last, cur, HSVIndex.SATURATION);
}

圖片編碼

圖片位圖經過處理之后,還是屬于解碼的狀態,還需要進行打包編碼成對應的格式,本章講解編碼的具體過程。

  1. 通過image組件創建打包工具packer。
  2. 使用PackingOption進行打包參數設定,比如格式、壓縮質量等。
  3. 打包成圖片信息數據imageData。
  4. 創建媒體庫media,獲取公共路徑。
  5. 創建媒體文件asset,獲取其fd。
  6. 使用fs將打包好的圖片數據寫入到媒體文件asset中。
// ImageSelect.ets
async encode(pixelMap: PixelMap | undefined) {
  if (pixelMap === undefined) {
    return;
  }

  const newPixelMap = pixelMap;
  // 打包圖片
  const imagePackerApi = image.createImagePacker();
  const packOptions: image.PackingOption = {
    format: CommonConstants.ENCODE_FORMAT,
    quality: CommonConstants.ENCODE_QUALITY
  }
  const imageData = await imagePackerApi.packing(newPixelMap, packOptions);
  Logger.info(TAG, `imageData's length is ${imageData.byteLength}`);
  // 獲取相冊路徑
  const context = getContext(this);
  const media = mediaLibrary.getMediaLibrary(context);
  const publicPath = await media.getPublicDirectory(mediaLibrary.DirectoryType.DIR_IMAGE);
  const currentTime = new Date().getTime();
  // 創建圖片資源
  const imageAssetInfo = await media.createAsset(
    mediaLibrary.MediaType.IMAGE,
    `${CommonConstants.IMAGE_PREFIX}_${currentTime}${CommonConstants.IMAGE_FORMAT}`,
    publicPath
  );
  const imageFd = await imageAssetInfo.open(CommonConstants.ENCODE_FILE_PERMISSION);
  await fs.write(imageFd, imageData);
  // 釋放資源
  await imageAssetInfo.close(imageFd);
  imagePackerApi.release();
  await media.release();
}

審核編輯 黃宇

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

    關注

    55

    文章

    1919

    瀏覽量

    42197
  • HarmonyOS
    +關注

    關注

    79

    文章

    1912

    瀏覽量

    29455
  • OpenHarmony
    +關注

    關注

    24

    文章

    3442

    瀏覽量

    15293
收藏 人收藏

    評論

    相關推薦

    TL3588-視頻開發案

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

    3568F-視頻開發案

    3568F-視頻開發案
    的頭像 發表于 04-12 13:51 ?582次閱讀
    3568F-視頻<b class='flag-5'>開發案</b>例

    HarmonyOS開發案例:【圖片編輯

    基于canvas組件、圖片編解碼,介紹了圖片編輯實現過程。
    的頭像 發表于 04-22 16:42 ?495次閱讀
    <b class='flag-5'>HarmonyOS</b><b class='flag-5'>開發案</b>例:【<b class='flag-5'>圖片</b><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設備開發基礎課程|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 ?1657次閱讀
    華為<b class='flag-5'>開發</b>者分論壇<b class='flag-5'>HarmonyOS</b>學生公開課-OpenHarmony Codelabs<b class='flag-5'>開發案</b>例

    OpenHarmony上實現圖片編輯功能

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

    RK3568---NPU開發案

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

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

    通過動態設置元素樣式的方式,實現幾種常見的圖片操作,包括裁剪、旋轉、縮放和鏡像。
    的頭像 發表于 04-23 09:42 ?194次閱讀
    <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>