Quellcode-Bibliothek SkGlyphRunPainter.cpp
Sprache: C
/* * Copyright 2018 The Android Open Source Project * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file.
*/
namespace {
SkScalerContextFlags compute_scaler_context_flags(const SkColorSpace* cs) { // If we're doing linear blending, then we can disable the gamma hacks. // Otherwise, leave them on. In either case, we still want the contrast boost: // TODO: Can we be even smarter about mask gamma based on the dest transfer function? if (cs && cs->gammaIsLinear()) { return SkScalerContextFlags::kBoostContrast;
} else { return SkScalerContextFlags::kFakeGammaAndBoostContrast;
}
}
// TODO: collect this up into a single class when all the details are worked out. // This is duplicate code. The original is in SubRunContainer.cpp.
std::tuple<SkZip<const SkGlyph*, SkPoint>, SkZip<SkGlyphID, SkPoint>>
prepare_for_path_drawing(SkStrike* strike,
SkZip<const SkGlyphID, const SkPoint> source,
SkZip<const SkGlyph*, SkPoint> acceptedBuffer,
SkZip<SkGlyphID, SkPoint> rejectedBuffer) { int acceptedSize = 0; int rejectedSize = 0;
strike->lock(); for (auto [glyphID, pos] : source) { if (!SkIsFinite(pos.x(), pos.y())) { continue;
} const SkPackedGlyphID packedID{glyphID}; switch (SkGlyphDigest digest = strike->digestFor(kPath, packedID);
digest.actionFor(kPath)) { case GlyphAction::kAccept:
acceptedBuffer[acceptedSize++] = std::make_tuple(strike->glyph(digest), pos); break; case GlyphAction::kReject:
rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos); break; default: break;
}
}
strike->unlock(); return {acceptedBuffer.first(acceptedSize), rejectedBuffer.first(rejectedSize)};
}
// TODO: collect this up into a single class when all the details are worked out. // This is duplicate code. The original is in SubRunContainer.cpp.
std::tuple<SkZip<const SkGlyph*, SkPoint>, SkZip<SkGlyphID, SkPoint>>
prepare_for_drawable_drawing(SkStrike* strike,
SkZip<const SkGlyphID, const SkPoint> source,
SkZip<const SkGlyph*, SkPoint> acceptedBuffer,
SkZip<SkGlyphID, SkPoint> rejectedBuffer) { int acceptedSize = 0; int rejectedSize = 0;
strike->lock(); for (auto [glyphID, pos] : source) { if (!SkIsFinite(pos.x(), pos.y())) { continue;
} const SkPackedGlyphID packedID{glyphID}; switch (SkGlyphDigest digest = strike->digestFor(kDrawable, packedID);
digest.actionFor(kDrawable)) { case GlyphAction::kAccept:
acceptedBuffer[acceptedSize++] = std::make_tuple(strike->glyph(digest), pos); break; case GlyphAction::kReject:
rejectedBuffer[rejectedSize++] = std::make_tuple(glyphID, pos); break; default: break;
}
}
strike->unlock(); return {acceptedBuffer.first(acceptedSize), rejectedBuffer.first(rejectedSize)};
}
// Build up the mapping from source space to device space. Add the rounding constant // halfSampleFreq, so we just need to floor to get the device result.
SkMatrix positionMatrixWithRounding = creationMatrix;
positionMatrixWithRounding.postTranslate(halfSampleFreq.x(), halfSampleFreq.y());
int acceptedSize = 0; int rejectedSize = 0;
strike->lock(); for (auto [glyphID, pos] : source) { if (!SkIsFinite(pos.x(), pos.y())) { continue;
}
// The bitmap blitters can only draw lcd text to a N32 bitmap in srcOver. Otherwise, // convert the lcd text into A8 text. The props communicate this to the scaler. auto& props = (kN32_SkColorType == fColorType && paint.isSrcOver())
? fDeviceProps
: fBitmapFallbackProps;
if (SkStrikeSpec::ShouldDrawAsPath(paint, runFont, positionMatrix)) { auto [strikeSpec, strikeToSourceScale] =
SkStrikeSpec::MakePath(runFont, paint, props, fScalerContextFlags);
auto strike = strikeSpec.findOrCreateStrike();
{ auto [accepted, rejected] = prepare_for_path_drawing(strike.get(),
source,
acceptedBuffer,
rejectedBuffer);
source = rejected; // The paint we draw paths with must have the same anti-aliasing state as the // runFont allowing the paths to have the same edging as the glyph masks.
SkPaint pathPaint = paint;
pathPaint.setAntiAlias(runFont.hasSomeAntiAliasing());
auto [accepted, rejected] = prepare_for_direct_mask_drawing(strike.get(),
positionMatrix,
source,
acceptedBuffer,
rejectedBuffer);
source = rejected;
bitmapDevice->paintMasks(accepted, paint);
} if (!source.empty()) {
std::vector<SkPoint> sourcePositions;
// Create a strike is source space to calculate scale information.
SkStrikeSpec scaleStrikeSpec = SkStrikeSpec::MakeMask(
runFont, paint, props, fScalerContextFlags, SkMatrix::I());
SkBulkGlyphMetrics metrics{scaleStrikeSpec};
auto glyphIDs = source.get<0>(); auto positions = source.get<1>();
SkSpan<const SkGlyph*> glyphs = metrics.glyphs(glyphIDs);
SkScalar maxScale = SK_ScalarMin;
// Calculate the scale that makes the longest edge 1:1 with its side in the cache. for (auto [glyph, pos] : SkMakeZip(glyphs, positions)) { if (glyph->isEmpty()) { continue;
}
SkPoint corners[4];
SkPoint srcPos = pos + drawOrigin; // Store off the positions in device space to position the glyphs during drawing.
sourcePositions.push_back(srcPos);
SkRect rect = glyph->rect();
rect.makeOffset(srcPos);
positionMatrix.mapRectToQuad(corners, rect); // left top -> right top
SkScalar scale = (corners[1] - corners[0]).length() / rect.width();
maxScale = std::max(maxScale, scale); // right top -> right bottom
scale = (corners[2] - corners[1]).length() / rect.height();
maxScale = std::max(maxScale, scale); // right bottom -> left bottom
scale = (corners[3] - corners[2]).length() / rect.width();
maxScale = std::max(maxScale, scale); // left bottom -> left top
scale = (corners[0] - corners[3]).length() / rect.height();
maxScale = std::max(maxScale, scale);
}
if (maxScale <= 0) { continue; // to the next run.
}
auto [accepted, rejected] = prepare_for_direct_mask_drawing(strike.get(),
positionMatrix,
source,
acceptedBuffer,
rejectedBuffer); const SkScalar invMaxScale = 1.0f/maxScale; for (auto [glyph, srcPos] : SkMakeZip(accepted.get<0>(), sourcePositions)) {
SkMask mask = glyph->mask(); // TODO: is this needed will A8 and BW just work? if (mask.fFormat != SkMask::kARGB32_Format) { continue;
}
SkBitmap bm;
bm.installPixels(SkImageInfo::MakeN32Premul(mask.fBounds.size()), const_cast<uint8_t*>(mask.fImage),
mask.fRowBytes);
bm.setImmutable();
// Since the glyph in the cache is scaled by maxScale, its top left vector is too // long. Reduce it to find proper positions on the device.
SkPoint realPos =
srcPos + SkPoint::Make(mask.fBounds.left(), mask.fBounds.top())*invMaxScale;
// Calculate the preConcat matrix for drawBitmap to get the rectangle from the // glyph cache (which is multiplied by maxScale) to land in the right place.
SkMatrix translate = SkMatrix::Translate(realPos);
translate.preScale(invMaxScale, invMaxScale);
// Draw the bitmap using the rect from the scaled cache, and not the source // rectangle for the glyph.
bitmapDevice->drawBitmap(bm, translate, nullptr, SkFilterMode::kLinear, paint);
}
}
// TODO: have the mask stage above reject the glyphs that are too big, and handle the // rejects in a more sophisticated stage.
}
}
Messung V0.5
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.6Bemerkung:
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.