bool GrLayerCache::lock(GrCachedLayer* layer, const GrTextureDesc& desc) { if (NULL != layer->texture()) { // This layer is already locked #ifdef SK_DEBUG if (!layer->rect().isEmpty()) { // It claims to be atlased SkASSERT(layer->rect().width() == desc.fWidth); SkASSERT(layer->rect().height() == desc.fHeight); } #endif return true; } #if USE_ATLAS SkIPoint16 loc; GrPlot* plot = fAtlas->addToAtlas(&fPlotUsage, desc.fWidth, desc.fHeight, NULL, &loc); if (NULL != plot) { GrIRect16 bounds = GrIRect16::MakeXYWH(loc.fX, loc.fY, SkToS16(desc.fWidth), SkToS16(desc.fHeight)); layer->setTexture(fAtlas->getTexture(), bounds); return false; } #endif // This path always uses a new scratch texture and (thus) doesn't cache anything. // This can yield a lot of re-rendering layer->setTexture(fContext->lockAndRefScratchTexture(desc, GrContext::kApprox_ScratchTexMatch), GrIRect16::MakeEmpty()); return false; }
/** GetAlphaTextureBounds succeeds but sometimes returns empty bounds like * { 0x80000000, 0x80000000, 0x80000000, 0x80000000 } * for small, but not quite zero, sized glyphs. * Only set as non-empty if the returned bounds are non-empty. */ static bool glyph_check_and_set_bounds(SkGlyph* glyph, const RECT& bbox) { if (bbox.left >= bbox.right || bbox.top >= bbox.bottom) { return false; } glyph->fWidth = SkToU16(bbox.right - bbox.left); glyph->fHeight = SkToU16(bbox.bottom - bbox.top); glyph->fLeft = SkToS16(bbox.left); glyph->fTop = SkToS16(bbox.top); return true; }
DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint) : fNext(NULL) { if (NULL != device) { device->ref(); device->lockPixels(); } fDevice = device; fX = SkToS16(x); fY = SkToS16(y); fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL; }
void SkAlphaRuns::Break(int16_t runs[], uint8_t alpha[], int x, int count) { SkASSERT(count > 0 && x >= 0); // SkAlphaRuns::BreakAt(runs, alpha, x); // SkAlphaRuns::BreakAt(&runs[x], &alpha[x], count); int16_t* next_runs = runs + x; uint8_t* next_alpha = alpha + x; while (x > 0) { int n = runs[0]; SkASSERT(n > 0); if (x < n) { alpha[x] = alpha[0]; runs[0] = SkToS16(x); runs[x] = SkToS16(n - x); break; } runs += n; alpha += n; x -= n; } runs = next_runs; alpha = next_alpha; x = count; for (;;) { int n = runs[0]; SkASSERT(n > 0); if (x < n) { alpha[x] = alpha[0]; runs[0] = SkToS16(x); runs[x] = SkToS16(n - x); break; } x -= n; if (x <= 0) break; runs += n; alpha += n; } }
void SkTime::GetDateTime(DateTime* dt) { if (dt) { SYSTEMTIME st; TIME_ZONE_INFORMATION timeZoneInfo; int tz_bias; GetLocalTime(&st); // https://gist.github.com/wrl/8924636 switch (GetTimeZoneInformation(&timeZoneInfo)) { case TIME_ZONE_ID_STANDARD: tz_bias = -timeZoneInfo.Bias - timeZoneInfo.StandardBias; break; case TIME_ZONE_ID_DAYLIGHT: tz_bias = -timeZoneInfo.Bias - timeZoneInfo.DaylightBias; break; default: tz_bias = -timeZoneInfo.Bias; break; } dt->fTimeZoneMinutes = SkToS16(tz_bias); dt->fYear = st.wYear; dt->fMonth = SkToU8(st.wMonth); dt->fDayOfWeek = SkToU8(st.wDayOfWeek); dt->fDay = SkToU8(st.wDay); dt->fHour = SkToU8(st.wHour); dt->fMinute = SkToU8(st.wMinute); dt->fSecond = SkToU8(st.wSecond); } }
void SkScalerContext_CairoFT::generateMetrics(SkGlyph* glyph) { SkASSERT(fScaledFont != NULL); cairo_text_extents_t extents; cairo_glyph_t cairoGlyph = { glyph->getGlyphID(), 0.0, 0.0 }; cairo_scaled_font_glyph_extents(fScaledFont, &cairoGlyph, 1, &extents); glyph->fAdvanceX = SkDoubleToFixed(extents.x_advance); glyph->fAdvanceY = SkDoubleToFixed(extents.y_advance); glyph->fWidth = SkToU16(SkScalarCeilToInt(extents.width)); glyph->fHeight = SkToU16(SkScalarCeilToInt(extents.height)); glyph->fLeft = SkToS16(SkScalarCeilToInt(extents.x_bearing)); glyph->fTop = SkToS16(SkScalarCeilToInt(extents.y_bearing)); glyph->fLsbDelta = 0; glyph->fRsbDelta = 0; }
const SkAdvancedTypefaceMetrics* SkPDFFont::GetMetrics(SkTypeface* typeface, SkPDFCanon* canon) { SkASSERT(typeface); SkFontID id = typeface->uniqueID(); if (std::unique_ptr<SkAdvancedTypefaceMetrics>* ptr = canon->fTypefaceMetrics.find(id)) { return ptr->get(); // canon retains ownership. } int count = typeface->countGlyphs(); if (count <= 0 || count > 1 + SK_MaxU16) { // Cache nullptr to skip this check. Use SkSafeUnref(). canon->fTypefaceMetrics.set(id, nullptr); return nullptr; } std::unique_ptr<SkAdvancedTypefaceMetrics> metrics = typeface->getAdvancedMetrics(); if (!metrics) { metrics = skstd::make_unique<SkAdvancedTypefaceMetrics>(); } if (0 == metrics->fStemV || 0 == metrics->fCapHeight) { SkPaint tmpPaint; tmpPaint.setHinting(SkPaint::kNo_Hinting); tmpPaint.setTypeface(sk_ref_sp(typeface)); tmpPaint.setTextSize(1000); // glyph coordinate system if (0 == metrics->fStemV) { // Figure out a good guess for StemV - Min width of i, I, !, 1. // This probably isn't very good with an italic font. int16_t stemV = SHRT_MAX; for (char c : {'i', 'I', '!', '1'}) { SkRect bounds; tmpPaint.measureText(&c, 1, &bounds); stemV = SkTMin(stemV, SkToS16(SkScalarRoundToInt(bounds.width()))); } metrics->fStemV = stemV; } if (0 == metrics->fCapHeight) { // Figure out a good guess for CapHeight: average the height of M and X. SkScalar capHeight = 0; for (char c : {'M', 'X'}) { SkRect bounds; tmpPaint.measureText(&c, 1, &bounds); capHeight += bounds.height(); } metrics->fCapHeight = SkToS16(SkScalarRoundToInt(capHeight / 2)); } } return canon->fTypefaceMetrics.set(id, std::move(metrics))->get(); }
void SkAlphaRuns::reset(int width) { SkASSERT(width > 0); fRuns[0] = SkToS16(width); fRuns[width] = 0; fAlpha[0] = 0; SkDEBUGCODE(fWidth = width;)
static void generate_bitmap_cache_id(const SkBitmap& bitmap, GrCacheID* id) { // Our id includes the offset, width, and height so that bitmaps created by extractSubset() // are unique. uint32_t genID = bitmap.getGenerationID(); SkIPoint origin = bitmap.pixelRefOrigin(); int16_t width = SkToS16(bitmap.width()); int16_t height = SkToS16(bitmap.height()); GrCacheID::Key key; memcpy(key.fData8 + 0, &genID, 4); memcpy(key.fData8 + 4, &origin.fX, 4); memcpy(key.fData8 + 8, &origin.fY, 4); memcpy(key.fData8 + 12, &width, 2); memcpy(key.fData8 + 14, &height, 2); static const size_t kKeyDataSize = 16; memset(key.fData8 + kKeyDataSize, 0, sizeof(key) - kKeyDataSize); GR_STATIC_ASSERT(sizeof(key) >= kKeyDataSize); static const GrCacheID::Domain gBitmapTextureDomain = GrCacheID::GenerateDomain(); id->reset(gBitmapTextureDomain, key); }
void SkAlphaRuns::reset(int width) { SkASSERT(width > 0); #ifdef SK_DEBUG sk_memset16((uint16_t*)fRuns, (uint16_t)(-42), width); #endif fRuns[0] = SkToS16(width); fRuns[width] = 0; fAlpha[0] = 0; SkDEBUGCODE(fWidth = width;)
void SkInterpolatorBase::reset(int elemCount, int frameCount) { fFlags = 0; fElemCount = SkToU8(elemCount); fFrameCount = SkToS16(frameCount); fRepeat = SK_Scalar1; if (fStorage) { sk_free(fStorage); fStorage = NULL; fTimes = NULL; SkDEBUGCODE(fTimesArray = NULL); } }
void SkTime::GetDateTime(DateTime* dt) { if (dt) { tzset(); // initialize timezone variable; time_t m_time; time(&m_time); struct tm* tstruct; tstruct = localtime(&m_time); int offset = tstruct->tm_isdst == 1 ? 60 : 0; // http://pubs.opengroup.org/onlinepubs/009695399/basedefs/time.h.html dt->fTimeZoneMinutes = SkToS16(offset - timezone / 60); dt->fYear = tstruct->tm_year + 1900; dt->fMonth = SkToU8(tstruct->tm_mon + 1); dt->fDayOfWeek = SkToU8(tstruct->tm_wday); dt->fDay = SkToU8(tstruct->tm_mday); dt->fHour = SkToU8(tstruct->tm_hour); dt->fMinute = SkToU8(tstruct->tm_min); dt->fSecond = SkToU8(tstruct->tm_sec); } }
SkAdvancedTypefaceMetrics* DWriteFontTypeface::onGetAdvancedTypefaceMetrics( PerGlyphInfo perGlyphInfo, const uint32_t* glyphIDs, uint32_t glyphIDsCount) const { SkAdvancedTypefaceMetrics* info = nullptr; HRESULT hr = S_OK; const unsigned glyphCount = fDWriteFontFace->GetGlyphCount(); DWRITE_FONT_METRICS dwfm; fDWriteFontFace->GetMetrics(&dwfm); info = new SkAdvancedTypefaceMetrics; info->fEmSize = dwfm.designUnitsPerEm; info->fLastGlyphID = SkToU16(glyphCount - 1); info->fAscent = SkToS16(dwfm.ascent); info->fDescent = SkToS16(dwfm.descent); info->fCapHeight = SkToS16(dwfm.capHeight); // SkAdvancedTypefaceMetrics::fFontName is in theory supposed to be // the PostScript name of the font. However, due to the way it is currently // used, it must actually be a family name. SkTScopedComPtr<IDWriteLocalizedStrings> familyNames; hr = fDWriteFontFamily->GetFamilyNames(&familyNames); UINT32 familyNameLen; hr = familyNames->GetStringLength(0, &familyNameLen); SkSMallocWCHAR familyName(familyNameLen+1); hr = familyNames->GetString(0, familyName.get(), familyNameLen+1); hr = sk_wchar_to_skstring(familyName.get(), familyNameLen, &info->fFontName); if (perGlyphInfo & kToUnicode_PerGlyphInfo) { populate_glyph_to_unicode(fDWriteFontFace.get(), glyphCount, &(info->fGlyphToUnicode)); } DWRITE_FONT_FACE_TYPE fontType = fDWriteFontFace->GetType(); if (fontType != DWRITE_FONT_FACE_TYPE_TRUETYPE && fontType != DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION) { return info; } // Simulated fonts aren't really TrueType fonts. if (fDWriteFontFace->GetSimulations() == DWRITE_FONT_SIMULATIONS_NONE) { info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font; } AutoTDWriteTable<SkOTTableHead> headTable(fDWriteFontFace.get()); AutoTDWriteTable<SkOTTablePostScript> postTable(fDWriteFontFace.get()); AutoTDWriteTable<SkOTTableHorizontalHeader> hheaTable(fDWriteFontFace.get()); AutoTDWriteTable<SkOTTableOS2> os2Table(fDWriteFontFace.get()); if (!headTable.fExists || !postTable.fExists || !hheaTable.fExists || !os2Table.fExists) { return info; } //There exist CJK fonts which set the IsFixedPitch and Monospace bits, //but have full width, latin half-width, and half-width kana. bool fixedWidth = (postTable->isFixedPitch && (1 == SkEndian_SwapBE16(hheaTable->numberOfHMetrics))); //Monospace if (fixedWidth) { info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style; } //Italic if (os2Table->version.v0.fsSelection.field.Italic) { info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style; } //Serif using SerifStyle = SkPanose::Data::TextAndDisplay::SerifStyle; SerifStyle serifStyle = os2Table->version.v0.panose.data.textAndDisplay.bSerifStyle; if (SkPanose::FamilyType::TextAndDisplay == os2Table->version.v0.panose.bFamilyType) { if (SerifStyle::Cove == serifStyle || SerifStyle::ObtuseCove == serifStyle || SerifStyle::SquareCove == serifStyle || SerifStyle::ObtuseSquareCove == serifStyle || SerifStyle::Square == serifStyle || SerifStyle::Thin == serifStyle || SerifStyle::Bone == serifStyle || SerifStyle::Exaggerated == serifStyle || SerifStyle::Triangle == serifStyle) { info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style; } //Script } else if (SkPanose::FamilyType::Script == os2Table->version.v0.panose.bFamilyType) { info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style; } info->fItalicAngle = SkEndian_SwapBE32(postTable->italicAngle) >> 16; info->fBBox = SkIRect::MakeLTRB((int32_t)SkEndian_SwapBE16((uint16_t)headTable->xMin), (int32_t)SkEndian_SwapBE16((uint16_t)headTable->yMax), (int32_t)SkEndian_SwapBE16((uint16_t)headTable->xMax), (int32_t)SkEndian_SwapBE16((uint16_t)headTable->yMin)); return info; }
// TODO(egouriou): Take advantage of periods in the convolution. // Practical resizing filters are periodic outside of the border area. // For Lanczos, a scaling by a (reduced) factor of p/q (q pixels in the // source become p pixels in the destination) will have a period of p. // A nice consequence is a period of 1 when downscaling by an integral // factor. Downscaling from typical display resolutions is also bound // to produce interesting periods as those are chosen to have multiple // small factors. // Small periods reduce computational load and improve cache usage if // the coefficients can be shared. For periods of 1 we can consider // loading the factors only once outside the borders. void SkResizeFilter::computeFilters(int srcSize, float destSubsetLo, float destSubsetSize, float scale, SkConvolutionFilter1D* output, const SkConvolutionProcs& convolveProcs) { float destSubsetHi = destSubsetLo + destSubsetSize; // [lo, hi) // When we're doing a magnification, the scale will be larger than one. This // means the destination pixels are much smaller than the source pixels, and // that the range covered by the filter won't necessarily cover any source // pixel boundaries. Therefore, we use these clamped values (max of 1) for // some computations. float clampedScale = SkTMin(1.0f, scale); // This is how many source pixels from the center we need to count // to support the filtering function. float srcSupport = fBitmapFilter->width() / clampedScale; float invScale = 1.0f / scale; SkSTArray<64, float, true> filterValuesArray; SkSTArray<64, SkConvolutionFilter1D::ConvolutionFixed, true> fixedFilterValuesArray; // Loop over all pixels in the output range. We will generate one set of // filter values for each one. Those values will tell us how to blend the // source pixels to compute the destination pixel. // This is the pixel in the source directly under the pixel in the dest. // Note that we base computations on the "center" of the pixels. To see // why, observe that the destination pixel at coordinates (0, 0) in a 5.0x // downscale should "cover" the pixels around the pixel with *its center* // at coordinates (2.5, 2.5) in the source, not those around (0, 0). // Hence we need to scale coordinates (0.5, 0.5), not (0, 0). destSubsetLo = SkScalarFloorToScalar(destSubsetLo); destSubsetHi = SkScalarCeilToScalar(destSubsetHi); float srcPixel = (destSubsetLo + 0.5f) * invScale; int destLimit = SkScalarTruncToInt(destSubsetHi - destSubsetLo); output->reserveAdditional(destLimit, SkScalarCeilToInt(destLimit * srcSupport * 2)); for (int destI = 0; destI < destLimit; srcPixel += invScale, destI++) { // Compute the (inclusive) range of source pixels the filter covers. float srcBegin = SkTMax(0.f, SkScalarFloorToScalar(srcPixel - srcSupport)); float srcEnd = SkTMin(srcSize - 1.f, SkScalarCeilToScalar(srcPixel + srcSupport)); // Compute the unnormalized filter value at each location of the source // it covers. // Sum of the filter values for normalizing. // Distance from the center of the filter, this is the filter coordinate // in source space. We also need to consider the center of the pixel // when comparing distance against 'srcPixel'. In the 5x downscale // example used above the distance from the center of the filter to // the pixel with coordinates (2, 2) should be 0, because its center // is at (2.5, 2.5). float destFilterDist = (srcBegin + 0.5f - srcPixel) * clampedScale; int filterCount = SkScalarTruncToInt(srcEnd - srcBegin) + 1; if (filterCount <= 0) { // true when srcSize is equal to srcPixel - srcSupport; this may be a bug return; } filterValuesArray.reset(filterCount); float filterSum = fBitmapFilter->evaluate_n(destFilterDist, clampedScale, filterCount, filterValuesArray.begin()); // The filter must be normalized so that we don't affect the brightness of // the image. Convert to normalized fixed point. int fixedSum = 0; fixedFilterValuesArray.reset(filterCount); const float* filterValues = filterValuesArray.begin(); SkConvolutionFilter1D::ConvolutionFixed* fixedFilterValues = fixedFilterValuesArray.begin(); float invFilterSum = 1 / filterSum; for (int fixedI = 0; fixedI < filterCount; fixedI++) { int curFixed = SkConvolutionFilter1D::FloatToFixed(filterValues[fixedI] * invFilterSum); fixedSum += curFixed; fixedFilterValues[fixedI] = SkToS16(curFixed); } SkASSERT(fixedSum <= 0x7FFF); // The conversion to fixed point will leave some rounding errors, which // we add back in to avoid affecting the brightness of the image. We // arbitrarily add this to the center of the filter array (this won't always // be the center of the filter function since it could get clipped on the // edges, but it doesn't matter enough to worry about that case). int leftovers = SkConvolutionFilter1D::FloatToFixed(1) - fixedSum; fixedFilterValues[filterCount / 2] += leftovers; // Now it's ready to go. output->AddFilter(SkScalarFloorToInt(srcBegin), fixedFilterValues, filterCount); } if (convolveProcs.fApplySIMDPadding) { convolveProcs.fApplySIMDPadding(output); } }
void SkPDFType0Font::getFontSubset(SkPDFCanon* canon) { const SkAdvancedTypefaceMetrics* metricsPtr = SkPDFFont::GetMetrics(this->typeface(), canon); SkASSERT(metricsPtr); if (!metricsPtr) { return; } const SkAdvancedTypefaceMetrics& metrics = *metricsPtr; SkASSERT(can_embed(metrics)); SkAdvancedTypefaceMetrics::FontType type = this->getType(); SkTypeface* face = this->typeface(); SkASSERT(face); auto descriptor = sk_make_sp<SkPDFDict>("FontDescriptor"); uint16_t emSize = SkToU16(this->typeface()->getUnitsPerEm()); add_common_font_descriptor_entries(descriptor.get(), metrics, emSize , 0); int ttcIndex; std::unique_ptr<SkStreamAsset> fontAsset(face->openStream(&ttcIndex)); size_t fontSize = fontAsset ? fontAsset->getLength() : 0; if (0 == fontSize) { SkDebugf("Error: (SkTypeface)(%p)::openStream() returned " "empty stream (%p) when identified as kType1CID_Font " "or kTrueType_Font.\n", face, fontAsset.get()); } else { switch (type) { case SkAdvancedTypefaceMetrics::kTrueType_Font: { #ifdef SK_PDF_USE_SFNTLY if (!SkToBool(metrics.fFlags & SkAdvancedTypefaceMetrics::kNotSubsettable_FontFlag)) { sk_sp<SkPDFStream> subsetStream = get_subset_font_stream( std::move(fontAsset), this->glyphUsage(), metrics.fFontName.c_str(), ttcIndex); if (subsetStream) { descriptor->insertObjRef("FontFile2", std::move(subsetStream)); break; } // If subsetting fails, fall back to original font data. fontAsset.reset(face->openStream(&ttcIndex)); SkASSERT(fontAsset); SkASSERT(fontAsset->getLength() == fontSize); if (!fontAsset || fontAsset->getLength() == 0) { break; } } #endif // SK_PDF_USE_SFNTLY auto fontStream = sk_make_sp<SkPDFSharedStream>(std::move(fontAsset)); fontStream->dict()->insertInt("Length1", fontSize); descriptor->insertObjRef("FontFile2", std::move(fontStream)); break; } case SkAdvancedTypefaceMetrics::kType1CID_Font: { auto fontStream = sk_make_sp<SkPDFSharedStream>(std::move(fontAsset)); fontStream->dict()->insertName("Subtype", "CIDFontType0C"); descriptor->insertObjRef("FontFile3", std::move(fontStream)); break; } default: SkASSERT(false); } } auto newCIDFont = sk_make_sp<SkPDFDict>("Font"); newCIDFont->insertObjRef("FontDescriptor", std::move(descriptor)); newCIDFont->insertName("BaseFont", metrics.fPostScriptName); switch (type) { case SkAdvancedTypefaceMetrics::kType1CID_Font: newCIDFont->insertName("Subtype", "CIDFontType0"); break; case SkAdvancedTypefaceMetrics::kTrueType_Font: newCIDFont->insertName("Subtype", "CIDFontType2"); newCIDFont->insertName("CIDToGIDMap", "Identity"); break; default: SkASSERT(false); } auto sysInfo = sk_make_sp<SkPDFDict>(); sysInfo->insertString("Registry", "Adobe"); sysInfo->insertString("Ordering", "Identity"); sysInfo->insertInt("Supplement", 0); newCIDFont->insertObject("CIDSystemInfo", std::move(sysInfo)); int16_t defaultWidth = 0; { int emSize; auto glyphCache = SkPDFFont::MakeVectorCache(face, &emSize); sk_sp<SkPDFArray> widths = SkPDFMakeCIDGlyphWidthsArray( glyphCache.get(), &this->glyphUsage(), SkToS16(emSize), &defaultWidth); if (widths && widths->size() > 0) { newCIDFont->insertObject("W", std::move(widths)); } newCIDFont->insertScalar( "DW", scaleFromFontUnits(defaultWidth, SkToS16(emSize))); } //////////////////////////////////////////////////////////////////////////// this->insertName("Subtype", "Type0"); this->insertName("BaseFont", metrics.fPostScriptName); this->insertName("Encoding", "Identity-H"); auto descendantFonts = sk_make_sp<SkPDFArray>(); descendantFonts->appendObjRef(std::move(newCIDFont)); this->insertObject("DescendantFonts", std::move(descendantFonts)); if (metrics.fGlyphToUnicode.count() > 0) { this->insertObjRef("ToUnicode", SkPDFMakeToUnicodeCmap(metrics.fGlyphToUnicode, &this->glyphUsage(), multiByteGlyphs(), firstGlyphID(), lastGlyphID())); } SkDEBUGCODE(fPopulated = true); return; }
void SkScalerContext_CairoFT::generateMetrics(SkGlyph* glyph) { SkASSERT(fScaledFont != nullptr); glyph->zeroMetrics(); CairoLockedFTFace faceLock(fScaledFont); FT_Face face = faceLock.getFace(); FT_Error err = FT_Load_Glyph( face, glyph->getGlyphID(), fLoadGlyphFlags ); if (err != 0) { return; } prepareGlyph(face->glyph); switch (face->glyph->format) { case FT_GLYPH_FORMAT_OUTLINE: if (!face->glyph->outline.n_contours) { break; } FT_BBox bbox; FT_Outline_Get_CBox(&face->glyph->outline, &bbox); bbox.xMin &= ~63; bbox.yMin &= ~63; bbox.xMax = (bbox.xMax + 63) & ~63; bbox.yMax = (bbox.yMax + 63) & ~63; glyph->fWidth = SkToU16(SkFDot6Floor(bbox.xMax - bbox.xMin)); glyph->fHeight = SkToU16(SkFDot6Floor(bbox.yMax - bbox.yMin)); glyph->fTop = -SkToS16(SkFDot6Floor(bbox.yMax)); glyph->fLeft = SkToS16(SkFDot6Floor(bbox.xMin)); if (isLCD(fRec) && gSetLcdFilter && (fLcdFilter == FT_LCD_FILTER_DEFAULT || fLcdFilter == FT_LCD_FILTER_LIGHT)) { if (fRec.fFlags & kLCD_Vertical_Flag) { glyph->fTop -= 1; glyph->fHeight += 2; } else { glyph->fLeft -= 1; glyph->fWidth += 2; } } break; case FT_GLYPH_FORMAT_BITMAP: #ifdef FT_LOAD_COLOR if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { glyph->fMaskFormat = SkMask::kARGB32_Format; } #endif if (isLCD(fRec)) { fRec.fMaskFormat = SkMask::kA8_Format; } if (fHaveShape) { // Apply the shape matrix to the glyph's bounding box. SkMatrix matrix; fRec.getSingleMatrix(&matrix); matrix.preScale(SkScalarInvert(fScaleX), SkScalarInvert(fScaleY)); SkRect srcRect = SkRect::MakeXYWH( SkIntToScalar(face->glyph->bitmap_left), -SkIntToScalar(face->glyph->bitmap_top), SkIntToScalar(face->glyph->bitmap.width), SkIntToScalar(face->glyph->bitmap.rows)); SkRect destRect; matrix.mapRect(&destRect, srcRect); SkIRect glyphRect = destRect.roundOut(); glyph->fWidth = SkToU16(glyphRect.width()); glyph->fHeight = SkToU16(glyphRect.height()); glyph->fTop = SkToS16(SkScalarRoundToInt(destRect.fTop)); glyph->fLeft = SkToS16(SkScalarRoundToInt(destRect.fLeft)); } else { glyph->fWidth = SkToU16(face->glyph->bitmap.width); glyph->fHeight = SkToU16(face->glyph->bitmap.rows); glyph->fTop = -SkToS16(face->glyph->bitmap_top); glyph->fLeft = SkToS16(face->glyph->bitmap_left); } break; default: SkDEBUGFAIL("unknown glyph format"); return; } if (fRec.fFlags & SkScalerContext::kVertical_Flag) { glyph->fAdvanceX = -SkFDot6ToFloat(face->glyph->advance.x); glyph->fAdvanceY = SkFDot6ToFloat(face->glyph->advance.y); } else { glyph->fAdvanceX = SkFDot6ToFloat(face->glyph->advance.x); glyph->fAdvanceY = -SkFDot6ToFloat(face->glyph->advance.y); } }
SkAdvancedTypefaceMetrics* DWriteFontTypeface::onGetAdvancedTypefaceMetrics( PerGlyphInfo perGlyphInfo, const uint32_t* glyphIDs, uint32_t glyphIDsCount) const { SkAdvancedTypefaceMetrics* info = nullptr; HRESULT hr = S_OK; const unsigned glyphCount = fDWriteFontFace->GetGlyphCount(); DWRITE_FONT_METRICS dwfm; fDWriteFontFace->GetMetrics(&dwfm); info = new SkAdvancedTypefaceMetrics; info->fEmSize = dwfm.designUnitsPerEm; info->fLastGlyphID = SkToU16(glyphCount - 1); // SkAdvancedTypefaceMetrics::fFontName is in theory supposed to be // the PostScript name of the font. However, due to the way it is currently // used, it must actually be a family name. SkTScopedComPtr<IDWriteLocalizedStrings> familyNames; hr = fDWriteFontFamily->GetFamilyNames(&familyNames); UINT32 familyNameLen; hr = familyNames->GetStringLength(0, &familyNameLen); SkSMallocWCHAR familyName(familyNameLen+1); hr = familyNames->GetString(0, familyName.get(), familyNameLen+1); hr = sk_wchar_to_skstring(familyName.get(), familyNameLen, &info->fFontName); if (perGlyphInfo & kToUnicode_PerGlyphInfo) { populate_glyph_to_unicode(fDWriteFontFace.get(), glyphCount, &(info->fGlyphToUnicode)); } DWRITE_FONT_FACE_TYPE fontType = fDWriteFontFace->GetType(); if (fontType == DWRITE_FONT_FACE_TYPE_TRUETYPE || fontType == DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION) { info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font; } else { info->fAscent = dwfm.ascent; info->fDescent = dwfm.descent; info->fCapHeight = dwfm.capHeight; return info; } AutoTDWriteTable<SkOTTableHead> headTable(fDWriteFontFace.get()); AutoTDWriteTable<SkOTTablePostScript> postTable(fDWriteFontFace.get()); AutoTDWriteTable<SkOTTableHorizontalHeader> hheaTable(fDWriteFontFace.get()); AutoTDWriteTable<SkOTTableOS2> os2Table(fDWriteFontFace.get()); if (!headTable.fExists || !postTable.fExists || !hheaTable.fExists || !os2Table.fExists) { info->fAscent = dwfm.ascent; info->fDescent = dwfm.descent; info->fCapHeight = dwfm.capHeight; return info; } //There exist CJK fonts which set the IsFixedPitch and Monospace bits, //but have full width, latin half-width, and half-width kana. bool fixedWidth = (postTable->isFixedPitch && (1 == SkEndian_SwapBE16(hheaTable->numberOfHMetrics))); //Monospace if (fixedWidth) { info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style; } //Italic if (os2Table->version.v0.fsSelection.field.Italic) { info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style; } //Script if (SkPanose::FamilyType::Script == os2Table->version.v0.panose.bFamilyType.value) { info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style; //Serif } else if (SkPanose::FamilyType::TextAndDisplay == os2Table->version.v0.panose.bFamilyType.value && SkPanose::Data::TextAndDisplay::SerifStyle::Triangle <= os2Table->version.v0.panose.data.textAndDisplay.bSerifStyle.value && SkPanose::Data::TextAndDisplay::SerifStyle::NoFit != os2Table->version.v0.panose.data.textAndDisplay.bSerifStyle.value) { info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style; } info->fItalicAngle = SkEndian_SwapBE32(postTable->italicAngle) >> 16; info->fAscent = SkToS16(dwfm.ascent); info->fDescent = SkToS16(dwfm.descent); info->fCapHeight = SkToS16(dwfm.capHeight); info->fBBox = SkIRect::MakeLTRB((int32_t)SkEndian_SwapBE16((uint16_t)headTable->xMin), (int32_t)SkEndian_SwapBE16((uint16_t)headTable->yMax), (int32_t)SkEndian_SwapBE16((uint16_t)headTable->xMax), (int32_t)SkEndian_SwapBE16((uint16_t)headTable->yMin)); //TODO: is this even desired? It seems PDF only wants this value for Type1 //fonts, and we only get here for TrueType fonts. info->fStemV = 0; /* // Figure out a good guess for StemV - Min width of i, I, !, 1. // This probably isn't very good with an italic font. int16_t min_width = SHRT_MAX; info->fStemV = 0; char stem_chars[] = {'i', 'I', '!', '1'}; for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) { ABC abcWidths; if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) { int16_t width = abcWidths.abcB; if (width > 0 && width < min_width) { min_width = width; info->fStemV = min_width; } } } */ if (perGlyphInfo & kHAdvance_PerGlyphInfo) { if (fixedWidth) { SkAdvancedTypefaceMetrics::WidthRange range(0); int16_t advance; getWidthAdvance(fDWriteFontFace.get(), 1, &advance); range.fAdvance.append(1, &advance); SkAdvancedTypefaceMetrics::FinishRange( &range, 0, SkAdvancedTypefaceMetrics::WidthRange::kDefault); info->fGlyphWidths.emplace_back(std::move(range)); } else { IDWriteFontFace* borrowedFontFace = fDWriteFontFace.get(); info->setGlyphWidths( glyphCount, glyphIDs, glyphIDsCount, SkAdvancedTypefaceMetrics::GetAdvance([borrowedFontFace](int gId, int16_t* data) { return getWidthAdvance(borrowedFontFace, gId, data); }) ); } } return info; }