Avoid depending on STB in //flutter/impeller/image. (flutter/engine#47)

This commit is contained in:
Chinmay Garde 2022-02-28 16:01:18 -08:00 committed by Dan Field
parent 85f5829abb
commit f5f03da988
6 changed files with 135 additions and 82 deletions

View File

@ -5,14 +5,19 @@
import("//flutter/impeller/tools/impeller.gni")
impeller_component("image") {
sources = [
"compressed_image.cc",
public = [
"compressed_image.h",
"decompressed_image.cc",
"decompressed_image.h",
]
deps = [ "../third_party/stb" ]
sources = [
"backends/skia/compressed_image_skia.cc",
"backends/skia/compressed_image_skia.h",
"compressed_image.cc",
"decompressed_image.cc",
]
deps = [ "//third_party/skia" ]
public_deps = [
"../base",

View File

@ -0,0 +1,73 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "impeller/image/backends/skia/compressed_image_skia.h"
#include <memory>
#include "impeller/base/validation.h"
#include "third_party/skia/include/core/SkData.h"
#include "third_party/skia/include/core/SkImageGenerator.h"
#include "third_party/skia/include/core/SkPixmap.h"
namespace impeller {
CompressedImageSkia::CompressedImageSkia(
std::shared_ptr<const fml::Mapping> allocation)
: CompressedImage(std::move(allocation)) {}
CompressedImageSkia::~CompressedImageSkia() = default;
// |CompressedImage|
DecompressedImage CompressedImageSkia::Decode() const {
if (!IsValid()) {
return {};
}
if (source_->GetSize() == 0u) {
return {};
}
auto src = new std::shared_ptr<const fml::Mapping>(source_);
auto sk_data = SkData::MakeWithProc(
source_->GetMapping(), source_->GetSize(),
[](const void* ptr, void* context) {
delete reinterpret_cast<decltype(src)>(context);
},
src);
auto generator = SkImageGenerator::MakeFromEncoded(sk_data);
if (!generator) {
return {};
}
auto info = SkImageInfo::MakeN32Premul(generator->getInfo().dimensions());
auto bitmap = std::make_shared<SkBitmap>();
if (!bitmap->tryAllocPixels(info)) {
VALIDATION_LOG << "Could not allocate arena for decompressing image.";
return {};
}
if (!generator->getPixels(bitmap->pixmap())) {
VALIDATION_LOG << "Could not decompress image into arena.";
return {};
}
auto mapping = std::make_shared<fml::NonOwnedMapping>(
reinterpret_cast<const uint8_t*>(bitmap->pixmap().addr()), // data
bitmap->pixmap().rowBytes() * bitmap->pixmap().height(), // size
[bitmap](const uint8_t* data, size_t size) mutable {
bitmap.reset();
} // proc
);
return {
{bitmap->pixmap().dimensions().fWidth,
bitmap->pixmap().dimensions().fHeight}, // size
DecompressedImage::Format::kRGBA, // format
mapping // allocation
};
}
} // namespace impeller

View File

@ -0,0 +1,25 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include "flutter/fml/macros.h"
#include "impeller/image/compressed_image.h"
namespace impeller {
class CompressedImageSkia final : public CompressedImage {
public:
CompressedImageSkia(std::shared_ptr<const fml::Mapping> allocation);
~CompressedImageSkia() override;
// |CompressedImage|
DecompressedImage Decode() const override;
private:
FML_DISALLOW_COPY_AND_ASSIGN(CompressedImageSkia);
};
} // namespace impeller

View File

@ -4,83 +4,24 @@
#include "impeller/image/compressed_image.h"
#include <stb_image.h>
#include "impeller/base/validation.h"
#include "impeller/image/backends/skia/compressed_image_skia.h"
namespace impeller {
CompressedImage::CompressedImage(
std::shared_ptr<const fml::Mapping> sourceAllocation)
: source_(std::move(sourceAllocation)) {}
std::shared_ptr<CompressedImage> CompressedImage::Create(
std::shared_ptr<const fml::Mapping> allocation) {
// There is only one backend today.
if (!allocation) {
return nullptr;
}
return std::make_shared<CompressedImageSkia>(std::move(allocation));
}
CompressedImage::CompressedImage(std::shared_ptr<const fml::Mapping> allocation)
: source_(std::move(allocation)) {}
CompressedImage::~CompressedImage() = default;
DecompressedImage CompressedImage::Decode() const {
if (!source_) {
return {};
}
int width = 0;
int height = 0;
int comps = 0;
stbi_uc* decoded =
::stbi_load_from_memory(source_->GetMapping(), // Source Data
source_->GetSize(), // Source Data Size
&width, // Out: Width
&height, // Out: Height
&comps, // Out: Components
STBI_default);
if (decoded == nullptr) {
VALIDATION_LOG << "Could not decode image from host memory.";
return {};
}
auto dest_allocation = std::make_shared<const fml::NonOwnedMapping>(
decoded, // bytes
width * height * comps * sizeof(stbi_uc), // byte size
[](const uint8_t* data, size_t size) {
::stbi_image_free(const_cast<uint8_t*>(data));
} // release proc
);
/*
* Make sure we got a valid component set.
*/
auto components = DecompressedImage::Format::kInvalid;
switch (comps) {
case STBI_grey:
components = DecompressedImage::Format::kGrey;
break;
case STBI_grey_alpha:
components = DecompressedImage::Format::kGreyAlpha;
break;
case STBI_rgb:
components = DecompressedImage::Format::kRGB;
break;
case STBI_rgb_alpha:
components = DecompressedImage::Format::kRGBA;
break;
default:
components = DecompressedImage::Format::kInvalid;
break;
}
if (components == DecompressedImage::Format::kInvalid) {
VALIDATION_LOG << "Could not detect image components when decoding.";
return {};
}
return DecompressedImage{
ISize{width, height}, // size
components, // components
std::move(dest_allocation) // allocation
};
}
bool CompressedImage::IsValid() const {
return static_cast<bool>(source_);
}

View File

@ -4,6 +4,8 @@
#pragma once
#include <memory>
#include "flutter/fml/macros.h"
#include "flutter/fml/mapping.h"
#include "impeller/geometry/size.h"
@ -15,16 +17,19 @@ class ImageSource;
class CompressedImage {
public:
CompressedImage(std::shared_ptr<const fml::Mapping> sourceAllocation);
static std::shared_ptr<CompressedImage> Create(
std::shared_ptr<const fml::Mapping> allocation);
~CompressedImage();
virtual ~CompressedImage();
[[nodiscard]] DecompressedImage Decode() const;
[[nodiscard]] virtual DecompressedImage Decode() const = 0;
bool IsValid() const;
private:
std::shared_ptr<const fml::Mapping> source_;
protected:
const std::shared_ptr<const fml::Mapping> source_;
CompressedImage(std::shared_ptr<const fml::Mapping> allocation);
};
} // namespace impeller

View File

@ -195,14 +195,18 @@ bool Playground::OpenPlaygroundHere(Renderer::RenderCallback render_callback) {
std::shared_ptr<Texture> Playground::CreateTextureForFixture(
const char* fixture_name) const {
CompressedImage compressed_image(
auto compressed_image = CompressedImage::Create(
flutter::testing::OpenFixtureAsMapping(fixture_name));
if (!compressed_image) {
VALIDATION_LOG << "Could not create compressed image.";
return nullptr;
}
// The decoded image is immediately converted into RGBA as that format is
// known to be supported everywhere. For image sources that don't need 32
// bit pixel strides, this is overkill. Since this is a test fixture we
// aren't necessarily trying to eke out memory savings here and instead
// favor simplicity.
auto image = compressed_image.Decode().ConvertToRGBA();
auto image = compressed_image->Decode().ConvertToRGBA();
if (!image.IsValid()) {
VALIDATION_LOG << "Could not find fixture named " << fixture_name;
return nullptr;