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.Color;
     22 import android.graphics.LinearGradient;
     23 import android.graphics.Paint;
     24 import android.graphics.PorterDuff;
     25 import android.graphics.PorterDuffXfermode;
     26 import android.graphics.Rect;
     27 import android.graphics.Shader;
     28 import android.graphics.Typeface;
     29 import android.graphics.Paint.Align;
     30 import android.util.FloatMath;
     31 
     32 import com.cooliris.app.App;
     33 
     34 public final class StringTexture extends Texture {
     35     private String mString;
     36     private Config mConfig;
     37     private Paint mPaint;
     38     private int mBaselineHeight;
     39 
     40     private static final Paint sPaint = new Paint();
     41 
     42     public static int computeTextWidthForConfig(String string, Config config) {
     43         return computeTextWidthForConfig(config.fontSize, config.bold ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT, string);
     44     }
     45 
     46     public static int computeTextWidthForConfig(float textSize, Typeface typeface, String string) {
     47         Paint paint = sPaint;
     48         synchronized (paint) {
     49             paint.setAntiAlias(true);
     50             paint.setTypeface(typeface);
     51             paint.setTextSize(textSize);
     52             // 10 pixel buffer to compensate for the shade at the end.
     53             return (int) (10.0f * App.PIXEL_DENSITY) + (int) FloatMath.ceil(paint.measureText(string));
     54         }
     55     }
     56 
     57     public static int lengthToFit(float textSize, float maxWidth, Typeface typeface, String string) {
     58         if (maxWidth <= 0)
     59             return 0;
     60         Paint paint = sPaint;
     61         paint.setAntiAlias(true);
     62         paint.setTypeface(typeface);
     63         paint.setTextSize(textSize);
     64         int length = string.length();
     65         float retVal = paint.measureText(string);
     66         if (retVal <= maxWidth)
     67             return length;
     68         else {
     69             while (retVal > maxWidth) {
     70                 --length;
     71                 retVal = paint.measureText(string, 0, length - 1);
     72             }
     73             return length;
     74         }
     75     }
     76 
     77     public StringTexture(String string) {
     78         mString = string;
     79         mConfig = Config.DEFAULT_CONFIG_SCALED;
     80     }
     81 
     82     public StringTexture(String string, Config config) {
     83         this(string, config, config.width, config.height);
     84     }
     85 
     86     public StringTexture(String string, Config config, int width, int height) {
     87         mString = string;
     88         mConfig = config;
     89         mWidth = width;
     90         mHeight = height;
     91     }
     92 
     93     public float computeTextWidth() {
     94         Paint paint = computePaint();
     95         if (paint != null) {
     96             if (mString != null)
     97                 return paint.measureText(mString);
     98             else
     99                 return 0;
    100         } else {
    101             return 0;
    102         }
    103     }
    104 
    105     @Override
    106     public boolean isCached() {
    107         return true;
    108     }
    109 
    110     public float getBaselineHeight() {
    111         return mBaselineHeight;
    112     }
    113 
    114     protected Paint computePaint() {
    115         if (mPaint != null)
    116             return mPaint;
    117         Paint paint = new Paint();
    118         mPaint = paint;
    119         paint.setAntiAlias(true);
    120         Config config = mConfig;
    121         int alpha = (int) (config.a * 255);
    122         int red = (int) (config.r * 255);
    123         int green = (int) (config.g * 255);
    124         int blue = (int) (config.b * 255);
    125         int color = Color.argb(alpha, red, green, blue);
    126         paint.setColor(color);
    127         paint.setShadowLayer(config.shadowRadius, 0, 0, Color.BLACK);
    128         paint.setUnderlineText(config.underline);
    129         paint.setTypeface(config.bold ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
    130         paint.setStrikeThruText(config.strikeThrough);
    131         // int originX = 0;
    132         if (config.xalignment == Config.ALIGN_LEFT) {
    133             paint.setTextAlign(Align.LEFT);
    134         } else if (config.xalignment == Config.ALIGN_RIGHT) {
    135             paint.setTextAlign(Align.RIGHT);
    136             // originX = (int)config.width;
    137         } else {
    138             paint.setTextAlign(Align.CENTER);
    139             // originX = (int)config.height;
    140         }
    141         if (config.italic)
    142             paint.setTextSkewX(-0.25f);
    143         String stringToDraw = mString;
    144         paint.setTextSize(config.fontSize);
    145         if (config.sizeMode == Config.SIZE_TEXT_TO_BOUNDS) {
    146             // we have to compute the fontsize appropriately
    147             while (true) {
    148                 // we measure the textwidth
    149                 float currentTextSize = paint.getTextSize();
    150                 float measuredTextWidth = 0;
    151                 measuredTextWidth = paint.measureText(stringToDraw);
    152                 if (measuredTextWidth < mWidth)
    153                     break;
    154                 paint.setTextSize(currentTextSize - 1.0f);
    155                 if (currentTextSize <= 6.0f)
    156                     break;
    157             }
    158         }
    159         return paint;
    160     }
    161 
    162     @Override
    163     protected Bitmap load(RenderView view) {
    164         if (mString == null)
    165             return null;
    166         Paint paint = computePaint();
    167         String stringToDraw = mString;
    168         Config config = mConfig;
    169         Bitmap.Config bmConfig = Bitmap.Config.ARGB_4444;
    170         Paint.FontMetricsInt metrics = paint.getFontMetricsInt();
    171         // 1 pixel for anti-aliasing
    172         int padding = 1 + config.shadowRadius;
    173         int ascent = metrics.ascent - padding;
    174         int descent = metrics.descent + padding;
    175         int backWidth = mWidth;
    176         int backHeight = mHeight;
    177 
    178         String string = mString;
    179         Rect bounds = new Rect();
    180         paint.getTextBounds(string, 0, string.length(), bounds);
    181 
    182         if (config.sizeMode == Config.SIZE_BOUNDS_TO_TEXT) {
    183             // do something else
    184             backWidth = bounds.width() + 2 * padding;
    185             int height = descent - ascent;
    186             backHeight = height + padding;
    187         }
    188         if (backWidth <= 0 || backHeight <= 0)
    189             return null;
    190         Bitmap bitmap = Bitmap.createBitmap(backWidth, backHeight, bmConfig);
    191         Canvas canvas = new Canvas(bitmap);
    192         // for top
    193         int x = (config.xalignment == Config.ALIGN_LEFT) ? padding : (config.xalignment == Config.ALIGN_RIGHT ? backWidth - padding
    194                 : backWidth / 2);
    195         int y = (config.yalignment == Config.ALIGN_TOP) ? -metrics.top + padding
    196                 : ((config.yalignment == Config.ALIGN_BOTTOM) ? (backHeight - descent)
    197                         : ((int) backHeight - (descent + ascent)) / 2);
    198         // bitmap.eraseColor(0xff00ff00);
    199         canvas.drawText(stringToDraw, x, y, paint);
    200 
    201         if (bounds.width() > backWidth && config.overflowMode == Config.OVERFLOW_FADE) {
    202             // Fade the right edge of the string if the text overflows. TODO:
    203             // BIDI text should fade on the left.
    204             float gradientLeft = backWidth - Config.FADE_WIDTH;
    205             LinearGradient gradient = new LinearGradient(gradientLeft, 0, backWidth, 0, 0xffffffff, 0x00ffffff,
    206                     Shader.TileMode.CLAMP);
    207             paint = new Paint();
    208             paint.setSubpixelText(true);
    209             paint.setShader(gradient);
    210             paint.setDither(true);
    211             paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
    212             canvas.drawRect(gradientLeft, 0, backWidth, backHeight, paint);
    213         }
    214 
    215         mBaselineHeight = padding + metrics.bottom;
    216         return bitmap;
    217     }
    218 
    219     public static final class Config {
    220         public static final int SIZE_EXACT = 0;
    221         public static final int SIZE_TEXT_TO_BOUNDS = 1;
    222         public static final int SIZE_BOUNDS_TO_TEXT = 2;
    223 
    224         public static final int OVERFLOW_CLIP = 0;
    225         public static final int OVERFLOW_ELLIPSIZE = 1;
    226         public static final int OVERFLOW_FADE = 2;
    227 
    228         public static final int ALIGN_HCENTER = 0;
    229         public static final int ALIGN_LEFT = 1;
    230         public static final int ALIGN_RIGHT = 2;
    231         public static final int ALIGN_TOP = 3;
    232         public static final int ALIGN_BOTTOM = 4;
    233         public static final int ALIGN_VCENTER = 5;
    234 
    235         private static final int FADE_WIDTH = 30;
    236 
    237         public static final Config DEFAULT_CONFIG_SCALED = new Config();
    238         public static final Config DEFAULT_CONFIG_TRUNCATED = new Config(SIZE_TEXT_TO_BOUNDS);
    239 
    240         public float fontSize = 20f;
    241         public float r = 1f;
    242         public float g = 1f;
    243         public float b = 1f;
    244         public float a = 1f;
    245         public int shadowRadius = 4 * (int) App.PIXEL_DENSITY;
    246         public boolean underline = false;
    247         public boolean bold = false;
    248         public boolean italic = false;
    249         public boolean strikeThrough = false;
    250         public int width = 256; // TODO: there is no good default for this,
    251                                 // require explicit specification.
    252         public int height = 32;
    253         public int xalignment = ALIGN_LEFT;
    254         public int yalignment = ALIGN_VCENTER;
    255         public int sizeMode = SIZE_BOUNDS_TO_TEXT;
    256         public int overflowMode = OVERFLOW_FADE;
    257 
    258         public Config() {
    259         }
    260 
    261         public Config(int sizeMode) {
    262             this.sizeMode = sizeMode;
    263         }
    264 
    265         public Config(float fontSize, int width, int height) {
    266             this.fontSize = fontSize;
    267             this.width = width;
    268             this.height = height;
    269             this.sizeMode = SIZE_EXACT;
    270         }
    271     };
    272 
    273 }
    274