void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { FS_STATE* iType = font->platformData().scaledFont(context->getCTM().yScale()); if (!iType) return; FloatPoint adjustedPoint = point; if (font->platformData().orientation() == Vertical) adjustedPoint.move(-(font->fontMetrics().floatAscent(IdeographicBaseline) - font->fontMetrics().floatAscent()), 0); bool softwareBlurRequired = context->state().shadowBlur / font->fontMetrics().xHeight() > 0.5; FloatSize currentShadowOffset; float currentShadowBlur; Color currentShadowColor; ColorSpace currentColorSpace; // If we have a shadow blur, and it is too big to apply at text-render time, we must render it now. if (context->hasShadow() && softwareBlurRequired) { context->getShadow(currentShadowOffset, currentShadowBlur, currentShadowColor, currentColorSpace); const GraphicsContextState state = context->state(); FloatSize offset = state.shadowOffset; if (state.shadowsIgnoreTransforms) offset.setHeight(-offset.height()); ShadowBlur shadow(FloatSize(state.shadowBlur, state.shadowBlur), offset, state.shadowColor, state.shadowColorSpace); FloatPoint minPoint(adjustedPoint.x(), adjustedPoint.y() - fontMetrics().ascent()); FloatPoint maxPoint(adjustedPoint.x(), adjustedPoint.y() + fontMetrics().descent()); FloatPoint currentPoint = adjustedPoint; for (int i = 0; i < numGlyphs; ++i) { currentPoint += *glyphBuffer.advances(from + i); minPoint.setX(std::min(minPoint.x(), currentPoint.x())); minPoint.setY(std::min(minPoint.y(), currentPoint.y())); maxPoint = maxPoint.expandedTo(currentPoint); } const FloatRect boundingRect(minPoint.x(), minPoint.y(), maxPoint.x() - minPoint.x(), maxPoint.y() - minPoint.y()); GraphicsContext* shadowContext = shadow.beginShadowLayer(context, boundingRect); if (shadowContext) { iType = font->platformData().scaledFont(shadowContext->getCTM().yScale()); shadowContext->platformContext()->addGlyphs(glyphBuffer.glyphs(from), reinterpret_cast<const BlackBerry::Platform::FloatSize*>(glyphBuffer.advances(from)), numGlyphs, adjustedPoint, iType, 0, 0); iType = font->platformData().scaledFont(context->getCTM().yScale()); shadow.endShadowLayer(context); } context->platformContext()->clearShadow(); } context->platformContext()->addGlyphs(glyphBuffer.glyphs(from), reinterpret_cast<const BlackBerry::Platform::FloatSize*>(glyphBuffer.advances(from)), numGlyphs, adjustedPoint, iType, context->fillGradient() ? context->fillGradient()->platformGradient() : (context->fillPattern() ? context->fillPattern()->platformPattern(AffineTransform()) : static_cast<BlackBerry::Platform::Graphics::Paint*>(0)), context->strokeGradient() ? context->strokeGradient()->platformGradient() : (context->strokePattern() ? context->strokePattern()->platformPattern(AffineTransform()) : static_cast<BlackBerry::Platform::Graphics::Paint*>(0))); if (softwareBlurRequired) context->platformContext()->setShadow(currentShadowOffset, currentShadowBlur, currentShadowColor.isValid() ? currentShadowColor.rgb() : makeRGBA(0, 0, 0, 0xFF / 3), context->state().shadowsIgnoreTransforms); }
void Font::drawGlyphs(GraphicsContext* graphicsContext, const FontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { CGContextRef cgContext = graphicsContext->platformContext(); uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext); const FontPlatformData& platformData = font->platformData(); //NSFont* drawFont; //if ([gContext isDrawingToScreen]) { // drawFont = [platformData.font screenFont]; // if (drawFont != platformData.font) // // We are getting this in too many places (3406411); use ERROR so it only prints on debug versions for now. (We should debug this also, eventually). // LOG_ERROR("Attempting to set non-screen font (%@) when drawing to screen. Using screen font anyway, may result in incorrect metrics.", // [[[platformData.font fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]); //} else { // drawFont = [platformData.font printerFont]; // if (drawFont != platformData.font) // NSLog(@"Attempting to set non-printer font (%@) when printing. Using printer font anyway, may result in incorrect metrics.", // [[[platformData.font fontDescriptor] fontAttributes] objectForKey:NSFontNameAttribute]); //} CGContextSetFont(cgContext, platformData.cgFont()); CGAffineTransform matrix = CGAffineTransformIdentity; matrix.b = -matrix.b; matrix.d = -matrix.d; if (platformData.syntheticOblique()) { static float skew = -tanf(syntheticObliqueAngle * acosf(0) / 90); matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, skew, 1, 0, 0)); } // Uniscribe gives us offsets to help refine the positioning of combining glyphs. FloatSize translation = glyphBuffer.offsetAt(from); if (translation.width() || translation.height()) CGAffineTransformTranslate(matrix, translation.width(), translation.height()); CGContextSetTextMatrix(cgContext, matrix); //wkSetCGFontRenderingMode(cgContext, drawFont); CGContextSetFontSize(cgContext, platformData.size()); CGContextSetTextPosition(cgContext, point.x(), point.y()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->m_syntheticBoldOffset) { CGContextSetTextPosition(cgContext, point.x() + font->m_syntheticBoldOffset, point.y()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } wkRestoreFontSmoothingStyle(cgContext, oldFontSmoothingStyle); }
void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* font, const wxColour& color, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) { #if USE(WXGC) wxGCDC* dc = static_cast<wxGCDC*>(graphicsContext->platformContext()); #else wxDC* dc = graphicsContext->platformContext(); #endif wxFont wxfont = font->getWxFont(); if (wxfont.IsOk()) dc->SetFont(wxfont); dc->SetTextForeground(color); // convert glyphs to wxString GlyphBufferGlyph* glyphs = const_cast<GlyphBufferGlyph*>(glyphBuffer.glyphs(from)); int offset = point.x(); wxString text = wxEmptyString; for (unsigned i = 0; i < numGlyphs; i++) { text = text.Append((wxChar)glyphs[i]); offset += glyphBuffer.advanceAt(from + i); } // the y point is actually the bottom point of the text, turn it into the top float height = font->ascent() - font->descent(); wxCoord ypoint = (wxCoord) (point.y() - height); dc->DrawText(text, (wxCoord)point.x(), ypoint); }
void Font::drawGlyphs(GraphicsContext* graphicsContext, const FontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { cairo_t* context = graphicsContext->platformContext(); // Set the text color to use for drawing. float red, green, blue, alpha; Color penColor = graphicsContext->fillColor(); penColor.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(context, red, green, blue, alpha); // This was commented out as it made "some text invisible" but seems to work now. font->setFont(context); GlyphBufferGlyph* glyphs = (GlyphBufferGlyph*) glyphBuffer.glyphs(from); float offset = point.x(); for (int i = 0; i < numGlyphs; i++) { glyphs[i].x = offset; glyphs[i].y = point.y(); offset += glyphBuffer.advanceAt(from + i); } cairo_show_glyphs(context, glyphs, numGlyphs); }
void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* font, const wxColour& color, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) { #if USE(WXGC) wxGCDC* dc = static_cast<wxGCDC*>(graphicsContext->platformContext()); #else wxDC* dc = graphicsContext->platformContext(); #endif wxFont* wxfont = font->getWxFont(); if (wxfont->IsOk()) dc->SetFont(*wxfont); dc->SetTextForeground(color); // convert glyphs to wxString GlyphBufferGlyph* glyphs = const_cast<GlyphBufferGlyph*>(glyphBuffer.glyphs(from)); int offset = point.x(); wxString text = wxEmptyString; for (unsigned i = 0; i < numGlyphs; i++) { text = text.Append((wxChar)glyphs[i]); offset += glyphBuffer.advanceAt(from + i); } // NOTE: The wx API actually adds the ascent to the y value internally, // so we have to subtract it from the y point here so that the ascent // isn't added twice. dc->DrawText(text, (wxCoord)point.x(), int(point.y() - font->ascent())); }
void Font::drawGlyphs(GraphicsContext* graphicsContext, const FontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { cairo_t* context = graphicsContext->platformContext(); // Set the text color to use for drawing. float red, green, blue, alpha; Color penColor = graphicsContext->pen().color(); penColor.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(context, red, green, blue, alpha); // Select the scaled font. font->setFont(context); GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); float offset = point.x(); for (unsigned i = 0; i < numGlyphs; i++) { glyphs[i].x = offset; glyphs[i].y = point.y(); offset += glyphBuffer.advanceAt(from + i); } cairo_show_glyphs(context, glyphs, numGlyphs); }
void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* font, const wxColour& color, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) { graphicsContext->save(); wxGCDC* dc = static_cast<wxGCDC*>(graphicsContext->platformContext()); wxFont* wxfont = font->getWxFont(); graphicsContext->setFillColor(graphicsContext->fillColor(), ColorSpaceDeviceRGB); CGContextRef cgContext = static_cast<CGContextRef>(dc->GetGraphicsContext()->GetNativeContext()); CGFontRef cgFont = font->platformData().cgFont(); CGContextSetFont(cgContext, cgFont); CGContextSetFontSize(cgContext, wxfont->GetPointSize()); CGFloat red, green, blue, alpha; graphicsContext->fillColor().getRGBA(red, green, blue, alpha); CGContextSetRGBFillColor(cgContext, red, green, blue, alpha); CGAffineTransform matrix = CGAffineTransformIdentity; matrix.b = -matrix.b; matrix.d = -matrix.d; CGContextSetTextMatrix(cgContext, matrix); CGContextSetTextPosition(cgContext, point.x(), point.y()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (cgFont) CGFontRelease(cgFont); graphicsContext->restore(); }
void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* font, const wxColour& color, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) { #if USE(WXGC) wxGCDC* dc = static_cast<wxGCDC*>(graphicsContext->platformContext()); #else wxDC* dc = graphicsContext->platformContext(); #endif // get the native HDC handle to draw using native APIs HDC hdc = 0; #if USE(WXGC) wxGraphicsContext* gc = dc->GetGraphicsContext(); Gdiplus::Graphics* g; if (gc) { g = (Gdiplus::Graphics*)gc->GetNativeContext(); hdc = g->GetHDC(); } #else hdc = static_cast<HDC>(dc->GetHDC()); #endif // ExtTextOut wants the offsets as an array of ints, so extract them // from the glyph buffer const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); const GlyphBufferAdvance* advances = glyphBuffer.advances(from); float y = point.y() - font->ascent(); float x = point.x(); int* spacing = new int[numGlyphs - from]; for (unsigned i = 0; i < numGlyphs; ++i) spacing[i] = advances[i].width(); wxFont* wxfont = font->getWxFont(); if (wxfont && wxfont->IsOk()) ::SelectObject(hdc, GetHfontOf(*wxfont)); if (color.Ok()) ::SetTextColor(hdc, color.GetPixel()); // do not draw background behind characters ::SetBkMode(hdc, TRANSPARENT); // draw text with optional character widths array wxString string = wxString((wxChar*)(&glyphs[from]), numGlyphs); ::ExtTextOut(hdc, x, y, 0, NULL, string.c_str(), string.length(), spacing); ::SetBkMode(hdc, TRANSPARENT); #if USE(WXGC) g->ReleaseHDC(hdc); #endif delete [] spacing; }
void TraceGlyphBufferToString(const SimpleFontData* pSimpleFontData, const GlyphBuffer& glyphBuffer, int glyphIndexBegin, int glyphCount, const FloatPoint& point) { eastl::fixed_string<char8_t, 128> sDebug; sDebug.sprintf("[%d,%d] ", (int)point.x(), (int)point.y()); GlyphBufferGlyph* pGlyphArray = const_cast<GlyphBufferGlyph*>(glyphBuffer.glyphs(glyphIndexBegin)); for(int i = glyphIndexBegin; i < glyphIndexBegin + glyphCount; ++i, ++pGlyphArray) sDebug += (char8_t)ConvertGlyphToChar(pSimpleFontData->m_font.mpFont, *pGlyphArray); sDebug += "\n"; OWB_PRINTF("%s", sDebug.c_str()); }
void Font::drawGlyphs(GraphicsContext* graphicsContext, const FontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { QPainter& p = *graphicsContext->platformContext(); Color color = graphicsContext->fillColor(); p.setPen(QColor(color)); p.setFont(font->platformData().font()); const QChar* buffer = reinterpret_cast<const QChar*>(glyphBuffer.glyphs(from)); QString str = QString::fromRawData(buffer, numGlyphs); p.drawText(point, str); }
TEST(GlyphBufferTest, GlyphArrayWithOffset) { RefPtr<SimpleFontData> font1 = TestSimpleFontData::create(); RefPtr<SimpleFontData> font2 = TestSimpleFontData::create(); GlyphBuffer glyphBuffer; glyphBuffer.add(42, font1.get(), 10); glyphBuffer.add(43, font1.get(), 15); glyphBuffer.add(44, font2.get(), 12); EXPECT_FALSE(glyphBuffer.isEmpty()); EXPECT_EQ(3u, glyphBuffer.size()); const Glyph* glyphs = glyphBuffer.glyphs(1); EXPECT_EQ(43, glyphs[0]); EXPECT_EQ(44, glyphs[1]); }
void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { if (!font->platformData().size()) return; GlyphBufferGlyph* glyphs = const_cast<GlyphBufferGlyph*>(glyphBuffer.glyphs(from)); float offset = point.x(); for (int i = 0; i < numGlyphs; i++) { glyphs[i].x = offset; glyphs[i].y = point.y(); offset += glyphBuffer.advanceAt(from + i).width(); } PlatformContextCairo* platformContext = context->platformContext(); drawGlyphsShadow(context, point, font, glyphs, numGlyphs); cairo_t* cr = platformContext->cr(); cairo_save(cr); if (context->textDrawingMode() & TextModeFill) { platformContext->prepareForFilling(context->state(), PlatformContextCairo::AdjustPatternForGlobalAlpha); drawGlyphsToContext(cr, font, glyphs, numGlyphs); } // Prevent running into a long computation within cairo. If the stroke width is // twice the size of the width of the text we will not ask cairo to stroke // the text as even one single stroke would cover the full wdth of the text. // See https://bugs.webkit.org/show_bug.cgi?id=33759. if (context->textDrawingMode() & TextModeStroke && context->strokeThickness() < 2 * offset) { platformContext->prepareForStroking(context->state()); cairo_set_line_width(cr, context->strokeThickness()); // This may disturb the CTM, but we are going to call cairo_restore soon after. cairo_set_scaled_font(cr, font->platformData().scaledFont()); cairo_glyph_path(cr, glyphs, numGlyphs); cairo_stroke(cr); } cairo_restore(cr); }
TEST(GlyphBufferTest, StoresGlyphs) { RefPtr<SimpleFontData> font1 = TestSimpleFontData::create(); RefPtr<SimpleFontData> font2 = TestSimpleFontData::create(); GlyphBuffer glyphBuffer; glyphBuffer.add(42, font1.get(), 10); glyphBuffer.add(43, font1.get(), 15); glyphBuffer.add(44, font2.get(), 22); EXPECT_FALSE(glyphBuffer.isEmpty()); EXPECT_FALSE(glyphBuffer.hasVerticalOffsets()); EXPECT_EQ(3u, glyphBuffer.size()); EXPECT_EQ(42, glyphBuffer.glyphAt(0)); EXPECT_EQ(43, glyphBuffer.glyphAt(1)); EXPECT_EQ(44, glyphBuffer.glyphAt(2)); const Glyph* glyphs = glyphBuffer.glyphs(0); EXPECT_EQ(42, glyphs[0]); EXPECT_EQ(43, glyphs[1]); EXPECT_EQ(44, glyphs[2]); }
void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { CGContextRef cgContext = graphicsContext->platformContext(); bool shouldUseFontSmoothing = WebCoreShouldUseFontSmoothing(); switch(fontDescription().fontSmoothing()) { case Antialiased: { graphicsContext->setShouldAntialias(true); shouldUseFontSmoothing = false; break; } case SubpixelAntialiased: { graphicsContext->setShouldAntialias(true); shouldUseFontSmoothing = true; break; } case NoSmoothing: { graphicsContext->setShouldAntialias(false); shouldUseFontSmoothing = false; break; } case AutoSmoothing: { // For the AutoSmooth case, don't do anything! Keep the default settings. break; } default: ASSERT_NOT_REACHED(); } if (font->platformData().useGDI() && !shouldUseFontSmoothing) { drawGDIGlyphs(graphicsContext, font, glyphBuffer, from, numGlyphs, point); return; } uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext, shouldUseFontSmoothing); const FontPlatformData& platformData = font->platformData(); CGContextSetFont(cgContext, platformData.cgFont()); CGAffineTransform matrix = CGAffineTransformIdentity; matrix.b = -matrix.b; matrix.d = -matrix.d; if (platformData.syntheticOblique()) { static float skew = -tanf(syntheticObliqueAngle * piFloat / 180.0f); matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, skew, 1, 0, 0)); } CGContextSetTextMatrix(cgContext, matrix); // Uniscribe gives us offsets to help refine the positioning of combining glyphs. FloatSize translation = glyphBuffer.offsetAt(from); CGContextSetFontSize(cgContext, platformData.size()); wkSetCGContextFontRenderingStyle(cgContext, font->isSystemFont(), false, font->platformData().useGDI()); FloatSize shadowOffset; float shadowBlur; Color shadowColor; ColorSpace shadowColorSpace; graphicsContext->getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace); bool hasSimpleShadow = graphicsContext->textDrawingMode() == TextModeFill && shadowColor.isValid() && !shadowBlur && (!graphicsContext->shadowsIgnoreTransforms() || graphicsContext->getCTM().isIdentityOrTranslationOrFlipped()); if (hasSimpleShadow) { // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing. graphicsContext->clearShadow(); Color fillColor = graphicsContext->fillColor(); Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); graphicsContext->setFillColor(shadowFillColor, ColorSpaceDeviceRGB); float shadowTextX = point.x() + translation.width() + shadowOffset.width(); // If shadows are ignoring transforms, then we haven't applied the Y coordinate flip yet, so down is negative. float shadowTextY = point.y() + translation.height() + shadowOffset.height() * (graphicsContext->shadowsIgnoreTransforms() ? -1 : 1); CGContextSetTextPosition(cgContext, shadowTextX, shadowTextY); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->syntheticBoldOffset()) { CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowOffset.width() + font->syntheticBoldOffset(), point.y() + translation.height() + shadowOffset.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } graphicsContext->setFillColor(fillColor, ColorSpaceDeviceRGB); } CGContextSetTextPosition(cgContext, point.x() + translation.width(), point.y() + translation.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->syntheticBoldOffset()) { CGContextSetTextPosition(cgContext, point.x() + translation.width() + font->syntheticBoldOffset(), point.y() + translation.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } if (hasSimpleShadow) graphicsContext->setShadow(shadowOffset, shadowBlur, shadowColor, ColorSpaceDeviceRGB); wkRestoreFontSmoothingStyle(cgContext, oldFontSmoothingStyle); }
static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) { Color fillColor = graphicsContext->fillColor(); bool drawIntoBitmap = false; TextDrawingModeFlags drawingMode = graphicsContext->textDrawingMode(); if (drawingMode == TextModeFill) { if (!fillColor.alpha()) return; drawIntoBitmap = fillColor.alpha() != 255 || graphicsContext->inTransparencyLayer(); if (!drawIntoBitmap) { FloatSize offset; float blur; Color color; ColorSpace shadowColorSpace; graphicsContext->getShadow(offset, blur, color, shadowColorSpace); drawIntoBitmap = offset.width() || offset.height() || blur; } } // We have to convert CG's two-dimensional floating point advances to just horizontal integer advances. Vector<int, 2048> gdiAdvances; int totalWidth = 0; for (int i = 0; i < numGlyphs; i++) { gdiAdvances.append(lroundf(glyphBuffer.advanceAt(from + i))); totalWidth += gdiAdvances[i]; } HDC hdc = 0; OwnPtr<GraphicsContext::WindowsBitmap> bitmap; IntRect textRect; if (!drawIntoBitmap) hdc = graphicsContext->getWindowsContext(textRect, true, false); if (!hdc) { drawIntoBitmap = true; // We put slop into this rect, since glyphs can overflow the ascent/descent bounds and the left/right edges. // FIXME: Can get glyphs' optical bounds (even from CG) to get this right. const FontMetrics& fontMetrics = font->fontMetrics(); int lineGap = fontMetrics.lineGap(); textRect = IntRect(point.x() - (fontMetrics.ascent() + fontMetrics.descent()) / 2, point.y() - fontMetrics.ascent() - lineGap, totalWidth + fontMetrics.ascent() + fontMetrics.descent(), fontMetrics.lineSpacing()); bitmap = graphicsContext->createWindowsBitmap(textRect.size()); memset(bitmap->buffer(), 255, bitmap->bufferLength()); hdc = bitmap->hdc(); XFORM xform; xform.eM11 = 1.0f; xform.eM12 = 0.0f; xform.eM21 = 0.0f; xform.eM22 = 1.0f; xform.eDx = -textRect.x(); xform.eDy = -textRect.y(); SetWorldTransform(hdc, &xform); } SelectObject(hdc, font->platformData().hfont()); // Set the correct color. if (drawIntoBitmap) SetTextColor(hdc, RGB(0, 0, 0)); else SetTextColor(hdc, RGB(fillColor.red(), fillColor.green(), fillColor.blue())); SetBkMode(hdc, TRANSPARENT); SetTextAlign(hdc, TA_LEFT | TA_BASELINE); // Uniscribe gives us offsets to help refine the positioning of combining glyphs. FloatSize translation = glyphBuffer.offsetAt(from); if (translation.width() || translation.height()) { XFORM xform; xform.eM11 = 1.0; xform.eM12 = 0; xform.eM21 = 0; xform.eM22 = 1.0; xform.eDx = translation.width(); xform.eDy = translation.height(); ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); } if (drawingMode == TextModeFill) { XFORM xform; xform.eM11 = 1.0; xform.eM12 = 0; xform.eM21 = font->platformData().syntheticOblique() ? -tanf(syntheticObliqueAngle * piFloat / 180.0f) : 0; xform.eM22 = 1.0; xform.eDx = point.x(); xform.eDy = point.y(); ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, reinterpret_cast<const WCHAR*>(glyphBuffer.glyphs(from)), numGlyphs, gdiAdvances.data()); if (font->syntheticBoldOffset()) { xform.eM21 = 0; xform.eDx = font->syntheticBoldOffset(); xform.eDy = 0; ModifyWorldTransform(hdc, &xform, MWT_LEFTMULTIPLY); ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, reinterpret_cast<const WCHAR*>(glyphBuffer.glyphs(from)), numGlyphs, gdiAdvances.data()); } } else { XFORM xform; GetWorldTransform(hdc, &xform); AffineTransform hdcTransform(xform.eM11, xform.eM21, xform.eM12, xform.eM22, xform.eDx, xform.eDy); CGAffineTransform initialGlyphTransform = hdcTransform.isInvertible() ? hdcTransform.inverse() : CGAffineTransformIdentity; if (font->platformData().syntheticOblique()) initialGlyphTransform = CGAffineTransformConcat(initialGlyphTransform, CGAffineTransformMake(1, 0, tanf(syntheticObliqueAngle * piFloat / 180.0f), 1, 0, 0)); initialGlyphTransform.tx = 0; initialGlyphTransform.ty = 0; CGContextRef cgContext = graphicsContext->platformContext(); CGContextSaveGState(cgContext); BOOL fontSmoothingEnabled = false; SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothingEnabled, 0); CGContextSetShouldAntialias(cgContext, fontSmoothingEnabled); CGContextScaleCTM(cgContext, 1.0, -1.0); CGContextTranslateCTM(cgContext, point.x() + glyphBuffer.offsetAt(from).width(), -(point.y() + glyphBuffer.offsetAt(from).height())); for (unsigned i = 0; i < numGlyphs; ++i) { RetainPtr<CGPathRef> glyphPath(AdoptCF, createPathForGlyph(hdc, glyphBuffer.glyphAt(from + i))); CGContextSaveGState(cgContext); CGContextConcatCTM(cgContext, initialGlyphTransform); if (drawingMode & TextModeFill) { CGContextAddPath(cgContext, glyphPath.get()); CGContextFillPath(cgContext); if (font->syntheticBoldOffset()) { CGContextTranslateCTM(cgContext, font->syntheticBoldOffset(), 0); CGContextAddPath(cgContext, glyphPath.get()); CGContextFillPath(cgContext); CGContextTranslateCTM(cgContext, -font->syntheticBoldOffset(), 0); } } if (drawingMode & TextModeStroke) { CGContextAddPath(cgContext, glyphPath.get()); CGContextStrokePath(cgContext); if (font->syntheticBoldOffset()) { CGContextTranslateCTM(cgContext, font->syntheticBoldOffset(), 0); CGContextAddPath(cgContext, glyphPath.get()); CGContextStrokePath(cgContext); CGContextTranslateCTM(cgContext, -font->syntheticBoldOffset(), 0); } } CGContextRestoreGState(cgContext); CGContextTranslateCTM(cgContext, gdiAdvances[i], 0); } CGContextRestoreGState(cgContext); } if (drawIntoBitmap) { UInt8* buffer = bitmap->buffer(); unsigned bufferLength = bitmap->bufferLength(); for (unsigned i = 0; i < bufferLength; i += 4) { // Use green, which is always in the middle. UInt8 alpha = (255 - buffer[i + 1]) * fillColor.alpha() / 255; buffer[i] = fillColor.blue(); buffer[i + 1] = fillColor.green(); buffer[i + 2] = fillColor.red(); buffer[i + 3] = alpha; } graphicsContext->drawWindowsBitmap(bitmap.get(), textRect.location()); } else graphicsContext->releaseWindowsContext(hdc, textRect, true, false); }
void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* font, const wxColour& color, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) { graphicsContext->save(); wxGCDC* dc = static_cast<wxGCDC*>(graphicsContext->platformContext()); wxFont* wxfont = font->getWxFont(); graphicsContext->setFillColor(graphicsContext->fillColor(), DeviceColorSpace); CGContextRef cgContext = static_cast<CGContextRef>(dc->GetGraphicsContext()->GetNativeContext()); CGFontRef cgFont; #ifdef wxOSX_USE_CORE_TEXT && wxOSX_USE_CORE_TEXT cgFont = CTFontCopyGraphicsFont((CTFontRef)wxfont->OSXGetCTFont(), NULL); #else ATSFontRef fontRef; fontRef = FMGetATSFontRefFromFont(wxfont->MacGetATSUFontID()); if (fontRef) cgFont = CGFontCreateWithPlatformFont((void*)&fontRef); #endif CGContextSetFont(cgContext, cgFont); CGContextSetFontSize(cgContext, wxfont->GetPointSize()); CGFloat red, green, blue, alpha; graphicsContext->fillColor().getRGBA(red, green, blue, alpha); CGContextSetRGBFillColor(cgContext, red, green, blue, alpha); CGAffineTransform matrix = CGAffineTransformIdentity; matrix.b = -matrix.b; matrix.d = -matrix.d; CGContextSetTextMatrix(cgContext, matrix); CGContextSetTextPosition(cgContext, point.x(), point.y()); const FloatSize* advanceSizes = static_cast<const FloatSize*>(glyphBuffer.advances(from)); int size = glyphBuffer.size() - from; CGSize sizes[size]; CGGlyph glyphs[numGlyphs]; // if the function doesn't exist, we're probably on tiger and need to grab the // function under its old name, CGFontGetGlyphsForUnicodes if (!CGFontGetGlyphsForUnichars) CGFontGetGlyphsForUnichars = (CGFontGetGlyphsForUnicharsPtr)dlsym(RTLD_DEFAULT, "CGFontGetGlyphsForUnicodes"); // Let's make sure we got the function under one name or another! ASSERT(CGFontGetGlyphsForUnichars); CGFontGetGlyphsForUnichars(cgFont, glyphBuffer.glyphs(from), glyphs, numGlyphs); for (int i = 0; i < size; i++) { FloatSize fsize = advanceSizes[i]; sizes[i] = CGSizeMake(fsize.width(), fsize.height()); } CGContextShowGlyphsWithAdvances(cgContext, glyphs, sizes, numGlyphs); if (cgFont) CGFontRelease(cgFont); graphicsContext->restore(); }
void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { cairo_t* cr = context->platformContext(); cairo_save(cr); font->setFont(cr); GlyphBufferGlyph* glyphs = (GlyphBufferGlyph*)glyphBuffer.glyphs(from); float offset = point.x(); for (int i = 0; i < numGlyphs; i++) { glyphs[i].x = offset; glyphs[i].y = point.y(); offset += glyphBuffer.advanceAt(from + i); } Color fillColor = context->fillColor(); // Text shadow, inspired by FontMac IntSize shadowSize; int shadowBlur = 0; Color shadowColor; bool hasShadow = context->textDrawingMode() == cTextFill && context->getShadow(shadowSize, shadowBlur, shadowColor); // TODO: Blur support if (hasShadow) { // Disable graphics context shadows (not yet implemented) and paint them manually context->clearShadow(); Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); cairo_save(cr); float red, green, blue, alpha; shadowFillColor.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha); cairo_translate(cr, shadowSize.width(), shadowSize.height()); cairo_show_glyphs(cr, glyphs, numGlyphs); cairo_restore(cr); } if (context->textDrawingMode() & cTextFill) { if (context->fillGradient()) { cairo_set_source(cr, context->fillGradient()->platformGradient()); if (context->getAlpha() < 1.0f) { cairo_push_group(cr); cairo_paint_with_alpha(cr, context->getAlpha()); cairo_pop_group_to_source(cr); } } else if (context->fillPattern()) { TransformationMatrix affine; cairo_set_source(cr, context->fillPattern()->createPlatformPattern(affine)); if (context->getAlpha() < 1.0f) { cairo_push_group(cr); cairo_paint_with_alpha(cr, context->getAlpha()); cairo_pop_group_to_source(cr); } } else { float red, green, blue, alpha; fillColor.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha()); } cairo_show_glyphs(cr, glyphs, numGlyphs); } if (context->textDrawingMode() & cTextStroke) { if (context->strokeGradient()) { cairo_set_source(cr, context->strokeGradient()->platformGradient()); if (context->getAlpha() < 1.0f) { cairo_push_group(cr); cairo_paint_with_alpha(cr, context->getAlpha()); cairo_pop_group_to_source(cr); } } else if (context->strokePattern()) { TransformationMatrix affine; cairo_set_source(cr, context->strokePattern()->createPlatformPattern(affine)); if (context->getAlpha() < 1.0f) { cairo_push_group(cr); cairo_paint_with_alpha(cr, context->getAlpha()); cairo_pop_group_to_source(cr); } } else { Color strokeColor = context->strokeColor(); float red, green, blue, alpha; strokeColor.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha()); } cairo_glyph_path(cr, glyphs, numGlyphs); cairo_set_line_width(cr, context->strokeThickness()); cairo_stroke(cr); } // Re-enable the platform shadow we disabled earlier if (hasShadow) context->setShadow(shadowSize, shadowBlur, shadowColor); cairo_restore(cr); }
static void drawglyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point, float height, float ascent, uint32 penalpha, uint32 penr, uint32 peng, uint32 penb, int shadowx, int shadowy) { GlyphBufferGlyph* glyphs = const_cast<GlyphBufferGlyph*>(glyphBuffer.glyphs(from)); struct OutlineFont *face = font->m_platformData.m_face; uint32 ysize = font->m_platformData.m_size; int32 imagewidth = 0; uint32 onlyspaces = TRUE; for (int i = 0; i < numGlyphs; i++) { if (32 != glyphs[i]) onlyspaces = FALSE; imagewidth += glyphBuffer.advanceAt(from + i); } if (onlyspaces) return; if (imagewidth <= 0) return; imagewidth += 2 * (ysize * amigaConfig.fontXDPI / 72); Vector<unsigned> *glyphRGBABuffer = new Vector<unsigned>(imagewidth * height); if (!glyphRGBABuffer) return; glyphRGBABuffer->fill(0); int32 offsetinimg = 0; uint32 shiftleft = 0; for (int i = 0; i < numGlyphs; i++) { if (32 == glyphs[i]) { offsetinimg += glyphBuffer.advanceAt(from + i); continue; } struct GlyphMap *glyph; if (IDiskfont->ESetInfo(&face->olf_EEngine, OT_GlyphCode, glyphs[i], TAG_END)) continue; //do not handle error struct OutlineFont *curface = face; if (IDiskfont->EObtainInfo(&face->olf_EEngine, OT_GlyphMap8Bit, &glyph, TAG_END)) { uint32 ok = FALSE; if (amigaConfig.unicodeFace && !IDiskfont->ESetInfo(&amigaConfig.unicodeFace->olf_EEngine, OT_PointHeight, ysize << 16, OT_GlyphCode, glyphs[i], TAG_END) && !IDiskfont->EObtainInfo(&amigaConfig.unicodeFace->olf_EEngine, OT_GlyphMap8Bit, &glyph, TAG_END)) { curface = amigaConfig.unicodeFace; ok = TRUE; } if (!ok && !IDiskfont->ESetInfo(&face->olf_EEngine, OT_GlyphCode, 0xFFFD, TAG_END) && !IDiskfont->EObtainInfo(&face->olf_EEngine, OT_GlyphMap8Bit, &glyph, TAG_END)) ok = TRUE; if (!ok && !IDiskfont->ESetInfo(&face->olf_EEngine, OT_GlyphCode, '?', TAG_END) && !IDiskfont->EObtainInfo(&face->olf_EEngine, OT_GlyphMap8Bit, &glyph, TAG_END)) ok = TRUE; if (!ok) continue; //do not handle error } if (amigaConfig.fontKerning && i > 0) if (!IDiskfont->ESetInfo(&curface->olf_EEngine, OT_GlyphCode, glyphs[i -1], OT_GlyphCode2, glyphs[i], TAG_END)) { FIXED kern = 0; if (!IDiskfont->EObtainInfo(&curface->olf_EEngine, OT_TextKernPair, &kern, TAG_END)) if (kern) offsetinimg -= (int)(kern / 65536.0 * ysize * amigaConfig.fontYDPI / 72); } uint32 left = glyph->glm_BlackLeft; uint32 top = glyph->glm_BlackTop; int32 width = glyph->glm_BlackWidth; int32 gheight = glyph->glm_BlackHeight; int32 x0 = glyph->glm_X0; int32 y0 = glyph->glm_Y0; uint32 modulo = glyph->glm_BMModulo; int32 imgoffsetx = -x0 + left; if (offsetinimg + imgoffsetx < 0) { if (0 == i) { shiftleft = -imgoffsetx; if (shiftleft > ysize) { shiftleft = ysize; imgoffsetx = -ysize; } offsetinimg = shiftleft; } else imgoffsetx = 0; } int32 imgoffsety = ascent - y0 + top; if (imgoffsety < 0) imgoffsety = 0; if (offsetinimg + imgoffsetx + width > imagewidth) { int32 signedwidth = imagewidth - offsetinimg - imgoffsetx; if (signedwidth < 0) signedwidth = 0; width = signedwidth; } if (imgoffsety + gheight > (int)height) gheight = height - imgoffsety; for (int32 y = 0; y < gheight ; y++) { uint32 top_mod_left = (y + top) * modulo + left; for (int32 x = 0; x < width; x++) if ((offsetinimg + imgoffsetx + x < imagewidth) && (offsetinimg + imgoffsetx + x >= 0)) { uint32 a = penalpha * glyph->glm_BitMap[top_mod_left + x] / 255; if (a) { uint32 oldARGB = (*glyphRGBABuffer)[(imgoffsety + y) * imagewidth + offsetinimg + imgoffsetx + x]; if (oldARGB) a = std::max((oldARGB >> 24), a); (*glyphRGBABuffer)[(imgoffsety + y) * imagewidth + offsetinimg + imgoffsetx + x] = (a << 24) | ((penr * a / 255) << 16) | ((peng * a / 255) << 8) | (penb * a / 255); } } } offsetinimg += glyphBuffer.advanceAt(from + i); IDiskfont->EReleaseInfo(&curface->olf_EEngine, OT_GlyphMap8Bit, glyph, TAG_END); }
void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { // compile-time assert SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); SkPaint paint; if (!setupForText(&paint, gc, font)) { return; } SkScalar x = SkFloatToScalar(point.x()); SkScalar y = SkFloatToScalar(point.y()); const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); const GlyphBufferAdvance* adv = glyphBuffer.advances(from); SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); SkPoint* pos = storage.get(); SkCanvas* canvas = gc->platformContext()->mCanvas; /* We need an array of [x,y,x,y,x,y,...], but webkit is giving us point.xy + [width, height, width, height, ...], so we have to convert */ if (EmojiFont::IsAvailable()) { // set filtering, to make scaled images look nice(r) paint.setFilterBitmap(true); int localIndex = 0; int localCount = 0; for (int i = 0; i < numGlyphs; i++) { if (EmojiFont::IsEmojiGlyph(glyphs[i])) { if (localCount) canvas->drawPosText(&glyphs[localIndex], localCount * sizeof(uint16_t), &pos[localIndex], paint); EmojiFont::Draw(canvas, glyphs[i], x, y, paint); // reset local index/count track for "real" glyphs localCount = 0; localIndex = i + 1; } else { pos[i].set(x, y); localCount += 1; } x += SkFloatToScalar(adv[i].width()); y += SkFloatToScalar(adv[i].height()); } // draw the last run of glyphs (if any) if (localCount) canvas->drawPosText(&glyphs[localIndex], localCount * sizeof(uint16_t), &pos[localIndex], paint); } else { for (int i = 0; i < numGlyphs; i++) { pos[i].set(x, y); x += SkFloatToScalar(adv[i].width()); y += SkFloatToScalar(adv[i].height()); } canvas->drawPosText(glyphs, numGlyphs * sizeof(uint16_t), pos, paint); } }
void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* font, const wxColour& color, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) { #if USE(WXGC) wxGCDC* dc = static_cast<wxGCDC*>(graphicsContext->platformContext()); wxGraphicsContext* gc = dc->GetGraphicsContext(); gc->PushState(); cairo_t* cr = (cairo_t*)gc->GetNativeContext(); wxFont* wxfont = font->getWxFont(); cairo_scaled_font_t* scaled_font = 0; #if wxUSE_PANGO PangoFont* pangoFont = createPangoFontForFont(wxfont); PangoFontMap* fontMap = pangoFontMap(); PangoContext* pangoContext = pango_cairo_font_map_create_context(PANGO_CAIRO_FONT_MAP(fontMap)); scaled_font = createScaledFontForFont(wxfont); #elif __WXMSW__ cairo_matrix_t sizeMatrix, ctm; cairo_matrix_init_identity(&ctm); int size = font->platformData().size(); cairo_matrix_init_scale(&sizeMatrix, size, size); cairo_font_options_t* fontOptions = cairo_font_options_create(); cairo_font_options_set_antialias(fontOptions, CAIRO_ANTIALIAS_SUBPIXEL); cairo_font_face_t* win_face = cairo_win32_font_face_create_for_hfont((HFONT)wxfont->GetHFONT()); scaled_font = cairo_scaled_font_create(win_face, &sizeMatrix, &ctm, fontOptions); #endif ASSERT(scaled_font); GlyphBufferGlyph* glyphs = const_cast<GlyphBufferGlyph*>(glyphBuffer.glyphs(from)); float offset = point.x(); for (int i = 0; i < numGlyphs; i++) { #if wxUSE_PANGO glyphs[i].index = pango_font_get_glyph(pangoFont, pangoContext, glyphBuffer.glyphAt(from + i)); #endif glyphs[i].x = offset; glyphs[i].y = point.y(); offset += glyphBuffer.advanceAt(from + i); } cairo_set_source_rgba(cr, color.Red()/255.0, color.Green()/255.0, color.Blue()/255.0, color.Alpha()/255.0); cairo_set_scaled_font(cr, scaled_font); cairo_show_glyphs(cr, glyphs, numGlyphs); cairo_scaled_font_destroy(scaled_font); gc->PopState(); #else wxDC* dc = graphicsContext->platformContext(); wxFont* wxfont = font->getWxFont(); if (wxfont && wxfont->IsOk()) dc->SetFont(*wxfont); dc->SetTextForeground(color); // convert glyphs to wxString GlyphBufferGlyph* glyphs = const_cast<GlyphBufferGlyph*>(glyphBuffer.glyphs(from)); int offset = point.x(); wxString text = wxEmptyString; for (unsigned i = 0; i < numGlyphs; i++) { text = text.Append((wxChar)glyphs[i]); offset += glyphBuffer.advanceAt(from + i); } // the y point is actually the bottom point of the text, turn it into the top float height = font->ascent() - font->descent(); wxCoord ypoint = (wxCoord) (point.y() - height); dc->DrawText(text, (wxCoord)point.x(), ypoint); #endif }
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)); // compile-time assert const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); SkScalar x = SkFloatToScalar(point.x()); SkScalar y = SkFloatToScalar(point.y()); // FIXME: text rendering speed: // Android has code in their WebCore fork to special case when the // GlyphBuffer has no advances other than the defaults. In that case the // text drawing can proceed faster. However, it's unclear when those // patches may be upstreamed to WebKit so we always use the slower path // here. 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(); // We draw text up to two times (once for fill, once for stroke). 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) { // If we also filled, we don't want to draw shadows twice. // See comment in FontChromiumWin.cpp::paintSkiaText() for more details. 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); } }
void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { // compile-time assert SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); if (numGlyphs == 1 && glyphBuffer.glyphAt(from) == 0x3) { // Webkit likes to draw end text control command for some reason // Just ignore it return; } SkPaint paint; if (!setupForText(&paint, gc, font)) { return; } SkScalar x = SkFloatToScalar(point.x()); SkScalar y = SkFloatToScalar(point.y()); const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); const GlyphBufferAdvance* adv = glyphBuffer.advances(from); SkAutoSTMalloc<32, SkPoint> storage(numGlyphs), storage2(numGlyphs), storage3(numGlyphs); SkPoint* pos = storage.get(); SkCanvas* canvas = gc->platformContext()->recordingCanvas(); /* We need an array of [x,y,x,y,x,y,...], but webkit is giving us point.xy + [width, height, width, height, ...], so we have to convert */ if (font->platformData().orientation() == Vertical) { float yOffset = SkFloatToScalar(font->fontMetrics().floatAscent(IdeographicBaseline) - font->fontMetrics().floatAscent()); gc->platformContext()->setTextOffset(FloatSize(0.0f, -yOffset)); // compensate for offset in bounds calculation y += yOffset; } if (EmojiFont::IsAvailable()) { // set filtering, to make scaled images look nice(r) paint.setFilterBitmap(true); SkMatrix rotator; rotator.reset(); if (font->platformData().orientation() == Vertical) { canvas->save(); canvas->rotate(-90); rotator.setRotate(90); } int localIndex = 0; int localCount = 0; for (int i = 0; i < numGlyphs; i++) { if (EmojiFont::IsEmojiGlyph(glyphs[i])) { if (localCount) { rotator.mapPoints(&pos[localIndex], localCount); canvas->drawPosText(&glyphs[localIndex], localCount * sizeof(uint16_t), &pos[localIndex], paint); } EmojiFont::Draw(canvas, glyphs[i], x, y, paint); // reset local index/count track for "real" glyphs localCount = 0; localIndex = i + 1; } else { pos[i].set(x, y); localCount += 1; } x += SkFloatToScalar(adv[i].width()); y += SkFloatToScalar(adv[i].height()); } // draw the last run of glyphs (if any) if (localCount) { rotator.mapPoints(&pos[localIndex], localCount); canvas->drawPosText(&glyphs[localIndex], localCount * sizeof(uint16_t), &pos[localIndex], paint); } if (font->platformData().orientation() == Vertical) canvas->restore(); } else { for (int i = 0; i < numGlyphs; i++) { pos[i].set(x, y); y += SkFloatToScalar(adv[i].height()); x += SkFloatToScalar(adv[i].width()); } if (font->platformData().orientation() == Vertical) { canvas->save(); canvas->rotate(-90); SkMatrix rotator; rotator.reset(); rotator.setRotate(90); rotator.mapPoints(pos, numGlyphs); } canvas->drawPosText(glyphs, numGlyphs * sizeof(uint16_t), pos, paint); if (font->platformData().orientation() == Vertical) canvas->restore(); } if (font->platformData().orientation() == Vertical) gc->platformContext()->setTextOffset(FloatSize()); // reset to undo above }
void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { cairo_t* cr = context->platformContext(); cairo_save(cr); cairo_set_scaled_font(cr, font->platformData().scaledFont()); GlyphBufferGlyph* glyphs = (GlyphBufferGlyph*)glyphBuffer.glyphs(from); float offset = 0.0f; for (int i = 0; i < numGlyphs; i++) { glyphs[i].x = offset; glyphs[i].y = 0.0f; offset += glyphBuffer.advanceAt(from + i); } Color fillColor = context->fillColor(); // Synthetic Oblique if(font->platformData().syntheticOblique()) { cairo_matrix_t mat = {1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, point.x(), point.y()}; cairo_transform(cr, &mat); } else { cairo_translate(cr, point.x(), point.y()); } // Text shadow, inspired by FontMac FloatSize shadowOffset; float shadowBlur = 0; Color shadowColor; bool hasShadow = context->textDrawingMode() & cTextFill && context->getShadow(shadowOffset, shadowBlur, shadowColor); // TODO: Blur support if (hasShadow) { // Disable graphics context shadows (not yet implemented) and paint them manually context->clearShadow(); Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); cairo_save(cr); float red, green, blue, alpha; shadowFillColor.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha); #if ENABLE(FILTERS) cairo_text_extents_t extents; cairo_scaled_font_glyph_extents(font->platformData().scaledFont(), glyphs, numGlyphs, &extents); FloatRect rect(FloatPoint(), FloatSize(extents.width, extents.height)); IntSize shadowBufferSize; FloatRect shadowRect; float radius = 0; context->calculateShadowBufferDimensions(shadowBufferSize, shadowRect, radius, rect, shadowOffset, shadowBlur); // Draw shadow into a new ImageBuffer OwnPtr<ImageBuffer> shadowBuffer = ImageBuffer::create(shadowBufferSize); GraphicsContext* shadowContext = shadowBuffer->context(); cairo_t* shadowCr = shadowContext->platformContext(); cairo_translate(shadowCr, radius, extents.height + radius); cairo_set_scaled_font(shadowCr, font->platformData().scaledFont()); cairo_show_glyphs(shadowCr, glyphs, numGlyphs); if (font->syntheticBoldOffset()) { cairo_save(shadowCr); cairo_translate(shadowCr, font->syntheticBoldOffset(), 0); cairo_show_glyphs(shadowCr, glyphs, numGlyphs); cairo_restore(shadowCr); } cairo_translate(cr, 0.0, -extents.height); context->applyPlatformShadow(shadowBuffer.release(), shadowColor, shadowRect, radius); #else cairo_translate(cr, shadowOffset.width(), shadowOffset.height()); cairo_show_glyphs(cr, glyphs, numGlyphs); if (font->syntheticBoldOffset()) { cairo_save(cr); cairo_translate(cr, font->syntheticBoldOffset(), 0); cairo_show_glyphs(cr, glyphs, numGlyphs); cairo_restore(cr); } #endif cairo_restore(cr); } if (context->textDrawingMode() & cTextFill) { if (context->fillGradient()) { cairo_set_source(cr, context->fillGradient()->platformGradient()); if (context->getAlpha() < 1.0f) { cairo_push_group(cr); cairo_paint_with_alpha(cr, context->getAlpha()); cairo_pop_group_to_source(cr); } } else if (context->fillPattern()) { AffineTransform affine; cairo_pattern_t* pattern = context->fillPattern()->createPlatformPattern(affine); cairo_set_source(cr, pattern); if (context->getAlpha() < 1.0f) { cairo_push_group(cr); cairo_paint_with_alpha(cr, context->getAlpha()); cairo_pop_group_to_source(cr); } cairo_pattern_destroy(pattern); } else { float red, green, blue, alpha; fillColor.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha()); } cairo_show_glyphs(cr, glyphs, numGlyphs); if (font->syntheticBoldOffset()) { cairo_save(cr); cairo_translate(cr, font->syntheticBoldOffset(), 0); cairo_show_glyphs(cr, glyphs, numGlyphs); cairo_restore(cr); } } // Prevent running into a long computation within cairo. If the stroke width is // twice the size of the width of the text we will not ask cairo to stroke // the text as even one single stroke would cover the full wdth of the text. // See https://bugs.webkit.org/show_bug.cgi?id=33759. if (context->textDrawingMode() & cTextStroke && context->strokeThickness() < 2 * offset) { if (context->strokeGradient()) { cairo_set_source(cr, context->strokeGradient()->platformGradient()); if (context->getAlpha() < 1.0f) { cairo_push_group(cr); cairo_paint_with_alpha(cr, context->getAlpha()); cairo_pop_group_to_source(cr); } } else if (context->strokePattern()) { AffineTransform affine; cairo_pattern_t* pattern = context->strokePattern()->createPlatformPattern(affine); cairo_set_source(cr, pattern); if (context->getAlpha() < 1.0f) { cairo_push_group(cr); cairo_paint_with_alpha(cr, context->getAlpha()); cairo_pop_group_to_source(cr); } cairo_pattern_destroy(pattern); } else { Color strokeColor = context->strokeColor(); float red, green, blue, alpha; strokeColor.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha()); } cairo_glyph_path(cr, glyphs, numGlyphs); cairo_set_line_width(cr, context->strokeThickness()); cairo_stroke(cr); } // Re-enable the platform shadow we disabled earlier if (hasShadow) context->setShadow(shadowOffset, shadowBlur, shadowColor, DeviceColorSpace); cairo_restore(cr); }
// TODO: This needs to be split into helper functions to better scope the // inputs/outputs, and reduce duplicate code. // This issue is tracked in https://bugs.webkit.org/show_bug.cgi?id=62989 void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { COMPILE_ASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t), GlyphBufferGlyphSize_equals_uint16_t); bool shouldSmoothFonts = true; bool shouldAntialias = true; switch (fontDescription().fontSmoothing()) { case Antialiased: shouldSmoothFonts = false; break; case SubpixelAntialiased: break; case NoSmoothing: shouldAntialias = false; shouldSmoothFonts = false; break; case AutoSmoothing: // For the AutoSmooth case, don't do anything! Keep the default settings. break; } if (!shouldUseSmoothing() || PlatformSupport::layoutTestMode()) shouldSmoothFonts = false; const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); SkScalar x = SkFloatToScalar(point.x()); SkScalar y = SkFloatToScalar(point.y()); if (font->platformData().orientation() == Vertical) y += SkFloatToScalar(font->fontMetrics().floatAscent(IdeographicBaseline) - font->fontMetrics().floatAscent()); // FIXME: text rendering speed: // Android has code in their WebCore fork to special case when the // GlyphBuffer has no advances other than the defaults. In that case the // text drawing can proceed faster. However, it's unclear when those // patches may be upstreamed to WebKit so we always use the slower path // here. const GlyphBufferAdvance* adv = glyphBuffer.advances(from); SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); SkPoint* pos = storage.get(); for (int i = 0; i < numGlyphs; i++) { pos[i].set(x, y); x += SkFloatToScalar(adv[i].width); y += SkFloatToScalar(adv[i].height); } SkCanvas* canvas = gc->platformContext()->canvas(); if (font->platformData().orientation() == Vertical) { canvas->save(); canvas->rotate(-90); SkMatrix rotator; rotator.reset(); rotator.setRotate(90); rotator.mapPoints(pos, numGlyphs); } TextDrawingModeFlags textMode = gc->platformContext()->getTextDrawingMode(); // We draw text up to two times (once for fill, once for stroke). if (textMode & TextModeFill) { SkPaint paint; gc->platformContext()->setupPaintForFilling(&paint); setupPaint(&paint, font, this, shouldAntialias, shouldSmoothFonts); gc->platformContext()->adjustTextRenderMode(&paint); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); canvas->drawPosText(glyphs, numGlyphs * sizeof(uint16_t), pos, paint); } if ((textMode & TextModeStroke) && gc->platformContext()->getStrokeStyle() != NoStroke && gc->platformContext()->getStrokeThickness() > 0) { SkPaint paint; gc->platformContext()->setupPaintForStroking(&paint, 0, 0); setupPaint(&paint, font, this, shouldAntialias, shouldSmoothFonts); gc->platformContext()->adjustTextRenderMode(&paint); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); if (textMode & TextModeFill) { // If we also filled, we don't want to draw shadows twice. // See comment in FontChromiumWin.cpp::paintSkiaText() for more details. paint.setLooper(0); } canvas->drawPosText(glyphs, numGlyphs * sizeof(uint16_t), pos, paint); } if (font->platformData().orientation() == Vertical) canvas->restore(); }
void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { CGContextRef cgContext = graphicsContext->platformContext(); bool shouldUseFontSmoothing = WebCoreShouldUseFontSmoothing(); if (font->platformData().useGDI()) { static bool canUsePlatformNativeGlyphs = wkCanUsePlatformNativeGlyphs(); if (!canUsePlatformNativeGlyphs || !shouldUseFontSmoothing || (graphicsContext->textDrawingMode() & cTextStroke)) { drawGDIGlyphs(graphicsContext, font, glyphBuffer, from, numGlyphs, point); return; } } uint32_t oldFontSmoothingStyle = wkSetFontSmoothingStyle(cgContext, shouldUseFontSmoothing); const FontPlatformData& platformData = font->platformData(); CGContextSetFont(cgContext, platformData.cgFont()); CGAffineTransform matrix = CGAffineTransformIdentity; matrix.b = -matrix.b; matrix.d = -matrix.d; if (platformData.syntheticOblique()) { static float skew = -tanf(syntheticObliqueAngle * piFloat / 180.0f); matrix = CGAffineTransformConcat(matrix, CGAffineTransformMake(1, 0, skew, 1, 0, 0)); } CGContextSetTextMatrix(cgContext, matrix); // Uniscribe gives us offsets to help refine the positioning of combining glyphs. FloatSize translation = glyphBuffer.offsetAt(from); CGContextSetFontSize(cgContext, platformData.size()); wkSetCGContextFontRenderingStyle(cgContext, font->isSystemFont(), false, font->platformData().useGDI()); IntSize shadowSize; int shadowBlur; Color shadowColor; graphicsContext->getShadow(shadowSize, shadowBlur, shadowColor); bool hasSimpleShadow = graphicsContext->textDrawingMode() == cTextFill && shadowColor.isValid() && !shadowBlur; if (hasSimpleShadow) { // Paint simple shadows ourselves instead of relying on CG shadows, to avoid losing subpixel antialiasing. graphicsContext->clearShadow(); Color fillColor = graphicsContext->fillColor(); Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); graphicsContext->setFillColor(shadowFillColor); CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowSize.width(), point.y() + translation.height() + shadowSize.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->m_syntheticBoldOffset) { CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowSize.width() + font->m_syntheticBoldOffset, point.y() + translation.height() + shadowSize.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } graphicsContext->setFillColor(fillColor); } CGContextSetTextPosition(cgContext, point.x() + translation.width(), point.y() + translation.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->m_syntheticBoldOffset) { CGContextSetTextPosition(cgContext, point.x() + translation.width() + font->m_syntheticBoldOffset, point.y() + translation.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } if (hasSimpleShadow) graphicsContext->setShadow(shadowSize, shadowBlur, shadowColor); wkRestoreFontSmoothingStyle(cgContext, oldFontSmoothingStyle); }
void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { SkColor color = graphicsContext->effectiveFillColor(); unsigned char alpha = SkColorGetA(color); // Skip 100% transparent text; no need to draw anything. if (!alpha && graphicsContext->strokeStyle() == NoStroke && !graphicsContext->hasShadow()) return; // We draw the glyphs in chunks to avoid having to do a heap allocation for // the arrays of characters and advances. const int kMaxBufferLength = 256; Vector<int, kMaxBufferLength> advances; int glyphIndex = 0; // The starting glyph of the current chunk. float horizontalOffset = point.x(); // The floating point offset of the left side of the current glyph. #if ENABLE(OPENTYPE_VERTICAL) const OpenTypeVerticalData* verticalData = font->verticalData(); if (verticalData) { Vector<FloatPoint, kMaxBufferLength> translations; Vector<GOFFSET, kMaxBufferLength> offsets; // Skia doesn't have matrix for glyph coordinate space, so we rotate back the CTM. AffineTransform savedMatrix = graphicsContext->getCTM(); graphicsContext->concatCTM(AffineTransform(0, -1, 1, 0, point.x(), point.y())); graphicsContext->concatCTM(AffineTransform(1, 0, 0, 1, -point.x(), -point.y())); const FontMetrics& metrics = font->fontMetrics(); SkScalar verticalOriginX = SkFloatToScalar(point.x() + metrics.floatAscent() - metrics.floatAscent(IdeographicBaseline)); while (glyphIndex < numGlyphs) { // How many chars will be in this chunk? int curLen = std::min(kMaxBufferLength, numGlyphs - glyphIndex); const Glyph* glyphs = glyphBuffer.glyphs(from + glyphIndex); translations.resize(curLen); verticalData->getVerticalTranslationsForGlyphs(font, &glyphs[0], curLen, reinterpret_cast<float*>(&translations[0])); // To position glyphs vertically, we use offsets instead of advances. advances.resize(curLen); advances.fill(0); offsets.resize(curLen); float currentWidth = 0; for (int i = 0; i < curLen; ++i, ++glyphIndex) { offsets[i].du = lroundf(translations[i].x()); offsets[i].dv = -lroundf(currentWidth - translations[i].y()); currentWidth += glyphBuffer.advanceAt(from + glyphIndex); } SkPoint origin; origin.set(verticalOriginX, SkFloatToScalar(point.y() + horizontalOffset - point.x())); horizontalOffset += currentWidth; paintSkiaText(graphicsContext, font->platformData(), curLen, &glyphs[0], &advances[0], &offsets[0], &origin); } graphicsContext->setCTM(savedMatrix); return; } #endif // In order to round all offsets to the correct pixel boundary, this code keeps track of the absolute position // of each glyph in floating point units and rounds to integer advances at the last possible moment. int lastHorizontalOffsetRounded = lroundf(horizontalOffset); // The rounded offset of the left side of the last glyph rendered. Vector<WORD, kMaxBufferLength> glyphs; while (glyphIndex < numGlyphs) { // How many chars will be in this chunk? 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); // Bug 26088 - very large positive or negative runs can fail to // render so we clamp the size here. In the specs, negative // letter-spacing is implementation-defined, so this should be // fine, and it matches Safari's implementation. The call actually // seems to crash if kMaxNegativeRun is set to somewhere around // -32830, so we give ourselves a little breathing room. const int maxNegativeRun = -32768; const int maxPositiveRun = 32768; if ((currentWidth + advances[i] < maxNegativeRun) || (currentWidth + advances[i] > maxPositiveRun)) advances[i] = 0; } SkPoint origin = point; origin.fX += SkFloatToScalar(horizontalOffset - point.x() - currentWidth); paintSkiaText(graphicsContext, font->platformData(), curLen, &glyphs[0], &advances[0], 0, &origin); } }
void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { TextRun run(glyphBuffer.glyphs(from), numGlyphs, false, 0, 0, false, false, false, false); drawComplexText(context, run, point, 0, numGlyphs); }
void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, unsigned from, unsigned numGlyphs, const FloatPoint& point, const FloatRect& textRect) const { SkScalar x = SkFloatToScalar(point.x()); SkScalar y = SkFloatToScalar(point.y()); const OpenTypeVerticalData* verticalData = font->verticalData(); if (font->platformData().orientation() == Vertical && verticalData) { SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); SkPoint* pos = storage.get(); AffineTransform savedMatrix = gc->getCTM(); gc->concatCTM(AffineTransform(0, -1, 1, 0, point.x(), point.y())); gc->concatCTM(AffineTransform(1, 0, 0, 1, -point.x(), -point.y())); const unsigned kMaxBufferLength = 256; Vector<FloatPoint, kMaxBufferLength> translations; const FontMetrics& metrics = font->fontMetrics(); SkScalar verticalOriginX = SkFloatToScalar(point.x() + metrics.floatAscent() - metrics.floatAscent(IdeographicBaseline)); float horizontalOffset = point.x(); unsigned glyphIndex = 0; while (glyphIndex < numGlyphs) { unsigned chunkLength = std::min(kMaxBufferLength, numGlyphs - glyphIndex); const Glyph* glyphs = glyphBuffer.glyphs(from + glyphIndex); translations.resize(chunkLength); verticalData->getVerticalTranslationsForGlyphs(font, &glyphs[0], chunkLength, reinterpret_cast<float*>(&translations[0])); x = verticalOriginX; y = SkFloatToScalar(point.y() + horizontalOffset - point.x()); float currentWidth = 0; for (unsigned i = 0; i < chunkLength; ++i, ++glyphIndex) { pos[i].set( x + SkIntToScalar(lroundf(translations[i].x())), y + -SkIntToScalar(-lroundf(currentWidth - translations[i].y()))); currentWidth += glyphBuffer.advanceAt(from + glyphIndex); } horizontalOffset += currentWidth; paintGlyphs(gc, font, glyphs, chunkLength, pos, textRect); } gc->setCTM(savedMatrix); return; } if (!glyphBuffer.hasOffsets()) { SkAutoSTMalloc<64, SkScalar> storage(numGlyphs); SkScalar* xpos = storage.get(); const float* adv = glyphBuffer.advances(from); for (unsigned i = 0; i < numGlyphs; i++) { xpos[i] = x; x += SkFloatToScalar(adv[i]); } const Glyph* glyphs = glyphBuffer.glyphs(from); paintGlyphsHorizontal(gc, font, glyphs, numGlyphs, xpos, SkFloatToScalar(y), textRect); return; } // FIXME: text rendering speed: // Android has code in their WebCore fork to special case when the // GlyphBuffer has no advances other than the defaults. In that case the // text drawing can proceed faster. However, it's unclear when those // patches may be upstreamed to WebKit so we always use the slower path // here. SkAutoSTMalloc<32, SkPoint> storage(numGlyphs); SkPoint* pos = storage.get(); const FloatSize* offsets = glyphBuffer.offsets(from); const float* advances = glyphBuffer.advances(from); SkScalar advanceSoFar = SkFloatToScalar(0); for (unsigned i = 0; i < numGlyphs; i++) { pos[i].set( x + SkFloatToScalar(offsets[i].width()) + advanceSoFar, y + SkFloatToScalar(offsets[i].height())); advanceSoFar += SkFloatToScalar(advances[i]); } const Glyph* glyphs = glyphBuffer.glyphs(from); paintGlyphs(gc, font, glyphs, numGlyphs, pos, textRect); }
// We need to draw the glyphBuffer glyphs/advances with the pSimpleFontData font onto the // pGraphicsContext pSurface. We draw glyphCount glyphs starting at the glyphIndexBegin. void Font::drawGlyphs(GraphicsContext* pGraphicsContext, const SimpleFontData* pSimpleFontData, const GlyphBuffer& glyphBuffer, int glyphIndexBegin, int glyphCount, const FloatPoint& point) const { // 11/09/09 CSidhall Added notify start of process to user NOTIFY_PROCESS_STATUS(EA::WebKit::kVProcessTypeDrawGlyph, EA::WebKit::kVProcessStatusStarted); #if defined(EA_DEBUG) && defined(AUTHOR_PPEDRIANA_DISABLED) && defined(EA_PLATFORM_WINDOWS) TraceGlyphBufferToString(pSimpleFontData, glyphBuffer, glyphIndexBegin, glyphCount, point); #endif GlyphBufferGlyph* glyphs = const_cast<GlyphBufferGlyph*>(glyphBuffer.glyphs(glyphIndexBegin)); float offset = 0; int x_offset = 0; EA::Internal::IFont* pFont = pSimpleFontData->m_font.mpFont; EA::Internal::IGlyphCache* pGlyphCache = EA::WebKit::GetGlyphCache(); EAW_ASSERT_MSG(pGlyphCache, "GlyphCache is not set"); if(!pGlyphCache) { NOTIFY_PROCESS_STATUS(EA::WebKit::kVProcessTypeDrawGlyph, EA::WebKit::kVProcessStatusEnded); return; } EA::Internal::ITextureInfo* pTI = pGlyphCache->GetTextureInfo(0); // Right now we hard-code usage of what is the only texture. EAW_ASSERT_MSG(pTI, "GlyphCache is not initialized with enough number of textures"); if(!pTI) { NOTIFY_PROCESS_STATUS(EA::WebKit::kVProcessTypeDrawGlyph, EA::WebKit::kVProcessStatusEnded); return; } const int32_t textureSize = (int32_t)pTI->GetSize(); EA::Internal::IGlyphTextureInfo gti; EA::Internal::GlyphMetrics glyphMetrics; GlyphDrawInfoArray gdiArray((size_t)(unsigned)glyphCount); // Walk through the list of glyphs and build up render info for each one. for (int i = 0; i < glyphCount; i++) { EA::Internal::GlyphId g = glyphs[i]; if(!pFont->GetGlyphMetrics(g, glyphMetrics)) { EAW_ASSERT_MSG(false, "Font::drawGlyphs: invalid glyph/Font combo."); pFont->GetGlyphIds(L"?", 1, &g, true); pFont->GetGlyphMetrics(g, glyphMetrics); } if(!pGlyphCache->GetGlyphTextureInfo(pFont, g, gti)) { const EA::Internal::GlyphBitmap* pGlyphBitmap; if(pFont->RenderGlyphBitmap(&pGlyphBitmap, g)) { if(pGlyphCache->AddGlyphTexture(pFont, g, pGlyphBitmap->mpData, pGlyphBitmap->mnWidth, pGlyphBitmap->mnHeight, pGlyphBitmap->mnStride, (uint32_t)pGlyphBitmap->mBitmapFormat, gti)) { pGlyphCache->EndUpdate(gti.mpTextureInfo); } else { EAW_ASSERT_MSG(false, "Font::drawGlyphs: AddGlyphTexture failed."); gti.mX1 = gti.mX2 = 0; // Make this an empty glyph. Normally this should never execute. } pFont->DoneGlyphBitmap(pGlyphBitmap); } else { EAW_ASSERT_MSG(false, "Font::drawGlyphs: invalid glyph/Font combo."); gti.mX1 = gti.mX2 = 0; // Make this an empty glyph. Normally this should never execute. } } // Apply kerning. // Note by Paul Pedriana: Can we really apply kerning here at the render stage without it looking // wrong? It seems to me this cannot work unless kerning is also taken into account at the // layout stage. To do: See if in fact kerning is taken into account at the layout stage. EA::Internal::Kerning kerning; if((i > 0) && pFont->GetKerning(glyphs[i - 1], g, kerning, 0)) offset += kerning.mfKernX; // The values we calculate here are relative to the current pen position at the // baseline position of [xoffset, 0], where +X is rightward and +Y is upward. gdiArray[i].x1 = (int)(offset + glyphMetrics.mfHBearingX); gdiArray[i].x2 = (int)(offset + glyphMetrics.mfHBearingX + glyphMetrics.mfSizeX); gdiArray[i].y1 = (int)(glyphMetrics.mfHBearingY); gdiArray[i].y2 = (int)(glyphMetrics.mfHBearingY - glyphMetrics.mfSizeY); gdiArray[i].tx = (int)(gti.mX1 * textureSize); // Convert [0..1] to [0..textureSize] gdiArray[i].ty = (int)(gti.mY1 * textureSize); // advanceAt should return a value that is usually equivalent to glyphMetrics.mfHAdvanceX, at least // for most simple Western text. A case where it would be different would be Arabic combining glyphs, // and custom kerning, though kerning adjustments are handled above. offset += glyphBuffer.advanceAt(glyphIndexBegin + i); // Free wrapper if(gti.mpTextureInfo) gti.mpTextureInfo->DestroyWrapper(); } // Find the X and Y minima and maxima of the glyph boxes. // To do: We can improve the efficiency of this code in a couple of ways, including moving the // min/max tests up into the glyphMetrics code above. int xMin = gdiArray[0].x1; int xMax = gdiArray[0].x2; int yMin = gdiArray[0].y2; // y2 is min because y goes upward. int yMax = gdiArray[0].y1; for (GlyphDrawInfoArray::const_iterator it = gdiArray.begin(); it != gdiArray.end(); ++it) { const GlyphDrawInfo& gdi = *it; // We assume tha x1 is always <= x2. if (gdi.x1 < xMin) xMin = gdi.x1; if (gdi.x2 > xMax) xMax = gdi.x2; // if (gdi.y1 < yMin) // We normally don't need this check, because y2 will usually (or always?) be less than y1. // yMin = gdi.y1; if (gdi.y2 < yMin) yMin = gdi.y2; if (gdi.y1 > yMax) yMax = gdi.y1; // if (gdi.y2 > yMax) // We normally don't need this check, because y1 will usually (or always?) be greater than y2. // yMax = gdi.y2; } const uint16_t destWidth = (abs(xMin) + xMax); // abs(xMin) because it's possible the left of the first glyph is to the left of the origin. Note by Paul Pedriana: Shouldn't this instead be (xMax - min(xMin, 0))? const uint16_t destHeight = (yMax - yMin); if(destWidth && destHeight) // (If we are drawing just space chars (which often happens), then destWidth and/or destHeight will be zero) { // Question by Paul Pedriana: What is the following code doing? I copied this from the // WebKit Freetype equivalent of this function assuming it must be useful. It seems to // me that it is chopping off any glyph pixels to the left of zero. This is probably // going to usually be OK, because it is abnormal for the first char of just about any // written language to be a combining char (and thus drawn to the left of the pen position). if (gdiArray[0].x1 < 0) { x_offset = gdiArray[0].x1; gdiArray[0].x1 = 0; } EA::Raster::Surface* const pSurface = pGraphicsContext->platformContext(); const Color penColor = pGraphicsContext->fillColor(); // Write the glyphs into a linear buffer. // To consider: See if it's possible to often get away with using stack space for this by // using Vector<uint32_t, N> in order to avoid a memory allocation. A typical // width is ~400 pixels and a typical height ~16 pixels, which is 25600 bytes. // So unless the string is small there won't be any benefit to this. Vector<uint32_t> glyphRGBABuffer(destWidth * destHeight); const int penA = penColor.alpha(); const int penR = penColor.red(); const int penG = penColor.green(); const int penB = penColor.blue(); const int penRGB = (penR << 16) | (penG << 8) | penB; glyphRGBABuffer.fill(0); for (int i = 0; i < glyphCount; i++) { const GlyphDrawInfo& gdi = gdiArray[i]; const uint8_t* pGlyphAlpha = (pTI->GetData()) + (gdi.ty * pTI->GetStride()) + gdi.tx; const int yOffset = (destHeight + yMin) - gdi.y1; // Note by Arpit Baldeva: Old index calculation below caused a memory overrun(or I should say underrun on http://get.adobe.com/flashplayer/). // Basically, yOffset * destWidth) + gdi.x1 could end up being negative if the yOffset is 0 and gdi.x1 is negative. Since I do want // to make sure that index is positive, I just force it to be at least 0. There is not enough understanding about this code as of now // and Paul said that he would take a look at this along with other Font problems he is currently looking at. const int bufferIndex = (yOffset * destWidth) + gdi.x1; EAW_ASSERT_FORMATTED(bufferIndex>=0, "Buffer Index is negative. This would corrupt memory. yOffset:%d,destWidth:%u,gdi.x1:%d",yOffset,destWidth,gdi.x1); uint32_t* pDestColor = glyphRGBABuffer.data() + (bufferIndex >= 0 ? bufferIndex : 0);//Old Index calculation- (yOffset * destWidth) + gdi.x1; const int glyphWidth = (gdi.x2 - gdi.x1); const int glyphHeight = (gdi.y1 - gdi.y2); for (int y = 0; y < glyphHeight; ++y) { for (int x = 0; x < glyphWidth; ++x) { //+ 1/5/10 CSidhall - When building the text string texture map, kerning can make certain letters overlap and stomp over the alpha of previous letters so // we need to preserve texels that were aleady set. Should be able to OR herer because we have a clean buffer and penRGB is the same. // Old code: // pDestColor[x] = ((penA * pGlyphAlpha[x] / 255) << 24) | penRGB; // New code: pDestColor[x] |= ((penA * pGlyphAlpha[x] / 255) << 24) | penRGB; // -CS } pDestColor += destWidth; pGlyphAlpha += pTI->GetStride(); } } // It would probably be faster if we kept around a surface for multiple usage instead of // continually recreating this one. EA::Raster::Surface* const pGlyphSurface = EA::Raster::CreateSurface((void*)glyphRGBABuffer.data(), destWidth, destHeight, destWidth * 4, EA::Raster::kPixelFormatTypeARGB, false, false); EA::Raster::Rect rectSrc; EA::Raster::Rect rectDest; rectSrc.w = destWidth; rectSrc.h = destHeight; rectSrc.x = 0; rectSrc.y = 0; rectDest.x = (int)point.x() + x_offset + pGraphicsContext->origin().width(); rectDest.y = (int)point.y() - yMax + pGraphicsContext->origin().height(); if (pGraphicsContext->transparencyLayer() == 1.0) EA::Raster::Blit(pGlyphSurface, &rectSrc, pSurface, &rectDest); else { const float fTransparency = pGraphicsContext->transparencyLayer(); EA::Raster::Surface* const pSurfaceWithAlpha = EA::Raster::CreateTransparentSurface(pGlyphSurface, (int)(fTransparency * 255)); EA::Raster::Blit(pSurfaceWithAlpha, &rectSrc, pSurface, &rectDest); EA::Raster::DestroySurface(pSurfaceWithAlpha); } EA::Raster::DestroySurface(pGlyphSurface); } pTI->DestroyWrapper(); NOTIFY_PROCESS_STATUS(EA::WebKit::kVProcessTypeDrawGlyph, EA::WebKit::kVProcessStatusEnded); }