Home | History | Annotate | Download | only in chromium
      1 /*
      2  * Copyright (C) 2006, 2007 Apple Computer, Inc.
      3  * Copyright (c) 2006, 2007, 2008, 2009, 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 are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include "config.h"
     33 #include "Font.h"
     34 
     35 #include "ChromiumBridge.h"
     36 #include "FontFallbackList.h"
     37 #include "GlyphBuffer.h"
     38 #include "PlatformContextSkia.h"
     39 #include "SimpleFontData.h"
     40 #include "SkiaFontWin.h"
     41 #include "SkiaUtils.h"
     42 #include "TransparencyWin.h"
     43 #include "UniscribeHelperTextRun.h"
     44 
     45 #include "skia/ext/platform_canvas_win.h"
     46 #include "skia/ext/skia_utils_win.h"  // FIXME: remove this dependency.
     47 
     48 #include <windows.h>
     49 
     50 namespace WebCore {
     51 
     52 namespace {
     53 
     54 bool canvasHasMultipleLayers(const SkCanvas* canvas)
     55 {
     56     SkCanvas::LayerIter iter(const_cast<SkCanvas*>(canvas), false);
     57     iter.next();  // There is always at least one layer.
     58     return !iter.done();  // There is > 1 layer if the the iterator can stil advance.
     59 }
     60 
     61 class TransparencyAwareFontPainter {
     62 public:
     63     TransparencyAwareFontPainter(GraphicsContext*, const FloatPoint&);
     64     ~TransparencyAwareFontPainter();
     65 
     66 protected:
     67     // Called by our subclass' constructor to initialize GDI if necessary. This
     68     // is a separate function so it can be called after the subclass finishes
     69     // construction (it calls virtual functions).
     70     void init();
     71 
     72     virtual IntRect estimateTextBounds() = 0;
     73 
     74     // Use the context from the transparency helper when drawing with GDI. It
     75     // may point to a temporary one.
     76     GraphicsContext* m_graphicsContext;
     77     PlatformGraphicsContext* m_platformContext;
     78 
     79     FloatPoint m_point;
     80 
     81     // Set when Windows can handle the type of drawing we're doing.
     82     bool m_useGDI;
     83 
     84     // These members are valid only when m_useGDI is set.
     85     HDC m_hdc;
     86     TransparencyWin m_transparency;
     87 
     88 private:
     89     // Call when we're using GDI mode to initialize the TransparencyWin to help
     90     // us draw GDI text.
     91     void initializeForGDI();
     92 
     93     bool m_createdTransparencyLayer;  // We created a layer to give the font some alpha.
     94 };
     95 
     96 TransparencyAwareFontPainter::TransparencyAwareFontPainter(GraphicsContext* context,
     97                                                            const FloatPoint& point)
     98     : m_graphicsContext(context)
     99     , m_platformContext(context->platformContext())
    100     , m_point(point)
    101     , m_useGDI(windowsCanHandleTextDrawing(context))
    102     , m_hdc(0)
    103     , m_createdTransparencyLayer(false)
    104 {
    105 }
    106 
    107 void TransparencyAwareFontPainter::init()
    108 {
    109     if (m_useGDI)
    110         initializeForGDI();
    111 }
    112 
    113 void TransparencyAwareFontPainter::initializeForGDI()
    114 {
    115     m_graphicsContext->save();
    116     SkColor color = m_platformContext->effectiveFillColor();
    117     // Used only when m_createdTransparencyLayer is true.
    118     float layerAlpha = 0.0f;
    119     if (SkColorGetA(color) != 0xFF) {
    120         // When the font has some transparency, apply it by creating a new
    121         // transparency layer with that opacity applied. We'll actually create
    122         // a new transparency layer after we calculate the bounding box.
    123         m_createdTransparencyLayer = true;
    124         layerAlpha = SkColorGetA(color) / 255.0f;
    125         // The color should be opaque now.
    126         color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
    127     }
    128 
    129     TransparencyWin::LayerMode layerMode;
    130     IntRect layerRect;
    131     if (m_platformContext->isDrawingToImageBuffer()) {
    132         // Assume if we're drawing to an image buffer that the background
    133         // is not opaque and we have to undo ClearType. We may want to
    134         // enhance this to actually check, since it will often be opaque
    135         // and we could do ClearType in that case.
    136         layerMode = TransparencyWin::TextComposite;
    137         layerRect = estimateTextBounds();
    138         m_graphicsContext->clip(layerRect);
    139         if (m_createdTransparencyLayer)
    140             m_graphicsContext->beginTransparencyLayer(layerAlpha);
    141 
    142         // The transparency helper requires that we draw text in black in
    143         // this mode and it will apply the color.
    144         m_transparency.setTextCompositeColor(color);
    145         color = SkColorSetRGB(0, 0, 0);
    146     } else if (m_createdTransparencyLayer || canvasHasMultipleLayers(m_platformContext->canvas())) {
    147         // When we're drawing a web page, we know the background is opaque,
    148         // but if we're drawing to a layer, we still need extra work.
    149         layerMode = TransparencyWin::OpaqueCompositeLayer;
    150         layerRect = estimateTextBounds();
    151         m_graphicsContext->clip(layerRect);
    152         if (m_createdTransparencyLayer)
    153             m_graphicsContext->beginTransparencyLayer(layerAlpha);
    154     } else {
    155         // Common case of drawing onto the bottom layer of a web page: we
    156         // know everything is opaque so don't need to do anything special.
    157         layerMode = TransparencyWin::NoLayer;
    158     }
    159 
    160     // Bug 26088 - init() might fail if layerRect is invalid. Given this, we
    161     // need to be careful to check for null pointers everywhere after this call
    162     m_transparency.init(m_graphicsContext, layerMode,
    163                         TransparencyWin::KeepTransform, layerRect);
    164 
    165     // Set up the DC, using the one from the transparency helper.
    166     if (m_transparency.platformContext()) {
    167         m_hdc = m_transparency.platformContext()->canvas()->beginPlatformPaint();
    168         SetTextColor(m_hdc, skia::SkColorToCOLORREF(color));
    169         SetBkMode(m_hdc, TRANSPARENT);
    170     }
    171 }
    172 
    173 TransparencyAwareFontPainter::~TransparencyAwareFontPainter()
    174 {
    175     if (!m_useGDI || !m_graphicsContext || !m_platformContext)
    176         return;  // Nothing to do.
    177     m_transparency.composite();
    178     if (m_createdTransparencyLayer)
    179         m_graphicsContext->endTransparencyLayer();
    180     m_graphicsContext->restore();
    181     m_platformContext->canvas()->endPlatformPaint();
    182 }
    183 
    184 // Specialization for simple GlyphBuffer painting.
    185 class TransparencyAwareGlyphPainter : public TransparencyAwareFontPainter {
    186  public:
    187     TransparencyAwareGlyphPainter(GraphicsContext*,
    188                                   const SimpleFontData*,
    189                                   const GlyphBuffer&,
    190                                   int from, int numGlyphs,
    191                                   const FloatPoint&);
    192     ~TransparencyAwareGlyphPainter();
    193 
    194     // Draws the partial string of glyphs, starting at |startAdvance| to the
    195     // left of m_point. We express it this way so that if we're using the Skia
    196     // drawing path we can use floating-point positioning, even though we have
    197     // to use integer positioning in the GDI path.
    198     bool drawGlyphs(int numGlyphs, const WORD* glyphs, const int* advances, int startAdvance) const;
    199 
    200  private:
    201     virtual IntRect estimateTextBounds();
    202 
    203     const SimpleFontData* m_font;
    204     const GlyphBuffer& m_glyphBuffer;
    205     int m_from;
    206     int m_numGlyphs;
    207 
    208     // When m_useGdi is set, this stores the previous HFONT selected into the
    209     // m_hdc so we can restore it.
    210     HGDIOBJ m_oldFont;  // For restoring the DC to its original state.
    211 };
    212 
    213 TransparencyAwareGlyphPainter::TransparencyAwareGlyphPainter(
    214     GraphicsContext* context,
    215     const SimpleFontData* font,
    216     const GlyphBuffer& glyphBuffer,
    217     int from, int numGlyphs,
    218     const FloatPoint& point)
    219     : TransparencyAwareFontPainter(context, point)
    220     , m_font(font)
    221     , m_glyphBuffer(glyphBuffer)
    222     , m_from(from)
    223     , m_numGlyphs(numGlyphs)
    224     , m_oldFont(0)
    225 {
    226     init();
    227 
    228     if (m_hdc)
    229         m_oldFont = ::SelectObject(m_hdc, m_font->platformData().hfont());
    230 }
    231 
    232 TransparencyAwareGlyphPainter::~TransparencyAwareGlyphPainter()
    233 {
    234     if (m_useGDI && m_hdc)
    235         ::SelectObject(m_hdc, m_oldFont);
    236 }
    237 
    238 
    239 // Estimates the bounding box of the given text. This is copied from
    240 // FontCGWin.cpp, it is possible, but a lot more work, to get the precide
    241 // bounds.
    242 IntRect TransparencyAwareGlyphPainter::estimateTextBounds()
    243 {
    244     int totalWidth = 0;
    245     for (int i = 0; i < m_numGlyphs; i++)
    246         totalWidth += lroundf(m_glyphBuffer.advanceAt(m_from + i));
    247 
    248     return IntRect(m_point.x() - (m_font->ascent() + m_font->descent()) / 2,
    249                    m_point.y() - m_font->ascent() - m_font->lineGap(),
    250                    totalWidth + m_font->ascent() + m_font->descent(),
    251                    m_font->lineSpacing());
    252 }
    253 
    254 bool TransparencyAwareGlyphPainter::drawGlyphs(int numGlyphs,
    255                                                const WORD* glyphs,
    256                                                const int* advances,
    257                                                int startAdvance) const
    258 {
    259     if (!m_useGDI) {
    260         SkPoint origin = m_point;
    261         origin.fX += startAdvance;
    262         return paintSkiaText(m_graphicsContext, m_font->platformData().hfont(),
    263                              numGlyphs, glyphs, advances, 0, &origin);
    264     }
    265 
    266     if (!m_graphicsContext || !m_hdc)
    267         return true;
    268 
    269     // Windows' origin is the top-left of the bounding box, so we have
    270     // to subtract off the font ascent to get it.
    271     int x = lroundf(m_point.x() + startAdvance);
    272     int y = lroundf(m_point.y() - m_font->ascent());
    273 
    274     // If there is a non-blur shadow and both the fill color and shadow color
    275     // are opaque, handle without skia.
    276     IntSize shadowSize;
    277     int shadowBlur;
    278     Color shadowColor;
    279     if (m_graphicsContext->getShadow(shadowSize, shadowBlur, shadowColor)) {
    280         // If there is a shadow and this code is reached, windowsCanHandleDrawTextShadow()
    281         // will have already returned true during the ctor initiatization of m_useGDI
    282         ASSERT(shadowColor.alpha() == 255);
    283         ASSERT(m_graphicsContext->fillColor().alpha() == 255);
    284         ASSERT(shadowBlur == 0);
    285         COLORREF textColor = skia::SkColorToCOLORREF(SkColorSetARGB(255, shadowColor.red(), shadowColor.green(), shadowColor.blue()));
    286         COLORREF savedTextColor = GetTextColor(m_hdc);
    287         SetTextColor(m_hdc, textColor);
    288         ExtTextOut(m_hdc, x + shadowSize.width(), y + shadowSize.height(), ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]);
    289         SetTextColor(m_hdc, savedTextColor);
    290     }
    291 
    292     return !!ExtTextOut(m_hdc, x, y, ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]);
    293 }
    294 
    295 class TransparencyAwareUniscribePainter : public TransparencyAwareFontPainter {
    296  public:
    297     TransparencyAwareUniscribePainter(GraphicsContext*,
    298                                       const Font*,
    299                                       const TextRun&,
    300                                       int from, int to,
    301                                       const FloatPoint&);
    302     ~TransparencyAwareUniscribePainter();
    303 
    304     // Uniscibe will draw directly into our buffer, so we need to expose our DC.
    305     HDC hdc() const { return m_hdc; }
    306 
    307  private:
    308     virtual IntRect estimateTextBounds();
    309 
    310     const Font* m_font;
    311     const TextRun& m_run;
    312     int m_from;
    313     int m_to;
    314 };
    315 
    316 TransparencyAwareUniscribePainter::TransparencyAwareUniscribePainter(
    317     GraphicsContext* context,
    318     const Font* font,
    319     const TextRun& run,
    320     int from, int to,
    321     const FloatPoint& point)
    322     : TransparencyAwareFontPainter(context, point)
    323     , m_font(font)
    324     , m_run(run)
    325     , m_from(from)
    326     , m_to(to)
    327 {
    328     init();
    329 }
    330 
    331 TransparencyAwareUniscribePainter::~TransparencyAwareUniscribePainter()
    332 {
    333 }
    334 
    335 IntRect TransparencyAwareUniscribePainter::estimateTextBounds()
    336 {
    337     // This case really really sucks. There is no convenient way to estimate
    338     // the bounding box. So we run Uniscribe twice. If we find this happens a
    339     // lot, the way to fix it is to make the extra layer after the
    340     // UniscribeHelper has measured the text.
    341     IntPoint intPoint(lroundf(m_point.x()),
    342                       lroundf(m_point.y()));
    343 
    344     UniscribeHelperTextRun state(m_run, *m_font);
    345     int left = lroundf(m_point.x()) + state.characterToX(m_from);
    346     int right = lroundf(m_point.x()) + state.characterToX(m_to);
    347 
    348     // Adjust for RTL script since we just want to know the text bounds.
    349     if (left > right)
    350         std::swap(left, right);
    351 
    352     // This algorithm for estimating how much extra space we need (the text may
    353     // go outside the selection rect) is based roughly on
    354     // TransparencyAwareGlyphPainter::estimateTextBounds above.
    355     return IntRect(left - (m_font->ascent() + m_font->descent()) / 2,
    356                    m_point.y() - m_font->ascent() - m_font->lineGap(),
    357                    (right - left) + m_font->ascent() + m_font->descent(),
    358                    m_font->lineSpacing());
    359 }
    360 
    361 }  // namespace
    362 
    363 bool Font::canReturnFallbackFontsForComplexText()
    364 {
    365     return false;
    366 }
    367 
    368 void Font::drawGlyphs(GraphicsContext* graphicsContext,
    369                       const SimpleFontData* font,
    370                       const GlyphBuffer& glyphBuffer,
    371                       int from,
    372                       int numGlyphs,
    373                       const FloatPoint& point) const
    374 {
    375     SkColor color = graphicsContext->platformContext()->effectiveFillColor();
    376     unsigned char alpha = SkColorGetA(color);
    377     // Skip 100% transparent text; no need to draw anything.
    378     if (!alpha && graphicsContext->platformContext()->getStrokeStyle() == NoStroke)
    379         return;
    380 
    381     TransparencyAwareGlyphPainter painter(graphicsContext, font, glyphBuffer, from, numGlyphs, point);
    382 
    383     // We draw the glyphs in chunks to avoid having to do a heap allocation for
    384     // the arrays of characters and advances. Since ExtTextOut is the
    385     // lowest-level text output function on Windows, there should be little
    386     // penalty for splitting up the text. On the other hand, the buffer cannot
    387     // be bigger than 4094 or the function will fail.
    388     const int kMaxBufferLength = 256;
    389     Vector<WORD, kMaxBufferLength> glyphs;
    390     Vector<int, kMaxBufferLength> advances;
    391     int glyphIndex = 0;  // The starting glyph of the current chunk.
    392     int curAdvance = 0;  // How far from the left the current chunk is.
    393     while (glyphIndex < numGlyphs) {
    394         // How many chars will be in this chunk?
    395         int curLen = std::min(kMaxBufferLength, numGlyphs - glyphIndex);
    396         glyphs.resize(curLen);
    397         advances.resize(curLen);
    398 
    399         int curWidth = 0;
    400         for (int i = 0; i < curLen; ++i, ++glyphIndex) {
    401             glyphs[i] = glyphBuffer.glyphAt(from + glyphIndex);
    402             advances[i] = static_cast<int>(glyphBuffer.advanceAt(from + glyphIndex));
    403 
    404             // Bug 26088 - very large positive or negative runs can fail to
    405             // render so we clamp the size here. In the specs, negative
    406             // letter-spacing is implementation-defined, so this should be
    407             // fine, and it matches Safari's implementation. The call actually
    408             // seems to crash if kMaxNegativeRun is set to somewhere around
    409             // -32830, so we give ourselves a little breathing room.
    410             const int maxNegativeRun = -32768;
    411             const int maxPositiveRun =  32768;
    412             if ((curWidth + advances[i] < maxNegativeRun) || (curWidth + advances[i] > maxPositiveRun))
    413                 advances[i] = 0;
    414             curWidth += advances[i];
    415         }
    416 
    417         // Actually draw the glyphs (with retry on failure).
    418         bool success = false;
    419         for (int executions = 0; executions < 2; ++executions) {
    420             success = painter.drawGlyphs(curLen, &glyphs[0], &advances[0], curAdvance);
    421             if (!success && executions == 0) {
    422                 // Ask the browser to load the font for us and retry.
    423                 ChromiumBridge::ensureFontLoaded(font->platformData().hfont());
    424                 continue;
    425             }
    426             break;
    427         }
    428 
    429         if (!success)
    430             LOG_ERROR("Unable to draw the glyphs after second attempt");
    431 
    432         curAdvance += curWidth;
    433     }
    434 }
    435 
    436 FloatRect Font::selectionRectForComplexText(const TextRun& run,
    437                                             const IntPoint& point,
    438                                             int h,
    439                                             int from,
    440                                             int to) const
    441 {
    442     UniscribeHelperTextRun state(run, *this);
    443     float left = static_cast<float>(point.x() + state.characterToX(from));
    444     float right = static_cast<float>(point.x() + state.characterToX(to));
    445 
    446     // If the text is RTL, left will actually be after right.
    447     if (left < right)
    448         return FloatRect(left, static_cast<float>(point.y()),
    449                        right - left, static_cast<float>(h));
    450 
    451     return FloatRect(right, static_cast<float>(point.y()),
    452                      left - right, static_cast<float>(h));
    453 }
    454 
    455 void Font::drawComplexText(GraphicsContext* graphicsContext,
    456                            const TextRun& run,
    457                            const FloatPoint& point,
    458                            int from,
    459                            int to) const
    460 {
    461     PlatformGraphicsContext* context = graphicsContext->platformContext();
    462     UniscribeHelperTextRun state(run, *this);
    463 
    464     SkColor color = graphicsContext->platformContext()->effectiveFillColor();
    465     unsigned char alpha = SkColorGetA(color);
    466     // Skip 100% transparent text; no need to draw anything.
    467     if (!alpha && graphicsContext->platformContext()->getStrokeStyle() == NoStroke)
    468         return;
    469 
    470     TransparencyAwareUniscribePainter painter(graphicsContext, this, run, from, to, point);
    471 
    472     HDC hdc = painter.hdc();
    473     if (windowsCanHandleTextDrawing(graphicsContext) && !hdc)
    474         return;
    475 
    476     // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency.
    477     // Enforce non-transparent color.
    478     color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
    479     if (hdc) {
    480         SetTextColor(hdc, skia::SkColorToCOLORREF(color));
    481         SetBkMode(hdc, TRANSPARENT);
    482     }
    483 
    484     // If there is a non-blur shadow and both the fill color and shadow color
    485     // are opaque, handle without skia.
    486     IntSize shadowSize;
    487     int shadowBlur;
    488     Color shadowColor;
    489     if (graphicsContext->getShadow(shadowSize, shadowBlur, shadowColor) && windowsCanHandleDrawTextShadow(graphicsContext)) {
    490         COLORREF textColor = skia::SkColorToCOLORREF(SkColorSetARGB(255, shadowColor.red(), shadowColor.green(), shadowColor.blue()));
    491         COLORREF savedTextColor = GetTextColor(hdc);
    492         SetTextColor(hdc, textColor);
    493         state.draw(graphicsContext, hdc, static_cast<int>(point.x()) + shadowSize.width(),
    494                    static_cast<int>(point.y() - ascent()) + shadowSize.height(), from, to);
    495         SetTextColor(hdc, savedTextColor);
    496     }
    497 
    498     // Uniscribe counts the coordinates from the upper left, while WebKit uses
    499     // the baseline, so we have to subtract off the ascent.
    500     state.draw(graphicsContext, hdc, static_cast<int>(point.x()),
    501                static_cast<int>(point.y() - ascent()), from, to);
    502 
    503     context->canvas()->endPlatformPaint();
    504 }
    505 
    506 float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* /* fallbackFonts */) const
    507 {
    508     UniscribeHelperTextRun state(run, *this);
    509     return static_cast<float>(state.width());
    510 }
    511 
    512 int Font::offsetForPositionForComplexText(const TextRun& run, int x,
    513                                           bool includePartialGlyphs) const
    514 {
    515     // Mac code ignores includePartialGlyphs, and they don't know what it's
    516     // supposed to do, so we just ignore it as well.
    517     UniscribeHelperTextRun state(run, *this);
    518     int charIndex = state.xToCharacter(x);
    519 
    520     // XToCharacter will return -1 if the position is before the first
    521     // character (we get called like this sometimes).
    522     if (charIndex < 0)
    523         charIndex = 0;
    524     return charIndex;
    525 }
    526 
    527 } // namespace WebCore
    528