Bug 878032 - Part 2: Factor out partial bitmap uploading and scaling. r=jrmuizel
☠☠ backed out by 5b3196ad66f4 ☠ ☠
authorBas Schouten <bschouten@mozilla.com>
Wed, 19 Jun 2013 22:48:40 +0200
changeset 135693 3cb61a7d57461117b4140c10c1c0cfcff5c3d0bd
parent 135692 627c03f469d882f08cb0d4a2c557d1e0396ea2d1
child 135694 8ed0409f36c27a082dcc93a19ab715c5ced132f9
push idunknown
push userunknown
push dateunknown
reviewersjrmuizel
bugs878032
milestone24.0a1
Bug 878032 - Part 2: Factor out partial bitmap uploading and scaling. r=jrmuizel
gfx/2d/BaseRect.h
gfx/2d/DrawTargetD2D.cpp
gfx/2d/DrawTargetD2D.h
gfx/2d/HelpersD2D.h
gfx/2d/unittest/TestDrawTargetBase.cpp
--- a/gfx/2d/BaseRect.h
+++ b/gfx/2d/BaseRect.h
@@ -84,20 +84,20 @@ struct BaseRect {
   }
   // Returns the rectangle containing the intersection of the points
   // (including edges) of *this and aRect. If there are no points in that
   // intersection, returns an empty rectangle with x/y set to the std::max of the x/y
   // of *this and aRect.
   Sub Intersect(const Sub& aRect) const
   {
     Sub result;
-    result.x = std::max(x, aRect.x);
-    result.y = std::max(y, aRect.y);
-    result.width = std::min(XMost(), aRect.XMost()) - result.x;
-    result.height = std::min(YMost(), aRect.YMost()) - result.y;
+    result.x = std::max<T>(x, aRect.x);
+    result.y = std::max<T>(y, aRect.y);
+    result.width = std::min<T>(XMost(), aRect.XMost()) - result.x;
+    result.height = std::min<T>(YMost(), aRect.YMost()) - result.y;
     if (result.width < 0 || result.height < 0) {
       result.SizeTo(0, 0);
     }
     return result;
   }
   // Sets *this to be the rectangle containing the intersection of the points
   // (including edges) of *this and aRect. If there are no points in that
   // intersection, sets *this to be an empty rectangle with x/y set to the std::max
--- a/gfx/2d/DrawTargetD2D.cpp
+++ b/gfx/2d/DrawTargetD2D.cpp
@@ -2257,17 +2257,17 @@ DrawTargetD2D::CreateBrushForPattern(con
     default:
       {
         RefPtr<DataSourceSurface> dataSurf = pat->mSurface->GetDataSurface();
         if (!dataSurf) {
           gfxWarning() << "Invalid surface type.";
           return nullptr;
         }
 
-        bitmap = CreatePartialBitmapForSurface(dataSurf, mat, pat->mExtendMode); 
+        bitmap = CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, pat->mExtendMode, mat, mRT); 
         if (!bitmap) {
           return nullptr;
         }
       }
       break;
     }
     
     mRT->CreateBitmapBrush(bitmap,
@@ -2416,141 +2416,16 @@ DrawTargetD2D::CreateTextureForAnalysis(
 
   if (FAILED(hr)) {
     return nullptr;
   }
 
   return tex;
 }
 
-TemporaryRef<ID2D1Bitmap>
-DrawTargetD2D::CreatePartialBitmapForSurface(DataSourceSurface *aSurface, Matrix &aMatrix, ExtendMode aExtendMode)
-{
-  RefPtr<ID2D1Bitmap> bitmap;
-
-  // This is where things get complicated. The source surface was
-  // created for a surface that was too large to fit in a texture.
-  // We'll need to figure out if we can work with a partial upload
-  // or downsample in software.
-
-  Matrix transform = mTransform;
-  Matrix invTransform = transform = aMatrix * transform;
-  if (!invTransform.Invert()) {
-    // Singular transform, nothing to be drawn.
-    return nullptr;
-  }
-
-  Rect rect(0, 0, Float(mSize.width), Float(mSize.height));
-
-  // Calculate the rectangle of the source mapped to our surface.
-  rect = invTransform.TransformBounds(rect);
-  rect.RoundOut();
-
-  IntSize size = aSurface->GetSize();
-
-  Rect uploadRect(0, 0, Float(size.width), Float(size.height));
-
-  // Limit the uploadRect as much as possible without supporting discontiguous uploads 
-  //
-  //                               region we will paint from
-  //   uploadRect
-  //   .---------------.              .---------------.         resulting uploadRect
-  //   |               |rect          |               |
-  //   |          .---------.         .----.     .----.          .---------------.
-  //   |          |         |  ---->  |    |     |    |   ---->  |               |
-  //   |          '---------'         '----'     '----'          '---------------'
-  //   '---------------'              '---------------'
-  //
-  //
-
-  if (uploadRect.Contains(rect)) {
-    // Extend mode is irrelevant, the displayed rect is completely contained
-    // by the source bitmap.
-    uploadRect = rect;
-  } else if (aExtendMode == EXTEND_CLAMP && uploadRect.Intersects(rect)) {
-    // Calculate the rectangle on the source bitmap that touches our
-    // surface, and upload that, for EXTEND_CLAMP we can actually guarantee
-    // correct behaviour in this case.
-    uploadRect = uploadRect.Intersect(rect);
-
-    // We now proceed to check if we can limit at least one dimension of the
-    // upload rect safely without looking at extend mode.
-  } else if (rect.x >= 0 && rect.XMost() < size.width) {
-    uploadRect.x = rect.x;
-    uploadRect.width = rect.width;
-  } else if (rect.y >= 0 && rect.YMost() < size.height) {
-    uploadRect.y = rect.y;
-    uploadRect.height = rect.height;
-  }
-
-
-  int stride = aSurface->Stride();
-
-  if (uploadRect.width <= mRT->GetMaximumBitmapSize() &&
-      uploadRect.height <= mRT->GetMaximumBitmapSize()) {
-
-    // A partial upload will suffice.
-    mRT->CreateBitmap(D2D1::SizeU(uint32_t(uploadRect.width), uint32_t(uploadRect.height)),
-                      aSurface->GetData() + int(uploadRect.x) * 4 + int(uploadRect.y) * stride,
-                      stride,
-                      D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())),
-                      byRef(bitmap));
-
-    aMatrix.Translate(uploadRect.x, uploadRect.y);
-
-    return bitmap;
-  } else {
-    int Bpp = BytesPerPixel(aSurface->GetFormat());
-
-    if (Bpp != 4) {
-      // This shouldn't actually happen in practice!
-      MOZ_ASSERT(false);
-      return nullptr;
-    }
-
-    ImageHalfScaler scaler(aSurface->GetData(), stride, size);
-
-    // Calculate the maximum width/height of the image post transform.
-    Point topRight = transform * Point(Float(size.width), 0);
-    Point topLeft = transform * Point(0, 0);
-    Point bottomRight = transform * Point(Float(size.width), Float(size.height));
-    Point bottomLeft = transform * Point(0, Float(size.height));
-    
-    IntSize scaleSize;
-
-    scaleSize.width = int32_t(max(Distance(topRight, topLeft),
-                                  Distance(bottomRight, bottomLeft)));
-    scaleSize.height = int32_t(max(Distance(topRight, bottomRight),
-                                   Distance(topLeft, bottomLeft)));
-
-    if (unsigned(scaleSize.width) > mRT->GetMaximumBitmapSize()) {
-      // Ok, in this case we'd really want a downscale of a part of the bitmap,
-      // perhaps we can do this later but for simplicity let's do something
-      // different here and assume it's good enough, this should be rare!
-      scaleSize.width = 4095;
-    }
-    if (unsigned(scaleSize.height) > mRT->GetMaximumBitmapSize()) {
-      scaleSize.height = 4095;
-    }
-
-    scaler.ScaleForSize(scaleSize);
-
-    IntSize newSize = scaler.GetSize();
-    
-    mRT->CreateBitmap(D2D1::SizeU(newSize.width, newSize.height),
-                      scaler.GetScaledData(), scaler.GetStride(),
-                      D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())),
-                      byRef(bitmap));
-
-    aMatrix.Scale(Float(size.width / newSize.width),
-                  Float(size.height / newSize.height));
-    return bitmap;
-  }
-}
-
 void
 DrawTargetD2D::SetupEffectForRadialGradient(const RadialGradientPattern *aPattern)
 {
   mPrivateData->mEffect->GetTechniqueByName("SampleRadialGradient")->GetPassByIndex(0)->Apply(0);
   mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()->
     SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f));
 
   float dimensions[] = { float(mSize.width), float(mSize.height), 0, 0 };
--- a/gfx/2d/DrawTargetD2D.h
+++ b/gfx/2d/DrawTargetD2D.h
@@ -198,22 +198,16 @@ private:
   // bounds to correctly reflect the total clip. This is in device space.
   TemporaryRef<ID2D1Geometry> GetClippedGeometry(IntRect *aClipBounds);
 
   TemporaryRef<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
 
   TemporaryRef<ID3D10Texture2D> CreateGradientTexture(const GradientStopsD2D *aStops);
   TemporaryRef<ID3D10Texture2D> CreateTextureForAnalysis(IDWriteGlyphRunAnalysis *aAnalysis, const IntRect &aBounds);
 
-  // This creates a (partially) uploaded bitmap for a DataSourceSurface. It
-  // uploads the minimum requirement and possibly downscales. It adjusts the
-  // input Matrix to compensate.
-  TemporaryRef<ID2D1Bitmap> CreatePartialBitmapForSurface(DataSourceSurface *aSurface, Matrix &aMatrix,
-                                                          ExtendMode aExtendMode);
-
   void SetupEffectForRadialGradient(const RadialGradientPattern *aPattern);
   void SetupStateForRendering();
 
   // Set the scissor rect to a certain IntRects, resets the scissor rect to
   // surface bounds when NULL is specified.
   void SetScissorToRect(IntRect *aRect);
 
   void PushD2DLayer(ID2D1RenderTarget *aRT, ID2D1Geometry *aGeometry, ID2D1Layer *aLayer, const D2D1_MATRIX_3X2_F &aTransform);
--- a/gfx/2d/HelpersD2D.h
+++ b/gfx/2d/HelpersD2D.h
@@ -8,16 +8,18 @@
 
 #include "moz-d2d1-1.h"
 
 #include <vector>
 
 #include <dwrite.h>
 #include "2D.h"
 #include "Logging.h"
+#include "Tools.h"
+#include "ImageScaling.h"
 
 #include "ScaledFontDWrite.h"
 
 namespace mozilla {
 namespace gfx {
 
 ID2D1Factory* D2DFactory();
 
@@ -356,12 +358,142 @@ CreateStrokeStyleForOptions(const Stroke
 
   if (FAILED(hr)) {
     gfxWarning() << "Failed to create Direct2D stroke style.";
   }
 
   return style;
 }
 
+// This creates a (partially) uploaded bitmap for a DataSourceSurface. It
+// uploads the minimum requirement and possibly downscales. It adjusts the
+// input Matrix to compensate.
+static TemporaryRef<ID2D1Bitmap>
+CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestinationTransform,
+                              const IntSize &aDestinationSize, ExtendMode aExtendMode,
+                              Matrix &aSourceTransform, ID2D1RenderTarget *aRT)
+{
+  RefPtr<ID2D1Bitmap> bitmap;
+
+  // This is where things get complicated. The source surface was
+  // created for a surface that was too large to fit in a texture.
+  // We'll need to figure out if we can work with a partial upload
+  // or downsample in software.
+
+  Matrix transform = aDestinationTransform;
+  Matrix invTransform = transform = aSourceTransform * transform;
+  if (!invTransform.Invert()) {
+    // Singular transform, nothing to be drawn.
+    return nullptr;
+  }
+
+  Rect rect(0, 0, Float(aDestinationSize.width), Float(aDestinationSize.height));
+
+  // Calculate the rectangle of the source mapped to our surface.
+  rect = invTransform.TransformBounds(rect);
+  rect.RoundOut();
+
+  IntSize size = aSurface->GetSize();
+
+  Rect uploadRect(0, 0, Float(size.width), Float(size.height));
+
+  // Limit the uploadRect as much as possible without supporting discontiguous uploads 
+  //
+  //                               region we will paint from
+  //   uploadRect
+  //   .---------------.              .---------------.         resulting uploadRect
+  //   |               |rect          |               |
+  //   |          .---------.         .----.     .----.          .---------------.
+  //   |          |         |  ---->  |    |     |    |   ---->  |               |
+  //   |          '---------'         '----'     '----'          '---------------'
+  //   '---------------'              '---------------'
+  //
+  //
+
+  if (uploadRect.Contains(rect)) {
+    // Extend mode is irrelevant, the displayed rect is completely contained
+    // by the source bitmap.
+    uploadRect = rect;
+  } else if (aExtendMode == EXTEND_CLAMP && uploadRect.Intersects(rect)) {
+    // Calculate the rectangle on the source bitmap that touches our
+    // surface, and upload that, for EXTEND_CLAMP we can actually guarantee
+    // correct behaviour in this case.
+    uploadRect = uploadRect.Intersect(rect);
+
+    // We now proceed to check if we can limit at least one dimension of the
+    // upload rect safely without looking at extend mode.
+  } else if (rect.x >= 0 && rect.XMost() < size.width) {
+    uploadRect.x = rect.x;
+    uploadRect.width = rect.width;
+  } else if (rect.y >= 0 && rect.YMost() < size.height) {
+    uploadRect.y = rect.y;
+    uploadRect.height = rect.height;
+  }
+
+
+  int stride = aSurface->Stride();
+
+  if (uploadRect.width <= aRT->GetMaximumBitmapSize() &&
+      uploadRect.height <= aRT->GetMaximumBitmapSize()) {
+
+    // A partial upload will suffice.
+    aRT->CreateBitmap(D2D1::SizeU(uint32_t(uploadRect.width), uint32_t(uploadRect.height)),
+                      aSurface->GetData() + int(uploadRect.x) * 4 + int(uploadRect.y) * stride,
+                      stride,
+                      D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())),
+                      byRef(bitmap));
+
+    aSourceTransform.Translate(uploadRect.x, uploadRect.y);
+
+    return bitmap;
+  } else {
+    int Bpp = BytesPerPixel(aSurface->GetFormat());
+
+    if (Bpp != 4) {
+      // This shouldn't actually happen in practice!
+      MOZ_ASSERT(false);
+      return nullptr;
+    }
+
+    ImageHalfScaler scaler(aSurface->GetData(), stride, size);
+
+    // Calculate the maximum width/height of the image post transform.
+    Point topRight = transform * Point(Float(size.width), 0);
+    Point topLeft = transform * Point(0, 0);
+    Point bottomRight = transform * Point(Float(size.width), Float(size.height));
+    Point bottomLeft = transform * Point(0, Float(size.height));
+    
+    IntSize scaleSize;
+
+    scaleSize.width = int32_t(max(Distance(topRight, topLeft),
+                                  Distance(bottomRight, bottomLeft)));
+    scaleSize.height = int32_t(max(Distance(topRight, bottomRight),
+                                   Distance(topLeft, bottomLeft)));
+
+    if (unsigned(scaleSize.width) > aRT->GetMaximumBitmapSize()) {
+      // Ok, in this case we'd really want a downscale of a part of the bitmap,
+      // perhaps we can do this later but for simplicity let's do something
+      // different here and assume it's good enough, this should be rare!
+      scaleSize.width = 4095;
+    }
+    if (unsigned(scaleSize.height) > aRT->GetMaximumBitmapSize()) {
+      scaleSize.height = 4095;
+    }
+
+    scaler.ScaleForSize(scaleSize);
+
+    IntSize newSize = scaler.GetSize();
+    
+    aRT->CreateBitmap(D2D1::SizeU(newSize.width, newSize.height),
+                      scaler.GetScaledData(), scaler.GetStride(),
+                      D2D1::BitmapProperties(D2DPixelFormat(aSurface->GetFormat())),
+                      byRef(bitmap));
+
+    aSourceTransform.Scale(Float(size.width / newSize.width),
+                           Float(size.height / newSize.height));
+    return bitmap;
+  }
+}
+
 }
 }
 
 #endif /* MOZILLA_GFX_HELPERSD2D_H_ */
--- a/gfx/2d/unittest/TestDrawTargetBase.cpp
+++ b/gfx/2d/unittest/TestDrawTargetBase.cpp
@@ -1,109 +1,109 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "TestDrawTargetBase.h"
-#include <sstream>
-
-using namespace mozilla;
-using namespace mozilla::gfx;
-using namespace std;
-
-TestDrawTargetBase::TestDrawTargetBase()
-{
-  REGISTER_TEST(TestDrawTargetBase, Initialized);
-  REGISTER_TEST(TestDrawTargetBase, FillCompletely);
-  REGISTER_TEST(TestDrawTargetBase, FillRect);
-}
-
-void
-TestDrawTargetBase::Initialized()
-{
-  VERIFY(mDT);
-}
-
-void
-TestDrawTargetBase::FillCompletely()
-{
-  mDT->FillRect(Rect(0, 0, DT_WIDTH, DT_HEIGHT), ColorPattern(Color(0, 0.5f, 0, 1.0f)));
-
-  RefreshSnapshot();
-
-  VerifyAllPixels(Color(0, 0.5f, 0, 1.0f));
-}
-
-void
-TestDrawTargetBase::FillRect()
-{
-  mDT->FillRect(Rect(0, 0, DT_WIDTH, DT_HEIGHT), ColorPattern(Color(0, 0.5f, 0, 1.0f)));
-  mDT->FillRect(Rect(50, 50, 50, 50), ColorPattern(Color(0.5f, 0, 0, 1.0f)));
-
-  RefreshSnapshot();
-
-  VerifyPixel(IntPoint(49, 49), Color(0, 0.5f, 0, 1.0f));
-  VerifyPixel(IntPoint(50, 50), Color(0.5f, 0, 0, 1.0f));
-  VerifyPixel(IntPoint(99, 99), Color(0.5f, 0, 0, 1.0f));
-  VerifyPixel(IntPoint(100, 100), Color(0, 0.5f, 0, 1.0f));
-}
-
-void
-TestDrawTargetBase::RefreshSnapshot()
-{
-  RefPtr<SourceSurface> snapshot = mDT->Snapshot();
-  mDataSnapshot = snapshot->GetDataSurface();
-}
-
-void
-TestDrawTargetBase::VerifyAllPixels(const Color &aColor)
-{
-  uint32_t *colVal = (uint32_t*)mDataSnapshot->GetData();
-
-  uint32_t expected = RGBAPixelFromColor(aColor);
-
-  for (int y = 0; y < DT_HEIGHT; y++) {
-    for (int x = 0; x < DT_WIDTH; x++) {
-      if (colVal[y * (mDataSnapshot->Stride() / 4) + x] != expected) {
-        LogMessage("VerifyAllPixels Failed\n");
-        mTestFailed = true;
-        return;
-      }
-    }
-  }
-}
-
-void
-TestDrawTargetBase::VerifyPixel(const IntPoint &aPoint, mozilla::gfx::Color &aColor)
-{
-  uint32_t *colVal = (uint32_t*)mDataSnapshot->GetData();
-
-  uint32_t expected = RGBAPixelFromColor(aColor);
-  uint32_t rawActual = colVal[aPoint.y * (mDataSnapshot->Stride() / 4) + aPoint.x];
-
-  if (rawActual != expected) {
-    stringstream message;
-    uint32_t actb = rawActual & 0xFF;
-    uint32_t actg = (rawActual & 0xFF00) >> 8;
-    uint32_t actr = (rawActual & 0xFF0000) >> 16;
-    uint32_t acta = (rawActual & 0xFF000000) >> 24;
-    uint32_t expb = expected & 0xFF;
-    uint32_t expg = (expected & 0xFF00) >> 8;
-    uint32_t expr = (expected & 0xFF0000) >> 16;
-    uint32_t expa = (expected & 0xFF000000) >> 24;
-
-    message << "Verify Pixel (" << aPoint.x << "x" << aPoint.y << ") Failed."
-      " Expected (" << expr << "," << expg << "," << expb << "," << expa << ") "
-      " Got (" << actr << "," << actg << "," << actb << "," << acta << ")\n";
-
-    LogMessage(message.str());
-    mTestFailed = true;
-    return;
-  }
-}
-
-uint32_t
-TestDrawTargetBase::RGBAPixelFromColor(const Color &aColor)
-{
-  return uint8_t((aColor.b * 255) + 0.5f) | uint8_t((aColor.g * 255) + 0.5f) << 8 |
-         uint8_t((aColor.r * 255) + 0.5f) << 16 | uint8_t((aColor.a * 255) + 0.5f) << 24;
-}
+
+#include "TestDrawTargetBase.h"
+#include <sstream>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace std;
+
+TestDrawTargetBase::TestDrawTargetBase()
+{
+  REGISTER_TEST(TestDrawTargetBase, Initialized);
+  REGISTER_TEST(TestDrawTargetBase, FillCompletely);
+  REGISTER_TEST(TestDrawTargetBase, FillRect);
+}
+
+void
+TestDrawTargetBase::Initialized()
+{
+  VERIFY(mDT);
+}
+
+void
+TestDrawTargetBase::FillCompletely()
+{
+  mDT->FillRect(Rect(0, 0, DT_WIDTH, DT_HEIGHT), ColorPattern(Color(0, 0.5f, 0, 1.0f)));
+
+  RefreshSnapshot();
+
+  VerifyAllPixels(Color(0, 0.5f, 0, 1.0f));
+}
+
+void
+TestDrawTargetBase::FillRect()
+{
+  mDT->FillRect(Rect(0, 0, DT_WIDTH, DT_HEIGHT), ColorPattern(Color(0, 0.5f, 0, 1.0f)));
+  mDT->FillRect(Rect(50, 50, 50, 50), ColorPattern(Color(0.5f, 0, 0, 1.0f)));
+
+  RefreshSnapshot();
+
+  VerifyPixel(IntPoint(49, 49), Color(0, 0.5f, 0, 1.0f));
+  VerifyPixel(IntPoint(50, 50), Color(0.5f, 0, 0, 1.0f));
+  VerifyPixel(IntPoint(99, 99), Color(0.5f, 0, 0, 1.0f));
+  VerifyPixel(IntPoint(100, 100), Color(0, 0.5f, 0, 1.0f));
+}
+
+void
+TestDrawTargetBase::RefreshSnapshot()
+{
+  RefPtr<SourceSurface> snapshot = mDT->Snapshot();
+  mDataSnapshot = snapshot->GetDataSurface();
+}
+
+void
+TestDrawTargetBase::VerifyAllPixels(const Color &aColor)
+{
+  uint32_t *colVal = (uint32_t*)mDataSnapshot->GetData();
+
+  uint32_t expected = RGBAPixelFromColor(aColor);
+
+  for (int y = 0; y < DT_HEIGHT; y++) {
+    for (int x = 0; x < DT_WIDTH; x++) {
+      if (colVal[y * (mDataSnapshot->Stride() / 4) + x] != expected) {
+        LogMessage("VerifyAllPixels Failed\n");
+        mTestFailed = true;
+        return;
+      }
+    }
+  }
+}
+
+void
+TestDrawTargetBase::VerifyPixel(const IntPoint &aPoint, mozilla::gfx::Color &aColor)
+{
+  uint32_t *colVal = (uint32_t*)mDataSnapshot->GetData();
+
+  uint32_t expected = RGBAPixelFromColor(aColor);
+  uint32_t rawActual = colVal[aPoint.y * (mDataSnapshot->Stride() / 4) + aPoint.x];
+
+  if (rawActual != expected) {
+    stringstream message;
+    uint32_t actb = rawActual & 0xFF;
+    uint32_t actg = (rawActual & 0xFF00) >> 8;
+    uint32_t actr = (rawActual & 0xFF0000) >> 16;
+    uint32_t acta = (rawActual & 0xFF000000) >> 24;
+    uint32_t expb = expected & 0xFF;
+    uint32_t expg = (expected & 0xFF00) >> 8;
+    uint32_t expr = (expected & 0xFF0000) >> 16;
+    uint32_t expa = (expected & 0xFF000000) >> 24;
+
+    message << "Verify Pixel (" << aPoint.x << "x" << aPoint.y << ") Failed."
+      " Expected (" << expr << "," << expg << "," << expb << "," << expa << ") "
+      " Got (" << actr << "," << actg << "," << actb << "," << acta << ")\n";
+
+    LogMessage(message.str());
+    mTestFailed = true;
+    return;
+  }
+}
+
+uint32_t
+TestDrawTargetBase::RGBAPixelFromColor(const Color &aColor)
+{
+  return uint8_t((aColor.b * 255) + 0.5f) | uint8_t((aColor.g * 255) + 0.5f) << 8 |
+         uint8_t((aColor.r * 255) + 0.5f) << 16 | uint8_t((aColor.a * 255) + 0.5f) << 24;
+}