Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2009 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.cooliris.media;
     18 
     19 import android.graphics.Bitmap;
     20 import android.graphics.Canvas;
     21 import android.graphics.LightingColorFilter;
     22 import android.graphics.Paint;
     23 
     24 // CR: class comment
     25 public final class AdaptiveBackgroundTexture extends Texture {
     26     private static final int RED_MASK = 0xff0000;
     27     private static final int RED_MASK_SHIFT = 16;
     28     private static final int GREEN_MASK = 0x00ff00;
     29     private static final int GREEN_MASK_SHIFT = 8;
     30     private static final int BLUE_MASK = 0x0000ff;
     31     private static final int RADIUS = 4;
     32     private static final int KERNEL_SIZE = RADIUS * 2 + 1;
     33     private static final int NUM_COLORS = 256;
     34     private static final int MAX_COLOR_VALUE = NUM_COLORS - 1;
     35     private static final int[] KERNEL_NORM = new int[KERNEL_SIZE * NUM_COLORS];
     36     private static final int MULTIPLY_COLOR = 0xffaaaaaa;
     37     private static final int START_FADE_X = 96;
     38     private static final int THUMBNAIL_MAX_X = 128;
     39 
     40     private final int mWidth;
     41     private final int mHeight;
     42     private final Bitmap mSource;
     43     private Texture mBaseTexture;
     44 
     45     static {
     46         // Build a lookup table from summed to normalized kernel values.
     47         for (int i = KERNEL_SIZE * NUM_COLORS - 1; i >= 0; --i) {
     48             KERNEL_NORM[i] = i / KERNEL_SIZE;
     49         }
     50     }
     51 
     52     public AdaptiveBackgroundTexture(Bitmap source, int width, int height) {
     53         mSource = source;
     54         mWidth = width;
     55         mHeight = height;
     56         mBaseTexture = null;
     57     }
     58 
     59     public AdaptiveBackgroundTexture(Texture texture, int width, int height) {
     60         mBaseTexture = texture;
     61         mSource = null;
     62         mWidth = width;
     63         mHeight = height;
     64     }
     65 
     66     @Override
     67     protected boolean shouldQueue() {
     68         return true;
     69     }
     70 
     71     @Override
     72     public boolean isCached() {
     73         return true;
     74     }
     75 
     76     @Override
     77     protected Bitmap load(RenderView view) {
     78         // Determine a crop rectangle for the source image that is the aspect
     79         // ratio of the destination.
     80         Bitmap source = mSource;
     81         if (source == null) {
     82             if (mBaseTexture != null) {
     83                 source = mBaseTexture.load(view);
     84                 if (source == null) {
     85                     return null;
     86                 }
     87             } else {
     88                 return null;
     89             }
     90         }
     91         source = Utils.resizeBitmap(source, THUMBNAIL_MAX_X);
     92         int sourceWidth = source.getWidth();
     93         int sourceHeight = source.getHeight();
     94         int destWidth = mWidth;
     95         int destHeight = mHeight;
     96         float fitX = (float) sourceWidth / (float) destWidth;
     97         float fitY = (float) sourceHeight / (float) destHeight;
     98         float scale;
     99         int cropX;
    100         int cropY;
    101         int cropWidth;
    102         int cropHeight;
    103         if (fitX < fitY) {
    104             // Full width, partial height.
    105             cropWidth = sourceWidth;
    106             cropHeight = (int) (destHeight * fitX);
    107             cropX = 0;
    108             cropY = (sourceHeight - cropHeight) / 2;
    109             scale = 1.0f / fitX;
    110         } else {
    111             // Full height, partial or full width.
    112             cropWidth = (int) (destHeight * fitY);
    113             cropHeight = sourceHeight;
    114             cropX = (sourceWidth - cropWidth) / 2;
    115             cropY = 0;
    116             scale = 1f / fitY;
    117         }
    118 
    119         // Create a source and destination buffer for the image.
    120         int numPixels = cropWidth * cropHeight;
    121         int[] in = new int[numPixels];
    122         int[] tmp = new int[numPixels];
    123 
    124         // Get the source pixels as 32-bit ARGB.
    125         source.getPixels(in, 0, cropWidth, cropX, cropY, cropWidth, cropHeight);
    126 
    127         // Box blur is a separable kernel, so it is decomposed into a horizontal
    128         // and vertical pass.
    129         // The filter function applies the kernel across each row and transposes
    130         // the output.
    131         // Hence we apply it twice to provide efficient horizontal and vertical
    132         // convolution.
    133         // The filter discards the alpha channel.
    134         boxBlurFilter(in, tmp, cropWidth, cropHeight, cropWidth);
    135         boxBlurFilter(tmp, in, cropHeight, cropWidth, START_FADE_X);
    136 
    137         // Return a bitmap scaled to the desired size.
    138         Bitmap filtered = Bitmap.createBitmap(in, cropWidth, cropHeight, Bitmap.Config.ARGB_8888);
    139 
    140         // Composite the bitmap scaled to the target size and darken the pixels.
    141         Bitmap output = Bitmap.createBitmap(destWidth, destHeight, Bitmap.Config.ARGB_8888);
    142         Canvas canvas = new Canvas(output);
    143         Paint paint = new Paint();
    144         paint.setFilterBitmap(true);
    145         paint.setDither(true);
    146         paint.setColorFilter(new LightingColorFilter(MULTIPLY_COLOR, 0));
    147         canvas.scale(scale, scale);
    148         canvas.drawBitmap(filtered, 0f, 0f, paint);
    149         filtered.recycle();
    150 
    151         // Clear the texture
    152         mBaseTexture = null;
    153         return output;
    154     }
    155 
    156     private static void boxBlurFilter(int[] in, int[] out, int width, int height, int startFadeX) {
    157         int inPos = 0;
    158         int maxX = width - 1;
    159         for (int y = 0; y < height; ++y) {
    160             // Evaluate the kernel for the first pixel in the row.
    161             int red = 0;
    162             int green = 0;
    163             int blue = 0;
    164             for (int i = -RADIUS; i <= RADIUS; ++i) {
    165                 int argb = in[inPos + FloatUtils.clamp(i, 0, maxX)];
    166                 red += (argb & RED_MASK) >> RED_MASK_SHIFT;
    167                 green += (argb & GREEN_MASK) >> GREEN_MASK_SHIFT;
    168                 blue += argb & BLUE_MASK;
    169             }
    170             // Compute the alpha value.
    171             int alpha = (y < startFadeX) ? 0xff : ((height - y - 1) * MAX_COLOR_VALUE / (height - startFadeX));
    172             // Compute output values for the row.
    173             int outPos = y;
    174             for (int x = 0; x != width; ++x) { // CR: x < width
    175                 // Output the current pixel.
    176                 out[outPos] = (alpha << 24) | (KERNEL_NORM[red] << RED_MASK_SHIFT) | (KERNEL_NORM[green] << GREEN_MASK_SHIFT)
    177                         | KERNEL_NORM[blue];
    178                 // Slide to the next pixel, adding the new rightmost pixel and
    179                 // subtracting the former leftmost.
    180                 int prevX = FloatUtils.clamp(x - RADIUS, 0, maxX);
    181                 int nextX = FloatUtils.clamp(x + RADIUS + 1, 0, maxX);
    182                 int prevArgb = in[inPos + prevX];
    183                 int nextArgb = in[inPos + nextX];
    184                 red += ((nextArgb & RED_MASK) - (prevArgb & RED_MASK)) >> RED_MASK_SHIFT;
    185                 green += ((nextArgb & GREEN_MASK) - (prevArgb & GREEN_MASK)) >> GREEN_MASK_SHIFT;
    186                 blue += (nextArgb & BLUE_MASK) - (prevArgb & BLUE_MASK);
    187                 outPos += height;
    188             }
    189             inPos += width;
    190         }
    191     }
    192 }
    193