cocos2d-x调用摄像头和相册并裁减图片

最近做个小游戏需要调用手机图库和摄像头并裁减图片,发现网上很多教程不是太全,有的安卓端裁减不了大图,下面是完整代码,可以裁减高清图的。

完整拼图游戏下载:
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 
#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 
#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_CLASS,JAVA_FUNC_OPEN_PHOTO,"()V");
    if (ret)
    {
        info.env->CallStaticVoidMethod(info.classID,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

2、iOS端代码,.m文件改成.mm文件

#ifndef ImagePickerViewController_h
#define ImagePickerViewController_h

#import 

@interface ImagePickerViewController:UIViewController<UINavigationControllerDelegate,UIImagePickerControllerDelegate>
{
    NSString *filePath;
}

//打开本地相册
-(void)localPhoto;

//打开相机
-(void)takePhoto;

@end
#endif

#import 
#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
}

本文参考:
http://blog.csdn.net/floodingfire/article/details/8144587

http://www.myexception.cn/operating-system/1937511.html

你可能感兴趣的:(cocos2d-x)