Home | History | Annotate | Download | only in chromium
      1 /*
      2  * Copyright (c) 2007, 2008, 2010 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "Font.h"
     33 
     34 #include "ComplexTextControllerLinux.h"
     35 #include "FloatRect.h"
     36 #include "GlyphBuffer.h"
     37 #include "GraphicsContext.h"
     38 #include "HarfbuzzSkia.h"
     39 #include "NotImplemented.h"
     40 #include "PlatformContextSkia.h"
     41 #include "SimpleFontData.h"
     42 
     43 #include "SkCanvas.h"
     44 #include "SkPaint.h"
     45 #include "SkTemplates.h"
     46 #include "SkTypeface.h"
     47 #include "SkUtils.h"
     48 
     49 #include <wtf/unicode/Unicode.h>
     50 
     51 namespace WebCore {
     52 
     53 bool Font::canReturnFallbackFontsForComplexText()
     54 {
     55     return false;
     56 }
     57 
     58 bool Font::canExpandAroundIdeographsInComplexText()
     59 {
     60     return false;
     61 }
     62 
     63 static bool isCanvasMultiLayered(SkCanvas* canvas)
     64 {
     65     SkCanvas::LayerIter layerIterator(canvas, false);
     66     layerIterator.next();
     67     return !layerIterator.done();
     68 }
     69 
     70 static void adjustTextRenderMode(SkPaint* paint, PlatformContextSkia* skiaContext)
     71 {
     72     // Our layers only have a single alpha channel. This means that subpixel
     73     // rendered text cannot be compositied correctly when the layer is
     74     // collapsed. Therefore, subpixel text is disabled when we are drawing
     75     // onto a layer or when the compositor is being used.
     76     if (isCanvasMultiLayered(skiaContext->canvas()) || skiaContext->isDrawingToImageBuffer())
     77         paint->setLCDRenderText(false);
     78 }
     79 
     80 void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font,
     81                       const GlyphBuffer& glyphBuffer,  int from, int numGlyphs,
     82                       const FloatPoint& point) const {
     83     SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert
     84 
     85     const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from);
     86     SkScalar x = SkFloatToScalar(point.x());
     87     SkScalar y = SkFloatToScalar(point.y());
     88 
     89     // FIXME: text rendering speed:
     90     // Android has code in their WebCore fork to special case when the
     91     // GlyphBuffer has no advances other than the defaults. In that case the
     92     // text drawing can proceed faster. However, it's unclear when those
     93     // patches may be upstreamed to WebKit so we always use the slower path
     94     // here.
     95     const GlyphBufferAdvance* adv = glyphBuffer.advances(from);
     96     SkAutoSTMalloc<32, SkPoint> storage(numGlyphs), storage2(numGlyphs), storage3(numGlyphs);
     97     SkPoint* pos = storage.get();
     98     SkPoint* vPosBegin = storage2.get();
     99     SkPoint* vPosEnd = storage3.get();
    100 
    101     bool isVertical = font->platformData().orientation() == Vertical;
    102     for (int i = 0; i < numGlyphs; i++) {
    103         SkScalar myWidth = SkFloatToScalar(adv[i].width());
    104         pos[i].set(x, y);
    105         if (isVertical) {
    106             vPosBegin[i].set(x + myWidth, y);
    107             vPosEnd[i].set(x + myWidth, y - myWidth);
    108         }
    109         x += myWidth;
    110         y += SkFloatToScalar(adv[i].height());
    111     }
    112 
    113     gc->platformContext()->prepareForSoftwareDraw();
    114 
    115     SkCanvas* canvas = gc->platformContext()->canvas();
    116     TextDrawingModeFlags textMode = gc->platformContext()->getTextDrawingMode();
    117 
    118     // We draw text up to two times (once for fill, once for stroke).
    119     if (textMode & TextModeFill) {
    120         SkPaint paint;
    121         gc->platformContext()->setupPaintForFilling(&paint);
    122         font->platformData().setupPaint(&paint);
    123         adjustTextRenderMode(&paint, gc->platformContext());
    124         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    125         paint.setColor(gc->fillColor().rgb());
    126 
    127         if (isVertical) {
    128             SkPath path;
    129             for (int i = 0; i < numGlyphs; ++i) {
    130                 path.reset();
    131                 path.moveTo(vPosBegin[i]);
    132                 path.lineTo(vPosEnd[i]);
    133                 canvas->drawTextOnPath(glyphs + i, 2, path, 0, paint);
    134             }
    135         } else
    136             canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint);
    137     }
    138 
    139     if ((textMode & TextModeStroke)
    140         && gc->platformContext()->getStrokeStyle() != NoStroke
    141         && gc->platformContext()->getStrokeThickness() > 0) {
    142 
    143         SkPaint paint;
    144         gc->platformContext()->setupPaintForStroking(&paint, 0, 0);
    145         font->platformData().setupPaint(&paint);
    146         adjustTextRenderMode(&paint, gc->platformContext());
    147         paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    148         paint.setColor(gc->strokeColor().rgb());
    149 
    150         if (textMode & TextModeFill) {
    151             // If we also filled, we don't want to draw shadows twice.
    152             // See comment in FontChromiumWin.cpp::paintSkiaText() for more details.
    153             SkSafeUnref(paint.setLooper(0));
    154         }
    155 
    156         if (isVertical) {
    157             SkPath path;
    158             for (int i = 0; i < numGlyphs; ++i) {
    159                 path.reset();
    160                 path.moveTo(vPosBegin[i]);
    161                 path.lineTo(vPosEnd[i]);
    162                 canvas->drawTextOnPath(glyphs + i, 2, path, 0, paint);
    163             }
    164         } else
    165             canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint);
    166     }
    167 }
    168 
    169 // Harfbuzz uses 26.6 fixed point values for pixel offsets. However, we don't
    170 // handle subpixel positioning so this function is used to truncate Harfbuzz
    171 // values to a number of pixels.
    172 static int truncateFixedPointToInteger(HB_Fixed value)
    173 {
    174     return value >> 6;
    175 }
    176 
    177 static void setupForTextPainting(SkPaint* paint, SkColor color)
    178 {
    179     paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    180     paint->setColor(color);
    181 }
    182 
    183 void Font::drawComplexText(GraphicsContext* gc, const TextRun& run,
    184                            const FloatPoint& point, int from, int to) const
    185 {
    186     if (!run.length())
    187         return;
    188 
    189     SkCanvas* canvas = gc->platformContext()->canvas();
    190     TextDrawingModeFlags textMode = gc->platformContext()->getTextDrawingMode();
    191     bool fill = textMode & TextModeFill;
    192     bool stroke = (textMode & TextModeStroke)
    193                && gc->platformContext()->getStrokeStyle() != NoStroke
    194                && gc->platformContext()->getStrokeThickness() > 0;
    195 
    196     if (!fill && !stroke)
    197         return;
    198 
    199     SkPaint strokePaint, fillPaint;
    200     if (fill) {
    201         gc->platformContext()->setupPaintForFilling(&fillPaint);
    202         setupForTextPainting(&fillPaint, gc->fillColor().rgb());
    203     }
    204     if (stroke) {
    205         gc->platformContext()->setupPaintForStroking(&strokePaint, 0, 0);
    206         setupForTextPainting(&strokePaint, gc->strokeColor().rgb());
    207     }
    208 
    209     ComplexTextController controller(run, point.x(), this);
    210     controller.setWordSpacingAdjustment(wordSpacing());
    211     controller.setLetterSpacingAdjustment(letterSpacing());
    212     controller.setPadding(run.expansion());
    213 
    214     if (run.rtl()) {
    215         // FIXME: this causes us to shape the text twice -- once to compute the width and then again
    216         // below when actually rendering.  Change ComplexTextController to match platform/mac and
    217         // platform/chromium/win by having it store the shaped runs, so we can reuse the results.
    218         controller.reset(point.x() + controller.widthOfFullRun());
    219         // We need to set the padding again because ComplexTextController layout consumed the value.
    220         // Fixing the above problem would help here too.
    221         controller.setPadding(run.expansion());
    222     }
    223 
    224     while (controller.nextScriptRun()) {
    225         if (fill) {
    226             controller.fontPlatformDataForScriptRun()->setupPaint(&fillPaint);
    227             adjustTextRenderMode(&fillPaint, gc->platformContext());
    228             canvas->drawPosTextH(controller.glyphs(), controller.length() << 1, controller.xPositions(), point.y(), fillPaint);
    229         }
    230 
    231         if (stroke) {
    232             controller.fontPlatformDataForScriptRun()->setupPaint(&strokePaint);
    233             adjustTextRenderMode(&strokePaint, gc->platformContext());
    234             canvas->drawPosTextH(controller.glyphs(), controller.length() << 1, controller.xPositions(), point.y(), strokePaint);
    235         }
    236     }
    237 }
    238 
    239 void Font::drawEmphasisMarksForComplexText(GraphicsContext* /* context */, const TextRun& /* run */, const AtomicString& /* mark */, const FloatPoint& /* point */, int /* from */, int /* to */) const
    240 {
    241     notImplemented();
    242 }
    243 
    244 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* /* fallbackFonts */, GlyphOverflow* /* glyphOverflow */) const
    245 {
    246     ComplexTextController controller(run, 0, this);
    247     controller.setWordSpacingAdjustment(wordSpacing());
    248     controller.setLetterSpacingAdjustment(letterSpacing());
    249     controller.setPadding(run.expansion());
    250     return controller.widthOfFullRun();
    251 }
    252 
    253 static int glyphIndexForXPositionInScriptRun(const ComplexTextController& controller, int targetX)
    254 {
    255     // Iterate through the glyphs in logical order, seeing whether targetX falls between the previous
    256     // position and halfway through the current glyph.
    257     // FIXME: this code probably belongs in ComplexTextController.
    258     int lastX = controller.offsetX() - (controller.rtl() ? -controller.width() : controller.width());
    259     for (int glyphIndex = 0; static_cast<unsigned>(glyphIndex) < controller.length(); ++glyphIndex) {
    260         int advance = truncateFixedPointToInteger(controller.advances()[glyphIndex]);
    261         int nextX = static_cast<int>(controller.xPositions()[glyphIndex]) + advance / 2;
    262         if (std::min(nextX, lastX) <= targetX && targetX <= std::max(nextX, lastX))
    263             return glyphIndex;
    264         lastX = nextX;
    265     }
    266 
    267     return controller.length() - 1;
    268 }
    269 
    270 // Return the code point index for the given |x| offset into the text run.
    271 int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat,
    272                                           bool includePartialGlyphs) const
    273 {
    274     // FIXME: This truncation is not a problem for HTML, but only affects SVG, which passes floating-point numbers
    275     // to Font::offsetForPosition(). Bug http://webkit.org/b/40673 tracks fixing this problem.
    276     int targetX = static_cast<int>(xFloat);
    277 
    278     // (Mac code ignores includePartialGlyphs, and they don't know what it's
    279     // supposed to do, so we just ignore it as well.)
    280     ComplexTextController controller(run, 0, this);
    281     controller.setWordSpacingAdjustment(wordSpacing());
    282     controller.setLetterSpacingAdjustment(letterSpacing());
    283     controller.setPadding(run.expansion());
    284     if (run.rtl()) {
    285         // See FIXME in drawComplexText.
    286         controller.reset(controller.widthOfFullRun());
    287         controller.setPadding(run.expansion());
    288     }
    289 
    290     unsigned basePosition = 0;
    291 
    292     int x = controller.offsetX();
    293     while (controller.nextScriptRun()) {
    294         int nextX = controller.offsetX();
    295 
    296         if (std::min(x, nextX) <= targetX && targetX <= std::max(x, nextX)) {
    297             // The x value in question is within this script run.
    298             const int glyphIndex = glyphIndexForXPositionInScriptRun(controller, targetX);
    299 
    300             // Now that we have a glyph index, we have to turn that into a
    301             // code-point index. Because of ligatures, several code-points may
    302             // have gone into a single glyph. We iterate over the clusters log
    303             // and find the first code-point which contributed to the glyph.
    304 
    305             // Some shapers (i.e. Khmer) will produce cluster logs which report
    306             // that /no/ code points contributed to certain glyphs. Because of
    307             // this, we take any code point which contributed to the glyph in
    308             // question, or any subsequent glyph. If we run off the end, then
    309             // we take the last code point.
    310             const unsigned short* log = controller.logClusters();
    311             for (unsigned j = 0; j < controller.numCodePoints(); ++j) {
    312                 if (log[j] >= glyphIndex)
    313                     return basePosition + j;
    314             }
    315 
    316             return basePosition + controller.numCodePoints() - 1;
    317         }
    318 
    319         basePosition += controller.numCodePoints();
    320     }
    321 
    322     return basePosition;
    323 }
    324 
    325 // Return the rectangle for selecting the given range of code-points in the TextRun.
    326 FloatRect Font::selectionRectForComplexText(const TextRun& run,
    327                                             const FloatPoint& point, int height,
    328                                             int from, int to) const
    329 {
    330     int fromX = -1, toX = -1;
    331     ComplexTextController controller(run, 0, this);
    332     controller.setWordSpacingAdjustment(wordSpacing());
    333     controller.setLetterSpacingAdjustment(letterSpacing());
    334     controller.setPadding(run.expansion());
    335     if (run.rtl()) {
    336         // See FIXME in drawComplexText.
    337         controller.reset(controller.widthOfFullRun());
    338         controller.setPadding(run.expansion());
    339     }
    340 
    341     // Iterate through the script runs in logical order, searching for the run covering the positions of interest.
    342     while (controller.nextScriptRun() && (fromX == -1 || toX == -1)) {
    343         if (fromX == -1 && from >= 0 && static_cast<unsigned>(from) < controller.numCodePoints()) {
    344             // |from| is within this script run. So we index the clusters log to
    345             // find which glyph this code-point contributed to and find its x
    346             // position.
    347             int glyph = controller.logClusters()[from];
    348             fromX = controller.xPositions()[glyph];
    349             if (controller.rtl())
    350                 fromX += truncateFixedPointToInteger(controller.advances()[glyph]);
    351         } else
    352             from -= controller.numCodePoints();
    353 
    354         if (toX == -1 && to >= 0 && static_cast<unsigned>(to) < controller.numCodePoints()) {
    355             int glyph = controller.logClusters()[to];
    356             toX = controller.xPositions()[glyph];
    357             if (controller.rtl())
    358                 toX += truncateFixedPointToInteger(controller.advances()[glyph]);
    359         } else
    360             to -= controller.numCodePoints();
    361     }
    362 
    363     // The position in question might be just after the text.
    364     if (fromX == -1)
    365         fromX = controller.offsetX();
    366     if (toX == -1)
    367         toX = controller.offsetX();
    368 
    369     ASSERT(fromX != -1 && toX != -1);
    370 
    371     if (fromX < toX)
    372         return FloatRect(point.x() + fromX, point.y(), toX - fromX, height);
    373 
    374     return FloatRect(point.x() + toX, point.y(), fromX - toX, height);
    375 }
    376 
    377 } // namespace WebCore
    378