|  | /* | 
|  | * Copyright (C) 2013 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | #include "MinikinSkia.h" | 
|  |  | 
|  | #include <SkFontDescriptor.h> | 
|  | #include <SkFont.h> | 
|  | #include <SkFontMetrics.h> | 
|  | #include <SkFontMgr.h> | 
|  | #include <SkTypeface.h> | 
|  | #include <log/log.h> | 
|  |  | 
|  | #include <minikin/Font.h> | 
|  | #include <minikin/MinikinExtent.h> | 
|  | #include <minikin/MinikinPaint.h> | 
|  | #include <minikin/MinikinRect.h> | 
|  |  | 
|  | namespace android { | 
|  |  | 
|  | MinikinFontSkia::MinikinFontSkia(sk_sp<SkTypeface> typeface, const void* fontData, size_t fontSize, | 
|  | std::string_view filePath, int ttcIndex, | 
|  | const std::vector<minikin::FontVariation>& axes) | 
|  | : mTypeface(std::move(typeface)) | 
|  | , mFontData(fontData) | 
|  | , mFontSize(fontSize) | 
|  | , mTtcIndex(ttcIndex) | 
|  | , mAxes(axes) | 
|  | , mFilePath(filePath) {} | 
|  |  | 
|  | static void MinikinFontSkia_SetSkiaFont(const minikin::MinikinFont* font, SkFont* skFont, | 
|  | const minikin::MinikinPaint& paint, | 
|  | const minikin::FontFakery& fakery) { | 
|  | skFont->setSize(paint.size); | 
|  | skFont->setScaleX(paint.scaleX); | 
|  | skFont->setSkewX(paint.skewX); | 
|  | MinikinFontSkia::unpackFontFlags(skFont, paint.fontFlags); | 
|  | // Apply font fakery on top of user-supplied flags. | 
|  | MinikinFontSkia::populateSkFont(skFont, font, fakery); | 
|  | } | 
|  |  | 
|  | float MinikinFontSkia::GetHorizontalAdvance(uint32_t glyph_id, const minikin::MinikinPaint& paint, | 
|  | const minikin::FontFakery& fakery) const { | 
|  | SkFont skFont; | 
|  | uint16_t glyph16 = glyph_id; | 
|  | SkScalar skWidth; | 
|  | MinikinFontSkia_SetSkiaFont(this, &skFont, paint, fakery); | 
|  | skFont.getWidths(&glyph16, 1, &skWidth); | 
|  | #ifdef VERBOSE | 
|  | ALOGD("width for typeface %d glyph %d = %f", mTypeface->uniqueID(), glyph_id, skWidth); | 
|  | #endif | 
|  | return skWidth; | 
|  | } | 
|  |  | 
|  | void MinikinFontSkia::GetHorizontalAdvances(uint16_t* glyph_ids, uint32_t count, | 
|  | const minikin::MinikinPaint& paint, | 
|  | const minikin::FontFakery& fakery, | 
|  | float* outAdvances) const { | 
|  | SkFont skFont; | 
|  | MinikinFontSkia_SetSkiaFont(this, &skFont, paint, fakery); | 
|  | skFont.getWidths(glyph_ids, count, outAdvances); | 
|  | } | 
|  |  | 
|  | void MinikinFontSkia::GetBounds(minikin::MinikinRect* bounds, uint32_t glyph_id, | 
|  | const minikin::MinikinPaint& paint, | 
|  | const minikin::FontFakery& fakery) const { | 
|  | SkFont skFont; | 
|  | uint16_t glyph16 = glyph_id; | 
|  | SkRect skBounds; | 
|  | MinikinFontSkia_SetSkiaFont(this, &skFont, paint, fakery); | 
|  | skFont.getWidths(&glyph16, 1, nullptr, &skBounds); | 
|  | bounds->mLeft = skBounds.fLeft; | 
|  | bounds->mTop = skBounds.fTop; | 
|  | bounds->mRight = skBounds.fRight; | 
|  | bounds->mBottom = skBounds.fBottom; | 
|  | } | 
|  |  | 
|  | void MinikinFontSkia::GetFontExtent(minikin::MinikinExtent* extent, | 
|  | const minikin::MinikinPaint& paint, | 
|  | const minikin::FontFakery& fakery) const { | 
|  | SkFont skFont; | 
|  | MinikinFontSkia_SetSkiaFont(this, &skFont, paint, fakery); | 
|  | SkFontMetrics metrics; | 
|  | skFont.getMetrics(&metrics); | 
|  | extent->ascent = metrics.fAscent; | 
|  | extent->descent = metrics.fDescent; | 
|  | } | 
|  |  | 
|  | SkTypeface* MinikinFontSkia::GetSkTypeface() const { | 
|  | return mTypeface.get(); | 
|  | } | 
|  |  | 
|  | sk_sp<SkTypeface> MinikinFontSkia::RefSkTypeface() const { | 
|  | return mTypeface; | 
|  | } | 
|  |  | 
|  | const void* MinikinFontSkia::GetFontData() const { | 
|  | return mFontData; | 
|  | } | 
|  |  | 
|  | size_t MinikinFontSkia::GetFontSize() const { | 
|  | return mFontSize; | 
|  | } | 
|  |  | 
|  | int MinikinFontSkia::GetFontIndex() const { | 
|  | return mTtcIndex; | 
|  | } | 
|  |  | 
|  | const std::vector<minikin::FontVariation>& MinikinFontSkia::GetAxes() const { | 
|  | return mAxes; | 
|  | } | 
|  |  | 
|  | std::shared_ptr<minikin::MinikinFont> MinikinFontSkia::createFontWithVariation( | 
|  | const std::vector<minikin::FontVariation>& variations) const { | 
|  | SkFontArguments args; | 
|  |  | 
|  | int ttcIndex; | 
|  | std::unique_ptr<SkStreamAsset> stream(mTypeface->openStream(&ttcIndex)); | 
|  | LOG_ALWAYS_FATAL_IF(stream == nullptr, "openStream failed"); | 
|  |  | 
|  | args.setCollectionIndex(ttcIndex); | 
|  | std::vector<SkFontArguments::VariationPosition::Coordinate> skVariation; | 
|  | skVariation.resize(variations.size()); | 
|  | for (size_t i = 0; i < variations.size(); i++) { | 
|  | skVariation[i].axis = variations[i].axisTag; | 
|  | skVariation[i].value = SkFloatToScalar(variations[i].value); | 
|  | } | 
|  | args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())}); | 
|  | sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); | 
|  | sk_sp<SkTypeface> face(fm->makeFromStream(std::move(stream), args)); | 
|  |  | 
|  | return std::make_shared<MinikinFontSkia>(std::move(face), mFontData, mFontSize, mFilePath, | 
|  | ttcIndex, variations); | 
|  | } | 
|  |  | 
|  | // hinting<<16 | edging<<8 | bools:5bits | 
|  | uint32_t MinikinFontSkia::packFontFlags(const SkFont& font) { | 
|  | uint32_t flags = (unsigned)font.getHinting() << 16; | 
|  | flags |= (unsigned)font.getEdging() << 8; | 
|  | flags |= font.isEmbolden()          << minikin::Embolden_Shift; | 
|  | flags |= font.isLinearMetrics()     << minikin::LinearMetrics_Shift; | 
|  | flags |= font.isSubpixel()          << minikin::Subpixel_Shift; | 
|  | flags |= font.isEmbeddedBitmaps()   << minikin::EmbeddedBitmaps_Shift; | 
|  | flags |= font.isForceAutoHinting()  << minikin::ForceAutoHinting_Shift; | 
|  | return flags; | 
|  | } | 
|  |  | 
|  | void MinikinFontSkia::unpackFontFlags(SkFont* font, uint32_t flags) { | 
|  | // We store hinting in the top 16 bits (only need 2 of them) | 
|  | font->setHinting((SkFontHinting)(flags >> 16)); | 
|  | // We store edging in bits 8:15 (only need 2 of them) | 
|  | font->setEdging((SkFont::Edging)((flags >> 8) & 0xFF)); | 
|  | font->setEmbolden(        (flags & minikin::Embolden_Flag) != 0); | 
|  | font->setLinearMetrics(   (flags & minikin::LinearMetrics_Flag) != 0); | 
|  | font->setSubpixel(        (flags & minikin::Subpixel_Flag) != 0); | 
|  | font->setEmbeddedBitmaps( (flags & minikin::EmbeddedBitmaps_Flag) != 0); | 
|  | font->setForceAutoHinting((flags & minikin::ForceAutoHinting_Flag) != 0); | 
|  | } | 
|  |  | 
|  | void MinikinFontSkia::populateSkFont(SkFont* skFont, const MinikinFont* font, | 
|  | minikin::FontFakery fakery) { | 
|  | skFont->setTypeface(reinterpret_cast<const MinikinFontSkia*>(font)->RefSkTypeface()); | 
|  | skFont->setEmbolden(skFont->isEmbolden() || fakery.isFakeBold()); | 
|  | if (fakery.isFakeItalic()) { | 
|  | skFont->setSkewX(skFont->getSkewX() - 0.25f); | 
|  | } | 
|  | } | 
|  | } |