gfx/thebes/gfxImageSurface.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:a17390918981
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 }

mercurial