1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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) 2010
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Vladimir Vukicevic <vladimir@pobox.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 "gfxUtils.h"
39 : #include "gfxContext.h"
40 : #include "gfxPlatform.h"
41 : #include "gfxDrawable.h"
42 : #include "nsRegion.h"
43 : #include "yuv_convert.h"
44 : #include "ycbcr_to_rgb565.h"
45 :
46 : #ifdef XP_WIN
47 : #include "gfxWindowsPlatform.h"
48 : #endif
49 :
50 : using namespace mozilla;
51 : using namespace mozilla::layers;
52 : using namespace mozilla::gfx;
53 :
54 : static PRUint8 sUnpremultiplyTable[256*256];
55 : static PRUint8 sPremultiplyTable[256*256];
56 : static bool sTablesInitialized = false;
57 :
58 0 : static const PRUint8 PremultiplyValue(PRUint8 a, PRUint8 v) {
59 0 : return sPremultiplyTable[a*256+v];
60 : }
61 :
62 0 : static const PRUint8 UnpremultiplyValue(PRUint8 a, PRUint8 v) {
63 0 : return sUnpremultiplyTable[a*256+v];
64 : }
65 :
66 : static void
67 0 : CalculateTables()
68 : {
69 : // It's important that the array be indexed first by alpha and then by rgb
70 : // value. When we unpremultiply a pixel, we're guaranteed to do three
71 : // lookups with the same alpha; indexing by alpha first makes it likely that
72 : // those three lookups will be close to one another in memory, thus
73 : // increasing the chance of a cache hit.
74 :
75 : // Unpremultiply table
76 :
77 : // a == 0 case
78 0 : for (PRUint32 c = 0; c <= 255; c++) {
79 0 : sUnpremultiplyTable[c] = c;
80 : }
81 :
82 0 : for (int a = 1; a <= 255; a++) {
83 0 : for (int c = 0; c <= 255; c++) {
84 0 : sUnpremultiplyTable[a*256+c] = (PRUint8)((c * 255) / a);
85 : }
86 : }
87 :
88 : // Premultiply table
89 :
90 0 : for (int a = 0; a <= 255; a++) {
91 0 : for (int c = 0; c <= 255; c++) {
92 0 : sPremultiplyTable[a*256+c] = (a * c + 254) / 255;
93 : }
94 : }
95 :
96 0 : sTablesInitialized = true;
97 0 : }
98 :
99 : void
100 0 : gfxUtils::PremultiplyImageSurface(gfxImageSurface *aSourceSurface,
101 : gfxImageSurface *aDestSurface)
102 : {
103 0 : if (!aDestSurface)
104 0 : aDestSurface = aSourceSurface;
105 :
106 0 : NS_ASSERTION(aSourceSurface->Format() == aDestSurface->Format() &&
107 : aSourceSurface->Width() == aDestSurface->Width() &&
108 : aSourceSurface->Height() == aDestSurface->Height() &&
109 : aSourceSurface->Stride() == aDestSurface->Stride(),
110 : "Source and destination surfaces don't have identical characteristics");
111 :
112 0 : NS_ASSERTION(aSourceSurface->Stride() == aSourceSurface->Width() * 4,
113 : "Source surface stride isn't tightly packed");
114 :
115 : // Only premultiply ARGB32
116 0 : if (aSourceSurface->Format() != gfxASurface::ImageFormatARGB32) {
117 0 : if (aDestSurface != aSourceSurface) {
118 0 : memcpy(aDestSurface->Data(), aSourceSurface->Data(),
119 0 : aSourceSurface->Stride() * aSourceSurface->Height());
120 : }
121 0 : return;
122 : }
123 :
124 0 : if (!sTablesInitialized)
125 0 : CalculateTables();
126 :
127 0 : PRUint8 *src = aSourceSurface->Data();
128 0 : PRUint8 *dst = aDestSurface->Data();
129 :
130 0 : PRUint32 dim = aSourceSurface->Width() * aSourceSurface->Height();
131 0 : for (PRUint32 i = 0; i < dim; ++i) {
132 : #ifdef IS_LITTLE_ENDIAN
133 0 : PRUint8 b = *src++;
134 0 : PRUint8 g = *src++;
135 0 : PRUint8 r = *src++;
136 0 : PRUint8 a = *src++;
137 :
138 0 : *dst++ = PremultiplyValue(a, b);
139 0 : *dst++ = PremultiplyValue(a, g);
140 0 : *dst++ = PremultiplyValue(a, r);
141 0 : *dst++ = a;
142 : #else
143 : PRUint8 a = *src++;
144 : PRUint8 r = *src++;
145 : PRUint8 g = *src++;
146 : PRUint8 b = *src++;
147 :
148 : *dst++ = a;
149 : *dst++ = PremultiplyValue(a, r);
150 : *dst++ = PremultiplyValue(a, g);
151 : *dst++ = PremultiplyValue(a, b);
152 : #endif
153 : }
154 : }
155 :
156 : void
157 0 : gfxUtils::UnpremultiplyImageSurface(gfxImageSurface *aSourceSurface,
158 : gfxImageSurface *aDestSurface)
159 : {
160 0 : if (!aDestSurface)
161 0 : aDestSurface = aSourceSurface;
162 :
163 0 : NS_ASSERTION(aSourceSurface->Format() == aDestSurface->Format() &&
164 : aSourceSurface->Width() == aDestSurface->Width() &&
165 : aSourceSurface->Height() == aDestSurface->Height() &&
166 : aSourceSurface->Stride() == aDestSurface->Stride(),
167 : "Source and destination surfaces don't have identical characteristics");
168 :
169 0 : NS_ASSERTION(aSourceSurface->Stride() == aSourceSurface->Width() * 4,
170 : "Source surface stride isn't tightly packed");
171 :
172 : // Only premultiply ARGB32
173 0 : if (aSourceSurface->Format() != gfxASurface::ImageFormatARGB32) {
174 0 : if (aDestSurface != aSourceSurface) {
175 0 : memcpy(aDestSurface->Data(), aSourceSurface->Data(),
176 0 : aSourceSurface->Stride() * aSourceSurface->Height());
177 : }
178 0 : return;
179 : }
180 :
181 0 : if (!sTablesInitialized)
182 0 : CalculateTables();
183 :
184 0 : PRUint8 *src = aSourceSurface->Data();
185 0 : PRUint8 *dst = aDestSurface->Data();
186 :
187 0 : PRUint32 dim = aSourceSurface->Width() * aSourceSurface->Height();
188 0 : for (PRUint32 i = 0; i < dim; ++i) {
189 : #ifdef IS_LITTLE_ENDIAN
190 0 : PRUint8 b = *src++;
191 0 : PRUint8 g = *src++;
192 0 : PRUint8 r = *src++;
193 0 : PRUint8 a = *src++;
194 :
195 0 : *dst++ = UnpremultiplyValue(a, b);
196 0 : *dst++ = UnpremultiplyValue(a, g);
197 0 : *dst++ = UnpremultiplyValue(a, r);
198 0 : *dst++ = a;
199 : #else
200 : PRUint8 a = *src++;
201 : PRUint8 r = *src++;
202 : PRUint8 g = *src++;
203 : PRUint8 b = *src++;
204 :
205 : *dst++ = a;
206 : *dst++ = UnpremultiplyValue(a, r);
207 : *dst++ = UnpremultiplyValue(a, g);
208 : *dst++ = UnpremultiplyValue(a, b);
209 : #endif
210 : }
211 : }
212 :
213 : static bool
214 0 : IsSafeImageTransformComponent(gfxFloat aValue)
215 : {
216 0 : return aValue >= -32768 && aValue <= 32767;
217 : }
218 :
219 : /**
220 : * This returns the fastest operator to use for solid surfaces which have no
221 : * alpha channel or their alpha channel is uniformly opaque.
222 : * This differs per render mode.
223 : */
224 : static gfxContext::GraphicsOperator
225 0 : OptimalFillOperator()
226 : {
227 : #ifdef XP_WIN
228 : if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() ==
229 : gfxWindowsPlatform::RENDER_DIRECT2D) {
230 : // D2D -really- hates operator source.
231 : return gfxContext::OPERATOR_OVER;
232 : } else {
233 : #endif
234 0 : return gfxContext::OPERATOR_SOURCE;
235 : #ifdef XP_WIN
236 : }
237 : #endif
238 : }
239 :
240 : // EXTEND_PAD won't help us here; we have to create a temporary surface to hold
241 : // the subimage of pixels we're allowed to sample.
242 : static already_AddRefed<gfxDrawable>
243 0 : CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable,
244 : gfxContext* aContext,
245 : const gfxMatrix& aUserSpaceToImageSpace,
246 : const gfxRect& aSourceRect,
247 : const gfxRect& aSubimage,
248 : const gfxImageSurface::gfxImageFormat aFormat)
249 : {
250 0 : gfxRect userSpaceClipExtents = aContext->GetClipExtents();
251 : // This isn't optimal --- if aContext has a rotation then GetClipExtents
252 : // will have to do a bounding-box computation, and TransformBounds might
253 : // too, so we could get a better result if we computed image space clip
254 : // extents in one go --- but it doesn't really matter and this is easier
255 : // to understand.
256 : gfxRect imageSpaceClipExtents =
257 0 : aUserSpaceToImageSpace.TransformBounds(userSpaceClipExtents);
258 : // Inflate by one pixel because bilinear filtering will sample at most
259 : // one pixel beyond the computed image pixel coordinate.
260 0 : imageSpaceClipExtents.Inflate(1.0);
261 :
262 0 : gfxRect needed = imageSpaceClipExtents.Intersect(aSourceRect);
263 0 : needed = needed.Intersect(aSubimage);
264 0 : needed.RoundOut();
265 :
266 : // if 'needed' is empty, nothing will be drawn since aFill
267 : // must be entirely outside the clip region, so it doesn't
268 : // matter what we do here, but we should avoid trying to
269 : // create a zero-size surface.
270 0 : if (needed.IsEmpty())
271 0 : return nsnull;
272 :
273 0 : gfxIntSize size(PRInt32(needed.Width()), PRInt32(needed.Height()));
274 : nsRefPtr<gfxASurface> temp =
275 0 : gfxPlatform::GetPlatform()->CreateOffscreenSurface(size, gfxASurface::ContentFromFormat(aFormat));
276 0 : if (!temp || temp->CairoStatus())
277 0 : return nsnull;
278 :
279 0 : nsRefPtr<gfxContext> tmpCtx = new gfxContext(temp);
280 0 : tmpCtx->SetOperator(OptimalFillOperator());
281 0 : aDrawable->Draw(tmpCtx, needed - needed.TopLeft(), true,
282 0 : gfxPattern::FILTER_FAST, gfxMatrix().Translate(needed.TopLeft()));
283 :
284 0 : nsRefPtr<gfxPattern> resultPattern = new gfxPattern(temp);
285 0 : if (!resultPattern)
286 0 : return nsnull;
287 :
288 : nsRefPtr<gfxDrawable> drawable =
289 0 : new gfxSurfaceDrawable(temp, size, gfxMatrix().Translate(-needed.TopLeft()));
290 0 : return drawable.forget();
291 : }
292 :
293 : // working around cairo/pixman bug (bug 364968)
294 : // Our device-space-to-image-space transform may not be acceptable to pixman.
295 : struct NS_STACK_CLASS AutoCairoPixmanBugWorkaround
296 : {
297 0 : AutoCairoPixmanBugWorkaround(gfxContext* aContext,
298 : const gfxMatrix& aDeviceSpaceToImageSpace,
299 : const gfxRect& aFill,
300 : const gfxASurface* aSurface)
301 0 : : mContext(aContext), mSucceeded(true), mPushedGroup(false)
302 : {
303 : // Quartz's limits for matrix are much larger than pixman
304 0 : if (!aSurface || aSurface->GetType() == gfxASurface::SurfaceTypeQuartz)
305 0 : return;
306 :
307 0 : if (!IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.xx) ||
308 0 : !IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.xy) ||
309 0 : !IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.yx) ||
310 0 : !IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.yy)) {
311 0 : NS_WARNING("Scaling up too much, bailing out");
312 0 : mSucceeded = false;
313 0 : return;
314 : }
315 :
316 0 : if (IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.x0) &&
317 0 : IsSafeImageTransformComponent(aDeviceSpaceToImageSpace.y0))
318 0 : return;
319 :
320 : // We'll push a group, which will hopefully reduce our transform's
321 : // translation so it's in bounds.
322 0 : gfxMatrix currentMatrix = mContext->CurrentMatrix();
323 0 : mContext->Save();
324 :
325 : // Clip the rounded-out-to-device-pixels bounds of the
326 : // transformed fill area. This is the area for the group we
327 : // want to push.
328 0 : mContext->IdentityMatrix();
329 0 : gfxRect bounds = currentMatrix.TransformBounds(aFill);
330 0 : bounds.RoundOut();
331 0 : mContext->Clip(bounds);
332 0 : mContext->SetMatrix(currentMatrix);
333 0 : mContext->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
334 0 : mContext->SetOperator(gfxContext::OPERATOR_OVER);
335 :
336 0 : mPushedGroup = true;
337 : }
338 :
339 0 : ~AutoCairoPixmanBugWorkaround()
340 : {
341 0 : if (mPushedGroup) {
342 0 : mContext->PopGroupToSource();
343 0 : mContext->Paint();
344 0 : mContext->Restore();
345 : }
346 0 : }
347 :
348 0 : bool PushedGroup() { return mPushedGroup; }
349 0 : bool Succeeded() { return mSucceeded; }
350 :
351 : private:
352 : gfxContext* mContext;
353 : bool mSucceeded;
354 : bool mPushedGroup;
355 : };
356 :
357 : static gfxMatrix
358 0 : DeviceToImageTransform(gfxContext* aContext,
359 : const gfxMatrix& aUserSpaceToImageSpace)
360 : {
361 : gfxFloat deviceX, deviceY;
362 : nsRefPtr<gfxASurface> currentTarget =
363 0 : aContext->CurrentSurface(&deviceX, &deviceY);
364 0 : gfxMatrix currentMatrix = aContext->CurrentMatrix();
365 0 : gfxMatrix deviceToUser = gfxMatrix(currentMatrix).Invert();
366 0 : deviceToUser.Translate(-gfxPoint(-deviceX, -deviceY));
367 0 : return gfxMatrix(deviceToUser).Multiply(aUserSpaceToImageSpace);
368 : }
369 :
370 : /* static */ void
371 0 : gfxUtils::DrawPixelSnapped(gfxContext* aContext,
372 : gfxDrawable* aDrawable,
373 : const gfxMatrix& aUserSpaceToImageSpace,
374 : const gfxRect& aSubimage,
375 : const gfxRect& aSourceRect,
376 : const gfxRect& aImageRect,
377 : const gfxRect& aFill,
378 : const gfxImageSurface::gfxImageFormat aFormat,
379 : const gfxPattern::GraphicsFilter& aFilter)
380 : {
381 0 : bool doTile = !aImageRect.Contains(aSourceRect);
382 :
383 0 : nsRefPtr<gfxASurface> currentTarget = aContext->CurrentSurface();
384 : gfxMatrix deviceSpaceToImageSpace =
385 0 : DeviceToImageTransform(aContext, aUserSpaceToImageSpace);
386 :
387 : AutoCairoPixmanBugWorkaround workaround(aContext, deviceSpaceToImageSpace,
388 0 : aFill, currentTarget);
389 0 : if (!workaround.Succeeded())
390 : return;
391 :
392 0 : nsRefPtr<gfxDrawable> drawable = aDrawable;
393 :
394 : // OK now, the hard part left is to account for the subimage sampling
395 : // restriction. If all the transforms involved are just integer
396 : // translations, then we assume no resampling will occur so there's
397 : // nothing to do.
398 : // XXX if only we had source-clipping in cairo!
399 0 : if (aContext->CurrentMatrix().HasNonIntegerTranslation() ||
400 0 : aUserSpaceToImageSpace.HasNonIntegerTranslation()) {
401 0 : if (doTile || !aSubimage.Contains(aImageRect)) {
402 : nsRefPtr<gfxDrawable> restrictedDrawable =
403 : CreateSamplingRestrictedDrawable(aDrawable, aContext,
404 : aUserSpaceToImageSpace, aSourceRect,
405 0 : aSubimage, aFormat);
406 0 : if (restrictedDrawable) {
407 0 : drawable.swap(restrictedDrawable);
408 : }
409 : }
410 : // We no longer need to tile: Either we never needed to, or we already
411 : // filled a surface with the tiled pattern; this surface can now be
412 : // drawn without tiling.
413 0 : doTile = false;
414 : }
415 :
416 0 : gfxContext::GraphicsOperator op = aContext->CurrentOperator();
417 0 : if ((op == gfxContext::OPERATOR_OVER || workaround.PushedGroup()) &&
418 : aFormat == gfxASurface::ImageFormatRGB24) {
419 0 : aContext->SetOperator(OptimalFillOperator());
420 : }
421 :
422 0 : drawable->Draw(aContext, aFill, doTile, aFilter, aUserSpaceToImageSpace);
423 :
424 0 : aContext->SetOperator(op);
425 : }
426 :
427 : /* static */ int
428 0 : gfxUtils::ImageFormatToDepth(gfxASurface::gfxImageFormat aFormat)
429 : {
430 0 : switch (aFormat) {
431 : case gfxASurface::ImageFormatARGB32:
432 0 : return 32;
433 : case gfxASurface::ImageFormatRGB24:
434 0 : return 24;
435 : case gfxASurface::ImageFormatRGB16_565:
436 0 : return 16;
437 : default:
438 : break;
439 : }
440 0 : return 0;
441 : }
442 :
443 : static void
444 0 : PathFromRegionInternal(gfxContext* aContext, const nsIntRegion& aRegion,
445 : bool aSnap)
446 : {
447 0 : aContext->NewPath();
448 0 : nsIntRegionRectIterator iter(aRegion);
449 : const nsIntRect* r;
450 0 : while ((r = iter.Next()) != nsnull) {
451 0 : aContext->Rectangle(gfxRect(r->x, r->y, r->width, r->height), aSnap);
452 : }
453 0 : }
454 :
455 : static void
456 0 : ClipToRegionInternal(gfxContext* aContext, const nsIntRegion& aRegion,
457 : bool aSnap)
458 : {
459 0 : PathFromRegionInternal(aContext, aRegion, aSnap);
460 0 : aContext->Clip();
461 0 : }
462 :
463 : /*static*/ void
464 0 : gfxUtils::ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion)
465 : {
466 0 : ClipToRegionInternal(aContext, aRegion, false);
467 0 : }
468 :
469 : /*static*/ void
470 0 : gfxUtils::ClipToRegionSnapped(gfxContext* aContext, const nsIntRegion& aRegion)
471 : {
472 0 : ClipToRegionInternal(aContext, aRegion, true);
473 0 : }
474 :
475 : /*static*/ gfxFloat
476 0 : gfxUtils::ClampToScaleFactor(gfxFloat aVal)
477 : {
478 : // Arbitary scale factor limitation. We can increase this
479 : // for better scaling performance at the cost of worse
480 : // quality.
481 : static const gfxFloat kScaleResolution = 2;
482 :
483 : // Negative scaling is just a flip and irrelevant to
484 : // our resolution calculation.
485 0 : if (aVal < 0.0) {
486 0 : aVal = -aVal;
487 : }
488 :
489 0 : gfxFloat power = log(aVal)/log(kScaleResolution);
490 :
491 : // If power is within 1e-6 of an integer, round to nearest to
492 : // prevent floating point errors, otherwise round up to the
493 : // next integer value.
494 0 : if (fabs(power - NS_round(power)) < 1e-6) {
495 0 : power = NS_round(power);
496 : } else {
497 0 : power = ceil(power);
498 : }
499 :
500 0 : return pow(kScaleResolution, power);
501 : }
502 :
503 :
504 : /*static*/ void
505 0 : gfxUtils::PathFromRegion(gfxContext* aContext, const nsIntRegion& aRegion)
506 : {
507 0 : PathFromRegionInternal(aContext, aRegion, false);
508 0 : }
509 :
510 : /*static*/ void
511 0 : gfxUtils::PathFromRegionSnapped(gfxContext* aContext, const nsIntRegion& aRegion)
512 : {
513 0 : PathFromRegionInternal(aContext, aRegion, true);
514 0 : }
515 :
516 :
517 : bool
518 0 : gfxUtils::GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut)
519 : {
520 0 : *aOut = nsIntRect(PRInt32(aIn.X()), PRInt32(aIn.Y()),
521 0 : PRInt32(aIn.Width()), PRInt32(aIn.Height()));
522 0 : return gfxRect(aOut->x, aOut->y, aOut->width, aOut->height).IsEqualEdges(aIn);
523 : }
524 :
525 : void
526 0 : gfxUtils::GetYCbCrToRGBDestFormatAndSize(const PlanarYCbCrImage::Data& aData,
527 : gfxASurface::gfxImageFormat& aSuggestedFormat,
528 : gfxIntSize& aSuggestedSize)
529 : {
530 : gfx::YUVType yuvtype =
531 : gfx::TypeFromSize(aData.mYSize.width,
532 : aData.mYSize.height,
533 : aData.mCbCrSize.width,
534 0 : aData.mCbCrSize.height);
535 :
536 : // 'prescale' is true if the scaling is to be done as part of the
537 : // YCbCr to RGB conversion rather than on the RGB data when rendered.
538 : bool prescale = aSuggestedSize.width > 0 && aSuggestedSize.height > 0 &&
539 0 : aSuggestedSize != aData.mPicSize;
540 :
541 0 : if (aSuggestedFormat == gfxASurface::ImageFormatRGB16_565) {
542 : #if defined(HAVE_YCBCR_TO_RGB565)
543 : if (prescale &&
544 : !gfx::IsScaleYCbCrToRGB565Fast(aData.mPicX,
545 : aData.mPicY,
546 : aData.mPicSize.width,
547 : aData.mPicSize.height,
548 : aSuggestedSize.width,
549 : aSuggestedSize.height,
550 : yuvtype,
551 : gfx::FILTER_BILINEAR) &&
552 : gfx::IsConvertYCbCrToRGB565Fast(aData.mPicX,
553 : aData.mPicY,
554 : aData.mPicSize.width,
555 : aData.mPicSize.height,
556 : yuvtype)) {
557 : prescale = false;
558 : }
559 : #else
560 : // yuv2rgb16 function not available
561 0 : aSuggestedFormat = gfxASurface::ImageFormatRGB24;
562 : #endif
563 : }
564 0 : else if (aSuggestedFormat != gfxASurface::ImageFormatRGB24) {
565 : // No other formats are currently supported.
566 0 : aSuggestedFormat = gfxASurface::ImageFormatRGB24;
567 : }
568 0 : if (aSuggestedFormat == gfxASurface::ImageFormatRGB24) {
569 : /* ScaleYCbCrToRGB32 does not support a picture offset, nor 4:4:4 data.
570 : See bugs 639415 and 640073. */
571 0 : if (aData.mPicX != 0 || aData.mPicY != 0 || yuvtype == gfx::YV24)
572 0 : prescale = false;
573 : }
574 0 : if (!prescale) {
575 0 : aSuggestedSize = aData.mPicSize;
576 : }
577 0 : }
578 :
579 : void
580 0 : gfxUtils::ConvertYCbCrToRGB(const PlanarYCbCrImage::Data& aData,
581 : const gfxASurface::gfxImageFormat& aDestFormat,
582 : const gfxIntSize& aDestSize,
583 : unsigned char* aDestBuffer,
584 : PRInt32 aStride)
585 : {
586 : gfx::YUVType yuvtype =
587 : gfx::TypeFromSize(aData.mYSize.width,
588 : aData.mYSize.height,
589 : aData.mCbCrSize.width,
590 0 : aData.mCbCrSize.height);
591 :
592 : // Convert from YCbCr to RGB now, scaling the image if needed.
593 0 : if (aDestSize != aData.mPicSize) {
594 : #if defined(HAVE_YCBCR_TO_RGB565)
595 : if (aDestFormat == gfxASurface::ImageFormatRGB16_565) {
596 : gfx::ScaleYCbCrToRGB565(aData.mYChannel,
597 : aData.mCbChannel,
598 : aData.mCrChannel,
599 : aDestBuffer,
600 : aData.mPicX,
601 : aData.mPicY,
602 : aData.mPicSize.width,
603 : aData.mPicSize.height,
604 : aDestSize.width,
605 : aDestSize.height,
606 : aData.mYStride,
607 : aData.mCbCrStride,
608 : aStride,
609 : yuvtype,
610 : gfx::FILTER_BILINEAR);
611 : } else
612 : #endif
613 : gfx::ScaleYCbCrToRGB32(aData.mYChannel,
614 : aData.mCbChannel,
615 : aData.mCrChannel,
616 : aDestBuffer,
617 : aData.mPicSize.width,
618 : aData.mPicSize.height,
619 : aDestSize.width,
620 : aDestSize.height,
621 : aData.mYStride,
622 : aData.mCbCrStride,
623 : aStride,
624 : yuvtype,
625 : gfx::ROTATE_0,
626 0 : gfx::FILTER_BILINEAR);
627 : } else { // no prescale
628 : #if defined(HAVE_YCBCR_TO_RGB565)
629 : if (aDestFormat == gfxASurface::ImageFormatRGB16_565) {
630 : gfx::ConvertYCbCrToRGB565(aData.mYChannel,
631 : aData.mCbChannel,
632 : aData.mCrChannel,
633 : aDestBuffer,
634 : aData.mPicX,
635 : aData.mPicY,
636 : aData.mPicSize.width,
637 : aData.mPicSize.height,
638 : aData.mYStride,
639 : aData.mCbCrStride,
640 : aStride,
641 : yuvtype);
642 : } else // aDestFormat != gfxASurface::ImageFormatRGB16_565
643 : #endif
644 : gfx::ConvertYCbCrToRGB32(aData.mYChannel,
645 : aData.mCbChannel,
646 : aData.mCrChannel,
647 : aDestBuffer,
648 : aData.mPicX,
649 : aData.mPicY,
650 : aData.mPicSize.width,
651 : aData.mPicSize.height,
652 : aData.mYStride,
653 : aData.mCbCrStride,
654 : aStride,
655 0 : yuvtype);
656 : }
657 0 : }
658 :
659 : #ifdef MOZ_DUMP_PAINTING
660 : /* static */ void
661 0 : gfxUtils::WriteAsPNG(DrawTarget* aDT, const char* aFile)
662 : {
663 0 : aDT->Flush();
664 0 : nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aDT);
665 0 : if (surf) {
666 0 : surf->WriteAsPNG(aFile);
667 : } else {
668 0 : NS_WARNING("Failed to get Thebes surface!");
669 : }
670 0 : }
671 :
672 : /* static */ void
673 0 : gfxUtils::DumpAsDataURL(DrawTarget* aDT)
674 : {
675 0 : aDT->Flush();
676 0 : nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aDT);
677 0 : if (surf) {
678 0 : surf->DumpAsDataURL();
679 : } else {
680 0 : NS_WARNING("Failed to get Thebes surface!");
681 : }
682 0 : }
683 :
684 : /* static */ void
685 0 : gfxUtils::CopyAsDataURL(DrawTarget* aDT)
686 : {
687 0 : aDT->Flush();
688 0 : nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aDT);
689 0 : if (surf) {
690 0 : surf->CopyAsDataURL();
691 : } else {
692 0 : NS_WARNING("Failed to get Thebes surface!");
693 : }
694 0 : }
695 :
696 1464 : bool gfxUtils::sDumpPainting = getenv("MOZ_DUMP_PAINT_LIST") != 0;
697 1464 : bool gfxUtils::sDumpPaintingToFile = getenv("MOZ_DUMP_PAINT_TO_FILE") != 0;
698 4392 : FILE *gfxUtils::sDumpPaintFile = NULL;
699 : #endif
|