OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2008 Apple Inc. All rights reserved. | 2 * Copyright (C) 2008 Apple Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * | 7 * |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 16 matching lines...) Expand all Loading... |
27 */ | 27 */ |
28 | 28 |
29 #include "core/html/ImageData.h" | 29 #include "core/html/ImageData.h" |
30 | 30 |
31 #include "bindings/core/v8/ExceptionState.h" | 31 #include "bindings/core/v8/ExceptionState.h" |
32 #include "bindings/core/v8/V8Uint8ClampedArray.h" | 32 #include "bindings/core/v8/V8Uint8ClampedArray.h" |
33 #include "core/dom/ExceptionCode.h" | 33 #include "core/dom/ExceptionCode.h" |
34 #include "core/frame/ImageBitmap.h" | 34 #include "core/frame/ImageBitmap.h" |
35 #include "core/imagebitmap/ImageBitmapOptions.h" | 35 #include "core/imagebitmap/ImageBitmapOptions.h" |
36 #include "platform/RuntimeEnabledFeatures.h" | 36 #include "platform/RuntimeEnabledFeatures.h" |
37 #include "wtf/CheckedNumeric.h" | |
38 | 37 |
39 namespace blink { | 38 namespace blink { |
40 | 39 |
| 40 bool ImageData::validateConstructorArguments(const unsigned& paramFlags, |
| 41 const IntSize* size, |
| 42 const unsigned& width, |
| 43 const unsigned& height, |
| 44 const DOMArrayBufferView* data, |
| 45 const String* colorSpace, |
| 46 ExceptionState* exceptionState, |
| 47 ImageDataType imageDataType) { |
| 48 if (paramFlags & kParamData) { |
| 49 if (data->type() != DOMArrayBufferView::ViewType::TypeUint8Clamped && |
| 50 data->type() != DOMArrayBufferView::ViewType::TypeFloat32) |
| 51 return false; |
| 52 if (data->type() == DOMArrayBufferView::ViewType::TypeUint8Clamped && |
| 53 imageDataType != kUint8ClampedImageData) |
| 54 imageDataType = kFloat32ImageData; |
| 55 } |
| 56 |
| 57 // ImageData::create parameters without ExceptionState |
| 58 if (paramFlags & kParamSize) { |
| 59 if (!size->width() || !size->height()) |
| 60 return false; |
| 61 CheckedNumeric<unsigned> dataSize = 4; |
| 62 dataSize *= size->width(); |
| 63 dataSize *= size->height(); |
| 64 if (!dataSize.IsValid()) |
| 65 return false; |
| 66 if (paramFlags & kParamData) { |
| 67 DCHECK(data); |
| 68 unsigned length = |
| 69 data->type() == DOMArrayBufferView::ViewType::TypeUint8Clamped |
| 70 ? (const_cast<DOMUint8ClampedArray*>( |
| 71 static_cast<const DOMUint8ClampedArray*>(data))) |
| 72 ->length() |
| 73 : (const_cast<DOMFloat32Array*>( |
| 74 static_cast<const DOMFloat32Array*>(data))) |
| 75 ->length(); |
| 76 if (dataSize.ValueOrDie() > length) |
| 77 return false; |
| 78 } |
| 79 return true; |
| 80 } |
| 81 |
| 82 // ImageData::create parameters with ExceptionState |
| 83 if ((paramFlags & kParamWidth) && !width) { |
| 84 exceptionState->throwDOMException( |
| 85 IndexSizeError, "The source width is zero or not a number."); |
| 86 return false; |
| 87 } |
| 88 if ((paramFlags & kParamHeight) && !height) { |
| 89 exceptionState->throwDOMException( |
| 90 IndexSizeError, "The source height is zero or not a number."); |
| 91 return false; |
| 92 } |
| 93 if (paramFlags & (kParamWidth | kParamHeight)) { |
| 94 CheckedNumeric<unsigned> dataSize = 4; |
| 95 dataSize *= width; |
| 96 dataSize *= height; |
| 97 if (!dataSize.IsValid()) { |
| 98 exceptionState->throwDOMException( |
| 99 IndexSizeError, |
| 100 "The requested image size exceeds the supported range."); |
| 101 return false; |
| 102 } |
| 103 } |
| 104 if (paramFlags & kParamData) { |
| 105 DCHECK(data); |
| 106 unsigned length = |
| 107 data->type() == DOMArrayBufferView::ViewType::TypeUint8Clamped |
| 108 ? (const_cast<DOMUint8ClampedArray*>( |
| 109 static_cast<const DOMUint8ClampedArray*>(data))) |
| 110 ->length() |
| 111 : (const_cast<DOMFloat32Array*>( |
| 112 static_cast<const DOMFloat32Array*>(data))) |
| 113 ->length(); |
| 114 if (!length) { |
| 115 exceptionState->throwDOMException(IndexSizeError, |
| 116 "The input data has zero elements."); |
| 117 return false; |
| 118 } |
| 119 if (length % 4) { |
| 120 exceptionState->throwDOMException( |
| 121 IndexSizeError, "The input data length is not a multiple of 4."); |
| 122 return false; |
| 123 } |
| 124 length /= 4; |
| 125 if (length % width) { |
| 126 exceptionState->throwDOMException( |
| 127 IndexSizeError, |
| 128 "The input data length is not a multiple of (4 * width)."); |
| 129 return false; |
| 130 } |
| 131 if ((paramFlags & kParamHeight) && height != length / width) { |
| 132 exceptionState->throwDOMException( |
| 133 IndexSizeError, |
| 134 "The input data length is not equal to (4 * width * height)."); |
| 135 return false; |
| 136 } |
| 137 } |
| 138 if (paramFlags & kParamColorSpace) { |
| 139 if (!colorSpace || colorSpace->length() == 0) { |
| 140 exceptionState->throwDOMException( |
| 141 NotSupportedError, "The source color space is not defined."); |
| 142 return false; |
| 143 } |
| 144 if (imageDataType == kUint8ClampedImageData && |
| 145 *colorSpace != kLegacyImageDataColorSpaceName && |
| 146 *colorSpace != kSRGBImageDataColorSpaceName) { |
| 147 exceptionState->throwDOMException(NotSupportedError, |
| 148 "The input color space is not " |
| 149 "supported in " |
| 150 "Uint8ClampedArray-backed ImageData."); |
| 151 return false; |
| 152 } |
| 153 if (imageDataType == kFloat32ImageData && |
| 154 *colorSpace != kLinearRGBImageDataColorSpaceName) { |
| 155 exceptionState->throwDOMException(NotSupportedError, |
| 156 "The input color space is not " |
| 157 "supported in " |
| 158 "Float32Array-backed ImageData."); |
| 159 return false; |
| 160 } |
| 161 } |
| 162 return true; |
| 163 } |
| 164 |
| 165 DOMUint8ClampedArray* ImageData::allocateAndValidateUint8ClampedArray( |
| 166 const unsigned& length, |
| 167 ExceptionState* exceptionState) { |
| 168 if (!length) |
| 169 return nullptr; |
| 170 DOMUint8ClampedArray* dataArray = DOMUint8ClampedArray::createOrNull(length); |
| 171 if (!dataArray || length != dataArray->length()) { |
| 172 if (exceptionState) { |
| 173 exceptionState->throwDOMException(V8RangeError, |
| 174 "Out of memory at ImageData creation"); |
| 175 } |
| 176 return nullptr; |
| 177 } |
| 178 return dataArray; |
| 179 } |
| 180 |
41 ImageData* ImageData::create(const IntSize& size) { | 181 ImageData* ImageData::create(const IntSize& size) { |
| 182 if (!ImageData::validateConstructorArguments(kParamSize, &size)) |
| 183 return nullptr; |
| 184 DOMUint8ClampedArray* byteArray = |
| 185 ImageData::allocateAndValidateUint8ClampedArray(4 * size.width() * |
| 186 size.height()); |
| 187 if (!byteArray) |
| 188 return nullptr; |
| 189 return new ImageData(size, byteArray); |
| 190 } |
| 191 |
| 192 // This function accepts size (0, 0). |
| 193 ImageData* ImageData::createForTest(const IntSize& size) { |
42 CheckedNumeric<unsigned> dataSize = 4; | 194 CheckedNumeric<unsigned> dataSize = 4; |
43 dataSize *= size.width(); | 195 dataSize *= size.width(); |
44 dataSize *= size.height(); | 196 dataSize *= size.height(); |
45 if (!dataSize.IsValid()) | 197 if (!dataSize.IsValid()) |
46 return nullptr; | 198 return nullptr; |
47 | 199 |
48 DOMUint8ClampedArray* byteArray = | 200 DOMUint8ClampedArray* byteArray = |
49 DOMUint8ClampedArray::createOrNull(dataSize.ValueOrDie()); | 201 DOMUint8ClampedArray::createOrNull(dataSize.ValueOrDie()); |
50 if (!byteArray) | 202 if (!byteArray) |
51 return nullptr; | 203 return nullptr; |
52 | 204 |
53 return new ImageData(size, byteArray); | 205 return new ImageData(size, byteArray); |
54 } | 206 } |
55 | 207 |
56 ImageData* ImageData::create(const IntSize& size, | 208 ImageData* ImageData::create(const IntSize& size, |
57 DOMUint8ClampedArray* byteArray) { | 209 DOMUint8ClampedArray* byteArray) { |
58 CheckedNumeric<unsigned> dataSize = 4; | 210 if (!ImageData::validateConstructorArguments(kParamSize | kParamData, &size, |
59 dataSize *= size.width(); | 211 0, 0, byteArray)) |
60 dataSize *= size.height(); | |
61 if (!dataSize.IsValid()) | |
62 return nullptr; | 212 return nullptr; |
63 | |
64 if (!dataSize.IsValid() || dataSize.ValueOrDie() > byteArray->length()) | |
65 return nullptr; | |
66 | |
67 return new ImageData(size, byteArray); | 213 return new ImageData(size, byteArray); |
68 } | 214 } |
69 | 215 |
70 ImageData* ImageData::create(unsigned width, | 216 ImageData* ImageData::create(unsigned width, |
71 unsigned height, | 217 unsigned height, |
72 ExceptionState& exceptionState) { | 218 ExceptionState& exceptionState) { |
73 if (!width || !height) { | 219 if (!ImageData::validateConstructorArguments(kParamWidth | kParamHeight, |
74 exceptionState.throwDOMException( | 220 nullptr, width, height, nullptr, |
75 IndexSizeError, String::format("The source %s is zero or not a number.", | 221 nullptr, &exceptionState)) |
76 width ? "height" : "width")); | |
77 return nullptr; | 222 return nullptr; |
78 } | |
79 | |
80 CheckedNumeric<unsigned> dataSize = 4; | |
81 dataSize *= width; | |
82 dataSize *= height; | |
83 if (!dataSize.IsValid() || static_cast<int>(width) < 0 || | |
84 static_cast<int>(height) < 0) { | |
85 exceptionState.throwDOMException( | |
86 IndexSizeError, | |
87 "The requested image size exceeds the supported range."); | |
88 return nullptr; | |
89 } | |
90 | |
91 DOMUint8ClampedArray* byteArray = | 223 DOMUint8ClampedArray* byteArray = |
92 DOMUint8ClampedArray::createOrNull(dataSize.ValueOrDie()); | 224 ImageData::allocateAndValidateUint8ClampedArray(4 * width * height, |
93 if (!byteArray) { | 225 &exceptionState); |
94 exceptionState.throwDOMException(V8Error, | 226 return byteArray ? new ImageData(IntSize(width, height), byteArray) : nullptr; |
95 "Out of memory at ImageData creation"); | |
96 return nullptr; | |
97 } | |
98 | |
99 return new ImageData(IntSize(width, height), byteArray); | |
100 } | |
101 | |
102 bool ImageData::validateConstructorArguments(DOMUint8ClampedArray* data, | |
103 unsigned width, | |
104 unsigned& lengthInPixels, | |
105 ExceptionState& exceptionState) { | |
106 if (!width) { | |
107 exceptionState.throwDOMException( | |
108 IndexSizeError, "The source width is zero or not a number."); | |
109 return false; | |
110 } | |
111 DCHECK(data); | |
112 unsigned length = data->length(); | |
113 if (!length) { | |
114 exceptionState.throwDOMException(IndexSizeError, | |
115 "The input data has a zero byte length."); | |
116 return false; | |
117 } | |
118 if (length % 4) { | |
119 exceptionState.throwDOMException( | |
120 IndexSizeError, "The input data byte length is not a multiple of 4."); | |
121 return false; | |
122 } | |
123 length /= 4; | |
124 if (length % width) { | |
125 exceptionState.throwDOMException( | |
126 IndexSizeError, | |
127 "The input data byte length is not a multiple of (4 * width)."); | |
128 return false; | |
129 } | |
130 lengthInPixels = length; | |
131 return true; | |
132 } | 227 } |
133 | 228 |
134 ImageData* ImageData::create(DOMUint8ClampedArray* data, | 229 ImageData* ImageData::create(DOMUint8ClampedArray* data, |
135 unsigned width, | 230 unsigned width, |
136 ExceptionState& exceptionState) { | 231 ExceptionState& exceptionState) { |
137 unsigned lengthInPixels = 0; | 232 if (!ImageData::validateConstructorArguments(kParamData | kParamWidth, |
138 if (!validateConstructorArguments(data, width, lengthInPixels, | 233 nullptr, width, 0, data, nullptr, |
139 exceptionState)) { | 234 &exceptionState)) |
140 DCHECK(exceptionState.hadException()); | |
141 return nullptr; | 235 return nullptr; |
142 } | 236 unsigned height = data->length() / (width * 4); |
143 DCHECK_GT(lengthInPixels, 0u); | |
144 DCHECK_GT(width, 0u); | |
145 unsigned height = lengthInPixels / width; | |
146 return new ImageData(IntSize(width, height), data); | 237 return new ImageData(IntSize(width, height), data); |
147 } | 238 } |
148 | 239 |
149 ImageData* ImageData::create(DOMUint8ClampedArray* data, | 240 ImageData* ImageData::create(DOMUint8ClampedArray* data, |
150 unsigned width, | 241 unsigned width, |
151 unsigned height, | 242 unsigned height, |
152 ExceptionState& exceptionState) { | 243 ExceptionState& exceptionState) { |
153 unsigned lengthInPixels = 0; | 244 if (!ImageData::validateConstructorArguments( |
154 if (!validateConstructorArguments(data, width, lengthInPixels, | 245 kParamData | kParamWidth | kParamHeight, nullptr, width, height, data, |
155 exceptionState)) { | 246 nullptr, &exceptionState)) |
156 DCHECK(exceptionState.hadException()); | |
157 return nullptr; | 247 return nullptr; |
158 } | |
159 DCHECK_GT(lengthInPixels, 0u); | |
160 DCHECK_GT(width, 0u); | |
161 if (height != lengthInPixels / width) { | |
162 exceptionState.throwDOMException( | |
163 IndexSizeError, | |
164 "The input data byte length is not equal to (4 * width * height)."); | |
165 return nullptr; | |
166 } | |
167 return new ImageData(IntSize(width, height), data); | 248 return new ImageData(IntSize(width, height), data); |
168 } | 249 } |
169 | 250 |
| 251 ImageDataColorSpace ImageData::getImageDataColorSpace(String colorSpaceName) { |
| 252 if (colorSpaceName == kLegacyImageDataColorSpaceName) |
| 253 return kLegacyImageDataColorSpace; |
| 254 if (colorSpaceName == kSRGBImageDataColorSpaceName) |
| 255 return kSRGBImageDataColorSpace; |
| 256 if (colorSpaceName == kLinearRGBImageDataColorSpaceName) |
| 257 return kLinearRGBImageDataColorSpace; |
| 258 NOTREACHED(); |
| 259 return kLegacyImageDataColorSpace; |
| 260 } |
| 261 |
| 262 String ImageData::getImageDataColorSpaceName(ImageDataColorSpace colorSpace) { |
| 263 switch (colorSpace) { |
| 264 case kLegacyImageDataColorSpace: |
| 265 return kLegacyImageDataColorSpaceName; |
| 266 case kSRGBImageDataColorSpace: |
| 267 return kSRGBImageDataColorSpaceName; |
| 268 case kLinearRGBImageDataColorSpace: |
| 269 return kLinearRGBImageDataColorSpaceName; |
| 270 } |
| 271 NOTREACHED(); |
| 272 return String(); |
| 273 } |
| 274 |
| 275 ImageData* ImageData::createImageData(unsigned width, |
| 276 unsigned height, |
| 277 String colorSpace, |
| 278 ExceptionState& exceptionState) { |
| 279 if (!ImageData::validateConstructorArguments( |
| 280 kParamWidth | kParamHeight | kParamColorSpace, nullptr, width, height, |
| 281 nullptr, &colorSpace, &exceptionState)) |
| 282 return nullptr; |
| 283 |
| 284 DOMUint8ClampedArray* byteArray = |
| 285 ImageData::allocateAndValidateUint8ClampedArray(4 * width * height, |
| 286 &exceptionState); |
| 287 return byteArray |
| 288 ? new ImageData(IntSize(width, height), byteArray, colorSpace) |
| 289 : nullptr; |
| 290 } |
| 291 |
| 292 ImageData* ImageData::createImageData(DOMUint8ClampedArray* data, |
| 293 unsigned width, |
| 294 String colorSpace, |
| 295 ExceptionState& exceptionState) { |
| 296 if (!ImageData::validateConstructorArguments( |
| 297 kParamData | kParamWidth | kParamColorSpace, nullptr, width, 0, data, |
| 298 &colorSpace, &exceptionState)) |
| 299 return nullptr; |
| 300 unsigned height = data->length() / (width * 4); |
| 301 return new ImageData(IntSize(width, height), data, colorSpace); |
| 302 } |
| 303 |
| 304 ImageData* ImageData::createImageData(DOMUint8ClampedArray* data, |
| 305 unsigned width, |
| 306 unsigned height, |
| 307 String colorSpace, |
| 308 ExceptionState& exceptionState) { |
| 309 if (!ImageData::validateConstructorArguments( |
| 310 kParamData | kParamWidth | kParamHeight | kParamColorSpace, nullptr, |
| 311 width, height, data, &colorSpace, &exceptionState)) |
| 312 return nullptr; |
| 313 return new ImageData(IntSize(width, height), data, colorSpace); |
| 314 } |
| 315 |
| 316 // TODO(zakerinasab): Fix this when ImageBitmap color correction code is landed. |
| 317 // Tip: If the source Image Data has a color space, createImageBitmap must |
| 318 // respect this color space even when no color space tag is passed to it. |
170 ScriptPromise ImageData::createImageBitmap(ScriptState* scriptState, | 319 ScriptPromise ImageData::createImageBitmap(ScriptState* scriptState, |
171 EventTarget& eventTarget, | 320 EventTarget& eventTarget, |
172 Optional<IntRect> cropRect, | 321 Optional<IntRect> cropRect, |
173 const ImageBitmapOptions& options, | 322 const ImageBitmapOptions& options, |
174 ExceptionState& exceptionState) { | 323 ExceptionState& exceptionState) { |
175 if ((cropRect && | 324 if ((cropRect && |
176 !ImageBitmap::isSourceSizeValid(cropRect->width(), cropRect->height(), | 325 !ImageBitmap::isSourceSizeValid(cropRect->width(), cropRect->height(), |
177 exceptionState)) || | 326 exceptionState)) || |
178 !ImageBitmap::isSourceSizeValid(bitmapSourceSize().width(), | 327 !ImageBitmap::isSourceSizeValid(bitmapSourceSize().width(), |
179 bitmapSourceSize().height(), | 328 bitmapSourceSize().height(), |
(...skipping 24 matching lines...) Expand all Loading... |
204 v8::Local<v8::Value> pixelArray = ToV8(m_data.get(), wrapper, isolate); | 353 v8::Local<v8::Value> pixelArray = ToV8(m_data.get(), wrapper, isolate); |
205 if (pixelArray.IsEmpty() || | 354 if (pixelArray.IsEmpty() || |
206 !v8CallBoolean(wrapper->DefineOwnProperty( | 355 !v8CallBoolean(wrapper->DefineOwnProperty( |
207 isolate->GetCurrentContext(), v8AtomicString(isolate, "data"), | 356 isolate->GetCurrentContext(), v8AtomicString(isolate, "data"), |
208 pixelArray, v8::ReadOnly))) | 357 pixelArray, v8::ReadOnly))) |
209 return v8::Local<v8::Object>(); | 358 return v8::Local<v8::Object>(); |
210 } | 359 } |
211 return wrapper; | 360 return wrapper; |
212 } | 361 } |
213 | 362 |
214 ImageData::ImageData(const IntSize& size, DOMUint8ClampedArray* byteArray) | 363 ImageData::ImageData(const IntSize& size, |
215 : m_size(size), m_data(byteArray) { | 364 DOMUint8ClampedArray* byteArray, |
| 365 String colorSpaceName) |
| 366 : m_size(size), |
| 367 m_colorSpace(getImageDataColorSpace(colorSpaceName)), |
| 368 m_data(byteArray) { |
216 DCHECK_GE(size.width(), 0); | 369 DCHECK_GE(size.width(), 0); |
217 DCHECK_GE(size.height(), 0); | 370 DCHECK_GE(size.height(), 0); |
218 SECURITY_CHECK(static_cast<unsigned>(size.width() * size.height() * 4) <= | 371 SECURITY_CHECK(static_cast<unsigned>(size.width() * size.height() * 4) <= |
219 m_data->length()); | 372 m_data->length()); |
220 } | 373 } |
221 | 374 |
222 } // namespace blink | 375 } // namespace blink |
OLD | NEW |