blob: 2eb4fde6763a2d3d57cd311ff0c3bb67b6643978 [file] [log] [blame]
/*
* Copyright 2023 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkAlphaType.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkColorType.h"
#include "include/core/SkData.h"
#include "include/core/SkImage.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkPixmap.h"
#include "include/core/SkRefCnt.h"
#include "include/private/base/SkMath.h"
#include "src/core/SkCompressedDataUtils.h"
#include "src/core/SkImageFilterTypes.h"
#include "src/core/SkImageFilter_Base.h"
#include "src/core/SkImagePriv.h"
#include "src/image/SkImage_Base.h"
#include "src/image/SkImage_Raster.h"
#include <cstddef>
#include <cstdint>
#include <utility>
class SkImageFilter;
struct SkIPoint;
struct SkIRect;
enum class SkTextureCompressionType;
static bool valid_args(const SkImageInfo& info, size_t rowBytes, size_t* minSize) {
const int maxDimension = SK_MaxS32 >> 2;
// TODO(mtklein): eliminate anything here that setInfo() has already checked.
SkBitmap b;
if (!b.setInfo(info, rowBytes)) {
return false;
}
if (info.width() <= 0 || info.height() <= 0) {
return false;
}
if (info.width() > maxDimension || info.height() > maxDimension) {
return false;
}
if ((unsigned)info.colorType() > (unsigned)kLastEnum_SkColorType) {
return false;
}
if ((unsigned)info.alphaType() > (unsigned)kLastEnum_SkAlphaType) {
return false;
}
if (kUnknown_SkColorType == info.colorType()) {
return false;
}
if (!info.validRowBytes(rowBytes)) {
return false;
}
size_t size = info.computeByteSize(rowBytes);
if (SkImageInfo::ByteSizeOverflowed(size)) {
return false;
}
if (minSize) {
*minSize = size;
}
return true;
}
namespace SkImages {
sk_sp<SkImage> RasterFromBitmap(const SkBitmap& bm) {
if (!bm.pixelRef()) {
return nullptr;
}
return SkMakeImageFromRasterBitmap(bm, kIfMutable_SkCopyPixelsMode);
}
sk_sp<SkImage> RasterFromPixmapCopy(const SkPixmap& pmap) {
return MakeRasterCopyPriv(pmap, kNeedNewImageUniqueID);
}
sk_sp<SkImage> RasterFromData(const SkImageInfo& info, sk_sp<SkData> data, size_t rowBytes) {
size_t size;
if (!valid_args(info, rowBytes, &size) || !data) {
return nullptr;
}
// did they give us enough data?
if (data->size() < size) {
return nullptr;
}
return sk_make_sp<SkImage_Raster>(info, std::move(data), rowBytes);
}
sk_sp<SkImage> MakeWithFilter(sk_sp<SkImage> src,
const SkImageFilter* filter,
const SkIRect& subset,
const SkIRect& clipBounds,
SkIRect* outSubset,
SkIPoint* offset) {
if (!src || !filter) {
return nullptr;
}
sk_sp<skif::Backend> backend = skif::MakeRasterBackend({}, src->colorType());
return as_IFB(filter)->makeImageWithFilter(std::move(backend),
std::move(src),
subset,
clipBounds,
outSubset,
offset);
}
// TODO: this could be improved to decode and make use of the mipmap
// levels potentially present in the compressed data. For now, any
// mipmap levels are discarded.
sk_sp<SkImage> RasterFromCompressedTextureData(sk_sp<SkData> data,
int width,
int height,
SkTextureCompressionType type) {
size_t expectedSize = SkCompressedFormatDataSize(type, {width, height}, false);
if (!data || data->size() < expectedSize) {
return nullptr;
}
SkAlphaType at =
SkTextureCompressionTypeIsOpaque(type) ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
SkImageInfo ii = SkImageInfo::MakeN32(width, height, at);
if (!valid_args(ii, ii.minRowBytes(), nullptr)) {
return nullptr;
}
SkBitmap bitmap;
if (!bitmap.tryAllocPixels(ii)) {
return nullptr;
}
if (!SkDecompress(std::move(data), {width, height}, type, &bitmap)) {
return nullptr;
}
bitmap.setImmutable();
return RasterFromBitmap(bitmap);
}
sk_sp<SkImage> RasterFromPixmap(const SkPixmap& pmap, RasterReleaseProc proc, ReleaseContext ctx) {
size_t size;
if (!valid_args(pmap.info(), pmap.rowBytes(), &size) || !pmap.addr()) {
return nullptr;
}
sk_sp<SkData> data(SkData::MakeWithProc(pmap.addr(), size, proc, ctx));
return sk_make_sp<SkImage_Raster>(pmap.info(), std::move(data), pmap.rowBytes());
}
} // namespace SkImages
sk_sp<SkImage> MakeRasterCopyPriv(const SkPixmap& pmap, uint32_t id) {
size_t size;
if (!valid_args(pmap.info(), pmap.rowBytes(), &size) || !pmap.addr()) {
return nullptr;
}
// Here we actually make a copy of the caller's pixel data
sk_sp<SkData> data(SkData::MakeWithCopy(pmap.addr(), size));
return sk_make_sp<SkImage_Raster>(pmap.info(), std::move(data), pmap.rowBytes(), id);
}