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 #include "ImageOps.h"
8 
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"
23 
24 using namespace mozilla::gfx;
25 
26 namespace mozilla::image {
27 
28 /* static */
Freeze(Image * aImage)29 already_AddRefed<Image> ImageOps::Freeze(Image* aImage) {
30   RefPtr<Image> frozenImage = new FrozenImage(aImage);
31   return frozenImage.forget();
32 }
33 
34 /* static */
Freeze(imgIContainer * aImage)35 already_AddRefed<imgIContainer> ImageOps::Freeze(imgIContainer* aImage) {
36   nsCOMPtr<imgIContainer> frozenImage =
37       new FrozenImage(static_cast<Image*>(aImage));
38   return frozenImage.forget();
39 }
40 
41 /* static */
Clip(Image * aImage,nsIntRect aClip,const Maybe<nsSize> & aSVGViewportSize)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();
47 }
48 
49 /* static */
Clip(imgIContainer * aImage,nsIntRect aClip,const Maybe<nsSize> & aSVGViewportSize)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();
56 }
57 
58 /* static */
Orient(Image * aImage,Orientation aOrientation)59 already_AddRefed<Image> ImageOps::Orient(Image* aImage,
60                                          Orientation aOrientation) {
61   if (aOrientation.IsIdentity()) {
62     return do_AddRef(aImage);
63   }
64   RefPtr<Image> image = new OrientedImage(aImage, aOrientation);
65   return image.forget();
66 }
67 
68 /* static */
Orient(imgIContainer * aImage,Orientation aOrientation)69 already_AddRefed<imgIContainer> ImageOps::Orient(imgIContainer* aImage,
70                                                  Orientation aOrientation) {
71   return Orient(static_cast<Image*>(aImage), aOrientation);
72 }
73 
74 /* static */
Unorient(imgIContainer * aImage)75 already_AddRefed<imgIContainer> ImageOps::Unorient(imgIContainer* aImage) {
76   return Orient(aImage, aImage->GetOrientation().Reversed());
77 }
78 
79 /* static */
CreateFromDrawable(gfxDrawable * aDrawable)80 already_AddRefed<imgIContainer> ImageOps::CreateFromDrawable(
81     gfxDrawable* aDrawable) {
82   nsCOMPtr<imgIContainer> drawableImage = new DynamicImage(aDrawable);
83   return drawableImage.forget();
84 }
85 
86 class ImageOps::ImageBufferImpl final : public ImageOps::ImageBuffer {
87  public:
ImageBufferImpl(already_AddRefed<SourceBuffer> aSourceBuffer)88   explicit ImageBufferImpl(already_AddRefed<SourceBuffer> aSourceBuffer)
89       : mSourceBuffer(aSourceBuffer) {}
90 
91  protected:
~ImageBufferImpl()92   ~ImageBufferImpl() override {}
93 
GetSourceBuffer() const94   already_AddRefed<SourceBuffer> GetSourceBuffer() const override {
95     RefPtr<SourceBuffer> sourceBuffer = mSourceBuffer;
96     return sourceBuffer.forget();
97   }
98 
99  private:
100   RefPtr<SourceBuffer> mSourceBuffer;
101 };
102 
103 /* static */ already_AddRefed<ImageOps::ImageBuffer>
CreateImageBuffer(already_AddRefed<nsIInputStream> aInputStream)104 ImageOps::CreateImageBuffer(already_AddRefed<nsIInputStream> aInputStream) {
105   nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
106   MOZ_ASSERT(inputStream);
107 
108   nsresult rv;
109 
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;
117     }
118     inputStream = std::move(bufStream);
119   }
120 
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;
126   }
127 
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;
134   }
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;
142   }
143   sourceBuffer->Complete(NS_OK);
144 
145   RefPtr<ImageBuffer> imageBuffer = new ImageBufferImpl(sourceBuffer.forget());
146   return imageBuffer.forget();
147 }
148 
149 /* static */
DecodeMetadata(already_AddRefed<nsIInputStream> aInputStream,const nsACString & aMimeType,ImageMetadata & aMetadata)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);
156 }
157 
158 /* static */
DecodeMetadata(ImageBuffer * aBuffer,const nsACString & aMimeType,ImageMetadata & aMetadata)159 nsresult ImageOps::DecodeMetadata(ImageBuffer* aBuffer,
160                                   const nsACString& aMimeType,
161                                   ImageMetadata& aMetadata) {
162   if (!aBuffer) {
163     return NS_ERROR_FAILURE;
164   }
165 
166   RefPtr<SourceBuffer> sourceBuffer = aBuffer->GetSourceBuffer();
167   if (NS_WARN_IF(!sourceBuffer)) {
168     return NS_ERROR_FAILURE;
169   }
170 
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;
178   }
179 
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;
186   }
187 
188   aMetadata = decoder->GetImageMetadata();
189   if (aMetadata.GetNativeSizes().IsEmpty() && aMetadata.HasSize()) {
190     aMetadata.AddNativeSize(aMetadata.GetSize());
191   }
192 
193   return NS_OK;
194 }
195 
DecodeToSurface(already_AddRefed<nsIInputStream> aInputStream,const nsACString & aMimeType,uint32_t aFlags,const Maybe<IntSize> & aSize)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);
202 }
203 
DecodeToSurface(ImageBuffer * aBuffer,const nsACString & aMimeType,uint32_t aFlags,const Maybe<IntSize> & 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;
209   }
210 
211   RefPtr<SourceBuffer> sourceBuffer = aBuffer->GetSourceBuffer();
212   if (NS_WARN_IF(!sourceBuffer)) {
213     return nullptr;
214   }
215 
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;
224   }
225 
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;
232   }
233 
234   // Pull out the surface.
235   RawAccessFrameRef frame = decoder->GetCurrentFrameRef();
236   if (!frame) {
237     return nullptr;
238   }
239 
240   RefPtr<SourceSurface> surface = frame->GetSourceSurface();
241   if (!surface) {
242     return nullptr;
243   }
244 
245   return surface.forget();
246 }
247 
248 }  // namespace mozilla::image
249