SkGifCodec.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  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. /*
  8. * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
  9. *
  10. * Redistribution and use in source and binary forms, with or without
  11. * modification, are permitted provided that the following conditions
  12. * are met:
  13. * 1. Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in the
  17. * documentation and/or other materials provided with the distribution.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
  20. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  21. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  22. * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
  23. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  24. * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  25. * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  26. * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  27. * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. */
  31. #include "include/codec/SkCodecAnimation.h"
  32. #include "include/core/SkStream.h"
  33. #include "include/private/SkColorData.h"
  34. #include "src/codec/SkCodecPriv.h"
  35. #include "src/codec/SkColorTable.h"
  36. #include "src/codec/SkGifCodec.h"
  37. #include "src/codec/SkSwizzler.h"
  38. #include "src/core/SkMakeUnique.h"
  39. #include <algorithm>
  40. #define GIF87_STAMP "GIF87a"
  41. #define GIF89_STAMP "GIF89a"
  42. #define GIF_STAMP_LEN 6
  43. /*
  44. * Checks the start of the stream to see if the image is a gif
  45. */
  46. bool SkGifCodec::IsGif(const void* buf, size_t bytesRead) {
  47. if (bytesRead >= GIF_STAMP_LEN) {
  48. if (memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||
  49. memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0)
  50. {
  51. return true;
  52. }
  53. }
  54. return false;
  55. }
  56. /*
  57. * Error function
  58. */
  59. static SkCodec::Result gif_error(const char* msg, SkCodec::Result result = SkCodec::kInvalidInput) {
  60. SkCodecPrintf("Gif Error: %s\n", msg);
  61. return result;
  62. }
  63. std::unique_ptr<SkCodec> SkGifCodec::MakeFromStream(std::unique_ptr<SkStream> stream,
  64. Result* result) {
  65. std::unique_ptr<SkGifImageReader> reader(new SkGifImageReader(std::move(stream)));
  66. *result = reader->parse(SkGifImageReader::SkGIFSizeQuery);
  67. if (*result != kSuccess) {
  68. return nullptr;
  69. }
  70. // If no images are in the data, or the first header is not yet defined, we cannot
  71. // create a codec. In either case, the width and height are not yet known.
  72. auto* frame = reader->frameContext(0);
  73. if (!frame || !frame->isHeaderDefined()) {
  74. *result = kInvalidInput;
  75. return nullptr;
  76. }
  77. // isHeaderDefined() will not return true if the screen size is empty.
  78. SkASSERT(reader->screenHeight() > 0 && reader->screenWidth() > 0);
  79. const auto alpha = reader->firstFrameHasAlpha() ? SkEncodedInfo::kBinary_Alpha
  80. : SkEncodedInfo::kOpaque_Alpha;
  81. // Use kPalette since Gifs are encoded with a color table.
  82. // FIXME: Gifs can actually be encoded with 4-bits per pixel. Using 8 works, but we could skip
  83. // expanding to 8 bits and take advantage of the SkSwizzler to work from 4.
  84. auto encodedInfo = SkEncodedInfo::Make(reader->screenWidth(), reader->screenHeight(),
  85. SkEncodedInfo::kPalette_Color, alpha, 8);
  86. return std::unique_ptr<SkCodec>(new SkGifCodec(std::move(encodedInfo), reader.release()));
  87. }
  88. bool SkGifCodec::onRewind() {
  89. fReader->clearDecodeState();
  90. return true;
  91. }
  92. SkGifCodec::SkGifCodec(SkEncodedInfo&& encodedInfo, SkGifImageReader* reader)
  93. : INHERITED(std::move(encodedInfo), skcms_PixelFormat_RGBA_8888, nullptr)
  94. , fReader(reader)
  95. , fTmpBuffer(nullptr)
  96. , fSwizzler(nullptr)
  97. , fCurrColorTable(nullptr)
  98. , fCurrColorTableIsReal(false)
  99. , fFilledBackground(false)
  100. , fFirstCallToIncrementalDecode(false)
  101. , fDst(nullptr)
  102. , fDstRowBytes(0)
  103. , fRowsDecoded(0)
  104. {
  105. reader->setClient(this);
  106. }
  107. int SkGifCodec::onGetFrameCount() {
  108. fReader->parse(SkGifImageReader::SkGIFFrameCountQuery);
  109. return fReader->imagesCount();
  110. }
  111. bool SkGifCodec::onGetFrameInfo(int i, SkCodec::FrameInfo* frameInfo) const {
  112. if (i >= fReader->imagesCount()) {
  113. return false;
  114. }
  115. const SkGIFFrameContext* frameContext = fReader->frameContext(i);
  116. SkASSERT(frameContext->reachedStartOfData());
  117. if (frameInfo) {
  118. frameInfo->fDuration = frameContext->getDuration();
  119. frameInfo->fRequiredFrame = frameContext->getRequiredFrame();
  120. frameInfo->fFullyReceived = frameContext->isComplete();
  121. frameInfo->fAlphaType = frameContext->hasAlpha() ? kUnpremul_SkAlphaType
  122. : kOpaque_SkAlphaType;
  123. frameInfo->fDisposalMethod = frameContext->getDisposalMethod();
  124. }
  125. return true;
  126. }
  127. int SkGifCodec::onGetRepetitionCount() {
  128. fReader->parse(SkGifImageReader::SkGIFLoopCountQuery);
  129. return fReader->loopCount();
  130. }
  131. static constexpr SkColorType kXformSrcColorType = kRGBA_8888_SkColorType;
  132. void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, int frameIndex) {
  133. SkColorType colorTableColorType = dstInfo.colorType();
  134. if (this->colorXform()) {
  135. colorTableColorType = kXformSrcColorType;
  136. }
  137. sk_sp<SkColorTable> currColorTable = fReader->getColorTable(colorTableColorType, frameIndex);
  138. fCurrColorTableIsReal = static_cast<bool>(currColorTable);
  139. if (!fCurrColorTableIsReal) {
  140. // This is possible for an empty frame. Create a dummy with one value (transparent).
  141. SkPMColor color = SK_ColorTRANSPARENT;
  142. fCurrColorTable.reset(new SkColorTable(&color, 1));
  143. } else if (this->colorXform() && !this->xformOnDecode()) {
  144. SkPMColor dstColors[256];
  145. this->applyColorXform(dstColors, currColorTable->readColors(),
  146. currColorTable->count());
  147. fCurrColorTable.reset(new SkColorTable(dstColors, currColorTable->count()));
  148. } else {
  149. fCurrColorTable = std::move(currColorTable);
  150. }
  151. }
  152. SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, const Options& opts) {
  153. if (opts.fSubset) {
  154. return gif_error("Subsets not supported.\n", kUnimplemented);
  155. }
  156. const int frameIndex = opts.fFrameIndex;
  157. if (frameIndex > 0 && kRGB_565_SkColorType == dstInfo.colorType()) {
  158. // FIXME: In theory, we might be able to support this, but it's not clear that it
  159. // is necessary (Chromium does not decode to 565, and Android does not decode
  160. // frames beyond the first). Disabling it because it is somewhat difficult:
  161. // - If there is a transparent pixel, and this frame draws on top of another frame
  162. // (if the frame is independent with a transparent pixel, we should not decode to
  163. // 565 anyway, since it is not opaque), we need to skip drawing the transparent
  164. // pixels (see writeTransparentPixels in haveDecodedRow). We currently do this by
  165. // first swizzling into temporary memory, then copying into the destination. (We
  166. // let the swizzler handle it first because it may need to sample.) After
  167. // swizzling to 565, we do not know which pixels in our temporary memory
  168. // correspond to the transparent pixel, so we do not know what to skip. We could
  169. // special case the non-sampled case (no need to swizzle), but as this is
  170. // currently unused we can just not support it.
  171. return gif_error("Cannot decode multiframe gif (except frame 0) as 565.\n",
  172. kInvalidConversion);
  173. }
  174. const auto* frame = fReader->frameContext(frameIndex);
  175. SkASSERT(frame);
  176. if (0 == frameIndex) {
  177. // SkCodec does not have a way to just parse through frame 0, so we
  178. // have to do so manually, here.
  179. fReader->parse((SkGifImageReader::SkGIFParseQuery) 0);
  180. if (!frame->reachedStartOfData()) {
  181. // We have parsed enough to know that there is a color map, but cannot
  182. // parse the map itself yet. Exit now, so we do not build an incorrect
  183. // table.
  184. return gif_error("color map not available yet\n", kIncompleteInput);
  185. }
  186. } else {
  187. // Parsing happened in SkCodec::getPixels.
  188. SkASSERT(frameIndex < fReader->imagesCount());
  189. SkASSERT(frame->reachedStartOfData());
  190. }
  191. if (this->xformOnDecode()) {
  192. fXformBuffer.reset(new uint32_t[dstInfo.width()]);
  193. sk_bzero(fXformBuffer.get(), dstInfo.width() * sizeof(uint32_t));
  194. }
  195. fTmpBuffer.reset(new uint8_t[dstInfo.minRowBytes()]);
  196. this->initializeColorTable(dstInfo, frameIndex);
  197. this->initializeSwizzler(dstInfo, frameIndex);
  198. SkASSERT(fCurrColorTable);
  199. return kSuccess;
  200. }
  201. void SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo, int frameIndex) {
  202. const SkGIFFrameContext* frame = fReader->frameContext(frameIndex);
  203. // This is only called by prepareToDecode, which ensures frameIndex is in range.
  204. SkASSERT(frame);
  205. const int xBegin = frame->xOffset();
  206. const int xEnd = std::min(frame->frameRect().right(), fReader->screenWidth());
  207. // CreateSwizzler only reads left and right of the frame. We cannot use the frame's raw
  208. // frameRect, since it might extend beyond the edge of the frame.
  209. SkIRect swizzleRect = SkIRect::MakeLTRB(xBegin, 0, xEnd, 0);
  210. SkImageInfo swizzlerInfo = dstInfo;
  211. if (this->colorXform()) {
  212. swizzlerInfo = swizzlerInfo.makeColorType(kXformSrcColorType);
  213. if (kPremul_SkAlphaType == dstInfo.alphaType()) {
  214. swizzlerInfo = swizzlerInfo.makeAlphaType(kUnpremul_SkAlphaType);
  215. }
  216. }
  217. // The default Options should be fine:
  218. // - we'll ignore if the memory is zero initialized - unless we're the first frame, this won't
  219. // matter anyway.
  220. // - subsets are not supported for gif
  221. // - the swizzler does not need to know about the frame.
  222. // We may not be able to use the real Options anyway, since getPixels does not store it (due to
  223. // a bug).
  224. fSwizzler = SkSwizzler::Make(this->getEncodedInfo(), fCurrColorTable->readColors(),
  225. swizzlerInfo, Options(), &swizzleRect);
  226. SkASSERT(fSwizzler.get());
  227. }
  228. /*
  229. * Initiates the gif decode
  230. */
  231. SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo,
  232. void* pixels, size_t dstRowBytes,
  233. const Options& opts,
  234. int* rowsDecoded) {
  235. Result result = this->prepareToDecode(dstInfo, opts);
  236. switch (result) {
  237. case kSuccess:
  238. break;
  239. case kIncompleteInput:
  240. // onStartIncrementalDecode treats this as incomplete, since it may
  241. // provide more data later, but in this case, no more data will be
  242. // provided, and there is nothing to draw. We also cannot return
  243. // kIncompleteInput, which will make SkCodec attempt to fill
  244. // remaining rows, but that requires an SkSwizzler, which we have
  245. // not created.
  246. return kInvalidInput;
  247. default:
  248. return result;
  249. }
  250. if (dstInfo.dimensions() != this->dimensions()) {
  251. return gif_error("Scaling not supported.\n", kInvalidScale);
  252. }
  253. fDst = pixels;
  254. fDstRowBytes = dstRowBytes;
  255. return this->decodeFrame(true, opts, rowsDecoded);
  256. }
  257. SkCodec::Result SkGifCodec::onStartIncrementalDecode(const SkImageInfo& dstInfo,
  258. void* pixels, size_t dstRowBytes,
  259. const SkCodec::Options& opts) {
  260. Result result = this->prepareToDecode(dstInfo, opts);
  261. if (result != kSuccess) {
  262. return result;
  263. }
  264. fDst = pixels;
  265. fDstRowBytes = dstRowBytes;
  266. fFirstCallToIncrementalDecode = true;
  267. return kSuccess;
  268. }
  269. SkCodec::Result SkGifCodec::onIncrementalDecode(int* rowsDecoded) {
  270. // It is possible the client has appended more data. Parse, if needed.
  271. const auto& options = this->options();
  272. const int frameIndex = options.fFrameIndex;
  273. fReader->parse((SkGifImageReader::SkGIFParseQuery) frameIndex);
  274. const bool firstCallToIncrementalDecode = fFirstCallToIncrementalDecode;
  275. fFirstCallToIncrementalDecode = false;
  276. return this->decodeFrame(firstCallToIncrementalDecode, options, rowsDecoded);
  277. }
  278. SkCodec::Result SkGifCodec::decodeFrame(bool firstAttempt, const Options& opts, int* rowsDecoded) {
  279. const SkImageInfo& dstInfo = this->dstInfo();
  280. const int scaledHeight = get_scaled_dimension(dstInfo.height(), fSwizzler->sampleY());
  281. const int frameIndex = opts.fFrameIndex;
  282. SkASSERT(frameIndex < fReader->imagesCount());
  283. const SkGIFFrameContext* frameContext = fReader->frameContext(frameIndex);
  284. if (firstAttempt) {
  285. // rowsDecoded reports how many rows have been initialized, so a layer above
  286. // can fill the rest. In some cases, we fill the background before decoding
  287. // (or it is already filled for us), so we report rowsDecoded to be the full
  288. // height.
  289. bool filledBackground = false;
  290. if (frameContext->getRequiredFrame() == kNoFrame) {
  291. // We may need to clear to transparent for one of the following reasons:
  292. // - The frameRect does not cover the full bounds. haveDecodedRow will
  293. // only draw inside the frameRect, so we need to clear the rest.
  294. // - The frame is interlaced. There is no obvious way to fill
  295. // afterwards for an incomplete image. (FIXME: Does the first pass
  296. // cover all rows? If so, we do not have to fill here.)
  297. // - There is no color table for this frame. In that case will not
  298. // draw anything, so we need to fill.
  299. if (frameContext->frameRect() != this->bounds()
  300. || frameContext->interlaced() || !fCurrColorTableIsReal) {
  301. auto fillInfo = dstInfo.makeWH(fSwizzler->fillWidth(), scaledHeight);
  302. SkSampler::Fill(fillInfo, fDst, fDstRowBytes, opts.fZeroInitialized);
  303. filledBackground = true;
  304. }
  305. } else {
  306. // Not independent.
  307. // SkCodec ensured that the prior frame has been decoded.
  308. filledBackground = true;
  309. }
  310. fFilledBackground = filledBackground;
  311. if (filledBackground) {
  312. // Report the full (scaled) height, since the client will never need to fill.
  313. fRowsDecoded = scaledHeight;
  314. } else {
  315. // This will be updated by haveDecodedRow.
  316. fRowsDecoded = 0;
  317. }
  318. }
  319. if (!fCurrColorTableIsReal) {
  320. // Nothing to draw this frame.
  321. return kSuccess;
  322. }
  323. bool frameDecoded = false;
  324. const bool fatalError = !fReader->decode(frameIndex, &frameDecoded);
  325. if (fatalError || !frameDecoded || fRowsDecoded != scaledHeight) {
  326. if (rowsDecoded) {
  327. *rowsDecoded = fRowsDecoded;
  328. }
  329. if (fatalError) {
  330. return kErrorInInput;
  331. }
  332. return kIncompleteInput;
  333. }
  334. return kSuccess;
  335. }
  336. void SkGifCodec::applyXformRow(const SkImageInfo& dstInfo, void* dst, const uint8_t* src) const {
  337. if (this->xformOnDecode()) {
  338. SkASSERT(this->colorXform());
  339. fSwizzler->swizzle(fXformBuffer.get(), src);
  340. const int xformWidth = get_scaled_dimension(dstInfo.width(), fSwizzler->sampleX());
  341. this->applyColorXform(dst, fXformBuffer.get(), xformWidth);
  342. } else {
  343. fSwizzler->swizzle(dst, src);
  344. }
  345. }
  346. template <typename T>
  347. static void blend_line(void* dstAsVoid, const void* srcAsVoid, int width) {
  348. T* dst = reinterpret_cast<T*>(dstAsVoid);
  349. const T* src = reinterpret_cast<const T*>(srcAsVoid);
  350. while (width --> 0) {
  351. if (*src != 0) { // GIF pixels are either transparent (== 0) or opaque (!= 0).
  352. *dst = *src;
  353. }
  354. src++;
  355. dst++;
  356. }
  357. }
  358. void SkGifCodec::haveDecodedRow(int frameIndex, const unsigned char* rowBegin,
  359. int rowNumber, int repeatCount, bool writeTransparentPixels)
  360. {
  361. const SkGIFFrameContext* frameContext = fReader->frameContext(frameIndex);
  362. // The pixel data and coordinates supplied to us are relative to the frame's
  363. // origin within the entire image size, i.e.
  364. // (frameContext->xOffset, frameContext->yOffset). There is no guarantee
  365. // that width == (size().width() - frameContext->xOffset), so
  366. // we must ensure we don't run off the end of either the source data or the
  367. // row's X-coordinates.
  368. const int width = frameContext->width();
  369. const int xBegin = frameContext->xOffset();
  370. const int yBegin = frameContext->yOffset() + rowNumber;
  371. const int xEnd = std::min(xBegin + width, this->dimensions().width());
  372. const int yEnd = std::min(yBegin + rowNumber + repeatCount, this->dimensions().height());
  373. // FIXME: No need to make the checks on width/xBegin/xEnd for every row. We could instead do
  374. // this once in prepareToDecode.
  375. if (!width || (xBegin < 0) || (yBegin < 0) || (xEnd <= xBegin) || (yEnd <= yBegin))
  376. return;
  377. // yBegin is the first row in the non-sampled image. dstRow will be the row in the output,
  378. // after potentially scaling it.
  379. int dstRow = yBegin;
  380. const int sampleY = fSwizzler->sampleY();
  381. if (sampleY > 1) {
  382. // Check to see whether this row or one that falls in the repeatCount is needed in the
  383. // output.
  384. bool foundNecessaryRow = false;
  385. for (int i = 0; i < repeatCount; i++) {
  386. const int potentialRow = yBegin + i;
  387. if (fSwizzler->rowNeeded(potentialRow)) {
  388. dstRow = potentialRow / sampleY;
  389. const int scaledHeight = get_scaled_dimension(this->dstInfo().height(), sampleY);
  390. if (dstRow >= scaledHeight) {
  391. return;
  392. }
  393. foundNecessaryRow = true;
  394. repeatCount -= i;
  395. repeatCount = (repeatCount - 1) / sampleY + 1;
  396. // Make sure the repeatCount does not take us beyond the end of the dst
  397. if (dstRow + repeatCount > scaledHeight) {
  398. repeatCount = scaledHeight - dstRow;
  399. SkASSERT(repeatCount >= 1);
  400. }
  401. break;
  402. }
  403. }
  404. if (!foundNecessaryRow) {
  405. return;
  406. }
  407. } else {
  408. // Make sure the repeatCount does not take us beyond the end of the dst
  409. SkASSERT(this->dstInfo().height() >= yBegin);
  410. repeatCount = SkTMin(repeatCount, this->dstInfo().height() - yBegin);
  411. }
  412. if (!fFilledBackground) {
  413. // At this point, we are definitely going to write the row, so count it towards the number
  414. // of rows decoded.
  415. // We do not consider the repeatCount, which only happens for interlaced, in which case we
  416. // have already set fRowsDecoded to the proper value (reflecting that we have filled the
  417. // background).
  418. fRowsDecoded++;
  419. }
  420. // decodeFrame will early exit if this is false, so this method will not be
  421. // called.
  422. SkASSERT(fCurrColorTableIsReal);
  423. // The swizzler takes care of offsetting into the dst width-wise.
  424. void* dstLine = SkTAddOffset<void>(fDst, dstRow * fDstRowBytes);
  425. // We may or may not need to write transparent pixels to the buffer.
  426. // If we're compositing against a previous image, it's wrong, but if
  427. // we're decoding an interlaced gif and displaying it "Haeberli"-style,
  428. // we must write these for passes beyond the first, or the initial passes
  429. // will "show through" the later ones.
  430. const auto dstInfo = this->dstInfo();
  431. if (writeTransparentPixels) {
  432. this->applyXformRow(dstInfo, dstLine, rowBegin);
  433. } else {
  434. this->applyXformRow(dstInfo, fTmpBuffer.get(), rowBegin);
  435. size_t offsetBytes = fSwizzler->swizzleOffsetBytes();
  436. if (dstInfo.colorType() == kRGBA_F16_SkColorType) {
  437. // Account for the fact that post-swizzling we converted to F16,
  438. // which is twice as wide.
  439. offsetBytes *= 2;
  440. }
  441. const void* src = SkTAddOffset<void>(fTmpBuffer.get(), offsetBytes);
  442. void* dst = SkTAddOffset<void>(dstLine, offsetBytes);
  443. switch (dstInfo.colorType()) {
  444. case kBGRA_8888_SkColorType:
  445. case kRGBA_8888_SkColorType:
  446. blend_line<uint32_t>(dst, src, fSwizzler->swizzleWidth());
  447. break;
  448. case kRGBA_F16_SkColorType:
  449. blend_line<uint64_t>(dst, src, fSwizzler->swizzleWidth());
  450. break;
  451. default:
  452. SkASSERT(false);
  453. return;
  454. }
  455. }
  456. // Tell the frame to copy the row data if need be.
  457. if (repeatCount > 1) {
  458. const size_t bytesPerPixel = this->dstInfo().bytesPerPixel();
  459. const size_t bytesToCopy = fSwizzler->swizzleWidth() * bytesPerPixel;
  460. void* copiedLine = SkTAddOffset<void>(dstLine, fSwizzler->swizzleOffsetBytes());
  461. void* dst = copiedLine;
  462. for (int i = 1; i < repeatCount; i++) {
  463. dst = SkTAddOffset<void>(dst, fDstRowBytes);
  464. memcpy(dst, copiedLine, bytesToCopy);
  465. }
  466. }
  467. }