FontChromiumWin.cpp [plain text]
#include "config.h"
#include "Font.h"
#include "FontFallbackList.h"
#include "GlyphBuffer.h"
#include "NotImplemented.h"
#include "PlatformBridge.h"
#include "PlatformContextSkia.h"
#include "SimpleFontData.h"
#include "SkiaFontWin.h"
#include "SkiaUtils.h"
#include "TransparencyWin.h"
#include "UniscribeHelperTextRun.h"
#include "skia/ext/platform_canvas.h"
#include "skia/ext/skia_utils_win.h" // FIXME: remove this dependency.
#include <windows.h>
namespace WebCore {
namespace {
bool canvasHasMultipleLayers(const SkCanvas* canvas)
{
SkCanvas::LayerIter iter(const_cast<SkCanvas*>(canvas), false);
iter.next(); return !iter.done(); }
class TransparencyAwareFontPainter {
public:
TransparencyAwareFontPainter(GraphicsContext*, const FloatPoint&);
~TransparencyAwareFontPainter();
protected:
void init();
virtual IntRect estimateTextBounds() = 0;
GraphicsContext* m_graphicsContext;
PlatformGraphicsContext* m_platformContext;
FloatPoint m_point;
bool m_useGDI;
HDC m_hdc;
TransparencyWin m_transparency;
private:
void initializeForGDI();
bool m_createdTransparencyLayer; };
TransparencyAwareFontPainter::TransparencyAwareFontPainter(GraphicsContext* context,
const FloatPoint& point)
: m_graphicsContext(context)
, m_platformContext(context->platformContext())
, m_point(point)
, m_useGDI(windowsCanHandleTextDrawing(context))
, m_hdc(0)
, m_createdTransparencyLayer(false)
{
}
void TransparencyAwareFontPainter::init()
{
if (m_useGDI)
initializeForGDI();
}
void TransparencyAwareFontPainter::initializeForGDI()
{
m_graphicsContext->save();
SkColor color = m_platformContext->effectiveFillColor();
float layerAlpha = 0.0f;
if (SkColorGetA(color) != 0xFF) {
m_createdTransparencyLayer = true;
layerAlpha = SkColorGetA(color) / 255.0f;
color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
}
TransparencyWin::LayerMode layerMode;
IntRect layerRect;
if (m_platformContext->isDrawingToImageBuffer()) {
layerMode = TransparencyWin::TextComposite;
layerRect = estimateTextBounds();
m_graphicsContext->clip(layerRect);
if (m_createdTransparencyLayer)
m_graphicsContext->beginTransparencyLayer(layerAlpha);
m_transparency.setTextCompositeColor(color);
color = SkColorSetRGB(0, 0, 0);
} else if (m_createdTransparencyLayer || canvasHasMultipleLayers(m_platformContext->canvas())) {
layerMode = TransparencyWin::OpaqueCompositeLayer;
layerRect = estimateTextBounds();
m_graphicsContext->clip(layerRect);
if (m_createdTransparencyLayer)
m_graphicsContext->beginTransparencyLayer(layerAlpha);
} else {
layerMode = TransparencyWin::NoLayer;
}
m_transparency.init(m_graphicsContext, layerMode,
TransparencyWin::KeepTransform, layerRect);
if (m_transparency.platformContext()) {
m_hdc = skia::BeginPlatformPaint(m_transparency.platformContext()->canvas());
SetTextColor(m_hdc, skia::SkColorToCOLORREF(color));
SetBkMode(m_hdc, TRANSPARENT);
}
}
TransparencyAwareFontPainter::~TransparencyAwareFontPainter()
{
if (!m_useGDI || !m_graphicsContext || !m_platformContext)
return; m_transparency.composite();
if (m_createdTransparencyLayer)
m_graphicsContext->endTransparencyLayer();
m_graphicsContext->restore();
if (m_transparency.platformContext())
skia::EndPlatformPaint(m_transparency.platformContext()->canvas());
}
class TransparencyAwareGlyphPainter : public TransparencyAwareFontPainter {
public:
TransparencyAwareGlyphPainter(GraphicsContext*,
const SimpleFontData*,
const GlyphBuffer&,
int from, int numGlyphs,
const FloatPoint&);
~TransparencyAwareGlyphPainter();
bool drawGlyphs(int numGlyphs, const WORD* glyphs, const int* advances, float startAdvance) const;
private:
virtual IntRect estimateTextBounds();
const SimpleFontData* m_font;
const GlyphBuffer& m_glyphBuffer;
int m_from;
int m_numGlyphs;
HGDIOBJ m_oldFont; };
TransparencyAwareGlyphPainter::TransparencyAwareGlyphPainter(
GraphicsContext* context,
const SimpleFontData* font,
const GlyphBuffer& glyphBuffer,
int from, int numGlyphs,
const FloatPoint& point)
: TransparencyAwareFontPainter(context, point)
, m_font(font)
, m_glyphBuffer(glyphBuffer)
, m_from(from)
, m_numGlyphs(numGlyphs)
, m_oldFont(0)
{
init();
if (m_hdc)
m_oldFont = ::SelectObject(m_hdc, m_font->platformData().hfont());
}
TransparencyAwareGlyphPainter::~TransparencyAwareGlyphPainter()
{
if (m_useGDI && m_hdc)
::SelectObject(m_hdc, m_oldFont);
}
IntRect TransparencyAwareGlyphPainter::estimateTextBounds()
{
int totalWidth = 0;
for (int i = 0; i < m_numGlyphs; i++)
totalWidth += lroundf(m_glyphBuffer.advanceAt(m_from + i));
const FontMetrics& fontMetrics = m_font->fontMetrics();
return IntRect(m_point.x() - (fontMetrics.ascent() + fontMetrics.descent()) / 2,
m_point.y() - fontMetrics.ascent() - fontMetrics.lineGap(),
totalWidth + fontMetrics.ascent() + fontMetrics.descent(),
fontMetrics.lineSpacing());
}
bool TransparencyAwareGlyphPainter::drawGlyphs(int numGlyphs,
const WORD* glyphs,
const int* advances,
float startAdvance) const
{
if (!m_useGDI) {
SkPoint origin = m_point;
origin.fX += SkFloatToScalar(startAdvance);
return paintSkiaText(m_graphicsContext, m_font->platformData().hfont(),
numGlyphs, glyphs, advances, 0, &origin);
}
if (!m_graphicsContext || !m_hdc)
return true;
int x = lroundf(m_point.x() + startAdvance);
int y = lroundf(m_point.y() - m_font->fontMetrics().ascent());
FloatSize shadowOffset;
float shadowBlur;
Color shadowColor;
ColorSpace shadowColorSpace;
if (m_graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace)) {
ASSERT(shadowColor.alpha() == 255);
ASSERT(m_graphicsContext->fillColor().alpha() == 255);
ASSERT(shadowBlur == 0);
COLORREF textColor = skia::SkColorToCOLORREF(SkColorSetARGB(255, shadowColor.red(), shadowColor.green(), shadowColor.blue()));
COLORREF savedTextColor = GetTextColor(m_hdc);
SetTextColor(m_hdc, textColor);
ExtTextOut(m_hdc, x + shadowOffset.width(), y + shadowOffset.height(), ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]);
SetTextColor(m_hdc, savedTextColor);
}
return !!ExtTextOut(m_hdc, x, y, ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]);
}
class TransparencyAwareUniscribePainter : public TransparencyAwareFontPainter {
public:
TransparencyAwareUniscribePainter(GraphicsContext*,
const Font*,
const TextRun&,
int from, int to,
const FloatPoint&);
~TransparencyAwareUniscribePainter();
HDC hdc() const { return m_hdc; }
private:
virtual IntRect estimateTextBounds();
const Font* m_font;
const TextRun& m_run;
int m_from;
int m_to;
};
TransparencyAwareUniscribePainter::TransparencyAwareUniscribePainter(
GraphicsContext* context,
const Font* font,
const TextRun& run,
int from, int to,
const FloatPoint& point)
: TransparencyAwareFontPainter(context, point)
, m_font(font)
, m_run(run)
, m_from(from)
, m_to(to)
{
init();
}
TransparencyAwareUniscribePainter::~TransparencyAwareUniscribePainter()
{
}
IntRect TransparencyAwareUniscribePainter::estimateTextBounds()
{
IntPoint intPoint(lroundf(m_point.x()),
lroundf(m_point.y()));
UniscribeHelperTextRun state(m_run, *m_font);
int left = lroundf(m_point.x()) + state.characterToX(m_from);
int right = lroundf(m_point.x()) + state.characterToX(m_to);
if (left > right)
std::swap(left, right);
const FontMetrics& fontMetrics = m_font->fontMetrics();
return IntRect(left - (fontMetrics.ascent() + fontMetrics.descent()) / 2,
m_point.y() - fontMetrics.ascent() - fontMetrics.lineGap(),
(right - left) + fontMetrics.ascent() + fontMetrics.descent(),
fontMetrics.lineSpacing());
}
}
bool Font::canReturnFallbackFontsForComplexText()
{
return false;
}
bool Font::canExpandAroundIdeographsInComplexText()
{
return false;
}
static void drawGlyphsWin(GraphicsContext* graphicsContext,
const SimpleFontData* font,
const GlyphBuffer& glyphBuffer,
int from,
int numGlyphs,
const FloatPoint& point) {
graphicsContext->platformContext()->prepareForSoftwareDraw();
TransparencyAwareGlyphPainter painter(graphicsContext, font, glyphBuffer, from, numGlyphs, point);
const int kMaxBufferLength = 256;
Vector<WORD, kMaxBufferLength> glyphs;
Vector<int, kMaxBufferLength> advances;
int glyphIndex = 0;
float horizontalOffset = point.x(); int lastHorizontalOffsetRounded = lroundf(horizontalOffset); while (glyphIndex < numGlyphs) {
int curLen = std::min(kMaxBufferLength, numGlyphs - glyphIndex);
glyphs.resize(curLen);
advances.resize(curLen);
float currentWidth = 0;
for (int i = 0; i < curLen; ++i, ++glyphIndex) {
glyphs[i] = glyphBuffer.glyphAt(from + glyphIndex);
horizontalOffset += glyphBuffer.advanceAt(from + glyphIndex);
advances[i] = lroundf(horizontalOffset) - lastHorizontalOffsetRounded;
lastHorizontalOffsetRounded += advances[i];
currentWidth += glyphBuffer.advanceAt(from + glyphIndex);
const int maxNegativeRun = -32768;
const int maxPositiveRun = 32768;
if ((currentWidth + advances[i] < maxNegativeRun) || (currentWidth + advances[i] > maxPositiveRun))
advances[i] = 0;
}
bool success = false;
for (int executions = 0; executions < 2; ++executions) {
success = painter.drawGlyphs(curLen, &glyphs[0], &advances[0], horizontalOffset - point.x() - currentWidth);
if (!success && executions == 0) {
PlatformBridge::ensureFontLoaded(font->platformData().hfont());
continue;
}
break;
}
if (!success)
LOG_ERROR("Unable to draw the glyphs after second attempt");
}
}
void Font::drawGlyphs(GraphicsContext* graphicsContext,
const SimpleFontData* font,
const GlyphBuffer& glyphBuffer,
int from,
int numGlyphs,
const FloatPoint& point) const
{
SkColor color = graphicsContext->platformContext()->effectiveFillColor();
unsigned char alpha = SkColorGetA(color);
if (!alpha && graphicsContext->platformContext()->getStrokeStyle() == NoStroke && !graphicsContext->hasShadow())
return;
drawGlyphsWin(graphicsContext, font, glyphBuffer, from, numGlyphs, point);
}
FloatRect Font::selectionRectForComplexText(const TextRun& run,
const FloatPoint& point,
int h,
int from,
int to) const
{
UniscribeHelperTextRun state(run, *this);
float left = static_cast<float>(point.x() + state.characterToX(from));
float right = static_cast<float>(point.x() + state.characterToX(to));
if (left < right)
return FloatRect(left, point.y(),
right - left, static_cast<float>(h));
return FloatRect(right, point.y(),
left - right, static_cast<float>(h));
}
void Font::drawComplexText(GraphicsContext* graphicsContext,
const TextRun& run,
const FloatPoint& point,
int from,
int to) const
{
PlatformGraphicsContext* context = graphicsContext->platformContext();
UniscribeHelperTextRun state(run, *this);
SkColor color = graphicsContext->platformContext()->effectiveFillColor();
unsigned char alpha = SkColorGetA(color);
if (!alpha && graphicsContext->platformContext()->getStrokeStyle() == NoStroke)
return;
TransparencyAwareUniscribePainter painter(graphicsContext, this, run, from, to, point);
HDC hdc = painter.hdc();
if (windowsCanHandleTextDrawing(graphicsContext) && !hdc)
return;
color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
if (hdc) {
SetTextColor(hdc, skia::SkColorToCOLORREF(color));
SetBkMode(hdc, TRANSPARENT);
}
FloatSize shadowOffset;
float shadowBlur;
Color shadowColor;
ColorSpace shadowColorSpace;
if (graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace) && windowsCanHandleDrawTextShadow(graphicsContext)) {
COLORREF textColor = skia::SkColorToCOLORREF(SkColorSetARGB(255, shadowColor.red(), shadowColor.green(), shadowColor.blue()));
COLORREF savedTextColor = GetTextColor(hdc);
SetTextColor(hdc, textColor);
state.draw(graphicsContext, hdc, static_cast<int>(point.x()) + shadowOffset.width(),
static_cast<int>(point.y() - fontMetrics().ascent()) + shadowOffset.height(), from, to);
SetTextColor(hdc, savedTextColor);
}
state.draw(graphicsContext, hdc, lroundf(point.x()), lroundf(point.y() - fontMetrics().ascent()), from, to);
}
void Font::drawEmphasisMarksForComplexText(GraphicsContext* , const TextRun& , const AtomicString& , const FloatPoint& , int , int ) const
{
notImplemented();
}
float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* , GlyphOverflow* ) const
{
UniscribeHelperTextRun state(run, *this);
return static_cast<float>(state.width());
}
int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat,
bool includePartialGlyphs) const
{
int x = static_cast<int>(xFloat);
UniscribeHelperTextRun state(run, *this);
int charIndex = state.xToCharacter(x);
if (charIndex < 0)
charIndex = 0;
return charIndex;
}
}