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