1 /* 2 * Copyright (C) 2003, 2004, 2005, 2006, 2009 Apple Inc. All rights reserved. 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 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. 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 APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "GraphicsContext.h" 28 29 #include "BidiResolver.h" 30 #include "Font.h" 31 #include "Generator.h" 32 #include "GraphicsContextPrivate.h" 33 34 using namespace std; 35 36 namespace WebCore { 37 38 class TextRunIterator { 39 public: 40 TextRunIterator() 41 : m_textRun(0) 42 , m_offset(0) 43 { 44 } 45 46 TextRunIterator(const TextRun* textRun, unsigned offset) 47 : m_textRun(textRun) 48 , m_offset(offset) 49 { 50 } 51 52 TextRunIterator(const TextRunIterator& other) 53 : m_textRun(other.m_textRun) 54 , m_offset(other.m_offset) 55 { 56 } 57 58 unsigned offset() const { return m_offset; } 59 void increment() { m_offset++; } 60 bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); } 61 UChar current() const { return (*m_textRun)[m_offset]; } 62 WTF::Unicode::Direction direction() const { return atEnd() ? WTF::Unicode::OtherNeutral : WTF::Unicode::direction(current()); } 63 64 bool operator==(const TextRunIterator& other) 65 { 66 return m_offset == other.m_offset && m_textRun == other.m_textRun; 67 } 68 69 bool operator!=(const TextRunIterator& other) { return !operator==(other); } 70 71 private: 72 const TextRun* m_textRun; 73 int m_offset; 74 }; 75 76 GraphicsContextPrivate* GraphicsContext::createGraphicsContextPrivate() 77 { 78 return new GraphicsContextPrivate; 79 } 80 81 void GraphicsContext::destroyGraphicsContextPrivate(GraphicsContextPrivate* deleteMe) 82 { 83 delete deleteMe; 84 } 85 86 void GraphicsContext::save() 87 { 88 if (paintingDisabled()) 89 return; 90 91 m_common->stack.append(m_common->state); 92 93 savePlatformState(); 94 } 95 96 void GraphicsContext::restore() 97 { 98 if (paintingDisabled()) 99 return; 100 101 if (m_common->stack.isEmpty()) { 102 LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty"); 103 return; 104 } 105 m_common->state = m_common->stack.last(); 106 m_common->stack.removeLast(); 107 108 restorePlatformState(); 109 } 110 111 void GraphicsContext::setStrokeThickness(float thickness) 112 { 113 m_common->state.strokeThickness = thickness; 114 setPlatformStrokeThickness(thickness); 115 } 116 117 void GraphicsContext::setStrokeStyle(const StrokeStyle& style) 118 { 119 m_common->state.strokeStyle = style; 120 setPlatformStrokeStyle(style); 121 } 122 123 void GraphicsContext::setStrokeColor(const Color& color, ColorSpace colorSpace) 124 { 125 m_common->state.strokeColor = color; 126 m_common->state.strokeColorSpace = colorSpace; 127 m_common->state.strokeGradient.clear(); 128 m_common->state.strokePattern.clear(); 129 setPlatformStrokeColor(color, colorSpace); 130 } 131 132 void GraphicsContext::setShadow(const IntSize& size, int blur, const Color& color, ColorSpace colorSpace) 133 { 134 m_common->state.shadowSize = size; 135 m_common->state.shadowBlur = blur; 136 m_common->state.shadowColor = color; 137 setPlatformShadow(size, blur, color, colorSpace); 138 } 139 140 void GraphicsContext::clearShadow() 141 { 142 m_common->state.shadowSize = IntSize(); 143 m_common->state.shadowBlur = 0; 144 m_common->state.shadowColor = Color(); 145 clearPlatformShadow(); 146 } 147 148 bool GraphicsContext::getShadow(IntSize& size, int& blur, Color& color) const 149 { 150 size = m_common->state.shadowSize; 151 blur = m_common->state.shadowBlur; 152 color = m_common->state.shadowColor; 153 154 return color.isValid() && color.alpha() && (blur || size.width() || size.height()); 155 } 156 157 float GraphicsContext::strokeThickness() const 158 { 159 return m_common->state.strokeThickness; 160 } 161 162 StrokeStyle GraphicsContext::strokeStyle() const 163 { 164 return m_common->state.strokeStyle; 165 } 166 167 Color GraphicsContext::strokeColor() const 168 { 169 return m_common->state.strokeColor; 170 } 171 172 ColorSpace GraphicsContext::strokeColorSpace() const 173 { 174 return m_common->state.strokeColorSpace; 175 } 176 177 WindRule GraphicsContext::fillRule() const 178 { 179 return m_common->state.fillRule; 180 } 181 182 void GraphicsContext::setFillRule(WindRule fillRule) 183 { 184 m_common->state.fillRule = fillRule; 185 } 186 187 void GraphicsContext::setFillColor(const Color& color, ColorSpace colorSpace) 188 { 189 m_common->state.fillColor = color; 190 m_common->state.fillColorSpace = colorSpace; 191 m_common->state.fillGradient.clear(); 192 m_common->state.fillPattern.clear(); 193 setPlatformFillColor(color, colorSpace); 194 } 195 196 Color GraphicsContext::fillColor() const 197 { 198 return m_common->state.fillColor; 199 } 200 201 ColorSpace GraphicsContext::fillColorSpace() const 202 { 203 return m_common->state.fillColorSpace; 204 } 205 206 void GraphicsContext::setShouldAntialias(bool b) 207 { 208 m_common->state.shouldAntialias = b; 209 setPlatformShouldAntialias(b); 210 } 211 212 bool GraphicsContext::shouldAntialias() const 213 { 214 return m_common->state.shouldAntialias; 215 } 216 217 void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern) 218 { 219 ASSERT(pattern); 220 if (!pattern) { 221 setStrokeColor(Color::black, DeviceColorSpace); 222 return; 223 } 224 m_common->state.strokeGradient.clear(); 225 m_common->state.strokePattern = pattern; 226 setPlatformStrokePattern(m_common->state.strokePattern.get()); 227 } 228 229 void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern) 230 { 231 ASSERT(pattern); 232 if (!pattern) { 233 setFillColor(Color::black, DeviceColorSpace); 234 return; 235 } 236 m_common->state.fillGradient.clear(); 237 m_common->state.fillPattern = pattern; 238 setPlatformFillPattern(m_common->state.fillPattern.get()); 239 } 240 241 void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient) 242 { 243 ASSERT(gradient); 244 if (!gradient) { 245 setStrokeColor(Color::black, DeviceColorSpace); 246 return; 247 } 248 m_common->state.strokeGradient = gradient; 249 m_common->state.strokePattern.clear(); 250 setPlatformStrokeGradient(m_common->state.strokeGradient.get()); 251 } 252 253 void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient) 254 { 255 ASSERT(gradient); 256 if (!gradient) { 257 setFillColor(Color::black, DeviceColorSpace); 258 return; 259 } 260 m_common->state.fillGradient = gradient; 261 m_common->state.fillPattern.clear(); 262 setPlatformFillGradient(m_common->state.fillGradient.get()); 263 } 264 265 Gradient* GraphicsContext::fillGradient() const 266 { 267 return m_common->state.fillGradient.get(); 268 } 269 270 Gradient* GraphicsContext::strokeGradient() const 271 { 272 return m_common->state.strokeGradient.get(); 273 } 274 275 Pattern* GraphicsContext::fillPattern() const 276 { 277 return m_common->state.fillPattern.get(); 278 } 279 280 Pattern* GraphicsContext::strokePattern() const 281 { 282 return m_common->state.strokePattern.get(); 283 } 284 285 void GraphicsContext::setShadowsIgnoreTransforms(bool ignoreTransforms) 286 { 287 m_common->state.shadowsIgnoreTransforms = ignoreTransforms; 288 } 289 290 bool GraphicsContext::updatingControlTints() const 291 { 292 return m_common->m_updatingControlTints; 293 } 294 295 void GraphicsContext::setUpdatingControlTints(bool b) 296 { 297 setPaintingDisabled(b); 298 m_common->m_updatingControlTints = b; 299 } 300 301 void GraphicsContext::setPaintingDisabled(bool f) 302 { 303 m_common->state.paintingDisabled = f; 304 } 305 306 bool GraphicsContext::paintingDisabled() const 307 { 308 return m_common->state.paintingDisabled; 309 } 310 311 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntPoint& p, CompositeOperator op) 312 { 313 drawImage(image, styleColorSpace, p, IntRect(0, 0, -1, -1), op); 314 } 315 316 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntRect& r, CompositeOperator op, bool useLowQualityScale) 317 { 318 drawImage(image, styleColorSpace, r, IntRect(0, 0, -1, -1), op, useLowQualityScale); 319 } 320 321 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntPoint& dest, const IntRect& srcRect, CompositeOperator op) 322 { 323 drawImage(image, styleColorSpace, IntRect(dest, srcRect.size()), srcRect, op); 324 } 325 326 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, CompositeOperator op, bool useLowQualityScale) 327 { 328 drawImage(image, styleColorSpace, FloatRect(dest), srcRect, op, useLowQualityScale); 329 } 330 331 #if !OS(WINCE) || PLATFORM(QT) 332 void GraphicsContext::drawText(const Font& font, const TextRun& run, const IntPoint& point, int from, int to) 333 { 334 if (paintingDisabled()) 335 return; 336 337 font.drawText(this, run, point, from, to); 338 } 339 #endif 340 341 void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point) 342 { 343 if (paintingDisabled()) 344 return; 345 346 BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; 347 WTF::Unicode::Direction paragraphDirection = run.ltr() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft; 348 349 bidiResolver.setStatus(BidiStatus(paragraphDirection, paragraphDirection, paragraphDirection, BidiContext::create(run.ltr() ? 0 : 1, paragraphDirection, run.directionalOverride()))); 350 351 bidiResolver.setPosition(TextRunIterator(&run, 0)); 352 bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length())); 353 354 if (!bidiResolver.runCount()) 355 return; 356 357 FloatPoint currPoint = point; 358 BidiCharacterRun* bidiRun = bidiResolver.firstRun(); 359 while (bidiRun) { 360 361 TextRun subrun = run; 362 subrun.setText(run.data(bidiRun->start()), bidiRun->stop() - bidiRun->start()); 363 subrun.setRTL(bidiRun->level() % 2); 364 subrun.setDirectionalOverride(bidiRun->dirOverride(false)); 365 366 font.drawText(this, subrun, currPoint); 367 368 bidiRun = bidiRun->next(); 369 // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here. 370 if (bidiRun) 371 currPoint.move(font.floatWidth(subrun), 0.f); 372 } 373 374 bidiResolver.deleteRuns(); 375 } 376 377 void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const IntPoint& point, int h, const Color& backgroundColor, ColorSpace colorSpace, int from, int to) 378 { 379 if (paintingDisabled()) 380 return; 381 382 fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor, colorSpace); 383 } 384 385 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op, bool useLowQualityScale) 386 { 387 if (paintingDisabled() || !image) 388 return; 389 390 float tsw = src.width(); 391 float tsh = src.height(); 392 float tw = dest.width(); 393 float th = dest.height(); 394 395 if (tsw == -1) 396 tsw = image->width(); 397 if (tsh == -1) 398 tsh = image->height(); 399 400 if (tw == -1) 401 tw = image->width(); 402 if (th == -1) 403 th = image->height(); 404 405 if (useLowQualityScale) { 406 save(); 407 setImageInterpolationQuality(InterpolationNone); 408 } 409 image->draw(this, FloatRect(dest.location(), FloatSize(tw, th)), FloatRect(src.location(), FloatSize(tsw, tsh)), styleColorSpace, op); 410 if (useLowQualityScale) 411 restore(); 412 } 413 414 void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& rect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, bool useLowQualityScale) 415 { 416 if (paintingDisabled() || !image) 417 return; 418 if (useLowQualityScale) { 419 save(); 420 setImageInterpolationQuality(InterpolationLow); 421 } 422 image->drawTiled(this, rect, srcPoint, tileSize, styleColorSpace, op); 423 if (useLowQualityScale) 424 restore(); 425 } 426 427 void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op, bool useLowQualityScale) 428 { 429 if (paintingDisabled() || !image) 430 return; 431 432 if (useLowQualityScale) { 433 save(); 434 setImageInterpolationQuality(InterpolationLow); 435 } 436 if (hRule == Image::StretchTile && vRule == Image::StretchTile) 437 // Just do a scale. 438 drawImage(image, styleColorSpace, dest, srcRect, op); 439 else 440 image->drawTiled(this, dest, srcRect, hRule, vRule, styleColorSpace, op); 441 if (useLowQualityScale) 442 restore(); 443 } 444 445 void GraphicsContext::addRoundedRectClip(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, 446 const IntSize& bottomLeft, const IntSize& bottomRight) 447 { 448 if (paintingDisabled()) 449 return; 450 451 clip(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight)); 452 } 453 454 void GraphicsContext::clipOutRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, 455 const IntSize& bottomLeft, const IntSize& bottomRight) 456 { 457 if (paintingDisabled()) 458 return; 459 460 clipOut(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight)); 461 } 462 463 int GraphicsContext::textDrawingMode() 464 { 465 return m_common->state.textDrawingMode; 466 } 467 468 void GraphicsContext::setTextDrawingMode(int mode) 469 { 470 m_common->state.textDrawingMode = mode; 471 if (paintingDisabled()) 472 return; 473 setPlatformTextDrawingMode(mode); 474 } 475 476 void GraphicsContext::fillRect(const FloatRect& rect, Generator& generator) 477 { 478 if (paintingDisabled()) 479 return; 480 generator.fill(this, rect); 481 } 482 483 #if !(PLATFORM(SKIA) && !PLATFORM(ANDROID)) 484 void GraphicsContext::setPlatformFillGradient(Gradient*) 485 { 486 } 487 488 void GraphicsContext::setPlatformFillPattern(Pattern*) 489 { 490 } 491 492 void GraphicsContext::setPlatformStrokeGradient(Gradient*) 493 { 494 } 495 496 void GraphicsContext::setPlatformStrokePattern(Pattern*) 497 { 498 } 499 #endif 500 501 #if !PLATFORM(CG) && !(PLATFORM(SKIA) && !PLATFORM(ANDROID)) 502 // Implement this if you want to go ahead and push the drawing mode into your native context 503 // immediately. 504 void GraphicsContext::setPlatformTextDrawingMode(int mode) 505 { 506 } 507 #endif 508 509 #if !PLATFORM(QT) && !PLATFORM(CAIRO) && !(PLATFORM(SKIA) && !PLATFORM(ANDROID)) && !PLATFORM(HAIKU) && !PLATFORM(OPENVG) 510 void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle&) 511 { 512 } 513 #endif 514 515 void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, const StrokeStyle& penStyle) 516 { 517 // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic 518 // works out. For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g., 519 // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave 520 // us a perfect position, but an odd width gave us a position that is off by exactly 0.5. 521 if (penStyle == DottedStroke || penStyle == DashedStroke) { 522 if (p1.x() == p2.x()) { 523 p1.setY(p1.y() + strokeWidth); 524 p2.setY(p2.y() - strokeWidth); 525 } else { 526 p1.setX(p1.x() + strokeWidth); 527 p2.setX(p2.x() - strokeWidth); 528 } 529 } 530 531 if (static_cast<int>(strokeWidth) % 2) { //odd 532 if (p1.x() == p2.x()) { 533 // We're a vertical line. Adjust our x. 534 p1.setX(p1.x() + 0.5f); 535 p2.setX(p2.x() + 0.5f); 536 } else { 537 // We're a horizontal line. Adjust our y. 538 p1.setY(p1.y() + 0.5f); 539 p2.setY(p2.y() + 0.5f); 540 } 541 } 542 } 543 544 } 545