void FatBits::drawLine(SkCanvas* canvas, SkPoint pts[]) {
    SkPaint paint;

    fInverse.mapPoints(pts, 2);

    if (fGrid) {
        SkScalar dd = 0;//SK_Scalar1 / 50;
        pts[0].set(SkScalarRoundToScalar(pts[0].fX) + dd,
                   SkScalarRoundToScalar(pts[0].fY) + dd);
        pts[1].set(SkScalarRoundToScalar(pts[1].fX) + dd,
                   SkScalarRoundToScalar(pts[1].fY) + dd);
    }

    erase(fMinSurface);
    this->setupPaint(&paint);
    paint.setColor(FAT_PIXEL_COLOR);
    if (fUseClip) {
        fMinSurface->getCanvas()->save();
        SkRect r = fClipRect;
        r.inset(SK_Scalar1/3, SK_Scalar1/3);
        fMinSurface->getCanvas()->clipRect(r, SkRegion::kIntersect_Op, true);
    }
    fMinSurface->getCanvas()->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
    if (fUseClip) {
        fMinSurface->getCanvas()->restore();
    }
    this->copyMinToMax();

    SkCanvas* max = fMaxSurface->getCanvas();

    fMatrix.mapPoints(pts, 2);
    this->drawLineSkeleton(max, pts);

    fMaxSurface->draw(canvas, 0, 0, NULL);
}
void FatBits::drawRect(SkCanvas* canvas, SkPoint pts[2]) {
    SkPaint paint;

    fInverse.mapPoints(pts, 2);

    if (fGrid) {
        pts[0].set(SkScalarRoundToScalar(pts[0].fX), SkScalarRoundToScalar(pts[0].fY));
        pts[1].set(SkScalarRoundToScalar(pts[1].fX), SkScalarRoundToScalar(pts[1].fY));
    }

    SkRect r;
    r.set(pts, 2);

    erase(fMinSurface);
    this->setupPaint(&paint);
    paint.setColor(FAT_PIXEL_COLOR);
    {
        SkCanvas* c = fMinSurface->getCanvas();
        fRectAsOval ? c->drawOval(r, paint) : c->drawRect(r, paint);
    }
    this->copyMinToMax();

    SkCanvas* max = fMaxSurface->getCanvas();

    fMatrix.mapPoints(pts, 2);
    r.set(pts, 2);
    this->drawRectSkeleton(max, r);

    fMaxSurface->draw(canvas, 0, 0, NULL);
}
/**
 *  Determine if the matrix can be treated as integral-only-translate,
 *  for the purpose of filtering.
 */
static bool just_trans_integral(const SkMatrix& m) {
    static constexpr SkScalar tol = SK_Scalar1 / 256;

    return m.getType() <= SkMatrix::kTranslate_Mask
        && SkScalarNearlyEqual(m.getTranslateX(), SkScalarRoundToScalar(m.getTranslateX()), tol)
        && SkScalarNearlyEqual(m.getTranslateY(), SkScalarRoundToScalar(m.getTranslateY()), tol);
}
Beispiel #4
0
/*
 *  High quality is implemented by performing up-right scale-only filtering and then
 *  using bilerp for any remaining transformations.
 */
bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmap& origBitmap) {
    if (fQuality != kHigh_SkFilterQuality) {
        return false;
    }
    
    // Our default return state is to downgrade the request to Medium, w/ or w/o setting fBitmap
    // to a valid bitmap. If we succeed, we will set this to Low instead.
    fQuality = kMedium_SkFilterQuality;
    
    if (kN32_SkColorType != origBitmap.colorType() || !cache_size_okay(origBitmap, fInvMatrix) ||
        fInvMatrix.hasPerspective())
    {
        return false; // can't handle the reqeust
    }
    
    SkScalar invScaleX = fInvMatrix.getScaleX();
    SkScalar invScaleY = fInvMatrix.getScaleY();
    if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
        SkSize scale;
        if (!fInvMatrix.decomposeScale(&scale)) {
            return false;
        }
        invScaleX = scale.width();
        invScaleY = scale.height();
    }
    if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) {
        return false; // no need for HQ
    }
    
    SkScalar trueDestWidth  = origBitmap.width() / invScaleX;
    SkScalar trueDestHeight = origBitmap.height() / invScaleY;
    SkScalar roundedDestWidth = SkScalarRoundToScalar(trueDestWidth);
    SkScalar roundedDestHeight = SkScalarRoundToScalar(trueDestHeight);
    
    if (!SkBitmapCache::Find(origBitmap, roundedDestWidth, roundedDestHeight, &fResultBitmap)) {
        SkAutoPixmapUnlock src;
        if (!origBitmap.requestLock(&src)) {
            return false;
        }
        if (!SkBitmapScaler::Resize(&fResultBitmap, src.pixmap(), SkBitmapScaler::RESIZE_BEST,
                                    roundedDestWidth, roundedDestHeight,
                                    SkResourceCache::GetAllocator())) {
            return false; // we failed to create fScaledBitmap
        }
        
        SkASSERT(fResultBitmap.getPixels());
        fResultBitmap.setImmutable();
        SkBitmapCache::Add(origBitmap, roundedDestWidth, roundedDestHeight, fResultBitmap);
    }
    
    SkASSERT(fResultBitmap.getPixels());
    
    fInvMatrix.postScale(roundedDestWidth / origBitmap.width(),
                         roundedDestHeight / origBitmap.height());
    fQuality = kLow_SkFilterQuality;
    return true;
}
static bool has_aligned_samples(const SkRect& srcRect, const SkRect& transformedRect) {
    // detect pixel disalignment
    if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) - transformedRect.left()) < kColorBleedTolerance &&
        SkScalarAbs(SkScalarRoundToScalar(transformedRect.top())  - transformedRect.top())  < kColorBleedTolerance &&
        SkScalarAbs(transformedRect.width()  - srcRect.width())  < kColorBleedTolerance &&
        SkScalarAbs(transformedRect.height() - srcRect.height()) < kColorBleedTolerance) {
        return true;
    }
    return false;
}
Beispiel #6
0
SkPMColor SkPerlinNoiseShader::PerlinNoiseShaderContext::shade(
        const SkPoint& point, StitchData& stitchData) const {
    SkPoint newPoint;
    fMatrix.mapPoints(&newPoint, &point, 1);
    newPoint.fX = SkScalarRoundToScalar(newPoint.fX);
    newPoint.fY = SkScalarRoundToScalar(newPoint.fY);

    U8CPU rgba[4];
    for (int channel = 3; channel >= 0; --channel) {
        rgba[channel] = SkScalarFloorToInt(255 *
            calculateTurbulenceValueForPoint(channel, stitchData, newPoint));
    }
    return SkPreMultiplyARGB(rgba[3], rgba[0], rgba[1], rgba[2]);
}
Beispiel #7
0
    virtual void onDrawContent(SkCanvas* canvas) {
        canvas->translate(SkIntToScalar(10), SkIntToScalar(50));

        const SkScalar W = SkIntToScalar(fBitmaps[0].width() + 1);
        const SkScalar H = SkIntToScalar(fBitmaps[0].height() + 1);
        SkPaint paint;

        const SkScalar scale = 0.897917f;
        canvas->scale(SK_Scalar1, scale);

        for (int k = 0; k < 2; k++) {
            paint.setFilterLevel(k == 1 ? SkPaint::kLow_FilterLevel : SkPaint::kNone_FilterLevel);
            for (int j = 0; j < 2; j++) {
                paint.setDither(j == 1);
                for (int i = 0; i < fBitmapCount; i++) {
                    SkScalar x = (k * fBitmapCount + j) * W;
                    SkScalar y = i * H;
                    x = SkScalarRoundToScalar(x);
                    y = SkScalarRoundToScalar(y);
                    canvas->drawBitmap(fBitmaps[i], x, y, &paint);
                    if (i == 0) {
                        SkPaint p;
                        p.setAntiAlias(true);
                        p.setTextAlign(SkPaint::kCenter_Align);
                        p.setTextSize(SkIntToScalar(18));
                        SkString s("dither=");
                        s.appendS32(paint.isDither());
                        s.append(" filter=");
                        s.appendS32(paint.getFilterLevel() != SkPaint::kNone_FilterLevel);
                        canvas->drawText(s.c_str(), s.size(), x + W/2,
                                         y - p.getTextSize(), p);
                    }
                    if (k+j == 2) {
                        SkPaint p;
                        p.setAntiAlias(true);
                        p.setTextSize(SkIntToScalar(18));
                        SkString s;
                        s.append(" depth=");
                        s.appendS32(fBitmaps[i].colorType() == kRGB_565_SkColorType ? 16 : 32);
                        canvas->drawText(s.c_str(), s.size(), x + W + SkIntToScalar(4),
                                         y + H/2, p);
                    }
                }
            }
        }
    }
void SkScalerContext_DW::generateAdvance(SkGlyph* glyph) {
    //Delta is the difference between the right/left side bearing metric
    //and where the right/left side bearing ends up after hinting.
    //DirectWrite does not provide this information.
    glyph->fRsbDelta = 0;
    glyph->fLsbDelta = 0;

    glyph->fAdvanceX = 0;
    glyph->fAdvanceY = 0;

    uint16_t glyphId = glyph->getGlyphID();
    DWRITE_GLYPH_METRICS gm;

    if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
            DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
    {
        SkAutoExclusive l(DWriteFactoryMutex);
        HRVM(fTypeface->fDWriteFontFace->GetGdiCompatibleGlyphMetrics(
                 fTextSizeMeasure,
                 1.0f, // pixelsPerDip
                 &fGsA,
                 DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode,
                 &glyphId, 1,
                 &gm),
             "Could not get gdi compatible glyph metrics.");
    } else {
        SkAutoExclusive l(DWriteFactoryMutex);
        HRVM(fTypeface->fDWriteFontFace->GetDesignGlyphMetrics(&glyphId, 1, &gm),
             "Could not get design metrics.");
    }

    DWRITE_FONT_METRICS dwfm;
    {
        Shared l(DWriteFactoryMutex);
        fTypeface->fDWriteFontFace->GetMetrics(&dwfm);
    }
    SkScalar advanceX = SkScalarMulDiv(fTextSizeMeasure,
                                       SkIntToScalar(gm.advanceWidth),
                                       SkIntToScalar(dwfm.designUnitsPerEm));

    SkVector vecs[1] = { { advanceX, 0 } };
    if (DWRITE_MEASURING_MODE_GDI_CLASSIC == fMeasuringMode ||
            DWRITE_MEASURING_MODE_GDI_NATURAL == fMeasuringMode)
    {
        // DirectWrite produced 'compatible' metrics, but while close,
        // the end result is not always an integer as it would be with GDI.
        vecs[0].fX = SkScalarRoundToScalar(advanceX);
        fG_inv.mapVectors(vecs, SK_ARRAY_COUNT(vecs));
    } else {
        fSkXform.mapVectors(vecs, SK_ARRAY_COUNT(vecs));
    }

    glyph->fAdvanceX = SkScalarToFloat(vecs[0].fX);
    glyph->fAdvanceY = SkScalarToFloat(vecs[0].fY);
}
SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface,
                                       const SkScalerContextEffects& effects,
                                       const SkDescriptor* desc)
    : SkScalerContext(typeface, effects, desc)
    , fTypeface(SkRef(typeface))
    , fGlyphCount(-1) {

#if SK_HAS_DWRITE_2_H
    fTypeface->fFactory->QueryInterface<IDWriteFactory2>(&fFactory2);

    SkTScopedComPtr<IDWriteFontFace2> fontFace2;
    fTypeface->fDWriteFontFace->QueryInterface<IDWriteFontFace2>(&fontFace2);
    fIsColorFont = fFactory2.get() && fontFace2.get() && fontFace2->IsColorFont();
#endif

    // In general, all glyphs should use CLEARTYPE_NATURAL_SYMMETRIC
    // except when bi-level rendering is requested or there are embedded
    // bi-level bitmaps (and the embedded bitmap flag is set and no rotation).
    //
    // DirectWrite's IDWriteFontFace::GetRecommendedRenderingMode does not do
    // this. As a result, determine the actual size of the text and then see if
    // there are any embedded bi-level bitmaps of that size. If there are, then
    // force bitmaps by requesting bi-level rendering.
    //
    // FreeType allows for separate ppemX and ppemY, but DirectWrite assumes
    // square pixels and only uses ppemY. Therefore the transform must track any
    // non-uniform x-scale.
    //
    // Also, rotated glyphs should have the same absolute advance widths as
    // horizontal glyphs and the subpixel flag should not affect glyph shapes.

    SkVector scale;
    SkMatrix GsA;
    fRec.computeMatrices(SkScalerContextRec::kVertical_PreMatrixScale,
                         &scale, &fSkXform, &GsA, &fG_inv);

    fXform.m11 = SkScalarToFloat(fSkXform.getScaleX());
    fXform.m12 = SkScalarToFloat(fSkXform.getSkewY());
    fXform.m21 = SkScalarToFloat(fSkXform.getSkewX());
    fXform.m22 = SkScalarToFloat(fSkXform.getScaleY());
    fXform.dx = 0;
    fXform.dy = 0;

    fGsA.m11 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleX));
    fGsA.m12 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
    fGsA.m21 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewX));
    fGsA.m22 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleY));
    fGsA.dx = 0;
    fGsA.dy = 0;

    // realTextSize is the actual device size we want (as opposed to the size the user requested).
    // gdiTextSize is the size we request when GDI compatible.
    // If the scale is negative, this means the matrix will do the flip anyway.
    const SkScalar realTextSize = scale.fY;
    // Due to floating point math, the lower bits are suspect. Round carefully.
    SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f;
    if (gdiTextSize == 0) {
        gdiTextSize = SK_Scalar1;
    }

    bool bitmapRequested = SkToBool(fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag);
    bool treatLikeBitmap = false;
    bool axisAlignedBitmap = false;
    if (bitmapRequested) {
        // When embedded bitmaps are requested, treat the entire range like
        // a bitmap strike if the range is gridfit only and contains a bitmap.
        int bitmapPPEM = SkScalarTruncToInt(gdiTextSize);
        PPEMRange range = { bitmapPPEM, bitmapPPEM };
        expand_range_if_gridfit_only(typeface, bitmapPPEM, &range);
        treatLikeBitmap = has_bitmap_strike(typeface, range);

        axisAlignedBitmap = is_axis_aligned(fRec);
    }

    // If the user requested aliased, do so with aliased compatible metrics.
    if (SkMask::kBW_Format == fRec.fMaskFormat) {
        fTextSizeRender = gdiTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_ALIASED;
        fTextureType = DWRITE_TEXTURE_ALIASED_1x1;
        fTextSizeMeasure = gdiTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;

        // If we can use a bitmap, use gdi classic rendering and measurement.
        // This will not always provide a bitmap, but matches expected behavior.
    } else if (treatLikeBitmap && axisAlignedBitmap) {
        fTextSizeRender = gdiTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
        fTextSizeMeasure = gdiTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;

        // If rotated but the horizontal text could have used a bitmap,
        // render high quality rotated glyphs but measure using bitmap metrics.
    } else if (treatLikeBitmap) {
        fTextSizeRender = gdiTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
        fTextSizeMeasure = gdiTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;

        // Fonts that have hints but no gasp table get non-symmetric rendering.
        // Usually such fonts have low quality hints which were never tested
        // with anything but GDI ClearType classic. Such fonts often rely on
        // drop out control in the y direction in order to be legible.
    } else if (is_hinted_without_gasp(typeface)) {
        fTextSizeRender = gdiTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
        fTextSizeMeasure = realTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;

        // The normal case is to use natural symmetric rendering and linear metrics.
    } else {
        fTextSizeRender = realTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
        fTextSizeMeasure = realTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
    }

    if (this->isSubpixel()) {
        fTextSizeMeasure = realTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
    }
}
Beispiel #10
0
SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface,
                                       const SkDescriptor* desc)
        : SkScalerContext(typeface, desc)
        , fTypeface(SkRef(typeface))
        , fGlyphCount(-1) {

    // In general, all glyphs should use CLEARTYPE_NATURAL_SYMMETRIC
    // except when bi-level rendering is requested or there are embedded
    // bi-level bitmaps (and the embedded bitmap flag is set and no rotation).
    //
    // DirectWrite's IDWriteFontFace::GetRecommendedRenderingMode does not do
    // this. As a result, determine the actual size of the text and then see if
    // there are any embedded bi-level bitmaps of that size. If there are, then
    // force bitmaps by requesting bi-level rendering.
    //
    // FreeType allows for separate ppemX and ppemY, but DirectWrite assumes
    // square pixels and only uses ppemY. Therefore the transform must track any
    // non-uniform x-scale.
    //
    // Also, rotated glyphs should have the same absolute advance widths as
    // horizontal glyphs and the subpixel flag should not affect glyph shapes.

    // A is the total matrix.
    SkMatrix A;
    fRec.getSingleMatrix(&A);

    // h is where A maps the horizontal baseline.
    SkPoint h = SkPoint::Make(SK_Scalar1, 0);
    A.mapPoints(&h, 1);

    // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0).
    SkMatrix G;
    SkComputeGivensRotation(h, &G);

    // GA is the matrix A with rotation removed.
    SkMatrix GA(G);
    GA.preConcat(A);

    // realTextSize is the actual device size we want (as opposed to the size the user requested).
    // gdiTextSize is the size we request when GDI compatible.
    // If the scale is negative, this means the matrix will do the flip anyway.
    SkScalar realTextSize = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
    // Due to floating point math, the lower bits are suspect. Round carefully.
    SkScalar gdiTextSize = SkScalarRoundToScalar(realTextSize * 64.0f) / 64.0f;
    if (gdiTextSize == 0) {
        gdiTextSize = SK_Scalar1;
    }

    bool bitmapRequested = SkToBool(fRec.fFlags & SkScalerContext::kEmbeddedBitmapText_Flag);
    bool treatLikeBitmap = false;
    bool axisAlignedBitmap = false;
    if (bitmapRequested) {
        // When embedded bitmaps are requested, treat the entire range like
        // a bitmap strike if the range is gridfit only and contains a bitmap.
        int bitmapPPEM = SkScalarTruncToInt(gdiTextSize);
        PPEMRange range = { bitmapPPEM, bitmapPPEM };
        expand_range_if_gridfit_only(typeface, bitmapPPEM, &range);
        treatLikeBitmap = has_bitmap_strike(typeface, range);

        axisAlignedBitmap = is_axis_aligned(fRec);
    }

    // If the user requested aliased, do so with aliased compatible metrics.
    if (SkMask::kBW_Format == fRec.fMaskFormat) {
        fTextSizeRender = gdiTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_ALIASED;
        fTextureType = DWRITE_TEXTURE_ALIASED_1x1;
        fTextSizeMeasure = gdiTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;

    // If we can use a bitmap, use gdi classic rendering and measurement.
    // This will not always provide a bitmap, but matches expected behavior.
    } else if (treatLikeBitmap && axisAlignedBitmap) {
        fTextSizeRender = gdiTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
        fTextSizeMeasure = gdiTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;

    // If rotated but the horizontal text could have used a bitmap,
    // render high quality rotated glyphs but measure using bitmap metrics.
    } else if (treatLikeBitmap) {
        fTextSizeRender = gdiTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
        fTextSizeMeasure = gdiTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;

    // Fonts that have hints but no gasp table get non-symmetric rendering.
    // Usually such fonts have low quality hints which were never tested
    // with anything but GDI ClearType classic. Such fonts often rely on
    // drop out control in the y direction in order to be legible.
    } else if (is_hinted_without_gasp(typeface)) {
        fTextSizeRender = gdiTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
        fTextSizeMeasure = realTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;

    // The normal case is to use natural symmetric rendering and linear metrics.
    } else {
        fTextSizeRender = realTextSize;
        fRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
        fTextureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
        fTextSizeMeasure = realTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
    }

    if (this->isSubpixel()) {
        fTextSizeMeasure = realTextSize;
        fMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
    }

    // Remove the realTextSize, as that is the text height scale currently in A.
    SkScalar scale = SkScalarInvert(realTextSize);

    // fSkXform is the total matrix A without the text height scale.
    fSkXform = A;
    fSkXform.preScale(scale, scale); //remove the text height scale.

    fXform.m11 = SkScalarToFloat(fSkXform.getScaleX());
    fXform.m12 = SkScalarToFloat(fSkXform.getSkewY());
    fXform.m21 = SkScalarToFloat(fSkXform.getSkewX());
    fXform.m22 = SkScalarToFloat(fSkXform.getScaleY());
    fXform.dx = 0;
    fXform.dy = 0;

    // GsA is the non-rotational part of A without the text height scale.
    SkMatrix GsA(GA);
    GsA.preScale(scale, scale); //remove text height scale, G is rotational so reorders with scale.

    fGsA.m11 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleX));
    fGsA.m12 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewY)); // This should be ~0.
    fGsA.m21 = SkScalarToFloat(GsA.get(SkMatrix::kMSkewX));
    fGsA.m22 = SkScalarToFloat(GsA.get(SkMatrix::kMScaleY));
    fGsA.dx = 0;
    fGsA.dy = 0;

    // fG_inv is G inverse, which is fairly simple since G is 2x2 rotational.
    fG_inv.setAll(G.get(SkMatrix::kMScaleX), -G.get(SkMatrix::kMSkewX), G.get(SkMatrix::kMTransX),
                  -G.get(SkMatrix::kMSkewY), G.get(SkMatrix::kMScaleY), G.get(SkMatrix::kMTransY),
                  G.get(SkMatrix::kMPersp0), G.get(SkMatrix::kMPersp1), G.get(SkMatrix::kMPersp2));
}
Beispiel #11
0
static SkScalar apply_grid(SkScalar x) {
    const SkScalar grid = 2;
    return SkScalarRoundToScalar(x * grid) / grid;
}
Beispiel #12
0
SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) {
    fState.get()->fImage.lockPixels();

    SkMatrix finalMatrix = fState.get()->fCanvasTransform;
    finalMatrix.preConcat(fState.get()->fShaderTransform);
    SkRect surfaceBBox;
    surfaceBBox.set(fState.get()->fBBox);
    transformBBox(finalMatrix, &surfaceBBox);

    SkMatrix unflip;
    unflip.setTranslate(0, SkScalarRoundToScalar(surfaceBBox.height()));
    unflip.preScale(SK_Scalar1, -SK_Scalar1);
    SkISize size = SkISize::Make(SkScalarRound(surfaceBBox.width()),
                                 SkScalarRound(surfaceBBox.height()));
    SkPDFDevice pattern(size, size, unflip);
    SkCanvas canvas(&pattern);
    canvas.translate(-surfaceBBox.fLeft, -surfaceBBox.fTop);
    finalMatrix.preTranslate(surfaceBBox.fLeft, surfaceBBox.fTop);

    const SkBitmap* image = &fState.get()->fImage;
    SkScalar width = SkIntToScalar(image->width());
    SkScalar height = SkIntToScalar(image->height());
    SkShader::TileMode tileModes[2];
    tileModes[0] = fState.get()->fImageTileModes[0];
    tileModes[1] = fState.get()->fImageTileModes[1];

    canvas.drawBitmap(*image, 0, 0);
    SkRect patternBBox = SkRect::MakeXYWH(-surfaceBBox.fLeft, -surfaceBBox.fTop,
                                          width, 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);
        canvas.drawBitmapMatrix(*image, xMirror);
        patternBBox.fRight += width;
    }
    if (tileModes[1] == SkShader::kMirror_TileMode) {
        SkMatrix yMirror;
        yMirror.setScale(SK_Scalar1, -SK_Scalar1);
        yMirror.postTranslate(0, 2 * height);
        canvas.drawBitmapMatrix(*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);
        canvas.drawBitmapMatrix(*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(surfaceBBox.fLeft, surfaceBBox.fTop, 0, 0);
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(0, 0));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(width, surfaceBBox.fTop, surfaceBBox.fRight, 0);
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(image->width() - 1, 0));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(width, height, surfaceBBox.fRight,
                                surfaceBBox.fBottom);
        if (!rect.isEmpty()) {
            paint.setColor(image->getColor(image->width() - 1,
                                           image->height() - 1));
            canvas.drawRect(rect, paint);
        }

        rect = SkRect::MakeLTRB(surfaceBBox.fLeft, height, 0,
                                surfaceBBox.fBottom);
        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 (surfaceBBox.fLeft < 0) {
            SkBitmap left;
            SkAssertResult(image->extractSubset(&left, subset));

            SkMatrix leftMatrix;
            leftMatrix.setScale(-surfaceBBox.fLeft, 1);
            leftMatrix.postTranslate(surfaceBBox.fLeft, 0);
            canvas.drawBitmapMatrix(left, leftMatrix);

            if (tileModes[1] == SkShader::kMirror_TileMode) {
                leftMatrix.postScale(SK_Scalar1, -SK_Scalar1);
                leftMatrix.postTranslate(0, 2 * height);
                canvas.drawBitmapMatrix(left, leftMatrix);
            }
            patternBBox.fLeft = 0;
        }

        if (surfaceBBox.fRight > width) {
            SkBitmap right;
            subset.offset(image->width() - 1, 0);
            SkAssertResult(image->extractSubset(&right, subset));

            SkMatrix rightMatrix;
            rightMatrix.setScale(surfaceBBox.fRight - width, 1);
            rightMatrix.postTranslate(width, 0);
            canvas.drawBitmapMatrix(right, rightMatrix);

            if (tileModes[1] == SkShader::kMirror_TileMode) {
                rightMatrix.postScale(SK_Scalar1, -SK_Scalar1);
                rightMatrix.postTranslate(0, 2 * height);
                canvas.drawBitmapMatrix(right, rightMatrix);
            }
            patternBBox.fRight = surfaceBBox.width();
        }
    }

    if (tileModes[1] == SkShader::kClamp_TileMode) {
        SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1);
        if (surfaceBBox.fTop < 0) {
            SkBitmap top;
            SkAssertResult(image->extractSubset(&top, subset));

            SkMatrix topMatrix;
            topMatrix.setScale(SK_Scalar1, -surfaceBBox.fTop);
            topMatrix.postTranslate(0, surfaceBBox.fTop);
            canvas.drawBitmapMatrix(top, topMatrix);

            if (tileModes[0] == SkShader::kMirror_TileMode) {
                topMatrix.postScale(-1, 1);
                topMatrix.postTranslate(2 * width, 0);
                canvas.drawBitmapMatrix(top, topMatrix);
            }
            patternBBox.fTop = 0;
        }

        if (surfaceBBox.fBottom > height) {
            SkBitmap bottom;
            subset.offset(0, image->height() - 1);
            SkAssertResult(image->extractSubset(&bottom, subset));

            SkMatrix bottomMatrix;
            bottomMatrix.setScale(SK_Scalar1, surfaceBBox.fBottom - height);
            bottomMatrix.postTranslate(0, height);
            canvas.drawBitmapMatrix(bottom, bottomMatrix);

            if (tileModes[0] == SkShader::kMirror_TileMode) {
                bottomMatrix.postScale(-1, 1);
                bottomMatrix.postTranslate(2 * width, 0);
                canvas.drawBitmapMatrix(bottom, bottomMatrix);
            }
            patternBBox.fBottom = surfaceBBox.height();
        }
    }

    SkRefPtr<SkPDFArray> patternBBoxArray = new SkPDFArray;
    patternBBoxArray->unref();  // SkRefPtr and new both took a reference.
    patternBBoxArray->reserve(4);
    patternBBoxArray->appendScalar(patternBBox.fLeft);
    patternBBoxArray->appendScalar(patternBBox.fTop);
    patternBBoxArray->appendScalar(patternBBox.fRight);
    patternBBoxArray->appendScalar(patternBBox.fBottom);

    // Put the canvas into the pattern stream (fContent).
    SkRefPtr<SkStream> content = pattern.content();
    content->unref();  // SkRefPtr and content() both took a reference.
    pattern.getResources(&fResources, false);

    setData(content.get());
    insertName("Type", "Pattern");
    insertInt("PatternType", 1);
    insertInt("PaintType", 1);
    insertInt("TilingType", 1);
    insert("BBox", patternBBoxArray.get());
    insertScalar("XStep", patternBBox.width());
    insertScalar("YStep", patternBBox.height());
    insert("Resources", pattern.getResourceDict());
    insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref();

    fState.get()->fImage.unlockPixels();
}
void SkStackViewLayout::onLayoutChildren(SkView* parent)
{
    static AlignProc gAlignProcs[] = {
        left_align_proc,
        center_align_proc,
        right_align_proc,
        fill_align_proc
    };

    SkScalar            startM, endM, crossStartM, crossLimit;
    GetSizeProc            mainGetSizeP, crossGetSizeP;
    SetLocProc            mainLocP, crossLocP;
    SetSizeProc            mainSetSizeP, crossSetSizeP;
    SkView::Flag_Mask    flexMask;

    if (fOrient == kHorizontal_Orient)
    {
        startM        = fMargin.fLeft;
        endM        = fMargin.fRight;
        crossStartM    = fMargin.fTop;
        crossLimit    = -fMargin.fTop - fMargin.fBottom;

        mainGetSizeP    = &SkView::width;
        crossGetSizeP    = &SkView::height;
        mainLocP    = &SkView::setLocX;
        crossLocP    = &SkView::setLocY;

        mainSetSizeP  = &SkView::setWidth;
        crossSetSizeP = &SkView::setHeight;

        flexMask    = SkView::kFlexH_Mask;
    }
    else
    {
        startM        = fMargin.fTop;
        endM        = fMargin.fBottom;
        crossStartM    = fMargin.fLeft;
        crossLimit    = -fMargin.fLeft - fMargin.fRight;

        mainGetSizeP    = &SkView::height;
        crossGetSizeP    = &SkView::width;
        mainLocP    = &SkView::setLocY;
        crossLocP    = &SkView::setLocX;

        mainSetSizeP  = &SkView::setHeight;
        crossSetSizeP = &SkView::setWidth;

        flexMask    = SkView::kFlexV_Mask;
    }
    crossLimit += (parent->*crossGetSizeP)();
    if (fAlign != kStretch_Align)
        crossSetSizeP = nullptr;

    int            childCount, flexCount;
    SkScalar    childLimit = compute_children_limit(parent, mainGetSizeP, &childCount, flexMask, &flexCount);

    if (childCount == 0)
        return;

    childLimit += (childCount - 1) * fSpacer;

    SkScalar        parentLimit = (parent->*mainGetSizeP)() - startM - endM;
    SkScalar        pos = startM + gAlignProcs[fPack](childLimit, parentLimit);
    SkScalar        flexAmount = 0;
    SkView::B2FIter    iter(parent);
    SkView*            child;

    if (flexCount > 0 && parentLimit > childLimit)
        flexAmount = (parentLimit - childLimit) / flexCount;

    while ((child = iter.next()) != nullptr)
    {
        if (fRound)
            pos = SkScalarRoundToScalar(pos);
        (child->*mainLocP)(pos);
        SkScalar crossLoc = crossStartM + gAlignProcs[fAlign]((child->*crossGetSizeP)(), crossLimit);
        if (fRound)
            crossLoc = SkScalarRoundToScalar(crossLoc);
        (child->*crossLocP)(crossLoc);

        if (crossSetSizeP)
            (child->*crossSetSizeP)(crossLimit);
        if (child->getFlags() & flexMask)
            (child->*mainSetSizeP)(flexAmount);
        pos += (child->*mainGetSizeP)() + fSpacer;
    }
}
void SkScalerContextRec::computeMatrices(PreMatrixScale preMatrixScale, SkVector* s, SkMatrix* sA,
                                         SkMatrix* GsA, SkMatrix* G_inv, SkMatrix* A_out)
{
    // A is the 'total' matrix.
    SkMatrix A;
    this->getSingleMatrix(&A);

    // The caller may find the 'total' matrix useful when dealing directly with EM sizes.
    if (A_out) {
        *A_out = A;
    }

    // If the 'total' matrix is singular, set the 'scale' to something finite and zero the matrices.
    // All underlying ports have issues with zero text size, so use the matricies to zero.

    // Map the vectors [0,1], [1,0], [1,1] and [1,-1] (the EM) through the 'total' matrix.
    // If the length of one of these vectors is less than 1/256 then an EM filling square will
    // never affect any pixels.
    SkVector diag[4] = { { A.getScaleX()               ,                 A.getSkewY() },
                         {                 A.getSkewX(), A.getScaleY()                },
                         { A.getScaleX() + A.getSkewX(), A.getScaleY() + A.getSkewY() },
                         { A.getScaleX() - A.getSkewX(), A.getScaleY() - A.getSkewY() }, };
    if (diag[0].lengthSqd() <= SK_ScalarNearlyZero * SK_ScalarNearlyZero ||
        diag[1].lengthSqd() <= SK_ScalarNearlyZero * SK_ScalarNearlyZero ||
        diag[2].lengthSqd() <= SK_ScalarNearlyZero * SK_ScalarNearlyZero ||
        diag[3].lengthSqd() <= SK_ScalarNearlyZero * SK_ScalarNearlyZero)
    {
        s->fX = SK_Scalar1;
        s->fY = SK_Scalar1;
        sA->setScale(0, 0);
        if (GsA) {
            GsA->setScale(0, 0);
        }
        if (G_inv) {
            G_inv->reset();
        }
        return;
    }

    // GA is the matrix A with rotation removed.
    SkMatrix GA;
    bool skewedOrFlipped = A.getSkewX() || A.getSkewY() || A.getScaleX() < 0 || A.getScaleY() < 0;
    if (skewedOrFlipped) {
        // h is where A maps the horizontal baseline.
        SkPoint h = SkPoint::Make(SK_Scalar1, 0);
        A.mapPoints(&h, 1);

        // G is the Givens Matrix for A (rotational matrix where GA[0][1] == 0).
        SkMatrix G;
        SkComputeGivensRotation(h, &G);

        GA = G;
        GA.preConcat(A);

        // The 'remainingRotation' is G inverse, which is fairly simple since G is 2x2 rotational.
        if (G_inv) {
            G_inv->setAll(
                G.get(SkMatrix::kMScaleX), -G.get(SkMatrix::kMSkewX), G.get(SkMatrix::kMTransX),
                -G.get(SkMatrix::kMSkewY), G.get(SkMatrix::kMScaleY), G.get(SkMatrix::kMTransY),
                G.get(SkMatrix::kMPersp0), G.get(SkMatrix::kMPersp1), G.get(SkMatrix::kMPersp2));
        }
    } else {
        GA = A;
        if (G_inv) {
            G_inv->reset();
        }
    }

    // At this point, given GA, create s.
    switch (preMatrixScale) {
        case kFull_PreMatrixScale:
            s->fX = SkScalarAbs(GA.get(SkMatrix::kMScaleX));
            s->fY = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
            break;
        case kVertical_PreMatrixScale: {
            SkScalar yScale = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
            s->fX = yScale;
            s->fY = yScale;
            break;
        }
        case kVerticalInteger_PreMatrixScale: {
            SkScalar realYScale = SkScalarAbs(GA.get(SkMatrix::kMScaleY));
            SkScalar intYScale = SkScalarRoundToScalar(realYScale);
            if (intYScale == 0) {
                intYScale = SK_Scalar1;
            }
            s->fX = intYScale;
            s->fY = intYScale;
            break;
        }
    }

    // The 'remaining' matrix sA is the total matrix A without the scale.
    if (!skewedOrFlipped && (
            (kFull_PreMatrixScale == preMatrixScale) ||
            (kVertical_PreMatrixScale == preMatrixScale && A.getScaleX() == A.getScaleY())))
    {
        // If GA == A and kFull_PreMatrixScale, sA is identity.
        // If GA == A and kVertical_PreMatrixScale and A.scaleX == A.scaleY, sA is identity.
        sA->reset();
    } else if (!skewedOrFlipped && kVertical_PreMatrixScale == preMatrixScale) {
        // If GA == A and kVertical_PreMatrixScale, sA.scaleY is SK_Scalar1.
        sA->reset();
        sA->setScaleX(A.getScaleX() / s->fY);
    } else {
        // TODO: like kVertical_PreMatrixScale, kVerticalInteger_PreMatrixScale with int scales.
        *sA = A;
        sA->preScale(SkScalarInvert(s->fX), SkScalarInvert(s->fY));
    }

    // The 'remainingWithoutRotation' matrix GsA is the non-rotational part of A without the scale.
    if (GsA) {
        *GsA = GA;
         // G is rotational so reorders with the scale.
        GsA->preScale(SkScalarInvert(s->fX), SkScalarInvert(s->fY));
    }
}
Beispiel #15
0
bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
    SkASSERT(fOrigBitmap.width() && fOrigBitmap.height());

    fBitmap = NULL;
    fInvMatrix = inv;
    fFilterLevel = paint.getFilterLevel();

    // possiblyScaleImage will look to see if it can rescale the image as a
    // preprocess; either by scaling up to the target size, or by selecting
    // a nearby mipmap level.  If it does, it will adjust the working
    // matrix as well as the working bitmap.  It may also adjust the filter
    // quality to avoid re-filtering an already perfectly scaled image.
    if (!this->possiblyScaleImage()) {
        if (!this->lockBaseBitmap()) {
            return false;
        }
    }
    // The above logic should have always assigned fBitmap, but in case it
    // didn't, we check for that now...
    // TODO(dominikg): Ask humper@ if we can just use an SkASSERT(fBitmap)?
    if (NULL == fBitmap) {
        return false;
    }

    // If we are "still" kMedium_FilterLevel, then the request was not fulfilled by possiblyScale,
    // so we downgrade to kLow (so the rest of the sniffing code can assume that)
    if (SkPaint::kMedium_FilterLevel == fFilterLevel) {
        fFilterLevel = SkPaint::kLow_FilterLevel;
    }

    bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
    bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
                      SkShader::kClamp_TileMode == fTileModeY;

    if (!(fAdjustedMatrix || clampClamp || trivialMatrix)) {
        fInvMatrix.postIDiv(fOrigBitmap.width(), fOrigBitmap.height());
    }

    // Now that all possible changes to the matrix have taken place, check
    // to see if we're really close to a no-scale matrix.  If so, explicitly
    // set it to be so.  Subsequent code may inspect this matrix to choose
    // a faster path in this case.

    // This code will only execute if the matrix has some scale component;
    // if it's already pure translate then we won't do this inversion.

    if (matrix_only_scale_translate(fInvMatrix)) {
        SkMatrix forward;
        if (fInvMatrix.invert(&forward)) {
            if (clampClamp ? just_trans_clamp(forward, *fBitmap)
                            : just_trans_general(forward)) {
                SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
                SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
                fInvMatrix.setTranslate(tx, ty);
            }
        }
    }

    fInvProc        = fInvMatrix.getMapXYProc();
    fInvType        = fInvMatrix.getType();
    fInvSx          = SkScalarToFixed(fInvMatrix.getScaleX());
    fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX());
    fInvKy          = SkScalarToFixed(fInvMatrix.getSkewY());
    fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY());

    fAlphaScale = SkAlpha255To256(paint.getAlpha());

    fShaderProc32 = NULL;
    fShaderProc16 = NULL;
    fSampleProc32 = NULL;
    fSampleProc16 = NULL;

    // recompute the triviality of the matrix here because we may have
    // changed it!

    trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;

    if (SkPaint::kHigh_FilterLevel == fFilterLevel) {
        // If this is still set, that means we wanted HQ sampling
        // but couldn't do it as a preprocess.  Let's try to install
        // the scanline version of the HQ sampler.  If that process fails,
        // downgrade to bilerp.

        // NOTE: Might need to be careful here in the future when we want
        // to have the platform proc have a shot at this; it's possible that
        // the chooseBitmapFilterProc will fail to install a shader but a
        // platform-specific one might succeed, so it might be premature here
        // to fall back to bilerp.  This needs thought.

        if (!this->setBitmapFilterProcs()) {
            fFilterLevel = SkPaint::kLow_FilterLevel;
        }
    }

    if (SkPaint::kLow_FilterLevel == fFilterLevel) {
        // Only try bilerp if the matrix is "interesting" and
        // the image has a suitable size.

        if (fInvType <= SkMatrix::kTranslate_Mask ||
                !valid_for_filtering(fBitmap->width() | fBitmap->height())) {
            fFilterLevel = SkPaint::kNone_FilterLevel;
        }
    }

    // At this point, we know exactly what kind of sampling the per-scanline
    // shader will perform.

    fMatrixProc = this->chooseMatrixProc(trivialMatrix);
    // TODO(dominikg): SkASSERT(fMatrixProc) instead? chooseMatrixProc never returns NULL.
    if (NULL == fMatrixProc) {
        return false;
    }

    ///////////////////////////////////////////////////////////////////////

    const SkAlphaType at = fBitmap->alphaType();

    // No need to do this if we're doing HQ sampling; if filter quality is
    // still set to HQ by the time we get here, then we must have installed
    // the shader procs above and can skip all this.

    if (fFilterLevel < SkPaint::kHigh_FilterLevel) {

        int index = 0;
        if (fAlphaScale < 256) {  // note: this distinction is not used for D16
            index |= 1;
        }
        if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
            index |= 2;
        }
        if (fFilterLevel > SkPaint::kNone_FilterLevel) {
            index |= 4;
        }
        // bits 3,4,5 encoding the source bitmap format
        switch (fBitmap->colorType()) {
            case kN32_SkColorType:
                if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
                    return false;
                }
                index |= 0;
                break;
            case kRGB_565_SkColorType:
                index |= 8;
                break;
            case kIndex_8_SkColorType:
                if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
                    return false;
                }
                index |= 16;
                break;
            case kARGB_4444_SkColorType:
                if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) {
                    return false;
                }
                index |= 24;
                break;
            case kAlpha_8_SkColorType:
                index |= 32;
                fPaintPMColor = SkPreMultiplyColor(paint.getColor());
                break;
            default:
                // TODO(dominikg): Should we ever get here? SkASSERT(false) instead?
                return false;
        }

    #if !SK_ARM_NEON_IS_ALWAYS
        static const SampleProc32 gSkBitmapProcStateSample32[] = {
            S32_opaque_D32_nofilter_DXDY,
            S32_alpha_D32_nofilter_DXDY,
            S32_opaque_D32_nofilter_DX,
            S32_alpha_D32_nofilter_DX,
            S32_opaque_D32_filter_DXDY,
            S32_alpha_D32_filter_DXDY,
            S32_opaque_D32_filter_DX,
            S32_alpha_D32_filter_DX,

            S16_opaque_D32_nofilter_DXDY,
            S16_alpha_D32_nofilter_DXDY,
            S16_opaque_D32_nofilter_DX,
            S16_alpha_D32_nofilter_DX,
            S16_opaque_D32_filter_DXDY,
            S16_alpha_D32_filter_DXDY,
            S16_opaque_D32_filter_DX,
            S16_alpha_D32_filter_DX,

            SI8_opaque_D32_nofilter_DXDY,
            SI8_alpha_D32_nofilter_DXDY,
            SI8_opaque_D32_nofilter_DX,
            SI8_alpha_D32_nofilter_DX,
            SI8_opaque_D32_filter_DXDY,
            SI8_alpha_D32_filter_DXDY,
            SI8_opaque_D32_filter_DX,
            SI8_alpha_D32_filter_DX,

            S4444_opaque_D32_nofilter_DXDY,
            S4444_alpha_D32_nofilter_DXDY,
            S4444_opaque_D32_nofilter_DX,
            S4444_alpha_D32_nofilter_DX,
            S4444_opaque_D32_filter_DXDY,
            S4444_alpha_D32_filter_DXDY,
            S4444_opaque_D32_filter_DX,
            S4444_alpha_D32_filter_DX,

            // A8 treats alpha/opaque the same (equally efficient)
            SA8_alpha_D32_nofilter_DXDY,
            SA8_alpha_D32_nofilter_DXDY,
            SA8_alpha_D32_nofilter_DX,
            SA8_alpha_D32_nofilter_DX,
            SA8_alpha_D32_filter_DXDY,
            SA8_alpha_D32_filter_DXDY,
            SA8_alpha_D32_filter_DX,
            SA8_alpha_D32_filter_DX
        };

        static const SampleProc16 gSkBitmapProcStateSample16[] = {
            S32_D16_nofilter_DXDY,
            S32_D16_nofilter_DX,
            S32_D16_filter_DXDY,
            S32_D16_filter_DX,

            S16_D16_nofilter_DXDY,
            S16_D16_nofilter_DX,
            S16_D16_filter_DXDY,
            S16_D16_filter_DX,

            SI8_D16_nofilter_DXDY,
            SI8_D16_nofilter_DX,
            SI8_D16_filter_DXDY,
            SI8_D16_filter_DX,

            // Don't support 4444 -> 565
            NULL, NULL, NULL, NULL,
            // Don't support A8 -> 565
            NULL, NULL, NULL, NULL
        };
    #endif

        fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
        index >>= 1;    // shift away any opaque/alpha distinction
        fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index];

        // our special-case shaderprocs
        if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) {
            if (clampClamp) {
                fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc);
            } else if (SkShader::kRepeat_TileMode == fTileModeX &&
                       SkShader::kRepeat_TileMode == fTileModeY) {
                fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc);
            }
        } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clampClamp) {
            fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc);
        }

        if (NULL == fShaderProc32) {
            fShaderProc32 = this->chooseShaderProc32();
        }
    }
Beispiel #16
0
void SkDisplayMath::executeFunction(SkDisplayable* target, int index,
        SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
        SkScriptValue* scriptValue) {
    if (scriptValue == NULL)
        return;
    SkASSERT(target == this);
    SkScriptValue* array = parameters.begin();
    SkScriptValue* end = parameters.end();
    SkScalar input = parameters[0].fOperand.fScalar;
    SkScalar scalarResult;
    switch (index) {
        case SK_FUNCTION(abs):
            scalarResult = SkScalarAbs(input);
            break;
        case SK_FUNCTION(acos):
            scalarResult = SkScalarACos(input);
            break;
        case SK_FUNCTION(asin):
            scalarResult = SkScalarASin(input);
            break;
        case SK_FUNCTION(atan):
            scalarResult = SkScalarATan2(input, SK_Scalar1);
            break;
        case SK_FUNCTION(atan2):
            scalarResult = SkScalarATan2(input, parameters[1].fOperand.fScalar);
            break;
        case SK_FUNCTION(ceil):
            scalarResult = SkScalarCeilToScalar(input);
            break;
        case SK_FUNCTION(cos):
            scalarResult = SkScalarCos(input);
            break;
        case SK_FUNCTION(exp):
            scalarResult = SkScalarExp(input);
            break;
        case SK_FUNCTION(floor):
            scalarResult = SkScalarFloorToScalar(input);
            break;
        case SK_FUNCTION(log):
            scalarResult = SkScalarLog(input);
            break;
        case SK_FUNCTION(max):
            scalarResult = -SK_ScalarMax;
            while (array < end) {
                scalarResult = SkMaxScalar(scalarResult, array->fOperand.fScalar);
                array++;
            }
            break;
        case SK_FUNCTION(min):
            scalarResult = SK_ScalarMax;
            while (array < end) {
                scalarResult = SkMinScalar(scalarResult, array->fOperand.fScalar);
                array++;
            }
            break;
        case SK_FUNCTION(pow):
            // not the greatest -- but use x^y = e^(y * ln(x))
            scalarResult = SkScalarLog(input);
            scalarResult = SkScalarMul(parameters[1].fOperand.fScalar, scalarResult);
            scalarResult = SkScalarExp(scalarResult);
            break;
        case SK_FUNCTION(random):
            scalarResult = fRandom.nextUScalar1();
            break;
        case SK_FUNCTION(round):
            scalarResult = SkScalarRoundToScalar(input);
            break;
        case SK_FUNCTION(sin):
            scalarResult = SkScalarSin(input);
            break;
        case SK_FUNCTION(sqrt): {
            SkASSERT(parameters.count() == 1);
            SkASSERT(type == SkType_Float);
            scalarResult = SkScalarSqrt(input);
            } break;
        case SK_FUNCTION(tan):
            scalarResult = SkScalarTan(input);
            break;
        default:
            SkASSERT(0);
            scalarResult = SK_ScalarNaN;
    }
    scriptValue->fOperand.fScalar = scalarResult;
    scriptValue->fType = SkType_Float;
}
Beispiel #17
0
/*
 *  High quality is implemented by performing up-right scale-only filtering and then
 *  using bilerp for any remaining transformations.
 */
bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmapProvider& provider) {
    if (fQuality != kHigh_SkFilterQuality) {
        return false;
    }

    // Our default return state is to downgrade the request to Medium, w/ or w/o setting fBitmap
    // to a valid bitmap. If we succeed, we will set this to Low instead.
    fQuality = kMedium_SkFilterQuality;

    if (kN32_SkColorType != provider.info().colorType() || !cache_size_okay(provider, fInvMatrix) ||
            fInvMatrix.hasPerspective())
    {
        return false; // can't handle the reqeust
    }

    SkScalar invScaleX = fInvMatrix.getScaleX();
    SkScalar invScaleY = fInvMatrix.getScaleY();
    if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) {
        SkSize scale;
        if (!fInvMatrix.decomposeScale(&scale)) {
            return false;
        }
        invScaleX = scale.width();
        invScaleY = scale.height();
    }
    if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) {
        return false; // no need for HQ
    }

#ifndef SK_SUPPORT_LEGACY_HQ_DOWNSAMPLING
    if (invScaleX > 1 || invScaleY > 1) {
        return false; // only use HQ when upsampling
    }
#endif

    const int dstW = SkScalarRoundToScalar(provider.width() / invScaleX);
    const int dstH = SkScalarRoundToScalar(provider.height() / invScaleY);
    const SkBitmapCacheDesc desc = provider.makeCacheDesc(dstW, dstH);

    if (!SkBitmapCache::FindWH(desc, &fResultBitmap)) {
        SkBitmap orig;
        if (!provider.asBitmap(&orig)) {
            return false;
        }
        SkAutoPixmapUnlock src;
        if (!orig.requestLock(&src)) {
            return false;
        }
        if (!SkBitmapScaler::Resize(&fResultBitmap, src.pixmap(), kHQ_RESIZE_METHOD,
                                    dstW, dstH, SkResourceCache::GetAllocator())) {
            return false; // we failed to create fScaledBitmap
        }

        SkASSERT(fResultBitmap.getPixels());
        fResultBitmap.setImmutable();
        if (!provider.isVolatile()) {
            if (SkBitmapCache::AddWH(desc, fResultBitmap)) {
                provider.notifyAddedToCache();
            }
        }
    }

    SkASSERT(fResultBitmap.getPixels());

    fInvMatrix.postScale(SkIntToScalar(dstW) / provider.width(),
                         SkIntToScalar(dstH) / provider.height());
    fQuality = kLow_SkFilterQuality;
    return true;
}
Beispiel #18
0
SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) {
    fState.get()->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 = fState.get()->fCanvasTransform;
    finalMatrix.preConcat(fState.get()->fShaderTransform);
    SkRect deviceBounds;
    deviceBounds.set(fState.get()->fBBox);
    if (!inverseTransformBBox(finalMatrix, &deviceBounds)) {
        return;
    }

    const SkBitmap* image = &fState.get()->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] = fState.get()->fImageTileModes[0];
    tileModes[1] = fState.get()->fImageTileModes[1];
    if (tileModes[0] != SkShader::kClamp_TileMode ||
            tileModes[1] != SkShader::kClamp_TileMode) {
        deviceBounds.join(bitmapBounds);
    }

    SkMatrix unflip;
    unflip.setTranslate(0, SkScalarRoundToScalar(deviceBounds.height()));
    unflip.preScale(SK_Scalar1, -SK_Scalar1);
    SkISize size = SkISize::Make(SkScalarRound(deviceBounds.width()),
                                 SkScalarRound(deviceBounds.height()));
    SkPDFDevice pattern(size, size, unflip);
    SkCanvas canvas(&pattern);

    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);
        canvas.drawBitmapMatrix(*image, xMirror);
        patternBBox.fRight += width;
    }
    if (tileModes[1] == SkShader::kMirror_TileMode) {
        SkMatrix yMirror;
        yMirror.setScale(SK_Scalar1, -SK_Scalar1);
        yMirror.postTranslate(0, 2 * height);
        canvas.drawBitmapMatrix(*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);
        canvas.drawBitmapMatrix(*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);
            canvas.drawBitmapMatrix(left, leftMatrix);

            if (tileModes[1] == SkShader::kMirror_TileMode) {
                leftMatrix.postScale(SK_Scalar1, -SK_Scalar1);
                leftMatrix.postTranslate(0, 2 * height);
                canvas.drawBitmapMatrix(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);
            canvas.drawBitmapMatrix(right, rightMatrix);

            if (tileModes[1] == SkShader::kMirror_TileMode) {
                rightMatrix.postScale(SK_Scalar1, -SK_Scalar1);
                rightMatrix.postTranslate(0, 2 * height);
                canvas.drawBitmapMatrix(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());
            canvas.drawBitmapMatrix(top, topMatrix);

            if (tileModes[0] == SkShader::kMirror_TileMode) {
                topMatrix.postScale(-1, 1);
                topMatrix.postTranslate(2 * width, 0);
                canvas.drawBitmapMatrix(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);
            canvas.drawBitmapMatrix(bottom, bottomMatrix);

            if (tileModes[0] == SkShader::kMirror_TileMode) {
                bottomMatrix.postScale(-1, 1);
                bottomMatrix.postTranslate(2 * width, 0);
                canvas.drawBitmapMatrix(bottom, bottomMatrix);
            }
            patternBBox.fBottom = deviceBounds.height();
        }
    }

    // Put the canvas into the pattern stream (fContent).
    SkAutoTUnref<SkStream> content(pattern.content());
    setData(content.get());
    SkPDFResourceDict* resourceDict = pattern.getResourceDict();
    resourceDict->getReferencedResources(fResources, &fResources, false);

    populate_tiling_pattern_dict(this, patternBBox,
                                 pattern.getResourceDict(), finalMatrix);

    fState.get()->fImage.unlockPixels();
}
Beispiel #19
0
void SkClipStack::Element::updateBoundAndGenID(const Element* prior) {
    // We set this first here but we may overwrite it later if we determine that the clip is
    // either wide-open or empty.
    fGenID = GetNextGenID();

    // First, optimistically update the current Element's bound information
    // with the current clip's bound
    fIsIntersectionOfRects = false;
    switch (fType) {
        case kRect_Type:
            fFiniteBound = this->getRect();
            fFiniteBoundType = kNormal_BoundsType;

            if (SkRegion::kReplace_Op == fOp ||
                (SkRegion::kIntersect_Op == fOp && nullptr == prior) ||
                (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects &&
                    prior->rectRectIntersectAllowed(this->getRect(), fDoAA))) {
                fIsIntersectionOfRects = true;
            }
            break;
        case kRRect_Type:
            fFiniteBound = fRRect.getBounds();
            fFiniteBoundType = kNormal_BoundsType;
            break;
        case kPath_Type:
            fFiniteBound = fPath.get()->getBounds();

            if (fPath.get()->isInverseFillType()) {
                fFiniteBoundType = kInsideOut_BoundsType;
            } else {
                fFiniteBoundType = kNormal_BoundsType;
            }
            break;
        case kEmpty_Type:
            SkDEBUGFAIL("We shouldn't get here with an empty element.");
            break;
    }

    if (!fDoAA) {
        fFiniteBound.set(SkScalarFloorToScalar(fFiniteBound.fLeft+0.45f),
                         SkScalarRoundToScalar(fFiniteBound.fTop),
                         SkScalarRoundToScalar(fFiniteBound.fRight),
                         SkScalarRoundToScalar(fFiniteBound.fBottom));
    }

    // Now determine the previous Element's bound information taking into
    // account that there may be no previous clip
    SkRect prevFinite;
    SkClipStack::BoundsType prevType;

    if (nullptr == prior) {
        // no prior clip means the entire plane is writable
        prevFinite.setEmpty();   // there are no pixels that cannot be drawn to
        prevType = kInsideOut_BoundsType;
    } else {
        prevFinite = prior->fFiniteBound;
        prevType = prior->fFiniteBoundType;
    }

    FillCombo combination = kPrev_Cur_FillCombo;
    if (kInsideOut_BoundsType == fFiniteBoundType) {
        combination = (FillCombo) (combination | 0x01);
    }
    if (kInsideOut_BoundsType == prevType) {
        combination = (FillCombo) (combination | 0x02);
    }

    SkASSERT(kInvPrev_InvCur_FillCombo == combination ||
                kInvPrev_Cur_FillCombo == combination ||
                kPrev_InvCur_FillCombo == combination ||
                kPrev_Cur_FillCombo == combination);

    // Now integrate with clip with the prior clips
    switch (fOp) {
        case SkRegion::kDifference_Op:
            this->combineBoundsDiff(combination, prevFinite);
            break;
        case SkRegion::kXOR_Op:
            this->combineBoundsXOR(combination, prevFinite);
            break;
        case SkRegion::kUnion_Op:
            this->combineBoundsUnion(combination, prevFinite);
            break;
        case SkRegion::kIntersect_Op:
            this->combineBoundsIntersection(combination, prevFinite);
            break;
        case SkRegion::kReverseDifference_Op:
            this->combineBoundsRevDiff(combination, prevFinite);
            break;
        case SkRegion::kReplace_Op:
            // Replace just ignores everything prior
            // The current clip's bound information is already filled in
            // so nothing to do
            break;
        default:
            SkDebugf("SkRegion::Op error\n");
            SkASSERT(0);
            break;
    }
}
Beispiel #20
0
/*
 *  Analyze filter-quality and matrix, and decide how to implement that.
 *
 *  In general, we cascade down the request level [ High ... None ]
 *  - for a given level, if we can fulfill it, fine, else
 *    - else we downgrade to the next lower level and try again.
 *  We can always fulfill requests for Low and None
 *  - sometimes we will "ignore" Low and give None, but this is likely a legacy perf hack
 *    and may be removed.
 */
bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) {
    fPixmap.reset();
    fInvMatrix = inv;
    fFilterLevel = paint.getFilterQuality();

    SkDefaultBitmapController controller;
    fBMState = controller.requestBitmap(fOrigBitmap, inv, paint.getFilterQuality(),
                                        fBMStateStorage.get(), fBMStateStorage.size());
    // Note : we allow the controller to return an empty (zero-dimension) result. Should we?
    if (NULL == fBMState || fBMState->pixmap().info().isEmpty()) {
        return false;
    }
    fPixmap = fBMState->pixmap();
    fInvMatrix = fBMState->invMatrix();
    fFilterLevel = fBMState->quality();
    SkASSERT(fPixmap.addr());
    
    bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;
    bool clampClamp = SkShader::kClamp_TileMode == fTileModeX &&
                      SkShader::kClamp_TileMode == fTileModeY;

    // Most of the scanline procs deal with "unit" texture coordinates, as this
    // makes it easy to perform tiling modes (repeat = (x & 0xFFFF)). To generate
    // those, we divide the matrix by its dimensions here.
    //
    // We don't do this if we're either trivial (can ignore the matrix) or clamping
    // in both X and Y since clamping to width,height is just as easy as to 0xFFFF.

    if (!(clampClamp || trivialMatrix)) {
        fInvMatrix.postIDiv(fPixmap.width(), fPixmap.height());
    }

    // Now that all possible changes to the matrix have taken place, check
    // to see if we're really close to a no-scale matrix.  If so, explicitly
    // set it to be so.  Subsequent code may inspect this matrix to choose
    // a faster path in this case.

    // This code will only execute if the matrix has some scale component;
    // if it's already pure translate then we won't do this inversion.

    if (matrix_only_scale_translate(fInvMatrix)) {
        SkMatrix forward;
        if (fInvMatrix.invert(&forward)) {
            if (clampClamp ? just_trans_clamp(forward, fPixmap)
                           : just_trans_general(forward)) {
                SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
                SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
                fInvMatrix.setTranslate(tx, ty);
            }
        }
    }

    fInvProc        = fInvMatrix.getMapXYProc();
    fInvType        = fInvMatrix.getType();
    fInvSx          = SkScalarToFixed(fInvMatrix.getScaleX());
    fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX());
    fInvKy          = SkScalarToFixed(fInvMatrix.getSkewY());
    fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY());

    fAlphaScale = SkAlpha255To256(paint.getAlpha());

    fShaderProc32 = NULL;
    fShaderProc16 = NULL;
    fSampleProc32 = NULL;
    fSampleProc16 = NULL;

    // recompute the triviality of the matrix here because we may have
    // changed it!

    trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0;

    if (kLow_SkFilterQuality == fFilterLevel) {
        // Only try bilerp if the matrix is "interesting" and
        // the image has a suitable size.

        if (fInvType <= SkMatrix::kTranslate_Mask ||
            !valid_for_filtering(fPixmap.width() | fPixmap.height()))
        {
            fFilterLevel = kNone_SkFilterQuality;
        }
    }

    return this->chooseScanlineProcs(trivialMatrix, clampClamp, paint);
}
Beispiel #21
0
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);
}
Beispiel #22
0
bool SkBitmapProcState::possiblyScaleImage() {
    SkASSERT(NULL == fBitmap);

    fAdjustedMatrix = false;

    if (fFilterLevel <= SkPaint::kLow_FilterLevel) {
        return false;
    }
    // Check to see if the transformation matrix is simple, and if we're
    // doing high quality scaling.  If so, do the bitmap scale here and
    // remove the (non-fractional) scaling component from the matrix.

    SkScalar invScaleX = fInvMatrix.getScaleX();
    SkScalar invScaleY = fInvMatrix.getScaleY();

    float trueDestWidth  = fOrigBitmap.width() / invScaleX;
    float trueDestHeight = fOrigBitmap.height() / invScaleY;

#ifndef SK_IGNORE_PROPER_FRACTIONAL_SCALING
    float roundedDestWidth = SkScalarRoundToScalar(trueDestWidth);
    float roundedDestHeight = SkScalarRoundToScalar(trueDestHeight);
#else
    float roundedDestWidth = trueDestWidth;
    float roundedDestHeight = trueDestHeight;
#endif

    if (SkPaint::kHigh_FilterLevel == fFilterLevel &&
        fInvMatrix.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask) &&
        kN32_SkColorType == fOrigBitmap.colorType() &&
        cache_size_okay(fOrigBitmap, fInvMatrix)) {

        if (SkScalarNearlyEqual(invScaleX,1.0f) &&
            SkScalarNearlyEqual(invScaleY,1.0f)) {
            // short-circuit identity scaling; the output is supposed to
            // be the same as the input, so we might as well go fast.

            // Note(humper): We could also probably do this if the scales
            // are close to -1 as well, since the flip doesn't require
            // any fancy re-sampling...

            // Set our filter level to low -- the only post-filtering this
            // image might require is some interpolation if the translation
            // is fractional.
            fFilterLevel = SkPaint::kLow_FilterLevel;
            return false;
        }

        if (!SkBitmapCache::Find(fOrigBitmap, roundedDestWidth, roundedDestHeight, &fScaledBitmap)) {
            // All the criteria are met; let's make a new bitmap.

            if (!SkBitmapScaler::Resize(&fScaledBitmap,
                                        fOrigBitmap,
                                        SkBitmapScaler::RESIZE_BEST,
                                        roundedDestWidth,
                                        roundedDestHeight,
                                        SkResourceCache::GetAllocator())) {
                // we failed to create fScaledBitmap, so just return and let
                // the scanline proc handle it.
                return false;

            }

            SkASSERT(fScaledBitmap.getPixels());
            fScaledBitmap.setImmutable();
            SkBitmapCache::Add(fOrigBitmap, roundedDestWidth, roundedDestHeight, fScaledBitmap);
        }

        SkASSERT(fScaledBitmap.getPixels());
        fBitmap = &fScaledBitmap;

        // set the inv matrix type to translate-only;
        fInvMatrix.setTranslate(fInvMatrix.getTranslateX() / fInvMatrix.getScaleX(),
                                fInvMatrix.getTranslateY() / fInvMatrix.getScaleY());

#ifndef SK_IGNORE_PROPER_FRACTIONAL_SCALING
        // reintroduce any fractional scaling missed by our integral scale done above.

       float fractionalScaleX = roundedDestWidth/trueDestWidth;
       float fractionalScaleY = roundedDestHeight/trueDestHeight;

       fInvMatrix.postScale(fractionalScaleX, fractionalScaleY);
#endif
        fAdjustedMatrix = true;

        // Set our filter level to low -- the only post-filtering this
        // image might require is some interpolation if the translation
        // is fractional or if there's any remaining scaling to be done.
        fFilterLevel = SkPaint::kLow_FilterLevel;
        return true;
    }

    /*
     *  If High, then our special-case for scale-only did not take, and so we
     *  have to make a choice:
     *      1. fall back on mipmaps + bilerp
     *      2. fall back on scanline bicubic filter
     *  For now, we compute the "scale" value from the matrix, and have a
     *  threshold to decide when bicubic is better, and when mips are better.
     *  No doubt a fancier decision tree could be used uere.
     *
     *  If Medium, then we just try to build a mipmap and select a level,
     *  setting the filter-level to kLow to signal that we just need bilerp
     *  to process the selected level.
     */

    SkScalar scaleSqd = effective_matrix_scale_sqrd(fInvMatrix);

    if (SkPaint::kHigh_FilterLevel == fFilterLevel) {
        // Set the limit at 0.25 for the CTM... if the CTM is scaling smaller
        // than this, then the mipmaps quality may be greater (certainly faster)
        // so we only keep High quality if the scale is greater than this.
        //
        // Since we're dealing with the inverse, we compare against its inverse.
        const SkScalar bicubicLimit = 4.0f;
        const SkScalar bicubicLimitSqd = bicubicLimit * bicubicLimit;
        if (scaleSqd < bicubicLimitSqd) {  // use bicubic scanline
            return false;
        }

        // else set the filter-level to Medium, since we're scaling down and
        // want to reqeust mipmaps
        fFilterLevel = SkPaint::kMedium_FilterLevel;
    }

    SkASSERT(SkPaint::kMedium_FilterLevel == fFilterLevel);

    /**
     *  Medium quality means use a mipmap for down-scaling, and just bilper
     *  for upscaling. Since we're examining the inverse matrix, we look for
     *  a scale > 1 to indicate down scaling by the CTM.
     */
    if (scaleSqd > SK_Scalar1) {
        fCurrMip.reset(SkMipMapCache::FindAndRef(fOrigBitmap));
        if (NULL == fCurrMip.get()) {
            fCurrMip.reset(SkMipMap::Build(fOrigBitmap));
            if (NULL == fCurrMip.get()) {
                return false;
            }
            SkMipMapCache::Add(fOrigBitmap, fCurrMip);
        }

        SkScalar levelScale = SkScalarInvert(SkScalarSqrt(scaleSqd));
        SkMipMap::Level level;
        if (fCurrMip->extractLevel(levelScale, &level)) {
            SkScalar invScaleFixup = level.fScale;
            fInvMatrix.postScale(invScaleFixup, invScaleFixup);

            const SkImageInfo info = fOrigBitmap.info().makeWH(level.fWidth, level.fHeight);
            // todo: if we could wrap the fCurrMip in a pixelref, then we could just install
            //       that here, and not need to explicitly track it ourselves.
            fScaledBitmap.installPixels(info, level.fPixels, level.fRowBytes);
            fBitmap = &fScaledBitmap;
            fFilterLevel = SkPaint::kLow_FilterLevel;
            return true;
        }
    }

    return false;
}