BlurBench(SkScalar rad, SkBlurStyle bs, uint32_t flags = 0) { fRadius = rad; fStyle = bs; fFlags = flags; const char* name = rad > 0 ? gStyleName[bs] : "none"; const char* quality = flags & SkBlurMaskFilter::kHighQuality_BlurFlag ? "high_quality" : "low_quality"; if (SkScalarFraction(rad) != 0) { fName.printf("blur_%.2f_%s_%s", SkScalarToFloat(rad), name, quality); } else { fName.printf("blur_%d_%s_%s", SkScalarRoundToInt(rad), name, quality); } }
static void draw_sprite(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) { SkPaint paint; SkIRect bounds; r.roundOut(&bounds); SkBitmap bm; bm.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height()); bm.allocPixels(); bm.eraseColor(SK_ColorRED); SkCanvas c(bm); SkIRect cropRect = SkIRect::MakeXYWH(10, 10, 44, 44); paint.setColor(SK_ColorGREEN); c.drawRect(SkRect::Make(cropRect), paint); paint.setImageFilter(imf); SkPoint loc = { r.fLeft, r.fTop }; canvas->getTotalMatrix().mapPoints(&loc, 1); canvas->drawSprite(bm, SkScalarRoundToInt(loc.fX), SkScalarRoundToInt(loc.fY), &paint); }
// Only called once. Could be part of the constructor. void stitch() { SkScalar tileWidth = SkIntToScalar(fTileSize.width()); SkScalar tileHeight = SkIntToScalar(fTileSize.height()); SkASSERT(tileWidth > 0 && tileHeight > 0); // When stitching tiled turbulence, the frequencies must be adjusted // so that the tile borders will be continuous. if (fBaseFrequency.fX) { SkScalar lowFrequencx = SkScalarFloorToScalar(tileWidth * fBaseFrequency.fX) / tileWidth; SkScalar highFrequencx = SkScalarCeilToScalar(tileWidth * fBaseFrequency.fX) / tileWidth; // BaseFrequency should be non-negative according to the standard. if (fBaseFrequency.fX / lowFrequencx < highFrequencx / fBaseFrequency.fX) { fBaseFrequency.fX = lowFrequencx; } else { fBaseFrequency.fX = highFrequencx; } } if (fBaseFrequency.fY) { SkScalar lowFrequency = SkScalarFloorToScalar(tileHeight * fBaseFrequency.fY) / tileHeight; SkScalar highFrequency = SkScalarCeilToScalar(tileHeight * fBaseFrequency.fY) / tileHeight; if (fBaseFrequency.fY / lowFrequency < highFrequency / fBaseFrequency.fY) { fBaseFrequency.fY = lowFrequency; } else { fBaseFrequency.fY = highFrequency; } } // Set up TurbulenceInitial stitch values. fStitchDataInit.fWidth = SkScalarRoundToInt(tileWidth * fBaseFrequency.fX); fStitchDataInit.fWrapX = kPerlinNoise + fStitchDataInit.fWidth; fStitchDataInit.fHeight = SkScalarRoundToInt(tileHeight * fBaseFrequency.fY); fStitchDataInit.fWrapY = kPerlinNoise + fStitchDataInit.fHeight; }
void SkColorCubeFilter::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const { const int* colorToIndex[2]; const SkScalar* colorToFactors[2]; const SkScalar* colorToScalar; fCache.getProcessingLuts(&colorToIndex, &colorToFactors, &colorToScalar); const int dim = fCache.cubeDimension(); SkColor* colorCube = (SkColor*)fCubeData->data(); for (int i = 0; i < count; ++i) { SkColor inputColor = SkUnPreMultiply::PMColorToColor(src[i]); uint8_t r = SkColorGetR(inputColor); uint8_t g = SkColorGetG(inputColor); uint8_t b = SkColorGetB(inputColor); uint8_t a = SkColorGetA(inputColor); SkScalar rOut(0), gOut(0), bOut(0); for (int x = 0; x < 2; ++x) { for (int y = 0; y < 2; ++y) { for (int z = 0; z < 2; ++z) { SkColor lutColor = colorCube[colorToIndex[x][r] + (colorToIndex[y][g] + colorToIndex[z][b] * dim) * dim]; SkScalar factor = colorToFactors[x][r] * colorToFactors[y][g] * colorToFactors[z][b]; rOut += colorToScalar[SkColorGetR(lutColor)] * factor; gOut += colorToScalar[SkColorGetG(lutColor)] * factor; bOut += colorToScalar[SkColorGetB(lutColor)] * factor; } } } const SkScalar aOut = SkIntToScalar(a); dst[i] = SkPackARGB32(a, SkScalarRoundToInt(rOut * aOut), SkScalarRoundToInt(gOut * aOut), SkScalarRoundToInt(bOut * aOut)); } }
void updateRenderTarget(int width, int height) { GrBackendRenderTargetDesc desc; desc.fWidth = SkScalarRoundToInt(width); desc.fHeight = SkScalarRoundToInt(height); desc.fConfig = kSkia8888_GrPixelConfig; desc.fOrigin = kBottomLeft_GrSurfaceOrigin; desc.fSampleCnt = 0; // Number of samples in the color/stencil attachments desc.fStencilBits = 8; GrGLint buffer; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &buffer); desc.fRenderTargetHandle = buffer; if (renderTarget != NULL) { SkSafeUnref(renderTarget); } renderTarget = context->wrapBackendRenderTarget(desc); context->setRenderTarget(renderTarget); if (NULL != context && NULL != renderTarget) { SkAutoTUnref<SkBaseDevice> device(new SkGpuDevice(context, renderTarget)); if (canvas != NULL) SkSafeUnref(canvas); canvas = new SkCanvas(device); } }
GrRenderTarget* SkWindow::renderTarget(const AttachmentInfo& attachmentInfo, const GrGLInterface* interface, GrContext* grContext) { GrBackendRenderTargetDesc desc; desc.fWidth = SkScalarRoundToInt(this->width()); desc.fHeight = SkScalarRoundToInt(this->height()); // TODO: Query the actual framebuffer for sRGB capable. However, to // preserve old (fake-linear) behavior, we don't do this. Instead, rely // on the flag (currently driven via 'C' mode in SampleApp). // // Also, we may not have real sRGB support (ANGLE, in particular), so check for // that, and fall back to L32: desc.fConfig = grContext->caps()->srgbSupport() && (info().profileType() == kSRGB_SkColorProfileType || info().colorType() == kRGBA_F16_SkColorType) ? kSkiaGamma8888_GrPixelConfig : kSkia8888_GrPixelConfig; desc.fOrigin = kBottomLeft_GrSurfaceOrigin; desc.fSampleCnt = attachmentInfo.fSampleCount; desc.fStencilBits = attachmentInfo.fStencilBits; GrGLint buffer; GR_GL_GetIntegerv(interface, GR_GL_FRAMEBUFFER_BINDING, &buffer); desc.fRenderTargetHandle = buffer; return grContext->textureProvider()->wrapBackendRenderTarget(desc); }
SkScalar next(SkPath* dst, SkScalar distance, SkPathMeasure&) const override { fMaker->setExtraPropertyCallBack(fDraw->fType, GetDistance, &distance); SkDrawPath* drawPath = nullptr; if (fDraw->addPath->isPath()) { drawPath = (SkDrawPath*) fDraw->addPath; } else { SkApply* apply = (SkApply*) fDraw->addPath; apply->refresh(*fMaker); apply->activate(*fMaker); apply->interpolate(*fMaker, SkScalarRoundToInt(distance * 1000)); drawPath = (SkDrawPath*) apply->getScope(); } SkMatrix m; m.reset(); if (fDraw->addMatrix) { SkDrawMatrix* matrix; if (fDraw->addMatrix->getType() == SkType_Matrix) matrix = (SkDrawMatrix*) fDraw->addMatrix; else { SkApply* apply = (SkApply*) fDraw->addMatrix; apply->refresh(*fMaker); apply->activate(*fMaker); apply->interpolate(*fMaker, SkScalarRoundToInt(distance * 1000)); matrix = (SkDrawMatrix*) apply->getScope(); } if (matrix) { m = matrix->getMatrix(); } } SkScalar result = 0; SkAnimatorScript::EvaluateFloat(*fMaker, nullptr, fDraw->spacing.c_str(), &result); if (drawPath) dst->addPath(drawPath->getPath(), m); fMaker->clearExtraPropertyCallBack(fDraw->fType); return result; }
static cairo_status_t _cairo_skia_context_paint_with_alpha (void *abstract_cr, double alpha) { cairo_skia_context_t *cr = (cairo_skia_context_t *) abstract_cr; cairo_status_t status; if (CAIRO_ALPHA_IS_OPAQUE (alpha)) return _cairo_skia_context_paint (cr); cr->paint->setAlpha(SkScalarRoundToInt(255*alpha)); status = _cairo_skia_context_paint (cr); cr->paint->setAlpha(255); return status; }
float SimpleFontData::platformWidthForGlyph(Glyph glyph) const { if (!m_platformData.size()) return 0; SkASSERT(sizeof(glyph) == 2); // compile-time assert SkPaint paint; m_platformData.setupPaint(&paint); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); SkScalar width = paint.measureText(&glyph, 2); if (!paint.isSubpixelText()) width = SkScalarRoundToInt(width); return SkScalarToFloat(width); }
// static void SkPDFScalar::Append(SkScalar value, SkWStream* stream) { // The range of reals in PDF/A is the same as SkFixed: +/- 32,767 and // +/- 1/65,536 (though integers can range from 2^31 - 1 to -2^31). // When using floats that are outside the whole value range, we can use // integers instead. #if !defined(SK_ALLOW_LARGE_PDF_SCALARS) if (value > 32767 || value < -32767) { stream->writeDecAsText(SkScalarRoundToInt(value)); return; } char buffer[SkStrAppendScalar_MaxSize]; char* end = SkStrAppendFixed(buffer, SkScalarToFixed(value)); stream->write(buffer, end - buffer); return; #endif // !SK_ALLOW_LARGE_PDF_SCALARS #if defined(SK_ALLOW_LARGE_PDF_SCALARS) // Floats have 24bits of significance, so anything outside that range is // no more precise than an int. (Plus PDF doesn't support scientific // notation, so this clamps to SK_Max/MinS32). if (value > (1 << 24) || value < -(1 << 24)) { stream->writeDecAsText(value); return; } // Continue to enforce the PDF limits for small floats. if (value < 1.0f/65536 && value > -1.0f/65536) { stream->writeDecAsText(0); return; } // SkStrAppendFloat might still use scientific notation, so use snprintf // directly.. static const int kFloat_MaxSize = 19; char buffer[kFloat_MaxSize]; int len = SNPRINTF(buffer, kFloat_MaxSize, "%#.8f", value); // %f always prints trailing 0s, so strip them. for (; buffer[len - 1] == '0' && len > 0; len--) { buffer[len - 1] = '\0'; } if (buffer[len - 1] == '.') { buffer[len - 1] = '\0'; } stream->writeText(buffer); return; #endif // SK_ALLOW_LARGE_PDF_SCALARS }
bool check_gamma(uint32_t src, uint32_t dst, bool toSRGB, float error, uint32_t* expected) { bool result = true; uint32_t expectedColor = src & 0xff000000; // Alpha should always be exactly preserved. if ((src & 0xff000000) != (dst & 0xff000000)) { result = false; } // need to unpremul before we can perform srgb magic float invScale = 0; float alpha = SkGetPackedA32(src); if (alpha) { invScale = 255.0f / alpha; } for (int c = 0; c < 3; ++c) { float srcComponent = ((src & (0xff << (c * 8))) >> (c * 8)) * invScale; float lower = SkTMax(0.f, srcComponent - error); float upper = SkTMin(255.f, srcComponent + error); if (toSRGB) { lower = linear_to_srgb(lower / 255.f); upper = linear_to_srgb(upper / 255.f); } else { lower = srgb_to_linear(lower / 255.f); upper = srgb_to_linear(upper / 255.f); } lower *= alpha; upper *= alpha; SkASSERT(lower >= 0.f && lower <= 255.f); SkASSERT(upper >= 0.f && upper <= 255.f); uint8_t dstComponent = (dst & (0xff << (c * 8))) >> (c * 8); if (dstComponent < SkScalarFloorToInt(lower) || dstComponent > SkScalarCeilToInt(upper)) { result = false; } uint8_t expectedComponent = SkScalarRoundToInt((lower + upper) * 0.5f); expectedColor |= expectedComponent << (c * 8); } *expected = expectedColor; return result; }
SkRect PaintNode::onRevalidate(InvalidationController*, const SkMatrix&) { SkASSERT(this->hasInval()); fPaint.reset(); fPaint.setAntiAlias(fAntiAlias); fPaint.setBlendMode(fBlendMode); fPaint.setStyle(fStyle); fPaint.setStrokeWidth(fStrokeWidth); fPaint.setStrokeMiter(fStrokeMiter); fPaint.setStrokeJoin(fStrokeJoin); fPaint.setStrokeCap(fStrokeCap); this->onApplyToPaint(&fPaint); // Compose opacity on top of the subclass value. fPaint.setAlpha(SkScalarRoundToInt(fPaint.getAlpha() * SkTPin<SkScalar>(fOpacity, 0, 1))); return SkRect::MakeEmpty(); }
static GrBatch* create_hairline_batch(GrColor color, const SkMatrix& viewMatrix, const SkPath& path, const GrStrokeInfo& stroke, const SkIRect& devClipBounds) { SkScalar hairlineCoverage; uint8_t newCoverage = 0xff; if (GrPathRenderer::IsStrokeHairlineOrEquivalent(stroke, viewMatrix, &hairlineCoverage)) { newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff); } AAHairlineBatch::Geometry geometry; geometry.fColor = color; geometry.fCoverage = newCoverage; geometry.fViewMatrix = viewMatrix; geometry.fPath = path; geometry.fDevClipBounds = devClipBounds; return AAHairlineBatch::Create(geometry); }
static void make_big_bitmap(SkBitmap* bm) { static const char gText[] = "We the people, in order to form a more perfect union, establish justice," " ensure domestic tranquility, provide for the common defense, promote the" " general welfare and ensure the blessings of liberty to ourselves and our" " posterity, do ordain and establish this constitution for the United" " States of America."; const int BIG_H = 120; SkFont font; font.setSize(SkIntToScalar(BIG_H)); const int BIG_W = SkScalarRoundToInt(font.measureText(gText, strlen(gText), SkTextEncoding::kUTF8)); bm->allocN32Pixels(BIG_W, BIG_H); bm->eraseColor(SK_ColorWHITE); SkCanvas canvas(*bm); canvas.drawSimpleText(gText, strlen(gText), SkTextEncoding::kUTF8, 0, font.getSize()*4/5, font, SkPaint()); }
static void clip_quads(const SkIRect& clipRect, char* currVertex, const char* blobVertices, size_t vertexStride, int glyphCount) { for (int i = 0; i < glyphCount; ++i) { const SkPoint* blobPositionLT = reinterpret_cast<const SkPoint*>(blobVertices); const SkPoint* blobPositionRB = reinterpret_cast<const SkPoint*>(blobVertices + 3 * vertexStride); // positions for bitmap glyphs are pixel boundary aligned SkIRect positionRect = SkIRect::MakeLTRB(SkScalarRoundToInt(blobPositionLT->fX), SkScalarRoundToInt(blobPositionLT->fY), SkScalarRoundToInt(blobPositionRB->fX), SkScalarRoundToInt(blobPositionRB->fY)); if (clipRect.contains(positionRect)) { memcpy(currVertex, blobVertices, 4 * vertexStride); currVertex += 4 * vertexStride; } else { // Pull out some more data that we'll need. // In the LCD case the color will be garbage, but we'll overwrite it with the texcoords // and it avoids a lot of conditionals. auto color = *reinterpret_cast<const SkColor*>(blobVertices + sizeof(SkPoint)); size_t coordOffset = vertexStride - 2*sizeof(uint16_t); auto* blobCoordsLT = reinterpret_cast<const uint16_t*>(blobVertices + coordOffset); auto* blobCoordsRB = reinterpret_cast<const uint16_t*>(blobVertices + 3 * vertexStride + coordOffset); // Pull out the texel coordinates and texture index bits uint16_t coordsRectL = blobCoordsLT[0] >> 1; uint16_t coordsRectT = blobCoordsLT[1] >> 1; uint16_t coordsRectR = blobCoordsRB[0] >> 1; uint16_t coordsRectB = blobCoordsRB[1] >> 1; uint16_t pageIndexX = blobCoordsLT[0] & 0x1; uint16_t pageIndexY = blobCoordsLT[1] & 0x1; int positionRectWidth = positionRect.width(); int positionRectHeight = positionRect.height(); SkASSERT(positionRectWidth == (coordsRectR - coordsRectL)); SkASSERT(positionRectHeight == (coordsRectB - coordsRectT)); // Clip position and texCoords to the clipRect unsigned int delta; delta = SkTMin(SkTMax(clipRect.fLeft - positionRect.fLeft, 0), positionRectWidth); coordsRectL += delta; positionRect.fLeft += delta; delta = SkTMin(SkTMax(clipRect.fTop - positionRect.fTop, 0), positionRectHeight); coordsRectT += delta; positionRect.fTop += delta; delta = SkTMin(SkTMax(positionRect.fRight - clipRect.fRight, 0), positionRectWidth); coordsRectR -= delta; positionRect.fRight -= delta; delta = SkTMin(SkTMax(positionRect.fBottom - clipRect.fBottom, 0), positionRectHeight); coordsRectB -= delta; positionRect.fBottom -= delta; // Repack texel coordinates and index coordsRectL = coordsRectL << 1 | pageIndexX; coordsRectT = coordsRectT << 1 | pageIndexY; coordsRectR = coordsRectR << 1 | pageIndexX; coordsRectB = coordsRectB << 1 | pageIndexY; // Set new positions and coords SkPoint* currPosition = reinterpret_cast<SkPoint*>(currVertex); currPosition->fX = positionRect.fLeft; currPosition->fY = positionRect.fTop; *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color; uint16_t* currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset); currCoords[0] = coordsRectL; currCoords[1] = coordsRectT; currVertex += vertexStride; currPosition = reinterpret_cast<SkPoint*>(currVertex); currPosition->fX = positionRect.fLeft; currPosition->fY = positionRect.fBottom; *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color; currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset); currCoords[0] = coordsRectL; currCoords[1] = coordsRectB; currVertex += vertexStride; currPosition = reinterpret_cast<SkPoint*>(currVertex); currPosition->fX = positionRect.fRight; currPosition->fY = positionRect.fTop; *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color; currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset); currCoords[0] = coordsRectR; currCoords[1] = coordsRectT; currVertex += vertexStride; currPosition = reinterpret_cast<SkPoint*>(currVertex); currPosition->fX = positionRect.fRight; currPosition->fY = positionRect.fBottom; *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color; currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset); currCoords[0] = coordsRectR; currCoords[1] = coordsRectB; currVertex += vertexStride; } blobVertices += 4 * vertexStride; } }
// return true if x is nearly integral (within 1/16) since that is the highest // precision our aa code can have. static bool is_integral(SkScalar x) { int ix = SkScalarRoundToInt(x); SkScalar sx = SkIntToScalar(ix); return SkScalarAbs(sx - x) < (SK_Scalar1 / 16); }
void SimpleFontData::platformInit() { if (!m_platformData.size()) { m_fontMetrics.reset(); m_avgCharWidth = 0; m_maxCharWidth = 0; return; } SkPaint paint; SkPaint::FontMetrics metrics; m_platformData.setupPaint(&paint); paint.getFontMetrics(&metrics); SkTypeface* face = paint.getTypeface(); ASSERT(face); int vdmxAscent = 0, vdmxDescent = 0; bool isVDMXValid = false; #if OS(LINUX) || OS(ANDROID) // Manually digging up VDMX metrics is only applicable when bytecode hinting // using FreeType. With GDI, the metrics will already have taken this into // account (as needed). With DirectWrite or CoreText, no bytecode hinting is // ever done. This code should be pushed into FreeType (hinted font metrics). static const uint32_t vdmxTag = SkSetFourByteTag('V', 'D', 'M', 'X'); int pixelSize = m_platformData.size() + 0.5; if (!paint.isAutohinted() && (paint.getHinting() == SkPaint::kFull_Hinting || paint.getHinting() == SkPaint::kNormal_Hinting)) { size_t vdmxSize = face->getTableSize(vdmxTag); if (vdmxSize && vdmxSize < maxVDMXTableSize) { uint8_t* vdmxTable = (uint8_t*)fastMalloc(vdmxSize); if (vdmxTable && face->getTableData(vdmxTag, 0, vdmxSize, vdmxTable) == vdmxSize && parseVDMX(&vdmxAscent, &vdmxDescent, vdmxTable, vdmxSize, pixelSize)) isVDMXValid = true; fastFree(vdmxTable); } } #endif float ascent; float descent; // Beware those who step here: This code is designed to match Win32 font // metrics *exactly* (except the adjustment of ascent/descent on // Linux/Android). if (isVDMXValid) { ascent = vdmxAscent; descent = -vdmxDescent; } else { ascent = SkScalarRoundToInt(-metrics.fAscent); descent = SkScalarRoundToInt(metrics.fDescent); #if OS(LINUX) || OS(ANDROID) // When subpixel positioning is enabled, if the descent is rounded down, the // descent part of the glyph may be truncated when displayed in a 'overflow: // hidden' container. To avoid that, borrow 1 unit from the ascent when // possible. // FIXME: This can be removed if sub-pixel ascent/descent is supported. if (platformData().fontRenderStyle().useSubpixelPositioning && descent < SkScalarToFloat(metrics.fDescent) && ascent >= 1) { ++descent; --ascent; } #endif } m_fontMetrics.setAscent(ascent); m_fontMetrics.setDescent(descent); float xHeight; if (metrics.fXHeight) { xHeight = metrics.fXHeight; m_fontMetrics.setXHeight(xHeight); } else { xHeight = ascent * 0.56; // Best guess from Windows font metrics. m_fontMetrics.setXHeight(xHeight); m_fontMetrics.setHasXHeight(false); } float lineGap = SkScalarToFloat(metrics.fLeading); m_fontMetrics.setLineGap(lineGap); m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); SkScalar underlineThickness, underlinePosition; if (metrics.hasUnderlineThickness(&underlineThickness) && metrics.hasUnderlinePosition(&underlinePosition)) { m_fontMetrics.setUnderlineThickness(SkScalarToFloat(underlineThickness)); m_fontMetrics.setUnderlinePosition(SkScalarToFloat(-underlinePosition)); } if (platformData().orientation() == Vertical && !isTextOrientationFallback()) { static const uint32_t vheaTag = SkSetFourByteTag('v', 'h', 'e', 'a'); static const uint32_t vorgTag = SkSetFourByteTag('V', 'O', 'R', 'G'); size_t vheaSize = face->getTableSize(vheaTag); size_t vorgSize = face->getTableSize(vorgTag); if ((vheaSize > 0) || (vorgSize > 0)) m_hasVerticalGlyphs = true; } // In WebKit/WebCore/platform/graphics/SimpleFontData.cpp, m_spaceWidth is // calculated for us, but we need to calculate m_maxCharWidth and // m_avgCharWidth in order for text entry widgets to be sized correctly. // FIXME: This seems incorrect and should probably use fMaxCharWidth as // the code path above. SkScalar xRange = metrics.fXMax - metrics.fXMin; m_maxCharWidth = SkScalarRoundToInt(xRange * SkScalarRoundToInt(m_platformData.size())); if (metrics.fAvgCharWidth) m_avgCharWidth = SkScalarRoundToInt(metrics.fAvgCharWidth); else { m_avgCharWidth = xHeight; GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); if (glyphPageZero) { static const UChar32 xChar = 'x'; const Glyph xGlyph = glyphPageZero->glyphForCharacter(xChar); if (xGlyph) { // In widthForGlyph(), xGlyph will be compared with // m_zeroWidthSpaceGlyph, which isn't initialized yet here. // Initialize it with zero to make sure widthForGlyph() returns // the right width. m_zeroWidthSpaceGlyph = 0; m_avgCharWidth = widthForGlyph(xGlyph); } } } if (int unitsPerEm = face->getUnitsPerEm()) m_fontMetrics.setUnitsPerEm(unitsPerEm); }
bool GrDefaultPathRenderer::internalDrawPath(const SkPath& path, const SkStrokeRec& origStroke, GrDrawTarget* target, bool stencilOnly) { SkMatrix viewM = target->getDrawState().getViewMatrix(); SkTCopyOnFirstWrite<SkStrokeRec> stroke(origStroke); SkScalar hairlineCoverage; if (IsStrokeHairlineOrEquivalent(*stroke, target->getDrawState().getViewMatrix(), &hairlineCoverage)) { uint8_t newCoverage = SkScalarRoundToInt(hairlineCoverage * target->getDrawState().getCoverage()); target->drawState()->setCoverage(newCoverage); if (!stroke->isHairlineStyle()) { stroke.writable()->setHairlineStyle(); } } SkScalar tol = SK_Scalar1; tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, path.getBounds()); int vertexCnt; int indexCnt; GrPrimitiveType primType; GrDrawTarget::AutoReleaseGeometry arg; if (!this->createGeom(path, *stroke, tol, target, &primType, &vertexCnt, &indexCnt, &arg)) { return false; } SkASSERT(NULL != target); GrDrawTarget::AutoStateRestore asr(target, GrDrawTarget::kPreserve_ASRInit); GrDrawState* drawState = target->drawState(); bool colorWritesWereDisabled = drawState->isColorWriteDisabled(); // face culling doesn't make sense here SkASSERT(GrDrawState::kBoth_DrawFace == drawState->getDrawFace()); int passCount = 0; const GrStencilSettings* passes[3]; GrDrawState::DrawFace drawFace[3]; bool reverse = false; bool lastPassIsBounds; if (stroke->isHairlineStyle()) { passCount = 1; if (stencilOnly) { passes[0] = &gDirectToStencil; } else { passes[0] = NULL; } lastPassIsBounds = false; drawFace[0] = GrDrawState::kBoth_DrawFace; } else { if (single_pass_path(path, *stroke)) { passCount = 1; if (stencilOnly) { passes[0] = &gDirectToStencil; } else { passes[0] = NULL; } drawFace[0] = GrDrawState::kBoth_DrawFace; lastPassIsBounds = false; } else { switch (path.getFillType()) { case SkPath::kInverseEvenOdd_FillType: reverse = true; // fallthrough case SkPath::kEvenOdd_FillType: passes[0] = &gEOStencilPass; if (stencilOnly) { passCount = 1; lastPassIsBounds = false; } else { passCount = 2; lastPassIsBounds = true; if (reverse) { passes[1] = &gInvEOColorPass; } else { passes[1] = &gEOColorPass; } } drawFace[0] = drawFace[1] = GrDrawState::kBoth_DrawFace; break; case SkPath::kInverseWinding_FillType: reverse = true; // fallthrough case SkPath::kWinding_FillType: if (fSeparateStencil) { if (fStencilWrapOps) { passes[0] = &gWindStencilSeparateWithWrap; } else { passes[0] = &gWindStencilSeparateNoWrap; } passCount = 2; drawFace[0] = GrDrawState::kBoth_DrawFace; } else { if (fStencilWrapOps) { passes[0] = &gWindSingleStencilWithWrapInc; passes[1] = &gWindSingleStencilWithWrapDec; } else { passes[0] = &gWindSingleStencilNoWrapInc; passes[1] = &gWindSingleStencilNoWrapDec; } // which is cw and which is ccw is arbitrary. drawFace[0] = GrDrawState::kCW_DrawFace; drawFace[1] = GrDrawState::kCCW_DrawFace; passCount = 3; } if (stencilOnly) { lastPassIsBounds = false; --passCount; } else { lastPassIsBounds = true; drawFace[passCount-1] = GrDrawState::kBoth_DrawFace; if (reverse) { passes[passCount-1] = &gInvWindColorPass; } else { passes[passCount-1] = &gWindColorPass; } } break; default: SkDEBUGFAIL("Unknown path fFill!"); return false; } } } SkRect devBounds; GetPathDevBounds(path, drawState->getRenderTarget(), viewM, &devBounds); for (int p = 0; p < passCount; ++p) { drawState->setDrawFace(drawFace[p]); if (NULL != passes[p]) { *drawState->stencil() = *passes[p]; } if (lastPassIsBounds && (p == passCount-1)) { if (!colorWritesWereDisabled) { drawState->disableState(GrDrawState::kNoColorWrites_StateBit); } SkRect bounds; GrDrawState::AutoViewMatrixRestore avmr; if (reverse) { SkASSERT(NULL != drawState->getRenderTarget()); // draw over the dev bounds (which will be the whole dst surface for inv fill). bounds = devBounds; SkMatrix vmi; // mapRect through persp matrix may not be correct if (!drawState->getViewMatrix().hasPerspective() && drawState->getViewInverse(&vmi)) { vmi.mapRect(&bounds); } else { avmr.setIdentity(drawState); } } else { bounds = path.getBounds(); } GrDrawTarget::AutoGeometryAndStatePush agasp(target, GrDrawTarget::kPreserve_ASRInit); target->drawSimpleRect(bounds, NULL); } else { if (passCount > 1) { drawState->enableState(GrDrawState::kNoColorWrites_StateBit); } if (indexCnt) { target->drawIndexed(primType, 0, 0, vertexCnt, indexCnt, &devBounds); } else { target->drawNonIndexed(primType, 0, vertexCnt, &devBounds); } } } return true; }
bool GrDefaultPathRenderer::internalDrawPath(GrDrawTarget* target, GrPipelineBuilder* pipelineBuilder, GrColor color, const SkMatrix& viewMatrix, const SkPath& path, const GrStrokeInfo& origStroke, bool stencilOnly) { SkTCopyOnFirstWrite<GrStrokeInfo> stroke(origStroke); SkScalar hairlineCoverage; uint8_t newCoverage = 0xff; if (IsStrokeHairlineOrEquivalent(*stroke, viewMatrix, &hairlineCoverage)) { newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff); if (!stroke->getStrokeRec().isHairlineStyle()) { stroke.writable()->getStrokeRecPtr()->setHairlineStyle(); } } const bool isHairline = stroke->getStrokeRec().isHairlineStyle(); // Save the current xp on the draw state so we can reset it if needed SkAutoTUnref<const GrXPFactory> backupXPFactory(SkRef(pipelineBuilder->getXPFactory())); // face culling doesn't make sense here SkASSERT(GrPipelineBuilder::kBoth_DrawFace == pipelineBuilder->getDrawFace()); int passCount = 0; const GrStencilSettings* passes[3]; GrPipelineBuilder::DrawFace drawFace[3]; bool reverse = false; bool lastPassIsBounds; if (isHairline) { passCount = 1; if (stencilOnly) { passes[0] = &gDirectToStencil; } else { passes[0] = NULL; } lastPassIsBounds = false; drawFace[0] = GrPipelineBuilder::kBoth_DrawFace; } else { if (single_pass_path(path, stroke->getStrokeRec())) { passCount = 1; if (stencilOnly) { passes[0] = &gDirectToStencil; } else { passes[0] = NULL; } drawFace[0] = GrPipelineBuilder::kBoth_DrawFace; lastPassIsBounds = false; } else { switch (path.getFillType()) { case SkPath::kInverseEvenOdd_FillType: reverse = true; // fallthrough case SkPath::kEvenOdd_FillType: passes[0] = &gEOStencilPass; if (stencilOnly) { passCount = 1; lastPassIsBounds = false; } else { passCount = 2; lastPassIsBounds = true; if (reverse) { passes[1] = &gInvEOColorPass; } else { passes[1] = &gEOColorPass; } } drawFace[0] = drawFace[1] = GrPipelineBuilder::kBoth_DrawFace; break; case SkPath::kInverseWinding_FillType: reverse = true; // fallthrough case SkPath::kWinding_FillType: if (fSeparateStencil) { if (fStencilWrapOps) { passes[0] = &gWindStencilSeparateWithWrap; } else { passes[0] = &gWindStencilSeparateNoWrap; } passCount = 2; drawFace[0] = GrPipelineBuilder::kBoth_DrawFace; } else { if (fStencilWrapOps) { passes[0] = &gWindSingleStencilWithWrapInc; passes[1] = &gWindSingleStencilWithWrapDec; } else { passes[0] = &gWindSingleStencilNoWrapInc; passes[1] = &gWindSingleStencilNoWrapDec; } // which is cw and which is ccw is arbitrary. drawFace[0] = GrPipelineBuilder::kCW_DrawFace; drawFace[1] = GrPipelineBuilder::kCCW_DrawFace; passCount = 3; } if (stencilOnly) { lastPassIsBounds = false; --passCount; } else { lastPassIsBounds = true; drawFace[passCount-1] = GrPipelineBuilder::kBoth_DrawFace; if (reverse) { passes[passCount-1] = &gInvWindColorPass; } else { passes[passCount-1] = &gWindColorPass; } } break; default: SkDEBUGFAIL("Unknown path fFill!"); return false; } } } SkScalar tol = GrPathUtils::kDefaultTolerance; SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds()); SkRect devBounds; GetPathDevBounds(path, pipelineBuilder->getRenderTarget(), viewMatrix, &devBounds); for (int p = 0; p < passCount; ++p) { pipelineBuilder->setDrawFace(drawFace[p]); if (passes[p]) { *pipelineBuilder->stencil() = *passes[p]; } if (lastPassIsBounds && (p == passCount-1)) { // Reset the XP Factory on pipelineBuilder pipelineBuilder->setXPFactory(backupXPFactory); SkRect bounds; SkMatrix localMatrix = SkMatrix::I(); if (reverse) { SkASSERT(pipelineBuilder->getRenderTarget()); // draw over the dev bounds (which will be the whole dst surface for inv fill). bounds = devBounds; SkMatrix vmi; // mapRect through persp matrix may not be correct if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) { vmi.mapRect(&bounds); } else { if (!viewMatrix.invert(&localMatrix)) { return false; } } } else { bounds = path.getBounds(); } const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() : viewMatrix; target->drawRect(pipelineBuilder, color, viewM, bounds, NULL, &localMatrix); } else { if (passCount > 1) { pipelineBuilder->setDisableColorXPFactory(); } DefaultPathBatch::Geometry geometry; geometry.fColor = color; geometry.fPath = path; geometry.fTolerance = srcSpaceTol; SkAutoTUnref<GrBatch> batch(DefaultPathBatch::Create(geometry, newCoverage, viewMatrix, isHairline, devBounds)); target->drawBatch(pipelineBuilder, batch); } } return true; }
SkPDFImageShader* SkPDFImageShader::Create( SkPDFCanon* canon, SkScalar dpi, SkAutoTDelete<SkPDFShader::State>* autoState) { const SkPDFShader::State& state = **autoState; state.fImage.lockPixels(); // The image shader pattern cell will be drawn into a separate device // in pattern cell space (no scaling on the bitmap, though there may be // translations so that all content is in the device, coordinates > 0). // Map clip bounds to shader space to ensure the device is large enough // to handle fake clamping. SkMatrix finalMatrix = state.fCanvasTransform; finalMatrix.preConcat(state.fShaderTransform); SkRect deviceBounds; deviceBounds.set(state.fBBox); if (!inverse_transform_bbox(finalMatrix, &deviceBounds)) { return NULL; } const SkBitmap* image = &state.fImage; SkRect bitmapBounds; image->getBounds(&bitmapBounds); // For tiling modes, the bounds should be extended to include the bitmap, // otherwise the bitmap gets clipped out and the shader is empty and awful. // For clamp modes, we're only interested in the clip region, whether // or not the main bitmap is in it. SkShader::TileMode tileModes[2]; tileModes[0] = state.fImageTileModes[0]; tileModes[1] = state.fImageTileModes[1]; if (tileModes[0] != SkShader::kClamp_TileMode || tileModes[1] != SkShader::kClamp_TileMode) { deviceBounds.join(bitmapBounds); } SkISize size = SkISize::Make(SkScalarRoundToInt(deviceBounds.width()), SkScalarRoundToInt(deviceBounds.height())); SkAutoTUnref<SkPDFDevice> patternDevice( SkPDFDevice::CreateUnflipped(size, dpi, canon)); SkCanvas canvas(patternDevice.get()); SkRect patternBBox; image->getBounds(&patternBBox); // Translate the canvas so that the bitmap origin is at (0, 0). canvas.translate(-deviceBounds.left(), -deviceBounds.top()); patternBBox.offset(-deviceBounds.left(), -deviceBounds.top()); // Undo the translation in the final matrix finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top()); // If the bitmap is out of bounds (i.e. clamp mode where we only see the // stretched sides), canvas will clip this out and the extraneous data // won't be saved to the PDF. canvas.drawBitmap(*image, 0, 0); SkScalar width = SkIntToScalar(image->width()); SkScalar height = SkIntToScalar(image->height()); // Tiling is implied. First we handle mirroring. if (tileModes[0] == SkShader::kMirror_TileMode) { SkMatrix xMirror; xMirror.setScale(-1, 1); xMirror.postTranslate(2 * width, 0); drawBitmapMatrix(&canvas, *image, xMirror); patternBBox.fRight += width; } if (tileModes[1] == SkShader::kMirror_TileMode) { SkMatrix yMirror; yMirror.setScale(SK_Scalar1, -SK_Scalar1); yMirror.postTranslate(0, 2 * height); drawBitmapMatrix(&canvas, *image, yMirror); patternBBox.fBottom += height; } if (tileModes[0] == SkShader::kMirror_TileMode && tileModes[1] == SkShader::kMirror_TileMode) { SkMatrix mirror; mirror.setScale(-1, -1); mirror.postTranslate(2 * width, 2 * height); drawBitmapMatrix(&canvas, *image, mirror); } // Then handle Clamping, which requires expanding the pattern canvas to // cover the entire surfaceBBox. // If both x and y are in clamp mode, we start by filling in the corners. // (Which are just a rectangles of the corner colors.) if (tileModes[0] == SkShader::kClamp_TileMode && tileModes[1] == SkShader::kClamp_TileMode) { SkPaint paint; SkRect rect; rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, deviceBounds.top(), deviceBounds.right(), 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(image->width() - 1, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, height, deviceBounds.right(), deviceBounds.bottom()); if (!rect.isEmpty()) { paint.setColor(image->getColor(image->width() - 1, image->height() - 1)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(deviceBounds.left(), height, 0, deviceBounds.bottom()); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, image->height() - 1)); canvas.drawRect(rect, paint); } } // Then expand the left, right, top, then bottom. if (tileModes[0] == SkShader::kClamp_TileMode) { SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image->height()); if (deviceBounds.left() < 0) { SkBitmap left; SkAssertResult(image->extractSubset(&left, subset)); SkMatrix leftMatrix; leftMatrix.setScale(-deviceBounds.left(), 1); leftMatrix.postTranslate(deviceBounds.left(), 0); drawBitmapMatrix(&canvas, left, leftMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { leftMatrix.postScale(SK_Scalar1, -SK_Scalar1); leftMatrix.postTranslate(0, 2 * height); drawBitmapMatrix(&canvas, left, leftMatrix); } patternBBox.fLeft = 0; } if (deviceBounds.right() > width) { SkBitmap right; subset.offset(image->width() - 1, 0); SkAssertResult(image->extractSubset(&right, subset)); SkMatrix rightMatrix; rightMatrix.setScale(deviceBounds.right() - width, 1); rightMatrix.postTranslate(width, 0); drawBitmapMatrix(&canvas, right, rightMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { rightMatrix.postScale(SK_Scalar1, -SK_Scalar1); rightMatrix.postTranslate(0, 2 * height); drawBitmapMatrix(&canvas, right, rightMatrix); } patternBBox.fRight = deviceBounds.width(); } } if (tileModes[1] == SkShader::kClamp_TileMode) { SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1); if (deviceBounds.top() < 0) { SkBitmap top; SkAssertResult(image->extractSubset(&top, subset)); SkMatrix topMatrix; topMatrix.setScale(SK_Scalar1, -deviceBounds.top()); topMatrix.postTranslate(0, deviceBounds.top()); drawBitmapMatrix(&canvas, top, topMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { topMatrix.postScale(-1, 1); topMatrix.postTranslate(2 * width, 0); drawBitmapMatrix(&canvas, top, topMatrix); } patternBBox.fTop = 0; } if (deviceBounds.bottom() > height) { SkBitmap bottom; subset.offset(0, image->height() - 1); SkAssertResult(image->extractSubset(&bottom, subset)); SkMatrix bottomMatrix; bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height); bottomMatrix.postTranslate(0, height); drawBitmapMatrix(&canvas, bottom, bottomMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { bottomMatrix.postScale(-1, 1); bottomMatrix.postTranslate(2 * width, 0); drawBitmapMatrix(&canvas, bottom, bottomMatrix); } patternBBox.fBottom = deviceBounds.height(); } } // Put the canvas into the pattern stream (fContent). SkAutoTDelete<SkStreamAsset> content(patternDevice->content()); SkPDFImageShader* imageShader = SkNEW_ARGS(SkPDFImageShader, (autoState->detach())); imageShader->setData(content.get()); SkAutoTUnref<SkPDFDict> resourceDict( patternDevice->createResourceDict()); populate_tiling_pattern_dict(imageShader, patternBBox, resourceDict.get(), finalMatrix); imageShader->fShaderState->fImage.unlockPixels(); canon->addImageShader(imageShader); return imageShader; }
SkPDFShader::State::State(const SkShader& shader, const SkMatrix& canvasTransform, const SkIRect& bbox, SkScalar rasterScale) : fCanvasTransform(canvasTransform), fBBox(bbox), fPixelGeneration(0) { fInfo.fColorCount = 0; fInfo.fColors = NULL; fInfo.fColorOffsets = NULL; fShaderTransform = shader.getLocalMatrix(); fImageTileModes[0] = fImageTileModes[1] = SkShader::kClamp_TileMode; fType = shader.asAGradient(&fInfo); if (fType == SkShader::kNone_GradientType) { SkShader::BitmapType bitmapType; SkMatrix matrix; bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes); if (bitmapType != SkShader::kDefault_BitmapType) { // Generic fallback for unsupported shaders: // * allocate a bbox-sized bitmap // * shade the whole area // * use the result as a bitmap shader // bbox is in device space. While that's exactly what we want for sizing our bitmap, // we need to map it into shader space for adjustments (to match // SkPDFImageShader::Create's behavior). SkRect shaderRect = SkRect::Make(bbox); if (!inverse_transform_bbox(canvasTransform, &shaderRect)) { fImage.reset(); return; } // Clamp the bitmap size to about 1M pixels static const SkScalar kMaxBitmapArea = 1024 * 1024; SkScalar bitmapArea = rasterScale * bbox.width() * rasterScale * bbox.height(); if (bitmapArea > kMaxBitmapArea) { rasterScale *= SkScalarSqrt(SkScalarDiv(kMaxBitmapArea, bitmapArea)); } SkISize size = SkISize::Make(SkScalarRoundToInt(rasterScale * bbox.width()), SkScalarRoundToInt(rasterScale * bbox.height())); SkSize scale = SkSize::Make(SkIntToScalar(size.width()) / shaderRect.width(), SkIntToScalar(size.height()) / shaderRect.height()); fImage.allocN32Pixels(size.width(), size.height()); fImage.eraseColor(SK_ColorTRANSPARENT); SkPaint p; p.setShader(const_cast<SkShader*>(&shader)); SkCanvas canvas(fImage); canvas.scale(scale.width(), scale.height()); canvas.translate(-shaderRect.x(), -shaderRect.y()); canvas.drawPaint(p); fShaderTransform.setTranslate(shaderRect.x(), shaderRect.y()); fShaderTransform.preScale(1 / scale.width(), 1 / scale.height()); } else { SkASSERT(matrix.isIdentity()); } fPixelGeneration = fImage.getGenerationID(); } else { AllocateGradientInfoStorage(); shader.asAGradient(&fInfo); } }
bool SkOffsetImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkImageFilter* input = getInput(0); SkBitmap src = source; SkIPoint srcOffset = SkIPoint::Make(0, 0); #ifdef SK_DISABLE_OFFSETIMAGEFILTER_OPTIMIZATION if (false) { #else if (!cropRectIsSet()) { #endif if (input && !input->filterImage(proxy, source, ctx, &src, &srcOffset)) { return false; } SkVector vec; ctx.ctm().mapVectors(&vec, &fOffset, 1); offset->fX = srcOffset.fX + SkScalarRoundToInt(vec.fX); offset->fY = srcOffset.fY + SkScalarRoundToInt(vec.fY); *result = src; } else { if (input && !input->filterImage(proxy, source, ctx, &src, &srcOffset)) { return false; } SkIRect bounds; if (!this->applyCropRect(ctx, src, srcOffset, &bounds)) { return false; } SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds.width(), bounds.height())); if (NULL == device.get()) { return false; } SkCanvas canvas(device); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); canvas.translate(SkIntToScalar(srcOffset.fX - bounds.fLeft), SkIntToScalar(srcOffset.fY - bounds.fTop)); SkVector vec; ctx.ctm().mapVectors(&vec, &fOffset, 1); canvas.drawBitmap(src, vec.x(), vec.y(), &paint); *result = device->accessBitmap(false); offset->fX = bounds.fLeft; offset->fY = bounds.fTop; } return true; } void SkOffsetImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const { if (getInput(0)) { getInput(0)->computeFastBounds(src, dst); } else { *dst = src; } SkRect copy = *dst; dst->offset(fOffset.fX, fOffset.fY); dst->join(copy); } bool SkOffsetImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const { SkVector vec; ctm.mapVectors(&vec, &fOffset, 1); SkIRect bounds = src; bounds.offset(-SkScalarCeilToInt(vec.fX), -SkScalarCeilToInt(vec.fY)); bounds.join(src); if (getInput(0)) { return getInput(0)->filterBounds(bounds, ctm, dst); } *dst = bounds; return true; }
static SkISize SkSizeToISize(const SkSize& size) { return SkISize::Make(SkScalarRoundToInt(size.width()), SkScalarRoundToInt(size.height())); }
bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src, SkScalar provided_radius, Style style, SkIPoint *margin, SkMask::CreateMode createMode) { int profile_size; float radius = SkScalarToFloat(SkScalarMul(provided_radius, kBlurRadiusFudgeFactor)); // adjust blur radius to match interpretation from boxfilter code radius = (radius + .5f) * 2.f; profile_size = compute_profile_size(radius); int pad = profile_size/2; if (margin) { margin->set( pad, pad ); } dst->fBounds.set(SkScalarRoundToInt(src.fLeft - pad), SkScalarRoundToInt(src.fTop - pad), SkScalarRoundToInt(src.fRight + pad), SkScalarRoundToInt(src.fBottom + pad)); dst->fRowBytes = dst->fBounds.width(); dst->fFormat = SkMask::kA8_Format; dst->fImage = NULL; int sw = SkScalarFloorToInt(src.width()); int sh = SkScalarFloorToInt(src.height()); if (createMode == SkMask::kJustComputeBounds_CreateMode) { if (style == kInner_Style) { dst->fBounds.set(SkScalarRoundToInt(src.fLeft), SkScalarRoundToInt(src.fTop), SkScalarRoundToInt(src.fRight), SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds dst->fRowBytes = sw; } return true; } unsigned int *profile = NULL; compute_profile(radius, &profile); SkAutoTDeleteArray<unsigned int> ada(profile); size_t dstSize = dst->computeImageSize(); if (0 == dstSize) { return false; // too big to allocate, abort } uint8_t* dp = SkMask::AllocImage(dstSize); dst->fImage = dp; int dstHeight = dst->fBounds.height(); int dstWidth = dst->fBounds.width(); // nearest odd number less than the profile size represents the center // of the (2x scaled) profile int center = ( profile_size & ~1 ) - 1; int w = sw - center; int h = sh - center; uint8_t *outptr = dp; SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth); for (int x = 0 ; x < dstWidth ; ++x) { if (profile_size <= sw) { horizontalScanline[x] = profile_lookup(profile, x, dstWidth, w); } else { float span = float(sw)/radius; float giX = 1.5f - (x+.5f)/radius; horizontalScanline[x] = (uint8_t) (255 * (gaussianIntegral(giX) - gaussianIntegral(giX + span))); } } for (int y = 0 ; y < dstHeight ; ++y) { unsigned int profile_y; if (profile_size <= sh) { profile_y = profile_lookup(profile, y, dstHeight, h); } else { float span = float(sh)/radius; float giY = 1.5f - (y+.5f)/radius; profile_y = (uint8_t) (255 * (gaussianIntegral(giY) - gaussianIntegral(giY + span))); } for (int x = 0 ; x < dstWidth ; x++) { unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], profile_y); *(outptr++) = maskval; } } if (style == kInner_Style) { // now we allocate the "real" dst, mirror the size of src size_t srcSize = (size_t)(src.width() * src.height()); if (0 == srcSize) { return false; // too big to allocate, abort } dst->fImage = SkMask::AllocImage(srcSize); for (int y = 0 ; y < sh ; y++) { uint8_t *blur_scanline = dp + (y+pad)*dstWidth + pad; uint8_t *inner_scanline = dst->fImage + y*sw; memcpy(inner_scanline, blur_scanline, sw); } SkMask::FreeImage(dp); dst->fBounds.set(SkScalarRoundToInt(src.fLeft), SkScalarRoundToInt(src.fTop), SkScalarRoundToInt(src.fRight), SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds dst->fRowBytes = sw; } else if (style == kOuter_Style) { for (int y = pad ; y < dstHeight-pad ; y++) { uint8_t *dst_scanline = dp + y*dstWidth + pad; memset(dst_scanline, 0, sw); } } else if (style == kSolid_Style) { for (int y = pad ; y < dstHeight-pad ; y++) { uint8_t *dst_scanline = dp + y*dstWidth + pad; memset(dst_scanline, 0xff, sw); } } // normal and solid styles are the same for analytic rect blurs, so don't // need to handle solid specially. return true; }
static int compute_profile_size(SkScalar radius) { return SkScalarRoundToInt(radius * 3); }
bool SkBlurMask::BoxBlur(SkMask* dst, const SkMask& src, SkScalar sigma, SkBlurStyle style, SkBlurQuality quality, SkIPoint* margin, bool force_quality) { if (src.fFormat != SkMask::kA8_Format) { return false; } SkIPoint border; #ifdef SK_SUPPORT_LEGACY_MASK_BLUR auto get_adjusted_radii = [](SkScalar passRadius, int *loRadius, int *hiRadius) { *loRadius = *hiRadius = SkScalarCeilToInt(passRadius); if (SkIntToScalar(*hiRadius) - passRadius > 0.5f) { *loRadius = *hiRadius - 1; } }; // Force high quality off for small radii (performance) if (!force_quality && sigma <= SkIntToScalar(2)) { quality = kLow_SkBlurQuality; } SkScalar passRadius; if (kHigh_SkBlurQuality == quality) { // For the high quality path the 3 pass box blur kernel width is // 6*rad+1 while the full Gaussian width is 6*sigma. passRadius = sigma - (1 / 6.0f); } else { // For the low quality path we only attempt to cover 3*sigma of the // Gaussian blur area (1.5*sigma on each side). The single pass box // blur's kernel size is 2*rad+1. passRadius = 1.5f * sigma - 0.5f; } // highQuality: use three box blur passes as a cheap way // to approximate a Gaussian blur int passCount = (kHigh_SkBlurQuality == quality) ? 3 : 1; int rx = SkScalarCeilToInt(passRadius); int outerWeight = 255 - SkScalarRoundToInt((SkIntToScalar(rx) - passRadius) * 255); SkASSERT(rx >= 0); SkASSERT((unsigned)outerWeight <= 255); if (rx <= 0) { return false; } int ry = rx; // only do square blur for now int padx = passCount * rx; int pady = passCount * ry; border = {padx, pady}; dst->fBounds.set(src.fBounds.fLeft - padx, src.fBounds.fTop - pady, src.fBounds.fRight + padx, src.fBounds.fBottom + pady); dst->fRowBytes = dst->fBounds.width(); dst->fFormat = SkMask::kA8_Format; dst->fImage = nullptr; if (src.fImage) { size_t dstSize = dst->computeImageSize(); if (0 == dstSize) { return false; // too big to allocate, abort } int sw = src.fBounds.width(); int sh = src.fBounds.height(); const uint8_t* sp = src.fImage; uint8_t* dp = SkMask::AllocImage(dstSize); SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp); // build the blurry destination SkAutoTMalloc<uint8_t> tmpBuffer(dstSize); uint8_t* tp = tmpBuffer.get(); int w = sw, h = sh; if (outerWeight == 255) { int loRadius, hiRadius; get_adjusted_radii(passRadius, &loRadius, &hiRadius); if (kHigh_SkBlurQuality == quality) { // Do three X blurs, with a transpose on the final one. w = boxBlur<false>(sp, src.fRowBytes, tp, loRadius, hiRadius, w, h); w = boxBlur<false>(tp, w, dp, hiRadius, loRadius, w, h); w = boxBlur<true>(dp, w, tp, hiRadius, hiRadius, w, h); // Do three Y blurs, with a transpose on the final one. h = boxBlur<false>(tp, h, dp, loRadius, hiRadius, h, w); h = boxBlur<false>(dp, h, tp, hiRadius, loRadius, h, w); h = boxBlur<true>(tp, h, dp, hiRadius, hiRadius, h, w); } else { w = boxBlur<true>(sp, src.fRowBytes, tp, rx, rx, w, h); h = boxBlur<true>(tp, h, dp, ry, ry, h, w); } } else { if (kHigh_SkBlurQuality == quality) { // Do three X blurs, with a transpose on the final one. w = boxBlurInterp<false>(sp, src.fRowBytes, tp, rx, w, h, outerWeight); w = boxBlurInterp<false>(tp, w, dp, rx, w, h, outerWeight); w = boxBlurInterp<true>(dp, w, tp, rx, w, h, outerWeight); // Do three Y blurs, with a transpose on the final one. h = boxBlurInterp<false>(tp, h, dp, ry, h, w, outerWeight); h = boxBlurInterp<false>(dp, h, tp, ry, h, w, outerWeight); h = boxBlurInterp<true>(tp, h, dp, ry, h, w, outerWeight); } else { w = boxBlurInterp<true>(sp, src.fRowBytes, tp, rx, w, h, outerWeight); h = boxBlurInterp<true>(tp, h, dp, ry, h, w, outerWeight); } } dst->fImage = autoCall.release(); } #else SkMaskBlurFilter blurFilter{sigma, sigma}; border = blurFilter.blur(src, dst); #endif // SK_SUPPORT_LEGACY_MASK_BLUR if (src.fImage != nullptr) { // if need be, alloc the "real" dst (same size as src) and copy/merge // the blur into it (applying the src) if (style == kInner_SkBlurStyle) { // now we allocate the "real" dst, mirror the size of src size_t srcSize = src.computeImageSize(); if (0 == srcSize) { return false; // too big to allocate, abort } auto blur = dst->fImage; dst->fImage = SkMask::AllocImage(srcSize); auto blurStart = &blur[border.x() + border.y() * dst->fRowBytes]; merge_src_with_blur(dst->fImage, src.fRowBytes, src.fImage, src.fRowBytes, blurStart, dst->fRowBytes, src.fBounds.width(), src.fBounds.height()); SkMask::FreeImage(blur); } else if (style != kNormal_SkBlurStyle) { auto dstStart = &dst->fImage[border.x() + border.y() * dst->fRowBytes]; clamp_with_orig(dstStart, dst->fRowBytes, src.fImage, src.fRowBytes, src.fBounds.width(), src.fBounds.height(), style); } } if (style == kInner_SkBlurStyle) { dst->fBounds = src.fBounds; // restore trimmed bounds dst->fRowBytes = src.fRowBytes; } if (margin != nullptr) { *margin = border; } return true; }
SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override { fAlpha = SkScalarRoundToInt(y); this->inval(NULL); return NULL; }
bool SkBlurMask::BlurRect(SkScalar sigma, SkMask *dst, const SkRect &src, SkBlurStyle style, SkIPoint *margin, SkMask::CreateMode createMode) { int profile_size = SkScalarCeilToInt(6*sigma); int pad = profile_size/2; if (margin) { margin->set( pad, pad ); } dst->fBounds.set(SkScalarRoundToInt(src.fLeft - pad), SkScalarRoundToInt(src.fTop - pad), SkScalarRoundToInt(src.fRight + pad), SkScalarRoundToInt(src.fBottom + pad)); dst->fRowBytes = dst->fBounds.width(); dst->fFormat = SkMask::kA8_Format; dst->fImage = nullptr; int sw = SkScalarFloorToInt(src.width()); int sh = SkScalarFloorToInt(src.height()); if (createMode == SkMask::kJustComputeBounds_CreateMode) { if (style == kInner_SkBlurStyle) { dst->fBounds.set(SkScalarRoundToInt(src.fLeft), SkScalarRoundToInt(src.fTop), SkScalarRoundToInt(src.fRight), SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds dst->fRowBytes = sw; } return true; } std::unique_ptr<uint8_t[]> profile(ComputeBlurProfile(sigma)); size_t dstSize = dst->computeImageSize(); if (0 == dstSize) { return false; // too big to allocate, abort } uint8_t* dp = SkMask::AllocImage(dstSize); dst->fImage = dp; int dstHeight = dst->fBounds.height(); int dstWidth = dst->fBounds.width(); uint8_t *outptr = dp; SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth); SkAutoTMalloc<uint8_t> verticalScanline(dstHeight); ComputeBlurredScanline(horizontalScanline, profile.get(), dstWidth, sigma); ComputeBlurredScanline(verticalScanline, profile.get(), dstHeight, sigma); for (int y = 0 ; y < dstHeight ; ++y) { for (int x = 0 ; x < dstWidth ; x++) { unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], verticalScanline[y]); *(outptr++) = maskval; } } if (style == kInner_SkBlurStyle) { // now we allocate the "real" dst, mirror the size of src size_t srcSize = (size_t)(src.width() * src.height()); if (0 == srcSize) { return false; // too big to allocate, abort } dst->fImage = SkMask::AllocImage(srcSize); for (int y = 0 ; y < sh ; y++) { uint8_t *blur_scanline = dp + (y+pad)*dstWidth + pad; uint8_t *inner_scanline = dst->fImage + y*sw; memcpy(inner_scanline, blur_scanline, sw); } SkMask::FreeImage(dp); dst->fBounds.set(SkScalarRoundToInt(src.fLeft), SkScalarRoundToInt(src.fTop), SkScalarRoundToInt(src.fRight), SkScalarRoundToInt(src.fBottom)); // restore trimmed bounds dst->fRowBytes = sw; } else if (style == kOuter_SkBlurStyle) { for (int y = pad ; y < dstHeight-pad ; y++) { uint8_t *dst_scanline = dp + y*dstWidth + pad; memset(dst_scanline, 0, sw); } } else if (style == kSolid_SkBlurStyle) { for (int y = pad ; y < dstHeight-pad ; y++) { uint8_t *dst_scanline = dp + y*dstWidth + pad; memset(dst_scanline, 0xff, sw); } } // normal and solid styles are the same for analytic rect blurs, so don't // need to handle solid specially. return true; }
void SimpleFontData::platformInit(bool subpixelAscentDescent) { if (!m_platformData.size()) { m_fontMetrics.reset(); m_avgCharWidth = 0; m_maxCharWidth = 0; return; } SkPaint::FontMetrics metrics; m_platformData.setupPaint(&m_paint); m_paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); m_paint.getFontMetrics(&metrics); SkTypeface* face = m_paint.getTypeface(); ASSERT(face); int vdmxAscent = 0, vdmxDescent = 0; bool isVDMXValid = false; #if OS(LINUX) || OS(ANDROID) // Manually digging up VDMX metrics is only applicable when bytecode hinting // using FreeType. With DirectWrite or CoreText, no bytecode hinting is ever // done. This code should be pushed into FreeType (hinted font metrics). static const uint32_t vdmxTag = SkSetFourByteTag('V', 'D', 'M', 'X'); int pixelSize = m_platformData.size() + 0.5; if (!m_paint.isAutohinted() && (m_paint.getHinting() == SkPaint::kFull_Hinting || m_paint.getHinting() == SkPaint::kNormal_Hinting)) { size_t vdmxSize = face->getTableSize(vdmxTag); if (vdmxSize && vdmxSize < maxVDMXTableSize) { uint8_t* vdmxTable = (uint8_t*)WTF::Partitions::fastMalloc( vdmxSize, WTF_HEAP_PROFILER_TYPE_NAME(SimpleFontData)); if (vdmxTable && face->getTableData(vdmxTag, 0, vdmxSize, vdmxTable) == vdmxSize && parseVDMX(&vdmxAscent, &vdmxDescent, vdmxTable, vdmxSize, pixelSize)) isVDMXValid = true; WTF::Partitions::fastFree(vdmxTable); } } #endif float ascent; float descent; // Beware those who step here: This code is designed to match Win32 font // metrics *exactly* except: // - the adjustment of ascent/descent on Linux/Android // - metrics.fAscent and .fDesscent are not rounded to int for tiny fonts if (isVDMXValid) { ascent = vdmxAscent; descent = -vdmxDescent; } else { // For tiny fonts, the rounding of fAscent and fDescent results in equal // baseline for different types of text baselines (crbug.com/338908). // Please see CanvasRenderingContext2D::getFontBaseline for the heuristic. if (subpixelAscentDescent && (-metrics.fAscent < 3 || -metrics.fAscent + metrics.fDescent < 2)) { ascent = -metrics.fAscent; descent = metrics.fDescent; } else { ascent = SkScalarRoundToScalar(-metrics.fAscent); descent = SkScalarRoundToScalar(metrics.fDescent); } #if OS(LINUX) || OS(ANDROID) // When subpixel positioning is enabled, if the descent is rounded down, the // descent part of the glyph may be truncated when displayed in a 'overflow: // hidden' container. To avoid that, borrow 1 unit from the ascent when // possible. // FIXME: This can be removed if sub-pixel ascent/descent is supported. if (platformData().getFontRenderStyle().useSubpixelPositioning && descent < SkScalarToFloat(metrics.fDescent) && ascent >= 1) { ++descent; --ascent; } #endif } #if OS(MACOSX) // We are preserving this ascent hack to match Safari's ascent adjustment // in their SimpleFontDataMac.mm, for details see crbug.com/445830. // We need to adjust Times, Helvetica, and Courier to closely match the // vertical metrics of their Microsoft counterparts that are the de facto // web standard. The AppKit adjustment of 20% is too big and is // incorrectly added to line spacing, so we use a 15% adjustment instead // and add it to the ascent. DEFINE_STATIC_LOCAL(AtomicString, timesName, ("Times")); DEFINE_STATIC_LOCAL(AtomicString, helveticaName, ("Helvetica")); DEFINE_STATIC_LOCAL(AtomicString, courierName, ("Courier")); String familyName = m_platformData.fontFamilyName(); if (familyName == timesName || familyName == helveticaName || familyName == courierName) ascent += floorf(((ascent + descent) * 0.15f) + 0.5f); #endif m_fontMetrics.setAscent(ascent); m_fontMetrics.setDescent(descent); float xHeight; if (metrics.fXHeight) { xHeight = metrics.fXHeight; #if OS(MACOSX) // Mac OS CTFontGetXHeight reports the bounding box height of x, // including parts extending below the baseline and apparently no x-height // value from the OS/2 table. However, the CSS ex unit // expects only parts above the baseline, hence measuring the glyph: // http://www.w3.org/TR/css3-values/#ex-unit const Glyph xGlyph = glyphForCharacter('x'); if (xGlyph) { FloatRect glyphBounds(boundsForGlyph(xGlyph)); // SkGlyph bounds, y down, based on rendering at (0,0). xHeight = -glyphBounds.y(); } #endif m_fontMetrics.setXHeight(xHeight); } else { xHeight = ascent * 0.56; // Best guess from Windows font metrics. m_fontMetrics.setXHeight(xHeight); m_fontMetrics.setHasXHeight(false); } float lineGap = SkScalarToFloat(metrics.fLeading); m_fontMetrics.setLineGap(lineGap); m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); if (platformData().isVerticalAnyUpright() && !isTextOrientationFallback()) { static const uint32_t vheaTag = SkSetFourByteTag('v', 'h', 'e', 'a'); static const uint32_t vorgTag = SkSetFourByteTag('V', 'O', 'R', 'G'); size_t vheaSize = face->getTableSize(vheaTag); size_t vorgSize = face->getTableSize(vorgTag); if ((vheaSize > 0) || (vorgSize > 0)) m_hasVerticalGlyphs = true; } // In WebKit/WebCore/platform/graphics/SimpleFontData.cpp, m_spaceWidth is // calculated for us, but we need to calculate m_maxCharWidth and // m_avgCharWidth in order for text entry widgets to be sized correctly. #if OS(WIN) m_maxCharWidth = SkScalarRoundToInt(metrics.fMaxCharWidth); // Older version of the DirectWrite API doesn't implement support for max // char width. Fall back on a multiple of the ascent. This is entirely // arbitrary but comes pretty close to the expected value in most cases. if (m_maxCharWidth < 1) m_maxCharWidth = ascent * 2; #elif OS(MACOSX) // FIXME: The current avg/max character width calculation is not ideal, // it should check either the OS2 table or, better yet, query FontMetrics. // Sadly FontMetrics provides incorrect data on Mac at the moment. // https://crbug.com/420901 m_maxCharWidth = std::max(m_avgCharWidth, m_fontMetrics.floatAscent()); #else // Better would be to rely on either fMaxCharWidth or fAveCharWidth. // skbug.com/3087 m_maxCharWidth = SkScalarRoundToInt(metrics.fXMax - metrics.fXMin); #endif #if !OS(MACOSX) if (metrics.fAvgCharWidth) { m_avgCharWidth = SkScalarRoundToInt(metrics.fAvgCharWidth); } else { #endif m_avgCharWidth = xHeight; const Glyph xGlyph = glyphForCharacter('x'); if (xGlyph) { m_avgCharWidth = widthForGlyph(xGlyph); } #if !OS(MACOSX) } #endif if (int unitsPerEm = face->getUnitsPerEm()) m_fontMetrics.setUnitsPerEm(unitsPerEm); }
// Only called once. Could be part of the constructor. void init(SkScalar seed) { static const SkScalar gInvBlockSizef = SkScalarInvert(SkIntToScalar(kBlockSize)); // According to the SVG spec, we must truncate (not round) the seed value. fSeed = SkScalarTruncToInt(seed); // The seed value clamp to the range [1, kRandMaximum - 1]. if (fSeed <= 0) { fSeed = -(fSeed % (kRandMaximum - 1)) + 1; } if (fSeed > kRandMaximum - 1) { fSeed = kRandMaximum - 1; } for (int channel = 0; channel < 4; ++channel) { for (int i = 0; i < kBlockSize; ++i) { fLatticeSelector[i] = i; fNoise[channel][i][0] = (random() % (2 * kBlockSize)); fNoise[channel][i][1] = (random() % (2 * kBlockSize)); } } for (int i = kBlockSize - 1; i > 0; --i) { int k = fLatticeSelector[i]; int j = random() % kBlockSize; SkASSERT(j >= 0); SkASSERT(j < kBlockSize); fLatticeSelector[i] = fLatticeSelector[j]; fLatticeSelector[j] = k; } // Perform the permutations now { // Copy noise data uint16_t noise[4][kBlockSize][2]; for (int i = 0; i < kBlockSize; ++i) { for (int channel = 0; channel < 4; ++channel) { for (int j = 0; j < 2; ++j) { noise[channel][i][j] = fNoise[channel][i][j]; } } } // Do permutations on noise data for (int i = 0; i < kBlockSize; ++i) { for (int channel = 0; channel < 4; ++channel) { for (int j = 0; j < 2; ++j) { fNoise[channel][i][j] = noise[channel][fLatticeSelector[i]][j]; } } } } // Half of the largest possible value for 16 bit unsigned int static const SkScalar gHalfMax16bits = 32767.5f; // Compute gradients from permutated noise data for (int channel = 0; channel < 4; ++channel) { for (int i = 0; i < kBlockSize; ++i) { fGradient[channel][i] = SkPoint::Make( SkScalarMul(SkIntToScalar(fNoise[channel][i][0] - kBlockSize), gInvBlockSizef), SkScalarMul(SkIntToScalar(fNoise[channel][i][1] - kBlockSize), gInvBlockSizef)); fGradient[channel][i].normalize(); // Put the normalized gradient back into the noise data fNoise[channel][i][0] = SkScalarRoundToInt(SkScalarMul( fGradient[channel][i].fX + SK_Scalar1, gHalfMax16bits)); fNoise[channel][i][1] = SkScalarRoundToInt(SkScalarMul( fGradient[channel][i].fY + SK_Scalar1, gHalfMax16bits)); } } }