|
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
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 |
|
7 #include "mozilla/MemoryReporting.h" |
|
8 #if defined(HAVE_POSIX_MEMALIGN) |
|
9 #include "gfxAlphaRecovery.h" |
|
10 #endif |
|
11 #include "gfxImageSurface.h" |
|
12 |
|
13 #include "cairo.h" |
|
14 #include "mozilla/gfx/2D.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 mFormat(gfxImageFormat::Unknown), |
|
25 mStride(0) |
|
26 { |
|
27 } |
|
28 |
|
29 void |
|
30 gfxImageSurface::InitFromSurface(cairo_surface_t *csurf) |
|
31 { |
|
32 mSize.width = cairo_image_surface_get_width(csurf); |
|
33 mSize.height = cairo_image_surface_get_height(csurf); |
|
34 mData = cairo_image_surface_get_data(csurf); |
|
35 mFormat = (gfxImageFormat) cairo_image_surface_get_format(csurf); |
|
36 mOwnsData = false; |
|
37 mStride = cairo_image_surface_get_stride(csurf); |
|
38 |
|
39 Init(csurf, true); |
|
40 } |
|
41 |
|
42 gfxImageSurface::gfxImageSurface(unsigned char *aData, const gfxIntSize& aSize, |
|
43 long aStride, gfxImageFormat aFormat) |
|
44 { |
|
45 InitWithData(aData, aSize, aStride, aFormat); |
|
46 } |
|
47 |
|
48 void |
|
49 gfxImageSurface::MakeInvalid() |
|
50 { |
|
51 mSize = gfxIntSize(-1, -1); |
|
52 mData = nullptr; |
|
53 mStride = 0; |
|
54 } |
|
55 |
|
56 void |
|
57 gfxImageSurface::InitWithData(unsigned char *aData, const gfxIntSize& aSize, |
|
58 long aStride, gfxImageFormat aFormat) |
|
59 { |
|
60 mSize = aSize; |
|
61 mOwnsData = false; |
|
62 mData = aData; |
|
63 mFormat = aFormat; |
|
64 mStride = aStride; |
|
65 |
|
66 if (!CheckSurfaceSize(aSize)) |
|
67 MakeInvalid(); |
|
68 |
|
69 cairo_surface_t *surface = |
|
70 cairo_image_surface_create_for_data((unsigned char*)mData, |
|
71 (cairo_format_t)(int)mFormat, |
|
72 mSize.width, |
|
73 mSize.height, |
|
74 mStride); |
|
75 |
|
76 // cairo_image_surface_create_for_data can return a 'null' surface |
|
77 // in out of memory conditions. The gfxASurface::Init call checks |
|
78 // the surface it receives to see if there is an error with the |
|
79 // surface and handles it appropriately. That is why there is |
|
80 // no check here. |
|
81 Init(surface); |
|
82 } |
|
83 |
|
84 static void* |
|
85 TryAllocAlignedBytes(size_t aSize) |
|
86 { |
|
87 // Use fallible allocators here |
|
88 #if defined(HAVE_POSIX_MEMALIGN) |
|
89 void* ptr; |
|
90 // Try to align for fast alpha recovery. This should only help |
|
91 // cairo too, can't hurt. |
|
92 return moz_posix_memalign(&ptr, |
|
93 1 << gfxAlphaRecovery::GoodAlignmentLog2(), |
|
94 aSize) ? |
|
95 nullptr : ptr; |
|
96 #else |
|
97 // Oh well, hope that luck is with us in the allocator |
|
98 return moz_malloc(aSize); |
|
99 #endif |
|
100 } |
|
101 |
|
102 gfxImageSurface::gfxImageSurface(const gfxIntSize& size, gfxImageFormat format, bool aClear) |
|
103 : mSize(size), mData(nullptr), mFormat(format) |
|
104 { |
|
105 AllocateAndInit(0, 0, aClear); |
|
106 } |
|
107 |
|
108 void |
|
109 gfxImageSurface::AllocateAndInit(long aStride, int32_t aMinimalAllocation, |
|
110 bool aClear) |
|
111 { |
|
112 // The callers should set mSize and mFormat. |
|
113 MOZ_ASSERT(!mData); |
|
114 mData = nullptr; |
|
115 mOwnsData = false; |
|
116 |
|
117 mStride = aStride > 0 ? aStride : ComputeStride(); |
|
118 if (aMinimalAllocation < mSize.height * mStride) |
|
119 aMinimalAllocation = mSize.height * mStride; |
|
120 |
|
121 if (!CheckSurfaceSize(mSize)) |
|
122 MakeInvalid(); |
|
123 |
|
124 // if we have a zero-sized surface, just leave mData nullptr |
|
125 if (mSize.height * mStride > 0) { |
|
126 |
|
127 // This can fail to allocate memory aligned as we requested, |
|
128 // or it can fail to allocate any memory at all. |
|
129 mData = (unsigned char *) TryAllocAlignedBytes(aMinimalAllocation); |
|
130 if (!mData) |
|
131 return; |
|
132 if (aClear) |
|
133 memset(mData, 0, aMinimalAllocation); |
|
134 } |
|
135 |
|
136 mOwnsData = true; |
|
137 |
|
138 cairo_surface_t *surface = |
|
139 cairo_image_surface_create_for_data((unsigned char*)mData, |
|
140 (cairo_format_t)(int)mFormat, |
|
141 mSize.width, |
|
142 mSize.height, |
|
143 mStride); |
|
144 |
|
145 Init(surface); |
|
146 |
|
147 if (mSurfaceValid) { |
|
148 RecordMemoryUsed(mSize.height * ComputeStride() + |
|
149 sizeof(gfxImageSurface)); |
|
150 } |
|
151 } |
|
152 |
|
153 gfxImageSurface::gfxImageSurface(const gfxIntSize& size, gfxImageFormat format, |
|
154 long aStride, int32_t aExtraBytes, bool aClear) |
|
155 : mSize(size), mData(nullptr), mFormat(format) |
|
156 { |
|
157 AllocateAndInit(aStride, aExtraBytes, aClear); |
|
158 } |
|
159 |
|
160 gfxImageSurface::gfxImageSurface(cairo_surface_t *csurf) |
|
161 { |
|
162 mSize.width = cairo_image_surface_get_width(csurf); |
|
163 mSize.height = cairo_image_surface_get_height(csurf); |
|
164 mData = cairo_image_surface_get_data(csurf); |
|
165 mFormat = (gfxImageFormat) cairo_image_surface_get_format(csurf); |
|
166 mOwnsData = false; |
|
167 mStride = cairo_image_surface_get_stride(csurf); |
|
168 |
|
169 Init(csurf, true); |
|
170 } |
|
171 |
|
172 gfxImageSurface::~gfxImageSurface() |
|
173 { |
|
174 if (mOwnsData) |
|
175 free(mData); |
|
176 } |
|
177 |
|
178 /*static*/ long |
|
179 gfxImageSurface::ComputeStride(const gfxIntSize& aSize, gfxImageFormat aFormat) |
|
180 { |
|
181 long stride; |
|
182 |
|
183 if (aFormat == gfxImageFormat::ARGB32) |
|
184 stride = aSize.width * 4; |
|
185 else if (aFormat == gfxImageFormat::RGB24) |
|
186 stride = aSize.width * 4; |
|
187 else if (aFormat == gfxImageFormat::RGB16_565) |
|
188 stride = aSize.width * 2; |
|
189 else if (aFormat == gfxImageFormat::A8) |
|
190 stride = aSize.width; |
|
191 else if (aFormat == gfxImageFormat::A1) { |
|
192 stride = (aSize.width + 7) / 8; |
|
193 } else { |
|
194 NS_WARNING("Unknown format specified to gfxImageSurface!"); |
|
195 stride = aSize.width * 4; |
|
196 } |
|
197 |
|
198 stride = ((stride + 3) / 4) * 4; |
|
199 |
|
200 return stride; |
|
201 } |
|
202 |
|
203 size_t |
|
204 gfxImageSurface::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
|
205 { |
|
206 size_t n = gfxASurface::SizeOfExcludingThis(aMallocSizeOf); |
|
207 if (mOwnsData) { |
|
208 n += aMallocSizeOf(mData); |
|
209 } |
|
210 return n; |
|
211 } |
|
212 |
|
213 size_t |
|
214 gfxImageSurface::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
|
215 { |
|
216 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
|
217 } |
|
218 |
|
219 bool |
|
220 gfxImageSurface::SizeOfIsMeasured() const |
|
221 { |
|
222 return true; |
|
223 } |
|
224 |
|
225 // helper function for the CopyFrom methods |
|
226 static void |
|
227 CopyForStride(unsigned char* aDest, unsigned char* aSrc, const gfxIntSize& aSize, long aDestStride, long aSrcStride) |
|
228 { |
|
229 if (aDestStride == aSrcStride) { |
|
230 memcpy (aDest, aSrc, aSrcStride * aSize.height); |
|
231 } else { |
|
232 int lineSize = std::min(aDestStride, aSrcStride); |
|
233 for (int i = 0; i < aSize.height; i++) { |
|
234 unsigned char* src = aSrc + aSrcStride * i; |
|
235 unsigned char* dst = aDest + aDestStride * i; |
|
236 |
|
237 memcpy (dst, src, lineSize); |
|
238 } |
|
239 } |
|
240 } |
|
241 |
|
242 // helper function for the CopyFrom methods |
|
243 static bool |
|
244 FormatsAreCompatible(gfxImageFormat a1, gfxImageFormat a2) |
|
245 { |
|
246 if (a1 != a2 && |
|
247 !(a1 == gfxImageFormat::ARGB32 && |
|
248 a2 == gfxImageFormat::RGB24) && |
|
249 !(a1 == gfxImageFormat::RGB24 && |
|
250 a2 == gfxImageFormat::ARGB32)) { |
|
251 return false; |
|
252 } |
|
253 |
|
254 return true; |
|
255 } |
|
256 |
|
257 bool |
|
258 gfxImageSurface::CopyFrom (SourceSurface *aSurface) |
|
259 { |
|
260 mozilla::RefPtr<DataSourceSurface> data = aSurface->GetDataSurface(); |
|
261 |
|
262 if (!data) { |
|
263 return false; |
|
264 } |
|
265 |
|
266 gfxIntSize size(data->GetSize().width, data->GetSize().height); |
|
267 if (size != mSize) { |
|
268 return false; |
|
269 } |
|
270 |
|
271 if (!FormatsAreCompatible(SurfaceFormatToImageFormat(aSurface->GetFormat()), |
|
272 mFormat)) { |
|
273 return false; |
|
274 } |
|
275 |
|
276 CopyForStride(mData, data->GetData(), size, mStride, data->Stride()); |
|
277 |
|
278 return true; |
|
279 } |
|
280 |
|
281 |
|
282 bool |
|
283 gfxImageSurface::CopyFrom(gfxImageSurface *other) |
|
284 { |
|
285 if (other->mSize != mSize) { |
|
286 return false; |
|
287 } |
|
288 |
|
289 if (!FormatsAreCompatible(other->mFormat, mFormat)) { |
|
290 return false; |
|
291 } |
|
292 |
|
293 CopyForStride(mData, other->mData, mSize, mStride, other->mStride); |
|
294 |
|
295 return true; |
|
296 } |
|
297 |
|
298 bool |
|
299 gfxImageSurface::CopyTo(SourceSurface *aSurface) { |
|
300 mozilla::RefPtr<DataSourceSurface> data = aSurface->GetDataSurface(); |
|
301 |
|
302 if (!data) { |
|
303 return false; |
|
304 } |
|
305 |
|
306 gfxIntSize size(data->GetSize().width, data->GetSize().height); |
|
307 if (size != mSize) { |
|
308 return false; |
|
309 } |
|
310 |
|
311 if (!FormatsAreCompatible(SurfaceFormatToImageFormat(aSurface->GetFormat()), |
|
312 mFormat)) { |
|
313 return false; |
|
314 } |
|
315 |
|
316 CopyForStride(data->GetData(), mData, size, data->Stride(), mStride); |
|
317 |
|
318 return true; |
|
319 } |
|
320 |
|
321 TemporaryRef<DataSourceSurface> |
|
322 gfxImageSurface::CopyToB8G8R8A8DataSourceSurface() |
|
323 { |
|
324 RefPtr<DataSourceSurface> dataSurface = |
|
325 Factory::CreateDataSourceSurface(IntSize(GetSize().width, GetSize().height), |
|
326 SurfaceFormat::B8G8R8A8); |
|
327 if (dataSurface) { |
|
328 CopyTo(dataSurface); |
|
329 } |
|
330 return dataSurface.forget(); |
|
331 } |
|
332 |
|
333 already_AddRefed<gfxSubimageSurface> |
|
334 gfxImageSurface::GetSubimage(const gfxRect& aRect) |
|
335 { |
|
336 gfxRect r(aRect); |
|
337 r.Round(); |
|
338 MOZ_ASSERT(gfxRect(0, 0, mSize.width, mSize.height).Contains(r)); |
|
339 |
|
340 gfxImageFormat format = Format(); |
|
341 |
|
342 unsigned char* subData = Data() + |
|
343 (Stride() * (int)r.Y()) + |
|
344 (int)r.X() * gfxASurface::BytePerPixelFromFormat(Format()); |
|
345 |
|
346 if (format == gfxImageFormat::ARGB32 && |
|
347 GetOpaqueRect().Contains(aRect)) { |
|
348 format = gfxImageFormat::RGB24; |
|
349 } |
|
350 |
|
351 nsRefPtr<gfxSubimageSurface> image = |
|
352 new gfxSubimageSurface(this, subData, |
|
353 gfxIntSize((int)r.Width(), (int)r.Height()), |
|
354 format); |
|
355 |
|
356 return image.forget(); |
|
357 } |
|
358 |
|
359 gfxSubimageSurface::gfxSubimageSurface(gfxImageSurface* aParent, |
|
360 unsigned char* aData, |
|
361 const gfxIntSize& aSize, |
|
362 gfxImageFormat aFormat) |
|
363 : gfxImageSurface(aData, aSize, aParent->Stride(), aFormat) |
|
364 , mParent(aParent) |
|
365 { |
|
366 } |
|
367 |
|
368 already_AddRefed<gfxImageSurface> |
|
369 gfxImageSurface::GetAsImageSurface() |
|
370 { |
|
371 nsRefPtr<gfxImageSurface> surface = this; |
|
372 return surface.forget(); |
|
373 } |
|
374 |
|
375 void |
|
376 gfxImageSurface::MovePixels(const nsIntRect& aSourceRect, |
|
377 const nsIntPoint& aDestTopLeft) |
|
378 { |
|
379 const nsIntRect bounds(0, 0, mSize.width, mSize.height); |
|
380 nsIntPoint offset = aDestTopLeft - aSourceRect.TopLeft(); |
|
381 nsIntRect clippedSource = aSourceRect; |
|
382 clippedSource.IntersectRect(clippedSource, bounds); |
|
383 nsIntRect clippedDest = clippedSource + offset; |
|
384 clippedDest.IntersectRect(clippedDest, bounds); |
|
385 const nsIntRect dest = clippedDest; |
|
386 const nsIntRect source = dest - offset; |
|
387 // NB: this relies on IntersectRect() and operator+/- preserving |
|
388 // x/y for empty rectangles |
|
389 NS_ABORT_IF_FALSE(bounds.Contains(dest) && bounds.Contains(source) && |
|
390 aSourceRect.Contains(source) && |
|
391 nsIntRect(aDestTopLeft, aSourceRect.Size()).Contains(dest) && |
|
392 source.Size() == dest.Size() && |
|
393 offset == (dest.TopLeft() - source.TopLeft()), |
|
394 "Messed up clipping, crash or corruption will follow"); |
|
395 if (source.IsEmpty() || source.IsEqualInterior(dest)) { |
|
396 return; |
|
397 } |
|
398 |
|
399 long naturalStride = ComputeStride(mSize, mFormat); |
|
400 if (mStride == naturalStride && dest.width == bounds.width) { |
|
401 // Fast path: this is a vertical shift of some rows in a |
|
402 // "normal" image surface. We can directly memmove and |
|
403 // hopefully stay in SIMD land. |
|
404 unsigned char* dst = mData + dest.y * mStride; |
|
405 const unsigned char* src = mData + source.y * mStride; |
|
406 size_t nBytes = dest.height * mStride; |
|
407 memmove(dst, src, nBytes); |
|
408 return; |
|
409 } |
|
410 |
|
411 // Slow(er) path: have to move row-by-row. |
|
412 const int32_t bpp = BytePerPixelFromFormat(mFormat); |
|
413 const size_t nRowBytes = dest.width * bpp; |
|
414 // dstRow points at the first pixel within the current destination |
|
415 // row, and similarly for srcRow. endSrcRow is one row beyond the |
|
416 // last row we need to copy. stride is either +mStride or |
|
417 // -mStride, depending on which direction we're copying. |
|
418 unsigned char* dstRow; |
|
419 unsigned char* srcRow; |
|
420 unsigned char* endSrcRow; // NB: this may point outside the image |
|
421 long stride; |
|
422 if (dest.y > source.y) { |
|
423 // We're copying down from source to dest, so walk backwards |
|
424 // starting from the last rows to avoid stomping pixels we |
|
425 // need. |
|
426 stride = -mStride; |
|
427 dstRow = mData + dest.x * bpp + (dest.YMost() - 1) * mStride; |
|
428 srcRow = mData + source.x * bpp + (source.YMost() - 1) * mStride; |
|
429 endSrcRow = mData + source.x * bpp + (source.y - 1) * mStride; |
|
430 } else { |
|
431 stride = mStride; |
|
432 dstRow = mData + dest.x * bpp + dest.y * mStride; |
|
433 srcRow = mData + source.x * bpp + source.y * mStride; |
|
434 endSrcRow = mData + source.x * bpp + source.YMost() * mStride; |
|
435 } |
|
436 |
|
437 for (; srcRow != endSrcRow; dstRow += stride, srcRow += stride) { |
|
438 memmove(dstRow, srcRow, nRowBytes); |
|
439 } |
|
440 } |