Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.

Commit cbf46ad

Browse files
committed
Bug 1343499 - Expose native image sizes to imagelib users. r=tnikkel
1 parent 51e127d commit cbf46ad

24 files changed

Lines changed: 530 additions & 119 deletions

image/DynamicImage.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ DynamicImage::GetHeight(int32_t* aHeight)
127127
return NS_OK;
128128
}
129129

130+
nsresult
131+
DynamicImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const
132+
{
133+
return NS_ERROR_NOT_IMPLEMENTED;
134+
}
135+
130136
NS_IMETHODIMP
131137
DynamicImage::GetIntrinsicSize(nsSize* aSize)
132138
{

image/DynamicImage.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class DynamicImage : public Image
3131
}
3232

3333
// Inherited methods from Image.
34+
nsresult GetNativeSizes(nsTArray<gfx::IntSize>& aNativeSizes) const override;
3435
virtual already_AddRefed<ProgressTracker> GetProgressTracker() override;
3536
virtual size_t SizeOfSourceWithComputedFallback(
3637
MallocSizeOf aMallocSizeOf) const override;

image/FrameTimeout.h

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
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+
#ifndef mozilla_image_FrameTimeout_h
8+
#define mozilla_image_FrameTimeout_h
9+
10+
#include <stdint.h>
11+
#include "mozilla/Assertions.h"
12+
13+
namespace mozilla {
14+
namespace image {
15+
16+
/**
17+
* FrameTimeout wraps a frame timeout value (measured in milliseconds) after
18+
* first normalizing it. This normalization is necessary because some tools
19+
* generate incorrect frame timeout values which we nevertheless have to
20+
* support. For this reason, code that deals with frame timeouts should always
21+
* use a FrameTimeout value rather than the raw value from the image header.
22+
*/
23+
struct FrameTimeout
24+
{
25+
/**
26+
* @return a FrameTimeout of zero. This should be used only for math
27+
* involving FrameTimeout values. You can't obtain a zero FrameTimeout from
28+
* FromRawMilliseconds().
29+
*/
30+
static FrameTimeout Zero() { return FrameTimeout(0); }
31+
32+
/// @return an infinite FrameTimeout.
33+
static FrameTimeout Forever() { return FrameTimeout(-1); }
34+
35+
/// @return a FrameTimeout obtained by normalizing a raw timeout value.
36+
static FrameTimeout FromRawMilliseconds(int32_t aRawMilliseconds)
37+
{
38+
// Normalize all infinite timeouts to the same value.
39+
if (aRawMilliseconds < 0) {
40+
return FrameTimeout::Forever();
41+
}
42+
43+
// Very small timeout values are problematic for two reasons: we don't want
44+
// to burn energy redrawing animated images extremely fast, and broken tools
45+
// generate these values when they actually want a "default" value, so such
46+
// images won't play back right without normalization. For some context,
47+
// see bug 890743, bug 125137, bug 139677, and bug 207059. The historical
48+
// behavior of IE and Opera was:
49+
// IE 6/Win:
50+
// 10 - 50ms is normalized to 100ms.
51+
// >50ms is used unnormalized.
52+
// Opera 7 final/Win:
53+
// 10ms is normalized to 100ms.
54+
// >10ms is used unnormalized.
55+
if (aRawMilliseconds >= 0 && aRawMilliseconds <= 10 ) {
56+
return FrameTimeout(100);
57+
}
58+
59+
// The provided timeout value is OK as-is.
60+
return FrameTimeout(aRawMilliseconds);
61+
}
62+
63+
bool operator==(const FrameTimeout& aOther) const
64+
{
65+
return mTimeout == aOther.mTimeout;
66+
}
67+
68+
bool operator!=(const FrameTimeout& aOther) const { return !(*this == aOther); }
69+
70+
FrameTimeout operator+(const FrameTimeout& aOther)
71+
{
72+
if (*this == Forever() || aOther == Forever()) {
73+
return Forever();
74+
}
75+
76+
return FrameTimeout(mTimeout + aOther.mTimeout);
77+
}
78+
79+
FrameTimeout& operator+=(const FrameTimeout& aOther)
80+
{
81+
*this = *this + aOther;
82+
return *this;
83+
}
84+
85+
/**
86+
* @return this FrameTimeout's value in milliseconds. Illegal to call on a
87+
* an infinite FrameTimeout value.
88+
*/
89+
uint32_t AsMilliseconds() const
90+
{
91+
if (*this == Forever()) {
92+
MOZ_ASSERT_UNREACHABLE("Calling AsMilliseconds() on an infinite FrameTimeout");
93+
return 100; // Fail to something sane.
94+
}
95+
96+
return uint32_t(mTimeout);
97+
}
98+
99+
/**
100+
* @return this FrameTimeout value encoded so that non-negative values
101+
* represent a timeout in milliseconds, and -1 represents an infinite
102+
* timeout.
103+
*
104+
* XXX(seth): This is a backwards compatibility hack that should be removed.
105+
*/
106+
int32_t AsEncodedValueDeprecated() const { return mTimeout; }
107+
108+
private:
109+
explicit FrameTimeout(int32_t aTimeout)
110+
: mTimeout(aTimeout)
111+
{ }
112+
113+
int32_t mTimeout;
114+
};
115+
116+
} // namespace image
117+
} // namespace mozilla
118+
119+
#endif // mozilla_image_FrameTimeout_h

image/ImageMetadata.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,11 @@
1111
#include "mozilla/Maybe.h"
1212
#include "nsSize.h"
1313
#include "Orientation.h"
14+
#include "FrameTimeout.h"
1415

1516
namespace mozilla {
1617
namespace image {
1718

18-
class RasterImage;
19-
2019
// The metadata about an image that decoders accumulate as they decode.
2120
class ImageMetadata
2221
{
@@ -64,6 +63,13 @@ class ImageMetadata
6463
nsIntSize GetSize() const { return *mSize; }
6564
bool HasSize() const { return mSize.isSome(); }
6665

66+
void AddNativeSize(const nsIntSize& aSize)
67+
{
68+
mNativeSizes.AppendElement(aSize);
69+
}
70+
71+
const nsTArray<nsIntSize>& GetNativeSizes() const { return mNativeSizes; }
72+
6773
Orientation GetOrientation() const { return *mOrientation; }
6874
bool HasOrientation() const { return mOrientation.isSome(); }
6975

@@ -90,6 +96,9 @@ class ImageMetadata
9096
Maybe<nsIntSize> mSize;
9197
Maybe<Orientation> mOrientation;
9298

99+
// Sizes the image can natively decode to.
100+
nsTArray<nsIntSize> mNativeSizes;
101+
93102
bool mHasAnimation : 1;
94103
};
95104

image/ImageOps.cpp

Lines changed: 103 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "FrozenImage.h"
1515
#include "IDecodingTask.h"
1616
#include "Image.h"
17+
#include "ImageMetadata.h"
1718
#include "imgIContainer.h"
1819
#include "mozilla/gfx/2D.h"
1920
#include "nsStreamUtils.h"
@@ -79,10 +80,27 @@ ImageOps::CreateFromDrawable(gfxDrawable* aDrawable)
7980
return drawableImage.forget();
8081
}
8182

82-
/* static */ already_AddRefed<gfx::SourceSurface>
83-
ImageOps::DecodeToSurface(nsIInputStream* aInputStream,
84-
const nsACString& aMimeType,
85-
uint32_t aFlags)
83+
class ImageOps::ImageBufferImpl final : public ImageOps::ImageBuffer {
84+
public:
85+
ImageBufferImpl(already_AddRefed<SourceBuffer> aSourceBuffer)
86+
: mSourceBuffer(aSourceBuffer)
87+
{ }
88+
89+
protected:
90+
~ImageBufferImpl() override { }
91+
92+
virtual already_AddRefed<SourceBuffer> GetSourceBuffer()
93+
{
94+
RefPtr<SourceBuffer> sourceBuffer = mSourceBuffer;
95+
return sourceBuffer.forget();
96+
}
97+
98+
private:
99+
RefPtr<SourceBuffer> mSourceBuffer;
100+
};
101+
102+
/* static */ already_AddRefed<ImageOps::ImageBuffer>
103+
ImageOps::CreateImageBuffer(nsIInputStream* aInputStream)
86104
{
87105
MOZ_ASSERT(aInputStream);
88106

@@ -107,7 +125,7 @@ ImageOps::DecodeToSurface(nsIInputStream* aInputStream,
107125
}
108126

109127
// Write the data into a SourceBuffer.
110-
NotNull<RefPtr<SourceBuffer>> sourceBuffer = WrapNotNull(new SourceBuffer());
128+
RefPtr<SourceBuffer> sourceBuffer = new SourceBuffer();
111129
sourceBuffer->ExpectLength(length);
112130
rv = sourceBuffer->AppendFromInputStream(inputStream, length);
113131
if (NS_FAILED(rv)) {
@@ -122,12 +140,90 @@ ImageOps::DecodeToSurface(nsIInputStream* aInputStream,
122140
}
123141
sourceBuffer->Complete(NS_OK);
124142

143+
RefPtr<ImageBuffer> imageBuffer = new ImageBufferImpl(sourceBuffer.forget());
144+
return imageBuffer.forget();
145+
}
146+
147+
/* static */ nsresult
148+
ImageOps::DecodeMetadata(nsIInputStream* aInputStream,
149+
const nsACString& aMimeType,
150+
ImageMetadata& aMetadata)
151+
{
152+
RefPtr<ImageBuffer> buffer = CreateImageBuffer(aInputStream);
153+
return DecodeMetadata(buffer, aMimeType, aMetadata);
154+
}
155+
156+
/* static */ nsresult
157+
ImageOps::DecodeMetadata(ImageBuffer* aBuffer,
158+
const nsACString& aMimeType,
159+
ImageMetadata& aMetadata)
160+
{
161+
if (!aBuffer) {
162+
return NS_ERROR_FAILURE;
163+
}
164+
165+
RefPtr<SourceBuffer> sourceBuffer = aBuffer->GetSourceBuffer();
166+
if (NS_WARN_IF(!sourceBuffer)) {
167+
return NS_ERROR_FAILURE;
168+
}
169+
170+
// Create a decoder.
171+
DecoderType decoderType =
172+
DecoderFactory::GetDecoderType(PromiseFlatCString(aMimeType).get());
173+
RefPtr<Decoder> decoder =
174+
DecoderFactory::CreateAnonymousMetadataDecoder(decoderType,
175+
WrapNotNull(sourceBuffer));
176+
if (!decoder) {
177+
return NS_ERROR_FAILURE;
178+
}
179+
180+
// Run the decoder synchronously.
181+
RefPtr<IDecodingTask> task = new AnonymousDecodingTask(WrapNotNull(decoder));
182+
task->Run();
183+
if (!decoder->GetDecodeDone() || decoder->HasError()) {
184+
return NS_ERROR_FAILURE;
185+
}
186+
187+
aMetadata = decoder->GetImageMetadata();
188+
if (aMetadata.GetNativeSizes().IsEmpty() && aMetadata.HasSize()) {
189+
aMetadata.AddNativeSize(aMetadata.GetSize());
190+
}
191+
192+
return NS_OK;
193+
}
194+
195+
/* static */ already_AddRefed<gfx::SourceSurface>
196+
ImageOps::DecodeToSurface(nsIInputStream* aInputStream,
197+
const nsACString& aMimeType,
198+
uint32_t aFlags,
199+
Maybe<IntSize> aSize /* = Nothing() */)
200+
{
201+
RefPtr<ImageBuffer> buffer = CreateImageBuffer(aInputStream);
202+
return DecodeToSurface(buffer, aMimeType, aFlags, aSize);
203+
}
204+
205+
/* static */ already_AddRefed<gfx::SourceSurface>
206+
ImageOps::DecodeToSurface(ImageBuffer* aBuffer,
207+
const nsACString& aMimeType,
208+
uint32_t aFlags,
209+
Maybe<IntSize> aSize /* = Nothing() */)
210+
{
211+
if (!aBuffer) {
212+
return nullptr;
213+
}
214+
215+
RefPtr<SourceBuffer> sourceBuffer = aBuffer->GetSourceBuffer();
216+
if (NS_WARN_IF(!sourceBuffer)) {
217+
return nullptr;
218+
}
219+
125220
// Create a decoder.
126221
DecoderType decoderType =
127222
DecoderFactory::GetDecoderType(PromiseFlatCString(aMimeType).get());
128223
RefPtr<Decoder> decoder =
129-
DecoderFactory::CreateAnonymousDecoder(decoderType, sourceBuffer,
130-
Nothing(), ToSurfaceFlags(aFlags));
224+
DecoderFactory::CreateAnonymousDecoder(decoderType,
225+
WrapNotNull(sourceBuffer),
226+
aSize, ToSurfaceFlags(aFlags));
131227
if (!decoder) {
132228
return nullptr;
133229
}

0 commit comments

Comments
 (0)