Bug 1141979 - part10 - hanlde drawing RGB24/BGR24/HSV/Lab onto canvas element; r=jrmuizel
MozReview-Commit-ID: FmiTy6tXNN7
--- a/dom/canvas/ImageBitmap.cpp
+++ b/dom/canvas/ImageBitmap.cpp
@@ -7,16 +7,17 @@
#include "mozilla/dom/ImageBitmap.h"
#include "mozilla/dom/ImageBitmapBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/StructuredCloneTags.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRunnable.h"
#include "mozilla/gfx/2D.h"
+#include "ImageBitmapColorUtils.h"
#include "ImageBitmapUtils.h"
#include "ImageUtils.h"
#include "imgTools.h"
#include "libyuv.h"
using namespace mozilla::gfx;
using namespace mozilla::layers;
@@ -422,16 +423,90 @@ ImageBitmap::Close()
}
void
ImageBitmap::SetPictureRect(const IntRect& aRect, ErrorResult& aRv)
{
mPictureRect = FixUpNegativeDimension(aRect, aRv);
}
+static already_AddRefed<SourceSurface>
+ConvertColorFormatIfNeeded(RefPtr<SourceSurface> aSurface)
+{
+ const SurfaceFormat srcFormat = aSurface->GetFormat();
+ if (srcFormat == SurfaceFormat::R8G8B8A8 ||
+ srcFormat == SurfaceFormat::B8G8R8A8 ||
+ srcFormat == SurfaceFormat::R8G8B8X8 ||
+ srcFormat == SurfaceFormat::B8G8R8X8 ||
+ srcFormat == SurfaceFormat::A8R8G8B8 ||
+ srcFormat == SurfaceFormat::X8R8G8B8) {
+ return aSurface.forget();
+ }
+
+ if (srcFormat == SurfaceFormat::A8 ||
+ srcFormat == SurfaceFormat::Depth) {
+ return nullptr;
+ }
+
+ const int bytesPerPixel = BytesPerPixel(SurfaceFormat::B8G8R8A8);
+ const IntSize dstSize = aSurface->GetSize();
+ const uint32_t dstStride = dstSize.width * bytesPerPixel;
+
+ RefPtr<DataSourceSurface> dstDataSurface =
+ Factory::CreateDataSourceSurfaceWithStride(dstSize,
+ SurfaceFormat::B8G8R8A8,
+ dstStride);
+
+ RefPtr<DataSourceSurface> srcDataSurface = aSurface->GetDataSurface();
+ if (NS_WARN_IF(!srcDataSurface)) {
+ return nullptr;
+ }
+
+ DataSourceSurface::ScopedMap srcMap(srcDataSurface, DataSourceSurface::READ);
+ DataSourceSurface::ScopedMap dstMap(dstDataSurface, DataSourceSurface::WRITE);
+ if (NS_WARN_IF(!srcMap.IsMapped()) || NS_WARN_IF(!dstMap.IsMapped())) {
+ return nullptr;
+ }
+
+ int rv = 0;
+ if (srcFormat == SurfaceFormat::R8G8B8) {
+ rv = RGB24ToBGRA32(srcMap.GetData(), srcMap.GetStride(),
+ dstMap.GetData(), dstMap.GetStride(),
+ dstSize.width, dstSize.height);
+ } else if (srcFormat == SurfaceFormat::B8G8R8) {
+ rv = BGR24ToBGRA32(srcMap.GetData(), srcMap.GetStride(),
+ dstMap.GetData(), dstMap.GetStride(),
+ dstSize.width, dstSize.height);
+ } else if (srcFormat == SurfaceFormat::HSV) {
+ rv = HSVToBGRA32((const float*)srcMap.GetData(), srcMap.GetStride(),
+ dstMap.GetData(), dstMap.GetStride(),
+ dstSize.width, dstSize.height);
+ } else if (srcFormat == SurfaceFormat::Lab) {
+ rv = LabToBGRA32((const float*)srcMap.GetData(), srcMap.GetStride(),
+ dstMap.GetData(), dstMap.GetStride(),
+ dstSize.width, dstSize.height);
+ }
+
+ if (NS_WARN_IF(rv != 0)) {
+ return nullptr;
+ }
+
+ return dstDataSurface.forget();
+}
+
+/*
+ * The functionality of PrepareForDrawTarget method:
+ * (1) Get a SourceSurface from the mData (which is a layers::Image).
+ * (2) Convert the SourceSurface to format B8G8R8A8 if the original format is
+ * R8G8B8, B8G8R8, HSV or Lab.
+ * Note: if the original format is A8 or Depth, then return null directly.
+ * (3) Do cropping if the size of SourceSurface does not equal to the
+ * mPictureRect.
+ * (4) Pre-multiply alpha if needed.
+ */
already_AddRefed<SourceSurface>
ImageBitmap::PrepareForDrawTarget(gfx::DrawTarget* aTarget)
{
MOZ_ASSERT(aTarget);
if (!mData) {
return nullptr;
}
@@ -439,16 +514,24 @@ ImageBitmap::PrepareForDrawTarget(gfx::D
if (!mSurface) {
mSurface = mData->GetAsSourceSurface();
if (!mSurface) {
return nullptr;
}
}
+ // Check if we need to convert the format.
+ // Convert R8G8B8/B8G8R8/HSV/Lab to B8G8R8A8.
+ // Return null if the original format is A8 or Depth.
+ mSurface = ConvertColorFormatIfNeeded(mSurface);
+ if (NS_WARN_IF(!mSurface)) {
+ return nullptr;
+ }
+
RefPtr<DrawTarget> target = aTarget;
IntRect surfRect(0, 0, mSurface->GetSize().width, mSurface->GetSize().height);
// Check if we still need to crop our surface
if (!mPictureRect.IsEqualEdges(surfRect)) {
IntRect surfPortion = surfRect.Intersect(mPictureRect);