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 CachingPipeline(FiltersManager filtersManager, String name)68 public CachingPipeline(FiltersManager filtersManager, String name) { 69 mFiltersManager = filtersManager; 70 mName = name; 71 } 72 getRenderScriptContext()73 public static synchronized RenderScript getRenderScriptContext() { 74 return sRS; 75 } 76 createRenderscriptContext(Context context)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 destroyRenderScriptContext()85 public static synchronized void destroyRenderScriptContext() { 86 if (sRS != null) { 87 sRS.destroy(); 88 } 89 sRS = null; 90 } 91 stop()92 public void stop() { 93 mEnvironment.setStop(true); 94 } 95 reset()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 getResources()121 public Resources getResources() { 122 return sRS.getApplicationContext().getResources(); 123 } 124 destroyPixelAllocations()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 getType(RenderingRequest request)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 setupEnvironment(ImagePreset preset, boolean highResPreview)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 setOriginal(Bitmap bitmap)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 updateOriginalAllocation(ImagePreset preset)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 renderHighres(RenderingRequest request)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 renderGeometry(RenderingRequest request)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 renderFilters(RenderingRequest request)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 render(RenderingRequest request)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 renderImage(ImagePreset preset, Allocation in, Allocation out)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 renderFinalImage(Bitmap bitmap, ImagePreset preset)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 renderGeometryIcon(Bitmap bitmap, ImagePreset preset)412 public Bitmap renderGeometryIcon(Bitmap bitmap, ImagePreset preset) { 413 return GeometryMathUtils.applyGeometryRepresentations(preset.getGeometryFilters(), bitmap); 414 } 415 compute(SharedBuffer buffer, ImagePreset preset, int type)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 needsRepaint()427 public boolean needsRepaint() { 428 SharedBuffer buffer = MasterImage.getImage().getPreviewBuffer(); 429 return buffer.checkRepaintNeeded(); 430 } 431 setPreviewScaleFactor(float previewScaleFactor)432 public void setPreviewScaleFactor(float previewScaleFactor) { 433 mPreviewScaleFactor = previewScaleFactor; 434 } 435 setHighResPreviewScaleFactor(float highResPreviewScaleFactor)436 public void setHighResPreviewScaleFactor(float highResPreviewScaleFactor) { 437 mHighResPreviewScaleFactor = highResPreviewScaleFactor; 438 } 439 isInitialized()440 public synchronized boolean isInitialized() { 441 return getRenderScriptContext() != null && mOriginalBitmap != null; 442 } 443 prepareRenderscriptAllocations(Bitmap bitmap)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 getInPixelsAllocation()475 public synchronized Allocation getInPixelsAllocation() { 476 return mInPixelsAllocation; 477 } 478 getOutPixelsAllocation()479 public synchronized Allocation getOutPixelsAllocation() { 480 return mOutPixelsAllocation; 481 } 482 getName()483 public String getName() { 484 return mName; 485 } 486 getRSContext()487 public RenderScript getRSContext() { 488 return CachingPipeline.getRenderScriptContext(); 489 } 490 } 491