Browse Source

import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1194059 (Part 1) - Ensure that metadata decode progress is always delivered atomically. r=tn (07f0441600)
- Bug 1191090 - Use the normal PNG decoder for PNG metadata decodes. r=tn (ce3fe1be5f)
- Bug 1191114 (Part 1) - Always detect HAS_TRANSPARENCY during the metadata decode. r=tn (3841132932)
- Bug 1191114 (Part 2) - Add support for creating an anonymous metadata decoder, for use in tests. r=tn (2cdcc0c278)
- Bug 1191114 (Part 3) - Add flags to image test cases. r=tn (4a6f5a5230)
- Bug 1191114 (Part 4) - Add tests for metadata decoding, including that we always deliver HAS_TRANSPARENCY during the metadata decode. r=tn (b9c5d1cd4a)
- Bug 1126330 - Remove the check for non-looping animations. r=seth (828dabba24)
- Bug 1194059 (Part 2) - Always detect IS_ANIMATED during the metadatadecode. r=tn (0ba5bf38f1)
- Bug 1194059 (Part 3) - Ensure the nsIInputStream LoadImage() returns is always buffered. r=tn (ed2b02205b)
- Bug 1194059 (Part 4) - Add tests that we detect IS_ANIMATED during the metadata decode. r=tn (298f14a7c9)
- Bug 1188705 (part 1) - Remove gfxASurface::GetMemoryLocation(). r=seth. (1f0da73a08)
- Bug 1188705 (part 2) - Remove unused SizeOfDecodedWithComputedFallbackIfHeap declaration. r=seth. (3356dbed06)
- Bug 1188705 (part 3) - Simplify imgFrame::SizeOfExcludingThis(). r=seth. (563262a834)
- Bug 1155252 - Don't allocate X11TextureClients bigger than xlib's maximum surface size. r=jrmuizel (3f11590667)
- Bug 1143994 - Fix some -Wunreachable-code and -Wswitch warnings in imagelib. r=seth (008becc7e2)
- Bug 1060609 (Part 1) - Disable downscale-during-decode when HQ scaling is disabled. r=tn (6da77e3cad)
- Bug 1187569 - PNGs getting stuck in a pixelated state. r=seth (da305ef99c)
- Bug 1194900 - Stop deciding when to send invalidations in nsPNGDecoder and let Decoder handle it. r=tn (50fa14a984)
- Bug 1151694 - Part 1 - Move CommonAnimationManager::sLayerAnimationInfo into LayerAnimationInfo.(cpp|h). r=bbirtles (9f93e0d569)
-  Bug 1151694 - Part 2 - imgTools should be inside mozilla::image namespace. r=bbirtles (8dfc3f2e4b)
- Bug 1196066 (Part 1) - Fix bad directory entries in two of our ICO reftests. r=tn (9e4c70d2b4)
- Bug 1196065 - Add sanity tests for image decoders. r=tn (557b9131cb)
- Bug 1194912 (Part 1) - Add CopyOnWrite<T> to support automatic copy-on-write for recursive writes to data structures. r=tn (b081a50716)
- Bug 1196066 (Part 2) - Add a streaming lexing framework to ImageLib. r=tn (59eb634ea5)
- Bug 1196476 - Replace ProgressTracker::FirstObserverIs() with a simpler mechanism on imgRequest. r=tn (db9ecc65ef)
- missing part of Bug 1139225 (Part 2) - Dispatch OnImageAvailable to the main thread manually in imgRequest. r=tn (e7b22db614)
- Bug 1194912 (Part 2) - Store ProgressTracker observers in a copy-on-write hash table, and dispatch notifications to them using a template. r=tn (5efd7b38b3)
- Bug 1180225. Make convolver more like upstream. r=seth (18e3c168fc)
- Bug 1149318 - Fix the calling convention on SkGetUserDefaultLocaleNameProc. r=eihrul (7b750d4e4e)
- Bug 1210493 - enlarge stroke bounds by line width when doing a quick-reject in SkDraw::drawRect. r=jmuizelaar (e8b5d0fe2d)
- Bug 1188206 - Fix more constructors in gfx; r=jrmuizel (944ea9938c)
master
roytam1 1 month ago
parent
commit
b75b514a74
  1. 2
      gfx/2d/RecordedEvent.h
  2. 92
      gfx/2d/convolver.cpp
  3. 77
      gfx/2d/convolverSSE2.cpp
  4. 4
      gfx/2d/convolverSSE2.h
  5. 4
      gfx/gl/HeapCopyOfStackArray.h
  6. 4
      gfx/layers/basic/TextureClientX11.cpp
  7. 26
      gfx/skia/skia/src/core/SkDraw.cpp
  8. 2
      gfx/skia/skia/src/utils/win/SkDWrite.h
  9. 6
      gfx/thebes/gfxASurface.cpp
  10. 6
      gfx/thebes/gfxASurface.h
  11. 11
      gfx/thebes/gfxTypes.h
  12. 6
      gfx/thebes/gfxWindowsSurface.cpp
  13. 4
      gfx/thebes/gfxWindowsSurface.h
  14. 11
      gfx/thebes/gfxXlibSurface.cpp
  15. 10
      gfx/thebes/gfxXlibSurface.h
  16. 250
      image/CopyOnWrite.h
  17. 5
      image/DecodePool.cpp
  18. 38
      image/Decoder.cpp
  19. 37
      image/Decoder.h
  20. 66
      image/DecoderFactory.cpp
  21. 64
      image/DecoderFactory.h
  22. 6
      image/Downscaler.cpp
  23. 26
      image/FrameAnimator.cpp
  24. 10
      image/FrameAnimator.h
  25. 2
      image/Image.h
  26. 44
      image/ImageMetadata.cpp
  27. 42
      image/ImageMetadata.h
  28. 178
      image/ProgressTracker.cpp
  29. 61
      image/ProgressTracker.h
  30. 177
      image/RasterImage.cpp
  31. 33
      image/RasterImage.h
  32. 4
      image/SourceBuffer.cpp
  33. 355
      image/StreamingLexer.h
  34. 9
      image/SurfaceCache.cpp
  35. 2
      image/SurfaceCache.h
  36. 2
      image/decoders/GIF2.h
  37. 9
      image/decoders/nsBMPDecoder.cpp
  38. 79
      image/decoders/nsGIFDecoder2.cpp
  39. 1
      image/decoders/nsGIFDecoder2.h
  40. 8
      image/decoders/nsICODecoder.cpp
  41. 7
      image/decoders/nsJPEGDecoder.cpp
  42. 209
      image/decoders/nsPNGDecoder.cpp
  43. 3
      image/decoders/nsPNGDecoder.h
  44. 42
      image/imgFrame.cpp
  45. 4
      image/imgFrame.h
  46. 10
      image/imgRequest.cpp
  47. 4
      image/imgRequest.h
  48. 7
      image/imgTools.cpp
  49. 7
      image/imgTools.h
  50. 1
      image/moz.build
  51. 74
      image/test/gtest/Common.cpp
  52. 24
      image/test/gtest/Common.h
  53. 235
      image/test/gtest/TestCopyOnWrite.cpp
  54. 3
      image/test/gtest/TestDecodeToSurface.cpp
  55. 249
      image/test/gtest/TestDecoders.cpp
  56. 254
      image/test/gtest/TestMetadata.cpp
  57. 266
      image/test/gtest/TestStreamingLexer.cpp
  58. 0
      image/test/gtest/first-frame-padding.gif
  59. 11
      image/test/gtest/moz.build
  60. BIN
      image/test/gtest/no-frame-delay.gif
  61. BIN
      image/test/gtest/rle4.bmp
  62. BIN
      image/test/gtest/rle8.bmp
  63. BIN
      image/test/gtest/transparent.bmp
  64. BIN
      image/test/gtest/transparent.gif
  65. BIN
      image/test/gtest/transparent.png
  66. 6
      image/test/mochitest/test_has_transparency.html
  67. BIN
      image/test/reftest/ico/ico-png/ico-size-1x1-png.ico
  68. BIN
      image/test/reftest/ico/ico-png/ico-size-256x256-png.ico
  69. 11
      layout/base/RestyleManager.cpp
  70. 3
      layout/build/nsLayoutStatics.cpp
  71. 66
      layout/style/AnimationCommon.cpp
  72. 16
      layout/style/AnimationCommon.h
  73. 53
      layout/style/LayerAnimationInfo.cpp
  74. 33
      layout/style/LayerAnimationInfo.h
  75. 2
      layout/style/moz.build

2
gfx/2d/RecordedEvent.h

@ -36,7 +36,7 @@ struct ReferencePtr
{}
template <typename T>
ReferencePtr(const RefPtr<T>& aPtr)
MOZ_IMPLICIT ReferencePtr(const RefPtr<T>& aPtr)
: mLongPtr(uint64_t(aPtr.get()))
{}

92
gfx/2d/convolver.cpp

@ -175,11 +175,11 @@ class CircularRowBuffer {
// |src_data| and continues for the [begin, end) of the filter.
template<bool has_alpha>
void ConvolveHorizontally(const unsigned char* src_data,
int begin, int end,
const ConvolutionFilter1D& filter,
unsigned char* out_row) {
int num_values = filter.num_values();
// Loop over each pixel on this row in the output image.
for (int out_x = begin; out_x < end; out_x++) {
for (int out_x = 0; out_x < num_values; out_x++) {
// Get the filter that determines the current output pixel.
int filter_offset, filter_length;
const ConvolutionFilter1D::Fixed* filter_values =
@ -220,17 +220,18 @@ void ConvolveHorizontally(const unsigned char* src_data,
// Does vertical convolution to produce one output row. The filter values and
// length are given in the first two parameters. These are applied to each
// of the rows pointed to in the |source_data_rows| array, with each row
// being |end - begin| wide.
// being |pixel_width| wide.
//
// The output must have room for |(end - begin) * 4| bytes.
// The output must have room for |pixel_width * 4| bytes.
template<bool has_alpha>
void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
int filter_length,
unsigned char* const* source_data_rows,
int begin, int end, unsigned char* out_row) {
int pixel_width,
unsigned char* out_row) {
// We go through each column in the output and do a vertical convolution,
// generating one output pixel each time.
for (int out_x = begin; out_x < end; out_x++) {
for (int out_x = 0; out_x < pixel_width; out_x++) {
// Compute the number of bytes over in each row that the current column
// we're convolving starts at. The pixel will cover the next 4 bytes.
int byte_offset = out_x * 4;
@ -288,28 +289,29 @@ void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
int filter_length,
unsigned char* const* source_data_rows,
int width, unsigned char* out_row,
int pixel_width, unsigned char* out_row,
bool has_alpha, bool use_simd) {
int processed = 0;
#if defined(USE_SSE2) || defined(_MIPS_ARCH_LOONGSON3A)
// If the binary was not built with SSE2 support, we had to fallback to C version.
int simd_width = width & ~3;
if (use_simd && simd_width) {
if (use_simd) {
ConvolveVertically_SIMD(filter_values, filter_length,
source_data_rows, 0, simd_width,
source_data_rows,
pixel_width,
out_row, has_alpha);
processed = simd_width;
}
} else
#endif
if (width > processed) {
{
if (has_alpha) {
ConvolveVertically<true>(filter_values, filter_length, source_data_rows,
processed, width, out_row);
ConvolveVertically<true>(filter_values, filter_length,
source_data_rows,
pixel_width,
out_row);
} else {
ConvolveVertically<false>(filter_values, filter_length, source_data_rows,
processed, width, out_row);
ConvolveVertically<false>(filter_values, filter_length,
source_data_rows,
pixel_width,
out_row);
}
}
}
@ -326,16 +328,16 @@ void ConvolveHorizontally(const unsigned char* src_data,
// SIMD implementation works with 4 pixels at a time.
// Therefore we process as much as we can using SSE and then use
// C implementation for leftovers
ConvolveHorizontally_SSE2(src_data, 0, simd_width, filter, out_row);
ConvolveHorizontally_SSE2(src_data, filter, out_row);
processed = simd_width;
}
#endif
if (width > processed) {
if (has_alpha) {
ConvolveHorizontally<true>(src_data, processed, width, filter, out_row);
ConvolveHorizontally<true>(src_data, filter, out_row);
} else {
ConvolveHorizontally<false>(src_data, processed, width, filter, out_row);
ConvolveHorizontally<false>(src_data, filter, out_row);
}
}
}
@ -457,9 +459,23 @@ void BGRAConvolve2D(const unsigned char* source_data,
int num_output_rows = filter_y.num_values();
int pixel_width = filter_x.num_values();
// We need to check which is the last line to convolve before we advance 4
// lines in one iteration.
int last_filter_offset, last_filter_length;
// SSE2 can access up to 3 extra pixels past the end of the
// buffer. At the bottom of the image, we have to be careful
// not to access data past the end of the buffer. Normally
// we fall back to the C++ implementation for the last row.
// If the last row is less than 3 pixels wide, we may have to fall
// back to the C++ version for more rows. Compute how many
// rows we need to avoid the SSE implementation for here.
filter_x.FilterForValue(filter_x.num_values() - 1, &last_filter_offset,
&last_filter_length);
#if defined(USE_SSE2) || defined(_MIPS_ARCH_LOONGSON3A)
int avoid_simd_rows = 1 + 3 /
(last_filter_offset + last_filter_length);
#endif
filter_y.FilterForValue(num_output_rows - 1, &last_filter_offset,
&last_filter_length);
@ -473,36 +489,32 @@ void BGRAConvolve2D(const unsigned char* source_data,
// We don't want to process too much rows in batches of 4 because
// we can go out-of-bounds at the end
while (next_x_row < filter_offset + filter_length) {
if (next_x_row + 3 < last_filter_offset + last_filter_length - 3) {
if (next_x_row + 3 < last_filter_offset + last_filter_length -
avoid_simd_rows) {
const unsigned char* src[4];
unsigned char* out_row[4];
for (int i = 0; i < 4; ++i) {
src[i] = &source_data[(next_x_row + i) * source_byte_row_stride];
out_row[i] = row_buffer.AdvanceRow();
}
ConvolveHorizontally4_SIMD(src, 0, pixel_width, filter_x, out_row);
ConvolveHorizontally4_SIMD(src, filter_x, out_row);
next_x_row += 4;
} else {
unsigned char* buffer = row_buffer.AdvanceRow();
// For last rows, SSE2 load possibly to access data beyond the
// image area. therefore we use cobined C+SSE version here
int simd_width = pixel_width & ~3;
if (simd_width) {
// Check if we need to avoid SSE2 for this row.
if (next_x_row < last_filter_offset + last_filter_length -
avoid_simd_rows) {
ConvolveHorizontally_SIMD(
&source_data[next_x_row * source_byte_row_stride],
0, simd_width, filter_x, buffer);
}
if (pixel_width > simd_width) {
filter_x, row_buffer.AdvanceRow());
} else {
if (source_has_alpha) {
ConvolveHorizontally<true>(
&source_data[next_x_row * source_byte_row_stride],
simd_width, pixel_width, filter_x, buffer);
filter_x, row_buffer.AdvanceRow());
} else {
ConvolveHorizontally<false>(
&source_data[next_x_row * source_byte_row_stride],
simd_width, pixel_width, filter_x, buffer);
filter_x, row_buffer.AdvanceRow());
}
}
next_x_row++;
@ -513,12 +525,12 @@ void BGRAConvolve2D(const unsigned char* source_data,
while (next_x_row < filter_offset + filter_length) {
if (source_has_alpha) {
ConvolveHorizontally<true>(
&source_data[next_x_row * source_byte_row_stride],
0, pixel_width, filter_x, row_buffer.AdvanceRow());
&source_data[next_x_row * source_byte_row_stride],
filter_x, row_buffer.AdvanceRow());
} else {
ConvolveHorizontally<false>(
&source_data[next_x_row * source_byte_row_stride],
0, pixel_width, filter_x, row_buffer.AdvanceRow());
&source_data[next_x_row * source_byte_row_stride],
filter_x, row_buffer.AdvanceRow());
}
next_x_row++;
}

77
gfx/2d/convolverSSE2.cpp

@ -35,26 +35,24 @@
namespace skia {
// Convolves horizontally along a single row. The row data is given in
// |src_data| and continues for the [begin, end) of the filter.
// |src_data| and continues for the num_values() of the filter.
void ConvolveHorizontally_SSE2(const unsigned char* src_data,
int begin, int end,
const ConvolutionFilter1D& filter,
unsigned char* out_row) {
int num_values = filter.num_values();
int filter_offset, filter_length;
__m128i zero = _mm_setzero_si128();
__m128i mask[3];
__m128i mask[4];
// |mask| will be used to decimate all extra filter coefficients that are
// loaded by SIMD when |filter_length| is not divisible by 4.
mask[0] = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, -1);
mask[1] = _mm_set_epi16(0, 0, 0, 0, 0, 0, -1, -1);
mask[2] = _mm_set_epi16(0, 0, 0, 0, 0, -1, -1, -1);
// This buffer is used for tails
__m128i buffer;
// mask[0] is not used in following algorithm.
mask[1] = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, -1);
mask[2] = _mm_set_epi16(0, 0, 0, 0, 0, 0, -1, -1);
mask[3] = _mm_set_epi16(0, 0, 0, 0, 0, -1, -1, -1);
// Output one pixel each iteration, calculating all channels (RGBA) together.
for (int out_x = begin; out_x < end; out_x++) {
for (int out_x = 0; out_x < num_values; out_x++) {
const ConvolutionFilter1D::Fixed* filter_values =
filter.FilterForValue(out_x, &filter_offset, &filter_length);
@ -117,22 +115,21 @@ void ConvolveHorizontally_SSE2(const unsigned char* src_data,
// When |filter_length| is not divisible by 4, we need to decimate some of
// the filter coefficient that was loaded incorrectly to zero; Other than
// that the algorithm is same with above, except that the 4th pixel will be
// that the algorithm is same with above, exceot that the 4th pixel will be
// always absent.
int r = filter_length & 3;
int r = filter_length&3;
if (r) {
memcpy(&buffer, row_to_filter, r * 4);
// Note: filter_values must be padded to align_up(filter_offset, 8).
__m128i coeff, coeff16;
coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
// Mask out extra filter taps.
coeff = _mm_and_si128(coeff, mask[r-1]);
coeff = _mm_and_si128(coeff, mask[r]);
coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
coeff16 = _mm_unpacklo_epi16(coeff16, coeff16);
// Note: line buffer must be padded to align_up(filter_offset, 16).
// We resolve this by temporary buffer
__m128i src8 = _mm_loadu_si128(&buffer);
// We resolve this by use C-version for the last horizontal line.
__m128i src8 = _mm_loadu_si128(row_to_filter);
__m128i src16 = _mm_unpacklo_epi8(src8, zero);
__m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
__m128i mul_lo = _mm_mullo_epi16(src16, coeff16);
@ -165,24 +162,26 @@ void ConvolveHorizontally_SSE2(const unsigned char* src_data,
}
// Convolves horizontally along four rows. The row data is given in
// |src_data| and continues for the [begin, end) of the filter.
// |src_data| and continues for the num_values() of the filter.
// The algorithm is almost same as |ConvolveHorizontally_SSE2|. Please
// refer to that function for detailed comments.
void ConvolveHorizontally4_SSE2(const unsigned char* src_data[4],
int begin, int end,
const ConvolutionFilter1D& filter,
unsigned char* out_row[4]) {
int num_values = filter.num_values();
int filter_offset, filter_length;
__m128i zero = _mm_setzero_si128();
__m128i mask[3];
__m128i mask[4];
// |mask| will be used to decimate all extra filter coefficients that are
// loaded by SIMD when |filter_length| is not divisible by 4.
mask[0] = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, -1);
mask[1] = _mm_set_epi16(0, 0, 0, 0, 0, 0, -1, -1);
mask[2] = _mm_set_epi16(0, 0, 0, 0, 0, -1, -1, -1);
// mask[0] is not used in following algorithm.
mask[1] = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, -1);
mask[2] = _mm_set_epi16(0, 0, 0, 0, 0, 0, -1, -1);
mask[3] = _mm_set_epi16(0, 0, 0, 0, 0, -1, -1, -1);
// Output one pixel each iteration, calculating all channels (RGBA) together.
for (int out_x = begin; out_x < end; out_x++) {
for (int out_x = 0; out_x < num_values; out_x++) {
const ConvolutionFilter1D::Fixed* filter_values =
filter.FilterForValue(out_x, &filter_offset, &filter_length);
@ -240,7 +239,7 @@ void ConvolveHorizontally4_SSE2(const unsigned char* src_data[4],
__m128i coeff;
coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
// Mask out extra filter taps.
coeff = _mm_and_si128(coeff, mask[r-1]);
coeff = _mm_and_si128(coeff, mask[r]);
__m128i coeff16lo = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
/* c1 c1 c1 c1 c0 c0 c0 c0 */
@ -284,21 +283,22 @@ void ConvolveHorizontally4_SSE2(const unsigned char* src_data[4],
// Does vertical convolution to produce one output row. The filter values and
// length are given in the first two parameters. These are applied to each
// of the rows pointed to in the |source_data_rows| array, with each row
// being |end - begin| wide.
// being |pixel_width| wide.
//
// The output must have room for |(end - begin) * 4| bytes.
// The output must have room for |pixel_width * 4| bytes.
template<bool has_alpha>
void ConvolveVertically_SSE2_impl(const ConvolutionFilter1D::Fixed* filter_values,
int filter_length,
unsigned char* const* source_data_rows,
int begin, int end,
int pixel_width,
unsigned char* out_row) {
int width = pixel_width & ~3;
__m128i zero = _mm_setzero_si128();
__m128i accum0, accum1, accum2, accum3, coeff16;
const __m128i* src;
int out_x;
// Output four pixels per iteration (16 bytes).
for (out_x = begin; out_x + 3 < end; out_x += 4) {
for (int out_x = 0; out_x < width; out_x += 4) {
// Accumulated result for each pixel. 32 bits per RGBA channel.
accum0 = _mm_setzero_si128();
@ -391,11 +391,7 @@ void ConvolveVertically_SSE2_impl(const ConvolutionFilter1D::Fixed* filter_value
// When the width of the output is not divisible by 4, We need to save one
// pixel (4 bytes) each time. And also the fourth pixel is always absent.
int r = end - out_x;
if (r > 0) {
// Since accum3 is never used here, we'll use it as a buffer
__m128i *buffer = &accum3;
if (pixel_width & 3) {
accum0 = _mm_setzero_si128();
accum1 = _mm_setzero_si128();
accum2 = _mm_setzero_si128();
@ -403,9 +399,8 @@ void ConvolveVertically_SSE2_impl(const ConvolutionFilter1D::Fixed* filter_value
coeff16 = _mm_set1_epi16(filter_values[filter_y]);
// [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
src = reinterpret_cast<const __m128i*>(
&source_data_rows[filter_y][out_x * 4]);
memcpy(buffer, src, r * 4);
__m128i src8 = _mm_loadu_si128(buffer);
&source_data_rows[filter_y][width<<2]);
__m128i src8 = _mm_loadu_si128(src);
// [16] a1 b1 g1 r1 a0 b0 g0 r0
__m128i src16 = _mm_unpacklo_epi8(src8, zero);
__m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
@ -451,7 +446,7 @@ void ConvolveVertically_SSE2_impl(const ConvolutionFilter1D::Fixed* filter_value
accum0 = _mm_or_si128(accum0, mask);
}
for (; out_x < end; out_x++) {
for (int out_x = width; out_x < pixel_width; out_x++) {
*(reinterpret_cast<int*>(out_row)) = _mm_cvtsi128_si32(accum0);
accum0 = _mm_srli_si128(accum0, 4);
out_row += 4;
@ -462,14 +457,14 @@ void ConvolveVertically_SSE2_impl(const ConvolutionFilter1D::Fixed* filter_value
void ConvolveVertically_SSE2(const ConvolutionFilter1D::Fixed* filter_values,
int filter_length,
unsigned char* const* source_data_rows,
int begin, int end,
int pixel_width,
unsigned char* out_row, bool has_alpha) {
if (has_alpha) {
ConvolveVertically_SSE2_impl<true>(filter_values, filter_length,
source_data_rows, begin, end, out_row);
source_data_rows, pixel_width, out_row);
} else {
ConvolveVertically_SSE2_impl<false>(filter_values, filter_length,
source_data_rows, begin, end, out_row);
source_data_rows, pixel_width, out_row);
}
}

4
gfx/2d/convolverSSE2.h

@ -40,7 +40,6 @@ namespace skia {
// Convolves horizontally along a single row. The row data is given in
// |src_data| and continues for the [begin, end) of the filter.
void ConvolveHorizontally_SSE2(const unsigned char* src_data,
int begin, int end,
const ConvolutionFilter1D& filter,
unsigned char* out_row);
@ -49,7 +48,6 @@ void ConvolveHorizontally_SSE2(const unsigned char* src_data,
// The algorithm is almost same as |ConvolveHorizontally_SSE2|. Please
// refer to that function for detailed comments.
void ConvolveHorizontally4_SSE2(const unsigned char* src_data[4],
int begin, int end,
const ConvolutionFilter1D& filter,
unsigned char* out_row[4]);
@ -62,7 +60,7 @@ void ConvolveHorizontally4_SSE2(const unsigned char* src_data[4],
void ConvolveVertically_SSE2(const ConvolutionFilter1D::Fixed* filter_values,
int filter_length,
unsigned char* const* source_data_rows,
int begin, int end,
int pixel_width,
unsigned char* out_row, bool has_alpha);
} // namespace skia

4
gfx/gl/HeapCopyOfStackArray.h

@ -23,7 +23,7 @@ class HeapCopyOfStackArray
{
public:
template<size_t N>
HeapCopyOfStackArray(ElemType (&array)[N])
MOZ_IMPLICIT HeapCopyOfStackArray(ElemType (&array)[N])
: mArrayLength(N)
, mArrayData(new ElemType[N])
{
@ -44,4 +44,4 @@ private:
} // namespace mozilla
#endif // HEAPCOPYOFSTACKARRAY_H_
#endif // HEAPCOPYOFSTACKARRAY_H_

4
gfx/layers/basic/TextureClientX11.cpp

@ -112,7 +112,9 @@ TextureClientX11::AllocateForSurface(IntSize aSize, TextureAllocationFlags aText
//MOZ_ASSERT(mFormat != gfx::FORMAT_YUV, "This TextureClient cannot use YCbCr data");
MOZ_ASSERT(aSize.width >= 0 && aSize.height >= 0);
if (aSize.width <= 0 || aSize.height <= 0) {
if (aSize.width <= 0 || aSize.height <= 0 ||
aSize.width > XLIB_IMAGE_SIDE_SIZE_LIMIT ||
aSize.height > XLIB_IMAGE_SIDE_SIZE_LIMIT) {
gfxDebug() << "Asking for X11 surface of invalid size " << aSize.width << "x" << aSize.height;
return false;
}

26
gfx/skia/skia/src/core/SkDraw.cpp

@ -734,6 +734,16 @@ void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count,
}
}
static inline SkPoint compute_stroke_size(const SkPaint& paint, const SkMatrix& matrix) {
SkASSERT(matrix.rectStaysRect());
SkASSERT(SkPaint::kFill_Style != paint.getStyle());
SkVector size;
SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
matrix.mapVectors(&size, &pt, 1);
return SkPoint::Make(SkScalarAbs(size.fX), SkScalarAbs(size.fY));
}
static bool easy_rect_join(const SkPaint& paint, const SkMatrix& matrix,
SkPoint* strokeSize) {
if (SkPaint::kMiter_Join != paint.getStrokeJoin() ||
@ -812,12 +822,22 @@ void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const {
devRect.sort();
// look for the quick exit, before we build a blitter
SkIRect ir;
devRect.roundOut(&ir);
SkRect bbox = devRect;
if (paint.getStyle() != SkPaint::kFill_Style) {
// extra space for hairlines
ir.inset(-1, -1);
if (paint.getStrokeWidth() == 0) {
bbox.outset(1, 1);
} else {
// For kStroke_RectType, strokeSize is already computed.
const SkPoint& ssize = (kStroke_RectType == rtype)
? strokeSize
: compute_stroke_size(paint, *fMatrix);
bbox.outset(SkScalarHalf(ssize.x()), SkScalarHalf(ssize.y()));
}
}
SkIRect ir;
bbox.roundOut(&ir);
if (fRC->quickReject(ir)) {
return;
}

2
gfx/skia/skia/src/utils/win/SkDWrite.h

@ -42,7 +42,7 @@ HRESULT sk_wchar_to_skstring(WCHAR* name, SkString* skname);
void sk_get_locale_string(IDWriteLocalizedStrings* names, const WCHAR* preferedLocale,
SkString* skname);
typedef int (*SkGetUserDefaultLocaleNameProc)(LPWSTR, int);
typedef int (WINAPI *SkGetUserDefaultLocaleNameProc)(LPWSTR, int);
HRESULT SkGetGetUserDefaultLocaleNameProc(SkGetUserDefaultLocaleNameProc* proc);
////////////////////////////////////////////////////////////////////////////////

6
gfx/thebes/gfxASurface.cpp

@ -499,12 +499,6 @@ gfxASurface::GetSubpixelAntialiasingEnabled()
#endif
}
gfxMemoryLocation
gfxASurface::GetMemoryLocation() const
{
return gfxMemoryLocation::IN_PROCESS_HEAP;
}
int32_t
gfxASurface::BytePerPixelFromFormat(gfxImageFormat format)
{

6
gfx/thebes/gfxASurface.h

@ -159,12 +159,6 @@ public:
// to a sub-class of gfxASurface.)
virtual bool SizeOfIsMeasured() const { return false; }
/**
* Where does this surface's memory live? By default, we say it's in this
* process's heap.
*/
virtual gfxMemoryLocation GetMemoryLocation() const;
static int32_t BytePerPixelFromFormat(gfxImageFormat format);
virtual const mozilla::gfx::IntSize GetSize() const;

11
gfx/thebes/gfxTypes.h

@ -92,15 +92,4 @@ enum class gfxContentType {
SENTINEL = 0xffff
};
/**
* The memory used by a gfxASurface (as reported by KnownMemoryUsed()) can
* either live in this process's heap, in this process but outside the
* heap, or in another process altogether.
*/
enum class gfxMemoryLocation {
IN_PROCESS_HEAP,
IN_PROCESS_NONHEAP,
OUT_OF_PROCESS
};
#endif /* GFX_TYPES_H */

6
gfx/thebes/gfxWindowsSurface.cpp

@ -300,9 +300,3 @@ gfxWindowsSurface::GetSize() const
return mozilla::gfx::IntSize(cairo_win32_surface_get_width(mSurface),
cairo_win32_surface_get_height(mSurface));
}
gfxMemoryLocation
gfxWindowsSurface::GetMemoryLocation() const
{
return gfxMemoryLocation::IN_PROCESS_NONHEAP;
}

4
gfx/thebes/gfxWindowsSurface.h

@ -65,10 +65,6 @@ public:
const mozilla::gfx::IntSize GetSize() const;
// The memory used by this surface lives in this process's address space,
// but not in the heap.
virtual gfxMemoryLocation GetMemoryLocation() const;
private:
void MakeInvalid(mozilla::gfx::IntSize& size);

11
gfx/thebes/gfxXlibSurface.cpp

@ -21,11 +21,6 @@
using namespace mozilla;
// Although the dimension parameters in the xCreatePixmapReq wire protocol are
// 16-bit unsigned integers, the server's CreatePixmap returns BadAlloc if
// either dimension cannot be represented by a 16-bit *signed* integer.
#define XLIB_IMAGE_SIDE_SIZE_LIMIT 0x7fff
gfxXlibSurface::gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual)
: mPixmapTaken(false), mDisplay(dpy), mDrawable(drawable)
#if defined(GL_PROVIDER_GLX)
@ -619,9 +614,3 @@ gfxXlibSurface::BindGLXPixmap(GLXPixmap aPixmap)
}
#endif
gfxMemoryLocation
gfxXlibSurface::GetMemoryLocation() const
{
return gfxMemoryLocation::OUT_OF_PROCESS;
}

10
gfx/thebes/gfxXlibSurface.h

@ -17,6 +17,12 @@
#include "nsSize.h"
// Although the dimension parameters in the xCreatePixmapReq wire protocol are
// 16-bit unsigned integers, the server's CreatePixmap returns BadAlloc if
// either dimension cannot be represented by a 16-bit *signed* integer.
#define XLIB_IMAGE_SIDE_SIZE_LIMIT 0x7fff
class gfxXlibSurface final : public gfxASurface {
public:
// construct a wrapper around the specified drawable with dpy/visual.
@ -79,10 +85,6 @@ public:
// Find a visual and colormap pair suitable for rendering to this surface.
bool GetColormapAndVisual(Colormap* colormap, Visual **visual);
// This surface is a wrapper around X pixmaps, which are stored in the X
// server, not the main application.
virtual gfxMemoryLocation GetMemoryLocation() const override;
#if defined(GL_PROVIDER_GLX)
GLXPixmap GetGLXPixmap();
// Binds a GLXPixmap backed by this context's surface.

250
image/CopyOnWrite.h

@ -0,0 +1,250 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* CopyOnWrite<T> allows code to safely read from a data structure without
* worrying that reentrant code will modify it.
*/
#ifndef mozilla_image_CopyOnWrite_h
#define mozilla_image_CopyOnWrite_h
#include "mozilla/nsRefPtr.h"
#include "MainThreadUtils.h"
#include "nsISupportsImpl.h"
namespace mozilla {
namespace image {
///////////////////////////////////////////////////////////////////////////////
// Implementation Details
///////////////////////////////////////////////////////////////////////////////
namespace detail {
template <typename T>
class CopyOnWriteValue final
{
public:
NS_INLINE_DECL_REFCOUNTING(CopyOnWriteValue)
explicit CopyOnWriteValue(T* aValue) : mValue(aValue) { }
explicit CopyOnWriteValue(already_AddRefed<T>& aValue) : mValue(aValue) { }
explicit CopyOnWriteValue(already_AddRefed<T>&& aValue) : mValue(aValue) { }
explicit CopyOnWriteValue(const nsRefPtr<T>& aValue) : mValue(aValue) { }
explicit CopyOnWriteValue(nsRefPtr<T>&& aValue) : mValue(aValue) { }
T* get() { return mValue.get(); }
const T* get() const { return mValue.get(); }
bool HasReaders() const { return mReaders > 0; }
bool HasWriter() const { return mWriter; }
bool HasUsers() const { return HasReaders() || HasWriter(); }
void LockForReading() { MOZ_ASSERT(!HasWriter()); mReaders++; }
void UnlockForReading() { MOZ_ASSERT(HasReaders()); mReaders--; }
struct MOZ_STACK_CLASS AutoReadLock
{
explicit AutoReadLock(CopyOnWriteValue* aValue)
: mValue(aValue)
{
mValue->LockForReading();
}
~AutoReadLock() { mValue->UnlockForReading(); }
CopyOnWriteValue<T>* mValue;
};
void LockForWriting() { MOZ_ASSERT(!HasUsers()); mWriter = true; }
void UnlockForWriting() { MOZ_ASSERT(HasWriter()); mWriter = false; }
struct MOZ_STACK_CLASS AutoWriteLock
{
explicit AutoWriteLock(CopyOnWriteValue* aValue)
: mValue(aValue)
{
mValue->LockForWriting();
}
~AutoWriteLock() { mValue->UnlockForWriting(); }
CopyOnWriteValue<T>* mValue;
};
private:
CopyOnWriteValue(const CopyOnWriteValue&) = delete;
CopyOnWriteValue(CopyOnWriteValue&&) = delete;
~CopyOnWriteValue() { }
nsRefPtr<T> mValue;
uint64_t mReaders = 0;
bool mWriter = false;
};
} // namespace detail
///////////////////////////////////////////////////////////////////////////////
// Public API
///////////////////////////////////////////////////////////////////////////////
/**
* CopyOnWrite<T> allows code to safely read from a data structure without
* worrying that reentrant code will modify it. If reentrant code would modify
* the data structure while other code is reading from it, a copy is made so
* that readers can continue to use the old version.
*
* Note that it's legal to nest a writer inside any number of readers, but
* nothing can be nested inside a writer. This is because it's assumed that the
* state of the contained data structure may not be consistent during the write.
*
* This is a main-thread-only data structure.
*
* To work with CopyOnWrite<T>, a type T needs to be reference counted and to
* support copy construction.
*/
template <typename T>
class CopyOnWrite final
{
typedef detail::CopyOnWriteValue<T> CopyOnWriteValue;
public:
explicit CopyOnWrite(T* aValue)
: mValue(new CopyOnWriteValue(aValue))
{ }
explicit CopyOnWrite(already_AddRefed<T>& aValue)
: mValue(new CopyOnWriteValue(aValue))
{ }
explicit CopyOnWrite(already_AddRefed<T>&& aValue)
: mValue(new CopyOnWriteValue(aValue))
{ }
explicit CopyOnWrite(const nsRefPtr<T>& aValue)
: mValue(new CopyOnWriteValue(aValue))
{ }
explicit CopyOnWrite(nsRefPtr<T>&& aValue)
: mValue(new CopyOnWriteValue(aValue))
{ }
/// @return true if it's safe to read at this time.
bool CanRead() const { return !mValue->HasWriter(); }
/**
* Read from the contained data structure using the function @aReader.
* @aReader will be passed a pointer of type |const T*|. It's not legal to
* call this while a writer is active.
*
* @return whatever value @aReader returns, or nothing if @aReader is a void
* function.
*/
template <typename ReadFunc>
auto Read(ReadFunc aReader) const
-> decltype(aReader(static_cast<const T*>(nullptr)))
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(CanRead());
// Run the provided function while holding a read lock.
nsRefPtr<CopyOnWriteValue> cowValue = mValue;
typename CopyOnWriteValue::AutoReadLock lock(cowValue);
return aReader(cowValue->get());
}
/**
* Read from the contained data structure using the function @aReader.
* @aReader will be passed a pointer of type |const T*|. If it's currently not
* possible to read because a writer is currently active, @aOnError will be
* called instead.
*
* @return whatever value @aReader or @aOnError returns (their return types
* must be consistent), or nothing if the provided functions are void.
*/
template <typename ReadFunc, typename ErrorFunc>
auto Read(ReadFunc aReader, ErrorFunc aOnError) const
-> decltype(aReader(static_cast<const T*>(nullptr)))
{
MOZ_ASSERT(NS_IsMainThread());
if (!CanRead()) {
return aOnError();
}
return Read(aReader);
}
/// @return true if it's safe to write at this time.
bool CanWrite() const { return !mValue->HasWriter(); }
/**
* Write to the contained data structure using the function @aWriter.
* @aWriter will be passed a pointer of type |T*|. It's not legal to call this
* while another writer is active.
*
* If readers are currently active, they will be able to continue reading from
* a copy of the old version of the data structure. The copy will be destroyed
* when all its readers finish. Later readers and writers will see the
* version of the data structure produced by the most recent call to Write().
*
* @return whatever value @aWriter returns, or nothing if @aWriter is a void
* function.
*/
template <typename WriteFunc>
auto Write(WriteFunc aWriter)
-> decltype(aWriter(static_cast<T*>(nullptr)))
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(CanWrite());
// If there are readers, we need to copy first.
if (mValue->HasReaders()) {
mValue = new CopyOnWriteValue(new T(*mValue->get()));
}
// Run the provided function while holding a write lock.
nsRefPtr<CopyOnWriteValue> cowValue = mValue;
typename CopyOnWriteValue::AutoWriteLock lock(cowValue);
return aWriter(cowValue->get());
}
/**
* Write to the contained data structure using the function @aWriter.
* @aWriter will be passed a pointer of type |T*|. If it's currently not
* possible to write because a writer is currently active, @aOnError will be
* called instead.
*
* If readers are currently active, they will be able to continue reading from
* a copy of the old version of the data structure. The copy will be destroyed
* when all its readers finish. Later readers and writers will see the
* version of the data structure produced by the most recent call to Write().
*
* @return whatever value @aWriter or @aOnError returns (their return types
* must be consistent), or nothing if the provided functions are void.
*/
template <typename WriteFunc, typename ErrorFunc>
auto Write(WriteFunc aWriter, ErrorFunc aOnError)
-> decltype(aWriter(static_cast<T*>(nullptr)))
{
MOZ_ASSERT(NS_IsMainThread());
if (!CanWrite()) {
return aOnError();
}
return Write(aWriter);
}
private:
CopyOnWrite(const CopyOnWrite&) = delete;
CopyOnWrite(CopyOnWrite&&) = delete;
nsRefPtr<CopyOnWriteValue> mValue;
};
} // namespace image
} // namespace mozilla
#endif // mozilla_image_CopyOnWrite_h

5
image/DecodePool.cpp

@ -451,7 +451,10 @@ DecodePool::Decode(Decoder* aDecoder)
nsresult rv = aDecoder->Decode();
if (NS_SUCCEEDED(rv) && !aDecoder->GetDecodeDone()) {
if (aDecoder->HasProgress()) {
// If this isn't a metadata decode, notify for the progress we've made so
// far. It's important that metadata decode results are delivered
// atomically, so for those decodes we wait until NotifyDecodeComplete.
if (aDecoder->HasProgress() && !aDecoder->IsMetadataDecode()) {
NotifyProgress(aDecoder);
}
// The decoder will ensure that a new worker gets enqueued to continue

38
image/Decoder.cpp

@ -37,10 +37,8 @@ Decoder::Decoder(RasterImage* aImage)
, mMetadataDecode(false)
, mSendPartialInvalidations(false)
, mImageIsTransient(false)
, mImageIsLocked(false)
, mFirstFrameDecode(false)
, mInFrame(false)
, mIsAnimated(false)
, mDataDone(false)
, mDecodeDone(false)
, mDataError(false)
@ -94,15 +92,18 @@ Decoder::Init()
}
nsresult
Decoder::Decode()
Decoder::Decode(IResumable* aOnResume)
{
MOZ_ASSERT(mInitialized, "Should be initialized here");
MOZ_ASSERT(mIterator, "Should have a SourceBufferIterator");
// If no IResumable was provided, default to |this|.
IResumable* onResume = aOnResume ? aOnResume : this;
// We keep decoding chunks until the decode completes or there are no more
// chunks available.
while (!GetDecodeDone() && !HasError()) {
auto newState = mIterator->AdvanceOrScheduleResume(this);
auto newState = mIterator->AdvanceOrScheduleResume(onResume);
if (newState == SourceBufferIterator::WAITING) {
// We can't continue because the rest of the data hasn't arrived from the
@ -237,7 +238,7 @@ Decoder::CompleteDecode()
// If this image wasn't animated and isn't a transient image, mark its frame
// as optimizable. We don't support optimizing animated images and
// optimizing transient images isn't worth it.
if (!mIsAnimated && !mImageIsTransient && mCurrentFrame) {
if (!HasAnimation() && !mImageIsTransient && mCurrentFrame) {
mCurrentFrame->SetOptimizable();
}
}
@ -260,7 +261,12 @@ Decoder::AllocateFrame(uint32_t aFrameNum,
mCurrentFrame->GetPaletteData(&mColormap, &mColormapSize);
if (aFrameNum + 1 == mFrameCount) {
PostFrameStart();
// If we're past the first frame, PostIsAnimated() should've been called.
MOZ_ASSERT_IF(mFrameCount > 1, HasAnimation());
// Update our state to reflect the new frame
MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
mInFrame = true;
}
} else {
PostDataError();
@ -406,19 +412,11 @@ Decoder::PostHasTransparency()
}
void
Decoder::PostFrameStart()
Decoder::PostIsAnimated(int32_t aFirstFrameTimeout)
{
// We shouldn't already be mid-frame
MOZ_ASSERT(!mInFrame, "Starting new frame but not done with old one!");
// Update our state to reflect the new frame
mInFrame = true;
// If we just became animated, record that fact.
if (mFrameCount > 1) {
mIsAnimated = true;
mProgress |= FLAG_IS_ANIMATED;
}
mProgress |= FLAG_IS_ANIMATED;
mImageMetadata.SetHasAnimation();
mImageMetadata.SetFirstFrameTimeout(aFirstFrameTimeout);
}
void
@ -442,7 +440,7 @@ Decoder::PostFrameStop(Opacity aFrameOpacity /* = Opacity::TRANSPARENT */,
// If we're not sending partial invalidations, then we send an invalidation
// here when the first frame is complete.
if (!mSendPartialInvalidations && !mIsAnimated) {
if (!mSendPartialInvalidations && !HasAnimation()) {
mInvalidRect.UnionRect(mInvalidRect,
gfx::IntRect(gfx::IntPoint(0, 0), GetSize()));
}
@ -459,7 +457,7 @@ Decoder::PostInvalidation(const nsIntRect& aRect,
// Record this invalidation, unless we're not sending partial invalidations
// or we're past the first frame.
if (mSendPartialInvalidations && !mIsAnimated) {
if (mSendPartialInvalidations && !HasAnimation()) {
mInvalidRect.UnionRect(mInvalidRect, aRect);
mCurrentFrame->ImageUpdated(aRectAtTargetSize.valueOr(aRect));
}

37
image/Decoder.h

@ -34,13 +34,16 @@ public:
void Init();
/**
* Decodes, reading all data currently available in the SourceBuffer. If more
* data is needed, Decode() automatically ensures that it will be called again
* on a DecodePool thread when the data becomes available.
* Decodes, reading all data currently available in the SourceBuffer.
*
* If more data is needed, Decode() will schedule @aOnResume to be called when
* more data is available. If @aOnResume is null or unspecified, the default
* implementation resumes decoding on a DecodePool thread. Most callers should
* use the default implementation.
*
* Any errors are reported by setting the appropriate state on the decoder.
*/
nsresult Decode();
nsresult Decode(IResumable* aOnResume = nullptr);
/**
* Given a maximum number of bytes we're willing to decode, @aByteLimit,
@ -181,20 +184,6 @@ public:
mImageIsTransient = aIsTransient;
}
/**
* Set whether the image is locked for the lifetime of this decoder. We lock
* the image during our initial decode to ensure that we don't evict any
* surfaces before we realize that the image is animated.
*/
void SetImageIsLocked()
{
MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
mImageIsLocked = true;
}
bool ImageIsLocked() const { return mImageIsLocked; }
/**
* Set whether we should stop decoding after the first frame.
*/
@ -225,7 +214,7 @@ public:
}
// Did we discover that the image we're decoding is animated?
bool HasAnimation() const { return mIsAnimated; }
bool HasAnimation() const { return mImageMetadata.HasAnimation(); }
// Error tracking
bool HasError() const { return HasDataError() || HasDecoderError(); }
@ -344,9 +333,11 @@ protected:
// actual contents of the frame and give a more accurate result.
void PostHasTransparency();
// Called by decoders when they begin a frame. Informs the image, sends
// notifications, and does internal book-keeping.
void PostFrameStart();
// Called by decoders if they determine that the image is animated.
//
// @param aTimeout The time for which the first frame should be shown before
// we advance to the next frame.
void PostIsAnimated(int32_t aFirstFrameTimeout);
// Called by decoders when they end a frame. Informs the image, sends
// notifications, and does internal book-keeping.
@ -451,10 +442,8 @@ private:
bool mMetadataDecode : 1;
bool mSendPartialInvalidations : 1;
bool mImageIsTransient : 1;
bool mImageIsLocked : 1;
bool mFirstFrameDecode : 1;
bool mInFrame : 1;
bool mIsAnimated : 1;
bool mDataDone : 1;
bool mDecodeDone : 1;
bool mDataError : 1;

66
image/DecoderFactory.cpp

@ -138,8 +138,7 @@ DecoderFactory::CreateDecoder(DecoderType aType,
int aSampleSize,
const IntSize& aResolution,
bool aIsRedecode,
bool aImageIsTransient,
bool aImageIsLocked)
bool aImageIsTransient)
{
if (aType == DecoderType::UNKNOWN) {
return nullptr;
@ -156,10 +155,7 @@ DecoderFactory::CreateDecoder(DecoderType aType,
decoder->SetResolution(aResolution);
decoder->SetSendPartialInvalidations(!aIsRedecode);
decoder->SetImageIsTransient(aImageIsTransient);
if (aImageIsLocked) {
decoder->SetImageIsLocked();
}
decoder->SetIsFirstFrameDecode();
// Set a target size for downscale-during-decode if applicable.
if (aTargetSize) {
@ -177,6 +173,39 @@ DecoderFactory::CreateDecoder(DecoderType aType,
return decoder.forget();
}
/* static */ already_AddRefed<Decoder>
DecoderFactory::CreateAnimationDecoder(DecoderType aType,
RasterImage* aImage,
SourceBuffer* aSourceBuffer,
uint32_t aFlags,
const IntSize& aResolution)
{
if (aType == DecoderType::UNKNOWN) {
return nullptr;
}
MOZ_ASSERT(aType == DecoderType::GIF || aType == DecoderType::PNG,
"Calling CreateAnimationDecoder for non-animating DecoderType");
nsRefPtr<Decoder> decoder =
GetDecoder(aType, aImage, /* aIsRedecode = */ true);
MOZ_ASSERT(decoder, "Should have a decoder now");
// Initialize the decoder.
decoder->SetMetadataDecode(false);
decoder->SetIterator(aSourceBuffer->Iterator());
decoder->SetFlags(aFlags);
decoder->SetResolution(aResolution);
decoder->SetSendPartialInvalidations(false);
decoder->Init();
if (NS_FAILED(decoder->GetDecoderError())) {
return nullptr;
}
return decoder.forget();
}
/* static */ already_AddRefed<Decoder>
DecoderFactory::CreateMetadataDecoder(DecoderType aType,
RasterImage* aImage,
@ -240,5 +269,30 @@ DecoderFactory::CreateAnonymousDecoder(DecoderType aType,
return decoder.forget();
}
/* static */ already_AddRefed<Decoder>
DecoderFactory::CreateAnonymousMetadataDecoder(DecoderType aType,
SourceBuffer* aSourceBuffer)
{
if (aType == DecoderType::UNKNOWN) {
return nullptr;
}
nsRefPtr<Decoder> decoder =
GetDecoder(aType, /* aImage = */ nullptr, /* aIsRedecode = */ false);
MOZ_ASSERT(decoder, "Should have a decoder now");
// Initialize the decoder.
decoder->SetMetadataDecode(true);
decoder->SetIterator(aSourceBuffer->Iterator());
decoder->SetIsFirstFrameDecode();
decoder->Init();
if (NS_FAILED(decoder->GetDecoderError())) {
return nullptr;
}
return decoder.forget();
}
} // namespace image
} // namespace mozilla

64
image/DecoderFactory.h

@ -40,12 +40,13 @@ public:
static DecoderType GetDecoderType(const char* aMimeType);
/**
* Creates and initializes a decoder of type @aType. The decoder will send
* notifications to @aImage.
* Creates and initializes a decoder for non-animated images of type @aType.
* (If the image *is* animated, only the first frame will be decoded.) The
* decoder will send notifications to @aImage.
*
* XXX(seth): @aIsRedecode, @aImageIsTransient, and @aImageIsLocked should
* really be part of @aFlags. This requires changes to the way that decoder
* flags work, though. See bug 1185800.
* XXX(seth): @aIsRedecode and @aImageIsTransient should really be part of
* @aFlags. This requires changes to the way that decoder flags work, though.
* See bug 1185800.
*
* @param aType Which type of decoder to create - JPEG, PNG, etc.
* @param aImage The image will own the decoder and which should receive
@ -64,9 +65,6 @@ public:
* empty rect if none).
* @param aIsRedecode Specify 'true' if this image has been decoded before.
* @param aImageIsTransient Specify 'true' if this image is transient.
* @param aImageIsLocked Specify 'true' if this image is locked for the
* lifetime of this decoder, and should be unlocked
* when the decoder finishes.
*/
static already_AddRefed<Decoder>
CreateDecoder(DecoderType aType,
@ -77,8 +75,28 @@ public: