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