View Javadoc
1   /*
2    * Copyright (c) 2002-2025 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.javascript.host.canvas;
16  
17  import org.htmlunit.corejs.javascript.Context;
18  import org.htmlunit.corejs.javascript.Function;
19  import org.htmlunit.corejs.javascript.Scriptable;
20  import org.htmlunit.corejs.javascript.ScriptableObject;
21  import org.htmlunit.corejs.javascript.typedarrays.NativeArrayBuffer;
22  import org.htmlunit.corejs.javascript.typedarrays.NativeUint8ClampedArray;
23  import org.htmlunit.javascript.HtmlUnitScriptable;
24  import org.htmlunit.javascript.JavaScriptEngine;
25  import org.htmlunit.javascript.configuration.JsxClass;
26  import org.htmlunit.javascript.configuration.JsxConstructor;
27  import org.htmlunit.javascript.configuration.JsxGetter;
28  import org.htmlunit.javascript.host.dom.DOMException;
29  import org.htmlunit.platform.canvas.rendering.RenderingBackend;
30  
31  /**
32   * A JavaScript object for {@code ImageData}.
33   *
34   * @author Ahmed Ashour
35   * @author Ronald Brill
36   */
37  @JsxClass
38  public class ImageData extends HtmlUnitScriptable {
39  
40      private final byte[] bytes_;
41      private final int width_;
42      private final int height_;
43      private NativeUint8ClampedArray data_;
44  
45      /**
46       * Default constructor.
47       */
48      public ImageData() {
49          this(null, 0, 0, 0, 0);
50      }
51  
52      /**
53       * JavaScript constructor.
54       * @param cx the current context
55       * @param scope the scope
56       * @param args the arguments to the WebSocket constructor
57       * @param ctorObj the function object
58       * @param inNewExpr Is new or not
59       * @return the java object to allow JavaScript to access
60       */
61      @JsxConstructor
62      public static ImageData jsConstructor(final Context cx, final Scriptable scope,
63              final Object[] args, final Function ctorObj, final boolean inNewExpr) {
64          if (args.length < 2) {
65              throw JavaScriptEngine.typeError("ImageData ctor - too less arguments");
66          }
67  
68          NativeUint8ClampedArray data = null;
69          final int width;
70          final int height;
71          if (args[0] instanceof NativeUint8ClampedArray) {
72              data = (NativeUint8ClampedArray) args[0];
73              if (data.getArrayLength() % 4 != 0) {
74                  throw JavaScriptEngine.asJavaScriptException(
75                          (HtmlUnitScriptable) JavaScriptEngine.getTopCallScope(),
76                          "ImageData ctor - data length mod 4 not zero",
77                          DOMException.INVALID_STATE_ERR);
78              }
79  
80              width = (int) JavaScriptEngine.toInteger(args[1]);
81              if (args.length < 3) {
82                  height = data.getArrayLength() / 4 / width;
83  
84                  if (data.getArrayLength() != 4 * width * height) {
85                      throw JavaScriptEngine.asJavaScriptException(
86                              (HtmlUnitScriptable) JavaScriptEngine.getTopCallScope(),
87                              "ImageData ctor - width not correct",
88                              DOMException.INDEX_SIZE_ERR);
89                  }
90              }
91              else {
92                  height = (int) JavaScriptEngine.toInteger(args[2]);
93              }
94  
95              if (data.getArrayLength() != 4 * width * height) {
96                  throw JavaScriptEngine.asJavaScriptException(
97                          (HtmlUnitScriptable) JavaScriptEngine.getTopCallScope(),
98                          "ImageData ctor - width/height not correct",
99                          DOMException.INDEX_SIZE_ERR);
100             }
101         }
102         else {
103             width = (int) JavaScriptEngine.toInteger(args[0]);
104             height = (int) JavaScriptEngine.toInteger(args[1]);
105         }
106 
107         if (width < 0) {
108             throw JavaScriptEngine.asJavaScriptException(
109                     (HtmlUnitScriptable) JavaScriptEngine.getTopCallScope(),
110                     "ImageData ctor - width negative",
111                     DOMException.INDEX_SIZE_ERR);
112         }
113         if (height < 0) {
114             throw JavaScriptEngine.asJavaScriptException(
115                     (HtmlUnitScriptable) JavaScriptEngine.getTopCallScope(),
116                     "ImageData ctor - height negative",
117                     DOMException.INDEX_SIZE_ERR);
118         }
119 
120         final ImageData result = new ImageData(null, 0, 0, width, height);
121         if (data != null) {
122             final byte[] bytes = data.getBuffer().getBuffer();
123             System.arraycopy(bytes, 0, result.bytes_, 0, Math.min(bytes.length, result.bytes_.length));
124         }
125         return result;
126     }
127 
128     ImageData(final RenderingBackend context, final int x, final int y, final int width, final int height) {
129         super();
130         if (context == null) {
131             bytes_ = new byte[width * height * 4];
132         }
133         else {
134             bytes_ = context.getBytes(width, height, x, y);
135         }
136 
137         width_ = width;
138         height_ = height;
139     }
140 
141     /**
142      * Returns the {@code width} property.
143      * @return the {@code width} property
144      */
145     @JsxGetter
146     public int getWidth() {
147         return width_;
148     }
149 
150     /**
151      * Returns the {@code height} property.
152      * @return the {@code height} property
153      */
154     @JsxGetter
155     public int getHeight() {
156         return height_;
157     }
158 
159     /**
160      * Returns a {@link NativeUint8ClampedArray} representing a one-dimensional array containing
161      * the data in the RGBA order, with integer values between 0 and 255 (included).
162      * @return the {@code data} property
163      */
164     @JsxGetter
165     public NativeUint8ClampedArray getData() {
166         if (data_ == null) {
167             final NativeArrayBuffer arrayBuffer = new NativeArrayBuffer(bytes_.length);
168             System.arraycopy(bytes_, 0, arrayBuffer.getBuffer(), 0, bytes_.length);
169 
170             data_ = new NativeUint8ClampedArray(arrayBuffer, 0, bytes_.length);
171             data_.setParentScope(getParentScope());
172             data_.setPrototype(ScriptableObject.getClassPrototype(getWindow(this), data_.getClassName()));
173         }
174 
175         return data_;
176     }
177 
178 }