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