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