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(); } }
/* * 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(); const int origW = fProvider.info().width(); const int origH = fProvider.info().height(); bool allow_ignore_fractional_translate = true; // historical default if (kMedium_SkFilterQuality == fFilterLevel) { allow_ignore_fractional_translate = false; } SkDefaultBitmapController controller; fBMState = controller.requestBitmap(fProvider, inv, paint.getFilterQuality(), fBMStateStorage.get(), fBMStateStorage.size()); // Note : we allow the controller to return an empty (zero-dimension) result. Should we? if (nullptr == 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 && allow_ignore_fractional_translate) ? just_trans_clamp(forward, fPixmap) : just_trans_general(forward)) { fInvMatrix.setTranslate(-forward.getTranslateX(), -forward.getTranslateY()); } } } 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 = nullptr; fShaderProc16 = nullptr; fSampleProc32 = nullptr; // recompute the triviality of the matrix here because we may have // changed it! trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; // If our target pixmap is the same as the original, then we revert back to legacy behavior // and allow the code to ignore fractional translate. // // The width/height check allows allow_ignore_fractional_translate to stay false if we // previously set it that way (e.g. we started in kMedium). // if (fPixmap.width() == origW && fPixmap.height() == origH) { allow_ignore_fractional_translate = true; } if (kLow_SkFilterQuality == fFilterLevel && allow_ignore_fractional_translate) { // 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); }