| /* |
| * 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 "include/utils/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 <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(SkImage::MakeFromGenerator( |
| 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 = SkImage::MakeRasterData(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 = SkImage::MakeRasterData(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; |
| } |
| |
| |