1/* 2 * Copyright (C) 2007-2009 Torch Mobile Inc. 3 * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com> 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public License 16 * along with this library; see the file COPYING.LIB. If not, write to 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 * 20 */ 21 22#include "config.h" 23#include "GraphicsContext.h" 24 25#include "AffineTransform.h" 26#include "FloatRoundedRect.h" 27#include "Font.h" 28#include "GDIExtras.h" 29#include "GlyphBuffer.h" 30#include "Gradient.h" 31#include "NotImplemented.h" 32#include "Path.h" 33#include "PlatformPathWinCE.h" 34#include "SharedBitmap.h" 35#include "SimpleFontData.h" 36#include <windows.h> 37#include <wtf/OwnPtr.h> 38#include <wtf/unicode/CharacterNames.h> 39 40namespace WebCore { 41 42typedef void (*FuncGradientFillRectLinear)(HDC hdc, const IntRect& r, const IntPoint& p0, const IntPoint& p1, const Vector<Gradient::ColorStop>& stops); 43typedef void (*FuncGradientFillRectRadial)(HDC hdc, const IntRect& r, const IntPoint& p0, const IntPoint& p1, float r0, float r1, const Vector<Gradient::ColorStop>& stops); 44FuncGradientFillRectLinear g_linearGradientFiller = 0; 45FuncGradientFillRectRadial g_radialGradientFiller = 0; 46 47static inline bool isZero(double d) 48{ 49 return d > 0 ? d <= 1.E-10 : d >= -1.E-10; 50} 51 52// stableRound rounds -0.5 to 0, where lround rounds -0.5 to -1. 53static inline int stableRound(double d) 54{ 55 if (d > 0) 56 return static_cast<int>(d + 0.5); 57 58 int i = static_cast<int>(d); 59 return i - d > 0.5 ? i - 1 : i; 60} 61 62// Unlike enclosingIntRect(), this function does strict rounding. 63static inline IntRect roundRect(const FloatRect& r) 64{ 65 return IntRect(stableRound(r.x()), stableRound(r.y()), stableRound(r.maxX()) - stableRound(r.x()), stableRound(r.maxY()) - stableRound(r.y())); 66} 67 68// Rotation transformation 69class RotationTransform { 70public: 71 RotationTransform() 72 : m_cosA(1.) 73 , m_sinA(0.) 74 , m_preShiftX(0) 75 , m_preShiftY(0) 76 , m_postShiftX(0) 77 , m_postShiftY(0) 78 { 79 } 80 RotationTransform operator-() const 81 { 82 RotationTransform rtn; 83 rtn.m_cosA = m_cosA; 84 rtn.m_sinA = -m_sinA; 85 rtn.m_preShiftX = m_postShiftX; 86 rtn.m_preShiftY = m_postShiftY; 87 rtn.m_postShiftX = m_preShiftX; 88 rtn.m_postShiftY = m_preShiftY; 89 return rtn; 90 } 91 void map(double x1, double y1, double* x2, double* y2) const 92 { 93 x1 += m_preShiftX; 94 y1 += m_preShiftY; 95 *x2 = x1 * m_cosA + y1 * m_sinA + m_postShiftX; 96 *y2 = y1 * m_cosA - x1 * m_sinA + m_postShiftY; 97 } 98 void map(int x1, int y1, int* x2, int* y2) const 99 { 100 x1 += m_preShiftX; 101 y1 += m_preShiftY; 102 *x2 = stableRound(x1 * m_cosA + y1 * m_sinA) + m_postShiftX; 103 *y2 = stableRound(y1 * m_cosA - x1 * m_sinA) + m_postShiftY; 104 } 105 106 double m_cosA; 107 double m_sinA; 108 int m_preShiftX; 109 int m_preShiftY; 110 int m_postShiftX; 111 int m_postShiftY; 112}; 113 114template<class T> static inline IntPoint mapPoint(const IntPoint& p, const T& t) 115{ 116 int x, y; 117 t.map(p.x(), p.y(), &x, &y); 118 return IntPoint(x, y); 119} 120 121template<class T> static inline FloatPoint mapPoint(const FloatPoint& p, const T& t) 122{ 123 double x, y; 124 t.map(p.x(), p.y(), &x, &y); 125 return FloatPoint(static_cast<float>(x), static_cast<float>(y)); 126} 127 128template<class Transform, class Rect, class Value> static inline Rect mapRect(const Rect& rect, const Transform& transform) 129{ 130 Value x[4], y[4]; 131 Value l, t, r, b; 132 r = rect.maxX() - 1; 133 b = rect.maxY() - 1; 134 transform.map(rect.x(), rect.y(), x, y); 135 transform.map(rect.x(), b, x + 1, y + 1); 136 transform.map(r, b, x + 2, y + 2); 137 transform.map(r, rect.y(), x + 3, y + 3); 138 l = r = x[3]; 139 t = b = y[3]; 140 for (int i = 0; i < 3; ++i) { 141 if (x[i] < l) 142 l = x[i]; 143 else if (x[i] > r) 144 r = x[i]; 145 146 if (y[i] < t) 147 t = y[i]; 148 else if (y[i] > b) 149 b = y[i]; 150 } 151 152 return IntRect(l, t, r - l + 1, b - t + 1); 153} 154 155template<class T> static inline IntRect mapRect(const IntRect& rect, const T& transform) 156{ 157 return mapRect<T, IntRect, int>(rect, transform); 158} 159 160template<class T> static inline FloatRect mapRect(const FloatRect& rect, const T& transform) 161{ 162 return mapRect<T, FloatRect, double>(rect, transform); 163} 164 165class GraphicsContextPlatformPrivateData { 166public: 167 GraphicsContextPlatformPrivateData() 168 : m_transform() 169 , m_opacity(1.0) 170 { 171 } 172 173 AffineTransform m_transform; 174 float m_opacity; 175}; 176 177enum AlphaPaintType { 178 AlphaPaintNone, 179 AlphaPaintImage, 180 AlphaPaintOther, 181}; 182 183class GraphicsContextPlatformPrivate : public GraphicsContextPlatformPrivateData { 184public: 185 GraphicsContextPlatformPrivate(HDC dc) 186 : m_dc(dc) 187 { 188 } 189 ~GraphicsContextPlatformPrivate() 190 { 191 while (!m_backupData.isEmpty()) 192 restore(); 193 } 194 195 void translate(float x, float y) 196 { 197 m_transform.translate(x, y); 198 } 199 200 void scale(const FloatSize& size) 201 { 202 m_transform.scaleNonUniform(size.width(), size.height()); 203 } 204 205 void rotate(float radians) 206 { 207 m_transform.rotate(rad2deg(radians)); 208 } 209 210 void concatCTM(const AffineTransform& transform) 211 { 212 m_transform *= transform; 213 } 214 215 void setCTM(const AffineTransform& transform) 216 { 217 m_transform = transform; 218 } 219 220 IntRect mapRect(const IntRect& rect) const 221 { 222 return m_transform.mapRect(rect); 223 } 224 225 FloatRect mapRect(const FloatRect& rect) const 226 { 227 return m_transform.mapRect(rect); 228 } 229 230 IntPoint mapPoint(const IntPoint& point) const 231 { 232 return m_transform.mapPoint(point); 233 } 234 235 FloatPoint mapPoint(const FloatPoint& point) const 236 { 237 return m_transform.mapPoint(point); 238 } 239 240 FloatSize mapSize(const FloatSize& size) const 241 { 242 double w, h; 243 m_transform.map(size.width(), size.height(), w, h); 244 return FloatSize(static_cast<float>(w), static_cast<float>(h)); 245 } 246 247 void save() 248 { 249 if (m_dc) 250 SaveDC(m_dc); 251 252 m_backupData.append(*static_cast<GraphicsContextPlatformPrivateData*>(this)); 253 } 254 255 void restore() 256 { 257 if (m_backupData.isEmpty()) 258 return; 259 260 if (m_dc) 261 RestoreDC(m_dc, -1); 262 263 GraphicsContextPlatformPrivateData::operator=(m_backupData.last()); 264 m_backupData.removeLast(); 265 } 266 267 bool hasAlpha() const { return m_bitmap && m_bitmap->hasAlpha(); } 268 269 PassRefPtr<SharedBitmap> getTransparentLayerBitmap(IntRect& origRect, AlphaPaintType alphaPaint, RECT& bmpRect, bool checkClipBox, bool force) const 270 { 271 if (m_opacity <= 0) 272 return 0; 273 274 if (force || m_opacity < 1.) { 275 if (checkClipBox) { 276 RECT clipBox; 277 int clipType = GetClipBox(m_dc, &clipBox); 278 if (clipType == SIMPLEREGION || clipType == COMPLEXREGION) 279 origRect.intersect(clipBox); 280 if (origRect.isEmpty()) 281 return 0; 282 } 283 284 RefPtr<SharedBitmap> bmp = SharedBitmap::create(origRect.size(), alphaPaint == AlphaPaintNone ? BitmapInfo::BitCount16 : BitmapInfo::BitCount32, false); 285 SetRect(&bmpRect, 0, 0, origRect.width(), origRect.height()); 286 if (bmp) { 287 switch (alphaPaint) { 288 case AlphaPaintNone: 289 case AlphaPaintImage: 290 { 291 SharedBitmap::DCHolder dc(bmp.get()); 292 if (dc.get()) { 293 BitBlt(dc.get(), 0, 0, origRect.width(), origRect.height(), m_dc, origRect.x(), origRect.y(), SRCCOPY); 294 if (bmp->is32bit() && (!m_bitmap || m_bitmap->is16bit())) { 295 // Set alpha channel 296 unsigned* pixels = (unsigned*)bmp->bytes(); 297 const unsigned* const pixelsEnd = pixels + bmp->bitmapInfo().numPixels(); 298 while (pixels < pixelsEnd) { 299 *pixels |= 0xFF000000; 300 ++pixels; 301 } 302 } 303 return bmp; 304 } 305 } 306 break; 307 //case AlphaPaintOther: 308 default: 309 memset(bmp->bytes(), 0xFF, bmp->bitmapInfo().numPixels() * 4); 310 return bmp; 311 break; 312 } 313 } 314 } 315 316 bmpRect = origRect; 317 return 0; 318 } 319 320 void paintBackTransparentLayerBitmap(HDC hdc, SharedBitmap* bmp, const IntRect& origRect, AlphaPaintType alphaPaint, const RECT& bmpRect) 321 { 322 if (hdc == m_dc) 323 return; 324 325 if (alphaPaint == AlphaPaintOther && hasAlphaBlendSupport()) { 326 ASSERT(bmp && bmp->bytes() && bmp->is32bit()); 327 unsigned* pixels = (unsigned*)bmp->bytes(); 328 const unsigned* const pixelsEnd = pixels + bmp->bitmapInfo().numPixels(); 329 while (pixels < pixelsEnd) { 330 *pixels ^= 0xFF000000; 331 ++pixels; 332 } 333 } 334 if ((m_opacity < 1. || alphaPaint == AlphaPaintOther) && hasAlphaBlendSupport()) { 335 const BLENDFUNCTION blend = { AC_SRC_OVER, 0 336 , m_opacity >= 1. ? 255 : (BYTE)(m_opacity * 255) 337 , alphaPaint == AlphaPaintNone ? 0 : AC_SRC_ALPHA }; 338 bool success = alphaBlendIfSupported(m_dc, origRect.x(), origRect.y(), origRect.width(), origRect.height(), hdc, 0, 0, bmpRect.right, bmpRect.bottom, blend); 339 ASSERT_UNUSED(success, success); 340 } else 341 StretchBlt(m_dc, origRect.x(), origRect.y(), origRect.width(), origRect.height(), hdc, 0, 0, bmpRect.right, bmpRect.bottom, SRCCOPY); 342 } 343 344 HDC m_dc; 345 RefPtr<SharedBitmap> m_bitmap; 346 Vector<GraphicsContextPlatformPrivateData> m_backupData; 347}; 348 349static GDIObject<HPEN> createPen(const Color& col, double fWidth, StrokeStyle style) 350{ 351 int width = stableRound(fWidth); 352 if (width < 1) 353 width = 1; 354 355 int penStyle = PS_NULL; 356 switch (style) { 357 case SolidStroke: 358 case DoubleStroke: 359 case WavyStroke: // FIXME: https://bugs.webkit.org/show_bug.cgi?id=94114 - Needs platform support. 360 penStyle = PS_SOLID; 361 break; 362 case DottedStroke: // not supported on Windows CE 363 case DashedStroke: 364 penStyle = PS_DASH; 365 width = 1; 366 break; 367 default: 368 break; 369 } 370 371 return adoptGDIObject(::CreatePen(penStyle, width, RGB(col.red(), col.green(), col.blue()))); 372} 373 374static inline GDIObject<HBRUSH> createBrush(const Color& col) 375{ 376 return adoptGDIObject(::CreateSolidBrush(RGB(col.red(), col.green(), col.blue()))); 377} 378 379template <typename PixelType, bool Is16bit> static void _rotateBitmap(SharedBitmap* destBmp, const SharedBitmap* sourceBmp, const RotationTransform& transform) 380{ 381 int destW = destBmp->width(); 382 int destH = destBmp->height(); 383 int sourceW = sourceBmp->width(); 384 int sourceH = sourceBmp->height(); 385 PixelType* dest = (PixelType*)destBmp->bytes(); 386 const PixelType* source = (const PixelType*)sourceBmp->bytes(); 387 int padding; 388 int paddedSourceW; 389 if (Is16bit) { 390 padding = destW & 1; 391 paddedSourceW = sourceW + (sourceW & 1); 392 } else { 393 padding = 0; 394 paddedSourceW = sourceW; 395 } 396 if (isZero(transform.m_sinA)) { 397 int cosA = transform.m_cosA > 0 ? 1 : -1; 398 for (int y = 0; y < destH; ++y) { 399 for (int x = 0; x < destW; ++x) { 400 int x1 = x + transform.m_preShiftX; 401 int y1 = y + transform.m_preShiftY; 402 int srcX = x1 * cosA + transform.m_postShiftX; 403 int srcY = y1 * cosA - transform.m_postShiftY; 404 if (srcX >= 0 && srcX <= sourceW && srcY >= 0 && srcY <= sourceH) 405 *dest++ = source[srcY * paddedSourceW + srcX] | 0xFF000000; 406 else 407 *dest++ |= 0xFF; 408 } 409 dest += padding; 410 } 411 } else if (isZero(transform.m_cosA)) { 412 int sinA = transform.m_sinA > 0 ? 1 : -1; 413 for (int y = 0; y < destH; ++y) { 414 for (int x = 0; x < destW; ++x) { 415 int x1 = x + transform.m_preShiftX; 416 int y1 = y + transform.m_preShiftY; 417 int srcX = y1 * sinA + transform.m_postShiftX; 418 int srcY = -x1 * sinA + transform.m_postShiftY; 419 if (srcX >= 0 && srcX <= sourceW && srcY >= 0 && srcY <= sourceH) 420 *dest++ = source[srcY * paddedSourceW + srcX]; 421 } 422 dest += padding; 423 } 424 } else { 425 for (int y = 0; y < destH; ++y) { 426 for (int x = 0; x < destW; ++x) { 427 // FIXME: for best quality, we should get weighted sum of four neighbours, 428 // but that will be too expensive 429 int srcX, srcY; 430 transform.map(x, y, &srcX, &srcY); 431 if (srcX >= 0 && srcX <= sourceW && srcY >= 0 && srcY <= sourceH) 432 *dest++ = source[srcY * paddedSourceW + srcX]; 433 } 434 dest += padding; 435 } 436 } 437} 438 439static void rotateBitmap(SharedBitmap* destBmp, const SharedBitmap* sourceBmp, const RotationTransform& transform) 440{ 441 ASSERT(destBmp->is16bit() == sourceBmp->is16bit()); 442 if (destBmp->is16bit()) 443 _rotateBitmap<unsigned short, true>(destBmp, sourceBmp, transform); 444 else 445 _rotateBitmap<unsigned, false>(destBmp, sourceBmp, transform); 446} 447 448class TransparentLayerDC { 449 WTF_MAKE_NONCOPYABLE(TransparentLayerDC); 450public: 451 TransparentLayerDC(GraphicsContextPlatformPrivate* data, IntRect& origRect, const IntRect* rectBeforeTransform = 0, int alpha = 255, bool paintImage = false); 452 ~TransparentLayerDC(); 453 454 HDC hdc() const { return m_memDc; } 455 const RECT& rect() const { return m_bmpRect; } 456 IntSize toShift() const { return IntSize(m_bmpRect.left - m_origRect.x(), m_bmpRect.top - m_origRect.y()); } 457 void fillAlphaChannel(); 458 459private: 460 GraphicsContextPlatformPrivate* m_data; 461 IntRect m_origRect; 462 IntRect m_rotatedOrigRect; 463 HDC m_memDc; 464 RefPtr<SharedBitmap> m_bitmap; 465 RefPtr<SharedBitmap> m_rotatedBitmap; 466 RECT m_bmpRect; 467 unsigned m_key; 468 RotationTransform m_rotation; 469 float m_oldOpacity; 470 AlphaPaintType m_alphaPaintType; 471}; 472 473TransparentLayerDC::TransparentLayerDC(GraphicsContextPlatformPrivate* data, IntRect& origRect, const IntRect* rectBeforeTransform, int alpha, bool paintImage) 474: m_data(data) 475, m_origRect(origRect) 476, m_oldOpacity(data->m_opacity) 477// m_key1 and m_key2 are not initalized here. They are used only in the case that 478// SharedBitmap::getDC() is called, I.E., when m_bitmap is not null. 479{ 480 m_data->m_opacity *= alpha / 255.; 481 bool mustCreateLayer; 482 if (!m_data->hasAlpha()) { 483 mustCreateLayer = false; 484 m_alphaPaintType = AlphaPaintNone; 485 } else { 486 mustCreateLayer = true; 487 m_alphaPaintType = paintImage ? AlphaPaintImage : AlphaPaintOther; 488 } 489 if (rectBeforeTransform && !isZero(m_data->m_transform.b())) { 490 m_rotatedOrigRect = origRect; 491 m_rotatedBitmap = m_data->getTransparentLayerBitmap(m_rotatedOrigRect, m_alphaPaintType, m_bmpRect, false, true); 492 if (m_rotatedBitmap) { 493 double a = m_data->m_transform.a(); 494 double b = m_data->m_transform.b(); 495 double c = _hypot(a, b); 496 m_rotation.m_cosA = a / c; 497 m_rotation.m_sinA = b / c; 498 499 int centerX = origRect.x() + origRect.width() / 2; 500 int centerY = origRect.y() + origRect.height() / 2; 501 m_rotation.m_preShiftX = -centerX; 502 m_rotation.m_preShiftY = -centerY; 503 m_rotation.m_postShiftX = centerX; 504 m_rotation.m_postShiftY = centerY; 505 506 m_origRect = mapRect(m_rotatedOrigRect, m_rotation); 507 508 m_rotation.m_preShiftX += m_rotatedOrigRect.x(); 509 m_rotation.m_preShiftY += m_rotatedOrigRect.y(); 510 m_rotation.m_postShiftX -= m_origRect.x(); 511 m_rotation.m_postShiftY -= m_origRect.y(); 512 513 FloatPoint topLeft = m_data->m_transform.mapPoint(FloatPoint(rectBeforeTransform->location())); 514 FloatPoint topRight(rectBeforeTransform->maxX() - 1, rectBeforeTransform->y()); 515 topRight = m_data->m_transform.mapPoint(topRight); 516 FloatPoint bottomLeft(rectBeforeTransform->x(), rectBeforeTransform->maxY() - 1); 517 bottomLeft = m_data->m_transform.mapPoint(bottomLeft); 518 FloatSize sideTop = topRight - topLeft; 519 FloatSize sideLeft = bottomLeft - topLeft; 520 float width = _hypot(sideTop.width() + 1, sideTop.height() + 1); 521 float height = _hypot(sideLeft.width() + 1, sideLeft.height() + 1); 522 523 origRect.inflateX(stableRound((width - origRect.width()) * 0.5)); 524 origRect.inflateY(stableRound((height - origRect.height()) * 0.5)); 525 526 m_bitmap = SharedBitmap::create(m_origRect.size(), m_rotatedBitmap->is16bit() ? BitmapInfo::BitCount16 : BitmapInfo::BitCount32, true); 527 if (m_bitmap) 528 rotateBitmap(m_bitmap.get(), m_rotatedBitmap.get(), -m_rotation); 529 else 530 m_rotatedBitmap = 0; 531 } 532 } else 533 m_bitmap = m_data->getTransparentLayerBitmap(m_origRect, m_alphaPaintType, m_bmpRect, true, mustCreateLayer); 534 if (m_bitmap) 535 m_memDc = m_bitmap->getDC(&m_key); 536 else 537 m_memDc = m_data->m_dc; 538} 539 540TransparentLayerDC::~TransparentLayerDC() 541{ 542 if (m_rotatedBitmap) { 543 m_bitmap->releaseDC(m_memDc, m_key); 544 m_key = 0; 545 rotateBitmap(m_rotatedBitmap.get(), m_bitmap.get(), m_rotation); 546 m_memDc = m_rotatedBitmap->getDC(&m_key); 547 m_data->paintBackTransparentLayerBitmap(m_memDc, m_rotatedBitmap.get(), m_rotatedOrigRect, m_alphaPaintType, m_bmpRect); 548 m_rotatedBitmap->releaseDC(m_memDc, m_key); 549 } else if (m_bitmap) { 550 m_data->paintBackTransparentLayerBitmap(m_memDc, m_bitmap.get(), m_origRect, m_alphaPaintType, m_bmpRect); 551 m_bitmap->releaseDC(m_memDc, m_key); 552 } 553 m_data->m_opacity = m_oldOpacity; 554} 555 556void TransparentLayerDC::fillAlphaChannel() 557{ 558 if (!m_bitmap || !m_bitmap->is32bit()) 559 return; 560 561 unsigned* pixels = (unsigned*)m_bitmap->bytes(); 562 const unsigned* const pixelsEnd = pixels + m_bitmap->bitmapInfo().numPixels(); 563 while (pixels < pixelsEnd) { 564 *pixels |= 0xFF000000; 565 ++pixels; 566 } 567} 568 569class ScopeDCProvider { 570 WTF_MAKE_NONCOPYABLE(ScopeDCProvider); 571public: 572 explicit ScopeDCProvider(GraphicsContextPlatformPrivate* data) 573 : m_data(data) 574 { 575 if (m_data->m_bitmap) 576 m_data->m_dc = m_data->m_bitmap->getDC(&m_key); 577 } 578 ~ScopeDCProvider() 579 { 580 if (m_data->m_bitmap) { 581 m_data->m_bitmap->releaseDC(m_data->m_dc, m_key); 582 m_data->m_dc = 0; 583 } 584 } 585private: 586 GraphicsContextPlatformPrivate* m_data; 587 unsigned m_key; 588}; 589 590 591void GraphicsContext::platformInit(PlatformGraphicsContext* dc, bool) 592{ 593 m_data = new GraphicsContextPlatformPrivate(dc); 594} 595 596void GraphicsContext::platformDestroy() 597{ 598 delete m_data; 599} 600 601void GraphicsContext::setBitmap(PassRefPtr<SharedBitmap> bmp) 602{ 603 ASSERT(!m_data->m_dc); 604 m_data->m_bitmap = bmp; 605} 606 607HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) 608{ 609 // FIXME: Add support for AlphaBlend. 610 ASSERT(!supportAlphaBlend); 611 return m_data->m_dc; 612} 613 614void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) 615{ 616} 617 618void GraphicsContext::savePlatformState() 619{ 620 m_data->save(); 621} 622 623void GraphicsContext::restorePlatformState() 624{ 625 m_data->restore(); 626} 627 628void GraphicsContext::drawRect(const FloatRect& rect, float) 629{ 630 if (!m_data->m_opacity || paintingDisabled() || rect.isEmpty()) 631 return; 632 633 ScopeDCProvider dcProvider(m_data); 634 if (!m_data->m_dc) 635 return; 636 637 IntRect trRect = m_data->mapRect(rect); 638 TransparentLayerDC transparentDC(m_data, trRect, &rect); 639 HDC dc = transparentDC.hdc(); 640 if (!dc) 641 return; 642 trRect.move(transparentDC.toShift()); 643 644 GDIObject<HBRUSH> brush; 645 HGDIOBJ oldBrush; 646 if (fillColor().alpha()) { 647 brush = createBrush(fillColor()); 648 oldBrush = SelectObject(dc, brush.get()); 649 } else 650 oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH)); 651 652 GDIObject<HPEN> pen; 653 HGDIOBJ oldPen; 654 if (strokeStyle() != NoStroke) { 655 pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); 656 oldPen = SelectObject(dc, pen.get()); 657 } else 658 oldPen = SelectObject(dc, GetStockObject(NULL_PEN)); 659 660 if (brush || pen) { 661 if (trRect.width() <= 0) 662 trRect.setWidth(1); 663 if (trRect.height() <= 0) 664 trRect.setHeight(1); 665 666 Rectangle(dc, trRect.x(), trRect.y(), trRect.maxX(), trRect.maxY()); 667 } 668 669 SelectObject(dc, oldPen); 670 SelectObject(dc, oldBrush); 671} 672 673void GraphicsContext::drawLine(const FloatPoint& point1, const FloatPoint& point2) 674{ 675 if (!m_data->m_opacity || paintingDisabled() || strokeStyle() == NoStroke || !strokeColor().alpha()) 676 return; 677 678 ScopeDCProvider dcProvider(m_data); 679 if (!m_data->m_dc) 680 return; 681 682 IntPoint trPoint1 = m_data->mapPoint(point1); 683 IntPoint trPoint2 = m_data->mapPoint(point2); 684 685 IntRect lineRect(trPoint1, trPoint2 - trPoint1); 686 lineRect.setHeight(lineRect.height() + strokeThickness()); 687 TransparentLayerDC transparentDC(m_data, lineRect, 0, strokeColor().alpha()); 688 HDC dc = transparentDC.hdc(); 689 if (!dc) 690 return; 691 trPoint1 += transparentDC.toShift(); 692 trPoint2 += transparentDC.toShift(); 693 694 auto pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); 695 HGDIOBJ oldPen = SelectObject(dc, pen.get()); 696 697 MoveToEx(dc, trPoint1.x(), trPoint1.y(), 0); 698 LineTo(dc, trPoint2.x(), trPoint2.y()); 699 700 SelectObject(dc, oldPen); 701} 702 703void GraphicsContext::drawEllipse(const IntRect& rect) 704{ 705 if (!m_data->m_opacity || paintingDisabled() || (!fillColor().alpha() && strokeStyle() == NoStroke)) 706 return; 707 708 ScopeDCProvider dcProvider(m_data); 709 if (!m_data->m_dc) 710 return; 711 712 IntRect trRect = m_data->mapRect(rect); 713 TransparentLayerDC transparentDC(m_data, trRect, &rect); 714 HDC dc = transparentDC.hdc(); 715 if (!dc) 716 return; 717 trRect.move(transparentDC.toShift()); 718 719 GDIObject<HBRUSH> brush; 720 HGDIOBJ oldBrush; 721 if (fillColor().alpha()) { 722 brush = createBrush(fillColor()); 723 oldBrush = SelectObject(dc, brush.get()); 724 } else 725 oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH)); 726 727 GDIObject<HPEN> pen; 728 HGDIOBJ oldPen = 0; 729 if (strokeStyle() != NoStroke) { 730 pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); 731 oldPen = SelectObject(dc, pen.get()); 732 } else 733 oldPen = SelectObject(dc, GetStockObject(NULL_PEN)); 734 735 if (brush || pen) 736 Ellipse(dc, trRect.x(), trRect.y(), trRect.maxX(), trRect.maxY()); 737 738 SelectObject(dc, oldPen); 739 SelectObject(dc, oldBrush); 740} 741 742static inline bool equalAngle(double a, double b) 743{ 744 return fabs(a - b) < 1E-5; 745} 746 747void getEllipsePointByAngle(double angle, double a, double b, float& x, float& y) 748{ 749 while (angle < 0) 750 angle += 2 * piDouble; 751 while (angle >= 2 * piDouble) 752 angle -= 2 * piDouble; 753 754 if (equalAngle(angle, 0) || equalAngle(angle, 2 * piDouble)) { 755 x = a; 756 y = 0; 757 } else if (equalAngle(angle, piDouble)) { 758 x = -a; 759 y = 0; 760 } else if (equalAngle(angle, .5 * piDouble)) { 761 x = 0; 762 y = b; 763 } else if (equalAngle(angle, 1.5 * piDouble)) { 764 x = 0; 765 y = -b; 766 } else { 767 double k = tan(angle); 768 double sqA = a * a; 769 double sqB = b * b; 770 double tmp = 1. / (1. / sqA + (k * k) / sqB); 771 tmp = tmp <= 0 ? 0 : sqrt(tmp); 772 if (angle > .5 * piDouble && angle < 1.5 * piDouble) 773 tmp = -tmp; 774 x = tmp; 775 776 k = tan(.5 * piDouble - angle); 777 tmp = 1. / ((k * k) / sqA + 1 / sqB); 778 tmp = tmp <= 0 ? 0 : sqrt(tmp); 779 if (angle > piDouble) 780 tmp = -tmp; 781 y = tmp; 782 } 783} 784 785void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias) 786{ 787 if (!m_data->m_opacity || paintingDisabled() || npoints <= 1 || !points) 788 return; 789 790 ScopeDCProvider dcProvider(m_data); 791 if (!m_data->m_dc) 792 return; 793 794 Vector<POINT, 20> winPoints(npoints); 795 FloatPoint trPoint = m_data->mapPoint(points[0]); 796 winPoints[0].x = stableRound(trPoint.x()); 797 winPoints[0].y = stableRound(trPoint.y()); 798 RECT rect = { winPoints[0].x, winPoints[0].y, winPoints[0].x, winPoints[0].y }; 799 for (size_t i = 1; i < npoints; ++i) { 800 trPoint = m_data->mapPoint(points[i]); 801 winPoints[i].x = stableRound(trPoint.x()); 802 winPoints[i].y = stableRound(trPoint.y()); 803 if (rect.left > winPoints[i].x) 804 rect.left = winPoints[i].x; 805 else if (rect.right < winPoints[i].x) 806 rect.right = winPoints[i].x; 807 if (rect.top > winPoints[i].y) 808 rect.top = winPoints[i].y; 809 else if (rect.bottom < winPoints[i].y) 810 rect.bottom = winPoints[i].y; 811 } 812 rect.bottom += 1; 813 rect.right += 1; 814 815 IntRect intRect(rect); 816 TransparentLayerDC transparentDC(m_data, intRect); 817 HDC dc = transparentDC.hdc(); 818 if (!dc) 819 return; 820 821 for (size_t i = 0; i < npoints; ++i) { 822 winPoints[i].x += transparentDC.toShift().width(); 823 winPoints[i].y += transparentDC.toShift().height(); 824 } 825 826 GDIObject<HBRUSH> brush; 827 HGDIOBJ oldBrush; 828 if (fillColor().alpha()) { 829 brush = createBrush(fillColor()); 830 oldBrush = SelectObject(dc, brush.get()); 831 } else 832 oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH)); 833 834 GDIObject<HPEN> pen; 835 HGDIOBJ oldPen; 836 if (strokeStyle() != NoStroke) { 837 pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); 838 oldPen = SelectObject(dc, pen.get()); 839 } else 840 oldPen = SelectObject(dc, GetStockObject(NULL_PEN)); 841 842 if (brush || pen) 843 Polygon(dc, winPoints.data(), npoints); 844 845 SelectObject(dc, oldPen); 846 SelectObject(dc, oldBrush); 847} 848 849void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased) 850{ 851 if (paintingDisabled()) 852 return; 853 854 if (numPoints <= 1) 855 return; 856 857 // FIXME: IMPLEMENT!! 858} 859 860void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace) 861{ 862 if (paintingDisabled() || !m_data->m_opacity) 863 return; 864 865 int alpha = color.alpha(); 866 if (!alpha) 867 return; 868 869 ScopeDCProvider dcProvider(m_data); 870 if (!m_data->m_dc) 871 return; 872 873 IntRect intRect = enclosingIntRect(rect); 874 TransparentLayerDC transparentDC(m_data, m_data->mapRect(intRect), &intRect, alpha); 875 876 if (!transparentDC.hdc()) 877 return; 878 879 auto hbrush = adoptGDIObject(::CreateSolidBrush(RGB(color.red(), color.green(), color.blue()))); 880 FillRect(transparentDC.hdc(), &transparentDC.rect(), hbrush.get()); 881} 882 883void GraphicsContext::clip(const FloatRect& rect) 884{ 885 if (paintingDisabled()) 886 return; 887 888 if (!m_data->m_dc) 889 return; 890 891 IntRect trRect = enclosingIntRect(m_data->mapRect(rect)); 892 893 auto clipRgn = adoptGDIObject(::CreateRectRgn(0, 0, 0, 0)); 894 if (GetClipRgn(m_data->m_dc, clipRgn.get()) > 0) 895 IntersectClipRect(m_data->m_dc, trRect.x(), trRect.y(), trRect.maxX(), trRect.maxY()); 896 else { 897 clipRgn = adoptGDIObject(::CreateRectRgn(trRect.x(), trRect.y(), trRect.maxX(), trRect.maxY())); 898 SelectClipRgn(m_data->m_dc, clipRgn.get()); 899 } 900} 901 902void GraphicsContext::clipOut(const FloatRect& rect) 903{ 904 if (paintingDisabled()) 905 return; 906 907 if (!m_data->m_dc) 908 return; 909 910 IntRect trRect = m_data->mapRect(rect); 911 912 ExcludeClipRect(m_data->m_dc, trRect.x(), trRect.y(), trRect.maxX(), trRect.maxY()); 913} 914 915void GraphicsContext::drawFocusRing(const Path& path, int width, int offset, const Color& color) 916{ 917 // FIXME: implement 918} 919 920void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color) 921{ 922 if (!m_data->m_opacity || paintingDisabled()) 923 return; 924 925 ScopeDCProvider dcProvider(m_data); 926 if (!m_data->m_dc) 927 return; 928 929 int radius = (width - 1) / 2; 930 offset += radius; 931 932 unsigned rectCount = rects.size(); 933 IntRect finalFocusRect; 934 for (unsigned i = 0; i < rectCount; i++) { 935 IntRect focusRect = rects[i]; 936 focusRect.inflate(offset); 937 finalFocusRect.unite(focusRect); 938 } 939 940 IntRect intRect = finalFocusRect; 941 IntRect trRect = m_data->mapRect(finalFocusRect); 942 TransparentLayerDC transparentDC(m_data, trRect, &intRect); 943 HDC dc = transparentDC.hdc(); 944 if (!dc) 945 return; 946 trRect.move(transparentDC.toShift()); 947 948 RECT rect = trRect; 949 DrawFocusRect(dc, &rect); 950} 951 952FloatRect GraphicsContext::computeLineBoundsForText(const FloatPoint& origin, float width, bool printing) 953{ 954 return FloatRect(origin, FloatSize(width, strokeThickness())); 955} 956 957void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool printing, bool doubleUnderlines) 958{ 959 if (paintingDisabled()) 960 return; 961 962 StrokeStyle oldStyle = strokeStyle(); 963 setStrokeStyle(SolidStroke); 964 drawLine(roundedIntPoint(origin), roundedIntPoint(origin + FloatSize(width, 0))); 965 if (doubleUnderlines) 966 drawLine(roundedIntPoint(origin + FloatSize(0, strokeThickness() * 2)), roundedIntPoint(origin + FloatSize(width, strokeThickness() * 2))); 967 setStrokeStyle(oldStyle); 968} 969 970void GraphicsContext::drawLinesForText(const FloatPoint& point, const DashArray& widths, bool printing, bool doubleUnderlines) 971{ 972 for (size_t i = 0; i < widths.size(); i += 2) 973 drawLineForText(FloatPoint(point.x() + widths[i], point.y()), widths[i+1] - widths[i], printing, doubleUnderlines); 974} 975 976void GraphicsContext::updateDocumentMarkerResources() 977{ 978 notImplemented(); 979} 980 981void GraphicsContext::drawLineForDocumentMarker(const FloatPoint&, float width, DocumentMarkerLineStyle style) 982{ 983 notImplemented(); 984} 985 986void GraphicsContext::setPlatformFillColor(const Color& col, ColorSpace colorSpace) 987{ 988 notImplemented(); 989} 990 991void GraphicsContext::setPlatformStrokeColor(const Color& col, ColorSpace colorSpace) 992{ 993 notImplemented(); 994} 995 996void GraphicsContext::setPlatformStrokeThickness(float strokeThickness) 997{ 998 notImplemented(); 999} 1000 1001void GraphicsContext::setURLForRect(const URL& link, const IntRect& destRect) 1002{ 1003 notImplemented(); 1004} 1005 1006void GraphicsContext::clearRect(const FloatRect& rect) 1007{ 1008 if (paintingDisabled()) 1009 return; 1010 1011 if (m_data->hasAlpha()) { 1012 IntRect trRect = enclosingIntRect(m_data->mapRect(rect)); 1013 m_data->m_bitmap->clearPixels(trRect); 1014 return; 1015 } 1016 1017 fillRect(rect, Color(Color::white), ColorSpaceDeviceRGB); 1018} 1019 1020void GraphicsContext::strokeRect(const FloatRect& rect, float width) 1021{ 1022 if (!m_data->m_opacity || paintingDisabled() || strokeStyle() == NoStroke) 1023 return; 1024 1025 ScopeDCProvider dcProvider(m_data); 1026 if (!m_data->m_dc) 1027 return; 1028 1029 IntRect intRect = enclosingIntRect(rect); 1030 IntRect trRect = m_data->mapRect(intRect); 1031 TransparentLayerDC transparentDC(m_data, trRect, &intRect); 1032 HDC dc = transparentDC.hdc(); 1033 if (!dc) 1034 return; 1035 trRect.move(transparentDC.toShift()); 1036 1037 auto pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); 1038 HGDIOBJ oldPen = SelectObject(dc, pen.get()); 1039 1040 int right = trRect.maxX() - 1; 1041 int bottom = trRect.maxY() - 1; 1042 const POINT intPoints[5] = 1043 { 1044 { trRect.x(), trRect.y() }, 1045 { right, trRect.y() }, 1046 { right, bottom }, 1047 { trRect.x(), bottom }, 1048 { trRect.x(), trRect.y() } 1049 }; 1050 1051 Polyline(dc, intPoints, 5); 1052 1053 SelectObject(dc, oldPen); 1054} 1055 1056void GraphicsContext::beginPlatformTransparencyLayer(float opacity) 1057{ 1058 m_data->save(); 1059 m_data->m_opacity *= opacity; 1060} 1061 1062void GraphicsContext::endPlatformTransparencyLayer() 1063{ 1064 m_data->restore(); 1065} 1066 1067bool GraphicsContext::supportsTransparencyLayers() 1068{ 1069 return true; 1070} 1071 1072void GraphicsContext::concatCTM(const AffineTransform& transform) 1073{ 1074 m_data->concatCTM(transform); 1075} 1076 1077void GraphicsContext::setCTM(const AffineTransform& transform) 1078{ 1079 m_data->setCTM(transform); 1080} 1081 1082AffineTransform& GraphicsContext::affineTransform() 1083{ 1084 return m_data->m_transform; 1085} 1086 1087const AffineTransform& GraphicsContext::affineTransform() const 1088{ 1089 return m_data->m_transform; 1090} 1091 1092void GraphicsContext::resetAffineTransform() 1093{ 1094 m_data->m_transform.makeIdentity(); 1095} 1096 1097void GraphicsContext::translate(float x, float y) 1098{ 1099 m_data->translate(x, y); 1100} 1101 1102void GraphicsContext::rotate(float radians) 1103{ 1104 m_data->rotate(radians); 1105} 1106 1107void GraphicsContext::scale(const FloatSize& size) 1108{ 1109 m_data->scale(size); 1110} 1111 1112void GraphicsContext::setLineCap(LineCap lineCap) 1113{ 1114 notImplemented(); 1115} 1116 1117void GraphicsContext::setLineJoin(LineJoin lineJoin) 1118{ 1119 notImplemented(); 1120} 1121 1122void GraphicsContext::setMiterLimit(float miter) 1123{ 1124 notImplemented(); 1125} 1126 1127void GraphicsContext::setAlpha(float alpha) 1128{ 1129 m_data->m_opacity = alpha; 1130} 1131 1132void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op, BlendMode blendMode) 1133{ 1134 notImplemented(); 1135} 1136 1137void GraphicsContext::clip(const Path& path, WindRule) 1138{ 1139 notImplemented(); 1140} 1141 1142void GraphicsContext::canvasClip(const Path& path, WindRule fillRule) 1143{ 1144 clip(path, fillRule); 1145} 1146 1147void GraphicsContext::clipOut(const Path&) 1148{ 1149 notImplemented(); 1150} 1151 1152static inline IntPoint rectCenterPoint(const RECT& rect) 1153{ 1154 return IntPoint(rect.left + (rect.right - rect.left) / 2, rect.top + (rect.bottom - rect.top) / 2); 1155} 1156 1157void GraphicsContext::fillRoundedRect(const FloatRoundedRect& rect, const Color& c, ColorSpace colorSpace) 1158{ 1159 ScopeDCProvider dcProvider(m_data); 1160 if (!m_data->m_dc) 1161 return; 1162 1163 FloatSize shadowOffset; 1164 float shadowBlur = 0; 1165 Color shadowColor; 1166 ColorSpace shadowColorSpace; 1167 1168 getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace); 1169 1170 const FloatRect& fillRect = rect.rect(); 1171 IntRect dstRect = fillRect; 1172 1173 dstRect.move(stableRound(shadowOffset.width()), stableRound(shadowOffset.height())); 1174 dstRect.inflate(stableRound(shadowBlur)); 1175 dstRect = m_data->mapRect(dstRect); 1176 1177 FloatSize newTopLeft(m_data->mapSize(rect.radii().topLeft())); 1178 FloatSize newTopRight(m_data->mapSize(rect.radii().topRight())); 1179 FloatSize newBottomLeft(m_data->mapSize(rect.radii().bottomLeft())); 1180 FloatSize newBottomRight(m_data->mapSize(rect.radii().bottomRight())); 1181 1182 TransparentLayerDC transparentDc(m_data, dstRect, &fillRect); 1183 HDC dc = transparentDc.hdc(); 1184 if (!dc) 1185 return; 1186 1187 dstRect.move(transparentDc.toShift()); 1188 1189 RECT rectWin = dstRect; 1190 1191 auto brush = createBrush(shadowColor); 1192 HGDIOBJ oldBrush = SelectObject(dc, brush.get()); 1193 1194 SelectObject(dc, GetStockObject(NULL_PEN)); 1195 1196 IntPoint centerPoint = rectCenterPoint(rectWin); 1197 // Draw top left half 1198 RECT clipRect(rectWin); 1199 clipRect.right = centerPoint.x(); 1200 clipRect.bottom = centerPoint.y(); 1201 1202 auto clipRgn = adoptGDIObject(::CreateRectRgn(0, 0, 0, 0)); 1203 bool needsNewClip = (GetClipRgn(dc, clipRgn.get()) <= 0); 1204 1205 drawRoundCorner(needsNewClip, clipRect, rectWin, dc, stableRound(newTopLeft.width() * 2), stableRound(newTopLeft.height() * 2)); 1206 1207 // Draw top right 1208 clipRect = rectWin; 1209 clipRect.left = centerPoint.x(); 1210 clipRect.bottom = centerPoint.y(); 1211 1212 drawRoundCorner(needsNewClip, clipRect, rectWin, dc, stableRound(newTopRight.width() * 2), stableRound(newTopRight.height() * 2)); 1213 1214 // Draw bottom left 1215 clipRect = rectWin; 1216 clipRect.right = centerPoint.x(); 1217 clipRect.top = centerPoint.y(); 1218 1219 drawRoundCorner(needsNewClip, clipRect, rectWin, dc, stableRound(newBottomLeft.width() * 2), stableRound(newBottomLeft.height() * 2)); 1220 1221 // Draw bottom right 1222 clipRect = rectWin; 1223 clipRect.left = centerPoint.x(); 1224 clipRect.top = centerPoint.y(); 1225 1226 drawRoundCorner(needsNewClip, clipRect, rectWin, dc, stableRound(newBottomRight.width() * 2), stableRound(newBottomRight.height() * 2)); 1227 1228 SelectObject(dc, oldBrush); 1229} 1230 1231void GraphicsContext::drawRoundCorner(bool needsNewClip, RECT clipRect, RECT rectWin, HDC dc, int width, int height) 1232{ 1233 if (!dc) 1234 return; 1235 1236 auto clipRgn = adoptGDIObject(::CreateRectRgn(0, 0, 0, 0)); 1237 if (needsNewClip) { 1238 clipRgn = adoptGDIObject(::CreateRectRgn(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom)); 1239 SelectClipRgn(dc, clipRgn.get()); 1240 } else 1241 IntersectClipRect(dc, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom); 1242 1243 ::RoundRect(dc, rectWin.left , rectWin.top , rectWin.right , rectWin.bottom , width, height); 1244 1245 SelectClipRgn(dc, needsNewClip ? 0 : clipRgn.get()); 1246} 1247 1248 1249FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingMode) 1250{ 1251 notImplemented(); 1252 return frect; 1253} 1254 1255Color gradientAverageColor(const Gradient* gradient) 1256{ 1257 const Vector<Gradient::ColorStop>& stops = gradient->getStops(); 1258 if (stops.isEmpty()) 1259 return Color(); 1260 1261 const Gradient::ColorStop& stop = stops.first(); 1262 if (stops.size() == 1) 1263 return Color(stop.red, stop.green, stop.blue, stop.alpha); 1264 1265 const Gradient::ColorStop& lastStop = stops.last(); 1266 return Color((stop.red + lastStop.red) * 0.5f 1267 , (stop.green + lastStop.green) * 0.5f 1268 , (stop.blue + lastStop.blue) * 0.5f 1269 , (stop.alpha + lastStop.alpha) * 0.5f); 1270} 1271 1272void GraphicsContext::fillPath(const Path& path) 1273{ 1274 if (path.isNull()) 1275 return; 1276 1277 Color c = m_state.fillGradient 1278 ? gradientAverageColor(m_state.fillGradient.get()) 1279 : fillColor(); 1280 1281 if (!c.alpha() || !m_data->m_opacity) 1282 return; 1283 1284 ScopeDCProvider dcProvider(m_data); 1285 if (!m_data->m_dc) 1286 return; 1287 1288 auto brush = createBrush(c); 1289 1290 if (m_data->m_opacity < 1.0f || m_data->hasAlpha()) { 1291 IntRect trRect = enclosingIntRect(m_data->mapRect(path.boundingRect())); 1292 trRect.inflate(1); 1293 TransparentLayerDC transparentDC(m_data, trRect); 1294 HDC dc = transparentDC.hdc(); 1295 if (!dc) 1296 return; 1297 1298 AffineTransform tr = m_data->m_transform; 1299 tr.translate(transparentDC.toShift().width(), transparentDC.toShift().height()); 1300 1301 SelectObject(dc, GetStockObject(NULL_PEN)); 1302 HGDIOBJ oldBrush = SelectObject(dc, brush.get()); 1303 path.platformPath()->fillPath(dc, &tr); 1304 SelectObject(dc, oldBrush); 1305 } else { 1306 SelectObject(m_data->m_dc, GetStockObject(NULL_PEN)); 1307 HGDIOBJ oldBrush = SelectObject(m_data->m_dc, brush.get()); 1308 path.platformPath()->fillPath(m_data->m_dc, &m_data->m_transform); 1309 SelectObject(m_data->m_dc, oldBrush); 1310 } 1311} 1312 1313 1314void GraphicsContext::strokePath(const Path& path) 1315{ 1316 if (path.isNull() || !m_data->m_opacity) 1317 return; 1318 1319 ScopeDCProvider dcProvider(m_data); 1320 if (!m_data->m_dc) 1321 return; 1322 1323 auto pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); 1324 1325 if (m_data->m_opacity < 1.0f || m_data->hasAlpha()) { 1326 IntRect trRect = enclosingIntRect(m_data->mapRect(path.boundingRect())); 1327 trRect.inflate(1); 1328 TransparentLayerDC transparentDC(m_data, trRect); 1329 HDC dc = transparentDC.hdc(); 1330 if (!dc) 1331 return; 1332 1333 AffineTransform tr = m_data->m_transform; 1334 tr.translate(transparentDC.toShift().width(), transparentDC.toShift().height()); 1335 1336 SelectObject(dc, GetStockObject(NULL_BRUSH)); 1337 HGDIOBJ oldPen = SelectObject(dc, pen.get()); 1338 path.platformPath()->strokePath(dc, &tr); 1339 SelectObject(dc, oldPen); 1340 } else { 1341 SelectObject(m_data->m_dc, GetStockObject(NULL_BRUSH)); 1342 HGDIOBJ oldPen = SelectObject(m_data->m_dc, pen.get()); 1343 path.platformPath()->strokePath(m_data->m_dc, &m_data->m_transform); 1344 SelectObject(m_data->m_dc, oldPen); 1345 } 1346} 1347 1348void GraphicsContext::fillRect(const FloatRect& r, const Gradient* gradient) 1349{ 1350 if (!m_data->m_opacity) 1351 return; 1352 1353 const Vector<Gradient::ColorStop>& stops = gradient->getStops(); 1354 if (stops.isEmpty()) 1355 return; 1356 1357 size_t numStops = stops.size(); 1358 if (numStops == 1) { 1359 const Gradient::ColorStop& stop = stops.first(); 1360 Color color(stop.red, stop.green, stop.blue, stop.alpha); 1361 fillRect(r, color, ColorSpaceDeviceRGB); 1362 return; 1363 } 1364 1365 ScopeDCProvider dcProvider(m_data); 1366 if (!m_data->m_dc) 1367 return; 1368 1369 IntRect intRect = enclosingIntRect(r); 1370 IntRect rect = m_data->mapRect(intRect); 1371 TransparentLayerDC transparentDC(m_data, rect, &intRect, 255, true); 1372 HDC dc = transparentDC.hdc(); 1373 if (!dc) 1374 return; 1375 1376 rect.move(transparentDC.toShift()); 1377 FloatPoint fp0 = m_data->mapPoint(gradient->p0()); 1378 FloatPoint fp1 = m_data->mapPoint(gradient->p1()); 1379 IntPoint p0(stableRound(fp0.x()), stableRound(fp0.y())); 1380 IntPoint p1(stableRound(fp1.x()), stableRound(fp1.y())); 1381 p0 += transparentDC.toShift(); 1382 p1 += transparentDC.toShift(); 1383 1384 if (gradient->isRadial()) { 1385 if (g_radialGradientFiller) { 1386 // FIXME: don't support 2D scaling at this time 1387 double scale = (m_data->m_transform.a() + m_data->m_transform.d()) * 0.5; 1388 float r0 = gradient->startRadius() * scale; 1389 float r1 = gradient->endRadius() * scale; 1390 g_radialGradientFiller(dc, rect, p0, p1, r0, r1, gradient->getStops()); 1391 return; 1392 } 1393 } else if (g_linearGradientFiller) { 1394 g_linearGradientFiller(dc, rect, p0, p1, gradient->getStops()); 1395 return; 1396 } 1397 1398 // Simple 1D linear solution that assumes p0 is on the top or left side, and p1 is on the right or bottom side 1399 size_t numRects = (numStops - 1); 1400 Vector<TRIVERTEX, 20> tv; 1401 tv.resize(numRects * 2); 1402 Vector<GRADIENT_RECT, 10> mesh; 1403 mesh.resize(numRects); 1404 int x = rect.x(); 1405 int y = rect.y(); 1406 int width = rect.width(); 1407 int height = rect.height(); 1408 FloatSize d = gradient->p1() - gradient->p0(); 1409 bool vertical = fabs(d.height()) > fabs(d.width()); 1410 for (size_t i = 0; i < numStops; ++i) { 1411 const Gradient::ColorStop& stop = stops[i]; 1412 int iTv = i ? 2 * i - 1 : 0; 1413 tv[iTv].Red = stop.red * 0xFFFF; 1414 tv[iTv].Green = stop.green * 0xFFFF; 1415 tv[iTv].Blue = stop.blue * 0xFFFF; 1416 tv[iTv].Alpha = stop.alpha * 0xFFFF; 1417 if (i) { 1418 tv[iTv].x = vertical ? x + width: x + width * stop.stop; 1419 tv[iTv].y = vertical ? y + height * stop.stop : y + height; 1420 mesh[i - 1].UpperLeft = iTv - 1; 1421 mesh[i - 1].LowerRight = iTv; 1422 } else { 1423 tv[iTv].x = x; 1424 tv[iTv].y = y; 1425 } 1426 1427 if (i && i < numRects) { 1428 tv[iTv + 1] = tv[iTv]; 1429 if (vertical) 1430 tv[iTv + 1].x = x; 1431 else 1432 tv[iTv + 1].y = y; 1433 } 1434 } 1435 1436 GradientFill(dc, tv.data(), tv.size(), mesh.data(), mesh.size(), vertical ? GRADIENT_FILL_RECT_V : GRADIENT_FILL_RECT_H); 1437} 1438 1439AffineTransform GraphicsContext::getCTM(IncludeDeviceScale) const 1440{ 1441 if (paintingDisabled()) 1442 return AffineTransform(); 1443 1444 return m_data->m_transform; 1445} 1446 1447void GraphicsContext::fillRect(const FloatRect& rect) 1448{ 1449 savePlatformState(); 1450 1451 if (m_state.fillGradient) 1452 fillRect(rect, m_state.fillGradient.get()); 1453 else 1454 fillRect(rect, fillColor(), ColorSpaceDeviceRGB); 1455 1456 restorePlatformState(); 1457} 1458 1459void GraphicsContext::setPlatformShadow(const FloatSize&, float, const Color&, ColorSpace) 1460{ 1461 notImplemented(); 1462} 1463 1464void GraphicsContext::clearPlatformShadow() 1465{ 1466 notImplemented(); 1467} 1468 1469InterpolationQuality GraphicsContext::imageInterpolationQuality() const 1470{ 1471 notImplemented(); 1472 return InterpolationDefault; 1473} 1474 1475void GraphicsContext::setImageInterpolationQuality(InterpolationQuality) 1476{ 1477 notImplemented(); 1478} 1479 1480static inline bool isCharVisible(UChar c) 1481{ 1482 return c && c != zeroWidthSpace; 1483} 1484 1485void GraphicsContext::drawText(const Font& font, const TextRun& run, const FloatPoint& point, int from, int to) 1486{ 1487 if (paintingDisabled() || !fillColor().alpha() || !m_data->m_opacity) 1488 return; 1489 1490 bool mustSupportAlpha = m_data->hasAlpha(); 1491 1492 if (!mustSupportAlpha && fillColor().alpha() == 0xFF && m_data->m_opacity >= 1.0) { 1493 font.drawText(this, run, point, from, to); 1494 return; 1495 } 1496 1497 float oldOpacity = m_data->m_opacity; 1498 m_data->m_opacity *= fillColor().alpha() / 255.0; 1499 1500 FloatRect textRect = font.selectionRectForText(run, point, font.fontMetrics().height(), from, to); 1501 textRect.setY(textRect.y() - font.fontMetrics().ascent()); 1502 IntRect trRect = enclosingIntRect(m_data->mapRect(textRect)); 1503 RECT bmpRect; 1504 AlphaPaintType alphaPaintType = mustSupportAlpha ? AlphaPaintOther : AlphaPaintNone; 1505 if (RefPtr<SharedBitmap> bmp = m_data->getTransparentLayerBitmap(trRect, alphaPaintType, bmpRect, true, mustSupportAlpha)) { 1506 { 1507 GraphicsContext gc(0); 1508 gc.setBitmap(bmp); 1509 gc.scale(FloatSize(m_data->m_transform.a(), m_data->m_transform.d())); 1510 font.drawText(&gc, run, IntPoint(0, font.fontMetrics().ascent()), from, to); 1511 } 1512 unsigned key1; 1513 HDC memDC = bmp->getDC(&key1); 1514 if (memDC) { 1515 m_data->paintBackTransparentLayerBitmap(memDC, bmp.get(), trRect, alphaPaintType, bmpRect); 1516 bmp->releaseDC(memDC, key1); 1517 } 1518 } 1519 1520 m_data->m_opacity = oldOpacity; 1521} 1522 1523void GraphicsContext::drawText(const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer, 1524 int from, int numGlyphs, const FloatPoint& point) 1525{ 1526 if (!m_data->m_opacity) 1527 return; 1528 1529 for (;;) { 1530 if (!numGlyphs) 1531 return; 1532 if (isCharVisible(*glyphBuffer.glyphs(from))) 1533 break; 1534 ++from; 1535 --numGlyphs; 1536 } 1537 1538 double scaleX = m_data->m_transform.a(); 1539 double scaleY = m_data->m_transform.d(); 1540 1541 int height = fontData->platformData().size() * scaleY; 1542 int width = fontData->avgCharWidth() * scaleX; 1543 1544 if (!height || !width) 1545 return; 1546 1547 ScopeDCProvider dcProvider(m_data); 1548 if (!m_data->m_dc) 1549 return; 1550 1551 HFONT hFont = height > 1 1552 ? fontData->platformData().getScaledFontHandle(height, scaleX == scaleY ? 0 : width) 1553 : 0; 1554 1555 FloatPoint startPoint(point.x(), point.y() - fontData->fontMetrics().ascent()); 1556 FloatPoint trPoint = m_data->mapPoint(startPoint); 1557 int y = stableRound(trPoint.y()); 1558 1559 Color color = fillColor(); 1560 if (!color.alpha()) 1561 return; 1562 1563 COLORREF fontColor = RGB(color.red(), color.green(), color.blue()); 1564 1565 if (!hFont) { 1566 double offset = trPoint.x(); 1567 const GlyphBufferAdvance* advance = glyphBuffer.advances(from); 1568 if (scaleX == 1.) 1569 for (int i = 1; i < numGlyphs; ++i) 1570 offset += (*advance++).width(); 1571 else 1572 for (int i = 1; i < numGlyphs; ++i) 1573 offset += (*advance++).width() * scaleX; 1574 1575 offset += width; 1576 1577 auto hPen = adoptGDIObject(::CreatePen(PS_DASH, 1, fontColor)); 1578 HGDIOBJ oldPen = SelectObject(m_data->m_dc, hPen.get()); 1579 1580 MoveToEx(m_data->m_dc, stableRound(trPoint.x()), y, 0); 1581 LineTo(m_data->m_dc, stableRound(offset), y); 1582 1583 SelectObject(m_data->m_dc, oldPen); 1584 return; 1585 } 1586 1587 FloatSize shadowOffset; 1588 float shadowBlur = 0; 1589 Color shadowColor; 1590 ColorSpace shadowColorSpace; 1591 bool hasShadow = textDrawingMode() == TextModeFill 1592 && getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace) 1593 && shadowColor.alpha(); 1594 COLORREF shadowRGBColor; 1595 FloatPoint trShadowPoint; 1596 if (hasShadow) { 1597 shadowRGBColor = RGB(shadowColor.red(), shadowColor.green(), shadowColor.blue()); 1598 trShadowPoint = m_data->mapPoint(startPoint + shadowOffset); 1599 } 1600 1601 HGDIOBJ hOldFont = SelectObject(m_data->m_dc, hFont); 1602 COLORREF oldTextColor = GetTextColor(m_data->m_dc); 1603 int oldTextAlign = GetTextAlign(m_data->m_dc); 1604 SetTextAlign(m_data->m_dc, 0); 1605 1606 int oldBkMode = GetBkMode(m_data->m_dc); 1607 SetBkMode(m_data->m_dc, TRANSPARENT); 1608 1609 if (numGlyphs > 1) { 1610 double offset = trPoint.x(); 1611 Vector<int, 256> glyphSpace(numGlyphs); 1612 Vector<UChar, 256> text(numGlyphs); 1613 int* curSpace = glyphSpace.data(); 1614 UChar* curChar = text.data(); 1615 const UChar* srcChar = glyphBuffer.glyphs(from); 1616 const UChar* const srcCharEnd = srcChar + numGlyphs; 1617 *curChar++ = *srcChar++; 1618 int firstOffset = stableRound(offset); 1619 int lastOffset = firstOffset; 1620 const GlyphBufferAdvance* advance = glyphBuffer.advances(from); 1621 // FIXME: ExtTextOut() can flip over each word for RTL languages, even when TA_RTLREADING is off. 1622 // (this can be GDI bug or font driver bug?) 1623 // We are not clear how it processes characters and handles specified spaces. On the other side, 1624 // our glyph buffer is already in the correct order for rendering. So, the solution is that we 1625 // call ExtTextOut() for each single character when the text contains any RTL character. 1626 // This solution is not perfect as it is slower than calling ExtTextOut() one time for all characters. 1627 // Drawing characters one by one may be too slow. 1628 bool drawOneByOne = false; 1629 if (scaleX == 1.) { 1630 for (; srcChar < srcCharEnd; ++srcChar) { 1631 offset += (*advance++).width(); 1632 int offsetInt = stableRound(offset); 1633 if (isCharVisible(*srcChar)) { 1634 if (!drawOneByOne && u_charDirection(*srcChar) == U_RIGHT_TO_LEFT) 1635 drawOneByOne = true; 1636 *curChar++ = *srcChar; 1637 *curSpace++ = offsetInt - lastOffset; 1638 lastOffset = offsetInt; 1639 } 1640 } 1641 } else { 1642 for (; srcChar < srcCharEnd; ++srcChar) { 1643 offset += (*advance++).width() * scaleX; 1644 int offsetInt = stableRound(offset); 1645 if (isCharVisible(*srcChar)) { 1646 if (!drawOneByOne && u_charDirection(*srcChar) == U_RIGHT_TO_LEFT) 1647 drawOneByOne = true; 1648 *curChar++ = *srcChar; 1649 *curSpace++ = offsetInt - lastOffset; 1650 lastOffset = offsetInt; 1651 } 1652 } 1653 } 1654 numGlyphs = curChar - text.data(); 1655 if (hasShadow) { 1656 SetTextColor(m_data->m_dc, shadowRGBColor); 1657 if (drawOneByOne) { 1658 int xShadow = firstOffset + stableRound(trShadowPoint.x() - trPoint.x()); 1659 int yShadow = stableRound(trShadowPoint.y()); 1660 for (int i = 0; i < numGlyphs; ++i) { 1661 ExtTextOut(m_data->m_dc, xShadow, yShadow, 0, NULL, text.data() + i, 1, 0); 1662 xShadow += glyphSpace[i]; 1663 } 1664 } else 1665 ExtTextOut(m_data->m_dc, firstOffset + stableRound(trShadowPoint.x() - trPoint.x()), stableRound(trShadowPoint.y()), 0, NULL, text.data(), numGlyphs, glyphSpace.data()); 1666 } 1667 SetTextColor(m_data->m_dc, fontColor); 1668 if (drawOneByOne) { 1669 int x = firstOffset; 1670 for (int i = 0; i < numGlyphs; ++i) { 1671 ExtTextOut(m_data->m_dc, x, y, 0, NULL, text.data() + i, 1, 0); 1672 x += glyphSpace[i]; 1673 } 1674 } else 1675 ExtTextOut(m_data->m_dc, firstOffset, y, 0, NULL, text.data(), numGlyphs, glyphSpace.data()); 1676 } else { 1677 UChar c = *glyphBuffer.glyphs(from); 1678 if (hasShadow) { 1679 SetTextColor(m_data->m_dc, shadowRGBColor); 1680 ExtTextOut(m_data->m_dc, stableRound(trShadowPoint.x()), stableRound(trShadowPoint.y()), 0, NULL, &c, 1, 0); 1681 } 1682 SetTextColor(m_data->m_dc, fontColor); 1683 ExtTextOut(m_data->m_dc, stableRound(trPoint.x()), y, 0, NULL, &c, 1, 0); 1684 } 1685 1686 SetTextAlign(m_data->m_dc, oldTextAlign); 1687 SetTextColor(m_data->m_dc, oldTextColor); 1688 SetBkMode(m_data->m_dc, oldBkMode); 1689 SelectObject(m_data->m_dc, hOldFont); 1690} 1691 1692void GraphicsContext::drawFrameControl(const IntRect& rect, unsigned type, unsigned state) 1693{ 1694 if (!m_data->m_opacity) 1695 return; 1696 1697 const int boxWidthBest = 8; 1698 const int boxHeightBest = 8; 1699 1700 ScopeDCProvider dcProvider(m_data); 1701 if (!m_data->m_dc) 1702 return; 1703 1704 IntRect trRect = m_data->mapRect(rect); 1705 TransparentLayerDC transparentDC(m_data, trRect, &rect, 255, true); 1706 HDC dc = transparentDC.hdc(); 1707 if (!dc) 1708 return; 1709 trRect.move(transparentDC.toShift()); 1710 1711 RECT rectWin = trRect; 1712 1713 if ((rectWin.right - rectWin.left) < boxWidthBest) { 1714 RefPtr<SharedBitmap> bmp = SharedBitmap::create(IntSize(boxWidthBest, boxHeightBest), BitmapInfo::BitCount16, true); 1715 SharedBitmap::DCHolder memDC(bmp.get()); 1716 if (memDC.get()) { 1717 RECT tempRect = {0, 0, boxWidthBest, boxHeightBest}; 1718 DrawFrameControl(memDC.get(), &tempRect, type, state); 1719 1720 ::StretchBlt(dc, rectWin.left, rectWin.top, rectWin.right - rectWin.left, rectWin.bottom - rectWin.top, memDC.get(), 0, 0, boxWidthBest, boxHeightBest, SRCCOPY); 1721 return; 1722 } 1723 } 1724 1725 DrawFrameControl(dc, &rectWin, type, state); 1726} 1727 1728void GraphicsContext::drawFocusRect(const IntRect& rect) 1729{ 1730 if (!m_data->m_opacity) 1731 return; 1732 1733 ScopeDCProvider dcProvider(m_data); 1734 if (!m_data->m_dc) 1735 return; 1736 1737 IntRect trRect = m_data->mapRect(rect); 1738 TransparentLayerDC transparentDC(m_data, trRect, &rect); 1739 HDC dc = transparentDC.hdc(); 1740 if (!dc) 1741 return; 1742 trRect.move(transparentDC.toShift()); 1743 1744 RECT rectWin = trRect; 1745 DrawFocusRect(dc, &rectWin); 1746} 1747 1748void GraphicsContext::paintTextField(const IntRect& rect, unsigned state) 1749{ 1750 if (!m_data->m_opacity) 1751 return; 1752 1753 ScopeDCProvider dcProvider(m_data); 1754 if (!m_data->m_dc) 1755 return; 1756 1757 IntRect trRect = m_data->mapRect(rect); 1758 TransparentLayerDC transparentDC(m_data, trRect, &rect); 1759 HDC dc = transparentDC.hdc(); 1760 if (!dc) 1761 return; 1762 trRect.move(transparentDC.toShift()); 1763 1764 RECT rectWin = trRect; 1765 DrawEdge(dc, &rectWin, EDGE_ETCHED, BF_RECT | BF_ADJUST); 1766 FillRect(dc, &rectWin, reinterpret_cast<HBRUSH>(((state & DFCS_INACTIVE) ? COLOR_BTNFACE : COLOR_WINDOW) + 1)); 1767} 1768 1769void GraphicsContext::drawBitmap(SharedBitmap* bmp, const IntRect& dstRectIn, const IntRect& srcRect, ColorSpace styleColorSpace, CompositeOperator compositeOp, BlendMode blendMode) 1770{ 1771 if (!m_data->m_opacity) 1772 return; 1773 1774 ScopeDCProvider dcProvider(m_data); 1775 if (!m_data->m_dc) 1776 return; 1777 1778 IntRect dstRect = m_data->mapRect(dstRectIn); 1779 TransparentLayerDC transparentDC(m_data, dstRect, &dstRectIn, 255, true); 1780 HDC dc = transparentDC.hdc(); 1781 if (!dc) 1782 return; 1783 dstRect.move(transparentDC.toShift()); 1784 1785 bmp->draw(dc, dstRect, srcRect, compositeOp, blendMode); 1786 1787 if (bmp->is16bit()) 1788 transparentDC.fillAlphaChannel(); 1789} 1790 1791void GraphicsContext::drawBitmapPattern(SharedBitmap* bmp, const FloatRect& tileRectIn, const AffineTransform& patternTransform, 1792 const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRectIn, const IntSize& origSourceSize) 1793{ 1794 if (!m_data->m_opacity) 1795 return; 1796 1797 ScopeDCProvider dcProvider(m_data); 1798 if (!m_data->m_dc) 1799 return; 1800 1801 IntRect intDstRect = enclosingIntRect(destRectIn); 1802 IntRect trRect = m_data->mapRect(intDstRect); 1803 TransparentLayerDC transparentDC(m_data, trRect, &intDstRect, 255, true); 1804 HDC dc = transparentDC.hdc(); 1805 if (!dc) 1806 return; 1807 trRect.move(transparentDC.toShift()); 1808 FloatRect movedDstRect = m_data->m_transform.inverse().mapRect(FloatRect(trRect)); 1809 FloatSize moved(movedDstRect.location() - destRectIn.location()); 1810 AffineTransform transform = m_data->m_transform; 1811 transform.translate(moved.width(), moved.height()); 1812 1813 bmp->drawPattern(dc, transform, tileRectIn, patternTransform, phase, styleColorSpace, op, destRectIn, origSourceSize); 1814 1815 if (!bmp->hasAlpha()) 1816 transparentDC.fillAlphaChannel(); 1817} 1818 1819void GraphicsContext::drawIcon(HICON icon, const IntRect& dstRectIn, UINT flags) 1820{ 1821 if (!m_data->m_opacity) 1822 return; 1823 1824 ScopeDCProvider dcProvider(m_data); 1825 if (!m_data->m_dc) 1826 return; 1827 1828 IntRect dstRect = m_data->mapRect(dstRectIn); 1829 TransparentLayerDC transparentDC(m_data, dstRect, &dstRectIn, 255, true); 1830 HDC dc = transparentDC.hdc(); 1831 if (!dc) 1832 return; 1833 dstRect.move(transparentDC.toShift()); 1834 1835 DrawIconEx(dc, dstRect.x(), dstRect.y(), icon, dstRect.width(), dstRect.height(), 0, NULL, flags); 1836} 1837 1838void GraphicsContext::setPlatformShouldAntialias(bool) 1839{ 1840 notImplemented(); 1841} 1842 1843void GraphicsContext::setLineDash(const DashArray&, float) 1844{ 1845 notImplemented(); 1846} 1847 1848void GraphicsContext::clipPath(const Path&, WindRule) 1849{ 1850 notImplemented(); 1851} 1852 1853} // namespace WebCore 1854