#include "config.h"
#include "Font.h"
#include "ComplexTextControllerLinux.h"
#include "FloatRect.h"
#include "GlyphBuffer.h"
#include "GraphicsContext.h"
#include "HarfbuzzSkia.h"
#include "NotImplemented.h"
#include "PlatformContextSkia.h"
#include "SimpleFontData.h"
#include "SkCanvas.h"
#include "SkPaint.h"
#include "SkTemplates.h"
#include "SkTypeface.h"
#include "SkUtils.h"
#include <wtf/unicode/Unicode.h>
namespace WebCore {
bool Font::canReturnFallbackFontsForComplexText()
{
return false;
}
bool Font::canExpandAroundIdeographsInComplexText()
{
return false;
}
static bool isCanvasMultiLayered(SkCanvas* canvas)
{
SkCanvas::LayerIter layerIterator(canvas, false);
layerIterator.next();
return !layerIterator.done();
}
static void adjustTextRenderMode(SkPaint* paint, PlatformContextSkia* skiaContext)
{
if (isCanvasMultiLayered(skiaContext->canvas()) || skiaContext->isDrawingToImageBuffer())
paint->setLCDRenderText(false);
}
void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font,
const GlyphBuffer& glyphBuffer, int from, int numGlyphs,
const FloatPoint& point) const {
SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t));
const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from);
SkScalar x = SkFloatToScalar(point.x());
SkScalar y = SkFloatToScalar(point.y());
const GlyphBufferAdvance* adv = glyphBuffer.advances(from);
SkAutoSTMalloc<32, SkPoint> storage(numGlyphs), storage2(numGlyphs), storage3(numGlyphs);
SkPoint* pos = storage.get();
SkPoint* vPosBegin = storage2.get();
SkPoint* vPosEnd = storage3.get();
bool isVertical = font->platformData().orientation() == Vertical;
for (int i = 0; i < numGlyphs; i++) {
SkScalar myWidth = SkFloatToScalar(adv[i].width());
pos[i].set(x, y);
if (isVertical) {
vPosBegin[i].set(x + myWidth, y);
vPosEnd[i].set(x + myWidth, y - myWidth);
}
x += myWidth;
y += SkFloatToScalar(adv[i].height());
}
gc->platformContext()->prepareForSoftwareDraw();
SkCanvas* canvas = gc->platformContext()->canvas();
TextDrawingModeFlags textMode = gc->platformContext()->getTextDrawingMode();
if (textMode & TextModeFill) {
SkPaint paint;
gc->platformContext()->setupPaintForFilling(&paint);
font->platformData().setupPaint(&paint);
adjustTextRenderMode(&paint, gc->platformContext());
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
paint.setColor(gc->fillColor().rgb());
if (isVertical) {
SkPath path;
for (int i = 0; i < numGlyphs; ++i) {
path.reset();
path.moveTo(vPosBegin[i]);
path.lineTo(vPosEnd[i]);
canvas->drawTextOnPath(glyphs + i, 2, path, 0, paint);
}
} else
canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint);
}
if ((textMode & TextModeStroke)
&& gc->platformContext()->getStrokeStyle() != NoStroke
&& gc->platformContext()->getStrokeThickness() > 0) {
SkPaint paint;
gc->platformContext()->setupPaintForStroking(&paint, 0, 0);
font->platformData().setupPaint(&paint);
adjustTextRenderMode(&paint, gc->platformContext());
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
paint.setColor(gc->strokeColor().rgb());
if (textMode & TextModeFill) {
SkSafeUnref(paint.setLooper(0));
}
if (isVertical) {
SkPath path;
for (int i = 0; i < numGlyphs; ++i) {
path.reset();
path.moveTo(vPosBegin[i]);
path.lineTo(vPosEnd[i]);
canvas->drawTextOnPath(glyphs + i, 2, path, 0, paint);
}
} else
canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint);
}
}
static int truncateFixedPointToInteger(HB_Fixed value)
{
return value >> 6;
}
static void setupForTextPainting(SkPaint* paint, SkColor color)
{
paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
paint->setColor(color);
}
void Font::drawComplexText(GraphicsContext* gc, const TextRun& run,
const FloatPoint& point, int from, int to) const
{
if (!run.length())
return;
SkCanvas* canvas = gc->platformContext()->canvas();
TextDrawingModeFlags textMode = gc->platformContext()->getTextDrawingMode();
bool fill = textMode & TextModeFill;
bool stroke = (textMode & TextModeStroke)
&& gc->platformContext()->getStrokeStyle() != NoStroke
&& gc->platformContext()->getStrokeThickness() > 0;
if (!fill && !stroke)
return;
SkPaint strokePaint, fillPaint;
if (fill) {
gc->platformContext()->setupPaintForFilling(&fillPaint);
setupForTextPainting(&fillPaint, gc->fillColor().rgb());
}
if (stroke) {
gc->platformContext()->setupPaintForStroking(&strokePaint, 0, 0);
setupForTextPainting(&strokePaint, gc->strokeColor().rgb());
}
ComplexTextController controller(run, point.x(), point.y(), this);
controller.setWordSpacingAdjustment(wordSpacing());
controller.setLetterSpacingAdjustment(letterSpacing());
controller.setPadding(run.expansion());
if (run.rtl()) {
controller.reset(point.x() + controller.widthOfFullRun());
controller.setPadding(run.expansion());
}
while (controller.nextScriptRun()) {
if (fill) {
controller.fontPlatformDataForScriptRun()->setupPaint(&fillPaint);
adjustTextRenderMode(&fillPaint, gc->platformContext());
canvas->drawPosText(controller.glyphs(), controller.length() << 1, controller.positions(), fillPaint);
}
if (stroke) {
controller.fontPlatformDataForScriptRun()->setupPaint(&strokePaint);
adjustTextRenderMode(&strokePaint, gc->platformContext());
canvas->drawPosText(controller.glyphs(), controller.length() << 1, controller.positions(), strokePaint);
}
}
}
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
{
ComplexTextController controller(run, 0, 0, this);
controller.setWordSpacingAdjustment(wordSpacing());
controller.setLetterSpacingAdjustment(letterSpacing());
controller.setPadding(run.expansion());
return controller.widthOfFullRun();
}
static int glyphIndexForXPositionInScriptRun(const ComplexTextController& controller, int targetX)
{
int lastX = controller.offsetX() - (controller.rtl() ? -controller.width() : controller.width());
for (int glyphIndex = 0; static_cast<unsigned>(glyphIndex) < controller.length(); ++glyphIndex) {
int advance = truncateFixedPointToInteger(controller.advances()[glyphIndex]);
int nextX = static_cast<int>(controller.positions()[glyphIndex].x()) + advance / 2;
if (std::min(nextX, lastX) <= targetX && targetX <= std::max(nextX, lastX))
return glyphIndex;
lastX = nextX;
}
return controller.length() - 1;
}
int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat,
bool includePartialGlyphs) const
{
int targetX = static_cast<int>(xFloat);
ComplexTextController controller(run, 0, 0, this);
controller.setWordSpacingAdjustment(wordSpacing());
controller.setLetterSpacingAdjustment(letterSpacing());
controller.setPadding(run.expansion());
if (run.rtl()) {
controller.reset(controller.widthOfFullRun());
controller.setPadding(run.expansion());
}
unsigned basePosition = 0;
int x = controller.offsetX();
while (controller.nextScriptRun()) {
int nextX = controller.offsetX();
if (std::min(x, nextX) <= targetX && targetX <= std::max(x, nextX)) {
const int glyphIndex = glyphIndexForXPositionInScriptRun(controller, targetX);
const unsigned short* log = controller.logClusters();
for (unsigned j = 0; j < controller.numCodePoints(); ++j) {
if (log[j] >= glyphIndex)
return basePosition + j;
}
return basePosition + controller.numCodePoints() - 1;
}
basePosition += controller.numCodePoints();
}
return basePosition;
}
FloatRect Font::selectionRectForComplexText(const TextRun& run,
const FloatPoint& point, int height,
int from, int to) const
{
int fromX = -1, toX = -1;
ComplexTextController controller(run, 0, 0, this);
controller.setWordSpacingAdjustment(wordSpacing());
controller.setLetterSpacingAdjustment(letterSpacing());
controller.setPadding(run.expansion());
if (run.rtl()) {
controller.reset(controller.widthOfFullRun());
controller.setPadding(run.expansion());
}
while (controller.nextScriptRun() && (fromX == -1 || toX == -1)) {
if (fromX == -1 && from >= 0 && static_cast<unsigned>(from) < controller.numCodePoints()) {
int glyph = controller.logClusters()[from];
fromX = controller.positions()[glyph].x();
if (controller.rtl())
fromX += truncateFixedPointToInteger(controller.advances()[glyph]);
} else
from -= controller.numCodePoints();
if (toX == -1 && to >= 0 && static_cast<unsigned>(to) < controller.numCodePoints()) {
int glyph = controller.logClusters()[to];
toX = controller.positions()[glyph].x();
if (controller.rtl())
toX += truncateFixedPointToInteger(controller.advances()[glyph]);
} else
to -= controller.numCodePoints();
}
if (fromX == -1)
fromX = controller.offsetX();
if (toX == -1)
toX = controller.offsetX();
ASSERT(fromX != -1 && toX != -1);
if (fromX < toX)
return FloatRect(point.x() + fromX, point.y(), toX - fromX, height);
return FloatRect(point.x() + toX, point.y(), fromX - toX, height);
}
}