1 /*
2  * Copyright (C) 2008 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "third_party/blink/renderer/core/html/canvas/image_data.h"
30 
31 #include "base/sys_byteorder.h"
32 #include "third_party/blink/renderer/bindings/core/v8/v8_image_bitmap_options.h"
33 #include "third_party/blink/renderer/bindings/core/v8/v8_uint8_clamped_array.h"
34 #include "third_party/blink/renderer/core/dom/dom_exception.h"
35 #include "third_party/blink/renderer/core/imagebitmap/image_bitmap.h"
36 #include "third_party/blink/renderer/platform/graphics/color_behavior.h"
37 #include "third_party/skia/include/third_party/skcms/skcms.h"
38 #include "v8/include/v8.h"
39 
40 namespace blink {
41 
42 namespace {
43 
RaiseDOMExceptionAndReturnFalse(ExceptionState * exception_state,DOMExceptionCode exception_code,const char * message)44 bool RaiseDOMExceptionAndReturnFalse(ExceptionState* exception_state,
45                                      DOMExceptionCode exception_code,
46                                      const char* message) {
47   if (exception_state)
48     exception_state->ThrowDOMException(exception_code, message);
49   return false;
50 }
51 
52 }  // namespace
53 
ValidateConstructorArguments(const unsigned & param_flags,const IntSize * size,const unsigned & width,const unsigned & height,const NotShared<DOMArrayBufferView> data,const ImageDataColorSettings * color_settings,ExceptionState * exception_state)54 bool ImageData::ValidateConstructorArguments(
55     const unsigned& param_flags,
56     const IntSize* size,
57     const unsigned& width,
58     const unsigned& height,
59     const NotShared<DOMArrayBufferView> data,
60     const ImageDataColorSettings* color_settings,
61     ExceptionState* exception_state) {
62   // We accept all the combinations of colorSpace and storageFormat in an
63   // ImageDataColorSettings to be stored in an ImageData. Therefore, we don't
64   // check the color settings in this function.
65 
66   if ((param_flags & kParamWidth) && !width) {
67     return RaiseDOMExceptionAndReturnFalse(
68         exception_state, DOMExceptionCode::kIndexSizeError,
69         "The source width is zero or not a number.");
70   }
71 
72   if ((param_flags & kParamHeight) && !height) {
73     return RaiseDOMExceptionAndReturnFalse(
74         exception_state, DOMExceptionCode::kIndexSizeError,
75         "The source height is zero or not a number.");
76   }
77 
78   if (param_flags & (kParamWidth | kParamHeight)) {
79     base::CheckedNumeric<unsigned> data_size = 4;
80     if (color_settings) {
81       data_size *=
82           ImageData::StorageFormatDataSize(color_settings->storageFormat());
83     }
84     data_size *= width;
85     data_size *= height;
86     if (!data_size.IsValid()) {
87       return RaiseDOMExceptionAndReturnFalse(
88           exception_state, DOMExceptionCode::kIndexSizeError,
89           "The requested image size exceeds the supported range.");
90     }
91 
92     if (data_size.ValueOrDie() > v8::TypedArray::kMaxLength) {
93       if (exception_state) {
94         exception_state->ThrowRangeError(
95             "Out of memory at ImageData creation.");
96       }
97       return false;
98     }
99   }
100 
101   unsigned data_length = 0;
102   if (param_flags & kParamData) {
103     DCHECK(data);
104     if (data->GetType() != DOMArrayBufferView::ViewType::kTypeUint8Clamped &&
105         data->GetType() != DOMArrayBufferView::ViewType::kTypeUint16 &&
106         data->GetType() != DOMArrayBufferView::ViewType::kTypeFloat32) {
107       return RaiseDOMExceptionAndReturnFalse(
108           exception_state, DOMExceptionCode::kNotSupportedError,
109           "The input data type is not supported.");
110     }
111 
112     static_assert(
113         std::numeric_limits<unsigned>::max() >=
114             std::numeric_limits<uint32_t>::max(),
115         "We use UINT32_MAX as the upper bound of the input size and expect "
116         "that the result fits into an `unsigned`.");
117     if (!base::CheckedNumeric<uint32_t>(data->byteLengthAsSizeT())
118              .AssignIfValid(&data_length)) {
119       return RaiseDOMExceptionAndReturnFalse(
120           exception_state, DOMExceptionCode::kNotSupportedError,
121           "The input data is too large. The maximum size is 4294967295.");
122     }
123     if (!data_length) {
124       return RaiseDOMExceptionAndReturnFalse(
125           exception_state, DOMExceptionCode::kInvalidStateError,
126           "The input data has zero elements.");
127     }
128     data_length /= data->TypeSize();
129     if (data_length % 4) {
130       return RaiseDOMExceptionAndReturnFalse(
131           exception_state, DOMExceptionCode::kInvalidStateError,
132           "The input data length is not a multiple of 4.");
133     }
134 
135     if ((param_flags & kParamWidth) && (data_length / 4) % width) {
136       return RaiseDOMExceptionAndReturnFalse(
137           exception_state, DOMExceptionCode::kIndexSizeError,
138           "The input data length is not a multiple of (4 * width).");
139     }
140 
141     if ((param_flags & kParamWidth) && (param_flags & kParamHeight) &&
142         height != data_length / (4 * width))
143       return RaiseDOMExceptionAndReturnFalse(
144           exception_state, DOMExceptionCode::kIndexSizeError,
145           "The input data length is not equal to (4 * width * height).");
146   }
147 
148   if (param_flags & kParamSize) {
149     if (size->Width() <= 0 || size->Height() <= 0)
150       return false;
151     base::CheckedNumeric<unsigned> data_size = 4;
152     data_size *= size->Width();
153     data_size *= size->Height();
154     if (!data_size.IsValid() ||
155         data_size.ValueOrDie() > v8::TypedArray::kMaxLength)
156       return false;
157     if (param_flags & kParamData) {
158       if (data_size.ValueOrDie() > data_length)
159         return false;
160     }
161   }
162 
163   return true;
164 }
165 
AllocateAndValidateDataArray(const unsigned & length,ImageDataStorageFormat storage_format,ExceptionState * exception_state)166 NotShared<DOMArrayBufferView> ImageData::AllocateAndValidateDataArray(
167     const unsigned& length,
168     ImageDataStorageFormat storage_format,
169     ExceptionState* exception_state) {
170   if (!length)
171     return NotShared<DOMArrayBufferView>();
172 
173   NotShared<DOMArrayBufferView> data_array;
174   switch (storage_format) {
175     case kUint8ClampedArrayStorageFormat:
176       data_array = NotShared<DOMArrayBufferView>(
177           DOMUint8ClampedArray::CreateOrNull(length));
178       break;
179     case kUint16ArrayStorageFormat:
180       data_array =
181           NotShared<DOMArrayBufferView>(DOMUint16Array::CreateOrNull(length));
182       break;
183     case kFloat32ArrayStorageFormat:
184       data_array =
185           NotShared<DOMArrayBufferView>(DOMFloat32Array::CreateOrNull(length));
186       break;
187     default:
188       NOTREACHED();
189   }
190 
191   size_t expected_size;
192   if (!data_array || (!base::CheckMul(length, data_array->TypeSize())
193                            .AssignIfValid(&expected_size) &&
194                       expected_size != data_array->byteLengthAsSizeT())) {
195     if (exception_state)
196       exception_state->ThrowRangeError("Out of memory at ImageData creation");
197     return NotShared<DOMArrayBufferView>();
198   }
199 
200   return data_array;
201 }
202 
AllocateAndValidateUint8ClampedArray(const unsigned & length,ExceptionState * exception_state)203 NotShared<DOMUint8ClampedArray> ImageData::AllocateAndValidateUint8ClampedArray(
204     const unsigned& length,
205     ExceptionState* exception_state) {
206   NotShared<DOMUint8ClampedArray> buffer_view;
207   buffer_view = AllocateAndValidateDataArray(
208       length, kUint8ClampedArrayStorageFormat, exception_state);
209   return buffer_view;
210 }
211 
AllocateAndValidateUint16Array(const unsigned & length,ExceptionState * exception_state)212 NotShared<DOMUint16Array> ImageData::AllocateAndValidateUint16Array(
213     const unsigned& length,
214     ExceptionState* exception_state) {
215   NotShared<DOMUint16Array> buffer_view;
216   buffer_view = AllocateAndValidateDataArray(length, kUint16ArrayStorageFormat,
217                                              exception_state);
218   return buffer_view;
219 }
220 
AllocateAndValidateFloat32Array(const unsigned & length,ExceptionState * exception_state)221 NotShared<DOMFloat32Array> ImageData::AllocateAndValidateFloat32Array(
222     const unsigned& length,
223     ExceptionState* exception_state) {
224   NotShared<DOMFloat32Array> buffer_view;
225   buffer_view = AllocateAndValidateDataArray(length, kFloat32ArrayStorageFormat,
226                                              exception_state);
227   return buffer_view;
228 }
229 
Create(const IntSize & size,const ImageDataColorSettings * color_settings)230 ImageData* ImageData::Create(const IntSize& size,
231                              const ImageDataColorSettings* color_settings) {
232   if (!ValidateConstructorArguments(kParamSize, &size, 0, 0,
233                                     NotShared<DOMArrayBufferView>(),
234                                     color_settings))
235     return nullptr;
236   ImageDataStorageFormat storage_format = kUint8ClampedArrayStorageFormat;
237   if (color_settings) {
238     storage_format =
239         ImageData::GetImageDataStorageFormat(color_settings->storageFormat());
240   }
241   NotShared<DOMArrayBufferView> data_array =
242       AllocateAndValidateDataArray(4 * static_cast<unsigned>(size.Width()) *
243                                        static_cast<unsigned>(size.Height()),
244                                    storage_format);
245   return data_array
246              ? MakeGarbageCollected<ImageData>(size, data_array, color_settings)
247              : nullptr;
248 }
249 
CanvasColorParamsToImageDataColorSettings(const CanvasColorParams & color_params)250 ImageDataColorSettings* CanvasColorParamsToImageDataColorSettings(
251     const CanvasColorParams& color_params) {
252   ImageDataColorSettings* color_settings = ImageDataColorSettings::Create();
253   switch (color_params.ColorSpace()) {
254     case CanvasColorSpace::kSRGB:
255       color_settings->setColorSpace(kSRGBCanvasColorSpaceName);
256       break;
257     case CanvasColorSpace::kLinearRGB:
258       color_settings->setColorSpace(kLinearRGBCanvasColorSpaceName);
259       break;
260     case CanvasColorSpace::kRec2020:
261       color_settings->setColorSpace(kRec2020CanvasColorSpaceName);
262       break;
263     case CanvasColorSpace::kP3:
264       color_settings->setColorSpace(kP3CanvasColorSpaceName);
265       break;
266   }
267   color_settings->setStorageFormat(kUint8ClampedArrayStorageFormatName);
268   if (color_params.PixelFormat() == CanvasPixelFormat::kF16)
269     color_settings->setStorageFormat(kFloat32ArrayStorageFormatName);
270   return color_settings;
271 }
272 
Create(const IntSize & size,const CanvasColorParams & color_params)273 ImageData* ImageData::Create(const IntSize& size,
274                              const CanvasColorParams& color_params) {
275   ImageDataColorSettings* color_settings =
276       CanvasColorParamsToImageDataColorSettings(color_params);
277   return ImageData::Create(size, color_settings);
278 }
279 
Create(const IntSize & size,CanvasColorSpace color_space,ImageDataStorageFormat storage_format)280 ImageData* ImageData::Create(const IntSize& size,
281                              CanvasColorSpace color_space,
282                              ImageDataStorageFormat storage_format) {
283   ImageDataColorSettings* color_settings = ImageDataColorSettings::Create();
284   switch (color_space) {
285     case CanvasColorSpace::kSRGB:
286       color_settings->setColorSpace(kSRGBCanvasColorSpaceName);
287       break;
288     case CanvasColorSpace::kLinearRGB:
289       color_settings->setColorSpace(kLinearRGBCanvasColorSpaceName);
290       break;
291     case CanvasColorSpace::kRec2020:
292       color_settings->setColorSpace(kRec2020CanvasColorSpaceName);
293       break;
294     case CanvasColorSpace::kP3:
295       color_settings->setColorSpace(kP3CanvasColorSpaceName);
296       break;
297   }
298 
299   switch (storage_format) {
300     case kUint8ClampedArrayStorageFormat:
301       color_settings->setStorageFormat(kUint8ClampedArrayStorageFormatName);
302       break;
303     case kUint16ArrayStorageFormat:
304       color_settings->setStorageFormat(kUint16ArrayStorageFormatName);
305       break;
306     case kFloat32ArrayStorageFormat:
307       color_settings->setStorageFormat(kFloat32ArrayStorageFormatName);
308       break;
309   }
310 
311   return ImageData::Create(size, color_settings);
312 }
313 
Create(const IntSize & size,NotShared<DOMArrayBufferView> data_array,const ImageDataColorSettings * color_settings)314 ImageData* ImageData::Create(const IntSize& size,
315                              NotShared<DOMArrayBufferView> data_array,
316                              const ImageDataColorSettings* color_settings) {
317   if (!ImageData::ValidateConstructorArguments(
318           kParamSize | kParamData, &size, 0, 0, data_array, color_settings))
319     return nullptr;
320   return MakeGarbageCollected<ImageData>(size, data_array, color_settings);
321 }
322 
GetImageInfo(scoped_refptr<StaticBitmapImage> image)323 static SkImageInfo GetImageInfo(scoped_refptr<StaticBitmapImage> image) {
324   sk_sp<SkImage> skia_image = image->PaintImageForCurrentFrame().GetSkImage();
325   return SkImageInfo::Make(skia_image->width(), skia_image->height(),
326                            skia_image->colorType(), skia_image->alphaType(),
327                            skia_image->refColorSpace());
328 }
329 
Create(scoped_refptr<StaticBitmapImage> image,AlphaDisposition alpha_disposition)330 ImageData* ImageData::Create(scoped_refptr<StaticBitmapImage> image,
331                              AlphaDisposition alpha_disposition) {
332   sk_sp<SkImage> skia_image = image->PaintImageForCurrentFrame().GetSkImage();
333   DCHECK(skia_image);
334   SkImageInfo image_info = GetImageInfo(image);
335   CanvasColorParams color_params(image_info);
336   if (image_info.alphaType() != kOpaque_SkAlphaType) {
337     if (alpha_disposition == kPremultiplyAlpha) {
338       image_info = image_info.makeAlphaType(kPremul_SkAlphaType);
339     } else if (alpha_disposition == kUnpremultiplyAlpha) {
340       image_info = image_info.makeAlphaType(kUnpremul_SkAlphaType);
341     }
342   }
343 
344   SkColorType color_type = image_info.colorType();
345   bool create_f32_image_data = (color_type == kRGBA_1010102_SkColorType ||
346                                 color_type == kRGB_101010x_SkColorType ||
347                                 color_type == kRGBA_F16_SkColorType ||
348                                 color_type == kRGBA_F32_SkColorType);
349 
350   if (!create_f32_image_data) {
351     ImageData* image_data = Create(image->Size(), color_params);
352     if (!image_data)
353       return nullptr;
354     image_info = image_info.makeColorType(kRGBA_8888_SkColorType);
355     skia_image->readPixels(image_info, image_data->data()->Data(),
356                            image_info.minRowBytes(), 0, 0);
357     return image_data;
358   }
359 
360   base::CheckedNumeric<uint32_t> area = image->Size().Area();
361   area *= 4;
362 
363   if (!area.IsValid())
364     return nullptr;
365   // Create image data with f32 storage
366   NotShared<DOMFloat32Array> f32_array =
367       ImageData::AllocateAndValidateFloat32Array(area.ValueOrDie(), nullptr);
368   if (!f32_array)
369     return nullptr;
370   image_info = image_info.makeColorType(kRGBA_F32_SkColorType);
371   skia_image->readPixels(image_info, f32_array->Data(),
372                          image_info.minRowBytes(), 0, 0);
373   ImageDataColorSettings* color_settings =
374       CanvasColorParamsToImageDataColorSettings(color_params);
375   return Create(image->Size(), f32_array, color_settings);
376 }
377 
Create(unsigned width,unsigned height,ExceptionState & exception_state)378 ImageData* ImageData::Create(unsigned width,
379                              unsigned height,
380                              ExceptionState& exception_state) {
381   if (!ImageData::ValidateConstructorArguments(
382           kParamWidth | kParamHeight, nullptr, width, height,
383           NotShared<DOMArrayBufferView>(), nullptr, &exception_state))
384     return nullptr;
385 
386   NotShared<DOMUint8ClampedArray> byte_array =
387       AllocateAndValidateUint8ClampedArray(4 * width * height,
388                                            &exception_state);
389   return byte_array ? MakeGarbageCollected<ImageData>(IntSize(width, height),
390                                                       byte_array)
391                     : nullptr;
392 }
393 
Create(NotShared<DOMUint8ClampedArray> data,unsigned width,ExceptionState & exception_state)394 ImageData* ImageData::Create(NotShared<DOMUint8ClampedArray> data,
395                              unsigned width,
396                              ExceptionState& exception_state) {
397   if (!ImageData::ValidateConstructorArguments(kParamData | kParamWidth,
398                                                nullptr, width, 0, data, nullptr,
399                                                &exception_state))
400     return nullptr;
401 
402   unsigned height =
403       base::checked_cast<unsigned>(data->lengthAsSizeT()) / (width * 4);
404   return MakeGarbageCollected<ImageData>(IntSize(width, height), data);
405 }
406 
Create(NotShared<DOMUint8ClampedArray> data,unsigned width,unsigned height,ExceptionState & exception_state)407 ImageData* ImageData::Create(NotShared<DOMUint8ClampedArray> data,
408                              unsigned width,
409                              unsigned height,
410                              ExceptionState& exception_state) {
411   if (!ImageData::ValidateConstructorArguments(
412           kParamData | kParamWidth | kParamHeight, nullptr, width, height, data,
413           nullptr, &exception_state))
414     return nullptr;
415 
416   return MakeGarbageCollected<ImageData>(IntSize(width, height), data);
417 }
418 
CreateImageData(unsigned width,unsigned height,const ImageDataColorSettings * color_settings,ExceptionState & exception_state)419 ImageData* ImageData::CreateImageData(
420     unsigned width,
421     unsigned height,
422     const ImageDataColorSettings* color_settings,
423     ExceptionState& exception_state) {
424   if (!ImageData::ValidateConstructorArguments(
425           kParamWidth | kParamHeight, nullptr, width, height,
426           NotShared<DOMArrayBufferView>(), color_settings, &exception_state))
427     return nullptr;
428 
429   ImageDataStorageFormat storage_format =
430       ImageData::GetImageDataStorageFormat(color_settings->storageFormat());
431   NotShared<DOMArrayBufferView> buffer_view = AllocateAndValidateDataArray(
432       4 * width * height, storage_format, &exception_state);
433 
434   if (!buffer_view)
435     return nullptr;
436 
437   return MakeGarbageCollected<ImageData>(IntSize(width, height), buffer_view,
438                                          color_settings);
439 }
440 
CreateImageData(ImageDataArray & data,unsigned width,unsigned height,ImageDataColorSettings * color_settings,ExceptionState & exception_state)441 ImageData* ImageData::CreateImageData(ImageDataArray& data,
442                                       unsigned width,
443                                       unsigned height,
444                                       ImageDataColorSettings* color_settings,
445                                       ExceptionState& exception_state) {
446   NotShared<DOMArrayBufferView> buffer_view;
447 
448   // When pixels data is provided, we need to override the storage format of
449   // ImageDataColorSettings with the one that matches the data type of the
450   // pixels.
451   String storage_format_name;
452 
453   if (data.IsUint8ClampedArray()) {
454     buffer_view = data.GetAsUint8ClampedArray();
455     storage_format_name = kUint8ClampedArrayStorageFormatName;
456   } else if (data.IsUint16Array()) {
457     buffer_view = data.GetAsUint16Array();
458     storage_format_name = kUint16ArrayStorageFormatName;
459   } else if (data.IsFloat32Array()) {
460     buffer_view = data.GetAsFloat32Array();
461     storage_format_name = kFloat32ArrayStorageFormatName;
462   } else {
463     NOTREACHED();
464   }
465 
466   if (storage_format_name != color_settings->storageFormat())
467     color_settings->setStorageFormat(storage_format_name);
468 
469   if (!ImageData::ValidateConstructorArguments(
470           kParamData | kParamWidth | kParamHeight, nullptr, width, height,
471           buffer_view, color_settings, &exception_state))
472     return nullptr;
473 
474   return MakeGarbageCollected<ImageData>(IntSize(width, height), buffer_view,
475                                          color_settings);
476 }
477 
478 // This function accepts size (0, 0) and always returns the ImageData in
479 // "srgb" color space and "uint8" storage format.
CreateForTest(const IntSize & size)480 ImageData* ImageData::CreateForTest(const IntSize& size) {
481   base::CheckedNumeric<unsigned> data_size = 4;
482   data_size *= size.Width();
483   data_size *= size.Height();
484   if (!data_size.IsValid() ||
485       data_size.ValueOrDie() > v8::TypedArray::kMaxLength)
486     return nullptr;
487 
488   NotShared<DOMUint8ClampedArray> byte_array(
489       DOMUint8ClampedArray::CreateOrNull(data_size.ValueOrDie()));
490   if (!byte_array)
491     return nullptr;
492 
493   return MakeGarbageCollected<ImageData>(size, byte_array);
494 }
495 
496 // This function is called from unit tests, and all the parameters are supposed
497 // to be validated on the call site.
CreateForTest(const IntSize & size,NotShared<DOMArrayBufferView> buffer_view,const ImageDataColorSettings * color_settings)498 ImageData* ImageData::CreateForTest(
499     const IntSize& size,
500     NotShared<DOMArrayBufferView> buffer_view,
501     const ImageDataColorSettings* color_settings) {
502   return MakeGarbageCollected<ImageData>(size, buffer_view, color_settings);
503 }
504 
505 // Crops ImageData to the intersect of its size and the given rectangle. If the
506 // intersection is empty or it cannot create the cropped ImageData it returns
507 // nullptr. This function leaves the source ImageData intact. When crop_rect
508 // covers all the ImageData, a copy of the ImageData is returned.
509 // TODO (zakerinasab): crbug.com/774484: As a rule of thumb ImageData belongs to
510 // the user and its state should not change unless directly modified by the
511 // user. Therefore, we should be able to remove the extra copy and return a
512 // "cropped view" on the source ImageData object.
CropRect(const IntRect & crop_rect,bool flip_y)513 ImageData* ImageData::CropRect(const IntRect& crop_rect, bool flip_y) {
514   IntRect src_rect(IntPoint(), size_);
515   const IntRect dst_rect = Intersection(src_rect, crop_rect);
516   if (dst_rect.IsEmpty())
517     return nullptr;
518 
519   unsigned data_size = 4 * dst_rect.Width() * dst_rect.Height();
520   NotShared<DOMArrayBufferView> buffer_view = AllocateAndValidateDataArray(
521       data_size,
522       ImageData::GetImageDataStorageFormat(color_settings_->storageFormat()));
523   if (!buffer_view)
524     return nullptr;
525 
526   if (src_rect == dst_rect && !flip_y) {
527     std::memcpy(buffer_view->BufferBase()->Data(), BufferBase()->Data(),
528                 data_size * buffer_view->TypeSize());
529   } else {
530     unsigned data_type_size =
531         ImageData::StorageFormatDataSize(color_settings_->storageFormat());
532     int src_index = (dst_rect.X() + dst_rect.Y() * src_rect.Width()) * 4;
533     int dst_index = 0;
534     if (flip_y)
535       dst_index = (dst_rect.Height() - 1) * dst_rect.Width() * 4;
536     int src_row_stride = src_rect.Width() * 4;
537     int dst_row_stride = flip_y ? -dst_rect.Width() * 4 : dst_rect.Width() * 4;
538     for (int i = 0; i < dst_rect.Height(); i++) {
539       std::memcpy(
540           static_cast<char*>(buffer_view->BufferBase()->Data()) +
541               dst_index * data_type_size,
542           static_cast<char*>(BufferBase()->Data()) + src_index * data_type_size,
543           dst_rect.Width() * 4 * data_type_size);
544       src_index += src_row_stride;
545       dst_index += dst_row_stride;
546     }
547   }
548   return MakeGarbageCollected<ImageData>(dst_rect.Size(), buffer_view,
549                                          color_settings_);
550 }
551 
CreateImageBitmap(ScriptState * script_state,EventTarget & event_target,base::Optional<IntRect> crop_rect,const ImageBitmapOptions * options,ExceptionState & exception_state)552 ScriptPromise ImageData::CreateImageBitmap(ScriptState* script_state,
553                                            EventTarget& event_target,
554                                            base::Optional<IntRect> crop_rect,
555                                            const ImageBitmapOptions* options,
556                                            ExceptionState& exception_state) {
557   if (BufferBase()->IsDetached()) {
558     exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
559                                       "The source data has been detached.");
560     return ScriptPromise();
561   }
562   return ImageBitmapSource::FulfillImageBitmap(
563       script_state, MakeGarbageCollected<ImageBitmap>(this, crop_rect, options),
564       exception_state);
565 }
566 
AssociateWithWrapper(v8::Isolate * isolate,const WrapperTypeInfo * wrapper_type,v8::Local<v8::Object> wrapper)567 v8::Local<v8::Object> ImageData::AssociateWithWrapper(
568     v8::Isolate* isolate,
569     const WrapperTypeInfo* wrapper_type,
570     v8::Local<v8::Object> wrapper) {
571   wrapper =
572       ScriptWrappable::AssociateWithWrapper(isolate, wrapper_type, wrapper);
573 
574   if (!wrapper.IsEmpty() && data_) {
575     // Create a V8 Uint8ClampedArray object and set the "data" property
576     // of the ImageData object to the created v8 object, eliminating the
577     // C++ callback when accessing the "data" property.
578     v8::Local<v8::Value> pixel_array = ToV8(data_.Get(), wrapper, isolate);
579     bool defined_property;
580     if (pixel_array.IsEmpty() ||
581         !wrapper
582              ->DefineOwnProperty(isolate->GetCurrentContext(),
583                                  V8AtomicString(isolate, "data"), pixel_array,
584                                  v8::ReadOnly)
585              .To(&defined_property) ||
586         !defined_property)
587       return v8::Local<v8::Object>();
588   }
589   return wrapper;
590 }
591 
data() const592 const DOMUint8ClampedArray* ImageData::data() const {
593   if (color_settings_->storageFormat() == kUint8ClampedArrayStorageFormatName)
594     return data_.Get();
595   return nullptr;
596 }
597 
data()598 DOMUint8ClampedArray* ImageData::data() {
599   if (color_settings_->storageFormat() == kUint8ClampedArrayStorageFormatName)
600     return data_.Get();
601   return nullptr;
602 }
603 
GetCanvasColorSpace(const String & color_space_name)604 CanvasColorSpace ImageData::GetCanvasColorSpace(
605     const String& color_space_name) {
606   if (color_space_name == kSRGBCanvasColorSpaceName)
607     return CanvasColorSpace::kSRGB;
608   if (color_space_name == kLinearRGBCanvasColorSpaceName)
609     return CanvasColorSpace::kLinearRGB;
610   if (color_space_name == kRec2020CanvasColorSpaceName)
611     return CanvasColorSpace::kRec2020;
612   if (color_space_name == kP3CanvasColorSpaceName)
613     return CanvasColorSpace::kP3;
614   NOTREACHED();
615   return CanvasColorSpace::kSRGB;
616 }
617 
CanvasColorSpaceName(CanvasColorSpace color_space)618 String ImageData::CanvasColorSpaceName(CanvasColorSpace color_space) {
619   switch (color_space) {
620     case CanvasColorSpace::kSRGB:
621       return kSRGBCanvasColorSpaceName;
622     case CanvasColorSpace::kLinearRGB:
623       return kLinearRGBCanvasColorSpaceName;
624     case CanvasColorSpace::kRec2020:
625       return kRec2020CanvasColorSpaceName;
626     case CanvasColorSpace::kP3:
627       return kP3CanvasColorSpaceName;
628     default:
629       NOTREACHED();
630   }
631   return kSRGBCanvasColorSpaceName;
632 }
633 
GetImageDataStorageFormat(const String & storage_format_name)634 ImageDataStorageFormat ImageData::GetImageDataStorageFormat(
635     const String& storage_format_name) {
636   if (storage_format_name == kUint8ClampedArrayStorageFormatName)
637     return kUint8ClampedArrayStorageFormat;
638   if (storage_format_name == kUint16ArrayStorageFormatName)
639     return kUint16ArrayStorageFormat;
640   if (storage_format_name == kFloat32ArrayStorageFormatName)
641     return kFloat32ArrayStorageFormat;
642   NOTREACHED();
643   return kUint8ClampedArrayStorageFormat;
644 }
645 
GetImageDataStorageFormat()646 ImageDataStorageFormat ImageData::GetImageDataStorageFormat() {
647   if (data_u16_)
648     return kUint16ArrayStorageFormat;
649   if (data_f32_)
650     return kFloat32ArrayStorageFormat;
651   return kUint8ClampedArrayStorageFormat;
652 }
653 
StorageFormatDataSize(const String & storage_format_name)654 unsigned ImageData::StorageFormatDataSize(const String& storage_format_name) {
655   if (storage_format_name == kUint8ClampedArrayStorageFormatName)
656     return 1;
657   if (storage_format_name == kUint16ArrayStorageFormatName)
658     return 2;
659   if (storage_format_name == kFloat32ArrayStorageFormatName)
660     return 4;
661   NOTREACHED();
662   return 1;
663 }
664 
StorageFormatDataSize(ImageDataStorageFormat storage_format)665 unsigned ImageData::StorageFormatDataSize(
666     ImageDataStorageFormat storage_format) {
667   switch (storage_format) {
668     case kUint8ClampedArrayStorageFormat:
669       return 1;
670     case kUint16ArrayStorageFormat:
671       return 2;
672     case kFloat32ArrayStorageFormat:
673       return 4;
674   }
675   NOTREACHED();
676   return 1;
677 }
678 
679 NotShared<DOMArrayBufferView>
ConvertPixelsFromCanvasPixelFormatToImageDataStorageFormat(ArrayBufferContents & content,CanvasPixelFormat pixel_format,ImageDataStorageFormat storage_format)680 ImageData::ConvertPixelsFromCanvasPixelFormatToImageDataStorageFormat(
681     ArrayBufferContents& content,
682     CanvasPixelFormat pixel_format,
683     ImageDataStorageFormat storage_format) {
684   if (!content.DataLength())
685     return NotShared<DOMArrayBufferView>();
686 
687   if (pixel_format == CanvasColorParams::GetNativeCanvasPixelFormat() &&
688       storage_format == kUint8ClampedArrayStorageFormat) {
689     DOMArrayBuffer* array_buffer = DOMArrayBuffer::Create(content);
690     return NotShared<DOMArrayBufferView>(DOMUint8ClampedArray::Create(
691         array_buffer, 0, array_buffer->ByteLengthAsSizeT()));
692   }
693 
694   skcms_PixelFormat src_format = skcms_PixelFormat_RGBA_8888;
695   unsigned num_pixels = content.DataLength() / 4;
696   if (pixel_format == CanvasPixelFormat::kF16) {
697     src_format = skcms_PixelFormat_RGBA_hhhh;
698     num_pixels /= 2;
699   }
700   skcms_AlphaFormat alpha_format = skcms_AlphaFormat_Unpremul;
701 
702   if (storage_format == kUint8ClampedArrayStorageFormat) {
703     NotShared<DOMUint8ClampedArray> u8_array =
704         AllocateAndValidateUint8ClampedArray(num_pixels * 4);
705     if (!u8_array)
706       return NotShared<DOMArrayBufferView>();
707     bool data_transform_successful = skcms_Transform(
708         content.Data(), src_format, alpha_format, nullptr, u8_array->Data(),
709         skcms_PixelFormat_RGBA_8888, alpha_format, nullptr, num_pixels);
710     DCHECK(data_transform_successful);
711     return u8_array;
712   }
713 
714   if (storage_format == kUint16ArrayStorageFormat) {
715     NotShared<DOMUint16Array> u16_array =
716         AllocateAndValidateUint16Array(num_pixels * 4);
717     if (!u16_array)
718       return NotShared<DOMArrayBufferView>();
719     bool data_transform_successful = skcms_Transform(
720         content.Data(), src_format, alpha_format, nullptr, u16_array->Data(),
721         skcms_PixelFormat_RGBA_16161616LE, alpha_format, nullptr, num_pixels);
722     DCHECK(data_transform_successful);
723     return u16_array;
724   }
725 
726   NotShared<DOMFloat32Array> f32_array =
727       AllocateAndValidateFloat32Array(num_pixels * 4);
728   if (!f32_array)
729     return NotShared<DOMArrayBufferView>();
730   bool data_transform_successful = skcms_Transform(
731       content.Data(), src_format, alpha_format, nullptr, f32_array->Data(),
732       skcms_PixelFormat_RGBA_ffff, alpha_format, nullptr, num_pixels);
733   DCHECK(data_transform_successful);
734   return f32_array;
735 }
736 
BufferBase() const737 DOMArrayBufferBase* ImageData::BufferBase() const {
738   if (data_)
739     return data_->BufferBase();
740   if (data_u16_)
741     return data_u16_->BufferBase();
742   if (data_f32_)
743     return data_f32_->BufferBase();
744   return nullptr;
745 }
746 
GetCanvasColorParams()747 CanvasColorParams ImageData::GetCanvasColorParams() {
748   if (!RuntimeEnabledFeatures::CanvasColorManagementEnabled())
749     return CanvasColorParams();
750   CanvasColorSpace color_space =
751       ImageData::GetCanvasColorSpace(color_settings_->colorSpace());
752   return CanvasColorParams(
753       color_space,
754       color_settings_->storageFormat() != kUint8ClampedArrayStorageFormatName
755           ? CanvasPixelFormat::kF16
756           : CanvasColorParams::GetNativeCanvasPixelFormat(),
757       kNonOpaque);
758 }
759 
ImageDataInCanvasColorSettings(CanvasColorSpace canvas_color_space,CanvasPixelFormat canvas_pixel_format,unsigned char * converted_pixels,DataU8ColorType u8_color_type,const IntRect * src_rect,const AlphaDisposition alpha_disposition)760 bool ImageData::ImageDataInCanvasColorSettings(
761     CanvasColorSpace canvas_color_space,
762     CanvasPixelFormat canvas_pixel_format,
763     unsigned char* converted_pixels,
764     DataU8ColorType u8_color_type,
765     const IntRect* src_rect,
766     const AlphaDisposition alpha_disposition) {
767   if (!data_ && !data_u16_ && !data_f32_)
768     return false;
769 
770   CanvasColorParams canvas_color_params =
771       CanvasColorParams(canvas_color_space, canvas_pixel_format, kNonOpaque);
772 
773   unsigned char* src_data = static_cast<unsigned char*>(BufferBase()->Data());
774 
775   ImageDataStorageFormat storage_format = GetImageDataStorageFormat();
776 
777   skcms_PixelFormat src_pixel_format = skcms_PixelFormat_RGBA_8888;
778   if (data_u16_)
779     src_pixel_format = skcms_PixelFormat_RGBA_16161616LE;
780   else if (data_f32_)
781     src_pixel_format = skcms_PixelFormat_RGBA_ffff;
782 
783   skcms_PixelFormat dst_pixel_format = skcms_PixelFormat_RGBA_8888;
784   if (canvas_pixel_format == CanvasPixelFormat::kF16) {
785     dst_pixel_format = skcms_PixelFormat_RGBA_hhhh;
786   }
787 #if SK_PMCOLOR_BYTE_ORDER(B, G, R, A)
788   else if (canvas_pixel_format ==
789                CanvasColorParams::GetNativeCanvasPixelFormat() &&
790            u8_color_type == kN32ColorType) {
791     dst_pixel_format = skcms_PixelFormat_BGRA_8888;
792   }
793 #endif
794 
795   skcms_AlphaFormat src_alpha_format = skcms_AlphaFormat_Unpremul;
796   skcms_AlphaFormat dst_alpha_format = skcms_AlphaFormat_Unpremul;
797   if (alpha_disposition == kPremultiplyAlpha)
798     dst_alpha_format = skcms_AlphaFormat_PremulAsEncoded;
799 
800   skcms_ICCProfile* src_profile_ptr = nullptr;
801   skcms_ICCProfile* dst_profile_ptr = nullptr;
802   skcms_ICCProfile src_profile, dst_profile;
803   GetCanvasColorParams().GetSkColorSpace()->toProfile(&src_profile);
804   canvas_color_params.GetSkColorSpace()->toProfile(&dst_profile);
805   // If the profiles are similar, we better leave them as nullptr, since
806   // skcms_Transform() only checks for profile pointer equality for the fast
807   // path.
808   if (!skcms_ApproximatelyEqualProfiles(&src_profile, &dst_profile)) {
809     src_profile_ptr = &src_profile;
810     dst_profile_ptr = &dst_profile;
811   }
812 
813   const IntRect* crop_rect = nullptr;
814   if (src_rect && *src_rect != IntRect(IntPoint(), Size()))
815     crop_rect = src_rect;
816 
817   // If only a portion of ImageData is required for canvas, we run the transform
818   // for every line.
819   if (crop_rect) {
820     unsigned bytes_per_pixel =
821         ImageData::StorageFormatDataSize(storage_format) * 4;
822     unsigned src_index =
823         (crop_rect->X() + crop_rect->Y() * width()) * bytes_per_pixel;
824     unsigned dst_index = 0;
825     unsigned src_row_stride = width() * bytes_per_pixel;
826     unsigned dst_row_stride =
827         crop_rect->Width() * canvas_color_params.BytesPerPixel();
828     bool data_transform_successful = true;
829 
830     for (int i = 0; data_transform_successful && i < crop_rect->Height(); i++) {
831       data_transform_successful = skcms_Transform(
832           src_data + src_index, src_pixel_format, src_alpha_format,
833           src_profile_ptr, converted_pixels + dst_index, dst_pixel_format,
834           dst_alpha_format, dst_profile_ptr, crop_rect->Width());
835       DCHECK(data_transform_successful);
836       src_index += src_row_stride;
837       dst_index += dst_row_stride;
838     }
839     return data_transform_successful;
840   }
841 
842   base::CheckedNumeric<uint32_t> area = size_.Area();
843   if (!area.IsValid())
844     return false;
845   bool data_transform_successful =
846       skcms_Transform(src_data, src_pixel_format, src_alpha_format,
847                       src_profile_ptr, converted_pixels, dst_pixel_format,
848                       dst_alpha_format, dst_profile_ptr, area.ValueOrDie());
849   return data_transform_successful;
850 }
851 
Trace(Visitor * visitor)852 void ImageData::Trace(Visitor* visitor) {
853   visitor->Trace(color_settings_);
854   visitor->Trace(data_);
855   visitor->Trace(data_u16_);
856   visitor->Trace(data_f32_);
857   visitor->Trace(data_union_);
858   ScriptWrappable::Trace(visitor);
859 }
860 
ImageData(const IntSize & size,NotShared<DOMArrayBufferView> data,const ImageDataColorSettings * color_settings)861 ImageData::ImageData(const IntSize& size,
862                      NotShared<DOMArrayBufferView> data,
863                      const ImageDataColorSettings* color_settings)
864     : size_(size), color_settings_(ImageDataColorSettings::Create()) {
865   DCHECK_GE(size.Width(), 0);
866   DCHECK_GE(size.Height(), 0);
867   DCHECK(data);
868 
869   data_.Clear();
870   data_u16_.Clear();
871   data_f32_.Clear();
872 
873   if (color_settings) {
874     color_settings_->setColorSpace(color_settings->colorSpace());
875     color_settings_->setStorageFormat(color_settings->storageFormat());
876   }
877 
878   ImageDataStorageFormat storage_format =
879       GetImageDataStorageFormat(color_settings_->storageFormat());
880 
881   // TODO (zakerinasab): crbug.com/779570
882   // The default color space for ImageData with U16/F32 data should be
883   // extended-srgb color space. It is temporarily set to linear-rgb, which is
884   // not correct, but fixes crbug.com/779419.
885 
886   switch (storage_format) {
887     case kUint8ClampedArrayStorageFormat:
888       DCHECK(data->GetType() ==
889              DOMArrayBufferView::ViewType::kTypeUint8Clamped);
890       data_ = data;
891       DCHECK(data_);
892       data_union_.SetUint8ClampedArray(data_);
893       SECURITY_CHECK(
894           (base::CheckedNumeric<size_t>(size.Width()) * size.Height() * 4)
895               .ValueOrDie() <= data_->lengthAsSizeT());
896       break;
897 
898     case kUint16ArrayStorageFormat:
899       DCHECK(data->GetType() == DOMArrayBufferView::ViewType::kTypeUint16);
900       data_u16_ = data;
901       DCHECK(data_u16_);
902       data_union_.SetUint16Array(data_u16_);
903       SECURITY_CHECK(
904           (base::CheckedNumeric<size_t>(size.Width()) * size.Height() * 4)
905               .ValueOrDie() <= data_u16_->lengthAsSizeT());
906       break;
907 
908     case kFloat32ArrayStorageFormat:
909       DCHECK(data->GetType() == DOMArrayBufferView::ViewType::kTypeFloat32);
910       data_f32_ = data;
911       DCHECK(data_f32_);
912       data_union_.SetFloat32Array(data_f32_);
913       SECURITY_CHECK(
914           (base::CheckedNumeric<size_t>(size.Width()) * size.Height() * 4)
915               .ValueOrDie() <= data_f32_->lengthAsSizeT());
916       break;
917 
918     default:
919       NOTREACHED();
920   }
921 }
922 
923 }  // namespace blink
924