1/* 2 * Copyright 2018 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "include/codec/SkCodec.h" 9#include "include/core/SkCanvas.h" 10#include "include/core/SkData.h" 11#include "include/core/SkImage.h" 12#include "include/utils/SkAnimCodecPlayer.h" 13#include "src/codec/SkCodecImageGenerator.h" 14#include "src/core/SkPixmapPriv.h" 15#include <algorithm> 16 17SkAnimCodecPlayer::SkAnimCodecPlayer(std::unique_ptr<SkCodec> codec) : fCodec(std::move(codec)) { 18 fImageInfo = fCodec->getInfo(); 19 fFrameInfos = fCodec->getFrameInfo(); 20 fImages.resize(fFrameInfos.size()); 21 22 // change the interpretation of fDuration to a end-time for that frame 23 size_t dur = 0; 24 for (auto& f : fFrameInfos) { 25 dur += f.fDuration; 26 f.fDuration = dur; 27 } 28 fTotalDuration = dur; 29 30 if (!fTotalDuration) { 31 // Static image -- may or may not have returned a single frame info. 32 fFrameInfos.clear(); 33 fImages.clear(); 34 fImages.push_back(SkImage::MakeFromGenerator( 35 SkCodecImageGenerator::MakeFromCodec(std::move(fCodec)))); 36 } 37} 38 39SkAnimCodecPlayer::~SkAnimCodecPlayer() {} 40 41SkISize SkAnimCodecPlayer::dimensions() const { 42 if (!fCodec) { 43 auto image = fImages.front(); 44 return image ? image->dimensions() : SkISize::MakeEmpty(); 45 } 46 if (SkEncodedOriginSwapsWidthHeight(fCodec->getOrigin())) { 47 return { fImageInfo.height(), fImageInfo.width() }; 48 } 49 return { fImageInfo.width(), fImageInfo.height() }; 50} 51 52sk_sp<SkImage> SkAnimCodecPlayer::getFrameAt(int index) { 53 SkASSERT((unsigned)index < fFrameInfos.size()); 54 55 if (fImages[index]) { 56 return fImages[index]; 57 } 58 59 size_t rb = fImageInfo.minRowBytes(); 60 size_t size = fImageInfo.computeByteSize(rb); 61 auto data = SkData::MakeUninitialized(size); 62 63 SkCodec::Options opts; 64 opts.fFrameIndex = index; 65 66 const auto origin = fCodec->getOrigin(); 67 const auto orientedDims = this->dimensions(); 68 const auto originMatrix = SkEncodedOriginToMatrix(origin, orientedDims.width(), 69 orientedDims.height()); 70 71 SkPaint paint; 72 paint.setBlendMode(SkBlendMode::kSrc); 73 74 auto imageInfo = fImageInfo; 75 if (fFrameInfos[index].fAlphaType != kOpaque_SkAlphaType && imageInfo.isOpaque()) { 76 imageInfo = imageInfo.makeAlphaType(kPremul_SkAlphaType); 77 } 78 const int requiredFrame = fFrameInfos[index].fRequiredFrame; 79 if (requiredFrame != SkCodec::kNoFrame && fImages[requiredFrame]) { 80 auto requiredImage = fImages[requiredFrame]; 81 auto canvas = SkCanvas::MakeRasterDirect(imageInfo, data->writable_data(), rb); 82 if (origin != kDefault_SkEncodedOrigin) { 83 // The required frame is stored after applying the origin. Undo that, 84 // because the codec decodes prior to applying the origin. 85 // FIXME: Another approach would be to decode the frame's delta on top 86 // of transparent black, and then draw that through the origin matrix 87 // onto the required frame. To do that, SkCodec needs to expose the 88 // rectangle of the delta and the blend mode, so we can handle 89 // kRestoreBGColor frames and Blend::kSrc. 90 SkMatrix inverse; 91 SkAssertResult(originMatrix.invert(&inverse)); 92 canvas->concat(inverse); 93 } 94 canvas->drawImage(requiredImage, 0, 0, SkSamplingOptions(), &paint); 95 opts.fPriorFrame = requiredFrame; 96 } 97 98 if (SkCodec::kSuccess != fCodec->getPixels(imageInfo, data->writable_data(), rb, &opts)) { 99 return nullptr; 100 } 101 102 auto image = SkImage::MakeRasterData(imageInfo, std::move(data), rb); 103 if (origin != kDefault_SkEncodedOrigin) { 104 imageInfo = imageInfo.makeDimensions(orientedDims); 105 rb = imageInfo.minRowBytes(); 106 size = imageInfo.computeByteSize(rb); 107 data = SkData::MakeUninitialized(size); 108 auto canvas = SkCanvas::MakeRasterDirect(imageInfo, data->writable_data(), rb); 109 canvas->concat(originMatrix); 110 canvas->drawImage(image, 0, 0, SkSamplingOptions(), &paint); 111 image = SkImage::MakeRasterData(imageInfo, std::move(data), rb); 112 } 113 return fImages[index] = image; 114} 115 116sk_sp<SkImage> SkAnimCodecPlayer::getFrame() { 117 SkASSERT(fTotalDuration > 0 || fImages.size() == 1); 118 119 return fTotalDuration > 0 120 ? this->getFrameAt(fCurrIndex) 121 : fImages.front(); 122} 123 124bool SkAnimCodecPlayer::seek(uint32_t msec) { 125 if (!fTotalDuration) { 126 return false; 127 } 128 129 msec %= fTotalDuration; 130 131 auto lower = std::lower_bound(fFrameInfos.begin(), fFrameInfos.end(), msec, 132 [](const SkCodec::FrameInfo& info, uint32_t msec) { 133 return (uint32_t)info.fDuration <= msec; 134 }); 135 int prevIndex = fCurrIndex; 136 fCurrIndex = lower - fFrameInfos.begin(); 137 return fCurrIndex != prevIndex; 138} 139 140 141