Line data Source code
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 "imgTools.h"
8 :
9 : #include "gfxUtils.h"
10 : #include "mozilla/gfx/2D.h"
11 : #include "mozilla/gfx/Logging.h"
12 : #include "mozilla/RefPtr.h"
13 : #include "nsCOMPtr.h"
14 : #include "nsIDocument.h"
15 : #include "nsIDOMDocument.h"
16 : #include "nsError.h"
17 : #include "imgLoader.h"
18 : #include "imgICache.h"
19 : #include "imgIContainer.h"
20 : #include "imgIEncoder.h"
21 : #include "nsStreamUtils.h"
22 : #include "nsContentUtils.h"
23 : #include "ImageFactory.h"
24 : #include "Image.h"
25 : #include "ScriptedNotificationObserver.h"
26 : #include "imgIScriptedNotificationObserver.h"
27 : #include "gfxPlatform.h"
28 :
29 : using namespace mozilla::gfx;
30 :
31 : namespace mozilla {
32 : namespace image {
33 : /* ========== imgITools implementation ========== */
34 :
35 :
36 :
37 24 : NS_IMPL_ISUPPORTS(imgTools, imgITools)
38 :
39 3 : imgTools::imgTools()
40 : {
41 : /* member initializers and constructor code */
42 3 : }
43 :
44 0 : imgTools::~imgTools()
45 : {
46 : /* destructor code */
47 0 : }
48 :
49 : NS_IMETHODIMP
50 0 : imgTools::DecodeImageData(nsIInputStream* aInStr,
51 : const nsACString& aMimeType,
52 : imgIContainer** aContainer)
53 : {
54 0 : MOZ_ASSERT(*aContainer == nullptr,
55 : "Cannot provide an existing image container to DecodeImageData");
56 :
57 0 : return DecodeImage(aInStr, aMimeType, aContainer);
58 : }
59 :
60 : NS_IMETHODIMP
61 0 : imgTools::DecodeImage(nsIInputStream* aInStr,
62 : const nsACString& aMimeType,
63 : imgIContainer** aContainer)
64 : {
65 0 : MOZ_ASSERT(NS_IsMainThread());
66 :
67 : nsresult rv;
68 :
69 0 : NS_ENSURE_ARG_POINTER(aInStr);
70 :
71 : // Create a new image container to hold the decoded data.
72 0 : nsAutoCString mimeType(aMimeType);
73 0 : RefPtr<image::Image> image = ImageFactory::CreateAnonymousImage(mimeType);
74 0 : RefPtr<ProgressTracker> tracker = image->GetProgressTracker();
75 :
76 0 : if (image->HasError()) {
77 0 : return NS_ERROR_FAILURE;
78 : }
79 :
80 : // Prepare the input stream.
81 0 : nsCOMPtr<nsIInputStream> inStream = aInStr;
82 0 : if (!NS_InputStreamIsBuffered(aInStr)) {
83 0 : nsCOMPtr<nsIInputStream> bufStream;
84 0 : rv = NS_NewBufferedInputStream(getter_AddRefs(bufStream), aInStr, 1024);
85 0 : if (NS_SUCCEEDED(rv)) {
86 0 : inStream = bufStream;
87 : }
88 : }
89 :
90 : // Figure out how much data we've been passed.
91 : uint64_t length;
92 0 : rv = inStream->Available(&length);
93 0 : NS_ENSURE_SUCCESS(rv, rv);
94 0 : NS_ENSURE_TRUE(length <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
95 :
96 : // Send the source data to the Image.
97 0 : rv = image->OnImageDataAvailable(nullptr, nullptr, inStream, 0,
98 0 : uint32_t(length));
99 0 : NS_ENSURE_SUCCESS(rv, rv);
100 :
101 : // Let the Image know we've sent all the data.
102 0 : rv = image->OnImageDataComplete(nullptr, nullptr, NS_OK, true);
103 0 : tracker->SyncNotifyProgress(FLAG_LOAD_COMPLETE);
104 0 : NS_ENSURE_SUCCESS(rv, rv);
105 :
106 : // All done.
107 0 : NS_ADDREF(*aContainer = image.get());
108 0 : return NS_OK;
109 : }
110 :
111 : /**
112 : * This takes a DataSourceSurface rather than a SourceSurface because some
113 : * of the callers have a DataSourceSurface and we don't want to call
114 : * GetDataSurface on such surfaces since that may incure a conversion to
115 : * SurfaceType::DATA which we don't need.
116 : */
117 : static nsresult
118 0 : EncodeImageData(DataSourceSurface* aDataSurface,
119 : const nsACString& aMimeType,
120 : const nsAString& aOutputOptions,
121 : nsIInputStream** aStream)
122 : {
123 0 : MOZ_ASSERT(aDataSurface->GetFormat() == SurfaceFormat::B8G8R8A8,
124 : "We're assuming B8G8R8A8");
125 :
126 : // Get an image encoder for the media type
127 : nsAutoCString encoderCID(
128 0 : NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType);
129 :
130 0 : nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
131 0 : if (!encoder) {
132 0 : return NS_IMAGELIB_ERROR_NO_ENCODER;
133 : }
134 :
135 : DataSourceSurface::MappedSurface map;
136 0 : if (!aDataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
137 0 : return NS_ERROR_FAILURE;
138 : }
139 :
140 0 : IntSize size = aDataSurface->GetSize();
141 0 : uint32_t dataLength = map.mStride * size.height;
142 :
143 : // Encode the bitmap
144 0 : nsresult rv = encoder->InitFromData(map.mData,
145 : dataLength,
146 0 : size.width,
147 0 : size.height,
148 0 : map.mStride,
149 : imgIEncoder::INPUT_FORMAT_HOSTARGB,
150 0 : aOutputOptions);
151 0 : aDataSurface->Unmap();
152 0 : NS_ENSURE_SUCCESS(rv, rv);
153 :
154 0 : encoder.forget(aStream);
155 0 : return NS_OK;
156 : }
157 :
158 : NS_IMETHODIMP
159 0 : imgTools::EncodeImage(imgIContainer* aContainer,
160 : const nsACString& aMimeType,
161 : const nsAString& aOutputOptions,
162 : nsIInputStream** aStream)
163 : {
164 : // Use frame 0 from the image container.
165 : RefPtr<SourceSurface> frame =
166 0 : aContainer->GetFrame(imgIContainer::FRAME_FIRST,
167 0 : imgIContainer::FLAG_SYNC_DECODE);
168 0 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
169 :
170 0 : RefPtr<DataSourceSurface> dataSurface;
171 :
172 0 : if (frame->GetFormat() == SurfaceFormat::B8G8R8A8) {
173 0 : dataSurface = frame->GetDataSurface();
174 : } else {
175 : // Convert format to SurfaceFormat::B8G8R8A8
176 : dataSurface = gfxUtils::
177 0 : CopySurfaceToDataSourceSurfaceWithFormat(frame,
178 0 : SurfaceFormat::B8G8R8A8);
179 : }
180 :
181 0 : NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
182 :
183 0 : return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
184 : }
185 :
186 : NS_IMETHODIMP
187 0 : imgTools::EncodeScaledImage(imgIContainer* aContainer,
188 : const nsACString& aMimeType,
189 : int32_t aScaledWidth,
190 : int32_t aScaledHeight,
191 : const nsAString& aOutputOptions,
192 : nsIInputStream** aStream)
193 : {
194 0 : NS_ENSURE_ARG(aScaledWidth >= 0 && aScaledHeight >= 0);
195 :
196 : // If no scaled size is specified, we'll just encode the image at its
197 : // original size (no scaling).
198 0 : if (aScaledWidth == 0 && aScaledHeight == 0) {
199 0 : return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream);
200 : }
201 :
202 : // Retrieve the image's size.
203 0 : int32_t imageWidth = 0;
204 0 : int32_t imageHeight = 0;
205 0 : aContainer->GetWidth(&imageWidth);
206 0 : aContainer->GetHeight(&imageHeight);
207 :
208 : // If the given width or height is zero we'll replace it with the image's
209 : // original dimensions.
210 : IntSize scaledSize(aScaledWidth == 0 ? imageWidth : aScaledWidth,
211 0 : aScaledHeight == 0 ? imageHeight : aScaledHeight);
212 :
213 : // Use frame 0 from the image container.
214 : RefPtr<SourceSurface> frame =
215 0 : aContainer->GetFrameAtSize(scaledSize,
216 : imgIContainer::FRAME_FIRST,
217 : imgIContainer::FLAG_HIGH_QUALITY_SCALING |
218 0 : imgIContainer::FLAG_SYNC_DECODE);
219 0 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
220 :
221 : RefPtr<DataSourceSurface> dataSurface =
222 0 : Factory::CreateDataSourceSurface(scaledSize, SurfaceFormat::B8G8R8A8);
223 0 : if (NS_WARN_IF(!dataSurface)) {
224 0 : return NS_ERROR_FAILURE;
225 : }
226 :
227 : DataSourceSurface::MappedSurface map;
228 0 : if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
229 0 : return NS_ERROR_FAILURE;
230 : }
231 :
232 : RefPtr<DrawTarget> dt =
233 0 : Factory::CreateDrawTargetForData(BackendType::CAIRO,
234 : map.mData,
235 0 : dataSurface->GetSize(),
236 : map.mStride,
237 0 : SurfaceFormat::B8G8R8A8);
238 0 : if (!dt) {
239 0 : gfxWarning() << "imgTools::EncodeImage failed in CreateDrawTargetForData";
240 0 : return NS_ERROR_OUT_OF_MEMORY;
241 : }
242 :
243 0 : IntSize frameSize = frame->GetSize();
244 0 : dt->DrawSurface(frame,
245 0 : Rect(0, 0, scaledSize.width, scaledSize.height),
246 0 : Rect(0, 0, frameSize.width, frameSize.height),
247 0 : DrawSurfaceOptions(),
248 0 : DrawOptions(1.0f, CompositionOp::OP_SOURCE));
249 :
250 0 : dataSurface->Unmap();
251 :
252 0 : return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
253 : }
254 :
255 : NS_IMETHODIMP
256 0 : imgTools::EncodeCroppedImage(imgIContainer* aContainer,
257 : const nsACString& aMimeType,
258 : int32_t aOffsetX,
259 : int32_t aOffsetY,
260 : int32_t aWidth,
261 : int32_t aHeight,
262 : const nsAString& aOutputOptions,
263 : nsIInputStream** aStream)
264 : {
265 0 : NS_ENSURE_ARG(aOffsetX >= 0 && aOffsetY >= 0 && aWidth >= 0 && aHeight >= 0);
266 :
267 : // Offsets must be zero when no width and height are given or else we're out
268 : // of bounds.
269 0 : NS_ENSURE_ARG(aWidth + aHeight > 0 || aOffsetX + aOffsetY == 0);
270 :
271 : // If no size is specified then we'll preserve the image's original dimensions
272 : // and don't need to crop.
273 0 : if (aWidth == 0 && aHeight == 0) {
274 0 : return EncodeImage(aContainer, aMimeType, aOutputOptions, aStream);
275 : }
276 :
277 : // Use frame 0 from the image container.
278 : RefPtr<SourceSurface> frame =
279 0 : aContainer->GetFrame(imgIContainer::FRAME_FIRST,
280 0 : imgIContainer::FLAG_SYNC_DECODE);
281 0 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
282 :
283 0 : int32_t frameWidth = frame->GetSize().width;
284 0 : int32_t frameHeight = frame->GetSize().height;
285 :
286 : // If the given width or height is zero we'll replace it with the image's
287 : // original dimensions.
288 0 : if (aWidth == 0) {
289 0 : aWidth = frameWidth;
290 0 : } else if (aHeight == 0) {
291 0 : aHeight = frameHeight;
292 : }
293 :
294 : // Check that the given crop rectangle is within image bounds.
295 0 : NS_ENSURE_ARG(frameWidth >= aOffsetX + aWidth &&
296 : frameHeight >= aOffsetY + aHeight);
297 :
298 : RefPtr<DataSourceSurface> dataSurface =
299 0 : Factory::CreateDataSourceSurface(IntSize(aWidth, aHeight),
300 : SurfaceFormat::B8G8R8A8,
301 0 : /* aZero = */ true);
302 0 : if (NS_WARN_IF(!dataSurface)) {
303 0 : return NS_ERROR_FAILURE;
304 : }
305 :
306 : DataSourceSurface::MappedSurface map;
307 0 : if (!dataSurface->Map(DataSourceSurface::MapType::WRITE, &map)) {
308 0 : return NS_ERROR_FAILURE;
309 : }
310 :
311 : RefPtr<DrawTarget> dt =
312 0 : Factory::CreateDrawTargetForData(BackendType::CAIRO,
313 : map.mData,
314 0 : dataSurface->GetSize(),
315 : map.mStride,
316 0 : SurfaceFormat::B8G8R8A8);
317 0 : if (!dt) {
318 0 : gfxWarning() <<
319 0 : "imgTools::EncodeCroppedImage failed in CreateDrawTargetForData";
320 0 : return NS_ERROR_OUT_OF_MEMORY;
321 : }
322 0 : dt->CopySurface(frame,
323 0 : IntRect(aOffsetX, aOffsetY, aWidth, aHeight),
324 0 : IntPoint(0, 0));
325 :
326 0 : dataSurface->Unmap();
327 :
328 0 : return EncodeImageData(dataSurface, aMimeType, aOutputOptions, aStream);
329 : }
330 :
331 : NS_IMETHODIMP
332 0 : imgTools::CreateScriptedObserver(imgIScriptedNotificationObserver* aInner,
333 : imgINotificationObserver** aObserver)
334 : {
335 0 : NS_ADDREF(*aObserver = new ScriptedNotificationObserver(aInner));
336 0 : return NS_OK;
337 : }
338 :
339 : NS_IMETHODIMP
340 0 : imgTools::GetImgLoaderForDocument(nsIDOMDocument* aDoc, imgILoader** aLoader)
341 : {
342 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
343 0 : NS_IF_ADDREF(*aLoader = nsContentUtils::GetImgLoaderForDocument(doc));
344 0 : return NS_OK;
345 : }
346 :
347 : NS_IMETHODIMP
348 0 : imgTools::GetImgCacheForDocument(nsIDOMDocument* aDoc, imgICache** aCache)
349 : {
350 0 : nsCOMPtr<imgILoader> loader;
351 0 : nsresult rv = GetImgLoaderForDocument(aDoc, getter_AddRefs(loader));
352 0 : NS_ENSURE_SUCCESS(rv, rv);
353 0 : return CallQueryInterface(loader, aCache);
354 : }
355 :
356 : } // namespace image
357 : } // namespace mozilla
|