SkWebpCodec.cpp 21 KB

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