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