Bug 1567650 [wpt PR 17950] - [ElementTiming] Replace responseEnd with loadTime, a...
[gecko.git] / image / ImageOps.cpp
blob622606a18d82449c94f6083e2ae51b6ef1965422
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 */
30 already_AddRefed<Image> ImageOps::Freeze(Image* aImage) {
31 RefPtr<Image> frozenImage = new FrozenImage(aImage);
32 return frozenImage.forget();
35 /* static */
36 already_AddRefed<imgIContainer> ImageOps::Freeze(imgIContainer* aImage) {
37 nsCOMPtr<imgIContainer> frozenImage =
38 new FrozenImage(static_cast<Image*>(aImage));
39 return frozenImage.forget();
42 /* static */
43 already_AddRefed<Image> ImageOps::Clip(Image* aImage, nsIntRect aClip,
44 const Maybe<nsSize>& aSVGViewportSize) {
45 RefPtr<Image> clippedImage =
46 new ClippedImage(aImage, aClip, aSVGViewportSize);
47 return clippedImage.forget();
50 /* static */
51 already_AddRefed<imgIContainer> ImageOps::Clip(
52 imgIContainer* aImage, nsIntRect aClip,
53 const Maybe<nsSize>& aSVGViewportSize) {
54 nsCOMPtr<imgIContainer> clippedImage =
55 new ClippedImage(static_cast<Image*>(aImage), aClip, aSVGViewportSize);
56 return clippedImage.forget();
59 /* static */
60 already_AddRefed<Image> ImageOps::Orient(Image* aImage,
61 Orientation aOrientation) {
62 RefPtr<Image> orientedImage = new OrientedImage(aImage, aOrientation);
63 return orientedImage.forget();
66 /* static */
67 already_AddRefed<imgIContainer> ImageOps::Orient(imgIContainer* aImage,
68 Orientation aOrientation) {
69 nsCOMPtr<imgIContainer> orientedImage =
70 new OrientedImage(static_cast<Image*>(aImage), aOrientation);
71 return orientedImage.forget();
74 /* static */
75 already_AddRefed<imgIContainer> ImageOps::CreateFromDrawable(
76 gfxDrawable* aDrawable) {
77 nsCOMPtr<imgIContainer> drawableImage = new DynamicImage(aDrawable);
78 return drawableImage.forget();
81 class ImageOps::ImageBufferImpl final : public ImageOps::ImageBuffer {
82 public:
83 explicit ImageBufferImpl(already_AddRefed<SourceBuffer> aSourceBuffer)
84 : mSourceBuffer(aSourceBuffer) {}
86 protected:
87 ~ImageBufferImpl() override {}
89 already_AddRefed<SourceBuffer> GetSourceBuffer() const override {
90 RefPtr<SourceBuffer> sourceBuffer = mSourceBuffer;
91 return sourceBuffer.forget();
94 private:
95 RefPtr<SourceBuffer> mSourceBuffer;
98 /* static */ already_AddRefed<ImageOps::ImageBuffer>
99 ImageOps::CreateImageBuffer(already_AddRefed<nsIInputStream> aInputStream) {
100 nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
101 MOZ_ASSERT(inputStream);
103 nsresult rv;
105 // Prepare the input stream.
106 if (!NS_InputStreamIsBuffered(inputStream)) {
107 nsCOMPtr<nsIInputStream> bufStream;
108 rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream),
109 inputStream.forget(), 1024);
110 if (NS_WARN_IF(NS_FAILED(rv))) {
111 return nullptr;
113 inputStream = std::move(bufStream);
116 // Figure out how much data we've been passed.
117 uint64_t length;
118 rv = inputStream->Available(&length);
119 if (NS_FAILED(rv) || length > UINT32_MAX) {
120 return nullptr;
123 // Write the data into a SourceBuffer.
124 RefPtr<SourceBuffer> sourceBuffer = new SourceBuffer();
125 sourceBuffer->ExpectLength(length);
126 rv = sourceBuffer->AppendFromInputStream(inputStream, length);
127 if (NS_FAILED(rv)) {
128 return nullptr;
130 // Make sure our sourceBuffer is marked as complete.
131 if (sourceBuffer->IsComplete()) {
132 NS_WARNING(
133 "The SourceBuffer was unexpectedly marked as complete. This may "
134 "indicate either an OOM condition, or that imagelib was not "
135 "initialized properly.");
136 return nullptr;
138 sourceBuffer->Complete(NS_OK);
140 RefPtr<ImageBuffer> imageBuffer = new ImageBufferImpl(sourceBuffer.forget());
141 return imageBuffer.forget();
144 /* static */
145 nsresult ImageOps::DecodeMetadata(already_AddRefed<nsIInputStream> aInputStream,
146 const nsACString& aMimeType,
147 ImageMetadata& aMetadata) {
148 nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
149 RefPtr<ImageBuffer> buffer = CreateImageBuffer(inputStream.forget());
150 return DecodeMetadata(buffer, aMimeType, aMetadata);
153 /* static */
154 nsresult ImageOps::DecodeMetadata(ImageBuffer* aBuffer,
155 const nsACString& aMimeType,
156 ImageMetadata& aMetadata) {
157 if (!aBuffer) {
158 return NS_ERROR_FAILURE;
161 RefPtr<SourceBuffer> sourceBuffer = aBuffer->GetSourceBuffer();
162 if (NS_WARN_IF(!sourceBuffer)) {
163 return NS_ERROR_FAILURE;
166 // Create a decoder.
167 DecoderType decoderType =
168 DecoderFactory::GetDecoderType(PromiseFlatCString(aMimeType).get());
169 RefPtr<Decoder> decoder = DecoderFactory::CreateAnonymousMetadataDecoder(
170 decoderType, WrapNotNull(sourceBuffer));
171 if (!decoder) {
172 return NS_ERROR_FAILURE;
175 // Run the decoder synchronously.
176 RefPtr<IDecodingTask> task =
177 new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
178 task->Run();
179 if (!decoder->GetDecodeDone() || decoder->HasError()) {
180 return NS_ERROR_FAILURE;
183 aMetadata = decoder->GetImageMetadata();
184 if (aMetadata.GetNativeSizes().IsEmpty() && aMetadata.HasSize()) {
185 aMetadata.AddNativeSize(aMetadata.GetSize());
188 return NS_OK;
191 /* static */ already_AddRefed<gfx::SourceSurface> ImageOps::DecodeToSurface(
192 already_AddRefed<nsIInputStream> aInputStream, const nsACString& aMimeType,
193 uint32_t aFlags, const Maybe<IntSize>& aSize /* = Nothing() */) {
194 nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
195 RefPtr<ImageBuffer> buffer = CreateImageBuffer(inputStream.forget());
196 return DecodeToSurface(buffer, aMimeType, aFlags, aSize);
199 /* static */ already_AddRefed<gfx::SourceSurface> ImageOps::DecodeToSurface(
200 ImageBuffer* aBuffer, const nsACString& aMimeType, uint32_t aFlags,
201 const Maybe<IntSize>& aSize /* = Nothing() */) {
202 if (!aBuffer) {
203 return nullptr;
206 RefPtr<SourceBuffer> sourceBuffer = aBuffer->GetSourceBuffer();
207 if (NS_WARN_IF(!sourceBuffer)) {
208 return nullptr;
211 // Create a decoder.
212 DecoderType decoderType =
213 DecoderFactory::GetDecoderType(PromiseFlatCString(aMimeType).get());
214 RefPtr<Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
215 decoderType, WrapNotNull(sourceBuffer), aSize,
216 DecoderFlags::FIRST_FRAME_ONLY, ToSurfaceFlags(aFlags));
217 if (!decoder) {
218 return nullptr;
221 // Run the decoder synchronously.
222 RefPtr<IDecodingTask> task =
223 new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
224 task->Run();
225 if (!decoder->GetDecodeDone() || decoder->HasError()) {
226 return nullptr;
229 // Pull out the surface.
230 RawAccessFrameRef frame = decoder->GetCurrentFrameRef();
231 if (!frame) {
232 return nullptr;
235 RefPtr<SourceSurface> surface = frame->GetSourceSurface();
236 if (!surface) {
237 return nullptr;
240 return surface.forget();
243 } // namespace image
244 } // namespace mozilla