blob: dd17ad67366f73f764fcf5b8d71f3ec6675fbff2 [file] [log] [blame]
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "GrAtlasTextOp.h"
#include "GrOpFlushState.h"
#include "GrResourceProvider.h"
#include "SkGlyphCache.h"
#include "SkMathPriv.h"
#include "effects/GrBitmapTextGeoProc.h"
#include "effects/GrDistanceFieldGeoProc.h"
#include "text/GrAtlasGlyphCache.h"
///////////////////////////////////////////////////////////////////////////////////////////////////
static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
unsigned r = SkColorGetR(c);
unsigned g = SkColorGetG(c);
unsigned b = SkColorGetB(c);
return GrColorPackRGBA(r, g, b, 0xff);
}
static const int kDistanceAdjustLumShift = 5;
SkString GrAtlasTextOp::dumpInfo() const {
SkString str;
for (int i = 0; i < fGeoCount; ++i) {
str.appendf("%d: Color: 0x%08x Trans: %.2f,%.2f Runs: %d\n",
i,
fGeoData[i].fColor,
fGeoData[i].fX,
fGeoData[i].fY,
fGeoData[i].fBlob->runCount());
}
str.append(DumpPipelineInfo(*this->pipeline()));
str.append(INHERITED::dumpInfo());
return str;
}
void GrAtlasTextOp::getPipelineAnalysisInput(GrPipelineAnalysisDrawOpInput* input) const {
if (kColorBitmapMask_MaskType == fMaskType) {
input->pipelineColorInput()->setUnknownFourComponents();
} else {
input->pipelineColorInput()->setKnownFourComponents(fColor);
}
switch (fMaskType) {
case kGrayscaleDistanceField_MaskType:
case kGrayscaleCoverageMask_MaskType:
input->pipelineCoverageInput()->setUnknownSingleComponent();
break;
case kLCDCoverageMask_MaskType:
case kLCDDistanceField_MaskType:
input->pipelineCoverageInput()->setUnknownOpaqueFourComponents();
input->pipelineCoverageInput()->setUsingLCDCoverage();
break;
case kColorBitmapMask_MaskType:
input->pipelineCoverageInput()->setKnownSingleComponent(0xff);
}
}
void GrAtlasTextOp::applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) {
optimizations.getOverrideColorIfSet(&fGeoData[0].fColor);
fColor = fGeoData[0].fColor;
fUsesLocalCoords = optimizations.readsLocalCoords();
}
void GrAtlasTextOp::onPrepareDraws(Target* target) const {
// if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
// TODO actually only invert if we don't have RGBA
SkMatrix localMatrix;
if (this->usesLocalCoords() && !this->viewMatrix().invert(&localMatrix)) {
SkDebugf("Cannot invert viewmatrix\n");
return;
}
GrTexture* texture = fFontCache->getTexture(this->maskFormat());
if (!texture) {
SkDebugf("Could not allocate backing texture for atlas\n");
return;
}
GrMaskFormat maskFormat = this->maskFormat();
FlushInfo flushInfo;
if (this->usesDistanceFields()) {
flushInfo.fGeometryProcessor =
this->setupDfProcessor(this->viewMatrix(), fFilteredColor, this->color(), texture);
} else {
GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kNone_FilterMode);
flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
this->color(), texture, params, maskFormat, localMatrix, this->usesLocalCoords());
}
flushInfo.fGlyphsToFlush = 0;
size_t vertexStride = flushInfo.fGeometryProcessor->getVertexStride();
SkASSERT(vertexStride == GrAtlasTextBlob::GetVertexStride(maskFormat));
int glyphCount = this->numGlyphs();
const GrBuffer* vertexBuffer;
void* vertices = target->makeVertexSpace(
vertexStride, glyphCount * kVerticesPerGlyph, &vertexBuffer, &flushInfo.fVertexOffset);
flushInfo.fVertexBuffer.reset(SkRef(vertexBuffer));
flushInfo.fIndexBuffer.reset(target->resourceProvider()->refQuadIndexBuffer());
if (!vertices || !flushInfo.fVertexBuffer) {
SkDebugf("Could not allocate vertices\n");
return;
}
unsigned char* currVertex = reinterpret_cast<unsigned char*>(vertices);
GrBlobRegenHelper helper(this, target, &flushInfo);
SkAutoGlyphCache glyphCache;
for (int i = 0; i < fGeoCount; i++) {
const Geometry& args = fGeoData[i];
Blob* blob = args.fBlob;
size_t byteCount;
void* blobVertices;
int subRunGlyphCount;
blob->regenInOp(target, fFontCache, &helper, args.fRun, args.fSubRun, &glyphCache,
vertexStride, args.fViewMatrix, args.fX, args.fY, args.fColor,
&blobVertices, &byteCount, &subRunGlyphCount);
// now copy all vertices
memcpy(currVertex, blobVertices, byteCount);
#ifdef SK_DEBUG
// bounds sanity check
SkRect rect;
rect.setLargestInverted();
SkPoint* vertex = (SkPoint*)((char*)blobVertices);
rect.growToInclude(vertex, vertexStride, kVerticesPerGlyph * subRunGlyphCount);
if (this->usesDistanceFields()) {
args.fViewMatrix.mapRect(&rect);
}
// Allow for small numerical error in the bounds.
SkRect bounds = this->bounds();
bounds.outset(0.001f, 0.001f);
SkASSERT(bounds.contains(rect));
#endif
currVertex += byteCount;
}
this->flush(target, &flushInfo);
}
void GrAtlasTextOp::flush(GrMeshDrawOp::Target* target, FlushInfo* flushInfo) const {
GrMesh mesh;
int maxGlyphsPerDraw =
static_cast<int>(flushInfo->fIndexBuffer->gpuMemorySize() / sizeof(uint16_t) / 6);
mesh.initInstanced(kTriangles_GrPrimitiveType, flushInfo->fVertexBuffer.get(),
flushInfo->fIndexBuffer.get(), flushInfo->fVertexOffset, kVerticesPerGlyph,
kIndicesPerGlyph, flushInfo->fGlyphsToFlush, maxGlyphsPerDraw);
target->draw(flushInfo->fGeometryProcessor.get(), mesh);
flushInfo->fVertexOffset += kVerticesPerGlyph * flushInfo->fGlyphsToFlush;
flushInfo->fGlyphsToFlush = 0;
}
bool GrAtlasTextOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) {
GrAtlasTextOp* that = t->cast<GrAtlasTextOp>();
if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
that->bounds(), caps)) {
return false;
}
if (fMaskType != that->fMaskType) {
return false;
}
if (!this->usesDistanceFields()) {
if (kColorBitmapMask_MaskType == fMaskType && this->color() != that->color()) {
return false;
}
if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
return false;
}
} else {
if (!this->viewMatrix().cheapEqualTo(that->viewMatrix())) {
return false;
}
if (fFilteredColor != that->fFilteredColor) {
return false;
}
if (fUseBGR != that->fUseBGR) {
return false;
}
}
fNumGlyphs += that->numGlyphs();
// Reallocate space for geo data if necessary and then import that's geo data.
int newGeoCount = that->fGeoCount + fGeoCount;
// We assume (and here enforce) that the allocation size is the smallest power of two that
// is greater than or equal to the number of geometries (and at least
// kMinGeometryAllocated).
int newAllocSize = GrNextPow2(newGeoCount);
int currAllocSize = SkTMax<int>(kMinGeometryAllocated, GrNextPow2(fGeoCount));
if (newGeoCount > currAllocSize) {
fGeoData.realloc(newAllocSize);
}
// We steal the ref on the blobs from the other AtlasTextOp and set its count to 0 so that
// it doesn't try to unref them.
memcpy(&fGeoData[fGeoCount], that->fGeoData.get(), that->fGeoCount * sizeof(Geometry));
#ifdef SK_DEBUG
for (int i = 0; i < that->fGeoCount; ++i) {
that->fGeoData.get()[i].fBlob = (Blob*)0x1;
}
#endif
that->fGeoCount = 0;
fGeoCount = newGeoCount;
this->joinBounds(*that);
return true;
}
// TODO just use class params
// TODO trying to figure out why lcd is so whack
sk_sp<GrGeometryProcessor> GrAtlasTextOp::setupDfProcessor(const SkMatrix& viewMatrix,
SkColor filteredColor,
GrColor color,
GrTexture* texture) const {
GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kBilerp_FilterMode);
bool isLCD = this->isLCD();
// set up any flags
uint32_t flags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
flags |= viewMatrix.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
flags |= fUseGammaCorrectDistanceTable ? kGammaCorrect_DistanceFieldEffectFlag : 0;
// see if we need to create a new effect
if (isLCD) {
flags |= kUseLCD_DistanceFieldEffectFlag;
flags |= fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
float redCorrection = fDistanceAdjustTable->getAdjustment(
GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift,
fUseGammaCorrectDistanceTable);
float greenCorrection = fDistanceAdjustTable->getAdjustment(
GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift,
fUseGammaCorrectDistanceTable);
float blueCorrection = fDistanceAdjustTable->getAdjustment(
GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift,
fUseGammaCorrectDistanceTable);
GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(
redCorrection, greenCorrection, blueCorrection);
return GrDistanceFieldLCDTextGeoProc::Make(
color, viewMatrix, texture, params, widthAdjust, flags, this->usesLocalCoords());
} else {
#ifdef SK_GAMMA_APPLY_TO_A8
U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredColor);
float correction = fDistanceAdjustTable->getAdjustment(lum >> kDistanceAdjustLumShift,
fUseGammaCorrectDistanceTable);
return GrDistanceFieldA8TextGeoProc::Make(
color, viewMatrix, texture, params, correction, flags, this->usesLocalCoords());
#else
return GrDistanceFieldA8TextGeoProc::Make(
color, viewMatrix, texture, params, flags, this->usesLocalCoords());
#endif
}
}
void GrBlobRegenHelper::flush() { fOp->flush(fTarget, fFlushInfo); }