OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2003, 2004, 2005, 2006, 2009 Apple Inc. All rights reserved. | |
3 * Copyright (C) 2013 Google Inc. All rights reserved. | |
4 * | |
5 * Redistribution and use in source and binary forms, with or without | |
6 * modification, are permitted provided that the following conditions | |
7 * are met: | |
8 * 1. Redistributions of source code must retain the above copyright | |
9 * notice, this list of conditions and the following disclaimer. | |
10 * 2. Redistributions in binary form must reproduce the above copyright | |
11 * notice, this list of conditions and the following disclaimer in the | |
12 * documentation and/or other materials provided with the distribution. | |
13 * | |
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' | |
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | |
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS | |
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
24 * THE POSSIBILITY OF SUCH DAMAGE. | |
25 */ | |
26 | |
27 #include "config.h" | |
28 #include "core/platform/graphics/GraphicsContext.h" | |
29 | |
30 #include "core/platform/graphics/BitmapImage.h" | |
31 #include "core/platform/graphics/Gradient.h" | |
32 #include "platform/geometry/IntRect.h" | |
33 #include "platform/geometry/RoundedRect.h" | |
34 #include "platform/graphics/DisplayList.h" | |
35 #include "platform/graphics/TextRunIterator.h" | |
36 #include "platform/text/BidiResolver.h" | |
37 #include "platform/weborigin/KURL.h" | |
38 #include "third_party/skia/include/core/SkAnnotation.h" | |
39 #include "third_party/skia/include/core/SkColorFilter.h" | |
40 #include "third_party/skia/include/core/SkData.h" | |
41 #include "third_party/skia/include/core/SkPicture.h" | |
42 #include "third_party/skia/include/core/SkRRect.h" | |
43 #include "third_party/skia/include/core/SkRefCnt.h" | |
44 #include "third_party/skia/include/effects/SkBlurMaskFilter.h" | |
45 #include "third_party/skia/include/effects/SkCornerPathEffect.h" | |
46 #include "third_party/skia/include/effects/SkLumaColorFilter.h" | |
47 #include "wtf/Assertions.h" | |
48 #include "wtf/MathExtras.h" | |
49 | |
50 #if OS(MACOSX) | |
51 #include <ApplicationServices/ApplicationServices.h> | |
52 #endif | |
53 | |
54 using namespace std; | |
55 using blink::WebBlendMode; | |
56 | |
57 namespace WebCore { | |
58 | |
59 struct GraphicsContext::DeferredSaveState { | |
60 DeferredSaveState(unsigned mask, int count) : m_flags(mask), m_restoreCount(
count) { } | |
61 | |
62 unsigned m_flags; | |
63 int m_restoreCount; | |
64 }; | |
65 | |
66 struct GraphicsContext::RecordingState { | |
67 RecordingState(SkCanvas* currentCanvas, const SkMatrix& currentMatrix, PassR
efPtr<DisplayList> displayList) | |
68 : m_savedCanvas(currentCanvas) | |
69 , m_displayList(displayList) | |
70 , m_savedMatrix(currentMatrix) | |
71 { | |
72 } | |
73 | |
74 SkCanvas* m_savedCanvas; | |
75 RefPtr<DisplayList> m_displayList; | |
76 const SkMatrix m_savedMatrix; | |
77 }; | |
78 | |
79 GraphicsContext::GraphicsContext(SkCanvas* canvas) | |
80 : m_canvas(canvas) | |
81 , m_deferredSaveFlags(0) | |
82 , m_annotationMode(0) | |
83 #if !ASSERT_DISABLED | |
84 , m_annotationCount(0) | |
85 , m_layerCount(0) | |
86 #endif | |
87 , m_trackOpaqueRegion(false) | |
88 , m_trackTextRegion(false) | |
89 , m_useHighResMarker(false) | |
90 , m_updatingControlTints(false) | |
91 , m_accelerated(false) | |
92 , m_isCertainlyOpaque(true) | |
93 , m_printing(false) | |
94 { | |
95 m_stateStack.append(adoptPtr(new GraphicsContextState())); | |
96 m_state = m_stateStack.last().get(); | |
97 } | |
98 | |
99 GraphicsContext::~GraphicsContext() | |
100 { | |
101 ASSERT(m_stateStack.size() == 1); | |
102 ASSERT(!m_annotationCount); | |
103 ASSERT(!m_layerCount); | |
104 ASSERT(m_recordingStateStack.isEmpty()); | |
105 } | |
106 | |
107 const SkBitmap* GraphicsContext::bitmap() const | |
108 { | |
109 TRACE_EVENT0("skia", "GraphicsContext::bitmap"); | |
110 return &m_canvas->getDevice()->accessBitmap(false); | |
111 } | |
112 | |
113 const SkBitmap& GraphicsContext::layerBitmap(AccessMode access) const | |
114 { | |
115 return m_canvas->getTopDevice()->accessBitmap(access == ReadWrite); | |
116 } | |
117 | |
118 SkBaseDevice* GraphicsContext::createCompatibleDevice(const IntSize& size, bool
hasAlpha) const | |
119 { | |
120 if (paintingDisabled()) | |
121 return 0; | |
122 | |
123 return m_canvas->createCompatibleDevice(SkBitmap::kARGB_8888_Config, size.wi
dth(), size.height(), !hasAlpha); | |
124 } | |
125 | |
126 void GraphicsContext::save() | |
127 { | |
128 if (paintingDisabled()) | |
129 return; | |
130 | |
131 m_stateStack.append(m_state->clone()); | |
132 m_state = m_stateStack.last().get(); | |
133 | |
134 m_saveStateStack.append(DeferredSaveState(m_deferredSaveFlags, m_canvas->get
SaveCount())); | |
135 m_deferredSaveFlags |= SkCanvas::kMatrixClip_SaveFlag; | |
136 } | |
137 | |
138 void GraphicsContext::restore() | |
139 { | |
140 if (paintingDisabled()) | |
141 return; | |
142 | |
143 if (m_stateStack.size() == 1) { | |
144 WTF_LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty"); | |
145 return; | |
146 } | |
147 | |
148 m_stateStack.removeLast(); | |
149 m_state = m_stateStack.last().get(); | |
150 | |
151 DeferredSaveState savedState = m_saveStateStack.last(); | |
152 m_saveStateStack.removeLast(); | |
153 m_deferredSaveFlags = savedState.m_flags; | |
154 m_canvas->restoreToCount(savedState.m_restoreCount); | |
155 } | |
156 | |
157 void GraphicsContext::saveLayer(const SkRect* bounds, const SkPaint* paint, SkCa
nvas::SaveFlags saveFlags) | |
158 { | |
159 if (paintingDisabled()) | |
160 return; | |
161 | |
162 realizeSave(SkCanvas::kMatrixClip_SaveFlag); | |
163 | |
164 m_canvas->saveLayer(bounds, paint, saveFlags); | |
165 if (bounds) | |
166 m_canvas->clipRect(*bounds); | |
167 if (m_trackOpaqueRegion) | |
168 m_opaqueRegion.pushCanvasLayer(paint); | |
169 } | |
170 | |
171 void GraphicsContext::restoreLayer() | |
172 { | |
173 if (paintingDisabled()) | |
174 return; | |
175 | |
176 m_canvas->restore(); | |
177 if (m_trackOpaqueRegion) | |
178 m_opaqueRegion.popCanvasLayer(this); | |
179 } | |
180 | |
181 void GraphicsContext::beginAnnotation(const GraphicsContextAnnotation& annotatio
n) | |
182 { | |
183 if (paintingDisabled()) | |
184 return; | |
185 | |
186 canvas()->beginCommentGroup("GraphicsContextAnnotation"); | |
187 | |
188 AnnotationList annotations; | |
189 annotation.asAnnotationList(annotations); | |
190 | |
191 AnnotationList::const_iterator end = annotations.end(); | |
192 for (AnnotationList::const_iterator it = annotations.begin(); it != end; ++i
t) | |
193 canvas()->addComment(it->first, it->second.ascii().data()); | |
194 | |
195 #if !ASSERT_DISABLED | |
196 ++m_annotationCount; | |
197 #endif | |
198 } | |
199 | |
200 void GraphicsContext::endAnnotation() | |
201 { | |
202 if (paintingDisabled()) | |
203 return; | |
204 | |
205 canvas()->endCommentGroup(); | |
206 | |
207 ASSERT(m_annotationCount > 0); | |
208 #if !ASSERT_DISABLED | |
209 --m_annotationCount; | |
210 #endif | |
211 } | |
212 | |
213 void GraphicsContext::setStrokeColor(const Color& color) | |
214 { | |
215 m_state->m_strokeData.setColor(color); | |
216 m_state->m_strokeData.clearGradient(); | |
217 m_state->m_strokeData.clearPattern(); | |
218 } | |
219 | |
220 void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern) | |
221 { | |
222 if (paintingDisabled()) | |
223 return; | |
224 | |
225 ASSERT(pattern); | |
226 if (!pattern) { | |
227 setStrokeColor(Color::black); | |
228 return; | |
229 } | |
230 m_state->m_strokeData.clearGradient(); | |
231 m_state->m_strokeData.setPattern(pattern); | |
232 } | |
233 | |
234 void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient) | |
235 { | |
236 if (paintingDisabled()) | |
237 return; | |
238 | |
239 ASSERT(gradient); | |
240 if (!gradient) { | |
241 setStrokeColor(Color::black); | |
242 return; | |
243 } | |
244 m_state->m_strokeData.setGradient(gradient); | |
245 m_state->m_strokeData.clearPattern(); | |
246 } | |
247 | |
248 void GraphicsContext::setFillColor(const Color& color) | |
249 { | |
250 m_state->m_fillColor = color; | |
251 m_state->m_fillGradient.clear(); | |
252 m_state->m_fillPattern.clear(); | |
253 } | |
254 | |
255 void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern) | |
256 { | |
257 if (paintingDisabled()) | |
258 return; | |
259 | |
260 ASSERT(pattern); | |
261 if (!pattern) { | |
262 setFillColor(Color::black); | |
263 return; | |
264 } | |
265 m_state->m_fillGradient.clear(); | |
266 m_state->m_fillPattern = pattern; | |
267 } | |
268 | |
269 void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient) | |
270 { | |
271 if (paintingDisabled()) | |
272 return; | |
273 | |
274 ASSERT(gradient); | |
275 if (!gradient) { | |
276 setFillColor(Color::black); | |
277 return; | |
278 } | |
279 m_state->m_fillGradient = gradient; | |
280 m_state->m_fillPattern.clear(); | |
281 } | |
282 | |
283 void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color
& color, | |
284 DrawLooper::ShadowTransformMode shadowTransformMode, | |
285 DrawLooper::ShadowAlphaMode shadowAlphaMode) | |
286 { | |
287 if (paintingDisabled()) | |
288 return; | |
289 | |
290 if (!color.isValid() || !color.alpha() || (!offset.width() && !offset.height
() && !blur)) { | |
291 clearShadow(); | |
292 return; | |
293 } | |
294 | |
295 DrawLooper drawLooper; | |
296 drawLooper.addShadow(offset, blur, color, shadowTransformMode, shadowAlphaMo
de); | |
297 drawLooper.addUnmodifiedContent(); | |
298 setDrawLooper(drawLooper); | |
299 } | |
300 | |
301 void GraphicsContext::setDrawLooper(const DrawLooper& drawLooper) | |
302 { | |
303 if (paintingDisabled()) | |
304 return; | |
305 | |
306 m_state->m_looper = drawLooper.skDrawLooper(); | |
307 } | |
308 | |
309 void GraphicsContext::clearDrawLooper() | |
310 { | |
311 if (paintingDisabled()) | |
312 return; | |
313 | |
314 m_state->m_looper.clear(); | |
315 } | |
316 | |
317 bool GraphicsContext::hasShadow() const | |
318 { | |
319 return !!m_state->m_looper; | |
320 } | |
321 | |
322 int GraphicsContext::getNormalizedAlpha() const | |
323 { | |
324 int alpha = roundf(m_state->m_alpha * 256); | |
325 if (alpha > 255) | |
326 alpha = 255; | |
327 else if (alpha < 0) | |
328 alpha = 0; | |
329 return alpha; | |
330 } | |
331 | |
332 bool GraphicsContext::getClipBounds(SkRect* bounds) const | |
333 { | |
334 if (paintingDisabled()) | |
335 return false; | |
336 return m_canvas->getClipBounds(bounds); | |
337 } | |
338 | |
339 bool GraphicsContext::getTransformedClipBounds(FloatRect* bounds) const | |
340 { | |
341 if (paintingDisabled()) | |
342 return false; | |
343 SkIRect skIBounds; | |
344 if (!m_canvas->getClipDeviceBounds(&skIBounds)) | |
345 return false; | |
346 SkRect skBounds = SkRect::MakeFromIRect(skIBounds); | |
347 *bounds = FloatRect(skBounds); | |
348 return true; | |
349 } | |
350 | |
351 SkMatrix GraphicsContext::getTotalMatrix() const | |
352 { | |
353 if (paintingDisabled()) | |
354 return SkMatrix::I(); | |
355 | |
356 if (!isRecording()) | |
357 return m_canvas->getTotalMatrix(); | |
358 | |
359 const RecordingState& recordingState = m_recordingStateStack.last(); | |
360 SkMatrix totalMatrix = recordingState.m_savedMatrix; | |
361 totalMatrix.preConcat(m_canvas->getTotalMatrix()); | |
362 | |
363 return totalMatrix; | |
364 } | |
365 | |
366 bool GraphicsContext::isPrintingDevice() const | |
367 { | |
368 if (paintingDisabled()) | |
369 return false; | |
370 return m_canvas->getTopDevice()->getDeviceCapabilities() & SkBaseDevice::kVe
ctor_Capability; | |
371 } | |
372 | |
373 void GraphicsContext::adjustTextRenderMode(SkPaint* paint) | |
374 { | |
375 if (paintingDisabled()) | |
376 return; | |
377 | |
378 if (!paint->isLCDRenderText()) | |
379 return; | |
380 | |
381 paint->setLCDRenderText(couldUseLCDRenderedText()); | |
382 } | |
383 | |
384 bool GraphicsContext::couldUseLCDRenderedText() | |
385 { | |
386 // Our layers only have a single alpha channel. This means that subpixel | |
387 // rendered text cannot be composited correctly when the layer is | |
388 // collapsed. Therefore, subpixel text is disabled when we are drawing | |
389 // onto a layer. | |
390 if (paintingDisabled() || isDrawingToLayer() || !isCertainlyOpaque()) | |
391 return false; | |
392 | |
393 return shouldSmoothFonts(); | |
394 } | |
395 | |
396 void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation
, WebBlendMode blendMode) | |
397 { | |
398 m_state->m_compositeOperator = compositeOperation; | |
399 m_state->m_blendMode = blendMode; | |
400 m_state->m_xferMode = WebCoreCompositeToSkiaComposite(compositeOperation, bl
endMode); | |
401 } | |
402 | |
403 SkColorFilter* GraphicsContext::colorFilter() | |
404 { | |
405 return m_state->m_colorFilter.get(); | |
406 } | |
407 | |
408 void GraphicsContext::setColorFilter(ColorFilter colorFilter) | |
409 { | |
410 // We only support one active color filter at the moment. If (when) this bec
omes a problem, | |
411 // we should switch to using color filter chains (Skia work in progress). | |
412 ASSERT(!m_state->m_colorFilter); | |
413 m_state->m_colorFilter = WebCoreColorFilterToSkiaColorFilter(colorFilter); | |
414 } | |
415 | |
416 bool GraphicsContext::readPixels(SkBitmap* bitmap, int x, int y, SkCanvas::Confi
g8888 config8888) | |
417 { | |
418 if (paintingDisabled()) | |
419 return false; | |
420 | |
421 return m_canvas->readPixels(bitmap, x, y, config8888); | |
422 } | |
423 | |
424 void GraphicsContext::setMatrix(const SkMatrix& matrix) | |
425 { | |
426 if (paintingDisabled()) | |
427 return; | |
428 | |
429 realizeSave(SkCanvas::kMatrix_SaveFlag); | |
430 | |
431 m_canvas->setMatrix(matrix); | |
432 } | |
433 | |
434 bool GraphicsContext::concat(const SkMatrix& matrix) | |
435 { | |
436 if (paintingDisabled()) | |
437 return false; | |
438 | |
439 realizeSave(SkCanvas::kMatrix_SaveFlag); | |
440 | |
441 return m_canvas->concat(matrix); | |
442 } | |
443 | |
444 void GraphicsContext::beginTransparencyLayer(float opacity, const FloatRect* bou
nds) | |
445 { | |
446 beginLayer(opacity, m_state->m_compositeOperator, bounds); | |
447 } | |
448 | |
449 void GraphicsContext::beginLayer(float opacity, CompositeOperator op, const Floa
tRect* bounds, ColorFilter colorFilter) | |
450 { | |
451 if (paintingDisabled()) | |
452 return; | |
453 | |
454 // We need the "alpha" layer flag here because the base layer is opaque | |
455 // (the surface of the page) but layers on top may have transparent parts. | |
456 // Without explicitly setting the alpha flag, the layer will inherit the | |
457 // opaque setting of the base and some things won't work properly. | |
458 SkCanvas::SaveFlags saveFlags = static_cast<SkCanvas::SaveFlags>(SkCanvas::k
HasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag); | |
459 | |
460 SkPaint layerPaint; | |
461 layerPaint.setAlpha(static_cast<unsigned char>(opacity * 255)); | |
462 layerPaint.setXfermode(WebCoreCompositeToSkiaComposite(op, m_state->m_blendM
ode).get()); | |
463 layerPaint.setColorFilter(WebCoreColorFilterToSkiaColorFilter(colorFilter).g
et()); | |
464 | |
465 if (bounds) { | |
466 SkRect skBounds = WebCoreFloatRectToSKRect(*bounds); | |
467 saveLayer(&skBounds, &layerPaint, saveFlags); | |
468 } else { | |
469 saveLayer(0, &layerPaint, saveFlags); | |
470 } | |
471 | |
472 #if !ASSERT_DISABLED | |
473 ++m_layerCount; | |
474 #endif | |
475 } | |
476 | |
477 void GraphicsContext::endLayer() | |
478 { | |
479 if (paintingDisabled()) | |
480 return; | |
481 | |
482 restoreLayer(); | |
483 | |
484 ASSERT(m_layerCount > 0); | |
485 #if !ASSERT_DISABLED | |
486 --m_layerCount; | |
487 #endif | |
488 } | |
489 | |
490 void GraphicsContext::beginRecording(const FloatRect& bounds) | |
491 { | |
492 RefPtr<DisplayList> displayList = adoptRef(new DisplayList(bounds)); | |
493 | |
494 SkCanvas* savedCanvas = m_canvas; | |
495 SkMatrix savedMatrix = getTotalMatrix(); | |
496 | |
497 IntRect recordingRect = enclosingIntRect(bounds); | |
498 m_canvas = displayList->picture()->beginRecording(recordingRect.width(), rec
ordingRect.height(), | |
499 SkPicture::kUsePathBoundsForClip_RecordingFlag); | |
500 | |
501 // We want the bounds offset mapped to (0, 0), such that the display list co
ntent | |
502 // is fully contained within the SkPictureRecord's bounds. | |
503 if (!toFloatSize(bounds.location()).isZero()) { | |
504 m_canvas->translate(-bounds.x(), -bounds.y()); | |
505 // To avoid applying the offset repeatedly in getTotalMatrix(), we pre-a
pply it here. | |
506 savedMatrix.preTranslate(bounds.x(), bounds.y()); | |
507 } | |
508 | |
509 m_recordingStateStack.append(RecordingState(savedCanvas, savedMatrix, displa
yList)); | |
510 } | |
511 | |
512 PassRefPtr<DisplayList> GraphicsContext::endRecording() | |
513 { | |
514 ASSERT(!m_recordingStateStack.isEmpty()); | |
515 | |
516 RecordingState recording = m_recordingStateStack.last(); | |
517 ASSERT(recording.m_displayList->picture()->getRecordingCanvas()); | |
518 recording.m_displayList->picture()->endRecording(); | |
519 | |
520 m_recordingStateStack.removeLast(); | |
521 m_canvas = recording.m_savedCanvas; | |
522 | |
523 return recording.m_displayList.release(); | |
524 } | |
525 | |
526 bool GraphicsContext::isRecording() const | |
527 { | |
528 return !m_recordingStateStack.isEmpty(); | |
529 } | |
530 | |
531 void GraphicsContext::drawDisplayList(DisplayList* displayList) | |
532 { | |
533 ASSERT(!displayList->picture()->getRecordingCanvas()); | |
534 | |
535 if (paintingDisabled() || !displayList) | |
536 return; | |
537 | |
538 realizeSave(SkCanvas::kMatrixClip_SaveFlag); | |
539 | |
540 const FloatRect& bounds = displayList->bounds(); | |
541 if (bounds.x() || bounds.y()) | |
542 m_canvas->translate(bounds.x(), bounds.y()); | |
543 | |
544 m_canvas->drawPicture(*displayList->picture()); | |
545 | |
546 if (bounds.x() || bounds.y()) | |
547 m_canvas->translate(-bounds.x(), -bounds.y()); | |
548 } | |
549 | |
550 void GraphicsContext::setupPaintForFilling(SkPaint* paint) const | |
551 { | |
552 if (paintingDisabled()) | |
553 return; | |
554 | |
555 setupPaintCommon(paint); | |
556 | |
557 setupShader(paint, m_state->m_fillGradient.get(), m_state->m_fillPattern.get
(), m_state->m_fillColor.rgb()); | |
558 } | |
559 | |
560 float GraphicsContext::setupPaintForStroking(SkPaint* paint, int length) const | |
561 { | |
562 if (paintingDisabled()) | |
563 return 0.0f; | |
564 | |
565 setupPaintCommon(paint); | |
566 | |
567 setupShader(paint, m_state->m_strokeData.gradient(), m_state->m_strokeData.p
attern(), | |
568 m_state->m_strokeData.color().rgb()); | |
569 | |
570 return m_state->m_strokeData.setupPaint(paint, length); | |
571 } | |
572 | |
573 void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* poin
ts, bool shouldAntialias) | |
574 { | |
575 if (paintingDisabled()) | |
576 return; | |
577 | |
578 if (numPoints <= 1) | |
579 return; | |
580 | |
581 SkPath path; | |
582 setPathFromConvexPoints(&path, numPoints, points); | |
583 | |
584 SkPaint paint; | |
585 setupPaintForFilling(&paint); | |
586 paint.setAntiAlias(shouldAntialias); | |
587 drawPath(path, paint); | |
588 | |
589 if (strokeStyle() != NoStroke) { | |
590 paint.reset(); | |
591 setupPaintForStroking(&paint); | |
592 drawPath(path, paint); | |
593 } | |
594 } | |
595 | |
596 // This method is only used to draw the little circles used in lists. | |
597 void GraphicsContext::drawEllipse(const IntRect& elipseRect) | |
598 { | |
599 if (paintingDisabled()) | |
600 return; | |
601 | |
602 SkRect rect = elipseRect; | |
603 SkPaint paint; | |
604 setupPaintForFilling(&paint); | |
605 drawOval(rect, paint); | |
606 | |
607 if (strokeStyle() != NoStroke) { | |
608 paint.reset(); | |
609 setupPaintForStroking(&paint); | |
610 drawOval(rect, paint); | |
611 } | |
612 } | |
613 | |
614 void GraphicsContext::drawFocusRing(const Path& focusRingPath, int width, int of
fset, const Color& color) | |
615 { | |
616 // FIXME: Implement support for offset. | |
617 UNUSED_PARAM(offset); | |
618 | |
619 if (paintingDisabled()) | |
620 return; | |
621 | |
622 SkPaint paint; | |
623 paint.setAntiAlias(true); | |
624 paint.setStyle(SkPaint::kStroke_Style); | |
625 paint.setColor(color.rgb()); | |
626 | |
627 drawOuterPath(focusRingPath.skPath(), paint, width); | |
628 drawInnerPath(focusRingPath.skPath(), paint, width); | |
629 } | |
630 | |
631 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int
offset, const Color& color) | |
632 { | |
633 if (paintingDisabled()) | |
634 return; | |
635 | |
636 unsigned rectCount = rects.size(); | |
637 if (!rectCount) | |
638 return; | |
639 | |
640 SkRegion focusRingRegion; | |
641 const int focusRingOutset = getFocusRingOutset(offset); | |
642 for (unsigned i = 0; i < rectCount; i++) { | |
643 SkIRect r = rects[i]; | |
644 r.inset(-focusRingOutset, -focusRingOutset); | |
645 focusRingRegion.op(r, SkRegion::kUnion_Op); | |
646 } | |
647 | |
648 SkPath path; | |
649 SkPaint paint; | |
650 paint.setAntiAlias(true); | |
651 paint.setStyle(SkPaint::kStroke_Style); | |
652 | |
653 paint.setColor(color.rgb()); | |
654 focusRingRegion.getBoundaryPath(&path); | |
655 drawOuterPath(path, paint, width); | |
656 drawInnerPath(path, paint, width); | |
657 } | |
658 | |
659 static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, int shado
wBlur, int shadowSpread, const IntSize& shadowOffset) | |
660 { | |
661 IntRect bounds(holeRect); | |
662 | |
663 bounds.inflate(shadowBlur); | |
664 | |
665 if (shadowSpread < 0) | |
666 bounds.inflate(-shadowSpread); | |
667 | |
668 IntRect offsetBounds = bounds; | |
669 offsetBounds.move(-shadowOffset); | |
670 return unionRect(bounds, offsetBounds); | |
671 } | |
672 | |
673 void GraphicsContext::drawInnerShadow(const RoundedRect& rect, const Color& shad
owColor, const IntSize shadowOffset, int shadowBlur, int shadowSpread, Edges cli
ppedEdges) | |
674 { | |
675 IntRect holeRect(rect.rect()); | |
676 holeRect.inflate(-shadowSpread); | |
677 | |
678 if (holeRect.isEmpty()) { | |
679 if (rect.isRounded()) | |
680 fillRoundedRect(rect, shadowColor); | |
681 else | |
682 fillRect(rect.rect(), shadowColor); | |
683 return; | |
684 } | |
685 | |
686 if (clippedEdges & LeftEdge) { | |
687 holeRect.move(-max(shadowOffset.width(), 0) - shadowBlur, 0); | |
688 holeRect.setWidth(holeRect.width() + max(shadowOffset.width(), 0) + shad
owBlur); | |
689 } | |
690 if (clippedEdges & TopEdge) { | |
691 holeRect.move(0, -max(shadowOffset.height(), 0) - shadowBlur); | |
692 holeRect.setHeight(holeRect.height() + max(shadowOffset.height(), 0) + s
hadowBlur); | |
693 } | |
694 if (clippedEdges & RightEdge) | |
695 holeRect.setWidth(holeRect.width() - min(shadowOffset.width(), 0) + shad
owBlur); | |
696 if (clippedEdges & BottomEdge) | |
697 holeRect.setHeight(holeRect.height() - min(shadowOffset.height(), 0) + s
hadowBlur); | |
698 | |
699 Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(),
255); | |
700 | |
701 IntRect outerRect = areaCastingShadowInHole(rect.rect(), shadowBlur, shadowS
pread, shadowOffset); | |
702 RoundedRect roundedHole(holeRect, rect.radii()); | |
703 | |
704 save(); | |
705 if (rect.isRounded()) { | |
706 Path path; | |
707 path.addRoundedRect(rect); | |
708 clipPath(path); | |
709 roundedHole.shrinkRadii(shadowSpread); | |
710 } else { | |
711 clip(rect.rect()); | |
712 } | |
713 | |
714 DrawLooper drawLooper; | |
715 drawLooper.addShadow(shadowOffset, shadowBlur, shadowColor, | |
716 DrawLooper::ShadowRespectsTransforms, DrawLooper::ShadowIgnoresAlpha); | |
717 setDrawLooper(drawLooper); | |
718 fillRectWithRoundedHole(outerRect, roundedHole, fillColor); | |
719 restore(); | |
720 clearDrawLooper(); | |
721 } | |
722 | |
723 // This is only used to draw borders. | |
724 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) | |
725 { | |
726 if (paintingDisabled()) | |
727 return; | |
728 | |
729 StrokeStyle penStyle = strokeStyle(); | |
730 if (penStyle == NoStroke) | |
731 return; | |
732 | |
733 SkPaint paint; | |
734 FloatPoint p1 = point1; | |
735 FloatPoint p2 = point2; | |
736 bool isVerticalLine = (p1.x() == p2.x()); | |
737 int width = roundf(strokeThickness()); | |
738 | |
739 // We know these are vertical or horizontal lines, so the length will just | |
740 // be the sum of the displacement component vectors give or take 1 - | |
741 // probably worth the speed up of no square root, which also won't be exact. | |
742 FloatSize disp = p2 - p1; | |
743 int length = SkScalarRound(disp.width() + disp.height()); | |
744 setupPaintForStroking(&paint, length); | |
745 | |
746 if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) { | |
747 // Do a rect fill of our endpoints. This ensures we always have the | |
748 // appearance of being a border. We then draw the actual dotted/dashed
line. | |
749 | |
750 SkRect r1, r2; | |
751 r1.set(p1.x(), p1.y(), p1.x() + width, p1.y() + width); | |
752 r2.set(p2.x(), p2.y(), p2.x() + width, p2.y() + width); | |
753 | |
754 if (isVerticalLine) { | |
755 r1.offset(-width / 2, 0); | |
756 r2.offset(-width / 2, -width); | |
757 } else { | |
758 r1.offset(0, -width / 2); | |
759 r2.offset(-width, -width / 2); | |
760 } | |
761 SkPaint fillPaint; | |
762 fillPaint.setColor(paint.getColor()); | |
763 drawRect(r1, fillPaint); | |
764 drawRect(r2, fillPaint); | |
765 } | |
766 | |
767 adjustLineToPixelBoundaries(p1, p2, width, penStyle); | |
768 SkPoint pts[2] = { (SkPoint)p1, (SkPoint)p2 }; | |
769 | |
770 m_canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint); | |
771 | |
772 if (m_trackOpaqueRegion) | |
773 m_opaqueRegion.didDrawPoints(this, SkCanvas::kLines_PointMode, 2, pts, p
aint); | |
774 } | |
775 | |
776 void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& pt, float widt
h, DocumentMarkerLineStyle style) | |
777 { | |
778 if (paintingDisabled()) | |
779 return; | |
780 | |
781 int deviceScaleFactor = m_useHighResMarker ? 2 : 1; | |
782 | |
783 // Create the pattern we'll use to draw the underline. | |
784 int index = style == DocumentMarkerGrammarLineStyle ? 1 : 0; | |
785 static SkBitmap* misspellBitmap1x[2] = { 0, 0 }; | |
786 static SkBitmap* misspellBitmap2x[2] = { 0, 0 }; | |
787 SkBitmap** misspellBitmap = deviceScaleFactor == 2 ? misspellBitmap2x : miss
pellBitmap1x; | |
788 if (!misspellBitmap[index]) { | |
789 #if OS(MACOSX) | |
790 // Match the artwork used by the Mac. | |
791 const int rowPixels = 4 * deviceScaleFactor; | |
792 const int colPixels = 3 * deviceScaleFactor; | |
793 misspellBitmap[index] = new SkBitmap; | |
794 misspellBitmap[index]->setConfig(SkBitmap::kARGB_8888_Config, | |
795 rowPixels, colPixels); | |
796 misspellBitmap[index]->allocPixels(); | |
797 | |
798 misspellBitmap[index]->eraseARGB(0, 0, 0, 0); | |
799 const uint32_t transparentColor = 0x00000000; | |
800 | |
801 if (deviceScaleFactor == 1) { | |
802 const uint32_t colors[2][6] = { | |
803 { 0x2a2a0600, 0x57571000, 0xa8a81b00, 0xbfbf1f00, 0x70701200,
0xe0e02400 }, | |
804 { 0x2a0f0f0f, 0x571e1e1e, 0xa83d3d3d, 0xbf454545, 0x70282828,
0xe0515151 } | |
805 }; | |
806 | |
807 // Pattern: a b a a b a | |
808 // c d c c d c | |
809 // e f e e f e | |
810 for (int x = 0; x < colPixels; ++x) { | |
811 uint32_t* row = misspellBitmap[index]->getAddr32(0, x); | |
812 row[0] = colors[index][x * 2]; | |
813 row[1] = colors[index][x * 2 + 1]; | |
814 row[2] = colors[index][x * 2]; | |
815 row[3] = transparentColor; | |
816 } | |
817 } else if (deviceScaleFactor == 2) { | |
818 const uint32_t colors[2][18] = { | |
819 { 0x0a090101, 0x33320806, 0x55540f0a, 0x37360906, 0x6e6c120c, 0
x6e6c120c, 0x7674140d, 0x8d8b1810, 0x8d8b1810, | |
820 0x96941a11, 0xb3b01f15, 0xb3b01f15, 0x6d6b130c, 0xd9d62619, 0
xd9d62619, 0x19180402, 0x7c7a150e, 0xcecb2418 }, | |
821 { 0x0a020202, 0x33141414, 0x55232323, 0x37161616, 0x6e2e2e2e, 0
x6e2e2e2e, 0x76313131, 0x8d3a3a3a, 0x8d3a3a3a, | |
822 0x963e3e3e, 0xb34b4b4b, 0xb34b4b4b, 0x6d2d2d2d, 0xd95b5b5b, 0
xd95b5b5b, 0x19090909, 0x7c343434, 0xce575757 } | |
823 }; | |
824 | |
825 // Pattern: a b c c b a | |
826 // d e f f e d | |
827 // g h j j h g | |
828 // k l m m l k | |
829 // n o p p o n | |
830 // q r s s r q | |
831 for (int x = 0; x < colPixels; ++x) { | |
832 uint32_t* row = misspellBitmap[index]->getAddr32(0, x); | |
833 row[0] = colors[index][x * 3]; | |
834 row[1] = colors[index][x * 3 + 1]; | |
835 row[2] = colors[index][x * 3 + 2]; | |
836 row[3] = colors[index][x * 3 + 2]; | |
837 row[4] = colors[index][x * 3 + 1]; | |
838 row[5] = colors[index][x * 3]; | |
839 row[6] = transparentColor; | |
840 row[7] = transparentColor; | |
841 } | |
842 } else | |
843 ASSERT_NOT_REACHED(); | |
844 #else | |
845 // We use a 2-pixel-high misspelling indicator because that seems to be | |
846 // what WebKit is designed for, and how much room there is in a typical | |
847 // page for it. | |
848 const int rowPixels = 32 * deviceScaleFactor; // Must be multiple of 4 f
or pattern below. | |
849 const int colPixels = 2 * deviceScaleFactor; | |
850 misspellBitmap[index] = new SkBitmap; | |
851 misspellBitmap[index]->setConfig(SkBitmap::kARGB_8888_Config, rowPixels,
colPixels); | |
852 misspellBitmap[index]->allocPixels(); | |
853 | |
854 misspellBitmap[index]->eraseARGB(0, 0, 0, 0); | |
855 if (deviceScaleFactor == 1) | |
856 draw1xMarker(misspellBitmap[index], index); | |
857 else if (deviceScaleFactor == 2) | |
858 draw2xMarker(misspellBitmap[index], index); | |
859 else | |
860 ASSERT_NOT_REACHED(); | |
861 #endif | |
862 } | |
863 | |
864 #if OS(MACOSX) | |
865 SkScalar originX = WebCoreFloatToSkScalar(pt.x()) * deviceScaleFactor; | |
866 SkScalar originY = WebCoreFloatToSkScalar(pt.y()) * deviceScaleFactor; | |
867 | |
868 // Make sure to draw only complete dots. | |
869 int rowPixels = misspellBitmap[index]->width(); | |
870 float widthMod = fmodf(width * deviceScaleFactor, rowPixels); | |
871 if (rowPixels - widthMod > deviceScaleFactor) | |
872 width -= widthMod / deviceScaleFactor; | |
873 #else | |
874 SkScalar originX = WebCoreFloatToSkScalar(pt.x()); | |
875 | |
876 // Offset it vertically by 1 so that there's some space under the text. | |
877 SkScalar originY = WebCoreFloatToSkScalar(pt.y()) + 1; | |
878 originX *= deviceScaleFactor; | |
879 originY *= deviceScaleFactor; | |
880 #endif | |
881 | |
882 RefPtr<SkShader> shader = adoptRef(SkShader::CreateBitmapShader( | |
883 *misspellBitmap[index], SkShader::kRepeat_TileMode, SkShader::kRepeat_Ti
leMode)); | |
884 SkMatrix matrix; | |
885 matrix.setTranslate(originX, originY); | |
886 shader->setLocalMatrix(matrix); | |
887 | |
888 SkPaint paint; | |
889 paint.setShader(shader.get()); | |
890 | |
891 SkRect rect; | |
892 rect.set(originX, originY, originX + WebCoreFloatToSkScalar(width) * deviceS
caleFactor, originY + SkIntToScalar(misspellBitmap[index]->height())); | |
893 | |
894 if (deviceScaleFactor == 2) { | |
895 save(); | |
896 scale(FloatSize(0.5, 0.5)); | |
897 } | |
898 drawRect(rect, paint); | |
899 if (deviceScaleFactor == 2) | |
900 restore(); | |
901 } | |
902 | |
903 void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool pr
inting) | |
904 { | |
905 if (paintingDisabled()) | |
906 return; | |
907 | |
908 if (width <= 0) | |
909 return; | |
910 | |
911 int thickness = SkMax32(static_cast<int>(strokeThickness()), 1); | |
912 SkRect r; | |
913 r.fLeft = WebCoreFloatToSkScalar(pt.x()); | |
914 // Avoid anti-aliasing lines. Currently, these are always horizontal. | |
915 // Round to nearest pixel to match text and other content. | |
916 r.fTop = WebCoreFloatToSkScalar(floorf(pt.y() + 0.5f)); | |
917 r.fRight = r.fLeft + WebCoreFloatToSkScalar(width); | |
918 r.fBottom = r.fTop + SkIntToScalar(thickness); | |
919 | |
920 SkPaint paint; | |
921 switch (strokeStyle()) { | |
922 case NoStroke: | |
923 case SolidStroke: | |
924 case DoubleStroke: | |
925 case WavyStroke: | |
926 setupPaintForFilling(&paint); | |
927 break; | |
928 case DottedStroke: | |
929 case DashedStroke: | |
930 setupPaintForStroking(&paint); | |
931 break; | |
932 } | |
933 | |
934 // Text lines are drawn using the stroke color. | |
935 paint.setColor(effectiveStrokeColor()); | |
936 drawRect(r, paint); | |
937 } | |
938 | |
939 // Draws a filled rectangle with a stroked border. | |
940 void GraphicsContext::drawRect(const IntRect& rect) | |
941 { | |
942 if (paintingDisabled()) | |
943 return; | |
944 | |
945 ASSERT(!rect.isEmpty()); | |
946 if (rect.isEmpty()) | |
947 return; | |
948 | |
949 SkRect skRect = rect; | |
950 SkPaint paint; | |
951 int fillcolorNotTransparent = m_state->m_fillColor.rgb() & 0xFF000000; | |
952 if (fillcolorNotTransparent) { | |
953 setupPaintForFilling(&paint); | |
954 drawRect(skRect, paint); | |
955 } | |
956 | |
957 if (m_state->m_strokeData.style() != NoStroke && (m_state->m_strokeData.colo
r().rgb() & 0xFF000000)) { | |
958 // We do a fill of four rects to simulate the stroke of a border. | |
959 paint.reset(); | |
960 setupPaintForFilling(&paint); | |
961 // need to jam in the strokeColor | |
962 paint.setColor(this->effectiveStrokeColor()); | |
963 | |
964 SkRect topBorder = { skRect.fLeft, skRect.fTop, skRect.fRight, skRect.fT
op + 1 }; | |
965 drawRect(topBorder, paint); | |
966 SkRect bottomBorder = { skRect.fLeft, skRect.fBottom - 1, skRect.fRight,
skRect.fBottom }; | |
967 drawRect(bottomBorder, paint); | |
968 SkRect leftBorder = { skRect.fLeft, skRect.fTop + 1, skRect.fLeft + 1, s
kRect.fBottom - 1 }; | |
969 drawRect(leftBorder, paint); | |
970 SkRect rightBorder = { skRect.fRight - 1, skRect.fTop + 1, skRect.fRight
, skRect.fBottom - 1 }; | |
971 drawRect(rightBorder, paint); | |
972 } | |
973 } | |
974 | |
975 void GraphicsContext::drawText(const Font& font, const TextRunPaintInfo& runInfo
, const FloatPoint& point) | |
976 { | |
977 if (paintingDisabled()) | |
978 return; | |
979 | |
980 font.drawText(this, runInfo, point); | |
981 } | |
982 | |
983 void GraphicsContext::drawEmphasisMarks(const Font& font, const TextRunPaintInfo
& runInfo, const AtomicString& mark, const FloatPoint& point) | |
984 { | |
985 if (paintingDisabled()) | |
986 return; | |
987 | |
988 font.drawEmphasisMarks(this, runInfo, mark, point); | |
989 } | |
990 | |
991 void GraphicsContext::drawBidiText(const Font& font, const TextRunPaintInfo& run
Info, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReady
Action) | |
992 { | |
993 if (paintingDisabled()) | |
994 return; | |
995 | |
996 // sub-run painting is not supported for Bidi text. | |
997 const TextRun& run = runInfo.run; | |
998 ASSERT((runInfo.from == 0) && (runInfo.to == run.length())); | |
999 BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; | |
1000 bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride()
)); | |
1001 bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); | |
1002 | |
1003 // FIXME: This ownership should be reversed. We should pass BidiRunList | |
1004 // to BidiResolver in createBidiRunsForLine. | |
1005 BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs(); | |
1006 bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length())); | |
1007 if (!bidiRuns.runCount()) | |
1008 return; | |
1009 | |
1010 FloatPoint currPoint = point; | |
1011 BidiCharacterRun* bidiRun = bidiRuns.firstRun(); | |
1012 while (bidiRun) { | |
1013 TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun-
>start()); | |
1014 bool isRTL = bidiRun->level() % 2; | |
1015 subrun.setDirection(isRTL ? RTL : LTR); | |
1016 subrun.setDirectionalOverride(bidiRun->dirOverride(false)); | |
1017 | |
1018 TextRunPaintInfo subrunInfo(subrun); | |
1019 subrunInfo.bounds = runInfo.bounds; | |
1020 font.drawText(this, subrunInfo, currPoint, customFontNotReadyAction); | |
1021 | |
1022 bidiRun = bidiRun->next(); | |
1023 // FIXME: Have Font::drawText return the width of what it drew so that w
e don't have to re-measure here. | |
1024 if (bidiRun) | |
1025 currPoint.move(font.width(subrun), 0); | |
1026 } | |
1027 | |
1028 bidiRuns.deleteRuns(); | |
1029 } | |
1030 | |
1031 void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run,
const FloatPoint& point, int h, const Color& backgroundColor, int from, int to) | |
1032 { | |
1033 if (paintingDisabled()) | |
1034 return; | |
1035 | |
1036 fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor
); | |
1037 } | |
1038 | |
1039 void GraphicsContext::drawImage(Image* image, const IntPoint& p, CompositeOperat
or op, RespectImageOrientationEnum shouldRespectImageOrientation) | |
1040 { | |
1041 if (!image) | |
1042 return; | |
1043 drawImage(image, FloatRect(IntRect(p, image->size())), FloatRect(FloatPoint(
), FloatSize(image->size())), op, shouldRespectImageOrientation); | |
1044 } | |
1045 | |
1046 void GraphicsContext::drawImage(Image* image, const IntRect& r, CompositeOperato
r op, RespectImageOrientationEnum shouldRespectImageOrientation, bool useLowQual
ityScale) | |
1047 { | |
1048 if (!image) | |
1049 return; | |
1050 drawImage(image, FloatRect(r), FloatRect(FloatPoint(), FloatSize(image->size
())), op, shouldRespectImageOrientation, useLowQualityScale); | |
1051 } | |
1052 | |
1053 void GraphicsContext::drawImage(Image* image, const IntPoint& dest, const IntRec
t& srcRect, CompositeOperator op, RespectImageOrientationEnum shouldRespectImage
Orientation) | |
1054 { | |
1055 drawImage(image, FloatRect(IntRect(dest, srcRect.size())), FloatRect(srcRect
), op, shouldRespectImageOrientation); | |
1056 } | |
1057 | |
1058 void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const Float
Rect& src, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageO
rientation, bool useLowQualityScale) | |
1059 { | |
1060 drawImage(image, dest, src, op, blink::WebBlendModeNormal, shouldRespectImag
eOrientation, useLowQualityScale); | |
1061 } | |
1062 | |
1063 void GraphicsContext::drawImage(Image* image, const FloatRect& dest) | |
1064 { | |
1065 if (!image) | |
1066 return; | |
1067 drawImage(image, dest, FloatRect(IntRect(IntPoint(), image->size()))); | |
1068 } | |
1069 | |
1070 void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const Float
Rect& src, CompositeOperator op, WebBlendMode blendMode, RespectImageOrientation
Enum shouldRespectImageOrientation, bool useLowQualityScale) | |
1071 { if (paintingDisabled() || !image) | |
1072 return; | |
1073 | |
1074 InterpolationQuality previousInterpolationQuality = InterpolationDefault; | |
1075 | |
1076 if (useLowQualityScale) { | |
1077 previousInterpolationQuality = imageInterpolationQuality(); | |
1078 setImageInterpolationQuality(InterpolationLow); | |
1079 } | |
1080 | |
1081 image->draw(this, dest, src, op, blendMode, shouldRespectImageOrientation); | |
1082 | |
1083 if (useLowQualityScale) | |
1084 setImageInterpolationQuality(previousInterpolationQuality); | |
1085 } | |
1086 | |
1087 void GraphicsContext::drawTiledImage(Image* image, const IntRect& destRect, cons
t IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, bool useLow
QualityScale, WebBlendMode blendMode, const IntSize& repeatSpacing) | |
1088 { | |
1089 if (paintingDisabled() || !image) | |
1090 return; | |
1091 | |
1092 if (useLowQualityScale) { | |
1093 InterpolationQuality previousInterpolationQuality = imageInterpolationQu
ality(); | |
1094 setImageInterpolationQuality(InterpolationLow); | |
1095 image->drawTiled(this, destRect, srcPoint, tileSize, op, blendMode, repe
atSpacing); | |
1096 setImageInterpolationQuality(previousInterpolationQuality); | |
1097 } else { | |
1098 image->drawTiled(this, destRect, srcPoint, tileSize, op, blendMode, repe
atSpacing); | |
1099 } | |
1100 } | |
1101 | |
1102 void GraphicsContext::drawTiledImage(Image* image, const IntRect& dest, const In
tRect& srcRect, | |
1103 const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRu
le, CompositeOperator op, bool useLowQualityScale) | |
1104 { | |
1105 if (paintingDisabled() || !image) | |
1106 return; | |
1107 | |
1108 if (hRule == Image::StretchTile && vRule == Image::StretchTile) { | |
1109 // Just do a scale. | |
1110 drawImage(image, dest, srcRect, op); | |
1111 return; | |
1112 } | |
1113 | |
1114 if (useLowQualityScale) { | |
1115 InterpolationQuality previousInterpolationQuality = imageInterpolationQu
ality(); | |
1116 setImageInterpolationQuality(InterpolationLow); | |
1117 image->drawTiled(this, dest, srcRect, tileScaleFactor, hRule, vRule, op)
; | |
1118 setImageInterpolationQuality(previousInterpolationQuality); | |
1119 } else { | |
1120 image->drawTiled(this, dest, srcRect, tileScaleFactor, hRule, vRule, op)
; | |
1121 } | |
1122 } | |
1123 | |
1124 void GraphicsContext::drawImageBuffer(ImageBuffer* image, const IntPoint& p, Com
positeOperator op, WebBlendMode blendMode) | |
1125 { | |
1126 if (!image) | |
1127 return; | |
1128 drawImageBuffer(image, FloatRect(IntRect(p, image->logicalSize())), FloatRec
t(FloatPoint(), FloatSize(image->logicalSize())), op, blendMode); | |
1129 } | |
1130 | |
1131 void GraphicsContext::drawImageBuffer(ImageBuffer* image, const IntRect& r, Comp
ositeOperator op, WebBlendMode blendMode, bool useLowQualityScale) | |
1132 { | |
1133 if (!image) | |
1134 return; | |
1135 drawImageBuffer(image, FloatRect(r), FloatRect(FloatPoint(), FloatSize(image
->logicalSize())), op, blendMode, useLowQualityScale); | |
1136 } | |
1137 | |
1138 void GraphicsContext::drawImageBuffer(ImageBuffer* image, const IntPoint& dest,
const IntRect& srcRect, CompositeOperator op, WebBlendMode blendMode) | |
1139 { | |
1140 drawImageBuffer(image, FloatRect(IntRect(dest, srcRect.size())), FloatRect(s
rcRect), op, blendMode); | |
1141 } | |
1142 | |
1143 void GraphicsContext::drawImageBuffer(ImageBuffer* image, const IntRect& dest, c
onst IntRect& srcRect, CompositeOperator op, WebBlendMode blendMode, bool useLow
QualityScale) | |
1144 { | |
1145 drawImageBuffer(image, FloatRect(dest), FloatRect(srcRect), op, blendMode, u
seLowQualityScale); | |
1146 } | |
1147 | |
1148 void GraphicsContext::drawImageBuffer(ImageBuffer* image, const FloatRect& dest) | |
1149 { | |
1150 if (!image) | |
1151 return; | |
1152 drawImageBuffer(image, dest, FloatRect(IntRect(IntPoint(), image->logicalSiz
e()))); | |
1153 } | |
1154 | |
1155 void GraphicsContext::drawImageBuffer(ImageBuffer* image, const FloatRect& dest,
const FloatRect& src, CompositeOperator op, WebBlendMode blendMode, bool useLow
QualityScale) | |
1156 { | |
1157 if (paintingDisabled() || !image) | |
1158 return; | |
1159 | |
1160 if (useLowQualityScale) { | |
1161 InterpolationQuality previousInterpolationQuality = imageInterpolationQu
ality(); | |
1162 setImageInterpolationQuality(InterpolationLow); | |
1163 image->draw(this, dest, src, op, blendMode, useLowQualityScale); | |
1164 setImageInterpolationQuality(previousInterpolationQuality); | |
1165 } else { | |
1166 image->draw(this, dest, src, op, blendMode, useLowQualityScale); | |
1167 } | |
1168 } | |
1169 | |
1170 void GraphicsContext::writePixels(const SkBitmap& bitmap, int x, int y, SkCanvas
::Config8888 config8888) | |
1171 { | |
1172 if (paintingDisabled()) | |
1173 return; | |
1174 | |
1175 m_canvas->writePixels(bitmap, x, y, config8888); | |
1176 | |
1177 if (m_trackOpaqueRegion) { | |
1178 SkRect rect = SkRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()); | |
1179 SkPaint paint; | |
1180 | |
1181 paint.setXfermodeMode(SkXfermode::kSrc_Mode); | |
1182 m_opaqueRegion.didDrawRect(this, rect, paint, &bitmap); | |
1183 } | |
1184 } | |
1185 | |
1186 void GraphicsContext::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar
top, const SkPaint* paint) | |
1187 { | |
1188 if (paintingDisabled()) | |
1189 return; | |
1190 | |
1191 m_canvas->drawBitmap(bitmap, left, top, paint); | |
1192 | |
1193 if (m_trackOpaqueRegion) { | |
1194 SkRect rect = SkRect::MakeXYWH(left, top, bitmap.width(), bitmap.height(
)); | |
1195 m_opaqueRegion.didDrawRect(this, rect, *paint, &bitmap); | |
1196 } | |
1197 } | |
1198 | |
1199 void GraphicsContext::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, | |
1200 const SkRect& dst, const SkPaint* paint) | |
1201 { | |
1202 if (paintingDisabled()) | |
1203 return; | |
1204 | |
1205 SkCanvas::DrawBitmapRectFlags flags = m_state->m_shouldClampToSourceRect ? S
kCanvas::kNone_DrawBitmapRectFlag : SkCanvas::kBleed_DrawBitmapRectFlag; | |
1206 | |
1207 m_canvas->drawBitmapRectToRect(bitmap, src, dst, paint, flags); | |
1208 | |
1209 if (m_trackOpaqueRegion) | |
1210 m_opaqueRegion.didDrawRect(this, dst, *paint, &bitmap); | |
1211 } | |
1212 | |
1213 void GraphicsContext::drawOval(const SkRect& oval, const SkPaint& paint) | |
1214 { | |
1215 if (paintingDisabled()) | |
1216 return; | |
1217 | |
1218 m_canvas->drawOval(oval, paint); | |
1219 | |
1220 if (m_trackOpaqueRegion) | |
1221 m_opaqueRegion.didDrawBounded(this, oval, paint); | |
1222 } | |
1223 | |
1224 void GraphicsContext::drawPath(const SkPath& path, const SkPaint& paint) | |
1225 { | |
1226 if (paintingDisabled()) | |
1227 return; | |
1228 | |
1229 m_canvas->drawPath(path, paint); | |
1230 | |
1231 if (m_trackOpaqueRegion) | |
1232 m_opaqueRegion.didDrawPath(this, path, paint); | |
1233 } | |
1234 | |
1235 void GraphicsContext::drawRect(const SkRect& rect, const SkPaint& paint) | |
1236 { | |
1237 if (paintingDisabled()) | |
1238 return; | |
1239 | |
1240 m_canvas->drawRect(rect, paint); | |
1241 | |
1242 if (m_trackOpaqueRegion) | |
1243 m_opaqueRegion.didDrawRect(this, rect, paint, 0); | |
1244 } | |
1245 | |
1246 void GraphicsContext::didDrawRect(const SkRect& rect, const SkPaint& paint, cons
t SkBitmap* bitmap) | |
1247 { | |
1248 if (m_trackOpaqueRegion) | |
1249 m_opaqueRegion.didDrawRect(this, rect, paint, bitmap); | |
1250 } | |
1251 | |
1252 void GraphicsContext::drawPosText(const void* text, size_t byteLength, | |
1253 const SkPoint pos[], const SkRect& textRect, const SkPaint& paint) | |
1254 { | |
1255 if (paintingDisabled()) | |
1256 return; | |
1257 | |
1258 m_canvas->drawPosText(text, byteLength, pos, paint); | |
1259 didDrawTextInRect(textRect); | |
1260 | |
1261 // FIXME: compute bounds for positioned text. | |
1262 if (m_trackOpaqueRegion) | |
1263 m_opaqueRegion.didDrawUnbounded(this, paint, OpaqueRegionSkia::FillOrStr
oke); | |
1264 } | |
1265 | |
1266 void GraphicsContext::drawPosTextH(const void* text, size_t byteLength, | |
1267 const SkScalar xpos[], SkScalar constY, const SkRect& textRect, const SkPai
nt& paint) | |
1268 { | |
1269 if (paintingDisabled()) | |
1270 return; | |
1271 | |
1272 m_canvas->drawPosTextH(text, byteLength, xpos, constY, paint); | |
1273 didDrawTextInRect(textRect); | |
1274 | |
1275 // FIXME: compute bounds for positioned text. | |
1276 if (m_trackOpaqueRegion) | |
1277 m_opaqueRegion.didDrawUnbounded(this, paint, OpaqueRegionSkia::FillOrStr
oke); | |
1278 } | |
1279 | |
1280 void GraphicsContext::drawTextOnPath(const void* text, size_t byteLength, | |
1281 const SkPath& path, const SkRect& textRect, const SkMatrix* matrix, const S
kPaint& paint) | |
1282 { | |
1283 if (paintingDisabled()) | |
1284 return; | |
1285 | |
1286 m_canvas->drawTextOnPath(text, byteLength, path, matrix, paint); | |
1287 didDrawTextInRect(textRect); | |
1288 | |
1289 // FIXME: compute bounds for positioned text. | |
1290 if (m_trackOpaqueRegion) | |
1291 m_opaqueRegion.didDrawUnbounded(this, paint, OpaqueRegionSkia::FillOrStr
oke); | |
1292 } | |
1293 | |
1294 void GraphicsContext::fillPath(const Path& pathToFill) | |
1295 { | |
1296 if (paintingDisabled() || pathToFill.isEmpty()) | |
1297 return; | |
1298 | |
1299 // Use const_cast and temporarily modify the fill type instead of copying th
e path. | |
1300 SkPath& path = const_cast<SkPath&>(pathToFill.skPath()); | |
1301 SkPath::FillType previousFillType = path.getFillType(); | |
1302 | |
1303 SkPath::FillType temporaryFillType = m_state->m_fillRule == RULE_EVENODD ? S
kPath::kEvenOdd_FillType : SkPath::kWinding_FillType; | |
1304 path.setFillType(temporaryFillType); | |
1305 | |
1306 SkPaint paint; | |
1307 setupPaintForFilling(&paint); | |
1308 drawPath(path, paint); | |
1309 | |
1310 path.setFillType(previousFillType); | |
1311 } | |
1312 | |
1313 void GraphicsContext::fillRect(const FloatRect& rect) | |
1314 { | |
1315 if (paintingDisabled()) | |
1316 return; | |
1317 | |
1318 SkRect r = rect; | |
1319 | |
1320 SkPaint paint; | |
1321 setupPaintForFilling(&paint); | |
1322 drawRect(r, paint); | |
1323 } | |
1324 | |
1325 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) | |
1326 { | |
1327 if (paintingDisabled()) | |
1328 return; | |
1329 | |
1330 SkRect r = rect; | |
1331 SkPaint paint; | |
1332 setupPaintCommon(&paint); | |
1333 paint.setColor(color.rgb()); | |
1334 drawRect(r, paint); | |
1335 } | |
1336 | |
1337 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLef
t, const IntSize& topRight, | |
1338 const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color) | |
1339 { | |
1340 if (paintingDisabled()) | |
1341 return; | |
1342 | |
1343 if (topLeft.width() + topRight.width() > rect.width() | |
1344 || bottomLeft.width() + bottomRight.width() > rect.width() | |
1345 || topLeft.height() + bottomLeft.height() > rect.height() | |
1346 || topRight.height() + bottomRight.height() > rect.height()) { | |
1347 // Not all the radii fit, return a rect. This matches the behavior of | |
1348 // Path::createRoundedRectangle. Without this we attempt to draw a round | |
1349 // shadow for a square box. | |
1350 fillRect(rect, color); | |
1351 return; | |
1352 } | |
1353 | |
1354 SkVector radii[4]; | |
1355 setRadii(radii, topLeft, topRight, bottomRight, bottomLeft); | |
1356 | |
1357 SkRRect rr; | |
1358 rr.setRectRadii(rect, radii); | |
1359 | |
1360 SkPaint paint; | |
1361 setupPaintForFilling(&paint); | |
1362 paint.setColor(color.rgb()); | |
1363 | |
1364 m_canvas->drawRRect(rr, paint); | |
1365 | |
1366 if (m_trackOpaqueRegion) | |
1367 m_opaqueRegion.didDrawBounded(this, rr.getBounds(), paint); | |
1368 } | |
1369 | |
1370 void GraphicsContext::fillEllipse(const FloatRect& ellipse) | |
1371 { | |
1372 if (paintingDisabled()) | |
1373 return; | |
1374 | |
1375 SkRect rect = ellipse; | |
1376 SkPaint paint; | |
1377 setupPaintForFilling(&paint); | |
1378 drawOval(rect, paint); | |
1379 } | |
1380 | |
1381 void GraphicsContext::strokePath(const Path& pathToStroke) | |
1382 { | |
1383 if (paintingDisabled() || pathToStroke.isEmpty()) | |
1384 return; | |
1385 | |
1386 const SkPath& path = pathToStroke.skPath(); | |
1387 SkPaint paint; | |
1388 setupPaintForStroking(&paint); | |
1389 drawPath(path, paint); | |
1390 } | |
1391 | |
1392 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) | |
1393 { | |
1394 if (paintingDisabled()) | |
1395 return; | |
1396 | |
1397 SkPaint paint; | |
1398 setupPaintForStroking(&paint); | |
1399 paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth)); | |
1400 // strokerect has special rules for CSS when the rect is degenerate: | |
1401 // if width==0 && height==0, do nothing | |
1402 // if width==0 || height==0, then just draw line for the other dimension | |
1403 SkRect r(rect); | |
1404 bool validW = r.width() > 0; | |
1405 bool validH = r.height() > 0; | |
1406 if (validW && validH) { | |
1407 drawRect(r, paint); | |
1408 } else if (validW || validH) { | |
1409 // we are expected to respect the lineJoin, so we can't just call | |
1410 // drawLine -- we have to create a path that doubles back on itself. | |
1411 SkPath path; | |
1412 path.moveTo(r.fLeft, r.fTop); | |
1413 path.lineTo(r.fRight, r.fBottom); | |
1414 path.close(); | |
1415 drawPath(path, paint); | |
1416 } | |
1417 } | |
1418 | |
1419 void GraphicsContext::strokeEllipse(const FloatRect& ellipse) | |
1420 { | |
1421 if (paintingDisabled()) | |
1422 return; | |
1423 | |
1424 SkRect rect(ellipse); | |
1425 SkPaint paint; | |
1426 setupPaintForStroking(&paint); | |
1427 drawOval(rect, paint); | |
1428 } | |
1429 | |
1430 void GraphicsContext::clipRoundedRect(const RoundedRect& rect) | |
1431 { | |
1432 if (paintingDisabled()) | |
1433 return; | |
1434 | |
1435 SkVector radii[4]; | |
1436 RoundedRect::Radii wkRadii = rect.radii(); | |
1437 setRadii(radii, wkRadii.topLeft(), wkRadii.topRight(), wkRadii.bottomRight()
, wkRadii.bottomLeft()); | |
1438 | |
1439 SkRRect r; | |
1440 r.setRectRadii(rect.rect(), radii); | |
1441 | |
1442 clipRRect(r, AntiAliased); | |
1443 } | |
1444 | |
1445 void GraphicsContext::clipOut(const Path& pathToClip) | |
1446 { | |
1447 if (paintingDisabled()) | |
1448 return; | |
1449 | |
1450 // Use const_cast and temporarily toggle the inverse fill type instead of co
pying the path. | |
1451 SkPath& path = const_cast<SkPath&>(pathToClip.skPath()); | |
1452 path.toggleInverseFillType(); | |
1453 clipPath(path, AntiAliased); | |
1454 path.toggleInverseFillType(); | |
1455 } | |
1456 | |
1457 void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule) | |
1458 { | |
1459 if (paintingDisabled() || pathToClip.isEmpty()) | |
1460 return; | |
1461 | |
1462 // Use const_cast and temporarily modify the fill type instead of copying th
e path. | |
1463 SkPath& path = const_cast<SkPath&>(pathToClip.skPath()); | |
1464 SkPath::FillType previousFillType = path.getFillType(); | |
1465 | |
1466 SkPath::FillType temporaryFillType = clipRule == RULE_EVENODD ? SkPath::kEve
nOdd_FillType : SkPath::kWinding_FillType; | |
1467 path.setFillType(temporaryFillType); | |
1468 clipPath(path, AntiAliased); | |
1469 | |
1470 path.setFillType(previousFillType); | |
1471 } | |
1472 | |
1473 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* poin
ts, bool antialiased) | |
1474 { | |
1475 if (paintingDisabled()) | |
1476 return; | |
1477 | |
1478 if (numPoints <= 1) | |
1479 return; | |
1480 | |
1481 SkPath path; | |
1482 setPathFromConvexPoints(&path, numPoints, points); | |
1483 clipPath(path, antialiased ? AntiAliased : NotAntiAliased); | |
1484 } | |
1485 | |
1486 void GraphicsContext::clipOutRoundedRect(const RoundedRect& rect) | |
1487 { | |
1488 if (paintingDisabled()) | |
1489 return; | |
1490 | |
1491 if (!rect.isRounded()) { | |
1492 clipOut(rect.rect()); | |
1493 return; | |
1494 } | |
1495 | |
1496 Path path; | |
1497 path.addRoundedRect(rect); | |
1498 clipOut(path); | |
1499 } | |
1500 | |
1501 void GraphicsContext::canvasClip(const Path& pathToClip, WindRule clipRule) | |
1502 { | |
1503 if (paintingDisabled()) | |
1504 return; | |
1505 | |
1506 // Use const_cast and temporarily modify the fill type instead of copying th
e path. | |
1507 SkPath& path = const_cast<SkPath&>(pathToClip.skPath()); | |
1508 SkPath::FillType previousFillType = path.getFillType(); | |
1509 | |
1510 SkPath::FillType temporaryFillType = clipRule == RULE_EVENODD ? SkPath::kEve
nOdd_FillType : SkPath::kWinding_FillType; | |
1511 path.setFillType(temporaryFillType); | |
1512 clipPath(path); | |
1513 | |
1514 path.setFillType(previousFillType); | |
1515 } | |
1516 | |
1517 bool GraphicsContext::clipRect(const SkRect& rect, AntiAliasingMode aa, SkRegion
::Op op) | |
1518 { | |
1519 if (paintingDisabled()) | |
1520 return false; | |
1521 | |
1522 realizeSave(SkCanvas::kClip_SaveFlag); | |
1523 | |
1524 return m_canvas->clipRect(rect, op, aa == AntiAliased); | |
1525 } | |
1526 | |
1527 bool GraphicsContext::clipPath(const SkPath& path, AntiAliasingMode aa, SkRegion
::Op op) | |
1528 { | |
1529 if (paintingDisabled()) | |
1530 return false; | |
1531 | |
1532 realizeSave(SkCanvas::kClip_SaveFlag); | |
1533 | |
1534 return m_canvas->clipPath(path, op, aa == AntiAliased); | |
1535 } | |
1536 | |
1537 bool GraphicsContext::clipRRect(const SkRRect& rect, AntiAliasingMode aa, SkRegi
on::Op op) | |
1538 { | |
1539 if (paintingDisabled()) | |
1540 return false; | |
1541 | |
1542 realizeSave(SkCanvas::kClip_SaveFlag); | |
1543 | |
1544 return m_canvas->clipRRect(rect, op, aa == AntiAliased); | |
1545 } | |
1546 | |
1547 void GraphicsContext::rotate(float angleInRadians) | |
1548 { | |
1549 if (paintingDisabled()) | |
1550 return; | |
1551 | |
1552 realizeSave(SkCanvas::kMatrix_SaveFlag); | |
1553 | |
1554 m_canvas->rotate(WebCoreFloatToSkScalar(angleInRadians * (180.0f / 3.1415926
5f))); | |
1555 } | |
1556 | |
1557 void GraphicsContext::translate(float w, float h) | |
1558 { | |
1559 if (paintingDisabled()) | |
1560 return; | |
1561 | |
1562 realizeSave(SkCanvas::kMatrix_SaveFlag); | |
1563 | |
1564 m_canvas->translate(WebCoreFloatToSkScalar(w), WebCoreFloatToSkScalar(h)); | |
1565 } | |
1566 | |
1567 void GraphicsContext::scale(const FloatSize& size) | |
1568 { | |
1569 if (paintingDisabled()) | |
1570 return; | |
1571 | |
1572 realizeSave(SkCanvas::kMatrix_SaveFlag); | |
1573 | |
1574 m_canvas->scale(WebCoreFloatToSkScalar(size.width()), WebCoreFloatToSkScalar
(size.height())); | |
1575 } | |
1576 | |
1577 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect) | |
1578 { | |
1579 if (paintingDisabled()) | |
1580 return; | |
1581 | |
1582 SkAutoDataUnref url(SkData::NewWithCString(link.string().utf8().data())); | |
1583 SkAnnotateRectWithURL(m_canvas, destRect, url.get()); | |
1584 } | |
1585 | |
1586 void GraphicsContext::setURLFragmentForRect(const String& destName, const IntRec
t& rect) | |
1587 { | |
1588 if (paintingDisabled()) | |
1589 return; | |
1590 | |
1591 SkAutoDataUnref skDestName(SkData::NewWithCString(destName.utf8().data())); | |
1592 SkAnnotateLinkToDestination(m_canvas, rect, skDestName.get()); | |
1593 } | |
1594 | |
1595 void GraphicsContext::addURLTargetAtPoint(const String& name, const IntPoint& po
s) | |
1596 { | |
1597 if (paintingDisabled()) | |
1598 return; | |
1599 | |
1600 SkAutoDataUnref nameData(SkData::NewWithCString(name.utf8().data())); | |
1601 SkAnnotateNamedDestination(m_canvas, SkPoint::Make(pos.x(), pos.y()), nameDa
ta); | |
1602 } | |
1603 | |
1604 AffineTransform GraphicsContext::getCTM(IncludeDeviceScale) const | |
1605 { | |
1606 if (paintingDisabled()) | |
1607 return AffineTransform(); | |
1608 | |
1609 SkMatrix m = getTotalMatrix(); | |
1610 return AffineTransform(SkScalarToDouble(m.getScaleX()), | |
1611 SkScalarToDouble(m.getSkewY()), | |
1612 SkScalarToDouble(m.getSkewX()), | |
1613 SkScalarToDouble(m.getScaleY()), | |
1614 SkScalarToDouble(m.getTranslateX()), | |
1615 SkScalarToDouble(m.getTranslateY())); | |
1616 } | |
1617 | |
1618 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, Compos
iteOperator op) | |
1619 { | |
1620 if (paintingDisabled()) | |
1621 return; | |
1622 | |
1623 CompositeOperator previousOperator = compositeOperation(); | |
1624 setCompositeOperation(op); | |
1625 fillRect(rect, color); | |
1626 setCompositeOperation(previousOperator); | |
1627 } | |
1628 | |
1629 void GraphicsContext::fillRoundedRect(const RoundedRect& rect, const Color& colo
r) | |
1630 { | |
1631 if (rect.isRounded()) | |
1632 fillRoundedRect(rect.rect(), rect.radii().topLeft(), rect.radii().topRig
ht(), rect.radii().bottomLeft(), rect.radii().bottomRight(), color); | |
1633 else | |
1634 fillRect(rect.rect(), color); | |
1635 } | |
1636 | |
1637 void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const Rounded
Rect& roundedHoleRect, const Color& color) | |
1638 { | |
1639 if (paintingDisabled()) | |
1640 return; | |
1641 | |
1642 Path path; | |
1643 path.addRect(rect); | |
1644 | |
1645 if (!roundedHoleRect.radii().isZero()) | |
1646 path.addRoundedRect(roundedHoleRect); | |
1647 else | |
1648 path.addRect(roundedHoleRect.rect()); | |
1649 | |
1650 WindRule oldFillRule = fillRule(); | |
1651 Color oldFillColor = fillColor(); | |
1652 | |
1653 setFillRule(RULE_EVENODD); | |
1654 setFillColor(color); | |
1655 | |
1656 fillPath(path); | |
1657 | |
1658 setFillRule(oldFillRule); | |
1659 setFillColor(oldFillColor); | |
1660 } | |
1661 | |
1662 void GraphicsContext::clearRect(const FloatRect& rect) | |
1663 { | |
1664 if (paintingDisabled()) | |
1665 return; | |
1666 | |
1667 SkRect r = rect; | |
1668 SkPaint paint; | |
1669 setupPaintForFilling(&paint); | |
1670 paint.setXfermodeMode(SkXfermode::kClear_Mode); | |
1671 drawRect(r, paint); | |
1672 } | |
1673 | |
1674 void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2
, float strokeWidth, StrokeStyle penStyle) | |
1675 { | |
1676 // For odd widths, we add in 0.5 to the appropriate x/y so that the float ar
ithmetic | |
1677 // works out. For example, with a border width of 3, WebKit will pass us (y
1+y2)/2, e.g., | |
1678 // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even
width gave | |
1679 // us a perfect position, but an odd width gave us a position that is off by
exactly 0.5. | |
1680 if (penStyle == DottedStroke || penStyle == DashedStroke) { | |
1681 if (p1.x() == p2.x()) { | |
1682 p1.setY(p1.y() + strokeWidth); | |
1683 p2.setY(p2.y() - strokeWidth); | |
1684 } else { | |
1685 p1.setX(p1.x() + strokeWidth); | |
1686 p2.setX(p2.x() - strokeWidth); | |
1687 } | |
1688 } | |
1689 | |
1690 if (static_cast<int>(strokeWidth) % 2) { //odd | |
1691 if (p1.x() == p2.x()) { | |
1692 // We're a vertical line. Adjust our x. | |
1693 p1.setX(p1.x() + 0.5f); | |
1694 p2.setX(p2.x() + 0.5f); | |
1695 } else { | |
1696 // We're a horizontal line. Adjust our y. | |
1697 p1.setY(p1.y() + 0.5f); | |
1698 p2.setY(p2.y() + 0.5f); | |
1699 } | |
1700 } | |
1701 } | |
1702 | |
1703 PassOwnPtr<ImageBuffer> GraphicsContext::createCompatibleBuffer(const IntSize& s
ize, bool hasAlpha) const | |
1704 { | |
1705 // Make the buffer larger if the context's transform is scaling it so we nee
d a higher | |
1706 // resolution than one pixel per unit. Also set up a corresponding scale fac
tor on the | |
1707 // graphics context. | |
1708 | |
1709 AffineTransform transform = getCTM(DefinitelyIncludeDeviceScale); | |
1710 IntSize scaledSize(static_cast<int>(ceil(size.width() * transform.xScale()))
, static_cast<int>(ceil(size.height() * transform.yScale()))); | |
1711 | |
1712 OwnPtr<ImageBuffer> buffer = ImageBuffer::createCompatibleBuffer(scaledSize,
1, this, hasAlpha); | |
1713 if (!buffer) | |
1714 return nullptr; | |
1715 | |
1716 buffer->context()->scale(FloatSize(static_cast<float>(scaledSize.width()) /
size.width(), | |
1717 static_cast<float>(scaledSize.height()) / size.height())); | |
1718 | |
1719 return buffer.release(); | |
1720 } | |
1721 | |
1722 void GraphicsContext::addCornerArc(SkPath* path, const SkRect& rect, const IntSi
ze& size, int startAngle) | |
1723 { | |
1724 SkIRect ir; | |
1725 int rx = SkMin32(SkScalarRound(rect.width()), size.width()); | |
1726 int ry = SkMin32(SkScalarRound(rect.height()), size.height()); | |
1727 | |
1728 ir.set(-rx, -ry, rx, ry); | |
1729 switch (startAngle) { | |
1730 case 0: | |
1731 ir.offset(rect.fRight - ir.fRight, rect.fBottom - ir.fBottom); | |
1732 break; | |
1733 case 90: | |
1734 ir.offset(rect.fLeft - ir.fLeft, rect.fBottom - ir.fBottom); | |
1735 break; | |
1736 case 180: | |
1737 ir.offset(rect.fLeft - ir.fLeft, rect.fTop - ir.fTop); | |
1738 break; | |
1739 case 270: | |
1740 ir.offset(rect.fRight - ir.fRight, rect.fTop - ir.fTop); | |
1741 break; | |
1742 default: | |
1743 ASSERT(0); | |
1744 } | |
1745 | |
1746 SkRect r; | |
1747 r.set(ir); | |
1748 path->arcTo(r, SkIntToScalar(startAngle), SkIntToScalar(90), false); | |
1749 } | |
1750 | |
1751 void GraphicsContext::setPathFromConvexPoints(SkPath* path, size_t numPoints, co
nst FloatPoint* points) | |
1752 { | |
1753 path->incReserve(numPoints); | |
1754 path->moveTo(WebCoreFloatToSkScalar(points[0].x()), | |
1755 WebCoreFloatToSkScalar(points[0].y())); | |
1756 for (size_t i = 1; i < numPoints; ++i) { | |
1757 path->lineTo(WebCoreFloatToSkScalar(points[i].x()), | |
1758 WebCoreFloatToSkScalar(points[i].y())); | |
1759 } | |
1760 | |
1761 /* The code used to just blindly call this | |
1762 path->setIsConvex(true); | |
1763 But webkit can sometimes send us non-convex 4-point values, so we mark t
he path's | |
1764 convexity as unknown, so it will get computed by skia at draw time. | |
1765 See crbug.com 108605 | |
1766 */ | |
1767 SkPath::Convexity convexity = SkPath::kConvex_Convexity; | |
1768 if (numPoints == 4) | |
1769 convexity = SkPath::kUnknown_Convexity; | |
1770 path->setConvexity(convexity); | |
1771 } | |
1772 | |
1773 void GraphicsContext::setupPaintCommon(SkPaint* paint) const | |
1774 { | |
1775 #if defined(SK_DEBUG) | |
1776 { | |
1777 SkPaint defaultPaint; | |
1778 SkASSERT(*paint == defaultPaint); | |
1779 } | |
1780 #endif | |
1781 | |
1782 paint->setAntiAlias(m_state->m_shouldAntialias); | |
1783 | |
1784 if (!SkXfermode::IsMode(m_state->m_xferMode.get(), SkXfermode::kSrcOver_Mode
)) | |
1785 paint->setXfermode(m_state->m_xferMode.get()); | |
1786 | |
1787 if (m_state->m_looper) | |
1788 paint->setLooper(m_state->m_looper.get()); | |
1789 | |
1790 paint->setColorFilter(m_state->m_colorFilter.get()); | |
1791 } | |
1792 | |
1793 void GraphicsContext::drawOuterPath(const SkPath& path, SkPaint& paint, int widt
h) | |
1794 { | |
1795 #if OS(MACOSX) | |
1796 paint.setAlpha(64); | |
1797 paint.setStrokeWidth(width); | |
1798 paint.setPathEffect(new SkCornerPathEffect((width - 1) * 0.5f))->unref(); | |
1799 #else | |
1800 paint.setStrokeWidth(1); | |
1801 paint.setPathEffect(new SkCornerPathEffect(1))->unref(); | |
1802 #endif | |
1803 drawPath(path, paint); | |
1804 } | |
1805 | |
1806 void GraphicsContext::drawInnerPath(const SkPath& path, SkPaint& paint, int widt
h) | |
1807 { | |
1808 #if OS(MACOSX) | |
1809 paint.setAlpha(128); | |
1810 paint.setStrokeWidth(width * 0.5f); | |
1811 drawPath(path, paint); | |
1812 #endif | |
1813 } | |
1814 | |
1815 void GraphicsContext::setRadii(SkVector* radii, IntSize topLeft, IntSize topRigh
t, IntSize bottomRight, IntSize bottomLeft) | |
1816 { | |
1817 radii[SkRRect::kUpperLeft_Corner].set(SkIntToScalar(topLeft.width()), | |
1818 SkIntToScalar(topLeft.height())); | |
1819 radii[SkRRect::kUpperRight_Corner].set(SkIntToScalar(topRight.width()), | |
1820 SkIntToScalar(topRight.height())); | |
1821 radii[SkRRect::kLowerRight_Corner].set(SkIntToScalar(bottomRight.width()), | |
1822 SkIntToScalar(bottomRight.height())); | |
1823 radii[SkRRect::kLowerLeft_Corner].set(SkIntToScalar(bottomLeft.width()), | |
1824 SkIntToScalar(bottomLeft.height())); | |
1825 } | |
1826 | |
1827 PassRefPtr<SkColorFilter> GraphicsContext::WebCoreColorFilterToSkiaColorFilter(C
olorFilter colorFilter) | |
1828 { | |
1829 switch (colorFilter) { | |
1830 case ColorFilterLuminanceToAlpha: | |
1831 return adoptRef(SkLumaColorFilter::Create()); | |
1832 case ColorFilterLinearRGBToSRGB: | |
1833 return ImageBuffer::createColorSpaceFilter(ColorSpaceLinearRGB, ColorSpa
ceDeviceRGB); | |
1834 case ColorFilterSRGBToLinearRGB: | |
1835 return ImageBuffer::createColorSpaceFilter(ColorSpaceDeviceRGB, ColorSpa
ceLinearRGB); | |
1836 case ColorFilterNone: | |
1837 break; | |
1838 default: | |
1839 ASSERT_NOT_REACHED(); | |
1840 break; | |
1841 } | |
1842 | |
1843 return 0; | |
1844 } | |
1845 | |
1846 | |
1847 #if OS(MACOSX) | |
1848 CGColorSpaceRef deviceRGBColorSpaceRef() | |
1849 { | |
1850 static CGColorSpaceRef deviceSpace = CGColorSpaceCreateDeviceRGB(); | |
1851 return deviceSpace; | |
1852 } | |
1853 #else | |
1854 void GraphicsContext::draw2xMarker(SkBitmap* bitmap, int index) | |
1855 { | |
1856 const SkPMColor lineColor = lineColors(index); | |
1857 const SkPMColor antiColor1 = antiColors1(index); | |
1858 const SkPMColor antiColor2 = antiColors2(index); | |
1859 | |
1860 uint32_t* row1 = bitmap->getAddr32(0, 0); | |
1861 uint32_t* row2 = bitmap->getAddr32(0, 1); | |
1862 uint32_t* row3 = bitmap->getAddr32(0, 2); | |
1863 uint32_t* row4 = bitmap->getAddr32(0, 3); | |
1864 | |
1865 // Pattern: X0o o0X0o o0 | |
1866 // XX0o o0XXX0o o0X | |
1867 // o0XXX0o o0XXX0o | |
1868 // o0X0o o0X0o | |
1869 const SkPMColor row1Color[] = { lineColor, antiColor1, antiColor2, 0,
0, 0, antiColor2, antiColor1 }; | |
1870 const SkPMColor row2Color[] = { lineColor, lineColor, antiColor1, antiColor
2, 0, antiColor2, antiColor1, lineColor }; | |
1871 const SkPMColor row3Color[] = { 0, antiColor2, antiColor1, lineColor
, lineColor, lineColor, antiColor1, antiColor2 }; | |
1872 const SkPMColor row4Color[] = { 0, 0, antiColor2, antiColor
1, lineColor, antiColor1, antiColor2, 0 }; | |
1873 | |
1874 for (int x = 0; x < bitmap->width() + 8; x += 8) { | |
1875 int count = std::min(bitmap->width() - x, 8); | |
1876 if (count > 0) { | |
1877 memcpy(row1 + x, row1Color, count * sizeof(SkPMColor)); | |
1878 memcpy(row2 + x, row2Color, count * sizeof(SkPMColor)); | |
1879 memcpy(row3 + x, row3Color, count * sizeof(SkPMColor)); | |
1880 memcpy(row4 + x, row4Color, count * sizeof(SkPMColor)); | |
1881 } | |
1882 } | |
1883 } | |
1884 | |
1885 void GraphicsContext::draw1xMarker(SkBitmap* bitmap, int index) | |
1886 { | |
1887 const uint32_t lineColor = lineColors(index); | |
1888 const uint32_t antiColor = antiColors2(index); | |
1889 | |
1890 // Pattern: X o o X o o X | |
1891 // o X o o X o | |
1892 uint32_t* row1 = bitmap->getAddr32(0, 0); | |
1893 uint32_t* row2 = bitmap->getAddr32(0, 1); | |
1894 for (int x = 0; x < bitmap->width(); x++) { | |
1895 switch (x % 4) { | |
1896 case 0: | |
1897 row1[x] = lineColor; | |
1898 break; | |
1899 case 1: | |
1900 row1[x] = antiColor; | |
1901 row2[x] = antiColor; | |
1902 break; | |
1903 case 2: | |
1904 row2[x] = lineColor; | |
1905 break; | |
1906 case 3: | |
1907 row1[x] = antiColor; | |
1908 row2[x] = antiColor; | |
1909 break; | |
1910 } | |
1911 } | |
1912 } | |
1913 | |
1914 const SkPMColor GraphicsContext::lineColors(int index) | |
1915 { | |
1916 static const SkPMColor colors[] = { | |
1917 SkPreMultiplyARGB(0xFF, 0xFF, 0x00, 0x00), // Opaque red. | |
1918 SkPreMultiplyARGB(0xFF, 0xC0, 0xC0, 0xC0) // Opaque gray. | |
1919 }; | |
1920 | |
1921 return colors[index]; | |
1922 } | |
1923 | |
1924 const SkPMColor GraphicsContext::antiColors1(int index) | |
1925 { | |
1926 static const SkPMColor colors[] = { | |
1927 SkPreMultiplyARGB(0xB0, 0xFF, 0x00, 0x00), // Semitransparent red. | |
1928 SkPreMultiplyARGB(0xB0, 0xC0, 0xC0, 0xC0) // Semitransparent gray. | |
1929 }; | |
1930 | |
1931 return colors[index]; | |
1932 } | |
1933 | |
1934 const SkPMColor GraphicsContext::antiColors2(int index) | |
1935 { | |
1936 static const SkPMColor colors[] = { | |
1937 SkPreMultiplyARGB(0x60, 0xFF, 0x00, 0x00), // More transparent red | |
1938 SkPreMultiplyARGB(0x60, 0xC0, 0xC0, 0xC0) // More transparent gray | |
1939 }; | |
1940 | |
1941 return colors[index]; | |
1942 } | |
1943 #endif | |
1944 | |
1945 void GraphicsContext::setupShader(SkPaint* paint, Gradient* grad, Pattern* pat,
SkColor color) const | |
1946 { | |
1947 RefPtr<SkShader> shader; | |
1948 | |
1949 if (grad) { | |
1950 shader = grad->shader(); | |
1951 color = SK_ColorBLACK; | |
1952 } else if (pat) { | |
1953 shader = pat->shader(); | |
1954 color = SK_ColorBLACK; | |
1955 paint->setFilterBitmap(imageInterpolationQuality() != InterpolationNone)
; | |
1956 } | |
1957 | |
1958 paint->setColor(m_state->applyAlpha(color)); | |
1959 | |
1960 if (!shader) | |
1961 return; | |
1962 | |
1963 paint->setShader(shader.get()); | |
1964 } | |
1965 | |
1966 void GraphicsContext::didDrawTextInRect(const SkRect& textRect) | |
1967 { | |
1968 if (m_trackTextRegion) { | |
1969 TRACE_EVENT0("skia", "PlatformContextSkia::trackTextRegion"); | |
1970 m_textRegion.join(textRect); | |
1971 } | |
1972 } | |
1973 | |
1974 } | |
OLD | NEW |