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

Side by Side Diff: third_party/WebKit/Source/core/html/ImageData.cpp

Issue 2555213002: Implement color management for ImageData (Closed)
Patch Set: Fixing ImageData::validateConstructorArguments Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/html/ImageData.h ('k') | third_party/WebKit/Source/core/html/ImageData.idl » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698