/* * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "GraphicsContext.h" #include "BidiResolver.h" #include "Font.h" #include "TextStyle.h" using namespace std; namespace WebCore { class TextRunIterator { public: TextRunIterator() : m_textRun(0) , m_offset(0) { } TextRunIterator(const TextRun* textRun, unsigned offset) : m_textRun(textRun) , m_offset(offset) { } TextRunIterator(const TextRunIterator& other) : m_textRun(other.m_textRun) , m_offset(other.m_offset) { } unsigned offset() const { return m_offset; } void increment(BidiResolver&) { m_offset++; } bool atEnd() const { return m_offset >= m_textRun->length(); } UChar current() const { return (*m_textRun)[m_offset]; } WTF::Unicode::Direction direction() const { return atEnd() ? WTF::Unicode::OtherNeutral : WTF::Unicode::direction(current()); } bool operator==(const TextRunIterator& other) { return m_offset == other.m_offset && m_textRun == other.m_textRun; } bool operator!=(const TextRunIterator& other) { return !operator==(other); } private: const TextRun* m_textRun; int m_offset; }; struct GraphicsContextState { GraphicsContextState() : strokeStyle(SolidStroke) , strokeThickness(0) , strokeColor(Color::black) , fillColor(Color::black) , textDrawingMode(cTextFill) , paintingDisabled(false) {} Font font; StrokeStyle strokeStyle; float strokeThickness; Color strokeColor; Color fillColor; int textDrawingMode; bool paintingDisabled; }; class GraphicsContextPrivate { public: GraphicsContextPrivate(); GraphicsContextState state; Vector stack; Vector m_focusRingRects; int m_focusRingWidth; int m_focusRingOffset; bool m_updatingControlTints; }; GraphicsContextPrivate::GraphicsContextPrivate() : m_focusRingWidth(0) , m_focusRingOffset(0) , m_updatingControlTints(false) { } GraphicsContextPrivate* GraphicsContext::createGraphicsContextPrivate() { return new GraphicsContextPrivate; } void GraphicsContext::destroyGraphicsContextPrivate(GraphicsContextPrivate* deleteMe) { delete deleteMe; } void GraphicsContext::save() { if (paintingDisabled()) return; m_common->stack.append(m_common->state); savePlatformState(); } void GraphicsContext::restore() { if (paintingDisabled()) return; if (m_common->stack.isEmpty()) { LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty"); return; } m_common->state = m_common->stack.last(); m_common->stack.removeLast(); restorePlatformState(); } const Font& GraphicsContext::font() const { return m_common->state.font; } void GraphicsContext::setFont(const Font& aFont) { m_common->state.font = aFont; setPlatformFont(aFont); } void GraphicsContext::setStrokeThickness(float thickness) { m_common->state.strokeThickness = thickness; setPlatformStrokeThickness(thickness); } void GraphicsContext::setStrokeStyle(const StrokeStyle& style) { m_common->state.strokeStyle = style; setPlatformStrokeStyle(style); } void GraphicsContext::setStrokeColor(const Color& color) { m_common->state.strokeColor = color; setPlatformStrokeColor(color); } float GraphicsContext::strokeThickness() const { return m_common->state.strokeThickness; } StrokeStyle GraphicsContext::strokeStyle() const { return m_common->state.strokeStyle; } Color GraphicsContext::strokeColor() const { return m_common->state.strokeColor; } void GraphicsContext::setFillColor(const Color& color) { m_common->state.fillColor = color; setPlatformFillColor(color); } Color GraphicsContext::fillColor() const { return m_common->state.fillColor; } bool GraphicsContext::updatingControlTints() const { return m_common->m_updatingControlTints; } void GraphicsContext::setUpdatingControlTints(bool b) { setPaintingDisabled(b); m_common->m_updatingControlTints = b; } void GraphicsContext::setPaintingDisabled(bool f) { m_common->state.paintingDisabled = f; } bool GraphicsContext::paintingDisabled() const { return m_common->state.paintingDisabled; } void GraphicsContext::drawImage(Image* image, const IntPoint& p, CompositeOperator op) { drawImage(image, p, IntRect(0, 0, -1, -1), op); } void GraphicsContext::drawImage(Image* image, const IntRect& r, CompositeOperator op) { drawImage(image, r, IntRect(0, 0, -1, -1), op); } void GraphicsContext::drawImage(Image* image, const IntPoint& dest, const IntRect& srcRect, CompositeOperator op) { drawImage(image, IntRect(dest, srcRect.size()), srcRect, op); } void GraphicsContext::drawImage(Image* image, const IntRect& dest, const IntRect& srcRect, CompositeOperator op) { drawImage(image, FloatRect(dest), srcRect, op); } void GraphicsContext::drawText(const TextRun& run, const IntPoint& point, int from, int to) { drawText(run, point, TextStyle(), from, to); } void GraphicsContext::drawText(const TextRun& run, const IntPoint& point, const TextStyle& style, int from, int to) { if (paintingDisabled()) return; font().drawText(this, run, style, point, from, to); } void GraphicsContext::drawBidiText(const TextRun& run, const IntPoint& point, const TextStyle& style) { if (paintingDisabled()) return; BidiResolver bidiResolver; WTF::Unicode::Direction paragraphDirection = style.ltr() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft; bidiResolver.setStatus(BidiStatus(paragraphDirection, paragraphDirection, paragraphDirection, new BidiContext(style.ltr() ? 0 : 1, paragraphDirection, style.directionalOverride()))); bidiResolver.createBidiRunsForLine(TextRunIterator(&run, 0), TextRunIterator(&run, run.length())); if (!bidiResolver.runCount()) return; FloatPoint currPoint = point; BidiCharacterRun* bidiRun = bidiResolver.firstRun(); while (bidiRun) { TextStyle subrunStyle(style); subrunStyle.setRTL(bidiRun->level() % 2); subrunStyle.setDirectionalOverride(bidiRun->dirOverride(false)); TextRun subrun(run.data(bidiRun->start()), bidiRun->stop() - bidiRun->start()); font().drawText(this, subrun, subrunStyle, currPoint); bidiRun = bidiRun->next(); // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here. if (bidiRun) currPoint.move(font().floatWidth(subrun, subrunStyle), 0.f); } bidiResolver.deleteRuns(); } void GraphicsContext::drawHighlightForText(const TextRun& run, const IntPoint& point, int h, const TextStyle& style, const Color& backgroundColor, int from, int to) { if (paintingDisabled()) return; fillRect(font().selectionRectForText(run, style, point, h, from, to), backgroundColor); } void GraphicsContext::initFocusRing(int width, int offset) { if (paintingDisabled()) return; clearFocusRing(); m_common->m_focusRingWidth = width; m_common->m_focusRingOffset = offset; } void GraphicsContext::clearFocusRing() { m_common->m_focusRingRects.clear(); } IntRect GraphicsContext::focusRingBoundingRect() { IntRect result = IntRect(0, 0, 0, 0); const Vector& rects = focusRingRects(); unsigned rectCount = rects.size(); for (unsigned i = 0; i < rectCount; i++) result.unite(rects[i]); return result; } void GraphicsContext::addFocusRingRect(const IntRect& rect) { if (paintingDisabled() || rect.isEmpty()) return; m_common->m_focusRingRects.append(rect); } int GraphicsContext::focusRingWidth() const { return m_common->m_focusRingWidth; } int GraphicsContext::focusRingOffset() const { return m_common->m_focusRingOffset; } const Vector& GraphicsContext::focusRingRects() const { return m_common->m_focusRingRects; } void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op) { if (paintingDisabled()) return; float tsw = src.width(); float tsh = src.height(); float tw = dest.width(); float th = dest.height(); if (tsw == -1) tsw = image->width(); if (tsh == -1) tsh = image->height(); if (tw == -1) tw = image->width(); if (th == -1) th = image->height(); image->draw(this, FloatRect(dest.location(), FloatSize(tw, th)), FloatRect(src.location(), FloatSize(tsw, tsh)), op); } void GraphicsContext::drawTiledImage(Image* image, const IntRect& rect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op) { if (paintingDisabled()) return; image->drawTiled(this, rect, srcPoint, tileSize, op); } void GraphicsContext::drawTiledImage(Image* image, const IntRect& dest, const IntRect& srcRect, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op) { if (paintingDisabled()) return; if (hRule == Image::StretchTile && vRule == Image::StretchTile) // Just do a scale. return drawImage(image, dest, srcRect); image->drawTiled(this, dest, srcRect, hRule, vRule, op); } void GraphicsContext::addRoundedRectClip(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight) { if (paintingDisabled()) return; clip(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight)); } void GraphicsContext::clipOutRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight) { if (paintingDisabled()) return; clipOut(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight)); } int GraphicsContext::textDrawingMode() { return m_common->state.textDrawingMode; } void GraphicsContext::setTextDrawingMode(int mode) { m_common->state.textDrawingMode = mode; if (paintingDisabled()) return; setPlatformTextDrawingMode(mode); } #if !PLATFORM(CG) // Implement this if you want to go ahead and push the drawing mode into your native context // immediately. void GraphicsContext::setPlatformTextDrawingMode(int mode) { } #endif #if !PLATFORM(QT) && !PLATFORM(CAIRO) void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle&) { } void GraphicsContext::setPlatformFont(const Font&) { } #endif }