Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6)

Side by Side Diff: third_party/WebKit/Source/core/frame/ImageBitmap.cpp

Issue 2924373002: color: Convert BitmapImage to sRGB ahead of time (Closed)
Patch Set: Add scale bits Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 <memory> 7 #include <memory>
8 #include "core/html/HTMLCanvasElement.h" 8 #include "core/html/HTMLCanvasElement.h"
9 #include "core/html/HTMLVideoElement.h" 9 #include "core/html/HTMLVideoElement.h"
10 #include "core/html/ImageData.h" 10 #include "core/html/ImageData.h"
11 #include "core/offscreencanvas/OffscreenCanvas.h" 11 #include "core/offscreencanvas/OffscreenCanvas.h"
12 #include "platform/graphics/CanvasColorParams.h" 12 #include "platform/graphics/CanvasColorParams.h"
13 #include "platform/graphics/skia/SkiaUtils.h" 13 #include "platform/graphics/skia/SkiaUtils.h"
14 #include "platform/image-decoders/ImageDecoder.h" 14 #include "platform/image-decoders/ImageDecoder.h"
15 #include "platform/wtf/CheckedNumeric.h" 15 #include "platform/wtf/CheckedNumeric.h"
16 #include "platform/wtf/PtrUtil.h" 16 #include "platform/wtf/PtrUtil.h"
17 #include "platform/wtf/RefPtr.h" 17 #include "platform/wtf/RefPtr.h"
18 #include "third_party/skia/include/core/SkCanvas.h" 18 #include "third_party/skia/include/core/SkCanvas.h"
19 #include "third_party/skia/include/core/SkColorSpaceXformCanvas.h"
19 #include "third_party/skia/include/core/SkImageInfo.h" 20 #include "third_party/skia/include/core/SkImageInfo.h"
20 #include "third_party/skia/include/core/SkSurface.h" 21 #include "third_party/skia/include/core/SkSurface.h"
21 22
22 namespace blink { 23 namespace blink {
23 24
24 constexpr const char* kImageOrientationFlipY = "flipY"; 25 constexpr const char* kImageOrientationFlipY = "flipY";
25 constexpr const char* kImageBitmapOptionNone = "none"; 26 constexpr const char* kImageBitmapOptionNone = "none";
26 constexpr const char* kImageBitmapOptionDefault = "default"; 27 constexpr const char* kImageBitmapOptionDefault = "default";
27 constexpr const char* kImageBitmapOptionPremultiply = "premultiply"; 28 constexpr const char* kImageBitmapOptionPremultiply = "premultiply";
28 constexpr const char* kImageBitmapOptionResizeQualityHigh = "high"; 29 constexpr const char* kImageBitmapOptionResizeQualityHigh = "high";
29 constexpr const char* kImageBitmapOptionResizeQualityMedium = "medium"; 30 constexpr const char* kImageBitmapOptionResizeQualityMedium = "medium";
30 constexpr const char* kImageBitmapOptionResizeQualityPixelated = "pixelated"; 31 constexpr const char* kImageBitmapOptionResizeQualityPixelated = "pixelated";
31 constexpr const char* kSRGBImageBitmapColorSpaceConversion = "srgb"; 32 constexpr const char* kSRGBImageBitmapColorSpaceConversion = "srgb";
32 constexpr const char* kLinearRGBImageBitmapColorSpaceConversion = "linear-rgb"; 33 constexpr const char* kLinearRGBImageBitmapColorSpaceConversion = "linear-rgb";
33 constexpr const char* kP3ImageBitmapColorSpaceConversion = "p3"; 34 constexpr const char* kP3ImageBitmapColorSpaceConversion = "p3";
34 constexpr const char* kRec2020ImageBitmapColorSpaceConversion = "rec2020"; 35 constexpr const char* kRec2020ImageBitmapColorSpaceConversion = "rec2020";
35 36
36 namespace { 37 namespace {
37 38
38 struct ParsedOptions { 39 struct ParsedOptions {
39 bool flip_y = false; 40 bool flip_y = false;
40 bool premultiply_alpha = true; 41 bool premultiply_alpha = true;
41 bool should_scale_input = false; 42 bool should_scale_input = false;
42 unsigned resize_width = 0; 43 unsigned resize_width = 0;
43 unsigned resize_height = 0; 44 unsigned resize_height = 0;
44 IntRect crop_rect; 45 IntRect crop_rect;
45 SkFilterQuality resize_quality = kLow_SkFilterQuality; 46 SkFilterQuality resize_quality = kLow_SkFilterQuality;
46 CanvasColorParams color_params; 47 CanvasColorParams color_params;
47 bool color_canvas_extensions_enabled = false; 48 bool color_canvas_extensions_enabled = false;
49 bool convert_to_srgb = false;
Justin Novosad 2017/06/09 14:08:34 This new member is confusing IMHO because it poten
ccameron 2017/06/09 17:01:33 I removed this and replaced it with "color_params.
48 }; 50 };
49 51
50 ParsedOptions DefaultOptions() { 52 ParsedOptions DefaultOptions() {
51 return ParsedOptions(); 53 return ParsedOptions();
52 } 54 }
53 55
54 // The following two functions are helpers used in cropImage 56 // The following two functions are helpers used in cropImage
55 static inline IntRect NormalizeRect(const IntRect& rect) { 57 static inline IntRect NormalizeRect(const IntRect& rect) {
56 return IntRect(std::min(rect.X(), rect.MaxX()), 58 return IntRect(std::min(rect.X(), rect.MaxX()),
57 std::min(rect.Y(), rect.MaxY()), 59 std::min(rect.Y(), rect.MaxY()),
(...skipping 13 matching lines...) Expand all
71 } 73 }
72 if (options.premultiplyAlpha() == kImageBitmapOptionNone) { 74 if (options.premultiplyAlpha() == kImageBitmapOptionNone) {
73 parsed_options.premultiply_alpha = false; 75 parsed_options.premultiply_alpha = false;
74 } else { 76 } else {
75 parsed_options.premultiply_alpha = true; 77 parsed_options.premultiply_alpha = true;
76 DCHECK(options.premultiplyAlpha() == kImageBitmapOptionDefault || 78 DCHECK(options.premultiplyAlpha() == kImageBitmapOptionDefault ||
77 options.premultiplyAlpha() == kImageBitmapOptionPremultiply); 79 options.premultiplyAlpha() == kImageBitmapOptionPremultiply);
78 } 80 }
79 81
80 if (options.colorSpaceConversion() != kImageBitmapOptionNone) { 82 if (options.colorSpaceConversion() != kImageBitmapOptionNone) {
81 parsed_options.color_canvas_extensions_enabled = 83 if (RuntimeEnabledFeatures::ColorCanvasExtensionsEnabled()) {
82 RuntimeEnabledFeatures::ColorCanvasExtensionsEnabled(); 84 parsed_options.color_canvas_extensions_enabled = true;
85 } else {
86 // When canvas color extensions are not enabled, all images may be
87 // converted ahead of time to sRGB, since that is what their destination
88 // will always be sRGB.
89 parsed_options.convert_to_srgb =
90 RuntimeEnabledFeatures::ColorCorrectRenderingEnabled();
91 }
92
83 if (!parsed_options.color_canvas_extensions_enabled) { 93 if (!parsed_options.color_canvas_extensions_enabled) {
84 DCHECK_EQ(options.colorSpaceConversion(), kImageBitmapOptionDefault); 94 DCHECK_EQ(options.colorSpaceConversion(), kImageBitmapOptionDefault);
85 } else { 95 } else {
86 if (options.colorSpaceConversion() == kImageBitmapOptionDefault || 96 if (options.colorSpaceConversion() == kImageBitmapOptionDefault ||
87 options.colorSpaceConversion() == 97 options.colorSpaceConversion() ==
88 kSRGBImageBitmapColorSpaceConversion) { 98 kSRGBImageBitmapColorSpaceConversion) {
89 parsed_options.color_params.SetCanvasColorSpace(kSRGBCanvasColorSpace); 99 parsed_options.color_params.SetCanvasColorSpace(kSRGBCanvasColorSpace);
90 } else if (options.colorSpaceConversion() == 100 } else if (options.colorSpaceConversion() ==
91 kLinearRGBImageBitmapColorSpaceConversion) { 101 kLinearRGBImageBitmapColorSpaceConversion) {
92 parsed_options.color_params.SetCanvasColorSpace(kSRGBCanvasColorSpace); 102 parsed_options.color_params.SetCanvasColorSpace(kSRGBCanvasColorSpace);
(...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after
295 RefPtr<Uint8Array> dst_pixels = CopySkImageData(input, info); 305 RefPtr<Uint8Array> dst_pixels = CopySkImageData(input, info);
296 if (!dst_pixels) 306 if (!dst_pixels)
297 return nullptr; 307 return nullptr;
298 return NewSkImageFromRaster( 308 return NewSkImageFromRaster(
299 info, std::move(dst_pixels), 309 info, std::move(dst_pixels),
300 static_cast<unsigned>(input->width()) * info.bytesPerPixel()); 310 static_cast<unsigned>(input->width()) * info.bytesPerPixel());
301 } 311 }
302 312
303 static void ApplyColorSpaceConversion(sk_sp<SkImage>& image, 313 static void ApplyColorSpaceConversion(sk_sp<SkImage>& image,
304 ParsedOptions& options) { 314 ParsedOptions& options) {
315 if (options.convert_to_srgb) {
316 image = image->makeColorSpace(SkColorSpace::MakeSRGB(),
317 SkTransferFunctionBehavior::kIgnore);
318 }
305 if (!options.color_canvas_extensions_enabled) 319 if (!options.color_canvas_extensions_enabled)
306 return; 320 return;
307 321
308 sk_sp<SkColorSpace> dst_color_space = nullptr; 322 sk_sp<SkColorSpace> dst_color_space = nullptr;
309 SkColorType dst_color_type = kN32_SkColorType; 323 SkColorType dst_color_type = kN32_SkColorType;
310 dst_color_space = options.color_params.GetSkColorSpace(); 324 dst_color_space = options.color_params.GetSkColorSpace();
311 dst_color_type = options.color_params.GetSkColorType(); 325 dst_color_type = options.color_params.GetSkColorType();
312 if (SkColorSpace::Equals(image->colorSpace(), dst_color_space.get())) 326 if (SkColorSpace::Equals(image->colorSpace(), dst_color_space.get()))
313 return; 327 return;
314 328
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after
466 // Special case: The first parameter image is unpremul but we need to turn 480 // Special case: The first parameter image is unpremul but we need to turn
467 // it into premul. 481 // it into premul.
468 if (parsed_options.premultiply_alpha && 482 if (parsed_options.premultiply_alpha &&
469 image_format == kDontPremultiplyAlpha) { 483 image_format == kDontPremultiplyAlpha) {
470 return StaticBitmapImage::Create( 484 return StaticBitmapImage::Create(
471 UnPremulSkImageToPremul(cropped_sk_image.get())); 485 UnPremulSkImageToPremul(cropped_sk_image.get()));
472 } 486 }
473 return StaticBitmapImage::Create(std::move(cropped_sk_image)); 487 return StaticBitmapImage::Create(std::move(cropped_sk_image));
474 } 488 }
475 489
476 sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul( 490 sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(
Justin Novosad 2017/06/09 14:08:34 I think it would be better to just specify a color
ccameron 2017/06/09 17:01:33 Doing that will opt us in to linear blending, whic
477 parsed_options.resize_width, parsed_options.resize_height); 491 parsed_options.resize_width, parsed_options.resize_height);
478 if (!surface) 492 if (!surface)
479 return nullptr; 493 return nullptr;
480 if (src_rect.IsEmpty()) 494 if (src_rect.IsEmpty())
481 return StaticBitmapImage::Create(surface->makeImageSnapshot()); 495 return StaticBitmapImage::Create(surface->makeImageSnapshot());
482 496
497 SkCanvas* canvas = surface->getCanvas();
498 std::unique_ptr<SkCanvas> color_transform_canvas;
499 if (parsed_options.convert_to_srgb) {
500 color_transform_canvas =
501 SkCreateColorSpaceXformCanvas(canvas, SkColorSpace::MakeSRGB());
502 canvas = color_transform_canvas.get();
503 }
504
483 SkScalar dst_left = std::min(0, -parsed_options.crop_rect.X()); 505 SkScalar dst_left = std::min(0, -parsed_options.crop_rect.X());
484 SkScalar dst_top = std::min(0, -parsed_options.crop_rect.Y()); 506 SkScalar dst_top = std::min(0, -parsed_options.crop_rect.Y());
485 if (parsed_options.crop_rect.X() < 0) 507 if (parsed_options.crop_rect.X() < 0)
486 dst_left = -parsed_options.crop_rect.X(); 508 dst_left = -parsed_options.crop_rect.X();
487 if (parsed_options.crop_rect.Y() < 0) 509 if (parsed_options.crop_rect.Y() < 0)
488 dst_top = -parsed_options.crop_rect.Y(); 510 dst_top = -parsed_options.crop_rect.Y();
489 if (parsed_options.flip_y) { 511 if (parsed_options.flip_y) {
490 surface->getCanvas()->translate(0, surface->height()); 512 canvas->translate(0, surface->height());
491 surface->getCanvas()->scale(1, -1); 513 canvas->scale(1, -1);
492 } 514 }
493 if (parsed_options.should_scale_input) { 515 if (parsed_options.should_scale_input) {
494 SkRect draw_src_rect = SkRect::MakeXYWH( 516 SkRect draw_src_rect = SkRect::MakeXYWH(
495 parsed_options.crop_rect.X(), parsed_options.crop_rect.Y(), 517 parsed_options.crop_rect.X(), parsed_options.crop_rect.Y(),
496 parsed_options.crop_rect.Width(), parsed_options.crop_rect.Height()); 518 parsed_options.crop_rect.Width(), parsed_options.crop_rect.Height());
497 SkRect draw_dst_rect = SkRect::MakeXYWH(0, 0, parsed_options.resize_width, 519 SkRect draw_dst_rect = SkRect::MakeXYWH(0, 0, parsed_options.resize_width,
498 parsed_options.resize_height); 520 parsed_options.resize_height);
499 SkPaint paint; 521 SkPaint paint;
500 paint.setFilterQuality(parsed_options.resize_quality); 522 paint.setFilterQuality(parsed_options.resize_quality);
501 surface->getCanvas()->drawImageRect(skia_image, draw_src_rect, 523 canvas->drawImageRect(skia_image, draw_src_rect, draw_dst_rect, &paint);
502 draw_dst_rect, &paint);
503 } else { 524 } else {
504 surface->getCanvas()->drawImage(skia_image, dst_left, dst_top); 525 canvas->drawImage(skia_image, dst_left, dst_top);
505 } 526 }
506 skia_image = surface->makeImageSnapshot(); 527 skia_image = surface->makeImageSnapshot();
507 ApplyColorSpaceConversion(skia_image, parsed_options); 528 ApplyColorSpaceConversion(skia_image, parsed_options);
508 529
509 if (parsed_options.premultiply_alpha) { 530 if (parsed_options.premultiply_alpha) {
510 if (image_format == kDontPremultiplyAlpha) 531 if (image_format == kDontPremultiplyAlpha)
511 return StaticBitmapImage::Create( 532 return StaticBitmapImage::Create(
512 UnPremulSkImageToPremul(skia_image.get())); 533 UnPremulSkImageToPremul(skia_image.get()));
513 return StaticBitmapImage::Create(std::move(skia_image)); 534 return StaticBitmapImage::Create(std::move(skia_image));
514 } 535 }
(...skipping 28 matching lines...) Expand all
543 SkPixmap pixmap; 564 SkPixmap pixmap;
544 if (!sk_image->isTextureBacked() && !sk_image->peekPixels(&pixmap)) { 565 if (!sk_image->isTextureBacked() && !sk_image->peekPixels(&pixmap)) {
545 sk_sp<SkColorSpace> dst_color_space = nullptr; 566 sk_sp<SkColorSpace> dst_color_space = nullptr;
546 SkColorType dst_color_type = kN32_SkColorType; 567 SkColorType dst_color_type = kN32_SkColorType;
547 if (parsed_options.color_canvas_extensions_enabled) { 568 if (parsed_options.color_canvas_extensions_enabled) {
548 dst_color_space = parsed_options.color_params.GetSkColorSpace(); 569 dst_color_space = parsed_options.color_params.GetSkColorSpace();
549 dst_color_type = parsed_options.color_params.GetSkColorType(); 570 dst_color_type = parsed_options.color_params.GetSkColorType();
550 } 571 }
551 SkImageInfo image_info = 572 SkImageInfo image_info =
552 SkImageInfo::Make(sk_image->width(), sk_image->height(), dst_color_type, 573 SkImageInfo::Make(sk_image->width(), sk_image->height(), dst_color_type,
553 kPremul_SkAlphaType, dst_color_space); 574 kPremul_SkAlphaType, dst_color_space);
Justin Novosad 2017/06/09 14:08:34 We should just tweak the dst_color_space used here
ccameron 2017/06/09 17:01:33 Updated this to still use the xform canvas, but to
554 sk_sp<SkSurface> surface = SkSurface::MakeRaster(image_info); 575 sk_sp<SkSurface> surface = SkSurface::MakeRaster(image_info);
555 surface->getCanvas()->drawImage(sk_image, 0, 0); 576 SkCanvas* canvas = surface->getCanvas();
577 std::unique_ptr<SkCanvas> color_transform_canvas;
578 if (parsed_options.convert_to_srgb) {
579 color_transform_canvas =
580 SkCreateColorSpaceXformCanvas(canvas, SkColorSpace::MakeSRGB());
581 canvas = color_transform_canvas.get();
582 }
583 canvas->drawImage(sk_image, 0, 0);
556 image_ = StaticBitmapImage::Create(surface->makeImageSnapshot()); 584 image_ = StaticBitmapImage::Create(surface->makeImageSnapshot());
557 } 585 }
558 if (!image_) 586 if (!image_)
559 return; 587 return;
560 image_->SetOriginClean( 588 image_->SetOriginClean(
561 !image->WouldTaintOrigin(document->GetSecurityOrigin())); 589 !image->WouldTaintOrigin(document->GetSecurityOrigin()));
562 image_->SetPremultiplied(parsed_options.premultiply_alpha); 590 image_->SetPremultiplied(parsed_options.premultiply_alpha);
563 } 591 }
564 592
565 ImageBitmap::ImageBitmap(HTMLVideoElement* video, 593 ImageBitmap::ImageBitmap(HTMLVideoElement* video,
(...skipping 332 matching lines...) Expand 10 before | Expand all | Expand 10 after
898 src_rect, dst_point); 926 src_rect, dst_point);
899 sk_sp<SkImage> sk_image = 927 sk_sp<SkImage> sk_image =
900 buffer->NewSkImageSnapshot(kPreferNoAcceleration, kSnapshotReasonUnknown); 928 buffer->NewSkImageSnapshot(kPreferNoAcceleration, kSnapshotReasonUnknown);
901 if (parsed_options.flip_y) 929 if (parsed_options.flip_y)
902 sk_image = FlipSkImageVertically(sk_image.get(), kEnforceAlphaPremultiply); 930 sk_image = FlipSkImageVertically(sk_image.get(), kEnforceAlphaPremultiply);
903 if (!sk_image) 931 if (!sk_image)
904 return; 932 return;
905 if (parsed_options.should_scale_input) { 933 if (parsed_options.should_scale_input) {
906 sk_sp<SkSurface> surface = SkSurface::MakeRaster(SkImageInfo::MakeN32Premul( 934 sk_sp<SkSurface> surface = SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(
907 parsed_options.resize_width, parsed_options.resize_height, 935 parsed_options.resize_width, parsed_options.resize_height,
908 data->GetSkColorSpace())); 936 data->GetSkColorSpace()));
Justin Novosad 2017/06/09 14:08:34 data->GetColorSpace() should return return srgb if
ccameron 2017/06/09 17:01:33 Same here.
909 if (!surface) 937 if (!surface)
910 return; 938 return;
911 SkPaint paint; 939 SkPaint paint;
912 paint.setFilterQuality(parsed_options.resize_quality); 940 paint.setFilterQuality(parsed_options.resize_quality);
913 SkRect dst_draw_rect = SkRect::MakeWH(parsed_options.resize_width, 941 SkRect dst_draw_rect = SkRect::MakeWH(parsed_options.resize_width,
914 parsed_options.resize_height); 942 parsed_options.resize_height);
915 surface->getCanvas()->drawImageRect(sk_image, dst_draw_rect, &paint); 943 SkCanvas* canvas = surface->getCanvas();
944 std::unique_ptr<SkCanvas> color_transform_canvas;
945 if (parsed_options.convert_to_srgb) {
946 color_transform_canvas =
947 SkCreateColorSpaceXformCanvas(canvas, SkColorSpace::MakeSRGB());
948 canvas = color_transform_canvas.get();
949 }
950 canvas->drawImageRect(sk_image, dst_draw_rect, &paint);
916 sk_image = surface->makeImageSnapshot(); 951 sk_image = surface->makeImageSnapshot();
917 } 952 }
918 ApplyColorSpaceConversion(sk_image, parsed_options); 953 ApplyColorSpaceConversion(sk_image, parsed_options);
919 image_ = StaticBitmapImage::Create(std::move(sk_image)); 954 image_ = StaticBitmapImage::Create(std::move(sk_image));
920 } 955 }
921 956
922 ImageBitmap::ImageBitmap(ImageBitmap* bitmap, 957 ImageBitmap::ImageBitmap(ImageBitmap* bitmap,
923 Optional<IntRect> crop_rect, 958 Optional<IntRect> crop_rect,
924 const ImageBitmapOptions& options) { 959 const ImageBitmapOptions& options) {
925 RefPtr<Image> input = bitmap->BitmapImage(); 960 RefPtr<Image> input = bitmap->BitmapImage();
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after
1115 void ImageBitmap::AdjustDrawRects(FloatRect* src_rect, 1150 void ImageBitmap::AdjustDrawRects(FloatRect* src_rect,
1116 FloatRect* dst_rect) const {} 1151 FloatRect* dst_rect) const {}
1117 1152
1118 FloatSize ImageBitmap::ElementSize(const FloatSize&) const { 1153 FloatSize ImageBitmap::ElementSize(const FloatSize&) const {
1119 return FloatSize(width(), height()); 1154 return FloatSize(width(), height());
1120 } 1155 }
1121 1156
1122 DEFINE_TRACE(ImageBitmap) {} 1157 DEFINE_TRACE(ImageBitmap) {}
1123 1158
1124 } // namespace blink 1159 } // namespace blink
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698