Merge "Handle EXIF orientation in hwui/ImageDecoder"

This commit is contained in:
TreeHugger Robot
2021-01-06 23:08:50 +00:00
committed by Android (Google) Code Review
4 changed files with 58 additions and 24 deletions

View File

@@ -40,13 +40,14 @@ sk_sp<SkColorSpace> ImageDecoder::getDefaultColorSpace() const {
ImageDecoder::ImageDecoder(std::unique_ptr<SkAndroidCodec> codec, sk_sp<SkPngChunkReader> peeker)
: mCodec(std::move(codec))
, mPeeker(std::move(peeker))
, mTargetSize(mCodec->getInfo().dimensions())
, mDecodeSize(mTargetSize)
, mDecodeSize(mCodec->codec()->dimensions())
, mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType))
, mUnpremultipliedRequired(false)
, mOutColorSpace(getDefaultColorSpace())
, mSampleSize(1)
{
mTargetSize = swapWidthHeight() ? SkISize { mDecodeSize.height(), mDecodeSize.width() }
: mDecodeSize;
}
SkAlphaType ImageDecoder::getOutAlphaType() const {
@@ -77,7 +78,8 @@ bool ImageDecoder::setTargetSize(int width, int height) {
}
}
SkISize targetSize = { width, height }, decodeSize = targetSize;
SkISize targetSize = { width, height };
SkISize decodeSize = swapWidthHeight() ? SkISize { height, width } : targetSize;
int sampleSize = mCodec->computeSampleSize(&decodeSize);
if (decodeSize != targetSize && mUnpremultipliedRequired && !opaque()) {
@@ -157,6 +159,22 @@ SkImageInfo ImageDecoder::getOutputInfo() const {
return SkImageInfo::Make(size, mOutColorType, getOutAlphaType(), getOutputColorSpace());
}
bool ImageDecoder::swapWidthHeight() const {
return SkEncodedOriginSwapsWidthHeight(mCodec->codec()->getOrigin());
}
int ImageDecoder::width() const {
return swapWidthHeight()
? mCodec->codec()->dimensions().height()
: mCodec->codec()->dimensions().width();
}
int ImageDecoder::height() const {
return swapWidthHeight()
? mCodec->codec()->dimensions().width()
: mCodec->codec()->dimensions().height();
}
bool ImageDecoder::opaque() const {
return mCodec->getInfo().alphaType() == kOpaque_SkAlphaType;
}
@@ -174,7 +192,9 @@ SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) {
// FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380.
SkBitmap tmp;
const bool scale = mDecodeSize != mTargetSize;
if (scale || mCropRect) {
const auto origin = mCodec->codec()->getOrigin();
const bool handleOrigin = origin != kDefault_SkEncodedOrigin;
if (scale || handleOrigin || mCropRect) {
if (!tmp.setInfo(decodeInfo)) {
return SkCodec::kInternalError;
}
@@ -189,7 +209,7 @@ SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) {
options.fSampleSize = mSampleSize;
auto result = mCodec->getAndroidPixels(decodeInfo, decodePixels, decodeRowBytes, &options);
if (scale || mCropRect) {
if (scale || handleOrigin || mCropRect) {
SkBitmap scaledBm;
if (!scaledBm.installPixels(getOutputInfo(), pixels, rowBytes)) {
return SkCodec::kInternalError;
@@ -200,15 +220,27 @@ SkCodec::Result ImageDecoder::decode(void* pixels, size_t rowBytes) {
paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering
SkCanvas canvas(scaledBm, SkCanvas::ColorBehavior::kLegacy);
SkMatrix outputMatrix;
if (mCropRect) {
canvas.translate(-mCropRect->fLeft, -mCropRect->fTop);
}
if (scale) {
float scaleX = (float) mTargetSize.width() / mDecodeSize.width();
float scaleY = (float) mTargetSize.height() / mDecodeSize.height();
canvas.scale(scaleX, scaleY);
outputMatrix.setTranslate(-mCropRect->fLeft, -mCropRect->fTop);
}
int targetWidth = mTargetSize.width();
int targetHeight = mTargetSize.height();
if (handleOrigin) {
outputMatrix.preConcat(SkEncodedOriginToMatrix(origin, targetWidth, targetHeight));
if (SkEncodedOriginSwapsWidthHeight(origin)) {
std::swap(targetWidth, targetHeight);
}
}
if (scale) {
float scaleX = (float) targetWidth / mDecodeSize.width();
float scaleY = (float) targetHeight / mDecodeSize.height();
outputMatrix.preScale(scaleX, scaleY);
}
canvas.setMatrix(outputMatrix);
canvas.drawBitmap(tmp, 0.0f, 0.0f, &paint);
}

View File

@@ -49,7 +49,11 @@ public:
// The size is the final size after scaling and cropping.
SkImageInfo getOutputInfo() const;
int width() const;
int height() const;
bool opaque() const;
bool gray() const;
SkCodec::Result decode(void* pixels, size_t rowBytes);
@@ -68,6 +72,7 @@ private:
SkAlphaType getOutAlphaType() const;
sk_sp<SkColorSpace> getOutputColorSpace() const;
bool swapWidthHeight() const;
};
} // namespace android

View File

@@ -135,19 +135,15 @@ static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream,
return throw_exception(env, kSourceException, "", jexception, source);
}
auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec),
SkAndroidCodec::ExifOrientationBehavior::kRespect);
auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
if (!androidCodec.get()) {
return throw_exception(env, kSourceMalformedData, "", nullptr, source);
}
const auto& info = androidCodec->getInfo();
const int width = info.width();
const int height = info.height();
const bool isNinePatch = peeker->mPatch != nullptr;
ImageDecoder* decoder = new ImageDecoder(std::move(androidCodec), std::move(peeker));
return env->NewObject(gImageDecoder_class, gImageDecoder_constructorMethodID,
reinterpret_cast<jlong>(decoder), width, height,
reinterpret_cast<jlong>(decoder), decoder->width(), decoder->height(),
animated, isNinePatch);
}

View File

@@ -65,17 +65,18 @@ static int createFromStream(std::unique_ptr<SkStreamRewindable> stream, AImageDe
SkCodec::Result result;
auto codec = SkCodec::MakeFromStream(std::move(stream), &result, nullptr,
SkCodec::SelectionPolicy::kPreferAnimation);
auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec),
SkAndroidCodec::ExifOrientationBehavior::kRespect);
// These may be swapped due to the SkEncodedOrigin, but we're just checking
// them to make sure they fit in int32_t.
auto dimensions = codec->dimensions();
auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
if (!androidCodec) {
return ResultToErrorCode(result);
}
// AImageDecoderHeaderInfo_getWidth/Height return an int32_t. Ensure that
// the conversion is safe.
const auto& info = androidCodec->getInfo();
if (info.width() > std::numeric_limits<int32_t>::max()
|| info.height() > std::numeric_limits<int32_t>::max()) {
if (dimensions.width() > std::numeric_limits<int32_t>::max() ||
dimensions.height() > std::numeric_limits<int32_t>::max()) {
return ANDROID_IMAGE_DECODER_INVALID_INPUT;
}
@@ -200,14 +201,14 @@ int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo* info) {
if (!info) {
return 0;
}
return toDecoder(info)->mCodec->getInfo().width();
return toDecoder(info)->width();
}
int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo* info) {
if (!info) {
return 0;
}
return toDecoder(info)->mCodec->getInfo().height();
return toDecoder(info)->height();
}
const char* AImageDecoderHeaderInfo_getMimeType(const AImageDecoderHeaderInfo* info) {