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