1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Corporation code.
16 : *
17 : * The Initial Developer of the Original Code is Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2009
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Bas Schouten <bschouten@mozilla.com>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "mozilla/ipc/Shmem.h"
39 : #include "mozilla/ipc/CrossProcessMutex.h"
40 : #include "ImageLayers.h"
41 : #include "gfxImageSurface.h"
42 : #include "yuv_convert.h"
43 :
44 : #ifdef XP_MACOSX
45 : #include "nsCoreAnimationSupport.h"
46 : #endif
47 :
48 : using namespace mozilla::ipc;
49 :
50 : namespace mozilla {
51 : namespace layers {
52 :
53 : already_AddRefed<Image>
54 0 : ImageFactory::CreateImage(const Image::Format *aFormats,
55 : PRUint32 aNumFormats,
56 : const gfxIntSize &,
57 : BufferRecycleBin *aRecycleBin)
58 : {
59 0 : if (!aNumFormats) {
60 0 : return nsnull;
61 : }
62 0 : nsRefPtr<Image> img;
63 0 : if (FormatInList(aFormats, aNumFormats, Image::PLANAR_YCBCR)) {
64 0 : img = new PlanarYCbCrImage(aRecycleBin);
65 0 : } else if (FormatInList(aFormats, aNumFormats, Image::CAIRO_SURFACE)) {
66 0 : img = new CairoImage();
67 : #ifdef XP_MACOSX
68 : } else if (FormatInList(aFormats, aNumFormats, Image::MAC_IO_SURFACE)) {
69 : img = new MacIOSurfaceImage();
70 : #endif
71 : }
72 0 : return img.forget();
73 : }
74 :
75 0 : BufferRecycleBin::BufferRecycleBin()
76 0 : : mLock("mozilla.layers.BufferRecycleBin.mLock")
77 : {
78 0 : }
79 :
80 : void
81 0 : BufferRecycleBin::RecycleBuffer(PRUint8* aBuffer, PRUint32 aSize)
82 : {
83 0 : MutexAutoLock lock(mLock);
84 :
85 0 : if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) {
86 0 : mRecycledBuffers.Clear();
87 : }
88 0 : mRecycledBufferSize = aSize;
89 0 : mRecycledBuffers.AppendElement(aBuffer);
90 0 : }
91 :
92 : PRUint8*
93 0 : BufferRecycleBin::GetBuffer(PRUint32 aSize)
94 : {
95 0 : MutexAutoLock lock(mLock);
96 :
97 0 : if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize)
98 0 : return new PRUint8[aSize];
99 :
100 0 : PRUint32 last = mRecycledBuffers.Length() - 1;
101 0 : PRUint8* result = mRecycledBuffers[last].forget();
102 0 : mRecycledBuffers.RemoveElementAt(last);
103 0 : return result;
104 : }
105 :
106 0 : ImageContainer::~ImageContainer()
107 : {
108 0 : }
109 :
110 : already_AddRefed<Image>
111 0 : ImageContainer::CreateImage(const Image::Format *aFormats,
112 : PRUint32 aNumFormats)
113 : {
114 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
115 0 : return mImageFactory->CreateImage(aFormats, aNumFormats, mScaleHint, mRecycleBin);
116 : }
117 :
118 : void
119 0 : ImageContainer::SetCurrentImage(Image *aImage)
120 : {
121 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
122 :
123 0 : if (mRemoteData) {
124 0 : NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
125 0 : mRemoteDataMutex->Lock();
126 : // This is important since it ensures we won't change the active image
127 : // when we currently have a locked image that depends on mRemoteData.
128 : }
129 :
130 0 : mActiveImage = aImage;
131 0 : CurrentImageChanged();
132 :
133 0 : if (mRemoteData) {
134 0 : mRemoteDataMutex->Unlock();
135 : }
136 0 : }
137 :
138 : bool
139 0 : ImageContainer::HasCurrentImage()
140 : {
141 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
142 :
143 0 : if (mRemoteData) {
144 0 : CrossProcessMutexAutoLock autoLock(*mRemoteDataMutex);
145 :
146 0 : EnsureActiveImage();
147 :
148 0 : return !!mActiveImage.get();
149 : }
150 :
151 0 : return !!mActiveImage.get();
152 : }
153 :
154 : already_AddRefed<Image>
155 0 : ImageContainer::LockCurrentImage()
156 : {
157 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
158 :
159 0 : if (mRemoteData) {
160 0 : NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
161 0 : mRemoteDataMutex->Lock();
162 : }
163 :
164 0 : EnsureActiveImage();
165 :
166 0 : nsRefPtr<Image> retval = mActiveImage;
167 0 : return retval.forget();
168 : }
169 :
170 : already_AddRefed<gfxASurface>
171 0 : ImageContainer::LockCurrentAsSurface(gfxIntSize *aSize, Image** aCurrentImage)
172 : {
173 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
174 :
175 0 : if (mRemoteData) {
176 0 : NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
177 0 : mRemoteDataMutex->Lock();
178 :
179 0 : EnsureActiveImage();
180 :
181 0 : if (aCurrentImage) {
182 0 : NS_IF_ADDREF(mActiveImage);
183 0 : *aCurrentImage = mActiveImage.get();
184 : }
185 :
186 0 : if (!mActiveImage) {
187 0 : return nsnull;
188 : }
189 :
190 0 : if (mActiveImage->GetFormat() == Image::REMOTE_IMAGE_BITMAP) {
191 : nsRefPtr<gfxImageSurface> newSurf =
192 : new gfxImageSurface(mRemoteData->mBitmap.mData, mRemoteData->mSize, mRemoteData->mBitmap.mStride,
193 : mRemoteData->mFormat == RemoteImageData::BGRX32 ?
194 : gfxASurface::ImageFormatARGB32 :
195 0 : gfxASurface::ImageFormatRGB24);
196 :
197 0 : *aSize = newSurf->GetSize();
198 :
199 0 : return newSurf.forget();
200 : }
201 : }
202 :
203 0 : if (aCurrentImage) {
204 0 : NS_IF_ADDREF(mActiveImage);
205 0 : *aCurrentImage = mActiveImage.get();
206 : }
207 :
208 0 : if (!mActiveImage) {
209 0 : return nsnull;
210 : }
211 :
212 0 : *aSize = mActiveImage->GetSize();
213 0 : return mActiveImage->GetAsSurface();
214 : }
215 :
216 : void
217 0 : ImageContainer::UnlockCurrentImage()
218 : {
219 0 : if (mRemoteData) {
220 0 : NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!");
221 0 : mRemoteDataMutex->Unlock();
222 : }
223 0 : }
224 :
225 : already_AddRefed<gfxASurface>
226 0 : ImageContainer::GetCurrentAsSurface(gfxIntSize *aSize)
227 : {
228 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
229 :
230 0 : if (mRemoteData) {
231 0 : CrossProcessMutexAutoLock autoLock(*mRemoteDataMutex);
232 0 : EnsureActiveImage();
233 :
234 0 : *aSize = mRemoteData->mSize;
235 0 : return mActiveImage ? mActiveImage->GetAsSurface() : nsnull;
236 : }
237 :
238 0 : *aSize = mActiveImage->GetSize();
239 0 : return mActiveImage ? mActiveImage->GetAsSurface() : nsnull;
240 : }
241 :
242 : gfxIntSize
243 0 : ImageContainer::GetCurrentSize()
244 : {
245 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
246 :
247 0 : if (mRemoteData) {
248 0 : CrossProcessMutexAutoLock autoLock(*mRemoteDataMutex);
249 :
250 : // We don't need to ensure we have an active image here, as we need to
251 : // be in the mutex anyway, and this is easiest to return from there.
252 0 : return mRemoteData->mSize;
253 : }
254 :
255 0 : if (!mActiveImage) {
256 0 : return gfxIntSize(0,0);
257 : }
258 :
259 0 : return mActiveImage->GetSize();
260 : }
261 :
262 : void
263 0 : ImageContainer::SetRemoteImageData(RemoteImageData *aData, CrossProcessMutex *aMutex)
264 : {
265 0 : ReentrantMonitorAutoEnter mon(mReentrantMonitor);
266 0 : NS_ASSERTION(!mActiveImage, "No active image expected when SetRemoteImageData is called.");
267 0 : NS_ASSERTION(!mRemoteData, "No remote data expected when SetRemoteImageData is called.");
268 :
269 0 : mRemoteData = aData;
270 :
271 0 : if (aData) {
272 0 : memset(aData, 0, sizeof(RemoteImageData));
273 : } else {
274 0 : mActiveImage = nsnull;
275 : }
276 :
277 0 : mRemoteDataMutex = aMutex;
278 0 : }
279 :
280 : void
281 0 : ImageContainer::EnsureActiveImage()
282 : {
283 0 : if (mRemoteData) {
284 0 : if (mRemoteData->mWasUpdated) {
285 0 : mActiveImage = nsnull;
286 : }
287 :
288 0 : if (mRemoteData->mType == RemoteImageData::RAW_BITMAP &&
289 0 : mRemoteData->mBitmap.mData && !mActiveImage) {
290 0 : nsRefPtr<RemoteBitmapImage> newImg = new RemoteBitmapImage();
291 :
292 0 : newImg->mFormat = mRemoteData->mFormat;
293 0 : newImg->mData = mRemoteData->mBitmap.mData;
294 0 : newImg->mSize = mRemoteData->mSize;
295 0 : newImg->mStride = mRemoteData->mBitmap.mStride;
296 0 : mRemoteData->mWasUpdated = false;
297 :
298 0 : mActiveImage = newImg;
299 : }
300 : }
301 0 : }
302 :
303 0 : PlanarYCbCrImage::PlanarYCbCrImage(BufferRecycleBin *aRecycleBin)
304 : : Image(nsnull, PLANAR_YCBCR)
305 : , mBufferSize(0)
306 0 : , mRecycleBin(aRecycleBin)
307 : {
308 0 : }
309 :
310 0 : PlanarYCbCrImage::~PlanarYCbCrImage()
311 : {
312 0 : if (mBuffer) {
313 0 : mRecycleBin->RecycleBuffer(mBuffer.forget(), mBufferSize);
314 : }
315 0 : }
316 :
317 : PRUint8*
318 0 : PlanarYCbCrImage::AllocateBuffer(PRUint32 aSize)
319 : {
320 0 : return mRecycleBin->GetBuffer(aSize);
321 : }
322 :
323 : void
324 0 : PlanarYCbCrImage::CopyData(const Data& aData)
325 : {
326 0 : mData = aData;
327 :
328 0 : mData.mYStride = mData.mYSize.width;
329 0 : mData.mCbCrStride = mData.mCbCrSize.width;
330 :
331 : // update buffer size
332 : mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
333 0 : mData.mYStride * mData.mYSize.height;
334 :
335 : // get new buffer
336 0 : mBuffer = AllocateBuffer(mBufferSize);
337 0 : if (!mBuffer)
338 0 : return;
339 :
340 0 : mData.mYChannel = mBuffer;
341 0 : mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height;
342 0 : mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height;
343 :
344 0 : for (int i = 0; i < mData.mYSize.height; i++) {
345 0 : memcpy(mData.mYChannel + i * mData.mYStride,
346 : aData.mYChannel + i * aData.mYStride,
347 0 : mData.mYStride);
348 : }
349 0 : for (int i = 0; i < mData.mCbCrSize.height; i++) {
350 0 : memcpy(mData.mCbChannel + i * mData.mCbCrStride,
351 : aData.mCbChannel + i * aData.mCbCrStride,
352 0 : mData.mCbCrStride);
353 0 : memcpy(mData.mCrChannel + i * mData.mCbCrStride,
354 : aData.mCrChannel + i * aData.mCbCrStride,
355 0 : mData.mCbCrStride);
356 : }
357 :
358 0 : mSize = aData.mPicSize;
359 : }
360 :
361 : void
362 0 : PlanarYCbCrImage::SetData(const Data &aData)
363 : {
364 0 : CopyData(aData);
365 0 : }
366 :
367 : already_AddRefed<gfxASurface>
368 0 : PlanarYCbCrImage::GetAsSurface()
369 : {
370 0 : if (mSurface) {
371 0 : nsRefPtr<gfxASurface> result = mSurface.get();
372 0 : return result.forget();
373 : }
374 :
375 : nsRefPtr<gfxImageSurface> imageSurface =
376 0 : new gfxImageSurface(mSize, gfxASurface::ImageFormatRGB24);
377 :
378 : gfx::YUVType type =
379 : gfx::TypeFromSize(mData.mYSize.width,
380 : mData.mYSize.height,
381 : mData.mCbCrSize.width,
382 0 : mData.mCbCrSize.height);
383 :
384 : // Convert from YCbCr to RGB now
385 : gfx::ConvertYCbCrToRGB32(mData.mYChannel,
386 : mData.mCbChannel,
387 : mData.mCrChannel,
388 : imageSurface->Data(),
389 : mData.mPicX,
390 : mData.mPicY,
391 : mData.mPicSize.width,
392 : mData.mPicSize.height,
393 : mData.mYStride,
394 : mData.mCbCrStride,
395 : imageSurface->Stride(),
396 0 : type);
397 :
398 0 : mSurface = imageSurface;
399 :
400 0 : return imageSurface.forget().get();
401 : }
402 :
403 : #ifdef XP_MACOSX
404 : void
405 : MacIOSurfaceImage::SetData(const Data& aData)
406 : {
407 : mIOSurface = nsIOSurface::LookupSurface(aData.mIOSurface->GetIOSurfaceID());
408 : mSize = gfxIntSize(mIOSurface->GetWidth(), mIOSurface->GetHeight());
409 : }
410 :
411 : already_AddRefed<gfxASurface>
412 : MacIOSurfaceImage::GetAsSurface()
413 : {
414 : return mIOSurface->GetAsSurface();
415 : }
416 :
417 : void
418 : MacIOSurfaceImage::Update(ImageContainer* aContainer)
419 : {
420 : if (mUpdateCallback) {
421 : mUpdateCallback(aContainer, mPluginInstanceOwner);
422 : }
423 : }
424 : #endif
425 : already_AddRefed<gfxASurface>
426 0 : RemoteBitmapImage::GetAsSurface()
427 : {
428 : nsRefPtr<gfxImageSurface> newSurf =
429 : new gfxImageSurface(mSize,
430 0 : mFormat == RemoteImageData::BGRX32 ? gfxASurface::ImageFormatRGB24 : gfxASurface::ImageFormatARGB32);
431 :
432 0 : for (int y = 0; y < mSize.height; y++) {
433 0 : memcpy(newSurf->Data() + newSurf->Stride() * y,
434 : mData + mStride * y,
435 0 : mSize.width * 4);
436 : }
437 :
438 0 : return newSurf.forget();
439 : }
440 :
441 : }
442 : }
|