Home | History | Annotate | Download | only in cache
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.gallery3d.filtershow.cache;
     18 
     19 import android.app.Activity;
     20 import android.content.res.Resources;
     21 import android.graphics.Bitmap;
     22 import android.support.v8.renderscript.Allocation;
     23 import android.support.v8.renderscript.RenderScript;
     24 import android.util.Log;
     25 
     26 import com.android.gallery3d.filtershow.filters.FiltersManager;
     27 import com.android.gallery3d.filtershow.filters.ImageFilterGeometry;
     28 import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
     29 import com.android.gallery3d.filtershow.imageshow.MasterImage;
     30 import com.android.gallery3d.filtershow.presets.FilterEnvironment;
     31 import com.android.gallery3d.filtershow.presets.ImagePreset;
     32 
     33 public class CachingPipeline {
     34     private static final String LOGTAG = "CachingPipeline";
     35     private boolean DEBUG = false;
     36 
     37     private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
     38 
     39     private static volatile RenderScript sRS = null;
     40     private static volatile Resources sResources = null;
     41 
     42     private FiltersManager mFiltersManager = null;
     43     private volatile Bitmap mOriginalBitmap = null;
     44     private volatile Bitmap mResizedOriginalBitmap = null;
     45 
     46     private FilterEnvironment mEnvironment = new FilterEnvironment();
     47 
     48     private volatile Allocation mOriginalAllocation = null;
     49     private volatile Allocation mFiltersOnlyOriginalAllocation =  null;
     50 
     51     protected volatile Allocation mInPixelsAllocation;
     52     protected volatile Allocation mOutPixelsAllocation;
     53     private volatile int mWidth = 0;
     54     private volatile int mHeight = 0;
     55 
     56     private volatile GeometryMetadata mPreviousGeometry = null;
     57     private volatile float mPreviewScaleFactor = 1.0f;
     58     private volatile float mHighResPreviewScaleFactor = 1.0f;
     59     private volatile String mName = "";
     60 
     61     private ImageFilterGeometry mGeometry = null;
     62 
     63     public CachingPipeline(FiltersManager filtersManager, String name) {
     64         mFiltersManager = filtersManager;
     65         mName = name;
     66     }
     67 
     68     public static synchronized Resources getResources() {
     69         return sResources;
     70     }
     71 
     72     public static synchronized void setResources(Resources resources) {
     73         sResources = resources;
     74     }
     75 
     76     public static synchronized RenderScript getRenderScriptContext() {
     77         return sRS;
     78     }
     79 
     80     public static synchronized void setRenderScriptContext(RenderScript RS) {
     81         sRS = RS;
     82     }
     83 
     84     public static synchronized void createRenderscriptContext(Activity context) {
     85         if (sRS != null) {
     86             Log.w(LOGTAG, "A prior RS context exists when calling setRenderScriptContext");
     87             destroyRenderScriptContext();
     88         }
     89         sRS = RenderScript.create(context);
     90         sResources = context.getResources();
     91     }
     92 
     93     public static synchronized void destroyRenderScriptContext() {
     94         if (sRS != null) {
     95             sRS.destroy();
     96         }
     97         sRS = null;
     98         sResources = null;
     99     }
    100 
    101     public void stop() {
    102         mEnvironment.setStop(true);
    103     }
    104 
    105     public synchronized void reset() {
    106         synchronized (CachingPipeline.class) {
    107             if (getRenderScriptContext() == null) {
    108                 return;
    109             }
    110             mOriginalBitmap = null; // just a reference to the bitmap in ImageLoader
    111             if (mResizedOriginalBitmap != null) {
    112                 mResizedOriginalBitmap.recycle();
    113                 mResizedOriginalBitmap = null;
    114             }
    115             if (mOriginalAllocation != null) {
    116                 mOriginalAllocation.destroy();
    117                 mOriginalAllocation = null;
    118             }
    119             if (mFiltersOnlyOriginalAllocation != null) {
    120                 mFiltersOnlyOriginalAllocation.destroy();
    121                 mFiltersOnlyOriginalAllocation = null;
    122             }
    123             mPreviousGeometry = null;
    124             mPreviewScaleFactor = 1.0f;
    125             mHighResPreviewScaleFactor = 1.0f;
    126 
    127             destroyPixelAllocations();
    128         }
    129     }
    130 
    131     private synchronized void destroyPixelAllocations() {
    132         if (DEBUG) {
    133             Log.v(LOGTAG, "destroyPixelAllocations in " + getName());
    134         }
    135         if (mInPixelsAllocation != null) {
    136             mInPixelsAllocation.destroy();
    137             mInPixelsAllocation = null;
    138         }
    139         if (mOutPixelsAllocation != null) {
    140             mOutPixelsAllocation.destroy();
    141             mOutPixelsAllocation = null;
    142         }
    143         mWidth = 0;
    144         mHeight = 0;
    145     }
    146 
    147     private String getType(RenderingRequest request) {
    148         if (request.getType() == RenderingRequest.ICON_RENDERING) {
    149             return "ICON_RENDERING";
    150         }
    151         if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
    152             return "FILTERS_RENDERING";
    153         }
    154         if (request.getType() == RenderingRequest.FULL_RENDERING) {
    155             return "FULL_RENDERING";
    156         }
    157         if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
    158             return "GEOMETRY_RENDERING";
    159         }
    160         if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
    161             return "PARTIAL_RENDERING";
    162         }
    163         if (request.getType() == RenderingRequest.HIGHRES_RENDERING) {
    164             return "HIGHRES_RENDERING";
    165         }
    166         return "UNKNOWN TYPE!";
    167     }
    168 
    169     private void setupEnvironment(ImagePreset preset, boolean highResPreview) {
    170         mEnvironment.setCachingPipeline(this);
    171         mEnvironment.setFiltersManager(mFiltersManager);
    172         if (highResPreview) {
    173             mEnvironment.setScaleFactor(mHighResPreviewScaleFactor);
    174         } else {
    175             mEnvironment.setScaleFactor(mPreviewScaleFactor);
    176         }
    177         mEnvironment.setQuality(ImagePreset.QUALITY_PREVIEW);
    178         mEnvironment.setImagePreset(preset);
    179         mEnvironment.setStop(false);
    180     }
    181 
    182     public void setOriginal(Bitmap bitmap) {
    183         mOriginalBitmap = bitmap;
    184         Log.v(LOGTAG,"setOriginal, size " + bitmap.getWidth() + " x " + bitmap.getHeight());
    185         ImagePreset preset = MasterImage.getImage().getPreset();
    186         setupEnvironment(preset, false);
    187         updateOriginalAllocation(preset);
    188     }
    189 
    190     private synchronized boolean updateOriginalAllocation(ImagePreset preset) {
    191         Bitmap originalBitmap = mOriginalBitmap;
    192 
    193         if (originalBitmap == null) {
    194             return false;
    195         }
    196 
    197         GeometryMetadata geometry = preset.getGeometry();
    198         if (mPreviousGeometry != null && geometry.equals(mPreviousGeometry)) {
    199             return false;
    200         }
    201 
    202         if (DEBUG) {
    203             Log.v(LOGTAG, "geometry has changed");
    204         }
    205 
    206         RenderScript RS = getRenderScriptContext();
    207 
    208         Allocation filtersOnlyOriginalAllocation = mFiltersOnlyOriginalAllocation;
    209         mFiltersOnlyOriginalAllocation = Allocation.createFromBitmap(RS, originalBitmap,
    210                 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
    211         if (filtersOnlyOriginalAllocation != null) {
    212             filtersOnlyOriginalAllocation.destroy();
    213         }
    214 
    215         Allocation originalAllocation = mOriginalAllocation;
    216         mResizedOriginalBitmap = preset.applyGeometry(originalBitmap, mEnvironment);
    217         mOriginalAllocation = Allocation.createFromBitmap(RS, mResizedOriginalBitmap,
    218                 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
    219         if (originalAllocation != null) {
    220             originalAllocation.destroy();
    221         }
    222 
    223         mPreviousGeometry = new GeometryMetadata(geometry);
    224         return true;
    225     }
    226 
    227     public synchronized void render(RenderingRequest request) {
    228         synchronized (CachingPipeline.class) {
    229             if (getRenderScriptContext() == null) {
    230                 return;
    231             }
    232             if (((request.getType() != RenderingRequest.PARTIAL_RENDERING
    233                     && request.getType() != RenderingRequest.HIGHRES_RENDERING)
    234                     && request.getBitmap() == null)
    235                     || request.getImagePreset() == null) {
    236                 return;
    237             }
    238 
    239             if (DEBUG) {
    240                 Log.v(LOGTAG, "render image of type " + getType(request));
    241             }
    242 
    243             Bitmap bitmap = request.getBitmap();
    244             ImagePreset preset = request.getImagePreset();
    245             setupEnvironment(preset,
    246                     request.getType() != RenderingRequest.HIGHRES_RENDERING);
    247             mFiltersManager.freeFilterResources(preset);
    248 
    249             if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
    250                 ImageLoader loader = MasterImage.getImage().getImageLoader();
    251                 if (loader == null) {
    252                     Log.w(LOGTAG, "loader not yet setup, cannot handle: " + getType(request));
    253                     return;
    254                 }
    255                 bitmap = loader.getScaleOneImageForPreset(request.getBounds(),
    256                         request.getDestination());
    257                 if (bitmap == null) {
    258                     Log.w(LOGTAG, "could not get bitmap for: " + getType(request));
    259                     return;
    260                 }
    261             }
    262 
    263             if (request.getType() == RenderingRequest.HIGHRES_RENDERING) {
    264                 ImageLoader loader = MasterImage.getImage().getImageLoader();
    265                 bitmap = loader.getOriginalBitmapHighres();
    266                 bitmap = preset.applyGeometry(bitmap, mEnvironment);
    267             }
    268 
    269             if (request.getType() == RenderingRequest.FULL_RENDERING
    270                     || request.getType() == RenderingRequest.GEOMETRY_RENDERING
    271                     || request.getType() == RenderingRequest.FILTERS_RENDERING) {
    272                 updateOriginalAllocation(preset);
    273             }
    274 
    275             if (DEBUG) {
    276                 Log.v(LOGTAG, "after update, req bitmap (" + bitmap.getWidth() + "x" + bitmap.getHeight()
    277                         + " ? resizeOriginal (" + mResizedOriginalBitmap.getWidth() + "x"
    278                         + mResizedOriginalBitmap.getHeight());
    279             }
    280 
    281             if (request.getType() == RenderingRequest.FULL_RENDERING
    282                     || request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
    283                 mOriginalAllocation.copyTo(bitmap);
    284             } else if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
    285                 mFiltersOnlyOriginalAllocation.copyTo(bitmap);
    286             }
    287 
    288             if (request.getType() == RenderingRequest.FULL_RENDERING
    289                     || request.getType() == RenderingRequest.FILTERS_RENDERING
    290                     || request.getType() == RenderingRequest.ICON_RENDERING
    291                     || request.getType() == RenderingRequest.PARTIAL_RENDERING
    292                     || request.getType() == RenderingRequest.HIGHRES_RENDERING
    293                     || request.getType() == RenderingRequest.STYLE_ICON_RENDERING) {
    294 
    295                 if (request.getType() == RenderingRequest.ICON_RENDERING) {
    296                     mEnvironment.setQuality(ImagePreset.QUALITY_ICON);
    297                 } else  if (request.getType() == RenderingRequest.STYLE_ICON_RENDERING) {
    298                     mEnvironment.setQuality(ImagePreset.STYLE_ICON);
    299                 } else {
    300                     mEnvironment.setQuality(ImagePreset.QUALITY_PREVIEW);
    301                 }
    302 
    303                 Bitmap bmp = preset.apply(bitmap, mEnvironment);
    304                 if (!mEnvironment.needsStop()) {
    305                     request.setBitmap(bmp);
    306                 }
    307                 mFiltersManager.freeFilterResources(preset);
    308             }
    309         }
    310     }
    311 
    312     public synchronized void renderImage(ImagePreset preset, Allocation in, Allocation out) {
    313         synchronized (CachingPipeline.class) {
    314             if (getRenderScriptContext() == null) {
    315                 return;
    316             }
    317             setupEnvironment(preset, false);
    318             mFiltersManager.freeFilterResources(preset);
    319             preset.applyFilters(-1, -1, in, out, mEnvironment);
    320             // TODO: we should render the border onto a different bitmap instead
    321             preset.applyBorder(in, out, mEnvironment);
    322         }
    323     }
    324 
    325     public synchronized Bitmap renderFinalImage(Bitmap bitmap, ImagePreset preset) {
    326         synchronized (CachingPipeline.class) {
    327             if (getRenderScriptContext() == null) {
    328                 return bitmap;
    329             }
    330             setupEnvironment(preset, false);
    331             mEnvironment.setQuality(ImagePreset.QUALITY_FINAL);
    332             mEnvironment.setScaleFactor(1.0f);
    333             mFiltersManager.freeFilterResources(preset);
    334             bitmap = preset.applyGeometry(bitmap, mEnvironment);
    335             bitmap = preset.apply(bitmap, mEnvironment);
    336             return bitmap;
    337         }
    338     }
    339 
    340     public Bitmap renderGeometryIcon(Bitmap bitmap, ImagePreset preset) {
    341         // Called by RenderRequest on the main thread
    342         // TODO: change this -- we should reuse a pool of bitmaps instead...
    343         if (mGeometry == null) {
    344             mGeometry = new ImageFilterGeometry();
    345         }
    346         mGeometry.useRepresentation(preset.getGeometry());
    347         return mGeometry.apply(bitmap, mPreviewScaleFactor,
    348                 ImagePreset.QUALITY_PREVIEW);
    349     }
    350 
    351     public synchronized void compute(TripleBufferBitmap buffer, ImagePreset preset, int type) {
    352         synchronized (CachingPipeline.class) {
    353             if (getRenderScriptContext() == null) {
    354                 return;
    355             }
    356             if (DEBUG) {
    357                 Log.v(LOGTAG, "compute preset " + preset);
    358                 preset.showFilters();
    359             }
    360 
    361             String thread = Thread.currentThread().getName();
    362             long time = System.currentTimeMillis();
    363             setupEnvironment(preset, false);
    364             mFiltersManager.freeFilterResources(preset);
    365 
    366             Bitmap resizedOriginalBitmap = mResizedOriginalBitmap;
    367             if (updateOriginalAllocation(preset)) {
    368                 resizedOriginalBitmap = mResizedOriginalBitmap;
    369                 mEnvironment.cache(buffer.getProducer());
    370                 buffer.updateProducerBitmap(resizedOriginalBitmap);
    371             }
    372             Bitmap bitmap = buffer.getProducer();
    373             long time2 = System.currentTimeMillis();
    374 
    375             if (bitmap == null || (bitmap.getWidth() != resizedOriginalBitmap.getWidth())
    376                     || (bitmap.getHeight() != resizedOriginalBitmap.getHeight())) {
    377                 mEnvironment.cache(buffer.getProducer());
    378                 buffer.updateProducerBitmap(resizedOriginalBitmap);
    379                 bitmap = buffer.getProducer();
    380             }
    381             mOriginalAllocation.copyTo(bitmap);
    382 
    383             Bitmap tmpbitmap = preset.apply(bitmap, mEnvironment);
    384             if (tmpbitmap != bitmap) {
    385                 mEnvironment.cache(buffer.getProducer());
    386                 buffer.setProducer(tmpbitmap);
    387             }
    388 
    389             mFiltersManager.freeFilterResources(preset);
    390 
    391             time = System.currentTimeMillis() - time;
    392             time2 = System.currentTimeMillis() - time2;
    393             if (DEBUG) {
    394                 Log.v(LOGTAG, "Applying type " + type + " filters to bitmap "
    395                         + bitmap + " (" + bitmap.getWidth() + " x " + bitmap.getHeight()
    396                         + ") took " + time + " ms, " + time2 + " ms for the filter, on thread " + thread);
    397             }
    398         }
    399     }
    400 
    401     public boolean needsRepaint() {
    402         TripleBufferBitmap buffer = MasterImage.getImage().getDoubleBuffer();
    403         return buffer.checkRepaintNeeded();
    404     }
    405 
    406     public void setPreviewScaleFactor(float previewScaleFactor) {
    407         mPreviewScaleFactor = previewScaleFactor;
    408     }
    409 
    410     public void setHighResPreviewScaleFactor(float highResPreviewScaleFactor) {
    411         mHighResPreviewScaleFactor = highResPreviewScaleFactor;
    412     }
    413 
    414     public synchronized boolean isInitialized() {
    415         return getRenderScriptContext() != null && mOriginalBitmap != null;
    416     }
    417 
    418     public boolean prepareRenderscriptAllocations(Bitmap bitmap) {
    419         RenderScript RS = getRenderScriptContext();
    420         boolean needsUpdate = false;
    421         if (mOutPixelsAllocation == null || mInPixelsAllocation == null ||
    422                 bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight) {
    423             destroyPixelAllocations();
    424             Bitmap bitmapBuffer = bitmap;
    425             if (bitmap.getConfig() == null || bitmap.getConfig() != BITMAP_CONFIG) {
    426                 bitmapBuffer = bitmap.copy(BITMAP_CONFIG, true);
    427             }
    428             mOutPixelsAllocation = Allocation.createFromBitmap(RS, bitmapBuffer,
    429                     Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
    430             mInPixelsAllocation = Allocation.createTyped(RS,
    431                     mOutPixelsAllocation.getType());
    432             needsUpdate = true;
    433         }
    434         if (RS != null) {
    435             mInPixelsAllocation.copyFrom(bitmap);
    436         }
    437         if (bitmap.getWidth() != mWidth
    438                 || bitmap.getHeight() != mHeight) {
    439             mWidth = bitmap.getWidth();
    440             mHeight = bitmap.getHeight();
    441             needsUpdate = true;
    442         }
    443         if (DEBUG) {
    444             Log.v(LOGTAG, "prepareRenderscriptAllocations: " + needsUpdate + " in " + getName());
    445         }
    446         return needsUpdate;
    447     }
    448 
    449     public synchronized Allocation getInPixelsAllocation() {
    450         return mInPixelsAllocation;
    451     }
    452 
    453     public synchronized Allocation getOutPixelsAllocation() {
    454         return mOutPixelsAllocation;
    455     }
    456 
    457     public String getName() {
    458         return mName;
    459     }
    460 
    461 }
    462