[文章]基于HarmonyOS图像编解码,实现图片的旋转、剪裁、缩放、镜像

阅读量0
0
5
1. 介绍      
HarmonyOS图像模块支持图像业务的开发,常见功能如图像解码、图像编码、基本的位图操作、图像编辑等。当然,也支持通过接口组合来实现更复杂的图像处理逻辑。本教程以图库图片中旋转、剪裁、缩放、镜像四种常见操作为例,给大家介绍HarmonyOS图像编解码的相关开发指导。

2. 搭建HarmonyOS环境   
我们首先需要完成HarmonyOS开发环境搭建,可参照如下步骤进行。
  • 安装DevEco Studio,详情请参考下载和安装软件。
  • 设置DevEco Studio开发环境,DevEco Studio开发环境依赖于网络环境,需要连接上网络才能确保工具的正常使用,可以根据如下两种情况来配置开发环境:
    • 如果可以直接访问Internet,只需进行下载HarmonyOS SDK操作。
    • 如果网络不能直接访问Internet,需要通过代理服务器才可以访问,请参考配置开发环境。
  • 开发者可以参考以下链接,完成设备调试的相关配置:
    • 使用真机进行调试
    • 使用模拟器进行调试
3. 将图片转换为PixelMap      
图像解码就是将所支持格式的存档图片解码成统一的PixelMap图像,用于后续图像显示或其他处理,比如旋转、缩放、剪裁等。当前支持格式包括JPEG、PNG、GIF、HEIF、WebP、BMP。本例为您提供了getPixelMapFromResource函数,可以将resources/base/media目录下的图片资源转换为PixelMap图像,其中入参为图片的资源ID,
  1. private PixelMap getPixelMapFromResource(int resourceId) {
  2.     InputStream inputStream = null;
  3.     try {
  4.         // 创建图像数据源ImageSource对象
  5.         inputStream = getContext().getResourceManager().getResource(resourceId);
  6.         ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
  7.         srcOpts.formatHint = "image/jpg";
  8.         ImageSource imageSource = ImageSource.create(inputStream, srcOpts);


  9.         // 设置图片参数
  10.         ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
  11.         return imageSource.createPixelmap(decodingOptions);
  12.     } catch (IOException e) {
  13.         HiLog.info(LABEL_LOG, "IOException");
  14.     } catch (NotExistException e) {
  15.         HiLog.info(LABEL_LOG, "NotExistException");
  16.     } finally {
  17.         if (inputStream != null) {
  18.             try {
  19.                 inputStream.close();
  20.             } catch (IOException e) {
  21.                 HiLog.info(LABEL_LOG, "inputStream IOException");
  22.             }
  23.         }
  24.     }
  25.     return null;
  26. }
复制代码
4. 图片参数设置
本例使用图片像素的尺寸为1024*768,点击一次旋转按钮会进行90度的旋转,缩放是按照2:1的比例进行缩放,剪裁是保证宽度不变的情况下对高度进行400像素的剪裁,相关参数设置如下所示:
  1. // 设置图片参数
  2. ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
  3. // 旋转
  4. decodingOptions.rotateDegrees = 90 * whirlCount;
  5. // 缩放
  6. decodingOptions.desiredSize = new Size(isScale ? 512 : 0, isScale ? 384 : 0);
  7. // 剪裁
  8. decodingOptions.desiredRegion = new Rect(0, 0, isCorp ? 1024 : 0, isCorp ? 400 : 0);
复制代码
5. 图片镜像操作
图片镜像操作就是对图片以纵坐标为轴制作对称图片。image绘制的时候会调用onDraw方法,本例采用对图像Canvas画布的镜像操作实现图片的镜像显示,示例代码如下所示:
  1. private void mirrorImage(PixelMap pixelMap) {
  2.     scaleX = -scaleX;
  3.     image.addDrawTask(
  4.             new Component.DrawTask() {
  5.                 [url=home.php?mod=space&uid=2735960]@Override[/url]
  6.                 public void onDraw(Component component, Canvas canvas) {
  7.                     if (isMirror) {
  8.                         isMirror = false;
  9.                         PixelMapHolder pmh = new PixelMapHolder(pixelMap);
  10.                         canvas.scale(
  11.                                 scaleX,
  12.                                 1.0f,
  13.                                 (float) pixelMap.getImageInfo().size.width / 2,
  14.                                 (float) pixelMap.getImageInfo().size.height / 2);
  15.                         canvas.drawPixelMapHolder(
  16.                                 pmh,
  17.                                 0,
  18.                                 0,
  19.                                 new Paint());
  20.                     }
  21.                 }
  22.             });
  23. }
复制代码
6. 完整示例   
以手机为例,初始化页面如图1所示,依次点击按钮可以实现图片的旋转、剪裁、缩放、镜像,效果如下所示(您需要准备一张像素尺寸为1024*768的图片,放到ImageDemoentrysrcmainresourcesbasemedia目录下):
示例代码如下:
  1. import com.huawei.codelab.ResourceTable;

  2. import ohos.aafwk.ability.AbilitySlice;
  3. import ohos.aafwk.content.Intent;
  4. import ohos.agp.components.Button;
  5. import ohos.agp.components.Component;
  6. import ohos.agp.components.Image;
  7. import ohos.agp.render.Canvas;
  8. import ohos.agp.render.Paint;
  9. import ohos.agp.render.PixelMapHolder;
  10. import ohos.global.resource.NotExistException;
  11. import ohos.hiviewdfx.HiLog;
  12. import ohos.hiviewdfx.HiLogLabel;
  13. import ohos.media.image.ImageSource;
  14. import ohos.media.image.PixelMap;
  15. import ohos.media.image.common.PixelFormat;
  16. import ohos.media.image.common.Rect;
  17. import ohos.media.image.common.Size;

  18. import java.io.IOException;
  19. import java.io.InputStream;

  20. /**
  21. * 图像主页面
  22. */
  23. public class MainAbilitySlice extends AbilitySlice {
  24.     private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "MainAbilitySlice");
  25.     Image image;
  26.     PixelMap imagePixelMap;
  27.     Button whirlImageBtn;
  28.     Button cropImageBtn;
  29.     Button scaleImageBtn;
  30.     Button mirrorImageBtn;
  31.     private int whirlCount = 0;
  32.     private boolean isCorp = false;
  33.     private boolean isScale = false;
  34.     private boolean isMirror = false;
  35.     private float scaleX = 1.0f;

  36.     @Override
  37.     public void onStart(Intent intent) {
  38.         super.onStart(intent);
  39.         super.setUIContent(ResourceTable.Layout_ability_main);
  40.         initView();
  41.     }

  42.     private void initView() {
  43.         if (findComponentById(ResourceTable.Id_whirl_image) instanceof Button) {
  44.             whirlImageBtn = (Button) findComponentById(ResourceTable.Id_whirl_image);
  45.         }
  46.         if (findComponentById(ResourceTable.Id_crop_image) instanceof Button) {
  47.             cropImageBtn = (Button) findComponentById(ResourceTable.Id_crop_image);
  48.         }
  49.         if (findComponentById(ResourceTable.Id_scale_image) instanceof Button) {
  50.             scaleImageBtn = (Button) findComponentById(ResourceTable.Id_scale_image);
  51.         }
  52.         if (findComponentById(ResourceTable.Id_mirror_image) instanceof Button) {
  53.             mirrorImageBtn = (Button) findComponentById(ResourceTable.Id_mirror_image);
  54.         }
  55.         if (findComponentById(ResourceTable.Id_image) instanceof Image) {
  56.             image = (Image) findComponentById(ResourceTable.Id_image);
  57.         }
  58.         whirlImageBtn.setClickedListener(new ButtonClick());
  59.         cropImageBtn.setClickedListener(new ButtonClick());
  60.         scaleImageBtn.setClickedListener(new ButtonClick());
  61.         mirrorImageBtn.setClickedListener(new ButtonClick());
  62.     }

  63.     private class ButtonClick implements Component.ClickedListener {
  64.         @Override
  65.         public void onClick(Component component) {
  66.             int btnId = component.getId();
  67.             switch (btnId) {
  68.                 case ResourceTable.Id_whirl_image:
  69.                     // 旋转图片
  70.                     whirlCount++;
  71.                     isCorp = false;
  72.                     isScale = false;
  73.                     isMirror = false;
  74.                     imagePixelMap = getPixelMapFromResource(ResourceTable.Media_shanghai);
  75.                     image.setPixelMap(imagePixelMap);
  76.                     break;
  77.                 case ResourceTable.Id_crop_image:
  78.                     // 剪裁图片
  79.                     whirlCount = 0;
  80.                     isCorp = !isCorp;
  81.                     isScale = false;
  82.                     isMirror = false;
  83.                     imagePixelMap = getPixelMapFromResource(ResourceTable.Media_shanghai);
  84.                     image.setPixelMap(imagePixelMap);
  85.                     break;
  86.                 case ResourceTable.Id_scale_image:
  87.                     // 缩放图片
  88.                     whirlCount = 0;
  89.                     isCorp = false;
  90.                     isScale = !isScale;
  91.                     isMirror = false;
  92.                     imagePixelMap = getPixelMapFromResource(ResourceTable.Media_shanghai);
  93.                     image.setPixelMap(imagePixelMap);
  94.                     break;
  95.                 case ResourceTable.Id_mirror_image:
  96.                     // 镜像图片
  97.                     whirlCount = 0;
  98.                     isCorp = false;
  99.                     isScale = false;
  100.                     isMirror = true;
  101.                     imagePixelMap = getPixelMapFromResource(ResourceTable.Media_shanghai);
  102.                     mirrorImage(imagePixelMap);
  103.                     image.setPixelMap(imagePixelMap);
  104.                     break;
  105.                 default:
  106.                     break;
  107.             }
  108.         }
  109.     }

  110.     private void mirrorImage(PixelMap pixelMap) {
  111.         scaleX = -scaleX;
  112.         image.addDrawTask(
  113.                 new Component.DrawTask() {
  114.                     @Override
  115.                     public void onDraw(Component component, Canvas canvas) {
  116.                         if (isMirror) {
  117.                             isMirror = false;
  118.                             PixelMapHolder pmh = new PixelMapHolder(pixelMap);
  119.                             canvas.scale(
  120.                                     scaleX,
  121.                                     1.0f,
  122.                                     (float) pixelMap.getImageInfo().size.width / 2,
  123.                                     (float) pixelMap.getImageInfo().size.height / 2);
  124.                             canvas.drawPixelMapHolder(
  125.                                     pmh,
  126.                                     0,
  127.                                     0,
  128.                                     new Paint());
  129.                         }
  130.                     }
  131.                 });
  132.     }

  133.     /**
  134.      * 通过图片ID返回PixelMap
  135.      *
  136.      * [url=home.php?mod=space&uid=3142012]@param[/url] resourceId 图片的资源ID
  137.      * [url=home.php?mod=space&uid=1141835]@Return[/url] 图片的PixelMap
  138.      */
  139.     private PixelMap getPixelMapFromResource(int resourceId) {
  140.         InputStream inputStream = null;
  141.         try {
  142.             // 创建图像数据源ImageSource对象
  143.             inputStream = getContext().getResourceManager().getResource(resourceId);
  144.             ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
  145.             srcOpts.formatHint = "image/jpg";
  146.             ImageSource imageSource = ImageSource.create(inputStream, srcOpts);

  147.             // 设置图片参数
  148.             ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
  149.             // 旋转
  150.             decodingOptions.rotateDegrees = 90 * whirlCount;
  151.             // 缩放
  152.             decodingOptions.desiredSize = new Size(isScale ? 512 : 0, isScale ? 384 : 0);
  153.             // 剪裁
  154.             decodingOptions.desiredRegion = new Rect(0, 0, isCorp ? 1024 : 0, isCorp ? 400 : 0);
  155.             decodingOptions.desiredPixelFormat = PixelFormat.ARGB_8888;
  156.             return imageSource.createPixelmap(decodingOptions);
  157.         } catch (IOException e) {
  158.             HiLog.info(LABEL_LOG, "IOException");
  159.         } catch (NotExistException e) {
  160.             HiLog.info(LABEL_LOG, "NotExistException");
  161.         } finally {
  162.             if (inputStream != null) {
  163.                 try {
  164.                     inputStream.close();
  165.                 } catch (IOException e) {
  166.                     HiLog.info(LABEL_LOG, "inputStream IOException");
  167.                 }
  168.             }
  169.         }
  170.         return null;
  171.     }

  172.     @Override
  173.     public void onActive() {
  174.         super.onActive();
  175.     }

  176.     @Override
  177.     public void onForeground(Intent intent) {
  178.         super.onForeground(intent);
  179.     }
  180. }
复制代码
布局代码如下:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <DirectionalLayout
  3.     xmlns:ohos="http://schemas.huawei.com/res/ohos"
  4.     ohos:height="match_parent"
  5.     ohos:width="match_parent"
  6.     ohos:orientation="vertical">

  7.     <Text
  8.         ohos:height="match_content"
  9.         ohos:width="match_content"
  10.         ohos:layout_alignment="horizontal_center"
  11.         ohos:text="HarmonyOS图像开发"
  12.         ohos:text_size="80"
  13.         ohos:top_margin="40vp"
  14.         />

  15.     <DirectionalLayout
  16.         xmlns:ohos="http://schemas.huawei.com/res/ohos"
  17.         ohos:height="match_content"
  18.         ohos:width="match_content"
  19.         ohos:layout_alignment="horizontal_center"
  20.         ohos:orientation="horizontal"
  21.         ohos:top_margin="20vp">

  22.         <Button
  23.             ohos:id="$+id:whirl_image"
  24.             ohos:height="match_content"
  25.             ohos:width="match_content"
  26.             ohos:background_element="$graphic:background_button"
  27.             ohos:padding="12vp"
  28.             ohos:right_margin="5vp"
  29.             ohos:text="旋转"
  30.             ohos:text_size="20vp"
  31.             ohos:top_margin="10vp">
  32.         </Button>

  33.         <Button
  34.             ohos:id="$+id:crop_image"
  35.             ohos:height="match_content"
  36.             ohos:width="match_content"
  37.             ohos:background_element="$graphic:background_button"
  38.             ohos:left_margin="5vp"
  39.             ohos:padding="12vp"
  40.             ohos:text="剪裁"
  41.             ohos:text_size="20vp"
  42.             ohos:top_margin="10vp">
  43.         </Button>

  44.         <Button
  45.             ohos:id="$+id:scale_image"
  46.             ohos:height="match_content"
  47.             ohos:width="match_content"
  48.             ohos:background_element="$graphic:background_button"
  49.             ohos:left_margin="5vp"
  50.             ohos:padding="12vp"
  51.             ohos:text="缩放"
  52.             ohos:text_size="20vp"
  53.             ohos:top_margin="10vp">
  54.         </Button>

  55.         <Button
  56.             ohos:id="$+id:mirror_image"
  57.             ohos:height="match_content"
  58.             ohos:width="match_content"
  59.             ohos:background_element="$graphic:background_button"
  60.             ohos:left_margin="5vp"
  61.             ohos:padding="12vp"
  62.             ohos:text="镜像"
  63.             ohos:text_size="20vp"
  64.             ohos:top_margin="10vp"/>
  65.     </DirectionalLayout>

  66.     <Image
  67.         ohos:id="$+id:image"
  68.         ohos:height="match_content"
  69.         ohos:width="match_content"
  70.         ohos:image_src="$media:shanghai.jpg"
  71.         ohos:layout_alignment="horizontal_center"
  72.         ohos:top_margin="20vp">
  73.     </Image>

  74. </DirectionalLayout>
复制代码
此外您还需在resource/base/graphic目录下添加background_button.xml
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
  3.        ohos:shape="rectangle">
  4.     <corners
  5.         ohos:radius="40"/>
  6.     <solid
  7.         ohos:color="#e9e9e9"/>
  8. </shape>
复制代码
说明
以上代码仅demo演示参考使用,产品化的代码需要考虑数据校验和国际化。
7. 恭喜你
通过对本教程的学习,你已经学会HarmonyOS图像编解码的基本操作。


回帖

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
链接复制成功,分享给好友