зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 4 changesets (bug 1781527) for causing Assertion failures in ImageBitmap.cpp CLOSED TREE
Backed out changeset d3ca9e70625b (bug 1781527) Backed out changeset f6c56b5e6450 (bug 1781527) Backed out changeset 34d140bc5b99 (bug 1781527) Backed out changeset f7a2f2b89ba8 (bug 1781527)
This commit is contained in:
Родитель
bec20309b7
Коммит
982856d479
|
@ -46,8 +46,7 @@ struct ImageCacheEntryData {
|
|||
mBackendType(aOther.mBackendType),
|
||||
mSourceSurface(aOther.mSourceSurface),
|
||||
mSize(aOther.mSize),
|
||||
mIntrinsicSize(aOther.mIntrinsicSize),
|
||||
mCropRect(aOther.mCropRect) {}
|
||||
mIntrinsicSize(aOther.mIntrinsicSize) {}
|
||||
explicit ImageCacheEntryData(const ImageCacheKey& aKey)
|
||||
: mImage(aKey.mImage),
|
||||
mCanvas(aKey.mCanvas),
|
||||
|
@ -64,7 +63,6 @@ struct ImageCacheEntryData {
|
|||
RefPtr<SourceSurface> mSourceSurface;
|
||||
IntSize mSize;
|
||||
IntSize mIntrinsicSize;
|
||||
Maybe<IntRect> mCropRect;
|
||||
nsExpirationState mState;
|
||||
};
|
||||
|
||||
|
@ -265,10 +263,12 @@ static already_AddRefed<imgIContainer> GetImageContainer(dom::Element* aImage) {
|
|||
return imgContainer.forget();
|
||||
}
|
||||
|
||||
void CanvasImageCache::NotifyDrawImage(
|
||||
Element* aImage, HTMLCanvasElement* aCanvas, DrawTarget* aTarget,
|
||||
SourceSurface* aSource, const IntSize& aSize, const IntSize& aIntrinsicSize,
|
||||
const Maybe<IntRect>& aCropRect) {
|
||||
void CanvasImageCache::NotifyDrawImage(Element* aImage,
|
||||
HTMLCanvasElement* aCanvas,
|
||||
DrawTarget* aTarget,
|
||||
SourceSurface* aSource,
|
||||
const IntSize& aSize,
|
||||
const IntSize& aIntrinsicSize) {
|
||||
if (!aTarget) {
|
||||
return;
|
||||
}
|
||||
|
@ -300,7 +300,6 @@ void CanvasImageCache::NotifyDrawImage(
|
|||
entry->mData->mSourceSurface = aSource;
|
||||
entry->mData->mSize = aSize;
|
||||
entry->mData->mIntrinsicSize = aIntrinsicSize;
|
||||
entry->mData->mCropRect = aCropRect;
|
||||
|
||||
AllCanvasImageCacheEntry* allEntry =
|
||||
gImageCache->mAllCanvasCache.PutEntry(allCanvasCacheKey);
|
||||
|
@ -334,8 +333,7 @@ SourceSurface* CanvasImageCache::LookupCanvas(Element* aImage,
|
|||
HTMLCanvasElement* aCanvas,
|
||||
DrawTarget* aTarget,
|
||||
IntSize* aSizeOut,
|
||||
IntSize* aIntrinsicSizeOut,
|
||||
Maybe<IntRect>* aCropRectOut) {
|
||||
IntSize* aIntrinsicSizeOut) {
|
||||
if (!gImageCache || !aTarget) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -362,7 +360,6 @@ SourceSurface* CanvasImageCache::LookupCanvas(Element* aImage,
|
|||
gImageCache->MarkUsed(entry->mData.get());
|
||||
*aSizeOut = entry->mData->mSize;
|
||||
*aIntrinsicSizeOut = entry->mData->mIntrinsicSize;
|
||||
*aCropRectOut = entry->mData->mCropRect;
|
||||
return entry->mData->mSourceSurface;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,7 @@
|
|||
#ifndef CANVASIMAGECACHE_H_
|
||||
#define CANVASIMAGECACHE_H_
|
||||
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/gfx/Rect.h"
|
||||
#include "nsSize.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -39,8 +37,7 @@ class CanvasImageCache {
|
|||
dom::HTMLCanvasElement* aCanvas,
|
||||
gfx::DrawTarget* aTarget, SourceSurface* aSource,
|
||||
const gfx::IntSize& aSize,
|
||||
const gfx::IntSize& aIntrinsicSize,
|
||||
const Maybe<gfx::IntRect>& aCropRect);
|
||||
const gfx::IntSize& aIntrinsicSize);
|
||||
|
||||
/**
|
||||
* Check whether aImage has recently been drawn any canvas. If we return
|
||||
|
@ -57,8 +54,7 @@ class CanvasImageCache {
|
|||
dom::HTMLCanvasElement* aCanvas,
|
||||
gfx::DrawTarget* aTarget,
|
||||
gfx::IntSize* aSizeOut,
|
||||
gfx::IntSize* aIntrinsicSizeOut,
|
||||
Maybe<gfx::IntRect>* aCropRectOut);
|
||||
gfx::IntSize* aIntrinsicSizeOut);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include "mozilla/dom/FontFaceSet.h"
|
||||
#include "mozilla/dom/HTMLCanvasElement.h"
|
||||
#include "mozilla/dom/GeneratePlaceholderCanvasData.h"
|
||||
#include "mozilla/dom/VideoFrame.h"
|
||||
#include "nsPresContext.h"
|
||||
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
|
@ -2282,7 +2281,6 @@ already_AddRefed<CanvasPattern> CanvasRenderingContext2D::CreatePattern(
|
|||
|
||||
Element* element = nullptr;
|
||||
OffscreenCanvas* offscreenCanvas = nullptr;
|
||||
VideoFrame* videoFrame = nullptr;
|
||||
|
||||
if (aSource.IsHTMLCanvasElement()) {
|
||||
HTMLCanvasElement* canvas = &aSource.GetAsHTMLCanvasElement();
|
||||
|
@ -2358,18 +2356,6 @@ already_AddRefed<CanvasPattern> CanvasRenderingContext2D::CreatePattern(
|
|||
|
||||
return pat.forget();
|
||||
}
|
||||
} else if (aSource.IsVideoFrame()) {
|
||||
videoFrame = &aSource.GetAsVideoFrame();
|
||||
|
||||
if (videoFrame->CodedWidth() == 0) {
|
||||
aError.ThrowInvalidStateError("Passed-in canvas has width 0");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (videoFrame->CodedHeight() == 0) {
|
||||
aError.ThrowInvalidStateError("Passed-in canvas has height 0");
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
// Special case for ImageBitmap
|
||||
ImageBitmap& imgBitmap = aSource.GetAsImageBitmap();
|
||||
|
@ -2404,15 +2390,11 @@ already_AddRefed<CanvasPattern> CanvasRenderingContext2D::CreatePattern(
|
|||
// of animated images
|
||||
auto flags = nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE |
|
||||
nsLayoutUtils::SFE_EXACT_SIZE_SURFACE;
|
||||
SurfaceFromElementResult res;
|
||||
if (offscreenCanvas) {
|
||||
res = nsLayoutUtils::SurfaceFromOffscreenCanvas(offscreenCanvas, flags,
|
||||
mTarget);
|
||||
} else if (videoFrame) {
|
||||
res = nsLayoutUtils::SurfaceFromVideoFrame(videoFrame, flags, mTarget);
|
||||
} else {
|
||||
res = nsLayoutUtils::SurfaceFromElement(element, flags, mTarget);
|
||||
}
|
||||
SurfaceFromElementResult res =
|
||||
offscreenCanvas
|
||||
? nsLayoutUtils::SurfaceFromOffscreenCanvas(offscreenCanvas, flags,
|
||||
mTarget)
|
||||
: nsLayoutUtils::SurfaceFromElement(element, flags, mTarget);
|
||||
|
||||
// Per spec, we should throw here for the HTMLImageElement and SVGImageElement
|
||||
// cases if the image request state is "broken". In terms of the infromation
|
||||
|
@ -4987,21 +4969,20 @@ static already_AddRefed<SourceSurface> ExtractSubrect(SourceSurface* aSurface,
|
|||
//
|
||||
|
||||
static void ClipImageDimension(double& aSourceCoord, double& aSourceSize,
|
||||
double& aClipOriginCoord, double& aClipSize,
|
||||
double& aDestCoord, double& aDestSize) {
|
||||
int32_t aImageSize, double& aDestCoord,
|
||||
double& aDestSize) {
|
||||
double scale = aDestSize / aSourceSize;
|
||||
double relativeCoord = aSourceCoord - aClipOriginCoord;
|
||||
if (relativeCoord < 0.0) {
|
||||
if (aSourceCoord < 0.0) {
|
||||
double destEnd = aDestCoord + aDestSize;
|
||||
aDestCoord -= relativeCoord * scale;
|
||||
aDestCoord -= aSourceCoord * scale;
|
||||
aDestSize = destEnd - aDestCoord;
|
||||
aSourceSize += relativeCoord;
|
||||
aSourceCoord = aClipOriginCoord;
|
||||
aSourceSize += aSourceCoord;
|
||||
aSourceCoord = 0.0;
|
||||
}
|
||||
double delta = aClipSize - (relativeCoord + aSourceSize);
|
||||
double delta = aImageSize - (aSourceCoord + aSourceSize);
|
||||
if (delta < 0.0) {
|
||||
aDestSize += delta * scale;
|
||||
aSourceSize = aClipSize - relativeCoord;
|
||||
aSourceSize = aImageSize - aSourceCoord;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5118,10 +5099,8 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
|
|||
RefPtr<SourceSurface> srcSurf;
|
||||
gfx::IntSize imgSize;
|
||||
gfx::IntSize intrinsicImgSize;
|
||||
Maybe<IntRect> cropRect;
|
||||
Element* element = nullptr;
|
||||
OffscreenCanvas* offscreenCanvas = nullptr;
|
||||
VideoFrame* videoFrame = nullptr;
|
||||
|
||||
EnsureTarget();
|
||||
if (!IsTargetValid()) {
|
||||
|
@ -5171,8 +5150,6 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
|
|||
|
||||
imgSize = intrinsicImgSize =
|
||||
gfx::IntSize(imageBitmap.Width(), imageBitmap.Height());
|
||||
} else if (aImage.IsVideoFrame()) {
|
||||
videoFrame = &aImage.GetAsVideoFrame();
|
||||
} else {
|
||||
if (aImage.IsHTMLImageElement()) {
|
||||
HTMLImageElement* img = &aImage.GetAsHTMLImageElement();
|
||||
|
@ -5187,9 +5164,8 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
|
|||
element = video;
|
||||
}
|
||||
|
||||
srcSurf =
|
||||
CanvasImageCache::LookupCanvas(element, mCanvasElement, mTarget,
|
||||
&imgSize, &intrinsicImgSize, &cropRect);
|
||||
srcSurf = CanvasImageCache::LookupCanvas(element, mCanvasElement, mTarget,
|
||||
&imgSize, &intrinsicImgSize);
|
||||
}
|
||||
|
||||
DirectDrawInfo drawInfo;
|
||||
|
@ -5199,14 +5175,12 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
|
|||
// of animated images. We also don't want to rasterize vector images.
|
||||
uint32_t sfeFlags = nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE |
|
||||
nsLayoutUtils::SFE_NO_RASTERIZING_VECTORS |
|
||||
nsLayoutUtils::SFE_ALLOW_UNCROPPED_UNSCALED;
|
||||
nsLayoutUtils::SFE_EXACT_SIZE_SURFACE;
|
||||
|
||||
SurfaceFromElementResult res;
|
||||
if (offscreenCanvas) {
|
||||
res = nsLayoutUtils::SurfaceFromOffscreenCanvas(offscreenCanvas, sfeFlags,
|
||||
mTarget);
|
||||
} else if (videoFrame) {
|
||||
res = nsLayoutUtils::SurfaceFromVideoFrame(videoFrame, sfeFlags, mTarget);
|
||||
} else {
|
||||
res = CanvasRenderingContext2D::CachedSurfaceFromElement(element);
|
||||
if (!res.mSourceSurface) {
|
||||
|
@ -5214,8 +5188,7 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
|
|||
}
|
||||
}
|
||||
|
||||
srcSurf = res.GetSourceSurface();
|
||||
if (!srcSurf && !res.mDrawInfo.mImgContainer) {
|
||||
if (!res.mSourceSurface && !res.mDrawInfo.mImgContainer) {
|
||||
// https://html.spec.whatwg.org/#check-the-usability-of-the-image-argument:
|
||||
//
|
||||
// Only throw if the request is broken and the element is an
|
||||
|
@ -5227,73 +5200,44 @@ void CanvasRenderingContext2D::DrawImage(const CanvasImageSource& aImage,
|
|||
if (!res.mIsStillLoading && !res.mHasSize &&
|
||||
(aImage.IsHTMLImageElement() || aImage.IsSVGImageElement())) {
|
||||
aError.ThrowInvalidStateError("Passed-in image is \"broken\"");
|
||||
} else if (videoFrame) {
|
||||
aError.ThrowInvalidStateError("Passed-in video frame is \"broken\"");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
imgSize = res.mSize;
|
||||
intrinsicImgSize = res.mIntrinsicSize;
|
||||
cropRect = res.mCropRect;
|
||||
DoSecurityCheck(res.mPrincipal, res.mIsWriteOnly, res.mCORSUsed);
|
||||
|
||||
if (srcSurf) {
|
||||
if (res.mSourceSurface) {
|
||||
if (res.mImageRequest) {
|
||||
CanvasImageCache::NotifyDrawImage(element, mCanvasElement, mTarget,
|
||||
srcSurf, imgSize, intrinsicImgSize,
|
||||
cropRect);
|
||||
res.mSourceSurface, imgSize,
|
||||
intrinsicImgSize);
|
||||
}
|
||||
srcSurf = res.mSourceSurface;
|
||||
} else {
|
||||
drawInfo = res.mDrawInfo;
|
||||
}
|
||||
}
|
||||
|
||||
double clipOriginX, clipOriginY, clipWidth, clipHeight;
|
||||
if (cropRect) {
|
||||
clipOriginX = cropRect.ref().X();
|
||||
clipOriginY = cropRect.ref().Y();
|
||||
clipWidth = cropRect.ref().Width();
|
||||
clipHeight = cropRect.ref().Height();
|
||||
} else {
|
||||
clipOriginX = clipOriginY = 0.0;
|
||||
clipWidth = imgSize.width;
|
||||
clipHeight = imgSize.height;
|
||||
}
|
||||
|
||||
// Any provided coordinates are in the display space, or the same as the
|
||||
// intrinsic size. In order to get to the surface coordinate space, we may
|
||||
// need to adjust for scaling and/or cropping. If no source coordinates are
|
||||
// provided, then we can just directly use the actual surface size.
|
||||
if (aOptional_argc == 0) {
|
||||
aSx = clipOriginX;
|
||||
aSy = clipOriginY;
|
||||
aSw = clipWidth;
|
||||
aSh = clipHeight;
|
||||
aSx = aSy = 0.0;
|
||||
aSw = (double)imgSize.width;
|
||||
aSh = (double)imgSize.height;
|
||||
aDw = (double)intrinsicImgSize.width;
|
||||
aDh = (double)intrinsicImgSize.height;
|
||||
} else if (aOptional_argc == 2) {
|
||||
aSx = clipOriginX;
|
||||
aSy = clipOriginY;
|
||||
aSw = clipWidth;
|
||||
aSh = clipHeight;
|
||||
} else if (cropRect || intrinsicImgSize != imgSize) {
|
||||
// We need to first scale between the cropped size and the intrinsic size,
|
||||
// and then adjust for the offset from the crop rect.
|
||||
double scaleXToCrop = clipWidth / intrinsicImgSize.width;
|
||||
double scaleYToCrop = clipHeight / intrinsicImgSize.height;
|
||||
aSx = aSx * scaleXToCrop + clipOriginX;
|
||||
aSy = aSy * scaleYToCrop + clipOriginY;
|
||||
aSw = aSw * scaleXToCrop;
|
||||
aSh = aSh * scaleYToCrop;
|
||||
aSx = aSy = 0.0;
|
||||
aSw = (double)imgSize.width;
|
||||
aSh = (double)imgSize.height;
|
||||
}
|
||||
|
||||
if (aSw == 0.0 || aSh == 0.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClipImageDimension(aSx, aSw, clipOriginX, clipWidth, aDx, aDw);
|
||||
ClipImageDimension(aSy, aSh, clipOriginY, clipHeight, aDy, aDh);
|
||||
ClipImageDimension(aSx, aSw, imgSize.width, aDx, aDw);
|
||||
ClipImageDimension(aSy, aSh, imgSize.height, aDy, aDh);
|
||||
|
||||
if (aSw <= 0.0 || aSh <= 0.0 || aDw <= 0.0 || aDh <= 0.0) {
|
||||
// source and/or destination are fully clipped, so nothing is painted
|
||||
|
|
|
@ -48,9 +48,9 @@ enum class LayersBackend : int8_t;
|
|||
|
||||
namespace dom {
|
||||
class
|
||||
HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrOffscreenCanvasOrImageBitmapOrVideoFrame;
|
||||
HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrOffscreenCanvasOrImageBitmap;
|
||||
using CanvasImageSource =
|
||||
HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrOffscreenCanvasOrImageBitmapOrVideoFrame;
|
||||
HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrOffscreenCanvasOrImageBitmap;
|
||||
class ImageBitmap;
|
||||
class ImageData;
|
||||
class UTF8StringOrCanvasGradientOrCanvasPattern;
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/dom/WorkerRef.h"
|
||||
#include "mozilla/dom/WorkerRunnable.h"
|
||||
#include "mozilla/dom/VideoFrame.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/Logging.h"
|
||||
#include "mozilla/gfx/Scale.h"
|
||||
|
@ -671,77 +670,6 @@ void ImageBitmap::SetPictureRect(const IntRect& aRect, ErrorResult& aRv) {
|
|||
mPictureRect = FixUpNegativeDimension(aRect, aRv);
|
||||
}
|
||||
|
||||
SurfaceFromElementResult ImageBitmap::SurfaceFrom(uint32_t aSurfaceFlags) {
|
||||
SurfaceFromElementResult sfer;
|
||||
|
||||
if (!mData) {
|
||||
return sfer;
|
||||
}
|
||||
|
||||
// An ImageBitmap, not being a DOM element, only has `origin-clean`
|
||||
// (via our `IsWriteOnly`), and does not participate in CORS.
|
||||
// Right now we mark this by setting mCORSUsed to true.
|
||||
sfer.mCORSUsed = true;
|
||||
sfer.mIsWriteOnly = mWriteOnly;
|
||||
|
||||
if (mParent) {
|
||||
sfer.mPrincipal = mParent->PrincipalOrNull();
|
||||
}
|
||||
|
||||
IntSize imageSize(mData->GetSize());
|
||||
IntRect imageRect(IntPoint(0, 0), imageSize);
|
||||
bool hasCropRect = mPictureRect.IsEqualEdges(imageRect);
|
||||
|
||||
bool wantExactSize =
|
||||
bool(aSurfaceFlags & nsLayoutUtils::SFE_EXACT_SIZE_SURFACE);
|
||||
bool allowNonPremult =
|
||||
bool(aSurfaceFlags & nsLayoutUtils::SFE_ALLOW_NON_PREMULT);
|
||||
bool allowUncropped =
|
||||
bool(aSurfaceFlags & nsLayoutUtils::SFE_ALLOW_UNCROPPED_UNSCALED);
|
||||
bool requiresPremult =
|
||||
!allowNonPremult && mAlphaType == gfxAlphaType::NonPremult;
|
||||
bool requiresCrop = !allowUncropped && hasCropRect;
|
||||
if (wantExactSize || requiresPremult || requiresCrop || mSurface) {
|
||||
RefPtr<DrawTarget> dt = Factory::CreateDrawTarget(
|
||||
BackendType::SKIA, IntSize(1, 1), SurfaceFormat::B8G8R8A8);
|
||||
sfer.mSourceSurface = PrepareForDrawTarget(dt);
|
||||
|
||||
if (!sfer.mSourceSurface) {
|
||||
return sfer;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mSurface);
|
||||
MOZ_ASSERT(mPictureRect.IsEmpty());
|
||||
|
||||
sfer.mSize = sfer.mIntrinsicSize = sfer.mSourceSurface->GetSize();
|
||||
sfer.mHasSize = true;
|
||||
sfer.mAlphaType = IsOpaque(sfer.mSourceSurface->GetFormat())
|
||||
? gfxAlphaType::Opaque
|
||||
: gfxAlphaType::Premult;
|
||||
return sfer;
|
||||
}
|
||||
|
||||
if (hasCropRect) {
|
||||
IntRect imagePortion = imageRect.Intersect(mPictureRect);
|
||||
|
||||
// the crop lies entirely outside the surface area, nothing to draw
|
||||
if (imagePortion.IsEmpty()) {
|
||||
return sfer;
|
||||
}
|
||||
|
||||
sfer.mCropRect = Some(imagePortion);
|
||||
sfer.mIntrinsicSize = imagePortion.Size();
|
||||
} else {
|
||||
sfer.mIntrinsicSize = imageSize;
|
||||
}
|
||||
|
||||
sfer.mSize = imageSize;
|
||||
sfer.mHasSize = true;
|
||||
sfer.mAlphaType = mAlphaType;
|
||||
sfer.mLayersImage = mData;
|
||||
return sfer;
|
||||
}
|
||||
|
||||
/*
|
||||
* The functionality of PrepareForDrawTarget method:
|
||||
* (1) Get a SourceSurface from the mData (which is a layers::Image).
|
||||
|
@ -1442,48 +1370,6 @@ already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
|
|||
needToReportMemoryAllocation, false, aImageBitmap.mAlphaType, aRv);
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
|
||||
nsIGlobalObject* aGlobal, VideoFrame& aVideoFrame,
|
||||
const Maybe<IntRect>& aCropRect, const ImageBitmapOptions& aOptions,
|
||||
ErrorResult& aRv) {
|
||||
if (aVideoFrame.CodedWidth() == 0) {
|
||||
aRv.ThrowInvalidStateError("Passed-in video frame has width 0");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aVideoFrame.CodedHeight() == 0) {
|
||||
aRv.ThrowInvalidStateError("Passed-in video frame has height 0");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t flags = nsLayoutUtils::SFE_WANT_FIRST_FRAME_IF_IMAGE;
|
||||
|
||||
// by default surfaces have premultiplied alpha
|
||||
// attempt to get non premultiplied if required
|
||||
if (aOptions.mPremultiplyAlpha == PremultiplyAlpha::None) {
|
||||
flags |= nsLayoutUtils::SFE_ALLOW_NON_PREMULT;
|
||||
}
|
||||
|
||||
SurfaceFromElementResult res =
|
||||
nsLayoutUtils::SurfaceFromVideoFrame(&aVideoFrame, flags);
|
||||
|
||||
RefPtr<SourceSurface> surface = res.GetSourceSurface();
|
||||
if (NS_WARN_IF(!surface)) {
|
||||
aRv.ThrowInvalidStateError("Passed-in video frame has no surface data");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gfxAlphaType alphaType = res.mAlphaType;
|
||||
bool writeOnly = res.mIsWriteOnly;
|
||||
bool needToReportMemoryAllocation = false;
|
||||
bool mustCopy = false;
|
||||
|
||||
return CreateImageBitmapInternal(aGlobal, surface, aCropRect, aOptions,
|
||||
writeOnly, needToReportMemoryAllocation,
|
||||
mustCopy, alphaType, aRv);
|
||||
}
|
||||
|
||||
class FulfillImageBitmapPromise {
|
||||
protected:
|
||||
FulfillImageBitmapPromise(Promise* aPromise, ImageBitmap* aImageBitmap)
|
||||
|
@ -1770,9 +1656,6 @@ already_AddRefed<Promise> ImageBitmap::Create(
|
|||
AsyncCreateImageBitmapFromBlob(promise, aGlobal, aSrc.GetAsBlob(),
|
||||
aCropRect, aOptions);
|
||||
return promise.forget();
|
||||
} else if (aSrc.IsVideoFrame()) {
|
||||
imageBitmap = CreateInternal(aGlobal, aSrc.GetAsVideoFrame(), aCropRect,
|
||||
aOptions, aRv);
|
||||
} else {
|
||||
MOZ_CRASH("Unsupported type!");
|
||||
return nullptr;
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#define mozilla_dom_ImageBitmap_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/SurfaceFromElementResult.h"
|
||||
#include "mozilla/dom/ImageBitmapBinding.h"
|
||||
#include "mozilla/dom/ImageBitmapSource.h"
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
|
@ -57,7 +56,6 @@ class ImageUtils;
|
|||
class Promise;
|
||||
class PostMessageEvent; // For StructuredClone between windows.
|
||||
class SVGImageElement;
|
||||
class VideoFrame;
|
||||
|
||||
struct ImageBitmapCloneData final {
|
||||
RefPtr<gfx::DataSourceSurface> mSurface;
|
||||
|
@ -94,8 +92,6 @@ class ImageBitmap final : public nsISupports, public nsWrapperCache {
|
|||
|
||||
void Close();
|
||||
|
||||
SurfaceFromElementResult SurfaceFrom(uint32_t aSurfaceFlags);
|
||||
|
||||
/*
|
||||
* The PrepareForDrawTarget() might return null if the mPictureRect does not
|
||||
* intersect with the size of mData.
|
||||
|
@ -223,11 +219,6 @@ class ImageBitmap final : public nsISupports, public nsWrapperCache {
|
|||
const Maybe<gfx::IntRect>& aCropRect, const ImageBitmapOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<ImageBitmap> CreateInternal(
|
||||
nsIGlobalObject* aGlobal, VideoFrame& aVideoFrame,
|
||||
const Maybe<gfx::IntRect>& aCropRect, const ImageBitmapOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mParent;
|
||||
|
||||
/*
|
||||
|
|
|
@ -11,8 +11,8 @@ namespace mozilla::dom {
|
|||
|
||||
// So we don't have to forward declare this elsewhere.
|
||||
class
|
||||
HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrOffscreenCanvasOrImageBitmapOrVideoFrameOrBlobOrCanvasRenderingContext2DOrImageData;
|
||||
typedef HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrOffscreenCanvasOrImageBitmapOrVideoFrameOrBlobOrCanvasRenderingContext2DOrImageData
|
||||
HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrOffscreenCanvasOrImageBitmapOrBlobOrCanvasRenderingContext2DOrImageData;
|
||||
typedef HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrOffscreenCanvasOrImageBitmapOrBlobOrCanvasRenderingContext2DOrImageData
|
||||
ImageBitmapSource;
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
|
|
@ -1754,13 +1754,6 @@ void VideoFrame::Close() {
|
|||
mDisplaySize = gfx::IntSize();
|
||||
}
|
||||
|
||||
already_AddRefed<layers::Image> VideoFrame::GetImage() const {
|
||||
if (!mResource) {
|
||||
return nullptr;
|
||||
}
|
||||
return do_AddRef(mResource->mImage);
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webcodecs/#ref-for-deserialization-steps%E2%91%A0
|
||||
/* static */
|
||||
JSObject* VideoFrame::ReadStructuredClone(
|
||||
|
|
|
@ -182,12 +182,6 @@ class VideoFrame final : public nsISupports, public nsWrapperCache {
|
|||
static already_AddRefed<VideoFrame> FromTransferred(nsIGlobalObject* aGlobal,
|
||||
TransferredData* aData);
|
||||
|
||||
// Native only methods.
|
||||
const gfx::IntSize& NativeCodedSize() const { return mCodedSize; }
|
||||
const gfx::IntSize& NativeDisplaySize() const { return mDisplaySize; }
|
||||
const gfx::IntRect& NativeVisibleRect() const { return mVisibleRect; }
|
||||
already_AddRefed<layers::Image> GetImage() const;
|
||||
|
||||
public:
|
||||
// A VideoPixelFormat wrapper providing utilities for VideoFrame.
|
||||
class Format final {
|
||||
|
|
|
@ -44,8 +44,7 @@ typedef (HTMLOrSVGImageElement or
|
|||
HTMLCanvasElement or
|
||||
HTMLVideoElement or
|
||||
OffscreenCanvas or
|
||||
ImageBitmap or
|
||||
VideoFrame) CanvasImageSource;
|
||||
ImageBitmap) CanvasImageSource;
|
||||
|
||||
[Exposed=Window]
|
||||
interface CanvasRenderingContext2D {
|
||||
|
|
|
@ -22,8 +22,7 @@ namespace mozilla {
|
|||
|
||||
namespace dom {
|
||||
class CanvasRenderingContext2D;
|
||||
class ImageBitmap;
|
||||
} // namespace dom
|
||||
}
|
||||
|
||||
namespace gfx {
|
||||
class SourceSurface;
|
||||
|
@ -40,7 +39,6 @@ struct DirectDrawInfo {
|
|||
|
||||
struct SurfaceFromElementResult {
|
||||
friend class mozilla::dom::CanvasRenderingContext2D;
|
||||
friend class mozilla::dom::ImageBitmap;
|
||||
friend class ::nsLayoutUtils;
|
||||
|
||||
/* If SFEResult contains a valid surface, it either mLayersImage or
|
||||
|
@ -67,9 +65,6 @@ struct SurfaceFromElementResult {
|
|||
mozilla::gfx::IntSize mSize;
|
||||
/* The size the surface is intended to be rendered at */
|
||||
mozilla::gfx::IntSize mIntrinsicSize;
|
||||
/* The crop rect of the surface, indicating what subset is valid. This will
|
||||
* always be Nothing() unless SFE_ALLOW_UNCROPPED is set. */
|
||||
mozilla::Maybe<mozilla::gfx::IntRect> mCropRect;
|
||||
/* The principal associated with the element whose surface was returned.
|
||||
If there is a surface, this will never be null. */
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
|
|
@ -52,7 +52,6 @@
|
|||
#include "mozilla/dom/KeyframeEffect.h"
|
||||
#include "mozilla/dom/SVGViewportElement.h"
|
||||
#include "mozilla/dom/UIEvent.h"
|
||||
#include "mozilla/dom/VideoFrame.h"
|
||||
#include "mozilla/intl/BidiEmbeddingLevel.h"
|
||||
#include "mozilla/EffectCompositor.h"
|
||||
#include "mozilla/EffectSet.h"
|
||||
|
@ -62,7 +61,6 @@
|
|||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/gfxVars.h"
|
||||
#include "mozilla/gfx/PathHelpers.h"
|
||||
#include "mozilla/gfx/DataSurfaceHelpers.h"
|
||||
#include "mozilla/IntegerRange.h"
|
||||
#include "mozilla/layers/APZCCallbackHelper.h"
|
||||
#include "mozilla/layers/APZPublicUtils.h" // for apz::CalculatePendingDisplayPort
|
||||
|
@ -7177,111 +7175,35 @@ SurfaceFromElementResult nsLayoutUtils::SurfaceFromOffscreenCanvas(
|
|||
return result;
|
||||
}
|
||||
|
||||
SurfaceFromElementResult nsLayoutUtils::SurfaceFromVideoFrame(
|
||||
VideoFrame* aVideoFrame, uint32_t aSurfaceFlags,
|
||||
RefPtr<DrawTarget>& aTarget) {
|
||||
SurfaceFromElementResult nsLayoutUtils::SurfaceFromImageBitmap(
|
||||
mozilla::dom::ImageBitmap* aImageBitmap, uint32_t aSurfaceFlags) {
|
||||
SurfaceFromElementResult result;
|
||||
RefPtr<DrawTarget> dt = Factory::CreateDrawTarget(
|
||||
BackendType::SKIA, IntSize(1, 1), SurfaceFormat::B8G8R8A8);
|
||||
|
||||
RefPtr<layers::Image> layersImage = aVideoFrame->GetImage();
|
||||
if (!layersImage) {
|
||||
return result;
|
||||
// An ImageBitmap, not being a DOM element, only has `origin-clean`
|
||||
// (via our `IsWriteOnly`), and does not participate in CORS.
|
||||
// Right now we mark this by setting mCORSUsed to true.
|
||||
result.mCORSUsed = true;
|
||||
result.mIsWriteOnly = aImageBitmap->IsWriteOnly();
|
||||
result.mSourceSurface = aImageBitmap->PrepareForDrawTarget(dt);
|
||||
|
||||
if (result.mSourceSurface) {
|
||||
result.mSize = result.mIntrinsicSize = result.mSourceSurface->GetSize();
|
||||
result.mHasSize = true;
|
||||
result.mAlphaType = IsOpaque(result.mSourceSurface->GetFormat())
|
||||
? gfxAlphaType::Opaque
|
||||
: gfxAlphaType::Premult;
|
||||
}
|
||||
|
||||
IntSize codedSize = aVideoFrame->NativeCodedSize();
|
||||
IntRect visibleRect = aVideoFrame->NativeVisibleRect();
|
||||
IntSize displaySize = aVideoFrame->NativeDisplaySize();
|
||||
|
||||
MOZ_ASSERT(layersImage->GetSize() == codedSize);
|
||||
IntRect codedRect(IntPoint(0, 0), codedSize);
|
||||
|
||||
if (visibleRect.IsEqualEdges(codedRect) && displaySize == codedSize) {
|
||||
// The display and coded rects are identical, which means we can just use
|
||||
// the image as is.
|
||||
result.mLayersImage = std::move(layersImage);
|
||||
result.mSize = codedSize;
|
||||
result.mIntrinsicSize = codedSize;
|
||||
} else if (aSurfaceFlags & SFE_ALLOW_UNCROPPED_UNSCALED) {
|
||||
// The caller supports cropping/scaling.
|
||||
result.mLayersImage = std::move(layersImage);
|
||||
result.mCropRect = Some(visibleRect);
|
||||
result.mSize = codedSize;
|
||||
result.mIntrinsicSize = displaySize;
|
||||
} else {
|
||||
// The caller does not support cropping/scaling. We need to on its behalf.
|
||||
RefPtr<SourceSurface> surface = layersImage->GetAsSourceSurface();
|
||||
if (!surface) {
|
||||
return result;
|
||||
}
|
||||
|
||||
RefPtr<DrawTarget> ref = aTarget
|
||||
? aTarget
|
||||
: gfxPlatform::GetPlatform()
|
||||
->ThreadLocalScreenReferenceDrawTarget();
|
||||
if (!ref->CanCreateSimilarDrawTarget(displaySize,
|
||||
SurfaceFormat::B8G8R8A8)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
RefPtr<DrawTarget> dt =
|
||||
ref->CreateSimilarDrawTarget(displaySize, SurfaceFormat::B8G8R8A8);
|
||||
if (!dt) {
|
||||
return result;
|
||||
}
|
||||
|
||||
gfx::Rect dstRect(0, 0, displaySize.Width(), displaySize.Height());
|
||||
gfx::Rect srcRect(visibleRect.X(), visibleRect.Y(), visibleRect.Width(),
|
||||
visibleRect.Height());
|
||||
dt->DrawSurface(surface, dstRect, srcRect);
|
||||
result.mSourceSurface = dt->Snapshot();
|
||||
if (NS_WARN_IF(!result.mSourceSurface)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result.mSize = displaySize;
|
||||
result.mIntrinsicSize = displaySize;
|
||||
}
|
||||
|
||||
// TODO(aosmond): Presumably we can do better than assuming premultiplied.
|
||||
// Depending on how the VideoFrame was created, we may have had more
|
||||
// information about its transpancy status.
|
||||
result.mAlphaType = gfxAlphaType::Premult;
|
||||
result.mHasSize = true;
|
||||
|
||||
// We shouldn't have a VideoFrame if either of these is true.
|
||||
result.mHadCrossOriginRedirects = false;
|
||||
result.mIsWriteOnly = false;
|
||||
|
||||
nsIGlobalObject* global = aVideoFrame->GetParentObject();
|
||||
nsCOMPtr<nsIGlobalObject> global = aImageBitmap->GetParentObject();
|
||||
if (global) {
|
||||
result.mPrincipal = global->PrincipalOrNull();
|
||||
}
|
||||
|
||||
if (aTarget) {
|
||||
// They gave us a DrawTarget to optimize for, so even though we may have a
|
||||
// layers::Image, we should unconditionally try to grab a SourceSurface and
|
||||
// try to optimize it.
|
||||
if (result.mLayersImage) {
|
||||
MOZ_ASSERT(!result.mSourceSurface);
|
||||
result.mSourceSurface = result.mLayersImage->GetAsSourceSurface();
|
||||
}
|
||||
|
||||
if (result.mSourceSurface) {
|
||||
RefPtr<SourceSurface> opt =
|
||||
aTarget->OptimizeSourceSurface(result.mSourceSurface);
|
||||
if (opt) {
|
||||
result.mSourceSurface = std::move(opt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SurfaceFromElementResult nsLayoutUtils::SurfaceFromImageBitmap(
|
||||
mozilla::dom::ImageBitmap* aImageBitmap, uint32_t aSurfaceFlags) {
|
||||
return aImageBitmap->SurfaceFrom(aSurfaceFlags);
|
||||
}
|
||||
|
||||
static RefPtr<SourceSurface> ScaleSourceSurface(SourceSurface& aSurface,
|
||||
const IntSize& aTargetSize) {
|
||||
const IntSize surfaceSize = aSurface.GetSize();
|
||||
|
@ -7412,15 +7334,12 @@ SurfaceFromElementResult nsLayoutUtils::SurfaceFromElement(
|
|||
if (!result.mSourceSurface) {
|
||||
return result;
|
||||
}
|
||||
IntSize surfSize = result.mSourceSurface->GetSize();
|
||||
if (exactSize && surfSize != result.mSize) {
|
||||
if (exactSize && result.mSourceSurface->GetSize() != result.mSize) {
|
||||
result.mSourceSurface =
|
||||
ScaleSourceSurface(*result.mSourceSurface, result.mSize);
|
||||
if (!result.mSourceSurface) {
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
result.mSize = surfSize;
|
||||
}
|
||||
// The surface we return is likely to be cached. We don't want to have to
|
||||
// convert to a surface that's compatible with aTarget each time it's used
|
||||
|
|
|
@ -100,7 +100,6 @@ class ImageBitmap;
|
|||
class InspectorFontFace;
|
||||
class OffscreenCanvas;
|
||||
class Selection;
|
||||
class VideoFrame;
|
||||
} // namespace dom
|
||||
namespace gfx {
|
||||
struct RectCornerRadii;
|
||||
|
@ -2198,9 +2197,7 @@ class nsLayoutUtils {
|
|||
* we don't rescale during decode. */
|
||||
SFE_EXACT_SIZE_SURFACE = 1 << 6,
|
||||
/* Use orientation from image */
|
||||
SFE_ORIENTATION_FROM_IMAGE = 1 << 7,
|
||||
/* Caller handles SFER::mCropRect.isSome() */
|
||||
SFE_ALLOW_UNCROPPED_UNSCALED = 1 << 8,
|
||||
SFE_ORIENTATION_FROM_IMAGE = 1 << 7
|
||||
};
|
||||
|
||||
// This function can be called on any thread.
|
||||
|
@ -2213,16 +2210,6 @@ class nsLayoutUtils {
|
|||
RefPtr<DrawTarget> target = nullptr;
|
||||
return SurfaceFromOffscreenCanvas(aOffscreenCanvas, aSurfaceFlags, target);
|
||||
}
|
||||
// This function can be called on any thread.
|
||||
static mozilla::SurfaceFromElementResult SurfaceFromVideoFrame(
|
||||
mozilla::dom::VideoFrame* aVideoFrame, uint32_t aSurfaceFlags,
|
||||
RefPtr<DrawTarget>& aTarget);
|
||||
static mozilla::SurfaceFromElementResult SurfaceFromVideoFrame(
|
||||
mozilla::dom::VideoFrame* aVideoFrame, uint32_t aSurfaceFlags = 0) {
|
||||
RefPtr<DrawTarget> target = nullptr;
|
||||
return SurfaceFromVideoFrame(aVideoFrame, aSurfaceFlags, target);
|
||||
}
|
||||
// This function can be called on any thread.
|
||||
static mozilla::SurfaceFromElementResult SurfaceFromImageBitmap(
|
||||
mozilla::dom::ImageBitmap* aImageBitmap, uint32_t aSurfaceFlags);
|
||||
|
||||
|
|
|
@ -1,5 +1,18 @@
|
|||
[videoFrame-alpha.any.html]
|
||||
prefs: [dom.media.webcodecs.enabled:true]
|
||||
expected:
|
||||
if (os == "android") and fission: [OK, TIMEOUT]
|
||||
[OffscreenCanvas source preserves alpha]
|
||||
expected: FAIL
|
||||
|
||||
[ImageBitmap source preserves alpha]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[videoFrame-alpha.any.worker.html]
|
||||
prefs: [dom.media.webcodecs.enabled:true]
|
||||
expected:
|
||||
if (os == "android") and fission: [OK, TIMEOUT]
|
||||
[OffscreenCanvas source preserves alpha]
|
||||
expected: FAIL
|
||||
|
||||
[ImageBitmap source preserves alpha]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,7 +1,20 @@
|
|||
[videoFrame-canvasImageSource.html]
|
||||
prefs: [dom.media.webcodecs.enabled:true]
|
||||
expected:
|
||||
if (os == "android") and fission: [OK, TIMEOUT]
|
||||
[<video> and VideoFrame constructed VideoFrame]
|
||||
expected: FAIL
|
||||
|
||||
[CSSImageValue constructed VideoFrame]
|
||||
expected: FAIL
|
||||
|
||||
[Image element constructed VideoFrame]
|
||||
expected: FAIL
|
||||
|
||||
[SVGImageElement constructed VideoFrame]
|
||||
expected: FAIL
|
||||
|
||||
[Canvas element constructed VideoFrame]
|
||||
expected: FAIL
|
||||
|
||||
[Copy of canvas element constructed VideoFrame]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,5 +1,20 @@
|
|||
[videoFrame-createImageBitmap.any.html]
|
||||
prefs: [dom.media.webcodecs.enabled:true]
|
||||
[ImageBitmap<->VideoFrame with canvas(48x36 srgb uint8).]
|
||||
expected: FAIL
|
||||
|
||||
[ImageBitmap<->VideoFrame with canvas(480x360 srgb uint8).]
|
||||
expected: FAIL
|
||||
|
||||
[createImageBitmap uses frame display size]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[videoFrame-createImageBitmap.any.worker.html]
|
||||
prefs: [dom.media.webcodecs.enabled:true]
|
||||
[ImageBitmap<->VideoFrame with canvas(48x36 srgb uint8).]
|
||||
expected: FAIL
|
||||
|
||||
[ImageBitmap<->VideoFrame with canvas(480x360 srgb uint8).]
|
||||
expected: FAIL
|
||||
|
||||
[createImageBitmap uses frame display size]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
[videoFrame-drawImage.any.worker.html]
|
||||
prefs: [dom.media.webcodecs.enabled:true]
|
||||
expected:
|
||||
if (os == "android") and fission: [OK, TIMEOUT]
|
||||
[drawImage(VideoFrame) with canvas(48x36 srgb uint8).]
|
||||
expected: FAIL
|
||||
|
||||
[drawImage(VideoFrame) with canvas(480x360 srgb uint8).]
|
||||
expected: FAIL
|
||||
|
||||
[drawImage(VideoFrame) with canvas(48x36 display-p3 uint8).]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -11,11 +16,25 @@
|
|||
[drawImage(VideoFrame) with canvas(48x36 rec2020 uint8).]
|
||||
expected: FAIL
|
||||
|
||||
[drawImage on a closed VideoFrame throws InvalidStateError.]
|
||||
expected: FAIL
|
||||
|
||||
[drawImage of nested frame works properly]
|
||||
expected: FAIL
|
||||
|
||||
[drawImage with display size != visible size]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[videoFrame-drawImage.any.html]
|
||||
prefs: [dom.media.webcodecs.enabled:true]
|
||||
expected:
|
||||
if (os == "android") and fission: [OK, TIMEOUT]
|
||||
[drawImage(VideoFrame) with canvas(48x36 srgb uint8).]
|
||||
expected: FAIL
|
||||
|
||||
[drawImage(VideoFrame) with canvas(480x360 srgb uint8).]
|
||||
expected: FAIL
|
||||
|
||||
[drawImage(VideoFrame) with canvas(48x36 display-p3 uint8).]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -24,3 +43,12 @@
|
|||
|
||||
[drawImage(VideoFrame) with canvas(48x36 rec2020 uint8).]
|
||||
expected: FAIL
|
||||
|
||||
[drawImage on a closed VideoFrame throws InvalidStateError.]
|
||||
expected: FAIL
|
||||
|
||||
[drawImage of nested frame works properly]
|
||||
expected: FAIL
|
||||
|
||||
[drawImage with display size != visible size]
|
||||
expected: FAIL
|
||||
|
|
Загрузка…
Ссылка в новой задаче