gfxImageSurface.cpp (9840B)
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 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 6 #include "mozilla/MemoryReporting.h" 7 #if defined(HAVE_POSIX_MEMALIGN) 8 # include "gfxAlphaRecovery.h" 9 #endif 10 #include "gfxImageSurface.h" 11 12 #include "cairo.h" 13 #include "mozilla/gfx/2D.h" 14 #include "mozilla/gfx/HelpersCairo.h" 15 #include "gfx2DGlue.h" 16 #include <algorithm> 17 18 using namespace mozilla; 19 using namespace mozilla::gfx; 20 21 gfxImageSurface::gfxImageSurface() 22 : mSize(0, 0), 23 mOwnsData(false), 24 mData(nullptr), 25 mFormat(SurfaceFormat::UNKNOWN), 26 mStride(0) {} 27 28 void gfxImageSurface::InitFromSurface(cairo_surface_t* csurf) { 29 if (!csurf || cairo_surface_status(csurf)) { 30 MakeInvalid(); 31 return; 32 } 33 34 mSize.width = cairo_image_surface_get_width(csurf); 35 mSize.height = cairo_image_surface_get_height(csurf); 36 mData = cairo_image_surface_get_data(csurf); 37 mFormat = CairoFormatToGfxFormat(cairo_image_surface_get_format(csurf)); 38 mOwnsData = false; 39 mStride = cairo_image_surface_get_stride(csurf); 40 41 Init(csurf, true); 42 } 43 44 gfxImageSurface::gfxImageSurface(unsigned char* aData, const IntSize& aSize, 45 long aStride, gfxImageFormat aFormat) { 46 InitWithData(aData, aSize, aStride, aFormat); 47 } 48 49 void gfxImageSurface::MakeInvalid() { 50 mSize = IntSize(-1, -1); 51 mData = nullptr; 52 mStride = 0; 53 } 54 55 void gfxImageSurface::InitWithData(unsigned char* aData, const IntSize& aSize, 56 long aStride, gfxImageFormat aFormat) { 57 mSize = aSize; 58 mOwnsData = false; 59 mData = aData; 60 mFormat = aFormat; 61 mStride = aStride; 62 63 if (!Factory::CheckSurfaceSize(aSize)) MakeInvalid(); 64 65 cairo_format_t cformat = GfxFormatToCairoFormat(mFormat); 66 cairo_surface_t* surface = cairo_image_surface_create_for_data( 67 (unsigned char*)mData, cformat, mSize.width, mSize.height, mStride); 68 69 // cairo_image_surface_create_for_data can return a 'null' surface 70 // in out of memory conditions. The gfxASurface::Init call checks 71 // the surface it receives to see if there is an error with the 72 // surface and handles it appropriately. That is why there is 73 // no check here. 74 Init(surface); 75 } 76 77 static void* TryAllocAlignedBytes(size_t aSize) { 78 // Use fallible allocators here 79 #if defined(HAVE_POSIX_MEMALIGN) 80 void* ptr; 81 // Try to align for fast alpha recovery. This should only help 82 // cairo too, can't hurt. 83 return posix_memalign(&ptr, 1 << gfxAlphaRecovery::GoodAlignmentLog2(), aSize) 84 ? nullptr 85 : ptr; 86 #else 87 // Oh well, hope that luck is with us in the allocator 88 return malloc(aSize); 89 #endif 90 } 91 92 gfxImageSurface::gfxImageSurface(const IntSize& size, gfxImageFormat format, 93 bool aClear) 94 : mSize(size), mData(nullptr), mFormat(format) { 95 AllocateAndInit(0, 0, aClear); 96 } 97 98 void gfxImageSurface::AllocateAndInit(long aStride, int32_t aMinimalAllocation, 99 bool aClear) { 100 // The callers should set mSize and mFormat. 101 MOZ_ASSERT(!mData); 102 mData = nullptr; 103 mOwnsData = false; 104 105 mStride = aStride > 0 ? aStride : ComputeStride(); 106 if (aMinimalAllocation < mSize.height * mStride) 107 aMinimalAllocation = mSize.height * mStride; 108 109 if (!Factory::CheckSurfaceSize(mSize)) MakeInvalid(); 110 111 // if we have a zero-sized surface, just leave mData nullptr 112 if (mSize.height * mStride > 0) { 113 // This can fail to allocate memory aligned as we requested, 114 // or it can fail to allocate any memory at all. 115 mData = (unsigned char*)TryAllocAlignedBytes(aMinimalAllocation); 116 if (!mData) return; 117 if (aClear) memset(mData, 0, aMinimalAllocation); 118 } 119 120 mOwnsData = true; 121 122 cairo_format_t cformat = GfxFormatToCairoFormat(mFormat); 123 cairo_surface_t* surface = cairo_image_surface_create_for_data( 124 (unsigned char*)mData, cformat, mSize.width, mSize.height, mStride); 125 126 Init(surface); 127 128 if (mSurfaceValid) { 129 RecordMemoryUsed(mSize.height * ComputeStride() + sizeof(gfxImageSurface)); 130 } 131 } 132 133 gfxImageSurface::gfxImageSurface(const IntSize& size, gfxImageFormat format, 134 long aStride, int32_t aExtraBytes, bool aClear) 135 : mSize(size), mData(nullptr), mFormat(format) { 136 AllocateAndInit(aStride, aExtraBytes, aClear); 137 } 138 139 gfxImageSurface::gfxImageSurface(cairo_surface_t* csurf) { 140 mSize.width = cairo_image_surface_get_width(csurf); 141 mSize.height = cairo_image_surface_get_height(csurf); 142 mData = cairo_image_surface_get_data(csurf); 143 mFormat = CairoFormatToGfxFormat(cairo_image_surface_get_format(csurf)); 144 mOwnsData = false; 145 mStride = cairo_image_surface_get_stride(csurf); 146 147 Init(csurf, true); 148 } 149 150 gfxImageSurface::~gfxImageSurface() { 151 if (mOwnsData) free(mData); 152 } 153 154 /*static*/ 155 long gfxImageSurface::ComputeStride(const IntSize& aSize, 156 gfxImageFormat aFormat) { 157 long stride; 158 159 if (aFormat == SurfaceFormat::A8R8G8B8_UINT32) 160 stride = aSize.width * 4; 161 else if (aFormat == SurfaceFormat::X8R8G8B8_UINT32) 162 stride = aSize.width * 4; 163 else if (aFormat == SurfaceFormat::R5G6B5_UINT16) 164 stride = aSize.width * 2; 165 else if (aFormat == SurfaceFormat::A8) 166 stride = aSize.width; 167 else { 168 NS_WARNING("Unknown format specified to gfxImageSurface!"); 169 stride = aSize.width * 4; 170 } 171 172 stride = ((stride + 3) / 4) * 4; 173 174 return stride; 175 } 176 177 size_t gfxImageSurface::SizeOfExcludingThis( 178 mozilla::MallocSizeOf aMallocSizeOf) const { 179 size_t n = gfxASurface::SizeOfExcludingThis(aMallocSizeOf); 180 if (mOwnsData) { 181 n += aMallocSizeOf(mData); 182 } 183 return n; 184 } 185 186 size_t gfxImageSurface::SizeOfIncludingThis( 187 mozilla::MallocSizeOf aMallocSizeOf) const { 188 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 189 } 190 191 bool gfxImageSurface::SizeOfIsMeasured() const { return true; } 192 193 // helper function for the CopyFrom methods 194 static void CopyForStride(unsigned char* aDest, unsigned char* aSrc, 195 const IntSize& aSize, long aDestStride, 196 long aSrcStride) { 197 if (aDestStride == aSrcStride) { 198 memcpy(aDest, aSrc, aSrcStride * aSize.height); 199 } else { 200 int lineSize = std::min(aDestStride, aSrcStride); 201 for (int i = 0; i < aSize.height; i++) { 202 unsigned char* src = aSrc + aSrcStride * i; 203 unsigned char* dst = aDest + aDestStride * i; 204 205 memcpy(dst, src, lineSize); 206 } 207 } 208 } 209 210 // helper function for the CopyFrom methods 211 static bool FormatsAreCompatible(gfxImageFormat a1, gfxImageFormat a2) { 212 if (a1 != a2 && 213 !(a1 == SurfaceFormat::A8R8G8B8_UINT32 && 214 a2 == SurfaceFormat::X8R8G8B8_UINT32) && 215 !(a1 == SurfaceFormat::X8R8G8B8_UINT32 && 216 a2 == SurfaceFormat::A8R8G8B8_UINT32)) { 217 return false; 218 } 219 220 return true; 221 } 222 223 bool gfxImageSurface::CopyFrom(SourceSurface* aSurface) { 224 RefPtr<DataSourceSurface> data = aSurface->GetDataSurface(); 225 226 if (!data) { 227 return false; 228 } 229 230 IntSize size(data->GetSize().width, data->GetSize().height); 231 if (size != mSize) { 232 return false; 233 } 234 235 if (!FormatsAreCompatible(SurfaceFormatToImageFormat(aSurface->GetFormat()), 236 mFormat)) { 237 return false; 238 } 239 240 DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ); 241 CopyForStride(mData, map.GetData(), size, mStride, map.GetStride()); 242 243 return true; 244 } 245 246 bool gfxImageSurface::CopyFrom(gfxImageSurface* other) { 247 if (other->mSize != mSize) { 248 return false; 249 } 250 251 if (!FormatsAreCompatible(other->mFormat, mFormat)) { 252 return false; 253 } 254 255 CopyForStride(mData, other->mData, mSize, mStride, other->mStride); 256 257 return true; 258 } 259 260 bool gfxImageSurface::CopyTo(SourceSurface* aSurface) { 261 RefPtr<DataSourceSurface> data = aSurface->GetDataSurface(); 262 263 if (!data) { 264 return false; 265 } 266 267 IntSize size(data->GetSize().width, data->GetSize().height); 268 if (size != mSize) { 269 return false; 270 } 271 272 if (!FormatsAreCompatible(SurfaceFormatToImageFormat(aSurface->GetFormat()), 273 mFormat)) { 274 return false; 275 } 276 277 DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ_WRITE); 278 CopyForStride(map.GetData(), mData, size, map.GetStride(), mStride); 279 280 return true; 281 } 282 283 already_AddRefed<DataSourceSurface> 284 gfxImageSurface::CopyToB8G8R8A8DataSourceSurface() { 285 RefPtr<DataSourceSurface> dataSurface = Factory::CreateDataSourceSurface( 286 IntSize(GetSize().width, GetSize().height), SurfaceFormat::B8G8R8A8); 287 if (dataSurface) { 288 CopyTo(dataSurface); 289 } 290 return dataSurface.forget(); 291 } 292 293 already_AddRefed<gfxSubimageSurface> gfxImageSurface::GetSubimage( 294 const gfxRect& aRect) { 295 gfxRect r(aRect); 296 r.Round(); 297 MOZ_ASSERT(gfxRect(0, 0, mSize.width, mSize.height).Contains(r)); 298 299 gfxImageFormat format = Format(); 300 301 unsigned char* subData = 302 Data() + (Stride() * (int)r.Y()) + 303 (int)r.X() * gfxASurface::BytePerPixelFromFormat(Format()); 304 305 if (format == SurfaceFormat::A8R8G8B8_UINT32 && 306 GetOpaqueRect().Contains(aRect)) { 307 format = SurfaceFormat::X8R8G8B8_UINT32; 308 } 309 310 RefPtr<gfxSubimageSurface> image = new gfxSubimageSurface( 311 this, subData, IntSize((int)r.Width(), (int)r.Height()), format); 312 313 return image.forget(); 314 } 315 316 gfxSubimageSurface::gfxSubimageSurface(gfxImageSurface* aParent, 317 unsigned char* aData, 318 const IntSize& aSize, 319 gfxImageFormat aFormat) 320 : gfxImageSurface(aData, aSize, aParent->Stride(), aFormat), 321 mParent(aParent) {} 322 323 already_AddRefed<gfxImageSurface> gfxImageSurface::GetAsImageSurface() { 324 RefPtr<gfxImageSurface> surface = this; 325 return surface.forget(); 326 }