ImageOps.cpp (7933B)
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 */ 29 already_AddRefed<Image> ImageOps::Freeze(Image* aImage) { 30 RefPtr<Image> frozenImage = new FrozenImage(aImage); 31 return frozenImage.forget(); 32 } 33 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(); 39 } 40 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(); 47 } 48 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(); 56 } 57 58 /* static */ 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 */ 69 already_AddRefed<imgIContainer> ImageOps::Orient(imgIContainer* aImage, 70 Orientation aOrientation) { 71 return Orient(static_cast<Image*>(aImage), aOrientation); 72 } 73 74 /* static */ 75 already_AddRefed<imgIContainer> ImageOps::Unorient(imgIContainer* aImage) { 76 return Orient(aImage, aImage->GetOrientation().Reversed()); 77 } 78 79 /* static */ 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: 88 explicit ImageBufferImpl(already_AddRefed<SourceBuffer> aSourceBuffer) 89 : mSourceBuffer(aSourceBuffer) {} 90 91 protected: 92 ~ImageBufferImpl() override {} 93 94 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> 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 */ 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 */ 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 DecoderFlags decoderFlags = 175 DecoderFactory::GetDefaultDecoderFlagsForType(decoderType); 176 decoderFlags |= DecoderFlags::FIRST_FRAME_ONLY; 177 RefPtr<Decoder> decoder = DecoderFactory::CreateAnonymousMetadataDecoder( 178 decoderType, WrapNotNull(sourceBuffer), decoderFlags); 179 if (!decoder) { 180 return NS_ERROR_FAILURE; 181 } 182 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; 189 } 190 191 aMetadata = decoder->GetImageMetadata(); 192 if (aMetadata.GetNativeSizes().IsEmpty() && aMetadata.HasSize()) { 193 aMetadata.AddNativeSize(aMetadata.GetSize()); 194 } 195 196 return NS_OK; 197 } 198 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); 205 } 206 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; 212 } 213 214 RefPtr<SourceBuffer> sourceBuffer = aBuffer->GetSourceBuffer(); 215 if (NS_WARN_IF(!sourceBuffer)) { 216 return nullptr; 217 } 218 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; 227 } 228 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; 235 } 236 237 // Pull out the surface. 238 RawAccessFrameRef frame = decoder->GetCurrentFrameRef(); 239 if (!frame) { 240 return nullptr; 241 } 242 243 RefPtr<SourceSurface> surface = frame->GetSourceSurface(); 244 if (!surface) { 245 return nullptr; 246 } 247 248 return surface.forget(); 249 } 250 251 } // namespace mozilla::image