All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.android.util.imagepool.ImagePoolImpl Maven / Gradle / Ivy

/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.util.imagepool;

import com.android.tools.layoutlib.annotations.Nullable;
import com.android.tools.layoutlib.annotations.VisibleForTesting;

import android.util.imagepool.Bucket.BucketCreationMetaData;
import android.util.imagepool.ImagePool.Image.Orientation;

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.lang.ref.Reference;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Consumer;

import com.google.common.base.FinalizablePhantomReference;
import com.google.common.base.FinalizableReferenceQueue;

class ImagePoolImpl implements ImagePool {

    private final ReentrantReadWriteLock mReentrantLock = new ReentrantReadWriteLock();
    private final ImagePoolPolicy mPolicy;
    @VisibleForTesting final Map mPool = new HashMap<>();
    @VisibleForTesting final ImagePoolStats mImagePoolStats = new ImagePoolStatsProdImpl();
    private final FinalizableReferenceQueue mFinalizableReferenceQueue = new FinalizableReferenceQueue();
    private final Set> mReferences = new HashSet<>();

    public ImagePoolImpl(ImagePoolPolicy policy) {
        mPolicy = policy;
        mImagePoolStats.start();
    }

    @Override
    public Image acquire(int w, int h, int type) {
        return acquire(w, h, type, null);
    }

    /* package private */ Image acquire(int w, int h, int type,
            @Nullable Consumer freedCallback) {
        mReentrantLock.writeLock().lock();
        try {
            BucketCreationMetaData metaData =
                    ImagePoolHelper.getBucketCreationMetaData(w, h, type, mPolicy, mImagePoolStats);
            if (metaData == null) {
                return defaultImageImpl(w, h, type, freedCallback);
            }

            final Bucket existingBucket = ImagePoolHelper.getBucket(mPool, metaData, mPolicy);
            final BufferedImage img =
                    ImagePoolHelper.getBufferedImage(existingBucket, metaData, mImagePoolStats);
            if (img == null) {
                return defaultImageImpl(w, h, type, freedCallback);
            }

            // Clear the image. - is this necessary?
            if (img.getRaster().getDataBuffer().getDataType() == java.awt.image.DataBuffer.TYPE_INT) {
                Arrays.fill(((DataBufferInt)img.getRaster().getDataBuffer()).getData(), 0);
            }

            return prepareImage(
                    new ImageImpl(w, h, img, metaData.mOrientation),
                    true,
                    img,
                    existingBucket,
                    freedCallback);
        } finally {
            mReentrantLock.writeLock().unlock();
        }
    }

    /**
     * Add statistics as well as dispose behaviour before returning image.
     */
    private Image prepareImage(
            Image image,
            boolean offerBackToBucket,
            @Nullable BufferedImage img,
            @Nullable Bucket existingBucket,
            @Nullable Consumer freedCallback) {
        final Integer imageHash = image.hashCode();
        mImagePoolStats.acquiredImage(imageHash);
        FinalizablePhantomReference reference =
                new FinalizablePhantomReference(image, mFinalizableReferenceQueue) {
                    @Override
                    public void finalizeReferent() {
                        // This method might be called twice if the user has manually called the free() method. The second call will have no effect.
                        if (mReferences.remove(this)) {
                            mImagePoolStats.disposeImage(imageHash);
                            if (offerBackToBucket) {
                                if (!mImagePoolStats.fitsMaxCacheSize(img.getWidth(), img.getHeight(),
                                        mPolicy.mBucketMaxCacheSize)) {
                                    mImagePoolStats.tooBigForCache();
                                    // Adding this back would go over the max cache size we set for ourselves. Release it.
                                    return;
                                }

                                // else stat does not change.
                                existingBucket.offer(img);
                            }
                            if (freedCallback != null) {
                                freedCallback.accept(img);
                            }
                        }
                    }
                };
        mReferences.add(reference);
        return image;
    }

    /**
     * Default Image Impl to be used when the pool is not big enough.
     */
    private Image defaultImageImpl(int w, int h, int type,
            @Nullable Consumer freedCallback) {
        BufferedImage bufferedImage = new BufferedImage(w, h, type);
        mImagePoolStats.tooBigForCache();
        mImagePoolStats.recordAllocOutsidePool(w, h);
        return prepareImage(new ImageImpl(w, h, bufferedImage, Orientation.NONE),
                false,  null, null, freedCallback);
    }

    @Override
    public void dispose() {
        mReentrantLock.writeLock().lock();
        try {
            for (Bucket bucket : mPool.values()) {
                bucket.clear();
            }
            mImagePoolStats.clear();
        } finally {
            mReentrantLock.writeLock().unlock();
        }
    }

    /* package private */ void printStat() {
        System.out.println(mImagePoolStats.getStatistic());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy