diff options
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/hwui/hwui/Bitmap.cpp | 57 | ||||
| -rw-r--r-- | libs/hwui/hwui/Bitmap.h | 22 | ||||
| -rw-r--r-- | libs/hwui/hwui/ImageDecoder.cpp | 47 | ||||
| -rw-r--r-- | libs/hwui/hwui/ImageDecoder.h | 8 | ||||
| -rw-r--r-- | libs/hwui/pipeline/skia/ATraceMemoryDump.cpp | 41 | ||||
| -rw-r--r-- | libs/hwui/pipeline/skia/ATraceMemoryDump.h | 4 |
6 files changed, 124 insertions, 55 deletions
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp index 84549e8ce6e4..3c402e996218 100644 --- a/libs/hwui/hwui/Bitmap.cpp +++ b/libs/hwui/hwui/Bitmap.cpp @@ -38,7 +38,7 @@ #include <SkCanvas.h> #include <SkImagePriv.h> - +#include <SkWebpEncoder.h> #include <SkHighContrastFilter.h> #include <limits> @@ -471,4 +471,59 @@ BitmapPalette Bitmap::computePalette(const SkImageInfo& info, const void* addr, return BitmapPalette::Unknown; } +Bitmap::CompressResult Bitmap::compress(JavaCompressFormat format, int32_t quality, + SkWStream* stream) { + SkBitmap skbitmap; + getSkBitmap(&skbitmap); + return compress(skbitmap, format, quality, stream); +} + +Bitmap::CompressResult Bitmap::compress(const SkBitmap& bitmap, JavaCompressFormat format, + int32_t quality, SkWStream* stream) { + SkBitmap skbitmap = bitmap; + if (skbitmap.colorType() == kRGBA_F16_SkColorType) { + // Convert to P3 before encoding. This matches + // SkAndroidCodec::computeOutputColorSpace for wide gamuts. Now that F16 + // could already be P3, we still want to convert to 8888. + auto cs = SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, SkNamedGamut::kDCIP3); + auto info = skbitmap.info().makeColorType(kRGBA_8888_SkColorType) + .makeColorSpace(std::move(cs)); + SkBitmap p3; + if (!p3.tryAllocPixels(info)) { + return CompressResult::AllocationFailed; + } + + SkPixmap pm; + SkAssertResult(p3.peekPixels(&pm)); // should always work if tryAllocPixels() did. + if (!skbitmap.readPixels(pm)) { + return CompressResult::Error; + } + skbitmap = p3; + } + + SkEncodedImageFormat fm; + switch (format) { + case JavaCompressFormat::Jpeg: + fm = SkEncodedImageFormat::kJPEG; + break; + case JavaCompressFormat::Png: + fm = SkEncodedImageFormat::kPNG; + break; + case JavaCompressFormat::Webp: + fm = SkEncodedImageFormat::kWEBP; + break; + case JavaCompressFormat::WebpLossy: + case JavaCompressFormat::WebpLossless: { + SkWebpEncoder::Options options; + options.fQuality = quality; + options.fCompression = format == JavaCompressFormat::WebpLossy ? + SkWebpEncoder::Compression::kLossy : SkWebpEncoder::Compression::kLossless; + return SkWebpEncoder::Encode(stream, skbitmap.pixmap(), options) + ? CompressResult::Success : CompressResult::Error; + } + } + + return SkEncodeImage(stream, skbitmap, fm, quality) + ? CompressResult::Success : CompressResult::Error; +} } // namespace android diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h index 1cda0465ae64..ee365af2f7be 100644 --- a/libs/hwui/hwui/Bitmap.h +++ b/libs/hwui/hwui/Bitmap.h @@ -27,6 +27,8 @@ #include <android/hardware_buffer.h> #endif +class SkWStream; + namespace android { enum class PixelStorageType { @@ -142,6 +144,26 @@ public: // and places that value in size. static bool computeAllocationSize(size_t rowBytes, int height, size_t* size); + // These must match the int values of CompressFormat in Bitmap.java, as well as + // AndroidBitmapCompressFormat. + enum class JavaCompressFormat { + Jpeg = 0, + Png = 1, + Webp = 2, + WebpLossy = 3, + WebpLossless = 4, + }; + + enum class CompressResult { + Success, + AllocationFailed, + Error, + }; + + CompressResult compress(JavaCompressFormat format, int32_t quality, SkWStream* stream); + + static CompressResult compress(const SkBitmap& bitmap, JavaCompressFormat format, + int32_t quality, SkWStream* stream); private: static sk_sp<Bitmap> allocateAshmemBitmap(size_t size, const SkImageInfo& i, size_t rowBytes); static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& i, size_t rowBytes); diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp index 4f2027d978a1..a6c4e9db7280 100644 --- a/libs/hwui/hwui/ImageDecoder.cpp +++ b/libs/hwui/hwui/ImageDecoder.cpp @@ -30,19 +30,25 @@ ImageDecoder::ImageDecoder(std::unique_ptr<SkAndroidCodec> codec, sk_sp<SkPngChu , mTargetSize(mCodec->getInfo().dimensions()) , mDecodeSize(mTargetSize) , mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType)) - , mOutAlphaType(mCodec->getInfo().isOpaque() ? - kOpaque_SkAlphaType : kPremul_SkAlphaType) + , mUnpremultipliedRequired(false) , mOutColorSpace(mCodec->getInfo().refColorSpace()) , mSampleSize(1) { } +SkAlphaType ImageDecoder::getOutAlphaType() const { + // While an SkBitmap may want to use kOpaque_SkAlphaType for a performance + // optimization, this class just outputs raw pixels. Using either + // premultiplication choice has no effect on decoding an opaque encoded image. + return mUnpremultipliedRequired ? kUnpremul_SkAlphaType : kPremul_SkAlphaType; +} + bool ImageDecoder::setTargetSize(int width, int height) { if (width <= 0 || height <= 0) { return false; } - auto info = SkImageInfo::Make(width, height, mOutColorType, mOutAlphaType); + auto info = SkImageInfo::Make(width, height, mOutColorType, getOutAlphaType()); size_t rowBytes = info.minRowBytes(); if (rowBytes == 0) { // This would have overflowed. @@ -63,7 +69,7 @@ bool ImageDecoder::setTargetSize(int width, int height) { SkISize targetSize = { width, height }, decodeSize = targetSize; int sampleSize = mCodec->computeSampleSize(&decodeSize); - if (decodeSize != targetSize && mOutAlphaType == kUnpremul_SkAlphaType + if (decodeSize != targetSize && mUnpremultipliedRequired && !mCodec->getInfo().isOpaque()) { return false; } @@ -119,29 +125,11 @@ bool ImageDecoder::setOutColorType(SkColorType colorType) { return true; } -bool ImageDecoder::setOutAlphaType(SkAlphaType alpha) { - switch (alpha) { - case kOpaque_SkAlphaType: - return opaque(); - case kPremul_SkAlphaType: - if (opaque()) { - // Opaque can be treated as premul. - return true; - } - break; - case kUnpremul_SkAlphaType: - if (opaque()) { - // Opaque can be treated as unpremul. - return true; - } - if (mDecodeSize != mTargetSize) { - return false; - } - break; - default: - return false; +bool ImageDecoder::setUnpremultipliedRequired(bool required) { + if (required && !opaque() && mDecodeSize != mTargetSize) { + return false; } - mOutAlphaType = alpha; + mUnpremultipliedRequired = required; return true; } @@ -151,11 +139,11 @@ void ImageDecoder::setOutColorSpace(sk_sp<SkColorSpace> colorSpace) { SkImageInfo ImageDecoder::getOutputInfo() const { SkISize size = mCropRect ? mCropRect->size() : mTargetSize; - return SkImageInfo::Make(size, mOutColorType, mOutAlphaType, mOutColorSpace); + return SkImageInfo::Make(size, mOutColorType, getOutAlphaType(), mOutColorSpace); } bool ImageDecoder::opaque() const { - return mOutAlphaType == kOpaque_SkAlphaType; + return mCodec->getInfo().alphaType() == kOpaque_SkAlphaType; } bool ImageDecoder::gray() const { @@ -165,7 +153,8 @@ bool ImageDecoder::gray() const { SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) { void* decodePixels = pixels; size_t decodeRowBytes = rowBytes; - auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, mOutAlphaType, mOutColorSpace); + auto decodeInfo = SkImageInfo::Make(mDecodeSize, mOutColorType, getOutAlphaType(), + mOutColorSpace); // Used if we need a temporary before scaling or subsetting. // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380. SkBitmap tmp; diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h index b956f4a77793..96f97e5421f3 100644 --- a/libs/hwui/hwui/ImageDecoder.h +++ b/libs/hwui/hwui/ImageDecoder.h @@ -41,14 +41,12 @@ public: bool setOutColorType(SkColorType outColorType); - bool setOutAlphaType(SkAlphaType outAlphaType); + bool setUnpremultipliedRequired(bool unpremultipliedRequired); void setOutColorSpace(sk_sp<SkColorSpace> cs); // The size is the final size after scaling and cropping. SkImageInfo getOutputInfo() const; - SkColorType getOutColorType() const { return mOutColorType; } - SkAlphaType getOutAlphaType() const { return mOutAlphaType; } bool opaque() const; bool gray() const; @@ -59,13 +57,15 @@ private: SkISize mTargetSize; SkISize mDecodeSize; SkColorType mOutColorType; - SkAlphaType mOutAlphaType; + bool mUnpremultipliedRequired; sk_sp<SkColorSpace> mOutColorSpace; int mSampleSize; std::optional<SkIRect> mCropRect; ImageDecoder(const ImageDecoder&) = delete; ImageDecoder& operator=(const ImageDecoder&) = delete; + + SkAlphaType getOutAlphaType() const; }; } // namespace android diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp index 2c78b024536b..551bdc63121d 100644 --- a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp @@ -24,8 +24,8 @@ namespace android { namespace uirenderer { namespace skiapipeline { -// When purgeable is INVALID_TIME it won't be logged at all. -#define INVALID_TIME -1 +// When purgeable is INVALID_MEMORY_SIZE it won't be logged at all. +#define INVALID_MEMORY_SIZE -1 /** * Skia invokes the following SkTraceMemoryDump functions: @@ -42,10 +42,10 @@ namespace skiapipeline { * "GPU Memory" category. */ static std::unordered_map<const char*, const char*> sResourceMap = { - {"malloc", "Graphics CPU Memory"}, // taken from setMemoryBacking(backingType) - {"gl_texture", "Graphics Texture Memory"}, // taken from setMemoryBacking(backingType) + {"malloc", "HWUI CPU Memory"}, // taken from setMemoryBacking(backingType) + {"gl_texture", "HWUI Texture Memory"}, // taken from setMemoryBacking(backingType) {"Texture", - "Graphics Texture Memory"}, // taken from dumpStringValue(value, valueName="type") + "HWUI Texture Memory"}, // taken from dumpStringValue(value, valueName="type") // Uncomment categories below to split "GPU Memory" into more brackets for debugging. /*{"vk_buffer", "vk_buffer"}, {"gl_renderbuffer", "gl_renderbuffer"}, @@ -105,10 +105,10 @@ void ATraceMemoryDump::startFrame() { // Once a category is observed in at least one frame, it is always reported in subsequent // frames (even if it is 0). Not logging a category to ATRACE would mean its value has not // changed since the previous frame, which is not what we want. - it.second.time = 0; - // If purgeableTime is INVALID_TIME, then logTraces won't log it at all. - if (it.second.purgeableTime != INVALID_TIME) { - it.second.purgeableTime = 0; + it.second.memory = 0; + // If purgeableMemory is INVALID_MEMORY_SIZE, then logTraces won't log it at all. + if (it.second.purgeableMemory != INVALID_MEMORY_SIZE) { + it.second.purgeableMemory = 0; } } } @@ -119,12 +119,15 @@ void ATraceMemoryDump::startFrame() { void ATraceMemoryDump::logTraces() { // Accumulate data from last dumpName recordAndResetCountersIfNeeded(""); + uint64_t hwui_all_frame_memory = 0; for (auto& it : mCurrentValues) { - ATRACE_INT64(it.first.c_str(), it.second.time); - if (it.second.purgeableTime != INVALID_TIME) { - ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableTime); + hwui_all_frame_memory += it.second.memory; + ATRACE_INT64(it.first.c_str(), it.second.memory); + if (it.second.purgeableMemory != INVALID_MEMORY_SIZE) { + ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableMemory); } } + ATRACE_INT64("HWUI All Memory", hwui_all_frame_memory); } /** @@ -145,12 +148,12 @@ void ATraceMemoryDump::recordAndResetCountersIfNeeded(const char* dumpName) { // A new dumpName observed -> store the data already collected. auto memoryCounter = mCurrentValues.find(mCategory); if (memoryCounter != mCurrentValues.end()) { - memoryCounter->second.time += mLastDumpValue; - if (mLastPurgeableDumpValue != INVALID_TIME) { - if (memoryCounter->second.purgeableTime == INVALID_TIME) { - memoryCounter->second.purgeableTime = mLastPurgeableDumpValue; + memoryCounter->second.memory += mLastDumpValue; + if (mLastPurgeableDumpValue != INVALID_MEMORY_SIZE) { + if (memoryCounter->second.purgeableMemory == INVALID_MEMORY_SIZE) { + memoryCounter->second.purgeableMemory = mLastPurgeableDumpValue; } else { - memoryCounter->second.purgeableTime += mLastPurgeableDumpValue; + memoryCounter->second.purgeableMemory += mLastPurgeableDumpValue; } } } else { @@ -164,10 +167,10 @@ void ATraceMemoryDump::recordAndResetCountersIfNeeded(const char* dumpName) { void ATraceMemoryDump::resetCurrentCounter(const char* dumpName) { mLastDumpValue = 0; - mLastPurgeableDumpValue = INVALID_TIME; + mLastPurgeableDumpValue = INVALID_MEMORY_SIZE; mLastDumpName = dumpName; // Categories not listed in sResourceMap are reported as "GPU memory" - mCategory = "GPU Memory"; + mCategory = "HWUI GPU Memory"; } } /* namespace skiapipeline */ diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.h b/libs/hwui/pipeline/skia/ATraceMemoryDump.h index aa5c401ad1ca..4592711dd5b5 100644 --- a/libs/hwui/pipeline/skia/ATraceMemoryDump.h +++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.h @@ -62,8 +62,8 @@ private: std::string mCategory; struct TraceValue { - uint64_t time; - uint64_t purgeableTime; + uint64_t memory; + uint64_t purgeableMemory; }; // keys are define in sResourceMap |