基于图像模块实现图库图片的四种常见操作开发分享 精华
1. 项目介绍
HarmonyOS图像模块支持图像业务的开发,常见功能如图像解码、图像编码、基本的位图操作、图像编辑等。当然,也支持通过接口组合来实现更复杂的图像处理逻辑。本教程以图库图片中旋转、剪裁、缩放、镜像四种常见操作为例,给大家介绍HarmonyOS图像编解码的相关开发指导。
2. 将图片转换为PixelMap
图像解码就是将所支持格式的存档图片解码成统一的PixelMap图像,用于后续图像显示或其他处理,比如旋转、缩放、剪裁等。当前支持格式包括JPEG、PNG、GIF、HEIF、WebP、BMP。本例为您提供了getPixelMapFromResource函数,可以将resources/base/media目录下的图片资源转换为PixelMap图像,其中入参为图片的资源ID。
private PixelMap getPixelMapFromResource(int resourceId) {
InputStream inputStream = null;
try {
// 创建图像数据源ImageSource对象
inputStream = getContext().getResourceManager().getResource(resourceId);
ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
srcOpts.formatHint = "image/jpg";
ImageSource imageSource = ImageSource.create(inputStream, srcOpts);
// 设置图片参数
ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
return imageSource.createPixelmap(decodingOptions);
} catch (IOException e) {
HiLog.info(LABEL_LOG, "IOException");
} catch (NotExistException e) {
HiLog.info(LABEL_LOG, "NotExistException");
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
HiLog.info(LABEL_LOG, "inputStream IOException");
}
}
}
return null;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
3. 图片参数设置
本例使用图片像素的尺寸为1024*768,点击一次旋转按钮会进行90度的旋转,缩放是按照2:1的比例进行缩放,剪裁是保证宽度不变的情况下对高度进行400像素的剪裁,相关参数设置如下所示:
// 设置图片参数
ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
// 旋转
decodingOptions.rotateDegrees = 90 * whirlCount;
// 缩放
decodingOptions.desiredSize = new Size(isScale ? 512 : 0, isScale ? 384 : 0);
// 剪裁
decodingOptions.desiredRegion = new Rect(0, 0, isCorp ? 1024 : 0, isCorp ? 400 : 0);
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
4. 图片镜像操作
图片镜像操作就是对图片以纵坐标为轴制作对称图片。image绘制的时候会调用onDraw方法,本例采用对图像Canvas画布的镜像操作实现图片的镜像显示,示例代码如下所示:
private void mirrorImage(PixelMap pixelMap) {
scaleX = -scaleX;
image.addDrawTask(
new Component.DrawTask() {
@Override
public void onDraw(Component component, Canvas canvas) {
if (isMirror) {
isMirror = false;
PixelMapHolder pmh = new PixelMapHolder(pixelMap);
canvas.scale(
scaleX,
1.0f,
(float) pixelMap.getImageInfo().size.width / 2,
(float) pixelMap.getImageInfo().size.height / 2);
canvas.drawPixelMapHolder(
pmh,
0,
0,
new Paint());
}
}
});
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
5. 完整示例
以手机为例,初始化页面如图1所示,依次点击按钮可以实现图片的旋转、剪裁、缩放、镜像,效果如下所示(您需要准备一张像素尺寸为1024*768的图片,放到ImageDemo\entry\src\main\resources\base\media目录下):
示例代码如下:
import com.huawei.codelab.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.Image;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapHolder;
import ohos.global.resource.NotExistException;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Rect;
import ohos.media.image.common.Size;
import java.io.IOException;
import java.io.InputStream;
/**
* 图像主页面
*/
public class MainAbilitySlice extends AbilitySlice {
private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD001100, "MainAbilitySlice");
Image image;
PixelMap imagePixelMap;
Button whirlImageBtn;
Button cropImageBtn;
Button scaleImageBtn;
Button mirrorImageBtn;
private int whirlCount = 0;
private boolean isCorp = false;
private boolean isScale = false;
private boolean isMirror = false;
private float scaleX = 1.0f;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
initView();
}
private void initView() {
if (findComponentById(ResourceTable.Id_whirl_image) instanceof Button) {
whirlImageBtn = (Button) findComponentById(ResourceTable.Id_whirl_image);
}
if (findComponentById(ResourceTable.Id_crop_image) instanceof Button) {
cropImageBtn = (Button) findComponentById(ResourceTable.Id_crop_image);
}
if (findComponentById(ResourceTable.Id_scale_image) instanceof Button) {
scaleImageBtn = (Button) findComponentById(ResourceTable.Id_scale_image);
}
if (findComponentById(ResourceTable.Id_mirror_image) instanceof Button) {
mirrorImageBtn = (Button) findComponentById(ResourceTable.Id_mirror_image);
}
if (findComponentById(ResourceTable.Id_image) instanceof Image) {
image = (Image) findComponentById(ResourceTable.Id_image);
}
whirlImageBtn.setClickedListener(new ButtonClick());
cropImageBtn.setClickedListener(new ButtonClick());
scaleImageBtn.setClickedListener(new ButtonClick());
mirrorImageBtn.setClickedListener(new ButtonClick());
}
private class ButtonClick implements Component.ClickedListener {
@Override
public void onClick(Component component) {
int btnId = component.getId();
switch (btnId) {
case ResourceTable.Id_whirl_image:
// 旋转图片
whirlCount++;
isCorp = false;
isScale = false;
isMirror = false;
imagePixelMap = getPixelMapFromResource(ResourceTable.Media_shanghai);
image.setPixelMap(imagePixelMap);
break;
case ResourceTable.Id_crop_image:
// 剪裁图片
whirlCount = 0;
isCorp = !isCorp;
isScale = false;
isMirror = false;
imagePixelMap = getPixelMapFromResource(ResourceTable.Media_shanghai);
image.setPixelMap(imagePixelMap);
break;
case ResourceTable.Id_scale_image:
// 缩放图片
whirlCount = 0;
isCorp = false;
isScale = !isScale;
isMirror = false;
imagePixelMap = getPixelMapFromResource(ResourceTable.Media_shanghai);
image.setPixelMap(imagePixelMap);
break;
case ResourceTable.Id_mirror_image:
// 镜像图片
whirlCount = 0;
isCorp = false;
isScale = false;
isMirror = true;
imagePixelMap = getPixelMapFromResource(ResourceTable.Media_shanghai);
mirrorImage(imagePixelMap);
image.setPixelMap(imagePixelMap);
break;
default:
break;
}
}
}
private void mirrorImage(PixelMap pixelMap) {
scaleX = -scaleX;
image.addDrawTask(
new Component.DrawTask() {
@Override
public void onDraw(Component component, Canvas canvas) {
if (isMirror) {
isMirror = false;
PixelMapHolder pmh = new PixelMapHolder(pixelMap);
canvas.scale(
scaleX,
1.0f,
(float) pixelMap.getImageInfo().size.width / 2,
(float) pixelMap.getImageInfo().size.height / 2);
canvas.drawPixelMapHolder(
pmh,
0,
0,
new Paint());
}
}
});
}
/**
* 通过图片ID返回PixelMap
*
* @param resourceId 图片的资源ID
* @return 图片的PixelMap
*/
private PixelMap getPixelMapFromResource(int resourceId) {
InputStream inputStream = null;
try {
// 创建图像数据源ImageSource对象
inputStream = getContext().getResourceManager().getResource(resourceId);
ImageSource.SourceOptions srcOpts = new ImageSource.SourceOptions();
srcOpts.formatHint = "image/jpg";
ImageSource imageSource = ImageSource.create(inputStream, srcOpts);
// 设置图片参数
ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
// 旋转
decodingOptions.rotateDegrees = 90 * whirlCount;
// 缩放
decodingOptions.desiredSize = new Size(isScale ? 512 : 0, isScale ? 384 : 0);
// 剪裁
decodingOptions.desiredRegion = new Rect(0, 0, isCorp ? 1024 : 0, isCorp ? 400 : 0);
decodingOptions.desiredPixelFormat = PixelFormat.ARGB_8888;
return imageSource.createPixelmap(decodingOptions);
} catch (IOException e) {
HiLog.info(LABEL_LOG, "IOException");
} catch (NotExistException e) {
HiLog.info(LABEL_LOG, "NotExistException");
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
HiLog.info(LABEL_LOG, "inputStream IOException");
}
}
}
return null;
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
布局代码如下:
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical">
<Text
ohos:height="match_content"
ohos:width="match_content"
ohos:layout_alignment="horizontal_center"
ohos:text="HarmonyOS图像开发"
ohos:text_size="80"
ohos:top_margin="40vp"
/>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_content"
ohos:width="match_content"
ohos:layout_alignment="horizontal_center"
ohos:orientation="horizontal"
ohos:top_margin="20vp">
<Button
ohos:id="$+id:whirl_image"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_button"
ohos:padding="12vp"
ohos:right_margin="5vp"
ohos:text="旋转"
ohos:text_size="20vp"
ohos:top_margin="10vp">
</Button>
<Button
ohos:id="$+id:crop_image"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_button"
ohos:left_margin="5vp"
ohos:padding="12vp"
ohos:text="剪裁"
ohos:text_size="20vp"
ohos:top_margin="10vp">
</Button>
<Button
ohos:id="$+id:scale_image"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_button"
ohos:left_margin="5vp"
ohos:padding="12vp"
ohos:text="缩放"
ohos:text_size="20vp"
ohos:top_margin="10vp">
</Button>
<Button
ohos:id="$+id:mirror_image"
ohos:height="match_content"
ohos:width="match_content"
ohos:background_element="$graphic:background_button"
ohos:left_margin="5vp"
ohos:padding="12vp"
ohos:text="镜像"
ohos:text_size="20vp"
ohos:top_margin="10vp"/>
</DirectionalLayout>
<Image
ohos:id="$+id:image"
ohos:height="match_content"
ohos:width="match_content"
ohos:image_src="$media:shanghai.jpg"
ohos:layout_alignment="horizontal_center"
ohos:top_margin="20vp">
</Image>
</DirectionalLayout>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
此外您还需在resource/base/graphic目录下添加background_button.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:shape="rectangle">
<corners
ohos:radius="40"/>
<solid
ohos:color="#e9e9e9"/>
</shape>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
说明
以上代码仅demo演示参考使用,产品化的代码需要考虑数据校验和国际化。
@文章转载自HUAWEI Codelabs
微信扫码分享
👍👍👍
您好,博主
我一直有个疑问,类似android的相册列表中的缩略图该如何压缩?直接使用图片自带的缩略图,图片内容会有马赛克的感觉,不想系统相册那样会比较清晰。根据图片真实长宽和列表中image组件长宽计算出一个合适的缩放值inSampleSize,并设置给decodingOptions.sampleSize。结果,压缩出来的图片大小居然没变化。
然后根据inSampleSize,将原图宽高同比缩小并设置给decodingOptions.desiredSize,发现压缩成功了,不过还是有马赛克的感觉。
这块我该如何压缩,才能保证清晰,而且压缩后大小不会很大,可以存储在LruCache中进行缓存呢?