FontChromiumWin.cpp [plain text]
#include "config.h"
#include "Font.h"
#include "ChromiumBridge.h"
#include "FontFallbackList.h"
#include "GlyphBuffer.h"
#include "PlatformContextSkia.h"
#include "SimpleFontData.h"
#include "SkiaFontWin.h"
#include "SkiaUtils.h"
#include "TransparencyWin.h"
#include "UniscribeHelperTextRun.h"
#include "skia/ext/platform_canvas_win.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 = m_transparency.platformContext()->canvas()->beginPlatformPaint();
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();
m_platformContext->canvas()->endPlatformPaint();
}
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, int 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));
return IntRect(m_point.x() - (m_font->ascent() + m_font->descent()) / 2,
m_point.y() - m_font->ascent() - m_font->lineGap(),
totalWidth + m_font->ascent() + m_font->descent(),
m_font->lineSpacing());
}
bool TransparencyAwareGlyphPainter::drawGlyphs(int numGlyphs,
const WORD* glyphs,
const int* advances,
int startAdvance) const
{
if (!m_useGDI) {
SkPoint origin = m_point;
origin.fX += 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->ascent());
IntSize shadowSize;
int shadowBlur;
Color shadowColor;
if (m_graphicsContext->getShadow(shadowSize, shadowBlur, shadowColor)) {
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 + shadowSize.width(), y + shadowSize.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);
return IntRect(left - (m_font->ascent() + m_font->descent()) / 2,
m_point.y() - m_font->ascent() - m_font->lineGap(),
(right - left) + m_font->ascent() + m_font->descent(),
m_font->lineSpacing());
}
}
bool Font::canReturnFallbackFontsForComplexText()
{
return false;
}
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)
return;
TransparencyAwareGlyphPainter painter(graphicsContext, font, glyphBuffer, from, numGlyphs, point);
const int kMaxBufferLength = 256;
Vector<WORD, kMaxBufferLength> glyphs;
Vector<int, kMaxBufferLength> advances;
int glyphIndex = 0; int curAdvance = 0; while (glyphIndex < numGlyphs) {
int curLen = std::min(kMaxBufferLength, numGlyphs - glyphIndex);
glyphs.resize(curLen);
advances.resize(curLen);
int curWidth = 0;
for (int i = 0; i < curLen; ++i, ++glyphIndex) {
glyphs[i] = glyphBuffer.glyphAt(from + glyphIndex);
advances[i] = static_cast<int>(glyphBuffer.advanceAt(from + glyphIndex));
const int maxNegativeRun = -32768;
const int maxPositiveRun = 32768;
if ((curWidth + advances[i] < maxNegativeRun) || (curWidth + advances[i] > maxPositiveRun))
advances[i] = 0;
curWidth += advances[i];
}
bool success = false;
for (int executions = 0; executions < 2; ++executions) {
success = painter.drawGlyphs(curLen, &glyphs[0], &advances[0], curAdvance);
if (!success && executions == 0) {
ChromiumBridge::ensureFontLoaded(font->platformData().hfont());
continue;
}
break;
}
if (!success)
LOG_ERROR("Unable to draw the glyphs after second attempt");
curAdvance += curWidth;
}
}
FloatRect Font::selectionRectForComplexText(const TextRun& run,
const IntPoint& 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, static_cast<float>(point.y()),
right - left, static_cast<float>(h));
return FloatRect(right, static_cast<float>(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);
}
IntSize shadowSize;
int shadowBlur;
Color shadowColor;
if (graphicsContext->getShadow(shadowSize, shadowBlur, shadowColor) && 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()) + shadowSize.width(),
static_cast<int>(point.y() - ascent()) + shadowSize.height(), from, to);
SetTextColor(hdc, savedTextColor);
}
state.draw(graphicsContext, hdc, static_cast<int>(point.x()),
static_cast<int>(point.y() - ascent()), from, to);
context->canvas()->endPlatformPaint();
}
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, int x,
bool includePartialGlyphs) const
{
UniscribeHelperTextRun state(run, *this);
int charIndex = state.xToCharacter(x);
if (charIndex < 0)
charIndex = 0;
return charIndex;
}
}