Bug 1686838 [wpt PR 27194] - Update wpt metadata, a=testonly
[gecko.git] / image / ImageOps.cpp
blob7347e72758fd325299121e28dc99a3f7975c6c04
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::Unorient(imgIContainer* aImage) {
76 Orientation orientation = aImage->GetOrientation().Reversed();
77 nsCOMPtr<imgIContainer> orientedImage =
78 new OrientedImage(static_cast<Image*>(aImage), orientation);
79 return orientedImage.forget();
82 /* static */
83 already_AddRefed<imgIContainer> ImageOps::CreateFromDrawable(
84 gfxDrawable* aDrawable) {
85 nsCOMPtr<imgIContainer> drawableImage = new DynamicImage(aDrawable);
86 return drawableImage.forget();
89 class ImageOps::ImageBufferImpl final : public ImageOps::ImageBuffer {
90 public:
91 explicit ImageBufferImpl(already_AddRefed<SourceBuffer> aSourceBuffer)
92 : mSourceBuffer(aSourceBuffer) {}
94 protected:
95 ~ImageBufferImpl() override {}
97 already_AddRefed<SourceBuffer> GetSourceBuffer() const override {
98 RefPtr<SourceBuffer> sourceBuffer = mSourceBuffer;
99 return sourceBuffer.forget();
102 private:
103 RefPtr<SourceBuffer> mSourceBuffer;
106 /* static */ already_AddRefed<ImageOps::ImageBuffer>
107 ImageOps::CreateImageBuffer(already_AddRefed<nsIInputStream> aInputStream) {
108 nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
109 MOZ_ASSERT(inputStream);
111 nsresult rv;
113 // Prepare the input stream.
114 if (!NS_InputStreamIsBuffered(inputStream)) {
115 nsCOMPtr<nsIInputStream> bufStream;
116 rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream),
117 inputStream.forget(), 1024);
118 if (NS_WARN_IF(NS_FAILED(rv))) {
119 return nullptr;
121 inputStream = std::move(bufStream);
124 // Figure out how much data we've been passed.
125 uint64_t length;
126 rv = inputStream->Available(&length);
127 if (NS_FAILED(rv) || length > UINT32_MAX) {
128 return nullptr;
131 // Write the data into a SourceBuffer.
132 RefPtr<SourceBuffer> sourceBuffer = new SourceBuffer();
133 sourceBuffer->ExpectLength(length);
134 rv = sourceBuffer->AppendFromInputStream(inputStream, length);
135 if (NS_FAILED(rv)) {
136 return nullptr;
138 // Make sure our sourceBuffer is marked as complete.
139 if (sourceBuffer->IsComplete()) {
140 NS_WARNING(
141 "The SourceBuffer was unexpectedly marked as complete. This may "
142 "indicate either an OOM condition, or that imagelib was not "
143 "initialized properly.");
144 return nullptr;
146 sourceBuffer->Complete(NS_OK);
148 RefPtr<ImageBuffer> imageBuffer = new ImageBufferImpl(sourceBuffer.forget());
149 return imageBuffer.forget();
152 /* static */
153 nsresult ImageOps::DecodeMetadata(already_AddRefed<nsIInputStream> aInputStream,
154 const nsACString& aMimeType,
155 ImageMetadata& aMetadata) {
156 nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
157 RefPtr<ImageBuffer> buffer = CreateImageBuffer(inputStream.forget());
158 return DecodeMetadata(buffer, aMimeType, aMetadata);
161 /* static */
162 nsresult ImageOps::DecodeMetadata(ImageBuffer* aBuffer,
163 const nsACString& aMimeType,
164 ImageMetadata& aMetadata) {
165 if (!aBuffer) {
166 return NS_ERROR_FAILURE;
169 RefPtr<SourceBuffer> sourceBuffer = aBuffer->GetSourceBuffer();
170 if (NS_WARN_IF(!sourceBuffer)) {
171 return NS_ERROR_FAILURE;
174 // Create a decoder.
175 DecoderType decoderType =
176 DecoderFactory::GetDecoderType(PromiseFlatCString(aMimeType).get());
177 RefPtr<Decoder> decoder = DecoderFactory::CreateAnonymousMetadataDecoder(
178 decoderType, WrapNotNull(sourceBuffer));
179 if (!decoder) {
180 return NS_ERROR_FAILURE;
183 // Run the decoder synchronously.
184 RefPtr<IDecodingTask> task =
185 new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
186 task->Run();
187 if (!decoder->GetDecodeDone() || decoder->HasError()) {
188 return NS_ERROR_FAILURE;
191 aMetadata = decoder->GetImageMetadata();
192 if (aMetadata.GetNativeSizes().IsEmpty() && aMetadata.HasSize()) {
193 aMetadata.AddNativeSize(aMetadata.GetSize());
196 return NS_OK;
199 /* static */ already_AddRefed<gfx::SourceSurface> ImageOps::DecodeToSurface(
200 already_AddRefed<nsIInputStream> aInputStream, const nsACString& aMimeType,
201 uint32_t aFlags, const Maybe<IntSize>& aSize /* = Nothing() */) {
202 nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
203 RefPtr<ImageBuffer> buffer = CreateImageBuffer(inputStream.forget());
204 return DecodeToSurface(buffer, aMimeType, aFlags, aSize);
207 /* static */ already_AddRefed<gfx::SourceSurface> ImageOps::DecodeToSurface(
208 ImageBuffer* aBuffer, const nsACString& aMimeType, uint32_t aFlags,
209 const Maybe<IntSize>& aSize /* = Nothing() */) {
210 if (!aBuffer) {
211 return nullptr;
214 RefPtr<SourceBuffer> sourceBuffer = aBuffer->GetSourceBuffer();
215 if (NS_WARN_IF(!sourceBuffer)) {
216 return nullptr;
219 // Create a decoder.
220 DecoderType decoderType =
221 DecoderFactory::GetDecoderType(PromiseFlatCString(aMimeType).get());
222 RefPtr<Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
223 decoderType, WrapNotNull(sourceBuffer), aSize,
224 DecoderFlags::FIRST_FRAME_ONLY, ToSurfaceFlags(aFlags));
225 if (!decoder) {
226 return nullptr;
229 // Run the decoder synchronously.
230 RefPtr<IDecodingTask> task =
231 new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
232 task->Run();
233 if (!decoder->GetDecodeDone() || decoder->HasError()) {
234 return nullptr;
237 // Pull out the surface.
238 RawAccessFrameRef frame = decoder->GetCurrentFrameRef();
239 if (!frame) {
240 return nullptr;
243 RefPtr<SourceSurface> surface = frame->GetSourceSurface();
244 if (!surface) {
245 return nullptr;
248 return surface.forget();
251 } // namespace image
252 } // namespace mozilla