Bug 1516095 [wpt PR 14643] - [Resource-Timing] Flakiness reduction, a=testonly
[gecko.git] / image / ImageOps.cpp
bloba7c474a90399b38cdc21a4ead42efa3345517be9
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
7 #include "ImageOps.h"
9 #include "ClippedImage.h"
10 #include "Decoder.h"
11 #include "DecoderFactory.h"
12 #include "DynamicImage.h"
13 #include "FrozenImage.h"
14 #include "IDecodingTask.h"
15 #include "Image.h"
16 #include "ImageMetadata.h"
17 #include "imgIContainer.h"
18 #include "mozilla/gfx/2D.h"
19 #include "nsNetUtil.h" // for NS_NewBufferedInputStream
20 #include "nsStreamUtils.h"
21 #include "OrientedImage.h"
22 #include "SourceBuffer.h"
24 using namespace mozilla::gfx;
26 namespace mozilla {
27 namespace image {
29 /* static */ already_AddRefed<Image> ImageOps::Freeze(Image* aImage) {
30 RefPtr<Image> frozenImage = new FrozenImage(aImage);
31 return frozenImage.forget();
34 /* static */ already_AddRefed<imgIContainer> ImageOps::Freeze(
35 imgIContainer* aImage) {
36 nsCOMPtr<imgIContainer> frozenImage =
37 new FrozenImage(static_cast<Image*>(aImage));
38 return frozenImage.forget();
41 /* static */ already_AddRefed<Image> ImageOps::Clip(
42 Image* aImage, nsIntRect aClip, const Maybe<nsSize>& aSVGViewportSize) {
43 RefPtr<Image> clippedImage =
44 new ClippedImage(aImage, aClip, aSVGViewportSize);
45 return clippedImage.forget();
48 /* static */ already_AddRefed<imgIContainer> ImageOps::Clip(
49 imgIContainer* aImage, nsIntRect aClip,
50 const Maybe<nsSize>& aSVGViewportSize) {
51 nsCOMPtr<imgIContainer> clippedImage =
52 new ClippedImage(static_cast<Image*>(aImage), aClip, aSVGViewportSize);
53 return clippedImage.forget();
56 /* static */ already_AddRefed<Image> ImageOps::Orient(
57 Image* aImage, Orientation aOrientation) {
58 RefPtr<Image> orientedImage = new OrientedImage(aImage, aOrientation);
59 return orientedImage.forget();
62 /* static */ already_AddRefed<imgIContainer> ImageOps::Orient(
63 imgIContainer* aImage, Orientation aOrientation) {
64 nsCOMPtr<imgIContainer> orientedImage =
65 new OrientedImage(static_cast<Image*>(aImage), aOrientation);
66 return orientedImage.forget();
69 /* static */ already_AddRefed<imgIContainer> ImageOps::CreateFromDrawable(
70 gfxDrawable* aDrawable) {
71 nsCOMPtr<imgIContainer> drawableImage = new DynamicImage(aDrawable);
72 return drawableImage.forget();
75 class ImageOps::ImageBufferImpl final : public ImageOps::ImageBuffer {
76 public:
77 explicit ImageBufferImpl(already_AddRefed<SourceBuffer> aSourceBuffer)
78 : mSourceBuffer(aSourceBuffer) {}
80 protected:
81 ~ImageBufferImpl() override {}
83 already_AddRefed<SourceBuffer> GetSourceBuffer() const override {
84 RefPtr<SourceBuffer> sourceBuffer = mSourceBuffer;
85 return sourceBuffer.forget();
88 private:
89 RefPtr<SourceBuffer> mSourceBuffer;
92 /* static */ already_AddRefed<ImageOps::ImageBuffer>
93 ImageOps::CreateImageBuffer(already_AddRefed<nsIInputStream> aInputStream) {
94 nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
95 MOZ_ASSERT(inputStream);
97 nsresult rv;
99 // Prepare the input stream.
100 if (!NS_InputStreamIsBuffered(inputStream)) {
101 nsCOMPtr<nsIInputStream> bufStream;
102 rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream),
103 inputStream.forget(), 1024);
104 if (NS_WARN_IF(NS_FAILED(rv))) {
105 return nullptr;
109 // Figure out how much data we've been passed.
110 uint64_t length;
111 rv = inputStream->Available(&length);
112 if (NS_FAILED(rv) || length > UINT32_MAX) {
113 return nullptr;
116 // Write the data into a SourceBuffer.
117 RefPtr<SourceBuffer> sourceBuffer = new SourceBuffer();
118 sourceBuffer->ExpectLength(length);
119 rv = sourceBuffer->AppendFromInputStream(inputStream, length);
120 if (NS_FAILED(rv)) {
121 return nullptr;
123 // Make sure our sourceBuffer is marked as complete.
124 if (sourceBuffer->IsComplete()) {
125 NS_WARNING(
126 "The SourceBuffer was unexpectedly marked as complete. This may "
127 "indicate either an OOM condition, or that imagelib was not "
128 "initialized properly.");
129 return nullptr;
131 sourceBuffer->Complete(NS_OK);
133 RefPtr<ImageBuffer> imageBuffer = new ImageBufferImpl(sourceBuffer.forget());
134 return imageBuffer.forget();
137 /* static */ nsresult ImageOps::DecodeMetadata(
138 already_AddRefed<nsIInputStream> aInputStream, const nsACString& aMimeType,
139 ImageMetadata& aMetadata) {
140 nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
141 RefPtr<ImageBuffer> buffer = CreateImageBuffer(inputStream.forget());
142 return DecodeMetadata(buffer, aMimeType, aMetadata);
145 /* static */ nsresult ImageOps::DecodeMetadata(ImageBuffer* aBuffer,
146 const nsACString& aMimeType,
147 ImageMetadata& aMetadata) {
148 if (!aBuffer) {
149 return NS_ERROR_FAILURE;
152 RefPtr<SourceBuffer> sourceBuffer = aBuffer->GetSourceBuffer();
153 if (NS_WARN_IF(!sourceBuffer)) {
154 return NS_ERROR_FAILURE;
157 // Create a decoder.
158 DecoderType decoderType =
159 DecoderFactory::GetDecoderType(PromiseFlatCString(aMimeType).get());
160 RefPtr<Decoder> decoder = DecoderFactory::CreateAnonymousMetadataDecoder(
161 decoderType, WrapNotNull(sourceBuffer));
162 if (!decoder) {
163 return NS_ERROR_FAILURE;
166 // Run the decoder synchronously.
167 RefPtr<IDecodingTask> task =
168 new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
169 task->Run();
170 if (!decoder->GetDecodeDone() || decoder->HasError()) {
171 return NS_ERROR_FAILURE;
174 aMetadata = decoder->GetImageMetadata();
175 if (aMetadata.GetNativeSizes().IsEmpty() && aMetadata.HasSize()) {
176 aMetadata.AddNativeSize(aMetadata.GetSize());
179 return NS_OK;
182 /* static */ already_AddRefed<gfx::SourceSurface> ImageOps::DecodeToSurface(
183 already_AddRefed<nsIInputStream> aInputStream, const nsACString& aMimeType,
184 uint32_t aFlags, const Maybe<IntSize>& aSize /* = Nothing() */) {
185 nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
186 RefPtr<ImageBuffer> buffer = CreateImageBuffer(inputStream.forget());
187 return DecodeToSurface(buffer, aMimeType, aFlags, aSize);
190 /* static */ already_AddRefed<gfx::SourceSurface> ImageOps::DecodeToSurface(
191 ImageBuffer* aBuffer, const nsACString& aMimeType, uint32_t aFlags,
192 const Maybe<IntSize>& aSize /* = Nothing() */) {
193 if (!aBuffer) {
194 return nullptr;
197 RefPtr<SourceBuffer> sourceBuffer = aBuffer->GetSourceBuffer();
198 if (NS_WARN_IF(!sourceBuffer)) {
199 return nullptr;
202 // Create a decoder.
203 DecoderType decoderType =
204 DecoderFactory::GetDecoderType(PromiseFlatCString(aMimeType).get());
205 RefPtr<Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
206 decoderType, WrapNotNull(sourceBuffer), aSize,
207 DecoderFlags::FIRST_FRAME_ONLY, ToSurfaceFlags(aFlags));
208 if (!decoder) {
209 return nullptr;
212 // Run the decoder synchronously.
213 RefPtr<IDecodingTask> task =
214 new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
215 task->Run();
216 if (!decoder->GetDecodeDone() || decoder->HasError()) {
217 return nullptr;
220 // Pull out the surface.
221 RawAccessFrameRef frame = decoder->GetCurrentFrameRef();
222 if (!frame) {
223 return nullptr;
226 RefPtr<SourceSurface> surface = frame->GetSourceSurface();
227 if (!surface) {
228 return nullptr;
231 return surface.forget();
234 } // namespace image
235 } // namespace mozilla