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

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

Issue 2555213002: Implement color management for ImageData (Closed)
Patch Set: Rebaselining layout tests 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 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
41 ImageData* ImageData::create(const IntSize& size) { 179 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) {
42 CheckedNumeric<unsigned> dataSize = 4; 192 CheckedNumeric<unsigned> dataSize = 4;
43 dataSize *= size.width(); 193 dataSize *= size.width();
44 dataSize *= size.height(); 194 dataSize *= size.height();
45 if (!dataSize.IsValid()) 195 if (!dataSize.IsValid())
46 return nullptr; 196 return nullptr;
47 197
48 DOMUint8ClampedArray* byteArray = 198 DOMUint8ClampedArray* byteArray =
49 DOMUint8ClampedArray::createOrNull(dataSize.ValueOrDie()); 199 DOMUint8ClampedArray::createOrNull(dataSize.ValueOrDie());
50 if (!byteArray) 200 if (!byteArray)
51 return nullptr; 201 return nullptr;
52 202
53 return new ImageData(size, byteArray); 203 return new ImageData(size, byteArray);
54 } 204 }
55 205
56 ImageData* ImageData::create(const IntSize& size, 206 ImageData* ImageData::create(const IntSize& size,
57 DOMUint8ClampedArray* byteArray) { 207 DOMUint8ClampedArray* byteArray) {
58 CheckedNumeric<unsigned> dataSize = 4; 208 if (!ImageData::validateConstructorArguments(kParamSize | kParamData, &size,
59 dataSize *= size.width(); 209 0, 0, byteArray))
60 dataSize *= size.height();
61 if (!dataSize.IsValid())
62 return nullptr; 210 return nullptr;
63
64 if (!dataSize.IsValid() || dataSize.ValueOrDie() > byteArray->length())
65 return nullptr;
66
67 return new ImageData(size, byteArray); 211 return new ImageData(size, byteArray);
68 } 212 }
69 213
70 ImageData* ImageData::create(unsigned width, 214 ImageData* ImageData::create(unsigned width,
71 unsigned height, 215 unsigned height,
72 ExceptionState& exceptionState) { 216 ExceptionState& exceptionState) {
73 if (!width || !height) { 217 if (!ImageData::validateConstructorArguments(kParamWidth | kParamHeight,
74 exceptionState.throwDOMException( 218 nullptr, width, height, nullptr,
75 IndexSizeError, String::format("The source %s is zero or not a number.", 219 nullptr, &exceptionState))
76 width ? "height" : "width"));
77 return nullptr; 220 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 = 221 DOMUint8ClampedArray* byteArray =
92 DOMUint8ClampedArray::createOrNull(dataSize.ValueOrDie()); 222 ImageData::allocateAndValidateUint8ClampedArray(4 * width * height,
93 if (!byteArray) { 223 &exceptionState);
94 exceptionState.throwDOMException(V8Error, 224 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 } 225 }
133 226
134 ImageData* ImageData::create(DOMUint8ClampedArray* data, 227 ImageData* ImageData::create(DOMUint8ClampedArray* data,
135 unsigned width, 228 unsigned width,
136 ExceptionState& exceptionState) { 229 ExceptionState& exceptionState) {
137 unsigned lengthInPixels = 0; 230 if (!ImageData::validateConstructorArguments(kParamData | kParamWidth,
138 if (!validateConstructorArguments(data, width, lengthInPixels, 231 nullptr, width, 0, data, nullptr,
139 exceptionState)) { 232 &exceptionState))
140 DCHECK(exceptionState.hadException());
141 return nullptr; 233 return nullptr;
142 } 234 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); 235 return new ImageData(IntSize(width, height), data);
147 } 236 }
148 237
149 ImageData* ImageData::create(DOMUint8ClampedArray* data, 238 ImageData* ImageData::create(DOMUint8ClampedArray* data,
150 unsigned width, 239 unsigned width,
151 unsigned height, 240 unsigned height,
152 ExceptionState& exceptionState) { 241 ExceptionState& exceptionState) {
153 unsigned lengthInPixels = 0; 242 if (!ImageData::validateConstructorArguments(
154 if (!validateConstructorArguments(data, width, lengthInPixels, 243 kParamData | kParamWidth | kParamHeight, nullptr, width, height, data,
155 exceptionState)) { 244 nullptr, &exceptionState))
156 DCHECK(exceptionState.hadException());
157 return nullptr; 245 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); 246 return new ImageData(IntSize(width, height), data);
168 } 247 }
169 248
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.
170 ScriptPromise ImageData::createImageBitmap(ScriptState* scriptState, 317 ScriptPromise ImageData::createImageBitmap(ScriptState* scriptState,
171 EventTarget& eventTarget, 318 EventTarget& eventTarget,
172 Optional<IntRect> cropRect, 319 Optional<IntRect> cropRect,
173 const ImageBitmapOptions& options, 320 const ImageBitmapOptions& options,
174 ExceptionState& exceptionState) { 321 ExceptionState& exceptionState) {
175 if ((cropRect && 322 if ((cropRect &&
176 !ImageBitmap::isSourceSizeValid(cropRect->width(), cropRect->height(), 323 !ImageBitmap::isSourceSizeValid(cropRect->width(), cropRect->height(),
177 exceptionState)) || 324 exceptionState)) ||
178 !ImageBitmap::isSourceSizeValid(bitmapSourceSize().width(), 325 !ImageBitmap::isSourceSizeValid(bitmapSourceSize().width(),
179 bitmapSourceSize().height(), 326 bitmapSourceSize().height(),
(...skipping 24 matching lines...) Expand all
204 v8::Local<v8::Value> pixelArray = ToV8(m_data.get(), wrapper, isolate); 351 v8::Local<v8::Value> pixelArray = ToV8(m_data.get(), wrapper, isolate);
205 if (pixelArray.IsEmpty() || 352 if (pixelArray.IsEmpty() ||
206 !v8CallBoolean(wrapper->DefineOwnProperty( 353 !v8CallBoolean(wrapper->DefineOwnProperty(
207 isolate->GetCurrentContext(), v8AtomicString(isolate, "data"), 354 isolate->GetCurrentContext(), v8AtomicString(isolate, "data"),
208 pixelArray, v8::ReadOnly))) 355 pixelArray, v8::ReadOnly)))
209 return v8::Local<v8::Object>(); 356 return v8::Local<v8::Object>();
210 } 357 }
211 return wrapper; 358 return wrapper;
212 } 359 }
213 360
214 ImageData::ImageData(const IntSize& size, DOMUint8ClampedArray* byteArray) 361 ImageData::ImageData(const IntSize& size,
215 : m_size(size), m_data(byteArray) { 362 DOMUint8ClampedArray* byteArray,
363 String colorSpaceName)
364 : m_size(size),
365 m_colorSpace(getImageDataColorSpace(colorSpaceName)),
366 m_data(byteArray) {
216 DCHECK_GE(size.width(), 0); 367 DCHECK_GE(size.width(), 0);
217 DCHECK_GE(size.height(), 0); 368 DCHECK_GE(size.height(), 0);
218 SECURITY_CHECK(static_cast<unsigned>(size.width() * size.height() * 4) <= 369 SECURITY_CHECK(static_cast<unsigned>(size.width() * size.height() * 4) <=
219 m_data->length()); 370 m_data->length());
220 } 371 }
221 372
222 } // namespace blink 373 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698