1 /* 2 * Copyright (C) 2007 Kevin Ollivier <kevino (at) theolliviers.com> 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 COMPUTER, INC. ``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 APPLE COMPUTER, INC. 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 #include "config.h" 27 #include "GraphicsContext.h" 28 29 #include "AffineTransform.h" 30 #include "FloatRect.h" 31 #include "Font.h" 32 #include "IntRect.h" 33 #include "NotImplemented.h" 34 #include <wtf/MathExtras.h> 35 36 #include <math.h> 37 #include <stdio.h> 38 39 #include <wx/defs.h> 40 #include <wx/window.h> 41 #include <wx/dcclient.h> 42 #include <wx/dcgraph.h> 43 #include <wx/graphics.h> 44 45 #if __WXMAC__ 46 #include <Carbon/Carbon.h> 47 #elif __WXMSW__ 48 #include <windows.h> 49 #endif 50 51 namespace WebCore { 52 53 int getWxCompositingOperation(CompositeOperator op, bool hasAlpha) 54 { 55 // FIXME: Add support for more operators. 56 if (op == CompositeSourceOver && !hasAlpha) 57 op = CompositeCopy; 58 59 int function; 60 switch (op) { 61 case CompositeClear: 62 function = wxCLEAR; 63 case CompositeCopy: 64 function = wxCOPY; 65 break; 66 default: 67 function = wxCOPY; 68 } 69 return function; 70 } 71 72 static int strokeStyleToWxPenStyle(int p) 73 { 74 if (p == SolidStroke) 75 return wxSOLID; 76 if (p == DottedStroke) 77 return wxDOT; 78 if (p == DashedStroke) 79 return wxLONG_DASH; 80 if (p == NoStroke) 81 return wxTRANSPARENT; 82 83 return wxSOLID; 84 } 85 86 class GraphicsContextPlatformPrivate { 87 public: 88 GraphicsContextPlatformPrivate(); 89 ~GraphicsContextPlatformPrivate(); 90 91 #if USE(WXGC) 92 wxGCDC* context; 93 #else 94 wxWindowDC* context; 95 #endif 96 int mswDCStateID; 97 wxRegion gtkCurrentClipRgn; 98 wxRegion gtkPaintClipRgn; 99 }; 100 101 GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate() : 102 context(0), 103 mswDCStateID(0), 104 gtkCurrentClipRgn(wxRegion()), 105 gtkPaintClipRgn(wxRegion()) 106 { 107 } 108 109 GraphicsContextPlatformPrivate::~GraphicsContextPlatformPrivate() 110 { 111 } 112 113 114 void GraphicsContext::platformInit(PlatformGraphicsContext* context) 115 { 116 m_data = new GraphicsContextPlatformPrivate; 117 setPaintingDisabled(!context); 118 119 if (context) { 120 // Make sure the context starts in sync with our state. 121 setPlatformFillColor(fillColor(), ColorSpaceDeviceRGB); 122 setPlatformStrokeColor(strokeColor(), ColorSpaceDeviceRGB); 123 } 124 #if USE(WXGC) 125 m_data->context = (wxGCDC*)context; 126 #else 127 m_data->context = (wxWindowDC*)context; 128 #endif 129 } 130 131 void GraphicsContext::platformDestroy() 132 { 133 delete m_data; 134 } 135 136 PlatformGraphicsContext* GraphicsContext::platformContext() const 137 { 138 return (PlatformGraphicsContext*)m_data->context; 139 } 140 141 void GraphicsContext::savePlatformState() 142 { 143 if (m_data->context) 144 { 145 #if USE(WXGC) 146 wxGraphicsContext* gc = m_data->context->GetGraphicsContext(); 147 gc->PushState(); 148 #else 149 // when everything is working with USE_WXGC, we can remove this 150 #if __WXMAC__ 151 CGContextRef context; 152 wxGraphicsContext* gc = m_data->context->GetGraphicsContext(); 153 if (gc) 154 context = (CGContextRef)gc->GetNativeContext(); 155 if (context) 156 CGContextSaveGState(context); 157 #elif __WXMSW__ 158 HDC dc = (HDC)m_data->context->GetHDC(); 159 m_data->mswDCStateID = ::SaveDC(dc); 160 #elif __WXGTK__ 161 m_data->gtkCurrentClipRgn = m_data->context->m_currentClippingRegion; 162 m_data->gtkPaintClipRgn = m_data->context->m_paintClippingRegion; 163 #endif 164 #endif // __WXMAC__ 165 } 166 } 167 168 void GraphicsContext::restorePlatformState() 169 { 170 if (m_data->context) 171 { 172 #if USE(WXGC) 173 wxGraphicsContext* gc = m_data->context->GetGraphicsContext(); 174 gc->PopState(); 175 #else 176 #if __WXMAC__ 177 CGContextRef context; 178 wxGraphicsContext* gc = m_data->context->GetGraphicsContext(); 179 if (gc) 180 context = (CGContextRef)gc->GetNativeContext(); 181 if (context) 182 CGContextRestoreGState(context); 183 #elif __WXMSW__ 184 HDC dc = (HDC)m_data->context->GetHDC(); 185 ::RestoreDC(dc, m_data->mswDCStateID); 186 #elif __WXGTK__ 187 m_data->context->m_currentClippingRegion = m_data->gtkCurrentClipRgn; 188 m_data->context->m_paintClippingRegion = m_data->gtkPaintClipRgn; 189 #endif 190 191 #endif // USE_WXGC 192 } 193 } 194 195 // Draws a filled rectangle with a stroked border. 196 void GraphicsContext::drawRect(const IntRect& rect) 197 { 198 if (paintingDisabled()) 199 return; 200 201 m_data->context->SetPen(wxPen(strokeColor(), strokeThickness(), strokeStyleToWxPenStyle(strokeStyle()))); 202 m_data->context->DrawRectangle(rect.x(), rect.y(), rect.width(), rect.height()); 203 } 204 205 // This is only used to draw borders. 206 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) 207 { 208 if (paintingDisabled()) 209 return; 210 211 FloatPoint p1 = point1; 212 FloatPoint p2 = point2; 213 214 m_data->context->SetPen(wxPen(strokeColor(), strokeThickness(), strokeStyleToWxPenStyle(strokeStyle()))); 215 m_data->context->DrawLine(point1.x(), point1.y(), point2.x(), point2.y()); 216 } 217 218 // This method is only used to draw the little circles used in lists. 219 void GraphicsContext::drawEllipse(const IntRect& rect) 220 { 221 if (paintingDisabled()) 222 return; 223 224 m_data->context->SetPen(wxPen(strokeColor(), strokeThickness(), strokeStyleToWxPenStyle(strokeStyle()))); 225 m_data->context->DrawEllipse(rect.x(), rect.y(), rect.width(), rect.height()); 226 } 227 228 void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan) 229 { 230 if (paintingDisabled()) 231 return; 232 233 m_data->context->SetPen(wxPen(strokeColor(), strokeThickness(), strokeStyleToWxPenStyle(strokeStyle()))); 234 m_data->context->DrawEllipticArc(rect.x(), rect.y(), rect.width(), rect.height(), startAngle, angleSpan); 235 } 236 237 void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias) 238 { 239 if (paintingDisabled()) 240 return; 241 242 if (npoints <= 1) 243 return; 244 245 wxPoint* polygon = new wxPoint[npoints]; 246 for (size_t i = 0; i < npoints; i++) 247 polygon[i] = wxPoint(points[i].x(), points[i].y()); 248 m_data->context->SetPen(wxPen(strokeColor(), strokeThickness(), strokeStyleToWxPenStyle(strokeStyle()))); 249 m_data->context->DrawPolygon((int)npoints, polygon); 250 delete [] polygon; 251 } 252 253 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased) 254 { 255 if (paintingDisabled()) 256 return; 257 258 if (numPoints <= 1) 259 return; 260 261 // FIXME: IMPLEMENT!! 262 } 263 264 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace) 265 { 266 if (paintingDisabled()) 267 return; 268 269 savePlatformState(); 270 271 m_data->context->SetPen(*wxTRANSPARENT_PEN); 272 m_data->context->SetBrush(wxBrush(color)); 273 m_data->context->DrawRectangle(rect.x(), rect.y(), rect.width(), rect.height()); 274 275 restorePlatformState(); 276 } 277 278 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace) 279 { 280 if (paintingDisabled()) 281 return; 282 283 notImplemented(); 284 } 285 286 void GraphicsContext::drawFocusRing(const Path& path, int width, int offset, const Color& color) 287 { 288 // FIXME: implement 289 } 290 291 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color) 292 { 293 if (paintingDisabled()) 294 return; 295 296 notImplemented(); 297 } 298 299 void GraphicsContext::clip(const FloatRect& r) 300 { 301 wxWindowDC* windc = dynamic_cast<wxWindowDC*>(m_data->context); 302 wxPoint pos(0, 0); 303 304 if (windc) { 305 #if !defined(__WXGTK__) || wxCHECK_VERSION(2,9,0) 306 wxWindow* window = windc->GetWindow(); 307 #else 308 wxWindow* window = windc->m_owner; 309 #endif 310 if (window) { 311 wxWindow* parent = window->GetParent(); 312 // we need to convert from WebView "global" to WebFrame "local" coords. 313 // FIXME: We only want to go to the top WebView. 314 while (parent) { 315 pos += window->GetPosition(); 316 parent = parent->GetParent(); 317 } 318 } 319 } 320 321 m_data->context->SetClippingRegion(r.x() - pos.x, r.y() - pos.y, r.width() + pos.x, r.height() + pos.y); 322 } 323 324 void GraphicsContext::clipOut(const Path&) 325 { 326 notImplemented(); 327 } 328 329 void GraphicsContext::clipOut(const IntRect&) 330 { 331 notImplemented(); 332 } 333 334 void GraphicsContext::clipPath(const Path&, WindRule) 335 { 336 notImplemented(); 337 } 338 339 void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool printing) 340 { 341 if (paintingDisabled()) 342 return; 343 344 FloatPoint endPoint = origin + FloatSize(width, 0); 345 m_data->context->SetPen(wxPen(strokeColor(), strokeThickness(), wxSOLID)); 346 m_data->context->DrawLine(origin.x(), origin.y(), endPoint.x(), endPoint.y()); 347 } 348 349 void GraphicsContext::drawLineForTextChecking(const FloatPoint& origin, float width, TextCheckingLineStyle style) 350 { 351 switch (style) { 352 case TextCheckingSpellingLineStyle: 353 m_data->context->SetPen(wxPen(*wxRED, 2, wxLONG_DASH)); 354 break; 355 case TextCheckingGrammarLineStyle: 356 m_data->context->SetPen(wxPen(*wxGREEN, 2, wxLONG_DASH)); 357 break; 358 default: 359 return; 360 } 361 m_data->context->DrawLine(origin.x(), origin.y(), origin.x() + width, origin.y()); 362 } 363 364 void GraphicsContext::clip(const Path&) 365 { 366 notImplemented(); 367 } 368 369 void GraphicsContext::canvasClip(const Path& path) 370 { 371 clip(path); 372 } 373 374 AffineTransform GraphicsContext::getCTM() const 375 { 376 #if USE(WXGC) 377 wxGraphicsContext* gc = m_data->context->GetGraphicsContext(); 378 if (gc) { 379 wxGraphicsMatrix matrix = gc->GetTransform(); 380 double a, b, c, d, e, f; 381 matrix.Get(&a, &b, &c, &d, &e, &f); 382 return AffineTransform(a, b, c, d, e, f); 383 } 384 #endif 385 return AffineTransform(); 386 } 387 388 void GraphicsContext::translate(float tx, float ty) 389 { 390 #if USE(WXGC) 391 if (m_data->context) { 392 wxGraphicsContext* gc = m_data->context->GetGraphicsContext(); 393 gc->Translate(tx, ty); 394 } 395 #endif 396 } 397 398 void GraphicsContext::rotate(float angle) 399 { 400 #if USE(WXGC) 401 if (m_data->context) { 402 wxGraphicsContext* gc = m_data->context->GetGraphicsContext(); 403 gc->Rotate(angle); 404 } 405 #endif 406 } 407 408 void GraphicsContext::scale(const FloatSize& scale) 409 { 410 #if USE(WXGC) 411 if (m_data->context) { 412 wxGraphicsContext* gc = m_data->context->GetGraphicsContext(); 413 gc->Scale(scale.width(), scale.height()); 414 } 415 #endif 416 } 417 418 419 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingMode) 420 { 421 FloatRect result; 422 423 wxCoord x = (wxCoord)frect.x(); 424 wxCoord y = (wxCoord)frect.y(); 425 426 x = m_data->context->LogicalToDeviceX(x); 427 y = m_data->context->LogicalToDeviceY(y); 428 result.setX((float)x); 429 result.setY((float)y); 430 x = (wxCoord)frect.width(); 431 y = (wxCoord)frect.height(); 432 x = m_data->context->LogicalToDeviceXRel(x); 433 y = m_data->context->LogicalToDeviceYRel(y); 434 result.setWidth((float)x); 435 result.setHeight((float)y); 436 return result; 437 } 438 439 void GraphicsContext::setURLForRect(const KURL&, const IntRect&) 440 { 441 notImplemented(); 442 } 443 444 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op) 445 { 446 if (m_data->context) 447 { 448 #if wxCHECK_VERSION(2,9,0) 449 m_data->context->SetLogicalFunction(static_cast<wxRasterOperationMode>(getWxCompositingOperation(op, false))); 450 #else 451 m_data->context->SetLogicalFunction(getWxCompositingOperation(op, false)); 452 #endif 453 } 454 } 455 456 void GraphicsContext::setPlatformStrokeColor(const Color& color, ColorSpace colorSpace) 457 { 458 if (paintingDisabled()) 459 return; 460 461 if (m_data->context) 462 m_data->context->SetPen(wxPen(color, strokeThickness(), strokeStyleToWxPenStyle(strokeStyle()))); 463 } 464 465 void GraphicsContext::setPlatformStrokeThickness(float thickness) 466 { 467 if (paintingDisabled()) 468 return; 469 470 if (m_data->context) 471 m_data->context->SetPen(wxPen(strokeColor(), thickness, strokeStyleToWxPenStyle(strokeStyle()))); 472 473 } 474 475 void GraphicsContext::setPlatformFillColor(const Color& color, ColorSpace colorSpace) 476 { 477 if (paintingDisabled()) 478 return; 479 480 if (m_data->context) 481 m_data->context->SetBrush(wxBrush(color)); 482 } 483 484 void GraphicsContext::concatCTM(const AffineTransform& transform) 485 { 486 if (paintingDisabled()) 487 return; 488 489 #if USE(WXGC) 490 wxGraphicsContext* gc = m_data->context->GetGraphicsContext(); 491 if (gc) 492 gc->ConcatTransform(transform); 493 #endif 494 return; 495 } 496 497 void GraphicsContext::setCTM(const AffineTransform& transform) 498 { 499 if (paintingDisabled()) 500 return; 501 502 #if USE(WXGC) 503 wxGraphicsContext* gc = m_data->context->GetGraphicsContext(); 504 if (gc) 505 gc->SetTransform(transform); 506 #endif 507 return; 508 } 509 510 void GraphicsContext::setPlatformShouldAntialias(bool enable) 511 { 512 if (paintingDisabled()) 513 return; 514 notImplemented(); 515 } 516 517 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality) 518 { 519 } 520 521 InterpolationQuality GraphicsContext::imageInterpolationQuality() const 522 { 523 return InterpolationDefault; 524 } 525 526 void GraphicsContext::fillPath(const Path& path) 527 { 528 #if USE(WXGC) 529 wxGraphicsContext* gc = m_data->context->GetGraphicsContext(); 530 if (gc) 531 gc->FillPath(*path.platformPath()); 532 #endif 533 } 534 535 void GraphicsContext::strokePath(const Path& path) 536 { 537 #if USE(WXGC) 538 wxGraphicsContext* gc = m_data->context->GetGraphicsContext(); 539 if (gc) 540 gc->StrokePath(*path.platformPath()); 541 #endif 542 } 543 544 void GraphicsContext::fillRect(const FloatRect& rect) 545 { 546 if (paintingDisabled()) 547 return; 548 } 549 550 void GraphicsContext::setPlatformShadow(FloatSize const&, float, Color const&, ColorSpace) 551 { 552 notImplemented(); 553 } 554 555 void GraphicsContext::clearPlatformShadow() 556 { 557 notImplemented(); 558 } 559 560 void GraphicsContext::beginTransparencyLayer(float) 561 { 562 notImplemented(); 563 } 564 565 void GraphicsContext::endTransparencyLayer() 566 { 567 notImplemented(); 568 } 569 570 void GraphicsContext::clearRect(const FloatRect&) 571 { 572 notImplemented(); 573 } 574 575 void GraphicsContext::strokeRect(const FloatRect&, float) 576 { 577 notImplemented(); 578 } 579 580 void GraphicsContext::setLineCap(LineCap) 581 { 582 notImplemented(); 583 } 584 585 void GraphicsContext::setLineDash(const DashArray&, float dashOffset) 586 { 587 notImplemented(); 588 } 589 590 void GraphicsContext::setLineJoin(LineJoin) 591 { 592 notImplemented(); 593 } 594 595 void GraphicsContext::setMiterLimit(float) 596 { 597 notImplemented(); 598 } 599 600 void GraphicsContext::setAlpha(float) 601 { 602 notImplemented(); 603 } 604 605 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) 606 { 607 notImplemented(); 608 } 609 610 #if OS(WINDOWS) 611 HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) 612 { 613 if (dstRect.isEmpty()) 614 return 0; 615 616 // Create a bitmap DC in which to draw. 617 BITMAPINFO bitmapInfo; 618 bitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 619 bitmapInfo.bmiHeader.biWidth = dstRect.width(); 620 bitmapInfo.bmiHeader.biHeight = dstRect.height(); 621 bitmapInfo.bmiHeader.biPlanes = 1; 622 bitmapInfo.bmiHeader.biBitCount = 32; 623 bitmapInfo.bmiHeader.biCompression = BI_RGB; 624 bitmapInfo.bmiHeader.biSizeImage = 0; 625 bitmapInfo.bmiHeader.biXPelsPerMeter = 0; 626 bitmapInfo.bmiHeader.biYPelsPerMeter = 0; 627 bitmapInfo.bmiHeader.biClrUsed = 0; 628 bitmapInfo.bmiHeader.biClrImportant = 0; 629 630 void* pixels = 0; 631 HBITMAP bitmap = ::CreateDIBSection(0, &bitmapInfo, DIB_RGB_COLORS, &pixels, 0, 0); 632 if (!bitmap) 633 return 0; 634 635 HDC displayDC = ::GetDC(0); 636 HDC bitmapDC = ::CreateCompatibleDC(displayDC); 637 ::ReleaseDC(0, displayDC); 638 639 ::SelectObject(bitmapDC, bitmap); 640 641 // Fill our buffer with clear if we're going to alpha blend. 642 if (supportAlphaBlend) { 643 BITMAP bmpInfo; 644 GetObject(bitmap, sizeof(bmpInfo), &bmpInfo); 645 int bufferSize = bmpInfo.bmWidthBytes * bmpInfo.bmHeight; 646 memset(bmpInfo.bmBits, 0, bufferSize); 647 } 648 return bitmapDC; 649 } 650 651 void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) 652 { 653 if (hdc) { 654 655 if (!dstRect.isEmpty()) { 656 657 HBITMAP bitmap = static_cast<HBITMAP>(GetCurrentObject(hdc, OBJ_BITMAP)); 658 BITMAP info; 659 GetObject(bitmap, sizeof(info), &info); 660 ASSERT(info.bmBitsPixel == 32); 661 662 wxBitmap bmp; 663 bmp.SetHBITMAP(bitmap); 664 #if !wxCHECK_VERSION(2,9,0) 665 if (supportAlphaBlend) 666 bmp.UseAlpha(); 667 #endif 668 m_data->context->DrawBitmap(bmp, dstRect.x(), dstRect.y(), supportAlphaBlend); 669 670 ::DeleteObject(bitmap); 671 } 672 673 ::DeleteDC(hdc); 674 } 675 } 676 #endif 677 678 } 679