| /* | |
| * Copyright 2018 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "modules/skresources/src/SkAnimCodecPlayer.h" | |
| #include "include/codec/SkCodec.h" | |
| #include "include/codec/SkEncodedOrigin.h" | |
| #include "include/core/SkAlphaType.h" | |
| #include "include/core/SkBlendMode.h" | |
| #include "include/core/SkCanvas.h" | |
| #include "include/core/SkData.h" | |
| #include "include/core/SkImage.h" | |
| #include "include/core/SkImageInfo.h" | |
| #include "include/core/SkMatrix.h" | |
| #include "include/core/SkPaint.h" | |
| #include "include/core/SkRefCnt.h" | |
| #include "include/core/SkSamplingOptions.h" | |
| #include "include/core/SkSize.h" | |
| #include "include/core/SkTypes.h" | |
| #include "src/codec/SkCodecImageGenerator.h" | |
| #include <algorithm> | |
| #include <cstddef> | |
| #include <memory> | |
| #include <utility> | |
| #include <vector> | |
| SkAnimCodecPlayer::SkAnimCodecPlayer(std::unique_ptr<SkCodec> codec) : fCodec(std::move(codec)) { | |
| fImageInfo = fCodec->getInfo(); | |
| fFrameInfos = fCodec->getFrameInfo(); | |
| fImages.resize(fFrameInfos.size()); | |
| // change the interpretation of fDuration to a end-time for that frame | |
| size_t dur = 0; | |
| for (auto& f : fFrameInfos) { | |
| dur += f.fDuration; | |
| f.fDuration = dur; | |
| } | |
| fTotalDuration = dur; | |
| if (!fTotalDuration) { | |
| // Static image -- may or may not have returned a single frame info. | |
| fFrameInfos.clear(); | |
| fImages.clear(); | |
| fImages.push_back(SkImages::DeferredFromGenerator( | |
| SkCodecImageGenerator::MakeFromCodec(std::move(fCodec)))); | |
| } | |
| } | |
| SkAnimCodecPlayer::~SkAnimCodecPlayer() {} | |
| SkISize SkAnimCodecPlayer::dimensions() const { | |
| if (!fCodec) { | |
| auto image = fImages.front(); | |
| return image ? image->dimensions() : SkISize::MakeEmpty(); | |
| } | |
| if (SkEncodedOriginSwapsWidthHeight(fCodec->getOrigin())) { | |
| return { fImageInfo.height(), fImageInfo.width() }; | |
| } | |
| return { fImageInfo.width(), fImageInfo.height() }; | |
| } | |
| sk_sp<SkImage> SkAnimCodecPlayer::getFrameAt(int index) { | |
| SkASSERT((unsigned)index < fFrameInfos.size()); | |
| if (fImages[index]) { | |
| return fImages[index]; | |
| } | |
| size_t rb = fImageInfo.minRowBytes(); | |
| size_t size = fImageInfo.computeByteSize(rb); | |
| auto data = SkData::MakeUninitialized(size); | |
| SkCodec::Options opts; | |
| opts.fFrameIndex = index; | |
| const auto origin = fCodec->getOrigin(); | |
| const auto orientedDims = this->dimensions(); | |
| const auto originMatrix = SkEncodedOriginToMatrix(origin, orientedDims.width(), | |
| orientedDims.height()); | |
| SkPaint paint; | |
| paint.setBlendMode(SkBlendMode::kSrc); | |
| auto imageInfo = fImageInfo; | |
| if (fFrameInfos[index].fAlphaType != kOpaque_SkAlphaType && imageInfo.isOpaque()) { | |
| imageInfo = imageInfo.makeAlphaType(kPremul_SkAlphaType); | |
| } | |
| const int requiredFrame = fFrameInfos[index].fRequiredFrame; | |
| if (requiredFrame != SkCodec::kNoFrame && fImages[requiredFrame]) { | |
| auto requiredImage = fImages[requiredFrame]; | |
| auto canvas = SkCanvas::MakeRasterDirect(imageInfo, data->writable_data(), rb); | |
| if (origin != kDefault_SkEncodedOrigin) { | |
| // The required frame is stored after applying the origin. Undo that, | |
| // because the codec decodes prior to applying the origin. | |
| // FIXME: Another approach would be to decode the frame's delta on top | |
| // of transparent black, and then draw that through the origin matrix | |
| // onto the required frame. To do that, SkCodec needs to expose the | |
| // rectangle of the delta and the blend mode, so we can handle | |
| // kRestoreBGColor frames and Blend::kSrc. | |
| SkMatrix inverse; | |
| SkAssertResult(originMatrix.invert(&inverse)); | |
| canvas->concat(inverse); | |
| } | |
| canvas->drawImage(requiredImage, 0, 0, SkSamplingOptions(), &paint); | |
| opts.fPriorFrame = requiredFrame; | |
| } | |
| if (SkCodec::kSuccess != fCodec->getPixels(imageInfo, data->writable_data(), rb, &opts)) { | |
| return nullptr; | |
| } | |
| auto image = SkImages::RasterFromData(imageInfo, std::move(data), rb); | |
| if (origin != kDefault_SkEncodedOrigin) { | |
| imageInfo = imageInfo.makeDimensions(orientedDims); | |
| rb = imageInfo.minRowBytes(); | |
| size = imageInfo.computeByteSize(rb); | |
| data = SkData::MakeUninitialized(size); | |
| auto canvas = SkCanvas::MakeRasterDirect(imageInfo, data->writable_data(), rb); | |
| canvas->concat(originMatrix); | |
| canvas->drawImage(image, 0, 0, SkSamplingOptions(), &paint); | |
| image = SkImages::RasterFromData(imageInfo, std::move(data), rb); | |
| } | |
| return fImages[index] = image; | |
| } | |
| sk_sp<SkImage> SkAnimCodecPlayer::getFrame() { | |
| SkASSERT(fTotalDuration > 0 || fImages.size() == 1); | |
| return fTotalDuration > 0 | |
| ? this->getFrameAt(fCurrIndex) | |
| : fImages.front(); | |
| } | |
| bool SkAnimCodecPlayer::seek(uint32_t msec) { | |
| if (!fTotalDuration) { | |
| return false; | |
| } | |
| msec %= fTotalDuration; | |
| auto lower = std::lower_bound(fFrameInfos.begin(), fFrameInfos.end(), msec, | |
| [](const SkCodec::FrameInfo& info, uint32_t msec) { | |
| return (uint32_t)info.fDuration <= msec; | |
| }); | |
| int prevIndex = fCurrIndex; | |
| fCurrIndex = lower - fFrameInfos.begin(); | |
| return fCurrIndex != prevIndex; | |
| } | |