gecko-dev/gfx/thebes/gfxImageSurface.cpp
Mike Hommey d7c4757af3 Bug 1423107 - Replace uses of moz_posix_memalign with posix_memalign. r=njn
moz_posix_memalign is a wrapper for posix_memalign that only exists if
posix_memalign exists.

On OSX, it has a fallback for an under-specified bug where it
purportedly returns a pointer that doesn't have the requested alignment.
That fallback was added in bug 414946, over 6 years ago, before jemalloc
was even enabled on OSX.

Considering posix_memalign is used directly in many other places in
Gecko, that we almost always use mozjemalloc, which doesn't have these
problems, and that in all likeliness, the bug was in some old version of
OSX that is not supported anymore, the fallback does not seem all that
useful.

So, just use posix_memalign directly.

--HG--
extra : rebase_source : b2151b5fb598dc20cbd70308555059f7545b18b2
2017-12-05 16:46:17 +09:00

382 lines
10 KiB
C++

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 "mozilla/MemoryReporting.h"
#if defined(HAVE_POSIX_MEMALIGN)
#include "gfxAlphaRecovery.h"
#endif
#include "gfxImageSurface.h"
#include "cairo.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/HelpersCairo.h"
#include "gfx2DGlue.h"
#include <algorithm>
using namespace mozilla;
using namespace mozilla::gfx;
gfxImageSurface::gfxImageSurface()
: mSize(0, 0),
mOwnsData(false),
mFormat(SurfaceFormat::UNKNOWN),
mStride(0)
{
}
void
gfxImageSurface::InitFromSurface(cairo_surface_t *csurf)
{
if (!csurf || cairo_surface_status(csurf)) {
MakeInvalid();
return;
}
mSize.width = cairo_image_surface_get_width(csurf);
mSize.height = cairo_image_surface_get_height(csurf);
mData = cairo_image_surface_get_data(csurf);
mFormat = CairoFormatToGfxFormat(cairo_image_surface_get_format(csurf));
mOwnsData = false;
mStride = cairo_image_surface_get_stride(csurf);
Init(csurf, true);
}
gfxImageSurface::gfxImageSurface(unsigned char *aData, const IntSize& aSize,
long aStride, gfxImageFormat aFormat)
{
InitWithData(aData, aSize, aStride, aFormat);
}
void
gfxImageSurface::MakeInvalid()
{
mSize = IntSize(-1, -1);
mData = nullptr;
mStride = 0;
}
void
gfxImageSurface::InitWithData(unsigned char *aData, const IntSize& aSize,
long aStride, gfxImageFormat aFormat)
{
mSize = aSize;
mOwnsData = false;
mData = aData;
mFormat = aFormat;
mStride = aStride;
if (!Factory::CheckSurfaceSize(aSize))
MakeInvalid();
cairo_format_t cformat = GfxFormatToCairoFormat(mFormat);
cairo_surface_t *surface =
cairo_image_surface_create_for_data((unsigned char*)mData,
cformat,
mSize.width,
mSize.height,
mStride);
// cairo_image_surface_create_for_data can return a 'null' surface
// in out of memory conditions. The gfxASurface::Init call checks
// the surface it receives to see if there is an error with the
// surface and handles it appropriately. That is why there is
// no check here.
Init(surface);
}
static void*
TryAllocAlignedBytes(size_t aSize)
{
// Use fallible allocators here
#if defined(HAVE_POSIX_MEMALIGN)
void* ptr;
// Try to align for fast alpha recovery. This should only help
// cairo too, can't hurt.
return posix_memalign(&ptr,
1 << gfxAlphaRecovery::GoodAlignmentLog2(),
aSize) ?
nullptr : ptr;
#else
// Oh well, hope that luck is with us in the allocator
return malloc(aSize);
#endif
}
gfxImageSurface::gfxImageSurface(const IntSize& size, gfxImageFormat format, bool aClear)
: mSize(size), mData(nullptr), mFormat(format)
{
AllocateAndInit(0, 0, aClear);
}
void
gfxImageSurface::AllocateAndInit(long aStride, int32_t aMinimalAllocation,
bool aClear)
{
// The callers should set mSize and mFormat.
MOZ_ASSERT(!mData);
mData = nullptr;
mOwnsData = false;
mStride = aStride > 0 ? aStride : ComputeStride();
if (aMinimalAllocation < mSize.height * mStride)
aMinimalAllocation = mSize.height * mStride;
if (!Factory::CheckSurfaceSize(mSize))
MakeInvalid();
// if we have a zero-sized surface, just leave mData nullptr
if (mSize.height * mStride > 0) {
// This can fail to allocate memory aligned as we requested,
// or it can fail to allocate any memory at all.
mData = (unsigned char *) TryAllocAlignedBytes(aMinimalAllocation);
if (!mData)
return;
if (aClear)
memset(mData, 0, aMinimalAllocation);
}
mOwnsData = true;
cairo_format_t cformat = GfxFormatToCairoFormat(mFormat);
cairo_surface_t *surface =
cairo_image_surface_create_for_data((unsigned char*)mData,
cformat,
mSize.width,
mSize.height,
mStride);
Init(surface);
if (mSurfaceValid) {
RecordMemoryUsed(mSize.height * ComputeStride() +
sizeof(gfxImageSurface));
}
}
gfxImageSurface::gfxImageSurface(const IntSize& size, gfxImageFormat format,
long aStride, int32_t aExtraBytes, bool aClear)
: mSize(size), mData(nullptr), mFormat(format)
{
AllocateAndInit(aStride, aExtraBytes, aClear);
}
gfxImageSurface::gfxImageSurface(cairo_surface_t *csurf)
{
mSize.width = cairo_image_surface_get_width(csurf);
mSize.height = cairo_image_surface_get_height(csurf);
mData = cairo_image_surface_get_data(csurf);
mFormat = CairoFormatToGfxFormat(cairo_image_surface_get_format(csurf));
mOwnsData = false;
mStride = cairo_image_surface_get_stride(csurf);
Init(csurf, true);
}
gfxImageSurface::~gfxImageSurface()
{
if (mOwnsData)
free(mData);
}
/*static*/ long
gfxImageSurface::ComputeStride(const IntSize& aSize, gfxImageFormat aFormat)
{
long stride;
if (aFormat == SurfaceFormat::A8R8G8B8_UINT32)
stride = aSize.width * 4;
else if (aFormat == SurfaceFormat::X8R8G8B8_UINT32)
stride = aSize.width * 4;
else if (aFormat == SurfaceFormat::R5G6B5_UINT16)
stride = aSize.width * 2;
else if (aFormat == SurfaceFormat::A8)
stride = aSize.width;
else {
NS_WARNING("Unknown format specified to gfxImageSurface!");
stride = aSize.width * 4;
}
stride = ((stride + 3) / 4) * 4;
return stride;
}
size_t
gfxImageSurface::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
size_t n = gfxASurface::SizeOfExcludingThis(aMallocSizeOf);
if (mOwnsData) {
n += aMallocSizeOf(mData);
}
return n;
}
size_t
gfxImageSurface::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
{
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
}
bool
gfxImageSurface::SizeOfIsMeasured() const
{
return true;
}
// helper function for the CopyFrom methods
static void
CopyForStride(unsigned char* aDest, unsigned char* aSrc, const IntSize& aSize, long aDestStride, long aSrcStride)
{
if (aDestStride == aSrcStride) {
memcpy (aDest, aSrc, aSrcStride * aSize.height);
} else {
int lineSize = std::min(aDestStride, aSrcStride);
for (int i = 0; i < aSize.height; i++) {
unsigned char* src = aSrc + aSrcStride * i;
unsigned char* dst = aDest + aDestStride * i;
memcpy (dst, src, lineSize);
}
}
}
// helper function for the CopyFrom methods
static bool
FormatsAreCompatible(gfxImageFormat a1, gfxImageFormat a2)
{
if (a1 != a2 &&
!(a1 == SurfaceFormat::A8R8G8B8_UINT32 &&
a2 == SurfaceFormat::X8R8G8B8_UINT32) &&
!(a1 == SurfaceFormat::X8R8G8B8_UINT32 &&
a2 == SurfaceFormat::A8R8G8B8_UINT32)) {
return false;
}
return true;
}
bool
gfxImageSurface::CopyFrom (SourceSurface *aSurface)
{
RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
if (!data) {
return false;
}
IntSize size(data->GetSize().width, data->GetSize().height);
if (size != mSize) {
return false;
}
if (!FormatsAreCompatible(SurfaceFormatToImageFormat(aSurface->GetFormat()),
mFormat)) {
return false;
}
DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ);
CopyForStride(mData, map.GetData(), size, mStride, map.GetStride());
return true;
}
bool
gfxImageSurface::CopyFrom(gfxImageSurface *other)
{
if (other->mSize != mSize) {
return false;
}
if (!FormatsAreCompatible(other->mFormat, mFormat)) {
return false;
}
CopyForStride(mData, other->mData, mSize, mStride, other->mStride);
return true;
}
bool
gfxImageSurface::CopyTo(SourceSurface *aSurface) {
RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
if (!data) {
return false;
}
IntSize size(data->GetSize().width, data->GetSize().height);
if (size != mSize) {
return false;
}
if (!FormatsAreCompatible(SurfaceFormatToImageFormat(aSurface->GetFormat()),
mFormat)) {
return false;
}
DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ_WRITE);
CopyForStride(map.GetData(), mData, size, map.GetStride(), mStride);
return true;
}
already_AddRefed<DataSourceSurface>
gfxImageSurface::CopyToB8G8R8A8DataSourceSurface()
{
RefPtr<DataSourceSurface> dataSurface =
Factory::CreateDataSourceSurface(IntSize(GetSize().width, GetSize().height),
SurfaceFormat::B8G8R8A8);
if (dataSurface) {
CopyTo(dataSurface);
}
return dataSurface.forget();
}
already_AddRefed<gfxSubimageSurface>
gfxImageSurface::GetSubimage(const gfxRect& aRect)
{
gfxRect r(aRect);
r.Round();
MOZ_ASSERT(gfxRect(0, 0, mSize.width, mSize.height).Contains(r));
gfxImageFormat format = Format();
unsigned char* subData = Data() +
(Stride() * (int)r.Y()) +
(int)r.X() * gfxASurface::BytePerPixelFromFormat(Format());
if (format == SurfaceFormat::A8R8G8B8_UINT32 &&
GetOpaqueRect().Contains(aRect)) {
format = SurfaceFormat::X8R8G8B8_UINT32;
}
RefPtr<gfxSubimageSurface> image =
new gfxSubimageSurface(this, subData,
IntSize((int)r.Width(), (int)r.Height()),
format);
return image.forget();
}
gfxSubimageSurface::gfxSubimageSurface(gfxImageSurface* aParent,
unsigned char* aData,
const IntSize& aSize,
gfxImageFormat aFormat)
: gfxImageSurface(aData, aSize, aParent->Stride(), aFormat)
, mParent(aParent)
{
}
already_AddRefed<gfxImageSurface>
gfxImageSurface::GetAsImageSurface()
{
RefPtr<gfxImageSurface> surface = this;
return surface.forget();
}