nsGIFDecoder2.cpp 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2. *
  3. * This Source Code Form is subject to the terms of the Mozilla Public
  4. * License, v. 2.0. If a copy of the MPL was not distributed with this
  5. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  6. /*
  7. The Graphics Interchange Format(c) is the copyright property of CompuServe
  8. Incorporated. Only CompuServe Incorporated is authorized to define, redefine,
  9. enhance, alter, modify or change in any way the definition of the format.
  10. CompuServe Incorporated hereby grants a limited, non-exclusive, royalty-free
  11. license for the use of the Graphics Interchange Format(sm) in computer
  12. software; computer software utilizing GIF(sm) must acknowledge ownership of the
  13. Graphics Interchange Format and its Service Mark by CompuServe Incorporated, in
  14. User and Technical Documentation. Computer software utilizing GIF, which is
  15. distributed or may be distributed without User or Technical Documentation must
  16. display to the screen or printer a message acknowledging ownership of the
  17. Graphics Interchange Format and the Service Mark by CompuServe Incorporated; in
  18. this case, the acknowledgement may be displayed in an opening screen or leading
  19. banner, or a closing screen or trailing banner. A message such as the following
  20. may be used:
  21. "The Graphics Interchange Format(c) is the Copyright property of
  22. CompuServe Incorporated. GIF(sm) is a Service Mark property of
  23. CompuServe Incorporated."
  24. For further information, please contact :
  25. CompuServe Incorporated
  26. Graphics Technology Department
  27. 5000 Arlington Center Boulevard
  28. Columbus, Ohio 43220
  29. U. S. A.
  30. CompuServe Incorporated maintains a mailing list with all those individuals and
  31. organizations who wish to receive copies of this document when it is corrected
  32. or revised. This service is offered free of charge; please provide us with your
  33. mailing address.
  34. */
  35. #include "nsGIFDecoder2.h"
  36. #include <stddef.h>
  37. #include "imgFrame.h"
  38. #include "mozilla/EndianUtils.h"
  39. #include "nsIInputStream.h"
  40. #include "RasterImage.h"
  41. #include "SurfacePipeFactory.h"
  42. #include "gfxColor.h"
  43. #include "gfxPlatform.h"
  44. #include "qcms.h"
  45. #include <algorithm>
  46. #include "mozilla/Telemetry.h"
  47. using namespace mozilla::gfx;
  48. using std::max;
  49. namespace mozilla {
  50. namespace image {
  51. //////////////////////////////////////////////////////////////////////
  52. // GIF Decoder Implementation
  53. static const size_t GIF_HEADER_LEN = 6;
  54. static const size_t GIF_SCREEN_DESCRIPTOR_LEN = 7;
  55. static const size_t BLOCK_HEADER_LEN = 1;
  56. static const size_t SUB_BLOCK_HEADER_LEN = 1;
  57. static const size_t EXTENSION_HEADER_LEN = 2;
  58. static const size_t GRAPHIC_CONTROL_EXTENSION_LEN = 4;
  59. static const size_t APPLICATION_EXTENSION_LEN = 11;
  60. static const size_t IMAGE_DESCRIPTOR_LEN = 9;
  61. // Masks for reading color table information from packed fields in the screen
  62. // descriptor and image descriptor blocks.
  63. static const uint8_t PACKED_FIELDS_COLOR_TABLE_BIT = 0x80;
  64. static const uint8_t PACKED_FIELDS_INTERLACED_BIT = 0x40;
  65. static const uint8_t PACKED_FIELDS_TABLE_DEPTH_MASK = 0x07;
  66. nsGIFDecoder2::nsGIFDecoder2(RasterImage* aImage)
  67. : Decoder(aImage)
  68. , mLexer(Transition::To(State::GIF_HEADER, GIF_HEADER_LEN),
  69. Transition::TerminateSuccess())
  70. , mOldColor(0)
  71. , mCurrentFrameIndex(-1)
  72. , mColorTablePos(0)
  73. , mGIFOpen(false)
  74. , mSawTransparency(false)
  75. {
  76. // Clear out the structure, excluding the arrays.
  77. memset(&mGIFStruct, 0, sizeof(mGIFStruct));
  78. // Initialize as "animate once" in case no NETSCAPE2.0 extension is found.
  79. mGIFStruct.loop_count = 1;
  80. }
  81. nsGIFDecoder2::~nsGIFDecoder2()
  82. {
  83. free(mGIFStruct.local_colormap);
  84. }
  85. nsresult
  86. nsGIFDecoder2::FinishInternal()
  87. {
  88. MOZ_ASSERT(!HasError(), "Shouldn't call FinishInternal after error!");
  89. // If the GIF got cut off, handle it anyway
  90. if (!IsMetadataDecode() && mGIFOpen) {
  91. if (mCurrentFrameIndex == mGIFStruct.images_decoded) {
  92. EndImageFrame();
  93. }
  94. PostDecodeDone(mGIFStruct.loop_count - 1);
  95. mGIFOpen = false;
  96. }
  97. return NS_OK;
  98. }
  99. void
  100. nsGIFDecoder2::FlushImageData()
  101. {
  102. Maybe<SurfaceInvalidRect> invalidRect = mPipe.TakeInvalidRect();
  103. if (!invalidRect) {
  104. return;
  105. }
  106. PostInvalidation(invalidRect->mInputSpaceRect,
  107. Some(invalidRect->mOutputSpaceRect));
  108. }
  109. //******************************************************************************
  110. // GIF decoder callback methods. Part of public API for GIF2
  111. //******************************************************************************
  112. //******************************************************************************
  113. void
  114. nsGIFDecoder2::BeginGIF()
  115. {
  116. if (mGIFOpen) {
  117. return;
  118. }
  119. mGIFOpen = true;
  120. PostSize(mGIFStruct.screen_width, mGIFStruct.screen_height);
  121. }
  122. bool
  123. nsGIFDecoder2::CheckForTransparency(const IntRect& aFrameRect)
  124. {
  125. // Check if the image has a transparent color in its palette.
  126. if (mGIFStruct.is_transparent) {
  127. PostHasTransparency();
  128. return true;
  129. }
  130. if (mGIFStruct.images_decoded > 0) {
  131. return false; // We only care about first frame padding below.
  132. }
  133. // If we need padding on the first frame, that means we don't draw into part
  134. // of the image at all. Report that as transparency.
  135. IntRect imageRect(0, 0, mGIFStruct.screen_width, mGIFStruct.screen_height);
  136. if (!imageRect.IsEqualEdges(aFrameRect)) {
  137. PostHasTransparency();
  138. mSawTransparency = true; // Make sure we don't optimize it away.
  139. return true;
  140. }
  141. return false;
  142. }
  143. //******************************************************************************
  144. nsresult
  145. nsGIFDecoder2::BeginImageFrame(const IntRect& aFrameRect,
  146. uint16_t aDepth,
  147. bool aIsInterlaced)
  148. {
  149. MOZ_ASSERT(HasSize());
  150. bool hasTransparency = CheckForTransparency(aFrameRect);
  151. gfx::SurfaceFormat format = hasTransparency ? SurfaceFormat::B8G8R8A8
  152. : SurfaceFormat::B8G8R8X8;
  153. // Make sure there's no animation if we're downscaling.
  154. MOZ_ASSERT_IF(Size() != OutputSize(), !GetImageMetadata().HasAnimation());
  155. AnimationParams animParams {
  156. aFrameRect,
  157. FrameTimeout::FromRawMilliseconds(mGIFStruct.delay_time),
  158. uint32_t(mGIFStruct.images_decoded),
  159. BlendMethod::OVER,
  160. DisposalMethod(mGIFStruct.disposal_method)
  161. };
  162. SurfacePipeFlags pipeFlags = aIsInterlaced
  163. ? SurfacePipeFlags::DEINTERLACE
  164. : SurfacePipeFlags();
  165. Maybe<SurfacePipe> pipe;
  166. if (mGIFStruct.images_decoded == 0) {
  167. // The first frame may be displayed progressively.
  168. pipeFlags |= SurfacePipeFlags::PROGRESSIVE_DISPLAY;
  169. // The first frame is always decoded into an RGB surface.
  170. pipe =
  171. SurfacePipeFactory::CreateSurfacePipe(this, Size(), OutputSize(),
  172. aFrameRect, format,
  173. Some(animParams), pipeFlags);
  174. } else {
  175. // This is an animation frame (and not the first). To minimize the memory
  176. // usage of animations, the image data is stored in paletted form.
  177. //
  178. // We should never use paletted surfaces with a draw target directly, so
  179. // the only practical difference between B8G8R8A8 and B8G8R8X8 is the
  180. // cleared pixel value if we get truncated. We want 0 in that case to
  181. // ensure it is an acceptable value for the color map as was the case
  182. // historically.
  183. MOZ_ASSERT(Size() == OutputSize());
  184. pipe =
  185. SurfacePipeFactory::CreatePalettedSurfacePipe(this, Size(), aFrameRect,
  186. SurfaceFormat::B8G8R8A8,
  187. aDepth, Some(animParams),
  188. pipeFlags);
  189. }
  190. mCurrentFrameIndex = mGIFStruct.images_decoded;
  191. if (!pipe) {
  192. mPipe = SurfacePipe();
  193. return NS_ERROR_FAILURE;
  194. }
  195. mPipe = Move(*pipe);
  196. return NS_OK;
  197. }
  198. //******************************************************************************
  199. void
  200. nsGIFDecoder2::EndImageFrame()
  201. {
  202. Opacity opacity = Opacity::SOME_TRANSPARENCY;
  203. if (mGIFStruct.images_decoded == 0) {
  204. // We need to send invalidations for the first frame.
  205. FlushImageData();
  206. // The first frame was preallocated with alpha; if it wasn't transparent, we
  207. // should fix that. We can also mark it opaque unconditionally if we didn't
  208. // actually see any transparent pixels - this test is only valid for the
  209. // first frame.
  210. if (!mGIFStruct.is_transparent && !mSawTransparency) {
  211. opacity = Opacity::FULLY_OPAQUE;
  212. }
  213. }
  214. // Unconditionally increment images_decoded, because we unconditionally
  215. // append frames in BeginImageFrame(). This ensures that images_decoded
  216. // always refers to the frame in mImage we're currently decoding,
  217. // even if some of them weren't decoded properly and thus are blank.
  218. mGIFStruct.images_decoded++;
  219. // Tell the superclass we finished a frame
  220. PostFrameStop(opacity);
  221. // Reset the transparent pixel
  222. if (mOldColor) {
  223. mColormap[mGIFStruct.tpixel] = mOldColor;
  224. mOldColor = 0;
  225. }
  226. mCurrentFrameIndex = -1;
  227. }
  228. template <typename PixelSize>
  229. PixelSize
  230. nsGIFDecoder2::ColormapIndexToPixel(uint8_t aIndex)
  231. {
  232. MOZ_ASSERT(sizeof(PixelSize) == sizeof(uint32_t));
  233. // Retrieve the next color, clamping to the size of the colormap.
  234. uint32_t color = mColormap[aIndex & mColorMask];
  235. // Check for transparency.
  236. if (mGIFStruct.is_transparent) {
  237. mSawTransparency = mSawTransparency || color == 0;
  238. }
  239. return color;
  240. }
  241. template <>
  242. uint8_t
  243. nsGIFDecoder2::ColormapIndexToPixel<uint8_t>(uint8_t aIndex)
  244. {
  245. return aIndex & mColorMask;
  246. }
  247. template <typename PixelSize>
  248. NextPixel<PixelSize>
  249. nsGIFDecoder2::YieldPixel(const uint8_t* aData,
  250. size_t aLength,
  251. size_t* aBytesReadOut)
  252. {
  253. MOZ_ASSERT(aData);
  254. MOZ_ASSERT(aBytesReadOut);
  255. MOZ_ASSERT(mGIFStruct.stackp >= mGIFStruct.stack);
  256. // Advance to the next byte we should read.
  257. const uint8_t* data = aData + *aBytesReadOut;
  258. // If we don't have any decoded data to yield, try to read some input and
  259. // produce some.
  260. if (mGIFStruct.stackp == mGIFStruct.stack) {
  261. while (mGIFStruct.bits < mGIFStruct.codesize && *aBytesReadOut < aLength) {
  262. // Feed the next byte into the decoder's 32-bit input buffer.
  263. mGIFStruct.datum += int32_t(*data) << mGIFStruct.bits;
  264. mGIFStruct.bits += 8;
  265. data += 1;
  266. *aBytesReadOut += 1;
  267. }
  268. if (mGIFStruct.bits < mGIFStruct.codesize) {
  269. return AsVariant(WriteState::NEED_MORE_DATA);
  270. }
  271. // Get the leading variable-length symbol from the data stream.
  272. int code = mGIFStruct.datum & mGIFStruct.codemask;
  273. mGIFStruct.datum >>= mGIFStruct.codesize;
  274. mGIFStruct.bits -= mGIFStruct.codesize;
  275. const int clearCode = ClearCode();
  276. // Reset the dictionary to its original state, if requested
  277. if (code == clearCode) {
  278. mGIFStruct.codesize = mGIFStruct.datasize + 1;
  279. mGIFStruct.codemask = (1 << mGIFStruct.codesize) - 1;
  280. mGIFStruct.avail = clearCode + 2;
  281. mGIFStruct.oldcode = -1;
  282. return AsVariant(WriteState::NEED_MORE_DATA);
  283. }
  284. // Check for explicit end-of-stream code. It should only appear after all
  285. // image data, but if that was the case we wouldn't be in this function, so
  286. // this is always an error condition.
  287. if (code == (clearCode + 1)) {
  288. return AsVariant(WriteState::FAILURE);
  289. }
  290. if (mGIFStruct.oldcode == -1) {
  291. if (code >= MAX_BITS) {
  292. return AsVariant(WriteState::FAILURE); // The code's too big; something's wrong.
  293. }
  294. mGIFStruct.firstchar = mGIFStruct.oldcode = code;
  295. // Yield a pixel at the appropriate index in the colormap.
  296. mGIFStruct.pixels_remaining--;
  297. return AsVariant(ColormapIndexToPixel<PixelSize>(mGIFStruct.suffix[code]));
  298. }
  299. int incode = code;
  300. if (code >= mGIFStruct.avail) {
  301. *mGIFStruct.stackp++ = mGIFStruct.firstchar;
  302. code = mGIFStruct.oldcode;
  303. if (mGIFStruct.stackp >= mGIFStruct.stack + MAX_BITS) {
  304. return AsVariant(WriteState::FAILURE); // Stack overflow; something's wrong.
  305. }
  306. }
  307. while (code >= clearCode) {
  308. if ((code >= MAX_BITS) || (code == mGIFStruct.prefix[code])) {
  309. return AsVariant(WriteState::FAILURE);
  310. }
  311. *mGIFStruct.stackp++ = mGIFStruct.suffix[code];
  312. code = mGIFStruct.prefix[code];
  313. if (mGIFStruct.stackp >= mGIFStruct.stack + MAX_BITS) {
  314. return AsVariant(WriteState::FAILURE); // Stack overflow; something's wrong.
  315. }
  316. }
  317. *mGIFStruct.stackp++ = mGIFStruct.firstchar = mGIFStruct.suffix[code];
  318. // Define a new codeword in the dictionary.
  319. if (mGIFStruct.avail < 4096) {
  320. mGIFStruct.prefix[mGIFStruct.avail] = mGIFStruct.oldcode;
  321. mGIFStruct.suffix[mGIFStruct.avail] = mGIFStruct.firstchar;
  322. mGIFStruct.avail++;
  323. // If we've used up all the codewords of a given length increase the
  324. // length of codewords by one bit, but don't exceed the specified maximum
  325. // codeword size of 12 bits.
  326. if (((mGIFStruct.avail & mGIFStruct.codemask) == 0) &&
  327. (mGIFStruct.avail < 4096)) {
  328. mGIFStruct.codesize++;
  329. mGIFStruct.codemask += mGIFStruct.avail;
  330. }
  331. }
  332. mGIFStruct.oldcode = incode;
  333. }
  334. if (MOZ_UNLIKELY(mGIFStruct.stackp <= mGIFStruct.stack)) {
  335. MOZ_ASSERT_UNREACHABLE("No decoded data but we didn't return early?");
  336. return AsVariant(WriteState::FAILURE);
  337. }
  338. // Yield a pixel at the appropriate index in the colormap.
  339. mGIFStruct.pixels_remaining--;
  340. return AsVariant(ColormapIndexToPixel<PixelSize>(*--mGIFStruct.stackp));
  341. }
  342. /// Expand the colormap from RGB to Packed ARGB as needed by Cairo.
  343. /// And apply any LCMS transformation.
  344. static void
  345. ConvertColormap(uint32_t* aColormap, uint32_t aColors)
  346. {
  347. // Apply CMS transformation if enabled and available
  348. if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
  349. qcms_transform* transform = gfxPlatform::GetCMSRGBTransform();
  350. if (transform) {
  351. qcms_transform_data(transform, aColormap, aColormap, aColors);
  352. }
  353. }
  354. // Convert from the GIF's RGB format to the Cairo format.
  355. // Work from end to begin, because of the in-place expansion
  356. uint8_t* from = ((uint8_t*)aColormap) + 3 * aColors;
  357. uint32_t* to = aColormap + aColors;
  358. // Convert color entries to Cairo format
  359. // set up for loops below
  360. if (!aColors) {
  361. return;
  362. }
  363. uint32_t c = aColors;
  364. // copy as bytes until source pointer is 32-bit-aligned
  365. // NB: can't use 32-bit reads, they might read off the end of the buffer
  366. for (; (NS_PTR_TO_UINT32(from) & 0x3) && c; --c) {
  367. from -= 3;
  368. *--to = gfxPackedPixel(0xFF, from[0], from[1], from[2]);
  369. }
  370. // bulk copy of pixels.
  371. while (c >= 4) {
  372. from -= 12;
  373. to -= 4;
  374. c -= 4;
  375. GFX_BLOCK_RGB_TO_FRGB(from,to);
  376. }
  377. // copy remaining pixel(s)
  378. // NB: can't use 32-bit reads, they might read off the end of the buffer
  379. while (c--) {
  380. from -= 3;
  381. *--to = gfxPackedPixel(0xFF, from[0], from[1], from[2]);
  382. }
  383. }
  384. LexerResult
  385. nsGIFDecoder2::DoDecode(SourceBufferIterator& aIterator, IResumable* aOnResume)
  386. {
  387. MOZ_ASSERT(!HasError(), "Shouldn't call DoDecode after error!");
  388. return mLexer.Lex(aIterator, aOnResume,
  389. [=](State aState, const char* aData, size_t aLength) {
  390. switch(aState) {
  391. case State::GIF_HEADER:
  392. return ReadGIFHeader(aData);
  393. case State::SCREEN_DESCRIPTOR:
  394. return ReadScreenDescriptor(aData);
  395. case State::GLOBAL_COLOR_TABLE:
  396. return ReadGlobalColorTable(aData, aLength);
  397. case State::FINISHED_GLOBAL_COLOR_TABLE:
  398. return FinishedGlobalColorTable();
  399. case State::BLOCK_HEADER:
  400. return ReadBlockHeader(aData);
  401. case State::EXTENSION_HEADER:
  402. return ReadExtensionHeader(aData);
  403. case State::GRAPHIC_CONTROL_EXTENSION:
  404. return ReadGraphicControlExtension(aData);
  405. case State::APPLICATION_IDENTIFIER:
  406. return ReadApplicationIdentifier(aData);
  407. case State::NETSCAPE_EXTENSION_SUB_BLOCK:
  408. return ReadNetscapeExtensionSubBlock(aData);
  409. case State::NETSCAPE_EXTENSION_DATA:
  410. return ReadNetscapeExtensionData(aData);
  411. case State::IMAGE_DESCRIPTOR:
  412. return ReadImageDescriptor(aData);
  413. case State::FINISH_IMAGE_DESCRIPTOR:
  414. return FinishImageDescriptor(aData);
  415. case State::LOCAL_COLOR_TABLE:
  416. return ReadLocalColorTable(aData, aLength);
  417. case State::FINISHED_LOCAL_COLOR_TABLE:
  418. return FinishedLocalColorTable();
  419. case State::IMAGE_DATA_BLOCK:
  420. return ReadImageDataBlock(aData);
  421. case State::IMAGE_DATA_SUB_BLOCK:
  422. return ReadImageDataSubBlock(aData);
  423. case State::LZW_DATA:
  424. return ReadLZWData(aData, aLength);
  425. case State::SKIP_LZW_DATA:
  426. return Transition::ContinueUnbuffered(State::SKIP_LZW_DATA);
  427. case State::FINISHED_LZW_DATA:
  428. return Transition::To(State::IMAGE_DATA_SUB_BLOCK, SUB_BLOCK_HEADER_LEN);
  429. case State::SKIP_SUB_BLOCKS:
  430. return SkipSubBlocks(aData);
  431. case State::SKIP_DATA_THEN_SKIP_SUB_BLOCKS:
  432. return Transition::ContinueUnbuffered(State::SKIP_DATA_THEN_SKIP_SUB_BLOCKS);
  433. case State::FINISHED_SKIPPING_DATA:
  434. return Transition::To(State::SKIP_SUB_BLOCKS, SUB_BLOCK_HEADER_LEN);
  435. default:
  436. MOZ_CRASH("Unknown State");
  437. }
  438. });
  439. }
  440. LexerTransition<nsGIFDecoder2::State>
  441. nsGIFDecoder2::ReadGIFHeader(const char* aData)
  442. {
  443. // We retrieve the version here but because many GIF encoders set header
  444. // fields incorrectly, we barely use it; features which should only appear in
  445. // GIF89a are always accepted.
  446. if (strncmp(aData, "GIF87a", GIF_HEADER_LEN) == 0) {
  447. mGIFStruct.version = 87;
  448. } else if (strncmp(aData, "GIF89a", GIF_HEADER_LEN) == 0) {
  449. mGIFStruct.version = 89;
  450. } else {
  451. return Transition::TerminateFailure();
  452. }
  453. return Transition::To(State::SCREEN_DESCRIPTOR, GIF_SCREEN_DESCRIPTOR_LEN);
  454. }
  455. LexerTransition<nsGIFDecoder2::State>
  456. nsGIFDecoder2::ReadScreenDescriptor(const char* aData)
  457. {
  458. mGIFStruct.screen_width = LittleEndian::readUint16(aData + 0);
  459. mGIFStruct.screen_height = LittleEndian::readUint16(aData + 2);
  460. const uint8_t packedFields = aData[4];
  461. // XXX: Should we be capturing these values even if there is no global color
  462. // table?
  463. mGIFStruct.global_colormap_depth =
  464. (packedFields & PACKED_FIELDS_TABLE_DEPTH_MASK) + 1;
  465. mGIFStruct.global_colormap_count = 1 << mGIFStruct.global_colormap_depth;
  466. // We ignore several fields in the header. We don't care about the 'sort
  467. // flag', which indicates if the global color table's entries are sorted in
  468. // order of importance - if we need to render this image for a device with a
  469. // narrower color gamut than GIF supports we'll handle that at a different
  470. // layer. We have no use for the pixel aspect ratio as well. Finally, we
  471. // intentionally ignore the background color index, as implementing that
  472. // feature would not be web compatible - when a GIF image frame doesn't cover
  473. // the entire area of the image, the area that's not covered should always be
  474. // transparent.
  475. if (packedFields & PACKED_FIELDS_COLOR_TABLE_BIT) {
  476. MOZ_ASSERT(mColorTablePos == 0);
  477. // We read the global color table in unbuffered mode since it can be quite
  478. // large and it'd be preferable to avoid unnecessary copies.
  479. const size_t globalColorTableSize = 3 * mGIFStruct.global_colormap_count;
  480. return Transition::ToUnbuffered(State::FINISHED_GLOBAL_COLOR_TABLE,
  481. State::GLOBAL_COLOR_TABLE,
  482. globalColorTableSize);
  483. }
  484. return Transition::To(State::BLOCK_HEADER, BLOCK_HEADER_LEN);
  485. }
  486. LexerTransition<nsGIFDecoder2::State>
  487. nsGIFDecoder2::ReadGlobalColorTable(const char* aData, size_t aLength)
  488. {
  489. uint8_t* dest = reinterpret_cast<uint8_t*>(mGIFStruct.global_colormap)
  490. + mColorTablePos;
  491. memcpy(dest, aData, aLength);
  492. mColorTablePos += aLength;
  493. return Transition::ContinueUnbuffered(State::GLOBAL_COLOR_TABLE);
  494. }
  495. LexerTransition<nsGIFDecoder2::State>
  496. nsGIFDecoder2::FinishedGlobalColorTable()
  497. {
  498. ConvertColormap(mGIFStruct.global_colormap, mGIFStruct.global_colormap_count);
  499. mColorTablePos = 0;
  500. return Transition::To(State::BLOCK_HEADER, BLOCK_HEADER_LEN);
  501. }
  502. LexerTransition<nsGIFDecoder2::State>
  503. nsGIFDecoder2::ReadBlockHeader(const char* aData)
  504. {
  505. // Determine what type of block we're dealing with.
  506. switch (aData[0]) {
  507. case GIF_EXTENSION_INTRODUCER:
  508. return Transition::To(State::EXTENSION_HEADER, EXTENSION_HEADER_LEN);
  509. case GIF_IMAGE_SEPARATOR:
  510. return Transition::To(State::IMAGE_DESCRIPTOR, IMAGE_DESCRIPTOR_LEN);
  511. case GIF_TRAILER:
  512. FinishInternal();
  513. return Transition::TerminateSuccess();
  514. default:
  515. // If we get anything other than GIF_IMAGE_SEPARATOR,
  516. // GIF_EXTENSION_INTRODUCER, or GIF_TRAILER, there is extraneous data
  517. // between blocks. The GIF87a spec tells us to keep reading until we find
  518. // an image separator, but GIF89a says such a file is corrupt. We follow
  519. // GIF89a and bail out.
  520. if (mGIFStruct.images_decoded > 0) {
  521. // The file is corrupt, but we successfully decoded some frames, so we
  522. // may as well consider the decode successful and display them.
  523. FinishInternal();
  524. return Transition::TerminateSuccess();
  525. }
  526. // No images decoded; there is nothing to display.
  527. return Transition::TerminateFailure();
  528. }
  529. }
  530. LexerTransition<nsGIFDecoder2::State>
  531. nsGIFDecoder2::ReadExtensionHeader(const char* aData)
  532. {
  533. const uint8_t label = aData[0];
  534. const uint8_t extensionHeaderLength = aData[1];
  535. // If the extension header is zero length, just treat it as a block terminator
  536. // and move on to the next block immediately.
  537. if (extensionHeaderLength == 0) {
  538. return Transition::To(State::BLOCK_HEADER, BLOCK_HEADER_LEN);
  539. }
  540. switch (label) {
  541. case GIF_GRAPHIC_CONTROL_LABEL:
  542. // The GIF spec mandates that the Control Extension header block length is
  543. // 4 bytes, and the parser for this block reads 4 bytes, so we must
  544. // enforce that the buffer contains at least this many bytes. If the GIF
  545. // specifies a different length, we allow that, so long as it's larger;
  546. // the additional data will simply be ignored.
  547. return Transition::To(State::GRAPHIC_CONTROL_EXTENSION,
  548. max<uint8_t>(extensionHeaderLength,
  549. GRAPHIC_CONTROL_EXTENSION_LEN));
  550. case GIF_APPLICATION_EXTENSION_LABEL:
  551. // Again, the spec specifies that an application extension header is 11
  552. // bytes, but for compatibility with GIFs in the wild, we allow deviation
  553. // from the spec. This is important for real-world compatibility, as GIFs
  554. // in the wild exist with application extension headers that are both
  555. // shorter and longer than 11 bytes. However, we only try to actually
  556. // interpret the application extension if the length is correct;
  557. // otherwise, we just skip the block unconditionally.
  558. return extensionHeaderLength == APPLICATION_EXTENSION_LEN
  559. ? Transition::To(State::APPLICATION_IDENTIFIER, extensionHeaderLength)
  560. : Transition::ToUnbuffered(State::FINISHED_SKIPPING_DATA,
  561. State::SKIP_DATA_THEN_SKIP_SUB_BLOCKS,
  562. extensionHeaderLength);
  563. default:
  564. // Skip over any other type of extension block, including comment and
  565. // plain text blocks.
  566. return Transition::ToUnbuffered(State::FINISHED_SKIPPING_DATA,
  567. State::SKIP_DATA_THEN_SKIP_SUB_BLOCKS,
  568. extensionHeaderLength);
  569. }
  570. }
  571. LexerTransition<nsGIFDecoder2::State>
  572. nsGIFDecoder2::ReadGraphicControlExtension(const char* aData)
  573. {
  574. mGIFStruct.is_transparent = aData[0] & 0x1;
  575. mGIFStruct.tpixel = uint8_t(aData[3]);
  576. mGIFStruct.disposal_method = (aData[0] >> 2) & 0x7;
  577. if (mGIFStruct.disposal_method == 4) {
  578. // Some encoders (and apparently some specs) represent
  579. // DisposalMethod::RESTORE_PREVIOUS as 4, but 3 is used in the canonical
  580. // spec and is more popular, so we normalize to 3.
  581. mGIFStruct.disposal_method = 3;
  582. } else if (mGIFStruct.disposal_method > 4) {
  583. // This GIF is using a disposal method which is undefined in the spec.
  584. // Treat it as DisposalMethod::NOT_SPECIFIED.
  585. mGIFStruct.disposal_method = 0;
  586. }
  587. DisposalMethod method = DisposalMethod(mGIFStruct.disposal_method);
  588. if (method == DisposalMethod::CLEAR_ALL || method == DisposalMethod::CLEAR) {
  589. // We may have to display the background under this image during animation
  590. // playback, so we regard it as transparent.
  591. PostHasTransparency();
  592. }
  593. mGIFStruct.delay_time = LittleEndian::readUint16(aData + 1) * 10;
  594. if (mGIFStruct.delay_time > 0) {
  595. PostIsAnimated(FrameTimeout::FromRawMilliseconds(mGIFStruct.delay_time));
  596. }
  597. return Transition::To(State::SKIP_SUB_BLOCKS, SUB_BLOCK_HEADER_LEN);
  598. }
  599. LexerTransition<nsGIFDecoder2::State>
  600. nsGIFDecoder2::ReadApplicationIdentifier(const char* aData)
  601. {
  602. if ((strncmp(aData, "NETSCAPE2.0", 11) == 0) ||
  603. (strncmp(aData, "ANIMEXTS1.0", 11) == 0)) {
  604. // This is a Netscape application extension block.
  605. return Transition::To(State::NETSCAPE_EXTENSION_SUB_BLOCK,
  606. SUB_BLOCK_HEADER_LEN);
  607. }
  608. // This is an application extension we don't care about. Just skip it.
  609. return Transition::To(State::SKIP_SUB_BLOCKS, SUB_BLOCK_HEADER_LEN);
  610. }
  611. LexerTransition<nsGIFDecoder2::State>
  612. nsGIFDecoder2::ReadNetscapeExtensionSubBlock(const char* aData)
  613. {
  614. const uint8_t blockLength = aData[0];
  615. if (blockLength == 0) {
  616. // We hit the block terminator.
  617. return Transition::To(State::BLOCK_HEADER, BLOCK_HEADER_LEN);
  618. }
  619. // We consume a minimum of 3 bytes in accordance with the specs for the
  620. // Netscape application extension block, such as they are.
  621. const size_t extensionLength = max<uint8_t>(blockLength, 3);
  622. return Transition::To(State::NETSCAPE_EXTENSION_DATA, extensionLength);
  623. }
  624. LexerTransition<nsGIFDecoder2::State>
  625. nsGIFDecoder2::ReadNetscapeExtensionData(const char* aData)
  626. {
  627. // Documentation for NETSCAPE2.0 / ANIMEXTS1.0 extensions can be found at:
  628. // https://wiki.whatwg.org/wiki/GIF
  629. static const uint8_t NETSCAPE_LOOPING_EXTENSION_SUB_BLOCK_ID = 1;
  630. static const uint8_t NETSCAPE_BUFFERING_EXTENSION_SUB_BLOCK_ID = 2;
  631. const uint8_t subBlockID = aData[0] & 7;
  632. switch (subBlockID) {
  633. case NETSCAPE_LOOPING_EXTENSION_SUB_BLOCK_ID:
  634. // This is looping extension.
  635. mGIFStruct.loop_count = LittleEndian::readUint16(aData + 1);
  636. return Transition::To(State::NETSCAPE_EXTENSION_SUB_BLOCK,
  637. SUB_BLOCK_HEADER_LEN);
  638. case NETSCAPE_BUFFERING_EXTENSION_SUB_BLOCK_ID:
  639. // We allow, but ignore, this extension.
  640. return Transition::To(State::NETSCAPE_EXTENSION_SUB_BLOCK,
  641. SUB_BLOCK_HEADER_LEN);
  642. default:
  643. return Transition::TerminateFailure();
  644. }
  645. }
  646. LexerTransition<nsGIFDecoder2::State>
  647. nsGIFDecoder2::ReadImageDescriptor(const char* aData)
  648. {
  649. // On the first frame, we don't need to yield, and none of the other checks
  650. // below apply, so we can just jump right into FinishImageDescriptor().
  651. if (mGIFStruct.images_decoded == 0) {
  652. return FinishImageDescriptor(aData);
  653. }
  654. if (!HasAnimation()) {
  655. // We should've already called PostIsAnimated(); this must be a corrupt
  656. // animated image with a first frame timeout of zero. Signal that we're
  657. // animated now, before the first-frame decode early exit below, so that
  658. // RasterImage can detect that this happened.
  659. PostIsAnimated(FrameTimeout::FromRawMilliseconds(0));
  660. }
  661. if (IsFirstFrameDecode()) {
  662. // We're about to get a second frame, but we only want the first. Stop
  663. // decoding now.
  664. FinishInternal();
  665. return Transition::TerminateSuccess();
  666. }
  667. MOZ_ASSERT(Size() == OutputSize(), "Downscaling an animated image?");
  668. // Yield to allow access to the previous frame before we start a new one.
  669. return Transition::ToAfterYield(State::FINISH_IMAGE_DESCRIPTOR);
  670. }
  671. LexerTransition<nsGIFDecoder2::State>
  672. nsGIFDecoder2::FinishImageDescriptor(const char* aData)
  673. {
  674. IntRect frameRect;
  675. // Get image offsets with respect to the screen origin.
  676. frameRect.x = LittleEndian::readUint16(aData + 0);
  677. frameRect.y = LittleEndian::readUint16(aData + 2);
  678. frameRect.width = LittleEndian::readUint16(aData + 4);
  679. frameRect.height = LittleEndian::readUint16(aData + 6);
  680. if (!mGIFStruct.images_decoded) {
  681. // Work around GIF files where
  682. // * at least one of the logical screen dimensions is smaller than the
  683. // same dimension in the first image, or
  684. // * GIF87a files where the first image's dimensions do not match the
  685. // logical screen dimensions.
  686. if (mGIFStruct.screen_height < frameRect.height ||
  687. mGIFStruct.screen_width < frameRect.width ||
  688. mGIFStruct.version == 87) {
  689. mGIFStruct.screen_height = frameRect.height;
  690. mGIFStruct.screen_width = frameRect.width;
  691. frameRect.MoveTo(0, 0);
  692. }
  693. // Create the image container with the right size.
  694. BeginGIF();
  695. if (HasError()) {
  696. // Setting the size led to an error.
  697. return Transition::TerminateFailure();
  698. }
  699. // If we're doing a metadata decode, we're done.
  700. if (IsMetadataDecode()) {
  701. CheckForTransparency(frameRect);
  702. FinishInternal();
  703. return Transition::TerminateSuccess();
  704. }
  705. }
  706. // Work around broken GIF files that have zero frame width or height; in this
  707. // case, we'll treat the frame as having the same size as the overall image.
  708. if (frameRect.height == 0 || frameRect.width == 0) {
  709. frameRect.height = mGIFStruct.screen_height;
  710. frameRect.width = mGIFStruct.screen_width;
  711. // If that still resulted in zero frame width or height, give up.
  712. if (frameRect.height == 0 || frameRect.width == 0) {
  713. return Transition::TerminateFailure();
  714. }
  715. }
  716. // Determine |depth| (log base 2 of the number of colors in the palette).
  717. bool haveLocalColorTable = false;
  718. uint16_t depth = 0;
  719. uint8_t packedFields = aData[8];
  720. if (packedFields & PACKED_FIELDS_COLOR_TABLE_BIT) {
  721. // Get the palette depth from the local color table.
  722. depth = (packedFields & PACKED_FIELDS_TABLE_DEPTH_MASK) + 1;
  723. haveLocalColorTable = true;
  724. } else {
  725. // Get the palette depth from the global color table.
  726. depth = mGIFStruct.global_colormap_depth;
  727. }
  728. // If the transparent color index is greater than the number of colors in the
  729. // color table, we may need a higher color depth than |depth| would specify.
  730. // Our internal representation of the image will instead use |realDepth|,
  731. // which is the smallest color depth that can accomodate the existing palette
  732. // *and* the transparent color index.
  733. uint16_t realDepth = depth;
  734. while (mGIFStruct.tpixel >= (1 << realDepth) &&
  735. realDepth < 8) {
  736. realDepth++;
  737. }
  738. // Create a mask used to ensure that color values fit within the colormap.
  739. mColorMask = 0xFF >> (8 - realDepth);
  740. // Determine if this frame is interlaced or not.
  741. const bool isInterlaced = packedFields & PACKED_FIELDS_INTERLACED_BIT;
  742. // Create the SurfacePipe we'll use to write output for this frame.
  743. if (NS_FAILED(BeginImageFrame(frameRect, realDepth, isInterlaced))) {
  744. return Transition::TerminateFailure();
  745. }
  746. // Clear state from last image.
  747. mGIFStruct.pixels_remaining = frameRect.width * frameRect.height;
  748. if (haveLocalColorTable) {
  749. // We have a local color table, so prepare to read it into the palette of
  750. // the current frame.
  751. mGIFStruct.local_colormap_size = 1 << depth;
  752. if (mGIFStruct.images_decoded == 0) {
  753. // The first frame has a local color table. Allocate space for it as we
  754. // use a BGRA or BGRX surface for the first frame; such surfaces don't
  755. // have their own palettes internally.
  756. mColormapSize = sizeof(uint32_t) << realDepth;
  757. if (!mGIFStruct.local_colormap) {
  758. mGIFStruct.local_colormap =
  759. static_cast<uint32_t*>(moz_xmalloc(mColormapSize));
  760. }
  761. mColormap = mGIFStruct.local_colormap;
  762. }
  763. const size_t size = 3 << depth;
  764. if (mColormapSize > size) {
  765. // Clear the part of the colormap which will be unused with this palette.
  766. // If a GIF references an invalid palette entry, ensure the entry is opaque white.
  767. // This is needed for Skia as if it isn't, RGBX surfaces will cause blending issues
  768. // with Skia.
  769. memset(reinterpret_cast<uint8_t*>(mColormap) + size, 0xFF,
  770. mColormapSize - size);
  771. }
  772. MOZ_ASSERT(mColorTablePos == 0);
  773. // We read the local color table in unbuffered mode since it can be quite
  774. // large and it'd be preferable to avoid unnecessary copies.
  775. return Transition::ToUnbuffered(State::FINISHED_LOCAL_COLOR_TABLE,
  776. State::LOCAL_COLOR_TABLE,
  777. size);
  778. }
  779. // There's no local color table; copy the global color table into the palette
  780. // of the current frame.
  781. if (mGIFStruct.images_decoded > 0) {
  782. memcpy(mColormap, mGIFStruct.global_colormap, mColormapSize);
  783. } else {
  784. mColormap = mGIFStruct.global_colormap;
  785. }
  786. return Transition::To(State::IMAGE_DATA_BLOCK, BLOCK_HEADER_LEN);
  787. }
  788. LexerTransition<nsGIFDecoder2::State>
  789. nsGIFDecoder2::ReadLocalColorTable(const char* aData, size_t aLength)
  790. {
  791. uint8_t* dest = reinterpret_cast<uint8_t*>(mColormap) + mColorTablePos;
  792. memcpy(dest, aData, aLength);
  793. mColorTablePos += aLength;
  794. return Transition::ContinueUnbuffered(State::LOCAL_COLOR_TABLE);
  795. }
  796. LexerTransition<nsGIFDecoder2::State>
  797. nsGIFDecoder2::FinishedLocalColorTable()
  798. {
  799. ConvertColormap(mColormap, mGIFStruct.local_colormap_size);
  800. mColorTablePos = 0;
  801. return Transition::To(State::IMAGE_DATA_BLOCK, BLOCK_HEADER_LEN);
  802. }
  803. LexerTransition<nsGIFDecoder2::State>
  804. nsGIFDecoder2::ReadImageDataBlock(const char* aData)
  805. {
  806. // Make sure the transparent pixel is transparent in the colormap.
  807. if (mGIFStruct.is_transparent) {
  808. // Save the old value so we can restore it later.
  809. if (mColormap == mGIFStruct.global_colormap) {
  810. mOldColor = mColormap[mGIFStruct.tpixel];
  811. }
  812. mColormap[mGIFStruct.tpixel] = 0;
  813. }
  814. // Initialize the LZW decoder.
  815. mGIFStruct.datasize = uint8_t(aData[0]);
  816. const int clearCode = ClearCode();
  817. if (mGIFStruct.datasize > MAX_LZW_BITS || clearCode >= MAX_BITS) {
  818. return Transition::TerminateFailure();
  819. }
  820. mGIFStruct.avail = clearCode + 2;
  821. mGIFStruct.oldcode = -1;
  822. mGIFStruct.codesize = mGIFStruct.datasize + 1;
  823. mGIFStruct.codemask = (1 << mGIFStruct.codesize) - 1;
  824. mGIFStruct.datum = mGIFStruct.bits = 0;
  825. // Initialize the tables.
  826. for (int i = 0; i < clearCode; i++) {
  827. mGIFStruct.suffix[i] = i;
  828. }
  829. mGIFStruct.stackp = mGIFStruct.stack;
  830. // Begin reading image data sub-blocks.
  831. return Transition::To(State::IMAGE_DATA_SUB_BLOCK, SUB_BLOCK_HEADER_LEN);
  832. }
  833. LexerTransition<nsGIFDecoder2::State>
  834. nsGIFDecoder2::ReadImageDataSubBlock(const char* aData)
  835. {
  836. const uint8_t subBlockLength = aData[0];
  837. if (subBlockLength == 0) {
  838. // We hit the block terminator.
  839. EndImageFrame();
  840. return Transition::To(State::BLOCK_HEADER, BLOCK_HEADER_LEN);
  841. }
  842. if (mGIFStruct.pixels_remaining == 0) {
  843. // We've already written to the entire image; we should've hit the block
  844. // terminator at this point. This image is corrupt, but we'll tolerate it.
  845. if (subBlockLength == GIF_TRAILER) {
  846. // This GIF is missing the block terminator for the final block; we'll put
  847. // up with it.
  848. FinishInternal();
  849. return Transition::TerminateSuccess();
  850. }
  851. // We're not at the end of the image, so just skip the extra data.
  852. return Transition::ToUnbuffered(State::FINISHED_LZW_DATA,
  853. State::SKIP_LZW_DATA,
  854. subBlockLength);
  855. }
  856. // Handle the standard case: there's data in the sub-block and pixels left to
  857. // fill in the image. We read the sub-block unbuffered so we can get pixels on
  858. // the screen as soon as possible.
  859. return Transition::ToUnbuffered(State::FINISHED_LZW_DATA,
  860. State::LZW_DATA,
  861. subBlockLength);
  862. }
  863. LexerTransition<nsGIFDecoder2::State>
  864. nsGIFDecoder2::ReadLZWData(const char* aData, size_t aLength)
  865. {
  866. const uint8_t* data = reinterpret_cast<const uint8_t*>(aData);
  867. size_t length = aLength;
  868. while (mGIFStruct.pixels_remaining > 0 &&
  869. (length > 0 || mGIFStruct.bits >= mGIFStruct.codesize)) {
  870. size_t bytesRead = 0;
  871. auto result = mGIFStruct.images_decoded == 0
  872. ? mPipe.WritePixels<uint32_t>([&]{ return YieldPixel<uint32_t>(data, length, &bytesRead); })
  873. : mPipe.WritePixels<uint8_t>([&]{ return YieldPixel<uint8_t>(data, length, &bytesRead); });
  874. if (MOZ_UNLIKELY(bytesRead > length)) {
  875. MOZ_ASSERT_UNREACHABLE("Overread?");
  876. bytesRead = length;
  877. }
  878. // Advance our position in the input based upon what YieldPixel() consumed.
  879. data += bytesRead;
  880. length -= bytesRead;
  881. switch (result) {
  882. case WriteState::NEED_MORE_DATA:
  883. continue;
  884. case WriteState::FINISHED:
  885. NS_WARNING_ASSERTION(mGIFStruct.pixels_remaining <= 0,
  886. "too many pixels");
  887. mGIFStruct.pixels_remaining = 0;
  888. break;
  889. case WriteState::FAILURE:
  890. return Transition::TerminateFailure();
  891. }
  892. }
  893. // We're done, but keep going until we consume all the data in the sub-block.
  894. return Transition::ContinueUnbuffered(State::LZW_DATA);
  895. }
  896. LexerTransition<nsGIFDecoder2::State>
  897. nsGIFDecoder2::SkipSubBlocks(const char* aData)
  898. {
  899. // In the SKIP_SUB_BLOCKS state we skip over data sub-blocks that we're not
  900. // interested in. Blocks consist of a block header (which can be up to 255
  901. // bytes in length) and a series of data sub-blocks. Each data sub-block
  902. // consists of a single byte length value, followed by the data itself. A data
  903. // sub-block with a length of zero terminates the overall block.
  904. // SKIP_SUB_BLOCKS reads a sub-block length value. If it's zero, we've arrived
  905. // at the next block. Otherwise, we enter the SKIP_DATA_THEN_SKIP_SUB_BLOCKS
  906. // state to skip over the sub-block data and return to SKIP_SUB_BLOCKS at the
  907. // start of the next sub-block.
  908. const uint8_t nextSubBlockLength = aData[0];
  909. if (nextSubBlockLength == 0) {
  910. // We hit the block terminator, so the sequence of data sub-blocks is over;
  911. // begin processing another block.
  912. return Transition::To(State::BLOCK_HEADER, BLOCK_HEADER_LEN);
  913. }
  914. // Skip to the next sub-block length value.
  915. return Transition::ToUnbuffered(State::FINISHED_SKIPPING_DATA,
  916. State::SKIP_DATA_THEN_SKIP_SUB_BLOCKS,
  917. nextSubBlockLength);
  918. }
  919. Maybe<Telemetry::ID>
  920. nsGIFDecoder2::SpeedHistogram() const
  921. {
  922. return Some(Telemetry::IMAGE_DECODE_SPEED_GIF);
  923. }
  924. } // namespace image
  925. } // namespace mozilla