RenderTexture the image is black(empty) on Android's gallery

Hi,
In my app , i’m using a RenderTexture as a Canvas.
I would like to save this canvas to android photo gallery , like this:

The cpp side

void Canvas::saveCanvasCallback(cocos2d::Ref *sender)
{
    time_t t = time(0);
    struct tm * timeinfo= localtime (&t);


char buffer[120];
strftime(buffer,120,"image_%d%m%Y%I%M%S.png",timeinfo);


auto callback = [&](RenderTexture* rt, const std::string& path)
{
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
    addImageToGallery(path.c_str());

 
 #else
      CGameManager_iOSBridge::addImageToGallery(path.c_str());
    
#endif
    
   
};

_target->saveToFile(buffer, Image::Format::PNG, true, callback);

CCLOG("Image saved %s \n", buffer);




}

The java side

static  void addImageToGallery (String path) {

   
    ContentValues values = new ContentValues();
    values.put(Images.Media.DATE_ADDED, System.currentTimeMillis());
    values.put(Images.Media.DATE_TAKEN, System.currentTimeMillis());
    values.put(Images.Media.MIME_TYPE, "image/png");
    values.put(MediaStore.MediaColumns.DATA, Environment.getExternalStorageDirectory().getPath()+path);
 
    getContentResolver().insert(Images.Media.EXTERNAL_CONTENT_URI, values);
 
    me.runOnUiThread(new Runnable() {
        
        @Override
        public void run() {

            Toast.makeText(me.getApplicationContext(), "Image saved ",
                    Toast.LENGTH_LONG).show();
            
            
        }
    });
}

The saved image it’s black(empty) in the gallery ( for iOS it works fine) .
I’m working with cocos2d-x V3.3
Please help me

Thanks

Any help ?

What happens if you replace
values.put(MediaStore.MediaColumns.DATA, Environment.getExternalStorageDirectory().getPath()+path);
with
values.put(MediaStore.MediaColumns.DATA, “”);

Do you then also get a black image?

And can you tell us the full path you receive on the java side?

I know for a fact that Android is trickier with resources/getWritable path then iOS (found that out the hard way when setting up the search path).

Kind regards,
Mich

yes it’s the same , i get a black image, and this is the full path: /data/data/com.companyname.drawing/files/image_26112014042241.png

You add that path behind Environment.getExternalStorageDirectory().getPath()
Are you sure that is correct?

Kind regards,
Mich

Here is what works for me on Android.

C++ code (part of my Screenshot class, but that’s easily changed by yourself):

// Call this to take the screenshot
cocos2d::utils::captureScreen( CC_CALLBACK_2( OnScreenshotSaved, this ), "screenshot.jpg" );


void Screenshot::OnScreenshotSaved( bool success, const std::string& screenshotFilename )
{
    if (success)    { AddImageToGallery( screenshotFilename.c_str() ); }
}


////////////////////////////////////////////////////////////////////////
// Copy an image to the gallery
// In:    fullFilename    = String containing the full filename of the image to copy
////////////////////////////////////////////////////////////////////////
void AddImageToGallery( const char* fullFilename )
{
#ifdef __IOS__
    NSString*    strFilename    = [NSString stringWithFormat:@"%s", fullFilename];
    UIImage*    pScreenshot    = [UIImage imageWithContentsOfFile:strFilename];
    if (pScreenshot)    { UIImageWriteToSavedPhotosAlbum(pScreenshot, nil, nil, nil); }
#elif defined (__ANDROID__) && defined (__COCOS2DX__)
    JniMethodInfo jniMethodInfo;
    if (JniHelper::getStaticMethodInfo( jniMethodInfo, "com/gameplayheaven/library/socialmedia/gallery/Gallery", "addImageToGallery", "(Ljava/lang/String;)V" ))
    {
        jstring    arg    = jniMethodInfo.env->NewStringUTF( fullFilename );
        jniMethodInfo.env->CallStaticVoidMethod( jniMethodInfo.classID, jniMethodInfo.methodID, arg );
        jniMethodInfo.env->DeleteLocalRef( arg );
        jniMethodInfo.env->DeleteLocalRef( jniMethodInfo.classID );
    }
#endif // __IOS__ || (__ANDROID__ && __COCOS2DX__)
}

The ifdef tags you need to replace with your own, or the ones used by cocos2d-x…

Here is the java function:

public static void addImageToGallery( String fullFilename )
{
    // I have this function in an IO class, which has the main activity stored in m_activity.
    // If you add this function to your main activity itself, you can just remove the m_activity check
    // and m_activity. in front of functions calls. Aka "m_activity.startActivity(" becomes "startActivity(".
    if (m_activity != null)
    {
        // Add date/time to filename
        String newFilename = new SimpleDateFormat("'MyGameName_'yyyyMMdd_hhmmss'.jpg'").format(new Date());
        // Determine output path
        File path = new File( Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES ).toString() + "/MyGameName/" );
        // Create output file
        File file = new File( path, newFilename );

        try
        { 
            // Make sure the Pictures directory exists.
            boolean dirExists = path.mkdirs();

            // Open an output stream to the new image location
            OutputStream fOut = new FileOutputStream( file );
            // Open the (passed in) image we want to copy
            Bitmap    bitmap = BitmapFactory.decodeFile( fullFilename );
            // Export the image to it's new location
            bitmap.compress( Bitmap.CompressFormat.JPEG, 85, fOut );
            fOut.flush();
            fOut.close();

            // Open the android dialog, allowing the player to chose what he wants to do with the image
            MediaScannerConnection.scanFile(
                m_activity.getApplicationContext(), 
                new String[]{file.toString()}, 
                new String[]{"image/*"}, 
                new OnScanCompletedListener()
                    {
                        @Override
                        public void onScanCompleted(String path, Uri uri)
                        {
                            Intent shareIntent = new Intent();
                            shareIntent.setAction(Intent.ACTION_SEND);
                            shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
                            shareIntent.setType("image/*");
                            shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                            m_activity.startActivity(Intent.createChooser(shareIntent, "Share Screenshot..."));
                        }
                    });
        }
        catch (IOException e)
        {
            // Unable to create file, likely because external storage is not currently mounted.
            Log.w( "ExternalStorage", "Error writing " + file, e );
        }
    }
}

To be able to do this, you will need the WRITE_EXTERNAL_STORAGE permission in your AndroidManifest.xml:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Hope this helps…

Kind regards,
Mich

Could you please post the code of this function?

UPDATE: I edited my post to include iOS code that saves the screenshot in the album.
I haven’t figured out yet how to open a share dialog on it like on iOS (looks like I might need to use a share lib to do that).

in my game, i just add the screenshot to the user Gallery.
i’m using this code

-(void)addImageToGallery:(NSString *)filePath {
    //get the screenshot as raw data
    NSData *data = [NSData dataWithContentsOfFile:filePath];
    //create an image from the raw data
    UIImage *img = [UIImage imageWithData:data];
    //save the image to the users Photo Library
    UIImageWriteToSavedPhotosAlbum(img, nil, nil, nil);
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@""
                                                    message:@"Image saved"
                                                   delegate:nil
                                          cancelButtonTitle:nil
                                          otherButtonTitles:@"OK", nil];
    [alert show];
}