1 /*
   2  * Copyright (c) 2011, 2015, 2016, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 #include "config.h"
   5 #include <math.h>
   6 #include <stdio.h>
   7 #include <wtf/MathExtras.h>
   8 #include <wtf/Vector.h>
   9 
  10 #include "AffineTransform.h"
  11 #include "Color.h"
  12 #include "FloatRect.h"
  13 #include "FloatSize.h"
  14 #include "FloatRoundedRect.h"
  15 #include "Font.h"
  16 #include "FontRanges.h" //XXX: FontData.h -> FontRanges.h
  17 #include "GraphicsContext.h"
  18 #include "GraphicsContextJava.h"
  19 #include "Gradient.h"
  20 #include "IntRect.h"
  21 #include "JavaEnv.h"
  22 #include "Logging.h"
  23 #include "NotImplemented.h"
  24 #include "Path.h"
  25 #include "Pattern.h"
  26 #include "RenderingQueue.h"
  27 #include "Font.h" //XXX: SimpleFontData.h -> Font.h
  28 #include "TransformationMatrix.h"
  29 
  30 #include "com_sun_webkit_graphics_GraphicsDecoder.h"
  31 #include "com_sun_webkit_graphics_WCPath.h"
  32 
  33 
  34 #ifndef M_PI
  35 #define M_PI 3.14159265358979323846
  36 #endif
  37 
  38 namespace WebCore {
  39 
  40 
  41 static void setGradient(Gradient &gradient, PlatformGraphicsContext* context, jint id)
  42 {
  43     Vector<Gradient::ColorStop, 2> stops = gradient.getStops(); //XXX recheck;
  44     int nStops = stops.size();
  45 
  46     AffineTransform gt = gradient.gradientSpaceTransform();
  47     FloatPoint p0(gt.mapPoint(gradient.p0()));
  48     FloatPoint p1(gt.mapPoint(gradient.p1()));
  49 
  50     context->rq().freeSpace(4 * 11 + 8 * nStops)
  51     << id
  52     << (jfloat)p0.x()
  53     << (jfloat)p0.y()
  54     << (jfloat)p1.x()
  55     << (jfloat)p1.y()
  56     << (jint)gradient.isRadial();
  57 
  58     if (gradient.isRadial()) {
  59         context->rq()
  60         << (jfloat)(gt.xScale()*gradient.startRadius())
  61         << (jfloat)(gt.xScale()*gradient.endRadius());
  62     }
  63     context->rq()
  64     << (jint)0 //is not proportional
  65     << (jint)gradient.spreadMethod()
  66     << (jint)nStops;
  67 
  68     for (int i = 0; i < nStops; i++) {
  69         Gradient::ColorStop cs = stops[i];
  70         int rgba =
  71             ((int)(cs.alpha * 255 + 0.5)) << 24 |
  72             ((int)(cs.red   * 255 + 0.5)) << 16 |
  73             ((int)(cs.green * 255 + 0.5)) << 8 |
  74             ((int)(cs.blue  * 255 + 0.5));
  75 
  76         context->rq()
  77         << (jint)rgba << (jfloat)cs.stop;
  78     }
  79 }
  80 
  81 class GraphicsContextPlatformPrivate : public PlatformGraphicsContext {
  82 };
  83 
  84 void GraphicsContext::platformInit(PlatformGraphicsContext* context) //XXX , bool shouldUseContextColors) // todo tav new param
  85 {
  86     m_data = static_cast<GraphicsContextPlatformPrivate *>(context);
  87 }
  88 
  89 PlatformGraphicsContext* GraphicsContext::platformContext() const
  90 {
  91     return m_data;
  92 }
  93 
  94 void GraphicsContext::platformDestroy()
  95 {
  96     delete m_data;
  97 }
  98 
  99 void GraphicsContext::savePlatformState()
 100 {
 101     if (paintingDisabled())
 102         return;
 103 
 104     platformContext()->rq().freeSpace(4)
 105     << (jint)com_sun_webkit_graphics_GraphicsDecoder_SAVESTATE;
 106 }
 107 
 108 void GraphicsContext::restorePlatformState()
 109 {
 110     if (paintingDisabled())
 111         return;
 112 
 113     platformContext()->rq().freeSpace(4)
 114     << (jint)com_sun_webkit_graphics_GraphicsDecoder_RESTORESTATE;
 115 }
 116 
 117 // Draws a filled rectangle with a stroked border.
 118 void GraphicsContext::drawRect(const FloatRect& rect, float borderThickness) // todo tav rect changed from IntRect to FloatRect
 119 {
 120     if (paintingDisabled())
 121         return;
 122 
 123     platformContext()->rq().freeSpace(20)
 124     << (jint)com_sun_webkit_graphics_GraphicsDecoder_DRAWRECT
 125     << (jint)rect.x() << (jint)rect.y() << (jint)rect.width() << (jint)rect.height();
 126 }
 127 
 128 // FIXME: Now that this is refactored, it should be shared by all contexts.
 129 static void adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle style)
 130 {
 131     // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
 132     // works out.  For example, with a border width of 3, KHTML will pass us (y1+y2)/2, e.g.,
 133     // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
 134     // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
 135     if (style == DottedStroke || style == DashedStroke) {
 136         if (p1.x() == p2.x()) {
 137             p1.setY(p1.y() + strokeWidth);
 138             p2.setY(p2.y() - strokeWidth);
 139         }
 140         else {
 141             p1.setX(p1.x() + strokeWidth);
 142             p2.setX(p2.x() - strokeWidth);
 143         }
 144     }
 145 
 146     if (static_cast<int>(strokeWidth) % 2) {
 147         if (p1.x() == p2.x()) {
 148             // We're a vertical line.  Adjust our x.
 149             p1.setX(p1.x() + 0.5);
 150             p2.setX(p2.x() + 0.5);
 151         }
 152         else {
 153             // We're a horizontal line. Adjust our y.
 154             p1.setY(p1.y() + 0.5);
 155             p2.setY(p2.y() + 0.5);
 156         }
 157     }
 158 }
 159 
 160 // This is only used to draw borders.
 161 void GraphicsContext::drawLine(const FloatPoint& point1, const FloatPoint& point2) // todo tav points changed from IntPoint to FloatPoint
 162 {
 163     if (paintingDisabled() || strokeStyle() == NoStroke)
 164         return;
 165 
 166     platformContext()->rq().freeSpace(20)
 167     << (jint)com_sun_webkit_graphics_GraphicsDecoder_DRAWLINE
 168     << (jint)point1.x() << (jint)point1.y() << (jint)point2.x() << (jint)point2.y();
 169 }
 170 
 171 // This method is only used to draw the little circles used in lists.
 172 void GraphicsContext::drawEllipse(const FloatRect& rect)
 173 {
 174     if (paintingDisabled())
 175         return;
 176 
 177     platformContext()->rq().freeSpace(20)
 178     << (jint)com_sun_webkit_graphics_GraphicsDecoder_DRAWELLIPSE
 179     << (jint)rect.x() << (jint)rect.y() << (jint)rect.width() << (jint)rect.height(); //XXX float to int conversion
 180 }
 181 
 182 // FIXME: This function needs to be adjusted to match the functionality on the Mac side.
 183 //void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
 184 //{
 185 //    if (paintingDisabled() || strokeStyle() == NoStroke)
 186 //        return;
 187 //
 188 //    platformContext()->rq().freeSpace(28)
 189 //    << (jint)com_sun_webkit_graphics_GraphicsDecoder_STROKEARC
 190 //    << (jint)rect.x() << (jint)rect.y() << (jint)rect.width() << (jint)rect.height()
 191 //    << (jint)startAngle << (jint)angleSpan;
 192 //}
 193 
 194 void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
 195 {
 196     if (paintingDisabled() || npoints <= 1)
 197         return;
 198 
 199     Path path;
 200     path.moveTo(FloatPoint(points[0].x(), points[0].y()));
 201     for (int i = 1; i < npoints; i++) {
 202         path.addLineTo(FloatPoint(points[i].x(), points[i].y()));
 203     }
 204     path.closeSubpath();
 205     platformContext()->rq().freeSpace(12)
 206     << (jint)com_sun_webkit_graphics_GraphicsDecoder_DRAWPOLYGON
 207     << copyPath(path.platformPath())
 208     << (jint)com_sun_webkit_graphics_WCPath_RULE_NONZERO // default rule for the path
 209     << (jint)((shouldAntialias)?-1:0);
 210 }
 211 
 212 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace)
 213 {
 214     if (paintingDisabled())
 215         return;
 216 
 217     platformContext()->rq().freeSpace(24)
 218     << (jint)com_sun_webkit_graphics_GraphicsDecoder_FILLRECT_FFFFI
 219     << rect.x() << rect.y()
 220     << rect.width() << rect.height()
 221     << (jint)color.rgb();
 222 }
 223 
 224 void GraphicsContext::fillRect(const FloatRect& rect)
 225 {
 226     if (paintingDisabled())
 227         return;
 228 
 229     if (m_state.fillPattern && m_state.fillPattern->tileImage()) {
 230         Image *img = m_state.fillPattern->tileImage();
 231         FloatRect destRect(
 232             rect.x(),
 233             rect.y(),
 234             m_state.fillPattern->repeatX() ? rect.width() : img->width(),
 235             m_state.fillPattern->repeatY() ? rect.height() : img->height());
 236         img->drawPattern(
 237             this,
 238             FloatRect(0., 0., img->width(), img->height()),
 239             m_state.fillPattern->getPatternSpaceTransform(),
 240             FloatPoint(),
 241             ColorSpaceDeviceRGB, //any
 242             CompositeCopy, //any
 243             destRect);
 244     } else {
 245         if (m_state.fillGradient) {
 246             setGradient(
 247                 *m_state.fillGradient,
 248                 platformContext(),
 249                 com_sun_webkit_graphics_GraphicsDecoder_SET_FILL_GRADIENT);
 250         }
 251 
 252         platformContext()->rq().freeSpace(20)
 253         << (jint)com_sun_webkit_graphics_GraphicsDecoder_FILLRECT_FFFF
 254         << rect.x() << rect.y()
 255         << rect.width() << rect.height();
 256     }
 257 }
 258 
 259 void GraphicsContext::clip(const FloatRect& rect)
 260 {
 261     if (paintingDisabled())
 262         return;
 263 
 264     m_state.clipBounds.intersect(m_state.transform.mapRect(rect));
 265     platformContext()->rq().freeSpace(20)
 266     << (jint)com_sun_webkit_graphics_GraphicsDecoder_SETCLIP_IIII
 267     << (jint)rect.x() << (jint)rect.y() << (jint)rect.width() << (jint)rect.height();
 268 }
 269 
 270 IntRect GraphicsContext::clipBounds() const
 271 {
 272     // Transformation has inverse effect on clip bounds.
 273     return enclosingIntRect(m_state.transform.inverse().mapRect(m_state.clipBounds));
 274 }
 275 
 276 void GraphicsContext::clipConvexPolygon(size_t numberOfPoints, const FloatPoint* points, bool antialias)
 277 {
 278     if (paintingDisabled() || numberOfPoints <= 1)
 279         return;
 280 
 281     bool oldAntialias = shouldAntialias();
 282     if (oldAntialias != antialias)
 283         setPlatformShouldAntialias(antialias);
 284 
 285     Path path;
 286     path.moveTo(points[0]);
 287     for (size_t i = 1; i < numberOfPoints; ++i)
 288         path.addLineTo(points[i]);
 289     path.closeSubpath();
 290 
 291     clipPath(path, RULE_NONZERO);
 292 
 293     if (oldAntialias != antialias)
 294         setPlatformShouldAntialias(oldAntialias);
 295 }
 296 
 297 void GraphicsContext::drawFocusRing(const Path&, int width, int offset, const Color&)
 298 {
 299     //utaTODO: IMPLEMENT!!!
 300 }
 301 
 302 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
 303 {
 304     if (paintingDisabled())
 305         return;
 306 
 307     unsigned rectCount = rects.size();
 308     // We can't draw all the focus rects because webkit can have several rings
 309     // nested into each other. We can't draw a union of all the rects as well
 310     // as it results in the problems like 6683162. An alternative could be to
 311     // construct a Path object, add all the focus rings to it and then
 312     // "flatten" it, but it can only be done with Area classes which are not
 313     // available here. That's why a simple algorithm here: unite all the
 314     // intersecting rects, while leaving standalone rects as is.
 315     Vector<IntRect> toDraw;
 316     for (unsigned i = 0; i < rectCount; i++) {
 317         IntRect focusRect = rects[i];
 318         focusRect.inflate(offset);
 319         bool needAdd = true;
 320         for (int j = 0; j < toDraw.size(); j++) {
 321             IntRect rect = toDraw[j];
 322             if (rect.contains(focusRect)) {
 323                 needAdd = false;
 324                 break;
 325             } else if (focusRect.contains(rect)) {
 326                 toDraw.remove(j);
 327             } else if (rect.intersects(focusRect)) {
 328                 focusRect.unite(rect);
 329                 toDraw.remove(j);
 330             }
 331         }
 332         if (needAdd) {
 333             toDraw.append(focusRect);
 334         }
 335     }
 336 
 337     platformContext()->rq().freeSpace(24*toDraw.size());
 338     for (int i = 0; i < toDraw.size(); i++) {
 339         IntRect focusRect = toDraw[i];
 340         platformContext()->rq() << (jint)com_sun_webkit_graphics_GraphicsDecoder_DRAWFOCUSRING
 341         << (jint)focusRect.x() << (jint)focusRect.y()
 342         << (jint)focusRect.width() << (jint)focusRect.height()
 343         << (jint)color.rgb();
 344     }
 345 }
 346 
 347 FloatRect GraphicsContext::computeLineBoundsForText(const FloatPoint& point, float width, bool printing)
 348 {
 349   //    NotImplemented(); // todo tav implement
 350     return FloatRect();
 351 }
 352 
 353 void GraphicsContext::updateDocumentMarkerResources()
 354 {
 355   //    NotImplemented(); // todo tav implement
 356 }
 357 
 358 void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool printing, bool doubleLines)
 359 {
 360     if (paintingDisabled() || width <= 0)
 361         return;
 362 
 363     // This is a workaround for http://bugs.webkit.org/show_bug.cgi?id=15659
 364     StrokeStyle savedStrokeStyle = strokeStyle();
 365     setStrokeStyle(SolidStroke);
 366 
 367     FloatPoint endPoint = origin + FloatPoint(width, 0);
 368     drawLine(
 369         IntPoint(origin.x(), origin.y()),
 370         IntPoint(endPoint.x(), endPoint.y()));
 371 
 372     setStrokeStyle(savedStrokeStyle);
 373 }
 374 
 375 static inline void drawLineTo(GraphicsContext &gc, IntPoint &curPos, double x, double y)
 376 {
 377     IntPoint endPoint(x, y);
 378     gc.drawLine(curPos, endPoint);
 379     curPos = endPoint;
 380 }
 381 
 382 //
 383 // Draws an error underline that looks like one of:
 384 //
 385 //              H       E                H
 386 //     /\      /\      /\        /\      /\               -
 387 //   A/  \    /  \    /  \     A/  \    /  \              |
 388 //    \   \  /    \  /   /D     \   \  /    \             |
 389 //     \   \/  C   \/   /        \   \/   C  \            | height = heightSquares * square
 390 //      \      /\  F   /          \  F   /\   \           |
 391 //       \    /  \    /            \    /  \   \G         |
 392 //        \  /    \  /              \  /    \  /          |
 393 //         \/      \/                \/      \/           -
 394 //         B                         B
 395 //         |---|
 396 //       unitWidth = (heightSquares - 1) * square
 397 //
 398 // The x, y, width, height passed in give the desired bounding box;
 399 // x/width are adjusted to make the underline a integer number of units
 400 // wide.
 401 //
 402 static inline void drawErrorUnderline(GraphicsContext &gc, double x, double y, double width, double height)
 403 {
 404     static const double heightSquares = 2.5;
 405 
 406     double square = height / heightSquares;
 407     double halfSquare = 0.5 * square;
 408 
 409     double unitWidth = (heightSquares - 1.0) * square;
 410     int widthUnits = static_cast<int>((width + 0.5 * unitWidth) / unitWidth);
 411 
 412     x += 0.5 * (width - widthUnits * unitWidth);
 413     width = widthUnits * unitWidth;
 414 
 415     double bottom = y + height;
 416     double top = y;
 417 
 418     // Bottom of squiggle
 419     IntPoint curPos(x - halfSquare, top + halfSquare); // A
 420 
 421     int i = 0;
 422     for (i = 0; i < widthUnits; i += 2) {
 423         double middle = x + (i + 1) * unitWidth;
 424         double right = x + (i + 2) * unitWidth;
 425 
 426         drawLineTo(gc, curPos, middle, bottom); // B
 427 
 428         if (i + 2 == widthUnits)
 429             drawLineTo(gc, curPos, right + halfSquare, top + halfSquare); // D
 430         else if (i + 1 != widthUnits)
 431             drawLineTo(gc, curPos, right, top + square); // C
 432     }
 433 
 434     // Top of squiggle
 435     for (i -= 2; i >= 0; i -= 2) {
 436         double left = x + i * unitWidth;
 437         double middle = x + (i + 1) * unitWidth;
 438         double right = x + (i + 2) * unitWidth;
 439 
 440         if (i + 1 == widthUnits)
 441             drawLineTo(gc, curPos, middle + halfSquare, bottom - halfSquare); // G
 442         else {
 443             if (i + 2 == widthUnits)
 444                 drawLineTo(gc, curPos, right, top); // E
 445 
 446             drawLineTo(gc, curPos, middle, bottom - halfSquare); // F
 447         }
 448 
 449         drawLineTo(gc, curPos, left, top); // H
 450     }
 451 }
 452 
 453 void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& origin, float width, DocumentMarkerLineStyle style)
 454 {
 455     savePlatformState(); //fake stroke
 456     switch (style) { //XXX: DocumentMarkerAutocorrectionReplacementLineStyle not handled in switch
 457     case DocumentMarkerSpellingLineStyle:
 458         {
 459             static Color red(255, 0, 0);
 460             setStrokeColor(red, ColorSpaceDeviceRGB);
 461         }
 462         break;
 463     case DocumentMarkerGrammarLineStyle:
 464         {
 465             static Color green(0, 255, 0);
 466             setStrokeColor(green, ColorSpaceDeviceRGB);
 467         }
 468         break;
 469     }
 470     drawErrorUnderline(*this, origin.x(), origin.y(), width, cMisspellingLineThickness);
 471     restorePlatformState(); //fake stroke
 472 }
 473 
 474 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingMode)
 475 {
 476     FloatRect result;
 477     result.setX(static_cast<float>(round(frect.x())));
 478     result.setY(static_cast<float>(round(frect.y())));
 479     result.setWidth(static_cast<float>(round(frect.width())));
 480     result.setHeight(static_cast<float>(round(frect.height())));
 481     return result;
 482 }
 483 
 484 void GraphicsContext::translate(float x, float y)
 485 {
 486     if (paintingDisabled())
 487         return;
 488 
 489     m_state.transform.translate(x, y);
 490     platformContext()->rq().freeSpace(12)
 491     << (jint)com_sun_webkit_graphics_GraphicsDecoder_TRANSLATE
 492     << x << y;
 493 }
 494 
 495 void GraphicsContext::setPlatformFillColor(const Color& col, ColorSpace)
 496 {
 497     if (paintingDisabled())
 498         return;
 499 
 500     platformContext()->rq().freeSpace(8)
 501     << (jint)com_sun_webkit_graphics_GraphicsDecoder_SETFILLCOLOR
 502     << (jint)col.rgb();
 503 }
 504 
 505 
 506 
 507 const Vector<Gradient::ColorStop, 2>& Gradient::getStops() const
 508 {
 509     return m_stops;
 510 }
 511 
 512 void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags mode)
 513 {
 514     if (paintingDisabled())
 515         return;
 516 
 517     platformContext()->rq().freeSpace(16)
 518     << (jint)com_sun_webkit_graphics_GraphicsDecoder_SET_TEXT_MODE
 519     << (jint)(mode & TextModeFill)
 520     << (jint)(mode & TextModeStroke)
 521     << (jint)0;
 522     //utatodo:
 523     //<< (jint)(mode & TextModeClip);
 524 }
 525 
 526 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle style)
 527 {
 528     if (paintingDisabled())
 529         return;
 530 
 531     platformContext()->rq().freeSpace(8)
 532     << (jint)com_sun_webkit_graphics_GraphicsDecoder_SETSTROKESTYLE
 533     << (jint)style;
 534 }
 535 
 536 void GraphicsContext::setPlatformStrokeColor(const Color& col, ColorSpace)
 537 {
 538     if (paintingDisabled())
 539         return;
 540 
 541     platformContext()->rq().freeSpace(8)
 542     << (jint)com_sun_webkit_graphics_GraphicsDecoder_SETSTROKECOLOR
 543     << (jint)col.rgb();
 544 }
 545 
 546 void GraphicsContext::setPlatformStrokeThickness(float strokeThickness)
 547 {
 548     if (paintingDisabled())
 549         return;
 550 
 551     platformContext()->rq().freeSpace(8)
 552     << (jint)com_sun_webkit_graphics_GraphicsDecoder_SETSTROKEWIDTH
 553     << strokeThickness;
 554 }
 555 
 556 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality interpolationQuality)
 557 {
 558     m_state.interpolationQuality = interpolationQuality;
 559 }
 560 
 561 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
 562 {
 563     return m_state.interpolationQuality;
 564 }
 565 
 566 void GraphicsContext::setPlatformShouldAntialias(bool b)
 567 {
 568     notImplemented();
 569 }
 570 
 571 void GraphicsContext::setURLForRect(const URL& link, const IntRect& destRect)
 572 {
 573     notImplemented();
 574 }
 575 
 576 void GraphicsContext::concatCTM(const AffineTransform& at)
 577 {
 578     if (paintingDisabled())
 579         return;
 580 
 581     m_state.transform.multiply(at);
 582     platformContext()->rq().freeSpace(28)
 583     << (jint)com_sun_webkit_graphics_GraphicsDecoder_CONCATTRANSFORM_FFFFFF
 584     << (float)at.a() << (float)at.b() << (float)at.c() << (float)at.d() << (float)at.e() << (float)at.f();
 585 }
 586 
 587 //void GraphicsContext::addInnerRoundedRectClip(const IntRect& r, int thickness)
 588 //{
 589 //    if (paintingDisabled())
 590 //        return;
 591 //
 592 //    FloatRect rect(r);
 593 //    Path path;
 594 //    path.addEllipse(rect);
 595 //    rect.inflate(-thickness);
 596 //    path.addEllipse(rect);
 597 //    clipPath(path, RULE_EVENODD);
 598 //}
 599 
 600 void GraphicsContext::setPlatformShadow(const FloatSize& s, float blur, const Color& color, ColorSpace)
 601 {
 602     if (paintingDisabled())
 603         return;
 604 
 605     float width = s.width();
 606     float height = s.height();
 607     if (shadowsIgnoreTransforms()) {
 608         // Meaning that this graphics context is associated with a CanvasRenderingContext
 609         // We flip the height since JavaFX Prism and HTML5 Canvas have opposite Y axis
 610         height = -height;
 611     }
 612 
 613     platformContext()->rq().freeSpace(20)
 614     << (jint)com_sun_webkit_graphics_GraphicsDecoder_SETSHADOW
 615     << width << height << blur << (jint)color.rgb();
 616 }
 617 
 618 void GraphicsContext::clearPlatformShadow()
 619 {
 620     setPlatformShadow(FloatSize(0, 0), 0, Color(), ColorSpaceDeviceRGB /* ? */);
 621 }
 622 
 623 bool GraphicsContext::supportsTransparencyLayers()
 624 {
 625     return true;
 626 }
 627 
 628 void GraphicsContext::beginPlatformTransparencyLayer(float opacity)
 629 {
 630     if (paintingDisabled())
 631       return;
 632 
 633     platformContext()->rq().freeSpace(8)
 634     << (jint)com_sun_webkit_graphics_GraphicsDecoder_BEGINTRANSPARENCYLAYER
 635     << opacity;
 636 }
 637 
 638 void GraphicsContext::endPlatformTransparencyLayer()
 639 {
 640     if (paintingDisabled())
 641       return;
 642 
 643     platformContext()->rq().freeSpace(4)
 644     << (jint)com_sun_webkit_graphics_GraphicsDecoder_ENDTRANSPARENCYLAYER;
 645 }
 646 
 647 void GraphicsContext::clearRect(const FloatRect& rect)
 648 {
 649     if (paintingDisabled())
 650         return;
 651 
 652     platformContext()->rq().freeSpace(20)
 653     << (jint)com_sun_webkit_graphics_GraphicsDecoder_CLEARRECT_FFFF
 654     << rect.x() << rect.y()
 655     << rect.width() << rect.height();
 656 }
 657 
 658 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
 659 {
 660     if (paintingDisabled())
 661         return;
 662 
 663     if (m_state.strokeGradient) {
 664         setGradient(
 665             *m_state.strokeGradient,
 666             platformContext(),
 667             com_sun_webkit_graphics_GraphicsDecoder_SET_STROKE_GRADIENT);
 668     }
 669 
 670     platformContext()->rq().freeSpace(24)
 671     << (jint)com_sun_webkit_graphics_GraphicsDecoder_STROKERECT_FFFFF
 672     << rect.x() << rect.y() << rect.width() << rect.height() << lineWidth;
 673 }
 674 
 675 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
 676 {
 677     if (paintingDisabled()) {
 678       return;
 679     }
 680     size_t size = dashes.size();
 681 
 682     platformContext()->rq().freeSpace((3 + size) * 4)
 683     << (jint)com_sun_webkit_graphics_GraphicsDecoder_SET_LINE_DASH
 684     << dashOffset
 685     << (jint)size;
 686 
 687     for (size_t i = 0; i < size; i++) {
 688         platformContext()->rq()
 689         << dashes.at(i);
 690     }
 691 }
 692 
 693 void GraphicsContext::setLineCap(LineCap cap)
 694 {
 695     if (paintingDisabled()) {
 696       return;
 697     }
 698 
 699     platformContext()->rq().freeSpace(8)
 700     << (jint)com_sun_webkit_graphics_GraphicsDecoder_SET_LINE_CAP
 701     << (jint)cap;
 702 }
 703 
 704 void GraphicsContext::setLineJoin(LineJoin join)
 705 {
 706     if (paintingDisabled())
 707         return;
 708 
 709     platformContext()->rq().freeSpace(8)
 710     << (jint)com_sun_webkit_graphics_GraphicsDecoder_SET_LINE_JOIN
 711     << (jint)join;
 712 }
 713 
 714 void GraphicsContext::setMiterLimit(float limit)
 715 {
 716     if (paintingDisabled())
 717         return;
 718 
 719     platformContext()->rq().freeSpace(8)
 720     << (jint)com_sun_webkit_graphics_GraphicsDecoder_SET_MITER_LIMIT
 721     << (jfloat)limit;
 722 }
 723 
 724 void GraphicsContext::setAlpha(float alpha)
 725 {
 726     m_state.globalAlpha = alpha;
 727     platformContext()->rq().freeSpace(8)
 728     << (jint)com_sun_webkit_graphics_GraphicsDecoder_SETALPHA
 729     << alpha;
 730 }
 731 
 732 float GraphicsContext::getAlpha()
 733 {
 734     return m_state.globalAlpha;
 735 }
 736 
 737 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op, BlendMode bm)
 738 {
 739     if (paintingDisabled())
 740         return;
 741 
 742     platformContext()->rq().freeSpace(8)
 743     << (jint)com_sun_webkit_graphics_GraphicsDecoder_SETCOMPOSITE
 744     << (jint)op;
 745     //utatodo: add BlendMode
 746 }
 747 
 748 void GraphicsContext::strokePath(const Path& path)
 749 {
 750     if (paintingDisabled())
 751         return;
 752 
 753     if (m_state.strokeGradient) {
 754         setGradient(
 755             *m_state.strokeGradient,
 756             platformContext(),
 757             com_sun_webkit_graphics_GraphicsDecoder_SET_STROKE_GRADIENT);
 758     }
 759 
 760     platformContext()->rq().freeSpace(12)
 761     << (jint)com_sun_webkit_graphics_GraphicsDecoder_STROKE_PATH
 762     << copyPath(path.platformPath())
 763     << (jint)fillRule();
 764 }
 765 
 766 static void setClipPath(
 767     GraphicsContext &gc,
 768     GraphicsContextState& state,
 769     const Path& path,
 770     WindRule wrule,
 771     bool isOut)
 772 {
 773     if (gc.paintingDisabled() || path.isEmpty())
 774         return;
 775 
 776     state.clipBounds.intersect(state.transform.mapRect(path.fastBoundingRect()));
 777     gc.platformContext()->rq().freeSpace(16)
 778     << jint(com_sun_webkit_graphics_GraphicsDecoder_CLIP_PATH)
 779     << copyPath(path.platformPath())
 780     << jint(wrule == RULE_EVENODD
 781        ? com_sun_webkit_graphics_WCPath_RULE_EVENODD
 782        : com_sun_webkit_graphics_WCPath_RULE_NONZERO)
 783     << jint(isOut);
 784 }
 785 
 786 void GraphicsContext::canvasClip(const Path& path, WindRule fillRule)
 787 {
 788     clip(path, fillRule);
 789 }
 790 
 791 void GraphicsContext::clip(const Path& path, WindRule wrule)
 792 {
 793     setClipPath(*this, m_state, path, wrule, false);
 794 }
 795 
 796 void GraphicsContext::clipPath(const Path &path, WindRule wrule)
 797 {
 798     setClipPath(*this, m_state, path, wrule, false);
 799 }
 800 
 801 void GraphicsContext::clipOut(const Path& path)
 802 {
 803     setClipPath(*this, m_state, path, RULE_EVENODD, true);
 804 }
 805 
 806 void GraphicsContext::clipOut(const FloatRect& rect)
 807 {
 808     Path path;
 809     path.addRoundedRect(rect, FloatSize());
 810     clipOut(path);
 811 }
 812 
 813 void GraphicsContext::fillPath(const Path& path)
 814 {
 815     if (paintingDisabled())
 816         return;
 817 
 818     if (m_state.fillPattern && m_state.fillPattern->tileImage()) {
 819         savePlatformState(); //fake clip isolation
 820         clipPath(path, m_state.fillRule);
 821         FloatRect rect(path.boundingRect());
 822 
 823         Image *img = m_state.fillPattern->tileImage();
 824         FloatRect destRect(
 825             rect.x(),
 826             rect.y(),
 827             m_state.fillPattern->repeatX() ? rect.width() : img->width(),
 828             m_state.fillPattern->repeatY() ? rect.height() : img->height());
 829         img->drawPattern(
 830             this,
 831             FloatRect(0., 0., img->width(), img->height()),
 832             m_state.fillPattern->getPatternSpaceTransform(),
 833             FloatPoint(),
 834             ColorSpaceDeviceRGB, //any
 835             CompositeCopy, //any
 836             destRect);
 837         restorePlatformState();
 838     } else {
 839         if (m_state.fillGradient) {
 840             setGradient(
 841                 *m_state.fillGradient,
 842                 platformContext(),
 843                 com_sun_webkit_graphics_GraphicsDecoder_SET_FILL_GRADIENT);
 844         }
 845 
 846         platformContext()->rq().freeSpace(12)
 847         << (jint)com_sun_webkit_graphics_GraphicsDecoder_FILL_PATH
 848         << copyPath(path.platformPath())
 849         << (jint)fillRule();
 850     }
 851 }
 852 
 853 void GraphicsContext::rotate(float radians)
 854 {
 855     if (paintingDisabled())
 856         return;
 857 
 858     m_state.transform.rotate(radians);
 859     platformContext()->rq().freeSpace(2 * 4)
 860     << (jint)com_sun_webkit_graphics_GraphicsDecoder_ROTATE
 861     << radians;
 862 
 863 }
 864 
 865 void GraphicsContext::scale(const FloatSize& size)
 866 {
 867     if (paintingDisabled())
 868         return;
 869 
 870     m_state.transform.scale(size.width(), size.height());
 871     platformContext()->rq().freeSpace(12)
 872     << (jint)com_sun_webkit_graphics_GraphicsDecoder_SCALE
 873     << size.width() << size.height();
 874 }
 875 
 876 void GraphicsContext::fillRoundedRect(const FloatRoundedRect& rect, const Color& color, ColorSpace, BlendMode) // todo tav Int to Float
 877 {
 878     if (paintingDisabled())
 879         return;
 880 
 881     platformContext()->rq().freeSpace(56)
 882     << (jint)com_sun_webkit_graphics_GraphicsDecoder_FILL_ROUNDED_RECT
 883     << (jfloat)rect.rect().x() << (jfloat)rect.rect().y()
 884     << (jfloat)rect.rect().width() << (jfloat)rect.rect().height()
 885     << (jfloat)rect.radii().topLeft().width() << (jfloat)rect.radii().topLeft().height()
 886     << (jfloat)rect.radii().topRight().width() << (jfloat)rect.radii().topRight().height()
 887     << (jfloat)rect.radii().bottomLeft().width() << (jfloat)rect.radii().bottomLeft().height()
 888     << (jfloat)rect.radii().bottomRight().width() << (jfloat)rect.radii().bottomRight().height()
 889     << (jint)color.rgb();
 890 }
 891 
 892 #if ENABLE(3D_RENDERING) && USE(TEXTURE_MAPPER)
 893 TransformationMatrix GraphicsContext::get3DTransform() const
 894 {
 895     // FIXME: Can we approximate the transformation better than this?
 896     return getCTM().toTransformationMatrix();
 897 }
 898 
 899 void GraphicsContext::concat3DTransform(const TransformationMatrix& transform)
 900 {
 901     concatCTM(transform.toAffineTransform());
 902 }
 903 
 904 void GraphicsContext::set3DTransform(const TransformationMatrix& transform)
 905 {
 906     setCTM(transform.toAffineTransform());
 907 }
 908 #endif
 909 
 910 //utatodo: do we need the Java-only m_state.transform?
 911 AffineTransform GraphicsContext::getCTM(IncludeDeviceScale) const
 912 {
 913     return m_state.transform;
 914 }
 915 
 916 
 917 void GraphicsContext::setCTM(const AffineTransform& tm)
 918 {
 919     if (paintingDisabled())
 920         return;
 921 
 922     m_state.transform = tm;
 923     platformContext()->rq().freeSpace(28)
 924     << (jint)com_sun_webkit_graphics_GraphicsDecoder_SET_TRANSFORM
 925     << (float)tm.a() << (float)tm.b() << (float)tm.c() << (float)tm.d() << (float)tm.e() << (float)tm.f();
 926 }
 927 
 928 void Gradient::platformDestroy()
 929 {
 930 }
 931 
 932 void Gradient::fill(GraphicsContext *gc, const FloatRect &rect)
 933 {
 934     gc->setFillGradient(*this);
 935     gc->fillRect(rect);
 936 }
 937 
 938 } // namespace WebCore