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