android - JPG图片转换HDR图片,heic格式

minSdk 28 //以前是24,heifwriter:heifwriter:1.1.0-alpha01 要求最新28
<!--读写文件-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
android:requestLegacyExternalStorage="true"
//HDR图片(heic)
implementation "androidx.heifwriter:heifwriter:1.1.0-alpha01"
//图片选择
implementation 'io.github.lucksiege:pictureselector:v2.7.3-rc08'
package com.example.androidkotlindemo2.hdr;
import android.Manifest;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.heifwriter.HeifWriter;
import com.bumptech.glide.Glide;
import com.example.androidkotlindemo2.R;
import com.luck.picture.lib.PictureSelector;
import com.luck.picture.lib.config.PictureConfig;
import com.luck.picture.lib.config.PictureMimeType;
import com.luck.picture.lib.entity.LocalMedia;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* 处理HDR(heic)图片
* 参考:https://developer.android.com/reference/androidx/heifwriter/HeifWriter
*/
public class HDRMainActivity extends AppCompatActivity {
private ImageView mImg1;
private TextView mTv1;
private Button mBtn1;
private ImageView mImg2;
private TextView mTv2;
private Button mBtn2;
private LocalMedia imgUrl = null;
private Context context = this;
private ImageView mImg3;
private TextView mTv3;
private Button mBtn3;
private String destination1;
private static final int PERMISSION_REQUEST_CODE = 100;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.hdr_main);
mImg1 = findViewById(R.id.img1);
mTv1 = findViewById(R.id.tv1);
mBtn1 = findViewById(R.id.btn1);
mImg2 = findViewById(R.id.img2);
mTv2 = findViewById(R.id.tv2);
mBtn2 = findViewById(R.id.btn2);
mImg3 = findViewById(R.id.img3);
mTv3 = findViewById(R.id.tv3);
mBtn3 = findViewById(R.id.btn3);
findViewById(R.id.btn11).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 检查并申请权限
checkAndRequestPermissions();
}
});
mBtn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//默认有权限
checkPic();
}
});
mBtn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
jpg2Heif();
}
});
mBtn3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
heif2Jpg();
}
});
}
private void checkAndRequestPermissions() {
// 需要申请的权限列表
String[] permissions = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
List<String> permissionsToRequest = new ArrayList<>();
// 检查每个权限是否已授予
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(this, permission)
!= PackageManager.PERMISSION_GRANTED) {
permissionsToRequest.add(permission);
}
}
// 如果有权限需要申请
if (!permissionsToRequest.isEmpty()) {
ActivityCompat.requestPermissions(this,
permissionsToRequest.toArray(new String[0]),
PERMISSION_REQUEST_CODE);
} else {
// 所有权限都已授予
onPermissionsGranted();
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) {
boolean allGranted = true;
// 检查所有权限是否都被授予
for (int grantResult : grantResults) {
if (grantResult != PackageManager.PERMISSION_GRANTED) {
allGranted = false;
break;
}
}
if (allGranted) {
onPermissionsGranted();
} else {
onPermissionsDenied();
}
}
}
private void onPermissionsGranted() {
// 权限已授予,执行相关操作
Toast.makeText(this, "存储权限已授予", Toast.LENGTH_SHORT).show();
// 这里可以调用需要权限的方法
//accessExternalStorage();
}
private void onPermissionsDenied() {
// 处理权限被拒绝的情况
if (shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE) ||
shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// 用户拒绝了权限,但没有选择"不再询问"
showPermissionRationaleDialog();
} else {
// 用户拒绝了权限并选择了"不再询问"
showGoToSettingsDialog();
}
}
private void showPermissionRationaleDialog() {
new AlertDialog.Builder(this)
.setTitle("需要存储权限")
.setMessage("应用需要存储权限来读取和保存文件")
.setPositiveButton("确定", (dialog, which) -> {
// 再次请求权限
checkAndRequestPermissions();
})
.setNegativeButton("取消", null)
.show();
}
private void showGoToSettingsDialog() {
new AlertDialog.Builder(this)
.setTitle("权限被永久拒绝")
.setMessage("您已永久拒绝存储权限,请在设置中手动授予权限")
.setPositiveButton("去设置", (dialog, which) -> {
// 跳转到应用设置页面
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
})
.setNegativeButton("取消", null)
.show();
}
private void heif2Jpg() {
// Using HEIFWriter from Google
// https://developer.android.com/reference/androidx/heifwriter/HeifWriter
/* val heif = HEIF()
// Step 1: Load file
heif.load("HEIC.heic")
// Step 2: Check type of HEIF format, cause it have many types: Still Image, Grid Image ... (Apple is using GridImage type for that format)
when (heif.primaryImage) {
is GridImageItem -> {}
is IdentityImageItem -> {}
is OverlayImageItem -> {}
is HEVCImageItem -> {}
is AVCImageItem -> {}
}
// Step 3: In-case this is GridImageItem
// Get size width, height
val originalWidth = primaryImage.size.width
val originalHeight = primaryImage.size.height
// Getting original rotation degree of Original Image file
val rotationDegree =
(heif.itemProperties.findLast { it is RotateProperty } as? RotateProperty)?.rotation?.value
?: 0
// Apple is using 48 tiles and join to 1 images, parse of its and then convert its to HEVC bitstream by FFMPEG
for (rowIndex in 0 until primaryImage.rowCount) {
for (columnIndex in 0 until primaryImage.columnCount) {
// Getting the tile image based column / row
val hevcImageItem = primaryImage.getImage(columnIndex, rowIndex) as HEVCImageItem
// Getting decoder config and then using mobile-ffmpeg to write to HEVC bitstream in local storage.
....
}
// After getting 48 tiles as bitstream files, join its and merge into 1 files by ffmpeg.
....
// Loading Color profiles and attach it.
....
// Convert BitStream to JPG.JPEG.PNG file by FFMPEG
....
// Loading Exifdata and attach it.
....
// Delete temp files and finish convert.
}*/
}
private void jpg2Heif() {
Bitmap bitmap;
bitmap = BitmapFactory.decodeFile(imgUrl.getRealPath());
Log.e("cxx", "获取bitmap成功");
int imageHeight = bitmap.getHeight();
int imageWidth = bitmap.getWidth();
Log.e("cxx", "获取宽高");
// destination1 = getExternalFilesDir("/").getAbsolutePath() + "/photo2.heic";
Random random = new Random();
int fileName2 = Integer.valueOf(random.nextInt(Integer.MAX_VALUE));
String destinations = Environment.getExternalStorageDirectory() + "/Pictures/"+fileName2+".heic";
Log.e("cxx", "生成路径:" + destinations);
try {
HeifWriter heifWriter = new HeifWriter.Builder(destinations, imageWidth, imageHeight, HeifWriter.INPUT_MODE_BITMAP).setQuality(90).build();
heifWriter.start();
heifWriter.addBitmap(bitmap);
heifWriter.stop(0);
heifWriter.close();
Log.e("cxx", "生成完毕:");
long mbSize = getFileSize(destinations);
mTv2.setText(destinations+"--大小:"+mbSize);
Glide.with(context).load(destinations).into(mImg2);
Log.e("cxx", "加载完毕");
File f = new File(destinations);
// 通知图库更新
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri uri = Uri.fromFile(f);
intent.setData(uri);
context.sendBroadcast(intent);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
private void checkPic() {
//maxSelectNum()选择图片的数量
PictureSelector.create(this)
.openGallery(PictureMimeType.ofImage())
.imageEngine(GlideEngine.createGlideEngine())
.maxSelectNum(1)
.forResult(PictureConfig.CHOOSE_REQUEST);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case PictureConfig.CHOOSE_REQUEST: {
List<LocalMedia> result = PictureSelector.obtainMultipleResult(data);
if (result.size() <= 0) {
return;
}
imgUrl = result.get(0);
long mbSize = getFileSize(imgUrl.getRealPath());
mTv1.setText(imgUrl.getFileName() + "--大小:" + mbSize+"mb");
Glide.with(this).load(result.get(0).getPath()).into(mImg1);
}
}
}
}
private long getFileSize(String realPath) {
File file = new File(realPath);
long size = 0;
long mbSize = 0;
FileInputStream fis = null;
try {
fis = new FileInputStream(file);
size = fis.available();
mbSize = size/1024/1024;
} catch (IOException e) {
e.printStackTrace();
}
return mbSize;
}
}
package com.example.androidkotlindemo2.hdr;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
public class ImageLoaderUtils {
public static boolean assertValidRequest(Context context) {
if (context instanceof Activity) {
Activity activity = (Activity) context;
return !isDestroy(activity);
} else if (context instanceof ContextWrapper){
ContextWrapper contextWrapper = (ContextWrapper) context;
if (contextWrapper.getBaseContext() instanceof Activity){
Activity activity = (Activity) contextWrapper.getBaseContext();
return !isDestroy(activity);
}
}
return true;
}
private static boolean isDestroy(Activity activity) {
if (activity == null) {
return true;
}
return activity.isFinishing() || activity.isDestroyed();
}
}
package com.example.androidkotlindemo2.hdr;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.BitmapImageViewTarget;
import com.bumptech.glide.request.target.ImageViewTarget;
import com.example.androidkotlindemo2.R;
import com.luck.picture.lib.engine.ImageEngine;
import com.luck.picture.lib.listener.OnImageCompleteCallback;
import com.luck.picture.lib.tools.MediaUtils;
import com.luck.picture.lib.widget.longimage.ImageSource;
import com.luck.picture.lib.widget.longimage.ImageViewState;
import com.luck.picture.lib.widget.longimage.SubsamplingScaleImageView;
public class GlideEngine implements ImageEngine {
/**
* 加载图片
*
* @param context
* @param url
* @param imageView
*/
@Override
public void loadImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) {
if (!ImageLoaderUtils.assertValidRequest(context)) {
return;
}
Glide.with(context)
.load(url)
.into(imageView);
}
/**
* 加载网络图片适配长图方案
* # 注意:此方法只有加载网络图片才会回调
*
* @param context
* @param url
* @param imageView
* @param longImageView
* @param callback 网络图片加载回调监听 {link after version 2.5.1 Please use the #OnImageCompleteCallback#}
*/
@Override
public void loadImage(@NonNull Context context, @NonNull String url,
@NonNull ImageView imageView,
SubsamplingScaleImageView longImageView, OnImageCompleteCallback callback) {
if (!ImageLoaderUtils.assertValidRequest(context)) {
return;
}
Glide.with(context)
.asBitmap()
.load(url)
.into(new ImageViewTarget<Bitmap>(imageView) {
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
super.onLoadStarted(placeholder);
if (callback != null) {
callback.onShowLoading();
}
}
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
super.onLoadFailed(errorDrawable);
if (callback != null) {
callback.onHideLoading();
}
}
@Override
protected void setResource(@Nullable Bitmap resource) {
if (callback != null) {
callback.onHideLoading();
}
if (resource != null) {
boolean eqLongImage = MediaUtils.isLongImg(resource.getWidth(),
resource.getHeight());
longImageView.setVisibility(eqLongImage ? View.VISIBLE : View.GONE);
imageView.setVisibility(eqLongImage ? View.GONE : View.VISIBLE);
if (eqLongImage) {
// 加载长图
longImageView.setQuickScaleEnabled(true);
longImageView.setZoomEnabled(true);
longImageView.setDoubleTapZoomDuration(100);
longImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP);
longImageView.setDoubleTapZoomDpi(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER);
longImageView.setImage(ImageSource.cachedBitmap(resource),
new ImageViewState(0, new PointF(0, 0), 0));
} else {
// 普通图片
imageView.setImageBitmap(resource);
}
}
}
});
}
/**
* 加载网络图片适配长图方案
* # 注意:此方法只有加载网络图片才会回调
*
* @param context
* @param url
* @param imageView
* @param longImageView
* @ 已废弃
*/
@Override
public void loadImage(@NonNull Context context, @NonNull String url,
@NonNull ImageView imageView,
SubsamplingScaleImageView longImageView) {
if (!ImageLoaderUtils.assertValidRequest(context)) {
return;
}
Glide.with(context)
.asBitmap()
.load(url)
.into(new ImageViewTarget<Bitmap>(imageView) {
@Override
protected void setResource(@Nullable Bitmap resource) {
if (resource != null) {
boolean eqLongImage = MediaUtils.isLongImg(resource.getWidth(),
resource.getHeight());
longImageView.setVisibility(eqLongImage ? View.VISIBLE : View.GONE);
imageView.setVisibility(eqLongImage ? View.GONE : View.VISIBLE);
if (eqLongImage) {
// 加载长图
longImageView.setQuickScaleEnabled(true);
longImageView.setZoomEnabled(true);
longImageView.setDoubleTapZoomDuration(100);
longImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP);
longImageView.setDoubleTapZoomDpi(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER);
longImageView.setImage(ImageSource.cachedBitmap(resource),
new ImageViewState(0, new PointF(0, 0), 0));
} else {
// 普通图片
imageView.setImageBitmap(resource);
}
}
}
});
}
/**
* 加载相册目录
*
* @param context 上下文
* @param url 图片路径
* @param imageView 承载图片ImageView
*/
@Override
public void loadFolderImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) {
if (!ImageLoaderUtils.assertValidRequest(context)) {
return;
}
Glide.with(context)
.asBitmap()
.load(url)
.override(180, 180)
.centerCrop()
.sizeMultiplier(0.5f)
.placeholder(R.drawable.ic_launcher_background)
.into(new BitmapImageViewTarget(imageView) {
@Override
protected void setResource(Bitmap resource) {
RoundedBitmapDrawable circularBitmapDrawable =
RoundedBitmapDrawableFactory.
create(context.getResources(), resource);
circularBitmapDrawable.setCornerRadius(8);
imageView.setImageDrawable(circularBitmapDrawable);
}
});
}
/**
* 加载gif
*
* @param context 上下文
* @param url 图片路径
* @param imageView 承载图片ImageView
*/
@Override
public void loadAsGifImage(@NonNull Context context, @NonNull String url,
@NonNull ImageView imageView) {
if (!ImageLoaderUtils.assertValidRequest(context)) {
return;
}
Glide.with(context)
.asGif()
.load(url)
.into(imageView);
}
/**
* 加载图片列表图片
*
* @param context 上下文
* @param url 图片路径
* @param imageView 承载图片ImageView
*/
@Override
public void loadGridImage(@NonNull Context context, @NonNull String url, @NonNull ImageView imageView) {
if (!ImageLoaderUtils.assertValidRequest(context)) {
return;
}
Glide.with(context)
.load(url)
.override(200, 200)
.centerCrop()
.placeholder(R.drawable.ic_launcher_background)
.into(imageView);
}
private GlideEngine() {
}
private static GlideEngine instance;
public static GlideEngine createGlideEngine() {
if (null == instance) {
synchronized (GlideEngine.class) {
if (null == instance) {
instance = new GlideEngine();
}
}
}
return instance;
}
}
hdr_main.xml布局
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_gravity="center_horizontal"
android:id="@+id/img1"
android:layout_marginTop="20dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="名字:无"
android:layout_marginTop="20dp"
android:id="@+id/tv1"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选择照片"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
android:id="@+id/btn1"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选择照片- 先授权"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
android:textColor="@color/red"
android:textSize="22sp"
android:id="@+id/btn11"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_gravity="center_horizontal"
android:id="@+id/img2"
android:layout_marginTop="20dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="名字:无"
android:layout_marginTop="20dp"
android:id="@+id/tv2"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="转换格式成heif - heic - HDR图片 "
android:textAllCaps="false"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
android:id="@+id/btn2"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_gravity="center_horizontal"
android:id="@+id/img3"
android:layout_marginTop="20dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="名字:无"
android:layout_marginTop="20dp"
android:id="@+id/tv3"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="heif转jpg"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
android:id="@+id/btn3"/>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>