Home | History | Annotate | Download | only in cairo
      1 /*
      2  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
      3  * Copyright (C) 2006 Michael Emmel mike.emmel (at) gmail.com
      4  * Copyright (C) 2007, 2008 Alp Toker <alp (at) atoker.com>
      5  * Copyright (C) 2009 Dirk Schulze <krit (at) webkit.org>
      6  * Copyright (C) 2010 Holger Hans Peter Freyther
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     25  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 
     30 #include "config.h"
     31 #include "Font.h"
     32 
     33 #include "AffineTransform.h"
     34 #include "CairoUtilities.h"
     35 #include "ContextShadow.h"
     36 #include "GlyphBuffer.h"
     37 #include "Gradient.h"
     38 #include "GraphicsContext.h"
     39 #include "PlatformContextCairo.h"
     40 #include "ImageBuffer.h"
     41 #include "Pattern.h"
     42 #include "SimpleFontData.h"
     43 
     44 namespace WebCore {
     45 
     46 static void prepareContextForGlyphDrawing(cairo_t* context, const SimpleFontData* font, const FloatPoint& point)
     47 {
     48     static const float syntheticObliqueSkew = -tanf(14 * acosf(0) / 90);
     49     cairo_set_scaled_font(context, font->platformData().scaledFont());
     50     if (font->platformData().syntheticOblique()) {
     51         cairo_matrix_t mat = {1, 0, syntheticObliqueSkew, 1, point.x(), point.y()};
     52         cairo_transform(context, &mat);
     53     } else
     54         cairo_translate(context, point.x(), point.y());
     55 }
     56 
     57 static void drawGlyphsToContext(cairo_t* context, const SimpleFontData* font, GlyphBufferGlyph* glyphs, int numGlyphs)
     58 {
     59     cairo_show_glyphs(context, glyphs, numGlyphs);
     60     if (font->syntheticBoldOffset()) {
     61         // We could use cairo_save/cairo_restore here, but two translations are likely faster.
     62         cairo_translate(context, font->syntheticBoldOffset(), 0);
     63         cairo_show_glyphs(context, glyphs, numGlyphs);
     64         cairo_translate(context, -font->syntheticBoldOffset(), 0);
     65     }
     66 }
     67 
     68 static void drawGlyphsShadow(GraphicsContext* graphicsContext, const FloatPoint& point, const SimpleFontData* font, GlyphBufferGlyph* glyphs, int numGlyphs)
     69 {
     70     ContextShadow* shadow = graphicsContext->contextShadow();
     71     ASSERT(shadow);
     72 
     73     if (!(graphicsContext->textDrawingMode() & TextModeFill) || shadow->m_type == ContextShadow::NoShadow)
     74         return;
     75 
     76     if (!shadow->mustUseContextShadow(graphicsContext)) {
     77         // Optimize non-blurry shadows, by just drawing text without the ContextShadow.
     78         cairo_t* context = graphicsContext->platformContext()->cr();
     79         cairo_save(context);
     80         cairo_translate(context, shadow->m_offset.width(), shadow->m_offset.height());
     81         setSourceRGBAFromColor(context, shadow->m_color);
     82         prepareContextForGlyphDrawing(context, font, point);
     83         cairo_show_glyphs(context, glyphs, numGlyphs);
     84         cairo_restore(context);
     85         return;
     86     }
     87 
     88     cairo_text_extents_t extents;
     89     cairo_scaled_font_glyph_extents(font->platformData().scaledFont(), glyphs, numGlyphs, &extents);
     90     FloatRect fontExtentsRect(point.x(), point.y() - extents.height, extents.width, extents.height);
     91     cairo_t* shadowContext = shadow->beginShadowLayer(graphicsContext, fontExtentsRect);
     92     if (shadowContext) {
     93         prepareContextForGlyphDrawing(shadowContext, font, point);
     94         drawGlyphsToContext(shadowContext, font, glyphs, numGlyphs);
     95         shadow->endShadowLayer(graphicsContext);
     96     }
     97 }
     98 
     99 void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer,
    100                       int from, int numGlyphs, const FloatPoint& point) const
    101 {
    102     GlyphBufferGlyph* glyphs = (GlyphBufferGlyph*)glyphBuffer.glyphs(from);
    103 
    104     float offset = 0.0f;
    105     for (int i = 0; i < numGlyphs; i++) {
    106         glyphs[i].x = offset;
    107         glyphs[i].y = 0.0f;
    108         offset += glyphBuffer.advanceAt(from + i);
    109     }
    110 
    111     PlatformContextCairo* platformContext = context->platformContext();
    112     drawGlyphsShadow(context, point, font, glyphs, numGlyphs);
    113 
    114     cairo_t* cr = platformContext->cr();
    115     cairo_save(cr);
    116     prepareContextForGlyphDrawing(cr, font, point);
    117     if (context->textDrawingMode() & TextModeFill) {
    118         if (context->fillGradient()) {
    119             cairo_set_source(cr, context->fillGradient()->platformGradient());
    120             if (context->getAlpha() < 1.0f) {
    121                 cairo_push_group(cr);
    122                 cairo_paint_with_alpha(cr, context->getAlpha());
    123                 cairo_pop_group_to_source(cr);
    124             }
    125         } else if (context->fillPattern()) {
    126             AffineTransform affine;
    127             cairo_pattern_t* pattern = context->fillPattern()->createPlatformPattern(affine);
    128             cairo_set_source(cr, pattern);
    129             if (context->getAlpha() < 1.0f) {
    130                 cairo_push_group(cr);
    131                 cairo_paint_with_alpha(cr, context->getAlpha());
    132                 cairo_pop_group_to_source(cr);
    133             }
    134             cairo_pattern_destroy(pattern);
    135         } else {
    136             float red, green, blue, alpha;
    137             context->fillColor().getRGBA(red, green, blue, alpha);
    138             cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha());
    139         }
    140         drawGlyphsToContext(cr, font, glyphs, numGlyphs);
    141     }
    142 
    143     // Prevent running into a long computation within cairo. If the stroke width is
    144     // twice the size of the width of the text we will not ask cairo to stroke
    145     // the text as even one single stroke would cover the full wdth of the text.
    146     //  See https://bugs.webkit.org/show_bug.cgi?id=33759.
    147     if (context->textDrawingMode() & TextModeStroke && context->strokeThickness() < 2 * offset) {
    148         if (context->strokeGradient()) {
    149             cairo_set_source(cr, context->strokeGradient()->platformGradient());
    150             if (context->getAlpha() < 1.0f) {
    151                 cairo_push_group(cr);
    152                 cairo_paint_with_alpha(cr, context->getAlpha());
    153                 cairo_pop_group_to_source(cr);
    154             }
    155         } else if (context->strokePattern()) {
    156             AffineTransform affine;
    157             cairo_pattern_t* pattern = context->strokePattern()->createPlatformPattern(affine);
    158             cairo_set_source(cr, pattern);
    159             if (context->getAlpha() < 1.0f) {
    160                 cairo_push_group(cr);
    161                 cairo_paint_with_alpha(cr, context->getAlpha());
    162                 cairo_pop_group_to_source(cr);
    163             }
    164             cairo_pattern_destroy(pattern);
    165         } else {
    166             float red, green, blue, alpha;
    167             context->strokeColor().getRGBA(red, green, blue, alpha);
    168             cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha());
    169         }
    170         cairo_glyph_path(cr, glyphs, numGlyphs);
    171         cairo_set_line_width(cr, context->strokeThickness());
    172         cairo_stroke(cr);
    173     }
    174 
    175     cairo_restore(cr);
    176 }
    177 
    178 }
    179