gfxUtils.cpp 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482
  1. /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "gfxUtils.h"
  6. #include "cairo.h"
  7. #include "gfxContext.h"
  8. #include "gfxEnv.h"
  9. #include "gfxImageSurface.h"
  10. #include "gfxPlatform.h"
  11. #include "gfxDrawable.h"
  12. #include "imgIEncoder.h"
  13. #include "libyuv.h"
  14. #include "mozilla/Base64.h"
  15. #include "mozilla/dom/ImageEncoder.h"
  16. #include "mozilla/dom/WorkerPrivate.h"
  17. #include "mozilla/dom/WorkerRunnable.h"
  18. #include "mozilla/gfx/2D.h"
  19. #include "mozilla/gfx/DataSurfaceHelpers.h"
  20. #include "mozilla/gfx/Logging.h"
  21. #include "mozilla/gfx/PathHelpers.h"
  22. #include "mozilla/Maybe.h"
  23. #include "mozilla/RefPtr.h"
  24. #include "mozilla/UniquePtrExtensions.h"
  25. #include "mozilla/Vector.h"
  26. #include "nsComponentManagerUtils.h"
  27. #include "nsIClipboardHelper.h"
  28. #include "nsIFile.h"
  29. #include "nsIGfxInfo.h"
  30. #include "nsIPresShell.h"
  31. #include "nsPresContext.h"
  32. #include "nsRegion.h"
  33. #include "nsServiceManagerUtils.h"
  34. #include "GeckoProfiler.h"
  35. #include "ImageContainer.h"
  36. #include "ImageRegion.h"
  37. #include "gfx2DGlue.h"
  38. #include "gfxPrefs.h"
  39. #ifdef XP_WIN
  40. #include "gfxWindowsPlatform.h"
  41. #endif
  42. using namespace mozilla;
  43. using namespace mozilla::image;
  44. using namespace mozilla::layers;
  45. using namespace mozilla::gfx;
  46. #include "DeprecatedPremultiplyTables.h"
  47. #undef compress
  48. #include "mozilla/Compression.h"
  49. using namespace mozilla::Compression;
  50. extern "C" {
  51. /**
  52. * Dump a raw image to the default log. This function is exported
  53. * from libxul, so it can be called from any library in addition to
  54. * (of course) from a debugger.
  55. *
  56. * Note: this helper currently assumes that all 2-bytepp images are
  57. * r5g6b5, and that all 4-bytepp images are r8g8b8a8.
  58. */
  59. NS_EXPORT
  60. void mozilla_dump_image(void* bytes, int width, int height, int bytepp,
  61. int strideBytes)
  62. {
  63. if (0 == strideBytes) {
  64. strideBytes = width * bytepp;
  65. }
  66. SurfaceFormat format;
  67. // TODO more flexible; parse string?
  68. switch (bytepp) {
  69. case 2:
  70. format = SurfaceFormat::R5G6B5_UINT16;
  71. break;
  72. case 4:
  73. default:
  74. format = SurfaceFormat::R8G8B8A8;
  75. break;
  76. }
  77. RefPtr<DataSourceSurface> surf =
  78. Factory::CreateWrappingDataSourceSurface((uint8_t*)bytes, strideBytes,
  79. IntSize(width, height),
  80. format);
  81. gfxUtils::DumpAsDataURI(surf);
  82. }
  83. }
  84. static uint8_t PremultiplyValue(uint8_t a, uint8_t v) {
  85. return gfxUtils::sPremultiplyTable[a*256+v];
  86. }
  87. static uint8_t UnpremultiplyValue(uint8_t a, uint8_t v) {
  88. return gfxUtils::sUnpremultiplyTable[a*256+v];
  89. }
  90. static void
  91. PremultiplyData(const uint8_t* srcData,
  92. size_t srcStride, // row-to-row stride in bytes
  93. uint8_t* destData,
  94. size_t destStride, // row-to-row stride in bytes
  95. size_t pixelWidth,
  96. size_t rowCount)
  97. {
  98. MOZ_ASSERT(srcData && destData);
  99. for (size_t y = 0; y < rowCount; ++y) {
  100. const uint8_t* src = srcData + y * srcStride;
  101. uint8_t* dest = destData + y * destStride;
  102. for (size_t x = 0; x < pixelWidth; ++x) {
  103. #ifdef IS_LITTLE_ENDIAN
  104. uint8_t b = *src++;
  105. uint8_t g = *src++;
  106. uint8_t r = *src++;
  107. uint8_t a = *src++;
  108. *dest++ = PremultiplyValue(a, b);
  109. *dest++ = PremultiplyValue(a, g);
  110. *dest++ = PremultiplyValue(a, r);
  111. *dest++ = a;
  112. #else
  113. uint8_t a = *src++;
  114. uint8_t r = *src++;
  115. uint8_t g = *src++;
  116. uint8_t b = *src++;
  117. *dest++ = a;
  118. *dest++ = PremultiplyValue(a, r);
  119. *dest++ = PremultiplyValue(a, g);
  120. *dest++ = PremultiplyValue(a, b);
  121. #endif
  122. }
  123. }
  124. }
  125. static void
  126. UnpremultiplyData(const uint8_t* srcData,
  127. size_t srcStride, // row-to-row stride in bytes
  128. uint8_t* destData,
  129. size_t destStride, // row-to-row stride in bytes
  130. size_t pixelWidth,
  131. size_t rowCount)
  132. {
  133. MOZ_ASSERT(srcData && destData);
  134. for (size_t y = 0; y < rowCount; ++y) {
  135. const uint8_t* src = srcData + y * srcStride;
  136. uint8_t* dest = destData + y * destStride;
  137. for (size_t x = 0; x < pixelWidth; ++x) {
  138. #ifdef IS_LITTLE_ENDIAN
  139. uint8_t b = *src++;
  140. uint8_t g = *src++;
  141. uint8_t r = *src++;
  142. uint8_t a = *src++;
  143. *dest++ = UnpremultiplyValue(a, b);
  144. *dest++ = UnpremultiplyValue(a, g);
  145. *dest++ = UnpremultiplyValue(a, r);
  146. *dest++ = a;
  147. #else
  148. uint8_t a = *src++;
  149. uint8_t r = *src++;
  150. uint8_t g = *src++;
  151. uint8_t b = *src++;
  152. *dest++ = a;
  153. *dest++ = UnpremultiplyValue(a, r);
  154. *dest++ = UnpremultiplyValue(a, g);
  155. *dest++ = UnpremultiplyValue(a, b);
  156. #endif
  157. }
  158. }
  159. }
  160. static bool
  161. MapSrcDest(DataSourceSurface* srcSurf,
  162. DataSourceSurface* destSurf,
  163. DataSourceSurface::MappedSurface* out_srcMap,
  164. DataSourceSurface::MappedSurface* out_destMap)
  165. {
  166. MOZ_ASSERT(srcSurf && destSurf);
  167. MOZ_ASSERT(out_srcMap && out_destMap);
  168. if (srcSurf->GetFormat() != SurfaceFormat::B8G8R8A8 ||
  169. destSurf->GetFormat() != SurfaceFormat::B8G8R8A8)
  170. {
  171. MOZ_ASSERT(false, "Only operate on BGRA8 surfs.");
  172. return false;
  173. }
  174. if (srcSurf->GetSize().width != destSurf->GetSize().width ||
  175. srcSurf->GetSize().height != destSurf->GetSize().height)
  176. {
  177. MOZ_ASSERT(false, "Width and height must match.");
  178. return false;
  179. }
  180. if (srcSurf == destSurf) {
  181. DataSourceSurface::MappedSurface map;
  182. if (!srcSurf->Map(DataSourceSurface::MapType::READ_WRITE, &map)) {
  183. NS_WARNING("Couldn't Map srcSurf/destSurf.");
  184. return false;
  185. }
  186. *out_srcMap = map;
  187. *out_destMap = map;
  188. return true;
  189. }
  190. // Map src for reading.
  191. DataSourceSurface::MappedSurface srcMap;
  192. if (!srcSurf->Map(DataSourceSurface::MapType::READ, &srcMap)) {
  193. NS_WARNING("Couldn't Map srcSurf.");
  194. return false;
  195. }
  196. // Map dest for writing.
  197. DataSourceSurface::MappedSurface destMap;
  198. if (!destSurf->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
  199. NS_WARNING("Couldn't Map aDest.");
  200. srcSurf->Unmap();
  201. return false;
  202. }
  203. *out_srcMap = srcMap;
  204. *out_destMap = destMap;
  205. return true;
  206. }
  207. static void
  208. UnmapSrcDest(DataSourceSurface* srcSurf,
  209. DataSourceSurface* destSurf)
  210. {
  211. if (srcSurf == destSurf) {
  212. srcSurf->Unmap();
  213. } else {
  214. srcSurf->Unmap();
  215. destSurf->Unmap();
  216. }
  217. }
  218. bool
  219. gfxUtils::PremultiplyDataSurface(DataSourceSurface* srcSurf,
  220. DataSourceSurface* destSurf)
  221. {
  222. MOZ_ASSERT(srcSurf && destSurf);
  223. DataSourceSurface::MappedSurface srcMap;
  224. DataSourceSurface::MappedSurface destMap;
  225. if (!MapSrcDest(srcSurf, destSurf, &srcMap, &destMap))
  226. return false;
  227. PremultiplyData(srcMap.mData, srcMap.mStride,
  228. destMap.mData, destMap.mStride,
  229. srcSurf->GetSize().width,
  230. srcSurf->GetSize().height);
  231. UnmapSrcDest(srcSurf, destSurf);
  232. return true;
  233. }
  234. bool
  235. gfxUtils::UnpremultiplyDataSurface(DataSourceSurface* srcSurf,
  236. DataSourceSurface* destSurf)
  237. {
  238. MOZ_ASSERT(srcSurf && destSurf);
  239. DataSourceSurface::MappedSurface srcMap;
  240. DataSourceSurface::MappedSurface destMap;
  241. if (!MapSrcDest(srcSurf, destSurf, &srcMap, &destMap))
  242. return false;
  243. UnpremultiplyData(srcMap.mData, srcMap.mStride,
  244. destMap.mData, destMap.mStride,
  245. srcSurf->GetSize().width,
  246. srcSurf->GetSize().height);
  247. UnmapSrcDest(srcSurf, destSurf);
  248. return true;
  249. }
  250. static bool
  251. MapSrcAndCreateMappedDest(DataSourceSurface* srcSurf,
  252. RefPtr<DataSourceSurface>* out_destSurf,
  253. DataSourceSurface::MappedSurface* out_srcMap,
  254. DataSourceSurface::MappedSurface* out_destMap)
  255. {
  256. MOZ_ASSERT(srcSurf);
  257. MOZ_ASSERT(out_destSurf && out_srcMap && out_destMap);
  258. if (srcSurf->GetFormat() != SurfaceFormat::B8G8R8A8) {
  259. MOZ_ASSERT(false, "Only operate on BGRA8.");
  260. return false;
  261. }
  262. // Ok, map source for reading.
  263. DataSourceSurface::MappedSurface srcMap;
  264. if (!srcSurf->Map(DataSourceSurface::MapType::READ, &srcMap)) {
  265. MOZ_ASSERT(false, "Couldn't Map srcSurf.");
  266. return false;
  267. }
  268. // Make our dest surface based on the src.
  269. RefPtr<DataSourceSurface> destSurf =
  270. Factory::CreateDataSourceSurfaceWithStride(srcSurf->GetSize(),
  271. srcSurf->GetFormat(),
  272. srcMap.mStride);
  273. if (NS_WARN_IF(!destSurf)) {
  274. return false;
  275. }
  276. DataSourceSurface::MappedSurface destMap;
  277. if (!destSurf->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
  278. MOZ_ASSERT(false, "Couldn't Map destSurf.");
  279. srcSurf->Unmap();
  280. return false;
  281. }
  282. *out_destSurf = destSurf;
  283. *out_srcMap = srcMap;
  284. *out_destMap = destMap;
  285. return true;
  286. }
  287. already_AddRefed<DataSourceSurface>
  288. gfxUtils::CreatePremultipliedDataSurface(DataSourceSurface* srcSurf)
  289. {
  290. RefPtr<DataSourceSurface> destSurf;
  291. DataSourceSurface::MappedSurface srcMap;
  292. DataSourceSurface::MappedSurface destMap;
  293. if (!MapSrcAndCreateMappedDest(srcSurf, &destSurf, &srcMap, &destMap)) {
  294. MOZ_ASSERT(false, "MapSrcAndCreateMappedDest failed.");
  295. RefPtr<DataSourceSurface> surface(srcSurf);
  296. return surface.forget();
  297. }
  298. PremultiplyData(srcMap.mData, srcMap.mStride,
  299. destMap.mData, destMap.mStride,
  300. srcSurf->GetSize().width,
  301. srcSurf->GetSize().height);
  302. UnmapSrcDest(srcSurf, destSurf);
  303. return destSurf.forget();
  304. }
  305. already_AddRefed<DataSourceSurface>
  306. gfxUtils::CreateUnpremultipliedDataSurface(DataSourceSurface* srcSurf)
  307. {
  308. RefPtr<DataSourceSurface> destSurf;
  309. DataSourceSurface::MappedSurface srcMap;
  310. DataSourceSurface::MappedSurface destMap;
  311. if (!MapSrcAndCreateMappedDest(srcSurf, &destSurf, &srcMap, &destMap)) {
  312. MOZ_ASSERT(false, "MapSrcAndCreateMappedDest failed.");
  313. RefPtr<DataSourceSurface> surface(srcSurf);
  314. return surface.forget();
  315. }
  316. UnpremultiplyData(srcMap.mData, srcMap.mStride,
  317. destMap.mData, destMap.mStride,
  318. srcSurf->GetSize().width,
  319. srcSurf->GetSize().height);
  320. UnmapSrcDest(srcSurf, destSurf);
  321. return destSurf.forget();
  322. }
  323. void
  324. gfxUtils::ConvertBGRAtoRGBA(uint8_t* aData, uint32_t aLength)
  325. {
  326. MOZ_ASSERT((aLength % 4) == 0, "Loop below will pass srcEnd!");
  327. libyuv::ABGRToARGB(aData, aLength, aData, aLength, aLength / 4, 1);
  328. }
  329. /**
  330. * This returns the fastest operator to use for solid surfaces which have no
  331. * alpha channel or their alpha channel is uniformly opaque.
  332. * This differs per render mode.
  333. */
  334. static CompositionOp
  335. OptimalFillOp()
  336. {
  337. #ifdef XP_WIN
  338. if (gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend()) {
  339. // D2D -really- hates operator source.
  340. return CompositionOp::OP_OVER;
  341. }
  342. #endif
  343. return CompositionOp::OP_SOURCE;
  344. }
  345. // EXTEND_PAD won't help us here; we have to create a temporary surface to hold
  346. // the subimage of pixels we're allowed to sample.
  347. static already_AddRefed<gfxDrawable>
  348. CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable,
  349. gfxContext* aContext,
  350. const ImageRegion& aRegion,
  351. const SurfaceFormat aFormat)
  352. {
  353. PROFILER_LABEL("gfxUtils", "CreateSamplingRestricedDrawable",
  354. js::ProfileEntry::Category::GRAPHICS);
  355. DrawTarget* destDrawTarget = aContext->GetDrawTarget();
  356. if (destDrawTarget->GetBackendType() == BackendType::DIRECT2D1_1) {
  357. return nullptr;
  358. }
  359. gfxRect clipExtents = aContext->GetClipExtents();
  360. // Inflate by one pixel because bilinear filtering will sample at most
  361. // one pixel beyond the computed image pixel coordinate.
  362. clipExtents.Inflate(1.0);
  363. gfxRect needed = aRegion.IntersectAndRestrict(clipExtents);
  364. needed.RoundOut();
  365. // if 'needed' is empty, nothing will be drawn since aFill
  366. // must be entirely outside the clip region, so it doesn't
  367. // matter what we do here, but we should avoid trying to
  368. // create a zero-size surface.
  369. if (needed.IsEmpty())
  370. return nullptr;
  371. IntSize size(int32_t(needed.Width()), int32_t(needed.Height()));
  372. RefPtr<DrawTarget> target =
  373. gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(size, aFormat);
  374. if (!target || !target->IsValid()) {
  375. return nullptr;
  376. }
  377. RefPtr<gfxContext> tmpCtx = gfxContext::CreateOrNull(target);
  378. MOZ_ASSERT(tmpCtx); // already checked the target above
  379. tmpCtx->SetOp(OptimalFillOp());
  380. aDrawable->Draw(tmpCtx, needed - needed.TopLeft(), ExtendMode::REPEAT,
  381. SamplingFilter::LINEAR,
  382. 1.0, gfxMatrix::Translation(needed.TopLeft()));
  383. RefPtr<SourceSurface> surface = target->Snapshot();
  384. RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(surface, size, gfxMatrix::Translation(-needed.TopLeft()));
  385. return drawable.forget();
  386. }
  387. static SamplingFilter ReduceResamplingFilter(SamplingFilter aSamplingFilter,
  388. int aImgWidth, int aImgHeight,
  389. int aSourceWidth, int aSourceHeight)
  390. {
  391. // Just pass the filter through unchanged
  392. return aSamplingFilter;
  393. }
  394. #ifdef MOZ_WIDGET_COCOA
  395. // Only prescale a temporary surface if we're going to repeat it often.
  396. // Scaling is expensive on OS X and without prescaling, we'd scale
  397. // every tile of the repeated rect. However, using a temp surface also potentially uses
  398. // more memory if the scaled image is large. So only prescale on a temp
  399. // surface if we know we're going to repeat the image in either the X or Y axis
  400. // multiple times.
  401. static bool
  402. ShouldUseTempSurface(Rect aImageRect, Rect aNeededRect)
  403. {
  404. int repeatX = aNeededRect.width / aImageRect.width;
  405. int repeatY = aNeededRect.height / aImageRect.height;
  406. return (repeatX >= 5) || (repeatY >= 5);
  407. }
  408. static bool
  409. PrescaleAndTileDrawable(gfxDrawable* aDrawable,
  410. gfxContext* aContext,
  411. const ImageRegion& aRegion,
  412. Rect aImageRect,
  413. const SamplingFilter aSamplingFilter,
  414. const SurfaceFormat aFormat,
  415. gfxFloat aOpacity,
  416. ExtendMode aExtendMode)
  417. {
  418. gfxSize scaleFactor = aContext->CurrentMatrix().ScaleFactors(true);
  419. gfxMatrix scaleMatrix = gfxMatrix::Scaling(scaleFactor.width, scaleFactor.height);
  420. const float fuzzFactor = 0.01;
  421. // If we aren't scaling or translating, don't go down this path
  422. if ((FuzzyEqual(scaleFactor.width, 1.0, fuzzFactor) &&
  423. FuzzyEqual(scaleFactor.width, 1.0, fuzzFactor)) ||
  424. aContext->CurrentMatrix().HasNonAxisAlignedTransform()) {
  425. return false;
  426. }
  427. gfxRect clipExtents = aContext->GetClipExtents();
  428. // Inflate by one pixel because bilinear filtering will sample at most
  429. // one pixel beyond the computed image pixel coordinate.
  430. clipExtents.Inflate(1.0);
  431. gfxRect needed = aRegion.IntersectAndRestrict(clipExtents);
  432. Rect scaledNeededRect = ToMatrix(scaleMatrix).TransformBounds(ToRect(needed));
  433. scaledNeededRect.RoundOut();
  434. if (scaledNeededRect.IsEmpty()) {
  435. return false;
  436. }
  437. Rect scaledImageRect = ToMatrix(scaleMatrix).TransformBounds(aImageRect);
  438. if (!ShouldUseTempSurface(scaledImageRect, scaledNeededRect)) {
  439. return false;
  440. }
  441. IntSize scaledImageSize((int32_t)scaledImageRect.width,
  442. (int32_t)scaledImageRect.height);
  443. if (scaledImageSize.width != scaledImageRect.width ||
  444. scaledImageSize.height != scaledImageRect.height) {
  445. // If the scaled image isn't pixel aligned, we'll get artifacts
  446. // so we have to take the slow path.
  447. return false;
  448. }
  449. RefPtr<DrawTarget> scaledDT =
  450. gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(scaledImageSize, aFormat);
  451. if (!scaledDT || !scaledDT->IsValid()) {
  452. return false;
  453. }
  454. RefPtr<gfxContext> tmpCtx = gfxContext::CreateOrNull(scaledDT);
  455. MOZ_ASSERT(tmpCtx); // already checked the target above
  456. scaledDT->SetTransform(ToMatrix(scaleMatrix));
  457. gfxRect gfxImageRect(aImageRect.x, aImageRect.y, aImageRect.width, aImageRect.height);
  458. // Since this is just the scaled image, we don't want to repeat anything yet.
  459. aDrawable->Draw(tmpCtx, gfxImageRect, ExtendMode::CLAMP, aSamplingFilter, 1.0, gfxMatrix());
  460. RefPtr<SourceSurface> scaledImage = scaledDT->Snapshot();
  461. {
  462. gfxContextMatrixAutoSaveRestore autoSR(aContext);
  463. Matrix withoutScale = ToMatrix(aContext->CurrentMatrix());
  464. DrawTarget* destDrawTarget = aContext->GetDrawTarget();
  465. // The translation still is in scaled units
  466. withoutScale.PreScale(1.0 / scaleFactor.width, 1.0 / scaleFactor.height);
  467. aContext->SetMatrix(ThebesMatrix(withoutScale));
  468. DrawOptions drawOptions(aOpacity, aContext->CurrentOp(),
  469. aContext->CurrentAntialiasMode());
  470. SurfacePattern scaledImagePattern(scaledImage, aExtendMode,
  471. Matrix(), aSamplingFilter);
  472. destDrawTarget->FillRect(scaledNeededRect, scaledImagePattern, drawOptions);
  473. }
  474. return true;
  475. }
  476. #endif // MOZ_WIDGET_COCOA
  477. /* static */ void
  478. gfxUtils::DrawPixelSnapped(gfxContext* aContext,
  479. gfxDrawable* aDrawable,
  480. const gfxSize& aImageSize,
  481. const ImageRegion& aRegion,
  482. const SurfaceFormat aFormat,
  483. SamplingFilter aSamplingFilter,
  484. uint32_t aImageFlags,
  485. gfxFloat aOpacity)
  486. {
  487. PROFILER_LABEL("gfxUtils", "DrawPixelSnapped",
  488. js::ProfileEntry::Category::GRAPHICS);
  489. gfxRect imageRect(gfxPoint(0, 0), aImageSize);
  490. gfxRect region(aRegion.Rect());
  491. ExtendMode extendMode = aRegion.GetExtendMode();
  492. RefPtr<gfxDrawable> drawable = aDrawable;
  493. aSamplingFilter =
  494. ReduceResamplingFilter(aSamplingFilter,
  495. imageRect.Width(), imageRect.Height(),
  496. region.Width(), region.Height());
  497. // OK now, the hard part left is to account for the subimage sampling
  498. // restriction. If all the transforms involved are just integer
  499. // translations, then we assume no resampling will occur so there's
  500. // nothing to do.
  501. // XXX if only we had source-clipping in cairo!
  502. if (aContext->CurrentMatrix().HasNonIntegerTranslation()) {
  503. if ((extendMode != ExtendMode::CLAMP) || !aRegion.RestrictionContains(imageRect)) {
  504. if (drawable->DrawWithSamplingRect(aContext->GetDrawTarget(),
  505. aContext->CurrentOp(),
  506. aContext->CurrentAntialiasMode(),
  507. aRegion.Rect(),
  508. aRegion.Restriction(),
  509. extendMode, aSamplingFilter,
  510. aOpacity)) {
  511. return;
  512. }
  513. #ifdef MOZ_WIDGET_COCOA
  514. if (PrescaleAndTileDrawable(aDrawable, aContext, aRegion,
  515. ToRect(imageRect), aSamplingFilter,
  516. aFormat, aOpacity, extendMode)) {
  517. return;
  518. }
  519. #endif
  520. RefPtr<gfxDrawable> restrictedDrawable =
  521. CreateSamplingRestrictedDrawable(aDrawable, aContext,
  522. aRegion, aFormat);
  523. if (restrictedDrawable) {
  524. drawable.swap(restrictedDrawable);
  525. // We no longer need to tile: Either we never needed to, or we already
  526. // filled a surface with the tiled pattern; this surface can now be
  527. // drawn without tiling.
  528. extendMode = ExtendMode::CLAMP;
  529. }
  530. }
  531. }
  532. drawable->Draw(aContext, aRegion.Rect(), extendMode, aSamplingFilter,
  533. aOpacity, gfxMatrix());
  534. }
  535. /* static */ int
  536. gfxUtils::ImageFormatToDepth(gfxImageFormat aFormat)
  537. {
  538. switch (aFormat) {
  539. case SurfaceFormat::A8R8G8B8_UINT32:
  540. return 32;
  541. case SurfaceFormat::X8R8G8B8_UINT32:
  542. return 24;
  543. case SurfaceFormat::R5G6B5_UINT16:
  544. return 16;
  545. default:
  546. break;
  547. }
  548. return 0;
  549. }
  550. /*static*/ void
  551. gfxUtils::ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion)
  552. {
  553. aContext->NewPath();
  554. for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
  555. const IntRect& r = iter.Get();
  556. aContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height));
  557. }
  558. aContext->Clip();
  559. }
  560. /*static*/ void
  561. gfxUtils::ClipToRegion(DrawTarget* aTarget, const nsIntRegion& aRegion)
  562. {
  563. uint32_t numRects = aRegion.GetNumRects();
  564. // If there is only one rect, then the region bounds are equivalent to the
  565. // contents. So just use push a single clip rect with the bounds.
  566. if (numRects == 1) {
  567. aTarget->PushClipRect(Rect(aRegion.GetBounds()));
  568. return;
  569. }
  570. // Check if the target's transform will preserve axis-alignment and
  571. // pixel-alignment for each rect. For now, just handle the common case
  572. // of integer translations.
  573. Matrix transform = aTarget->GetTransform();
  574. if (transform.IsIntegerTranslation()) {
  575. IntPoint translation = RoundedToInt(transform.GetTranslation());
  576. AutoTArray<IntRect, 16> rects;
  577. rects.SetLength(numRects);
  578. uint32_t i = 0;
  579. // Build the list of transformed rects by adding in the translation.
  580. for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
  581. IntRect rect = iter.Get();
  582. rect.MoveBy(translation);
  583. rects[i++] = rect;
  584. }
  585. aTarget->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
  586. } else {
  587. // The transform does not produce axis-aligned rects or a rect was not
  588. // pixel-aligned. So just build a path with all the rects and clip to it
  589. // instead.
  590. RefPtr<PathBuilder> pathBuilder = aTarget->CreatePathBuilder();
  591. for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
  592. AppendRectToPath(pathBuilder, Rect(iter.Get()));
  593. }
  594. RefPtr<Path> path = pathBuilder->Finish();
  595. aTarget->PushClip(path);
  596. }
  597. }
  598. /*static*/ gfxFloat
  599. gfxUtils::ClampToScaleFactor(gfxFloat aVal)
  600. {
  601. // Arbitary scale factor limitation. We can increase this
  602. // for better scaling performance at the cost of worse
  603. // quality.
  604. static const gfxFloat kScaleResolution = 2;
  605. // Negative scaling is just a flip and irrelevant to
  606. // our resolution calculation.
  607. if (aVal < 0.0) {
  608. aVal = -aVal;
  609. }
  610. bool inverse = false;
  611. if (aVal < 1.0) {
  612. inverse = true;
  613. aVal = 1 / aVal;
  614. }
  615. gfxFloat power = log(aVal)/log(kScaleResolution);
  616. // If power is within 1e-5 of an integer, round to nearest to
  617. // prevent floating point errors, otherwise round up to the
  618. // next integer value.
  619. if (fabs(power - NS_round(power)) < 1e-5) {
  620. power = NS_round(power);
  621. } else if (inverse) {
  622. power = floor(power);
  623. } else {
  624. power = ceil(power);
  625. }
  626. gfxFloat scale = pow(kScaleResolution, power);
  627. if (inverse) {
  628. scale = 1 / scale;
  629. }
  630. return scale;
  631. }
  632. gfxMatrix
  633. gfxUtils::TransformRectToRect(const gfxRect& aFrom, const gfxPoint& aToTopLeft,
  634. const gfxPoint& aToTopRight, const gfxPoint& aToBottomRight)
  635. {
  636. gfxMatrix m;
  637. if (aToTopRight.y == aToTopLeft.y && aToTopRight.x == aToBottomRight.x) {
  638. // Not a rotation, so xy and yx are zero
  639. m._21 = m._12 = 0.0;
  640. m._11 = (aToBottomRight.x - aToTopLeft.x)/aFrom.width;
  641. m._22 = (aToBottomRight.y - aToTopLeft.y)/aFrom.height;
  642. m._31 = aToTopLeft.x - m._11*aFrom.x;
  643. m._32 = aToTopLeft.y - m._22*aFrom.y;
  644. } else {
  645. NS_ASSERTION(aToTopRight.y == aToBottomRight.y && aToTopRight.x == aToTopLeft.x,
  646. "Destination rectangle not axis-aligned");
  647. m._11 = m._22 = 0.0;
  648. m._21 = (aToBottomRight.x - aToTopLeft.x)/aFrom.height;
  649. m._12 = (aToBottomRight.y - aToTopLeft.y)/aFrom.width;
  650. m._31 = aToTopLeft.x - m._21*aFrom.y;
  651. m._32 = aToTopLeft.y - m._12*aFrom.x;
  652. }
  653. return m;
  654. }
  655. Matrix
  656. gfxUtils::TransformRectToRect(const gfxRect& aFrom, const IntPoint& aToTopLeft,
  657. const IntPoint& aToTopRight, const IntPoint& aToBottomRight)
  658. {
  659. Matrix m;
  660. if (aToTopRight.y == aToTopLeft.y && aToTopRight.x == aToBottomRight.x) {
  661. // Not a rotation, so xy and yx are zero
  662. m._12 = m._21 = 0.0;
  663. m._11 = (aToBottomRight.x - aToTopLeft.x)/aFrom.width;
  664. m._22 = (aToBottomRight.y - aToTopLeft.y)/aFrom.height;
  665. m._31 = aToTopLeft.x - m._11*aFrom.x;
  666. m._32 = aToTopLeft.y - m._22*aFrom.y;
  667. } else {
  668. NS_ASSERTION(aToTopRight.y == aToBottomRight.y && aToTopRight.x == aToTopLeft.x,
  669. "Destination rectangle not axis-aligned");
  670. m._11 = m._22 = 0.0;
  671. m._21 = (aToBottomRight.x - aToTopLeft.x)/aFrom.height;
  672. m._12 = (aToBottomRight.y - aToTopLeft.y)/aFrom.width;
  673. m._31 = aToTopLeft.x - m._21*aFrom.y;
  674. m._32 = aToTopLeft.y - m._12*aFrom.x;
  675. }
  676. return m;
  677. }
  678. /* This function is sort of shitty. We truncate doubles
  679. * to ints then convert those ints back to doubles to make sure that
  680. * they equal the doubles that we got in. */
  681. bool
  682. gfxUtils::GfxRectToIntRect(const gfxRect& aIn, IntRect* aOut)
  683. {
  684. *aOut = IntRect(int32_t(aIn.X()), int32_t(aIn.Y()),
  685. int32_t(aIn.Width()), int32_t(aIn.Height()));
  686. return gfxRect(aOut->x, aOut->y, aOut->width, aOut->height).IsEqualEdges(aIn);
  687. }
  688. /* static */ void gfxUtils::ClearThebesSurface(gfxASurface* aSurface)
  689. {
  690. if (aSurface->CairoStatus()) {
  691. return;
  692. }
  693. cairo_surface_t* surf = aSurface->CairoSurface();
  694. if (cairo_surface_status(surf)) {
  695. return;
  696. }
  697. cairo_t* ctx = cairo_create(surf);
  698. cairo_set_source_rgba(ctx, 0.0, 0.0, 0.0, 0.0);
  699. cairo_set_operator(ctx, CAIRO_OPERATOR_SOURCE);
  700. IntRect bounds(nsIntPoint(0, 0), aSurface->GetSize());
  701. cairo_rectangle(ctx, bounds.x, bounds.y, bounds.width, bounds.height);
  702. cairo_fill(ctx);
  703. cairo_destroy(ctx);
  704. }
  705. /* static */ already_AddRefed<DataSourceSurface>
  706. gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface* aSurface,
  707. SurfaceFormat aFormat)
  708. {
  709. MOZ_ASSERT(aFormat != aSurface->GetFormat(),
  710. "Unnecessary - and very expersive - surface format conversion");
  711. Rect bounds(0, 0, aSurface->GetSize().width, aSurface->GetSize().height);
  712. if (aSurface->GetType() != SurfaceType::DATA) {
  713. // If the surface is NOT of type DATA then its data is not mapped into main
  714. // memory. Format conversion is probably faster on the GPU, and by doing it
  715. // there we can avoid any expensive uploads/readbacks except for (possibly)
  716. // a single readback due to the unavoidable GetDataSurface() call. Using
  717. // CreateOffscreenContentDrawTarget ensures the conversion happens on the
  718. // GPU.
  719. RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->
  720. CreateOffscreenContentDrawTarget(aSurface->GetSize(), aFormat);
  721. if (!dt) {
  722. gfxWarning() << "gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat failed in CreateOffscreenContentDrawTarget";
  723. return nullptr;
  724. }
  725. // Using DrawSurface() here rather than CopySurface() because CopySurface
  726. // is optimized for memcpy and therefore isn't good for format conversion.
  727. // Using OP_OVER since in our case it's equivalent to OP_SOURCE and
  728. // generally more optimized.
  729. dt->DrawSurface(aSurface, bounds, bounds, DrawSurfaceOptions(),
  730. DrawOptions(1.0f, CompositionOp::OP_OVER));
  731. RefPtr<SourceSurface> surface = dt->Snapshot();
  732. return surface->GetDataSurface();
  733. }
  734. // If the surface IS of type DATA then it may or may not be in main memory
  735. // depending on whether or not it has been mapped yet. We have no way of
  736. // knowing, so we can't be sure if it's best to create a data wrapping
  737. // DrawTarget for the conversion or an offscreen content DrawTarget. We could
  738. // guess it's not mapped and create an offscreen content DrawTarget, but if
  739. // it is then we'll end up uploading the surface data, and most likely the
  740. // caller is going to be accessing the resulting surface data, resulting in a
  741. // readback (both very expensive operations). Alternatively we could guess
  742. // the data is mapped and create a data wrapping DrawTarget and, if the
  743. // surface is not in main memory, then we will incure a readback. The latter
  744. // of these two "wrong choices" is the least costly (a readback, vs an
  745. // upload and a readback), and more than likely the DATA surface that we've
  746. // been passed actually IS in main memory anyway. For these reasons it's most
  747. // likely best to create a data wrapping DrawTarget here to do the format
  748. // conversion.
  749. RefPtr<DataSourceSurface> dataSurface =
  750. Factory::CreateDataSourceSurface(aSurface->GetSize(), aFormat);
  751. DataSourceSurface::MappedSurface map;
  752. if (!dataSurface ||
  753. !dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map)) {
  754. return nullptr;
  755. }
  756. RefPtr<DrawTarget> dt =
  757. Factory::CreateDrawTargetForData(BackendType::CAIRO,
  758. map.mData,
  759. dataSurface->GetSize(),
  760. map.mStride,
  761. aFormat);
  762. if (!dt) {
  763. dataSurface->Unmap();
  764. return nullptr;
  765. }
  766. // Using DrawSurface() here rather than CopySurface() because CopySurface
  767. // is optimized for memcpy and therefore isn't good for format conversion.
  768. // Using OP_OVER since in our case it's equivalent to OP_SOURCE and
  769. // generally more optimized.
  770. dt->DrawSurface(aSurface, bounds, bounds, DrawSurfaceOptions(),
  771. DrawOptions(1.0f, CompositionOp::OP_OVER));
  772. dataSurface->Unmap();
  773. return dataSurface.forget();
  774. }
  775. const uint32_t gfxUtils::sNumFrameColors = 8;
  776. /* static */ const gfx::Color&
  777. gfxUtils::GetColorForFrameNumber(uint64_t aFrameNumber)
  778. {
  779. static bool initialized = false;
  780. static gfx::Color colors[sNumFrameColors];
  781. if (!initialized) {
  782. uint32_t i = 0;
  783. colors[i++] = gfx::Color::FromABGR(0xffff0000);
  784. colors[i++] = gfx::Color::FromABGR(0xffcc00ff);
  785. colors[i++] = gfx::Color::FromABGR(0xff0066cc);
  786. colors[i++] = gfx::Color::FromABGR(0xff00ff00);
  787. colors[i++] = gfx::Color::FromABGR(0xff33ffff);
  788. colors[i++] = gfx::Color::FromABGR(0xffff0099);
  789. colors[i++] = gfx::Color::FromABGR(0xff0000ff);
  790. colors[i++] = gfx::Color::FromABGR(0xff999999);
  791. MOZ_ASSERT(i == sNumFrameColors);
  792. initialized = true;
  793. }
  794. return colors[aFrameNumber % sNumFrameColors];
  795. }
  796. static nsresult
  797. EncodeSourceSurfaceInternal(SourceSurface* aSurface,
  798. const nsACString& aMimeType,
  799. const nsAString& aOutputOptions,
  800. gfxUtils::BinaryOrData aBinaryOrData,
  801. FILE* aFile,
  802. nsCString* aStrOut)
  803. {
  804. MOZ_ASSERT(aBinaryOrData == gfxUtils::eDataURIEncode || aFile || aStrOut,
  805. "Copying binary encoding to clipboard not currently supported");
  806. const IntSize size = aSurface->GetSize();
  807. if (size.IsEmpty()) {
  808. return NS_ERROR_INVALID_ARG;
  809. }
  810. const Size floatSize(size.width, size.height);
  811. RefPtr<DataSourceSurface> dataSurface;
  812. if (aSurface->GetFormat() != SurfaceFormat::B8G8R8A8) {
  813. // FIXME bug 995807 (B8G8R8X8), bug 831898 (R5G6B5)
  814. dataSurface =
  815. gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(aSurface,
  816. SurfaceFormat::B8G8R8A8);
  817. } else {
  818. dataSurface = aSurface->GetDataSurface();
  819. }
  820. if (!dataSurface) {
  821. return NS_ERROR_FAILURE;
  822. }
  823. DataSourceSurface::MappedSurface map;
  824. if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
  825. return NS_ERROR_FAILURE;
  826. }
  827. nsAutoCString encoderCID(
  828. NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType);
  829. nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
  830. if (!encoder) {
  831. #ifdef DEBUG
  832. int32_t w = std::min(size.width, 8);
  833. int32_t h = std::min(size.height, 8);
  834. printf("Could not create encoder. Top-left %dx%d pixels contain:\n", w, h);
  835. for (int32_t y = 0; y < h; ++y) {
  836. for (int32_t x = 0; x < w; ++x) {
  837. printf("%x ", reinterpret_cast<uint32_t*>(map.mData)[y*map.mStride+x]);
  838. }
  839. }
  840. #endif
  841. dataSurface->Unmap();
  842. return NS_ERROR_FAILURE;
  843. }
  844. nsresult rv = encoder->InitFromData(map.mData,
  845. BufferSizeFromStrideAndHeight(map.mStride, size.height),
  846. size.width,
  847. size.height,
  848. map.mStride,
  849. imgIEncoder::INPUT_FORMAT_HOSTARGB,
  850. aOutputOptions);
  851. dataSurface->Unmap();
  852. NS_ENSURE_SUCCESS(rv, rv);
  853. nsCOMPtr<nsIInputStream> imgStream;
  854. CallQueryInterface(encoder.get(), getter_AddRefs(imgStream));
  855. if (!imgStream) {
  856. return NS_ERROR_FAILURE;
  857. }
  858. uint64_t bufSize64;
  859. rv = imgStream->Available(&bufSize64);
  860. NS_ENSURE_SUCCESS(rv, rv);
  861. NS_ENSURE_TRUE(bufSize64 < UINT32_MAX - 16, NS_ERROR_FAILURE);
  862. uint32_t bufSize = (uint32_t)bufSize64;
  863. // ...leave a little extra room so we can call read again and make sure we
  864. // got everything. 16 bytes for better padding (maybe)
  865. bufSize += 16;
  866. uint32_t imgSize = 0;
  867. Vector<char> imgData;
  868. if (!imgData.initCapacity(bufSize)) {
  869. return NS_ERROR_OUT_OF_MEMORY;
  870. }
  871. uint32_t numReadThisTime = 0;
  872. while ((rv = imgStream->Read(imgData.begin() + imgSize,
  873. bufSize - imgSize,
  874. &numReadThisTime)) == NS_OK && numReadThisTime > 0)
  875. {
  876. // Update the length of the vector without overwriting the new data.
  877. if (!imgData.growByUninitialized(numReadThisTime)) {
  878. return NS_ERROR_OUT_OF_MEMORY;
  879. }
  880. imgSize += numReadThisTime;
  881. if (imgSize == bufSize) {
  882. // need a bigger buffer, just double
  883. bufSize *= 2;
  884. if (!imgData.resizeUninitialized(bufSize)) {
  885. return NS_ERROR_OUT_OF_MEMORY;
  886. }
  887. }
  888. }
  889. NS_ENSURE_SUCCESS(rv, rv);
  890. NS_ENSURE_TRUE(!imgData.empty(), NS_ERROR_FAILURE);
  891. if (aBinaryOrData == gfxUtils::eBinaryEncode) {
  892. if (aFile) {
  893. fwrite(imgData.begin(), 1, imgSize, aFile);
  894. }
  895. return NS_OK;
  896. }
  897. // base 64, result will be null-terminated
  898. nsCString encodedImg;
  899. rv = Base64Encode(Substring(imgData.begin(), imgSize), encodedImg);
  900. NS_ENSURE_SUCCESS(rv, rv);
  901. nsCString string("data:");
  902. string.Append(aMimeType);
  903. string.Append(";base64,");
  904. string.Append(encodedImg);
  905. if (aFile) {
  906. #ifdef ANDROID
  907. if (aFile == stdout || aFile == stderr) {
  908. // ADB logcat cuts off long strings so we will break it down
  909. const char* cStr = string.BeginReading();
  910. size_t len = strlen(cStr);
  911. while (true) {
  912. printf_stderr("IMG: %.140s\n", cStr);
  913. if (len <= 140)
  914. break;
  915. len -= 140;
  916. cStr += 140;
  917. }
  918. }
  919. #endif
  920. fprintf(aFile, "%s", string.BeginReading());
  921. } else if (aStrOut) {
  922. *aStrOut = string;
  923. } else {
  924. nsCOMPtr<nsIClipboardHelper> clipboard(do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv));
  925. if (clipboard) {
  926. clipboard->CopyString(NS_ConvertASCIItoUTF16(string));
  927. }
  928. }
  929. return NS_OK;
  930. }
  931. static nsCString
  932. EncodeSourceSurfaceAsPNGURI(SourceSurface* aSurface)
  933. {
  934. nsCString string;
  935. EncodeSourceSurfaceInternal(aSurface, NS_LITERAL_CSTRING("image/png"),
  936. EmptyString(), gfxUtils::eDataURIEncode,
  937. nullptr, &string);
  938. return string;
  939. }
  940. /* static */ nsresult
  941. gfxUtils::EncodeSourceSurface(SourceSurface* aSurface,
  942. const nsACString& aMimeType,
  943. const nsAString& aOutputOptions,
  944. BinaryOrData aBinaryOrData,
  945. FILE* aFile)
  946. {
  947. return EncodeSourceSurfaceInternal(aSurface, aMimeType, aOutputOptions,
  948. aBinaryOrData, aFile, nullptr);
  949. }
  950. /* From Rec601:
  951. [R] [1.1643835616438356, 0.0, 1.5960267857142858] [ Y - 16]
  952. [G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708] x [Cb - 128]
  953. [B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [Cr - 128]
  954. For [0,1] instead of [0,255], and to 5 places:
  955. [R] [1.16438, 0.00000, 1.59603] [ Y - 0.06275]
  956. [G] = [1.16438, -0.39176, -0.81297] x [Cb - 0.50196]
  957. [B] [1.16438, 2.01723, 0.00000] [Cr - 0.50196]
  958. From Rec709:
  959. [R] [1.1643835616438356, 4.2781193979771426e-17, 1.7927410714285714] [ Y - 16]
  960. [G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444] x [Cb - 128]
  961. [B] [1.1643835616438356, 2.1124017857142854, 0.0] [Cr - 128]
  962. For [0,1] instead of [0,255], and to 5 places:
  963. [R] [1.16438, 0.00000, 1.79274] [ Y - 0.06275]
  964. [G] = [1.16438, -0.21325, -0.53291] x [Cb - 0.50196]
  965. [B] [1.16438, 2.11240, 0.00000] [Cr - 0.50196]
  966. */
  967. /* static */ float*
  968. gfxUtils::Get4x3YuvColorMatrix(YUVColorSpace aYUVColorSpace)
  969. {
  970. static const float yuv_to_rgb_rec601[12] = { 1.16438f, 0.0f, 1.59603f, 0.0f,
  971. 1.16438f, -0.39176f, -0.81297f, 0.0f,
  972. 1.16438f, 2.01723f, 0.0f, 0.0f,
  973. };
  974. static const float yuv_to_rgb_rec709[12] = { 1.16438f, 0.0f, 1.79274f, 0.0f,
  975. 1.16438f, -0.21325f, -0.53291f, 0.0f,
  976. 1.16438f, 2.11240f, 0.0f, 0.0f,
  977. };
  978. if (aYUVColorSpace == YUVColorSpace::BT709) {
  979. return const_cast<float*>(yuv_to_rgb_rec709);
  980. } else {
  981. return const_cast<float*>(yuv_to_rgb_rec601);
  982. }
  983. }
  984. /* static */ float*
  985. gfxUtils::Get3x3YuvColorMatrix(YUVColorSpace aYUVColorSpace)
  986. {
  987. static const float yuv_to_rgb_rec601[9] = {
  988. 1.16438f, 1.16438f, 1.16438f, 0.0f, -0.39176f, 2.01723f, 1.59603f, -0.81297f, 0.0f,
  989. };
  990. static const float yuv_to_rgb_rec709[9] = {
  991. 1.16438f, 1.16438f, 1.16438f, 0.0f, -0.21325f, 2.11240f, 1.79274f, -0.53291f, 0.0f,
  992. };
  993. if (aYUVColorSpace == YUVColorSpace::BT709) {
  994. return const_cast<float*>(yuv_to_rgb_rec709);
  995. } else {
  996. return const_cast<float*>(yuv_to_rgb_rec601);
  997. }
  998. }
  999. /* static */ void
  1000. gfxUtils::WriteAsPNG(SourceSurface* aSurface, const nsAString& aFile)
  1001. {
  1002. WriteAsPNG(aSurface, NS_ConvertUTF16toUTF8(aFile).get());
  1003. }
  1004. /* static */ void
  1005. gfxUtils::WriteAsPNG(SourceSurface* aSurface, const char* aFile)
  1006. {
  1007. FILE* file = fopen(aFile, "wb");
  1008. if (!file) {
  1009. // Maybe the directory doesn't exist; try creating it, then fopen again.
  1010. nsresult rv = NS_ERROR_FAILURE;
  1011. nsCOMPtr<nsIFile> comFile = do_CreateInstance("@mozilla.org/file/local;1");
  1012. if (comFile) {
  1013. NS_ConvertUTF8toUTF16 utf16path((nsDependentCString(aFile)));
  1014. rv = comFile->InitWithPath(utf16path);
  1015. if (NS_SUCCEEDED(rv)) {
  1016. nsCOMPtr<nsIFile> dirPath;
  1017. comFile->GetParent(getter_AddRefs(dirPath));
  1018. if (dirPath) {
  1019. rv = dirPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
  1020. if (NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_ALREADY_EXISTS) {
  1021. file = fopen(aFile, "wb");
  1022. }
  1023. }
  1024. }
  1025. }
  1026. if (!file) {
  1027. NS_WARNING("Failed to open file to create PNG!");
  1028. return;
  1029. }
  1030. }
  1031. EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
  1032. EmptyString(), eBinaryEncode, file);
  1033. fclose(file);
  1034. }
  1035. /* static */ void
  1036. gfxUtils::WriteAsPNG(DrawTarget* aDT, const nsAString& aFile)
  1037. {
  1038. WriteAsPNG(aDT, NS_ConvertUTF16toUTF8(aFile).get());
  1039. }
  1040. /* static */ void
  1041. gfxUtils::WriteAsPNG(DrawTarget* aDT, const char* aFile)
  1042. {
  1043. RefPtr<SourceSurface> surface = aDT->Snapshot();
  1044. if (surface) {
  1045. WriteAsPNG(surface, aFile);
  1046. } else {
  1047. NS_WARNING("Failed to get surface!");
  1048. }
  1049. }
  1050. /* static */ void
  1051. gfxUtils::WriteAsPNG(nsIPresShell* aShell, const char* aFile)
  1052. {
  1053. int32_t width = 1000, height = 1000;
  1054. nsRect r(0, 0, aShell->GetPresContext()->DevPixelsToAppUnits(width),
  1055. aShell->GetPresContext()->DevPixelsToAppUnits(height));
  1056. RefPtr<mozilla::gfx::DrawTarget> dt = gfxPlatform::GetPlatform()->
  1057. CreateOffscreenContentDrawTarget(IntSize(width, height),
  1058. SurfaceFormat::B8G8R8A8);
  1059. NS_ENSURE_TRUE(dt && dt->IsValid(), /*void*/);
  1060. RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt);
  1061. MOZ_ASSERT(context); // already checked the draw target above
  1062. aShell->RenderDocument(r, 0, NS_RGB(255, 255, 0), context);
  1063. WriteAsPNG(dt.get(), aFile);
  1064. }
  1065. /* static */ void
  1066. gfxUtils::DumpAsDataURI(SourceSurface* aSurface, FILE* aFile)
  1067. {
  1068. EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
  1069. EmptyString(), eDataURIEncode, aFile);
  1070. }
  1071. /* static */ nsCString
  1072. gfxUtils::GetAsDataURI(SourceSurface* aSurface)
  1073. {
  1074. return EncodeSourceSurfaceAsPNGURI(aSurface);
  1075. }
  1076. /* static */ void
  1077. gfxUtils::DumpAsDataURI(DrawTarget* aDT, FILE* aFile)
  1078. {
  1079. RefPtr<SourceSurface> surface = aDT->Snapshot();
  1080. if (surface) {
  1081. DumpAsDataURI(surface, aFile);
  1082. } else {
  1083. NS_WARNING("Failed to get surface!");
  1084. }
  1085. }
  1086. /* static */ nsCString
  1087. gfxUtils::GetAsLZ4Base64Str(DataSourceSurface* aSourceSurface)
  1088. {
  1089. int32_t dataSize = aSourceSurface->GetSize().height * aSourceSurface->Stride();
  1090. auto compressedData = MakeUnique<char[]>(LZ4::maxCompressedSize(dataSize));
  1091. if (compressedData) {
  1092. int nDataSize = LZ4::compress((char*)aSourceSurface->GetData(),
  1093. dataSize,
  1094. compressedData.get());
  1095. if (nDataSize > 0) {
  1096. nsCString encodedImg;
  1097. nsresult rv = Base64Encode(Substring(compressedData.get(), nDataSize), encodedImg);
  1098. if (rv == NS_OK) {
  1099. nsCString string("");
  1100. string.AppendPrintf("data:image/lz4bgra;base64,%i,%i,%i,",
  1101. aSourceSurface->GetSize().width,
  1102. aSourceSurface->Stride(),
  1103. aSourceSurface->GetSize().height);
  1104. string.Append(encodedImg);
  1105. return string;
  1106. }
  1107. }
  1108. }
  1109. return nsCString("");
  1110. }
  1111. /* static */ nsCString
  1112. gfxUtils::GetAsDataURI(DrawTarget* aDT)
  1113. {
  1114. RefPtr<SourceSurface> surface = aDT->Snapshot();
  1115. if (surface) {
  1116. return EncodeSourceSurfaceAsPNGURI(surface);
  1117. } else {
  1118. NS_WARNING("Failed to get surface!");
  1119. return nsCString("");
  1120. }
  1121. }
  1122. /* static */ void
  1123. gfxUtils::CopyAsDataURI(SourceSurface* aSurface)
  1124. {
  1125. EncodeSourceSurface(aSurface, NS_LITERAL_CSTRING("image/png"),
  1126. EmptyString(), eDataURIEncode, nullptr);
  1127. }
  1128. /* static */ void
  1129. gfxUtils::CopyAsDataURI(DrawTarget* aDT)
  1130. {
  1131. RefPtr<SourceSurface> surface = aDT->Snapshot();
  1132. if (surface) {
  1133. CopyAsDataURI(surface);
  1134. } else {
  1135. NS_WARNING("Failed to get surface!");
  1136. }
  1137. }
  1138. /* static */ UniquePtr<uint8_t[]>
  1139. gfxUtils::GetImageBuffer(gfx::DataSourceSurface* aSurface,
  1140. bool aIsAlphaPremultiplied,
  1141. int32_t* outFormat)
  1142. {
  1143. *outFormat = 0;
  1144. DataSourceSurface::MappedSurface map;
  1145. if (!aSurface->Map(DataSourceSurface::MapType::READ, &map))
  1146. return nullptr;
  1147. uint32_t bufferSize = aSurface->GetSize().width * aSurface->GetSize().height * 4;
  1148. auto imageBuffer = MakeUniqueFallible<uint8_t[]>(bufferSize);
  1149. if (!imageBuffer) {
  1150. aSurface->Unmap();
  1151. return nullptr;
  1152. }
  1153. memcpy(imageBuffer.get(), map.mData, bufferSize);
  1154. aSurface->Unmap();
  1155. int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
  1156. if (!aIsAlphaPremultiplied) {
  1157. // We need to convert to INPUT_FORMAT_RGBA, otherwise
  1158. // we are automatically considered premult, and unpremult'd.
  1159. // Yes, it is THAT silly.
  1160. // Except for different lossy conversions by color,
  1161. // we could probably just change the label, and not change the data.
  1162. gfxUtils::ConvertBGRAtoRGBA(imageBuffer.get(), bufferSize);
  1163. format = imgIEncoder::INPUT_FORMAT_RGBA;
  1164. }
  1165. *outFormat = format;
  1166. return imageBuffer;
  1167. }
  1168. /* static */ nsresult
  1169. gfxUtils::GetInputStream(gfx::DataSourceSurface* aSurface,
  1170. bool aIsAlphaPremultiplied,
  1171. const char* aMimeType,
  1172. const char16_t* aEncoderOptions,
  1173. nsIInputStream** outStream)
  1174. {
  1175. nsCString enccid("@mozilla.org/image/encoder;2?type=");
  1176. enccid += aMimeType;
  1177. nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
  1178. if (!encoder)
  1179. return NS_ERROR_FAILURE;
  1180. int32_t format = 0;
  1181. UniquePtr<uint8_t[]> imageBuffer = GetImageBuffer(aSurface, aIsAlphaPremultiplied, &format);
  1182. if (!imageBuffer)
  1183. return NS_ERROR_FAILURE;
  1184. return dom::ImageEncoder::GetInputStream(aSurface->GetSize().width,
  1185. aSurface->GetSize().height,
  1186. imageBuffer.get(), format,
  1187. encoder, aEncoderOptions, outStream);
  1188. }
  1189. class GetFeatureStatusRunnable final : public dom::workers::WorkerMainThreadRunnable
  1190. {
  1191. public:
  1192. GetFeatureStatusRunnable(dom::workers::WorkerPrivate* workerPrivate,
  1193. const nsCOMPtr<nsIGfxInfo>& gfxInfo,
  1194. int32_t feature,
  1195. nsACString& failureId,
  1196. int32_t* status)
  1197. : WorkerMainThreadRunnable(workerPrivate,
  1198. NS_LITERAL_CSTRING("GFX :: GetFeatureStatus"))
  1199. , mGfxInfo(gfxInfo)
  1200. , mFeature(feature)
  1201. , mStatus(status)
  1202. , mFailureId(failureId)
  1203. , mNSResult(NS_OK)
  1204. {
  1205. }
  1206. bool MainThreadRun() override
  1207. {
  1208. if (mGfxInfo) {
  1209. mNSResult = mGfxInfo->GetFeatureStatus(mFeature, mFailureId, mStatus);
  1210. }
  1211. return true;
  1212. }
  1213. nsresult GetNSResult() const
  1214. {
  1215. return mNSResult;
  1216. }
  1217. protected:
  1218. ~GetFeatureStatusRunnable() {}
  1219. private:
  1220. nsCOMPtr<nsIGfxInfo> mGfxInfo;
  1221. int32_t mFeature;
  1222. int32_t* mStatus;
  1223. nsACString& mFailureId;
  1224. nsresult mNSResult;
  1225. };
  1226. /* static */ nsresult
  1227. gfxUtils::ThreadSafeGetFeatureStatus(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
  1228. int32_t feature, nsACString& failureId,
  1229. int32_t* status)
  1230. {
  1231. if (!NS_IsMainThread()) {
  1232. dom::workers::WorkerPrivate* workerPrivate =
  1233. dom::workers::GetCurrentThreadWorkerPrivate();
  1234. RefPtr<GetFeatureStatusRunnable> runnable =
  1235. new GetFeatureStatusRunnable(workerPrivate, gfxInfo, feature, failureId,
  1236. status);
  1237. ErrorResult rv;
  1238. runnable->Dispatch(dom::workers::Terminating, rv);
  1239. if (rv.Failed()) {
  1240. // XXXbz This is totally broken, since we're supposed to just abort
  1241. // everything up the callstack but the callers basically eat the
  1242. // exception. Ah, well.
  1243. return rv.StealNSResult();
  1244. }
  1245. return runnable->GetNSResult();
  1246. }
  1247. return gfxInfo->GetFeatureStatus(feature, failureId, status);
  1248. }
  1249. /* static */ bool
  1250. gfxUtils::IsFeatureBlacklisted(nsCOMPtr<nsIGfxInfo> gfxInfo, int32_t feature,
  1251. nsACString* const out_blacklistId)
  1252. {
  1253. if (!gfxInfo) {
  1254. gfxInfo = services::GetGfxInfo();
  1255. }
  1256. int32_t status;
  1257. if (!NS_SUCCEEDED(gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo, feature,
  1258. *out_blacklistId, &status)))
  1259. {
  1260. out_blacklistId->AssignLiteral("");
  1261. return true;
  1262. }
  1263. return status != nsIGfxInfo::FEATURE_STATUS_OK;
  1264. }
  1265. /* static */ bool
  1266. gfxUtils::DumpDisplayList() {
  1267. return gfxPrefs::LayoutDumpDisplayList() ||
  1268. (gfxPrefs::LayoutDumpDisplayListContent() && XRE_IsContentProcess());
  1269. }
  1270. FILE *gfxUtils::sDumpPaintFile = stderr;
  1271. namespace mozilla {
  1272. namespace gfx {
  1273. Color ToDeviceColor(Color aColor)
  1274. {
  1275. // aColor is pass-by-value since to get return value optimization goodness we
  1276. // need to return the same object from all return points in this function. We
  1277. // could declare a local Color variable and use that, but we might as well
  1278. // just use aColor.
  1279. if (gfxPlatform::GetCMSMode() == eCMSMode_All) {
  1280. qcms_transform *transform = gfxPlatform::GetCMSRGBTransform();
  1281. if (transform) {
  1282. gfxPlatform::TransformPixel(aColor, aColor, transform);
  1283. // Use the original alpha to avoid unnecessary float->byte->float
  1284. // conversion errors
  1285. }
  1286. }
  1287. return aColor;
  1288. }
  1289. Color ToDeviceColor(nscolor aColor)
  1290. {
  1291. return ToDeviceColor(Color::FromABGR(aColor));
  1292. }
  1293. } // namespace gfx
  1294. } // namespace mozilla