diff options
-rw-r--r-- | libs/hwui/Properties.cpp | 9 | ||||
-rw-r--r-- | libs/hwui/Properties.h | 2 | ||||
-rw-r--r-- | libs/hwui/jni/BitmapRegionDecoder.cpp | 71 | ||||
-rw-r--r-- | libs/hwui/jni/Graphics.cpp | 2 |
4 files changed, 71 insertions, 13 deletions
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index ae46a99f09c8..064cac2a6fc6 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -113,7 +113,6 @@ float Properties::maxHdrHeadroomOn8bit = 5.f; // TODO: Refine this number bool Properties::clipSurfaceViews = false; bool Properties::hdr10bitPlus = false; bool Properties::skipTelemetry = false; -bool Properties::resampleGainmapRegions = false; bool Properties::queryGlobalPriority = false; int Properties::timeoutMultiplier = 1; @@ -190,8 +189,6 @@ bool Properties::load() { clipSurfaceViews = base::GetBoolProperty("debug.hwui.clip_surfaceviews", hwui_flags::clip_surfaceviews()); hdr10bitPlus = hwui_flags::hdr_10bit_plus(); - resampleGainmapRegions = base::GetBoolProperty("debug.hwui.resample_gainmap_regions", - hwui_flags::resample_gainmap_regions()); queryGlobalPriority = hwui_flags::query_global_priority(); timeoutMultiplier = android::base::GetIntProperty("ro.hw_timeout_multiplier", 1); @@ -288,5 +285,11 @@ bool Properties::initializeGlAlways() { return base::GetBoolProperty(PROPERTY_INITIALIZE_GL_ALWAYS, hwui_flags::initialize_gl_always()); } +bool Properties::resampleGainmapRegions() { + static bool sResampleGainmapRegions = base::GetBoolProperty( + "debug.hwui.resample_gainmap_regions", hwui_flags::resample_gainmap_regions()); + return sResampleGainmapRegions; +} + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 6f84796fb11e..db930f3904de 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -345,7 +345,6 @@ public: static bool clipSurfaceViews; static bool hdr10bitPlus; static bool skipTelemetry; - static bool resampleGainmapRegions; static bool queryGlobalPriority; static int timeoutMultiplier; @@ -381,6 +380,7 @@ public: static void setDrawingEnabled(bool enable); static bool initializeGlAlways(); + static bool resampleGainmapRegions(); private: static StretchEffectBehavior stretchEffectBehavior; diff --git a/libs/hwui/jni/BitmapRegionDecoder.cpp b/libs/hwui/jni/BitmapRegionDecoder.cpp index 5ffd5b9016d8..491066b05952 100644 --- a/libs/hwui/jni/BitmapRegionDecoder.cpp +++ b/libs/hwui/jni/BitmapRegionDecoder.cpp @@ -112,9 +112,7 @@ public: return false; } - // Round out the subset so that we decode a slightly larger region, in - // case the subset has fractional components. - SkIRect roundedSubset = desiredSubset.roundOut(); + sampleSize = std::max(sampleSize, 1); // Map the desired subset to the space of the decoded gainmap. The // subset is repositioned relative to the resulting bitmap, and then @@ -123,10 +121,51 @@ public: // for existing gainmap formats. SkRect logicalSubset = desiredSubset.makeOffset(-std::floorf(desiredSubset.left()), -std::floorf(desiredSubset.top())); - logicalSubset.fLeft /= sampleSize; - logicalSubset.fTop /= sampleSize; - logicalSubset.fRight /= sampleSize; - logicalSubset.fBottom /= sampleSize; + logicalSubset = scale(logicalSubset, 1.0f / sampleSize); + + // Round out the subset so that we decode a slightly larger region, in + // case the subset has fractional components. When we round, we need to + // round the downsampled subset to avoid possibly rounding down by accident. + // Consider this concrete example if we round the desired subset directly: + // + // * We are decoding a 18x18 corner of an image + // + // * Gainmap is 1/4 resolution, which is logically a 4.5x4.5 gainmap + // that we would want + // + // * The app wants to downsample by a factor of 2x + // + // * The desired gainmap dimensions are computed to be 3x3 to fit the + // downsampled gainmap, since we need to fill a 2.25x2.25 region that's + // later upscaled to 3x3 + // + // * But, if we round out the desired gainmap region _first_, then we + // request to decode a 5x5 region, downsampled by 2, which actually + // decodes a 2x2 region since skia rounds down internally. But then we transfer + // the result to a 3x3 bitmap using a clipping allocator, which leaves an inset. + // Not only did we get a smaller region than we expected (so, our desired subset is + // not valid), but because the API allows for decoding regions using a recycled + // bitmap, we can't really safely fill in the inset since then we might + // extend the gainmap beyond intended the image bounds. Oops. + // + // * If we instead round out as if we downsampled, then we downsample + // the desired region to 2.25x2.25, round out to 3x3, then upsample back + // into the source gainmap space to get 6x6. Then we decode a 6x6 region + // downsampled into a 3x3 region, and everything's now correct. + // + // Note that we don't always run into this problem, because + // decoders actually round *up* for subsampling when decoding a subset + // that matches the dimensions of the image. E.g., if the original image + // size in the above example was a 20x20 image, so that the gainmap was + // 5x5, then we still manage to downsample into a 3x3 bitmap even with + // the "wrong" math. but that's what we wanted! + // + // Note also that if we overshoot the gainmap bounds with the requested + // subset it isn't a problem either, since now the decoded bitmap is too + // large, rather than too small, so now we can use the desired subset to + // avoid sampling "invalid" colors. + SkRect scaledSubset = scale(desiredSubset, 1.0f / sampleSize); + SkIRect roundedSubset = scale(scaledSubset.roundOut(), static_cast<float>(sampleSize)); RecyclingClippingPixelAllocator allocator(nativeBitmap.get(), false, logicalSubset); if (!mGainmapBRD->decodeRegion(&bm, &allocator, roundedSubset, sampleSize, decodeColorType, @@ -154,7 +193,7 @@ public: const float scaleX = ((float)mGainmapBRD->width()) / mMainImageBRD->width(); const float scaleY = ((float)mGainmapBRD->height()) / mMainImageBRD->height(); - if (uirenderer::Properties::resampleGainmapRegions) { + if (uirenderer::Properties::resampleGainmapRegions()) { const auto srcRect = SkRect::MakeLTRB( mainImageRegion.left() * scaleX, mainImageRegion.top() * scaleY, mainImageRegion.right() * scaleX, mainImageRegion.bottom() * scaleY); @@ -186,6 +225,22 @@ private: , mGainmapBRD(std::move(gainmapBRD)) , mGainmapInfo(info) {} + SkRect scale(SkRect rect, float scale) const { + rect.fLeft *= scale; + rect.fTop *= scale; + rect.fRight *= scale; + rect.fBottom *= scale; + return rect; + } + + SkIRect scale(SkIRect rect, float scale) const { + rect.fLeft *= scale; + rect.fTop *= scale; + rect.fRight *= scale; + rect.fBottom *= scale; + return rect; + } + std::unique_ptr<skia::BitmapRegionDecoder> mMainImageBRD; std::unique_ptr<skia::BitmapRegionDecoder> mGainmapBRD; SkGainmapInfo mGainmapInfo; diff --git a/libs/hwui/jni/Graphics.cpp b/libs/hwui/jni/Graphics.cpp index 258bf91f2124..a210ddf54b2e 100644 --- a/libs/hwui/jni/Graphics.cpp +++ b/libs/hwui/jni/Graphics.cpp @@ -750,7 +750,7 @@ void RecyclingClippingPixelAllocator::copyIfNecessary() { std::optional<SkRect> RecyclingClippingPixelAllocator::getSourceBoundsForUpsample( std::optional<SkRect> subset) { - if (!uirenderer::Properties::resampleGainmapRegions || !subset || subset->isEmpty()) { + if (!uirenderer::Properties::resampleGainmapRegions() || !subset || subset->isEmpty()) { return std::nullopt; } |