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 paint->setFilterBitmap(true); 86 paint->setAntiAlias(true); 87 return true; 88 } 89 90 //************************************** 91 // State implementation 92 //************************************** 93 94 PlatformGraphicsContext::State::State() 95 : pathEffect(0) 96 , miterLimit(4) 97 , alpha(1) 98 , strokeThickness(0) // Same as default in GraphicsContextPrivate.h 99 , lineCap(SkPaint::kDefault_Cap) 100 , lineJoin(SkPaint::kDefault_Join) 101 , mode(SkXfermode::kSrcOver_Mode) 102 , dashRatio(3) 103 , fillColor(SK_ColorBLACK) 104 , fillShader(0) 105 , strokeColor(SK_ColorBLACK) 106 , strokeShader(0) 107 , useAA(true) 108 , strokeStyle(SolidStroke) 109 { 110 } 111 112 PlatformGraphicsContext::State::State(const State& other) 113 : pathEffect(other.pathEffect) 114 , miterLimit(other.miterLimit) 115 , alpha(other.alpha) 116 , strokeThickness(other.strokeThickness) 117 , lineCap(other.lineCap) 118 , lineJoin(other.lineJoin) 119 , mode(other.mode) 120 , dashRatio(other.dashRatio) 121 , shadow(other.shadow) 122 , fillColor(other.fillColor) 123 , fillShader(other.fillShader) 124 , strokeColor(other.strokeColor) 125 , strokeShader(other.strokeShader) 126 , useAA(other.useAA) 127 , strokeStyle(other.strokeStyle) 128 { 129 SkSafeRef(pathEffect); 130 SkSafeRef(fillShader); 131 SkSafeRef(strokeShader); 132 } 133 134 PlatformGraphicsContext::State::~State() 135 { 136 SkSafeUnref(pathEffect); 137 SkSafeUnref(fillShader); 138 SkSafeUnref(strokeShader); 139 } 140 141 void PlatformGraphicsContext::State::setShadow(int radius, int dx, int dy, SkColor c) 142 { 143 // Cut the radius in half, to visually match the effect seen in 144 // safari browser 145 shadow.blur = SkScalarHalf(SkIntToScalar(radius)); 146 shadow.dx = SkIntToScalar(dx); 147 shadow.dy = SkIntToScalar(dy); 148 shadow.color = c; 149 } 150 151 bool PlatformGraphicsContext::State::setupShadowPaint(SkPaint* paint, SkPoint* offset, 152 bool shadowsIgnoreTransforms) 153 { 154 paint->setAntiAlias(true); 155 paint->setDither(true); 156 paint->setXfermodeMode(mode); 157 paint->setColor(shadow.color); 158 offset->set(shadow.dx, shadow.dy); 159 160 // Currently, only GraphicsContexts associated with the 161 // HTMLCanvasElement have shadows ignore transforms set. This 162 // allows us to distinguish between CSS and Canvas shadows which 163 // have different rendering specifications. 164 uint32_t flags = SkBlurMaskFilter::kNone_BlurFlag; 165 if (shadowsIgnoreTransforms) { 166 offset->fY = -offset->fY; 167 flags |= SkBlurMaskFilter::kIgnoreTransform_BlurFlag; 168 } 169 170 if (shadow.blur > 0) { 171 paint->setMaskFilter(SkBlurMaskFilter::Create(shadow.blur, 172 SkBlurMaskFilter::kNormal_BlurStyle, flags))->unref(); 173 } 174 return SkColorGetA(shadow.color) && (shadow.blur || shadow.dx || shadow.dy); 175 } 176 177 SkColor PlatformGraphicsContext::State::applyAlpha(SkColor c) const 178 { 179 int s = RoundToInt(alpha * 256); 180 if (s >= 256) 181 return c; 182 if (s < 0) 183 return 0; 184 185 int a = SkAlphaMul(SkColorGetA(c), s); 186 return (c & 0x00FFFFFF) | (a << 24); 187 } 188 189 // Returns a new State with all of this object's inherited properties copied. 190 PlatformGraphicsContext::State PlatformGraphicsContext::State::cloneInheritedProperties() 191 { 192 return PlatformGraphicsContext::State(*this); 193 } 194 195 //************************************** 196 // PlatformGraphicsContext 197 //************************************** 198 199 PlatformGraphicsContext::PlatformGraphicsContext() 200 { 201 m_stateStack.append(State()); 202 m_state = &m_stateStack.last(); 203 } 204 205 PlatformGraphicsContext::~PlatformGraphicsContext() 206 { 207 } 208 209 //************************************** 210 // State management 211 //************************************** 212 213 void PlatformGraphicsContext::save() 214 { 215 m_stateStack.append(m_state->cloneInheritedProperties()); 216 m_state = &m_stateStack.last(); 217 } 218 219 void PlatformGraphicsContext::restore() 220 { 221 m_stateStack.removeLast(); 222 m_state = &m_stateStack.last(); 223 } 224 225 //************************************** 226 // State setters 227 //************************************** 228 229 void PlatformGraphicsContext::setAlpha(float alpha) 230 { 231 m_state->alpha = alpha; 232 } 233 234 int PlatformGraphicsContext::getNormalizedAlpha() const 235 { 236 int alpha = roundf(m_state->alpha * 256); 237 if (alpha > 255) 238 alpha = 255; 239 else if (alpha < 0) 240 alpha = 0; 241 return alpha; 242 } 243 244 void PlatformGraphicsContext::setCompositeOperation(CompositeOperator op) 245 { 246 m_state->mode = WebCoreCompositeToSkiaComposite(op); 247 } 248 249 bool PlatformGraphicsContext::setFillColor(const Color& c) 250 { 251 bool dirty = false; 252 if (m_state->fillColor != c.rgb()) { 253 m_state->fillColor = c.rgb(); 254 dirty = true; 255 } 256 return setFillShader(0) || dirty; 257 } 258 259 bool PlatformGraphicsContext::setFillShader(SkShader* fillShader) 260 { 261 bool dirty = false; 262 if (fillShader && m_state->fillColor != Color::black) { 263 m_state->fillColor = Color::black; 264 dirty = true; 265 } 266 267 if (fillShader != m_state->fillShader) { 268 SkSafeUnref(m_state->fillShader); 269 m_state->fillShader = fillShader; 270 SkSafeRef(m_state->fillShader); 271 dirty = true; 272 } 273 return dirty; 274 } 275 276 void PlatformGraphicsContext::setLineCap(LineCap cap) 277 { 278 switch (cap) { 279 case ButtCap: 280 m_state->lineCap = SkPaint::kButt_Cap; 281 break; 282 case RoundCap: 283 m_state->lineCap = SkPaint::kRound_Cap; 284 break; 285 case SquareCap: 286 m_state->lineCap = SkPaint::kSquare_Cap; 287 break; 288 default: 289 ALOGD("PlatformGraphicsContextSkia::setLineCap: unknown LineCap %d\n", cap); 290 break; 291 } 292 } 293 294 void PlatformGraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) 295 { 296 size_t dashLength = dashes.size(); 297 if (!dashLength) 298 return; 299 300 size_t count = !(dashLength % 2) ? dashLength : dashLength * 2; 301 SkScalar* intervals = new SkScalar[count]; 302 303 for (unsigned int i = 0; i < count; i++) 304 intervals[i] = SkFloatToScalar(dashes[i % dashLength]); 305 SkPathEffect **effectPtr = &m_state->pathEffect; 306 SkSafeUnref(*effectPtr); 307 *effectPtr = new SkDashPathEffect(intervals, count, SkFloatToScalar(dashOffset)); 308 309 delete[] intervals; 310 } 311 312 void PlatformGraphicsContext::setLineJoin(LineJoin join) 313 { 314 switch (join) { 315 case MiterJoin: 316 m_state->lineJoin = SkPaint::kMiter_Join; 317 break; 318 case RoundJoin: 319 m_state->lineJoin = SkPaint::kRound_Join; 320 break; 321 case BevelJoin: 322 m_state->lineJoin = SkPaint::kBevel_Join; 323 break; 324 default: 325 ALOGD("PlatformGraphicsContextSkia::setLineJoin: unknown LineJoin %d\n", join); 326 break; 327 } 328 } 329 330 void PlatformGraphicsContext::setMiterLimit(float limit) 331 { 332 m_state->miterLimit = limit; 333 } 334 335 void PlatformGraphicsContext::setShadow(int radius, int dx, int dy, SkColor c) 336 { 337 m_state->setShadow(radius, dx, dy, c); 338 } 339 340 void PlatformGraphicsContext::setShouldAntialias(bool useAA) 341 { 342 m_state->useAA = useAA; 343 } 344 345 bool PlatformGraphicsContext::setStrokeColor(const Color& c) 346 { 347 bool dirty = false; 348 if (m_state->strokeColor != c.rgb()) { 349 m_state->strokeColor = c.rgb(); 350 dirty = true; 351 } 352 return setStrokeShader(0) || dirty; 353 } 354 355 bool PlatformGraphicsContext::setStrokeShader(SkShader* strokeShader) 356 { 357 bool dirty = false; 358 if (strokeShader && m_state->strokeColor != Color::black) { 359 m_state->strokeColor = Color::black; 360 dirty = true; 361 } 362 363 if (strokeShader != m_state->strokeShader) { 364 SkSafeUnref(m_state->strokeShader); 365 m_state->strokeShader = strokeShader; 366 SkSafeRef(m_state->strokeShader); 367 dirty = true; 368 } 369 return dirty; 370 } 371 372 void PlatformGraphicsContext::setStrokeStyle(StrokeStyle style) 373 { 374 m_state->strokeStyle = style; 375 } 376 377 void PlatformGraphicsContext::setStrokeThickness(float f) 378 { 379 m_state->strokeThickness = f; 380 } 381 382 //************************************** 383 // Paint setup 384 //************************************** 385 386 void PlatformGraphicsContext::setupPaintCommon(SkPaint* paint) const 387 { 388 paint->setAntiAlias(m_state->useAA); 389 paint->setDither(true); 390 paint->setXfermodeMode(m_state->mode); 391 if (SkColorGetA(m_state->shadow.color) > 0) { 392 393 // Currently, only GraphicsContexts associated with the 394 // HTMLCanvasElement have shadows ignore transforms set. This 395 // allows us to distinguish between CSS and Canvas shadows which 396 // have different rendering specifications. 397 SkScalar dy = m_state->shadow.dy; 398 uint32_t flags = SkBlurDrawLooper::kNone_BlurFlag; 399 if (shadowsIgnoreTransforms()) { 400 dy = -dy; 401 flags |= SkBlurDrawLooper::kIgnoreTransform_BlurFlag; 402 flags |= SkBlurDrawLooper::kOverrideColor_BlurFlag; 403 } 404 405 SkDrawLooper* looper = new SkBlurDrawLooper(m_state->shadow.blur, 406 m_state->shadow.dx, 407 dy, 408 m_state->shadow.color, 409 flags); 410 paint->setLooper(looper)->unref(); 411 } 412 paint->setFilterBitmap(true); 413 } 414 415 void PlatformGraphicsContext::setupPaintFill(SkPaint* paint) const 416 { 417 this->setupPaintCommon(paint); 418 paint->setColor(m_state->applyAlpha(m_state->fillColor)); 419 paint->setShader(m_state->fillShader); 420 } 421 422 bool PlatformGraphicsContext::setupPaintShadow(SkPaint* paint, SkPoint* offset) const 423 { 424 return m_state->setupShadowPaint(paint, offset, shadowsIgnoreTransforms()); 425 } 426 427 bool PlatformGraphicsContext::setupPaintStroke(SkPaint* paint, SkRect* rect, 428 bool isHLine) 429 { 430 this->setupPaintCommon(paint); 431 paint->setColor(m_state->applyAlpha(m_state->strokeColor)); 432 paint->setShader(m_state->strokeShader); 433 434 float width = m_state->strokeThickness; 435 436 // This allows dashing and dotting to work properly for hairline strokes 437 // FIXME: Should we only do this for dashed and dotted strokes? 438 if (!width) 439 width = 1; 440 441 paint->setStyle(SkPaint::kStroke_Style); 442 paint->setStrokeWidth(SkFloatToScalar(width)); 443 paint->setStrokeCap(m_state->lineCap); 444 paint->setStrokeJoin(m_state->lineJoin); 445 paint->setStrokeMiter(SkFloatToScalar(m_state->miterLimit)); 446 447 if (rect && (RoundToInt(width) & 1)) 448 rect->inset(-SK_ScalarHalf, -SK_ScalarHalf); 449 450 SkPathEffect* pe = m_state->pathEffect; 451 if (pe) { 452 paint->setPathEffect(pe); 453 return false; 454 } 455 switch (m_state->strokeStyle) { 456 case NoStroke: 457 case SolidStroke: 458 width = 0; 459 break; 460 case DashedStroke: 461 width = m_state->dashRatio * width; 462 break; 463 // No break 464 case DottedStroke: 465 break; 466 } 467 468 if (width > 0) { 469 // Return true if we're basically a dotted dash of squares 470 bool justSqrs = RoundToInt(width) == RoundToInt(paint->getStrokeWidth()); 471 472 if (justSqrs || !isHLine || !setBitmapDash(paint, width)) { 473 #if 0 474 // this is slow enough that we just skip it for now 475 // see http://b/issue?id=4163023 476 SkScalar intervals[] = { width, width }; 477 pe = new SkDashPathEffect(intervals, 2, 0); 478 paint->setPathEffect(pe)->unref(); 479 #endif 480 } 481 return justSqrs; 482 } 483 return false; 484 } 485 486 } // WebCore 487