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