1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxImageSurface.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,440 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 + 1.10 +#include "mozilla/MemoryReporting.h" 1.11 +#if defined(HAVE_POSIX_MEMALIGN) 1.12 +#include "gfxAlphaRecovery.h" 1.13 +#endif 1.14 +#include "gfxImageSurface.h" 1.15 + 1.16 +#include "cairo.h" 1.17 +#include "mozilla/gfx/2D.h" 1.18 +#include "gfx2DGlue.h" 1.19 +#include <algorithm> 1.20 + 1.21 +using namespace mozilla; 1.22 +using namespace mozilla::gfx; 1.23 + 1.24 +gfxImageSurface::gfxImageSurface() 1.25 + : mSize(0, 0), 1.26 + mOwnsData(false), 1.27 + mFormat(gfxImageFormat::Unknown), 1.28 + mStride(0) 1.29 +{ 1.30 +} 1.31 + 1.32 +void 1.33 +gfxImageSurface::InitFromSurface(cairo_surface_t *csurf) 1.34 +{ 1.35 + mSize.width = cairo_image_surface_get_width(csurf); 1.36 + mSize.height = cairo_image_surface_get_height(csurf); 1.37 + mData = cairo_image_surface_get_data(csurf); 1.38 + mFormat = (gfxImageFormat) cairo_image_surface_get_format(csurf); 1.39 + mOwnsData = false; 1.40 + mStride = cairo_image_surface_get_stride(csurf); 1.41 + 1.42 + Init(csurf, true); 1.43 +} 1.44 + 1.45 +gfxImageSurface::gfxImageSurface(unsigned char *aData, const gfxIntSize& aSize, 1.46 + long aStride, gfxImageFormat aFormat) 1.47 +{ 1.48 + InitWithData(aData, aSize, aStride, aFormat); 1.49 +} 1.50 + 1.51 +void 1.52 +gfxImageSurface::MakeInvalid() 1.53 +{ 1.54 + mSize = gfxIntSize(-1, -1); 1.55 + mData = nullptr; 1.56 + mStride = 0; 1.57 +} 1.58 + 1.59 +void 1.60 +gfxImageSurface::InitWithData(unsigned char *aData, const gfxIntSize& aSize, 1.61 + long aStride, gfxImageFormat aFormat) 1.62 +{ 1.63 + mSize = aSize; 1.64 + mOwnsData = false; 1.65 + mData = aData; 1.66 + mFormat = aFormat; 1.67 + mStride = aStride; 1.68 + 1.69 + if (!CheckSurfaceSize(aSize)) 1.70 + MakeInvalid(); 1.71 + 1.72 + cairo_surface_t *surface = 1.73 + cairo_image_surface_create_for_data((unsigned char*)mData, 1.74 + (cairo_format_t)(int)mFormat, 1.75 + mSize.width, 1.76 + mSize.height, 1.77 + mStride); 1.78 + 1.79 + // cairo_image_surface_create_for_data can return a 'null' surface 1.80 + // in out of memory conditions. The gfxASurface::Init call checks 1.81 + // the surface it receives to see if there is an error with the 1.82 + // surface and handles it appropriately. That is why there is 1.83 + // no check here. 1.84 + Init(surface); 1.85 +} 1.86 + 1.87 +static void* 1.88 +TryAllocAlignedBytes(size_t aSize) 1.89 +{ 1.90 + // Use fallible allocators here 1.91 +#if defined(HAVE_POSIX_MEMALIGN) 1.92 + void* ptr; 1.93 + // Try to align for fast alpha recovery. This should only help 1.94 + // cairo too, can't hurt. 1.95 + return moz_posix_memalign(&ptr, 1.96 + 1 << gfxAlphaRecovery::GoodAlignmentLog2(), 1.97 + aSize) ? 1.98 + nullptr : ptr; 1.99 +#else 1.100 + // Oh well, hope that luck is with us in the allocator 1.101 + return moz_malloc(aSize); 1.102 +#endif 1.103 +} 1.104 + 1.105 +gfxImageSurface::gfxImageSurface(const gfxIntSize& size, gfxImageFormat format, bool aClear) 1.106 + : mSize(size), mData(nullptr), mFormat(format) 1.107 +{ 1.108 + AllocateAndInit(0, 0, aClear); 1.109 +} 1.110 + 1.111 +void 1.112 +gfxImageSurface::AllocateAndInit(long aStride, int32_t aMinimalAllocation, 1.113 + bool aClear) 1.114 +{ 1.115 + // The callers should set mSize and mFormat. 1.116 + MOZ_ASSERT(!mData); 1.117 + mData = nullptr; 1.118 + mOwnsData = false; 1.119 + 1.120 + mStride = aStride > 0 ? aStride : ComputeStride(); 1.121 + if (aMinimalAllocation < mSize.height * mStride) 1.122 + aMinimalAllocation = mSize.height * mStride; 1.123 + 1.124 + if (!CheckSurfaceSize(mSize)) 1.125 + MakeInvalid(); 1.126 + 1.127 + // if we have a zero-sized surface, just leave mData nullptr 1.128 + if (mSize.height * mStride > 0) { 1.129 + 1.130 + // This can fail to allocate memory aligned as we requested, 1.131 + // or it can fail to allocate any memory at all. 1.132 + mData = (unsigned char *) TryAllocAlignedBytes(aMinimalAllocation); 1.133 + if (!mData) 1.134 + return; 1.135 + if (aClear) 1.136 + memset(mData, 0, aMinimalAllocation); 1.137 + } 1.138 + 1.139 + mOwnsData = true; 1.140 + 1.141 + cairo_surface_t *surface = 1.142 + cairo_image_surface_create_for_data((unsigned char*)mData, 1.143 + (cairo_format_t)(int)mFormat, 1.144 + mSize.width, 1.145 + mSize.height, 1.146 + mStride); 1.147 + 1.148 + Init(surface); 1.149 + 1.150 + if (mSurfaceValid) { 1.151 + RecordMemoryUsed(mSize.height * ComputeStride() + 1.152 + sizeof(gfxImageSurface)); 1.153 + } 1.154 +} 1.155 + 1.156 +gfxImageSurface::gfxImageSurface(const gfxIntSize& size, gfxImageFormat format, 1.157 + long aStride, int32_t aExtraBytes, bool aClear) 1.158 + : mSize(size), mData(nullptr), mFormat(format) 1.159 +{ 1.160 + AllocateAndInit(aStride, aExtraBytes, aClear); 1.161 +} 1.162 + 1.163 +gfxImageSurface::gfxImageSurface(cairo_surface_t *csurf) 1.164 +{ 1.165 + mSize.width = cairo_image_surface_get_width(csurf); 1.166 + mSize.height = cairo_image_surface_get_height(csurf); 1.167 + mData = cairo_image_surface_get_data(csurf); 1.168 + mFormat = (gfxImageFormat) cairo_image_surface_get_format(csurf); 1.169 + mOwnsData = false; 1.170 + mStride = cairo_image_surface_get_stride(csurf); 1.171 + 1.172 + Init(csurf, true); 1.173 +} 1.174 + 1.175 +gfxImageSurface::~gfxImageSurface() 1.176 +{ 1.177 + if (mOwnsData) 1.178 + free(mData); 1.179 +} 1.180 + 1.181 +/*static*/ long 1.182 +gfxImageSurface::ComputeStride(const gfxIntSize& aSize, gfxImageFormat aFormat) 1.183 +{ 1.184 + long stride; 1.185 + 1.186 + if (aFormat == gfxImageFormat::ARGB32) 1.187 + stride = aSize.width * 4; 1.188 + else if (aFormat == gfxImageFormat::RGB24) 1.189 + stride = aSize.width * 4; 1.190 + else if (aFormat == gfxImageFormat::RGB16_565) 1.191 + stride = aSize.width * 2; 1.192 + else if (aFormat == gfxImageFormat::A8) 1.193 + stride = aSize.width; 1.194 + else if (aFormat == gfxImageFormat::A1) { 1.195 + stride = (aSize.width + 7) / 8; 1.196 + } else { 1.197 + NS_WARNING("Unknown format specified to gfxImageSurface!"); 1.198 + stride = aSize.width * 4; 1.199 + } 1.200 + 1.201 + stride = ((stride + 3) / 4) * 4; 1.202 + 1.203 + return stride; 1.204 +} 1.205 + 1.206 +size_t 1.207 +gfxImageSurface::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const 1.208 +{ 1.209 + size_t n = gfxASurface::SizeOfExcludingThis(aMallocSizeOf); 1.210 + if (mOwnsData) { 1.211 + n += aMallocSizeOf(mData); 1.212 + } 1.213 + return n; 1.214 +} 1.215 + 1.216 +size_t 1.217 +gfxImageSurface::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const 1.218 +{ 1.219 + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1.220 +} 1.221 + 1.222 +bool 1.223 +gfxImageSurface::SizeOfIsMeasured() const 1.224 +{ 1.225 + return true; 1.226 +} 1.227 + 1.228 +// helper function for the CopyFrom methods 1.229 +static void 1.230 +CopyForStride(unsigned char* aDest, unsigned char* aSrc, const gfxIntSize& aSize, long aDestStride, long aSrcStride) 1.231 +{ 1.232 + if (aDestStride == aSrcStride) { 1.233 + memcpy (aDest, aSrc, aSrcStride * aSize.height); 1.234 + } else { 1.235 + int lineSize = std::min(aDestStride, aSrcStride); 1.236 + for (int i = 0; i < aSize.height; i++) { 1.237 + unsigned char* src = aSrc + aSrcStride * i; 1.238 + unsigned char* dst = aDest + aDestStride * i; 1.239 + 1.240 + memcpy (dst, src, lineSize); 1.241 + } 1.242 + } 1.243 +} 1.244 + 1.245 +// helper function for the CopyFrom methods 1.246 +static bool 1.247 +FormatsAreCompatible(gfxImageFormat a1, gfxImageFormat a2) 1.248 +{ 1.249 + if (a1 != a2 && 1.250 + !(a1 == gfxImageFormat::ARGB32 && 1.251 + a2 == gfxImageFormat::RGB24) && 1.252 + !(a1 == gfxImageFormat::RGB24 && 1.253 + a2 == gfxImageFormat::ARGB32)) { 1.254 + return false; 1.255 + } 1.256 + 1.257 + return true; 1.258 +} 1.259 + 1.260 +bool 1.261 +gfxImageSurface::CopyFrom (SourceSurface *aSurface) 1.262 +{ 1.263 + mozilla::RefPtr<DataSourceSurface> data = aSurface->GetDataSurface(); 1.264 + 1.265 + if (!data) { 1.266 + return false; 1.267 + } 1.268 + 1.269 + gfxIntSize size(data->GetSize().width, data->GetSize().height); 1.270 + if (size != mSize) { 1.271 + return false; 1.272 + } 1.273 + 1.274 + if (!FormatsAreCompatible(SurfaceFormatToImageFormat(aSurface->GetFormat()), 1.275 + mFormat)) { 1.276 + return false; 1.277 + } 1.278 + 1.279 + CopyForStride(mData, data->GetData(), size, mStride, data->Stride()); 1.280 + 1.281 + return true; 1.282 +} 1.283 + 1.284 + 1.285 +bool 1.286 +gfxImageSurface::CopyFrom(gfxImageSurface *other) 1.287 +{ 1.288 + if (other->mSize != mSize) { 1.289 + return false; 1.290 + } 1.291 + 1.292 + if (!FormatsAreCompatible(other->mFormat, mFormat)) { 1.293 + return false; 1.294 + } 1.295 + 1.296 + CopyForStride(mData, other->mData, mSize, mStride, other->mStride); 1.297 + 1.298 + return true; 1.299 +} 1.300 + 1.301 +bool 1.302 +gfxImageSurface::CopyTo(SourceSurface *aSurface) { 1.303 + mozilla::RefPtr<DataSourceSurface> data = aSurface->GetDataSurface(); 1.304 + 1.305 + if (!data) { 1.306 + return false; 1.307 + } 1.308 + 1.309 + gfxIntSize size(data->GetSize().width, data->GetSize().height); 1.310 + if (size != mSize) { 1.311 + return false; 1.312 + } 1.313 + 1.314 + if (!FormatsAreCompatible(SurfaceFormatToImageFormat(aSurface->GetFormat()), 1.315 + mFormat)) { 1.316 + return false; 1.317 + } 1.318 + 1.319 + CopyForStride(data->GetData(), mData, size, data->Stride(), mStride); 1.320 + 1.321 + return true; 1.322 +} 1.323 + 1.324 +TemporaryRef<DataSourceSurface> 1.325 +gfxImageSurface::CopyToB8G8R8A8DataSourceSurface() 1.326 +{ 1.327 + RefPtr<DataSourceSurface> dataSurface = 1.328 + Factory::CreateDataSourceSurface(IntSize(GetSize().width, GetSize().height), 1.329 + SurfaceFormat::B8G8R8A8); 1.330 + if (dataSurface) { 1.331 + CopyTo(dataSurface); 1.332 + } 1.333 + return dataSurface.forget(); 1.334 +} 1.335 + 1.336 +already_AddRefed<gfxSubimageSurface> 1.337 +gfxImageSurface::GetSubimage(const gfxRect& aRect) 1.338 +{ 1.339 + gfxRect r(aRect); 1.340 + r.Round(); 1.341 + MOZ_ASSERT(gfxRect(0, 0, mSize.width, mSize.height).Contains(r)); 1.342 + 1.343 + gfxImageFormat format = Format(); 1.344 + 1.345 + unsigned char* subData = Data() + 1.346 + (Stride() * (int)r.Y()) + 1.347 + (int)r.X() * gfxASurface::BytePerPixelFromFormat(Format()); 1.348 + 1.349 + if (format == gfxImageFormat::ARGB32 && 1.350 + GetOpaqueRect().Contains(aRect)) { 1.351 + format = gfxImageFormat::RGB24; 1.352 + } 1.353 + 1.354 + nsRefPtr<gfxSubimageSurface> image = 1.355 + new gfxSubimageSurface(this, subData, 1.356 + gfxIntSize((int)r.Width(), (int)r.Height()), 1.357 + format); 1.358 + 1.359 + return image.forget(); 1.360 +} 1.361 + 1.362 +gfxSubimageSurface::gfxSubimageSurface(gfxImageSurface* aParent, 1.363 + unsigned char* aData, 1.364 + const gfxIntSize& aSize, 1.365 + gfxImageFormat aFormat) 1.366 + : gfxImageSurface(aData, aSize, aParent->Stride(), aFormat) 1.367 + , mParent(aParent) 1.368 +{ 1.369 +} 1.370 + 1.371 +already_AddRefed<gfxImageSurface> 1.372 +gfxImageSurface::GetAsImageSurface() 1.373 +{ 1.374 + nsRefPtr<gfxImageSurface> surface = this; 1.375 + return surface.forget(); 1.376 +} 1.377 + 1.378 +void 1.379 +gfxImageSurface::MovePixels(const nsIntRect& aSourceRect, 1.380 + const nsIntPoint& aDestTopLeft) 1.381 +{ 1.382 + const nsIntRect bounds(0, 0, mSize.width, mSize.height); 1.383 + nsIntPoint offset = aDestTopLeft - aSourceRect.TopLeft(); 1.384 + nsIntRect clippedSource = aSourceRect; 1.385 + clippedSource.IntersectRect(clippedSource, bounds); 1.386 + nsIntRect clippedDest = clippedSource + offset; 1.387 + clippedDest.IntersectRect(clippedDest, bounds); 1.388 + const nsIntRect dest = clippedDest; 1.389 + const nsIntRect source = dest - offset; 1.390 + // NB: this relies on IntersectRect() and operator+/- preserving 1.391 + // x/y for empty rectangles 1.392 + NS_ABORT_IF_FALSE(bounds.Contains(dest) && bounds.Contains(source) && 1.393 + aSourceRect.Contains(source) && 1.394 + nsIntRect(aDestTopLeft, aSourceRect.Size()).Contains(dest) && 1.395 + source.Size() == dest.Size() && 1.396 + offset == (dest.TopLeft() - source.TopLeft()), 1.397 + "Messed up clipping, crash or corruption will follow"); 1.398 + if (source.IsEmpty() || source.IsEqualInterior(dest)) { 1.399 + return; 1.400 + } 1.401 + 1.402 + long naturalStride = ComputeStride(mSize, mFormat); 1.403 + if (mStride == naturalStride && dest.width == bounds.width) { 1.404 + // Fast path: this is a vertical shift of some rows in a 1.405 + // "normal" image surface. We can directly memmove and 1.406 + // hopefully stay in SIMD land. 1.407 + unsigned char* dst = mData + dest.y * mStride; 1.408 + const unsigned char* src = mData + source.y * mStride; 1.409 + size_t nBytes = dest.height * mStride; 1.410 + memmove(dst, src, nBytes); 1.411 + return; 1.412 + } 1.413 + 1.414 + // Slow(er) path: have to move row-by-row. 1.415 + const int32_t bpp = BytePerPixelFromFormat(mFormat); 1.416 + const size_t nRowBytes = dest.width * bpp; 1.417 + // dstRow points at the first pixel within the current destination 1.418 + // row, and similarly for srcRow. endSrcRow is one row beyond the 1.419 + // last row we need to copy. stride is either +mStride or 1.420 + // -mStride, depending on which direction we're copying. 1.421 + unsigned char* dstRow; 1.422 + unsigned char* srcRow; 1.423 + unsigned char* endSrcRow; // NB: this may point outside the image 1.424 + long stride; 1.425 + if (dest.y > source.y) { 1.426 + // We're copying down from source to dest, so walk backwards 1.427 + // starting from the last rows to avoid stomping pixels we 1.428 + // need. 1.429 + stride = -mStride; 1.430 + dstRow = mData + dest.x * bpp + (dest.YMost() - 1) * mStride; 1.431 + srcRow = mData + source.x * bpp + (source.YMost() - 1) * mStride; 1.432 + endSrcRow = mData + source.x * bpp + (source.y - 1) * mStride; 1.433 + } else { 1.434 + stride = mStride; 1.435 + dstRow = mData + dest.x * bpp + dest.y * mStride; 1.436 + srcRow = mData + source.x * bpp + source.y * mStride; 1.437 + endSrcRow = mData + source.x * bpp + source.YMost() * mStride; 1.438 + } 1.439 + 1.440 + for (; srcRow != endSrcRow; dstRow += stride, srcRow += stride) { 1.441 + memmove(dstRow, srcRow, nRowBytes); 1.442 + } 1.443 +}