Loading libs/hwui/Properties.cpp +6 −3 Original line number Original line Diff line number Diff line Loading @@ -113,7 +113,6 @@ float Properties::maxHdrHeadroomOn8bit = 5.f; // TODO: Refine this number bool Properties::clipSurfaceViews = false; bool Properties::clipSurfaceViews = false; bool Properties::hdr10bitPlus = false; bool Properties::hdr10bitPlus = false; bool Properties::skipTelemetry = false; bool Properties::skipTelemetry = false; bool Properties::resampleGainmapRegions = false; bool Properties::queryGlobalPriority = false; bool Properties::queryGlobalPriority = false; int Properties::timeoutMultiplier = 1; int Properties::timeoutMultiplier = 1; Loading Loading @@ -190,8 +189,6 @@ bool Properties::load() { clipSurfaceViews = clipSurfaceViews = base::GetBoolProperty("debug.hwui.clip_surfaceviews", hwui_flags::clip_surfaceviews()); base::GetBoolProperty("debug.hwui.clip_surfaceviews", hwui_flags::clip_surfaceviews()); hdr10bitPlus = hwui_flags::hdr_10bit_plus(); 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(); queryGlobalPriority = hwui_flags::query_global_priority(); timeoutMultiplier = android::base::GetIntProperty("ro.hw_timeout_multiplier", 1); timeoutMultiplier = android::base::GetIntProperty("ro.hw_timeout_multiplier", 1); Loading Loading @@ -288,5 +285,11 @@ bool Properties::initializeGlAlways() { return base::GetBoolProperty(PROPERTY_INITIALIZE_GL_ALWAYS, hwui_flags::initialize_gl_always()); 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 uirenderer } // namespace android } // namespace android libs/hwui/Properties.h +1 −1 Original line number Original line Diff line number Diff line Loading @@ -345,7 +345,6 @@ public: static bool clipSurfaceViews; static bool clipSurfaceViews; static bool hdr10bitPlus; static bool hdr10bitPlus; static bool skipTelemetry; static bool skipTelemetry; static bool resampleGainmapRegions; static bool queryGlobalPriority; static bool queryGlobalPriority; static int timeoutMultiplier; static int timeoutMultiplier; Loading Loading @@ -381,6 +380,7 @@ public: static void setDrawingEnabled(bool enable); static void setDrawingEnabled(bool enable); static bool initializeGlAlways(); static bool initializeGlAlways(); static bool resampleGainmapRegions(); private: private: static StretchEffectBehavior stretchEffectBehavior; static StretchEffectBehavior stretchEffectBehavior; Loading libs/hwui/jni/BitmapRegionDecoder.cpp +63 −8 Original line number Original line Diff line number Diff line Loading @@ -112,9 +112,7 @@ public: return false; return false; } } // Round out the subset so that we decode a slightly larger region, in sampleSize = std::max(sampleSize, 1); // case the subset has fractional components. SkIRect roundedSubset = desiredSubset.roundOut(); // Map the desired subset to the space of the decoded gainmap. The // Map the desired subset to the space of the decoded gainmap. The // subset is repositioned relative to the resulting bitmap, and then // subset is repositioned relative to the resulting bitmap, and then Loading @@ -123,10 +121,51 @@ public: // for existing gainmap formats. // for existing gainmap formats. SkRect logicalSubset = desiredSubset.makeOffset(-std::floorf(desiredSubset.left()), SkRect logicalSubset = desiredSubset.makeOffset(-std::floorf(desiredSubset.left()), -std::floorf(desiredSubset.top())); -std::floorf(desiredSubset.top())); logicalSubset.fLeft /= sampleSize; logicalSubset = scale(logicalSubset, 1.0f / sampleSize); logicalSubset.fTop /= sampleSize; logicalSubset.fRight /= sampleSize; // Round out the subset so that we decode a slightly larger region, in logicalSubset.fBottom /= sampleSize; // 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); RecyclingClippingPixelAllocator allocator(nativeBitmap.get(), false, logicalSubset); if (!mGainmapBRD->decodeRegion(&bm, &allocator, roundedSubset, sampleSize, decodeColorType, if (!mGainmapBRD->decodeRegion(&bm, &allocator, roundedSubset, sampleSize, decodeColorType, Loading Loading @@ -154,7 +193,7 @@ public: const float scaleX = ((float)mGainmapBRD->width()) / mMainImageBRD->width(); const float scaleX = ((float)mGainmapBRD->width()) / mMainImageBRD->width(); const float scaleY = ((float)mGainmapBRD->height()) / mMainImageBRD->height(); const float scaleY = ((float)mGainmapBRD->height()) / mMainImageBRD->height(); if (uirenderer::Properties::resampleGainmapRegions) { if (uirenderer::Properties::resampleGainmapRegions()) { const auto srcRect = SkRect::MakeLTRB( const auto srcRect = SkRect::MakeLTRB( mainImageRegion.left() * scaleX, mainImageRegion.top() * scaleY, mainImageRegion.left() * scaleX, mainImageRegion.top() * scaleY, mainImageRegion.right() * scaleX, mainImageRegion.bottom() * scaleY); mainImageRegion.right() * scaleX, mainImageRegion.bottom() * scaleY); Loading Loading @@ -186,6 +225,22 @@ private: , mGainmapBRD(std::move(gainmapBRD)) , mGainmapBRD(std::move(gainmapBRD)) , mGainmapInfo(info) {} , 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> mMainImageBRD; std::unique_ptr<skia::BitmapRegionDecoder> mGainmapBRD; std::unique_ptr<skia::BitmapRegionDecoder> mGainmapBRD; SkGainmapInfo mGainmapInfo; SkGainmapInfo mGainmapInfo; Loading libs/hwui/jni/Graphics.cpp +1 −1 Original line number Original line Diff line number Diff line Loading @@ -750,7 +750,7 @@ void RecyclingClippingPixelAllocator::copyIfNecessary() { std::optional<SkRect> RecyclingClippingPixelAllocator::getSourceBoundsForUpsample( std::optional<SkRect> RecyclingClippingPixelAllocator::getSourceBoundsForUpsample( std::optional<SkRect> subset) { std::optional<SkRect> subset) { if (!uirenderer::Properties::resampleGainmapRegions || !subset || subset->isEmpty()) { if (!uirenderer::Properties::resampleGainmapRegions() || !subset || subset->isEmpty()) { return std::nullopt; return std::nullopt; } } Loading Loading
libs/hwui/Properties.cpp +6 −3 Original line number Original line Diff line number Diff line Loading @@ -113,7 +113,6 @@ float Properties::maxHdrHeadroomOn8bit = 5.f; // TODO: Refine this number bool Properties::clipSurfaceViews = false; bool Properties::clipSurfaceViews = false; bool Properties::hdr10bitPlus = false; bool Properties::hdr10bitPlus = false; bool Properties::skipTelemetry = false; bool Properties::skipTelemetry = false; bool Properties::resampleGainmapRegions = false; bool Properties::queryGlobalPriority = false; bool Properties::queryGlobalPriority = false; int Properties::timeoutMultiplier = 1; int Properties::timeoutMultiplier = 1; Loading Loading @@ -190,8 +189,6 @@ bool Properties::load() { clipSurfaceViews = clipSurfaceViews = base::GetBoolProperty("debug.hwui.clip_surfaceviews", hwui_flags::clip_surfaceviews()); base::GetBoolProperty("debug.hwui.clip_surfaceviews", hwui_flags::clip_surfaceviews()); hdr10bitPlus = hwui_flags::hdr_10bit_plus(); 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(); queryGlobalPriority = hwui_flags::query_global_priority(); timeoutMultiplier = android::base::GetIntProperty("ro.hw_timeout_multiplier", 1); timeoutMultiplier = android::base::GetIntProperty("ro.hw_timeout_multiplier", 1); Loading Loading @@ -288,5 +285,11 @@ bool Properties::initializeGlAlways() { return base::GetBoolProperty(PROPERTY_INITIALIZE_GL_ALWAYS, hwui_flags::initialize_gl_always()); 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 uirenderer } // namespace android } // namespace android
libs/hwui/Properties.h +1 −1 Original line number Original line Diff line number Diff line Loading @@ -345,7 +345,6 @@ public: static bool clipSurfaceViews; static bool clipSurfaceViews; static bool hdr10bitPlus; static bool hdr10bitPlus; static bool skipTelemetry; static bool skipTelemetry; static bool resampleGainmapRegions; static bool queryGlobalPriority; static bool queryGlobalPriority; static int timeoutMultiplier; static int timeoutMultiplier; Loading Loading @@ -381,6 +380,7 @@ public: static void setDrawingEnabled(bool enable); static void setDrawingEnabled(bool enable); static bool initializeGlAlways(); static bool initializeGlAlways(); static bool resampleGainmapRegions(); private: private: static StretchEffectBehavior stretchEffectBehavior; static StretchEffectBehavior stretchEffectBehavior; Loading
libs/hwui/jni/BitmapRegionDecoder.cpp +63 −8 Original line number Original line Diff line number Diff line Loading @@ -112,9 +112,7 @@ public: return false; return false; } } // Round out the subset so that we decode a slightly larger region, in sampleSize = std::max(sampleSize, 1); // case the subset has fractional components. SkIRect roundedSubset = desiredSubset.roundOut(); // Map the desired subset to the space of the decoded gainmap. The // Map the desired subset to the space of the decoded gainmap. The // subset is repositioned relative to the resulting bitmap, and then // subset is repositioned relative to the resulting bitmap, and then Loading @@ -123,10 +121,51 @@ public: // for existing gainmap formats. // for existing gainmap formats. SkRect logicalSubset = desiredSubset.makeOffset(-std::floorf(desiredSubset.left()), SkRect logicalSubset = desiredSubset.makeOffset(-std::floorf(desiredSubset.left()), -std::floorf(desiredSubset.top())); -std::floorf(desiredSubset.top())); logicalSubset.fLeft /= sampleSize; logicalSubset = scale(logicalSubset, 1.0f / sampleSize); logicalSubset.fTop /= sampleSize; logicalSubset.fRight /= sampleSize; // Round out the subset so that we decode a slightly larger region, in logicalSubset.fBottom /= sampleSize; // 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); RecyclingClippingPixelAllocator allocator(nativeBitmap.get(), false, logicalSubset); if (!mGainmapBRD->decodeRegion(&bm, &allocator, roundedSubset, sampleSize, decodeColorType, if (!mGainmapBRD->decodeRegion(&bm, &allocator, roundedSubset, sampleSize, decodeColorType, Loading Loading @@ -154,7 +193,7 @@ public: const float scaleX = ((float)mGainmapBRD->width()) / mMainImageBRD->width(); const float scaleX = ((float)mGainmapBRD->width()) / mMainImageBRD->width(); const float scaleY = ((float)mGainmapBRD->height()) / mMainImageBRD->height(); const float scaleY = ((float)mGainmapBRD->height()) / mMainImageBRD->height(); if (uirenderer::Properties::resampleGainmapRegions) { if (uirenderer::Properties::resampleGainmapRegions()) { const auto srcRect = SkRect::MakeLTRB( const auto srcRect = SkRect::MakeLTRB( mainImageRegion.left() * scaleX, mainImageRegion.top() * scaleY, mainImageRegion.left() * scaleX, mainImageRegion.top() * scaleY, mainImageRegion.right() * scaleX, mainImageRegion.bottom() * scaleY); mainImageRegion.right() * scaleX, mainImageRegion.bottom() * scaleY); Loading Loading @@ -186,6 +225,22 @@ private: , mGainmapBRD(std::move(gainmapBRD)) , mGainmapBRD(std::move(gainmapBRD)) , mGainmapInfo(info) {} , 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> mMainImageBRD; std::unique_ptr<skia::BitmapRegionDecoder> mGainmapBRD; std::unique_ptr<skia::BitmapRegionDecoder> mGainmapBRD; SkGainmapInfo mGainmapInfo; SkGainmapInfo mGainmapInfo; Loading
libs/hwui/jni/Graphics.cpp +1 −1 Original line number Original line Diff line number Diff line Loading @@ -750,7 +750,7 @@ void RecyclingClippingPixelAllocator::copyIfNecessary() { std::optional<SkRect> RecyclingClippingPixelAllocator::getSourceBoundsForUpsample( std::optional<SkRect> RecyclingClippingPixelAllocator::getSourceBoundsForUpsample( std::optional<SkRect> subset) { std::optional<SkRect> subset) { if (!uirenderer::Properties::resampleGainmapRegions || !subset || subset->isEmpty()) { if (!uirenderer::Properties::resampleGainmapRegions() || !subset || subset->isEmpty()) { return std::nullopt; return std::nullopt; } } Loading