1 /* 2 * Copyright 2006, The Android Open Source Project 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #define LOG_TAG "PlatformGraphicsContext" 27 #define LOG_NDEBUG 1 28 29 #include "config.h" 30 #include "PlatformGraphicsContext.h" 31 32 #include "AndroidLog.h" 33 #include "SkBlurDrawLooper.h" 34 #include "SkBlurMaskFilter.h" 35 #include "SkColorPriv.h" 36 #include "SkDashPathEffect.h" 37 #include "SkPaint.h" 38 #include "SkShader.h" 39 #include "SkiaUtils.h" 40 41 namespace WebCore { 42 43 //************************************** 44 // Helper functions 45 //************************************** 46 47 static int RoundToInt(float x) 48 { 49 return (int)roundf(x); 50 } 51 52 template <typename T> T* deepCopyPtr(const T* src) 53 { 54 return src ? new T(*src) : 0; 55 } 56 57 // Set a bitmap shader that mimics dashing by width-on, width-off. 58 // Returns false if it could not succeed (e.g. there was an existing shader) 59 static bool setBitmapDash(SkPaint* paint, int width) { 60 if (width <= 0 || paint->getShader()) 61 return false; 62 63 SkColor c = paint->getColor(); 64 65 SkBitmap bm; 66 bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 1); 67 bm.allocPixels(); 68 bm.lockPixels(); 69 70 // set the ON pixel 71 *bm.getAddr32(0, 0) = SkPreMultiplyARGB(0xFF, SkColorGetR(c), 72 SkColorGetG(c), SkColorGetB(c)); 73 // set the OFF pixel 74 *bm.getAddr32(1, 0) = 0; 75 bm.unlockPixels(); 76 77 SkMatrix matrix; 78 matrix.setScale(SkIntToScalar(width), SK_Scalar1); 79 80 SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, 81 SkShader::kClamp_TileMode); 82 s->setLocalMatrix(matrix); 83 84 paint->setShader(s)->unref(); 85 return true; 86 } 87 88 //************************************** 89 // State implementation 90 //************************************** 91 92 PlatformGraphicsContext::State::State() 93 : pathEffect(0) 94 , miterLimit(4) 95 , alpha(1) 96 , strokeThickness(0) // Same as default in GraphicsContextPrivate.h 97 , lineCap(SkPaint::kDefault_Cap) 98 , lineJoin(SkPaint::kDefault_Join) 99 , mode(SkXfermode::kSrcOver_Mode) 100 , dashRatio(3) 101 , fillColor(SK_ColorBLACK) 102 , fillShader(0) 103 , strokeColor(SK_ColorBLACK) 104 , strokeShader(0) 105 , useAA(true) 106 , strokeStyle(SolidStroke) 107 { 108 } 109 110 PlatformGraphicsContext::State::State(const State& other) 111 : pathEffect(other.pathEffect) 112 , miterLimit(other.miterLimit) 113 , alpha(other.alpha) 114 , strokeThickness(other.strokeThickness) 115 , lineCap(other.lineCap) 116 , lineJoin(other.lineJoin) 117 , mode(other.mode) 118 , dashRatio(other.dashRatio) 119 , shadow(other.shadow) 120 , fillColor(other.fillColor) 121 , fillShader(other.fillShader) 122 , strokeColor(other.strokeColor) 123 , strokeShader(other.strokeShader) 124 , useAA(other.useAA) 125 , strokeStyle(other.strokeStyle) 126 { 127 SkSafeRef(pathEffect); 128 SkSafeRef(fillShader); 129 SkSafeRef(strokeShader); 130 } 131 132 PlatformGraphicsContext::State::~State() 133 { 134 SkSafeUnref(pathEffect); 135 SkSafeUnref(fillShader); 136 SkSafeUnref(strokeShader); 137 } 138 139 void PlatformGraphicsContext::State::setShadow(int radius, int dx, int dy, SkColor c) 140 { 141 // Cut the radius in half, to visually match the effect seen in 142 // safari browser 143 shadow.blur = SkScalarHalf(SkIntToScalar(radius)); 144 shadow.dx = SkIntToScalar(dx); 145 shadow.dy = SkIntToScalar(dy); 146 shadow.color = c; 147 } 148 149 bool PlatformGraphicsContext::State::setupShadowPaint(SkPaint* paint, SkPoint* offset, 150 bool shadowsIgnoreTransforms) 151 { 152 paint->setAntiAlias(true); 153 paint->setDither(true); 154 paint->setXfermodeMode(mode); 155 paint->setColor(shadow.color); 156 offset->set(shadow.dx, shadow.dy); 157 158 // Currently, only GraphicsContexts associated with the 159 // HTMLCanvasElement have shadows ignore transforms set. This 160 // allows us to distinguish between CSS and Canvas shadows which 161 // have different rendering specifications. 162 uint32_t flags = SkBlurMaskFilter::kHighQuality_BlurFlag; 163 if (shadowsIgnoreTransforms) { 164 offset->fY = -offset->fY; 165 flags |= SkBlurMaskFilter::kIgnoreTransform_BlurFlag; 166 } 167 168 if (shadow.blur > 0) { 169 paint->setMaskFilter(SkBlurMaskFilter::Create(shadow.blur, 170 SkBlurMaskFilter::kNormal_BlurStyle))->unref(); 171 } 172 return SkColorGetA(shadow.color) && (shadow.blur || shadow.dx || shadow.dy); 173 } 174 175 SkColor PlatformGraphicsContext::State::applyAlpha(SkColor c) const 176 { 177 int s = RoundToInt(alpha * 256); 178 if (s >= 256) 179 return c; 180 if (s < 0) 181 return 0; 182 183 int a = SkAlphaMul(SkColorGetA(c), s); 184 return (c & 0x00FFFFFF) | (a << 24); 185 } 186 187 // Returns a new State with all of this object's inherited properties copied. 188 PlatformGraphicsContext::State PlatformGraphicsContext::State::cloneInheritedProperties() 189 { 190 return PlatformGraphicsContext::State(*this); 191 } 192 193 //************************************** 194 // PlatformGraphicsContext 195 //************************************** 196 197 PlatformGraphicsContext::PlatformGraphicsContext() 198 { 199 m_stateStack.append(State()); 200 m_state = &m_stateStack.last(); 201 } 202 203 PlatformGraphicsContext::~PlatformGraphicsContext() 204 { 205 } 206 207 //************************************** 208 // State management 209 //************************************** 210 211 void PlatformGraphicsContext::save() 212 { 213 m_stateStack.append(m_state->cloneInheritedProperties()); 214 m_state = &m_stateStack.last(); 215 } 216 217 void PlatformGraphicsContext::restore() 218 { 219 m_stateStack.removeLast(); 220 m_state = &m_stateStack.last(); 221 } 222 223 //************************************** 224 // State setters 225 //************************************** 226 227 void PlatformGraphicsContext::setAlpha(float alpha) 228 { 229 m_state->alpha = alpha; 230 } 231 232 int PlatformGraphicsContext::getNormalizedAlpha() const 233 { 234 int alpha = roundf(m_state->alpha * 256); 235 if (alpha > 255) 236 alpha = 255; 237 else if (alpha < 0) 238 alpha = 0; 239 return alpha; 240 } 241 242 void PlatformGraphicsContext::setCompositeOperation(CompositeOperator op) 243 { 244 m_state->mode = WebCoreCompositeToSkiaComposite(op); 245 } 246 247 void PlatformGraphicsContext::setFillColor(const Color& c) 248 { 249 m_state->fillColor = c.rgb(); 250 setFillShader(0); 251 } 252 253 void PlatformGraphicsContext::setFillShader(SkShader* fillShader) 254 { 255 if (fillShader) 256 m_state->fillColor = Color::black; 257 258 if (fillShader != m_state->fillShader) { 259 SkSafeUnref(m_state->fillShader); 260 m_state->fillShader = fillShader; 261 SkSafeRef(m_state->fillShader); 262 } 263 } 264 265 void PlatformGraphicsContext::setLineCap(LineCap cap) 266 { 267 switch (cap) { 268 case ButtCap: 269 m_state->lineCap = SkPaint::kButt_Cap; 270 break; 271 case RoundCap: 272 m_state->lineCap = SkPaint::kRound_Cap; 273 break; 274 case SquareCap: 275 m_state->lineCap = SkPaint::kSquare_Cap; 276 break; 277 default: 278 ALOGD("PlatformGraphicsContextSkia::setLineCap: unknown LineCap %d\n", cap); 279 break; 280 } 281 } 282 283 void PlatformGraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) 284 { 285 size_t dashLength = dashes.size(); 286 if (!dashLength) 287 return; 288 289 size_t count = !(dashLength % 2) ? dashLength : dashLength * 2; 290 SkScalar* intervals = new SkScalar[count]; 291 292 for (unsigned int i = 0; i < count; i++) 293 intervals[i] = SkFloatToScalar(dashes[i % dashLength]); 294 SkPathEffect **effectPtr = &m_state->pathEffect; 295 SkSafeUnref(*effectPtr); 296 *effectPtr = new SkDashPathEffect(intervals, count, SkFloatToScalar(dashOffset)); 297 298 delete[] intervals; 299 } 300 301 void PlatformGraphicsContext::setLineJoin(LineJoin join) 302 { 303 switch (join) { 304 case MiterJoin: 305 m_state->lineJoin = SkPaint::kMiter_Join; 306 break; 307 case RoundJoin: 308 m_state->lineJoin = SkPaint::kRound_Join; 309 break; 310 case BevelJoin: 311 m_state->lineJoin = SkPaint::kBevel_Join; 312 break; 313 default: 314 ALOGD("PlatformGraphicsContextSkia::setLineJoin: unknown LineJoin %d\n", join); 315 break; 316 } 317 } 318 319 void PlatformGraphicsContext::setMiterLimit(float limit) 320 { 321 m_state->miterLimit = limit; 322 } 323 324 void PlatformGraphicsContext::setShadow(int radius, int dx, int dy, SkColor c) 325 { 326 m_state->setShadow(radius, dx, dy, c); 327 } 328 329 void PlatformGraphicsContext::setShouldAntialias(bool useAA) 330 { 331 m_state->useAA = useAA; 332 } 333 334 void PlatformGraphicsContext::setStrokeColor(const Color& c) 335 { 336 m_state->strokeColor = c.rgb(); 337 setStrokeShader(0); 338 } 339 340 void PlatformGraphicsContext::setStrokeShader(SkShader* strokeShader) 341 { 342 if (strokeShader) 343 m_state->strokeColor = Color::black; 344 345 if (strokeShader != m_state->strokeShader) { 346 SkSafeUnref(m_state->strokeShader); 347 m_state->strokeShader = strokeShader; 348 SkSafeRef(m_state->strokeShader); 349 } 350 } 351 352 void PlatformGraphicsContext::setStrokeStyle(StrokeStyle style) 353 { 354 m_state->strokeStyle = style; 355 } 356 357 void PlatformGraphicsContext::setStrokeThickness(float f) 358 { 359 m_state->strokeThickness = f; 360 } 361 362 //************************************** 363 // Paint setup 364 //************************************** 365 366 void PlatformGraphicsContext::setupPaintCommon(SkPaint* paint) const 367 { 368 paint->setAntiAlias(m_state->useAA); 369 paint->setDither(true); 370 paint->setXfermodeMode(m_state->mode); 371 if (SkColorGetA(m_state->shadow.color) > 0) { 372 373 // Currently, only GraphicsContexts associated with the 374 // HTMLCanvasElement have shadows ignore transforms set. This 375 // allows us to distinguish between CSS and Canvas shadows which 376 // have different rendering specifications. 377 SkScalar dy = m_state->shadow.dy; 378 uint32_t flags = SkBlurDrawLooper::kHighQuality_BlurFlag; 379 if (shadowsIgnoreTransforms()) { 380 dy = -dy; 381 flags |= SkBlurDrawLooper::kIgnoreTransform_BlurFlag; 382 flags |= SkBlurDrawLooper::kOverrideColor_BlurFlag; 383 } 384 385 SkDrawLooper* looper = new SkBlurDrawLooper(m_state->shadow.blur, 386 m_state->shadow.dx, 387 dy, 388 m_state->shadow.color, 389 flags); 390 paint->setLooper(looper)->unref(); 391 } 392 paint->setFilterBitmap(true); 393 } 394 395 void PlatformGraphicsContext::setupPaintFill(SkPaint* paint) const 396 { 397 this->setupPaintCommon(paint); 398 paint->setColor(m_state->applyAlpha(m_state->fillColor)); 399 paint->setShader(m_state->fillShader); 400 } 401 402 bool PlatformGraphicsContext::setupPaintShadow(SkPaint* paint, SkPoint* offset) const 403 { 404 return m_state->setupShadowPaint(paint, offset, shadowsIgnoreTransforms()); 405 } 406 407 bool PlatformGraphicsContext::setupPaintStroke(SkPaint* paint, SkRect* rect, 408 bool isHLine) 409 { 410 this->setupPaintCommon(paint); 411 paint->setColor(m_state->applyAlpha(m_state->strokeColor)); 412 paint->setShader(m_state->strokeShader); 413 414 float width = m_state->strokeThickness; 415 416 // This allows dashing and dotting to work properly for hairline strokes 417 // FIXME: Should we only do this for dashed and dotted strokes? 418 if (!width) 419 width = 1; 420 421 paint->setStyle(SkPaint::kStroke_Style); 422 paint->setStrokeWidth(SkFloatToScalar(width)); 423 paint->setStrokeCap(m_state->lineCap); 424 paint->setStrokeJoin(m_state->lineJoin); 425 paint->setStrokeMiter(SkFloatToScalar(m_state->miterLimit)); 426 427 if (rect && (RoundToInt(width) & 1)) 428 rect->inset(-SK_ScalarHalf, -SK_ScalarHalf); 429 430 SkPathEffect* pe = m_state->pathEffect; 431 if (pe) { 432 paint->setPathEffect(pe); 433 return false; 434 } 435 switch (m_state->strokeStyle) { 436 case NoStroke: 437 case SolidStroke: 438 width = 0; 439 break; 440 case DashedStroke: 441 width = m_state->dashRatio * width; 442 break; 443 // No break 444 case DottedStroke: 445 break; 446 } 447 448 if (width > 0) { 449 // Return true if we're basically a dotted dash of squares 450 bool justSqrs = RoundToInt(width) == RoundToInt(paint->getStrokeWidth()); 451 452 if (justSqrs || !isHLine || !setBitmapDash(paint, width)) { 453 #if 0 454 // this is slow enough that we just skip it for now 455 // see http://b/issue?id=4163023 456 SkScalar intervals[] = { width, width }; 457 pe = new SkDashPathEffect(intervals, 2, 0); 458 paint->setPathEffect(pe)->unref(); 459 #endif 460 } 461 return justSqrs; 462 } 463 return false; 464 } 465 466 } // WebCore 467