imgTools.cpp 11 KB

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