void SkRandomScalerContext::generateAdvance(SkGlyph* glyph) { fProxy->getAdvance(glyph); SkVector advance; fMatrix.mapXY(SkFixedToScalar(glyph->fAdvanceX), SkFixedToScalar(glyph->fAdvanceY), &advance); glyph->fAdvanceX = SkScalarToFixed(advance.fX); glyph->fAdvanceY = SkScalarToFixed(advance.fY); }
void SkRandomScalerContext::generateMetrics(SkGlyph* glyph) { fProxy->getAdvance(glyph); SkVector advance; fMatrix.mapXY(SkFixedToScalar(glyph->fAdvanceX), SkFixedToScalar(glyph->fAdvanceY), &advance); glyph->fAdvanceX = SkScalarToFixed(advance.fX); glyph->fAdvanceY = SkScalarToFixed(advance.fY); SkPath path; fProxy->getPath(*glyph, &path); path.transform(fMatrix); SkRect storage; const SkPaint& paint = fFace->paint(); const SkRect& newBounds = paint.doComputeFastBounds(path.getBounds(), &storage, SkPaint::kFill_Style); SkIRect ibounds; newBounds.roundOut(&ibounds); glyph->fLeft = ibounds.fLeft; glyph->fTop = ibounds.fTop; glyph->fWidth = ibounds.width(); glyph->fHeight = ibounds.height(); // Here we will change the mask format of the glyph // NOTE this is being overridden by the base class SkMask::Format format; switch (glyph->getGlyphID() % 6) { case 0: format = SkMask::kLCD16_Format; break; case 1: format = SkMask::kA8_Format; break; case 2: format = SkMask::kARGB32_Format; break; default: // we will fiddle with these in generate image format = (SkMask::Format)MASK_FORMAT_UNKNOWN; } glyph->fMaskFormat = format; }
const char* SkParse::FindScalar(const char str[], SkScalar* value) { SkASSERT(str); str = skip_ws(str); #ifdef SK_SCALAR_IS_FLOAT char* stop; float v = (float)strtod(str, &stop); if (str == stop) { return NULL; } if (value) { *value = v; } return stop; #else int sign = 0; if (*str == '-') { sign = -1; str += 1; } if (!is_digit(*str) && *str != '.') return NULL; int n = 0; while (is_digit(*str)) { n = 10*n + *str - '0'; if (n > 0x7FFF) return NULL; str += 1; } n <<= 16; if (*str == '.') { static const int gFractions[] = { (1 << 24) / 10, (1 << 24) / 100, (1 << 24) / 1000, (1 << 24) / 10000, (1 << 24) / 100000 }; str += 1; int d = 0; const int* fraction = gFractions; const int* end = &fraction[SK_ARRAY_COUNT(gFractions)]; while (is_digit(*str) && fraction < end) d += (*str++ - '0') * *fraction++; d += 0x80; // round n += d >> 8; } while (is_digit(*str)) str += 1; if (value) { n = (n ^ sign) - sign; // apply the sign *value = SkFixedToScalar(n); } #endif return str; }
void SkGLDevice::drawDevice(const SkDraw& draw, SkDevice* dev, int x, int y, const SkPaint& paint) { TRACE_DRAW("coreDrawDevice", this, draw); SkGLDevice::TexOrientation to = ((SkGLDevice*)dev)->bindDeviceAsTexture(); if (SkGLDevice::kNo_TexOrientation != to) { SkGLClipIter* iter = this->updateMatrixClip(); const SkBitmap& bm = dev->accessBitmap(false); int w = bm.width(); int h = bm.height(); SkPoint max; max.set(SkFixedToScalar(w << (16 - SkNextLog2(bm.rowBytesAsPixels()))), SkFixedToScalar(h << (16 - SkNextLog2(h)))); if (SkGLDevice::kBottomToTop_TexOrientation == to) { h = -h; } gl_drawSprite(x, y, w, h, max, paint, iter); } }
SkGradientShaderBase::GpuColorType SkGradientShaderBase::getGpuColorType(SkColor colors[3]) const { if (fColorCount <= 3) { memcpy(colors, fOrigColors, fColorCount * sizeof(SkColor)); } if (SkShader::kClamp_TileMode == fTileMode) { if (2 == fColorCount) { return kTwo_GpuColorType; } else if (3 == fColorCount && (SkScalarAbs( SkFixedToScalar(fRecs[1].fPos) - SK_ScalarHalf) < SK_Scalar1 / 1000)) { return kThree_GpuColorType; } } return kTexture_GpuColorType; }
void SkProgressView::onDraw(SkCanvas* canvas) { if (fMax == 0) return; SkFixed percent; if (fInterp) { SkScalar x; if (fInterp->timeToValues(SkTime::GetMSecs(), &x) == SkInterpolator::kFreezeEnd_Result) { delete fInterp; fInterp = NULL; } percent = (SkFixed)x; // now its 16.8 percent = SkMax32(0, SkMin32(percent, fMax << 8)); // now its pinned percent = SkFixedDiv(percent, fMax << 8); // now its 0.16 this->inval(NULL); } else { U16CPU value = SkMax32(0, SkMin32(fValue, fMax)); percent = SkFixedDiv(value, fMax); } SkRect r; SkPaint p; r.set(0, 0, this->width(), this->height()); p.setAntiAlias(true); r.fRight = r.fLeft + SkScalarMul(r.width(), SkFixedToScalar(percent)); p.setStyle(SkPaint::kFill_Style); p.setColor(SK_ColorDKGRAY); p.setShader(fOnShader); canvas->drawRect(r, p); p.setColor(SK_ColorWHITE); p.setShader(fOffShader); r.fLeft = r.fRight; r.fRight = this->width() - SK_Scalar1; if (r.width() > 0) canvas->drawRect(r, p); }
SkGradientShaderBase::SkGradientShaderBase(SkReadBuffer& buffer) : INHERITED(buffer) { if (buffer.isVersionLT(SkReadBuffer::kNoUnitMappers_Version)) { // skip the old SkUnitMapper slot buffer.skipFlattenable(); } int colorCount = fColorCount = buffer.getArrayCount(); if (colorCount > kColorStorageCount) { size_t allocSize = (sizeof(SkColor) + sizeof(SkScalar) + sizeof(Rec)) * colorCount; if (buffer.validateAvailable(allocSize)) { fOrigColors = reinterpret_cast<SkColor*>(sk_malloc_throw(allocSize)); } else { fOrigColors = NULL; colorCount = fColorCount = 0; } } else { fOrigColors = fStorage; } buffer.readColorArray(fOrigColors, colorCount); fOrigPos = (SkScalar*)(fOrigColors + colorCount); { uint32_t packed = buffer.readUInt(); fGradFlags = SkToU8(unpack_flags(packed)); fTileMode = unpack_mode(packed); } fTileProc = gTileProcs[fTileMode]; fRecs = (Rec*)(fOrigPos + colorCount); if (colorCount > 2) { Rec* recs = fRecs; recs[0].fPos = 0; fOrigPos[0] = 0; for (int i = 1; i < colorCount; i++) { recs[i].fPos = buffer.readInt(); recs[i].fScale = buffer.readUInt(); fOrigPos[i] = SkFixedToScalar(recs[i].fPos); } } else { fOrigPos = NULL; } buffer.readMatrix(&fPtsToUnit); this->initCommon(); }
SkScalar SkUnitCubicInterp(SkScalar value, SkScalar bx, SkScalar by, SkScalar cx, SkScalar cy) { // pin to the unit-square, and convert to 2.14 Dot14 x = pin_and_convert(value); if (x == 0) return 0; if (x == Dot14_ONE) return SK_Scalar1; Dot14 b = pin_and_convert(bx); Dot14 c = pin_and_convert(cx); // Now compute our coefficients from the control points // t -> 3b // t^2 -> 3c - 6b // t^3 -> 3b - 3c + 1 Dot14 A = 3*b; Dot14 B = 3*(c - 2*b); Dot14 C = 3*(b - c) + Dot14_ONE; // Now search for a t value given x Dot14 t = Dot14_HALF; Dot14 dt = Dot14_HALF; for (int i = 0; i < 13; i++) { dt >>= 1; Dot14 guess = eval_cubic(t, A, B, C); if (x < guess) { t -= dt; } else { t += dt; } } // Now we have t, so compute the coeff for Y and evaluate b = pin_and_convert(by); c = pin_and_convert(cy); A = 3*b; B = 3*(c - 2*b); C = 3*(b - c) + Dot14_ONE; return SkFixedToScalar(eval_cubic(t, A, B, C) << 2); }
void SkSliderView::onDraw(SkCanvas* canvas) { this->INHERITED::onDraw(canvas); U16CPU value = SkMax32(0, SkMin32(fValue, fMax)); SkRect r; SkPaint p; r.set(0, 0, this->width(), this->height()); p.setAntiAliasOn(true); p.setStyle(SkPaint::kStroke_Style); p.setStrokeWidth(SK_Scalar1); r.inset(SK_Scalar1/2, SK_Scalar1/2); canvas->drawRect(r, p); if (fMax) { SkFixed percent = SkFixedDiv(value, fMax); r.inset(SK_Scalar1/2, SK_Scalar1/2); r.fRight = r.fLeft + SkScalarMul(r.width(), SkFixedToScalar(percent)); p.setStyle(SkPaint::kFill_Style); setgrad(&p, r); canvas->drawRect(r, p); } #if 0 r.set(0, 0, this->width(), this->height()); r.inset(SK_Scalar1, SK_Scalar1); r.inset(r.width()/2, 0); p.setColor(SK_ColorBLACK); canvas->drawLine(*(SkPoint*)&r.fLeft, *(SkPoint*)&r.fRight, p); #endif }
void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath, SkPath* devPath, SkMatrix* fillToDevMatrix) { SkPath path; generatePath(glyph, &path); if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) { SkFixed dx = glyph.getSubXFixed(); SkFixed dy = glyph.getSubYFixed(); if (dx | dy) { path.offset(SkFixedToScalar(dx), SkFixedToScalar(dy)); } } if (fRec.fFrameWidth > 0 || fPathEffect != nullptr) { // need the path in user-space, with only the point-size applied // so that our stroking and effects will operate the same way they // would if the user had extracted the path themself, and then // called drawPath SkPath localPath; SkMatrix matrix, inverse; fRec.getMatrixFrom2x2(&matrix); if (!matrix.invert(&inverse)) { // assume fillPath and devPath are already empty. return; } path.transform(inverse, &localPath); // now localPath is only affected by the paint settings, and not the canvas matrix SkStrokeRec rec(SkStrokeRec::kFill_InitStyle); if (fRec.fFrameWidth > 0) { rec.setStrokeStyle(fRec.fFrameWidth, SkToBool(fRec.fFlags & kFrameAndFill_Flag)); // glyphs are always closed contours, so cap type is ignored, // so we just pass something. rec.setStrokeParams((SkPaint::Cap)fRec.fStrokeCap, (SkPaint::Join)fRec.fStrokeJoin, fRec.fMiterLimit); } if (fPathEffect) { SkPath effectPath; if (fPathEffect->filterPath(&effectPath, localPath, &rec, nullptr)) { localPath.swap(effectPath); } } if (rec.needToApply()) { SkPath strokePath; if (rec.applyToPath(&strokePath, localPath)) { localPath.swap(strokePath); } } // now return stuff to the caller if (fillToDevMatrix) { *fillToDevMatrix = matrix; } if (devPath) { localPath.transform(matrix, devPath); } if (fillPath) { fillPath->swap(localPath); } } else { // nothing tricky to do if (fillToDevMatrix) { fillToDevMatrix->reset(); } if (devPath) { if (fillPath == nullptr) { devPath->swap(path); } else { *devPath = path; } } if (fillPath) { fillPath->swap(path); } } if (devPath) { devPath->updateBoundsCache(); } if (fillPath) { fillPath->updateBoundsCache(); } }
void GrStencilAndCoverTextContext::onDrawText(GrDrawContext* drawContext, GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint, const SkPaint& skPaint, const SkMatrix& viewMatrix, const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkIRect& regionClipBounds) { SkASSERT(byteLength == 0 || text != NULL); if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) { return; } // This is the slow path, mainly used by Skia unit tests. The other // backends (8888, gpu, ...) use device-space dependent glyph caches. In // order to match the glyph positions that the other code paths produce, we // must also use device-space dependent glyph cache. This has the // side-effect that the glyph shape outline will be in device-space, // too. This in turn has the side-effect that NVPR can not stroke the paths, // as the stroke in NVPR is defined in object-space. // NOTE: here we have following coincidence that works at the moment: // - When using the device-space glyphs, the transforms we pass to NVPR // instanced drawing are the global transforms, and the view transform is // identity. NVPR can not use non-affine transforms in the instanced // drawing. This is taken care of by SkDraw::ShouldDrawTextAsPaths since it // will turn off the use of device-space glyphs when perspective transforms // are in use. this->init(rt, clip, paint, skPaint, byteLength, kMaxAccuracy_RenderMode, viewMatrix, regionClipBounds); // Transform our starting point. if (fUsingDeviceSpaceGlyphs) { SkPoint loc; fContextInitialMatrix.mapXY(x, y, &loc); x = loc.fX; y = loc.fY; } SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); const char* stop = text + byteLength; // Measure first if needed. if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) { SkFixed stopX = 0; SkFixed stopY = 0; const char* textPtr = text; while (textPtr < stop) { // We don't need x, y here, since all subpixel variants will have the // same advance. const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &textPtr, 0, 0); stopX += glyph.fAdvanceX; stopY += glyph.fAdvanceY; } SkASSERT(textPtr == stop); SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio; SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio; if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) { alignX = SkScalarHalf(alignX); alignY = SkScalarHalf(alignY); } x -= alignX; y -= alignY; } SkAutoKern autokern; SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio); SkFixed fx = SkScalarToFixed(x); SkFixed fy = SkScalarToFixed(y); while (text < stop) { const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0); fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio); if (glyph.fWidth) { this->appendGlyph(drawContext, glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy))); } fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio); fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio); } this->finish(drawContext); }
/** Return the next pseudo random number expressed as a SkScalar in the range [-SK_Scalar1..SK_Scalar1). */ SkScalar nextSScalar1() { return SkFixedToScalar(this->nextSFixed1()); }
SkPowerMode(SkFlattenableReadBuffer& b) : SkXfermode(b) { // read the exponent this->init(SkFixedToScalar(b.readS32())); }
void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath, SkPath* devPath, SkMatrix* fillToDevMatrix) { SkPath path; this->getGlyphContext(glyph)->generatePath(glyph, &path); if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) { SkFixed dx = glyph.getSubXFixed(); SkFixed dy = glyph.getSubYFixed(); if (dx | dy) { path.offset(SkFixedToScalar(dx), SkFixedToScalar(dy)); } } if (fRec.fFrameWidth > 0 || fPathEffect != NULL) { // need the path in user-space, with only the point-size applied // so that our stroking and effects will operate the same way they // would if the user had extracted the path themself, and then // called drawPath SkPath localPath; SkMatrix matrix, inverse; fRec.getMatrixFrom2x2(&matrix); matrix.invert(&inverse); path.transform(inverse, &localPath); // now localPath is only affected by the paint settings, and not the canvas matrix SkScalar width = fRec.fFrameWidth; if (fPathEffect) { SkPath effectPath; if (fPathEffect->filterPath(&effectPath, localPath, &width)) { localPath.swap(effectPath); } } if (width > 0) { SkStroke stroker; SkPath outline; stroker.setWidth(width); stroker.setMiterLimit(fRec.fMiterLimit); stroker.setJoin((SkPaint::Join)fRec.fStrokeJoin); stroker.setDoFill(SkToBool(fRec.fFlags & kFrameAndFill_Flag)); stroker.strokePath(localPath, &outline); localPath.swap(outline); } // now return stuff to the caller if (fillToDevMatrix) { *fillToDevMatrix = matrix; } if (devPath) { localPath.transform(matrix, devPath); } if (fillPath) { fillPath->swap(localPath); } } else { // nothing tricky to do if (fillToDevMatrix) { fillToDevMatrix->reset(); } if (devPath) { if (fillPath == NULL) { devPath->swap(path); } else { *devPath = path; } } if (fillPath) { fillPath->swap(path); } } if (devPath) { devPath->updateBoundsCache(); } if (fillPath) { fillPath->updateBoundsCache(); } }
void GrDistanceFieldTextContext::drawPackedGlyph(GrGlyph::PackedID packed, GrFixed vx, GrFixed vy, GrFontScaler* scaler) { if (NULL == fDrawTarget) { return; } if (NULL == fStrike) { fStrike = fContext->getFontCache()->getStrike(scaler, true); } GrGlyph* glyph = fStrike->getGlyph(packed, scaler); if (NULL == glyph || glyph->fBounds.isEmpty()) { return; } SkScalar sx = SkFixedToScalar(vx); SkScalar sy = SkFixedToScalar(vy); /* // not valid, need to find a different solution for this vx += SkIntToFixed(glyph->fBounds.fLeft); vy += SkIntToFixed(glyph->fBounds.fTop); // keep them as ints until we've done the clip-test GrFixed width = glyph->fBounds.width(); GrFixed height = glyph->fBounds.height(); // check if we clipped out if (true || NULL == glyph->fPlot) { int x = vx >> 16; int y = vy >> 16; if (fClipRect.quickReject(x, y, x + width, y + height)) { // SkCLZ(3); // so we can set a break-point in the debugger return; } } */ if (NULL == glyph->fPlot) { if (fStrike->getGlyphAtlas(glyph, scaler)) { goto HAS_ATLAS; } // try to clear out an unused plot before we flush fContext->getFontCache()->freePlotExceptFor(fStrike); if (fStrike->getGlyphAtlas(glyph, scaler)) { goto HAS_ATLAS; } if (c_DumpFontCache) { #ifdef SK_DEVELOPER fContext->getFontCache()->dump(); #endif } // before we purge the cache, we must flush any accumulated draws this->flushGlyphs(); fContext->flush(); // try to purge fContext->getFontCache()->purgeExceptFor(fStrike); // need to use new flush count here if (fStrike->getGlyphAtlas(glyph, scaler)) { goto HAS_ATLAS; } if (NULL == glyph->fPath) { SkPath* path = SkNEW(SkPath); if (!scaler->getGlyphPath(glyph->glyphID(), path)) { // flag the glyph as being dead? delete path; return; } glyph->fPath = path; } GrContext::AutoMatrix am; SkMatrix translate; translate.setTranslate(sx, sy); GrPaint tmpPaint(fPaint); am.setPreConcat(fContext, translate, &tmpPaint); SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle); fContext->drawPath(tmpPaint, *glyph->fPath, stroke); return; } HAS_ATLAS: SkASSERT(glyph->fPlot); GrDrawTarget::DrawToken drawToken = fDrawTarget->getCurrentDrawToken(); glyph->fPlot->setDrawToken(drawToken); GrTexture* texture = glyph->fPlot->texture(); SkASSERT(texture); if (fCurrTexture != texture || fCurrVertex + 4 > fMaxVertices) { this->flushGlyphs(); fCurrTexture = texture; fCurrTexture->ref(); } if (NULL == fVertices) { // If we need to reserve vertices allow the draw target to suggest // a number of verts to reserve and whether to perform a flush. fMaxVertices = kMinRequestedVerts; fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>( SK_ARRAY_COUNT(gTextVertexAttribs)); bool flush = fDrawTarget->geometryHints(&fMaxVertices, NULL); if (flush) { this->flushGlyphs(); fContext->flush(); fDrawTarget->drawState()->setVertexAttribs<gTextVertexAttribs>( SK_ARRAY_COUNT(gTextVertexAttribs)); } fMaxVertices = kDefaultRequestedVerts; // ignore return, no point in flushing again. fDrawTarget->geometryHints(&fMaxVertices, NULL); int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads(); if (fMaxVertices < kMinRequestedVerts) { fMaxVertices = kDefaultRequestedVerts; } else if (fMaxVertices > maxQuadVertices) { // don't exceed the limit of the index buffer fMaxVertices = maxQuadVertices; } bool success = fDrawTarget->reserveVertexAndIndexSpace(fMaxVertices, 0, GrTCast<void**>(&fVertices), NULL); GrAlwaysAssert(success); SkASSERT(2*sizeof(GrPoint) == fDrawTarget->getDrawState().getVertexSize()); } SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft); SkScalar dy = SkIntToScalar(glyph->fBounds.fTop); SkScalar width = SkIntToScalar(glyph->fBounds.width()); SkScalar height = SkIntToScalar(glyph->fBounds.height()); SkScalar scale = fTextRatio; dx *= scale; dy *= scale; sx += dx; sy += dy; width *= scale; height *= scale; GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX); GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY); GrFixed tw = SkIntToFixed(glyph->fBounds.width()); GrFixed th = SkIntToFixed(glyph->fBounds.height()); fVertices[2*fCurrVertex].setRectFan(sx, sy, sx + width, sy + height, 2 * sizeof(SkPoint)); fVertices[2*fCurrVertex+1].setRectFan(SkFixedToFloat(texture->normalizeFixedX(tx)), SkFixedToFloat(texture->normalizeFixedY(ty)), SkFixedToFloat(texture->normalizeFixedX(tx + tw)), SkFixedToFloat(texture->normalizeFixedY(ty + th)), 2 * sizeof(SkPoint)); fCurrVertex += 4; }
static SkScalar FIXEDToSkScalar(FIXED fixed) { SkFixed skFixed; memcpy(&skFixed, &fixed, sizeof(SkFixed)); return SkFixedToScalar(skFixed); }
GLuint SkGL::BindNewTexture(const SkBitmap& origBitmap, SkPoint* max) { SkBitmap tmpBitmap; const SkBitmap* bitmap = &origBitmap; if (needToPromoteTo32bit(origBitmap)) { origBitmap.copyTo(&tmpBitmap, SkBitmap::kARGB_8888_Config); // now bitmap points to our temp, which has been promoted to 32bits bitmap = &tmpBitmap; } GLenum format, type; if (!canBeTexture(*bitmap, &format, &type)) { return 0; } SkAutoLockPixels alp(*bitmap); if (!bitmap->readyToDraw()) { return 0; } GLuint textureName; glGenTextures(1, &textureName); glBindTexture(GL_TEXTURE_2D, textureName); // express rowbytes as a number of pixels for ow int ow = bitmap->rowBytesAsPixels(); int oh = bitmap->height(); int nw = SkNextPow2(ow); int nh = SkNextPow2(oh); glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); // check if we need to scale to create power-of-2 dimensions #ifdef SK_GL_SUPPORT_COMPRESSEDTEXIMAGE2D if (SkBitmap::kIndex8_Config == bitmap->config()) { size_t imagesize = bitmap->getSize() + SK_GL_SIZE_OF_PALETTE; SkAutoMalloc storage(imagesize); build_compressed_data(storage.get(), *bitmap); // we only support POW2 here (GLES 1.0 restriction) SkASSERT(ow == nw); SkASSERT(oh == nh); glCompressedTexImage2D(GL_TEXTURE_2D, 0, format, ow, oh, 0, imagesize, storage.get()); } else // fall through to non-compressed logic #endif { if (ow != nw || oh != nh) { glTexImage2D(GL_TEXTURE_2D, 0, format, nw, nh, 0, format, type, NULL); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ow, oh, format, type, bitmap->getPixels()); } else { // easy case, the bitmap is already pow2 glTexImage2D(GL_TEXTURE_2D, 0, format, ow, oh, 0, format, type, bitmap->getPixels()); } } #ifdef TRACE_TEXTURE_CREATION SkDebugf("--- new texture [%d] size=(%d %d) bpp=%d\n", textureName, ow, oh, bitmap->bytesPerPixel()); #endif if (max) { max->fX = SkFixedToScalar(bitmap->width() << (16 - SkNextLog2(nw))); max->fY = SkFixedToScalar(oh << (16 - SkNextLog2(nh))); } return textureName; }
void GrDistanceFieldTextContext::onDrawPosText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint, const SkPaint& skPaint, const SkMatrix& viewMatrix, const char text[], size_t byteLength, const SkScalar pos[], int scalarsPerPosition, const SkPoint& offset, const SkIRect& regionClipBounds) { SkASSERT(byteLength == 0 || text != NULL); SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); // nothing to draw if (text == NULL || byteLength == 0 /* no raster clip? || fRC->isEmpty()*/) { return; } fViewMatrix = viewMatrix; this->init(rt, clip, paint, skPaint, regionClipBounds); SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); SkAutoGlyphCacheNoGamma autoCache(fSkPaint, &fDeviceProperties, NULL); SkGlyphCache* cache = autoCache.getCache(); GrFontScaler* fontScaler = GetGrFontScaler(cache); int numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL); fTotalVertexCount = kVerticesPerGlyph*numGlyphs; const char* stop = text + byteLength; SkTArray<char> fallbackTxt; SkTArray<SkScalar> fallbackPos; if (SkPaint::kLeft_Align == fSkPaint.getTextAlign()) { while (text < stop) { const char* lastText = text; // the last 2 parameters are ignored const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); if (glyph.fWidth) { SkScalar x = offset.x() + pos[0]; SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0); if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), glyph.getSubXFixed(), glyph.getSubYFixed(), GrGlyph::kDistance_MaskStyle), x, y, fontScaler)) { // couldn't append, send to fallback fallbackTxt.push_back_n(SkToInt(text-lastText), lastText); fallbackPos.push_back(pos[0]); if (2 == scalarsPerPosition) { fallbackPos.push_back(pos[1]); } } } pos += scalarsPerPosition; } } else { SkScalar alignMul = SkPaint::kCenter_Align == fSkPaint.getTextAlign() ? SK_ScalarHalf : SK_Scalar1; while (text < stop) { const char* lastText = text; // the last 2 parameters are ignored const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); if (glyph.fWidth) { SkScalar x = offset.x() + pos[0]; SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0); SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX)*alignMul*fTextRatio; SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY)*alignMul*fTextRatio; if (!this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), glyph.getSubXFixed(), glyph.getSubYFixed(), GrGlyph::kDistance_MaskStyle), x - advanceX, y - advanceY, fontScaler)) { // couldn't append, send to fallback fallbackTxt.push_back_n(SkToInt(text-lastText), lastText); fallbackPos.push_back(pos[0]); if (2 == scalarsPerPosition) { fallbackPos.push_back(pos[1]); } } } pos += scalarsPerPosition; } } this->finish(); if (fallbackTxt.count() > 0) { fFallbackTextContext->drawPosText(rt, clip, paint, skPaint, viewMatrix, fallbackTxt.begin(), fallbackTxt.count(), fallbackPos.begin(), scalarsPerPosition, offset, regionClipBounds); } }
void GrStencilAndCoverTextContext::TextRun::setText(const char text[], size_t byteLength, SkScalar x, SkScalar y) { SkASSERT(byteLength == 0 || text != nullptr); SkGlyphCache* glyphCache = this->getGlyphCache(); SkDrawCacheProc glyphCacheProc = fFont.getDrawCacheProc(); fTotalGlyphCount = fFont.countText(text, byteLength); fInstanceData.reset(InstanceData::Alloc(GrPathRendering::kTranslate_PathTransformType, fTotalGlyphCount)); const char* stop = text + byteLength; // Measure first if needed. if (fFont.getTextAlign() != SkPaint::kLeft_Align) { SkFixed stopX = 0; SkFixed stopY = 0; const char* textPtr = text; while (textPtr < stop) { // We don't need x, y here, since all subpixel variants will have the // same advance. const SkGlyph& glyph = glyphCacheProc(glyphCache, &textPtr, 0, 0); stopX += glyph.fAdvanceX; stopY += glyph.fAdvanceY; } SkASSERT(textPtr == stop); SkScalar alignX = SkFixedToScalar(stopX) * fTextRatio; SkScalar alignY = SkFixedToScalar(stopY) * fTextRatio; if (fFont.getTextAlign() == SkPaint::kCenter_Align) { alignX = SkScalarHalf(alignX); alignY = SkScalarHalf(alignY); } x -= alignX; y -= alignY; } SkAutoKern autokern; SkFixed fixedSizeRatio = SkScalarToFixed(fTextRatio); SkFixed fx = SkScalarToFixed(x); SkFixed fy = SkScalarToFixed(y); FallbackBlobBuilder fallback; while (text < stop) { const SkGlyph& glyph = glyphCacheProc(glyphCache, &text, 0, 0); fx += SkFixedMul(autokern.adjust(glyph), fixedSizeRatio); if (glyph.fWidth) { this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)), &fallback); } fx += SkFixedMul(glyph.fAdvanceX, fixedSizeRatio); fy += SkFixedMul(glyph.fAdvanceY, fixedSizeRatio); } fFallbackTextBlob.reset(fallback.buildIfNeeded(&fFallbackGlyphCount)); }
void GrTextUtils::DrawDFPosText(GrAtlasTextBlob* blob, int runIndex, GrBatchFontCache* fontCache, const SkSurfaceProps& props, const SkPaint& origPaint, GrColor color, const SkMatrix& viewMatrix, const char text[], size_t byteLength, const SkScalar pos[], int scalarsPerPosition, const SkPoint& offset) { SkASSERT(byteLength == 0 || text != nullptr); SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); // nothing to draw if (text == nullptr || byteLength == 0) { return; } SkTDArray<char> fallbackTxt; SkTDArray<SkScalar> fallbackPos; // Setup distance field paint and text ratio SkScalar textRatio; SkPaint dfPaint(origPaint); GrTextUtils::InitDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix); blob->setHasDistanceField(); blob->setSubRunHasDistanceFields(runIndex, origPaint.isLCDRenderText()); GrBatchTextStrike* currStrike = nullptr; SkGlyphCache* cache = blob->setupCache(runIndex, props, SkPaint::FakeGamma::Off, dfPaint, nullptr); SkPaint::GlyphCacheProc glyphCacheProc = dfPaint.getGlyphCacheProc(true); GrFontScaler* fontScaler = GrTextUtils::GetGrFontScaler(cache); const char* stop = text + byteLength; if (SkPaint::kLeft_Align == dfPaint.getTextAlign()) { while (text < stop) { const char* lastText = text; // the last 2 parameters are ignored const SkGlyph& glyph = glyphCacheProc(cache, &text); if (glyph.fWidth) { SkScalar x = offset.x() + pos[0]; SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0); if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x, y, color, fontScaler, textRatio, viewMatrix)) { // couldn't append, send to fallback fallbackTxt.append(SkToInt(text-lastText), lastText); *fallbackPos.append() = pos[0]; if (2 == scalarsPerPosition) { *fallbackPos.append() = pos[1]; } } } pos += scalarsPerPosition; } } else { SkScalar alignMul = SkPaint::kCenter_Align == dfPaint.getTextAlign() ? SK_ScalarHalf : SK_Scalar1; while (text < stop) { const char* lastText = text; // the last 2 parameters are ignored const SkGlyph& glyph = glyphCacheProc(cache, &text); if (glyph.fWidth) { SkScalar x = offset.x() + pos[0]; SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0); SkScalar advanceX = SkFixedToScalar(glyph.fAdvanceX) * alignMul * textRatio; SkScalar advanceY = SkFixedToScalar(glyph.fAdvanceY) * alignMul * textRatio; if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x - advanceX, y - advanceY, color, fontScaler, textRatio, viewMatrix)) { // couldn't append, send to fallback fallbackTxt.append(SkToInt(text-lastText), lastText); *fallbackPos.append() = pos[0]; if (2 == scalarsPerPosition) { *fallbackPos.append() = pos[1]; } } } pos += scalarsPerPosition; } } SkGlyphCache::AttachCache(cache); if (fallbackTxt.count()) { blob->initOverride(runIndex); GrTextUtils::DrawBmpPosText(blob, runIndex, fontCache, props, origPaint, origPaint.getColor(), viewMatrix, fallbackTxt.begin(), fallbackTxt.count(), fallbackPos.begin(), scalarsPerPosition, offset); } }
void GrTextUtils::DrawDFText(GrAtlasTextBlob* blob, int runIndex, GrBatchFontCache* fontCache, const SkSurfaceProps& props, const SkPaint& skPaint, GrColor color, const SkMatrix& viewMatrix, const char text[], size_t byteLength, SkScalar x, SkScalar y) { SkASSERT(byteLength == 0 || text != nullptr); // nothing to draw if (text == nullptr || byteLength == 0) { return; } SkPaint::GlyphCacheProc glyphCacheProc = skPaint.getGlyphCacheProc(true); SkAutoDescriptor desc; skPaint.getScalerContextDescriptor(&desc, props, SkPaint::FakeGamma::Off, nullptr); SkGlyphCache* origPaintCache = SkGlyphCache::DetachCache(skPaint.getTypeface(), desc.getDesc()); SkTArray<SkScalar> positions; const char* textPtr = text; SkFixed stopX = 0; SkFixed stopY = 0; SkFixed origin = 0; switch (skPaint.getTextAlign()) { case SkPaint::kRight_Align: origin = SK_Fixed1; break; case SkPaint::kCenter_Align: origin = SK_FixedHalf; break; case SkPaint::kLeft_Align: origin = 0; break; } SkAutoKern autokern; const char* stop = text + byteLength; while (textPtr < stop) { // don't need x, y here, since all subpixel variants will have the // same advance const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr); SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph); positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width))); SkFixed height = glyph.fAdvanceY; positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height))); stopX += width; stopY += height; } SkASSERT(textPtr == stop); SkGlyphCache::AttachCache(origPaintCache); // now adjust starting point depending on alignment SkScalar alignX = SkFixedToScalar(stopX); SkScalar alignY = SkFixedToScalar(stopY); if (skPaint.getTextAlign() == SkPaint::kCenter_Align) { alignX = SkScalarHalf(alignX); alignY = SkScalarHalf(alignY); } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) { alignX = 0; alignY = 0; } x -= alignX; y -= alignY; SkPoint offset = SkPoint::Make(x, y); DrawDFPosText(blob, runIndex, fontCache, props, skPaint, color, viewMatrix, text, byteLength, positions.begin(), 2, offset); }
static sk_sp<GrTextureProxy> create_profile_texture(GrProxyProvider* proxyProvider, const SkRect& circle, float sigma, float* solidRadius, float* textureRadius) { float circleR = circle.width() / 2.0f; if (circleR < SK_ScalarNearlyZero) { return nullptr; } // Profile textures are cached by the ratio of sigma to circle radius and by the size of the // profile texture (binned by powers of 2). SkScalar sigmaToCircleRRatio = sigma / circleR; // When sigma is really small this becomes a equivalent to convolving a Gaussian with a // half-plane. Similarly, in the extreme high ratio cases circle becomes a point WRT to the // Guassian and the profile texture is a just a Gaussian evaluation. However, we haven't yet // implemented this latter optimization. sigmaToCircleRRatio = SkTMin(sigmaToCircleRRatio, 8.f); SkFixed sigmaToCircleRRatioFixed; static const SkScalar kHalfPlaneThreshold = 0.1f; bool useHalfPlaneApprox = false; if (sigmaToCircleRRatio <= kHalfPlaneThreshold) { useHalfPlaneApprox = true; sigmaToCircleRRatioFixed = 0; *solidRadius = circleR - 3 * sigma; *textureRadius = 6 * sigma; } else { // Convert to fixed point for the key. sigmaToCircleRRatioFixed = SkScalarToFixed(sigmaToCircleRRatio); // We shave off some bits to reduce the number of unique entries. We could probably // shave off more than we do. sigmaToCircleRRatioFixed &= ~0xff; sigmaToCircleRRatio = SkFixedToScalar(sigmaToCircleRRatioFixed); sigma = circleR * sigmaToCircleRRatio; *solidRadius = 0; *textureRadius = circleR + 3 * sigma; } static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); GrUniqueKey key; GrUniqueKey::Builder builder(&key, kDomain, 1); builder[0] = sigmaToCircleRRatioFixed; builder.finish(); sk_sp<GrTextureProxy> blurProfile = proxyProvider->findOrCreateProxyByUniqueKey(key, kTopLeft_GrSurfaceOrigin); if (!blurProfile) { static constexpr int kProfileTextureWidth = 512; GrSurfaceDesc texDesc; texDesc.fOrigin = kTopLeft_GrSurfaceOrigin; texDesc.fWidth = kProfileTextureWidth; texDesc.fHeight = 1; texDesc.fConfig = kAlpha_8_GrPixelConfig; std::unique_ptr<uint8_t[]> profile(nullptr); if (useHalfPlaneApprox) { profile.reset(create_half_plane_profile(kProfileTextureWidth)); } else { // Rescale params to the size of the texture we're creating. SkScalar scale = kProfileTextureWidth / *textureRadius; profile.reset( create_circle_profile(sigma * scale, circleR * scale, kProfileTextureWidth)); } blurProfile = proxyProvider->createTextureProxy(texDesc, SkBudgeted::kYes, profile.get(), 0); if (!blurProfile) { return nullptr; } SkASSERT(blurProfile->origin() == kTopLeft_GrSurfaceOrigin); proxyProvider->assignUniqueKeyToProxy(key, blurProfile.get()); } return blurProfile; }
void GrBitmapTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint, const SkPaint& skPaint, const SkMatrix& viewMatrix, const char text[], size_t byteLength, SkScalar x, SkScalar y) { SkASSERT(byteLength == 0 || text != NULL); // nothing to draw if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) { return; } this->init(rt, clip, paint, skPaint); SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc(); SkAutoGlyphCache autoCache(fSkPaint, &fDeviceProperties, &viewMatrix); SkGlyphCache* cache = autoCache.getCache(); GrFontScaler* fontScaler = GetGrFontScaler(cache); // transform our starting point { SkPoint loc; viewMatrix.mapXY(x, y, &loc); x = loc.fX; y = loc.fY; } // need to measure first int numGlyphs; if (fSkPaint.getTextAlign() != SkPaint::kLeft_Align) { SkVector stopVector; numGlyphs = MeasureText(cache, glyphCacheProc, text, byteLength, &stopVector); SkScalar stopX = stopVector.fX; SkScalar stopY = stopVector.fY; if (fSkPaint.getTextAlign() == SkPaint::kCenter_Align) { stopX = SkScalarHalf(stopX); stopY = SkScalarHalf(stopY); } x -= stopX; y -= stopY; } else { numGlyphs = fSkPaint.textToGlyphs(text, byteLength, NULL); } fTotalVertexCount = kVerticesPerGlyph*numGlyphs; const char* stop = text + byteLength; SkAutoKern autokern; SkFixed fxMask = ~0; SkFixed fyMask = ~0; SkScalar halfSampleX, halfSampleY; if (cache->isSubpixel()) { halfSampleX = halfSampleY = SkFixedToScalar(SkGlyph::kSubpixelRound); SkAxisAlignment baseline = SkComputeAxisAlignmentForHText(viewMatrix); if (kX_SkAxisAlignment == baseline) { fyMask = 0; halfSampleY = SK_ScalarHalf; } else if (kY_SkAxisAlignment == baseline) { fxMask = 0; halfSampleX = SK_ScalarHalf; } } else { halfSampleX = halfSampleY = SK_ScalarHalf; } Sk48Dot16 fx = SkScalarTo48Dot16(x + halfSampleX); Sk48Dot16 fy = SkScalarTo48Dot16(y + halfSampleY); // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix, but for // performance reasons we just invert here instead if (!viewMatrix.invert(&fLocalMatrix)) { SkDebugf("Cannot invert viewmatrix\n"); return; } while (text < stop) { const SkGlyph& glyph = glyphCacheProc(cache, &text, fx & fxMask, fy & fyMask); fx += autokern.adjust(glyph); if (glyph.fWidth) { this->appendGlyph(GrGlyph::Pack(glyph.getGlyphID(), glyph.getSubXFixed(), glyph.getSubYFixed(), GrGlyph::kCoverage_MaskStyle), Sk48Dot16FloorToInt(fx), Sk48Dot16FloorToInt(fy), fontScaler); } fx += glyph.fAdvanceX; fy += glyph.fAdvanceY; } this->finish(); }
void GrDistanceFieldTextContext::onDrawText(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint, const SkPaint& skPaint, const SkMatrix& viewMatrix, const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkIRect& regionClipBounds) { SkASSERT(byteLength == 0 || text != NULL); // nothing to draw if (text == NULL || byteLength == 0) { return; } fViewMatrix = viewMatrix; SkDrawCacheProc glyphCacheProc = skPaint.getDrawCacheProc(); SkAutoGlyphCache autoCache(skPaint, &fDeviceProperties, NULL); SkGlyphCache* cache = autoCache.getCache(); SkTArray<SkScalar> positions; const char* textPtr = text; SkFixed stopX = 0; SkFixed stopY = 0; SkFixed origin; switch (skPaint.getTextAlign()) { case SkPaint::kRight_Align: origin = SK_Fixed1; break; case SkPaint::kCenter_Align: origin = SK_FixedHalf; break; case SkPaint::kLeft_Align: origin = 0; break; default: SkFAIL("Invalid paint origin"); return; } SkAutoKern autokern; const char* stop = text + byteLength; while (textPtr < stop) { // don't need x, y here, since all subpixel variants will have the // same advance const SkGlyph& glyph = glyphCacheProc(cache, &textPtr, 0, 0); SkFixed width = glyph.fAdvanceX + autokern.adjust(glyph); positions.push_back(SkFixedToScalar(stopX + SkFixedMul(origin, width))); SkFixed height = glyph.fAdvanceY; positions.push_back(SkFixedToScalar(stopY + SkFixedMul(origin, height))); stopX += width; stopY += height; } SkASSERT(textPtr == stop); // now adjust starting point depending on alignment SkScalar alignX = SkFixedToScalar(stopX); SkScalar alignY = SkFixedToScalar(stopY); if (skPaint.getTextAlign() == SkPaint::kCenter_Align) { alignX = SkScalarHalf(alignX); alignY = SkScalarHalf(alignY); } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) { alignX = 0; alignY = 0; } x -= alignX; y -= alignY; SkPoint offset = SkPoint::Make(x, y); this->onDrawPosText(rt, clip, paint, skPaint, viewMatrix, text, byteLength, positions.begin(), 2, offset, regionClipBounds); }