最近做个小游戏需要调用手机图库和摄像头并裁减图片,发现网上很多教程不是太全,有的安卓端裁减不了大图,下面是完整代码,可以裁减高清图的。
完整拼图游戏下载:
https://github.com/sky068/Puzzles
1、首先是跨平台入口代码,iOS平台需要把ImageCrop.cpp改成ImageCrop.mm,并且在AppController.mm里调用setViewController(void* viewController)把rootView传进来初始化m_viewController
#ifndef ImageCrop_hpp
#define ImageCrop_hpp
#include <stdio.h>
#include "cocos2d.h"
USING_NS_CC;
#define kImageCropEvent "ImageCropEvent"
class ImageCrop {
public:
ImageCrop();
static ImageCrop* getInstance();
static void destroyInstance();
void openCamera(std::function<void(std::string)> callback);
void openPhoto(std::function<void(std::string)> callback);
#if ( CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
//设置AppController iOS平台在AppController.mm里调用把rootViewController传递过来
void setViewController(void* viewController);
void *m_viewController;
#endif
private:
static ImageCrop* _instance;
std::function<void(std::string)> _callback;
};
#endif
//
// ImageCrop.cpp
// PhotoCrop
//
// Created by xujw on 15/12/28.
//
//
#include "ImageCrop.hpp"
#if ( CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
#import "ImagePickerViewController.h"
#import "RootViewController.h"
#endif
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "platform/android/jni/JniHelper.h"
#include <jni.h>
#define JAVA_CLASS "org/cocos2dx/cpp/ImageCrop"
#define JAVA_FUNC_OPEN_PHOTO "openPhoto"
#define JAVA_FUNC_OPEN_CAMERA "openCamera"
#endif
ImageCrop::ImageCrop()
:_callback(nullptr)
{
Director::getInstance()->getEventDispatcher()->addCustomEventListener(kImageCropEvent,[=](EventCustom *event)
{
std::string *imgPath = (std::string*)event->getUserData();
_callback(*imgPath);
});
}
ImageCrop* ImageCrop::_instance = nullptr;
ImageCrop* ImageCrop::getInstance()
{
if (!_instance)
{
_instance = new (std::nothrow) ImageCrop();
}
return _instance;
}
void ImageCrop::destroyInstance()
{
CC_SAFE_DELETE(_instance);
}
void ImageCrop::openCamera(std::function<void (std::string)> callback)
{
_callback = callback;
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
ImagePickerViewController * imagePickerViewController = [[ImagePickerViewController alloc]initWithNibName:nil bundle:nil];
RootViewController *_viewController = (RootViewController*)m_viewController;
[_viewController.view addSubview:imagePickerViewController.view];
[imagePickerViewController takePhoto];
#endif
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
JniMethodInfo info;
bool ret = JniHelper::getStaticMethodInfo(info,JAVA_CLASS,JAVA_FUNC_OPEN_CAMERA,"()V");
if (ret)
{
info.env->CallStaticVoidMethod(info.classID,info.methodID);
}
#endif
}
void ImageCrop::openPhoto(std::function<void (std::string)> callback)
{
_callback = callback;
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
ImagePickerViewController * imagePickerViewController = [[ImagePickerViewController alloc]initWithNibName:nil bundle:nil];
RootViewController *_viewController = (RootViewController*)m_viewController;
[_viewController.view addSubview:imagePickerViewController.view];
[imagePickerViewController localPhoto];
#endif
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
JniMethodInfo info;
bool ret = JniHelper::getStaticMethodInfo(info,JAVA_FUNC_OPEN_PHOTO,info.methodID);
}
#endif
}
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
void ImageCrop::setViewController(void *viewController)
{
m_viewController = viewController;
}
#endif
//--------Java回调C++--------native 方法
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
extern "C"
{
void Java_org_cocos2dx_cpp_ImageCrop_onImageSaved(JNIEnv *env,jobject thiz,jstring path)
{
std::string strPath = JniHelper::jstring2string(path);
//必须这样,否则由于线程问题会显示黑块无法正常创建精灵
Director::getInstance()->getScheduler()->performFunctionInCocosThread([=,strPath]()mutable{
Director::getInstance()->getEventDispatcher()->dispatchCustomEvent(kImageCropEvent,&strPath);
});
}
}
#endif
#ifndef ImagePickerViewController_h
#define ImagePickerViewController_h
#import <UIKit/UIKit.h>
@interface ImagePickerViewController:UIViewController<UINavigationControllerDelegate,UIImagePickerControllerDelegate>
{
NSString *filePath;
}
//打开本地相册
-(void)localPhoto;
//打开相机
-(void)takePhoto;
@end
#endif
#import <Foundation/Foundation.h>
#import "ImagePickerViewController.h"
@interface ImagePickerViewController ()
@end
@implementation ImagePickerViewController
-(void)viewDidLoad
{
[super viewDidLoad];
}
-(void)viewDidUnload
{
[super viewDidUnload];
}
-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
-(void)localPhoto
{
NSLog(@"-(void)localPhoto");
UIImagePickerController *picker = [[UIImagePickerController alloc]init];
picker.delegate = self;
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
picker.allowsEditing = YES;
[self presentViewController:picker animated:YES completion:^(void)
{
NSLog(@"ImageViewController is presented");
}];
[picker release];
}
-(void)takePhoto
{
UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera;
if ([UIImagePickerController isSourceTypeAvailable:sourceType])
{
UIImagePickerController *picker = [[UIImagePickerController alloc]init];
picker.delegate = self;
//设置拍照后可以编辑
picker.allowsEditing = YES;
picker.sourceType = sourceType;
[self presentViewController:picker animated:YES completion:^(void)
{
NSLog(@"ImageViewController is presented");
}];
}
else
{
NSLog(@"模拟器无法使用相机,请在真机中调试");
}
NSLog(@"-(void)takePhoto");
}
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
NSString *type = [info objectForKey:@"UIImagePickerControllerMediaType"];
if ([type isEqualToString:@"public.image"])
{
//先把图片转成NSData
//原始图
// UIImage *image = [info objectForKey:@"UIImagePickerControllerOriginalImage"];
UIImage *image = [info objectForKey:@"UIImagePickerControllerEditedImage"];
NSData *data;
if (UIImagePNGRepresentation(image) == nil)
{
data = UIImageJPEGRepresentation(image,1.0);
}
else
{
data = UIImagePNGRepresentation(image);
}
//图片保存路径
//这里将图片放在沙盒的documents/image文件夹中
NSString *documentsPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
NSString *imgPath = [documentsPath stringByAppendingPathComponent:@"image"];
//文件管理器
NSFileManager *fileManager = [NSFileManager defaultManager];
//生成唯一字符串
NSString *uuid = [[NSUUID UUID]UUIDString];
//生成文件名
NSString *fileName = [NSString stringWithFormat:@"%@.png",uuid];
//把刚刚由图片转成的data对象拷贝至沙盒中 并保存为xxxxx-xxxx-xxx...xxx.png
/******保存之前最好先清空下,不然占用磁盘越来越大********/
[fileManager removeItemAtPath:imgPath error:nil];
/*************************************************/
[fileManager createDirectoryAtPath:imgPath withIntermediateDirectories:YES attributes:nil error:nil];
[fileManager createFileAtPath:[imgPath stringByAppendingPathComponent:fileName] contents:data attributes:nil];
//得到选择后沙盒中图片的完整路径
filePath = [[NSString alloc]initWithFormat:@"%@",[imgPath stringByAppendingPathComponent:fileName]];
//关闭相册界面
[picker dismissViewControllerAnimated:YES completion:^(void){}];
//通知ImageCrop完成取图
std::string strFilePath = [filePath UTF8String];
cocos2d::Director::getInstance()->getEventDispatcher()->dispatchCustomEvent("ImageCropEvent",&strFilePath);
}
}
-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
NSLog(@"您取消了照片选择");
[picker dismissViewControllerAnimated:YES completion:^(void){}];
}
@end
3、安卓端,安卓平台需要把ImageCrop.java导入工程包里(src下org.cocos2dx.cpp包里),在主activity(AppActivity.java)的onCreate里调用ImageCrop.getInstance().initImageCrop(this)初始化,然后重载onActivityResult并回调ImageCrop的onActivityResult方法
ImageCrop.java:
package org.cocos2dx.cpp;
import java.io.File;
import java.io.FileNotFoundException;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
public class ImageCrop {
public static final int NONE = 0;
public static final int PHOTOHRAPH = 1;
public static final int PHOTOZOOM = 2;
public static final int PHOTORESOULT = 3;
public static final String IMAGE_UNSPECIFIED = "image/*";
private static ImageCrop instance = null;
private static Activity activity = null;
private static String TAG = "ImageCrop";
public static native void onImageSaved(String path);
//拍摄照片保存路径
private static String savePath = Environment.getExternalStorageDirectory() +"/CropImage";
private static String photoName = "";
private static Uri imgUri = null;
public static ImageCrop getInstance()
{
if(null == instance)
{
instance = new ImageCrop();
}
return instance;
}
//初始化
public void init(Activity activity)
{
ImageCrop.activity = activity;
}
//打开相册
static public void openPhoto()
{
Intent intent = new Intent(Intent.ACTION_PICK,null);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,IMAGE_UNSPECIFIED);
activity.startActivityForResult(intent,PHOTOZOOM);
}
//打开相机
static public void openCamera()
{
File destDir = new File(savePath);
if (!destDir.exists())
{
destDir.mkdirs();
}
photoName = "temp.jpg";
File file = new File(savePath + "/" + photoName);
imgUri = Uri.fromFile(file);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT,imgUri);
activity.startActivityForResult(intent,PHOTOHRAPH);
}
//回调
public void onActivityResult(int requestCode,int resultCode,Intent data)
{
if (resultCode == NONE)
return;
// 拍照 不能使用data,因为没有返回是空的
if (requestCode == PHOTOHRAPH)
{
if (imgUri == null)
{
Log.e(TAG,"PHOTOHRAPH imgUri is null");
return;
}
startPhotoZoom(imgUri);
}
if (requestCode == PHOTOZOOM)
{
// 读取相册缩放图片
if (data==null )
{
Log.e(TAG,"data is null");
return;
}
if (data.getData()==null)
{
Log.e(TAG,"data.getData() is null");
return;
}
startPhotoZoom(data.getData());
}
// 处理结果
if (requestCode == PHOTORESOULT)
{
Bitmap bitmap = decodeUriAsBitmap(imgUri);
if (bitmap == null)
{
Log.e(TAG,"bitmap is null");
}
Log.e("ImageCrop","图片已经保存,通知c++层,");
onImageSaved(savePath + "/" + photoName);
}
}
public void startPhotoZoom(Uri uri)
{
photoName = System.currentTimeMillis() + ".jpg";
File file = new File(savePath,photoName);
imgUri = Uri.fromFile(file);
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri,IMAGE_UNSPECIFIED);
intent.putExtra("crop","true");
intent.putExtra("aspectX",1);
intent.putExtra("aspectY",1);
intent.putExtra("outputX",600);
intent.putExtra("outputY",600);
intent.putExtra("return-data",false);
intent.putExtra(MediaStore.EXTRA_OUTPUT,imgUri);
activity.startActivityForResult(intent,PHOTORESOULT);
}
private Bitmap decodeUriAsBitmap(Uri uri)
{
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream(activity.getContentResolver().openInputStream(uri));
} catch (FileNotFoundException e) {
// TODO: handle exception
e.printStackTrace();
return null;
}
return bitmap;
}
}
4、示例
HelloWorld工程里直接引入ImageCrop.hpp,调用拍照或者打开相册即可,裁减完成后会返回图片保存路径,直接就可以在C++环境里创建精灵等操作了,欧了!
#include "HelloWorldScene.h"
#include "ImageCrop.hpp"
USING_NS_CC;
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
/////////////////////////////
// 2. add a menu item with "X" image,which is clicked to quit the program
// you may modify it.
// add a "close" icon to exit the progress. it's an autorelease object
auto closeItem = MenuItemImage::create(
"CloseNormal.png","CloseSelected.png",CC_CALLBACK_1(HelloWorld::menuCloseCallback,this));
closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2,origin.y + closeItem->getContentSize().height/2));
auto openPhoto = MenuItemFont::create("打开相册",[=](Ref*)
{
ImageCrop::getInstance()->openPhoto([=](std::string path)
{
CCLOG("photo path:%s",path.c_str());
_img -> removeFromParent();
_img = Sprite::create(path);
this->addChild(_img);
_img->setPosition(Vec2(visibleSize.width/2 + origin.x,visibleSize.height/2 + origin.y));
});
});
openPhoto->setPosition(Vec2(origin.x + openPhoto->getContentSize().width/2,origin.y + openPhoto->getContentSize().height/2));
auto openCamera = MenuItemFont::create("打开相机",[=](Ref*)
{
ImageCrop::getInstance()->openCamera([=](std::string path)
{
CCLOG("photo path:%s",path.c_str());
_img -> removeFromParent();
_img = Sprite::create(path);
this->addChild(_img);
_img->setPosition(Vec2(visibleSize.width/2 + origin.x,visibleSize.height/2 + origin.y));
});
});
openCamera->setPosition(Vec2(origin.x + openCamera->getContentSize().width/2,origin.y + openCamera->getContentSize().height));
auto menu = Menu::create(closeItem,openPhoto,openCamera,NULL);
menu->setPosition(Vec2::ZERO);
this->addChild(menu,1);
/////////////////////////////
// 3. add your codes below...
// add a label shows "Hello World"
// create and initialize a label
auto label = Label::createWithTTF("Hello World","fonts/Marker Felt.ttf",24);
// position the label on the center of the screen
label->setPosition(Vec2(origin.x + visibleSize.width/2,origin.y + visibleSize.height - label->getContentSize().height));
// add the label as a child to this layer
this->addChild(label,1);
// add "HelloWorld" splash screen"
_img = Sprite::create("HelloWorld.png");
// position the sprite on the center of the screen
_img->setPosition(Vec2(visibleSize.width/2 + origin.x,visibleSize.height/2 + origin.y));
// add the sprite as a child to this layer
this->addChild(_img,0);
return true;
}
void HelloWorld::menuCloseCallback(Ref* pSender)
{
Director::getInstance()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
exit(0);
#endif
}