1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2015 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "src/codec/SkWebpCodec.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "include/codec/SkCodecAnimation.h" 11cb93a386Sopenharmony_ci#include "include/core/SkBitmap.h" 12cb93a386Sopenharmony_ci#include "include/core/SkCanvas.h" 13cb93a386Sopenharmony_ci#include "include/private/SkTemplates.h" 14cb93a386Sopenharmony_ci#include "include/private/SkTo.h" 15cb93a386Sopenharmony_ci#include "src/codec/SkCodecPriv.h" 16cb93a386Sopenharmony_ci#include "src/codec/SkParseEncodedOrigin.h" 17cb93a386Sopenharmony_ci#include "src/codec/SkSampler.h" 18cb93a386Sopenharmony_ci#include "src/core/SkRasterPipeline.h" 19cb93a386Sopenharmony_ci#include "src/core/SkStreamPriv.h" 20cb93a386Sopenharmony_ci 21cb93a386Sopenharmony_ci// A WebP decoder on top of (subset of) libwebp 22cb93a386Sopenharmony_ci// For more information on WebP image format, and libwebp library, see: 23cb93a386Sopenharmony_ci// https://code.google.com/speed/webp/ 24cb93a386Sopenharmony_ci// http://www.webmproject.org/code/#libwebp-webp-image-library 25cb93a386Sopenharmony_ci// https://chromium.googlesource.com/webm/libwebp 26cb93a386Sopenharmony_ci 27cb93a386Sopenharmony_ci// If moving libwebp out of skia source tree, path for webp headers must be 28cb93a386Sopenharmony_ci// updated accordingly. Here, we enforce using local copy in webp sub-directory. 29cb93a386Sopenharmony_ci#include "webp/decode.h" 30cb93a386Sopenharmony_ci#include "webp/demux.h" 31cb93a386Sopenharmony_ci#include "webp/encode.h" 32cb93a386Sopenharmony_ci 33cb93a386Sopenharmony_cibool SkWebpCodec::IsWebp(const void* buf, size_t bytesRead) { 34cb93a386Sopenharmony_ci // WEBP starts with the following: 35cb93a386Sopenharmony_ci // RIFFXXXXWEBPVP 36cb93a386Sopenharmony_ci // Where XXXX is unspecified. 37cb93a386Sopenharmony_ci const char* bytes = static_cast<const char*>(buf); 38cb93a386Sopenharmony_ci return bytesRead >= 14 && !memcmp(bytes, "RIFF", 4) && !memcmp(&bytes[8], "WEBPVP", 6); 39cb93a386Sopenharmony_ci} 40cb93a386Sopenharmony_ci 41cb93a386Sopenharmony_ci// Parse headers of RIFF container, and check for valid Webp (VP8) content. 42cb93a386Sopenharmony_ci// Returns an SkWebpCodec on success 43cb93a386Sopenharmony_cistd::unique_ptr<SkCodec> SkWebpCodec::MakeFromStream(std::unique_ptr<SkStream> stream, 44cb93a386Sopenharmony_ci Result* result) { 45cb93a386Sopenharmony_ci // Webp demux needs a contiguous data buffer. 46cb93a386Sopenharmony_ci sk_sp<SkData> data = nullptr; 47cb93a386Sopenharmony_ci if (stream->getMemoryBase()) { 48cb93a386Sopenharmony_ci // It is safe to make without copy because we'll hold onto the stream. 49cb93a386Sopenharmony_ci data = SkData::MakeWithoutCopy(stream->getMemoryBase(), stream->getLength()); 50cb93a386Sopenharmony_ci } else { 51cb93a386Sopenharmony_ci data = SkCopyStreamToData(stream.get()); 52cb93a386Sopenharmony_ci 53cb93a386Sopenharmony_ci // If we are forced to copy the stream to a data, we can go ahead and delete the stream. 54cb93a386Sopenharmony_ci stream.reset(nullptr); 55cb93a386Sopenharmony_ci } 56cb93a386Sopenharmony_ci 57cb93a386Sopenharmony_ci // It's a little strange that the |demux| will outlive |webpData|, though it needs the 58cb93a386Sopenharmony_ci // pointer in |webpData| to remain valid. This works because the pointer remains valid 59cb93a386Sopenharmony_ci // until the SkData is freed. 60cb93a386Sopenharmony_ci WebPData webpData = { data->bytes(), data->size() }; 61cb93a386Sopenharmony_ci WebPDemuxState state; 62cb93a386Sopenharmony_ci SkAutoTCallVProc<WebPDemuxer, WebPDemuxDelete> demux(WebPDemuxPartial(&webpData, &state)); 63cb93a386Sopenharmony_ci switch (state) { 64cb93a386Sopenharmony_ci case WEBP_DEMUX_PARSE_ERROR: 65cb93a386Sopenharmony_ci *result = kInvalidInput; 66cb93a386Sopenharmony_ci return nullptr; 67cb93a386Sopenharmony_ci case WEBP_DEMUX_PARSING_HEADER: 68cb93a386Sopenharmony_ci *result = kIncompleteInput; 69cb93a386Sopenharmony_ci return nullptr; 70cb93a386Sopenharmony_ci case WEBP_DEMUX_PARSED_HEADER: 71cb93a386Sopenharmony_ci case WEBP_DEMUX_DONE: 72cb93a386Sopenharmony_ci SkASSERT(demux); 73cb93a386Sopenharmony_ci break; 74cb93a386Sopenharmony_ci } 75cb93a386Sopenharmony_ci 76cb93a386Sopenharmony_ci const int width = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH); 77cb93a386Sopenharmony_ci const int height = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT); 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_ci // Validate the image size that's about to be decoded. 80cb93a386Sopenharmony_ci { 81cb93a386Sopenharmony_ci const int64_t size = sk_64_mul(width, height); 82cb93a386Sopenharmony_ci // now check that if we are 4-bytes per pixel, we also don't overflow 83cb93a386Sopenharmony_ci if (!SkTFitsIn<int32_t>(size) || SkTo<int32_t>(size) > (0x7FFFFFFF >> 2)) { 84cb93a386Sopenharmony_ci *result = kInvalidInput; 85cb93a386Sopenharmony_ci return nullptr; 86cb93a386Sopenharmony_ci } 87cb93a386Sopenharmony_ci } 88cb93a386Sopenharmony_ci 89cb93a386Sopenharmony_ci std::unique_ptr<SkEncodedInfo::ICCProfile> profile = nullptr; 90cb93a386Sopenharmony_ci { 91cb93a386Sopenharmony_ci WebPChunkIterator chunkIterator; 92cb93a386Sopenharmony_ci SkAutoTCallVProc<WebPChunkIterator, WebPDemuxReleaseChunkIterator> autoCI(&chunkIterator); 93cb93a386Sopenharmony_ci if (WebPDemuxGetChunk(demux, "ICCP", 1, &chunkIterator)) { 94cb93a386Sopenharmony_ci // FIXME: I think this could be MakeWithoutCopy 95cb93a386Sopenharmony_ci auto chunk = SkData::MakeWithCopy(chunkIterator.chunk.bytes, chunkIterator.chunk.size); 96cb93a386Sopenharmony_ci profile = SkEncodedInfo::ICCProfile::Make(std::move(chunk)); 97cb93a386Sopenharmony_ci } 98cb93a386Sopenharmony_ci if (profile && profile->profile()->data_color_space != skcms_Signature_RGB) { 99cb93a386Sopenharmony_ci profile = nullptr; 100cb93a386Sopenharmony_ci } 101cb93a386Sopenharmony_ci } 102cb93a386Sopenharmony_ci 103cb93a386Sopenharmony_ci SkEncodedOrigin origin = kDefault_SkEncodedOrigin; 104cb93a386Sopenharmony_ci { 105cb93a386Sopenharmony_ci WebPChunkIterator chunkIterator; 106cb93a386Sopenharmony_ci SkAutoTCallVProc<WebPChunkIterator, WebPDemuxReleaseChunkIterator> autoCI(&chunkIterator); 107cb93a386Sopenharmony_ci if (WebPDemuxGetChunk(demux, "EXIF", 1, &chunkIterator)) { 108cb93a386Sopenharmony_ci SkParseEncodedOrigin(chunkIterator.chunk.bytes, chunkIterator.chunk.size, &origin); 109cb93a386Sopenharmony_ci } 110cb93a386Sopenharmony_ci } 111cb93a386Sopenharmony_ci 112cb93a386Sopenharmony_ci // Get the first frame and its "features" to determine the color and alpha types. 113cb93a386Sopenharmony_ci WebPIterator frame; 114cb93a386Sopenharmony_ci SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame); 115cb93a386Sopenharmony_ci if (!WebPDemuxGetFrame(demux, 1, &frame)) { 116cb93a386Sopenharmony_ci *result = kIncompleteInput; 117cb93a386Sopenharmony_ci return nullptr; 118cb93a386Sopenharmony_ci } 119cb93a386Sopenharmony_ci 120cb93a386Sopenharmony_ci WebPBitstreamFeatures features; 121cb93a386Sopenharmony_ci switch (WebPGetFeatures(frame.fragment.bytes, frame.fragment.size, &features)) { 122cb93a386Sopenharmony_ci case VP8_STATUS_OK: 123cb93a386Sopenharmony_ci break; 124cb93a386Sopenharmony_ci case VP8_STATUS_SUSPENDED: 125cb93a386Sopenharmony_ci case VP8_STATUS_NOT_ENOUGH_DATA: 126cb93a386Sopenharmony_ci *result = kIncompleteInput; 127cb93a386Sopenharmony_ci return nullptr; 128cb93a386Sopenharmony_ci default: 129cb93a386Sopenharmony_ci *result = kInvalidInput; 130cb93a386Sopenharmony_ci return nullptr; 131cb93a386Sopenharmony_ci } 132cb93a386Sopenharmony_ci 133cb93a386Sopenharmony_ci const bool hasAlpha = SkToBool(frame.has_alpha) 134cb93a386Sopenharmony_ci || frame.width != width || frame.height != height; 135cb93a386Sopenharmony_ci SkEncodedInfo::Color color; 136cb93a386Sopenharmony_ci SkEncodedInfo::Alpha alpha; 137cb93a386Sopenharmony_ci switch (features.format) { 138cb93a386Sopenharmony_ci case 0: 139cb93a386Sopenharmony_ci // This indicates a "mixed" format. We could see this for 140cb93a386Sopenharmony_ci // animated webps (multiple fragments). 141cb93a386Sopenharmony_ci // We could also guess kYUV here, but I think it makes more 142cb93a386Sopenharmony_ci // sense to guess kBGRA which is likely closer to the final 143cb93a386Sopenharmony_ci // output. Otherwise, we might end up converting 144cb93a386Sopenharmony_ci // BGRA->YUVA->BGRA. 145cb93a386Sopenharmony_ci [[fallthrough]]; 146cb93a386Sopenharmony_ci case 2: 147cb93a386Sopenharmony_ci // This is the lossless format (BGRA). 148cb93a386Sopenharmony_ci if (hasAlpha) { 149cb93a386Sopenharmony_ci color = SkEncodedInfo::kBGRA_Color; 150cb93a386Sopenharmony_ci alpha = SkEncodedInfo::kUnpremul_Alpha; 151cb93a386Sopenharmony_ci } else { 152cb93a386Sopenharmony_ci color = SkEncodedInfo::kBGRX_Color; 153cb93a386Sopenharmony_ci alpha = SkEncodedInfo::kOpaque_Alpha; 154cb93a386Sopenharmony_ci } 155cb93a386Sopenharmony_ci break; 156cb93a386Sopenharmony_ci case 1: 157cb93a386Sopenharmony_ci // This is the lossy format (YUV). 158cb93a386Sopenharmony_ci if (hasAlpha) { 159cb93a386Sopenharmony_ci color = SkEncodedInfo::kYUVA_Color; 160cb93a386Sopenharmony_ci alpha = SkEncodedInfo::kUnpremul_Alpha; 161cb93a386Sopenharmony_ci } else { 162cb93a386Sopenharmony_ci color = SkEncodedInfo::kYUV_Color; 163cb93a386Sopenharmony_ci alpha = SkEncodedInfo::kOpaque_Alpha; 164cb93a386Sopenharmony_ci } 165cb93a386Sopenharmony_ci break; 166cb93a386Sopenharmony_ci default: 167cb93a386Sopenharmony_ci *result = kInvalidInput; 168cb93a386Sopenharmony_ci return nullptr; 169cb93a386Sopenharmony_ci } 170cb93a386Sopenharmony_ci 171cb93a386Sopenharmony_ci 172cb93a386Sopenharmony_ci *result = kSuccess; 173cb93a386Sopenharmony_ci SkEncodedInfo info = SkEncodedInfo::Make(width, height, color, alpha, 8, std::move(profile)); 174cb93a386Sopenharmony_ci return std::unique_ptr<SkCodec>(new SkWebpCodec(std::move(info), std::move(stream), 175cb93a386Sopenharmony_ci demux.release(), std::move(data), origin)); 176cb93a386Sopenharmony_ci} 177cb93a386Sopenharmony_ci 178cb93a386Sopenharmony_cistatic WEBP_CSP_MODE webp_decode_mode(SkColorType dstCT, bool premultiply) { 179cb93a386Sopenharmony_ci switch (dstCT) { 180cb93a386Sopenharmony_ci case kBGRA_8888_SkColorType: 181cb93a386Sopenharmony_ci return premultiply ? MODE_bgrA : MODE_BGRA; 182cb93a386Sopenharmony_ci case kRGBA_8888_SkColorType: 183cb93a386Sopenharmony_ci return premultiply ? MODE_rgbA : MODE_RGBA; 184cb93a386Sopenharmony_ci case kRGB_565_SkColorType: 185cb93a386Sopenharmony_ci return MODE_RGB_565; 186cb93a386Sopenharmony_ci default: 187cb93a386Sopenharmony_ci return MODE_LAST; 188cb93a386Sopenharmony_ci } 189cb93a386Sopenharmony_ci} 190cb93a386Sopenharmony_ci 191cb93a386Sopenharmony_ciSkWebpCodec::Frame* SkWebpCodec::FrameHolder::appendNewFrame(bool hasAlpha) { 192cb93a386Sopenharmony_ci const int i = this->size(); 193cb93a386Sopenharmony_ci fFrames.emplace_back(i, hasAlpha ? SkEncodedInfo::kUnpremul_Alpha 194cb93a386Sopenharmony_ci : SkEncodedInfo::kOpaque_Alpha); 195cb93a386Sopenharmony_ci return &fFrames[i]; 196cb93a386Sopenharmony_ci} 197cb93a386Sopenharmony_ci 198cb93a386Sopenharmony_cibool SkWebpCodec::onGetValidSubset(SkIRect* desiredSubset) const { 199cb93a386Sopenharmony_ci if (!desiredSubset) { 200cb93a386Sopenharmony_ci return false; 201cb93a386Sopenharmony_ci } 202cb93a386Sopenharmony_ci 203cb93a386Sopenharmony_ci if (!this->bounds().contains(*desiredSubset)) { 204cb93a386Sopenharmony_ci return false; 205cb93a386Sopenharmony_ci } 206cb93a386Sopenharmony_ci 207cb93a386Sopenharmony_ci // As stated below, libwebp snaps to even left and top. Make sure top and left are even, so we 208cb93a386Sopenharmony_ci // decode this exact subset. 209cb93a386Sopenharmony_ci // Leave right and bottom unmodified, so we suggest a slightly larger subset than requested. 210cb93a386Sopenharmony_ci desiredSubset->fLeft = (desiredSubset->fLeft >> 1) << 1; 211cb93a386Sopenharmony_ci desiredSubset->fTop = (desiredSubset->fTop >> 1) << 1; 212cb93a386Sopenharmony_ci return true; 213cb93a386Sopenharmony_ci} 214cb93a386Sopenharmony_ci 215cb93a386Sopenharmony_ciint SkWebpCodec::onGetRepetitionCount() { 216cb93a386Sopenharmony_ci auto flags = WebPDemuxGetI(fDemux.get(), WEBP_FF_FORMAT_FLAGS); 217cb93a386Sopenharmony_ci if (!(flags & ANIMATION_FLAG)) { 218cb93a386Sopenharmony_ci return 0; 219cb93a386Sopenharmony_ci } 220cb93a386Sopenharmony_ci 221cb93a386Sopenharmony_ci int loopCount = WebPDemuxGetI(fDemux.get(), WEBP_FF_LOOP_COUNT); 222cb93a386Sopenharmony_ci if (0 == loopCount) { 223cb93a386Sopenharmony_ci return kRepetitionCountInfinite; 224cb93a386Sopenharmony_ci } 225cb93a386Sopenharmony_ci 226cb93a386Sopenharmony_ci loopCount--; 227cb93a386Sopenharmony_ci return loopCount; 228cb93a386Sopenharmony_ci} 229cb93a386Sopenharmony_ci 230cb93a386Sopenharmony_ciint SkWebpCodec::onGetFrameCount() { 231cb93a386Sopenharmony_ci auto flags = WebPDemuxGetI(fDemux.get(), WEBP_FF_FORMAT_FLAGS); 232cb93a386Sopenharmony_ci if (!(flags & ANIMATION_FLAG)) { 233cb93a386Sopenharmony_ci return 1; 234cb93a386Sopenharmony_ci } 235cb93a386Sopenharmony_ci 236cb93a386Sopenharmony_ci const uint32_t oldFrameCount = fFrameHolder.size(); 237cb93a386Sopenharmony_ci if (fFailed) { 238cb93a386Sopenharmony_ci return oldFrameCount; 239cb93a386Sopenharmony_ci } 240cb93a386Sopenharmony_ci 241cb93a386Sopenharmony_ci const uint32_t frameCount = WebPDemuxGetI(fDemux, WEBP_FF_FRAME_COUNT); 242cb93a386Sopenharmony_ci if (oldFrameCount == frameCount) { 243cb93a386Sopenharmony_ci // We have already parsed this. 244cb93a386Sopenharmony_ci return frameCount; 245cb93a386Sopenharmony_ci } 246cb93a386Sopenharmony_ci 247cb93a386Sopenharmony_ci fFrameHolder.reserve(frameCount); 248cb93a386Sopenharmony_ci 249cb93a386Sopenharmony_ci for (uint32_t i = oldFrameCount; i < frameCount; i++) { 250cb93a386Sopenharmony_ci WebPIterator iter; 251cb93a386Sopenharmony_ci SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoIter(&iter); 252cb93a386Sopenharmony_ci 253cb93a386Sopenharmony_ci if (!WebPDemuxGetFrame(fDemux.get(), i + 1, &iter)) { 254cb93a386Sopenharmony_ci fFailed = true; 255cb93a386Sopenharmony_ci break; 256cb93a386Sopenharmony_ci } 257cb93a386Sopenharmony_ci 258cb93a386Sopenharmony_ci // libwebp only reports complete frames of an animated image. 259cb93a386Sopenharmony_ci SkASSERT(iter.complete); 260cb93a386Sopenharmony_ci 261cb93a386Sopenharmony_ci Frame* frame = fFrameHolder.appendNewFrame(iter.has_alpha); 262cb93a386Sopenharmony_ci frame->setXYWH(iter.x_offset, iter.y_offset, iter.width, iter.height); 263cb93a386Sopenharmony_ci frame->setDisposalMethod(iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ? 264cb93a386Sopenharmony_ci SkCodecAnimation::DisposalMethod::kRestoreBGColor : 265cb93a386Sopenharmony_ci SkCodecAnimation::DisposalMethod::kKeep); 266cb93a386Sopenharmony_ci frame->setDuration(iter.duration); 267cb93a386Sopenharmony_ci if (WEBP_MUX_BLEND != iter.blend_method) { 268cb93a386Sopenharmony_ci frame->setBlend(SkCodecAnimation::Blend::kSrc); 269cb93a386Sopenharmony_ci } 270cb93a386Sopenharmony_ci fFrameHolder.setAlphaAndRequiredFrame(frame); 271cb93a386Sopenharmony_ci } 272cb93a386Sopenharmony_ci 273cb93a386Sopenharmony_ci return fFrameHolder.size(); 274cb93a386Sopenharmony_ci 275cb93a386Sopenharmony_ci} 276cb93a386Sopenharmony_ci 277cb93a386Sopenharmony_ciconst SkFrame* SkWebpCodec::FrameHolder::onGetFrame(int i) const { 278cb93a386Sopenharmony_ci return static_cast<const SkFrame*>(this->frame(i)); 279cb93a386Sopenharmony_ci} 280cb93a386Sopenharmony_ci 281cb93a386Sopenharmony_ciconst SkWebpCodec::Frame* SkWebpCodec::FrameHolder::frame(int i) const { 282cb93a386Sopenharmony_ci SkASSERT(i >= 0 && i < this->size()); 283cb93a386Sopenharmony_ci return &fFrames[i]; 284cb93a386Sopenharmony_ci} 285cb93a386Sopenharmony_ci 286cb93a386Sopenharmony_cibool SkWebpCodec::onGetFrameInfo(int i, FrameInfo* frameInfo) const { 287cb93a386Sopenharmony_ci if (i >= fFrameHolder.size()) { 288cb93a386Sopenharmony_ci return false; 289cb93a386Sopenharmony_ci } 290cb93a386Sopenharmony_ci 291cb93a386Sopenharmony_ci const Frame* frame = fFrameHolder.frame(i); 292cb93a386Sopenharmony_ci if (!frame) { 293cb93a386Sopenharmony_ci return false; 294cb93a386Sopenharmony_ci } 295cb93a386Sopenharmony_ci 296cb93a386Sopenharmony_ci if (frameInfo) { 297cb93a386Sopenharmony_ci // libwebp only reports fully received frames for an 298cb93a386Sopenharmony_ci // animated image. 299cb93a386Sopenharmony_ci frame->fillIn(frameInfo, true); 300cb93a386Sopenharmony_ci } 301cb93a386Sopenharmony_ci 302cb93a386Sopenharmony_ci return true; 303cb93a386Sopenharmony_ci} 304cb93a386Sopenharmony_ci 305cb93a386Sopenharmony_cistatic bool is_8888(SkColorType colorType) { 306cb93a386Sopenharmony_ci switch (colorType) { 307cb93a386Sopenharmony_ci case kRGBA_8888_SkColorType: 308cb93a386Sopenharmony_ci case kBGRA_8888_SkColorType: 309cb93a386Sopenharmony_ci return true; 310cb93a386Sopenharmony_ci default: 311cb93a386Sopenharmony_ci return false; 312cb93a386Sopenharmony_ci } 313cb93a386Sopenharmony_ci} 314cb93a386Sopenharmony_ci 315cb93a386Sopenharmony_ci// Requires that the src input be unpremultiplied (or opaque). 316cb93a386Sopenharmony_cistatic void blend_line(SkColorType dstCT, void* dst, 317cb93a386Sopenharmony_ci SkColorType srcCT, const void* src, 318cb93a386Sopenharmony_ci SkAlphaType dstAt, 319cb93a386Sopenharmony_ci bool srcHasAlpha, 320cb93a386Sopenharmony_ci int width) { 321cb93a386Sopenharmony_ci SkRasterPipeline_MemoryCtx dst_ctx = { (void*)dst, 0 }, 322cb93a386Sopenharmony_ci src_ctx = { (void*)src, 0 }; 323cb93a386Sopenharmony_ci 324cb93a386Sopenharmony_ci SkRasterPipeline_<256> p; 325cb93a386Sopenharmony_ci 326cb93a386Sopenharmony_ci p.append_load_dst(dstCT, &dst_ctx); 327cb93a386Sopenharmony_ci if (kUnpremul_SkAlphaType == dstAt) { 328cb93a386Sopenharmony_ci p.append(SkRasterPipeline::premul_dst); 329cb93a386Sopenharmony_ci } 330cb93a386Sopenharmony_ci 331cb93a386Sopenharmony_ci p.append_load(srcCT, &src_ctx); 332cb93a386Sopenharmony_ci if (srcHasAlpha) { 333cb93a386Sopenharmony_ci p.append(SkRasterPipeline::premul); 334cb93a386Sopenharmony_ci } 335cb93a386Sopenharmony_ci 336cb93a386Sopenharmony_ci p.append(SkRasterPipeline::srcover); 337cb93a386Sopenharmony_ci 338cb93a386Sopenharmony_ci if (kUnpremul_SkAlphaType == dstAt) { 339cb93a386Sopenharmony_ci p.append(SkRasterPipeline::unpremul); 340cb93a386Sopenharmony_ci } 341cb93a386Sopenharmony_ci p.append_store(dstCT, &dst_ctx); 342cb93a386Sopenharmony_ci 343cb93a386Sopenharmony_ci p.run(0,0, width,1); 344cb93a386Sopenharmony_ci} 345cb93a386Sopenharmony_ci 346cb93a386Sopenharmony_ciSkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t rowBytes, 347cb93a386Sopenharmony_ci const Options& options, int* rowsDecodedPtr) { 348cb93a386Sopenharmony_ci const int index = options.fFrameIndex; 349cb93a386Sopenharmony_ci SkASSERT(0 == index || index < fFrameHolder.size()); 350cb93a386Sopenharmony_ci SkASSERT(0 == index || !options.fSubset); 351cb93a386Sopenharmony_ci 352cb93a386Sopenharmony_ci WebPDecoderConfig config; 353cb93a386Sopenharmony_ci if (0 == WebPInitDecoderConfig(&config)) { 354cb93a386Sopenharmony_ci // ABI mismatch. 355cb93a386Sopenharmony_ci // FIXME: New enum for this? 356cb93a386Sopenharmony_ci return kInvalidInput; 357cb93a386Sopenharmony_ci } 358cb93a386Sopenharmony_ci 359cb93a386Sopenharmony_ci // Free any memory associated with the buffer. Must be called last, so we declare it first. 360cb93a386Sopenharmony_ci SkAutoTCallVProc<WebPDecBuffer, WebPFreeDecBuffer> autoFree(&(config.output)); 361cb93a386Sopenharmony_ci 362cb93a386Sopenharmony_ci WebPIterator frame; 363cb93a386Sopenharmony_ci SkAutoTCallVProc<WebPIterator, WebPDemuxReleaseIterator> autoFrame(&frame); 364cb93a386Sopenharmony_ci // If this succeeded in onGetFrameCount(), it should succeed again here. 365cb93a386Sopenharmony_ci SkAssertResult(WebPDemuxGetFrame(fDemux, index + 1, &frame)); 366cb93a386Sopenharmony_ci 367cb93a386Sopenharmony_ci const bool independent = index == 0 ? true : 368cb93a386Sopenharmony_ci (fFrameHolder.frame(index)->getRequiredFrame() == kNoFrame); 369cb93a386Sopenharmony_ci // Get the frameRect. libwebp will have already signaled an error if this is not fully 370cb93a386Sopenharmony_ci // contained by the canvas. 371cb93a386Sopenharmony_ci auto frameRect = SkIRect::MakeXYWH(frame.x_offset, frame.y_offset, frame.width, frame.height); 372cb93a386Sopenharmony_ci SkASSERT(this->bounds().contains(frameRect)); 373cb93a386Sopenharmony_ci const bool frameIsSubset = frameRect != this->bounds(); 374cb93a386Sopenharmony_ci if (independent && frameIsSubset) { 375cb93a386Sopenharmony_ci SkSampler::Fill(dstInfo, dst, rowBytes, options.fZeroInitialized); 376cb93a386Sopenharmony_ci } 377cb93a386Sopenharmony_ci 378cb93a386Sopenharmony_ci int dstX = frameRect.x(); 379cb93a386Sopenharmony_ci int dstY = frameRect.y(); 380cb93a386Sopenharmony_ci int subsetWidth = frameRect.width(); 381cb93a386Sopenharmony_ci int subsetHeight = frameRect.height(); 382cb93a386Sopenharmony_ci if (options.fSubset) { 383cb93a386Sopenharmony_ci SkIRect subset = *options.fSubset; 384cb93a386Sopenharmony_ci SkASSERT(this->bounds().contains(subset)); 385cb93a386Sopenharmony_ci SkASSERT(SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop)); 386cb93a386Sopenharmony_ci SkASSERT(this->getValidSubset(&subset) && subset == *options.fSubset); 387cb93a386Sopenharmony_ci 388cb93a386Sopenharmony_ci if (!SkIRect::Intersects(subset, frameRect)) { 389cb93a386Sopenharmony_ci return kSuccess; 390cb93a386Sopenharmony_ci } 391cb93a386Sopenharmony_ci 392cb93a386Sopenharmony_ci int minXOffset = std::min(dstX, subset.x()); 393cb93a386Sopenharmony_ci int minYOffset = std::min(dstY, subset.y()); 394cb93a386Sopenharmony_ci dstX -= minXOffset; 395cb93a386Sopenharmony_ci dstY -= minYOffset; 396cb93a386Sopenharmony_ci frameRect.offset(-minXOffset, -minYOffset); 397cb93a386Sopenharmony_ci subset.offset(-minXOffset, -minYOffset); 398cb93a386Sopenharmony_ci 399cb93a386Sopenharmony_ci // Just like we require that the requested subset x and y offset are even, libwebp 400cb93a386Sopenharmony_ci // guarantees that the frame x and y offset are even (it's actually impossible to specify 401cb93a386Sopenharmony_ci // an odd frame offset). So we can still guarantee that the adjusted offsets are even. 402cb93a386Sopenharmony_ci SkASSERT(SkIsAlign2(subset.fLeft) && SkIsAlign2(subset.fTop)); 403cb93a386Sopenharmony_ci 404cb93a386Sopenharmony_ci SkIRect intersection; 405cb93a386Sopenharmony_ci SkAssertResult(intersection.intersect(frameRect, subset)); 406cb93a386Sopenharmony_ci subsetWidth = intersection.width(); 407cb93a386Sopenharmony_ci subsetHeight = intersection.height(); 408cb93a386Sopenharmony_ci 409cb93a386Sopenharmony_ci config.options.use_cropping = 1; 410cb93a386Sopenharmony_ci config.options.crop_left = subset.x(); 411cb93a386Sopenharmony_ci config.options.crop_top = subset.y(); 412cb93a386Sopenharmony_ci config.options.crop_width = subsetWidth; 413cb93a386Sopenharmony_ci config.options.crop_height = subsetHeight; 414cb93a386Sopenharmony_ci } 415cb93a386Sopenharmony_ci 416cb93a386Sopenharmony_ci // Ignore the frame size and offset when determining if scaling is necessary. 417cb93a386Sopenharmony_ci int scaledWidth = subsetWidth; 418cb93a386Sopenharmony_ci int scaledHeight = subsetHeight; 419cb93a386Sopenharmony_ci SkISize srcSize = options.fSubset ? options.fSubset->size() : this->dimensions(); 420cb93a386Sopenharmony_ci if (srcSize != dstInfo.dimensions()) { 421cb93a386Sopenharmony_ci config.options.use_scaling = 1; 422cb93a386Sopenharmony_ci 423cb93a386Sopenharmony_ci if (frameIsSubset) { 424cb93a386Sopenharmony_ci float scaleX = ((float) dstInfo.width()) / srcSize.width(); 425cb93a386Sopenharmony_ci float scaleY = ((float) dstInfo.height()) / srcSize.height(); 426cb93a386Sopenharmony_ci 427cb93a386Sopenharmony_ci // We need to be conservative here and floor rather than round. 428cb93a386Sopenharmony_ci // Otherwise, we may find ourselves decoding off the end of memory. 429cb93a386Sopenharmony_ci dstX = scaleX * dstX; 430cb93a386Sopenharmony_ci scaledWidth = scaleX * scaledWidth; 431cb93a386Sopenharmony_ci dstY = scaleY * dstY; 432cb93a386Sopenharmony_ci scaledHeight = scaleY * scaledHeight; 433cb93a386Sopenharmony_ci if (0 == scaledWidth || 0 == scaledHeight) { 434cb93a386Sopenharmony_ci return kSuccess; 435cb93a386Sopenharmony_ci } 436cb93a386Sopenharmony_ci } else { 437cb93a386Sopenharmony_ci scaledWidth = dstInfo.width(); 438cb93a386Sopenharmony_ci scaledHeight = dstInfo.height(); 439cb93a386Sopenharmony_ci } 440cb93a386Sopenharmony_ci 441cb93a386Sopenharmony_ci config.options.scaled_width = scaledWidth; 442cb93a386Sopenharmony_ci config.options.scaled_height = scaledHeight; 443cb93a386Sopenharmony_ci } 444cb93a386Sopenharmony_ci 445cb93a386Sopenharmony_ci const bool blendWithPrevFrame = !independent && frame.blend_method == WEBP_MUX_BLEND 446cb93a386Sopenharmony_ci && frame.has_alpha; 447cb93a386Sopenharmony_ci 448cb93a386Sopenharmony_ci auto webpInfo = dstInfo; 449cb93a386Sopenharmony_ci if (!frame.has_alpha) { 450cb93a386Sopenharmony_ci webpInfo = webpInfo.makeAlphaType(kOpaque_SkAlphaType); 451cb93a386Sopenharmony_ci } else if (this->colorXform() || blendWithPrevFrame) { 452cb93a386Sopenharmony_ci // the colorXform and blend_line expect unpremul. 453cb93a386Sopenharmony_ci webpInfo = webpInfo.makeAlphaType(kUnpremul_SkAlphaType); 454cb93a386Sopenharmony_ci } 455cb93a386Sopenharmony_ci if (this->colorXform()) { 456cb93a386Sopenharmony_ci // Swizzling between RGBA and BGRA is zero cost in a color transform. So when we have a 457cb93a386Sopenharmony_ci // color transform, we should decode to whatever is easiest for libwebp, and then let the 458cb93a386Sopenharmony_ci // color transform swizzle if necessary. 459cb93a386Sopenharmony_ci // Lossy webp is encoded as YUV (so RGBA and BGRA are the same cost). Lossless webp is 460cb93a386Sopenharmony_ci // encoded as BGRA. This means decoding to BGRA is either faster or the same cost as RGBA. 461cb93a386Sopenharmony_ci webpInfo = webpInfo.makeColorType(kBGRA_8888_SkColorType); 462cb93a386Sopenharmony_ci } 463cb93a386Sopenharmony_ci 464cb93a386Sopenharmony_ci SkBitmap webpDst; 465cb93a386Sopenharmony_ci if ((this->colorXform() && !is_8888(dstInfo.colorType())) || blendWithPrevFrame) { 466cb93a386Sopenharmony_ci // We will decode the entire image and then perform the color transform. libwebp 467cb93a386Sopenharmony_ci // does not provide a row-by-row API. This is a shame particularly when we do not want 468cb93a386Sopenharmony_ci // 8888, since we will need to create another image sized buffer. 469cb93a386Sopenharmony_ci webpDst.allocPixels(webpInfo); 470cb93a386Sopenharmony_ci } else { 471cb93a386Sopenharmony_ci // libwebp can decode directly into the output memory. 472cb93a386Sopenharmony_ci webpDst.installPixels(webpInfo, dst, rowBytes); 473cb93a386Sopenharmony_ci } 474cb93a386Sopenharmony_ci 475cb93a386Sopenharmony_ci config.output.colorspace = webp_decode_mode(webpInfo.colorType(), 476cb93a386Sopenharmony_ci webpInfo.alphaType() == kPremul_SkAlphaType); 477cb93a386Sopenharmony_ci config.output.is_external_memory = 1; 478cb93a386Sopenharmony_ci 479cb93a386Sopenharmony_ci config.output.u.RGBA.rgba = reinterpret_cast<uint8_t*>(webpDst.getAddr(dstX, dstY)); 480cb93a386Sopenharmony_ci config.output.u.RGBA.stride = static_cast<int>(webpDst.rowBytes()); 481cb93a386Sopenharmony_ci config.output.u.RGBA.size = webpDst.computeByteSize(); 482cb93a386Sopenharmony_ci 483cb93a386Sopenharmony_ci SkAutoTCallVProc<WebPIDecoder, WebPIDelete> idec(WebPIDecode(nullptr, 0, &config)); 484cb93a386Sopenharmony_ci if (!idec) { 485cb93a386Sopenharmony_ci return kInvalidInput; 486cb93a386Sopenharmony_ci } 487cb93a386Sopenharmony_ci 488cb93a386Sopenharmony_ci int rowsDecoded = 0; 489cb93a386Sopenharmony_ci SkCodec::Result result; 490cb93a386Sopenharmony_ci switch (WebPIUpdate(idec, frame.fragment.bytes, frame.fragment.size)) { 491cb93a386Sopenharmony_ci case VP8_STATUS_OK: 492cb93a386Sopenharmony_ci rowsDecoded = scaledHeight; 493cb93a386Sopenharmony_ci result = kSuccess; 494cb93a386Sopenharmony_ci break; 495cb93a386Sopenharmony_ci case VP8_STATUS_SUSPENDED: 496cb93a386Sopenharmony_ci if (!WebPIDecGetRGB(idec, &rowsDecoded, nullptr, nullptr, nullptr) 497cb93a386Sopenharmony_ci || rowsDecoded <= 0) { 498cb93a386Sopenharmony_ci return kInvalidInput; 499cb93a386Sopenharmony_ci } 500cb93a386Sopenharmony_ci *rowsDecodedPtr = rowsDecoded + dstY; 501cb93a386Sopenharmony_ci result = kIncompleteInput; 502cb93a386Sopenharmony_ci break; 503cb93a386Sopenharmony_ci default: 504cb93a386Sopenharmony_ci return kInvalidInput; 505cb93a386Sopenharmony_ci } 506cb93a386Sopenharmony_ci 507cb93a386Sopenharmony_ci const size_t dstBpp = dstInfo.bytesPerPixel(); 508cb93a386Sopenharmony_ci dst = SkTAddOffset<void>(dst, dstBpp * dstX + rowBytes * dstY); 509cb93a386Sopenharmony_ci const size_t srcRowBytes = config.output.u.RGBA.stride; 510cb93a386Sopenharmony_ci 511cb93a386Sopenharmony_ci const auto dstCT = dstInfo.colorType(); 512cb93a386Sopenharmony_ci if (this->colorXform()) { 513cb93a386Sopenharmony_ci uint32_t* xformSrc = (uint32_t*) config.output.u.RGBA.rgba; 514cb93a386Sopenharmony_ci SkBitmap tmp; 515cb93a386Sopenharmony_ci void* xformDst; 516cb93a386Sopenharmony_ci 517cb93a386Sopenharmony_ci if (blendWithPrevFrame) { 518cb93a386Sopenharmony_ci // Xform into temporary bitmap big enough for one row. 519cb93a386Sopenharmony_ci tmp.allocPixels(dstInfo.makeWH(scaledWidth, 1)); 520cb93a386Sopenharmony_ci xformDst = tmp.getPixels(); 521cb93a386Sopenharmony_ci } else { 522cb93a386Sopenharmony_ci xformDst = dst; 523cb93a386Sopenharmony_ci } 524cb93a386Sopenharmony_ci 525cb93a386Sopenharmony_ci for (int y = 0; y < rowsDecoded; y++) { 526cb93a386Sopenharmony_ci this->applyColorXform(xformDst, xformSrc, scaledWidth); 527cb93a386Sopenharmony_ci if (blendWithPrevFrame) { 528cb93a386Sopenharmony_ci blend_line(dstCT, dst, dstCT, xformDst, 529cb93a386Sopenharmony_ci dstInfo.alphaType(), frame.has_alpha, scaledWidth); 530cb93a386Sopenharmony_ci dst = SkTAddOffset<void>(dst, rowBytes); 531cb93a386Sopenharmony_ci } else { 532cb93a386Sopenharmony_ci xformDst = SkTAddOffset<void>(xformDst, rowBytes); 533cb93a386Sopenharmony_ci } 534cb93a386Sopenharmony_ci xformSrc = SkTAddOffset<uint32_t>(xformSrc, srcRowBytes); 535cb93a386Sopenharmony_ci } 536cb93a386Sopenharmony_ci } else if (blendWithPrevFrame) { 537cb93a386Sopenharmony_ci const uint8_t* src = config.output.u.RGBA.rgba; 538cb93a386Sopenharmony_ci 539cb93a386Sopenharmony_ci for (int y = 0; y < rowsDecoded; y++) { 540cb93a386Sopenharmony_ci blend_line(dstCT, dst, webpDst.colorType(), src, 541cb93a386Sopenharmony_ci dstInfo.alphaType(), frame.has_alpha, scaledWidth); 542cb93a386Sopenharmony_ci src = SkTAddOffset<const uint8_t>(src, srcRowBytes); 543cb93a386Sopenharmony_ci dst = SkTAddOffset<void>(dst, rowBytes); 544cb93a386Sopenharmony_ci } 545cb93a386Sopenharmony_ci } 546cb93a386Sopenharmony_ci 547cb93a386Sopenharmony_ci return result; 548cb93a386Sopenharmony_ci} 549cb93a386Sopenharmony_ci 550cb93a386Sopenharmony_ciSkWebpCodec::SkWebpCodec(SkEncodedInfo&& info, std::unique_ptr<SkStream> stream, 551cb93a386Sopenharmony_ci WebPDemuxer* demux, sk_sp<SkData> data, SkEncodedOrigin origin) 552cb93a386Sopenharmony_ci : INHERITED(std::move(info), skcms_PixelFormat_BGRA_8888, std::move(stream), 553cb93a386Sopenharmony_ci origin) 554cb93a386Sopenharmony_ci , fDemux(demux) 555cb93a386Sopenharmony_ci , fData(std::move(data)) 556cb93a386Sopenharmony_ci , fFailed(false) 557cb93a386Sopenharmony_ci{ 558cb93a386Sopenharmony_ci const auto& eInfo = this->getEncodedInfo(); 559cb93a386Sopenharmony_ci fFrameHolder.setScreenSize(eInfo.width(), eInfo.height()); 560cb93a386Sopenharmony_ci} 561