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.PrimaryImage; 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(PrimaryImage.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 = PrimaryImage.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 = PrimaryImage.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 = PrimaryImage.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 = PrimaryImage.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 PrimaryImage primary = PrimaryImage.getImage(); 308 bitmap = ImageLoader.getScaleOneImageForPreset(primary.getActivity(), 309 mEnvironment.getBimapCache(), 310 primary.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" 326 + bitmap.getHeight() + " ? resizeOriginal (" 327 + mResizedOriginalBitmap.getWidth() + "x" 328 + mResizedOriginalBitmap.getHeight()); 329 } 330 331 if (request.getType() == RenderingRequest.FULL_RENDERING 332 || request.getType() == RenderingRequest.GEOMETRY_RENDERING) { 333 mOriginalAllocation.copyTo(bitmap); 334 } else if (request.getType() == RenderingRequest.FILTERS_RENDERING) { 335 mFiltersOnlyOriginalAllocation.copyTo(bitmap); 336 } 337 338 if (request.getType() == RenderingRequest.FULL_RENDERING 339 || request.getType() == RenderingRequest.FILTERS_RENDERING 340 || request.getType() == RenderingRequest.ICON_RENDERING 341 || request.getType() == RenderingRequest.PARTIAL_RENDERING 342 || request.getType() == RenderingRequest.STYLE_ICON_RENDERING) { 343 344 if (request.getType() == RenderingRequest.ICON_RENDERING) { 345 mEnvironment.setQuality(FilterEnvironment.QUALITY_ICON); 346 } else { 347 mEnvironment.setQuality(FilterEnvironment.QUALITY_PREVIEW); 348 } 349 350 if (request.getType() == RenderingRequest.ICON_RENDERING) { 351 Rect iconBounds = request.getIconBounds(); 352 Bitmap source = PrimaryImage.getImage().getThumbnailBitmap(); 353 if (iconBounds.width() > source.getWidth() * 2) { 354 source = PrimaryImage.getImage().getLargeThumbnailBitmap(); 355 } 356 if (iconBounds != null) { 357 bitmap = mEnvironment.getBitmap(iconBounds.width(), 358 iconBounds.height(), BitmapCache.ICON); 359 Canvas canvas = new Canvas(bitmap); 360 Matrix m = new Matrix(); 361 float minSize = Math.min(source.getWidth(), source.getHeight()); 362 float maxSize = Math.max(iconBounds.width(), iconBounds.height()); 363 float scale = maxSize / minSize; 364 m.setScale(scale, scale); 365 float dx = (iconBounds.width() - (source.getWidth() * scale))/2.0f; 366 float dy = (iconBounds.height() - (source.getHeight() * scale))/2.0f; 367 m.postTranslate(dx, dy); 368 canvas.drawBitmap(source, m, new Paint(Paint.FILTER_BITMAP_FLAG)); 369 } else { 370 bitmap = mEnvironment.getBitmapCopy(source, BitmapCache.ICON); 371 } 372 } 373 Bitmap bmp = preset.apply(bitmap, mEnvironment); 374 if (!mEnvironment.needsStop()) { 375 request.setBitmap(bmp); 376 } 377 mFiltersManager.freeFilterResources(preset); 378 } 379 } 380 } 381 renderImage(ImagePreset preset, Allocation in, Allocation out)382 public synchronized void renderImage(ImagePreset preset, Allocation in, Allocation out) { 383 synchronized (CachingPipeline.class) { 384 if (getRenderScriptContext() == null) { 385 return; 386 } 387 setupEnvironment(preset, false); 388 mFiltersManager.freeFilterResources(preset); 389 preset.applyFilters(-1, -1, in, out, mEnvironment); 390 boolean copyOut = false; 391 if (preset.nbFilters() > 0) { 392 copyOut = true; 393 } 394 preset.applyBorder(in, out, copyOut, mEnvironment); 395 } 396 } 397 renderFinalImage(Bitmap bitmap, ImagePreset preset)398 public synchronized Bitmap renderFinalImage(Bitmap bitmap, ImagePreset preset) { 399 synchronized (CachingPipeline.class) { 400 if (getRenderScriptContext() == null) { 401 return bitmap; 402 } 403 setupEnvironment(preset, false); 404 mEnvironment.setQuality(FilterEnvironment.QUALITY_FINAL); 405 mEnvironment.setScaleFactor(1.0f); 406 mFiltersManager.freeFilterResources(preset); 407 bitmap = preset.applyGeometry(bitmap, mEnvironment); 408 bitmap = preset.apply(bitmap, mEnvironment); 409 return bitmap; 410 } 411 } 412 renderGeometryIcon(Bitmap bitmap, ImagePreset preset)413 public Bitmap renderGeometryIcon(Bitmap bitmap, ImagePreset preset) { 414 return GeometryMathUtils.applyGeometryRepresentations(preset.getGeometryFilters(), bitmap); 415 } 416 compute(SharedBuffer buffer, ImagePreset preset, int type)417 public void compute(SharedBuffer buffer, ImagePreset preset, int type) { 418 if (getRenderScriptContext() == null) { 419 return; 420 } 421 setupEnvironment(preset, false); 422 Vector<FilterRepresentation> filters = preset.getFilters(); 423 Bitmap result = mCachedProcessing.process(mOriginalBitmap, filters, mEnvironment); 424 buffer.setProducer(result); 425 mEnvironment.cache(result); 426 } 427 needsRepaint()428 public boolean needsRepaint() { 429 SharedBuffer buffer = PrimaryImage.getImage().getPreviewBuffer(); 430 return buffer.checkRepaintNeeded(); 431 } 432 setPreviewScaleFactor(float previewScaleFactor)433 public void setPreviewScaleFactor(float previewScaleFactor) { 434 mPreviewScaleFactor = previewScaleFactor; 435 } 436 setHighResPreviewScaleFactor(float highResPreviewScaleFactor)437 public void setHighResPreviewScaleFactor(float highResPreviewScaleFactor) { 438 mHighResPreviewScaleFactor = highResPreviewScaleFactor; 439 } 440 isInitialized()441 public synchronized boolean isInitialized() { 442 return getRenderScriptContext() != null && mOriginalBitmap != null; 443 } 444 prepareRenderscriptAllocations(Bitmap bitmap)445 public boolean prepareRenderscriptAllocations(Bitmap bitmap) { 446 RenderScript RS = getRenderScriptContext(); 447 boolean needsUpdate = false; 448 if (mOutPixelsAllocation == null || mInPixelsAllocation == null || 449 bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight) { 450 destroyPixelAllocations(); 451 Bitmap bitmapBuffer = bitmap; 452 if (bitmap.getConfig() == null || bitmap.getConfig() != BITMAP_CONFIG) { 453 bitmapBuffer = bitmap.copy(BITMAP_CONFIG, true); 454 } 455 mOutPixelsAllocation = Allocation.createFromBitmap(RS, bitmapBuffer, 456 Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT); 457 mInPixelsAllocation = Allocation.createTyped(RS, 458 mOutPixelsAllocation.getType()); 459 needsUpdate = true; 460 } 461 if (RS != null) { 462 mInPixelsAllocation.copyFrom(bitmap); 463 } 464 if (bitmap.getWidth() != mWidth 465 || bitmap.getHeight() != mHeight) { 466 mWidth = bitmap.getWidth(); 467 mHeight = bitmap.getHeight(); 468 needsUpdate = true; 469 } 470 if (DEBUG) { 471 Log.v(LOGTAG, "prepareRenderscriptAllocations: " + needsUpdate + " in " + getName()); 472 } 473 return needsUpdate; 474 } 475 getInPixelsAllocation()476 public synchronized Allocation getInPixelsAllocation() { 477 return mInPixelsAllocation; 478 } 479 getOutPixelsAllocation()480 public synchronized Allocation getOutPixelsAllocation() { 481 return mOutPixelsAllocation; 482 } 483 getName()484 public String getName() { 485 return mName; 486 } 487 getRSContext()488 public RenderScript getRSContext() { 489 return CachingPipeline.getRenderScriptContext(); 490 } 491 } 492