ImageBitmapRenderingContext.cpp (9723B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 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 #include "ImageBitmapRenderingContext.h" 7 8 #include "ImageContainer.h" 9 #include "gfx2DGlue.h" 10 #include "gfxPlatform.h" 11 #include "mozilla/dom/ImageBitmapRenderingContextBinding.h" 12 #include "mozilla/gfx/Types.h" 13 #include "nsComponentManagerUtils.h" 14 #include "nsRegion.h" 15 16 namespace mozilla::dom { 17 18 ImageBitmapRenderingContext::ImageBitmapRenderingContext() 19 : mWidth(0), 20 mHeight(0), 21 mFrameCaptureState(FrameCaptureState::CLEAN, 22 "ImageBitmapRenderingContext::mFrameCaptureState") {} 23 24 ImageBitmapRenderingContext::~ImageBitmapRenderingContext() { 25 RemovePostRefreshObserver(); 26 } 27 28 JSObject* ImageBitmapRenderingContext::WrapObject( 29 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { 30 return ImageBitmapRenderingContext_Binding::Wrap(aCx, this, aGivenProto); 31 } 32 33 already_AddRefed<layers::Image> 34 ImageBitmapRenderingContext::ClipToIntrinsicSize() { 35 if (!mImage) { 36 return nullptr; 37 } 38 39 // If image is larger than canvas intrinsic size, clip it to the intrinsic 40 // size. 41 RefPtr<gfx::SourceSurface> surface; 42 RefPtr<layers::Image> result; 43 if (mWidth < mImage->GetSize().width || mHeight < mImage->GetSize().height) { 44 surface = MatchWithIntrinsicSize(); 45 } else { 46 surface = mImage->GetAsSourceSurface(); 47 } 48 if (!surface) { 49 return nullptr; 50 } 51 result = 52 new layers::SourceSurfaceImage(gfx::IntSize(mWidth, mHeight), surface); 53 return result.forget(); 54 } 55 56 void ImageBitmapRenderingContext::GetCanvas( 57 Nullable<OwningHTMLCanvasElementOrOffscreenCanvas>& retval) const { 58 if (mCanvasElement && !mCanvasElement->IsInNativeAnonymousSubtree()) { 59 retval.SetValue().SetAsHTMLCanvasElement() = mCanvasElement; 60 } else if (mOffscreenCanvas) { 61 retval.SetValue().SetAsOffscreenCanvas() = mOffscreenCanvas; 62 } else { 63 retval.SetNull(); 64 } 65 } 66 67 void ImageBitmapRenderingContext::TransferImageBitmap(ImageBitmap& aImageBitmap, 68 ErrorResult& aRv) { 69 TransferFromImageBitmap(&aImageBitmap, aRv); 70 } 71 72 void ImageBitmapRenderingContext::TransferFromImageBitmap( 73 ImageBitmap* aImageBitmap, ErrorResult& aRv) { 74 ResetBitmap(); 75 76 if (aImageBitmap) { 77 mImage = aImageBitmap->TransferAsImage(); 78 79 if (!mImage) { 80 aRv.ThrowInvalidStateError("The input ImageBitmap has been detached"); 81 return; 82 } 83 84 // Note that this is reentrant and will call back into SetDimensions. 85 if (mCanvasElement) { 86 mCanvasElement->SetSize(mImage->GetSize(), aRv); 87 } else if (mOffscreenCanvas) { 88 mOffscreenCanvas->SetSize(mImage->GetSize(), aRv); 89 } 90 91 if (NS_WARN_IF(aRv.Failed())) { 92 mImage = nullptr; 93 return; 94 } 95 96 if (aImageBitmap->IsWriteOnly()) { 97 if (mCanvasElement) { 98 mCanvasElement->SetWriteOnly(); 99 } else if (mOffscreenCanvas) { 100 mOffscreenCanvas->SetWriteOnly(); 101 } 102 } 103 } 104 105 Redraw(gfxRect(0, 0, mWidth, mHeight)); 106 } 107 108 NS_IMETHODIMP 109 ImageBitmapRenderingContext::SetDimensions(int32_t aWidth, int32_t aHeight) { 110 mWidth = aWidth; 111 mHeight = aHeight; 112 113 if (mOffscreenCanvas) { 114 OffscreenCanvasDisplayData data; 115 data.mSize = {mWidth, mHeight}; 116 data.mIsOpaque = GetIsOpaque(); 117 data.mIsAlphaPremult = true; 118 data.mDoPaintCallbacks = false; 119 mOffscreenCanvas->UpdateDisplayData(data); 120 } 121 122 return NS_OK; 123 } 124 125 NS_IMETHODIMP 126 ImageBitmapRenderingContext::InitializeWithDrawTarget( 127 nsIDocShell* aDocShell, NotNull<gfx::DrawTarget*> aTarget) { 128 return NS_ERROR_NOT_IMPLEMENTED; 129 } 130 131 already_AddRefed<gfx::DataSourceSurface> 132 ImageBitmapRenderingContext::MatchWithIntrinsicSize() { 133 RefPtr<gfx::SourceSurface> surface = mImage->GetAsSourceSurface(); 134 if (!surface) { 135 return nullptr; 136 } 137 RefPtr<gfx::DataSourceSurface> temp = gfx::Factory::CreateDataSourceSurface( 138 gfx::IntSize(mWidth, mHeight), surface->GetFormat()); 139 if (!temp) { 140 return nullptr; 141 } 142 143 gfx::DataSourceSurface::ScopedMap map(temp, 144 gfx::DataSourceSurface::READ_WRITE); 145 if (!map.IsMapped()) { 146 return nullptr; 147 } 148 149 RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateDrawTargetForData( 150 gfxPlatform::GetPlatform()->GetSoftwareBackend(), map.GetData(), 151 temp->GetSize(), map.GetStride(), temp->GetFormat()); 152 if (!dt || !dt->IsValid()) { 153 gfxWarning() 154 << "ImageBitmapRenderingContext::MatchWithIntrinsicSize failed"; 155 return nullptr; 156 } 157 158 dt->ClearRect(gfx::Rect(0, 0, mWidth, mHeight)); 159 dt->CopySurface( 160 surface, 161 gfx::IntRect(0, 0, surface->GetSize().width, surface->GetSize().height), 162 gfx::IntPoint(0, 0)); 163 164 return temp.forget(); 165 } 166 167 mozilla::UniquePtr<uint8_t[]> ImageBitmapRenderingContext::GetImageBuffer( 168 mozilla::CanvasUtils::ImageExtraction aExtractionBehavior, int32_t* aFormat, 169 gfx::IntSize* aImageSize) { 170 *aFormat = 0; 171 *aImageSize = {}; 172 173 if (!mImage) { 174 return nullptr; 175 } 176 177 RefPtr<gfx::SourceSurface> surface = mImage->GetAsSourceSurface(); 178 if (!surface) { 179 return nullptr; 180 } 181 RefPtr<gfx::DataSourceSurface> data = surface->GetDataSurface(); 182 if (!data) { 183 return nullptr; 184 } 185 186 if (data->GetSize() != gfx::IntSize(mWidth, mHeight)) { 187 data = MatchWithIntrinsicSize(); 188 if (!data) { 189 return nullptr; 190 } 191 } 192 193 *aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB; 194 *aImageSize = data->GetSize(); 195 196 UniquePtr<uint8_t[]> ret = gfx::SurfaceToPackedBGRA(data); 197 198 if (ret) { 199 nsRFPService::PotentiallyDumpImage( 200 PrincipalOrNull(), ret.get(), data->GetSize().width, 201 data->GetSize().height, 202 data->GetSize().width * data->GetSize().height * 4); 203 if (ShouldResistFingerprinting(RFPTarget::CanvasRandomization)) { 204 nsRFPService::RandomizePixels( 205 GetCookieJarSettings(), PrincipalOrNull(), ret.get(), 206 data->GetSize().width, data->GetSize().height, 207 data->GetSize().width * data->GetSize().height * 4, 208 gfx::SurfaceFormat::A8R8G8B8_UINT32); 209 } 210 } 211 return ret; 212 } 213 214 NS_IMETHODIMP 215 ImageBitmapRenderingContext::GetInputStream( 216 const char* aMimeType, const nsAString& aEncoderOptions, 217 mozilla::CanvasUtils::ImageExtraction aExtractionBehavior, 218 const nsACString& aRandomizationKey, nsIInputStream** aStream) { 219 nsCString enccid("@mozilla.org/image/encoder;2?type="); 220 enccid += aMimeType; 221 nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get()); 222 if (!encoder) { 223 return NS_ERROR_FAILURE; 224 } 225 226 int32_t format = 0; 227 gfx::IntSize imageSize = {}; 228 UniquePtr<uint8_t[]> imageBuffer = 229 GetImageBuffer(aExtractionBehavior, &format, &imageSize); 230 if (!imageBuffer) { 231 return NS_ERROR_FAILURE; 232 } 233 234 return ImageEncoder::GetInputStream( 235 imageSize.width, imageSize.height, imageBuffer.get(), format, encoder, 236 aEncoderOptions, aRandomizationKey, aStream); 237 } 238 239 already_AddRefed<mozilla::gfx::SourceSurface> 240 ImageBitmapRenderingContext::GetSurfaceSnapshot( 241 gfxAlphaType* const aOutAlphaType) { 242 if (!mImage) { 243 return nullptr; 244 } 245 246 if (aOutAlphaType) { 247 *aOutAlphaType = 248 (GetIsOpaque() ? gfxAlphaType::Opaque : gfxAlphaType::Premult); 249 } 250 251 RefPtr<gfx::SourceSurface> surface = mImage->GetAsSourceSurface(); 252 if (!surface) { 253 return nullptr; 254 } 255 256 if (surface->GetSize() != gfx::IntSize(mWidth, mHeight)) { 257 return MatchWithIntrinsicSize(); 258 } 259 260 return surface.forget(); 261 } 262 263 void ImageBitmapRenderingContext::SetOpaqueValueFromOpaqueAttr( 264 bool aOpaqueAttrValue) { 265 // ignored 266 } 267 268 bool ImageBitmapRenderingContext::GetIsOpaque() { return false; } 269 270 void ImageBitmapRenderingContext::ResetBitmap() { 271 if (mCanvasElement) { 272 mCanvasElement->InvalidateCanvas(); 273 } 274 275 mImage = nullptr; 276 mFrameCaptureState = FrameCaptureState::CLEAN; 277 } 278 279 bool ImageBitmapRenderingContext::UpdateWebRenderCanvasData( 280 nsDisplayListBuilder* aBuilder, WebRenderCanvasData* aCanvasData) { 281 if (!mImage) { 282 // No DidTransactionCallback will be received, so mark the context clean 283 // now so future invalidations will be dispatched. 284 MarkContextClean(); 285 return false; 286 } 287 288 RefPtr<layers::ImageContainer> imageContainer = 289 aCanvasData->GetImageContainer(); 290 AutoTArray<layers::ImageContainer::NonOwningImage, 1> imageList; 291 RefPtr<layers::Image> image = ClipToIntrinsicSize(); 292 if (!image) { 293 return false; 294 } 295 296 imageList.AppendElement(layers::ImageContainer::NonOwningImage(image)); 297 imageContainer->SetCurrentImages(imageList); 298 return true; 299 } 300 301 void ImageBitmapRenderingContext::MarkContextClean() {} 302 303 NS_IMETHODIMP 304 ImageBitmapRenderingContext::Redraw(const gfxRect& aDirty) { 305 mFrameCaptureState = FrameCaptureState::DIRTY; 306 307 if (mOffscreenCanvas) { 308 mOffscreenCanvas->CommitFrameToCompositor(); 309 } else if (mCanvasElement) { 310 mozilla::gfx::Rect rect = ToRect(aDirty); 311 mCanvasElement->InvalidateCanvasContent(&rect); 312 } 313 314 return NS_OK; 315 } 316 317 void ImageBitmapRenderingContext::DidRefresh() {} 318 319 NS_IMPL_CYCLE_COLLECTING_ADDREF(ImageBitmapRenderingContext) 320 NS_IMPL_CYCLE_COLLECTING_RELEASE(ImageBitmapRenderingContext) 321 322 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(ImageBitmapRenderingContext, 323 mCanvasElement, mOffscreenCanvas) 324 325 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageBitmapRenderingContext) 326 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 327 NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal) 328 NS_INTERFACE_MAP_ENTRY(nsISupports) 329 NS_INTERFACE_MAP_END 330 331 } // namespace mozilla::dom