Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "core/frame/ImageBitmap.h" | 5 #include "core/frame/ImageBitmap.h" |
| 6 | 6 |
| 7 #include "core/html/HTMLCanvasElement.h" | 7 #include "core/html/HTMLCanvasElement.h" |
| 8 #include "core/html/HTMLVideoElement.h" | 8 #include "core/html/HTMLVideoElement.h" |
| 9 #include "core/html/ImageData.h" | 9 #include "core/html/ImageData.h" |
| 10 #include "platform/graphics/skia/SkiaUtils.h" | 10 #include "platform/graphics/skia/SkiaUtils.h" |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 24 namespace { | 24 namespace { |
| 25 | 25 |
| 26 struct ParsedOptions { | 26 struct ParsedOptions { |
| 27 bool flipY = false; | 27 bool flipY = false; |
| 28 bool premultiplyAlpha = true; | 28 bool premultiplyAlpha = true; |
| 29 bool shouldScaleInput = false; | 29 bool shouldScaleInput = false; |
| 30 unsigned resizeWidth = 0; | 30 unsigned resizeWidth = 0; |
| 31 unsigned resizeHeight = 0; | 31 unsigned resizeHeight = 0; |
| 32 IntRect cropRect; | 32 IntRect cropRect; |
| 33 SkFilterQuality resizeQuality = kLow_SkFilterQuality; | 33 SkFilterQuality resizeQuality = kLow_SkFilterQuality; |
| 34 // This value should be changed in the future when we support | |
| 35 // createImageBitmap with higher bit depth, in the parseOptions() function. | |
| 36 // For now, it is always 4. | |
| 37 int bytesPerPixel = 4; | 34 int bytesPerPixel = 4; |
| 35 sk_sp<SkColorSpace> dstColorSpace = nullptr; | |
| 36 SkColorType dstColorType = SkColorType::kUnknown_SkColorType; | |
| 38 }; | 37 }; |
| 39 | 38 |
| 40 // The following two functions are helpers used in cropImage | 39 // The following two functions are helpers used in cropImage |
| 41 static inline IntRect normalizeRect(const IntRect& rect) { | 40 static inline IntRect normalizeRect(const IntRect& rect) { |
| 42 return IntRect(std::min(rect.x(), rect.maxX()), | 41 return IntRect(std::min(rect.x(), rect.maxX()), |
| 43 std::min(rect.y(), rect.maxY()), | 42 std::min(rect.y(), rect.maxY()), |
| 44 std::max(rect.width(), -rect.width()), | 43 std::max(rect.width(), -rect.width()), |
| 45 std::max(rect.height(), -rect.height())); | 44 std::max(rect.height(), -rect.height())); |
| 46 } | 45 } |
| 47 | 46 |
| 48 ParsedOptions parseOptions(const ImageBitmapOptions& options, | 47 ParsedOptions parseOptions(const ImageBitmapOptions& options, |
| 49 Optional<IntRect> cropRect, | 48 Optional<IntRect> cropRect, |
| 50 IntSize sourceSize) { | 49 IntSize sourceSize) { |
| 51 ParsedOptions parsedOptions; | 50 ParsedOptions parsedOptions; |
| 52 if (options.imageOrientation() == imageOrientationFlipY) { | 51 if (options.imageOrientation() == imageOrientationFlipY) { |
| 53 parsedOptions.flipY = true; | 52 parsedOptions.flipY = true; |
| 54 } else { | 53 } else { |
| 55 parsedOptions.flipY = false; | 54 parsedOptions.flipY = false; |
| 56 DCHECK(options.imageOrientation() == imageBitmapOptionNone); | 55 DCHECK(options.imageOrientation() == imageBitmapOptionNone); |
| 57 } | 56 } |
| 58 if (options.premultiplyAlpha() == imageBitmapOptionNone) { | 57 if (options.premultiplyAlpha() == imageBitmapOptionNone) { |
| 59 parsedOptions.premultiplyAlpha = false; | 58 parsedOptions.premultiplyAlpha = false; |
| 60 } else { | 59 } else { |
| 61 parsedOptions.premultiplyAlpha = true; | 60 parsedOptions.premultiplyAlpha = true; |
| 62 DCHECK(options.premultiplyAlpha() == "default" || | 61 DCHECK(options.premultiplyAlpha() == "default" || |
| 63 options.premultiplyAlpha() == "premultiply"); | 62 options.premultiplyAlpha() == "premultiply"); |
| 64 } | 63 } |
| 65 | 64 |
| 65 if (options.colorSpaceConversion() != "none") { | |
| 66 if (!RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled() || | |
| 67 !RuntimeEnabledFeatures::colorCorrectRenderingEnabled()) { | |
| 68 DCHECK_EQ(options.colorSpaceConversion(), "default"); | |
| 69 // TODO(zakerinasab): Replace sRGB with a call to | |
| 70 // ImageDecoder::globalTargetColorSpace() when the crash problem on Mac | |
| 71 // is fixed. crbug.com/668546. | |
| 72 parsedOptions.dstColorSpace = | |
| 73 SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named); | |
| 74 parsedOptions.dstColorType = SkColorType::kN32_SkColorType; | |
| 75 } else { | |
| 76 if (options.colorSpaceConversion() == "default" || | |
| 77 options.colorSpaceConversion() == "srgb") { | |
| 78 parsedOptions.dstColorSpace = | |
| 79 SkColorSpace::MakeNamed(SkColorSpace::kSRGB_Named); | |
| 80 parsedOptions.dstColorType = SkColorType::kN32_SkColorType; | |
| 81 } else if (options.colorSpaceConversion() == "linear-rgb") { | |
| 82 parsedOptions.bytesPerPixel = 8; | |
|
Justin Novosad
2016/11/28 16:30:43
avoid hard-coded values. IMHO, we should remove '
zakerinasab
2016/11/28 19:12:31
Done.
| |
| 83 parsedOptions.dstColorSpace = | |
| 84 SkColorSpace::MakeNamed(SkColorSpace::kSRGBLinear_Named); | |
| 85 parsedOptions.dstColorType = SkColorType::kRGBA_F16_SkColorType; | |
| 86 } else { | |
| 87 NOTREACHED() | |
| 88 << "Invalid ImageBitmap creation attribute colorSpaceConversion: " | |
| 89 << options.colorSpaceConversion(); | |
| 90 } | |
| 91 } | |
| 92 } | |
| 93 | |
| 66 int sourceWidth = sourceSize.width(); | 94 int sourceWidth = sourceSize.width(); |
| 67 int sourceHeight = sourceSize.height(); | 95 int sourceHeight = sourceSize.height(); |
| 68 if (!cropRect) { | 96 if (!cropRect) { |
| 69 parsedOptions.cropRect = IntRect(0, 0, sourceWidth, sourceHeight); | 97 parsedOptions.cropRect = IntRect(0, 0, sourceWidth, sourceHeight); |
| 70 } else { | 98 } else { |
| 71 parsedOptions.cropRect = normalizeRect(*cropRect); | 99 parsedOptions.cropRect = normalizeRect(*cropRect); |
| 72 } | 100 } |
| 73 if (!options.hasResizeWidth() && !options.hasResizeHeight()) { | 101 if (!options.hasResizeWidth() && !options.hasResizeHeight()) { |
| 74 parsedOptions.resizeWidth = parsedOptions.cropRect.width(); | 102 parsedOptions.resizeWidth = parsedOptions.cropRect.width(); |
| 75 parsedOptions.resizeHeight = parsedOptions.cropRect.height(); | 103 parsedOptions.resizeHeight = parsedOptions.cropRect.height(); |
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 225 SkImageInfo info = SkImageInfo::Make(input->width(), input->height(), | 253 SkImageInfo info = SkImageInfo::Make(input->width(), input->height(), |
| 226 kN32_SkColorType, kPremul_SkAlphaType); | 254 kN32_SkColorType, kPremul_SkAlphaType); |
| 227 RefPtr<Uint8Array> dstPixels = copySkImageData(input, info); | 255 RefPtr<Uint8Array> dstPixels = copySkImageData(input, info); |
| 228 if (!dstPixels) | 256 if (!dstPixels) |
| 229 return nullptr; | 257 return nullptr; |
| 230 return newSkImageFromRaster( | 258 return newSkImageFromRaster( |
| 231 info, std::move(dstPixels), | 259 info, std::move(dstPixels), |
| 232 static_cast<unsigned>(input->width()) * info.bytesPerPixel()); | 260 static_cast<unsigned>(input->width()) * info.bytesPerPixel()); |
| 233 } | 261 } |
| 234 | 262 |
| 263 static sk_sp<SkImage> applyColorSpaceConversion( | |
| 264 sk_sp<SkImage> image, | |
| 265 const ParsedOptions& parsedOptions) { | |
| 266 if (!parsedOptions.dstColorSpace) | |
| 267 return image; | |
| 268 | |
| 269 SkImageInfo imageInfo = SkImageInfo::Make( | |
| 270 image->width(), image->height(), parsedOptions.dstColorType, | |
| 271 image->alphaType(), parsedOptions.dstColorSpace); | |
| 272 sk_sp<SkSurface> surface = SkSurface::MakeRaster(imageInfo); | |
| 273 surface->getCanvas()->drawImage(image, 0, 0); | |
|
Justin Novosad
2016/11/28 16:30:42
This works, but it would be even better to have a
zakerinasab
2016/11/28 19:12:31
Done.
| |
| 274 return surface->makeImageSnapshot(); | |
| 275 } | |
| 276 | |
| 235 sk_sp<SkImage> ImageBitmap::getSkImageFromDecoder( | 277 sk_sp<SkImage> ImageBitmap::getSkImageFromDecoder( |
| 236 std::unique_ptr<ImageDecoder> decoder) { | 278 std::unique_ptr<ImageDecoder> decoder) { |
| 237 if (!decoder->frameCount()) | 279 if (!decoder->frameCount()) |
| 238 return nullptr; | 280 return nullptr; |
| 239 ImageFrame* frame = decoder->frameBufferAtIndex(0); | 281 ImageFrame* frame = decoder->frameBufferAtIndex(0); |
| 240 if (!frame || frame->getStatus() != ImageFrame::FrameComplete) | 282 if (!frame || frame->getStatus() != ImageFrame::FrameComplete) |
| 241 return nullptr; | 283 return nullptr; |
| 242 DCHECK(!frame->bitmap().isNull() && !frame->bitmap().empty()); | 284 DCHECK(!frame->bitmap().isNull() && !frame->bitmap().empty()); |
| 243 return frame->finalizePixelsAndGetImage(); | 285 return frame->finalizePixelsAndGetImage(); |
| 244 } | 286 } |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 315 colorSpaceOp)); | 357 colorSpaceOp)); |
| 316 if (!decoder) | 358 if (!decoder) |
| 317 return nullptr; | 359 return nullptr; |
| 318 skiaImage = ImageBitmap::getSkImageFromDecoder(std::move(decoder)); | 360 skiaImage = ImageBitmap::getSkImageFromDecoder(std::move(decoder)); |
| 319 if (!skiaImage) | 361 if (!skiaImage) |
| 320 return nullptr; | 362 return nullptr; |
| 321 } | 363 } |
| 322 | 364 |
| 323 if (parsedOptions.cropRect == srcRect && !parsedOptions.shouldScaleInput) { | 365 if (parsedOptions.cropRect == srcRect && !parsedOptions.shouldScaleInput) { |
| 324 sk_sp<SkImage> croppedSkImage = skiaImage->makeSubset(srcRect); | 366 sk_sp<SkImage> croppedSkImage = skiaImage->makeSubset(srcRect); |
| 367 if (parsedOptions.dstColorSpace) { | |
| 368 croppedSkImage = applyColorSpaceConversion(croppedSkImage, parsedOptions); | |
| 369 } | |
| 325 if (parsedOptions.flipY) | 370 if (parsedOptions.flipY) |
| 326 return StaticBitmapImage::create(flipSkImageVertically( | 371 return StaticBitmapImage::create(flipSkImageVertically( |
| 327 croppedSkImage.get(), parsedOptions.premultiplyAlpha | 372 croppedSkImage.get(), parsedOptions.premultiplyAlpha |
| 328 ? PremultiplyAlpha | 373 ? PremultiplyAlpha |
| 329 : DontPremultiplyAlpha)); | 374 : DontPremultiplyAlpha)); |
| 330 // Special case: The first parameter image is unpremul but we need to turn | 375 // Special case: The first parameter image is unpremul but we need to turn |
| 331 // it into premul. | 376 // it into premul. |
| 332 if (parsedOptions.premultiplyAlpha && imageFormat == DontPremultiplyAlpha) | 377 if (parsedOptions.premultiplyAlpha && imageFormat == DontPremultiplyAlpha) |
| 333 return StaticBitmapImage::create( | 378 return StaticBitmapImage::create( |
| 334 unPremulSkImageToPremul(croppedSkImage.get())); | 379 unPremulSkImageToPremul(croppedSkImage.get())); |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 361 SkRect drawDstRect = SkRect::MakeXYWH(0, 0, parsedOptions.resizeWidth, | 406 SkRect drawDstRect = SkRect::MakeXYWH(0, 0, parsedOptions.resizeWidth, |
| 362 parsedOptions.resizeHeight); | 407 parsedOptions.resizeHeight); |
| 363 SkPaint paint; | 408 SkPaint paint; |
| 364 paint.setFilterQuality(parsedOptions.resizeQuality); | 409 paint.setFilterQuality(parsedOptions.resizeQuality); |
| 365 surface->getCanvas()->drawImageRect(skiaImage, drawSrcRect, drawDstRect, | 410 surface->getCanvas()->drawImageRect(skiaImage, drawSrcRect, drawDstRect, |
| 366 &paint); | 411 &paint); |
| 367 } else { | 412 } else { |
| 368 surface->getCanvas()->drawImage(skiaImage, dstLeft, dstTop); | 413 surface->getCanvas()->drawImage(skiaImage, dstLeft, dstTop); |
| 369 } | 414 } |
| 370 skiaImage = surface->makeImageSnapshot(); | 415 skiaImage = surface->makeImageSnapshot(); |
| 416 if (parsedOptions.dstColorSpace) | |
| 417 skiaImage = applyColorSpaceConversion(skiaImage, parsedOptions); | |
| 371 | 418 |
| 372 if (parsedOptions.premultiplyAlpha) { | 419 if (parsedOptions.premultiplyAlpha) { |
| 373 if (imageFormat == DontPremultiplyAlpha) | 420 if (imageFormat == DontPremultiplyAlpha) |
| 374 return StaticBitmapImage::create( | 421 return StaticBitmapImage::create( |
| 375 unPremulSkImageToPremul(skiaImage.get())); | 422 unPremulSkImageToPremul(skiaImage.get())); |
| 376 return StaticBitmapImage::create(std::move(skiaImage)); | 423 return StaticBitmapImage::create(std::move(skiaImage)); |
| 377 } | 424 } |
| 378 return StaticBitmapImage::create(premulSkImageToUnPremul(skiaImage.get())); | 425 return StaticBitmapImage::create(premulSkImageToUnPremul(skiaImage.get())); |
| 379 } | 426 } |
| 380 | 427 |
| 381 ImageBitmap::ImageBitmap(HTMLImageElement* image, | 428 ImageBitmap::ImageBitmap(HTMLImageElement* image, |
| 382 Optional<IntRect> cropRect, | 429 Optional<IntRect> cropRect, |
| 383 Document* document, | 430 Document* document, |
| 384 const ImageBitmapOptions& options) { | 431 const ImageBitmapOptions& options) { |
| 385 RefPtr<Image> input = image->cachedImage()->getImage(); | 432 RefPtr<Image> input = image->cachedImage()->getImage(); |
| 386 ParsedOptions parsedOptions = | 433 ParsedOptions parsedOptions = |
| 387 parseOptions(options, cropRect, image->bitmapSourceSize()); | 434 parseOptions(options, cropRect, image->bitmapSourceSize()); |
| 388 if (dstBufferSizeHasOverflow(parsedOptions)) | 435 if (dstBufferSizeHasOverflow(parsedOptions)) |
| 389 return; | 436 return; |
| 390 | 437 |
| 391 if (options.colorSpaceConversion() == "none") { | 438 m_image = |
| 392 m_image = cropImage(input.get(), parsedOptions, PremultiplyAlpha, | 439 cropImage(input.get(), parsedOptions, PremultiplyAlpha, |
| 393 ImageDecoder::ColorSpaceIgnored); | 440 parsedOptions.dstColorSpace ? ImageDecoder::ColorSpaceApplied |
| 394 } else { | 441 : ImageDecoder::ColorSpaceIgnored); |
| 395 m_image = cropImage(input.get(), parsedOptions, PremultiplyAlpha, | |
| 396 ImageDecoder::ColorSpaceApplied); | |
| 397 } | |
| 398 if (!m_image) | 442 if (!m_image) |
| 399 return; | 443 return; |
| 400 // In the case where the source image is lazy-decoded, m_image may not be in | 444 // In the case where the source image is lazy-decoded, m_image may not be in |
| 401 // a decoded state, we trigger it here. | 445 // a decoded state, we trigger it here. |
| 402 sk_sp<SkImage> skImage = m_image->imageForCurrentFrame(); | 446 sk_sp<SkImage> skImage = m_image->imageForCurrentFrame(); |
| 403 SkPixmap pixmap; | 447 SkPixmap pixmap; |
| 404 if (!skImage->isTextureBacked() && !skImage->peekPixels(&pixmap)) { | 448 if (!skImage->isTextureBacked() && !skImage->peekPixels(&pixmap)) { |
| 405 sk_sp<SkSurface> surface = | 449 sk_sp<SkSurface> surface = |
| 406 SkSurface::MakeRasterN32Premul(skImage->width(), skImage->height()); | 450 SkSurface::MakeRasterN32Premul(skImage->width(), skImage->height()); |
| 407 surface->getCanvas()->drawImage(skImage, 0, 0); | 451 surface->getCanvas()->drawImage(skImage, 0, 0); |
| (...skipping 454 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 862 void ImageBitmap::adjustDrawRects(FloatRect* srcRect, | 906 void ImageBitmap::adjustDrawRects(FloatRect* srcRect, |
| 863 FloatRect* dstRect) const {} | 907 FloatRect* dstRect) const {} |
| 864 | 908 |
| 865 FloatSize ImageBitmap::elementSize(const FloatSize&) const { | 909 FloatSize ImageBitmap::elementSize(const FloatSize&) const { |
| 866 return FloatSize(width(), height()); | 910 return FloatSize(width(), height()); |
| 867 } | 911 } |
| 868 | 912 |
| 869 DEFINE_TRACE(ImageBitmap) {} | 913 DEFINE_TRACE(ImageBitmap) {} |
| 870 | 914 |
| 871 } // namespace blink | 915 } // namespace blink |
| OLD | NEW |