Home | History | Annotate | Download | only in pipeline
      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.pipeline;
     18 
     19 import android.content.Context;
     20 import android.content.res.Resources;
     21 import android.graphics.Bitmap;
     22 import android.graphics.Canvas;
     23 import android.graphics.Matrix;
     24 import android.graphics.Paint;
     25 import android.graphics.Rect;
     26 import android.graphics.RectF;
     27 import android.renderscript.Allocation;
     28 import android.renderscript.RenderScript;
     29 import android.util.Log;
     30 
     31 import com.android.gallery3d.filtershow.cache.BitmapCache;
     32 import com.android.gallery3d.filtershow.cache.ImageLoader;
     33 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
     34 import com.android.gallery3d.filtershow.filters.FiltersManager;
     35 import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils;
     36 import com.android.gallery3d.filtershow.imageshow.MasterImage;
     37 
     38 import java.util.Vector;
     39 
     40 public class CachingPipeline implements PipelineInterface {
     41     private static final String LOGTAG = "CachingPipeline";
     42     private boolean DEBUG = false;
     43 
     44     private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888;
     45 
     46     private static volatile RenderScript sRS = null;
     47 
     48     private FiltersManager mFiltersManager = null;
     49     private volatile Bitmap mOriginalBitmap = null;
     50     private volatile Bitmap mResizedOriginalBitmap = null;
     51 
     52     private FilterEnvironment mEnvironment = new FilterEnvironment();
     53     private CacheProcessing mCachedProcessing = new CacheProcessing();
     54 
     55 
     56     private volatile Allocation mOriginalAllocation = null;
     57     private volatile Allocation mFiltersOnlyOriginalAllocation =  null;
     58 
     59     protected volatile Allocation mInPixelsAllocation;
     60     protected volatile Allocation mOutPixelsAllocation;
     61     private volatile int mWidth = 0;
     62     private volatile int mHeight = 0;
     63 
     64     private volatile float mPreviewScaleFactor = 1.0f;
     65     private volatile float mHighResPreviewScaleFactor = 1.0f;
     66     private volatile String mName = "";
     67 
     68     public CachingPipeline(FiltersManager filtersManager, String name) {
     69         mFiltersManager = filtersManager;
     70         mName = name;
     71     }
     72 
     73     public static synchronized RenderScript getRenderScriptContext() {
     74         return sRS;
     75     }
     76 
     77     public static synchronized void createRenderscriptContext(Context context) {
     78         if (sRS != null) {
     79             Log.w(LOGTAG, "A prior RS context exists when calling setRenderScriptContext");
     80             destroyRenderScriptContext();
     81         }
     82         sRS = RenderScript.create(context);
     83     }
     84 
     85     public static synchronized void destroyRenderScriptContext() {
     86         if (sRS != null) {
     87             sRS.destroy();
     88         }
     89         sRS = null;
     90     }
     91 
     92     public void stop() {
     93         mEnvironment.setStop(true);
     94     }
     95 
     96     public synchronized void reset() {
     97         synchronized (CachingPipeline.class) {
     98             if (getRenderScriptContext() == null) {
     99                 return;
    100             }
    101             mOriginalBitmap = null; // just a reference to the bitmap in ImageLoader
    102             if (mResizedOriginalBitmap != null) {
    103                 mResizedOriginalBitmap.recycle();
    104                 mResizedOriginalBitmap = null;
    105             }
    106             if (mOriginalAllocation != null) {
    107                 mOriginalAllocation.destroy();
    108                 mOriginalAllocation = null;
    109             }
    110             if (mFiltersOnlyOriginalAllocation != null) {
    111                 mFiltersOnlyOriginalAllocation.destroy();
    112                 mFiltersOnlyOriginalAllocation = null;
    113             }
    114             mPreviewScaleFactor = 1.0f;
    115             mHighResPreviewScaleFactor = 1.0f;
    116 
    117             destroyPixelAllocations();
    118         }
    119     }
    120 
    121     public Resources getResources() {
    122         return sRS.getApplicationContext().getResources();
    123     }
    124 
    125     private synchronized void destroyPixelAllocations() {
    126         if (DEBUG) {
    127             Log.v(LOGTAG, "destroyPixelAllocations in " + getName());
    128         }
    129         if (mInPixelsAllocation != null) {
    130             mInPixelsAllocation.destroy();
    131             mInPixelsAllocation = null;
    132         }
    133         if (mOutPixelsAllocation != null) {
    134             mOutPixelsAllocation.destroy();
    135             mOutPixelsAllocation = null;
    136         }
    137         mWidth = 0;
    138         mHeight = 0;
    139     }
    140 
    141     private String getType(RenderingRequest request) {
    142         if (request.getType() == RenderingRequest.ICON_RENDERING) {
    143             return "ICON_RENDERING";
    144         }
    145         if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
    146             return "FILTERS_RENDERING";
    147         }
    148         if (request.getType() == RenderingRequest.FULL_RENDERING) {
    149             return "FULL_RENDERING";
    150         }
    151         if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
    152             return "GEOMETRY_RENDERING";
    153         }
    154         if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
    155             return "PARTIAL_RENDERING";
    156         }
    157         if (request.getType() == RenderingRequest.HIGHRES_RENDERING) {
    158             return "HIGHRES_RENDERING";
    159         }
    160         return "UNKNOWN TYPE!";
    161     }
    162 
    163     private void setupEnvironment(ImagePreset preset, boolean highResPreview) {
    164         mEnvironment.setPipeline(this);
    165         mEnvironment.setFiltersManager(mFiltersManager);
    166         mEnvironment.setBitmapCache(MasterImage.getImage().getBitmapCache());
    167         if (highResPreview) {
    168             mEnvironment.setScaleFactor(mHighResPreviewScaleFactor);
    169         } else {
    170             mEnvironment.setScaleFactor(mPreviewScaleFactor);
    171         }
    172         mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
    173         mEnvironment.setImagePreset(preset);
    174         mEnvironment.setStop(false);
    175     }
    176 
    177     public void setOriginal(Bitmap bitmap) {
    178         mOriginalBitmap = bitmap;
    179         Log.v(LOGTAG,"setOriginal, size " + bitmap.getWidth() + " x " + bitmap.getHeight());
    180         ImagePreset preset = MasterImage.getImage().getPreset();
    181         setupEnvironment(preset, false);
    182         updateOriginalAllocation(preset);
    183     }
    184 
    185     private synchronized boolean updateOriginalAllocation(ImagePreset preset) {
    186         if (preset == null) {
    187             return false;
    188         }
    189         Bitmap originalBitmap = mOriginalBitmap;
    190 
    191         if (originalBitmap == null) {
    192             return false;
    193         }
    194 
    195         RenderScript RS = getRenderScriptContext();
    196 
    197         Allocation filtersOnlyOriginalAllocation = mFiltersOnlyOriginalAllocation;
    198         mFiltersOnlyOriginalAllocation = Allocation.createFromBitmap(RS, originalBitmap,
    199                 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
    200         if (filtersOnlyOriginalAllocation != null) {
    201             filtersOnlyOriginalAllocation.destroy();
    202         }
    203 
    204         Allocation originalAllocation = mOriginalAllocation;
    205         mResizedOriginalBitmap = preset.applyGeometry(originalBitmap, mEnvironment);
    206         mOriginalAllocation = Allocation.createFromBitmap(RS, mResizedOriginalBitmap,
    207                 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
    208         if (originalAllocation != null) {
    209             originalAllocation.destroy();
    210         }
    211 
    212         return true;
    213     }
    214 
    215     public void renderHighres(RenderingRequest request) {
    216         synchronized (CachingPipeline.class) {
    217             if (getRenderScriptContext() == null) {
    218                 return;
    219             }
    220             ImagePreset preset = request.getImagePreset();
    221             setupEnvironment(preset, false);
    222             Bitmap bitmap = MasterImage.getImage().getOriginalBitmapHighres();
    223             if (bitmap == null) {
    224                 return;
    225             }
    226             bitmap = mEnvironment.getBitmapCopy(bitmap, BitmapCache.HIGHRES);
    227             bitmap = preset.applyGeometry(bitmap, mEnvironment);
    228 
    229             mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
    230             Bitmap bmp = preset.apply(bitmap, mEnvironment);
    231             if (!mEnvironment.needsStop()) {
    232                 request.setBitmap(bmp);
    233             } else {
    234                 mEnvironment.cache(bmp);
    235             }
    236             mFiltersManager.freeFilterResources(preset);
    237         }
    238     }
    239 
    240     public void renderGeometry(RenderingRequest request) {
    241         synchronized (CachingPipeline.class) {
    242             if (getRenderScriptContext() == null) {
    243                 return;
    244             }
    245             ImagePreset preset = request.getImagePreset();
    246             setupEnvironment(preset, false);
    247             Bitmap bitmap = MasterImage.getImage().getOriginalBitmapHighres();
    248             if (bitmap == null) {
    249                 return;
    250             }
    251             bitmap = mEnvironment.getBitmapCopy(bitmap, BitmapCache.GEOMETRY);
    252             bitmap = preset.applyGeometry(bitmap, mEnvironment);
    253             if (!mEnvironment.needsStop()) {
    254                 request.setBitmap(bitmap);
    255             } else {
    256                 mEnvironment.cache(bitmap);
    257             }
    258             mFiltersManager.freeFilterResources(preset);
    259         }
    260     }
    261 
    262     public void renderFilters(RenderingRequest request) {
    263         synchronized (CachingPipeline.class) {
    264             if (getRenderScriptContext() == null) {
    265                 return;
    266             }
    267             ImagePreset preset = request.getImagePreset();
    268             setupEnvironment(preset, false);
    269             Bitmap bitmap = MasterImage.getImage().getOriginalBitmapHighres();
    270             if (bitmap == null) {
    271                 return;
    272             }
    273             bitmap = mEnvironment.getBitmapCopy(bitmap, BitmapCache.FILTERS);
    274             bitmap = preset.apply(bitmap, mEnvironment);
    275             if (!mEnvironment.needsStop()) {
    276                 request.setBitmap(bitmap);
    277             } else {
    278                 mEnvironment.cache(bitmap);
    279             }
    280             mFiltersManager.freeFilterResources(preset);
    281         }
    282     }
    283 
    284     public synchronized void render(RenderingRequest request) {
    285         // TODO: cleanup/remove GEOMETRY / FILTERS paths
    286         synchronized (CachingPipeline.class) {
    287             if (getRenderScriptContext() == null) {
    288                 return;
    289             }
    290             if ((request.getType() != RenderingRequest.PARTIAL_RENDERING
    291                   && request.getType() != RenderingRequest.ICON_RENDERING
    292                     && request.getBitmap() == null)
    293                     || request.getImagePreset() == null) {
    294                 return;
    295             }
    296 
    297             if (DEBUG) {
    298                 Log.v(LOGTAG, "render image of type " + getType(request));
    299             }
    300 
    301             Bitmap bitmap = request.getBitmap();
    302             ImagePreset preset = request.getImagePreset();
    303             setupEnvironment(preset, true);
    304             mFiltersManager.freeFilterResources(preset);
    305 
    306             if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
    307                 MasterImage master = MasterImage.getImage();
    308                 bitmap = ImageLoader.getScaleOneImageForPreset(master.getActivity(),
    309                         mEnvironment.getBimapCache(),
    310                         master.getUri(), request.getBounds(),
    311                         request.getDestination());
    312                 if (bitmap == null) {
    313                     Log.w(LOGTAG, "could not get bitmap for: " + getType(request));
    314                     return;
    315                 }
    316             }
    317 
    318             if (request.getType() == RenderingRequest.FULL_RENDERING
    319                     || request.getType() == RenderingRequest.GEOMETRY_RENDERING
    320                     || request.getType() == RenderingRequest.FILTERS_RENDERING) {
    321                 updateOriginalAllocation(preset);
    322             }
    323 
    324             if (DEBUG && bitmap != null) {
    325                 Log.v(LOGTAG, "after update, req bitmap (" + bitmap.getWidth() + "x" + bitmap.getHeight()
    326                         + " ? resizeOriginal (" + mResizedOriginalBitmap.getWidth() + "x"
    327                         + mResizedOriginalBitmap.getHeight());
    328             }
    329 
    330             if (request.getType() == RenderingRequest.FULL_RENDERING
    331                     || request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
    332                 mOriginalAllocation.copyTo(bitmap);
    333             } else if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
    334                 mFiltersOnlyOriginalAllocation.copyTo(bitmap);
    335             }
    336 
    337             if (request.getType() == RenderingRequest.FULL_RENDERING
    338                     || request.getType() == RenderingRequest.FILTERS_RENDERING
    339                     || request.getType() == RenderingRequest.ICON_RENDERING
    340                     || request.getType() == RenderingRequest.PARTIAL_RENDERING
    341                     || request.getType() == RenderingRequest.STYLE_ICON_RENDERING) {
    342 
    343                 if (request.getType() == RenderingRequest.ICON_RENDERING) {
    344                     mEnvironment.setQuality(FilterEnvironment.QUALITY_ICON);
    345                 } else {
    346                     mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW);
    347                 }
    348 
    349                 if (request.getType() == RenderingRequest.ICON_RENDERING) {
    350                     Rect iconBounds = request.getIconBounds();
    351                     Bitmap source = MasterImage.getImage().getThumbnailBitmap();
    352                     if (iconBounds.width() > source.getWidth() * 2) {
    353                         source = MasterImage.getImage().getLargeThumbnailBitmap();
    354                     }
    355                     if (iconBounds != null) {
    356                         bitmap = mEnvironment.getBitmap(iconBounds.width(),
    357                                 iconBounds.height(), BitmapCache.ICON);
    358                         Canvas canvas = new Canvas(bitmap);
    359                         Matrix m = new Matrix();
    360                         float minSize = Math.min(source.getWidth(), source.getHeight());
    361                         float maxSize = Math.max(iconBounds.width(), iconBounds.height());
    362                         float scale = maxSize / minSize;
    363                         m.setScale(scale, scale);
    364                         float dx = (iconBounds.width() - (source.getWidth() * scale))/2.0f;
    365                         float dy = (iconBounds.height() - (source.getHeight() * scale))/2.0f;
    366                         m.postTranslate(dx, dy);
    367                         canvas.drawBitmap(source, m, new Paint(Paint.FILTER_BITMAP_FLAG));
    368                     } else {
    369                         bitmap = mEnvironment.getBitmapCopy(source, BitmapCache.ICON);
    370                     }
    371                 }
    372                 Bitmap bmp = preset.apply(bitmap, mEnvironment);
    373                 if (!mEnvironment.needsStop()) {
    374                     request.setBitmap(bmp);
    375                 }
    376                 mFiltersManager.freeFilterResources(preset);
    377             }
    378         }
    379     }
    380 
    381     public synchronized void renderImage(ImagePreset preset, Allocation in, Allocation out) {
    382         synchronized (CachingPipeline.class) {
    383             if (getRenderScriptContext() == null) {
    384                 return;
    385             }
    386             setupEnvironment(preset, false);
    387             mFiltersManager.freeFilterResources(preset);
    388             preset.applyFilters(-1, -1, in, out, mEnvironment);
    389             boolean copyOut = false;
    390             if (preset.nbFilters() > 0) {
    391                 copyOut = true;
    392             }
    393             preset.applyBorder(in, out, copyOut, mEnvironment);
    394         }
    395     }
    396 
    397     public synchronized Bitmap renderFinalImage(Bitmap bitmap, ImagePreset preset) {
    398         synchronized (CachingPipeline.class) {
    399             if (getRenderScriptContext() == null) {
    400                 return bitmap;
    401             }
    402             setupEnvironment(preset, false);
    403             mEnvironment.setQuality(FilterEnvironment.QUALITY_FINAL);
    404             mEnvironment.setScaleFactor(1.0f);
    405             mFiltersManager.freeFilterResources(preset);
    406             bitmap = preset.applyGeometry(bitmap, mEnvironment);
    407             bitmap = preset.apply(bitmap, mEnvironment);
    408             return bitmap;
    409         }
    410     }
    411 
    412     public Bitmap renderGeometryIcon(Bitmap bitmap, ImagePreset preset) {
    413         return GeometryMathUtils.applyGeometryRepresentations(preset.getGeometryFilters(), bitmap);
    414     }
    415 
    416     public void compute(SharedBuffer buffer, ImagePreset preset, int type) {
    417         if (getRenderScriptContext() == null) {
    418             return;
    419         }
    420         setupEnvironment(preset, false);
    421         Vector<FilterRepresentation> filters = preset.getFilters();
    422         Bitmap result = mCachedProcessing.process(mOriginalBitmap, filters, mEnvironment);
    423         buffer.setProducer(result);
    424         mEnvironment.cache(result);
    425     }
    426 
    427     public boolean needsRepaint() {
    428         SharedBuffer buffer = MasterImage.getImage().getPreviewBuffer();
    429         return buffer.checkRepaintNeeded();
    430     }
    431 
    432     public void setPreviewScaleFactor(float previewScaleFactor) {
    433         mPreviewScaleFactor = previewScaleFactor;
    434     }
    435 
    436     public void setHighResPreviewScaleFactor(float highResPreviewScaleFactor) {
    437         mHighResPreviewScaleFactor = highResPreviewScaleFactor;
    438     }
    439 
    440     public synchronized boolean isInitialized() {
    441         return getRenderScriptContext() != null && mOriginalBitmap != null;
    442     }
    443 
    444     public boolean prepareRenderscriptAllocations(Bitmap bitmap) {
    445         RenderScript RS = getRenderScriptContext();
    446         boolean needsUpdate = false;
    447         if (mOutPixelsAllocation == null || mInPixelsAllocation == null ||
    448                 bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight) {
    449             destroyPixelAllocations();
    450             Bitmap bitmapBuffer = bitmap;
    451             if (bitmap.getConfig() == null || bitmap.getConfig() != BITMAP_CONFIG) {
    452                 bitmapBuffer = bitmap.copy(BITMAP_CONFIG, true);
    453             }
    454             mOutPixelsAllocation = Allocation.createFromBitmap(RS, bitmapBuffer,
    455                     Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
    456             mInPixelsAllocation = Allocation.createTyped(RS,
    457                     mOutPixelsAllocation.getType());
    458             needsUpdate = true;
    459         }
    460         if (RS != null) {
    461             mInPixelsAllocation.copyFrom(bitmap);
    462         }
    463         if (bitmap.getWidth() != mWidth
    464                 || bitmap.getHeight() != mHeight) {
    465             mWidth = bitmap.getWidth();
    466             mHeight = bitmap.getHeight();
    467             needsUpdate = true;
    468         }
    469         if (DEBUG) {
    470             Log.v(LOGTAG, "prepareRenderscriptAllocations: " + needsUpdate + " in " + getName());
    471         }
    472         return needsUpdate;
    473     }
    474 
    475     public synchronized Allocation getInPixelsAllocation() {
    476         return mInPixelsAllocation;
    477     }
    478 
    479     public synchronized Allocation getOutPixelsAllocation() {
    480         return mOutPixelsAllocation;
    481     }
    482 
    483     public String getName() {
    484         return mName;
    485     }
    486 
    487     public RenderScript getRSContext() {
    488         return CachingPipeline.getRenderScriptContext();
    489     }
    490 }
    491