/* * Copyright (C) 2013 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 com.android.gallery3d.filtershow.pipeline; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.renderscript.Allocation; import android.renderscript.RenderScript; import android.util.Log; import com.android.gallery3d.filtershow.cache.BitmapCache; import com.android.gallery3d.filtershow.cache.ImageLoader; import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.filters.FiltersManager; import com.android.gallery3d.filtershow.imageshow.GeometryMathUtils; import com.android.gallery3d.filtershow.imageshow.MasterImage; import java.util.Vector; public class CachingPipeline implements PipelineInterface { private static final String LOGTAG = "CachingPipeline"; private boolean DEBUG = false; private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_8888; private static volatile RenderScript sRS = null; private FiltersManager mFiltersManager = null; private volatile Bitmap mOriginalBitmap = null; private volatile Bitmap mResizedOriginalBitmap = null; private FilterEnvironment mEnvironment = new FilterEnvironment(); private CacheProcessing mCachedProcessing = new CacheProcessing(); private volatile Allocation mOriginalAllocation = null; private volatile Allocation mFiltersOnlyOriginalAllocation = null; protected volatile Allocation mInPixelsAllocation; protected volatile Allocation mOutPixelsAllocation; private volatile int mWidth = 0; private volatile int mHeight = 0; private volatile float mPreviewScaleFactor = 1.0f; private volatile float mHighResPreviewScaleFactor = 1.0f; private volatile String mName = ""; public CachingPipeline(FiltersManager filtersManager, String name) { mFiltersManager = filtersManager; mName = name; } public static synchronized RenderScript getRenderScriptContext() { return sRS; } public static synchronized void createRenderscriptContext(Context context) { if (sRS != null) { Log.w(LOGTAG, "A prior RS context exists when calling setRenderScriptContext"); destroyRenderScriptContext(); } sRS = RenderScript.create(context); } public static synchronized void destroyRenderScriptContext() { if (sRS != null) { sRS.destroy(); } sRS = null; } public void stop() { mEnvironment.setStop(true); } public synchronized void reset() { synchronized (CachingPipeline.class) { if (getRenderScriptContext() == null) { return; } mOriginalBitmap = null; // just a reference to the bitmap in ImageLoader if (mResizedOriginalBitmap != null) { mResizedOriginalBitmap.recycle(); mResizedOriginalBitmap = null; } if (mOriginalAllocation != null) { mOriginalAllocation.destroy(); mOriginalAllocation = null; } if (mFiltersOnlyOriginalAllocation != null) { mFiltersOnlyOriginalAllocation.destroy(); mFiltersOnlyOriginalAllocation = null; } mPreviewScaleFactor = 1.0f; mHighResPreviewScaleFactor = 1.0f; destroyPixelAllocations(); } } public Resources getResources() { return sRS.getApplicationContext().getResources(); } private synchronized void destroyPixelAllocations() { if (DEBUG) { Log.v(LOGTAG, "destroyPixelAllocations in " + getName()); } if (mInPixelsAllocation != null) { mInPixelsAllocation.destroy(); mInPixelsAllocation = null; } if (mOutPixelsAllocation != null) { mOutPixelsAllocation.destroy(); mOutPixelsAllocation = null; } mWidth = 0; mHeight = 0; } private String getType(RenderingRequest request) { if (request.getType() == RenderingRequest.ICON_RENDERING) { return "ICON_RENDERING"; } if (request.getType() == RenderingRequest.FILTERS_RENDERING) { return "FILTERS_RENDERING"; } if (request.getType() == RenderingRequest.FULL_RENDERING) { return "FULL_RENDERING"; } if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) { return "GEOMETRY_RENDERING"; } if (request.getType() == RenderingRequest.PARTIAL_RENDERING) { return "PARTIAL_RENDERING"; } if (request.getType() == RenderingRequest.HIGHRES_RENDERING) { return "HIGHRES_RENDERING"; } return "UNKNOWN TYPE!"; } private void setupEnvironment(ImagePreset preset, boolean highResPreview) { mEnvironment.setPipeline(this); mEnvironment.setFiltersManager(mFiltersManager); mEnvironment.setBitmapCache(MasterImage.getImage().getBitmapCache()); if (highResPreview) { mEnvironment.setScaleFactor(mHighResPreviewScaleFactor); } else { mEnvironment.setScaleFactor(mPreviewScaleFactor); } mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW); mEnvironment.setImagePreset(preset); mEnvironment.setStop(false); } public void setOriginal(Bitmap bitmap) { mOriginalBitmap = bitmap; Log.v(LOGTAG,"setOriginal, size " + bitmap.getWidth() + " x " + bitmap.getHeight()); ImagePreset preset = MasterImage.getImage().getPreset(); setupEnvironment(preset, false); updateOriginalAllocation(preset); } private synchronized boolean updateOriginalAllocation(ImagePreset preset) { if (preset == null) { return false; } Bitmap originalBitmap = mOriginalBitmap; if (originalBitmap == null) { return false; } RenderScript RS = getRenderScriptContext(); Allocation filtersOnlyOriginalAllocation = mFiltersOnlyOriginalAllocation; mFiltersOnlyOriginalAllocation = Allocation.createFromBitmap(RS, originalBitmap, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); if (filtersOnlyOriginalAllocation != null) { filtersOnlyOriginalAllocation.destroy(); } Allocation originalAllocation = mOriginalAllocation; mResizedOriginalBitmap = preset.applyGeometry(originalBitmap, mEnvironment); mOriginalAllocation = Allocation.createFromBitmap(RS, mResizedOriginalBitmap, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); if (originalAllocation != null) { originalAllocation.destroy(); } return true; } public void renderHighres(RenderingRequest request) { synchronized (CachingPipeline.class) { if (getRenderScriptContext() == null) { return; } ImagePreset preset = request.getImagePreset(); setupEnvironment(preset, false); Bitmap bitmap = MasterImage.getImage().getOriginalBitmapHighres(); if (bitmap == null) { return; } bitmap = mEnvironment.getBitmapCopy(bitmap, BitmapCache.HIGHRES); bitmap = preset.applyGeometry(bitmap, mEnvironment); mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW); Bitmap bmp = preset.apply(bitmap, mEnvironment); if (!mEnvironment.needsStop()) { request.setBitmap(bmp); } else { mEnvironment.cache(bmp); } mFiltersManager.freeFilterResources(preset); } } public void renderGeometry(RenderingRequest request) { synchronized (CachingPipeline.class) { if (getRenderScriptContext() == null) { return; } ImagePreset preset = request.getImagePreset(); setupEnvironment(preset, false); Bitmap bitmap = MasterImage.getImage().getOriginalBitmapHighres(); if (bitmap == null) { return; } bitmap = mEnvironment.getBitmapCopy(bitmap, BitmapCache.GEOMETRY); bitmap = preset.applyGeometry(bitmap, mEnvironment); if (!mEnvironment.needsStop()) { request.setBitmap(bitmap); } else { mEnvironment.cache(bitmap); } mFiltersManager.freeFilterResources(preset); } } public void renderFilters(RenderingRequest request) { synchronized (CachingPipeline.class) { if (getRenderScriptContext() == null) { return; } ImagePreset preset = request.getImagePreset(); setupEnvironment(preset, false); Bitmap bitmap = MasterImage.getImage().getOriginalBitmapHighres(); if (bitmap == null) { return; } bitmap = mEnvironment.getBitmapCopy(bitmap, BitmapCache.FILTERS); bitmap = preset.apply(bitmap, mEnvironment); if (!mEnvironment.needsStop()) { request.setBitmap(bitmap); } else { mEnvironment.cache(bitmap); } mFiltersManager.freeFilterResources(preset); } } public synchronized void render(RenderingRequest request) { // TODO: cleanup/remove GEOMETRY / FILTERS paths synchronized (CachingPipeline.class) { if (getRenderScriptContext() == null) { return; } if ((request.getType() != RenderingRequest.PARTIAL_RENDERING && request.getType() != RenderingRequest.ICON_RENDERING && request.getBitmap() == null) || request.getImagePreset() == null) { return; } if (DEBUG) { Log.v(LOGTAG, "render image of type " + getType(request)); } Bitmap bitmap = request.getBitmap(); ImagePreset preset = request.getImagePreset(); setupEnvironment(preset, true); mFiltersManager.freeFilterResources(preset); if (request.getType() == RenderingRequest.PARTIAL_RENDERING) { MasterImage master = MasterImage.getImage(); bitmap = ImageLoader.getScaleOneImageForPreset(master.getActivity(), mEnvironment.getBimapCache(), master.getUri(), request.getBounds(), request.getDestination()); if (bitmap == null) { Log.w(LOGTAG, "could not get bitmap for: " + getType(request)); return; } } if (request.getType() == RenderingRequest.FULL_RENDERING || request.getType() == RenderingRequest.GEOMETRY_RENDERING || request.getType() == RenderingRequest.FILTERS_RENDERING) { updateOriginalAllocation(preset); } if (DEBUG && bitmap != null) { Log.v(LOGTAG, "after update, req bitmap (" + bitmap.getWidth() + "x" + bitmap.getHeight() + " ? resizeOriginal (" + mResizedOriginalBitmap.getWidth() + "x" + mResizedOriginalBitmap.getHeight()); } if (request.getType() == RenderingRequest.FULL_RENDERING || request.getType() == RenderingRequest.GEOMETRY_RENDERING) { mOriginalAllocation.copyTo(bitmap); } else if (request.getType() == RenderingRequest.FILTERS_RENDERING) { mFiltersOnlyOriginalAllocation.copyTo(bitmap); } if (request.getType() == RenderingRequest.FULL_RENDERING || request.getType() == RenderingRequest.FILTERS_RENDERING || request.getType() == RenderingRequest.ICON_RENDERING || request.getType() == RenderingRequest.PARTIAL_RENDERING || request.getType() == RenderingRequest.STYLE_ICON_RENDERING) { if (request.getType() == RenderingRequest.ICON_RENDERING) { mEnvironment.setQuality(FilterEnvironment.QUALITY_ICON); } else { mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW); } if (request.getType() == RenderingRequest.ICON_RENDERING) { Rect iconBounds = request.getIconBounds(); Bitmap source = MasterImage.getImage().getThumbnailBitmap(); if (iconBounds.width() > source.getWidth() * 2) { source = MasterImage.getImage().getLargeThumbnailBitmap(); } if (iconBounds != null) { bitmap = mEnvironment.getBitmap(iconBounds.width(), iconBounds.height(), BitmapCache.ICON); Canvas canvas = new Canvas(bitmap); Matrix m = new Matrix(); float minSize = Math.min(source.getWidth(), source.getHeight()); float maxSize = Math.max(iconBounds.width(), iconBounds.height()); float scale = maxSize / minSize; m.setScale(scale, scale); float dx = (iconBounds.width() - (source.getWidth() * scale))/2.0f; float dy = (iconBounds.height() - (source.getHeight() * scale))/2.0f; m.postTranslate(dx, dy); canvas.drawBitmap(source, m, new Paint(Paint.FILTER_BITMAP_FLAG)); } else { bitmap = mEnvironment.getBitmapCopy(source, BitmapCache.ICON); } } Bitmap bmp = preset.apply(bitmap, mEnvironment); if (!mEnvironment.needsStop()) { request.setBitmap(bmp); } mFiltersManager.freeFilterResources(preset); } } } public synchronized void renderImage(ImagePreset preset, Allocation in, Allocation out) { synchronized (CachingPipeline.class) { if (getRenderScriptContext() == null) { return; } setupEnvironment(preset, false); mFiltersManager.freeFilterResources(preset); preset.applyFilters(-1, -1, in, out, mEnvironment); boolean copyOut = false; if (preset.nbFilters() > 0) { copyOut = true; } preset.applyBorder(in, out, copyOut, mEnvironment); } } public synchronized Bitmap renderFinalImage(Bitmap bitmap, ImagePreset preset) { synchronized (CachingPipeline.class) { if (getRenderScriptContext() == null) { return bitmap; } setupEnvironment(preset, false); mEnvironment.setQuality(FilterEnvironment.QUALITY_FINAL); mEnvironment.setScaleFactor(1.0f); mFiltersManager.freeFilterResources(preset); bitmap = preset.applyGeometry(bitmap, mEnvironment); bitmap = preset.apply(bitmap, mEnvironment); return bitmap; } } public Bitmap renderGeometryIcon(Bitmap bitmap, ImagePreset preset) { return GeometryMathUtils.applyGeometryRepresentations(preset.getGeometryFilters(), bitmap); } public void compute(SharedBuffer buffer, ImagePreset preset, int type) { if (getRenderScriptContext() == null) { return; } setupEnvironment(preset, false); Vector filters = preset.getFilters(); Bitmap result = mCachedProcessing.process(mOriginalBitmap, filters, mEnvironment); buffer.setProducer(result); mEnvironment.cache(result); } public boolean needsRepaint() { SharedBuffer buffer = MasterImage.getImage().getPreviewBuffer(); return buffer.checkRepaintNeeded(); } public void setPreviewScaleFactor(float previewScaleFactor) { mPreviewScaleFactor = previewScaleFactor; } public void setHighResPreviewScaleFactor(float highResPreviewScaleFactor) { mHighResPreviewScaleFactor = highResPreviewScaleFactor; } public synchronized boolean isInitialized() { return getRenderScriptContext() != null && mOriginalBitmap != null; } public boolean prepareRenderscriptAllocations(Bitmap bitmap) { RenderScript RS = getRenderScriptContext(); boolean needsUpdate = false; if (mOutPixelsAllocation == null || mInPixelsAllocation == null || bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight) { destroyPixelAllocations(); Bitmap bitmapBuffer = bitmap; if (bitmap.getConfig() == null || bitmap.getConfig() != BITMAP_CONFIG) { bitmapBuffer = bitmap.copy(BITMAP_CONFIG, true); } mOutPixelsAllocation = Allocation.createFromBitmap(RS, bitmapBuffer, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); mInPixelsAllocation = Allocation.createTyped(RS, mOutPixelsAllocation.getType()); needsUpdate = true; } if (RS != null) { mInPixelsAllocation.copyFrom(bitmap); } if (bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight) { mWidth = bitmap.getWidth(); mHeight = bitmap.getHeight(); needsUpdate = true; } if (DEBUG) { Log.v(LOGTAG, "prepareRenderscriptAllocations: " + needsUpdate + " in " + getName()); } return needsUpdate; } public synchronized Allocation getInPixelsAllocation() { return mInPixelsAllocation; } public synchronized Allocation getOutPixelsAllocation() { return mOutPixelsAllocation; } public String getName() { return mName; } public RenderScript getRSContext() { return CachingPipeline.getRenderScriptContext(); } }