Thursday, 30 August 2018

Using Glide, how can I go over each frame of GifDrawable, as Bitmap?

Background

In a live wallpaper, I have a Canvas instance that I wish to draw GIF/WEBP content into, which was loaded via Glide.

The problem

Glide seems to be optimized to work only with normal UI (Views). It has some basic functions, but the most important ones for what I'm trying to do seems to be private.

What I've found

I use official Glide library for GIF loading, and GlideWebpDecoder for WEBP loading.

The basic call to load each of those, is as such:

GIF:

    GlideApp.with(this).asGif()
            .load("https://res.cloudinary.com/demo/image/upload/bored_animation.gif")
            .into(object : SimpleTarget<GifDrawable>() {
                override fun onResourceReady(resource: GifDrawable, transition: Transition<in GifDrawable>?) {
                    //example of usage:
                    imageView.setImageDrawable(resource)
                    resource.start()
                }
            })

WEBP:

        GlideApp.with(this).asDrawable()
                .load("https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp")
//                .optionalTransform(WebpDrawable::class.java, WebpDrawableTransformation(CircleCrop()))
                .into(object : SimpleTarget<Drawable>() {
                    override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
                        //example of usage:
                        imageView.setImageDrawable(resource)
                        if (resource is Animatable) {
                            (resource as Animatable).start()
                        }
                    }
                })

Now, remember I don't really have an ImageView, and instead I only have a Canvas, which I get via surfaceHolder.lockCanvas() call.

                    resource.callback = object : Drawable.Callback {
                        override fun invalidateDrawable(who: Drawable) {
                            Log.d("AppLog", "frame ${resource.frameIndex}/${resource.frameCount}")
                        }

                    }

However, when I try to fetch the Bitmap to be used for the current frame, I fail to find the correct function.

I tried this for example (and this is only an example, to see if it can work with canvas):

    val bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(bitmap)

    ...
    resource.draw(canvas)

But it doesn't seem to draw the content into the bitmap, and I think it's because its draw function has these lines of code:

  @Override
  public void draw(@NonNull Canvas canvas) {
    if (isRecycled) {
      return;
    }

    if (applyGravity) {
      Gravity.apply(GRAVITY, getIntrinsicWidth(), getIntrinsicHeight(), getBounds(), getDestRect());
      applyGravity = false;
    }

    Bitmap currentFrame = state.frameLoader.getCurrentFrame();
    canvas.drawBitmap(currentFrame, null, getDestRect(), getPaint());
  }

Yet the getDestRect() returns a 0-sized rectangle, which I can't find how to modify : it's also private, and I don't see anything that changes it.

The questions

  1. Suppose I got the Drawable I wish to use (GIF/WEBP), how can I get each of the frames it can produce, and draw it into a canvas?

  2. Can I also set the scaling type somehow, just like on ImageView (center-crop, fit-center, center-inside...) ?

  3. Is there perhaps a better alternative to this? Maybe suppose I have a GIF/WEBP animation file, does Glide allow me to just use its decoder?



from Using Glide, how can I go over each frame of GifDrawable, as Bitmap?

How to find what crashes a Cordova App in Android?

I have written a hybrid game using Cordova for iOS and Android. The game ran perfectly on both iOS and Android and I was almost ready to publish it but I only needed to make a few splash screens. When that was done, I did one final test on both platforms only to find out the game crashes on Android after a few minutes of play! It has never done that before and I have no idea what is causing it. What's also new is that my phone gets so hot when playing the game, you can hardly hold it anymore after a while (I measured 126F).

I'm only using two plugins: cordova-plugin-admob-free and cordova-plugin-splashscreen. For testing purposes, I removed both to see if they were causing the crashes, but the crashes were still there.

The game crashes when built with Cordova (cordova run android --devices) and also if I build it on PhoneGab Build and install the apk manually.

I've tried debugging with chrome://inspect but I don't see any errors appearing on the console. I've also run

adb logcat

but the log is too complicated for me to understand what is causing the crash. If anyone wants to take a look at it, I've uploaded a copy of the log here. The name of the game is "mygame" (that's not really its name, but I changed it to that in the log file).

I have no idea how to find out what is causing this crash. On iOS, everything works perfectly fine. It's only Android that is causing this problem. If you need more information or if I need to be more specific, please let me know what more information you need and I will add it to this post.

EDIT August 24th 2018: I've been able to test it on another Android phone and on the simulator and it didn't crash there. I still need to figure out what is causing the crash since it's the only app that crashes on my phone. I would also like to know what is causing the tremendous battery drain when I run it. This too is new.

EDIT August 26th 2018: Well, more Android devices are experiencing the crash. I've done a few tests on my device. I removed the two Cordova plugins (AdMob en SplashScreen) en started the game. I let it sit at the opening screen so at that point, no game objects have been created yet and all it does are a few simple calculations and a few context.drawImage() calls. And still the phone gets hot and crashes after a while. Could it be that the context.drawImage() has a memory leak?

EDIT August 27th 2018: I stripped the app so that in the game's main loop only two drawImage() (and a requestAnimatonFrame()) were left. One drawImage() had a canvas as source, the other an img object. It still crashed after a while. Then I removed the drawImage() with an img object as source and it still hadn't crashed after an hour of testing. I then added a few more drawImage() all with a canvas as source - and no crash. I then replaced the canvas with an img object as source and it crashed after a few minutes. So I think it's safe to assume that a drawImage() with an img object as source is causing the crashes?

EDIT August 29th 2018: I changed all drawImages() in my code to use a canvas as source. The canvas is only filled once with an image. The first time I ran the game aferwards, it didn't crash! I thought I had solved the problem but after some more testing I found out it does crash but it takes a lot longer for the game to crash. So even though this is a good step towards a solution, it still isn't the solution.

EDIT August 30th 2018: I was able to run the game through Android Studio and when it crashed, the debug console showed this:

E/chromium: [ERROR:gl_fence_egl.cc(55)] Failed to get EGLSync attribute. error code:12300
W/google-breakpad: ### ### ### ### ### ### ### ### ### ### ### ### ###
                   Chrome build fingerprint:
                   68.0.3440.91
                   344009152
                   ### ### ### ### ### ### ### ### ### ### ### ### ###
A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 13865 (Chrome_InProcGp)

Can anyone please help me debug this?



from How to find what crashes a Cordova App in Android?

How to protect a file on sub-domain of main site from download without auth in wordpress

For example my main domain www.example.com which is a file sharing website where we keep files in sub domain like server1.example.com and server2.example.com to share with visitor. My question is how to protect a file on sub-domain (sub-domain might point to different server or host) direct from download without authentication in wordpress/mainsite.

My current code credit goes to http://wordpress.stackexchange.com/a/37743/12438

.htaccess file

RewriteCond %{REQUEST_FILENAME} -s
RewriteRule ^wp-content/uploads/(.*)$ dl-file.php?file=$1 [QSA,L]

SO my entire .htaccess code looks like

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]

# BEGIN THIS DL-FILE.PHP ADDITION
RewriteCond %{REQUEST_URI} ^.*wp-content/uploads/.*
RewriteRule ^wp-content/uploads/(.*)$ dl-file.php?file=$1 [QSA,L]
# END THIS DL-FILE.PHP ADDITION

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

# END WordPress

Options -Indexes

but it can Protect only a file inside wp-content/uploads/*

<?php
ob_start();
require_once('wp-load.php');
require_once ABSPATH . WPINC . '/formatting.php';
require_once ABSPATH . WPINC . '/capabilities.php';
require_once ABSPATH . WPINC . '/user.php';
require_once ABSPATH . WPINC . '/meta.php';
require_once ABSPATH . WPINC . '/post.php';
require_once ABSPATH . WPINC . '/pluggable.php';
wp_cookie_constants();
$discard = ob_get_clean();

is_user_logged_in() ||  auth_redirect();


list($basedir) = array_values(array_intersect_key(wp_upload_dir(), array('basedir' => 1)))+array(NULL);

$file = rtrim($basedir, '/') . '/' . (isset($_GET['file']) ? $_GET['file'] : '');
$file = realpath($file);

if ($file === FALSE || !$basedir || !is_file($file)) {
    status_header(404);
    die('404 &#8212; File not found.');
}

if (strpos($file, $basedir) !== 0) {
    status_header(403);
    die('403 &#8212; Access denied.');
}


$mime = wp_check_filetype($file);
if( false === $mime[ 'type' ] && function_exists( 'mime_content_type' ) )
    $mime[ 'type' ] = mime_content_type( $file );
if( $mime[ 'type' ] )
    $mimetype = $mime[ 'type' ];
else
    $mimetype = 'image/' . substr( $file, strrpos( $file, '.' ) + 1 );
header( 'Content-Type: ' . $mimetype ); // always send this
if ( false === strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS' ) )
    header( 'Content-Length: ' . filesize( $file ) );
$last_modified = gmdate( 'D, d M Y H:i:s', filemtime( $file ) );
$etag = '"' . md5( $last_modified ) . '"';
header( "Last-Modified: $last_modified GMT" );
header( 'ETag: ' . $etag );
header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', time() + 100000000 ) . ' GMT' );
// Support for Conditional GET
$client_etag = isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ? stripslashes( $_SERVER['HTTP_IF_NONE_MATCH'] ) : false;
if( ! isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) )
    $_SERVER['HTTP_IF_MODIFIED_SINCE'] = false;
$client_last_modified = trim( $_SERVER['HTTP_IF_MODIFIED_SINCE'] );
// If string is empty, return 0. If not, attempt to parse into a timestamp
$client_modified_timestamp = $client_last_modified ? strtotime( $client_last_modified ) : 0;
// Make a timestamp for our most recent modification...
$modified_timestamp = strtotime($last_modified);
if ( ( $client_last_modified && $client_etag )
    ? ( ( $client_modified_timestamp >= $modified_timestamp) && ( $client_etag == $etag ) )
    : ( ( $client_modified_timestamp >= $modified_timestamp) || ( $client_etag == $etag ) )
    ) {
    status_header( 304 );
    exit;
}
// If we made it this far, just serve the file
readfile( $file );



from How to protect a file on sub-domain of main site from download without auth in wordpress