Bug 1869043 assert that graph set access is main thread only r=padenot
[gecko.git] / image / ImageOps.cpp
blob875dc7b98502b7c33b9e2c091f428b164d4fcd72
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::image {
28 /* static */
29 already_AddRefed<Image> ImageOps::Freeze(Image* aImage) {
30 RefPtr<Image> frozenImage = new FrozenImage(aImage);
31 return frozenImage.forget();
34 /* static */
35 already_AddRefed<imgIContainer> ImageOps::Freeze(imgIContainer* aImage) {
36 nsCOMPtr<imgIContainer> frozenImage =
37 new FrozenImage(static_cast<Image*>(aImage));
38 return frozenImage.forget();
41 /* static */
42 already_AddRefed<Image> ImageOps::Clip(Image* aImage, nsIntRect aClip,
43 const Maybe<nsSize>& aSVGViewportSize) {
44 RefPtr<Image> clippedImage =
45 new ClippedImage(aImage, aClip, aSVGViewportSize);
46 return clippedImage.forget();
49 /* static */
50 already_AddRefed<imgIContainer> ImageOps::Clip(
51 imgIContainer* aImage, nsIntRect aClip,
52 const Maybe<nsSize>& aSVGViewportSize) {
53 nsCOMPtr<imgIContainer> clippedImage =
54 new ClippedImage(static_cast<Image*>(aImage), aClip, aSVGViewportSize);
55 return clippedImage.forget();
58 /* static */
59 already_AddRefed<Image> ImageOps::Orient(Image* aImage,
60 Orientation aOrientation) {
61 if (aOrientation.IsIdentity()) {
62 return do_AddRef(aImage);
64 RefPtr<Image> image = new OrientedImage(aImage, aOrientation);
65 return image.forget();
68 /* static */
69 already_AddRefed<imgIContainer> ImageOps::Orient(imgIContainer* aImage,
70 Orientation aOrientation) {
71 return Orient(static_cast<Image*>(aImage), aOrientation);
74 /* static */
75 already_AddRefed<imgIContainer> ImageOps::Unorient(imgIContainer* aImage) {
76 return Orient(aImage, aImage->GetOrientation().Reversed());
79 /* static */
80 already_AddRefed<imgIContainer> ImageOps::CreateFromDrawable(
81 gfxDrawable* aDrawable) {
82 nsCOMPtr<imgIContainer> drawableImage = new DynamicImage(aDrawable);
83 return drawableImage.forget();
86 class ImageOps::ImageBufferImpl final : public ImageOps::ImageBuffer {
87 public:
88 explicit ImageBufferImpl(already_AddRefed<SourceBuffer> aSourceBuffer)
89 : mSourceBuffer(aSourceBuffer) {}
91 protected:
92 ~ImageBufferImpl() override {}
94 already_AddRefed<SourceBuffer> GetSourceBuffer() const override {
95 RefPtr<SourceBuffer> sourceBuffer = mSourceBuffer;
96 return sourceBuffer.forget();
99 private:
100 RefPtr<SourceBuffer> mSourceBuffer;
103 /* static */ already_AddRefed<ImageOps::ImageBuffer>
104 ImageOps::CreateImageBuffer(already_AddRefed<nsIInputStream> aInputStream) {
105 nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
106 MOZ_ASSERT(inputStream);
108 nsresult rv;
110 // Prepare the input stream.
111 if (!NS_InputStreamIsBuffered(inputStream)) {
112 nsCOMPtr<nsIInputStream> bufStream;
113 rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream),
114 inputStream.forget(), 1024);
115 if (NS_WARN_IF(NS_FAILED(rv))) {
116 return nullptr;
118 inputStream = std::move(bufStream);
121 // Figure out how much data we've been passed.
122 uint64_t length;
123 rv = inputStream->Available(&length);
124 if (NS_FAILED(rv) || length > UINT32_MAX) {
125 return nullptr;
128 // Write the data into a SourceBuffer.
129 RefPtr<SourceBuffer> sourceBuffer = new SourceBuffer();
130 sourceBuffer->ExpectLength(length);
131 rv = sourceBuffer->AppendFromInputStream(inputStream, length);
132 if (NS_FAILED(rv)) {
133 return nullptr;
135 // Make sure our sourceBuffer is marked as complete.
136 if (sourceBuffer->IsComplete()) {
137 NS_WARNING(
138 "The SourceBuffer was unexpectedly marked as complete. This may "
139 "indicate either an OOM condition, or that imagelib was not "
140 "initialized properly.");
141 return nullptr;
143 sourceBuffer->Complete(NS_OK);
145 RefPtr<ImageBuffer> imageBuffer = new ImageBufferImpl(sourceBuffer.forget());
146 return imageBuffer.forget();
149 /* static */
150 nsresult ImageOps::DecodeMetadata(already_AddRefed<nsIInputStream> aInputStream,
151 const nsACString& aMimeType,
152 ImageMetadata& aMetadata) {
153 nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
154 RefPtr<ImageBuffer> buffer = CreateImageBuffer(inputStream.forget());
155 return DecodeMetadata(buffer, aMimeType, aMetadata);
158 /* static */
159 nsresult ImageOps::DecodeMetadata(ImageBuffer* aBuffer,
160 const nsACString& aMimeType,
161 ImageMetadata& aMetadata) {
162 if (!aBuffer) {
163 return NS_ERROR_FAILURE;
166 RefPtr<SourceBuffer> sourceBuffer = aBuffer->GetSourceBuffer();
167 if (NS_WARN_IF(!sourceBuffer)) {
168 return NS_ERROR_FAILURE;
171 // Create a decoder.
172 DecoderType decoderType =
173 DecoderFactory::GetDecoderType(PromiseFlatCString(aMimeType).get());
174 RefPtr<Decoder> decoder = DecoderFactory::CreateAnonymousMetadataDecoder(
175 decoderType, WrapNotNull(sourceBuffer));
176 if (!decoder) {
177 return NS_ERROR_FAILURE;
180 // Run the decoder synchronously.
181 RefPtr<IDecodingTask> task =
182 new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
183 task->Run();
184 if (!decoder->GetDecodeDone() || decoder->HasError()) {
185 return NS_ERROR_FAILURE;
188 aMetadata = decoder->GetImageMetadata();
189 if (aMetadata.GetNativeSizes().IsEmpty() && aMetadata.HasSize()) {
190 aMetadata.AddNativeSize(aMetadata.GetSize());
193 return NS_OK;
196 /* static */ already_AddRefed<gfx::SourceSurface> ImageOps::DecodeToSurface(
197 already_AddRefed<nsIInputStream> aInputStream, const nsACString& aMimeType,
198 uint32_t aFlags, const Maybe<IntSize>& aSize /* = Nothing() */) {
199 nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
200 RefPtr<ImageBuffer> buffer = CreateImageBuffer(inputStream.forget());
201 return DecodeToSurface(buffer, aMimeType, aFlags, aSize);
204 /* static */ already_AddRefed<gfx::SourceSurface> ImageOps::DecodeToSurface(
205 ImageBuffer* aBuffer, const nsACString& aMimeType, uint32_t aFlags,
206 const Maybe<IntSize>& aSize /* = Nothing() */) {
207 if (!aBuffer) {
208 return nullptr;
211 RefPtr<SourceBuffer> sourceBuffer = aBuffer->GetSourceBuffer();
212 if (NS_WARN_IF(!sourceBuffer)) {
213 return nullptr;
216 // Create a decoder.
217 DecoderType decoderType =
218 DecoderFactory::GetDecoderType(PromiseFlatCString(aMimeType).get());
219 RefPtr<Decoder> decoder = DecoderFactory::CreateAnonymousDecoder(
220 decoderType, WrapNotNull(sourceBuffer), aSize,
221 DecoderFlags::FIRST_FRAME_ONLY, ToSurfaceFlags(aFlags));
222 if (!decoder) {
223 return nullptr;
226 // Run the decoder synchronously.
227 RefPtr<IDecodingTask> task =
228 new AnonymousDecodingTask(WrapNotNull(decoder), /* aResumable */ false);
229 task->Run();
230 if (!decoder->GetDecodeDone() || decoder->HasError()) {
231 return nullptr;
234 // Pull out the surface.
235 RawAccessFrameRef frame = decoder->GetCurrentFrameRef();
236 if (!frame) {
237 return nullptr;
240 RefPtr<SourceSurface> surface = frame->GetSourceSurface();
241 if (!surface) {
242 return nullptr;
245 return surface.forget();
248 } // namespace mozilla::image