p5 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{src → dist}/accessibility/color_namer.js +48 -3
- package/{src → dist}/accessibility/describe.js +12 -2
- package/{src → dist}/accessibility/gridOutput.js +2 -2
- package/dist/accessibility/index.js +60 -0
- package/{src → dist}/accessibility/outputs.js +23 -2
- package/{src → dist}/accessibility/textOutput.js +2 -2
- package/dist/app.js +120 -0
- package/{src → dist}/color/color_conversion.js +48 -10
- package/{src → dist}/color/color_spaces/hsb.js +3 -1
- package/dist/color/creating_reading.js +3 -0
- package/dist/color/index.js +13 -0
- package/dist/color/p5.Color.culori.js +1 -0
- package/dist/color/p5.Color.js +3 -0
- package/{src → dist}/color/setting.js +9 -6
- package/{src/core/constants.js → dist/constants-tYr0tCl8.js} +284 -132
- package/{src → dist}/core/States.js +3 -1
- package/dist/core/constants.js +1 -0
- package/{src → dist}/core/environment.js +12 -10
- package/{src → dist}/core/friendly_errors/browser_errors.js +1 -1
- package/{src → dist}/core/friendly_errors/fes_core.js +14 -44
- package/{src → dist}/core/friendly_errors/file_errors.js +6 -3
- package/dist/core/friendly_errors/index.js +23 -0
- package/dist/core/friendly_errors/param_validator.js +5421 -0
- package/{src → dist}/core/friendly_errors/sketch_reader.js +50 -4
- package/{src → dist}/core/friendly_errors/sketch_verifier.js +6 -6
- package/{src → dist}/core/friendly_errors/stacktrace.js +3 -5
- package/{src → dist}/core/friendly_errors/validate_params.js +50 -41
- package/{src → dist}/core/helpers.js +9 -6
- package/dist/core/init.js +105 -0
- package/dist/core/internationalization.js +302 -0
- package/dist/core/legacy.js +73 -0
- package/dist/core/main.js +44 -0
- package/dist/core/noop.js +3 -0
- package/dist/core/p5.Graphics.js +40 -0
- package/dist/core/p5.Renderer.js +11 -0
- package/dist/core/p5.Renderer2D.js +44 -0
- package/dist/core/reference.js +1 -0
- package/dist/core/rendering.js +40 -0
- package/{src → dist}/core/structure.js +3 -3
- package/{src → dist}/core/transform.js +2 -2
- package/{src/color/creating_reading.js → dist/creating_reading-Cr8L2Jnm.js} +841 -13
- package/{src → dist}/data/index.js +3 -1
- package/{src → dist}/data/local_storage.js +2 -8
- package/{src → dist}/dom/dom.js +19 -13
- package/dist/dom/index.js +18 -0
- package/{src → dist}/dom/p5.Element.js +14 -12
- package/{src → dist}/dom/p5.File.js +4 -4
- package/{src → dist}/dom/p5.MediaElement.js +10 -4
- package/{src → dist}/events/acceleration.js +26 -26
- package/{src → dist}/events/index.js +3 -1
- package/{src → dist}/events/keyboard.js +14 -12
- package/{src → dist}/events/pointer.js +16 -17
- package/dist/image/const.js +9 -0
- package/{src → dist}/image/filterRenderer2D.js +57 -37
- package/{src → dist}/image/filters.js +1 -3
- package/dist/image/image.js +40 -0
- package/dist/image/index.js +51 -0
- package/dist/image/loading_displaying.js +40 -0
- package/dist/image/p5.Image.js +11 -0
- package/{src → dist}/image/pixels.js +5 -3
- package/{src → dist}/io/csv.js +72 -70
- package/dist/io/files.js +40 -0
- package/dist/io/index.js +51 -0
- package/{src → dist}/io/p5.Table.js +6 -6
- package/{src → dist}/io/p5.TableRow.js +5 -6
- package/{src → dist}/io/p5.XML.js +2 -5
- package/{src → dist}/io/utilities.js +1 -1
- package/{src/core/p5.Renderer2D.js → dist/main-CAxvgiOV.js} +738 -57
- package/{src → dist}/math/Matrices/Matrix.js +10 -8
- package/{src → dist}/math/Matrices/MatrixInterface.js +5 -3
- package/{src → dist}/math/Matrices/MatrixNumjs.js +12 -26
- package/{src → dist}/math/calculation.js +2 -2
- package/{src → dist}/math/index.js +6 -3
- package/{src → dist}/math/math.js +3 -3
- package/{src → dist}/math/noise.js +2 -2
- package/{src → dist}/math/p5.Matrix.js +7 -4
- package/{src → dist}/math/p5.Vector.js +6 -6
- package/{src → dist}/math/random.js +2 -2
- package/{src → dist}/math/trigonometry.js +16 -15
- package/{src/image/p5.Image.js → dist/p5.Renderer-Swjl9HQO.js} +393 -22
- package/dist/rendering-B5TRR7aY.js +24960 -0
- package/{src → dist}/shape/2d_primitives.js +18 -17
- package/{src → dist}/shape/attributes.js +18 -17
- package/{src → dist}/shape/curves.js +2 -2
- package/{src → dist}/shape/custom_shapes.js +44 -64
- package/{src → dist}/shape/index.js +10 -2
- package/{src → dist}/shape/vertex.js +2 -3
- package/dist/type/index.js +25 -0
- package/{src → dist}/type/lib/Typr.js +76 -94
- package/{src → dist}/type/p5.Font.js +37 -63
- package/{src → dist}/type/textCore.js +35 -58
- package/{src → dist}/type/unicodeRanges.js +3 -1
- package/{src → dist}/utilities/conversion.js +2 -2
- package/{src → dist}/utilities/index.js +3 -1
- package/{src → dist}/utilities/time_date.js +6 -7
- package/{src → dist}/utilities/utility_functions.js +2 -2
- package/dist/webgl/3d_primitives.js +40 -0
- package/{src → dist}/webgl/GeometryBufferCache.js +3 -1
- package/{src → dist}/webgl/GeometryBuilder.js +12 -8
- package/{src → dist}/webgl/ShaderGenerator.js +79 -82
- package/{src → dist}/webgl/ShapeBuilder.js +26 -23
- package/dist/webgl/index.js +76 -0
- package/{src → dist}/webgl/interaction.js +7 -6
- package/dist/webgl/light.js +40 -0
- package/{src → dist}/webgl/loading.js +45 -12
- package/dist/webgl/material.js +40 -0
- package/dist/webgl/p5.Camera.js +40 -0
- package/{src → dist}/webgl/p5.DataArray.js +3 -5
- package/dist/webgl/p5.Framebuffer.js +40 -0
- package/{src → dist}/webgl/p5.Geometry.js +12 -15
- package/{src → dist}/webgl/p5.Quat.js +5 -4
- package/{src → dist}/webgl/p5.RenderBuffer.js +2 -3
- package/dist/webgl/p5.RendererGL.js +40 -0
- package/dist/webgl/p5.Shader.js +40 -0
- package/dist/webgl/p5.Texture.js +40 -0
- package/{src → dist}/webgl/text.js +78 -38
- package/lib/p5.esm.js +296 -194
- package/lib/p5.js +296 -194
- package/lib/p5.min.js +1 -1
- package/package.json +17 -17
- package/translations/dev.js +6 -6
- package/translations/index.js +1 -1
- package/types/accessibility/color_namer.d.ts +8 -0
- package/types/accessibility/describe.d.ts +184 -0
- package/types/accessibility/gridOutput.d.ts +8 -0
- package/types/accessibility/outputs.d.ts +235 -0
- package/types/accessibility/textOutput.d.ts +8 -0
- package/types/color/color_conversion.d.ts +47 -0
- package/types/color/creating_reading.d.ts +1348 -0
- package/types/color/p5.Color.d.ts +1070 -0
- package/types/color/setting.d.ts +2085 -0
- package/types/core/constants.d.ts +341 -0
- package/types/core/environment.d.ts +668 -0
- package/types/core/friendly_errors/fes_core.d.ts +8 -0
- package/types/core/friendly_errors/file_errors.d.ts +8 -0
- package/types/core/friendly_errors/param_validator.d.ts +30 -0
- package/types/core/friendly_errors/sketch_reader.d.ts +8 -0
- package/types/core/friendly_errors/stacktrace.d.ts +11 -0
- package/types/core/friendly_errors/validate_params.d.ts +8 -0
- package/types/core/helpers.d.ts +8 -0
- package/types/core/legacy.d.ts +8 -0
- package/types/core/main.d.ts +5996 -0
- package/types/core/p5.Graphics.d.ts +484 -0
- package/types/core/p5.Renderer.d.ts +14 -0
- package/types/core/reference.d.ts +8 -0
- package/types/core/rendering.d.ts +481 -0
- package/types/core/structure.d.ts +492 -0
- package/types/core/transform.d.ts +1638 -0
- package/types/data/local_storage.d.ts +323 -0
- package/types/dom/dom.d.ts +1295 -0
- package/types/dom/p5.Element.d.ts +2011 -0
- package/types/dom/p5.File.d.ts +13 -0
- package/types/dom/p5.MediaElement.d.ts +1249 -0
- package/types/events/acceleration.d.ts +193 -0
- package/types/events/keyboard.d.ts +499 -0
- package/types/events/pointer.d.ts +782 -0
- package/types/global.d.ts +5542 -0
- package/types/image/filterRenderer2D.d.ts +54 -0
- package/types/image/image.d.ts +326 -0
- package/types/image/loading_displaying.d.ts +580 -0
- package/types/image/p5.Image.d.ts +5882 -0
- package/types/image/pixels.d.ts +832 -0
- package/types/io/files.d.ts +1447 -0
- package/types/io/p5.Table.d.ts +1247 -0
- package/types/io/p5.TableRow.d.ts +343 -0
- package/types/io/p5.XML.d.ts +1188 -0
- package/types/math/Matrices/Matrix.d.ts +1029 -0
- package/types/math/Matrices/MatrixNumjs.d.ts +8 -0
- package/types/math/calculation.d.ts +923 -0
- package/types/math/math.d.ts +90 -0
- package/types/math/noise.d.ts +311 -0
- package/types/math/p5.Matrix.d.ts +8 -0
- package/types/math/p5.Vector.d.ts +3416 -0
- package/types/math/random.d.ts +267 -0
- package/types/math/trigonometry.d.ts +663 -0
- package/types/p5.d.ts +6663 -0
- package/types/shape/2d_primitives.d.ts +1033 -0
- package/types/shape/attributes.d.ts +466 -0
- package/types/shape/curves.d.ts +740 -0
- package/types/shape/custom_shapes.d.ts +888 -0
- package/types/shape/vertex.d.ts +1141 -0
- package/types/type/p5.Font.d.ts +575 -0
- package/types/type/textCore.d.ts +1198 -0
- package/types/utilities/conversion.d.ts +894 -0
- package/types/utilities/time_date.d.ts +295 -0
- package/types/utilities/utility_functions.d.ts +587 -0
- package/types/webgl/3d_primitives.d.ts +1432 -0
- package/types/webgl/ShaderGenerator.d.ts +8 -0
- package/types/webgl/interaction.d.ts +371 -0
- package/types/webgl/light.d.ts +1184 -0
- package/types/webgl/loading.d.ts +481 -0
- package/types/webgl/material.d.ts +2656 -0
- package/types/webgl/p5.Camera.d.ts +3023 -0
- package/types/webgl/p5.DataArray.d.ts +61 -0
- package/types/webgl/p5.Framebuffer.d.ts +760 -0
- package/types/webgl/p5.Geometry.d.ts +1191 -0
- package/types/webgl/p5.Quat.d.ts +45 -0
- package/types/webgl/p5.RendererGL.d.ts +234 -0
- package/types/webgl/p5.Shader.d.ts +660 -0
- package/types/webgl/p5.Texture.d.ts +61 -0
- package/types/webgl/text.d.ts +74 -0
- package/src/README.md +0 -27
- package/src/accessibility/index.js +0 -13
- package/src/app.js +0 -61
- package/src/color/index.js +0 -9
- package/src/color/p5.Color.culori.js +0 -66
- package/src/color/p5.Color.js +0 -851
- package/src/core/README.md +0 -91
- package/src/core/friendly_errors/index.js +0 -13
- package/src/core/friendly_errors/param_validator.js +0 -561
- package/src/core/init.js +0 -58
- package/src/core/internationalization.js +0 -195
- package/src/core/legacy.js +0 -29
- package/src/core/main.js +0 -689
- package/src/core/noop.js +0 -1
- package/src/core/p5.Graphics.js +0 -696
- package/src/core/p5.Renderer.js +0 -408
- package/src/core/reference.js +0 -2060
- package/src/core/rendering.js +0 -697
- package/src/dom/index.js +0 -11
- package/src/image/const.js +0 -6
- package/src/image/image.js +0 -731
- package/src/image/index.js +0 -15
- package/src/image/loading_displaying.js +0 -1431
- package/src/io/files.js +0 -2210
- package/src/io/index.js +0 -11
- package/src/math/README.md +0 -40
- package/src/type/index.js +0 -9
- package/src/webgl/3d_primitives.js +0 -2741
- package/src/webgl/index.js +0 -37
- package/src/webgl/light.js +0 -1851
- package/src/webgl/material.js +0 -3854
- package/src/webgl/p5.Camera.js +0 -4010
- package/src/webgl/p5.Framebuffer.js +0 -1865
- package/src/webgl/p5.RendererGL.js +0 -2867
- package/src/webgl/p5.Shader.js +0 -1505
- package/src/webgl/p5.Texture.js +0 -541
- package/src/webgl/shaders/basic.frag +0 -6
- package/src/webgl/shaders/filters/base.frag +0 -22
- package/src/webgl/shaders/filters/base.vert +0 -19
- package/src/webgl/shaders/filters/blur.frag +0 -60
- package/src/webgl/shaders/filters/default.vert +0 -18
- package/src/webgl/shaders/filters/dilate.frag +0 -39
- package/src/webgl/shaders/filters/erode.frag +0 -39
- package/src/webgl/shaders/filters/gray.frag +0 -16
- package/src/webgl/shaders/filters/invert.frag +0 -15
- package/src/webgl/shaders/filters/opaque.frag +0 -12
- package/src/webgl/shaders/filters/posterize.frag +0 -29
- package/src/webgl/shaders/filters/threshold.frag +0 -23
- package/src/webgl/shaders/font.frag +0 -216
- package/src/webgl/shaders/font.vert +0 -44
- package/src/webgl/shaders/imageLight.vert +0 -33
- package/src/webgl/shaders/imageLightDiffused.frag +0 -82
- package/src/webgl/shaders/imageLightSpecular.frag +0 -134
- package/src/webgl/shaders/light.vert +0 -37
- package/src/webgl/shaders/light_texture.frag +0 -26
- package/src/webgl/shaders/lighting.glsl +0 -227
- package/src/webgl/shaders/line.frag +0 -74
- package/src/webgl/shaders/line.vert +0 -294
- package/src/webgl/shaders/normal.frag +0 -6
- package/src/webgl/shaders/normal.vert +0 -72
- package/src/webgl/shaders/phong.frag +0 -84
- package/src/webgl/shaders/phong.vert +0 -87
- package/src/webgl/shaders/point.frag +0 -29
- package/src/webgl/shaders/point.vert +0 -19
- package/src/webgl/shaders/sphereMapping.frag +0 -26
- package/src/webgl/shaders/webgl2Compatibility.glsl +0 -34
|
@@ -1,1431 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module Image
|
|
3
|
-
* @submodule Loading & Displaying
|
|
4
|
-
* @for p5
|
|
5
|
-
* @requires core
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import canvas from '../core/helpers';
|
|
9
|
-
import * as constants from '../core/constants';
|
|
10
|
-
import { request } from '../io/files';
|
|
11
|
-
import * as omggif from 'omggif';
|
|
12
|
-
import { GIFEncoder, quantize, nearestColorIndex } from 'gifenc';
|
|
13
|
-
|
|
14
|
-
function loadingDisplaying(p5, fn){
|
|
15
|
-
/**
|
|
16
|
-
* Loads an image to create a <a href="#/p5.Image">p5.Image</a> object.
|
|
17
|
-
*
|
|
18
|
-
* `loadImage()` interprets the first parameter one of three ways. If the path
|
|
19
|
-
* to an image file is provided, `loadImage()` will load it. Paths to local
|
|
20
|
-
* files should be relative, such as `'assets/thundercat.jpg'`. URLs such as
|
|
21
|
-
* `'https://example.com/thundercat.jpg'` may be blocked due to browser
|
|
22
|
-
* security. Raw image data can also be passed as a base64 encoded image in
|
|
23
|
-
* the form `'data:image/png;base64,arandomsequenceofcharacters'`. The `path`
|
|
24
|
-
* parameter can also be defined as a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
|
|
25
|
-
* object for more advanced usage.
|
|
26
|
-
*
|
|
27
|
-
* The second parameter is optional. If a function is passed, it will be
|
|
28
|
-
* called once the image has loaded. The callback function can optionally use
|
|
29
|
-
* the new <a href="#/p5.Image">p5.Image</a> object. The return value of the
|
|
30
|
-
* function will be used as the final return value of `loadImage()`.
|
|
31
|
-
*
|
|
32
|
-
* The third parameter is also optional. If a function is passed, it will be
|
|
33
|
-
* called if the image fails to load. The callback function can optionally use
|
|
34
|
-
* the event error. The return value of the function will be used as the final
|
|
35
|
-
* return value of `loadImage()`.
|
|
36
|
-
*
|
|
37
|
-
* This function returns a `Promise` and should be used in an `async` setup with
|
|
38
|
-
* `await`. See the examples for the usage syntax.
|
|
39
|
-
*
|
|
40
|
-
* @method loadImage
|
|
41
|
-
* @param {String|Request} path path of the image to be loaded or base64 encoded image.
|
|
42
|
-
* @param {function(p5.Image)} [successCallback] function called with
|
|
43
|
-
* <a href="#/p5.Image">p5.Image</a> once it
|
|
44
|
-
* loads.
|
|
45
|
-
* @param {function(Event)} [failureCallback] function called with event
|
|
46
|
-
* error if the image fails to load.
|
|
47
|
-
* @return {Promise<p5.Image>} the <a href="#/p5.Image">p5.Image</a> object.
|
|
48
|
-
*
|
|
49
|
-
* @example
|
|
50
|
-
* <div>
|
|
51
|
-
* <code>
|
|
52
|
-
* let img;
|
|
53
|
-
*
|
|
54
|
-
* // Load the image and create a p5.Image object.
|
|
55
|
-
* async function setup() {
|
|
56
|
-
* img = await loadImage('assets/laDefense.jpg');
|
|
57
|
-
* createCanvas(100, 100);
|
|
58
|
-
*
|
|
59
|
-
* // Draw the image.
|
|
60
|
-
* image(img, 0, 0);
|
|
61
|
-
*
|
|
62
|
-
* describe('Image of the underside of a white umbrella and a gridded ceiling.');
|
|
63
|
-
* }
|
|
64
|
-
* </code>
|
|
65
|
-
* </div>
|
|
66
|
-
*
|
|
67
|
-
* <div>
|
|
68
|
-
* <code>
|
|
69
|
-
* async function setup() {
|
|
70
|
-
* // Call handleImage() once the image loads.
|
|
71
|
-
* await loadImage('assets/laDefense.jpg', handleImage);
|
|
72
|
-
*
|
|
73
|
-
* describe('Image of the underside of a white umbrella and a gridded ceiling.');
|
|
74
|
-
* }
|
|
75
|
-
*
|
|
76
|
-
* // Display the image.
|
|
77
|
-
* function handleImage(img) {
|
|
78
|
-
* image(img, 0, 0);
|
|
79
|
-
* }
|
|
80
|
-
* </code>
|
|
81
|
-
* </div>
|
|
82
|
-
*
|
|
83
|
-
* <div>
|
|
84
|
-
* <code>
|
|
85
|
-
* async function setup() {
|
|
86
|
-
* // Call handleImage() once the image loads or
|
|
87
|
-
* // call handleError() if an error occurs.
|
|
88
|
-
* await loadImage('assets/laDefense.jpg', handleImage, handleError);
|
|
89
|
-
* }
|
|
90
|
-
*
|
|
91
|
-
* // Display the image.
|
|
92
|
-
* function handleImage(img) {
|
|
93
|
-
* image(img, 0, 0);
|
|
94
|
-
*
|
|
95
|
-
* describe('Image of the underside of a white umbrella and a gridded ceiling.');
|
|
96
|
-
* }
|
|
97
|
-
*
|
|
98
|
-
* // Log the error.
|
|
99
|
-
* function handleError(event) {
|
|
100
|
-
* console.error('Oops!', event);
|
|
101
|
-
* }
|
|
102
|
-
* </code>
|
|
103
|
-
* </div>
|
|
104
|
-
*/
|
|
105
|
-
fn.loadImage = async function(
|
|
106
|
-
path,
|
|
107
|
-
successCallback,
|
|
108
|
-
failureCallback
|
|
109
|
-
) {
|
|
110
|
-
// p5._validateParameters('loadImage', arguments);
|
|
111
|
-
|
|
112
|
-
try{
|
|
113
|
-
let pImg = new p5.Image(1, 1, this);
|
|
114
|
-
|
|
115
|
-
const req = new Request(path, {
|
|
116
|
-
method: 'GET',
|
|
117
|
-
mode: 'cors'
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
const { data, headers } = await request(req, 'bytes');
|
|
121
|
-
|
|
122
|
-
// GIF section
|
|
123
|
-
const contentType = headers.get('content-type');
|
|
124
|
-
|
|
125
|
-
if (contentType === null) {
|
|
126
|
-
console.warn(
|
|
127
|
-
'The image you loaded does not have a Content-Type header. If you are using the online editor consider reuploading the asset.'
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (contentType && contentType.includes('image/gif')) {
|
|
132
|
-
await _createGif(
|
|
133
|
-
data,
|
|
134
|
-
pImg
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
} else {
|
|
138
|
-
// Non-GIF Section
|
|
139
|
-
const blob = new Blob([data]);
|
|
140
|
-
const img = await createImageBitmap(blob);
|
|
141
|
-
|
|
142
|
-
pImg.width = pImg.canvas.width = img.width;
|
|
143
|
-
pImg.height = pImg.canvas.height = img.height;
|
|
144
|
-
|
|
145
|
-
// Draw the image into the backing canvas of the p5.Image
|
|
146
|
-
pImg.drawingContext.drawImage(img, 0, 0);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
pImg.modified = true;
|
|
150
|
-
|
|
151
|
-
if(successCallback){
|
|
152
|
-
return successCallback(pImg);
|
|
153
|
-
}else{
|
|
154
|
-
return pImg;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
} catch(err) {
|
|
158
|
-
p5._friendlyFileLoadError(0, path);
|
|
159
|
-
if (typeof failureCallback === 'function') {
|
|
160
|
-
return failureCallback(err);
|
|
161
|
-
} else {
|
|
162
|
-
throw err;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Generates a gif from a sketch and saves it to a file.
|
|
169
|
-
*
|
|
170
|
-
* `saveGif()` may be called in <a href="#/p5/setup">setup()</a> or at any
|
|
171
|
-
* point while a sketch is running.
|
|
172
|
-
*
|
|
173
|
-
* The first parameter, `fileName`, sets the gif's file name.
|
|
174
|
-
*
|
|
175
|
-
* The second parameter, `duration`, sets the gif's duration in seconds.
|
|
176
|
-
*
|
|
177
|
-
* The third parameter, `options`, is optional. If an object is passed,
|
|
178
|
-
* `saveGif()` will use its properties to customize the gif. `saveGif()`
|
|
179
|
-
* recognizes the properties `delay`, `units`, `silent`,
|
|
180
|
-
* `notificationDuration`, and `notificationID`.
|
|
181
|
-
*
|
|
182
|
-
* @method saveGif
|
|
183
|
-
* @param {String} filename file name of gif.
|
|
184
|
-
* @param {Number} duration duration in seconds to capture from the sketch.
|
|
185
|
-
* @param {Object} [options] an object that can contain five more properties:
|
|
186
|
-
* `delay`, a Number specifying how much time to wait before recording;
|
|
187
|
-
* `units`, a String that can be either 'seconds' or 'frames'. By default it's 'seconds’;
|
|
188
|
-
* `silent`, a Boolean that defines presence of progress notifications. By default it’s `false`;
|
|
189
|
-
* `notificationDuration`, a Number that defines how long in seconds the final notification
|
|
190
|
-
* will live. By default it's `0`, meaning the notification will never be removed;
|
|
191
|
-
* `notificationID`, a String that specifies the id of the notification's DOM element. By default it’s `'progressBar’`.
|
|
192
|
-
*
|
|
193
|
-
* @example
|
|
194
|
-
* <div>
|
|
195
|
-
* <code>
|
|
196
|
-
* function setup() {
|
|
197
|
-
* createCanvas(100, 100);
|
|
198
|
-
*
|
|
199
|
-
* describe('A circle drawn in the middle of a gray square. The circle changes color from black to white, then repeats.');
|
|
200
|
-
* }
|
|
201
|
-
*
|
|
202
|
-
* function draw() {
|
|
203
|
-
* background(200);
|
|
204
|
-
*
|
|
205
|
-
* // Style the circle.
|
|
206
|
-
* let c = frameCount % 255;
|
|
207
|
-
* fill(c);
|
|
208
|
-
*
|
|
209
|
-
* // Display the circle.
|
|
210
|
-
* circle(50, 50, 25);
|
|
211
|
-
* }
|
|
212
|
-
*
|
|
213
|
-
* // Save a 5-second gif when the user presses the 's' key.
|
|
214
|
-
* function keyPressed() {
|
|
215
|
-
* if (key === 's') {
|
|
216
|
-
* saveGif('mySketch', 5);
|
|
217
|
-
* }
|
|
218
|
-
* }
|
|
219
|
-
* </code>
|
|
220
|
-
* </div>
|
|
221
|
-
*
|
|
222
|
-
* <div>
|
|
223
|
-
* <code>
|
|
224
|
-
* function setup() {
|
|
225
|
-
* createCanvas(100, 100);
|
|
226
|
-
*
|
|
227
|
-
* describe('A circle drawn in the middle of a gray square. The circle changes color from black to white, then repeats.');
|
|
228
|
-
* }
|
|
229
|
-
*
|
|
230
|
-
* function draw() {
|
|
231
|
-
* background(200);
|
|
232
|
-
*
|
|
233
|
-
* // Style the circle.
|
|
234
|
-
* let c = frameCount % 255;
|
|
235
|
-
* fill(c);
|
|
236
|
-
*
|
|
237
|
-
* // Display the circle.
|
|
238
|
-
* circle(50, 50, 25);
|
|
239
|
-
* }
|
|
240
|
-
*
|
|
241
|
-
* // Save a 5-second gif when the user presses the 's' key.
|
|
242
|
-
* // Wait 1 second after the key press before recording.
|
|
243
|
-
* function keyPressed() {
|
|
244
|
-
* if (key === 's') {
|
|
245
|
-
* saveGif('mySketch', 5, { delay: 1 });
|
|
246
|
-
* }
|
|
247
|
-
* }
|
|
248
|
-
* </code>
|
|
249
|
-
* </div>
|
|
250
|
-
*/
|
|
251
|
-
fn.saveGif = async function(
|
|
252
|
-
fileName,
|
|
253
|
-
duration,
|
|
254
|
-
options = {
|
|
255
|
-
delay: 0,
|
|
256
|
-
units: 'seconds',
|
|
257
|
-
silent: false,
|
|
258
|
-
notificationDuration: 0,
|
|
259
|
-
notificationID: 'progressBar'
|
|
260
|
-
}
|
|
261
|
-
) {
|
|
262
|
-
// validate parameters
|
|
263
|
-
if (typeof fileName !== 'string') {
|
|
264
|
-
throw TypeError('fileName parameter must be a string');
|
|
265
|
-
}
|
|
266
|
-
if (typeof duration !== 'number') {
|
|
267
|
-
throw TypeError('Duration parameter must be a number');
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// extract variables for more comfortable use
|
|
271
|
-
const delay = (options && options.delay) || 0; // in seconds
|
|
272
|
-
const units = (options && options.units) || 'seconds'; // either 'seconds' or 'frames'
|
|
273
|
-
const silent = (options && options.silent) || false;
|
|
274
|
-
const notificationDuration = (options && options.notificationDuration) || 0;
|
|
275
|
-
const notificationID = (options && options.notificationID) || 'progressBar';
|
|
276
|
-
|
|
277
|
-
// if arguments in the options object are not correct, cancel operation
|
|
278
|
-
if (typeof delay !== 'number') {
|
|
279
|
-
throw TypeError('Delay parameter must be a number');
|
|
280
|
-
}
|
|
281
|
-
// if units is not seconds nor frames, throw error
|
|
282
|
-
if (units !== 'seconds' && units !== 'frames') {
|
|
283
|
-
throw TypeError('Units parameter must be either "frames" or "seconds"');
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (typeof silent !== 'boolean') {
|
|
287
|
-
throw TypeError('Silent parameter must be a boolean');
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (typeof notificationDuration !== 'number') {
|
|
291
|
-
throw TypeError('Notification duration parameter must be a number');
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
if (typeof notificationID !== 'string') {
|
|
295
|
-
throw TypeError('Notification ID parameter must be a string');
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
this._recording = true;
|
|
299
|
-
|
|
300
|
-
// get the project's framerate
|
|
301
|
-
let _frameRate = this._targetFrameRate;
|
|
302
|
-
// if it is undefined or some non useful value, assume it's 60
|
|
303
|
-
if (_frameRate === Infinity || _frameRate === undefined || _frameRate === 0) {
|
|
304
|
-
_frameRate = 60;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// calculate frame delay based on frameRate
|
|
308
|
-
|
|
309
|
-
// this delay has nothing to do with the
|
|
310
|
-
// delay in options, but rather is the delay
|
|
311
|
-
// we have to specify to the gif encoder between frames.
|
|
312
|
-
let gifFrameDelay = 1 / _frameRate * 1000;
|
|
313
|
-
|
|
314
|
-
// constrain it to be always greater than 20,
|
|
315
|
-
// otherwise it won't work in some browsers and systems
|
|
316
|
-
// reference: https://stackoverflow.com/questions/64473278/gif-frame-duration-seems-slower-than-expected
|
|
317
|
-
gifFrameDelay = gifFrameDelay < 20 ? 20 : gifFrameDelay;
|
|
318
|
-
|
|
319
|
-
// check the mode we are in and how many frames
|
|
320
|
-
// that duration translates to
|
|
321
|
-
const nFrames = units === 'seconds' ? duration * _frameRate : duration;
|
|
322
|
-
const nFramesDelay = units === 'seconds' ? delay * _frameRate : delay;
|
|
323
|
-
const totalNumberOfFrames = nFrames + nFramesDelay;
|
|
324
|
-
|
|
325
|
-
// initialize variables for the frames processing
|
|
326
|
-
let frameIterator = nFramesDelay;
|
|
327
|
-
this.frameCount = frameIterator;
|
|
328
|
-
|
|
329
|
-
const lastPixelDensity = this._renderer._pixelDensity;
|
|
330
|
-
this.pixelDensity(1);
|
|
331
|
-
|
|
332
|
-
// We first take every frame that we are going to use for the animation
|
|
333
|
-
let frames = [];
|
|
334
|
-
|
|
335
|
-
if (document.getElementById(notificationID) !== null)
|
|
336
|
-
document.getElementById(notificationID).remove();
|
|
337
|
-
|
|
338
|
-
let p;
|
|
339
|
-
if (!silent){
|
|
340
|
-
p = this.createP('');
|
|
341
|
-
p.id(notificationID);
|
|
342
|
-
p.style('font-size', '16px');
|
|
343
|
-
p.style('font-family', 'Montserrat');
|
|
344
|
-
p.style('background-color', '#ffffffa0');
|
|
345
|
-
p.style('padding', '8px');
|
|
346
|
-
p.style('border-radius', '10px');
|
|
347
|
-
p.position(0, 0);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
let pixels;
|
|
351
|
-
let gl;
|
|
352
|
-
if (this._renderer instanceof p5.RendererGL) {
|
|
353
|
-
// if we have a WEBGL context, initialize the pixels array
|
|
354
|
-
// and the gl context to use them inside the loop
|
|
355
|
-
gl = this.drawingContext;
|
|
356
|
-
pixels = new Uint8Array(gl.drawingBufferWidth * gl.drawingBufferHeight * 4);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// stop the loop since we are going to manually redraw
|
|
360
|
-
this.noLoop();
|
|
361
|
-
|
|
362
|
-
// Defer execution until the rest of the call stack finishes, allowing the
|
|
363
|
-
// rest of `setup` to be called (and, importantly, canvases hidden in setup
|
|
364
|
-
// to be unhidden.)
|
|
365
|
-
//
|
|
366
|
-
// Waiting on this empty promise means we'll continue as soon as setup
|
|
367
|
-
// finishes without waiting for another frame.
|
|
368
|
-
await Promise.resolve();
|
|
369
|
-
|
|
370
|
-
while (frameIterator < totalNumberOfFrames) {
|
|
371
|
-
/*
|
|
372
|
-
we draw the next frame. this is important, since
|
|
373
|
-
busy sketches or low end devices might take longer
|
|
374
|
-
to render some frames. So we just wait for the frame
|
|
375
|
-
to be drawn and immediately save it to a buffer and continue
|
|
376
|
-
*/
|
|
377
|
-
this.redraw();
|
|
378
|
-
|
|
379
|
-
// depending on the context we'll extract the pixels one way
|
|
380
|
-
// or another
|
|
381
|
-
let data = undefined;
|
|
382
|
-
|
|
383
|
-
if (this._renderer instanceof p5.RendererGL) {
|
|
384
|
-
pixels = new Uint8Array(
|
|
385
|
-
gl.drawingBufferWidth * gl.drawingBufferHeight * 4
|
|
386
|
-
);
|
|
387
|
-
gl.readPixels(
|
|
388
|
-
0,
|
|
389
|
-
0,
|
|
390
|
-
gl.drawingBufferWidth,
|
|
391
|
-
gl.drawingBufferHeight,
|
|
392
|
-
gl.RGBA,
|
|
393
|
-
gl.UNSIGNED_BYTE,
|
|
394
|
-
pixels
|
|
395
|
-
);
|
|
396
|
-
|
|
397
|
-
data = _flipPixels(pixels, this.width, this.height);
|
|
398
|
-
} else {
|
|
399
|
-
data = this.drawingContext.getImageData(0, 0, this.width, this.height)
|
|
400
|
-
.data;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
frames.push(data);
|
|
404
|
-
frameIterator++;
|
|
405
|
-
|
|
406
|
-
if (!silent) {
|
|
407
|
-
p.html(
|
|
408
|
-
'Saved frame <b>' +
|
|
409
|
-
frames.length.toString() +
|
|
410
|
-
'</b> out of ' +
|
|
411
|
-
nFrames.toString()
|
|
412
|
-
);
|
|
413
|
-
}
|
|
414
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
415
|
-
}
|
|
416
|
-
if (!silent) p.html('Frames processed, generating color palette...');
|
|
417
|
-
|
|
418
|
-
this.loop();
|
|
419
|
-
this.pixelDensity(lastPixelDensity);
|
|
420
|
-
|
|
421
|
-
// create the gif encoder and the colorspace format
|
|
422
|
-
const gif = GIFEncoder();
|
|
423
|
-
|
|
424
|
-
// calculate the global palette for this set of frames
|
|
425
|
-
const globalPalette = _generateGlobalPalette(frames);
|
|
426
|
-
|
|
427
|
-
// Rather than using applyPalette() from the gifenc library, we use our
|
|
428
|
-
// own function to map frame pixels to a palette color. This way, we can
|
|
429
|
-
// cache palette color mappings between frames for extra performance, and
|
|
430
|
-
// use our own caching mechanism to avoid flickering colors from cache
|
|
431
|
-
// key collisions.
|
|
432
|
-
const paletteCache = {};
|
|
433
|
-
const getIndexedFrame = frame => {
|
|
434
|
-
const length = frame.length / 4;
|
|
435
|
-
const index = new Uint8Array(length);
|
|
436
|
-
for (let i = 0; i < length; i++) {
|
|
437
|
-
const key =
|
|
438
|
-
(frame[i * 4] << 24) |
|
|
439
|
-
(frame[i * 4 + 1] << 16) |
|
|
440
|
-
(frame[i * 4 + 2] << 8) |
|
|
441
|
-
frame[i * 4 + 3];
|
|
442
|
-
if (paletteCache[key] === undefined) {
|
|
443
|
-
paletteCache[key] = nearestColorIndex(
|
|
444
|
-
globalPalette,
|
|
445
|
-
frame.slice(i * 4, (i + 1) * 4)
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
index[i] = paletteCache[key];
|
|
449
|
-
}
|
|
450
|
-
return index;
|
|
451
|
-
};
|
|
452
|
-
|
|
453
|
-
// the way we designed the palette means we always take the last index for transparency
|
|
454
|
-
const transparentIndex = globalPalette.length - 1;
|
|
455
|
-
|
|
456
|
-
// we are going to iterate the frames in pairs, n-1 and n
|
|
457
|
-
let prevIndexedFrame = [];
|
|
458
|
-
for (let i = 0; i < frames.length; i++) {
|
|
459
|
-
//const indexedFrame = applyPalette(frames[i], globalPaletteWithoutAlpha, 'rgba565');
|
|
460
|
-
const indexedFrame = getIndexedFrame(frames[i]);
|
|
461
|
-
|
|
462
|
-
// Make a copy of the palette-applied frame before editing the original
|
|
463
|
-
// to use transparent pixels
|
|
464
|
-
const originalIndexedFrame = indexedFrame.slice();
|
|
465
|
-
|
|
466
|
-
if (i === 0) {
|
|
467
|
-
gif.writeFrame(indexedFrame, this.width, this.height, {
|
|
468
|
-
palette: globalPalette,
|
|
469
|
-
delay: gifFrameDelay,
|
|
470
|
-
dispose: 1
|
|
471
|
-
});
|
|
472
|
-
} else {
|
|
473
|
-
// Matching pixels between frames can be set to full transparency,
|
|
474
|
-
// allowing the previous frame's pixels to show through. We only do
|
|
475
|
-
// this for pixels that get mapped to the same quantized color so that
|
|
476
|
-
// the resulting image would be the same.
|
|
477
|
-
for (let i = 0; i < indexedFrame.length; i++) {
|
|
478
|
-
if (indexedFrame[i] === prevIndexedFrame[i]) {
|
|
479
|
-
indexedFrame[i] = transparentIndex;
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// Write frame into the encoder
|
|
484
|
-
gif.writeFrame(indexedFrame, this.width, this.height, {
|
|
485
|
-
delay: gifFrameDelay,
|
|
486
|
-
transparent: true,
|
|
487
|
-
transparentIndex,
|
|
488
|
-
dispose: 1
|
|
489
|
-
});
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
prevIndexedFrame = originalIndexedFrame;
|
|
493
|
-
|
|
494
|
-
if (!silent) {
|
|
495
|
-
p.html(
|
|
496
|
-
'Rendered frame <b>' + i.toString() + '</b> out of ' + nFrames.toString()
|
|
497
|
-
);
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
// this just makes the process asynchronous, preventing
|
|
502
|
-
// that the encoding locks up the browser
|
|
503
|
-
await new Promise(resolve => setTimeout(resolve, 0));
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
gif.finish();
|
|
507
|
-
|
|
508
|
-
// Get a direct typed array view into the buffer to avoid copying it
|
|
509
|
-
const buffer = gif.bytesView();
|
|
510
|
-
const extension = 'gif';
|
|
511
|
-
|
|
512
|
-
const blob = new Blob([buffer], {
|
|
513
|
-
type: 'image/gif'
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
frames = [];
|
|
517
|
-
this._recording = false;
|
|
518
|
-
this.loop();
|
|
519
|
-
|
|
520
|
-
if (!silent){
|
|
521
|
-
p.html('Done. Downloading your gif!🌸');
|
|
522
|
-
if(notificationDuration > 0)
|
|
523
|
-
setTimeout(() => p.remove(), notificationDuration * 1000);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
fn.downloadFile(blob, fileName, extension);
|
|
527
|
-
};
|
|
528
|
-
|
|
529
|
-
function _flipPixels(pixels, width, height) {
|
|
530
|
-
// extracting the pixels using readPixels returns
|
|
531
|
-
// an upside down image. we have to flip it back
|
|
532
|
-
// first. this solution is proposed by gman on
|
|
533
|
-
// this stack overflow answer:
|
|
534
|
-
// https://stackoverflow.com/questions/41969562/how-can-i-flip-the-result-of-webglrenderingcontext-readpixels
|
|
535
|
-
|
|
536
|
-
const halfHeight = parseInt(height / 2);
|
|
537
|
-
const bytesPerRow = width * 4;
|
|
538
|
-
|
|
539
|
-
// make a temp buffer to hold one row
|
|
540
|
-
const temp = new Uint8Array(width * 4);
|
|
541
|
-
for (let y = 0; y < halfHeight; ++y) {
|
|
542
|
-
const topOffset = y * bytesPerRow;
|
|
543
|
-
const bottomOffset = (height - y - 1) * bytesPerRow;
|
|
544
|
-
|
|
545
|
-
// make copy of a row on the top half
|
|
546
|
-
temp.set(pixels.subarray(topOffset, topOffset + bytesPerRow));
|
|
547
|
-
|
|
548
|
-
// copy a row from the bottom half to the top
|
|
549
|
-
pixels.copyWithin(topOffset, bottomOffset, bottomOffset + bytesPerRow);
|
|
550
|
-
|
|
551
|
-
// copy the copy of the top half row to the bottom half
|
|
552
|
-
pixels.set(temp, bottomOffset);
|
|
553
|
-
}
|
|
554
|
-
return pixels;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
function _generateGlobalPalette(frames) {
|
|
558
|
-
// make an array the size of every possible color in every possible frame
|
|
559
|
-
// that is: width * height * frames.
|
|
560
|
-
let allColors = new Uint8Array(frames.length * frames[0].length);
|
|
561
|
-
|
|
562
|
-
// put every frame one after the other in sequence.
|
|
563
|
-
// this array will hold absolutely every pixel from the animation.
|
|
564
|
-
// the set function on the Uint8Array works super fast tho!
|
|
565
|
-
for (let f = 0; f < frames.length; f++) {
|
|
566
|
-
allColors.set(frames[f], f * frames[0].length);
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
// quantize this massive array into 256 colors and return it!
|
|
570
|
-
let colorPalette = quantize(allColors, 256, {
|
|
571
|
-
format: 'rgba4444',
|
|
572
|
-
oneBitAlpha: true
|
|
573
|
-
});
|
|
574
|
-
|
|
575
|
-
// when generating the palette, we have to leave space for 1 of the
|
|
576
|
-
// indices to be a random color that does not appear anywhere in our
|
|
577
|
-
// animation to use for transparency purposes. So, if the palette is full
|
|
578
|
-
// (has 256 colors), we overwrite the last one with a random, fully transparent
|
|
579
|
-
// color. Otherwise, we just push a new color into the palette the same way.
|
|
580
|
-
|
|
581
|
-
// this guarantees that when using the transparency index, there are no matches
|
|
582
|
-
// between some colors of the animation and the "holes" we want to dig on them,
|
|
583
|
-
// which would cause pieces of some frames to be transparent and thus look glitchy.
|
|
584
|
-
if (colorPalette.length === 256) {
|
|
585
|
-
colorPalette[colorPalette.length - 1] = [
|
|
586
|
-
Math.random() * 255,
|
|
587
|
-
Math.random() * 255,
|
|
588
|
-
Math.random() * 255,
|
|
589
|
-
0
|
|
590
|
-
];
|
|
591
|
-
} else {
|
|
592
|
-
colorPalette.push([
|
|
593
|
-
Math.random() * 255,
|
|
594
|
-
Math.random() * 255,
|
|
595
|
-
Math.random() * 255,
|
|
596
|
-
0
|
|
597
|
-
]);
|
|
598
|
-
}
|
|
599
|
-
return colorPalette;
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
/**
|
|
603
|
-
* Helper function for loading GIF-based images
|
|
604
|
-
*/
|
|
605
|
-
async function _createGif(arrayBuffer, pImg) {
|
|
606
|
-
// TODO: Replace with ImageDecoder once it is widely available
|
|
607
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/ImageDecoder
|
|
608
|
-
const gifReader = new omggif.GifReader(arrayBuffer);
|
|
609
|
-
pImg.width = pImg.canvas.width = gifReader.width;
|
|
610
|
-
pImg.height = pImg.canvas.height = gifReader.height;
|
|
611
|
-
const frames = [];
|
|
612
|
-
const numFrames = gifReader.numFrames();
|
|
613
|
-
let framePixels = new Uint8ClampedArray(pImg.width * pImg.height * 4);
|
|
614
|
-
|
|
615
|
-
const loadGIFFrameIntoImage = (frameNum, gifReader) => {
|
|
616
|
-
try {
|
|
617
|
-
gifReader.decodeAndBlitFrameRGBA(frameNum, framePixels);
|
|
618
|
-
} catch (e) {
|
|
619
|
-
p5._friendlyFileLoadError(8, pImg.src);
|
|
620
|
-
throw e;
|
|
621
|
-
}
|
|
622
|
-
};
|
|
623
|
-
|
|
624
|
-
for (let j = 0; j < numFrames; j++) {
|
|
625
|
-
const frameInfo = gifReader.frameInfo(j);
|
|
626
|
-
const prevFrameData = pImg.drawingContext.getImageData(
|
|
627
|
-
0,
|
|
628
|
-
0,
|
|
629
|
-
pImg.width,
|
|
630
|
-
pImg.height
|
|
631
|
-
);
|
|
632
|
-
framePixels = prevFrameData.data.slice();
|
|
633
|
-
loadGIFFrameIntoImage(j, gifReader);
|
|
634
|
-
const imageData = new ImageData(framePixels, pImg.width, pImg.height);
|
|
635
|
-
pImg.drawingContext.putImageData(imageData, 0, 0);
|
|
636
|
-
let frameDelay = frameInfo.delay;
|
|
637
|
-
// To maintain the default of 10FPS when frameInfo.delay equals to 0
|
|
638
|
-
if (frameDelay === 0) {
|
|
639
|
-
frameDelay = 10;
|
|
640
|
-
}
|
|
641
|
-
frames.push({
|
|
642
|
-
image: pImg.drawingContext.getImageData(0, 0, pImg.width, pImg.height),
|
|
643
|
-
delay: frameDelay * 10 //GIF stores delay in one-hundredth of a second, shift to ms
|
|
644
|
-
});
|
|
645
|
-
|
|
646
|
-
// Some GIFs are encoded so that they expect the previous frame
|
|
647
|
-
// to be under the current frame. This can occur at a sub-frame level
|
|
648
|
-
//
|
|
649
|
-
// Values : 0 - No disposal specified. The decoder is
|
|
650
|
-
// not required to take any action.
|
|
651
|
-
// 1 - Do not dispose. The graphic is to be left
|
|
652
|
-
// in place.
|
|
653
|
-
// 2 - Restore to background color. The area used by the
|
|
654
|
-
// graphic must be restored to the background color.
|
|
655
|
-
// 3 - Restore to previous. The decoder is required to
|
|
656
|
-
// restore the area overwritten by the graphic with
|
|
657
|
-
// what was there prior to rendering the graphic.
|
|
658
|
-
// 4-7 - To be defined.
|
|
659
|
-
if (frameInfo.disposal === 2) {
|
|
660
|
-
// Restore background color
|
|
661
|
-
pImg.drawingContext.clearRect(
|
|
662
|
-
frameInfo.x,
|
|
663
|
-
frameInfo.y,
|
|
664
|
-
frameInfo.width,
|
|
665
|
-
frameInfo.height
|
|
666
|
-
);
|
|
667
|
-
} else if (frameInfo.disposal === 3) {
|
|
668
|
-
// Restore previous
|
|
669
|
-
pImg.drawingContext.putImageData(
|
|
670
|
-
prevFrameData,
|
|
671
|
-
0,
|
|
672
|
-
0,
|
|
673
|
-
frameInfo.x,
|
|
674
|
-
frameInfo.y,
|
|
675
|
-
frameInfo.width,
|
|
676
|
-
frameInfo.height
|
|
677
|
-
);
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
//Uses Netscape block encoding
|
|
682
|
-
//to repeat forever, this will be 0
|
|
683
|
-
//to repeat just once, this will be null
|
|
684
|
-
//to repeat N times (1<N), should contain integer for loop number
|
|
685
|
-
//this is changed to more usable values for us
|
|
686
|
-
//to repeat forever, loopCount = null
|
|
687
|
-
//everything else is just the number of loops
|
|
688
|
-
let loopLimit = gifReader.loopCount();
|
|
689
|
-
if (loopLimit === null) {
|
|
690
|
-
loopLimit = 1;
|
|
691
|
-
} else if (loopLimit === 0) {
|
|
692
|
-
loopLimit = null;
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
// we used the pImg for painting and saving during load
|
|
696
|
-
// so we have to reset it to the first frame
|
|
697
|
-
pImg.drawingContext.putImageData(frames[0].image, 0, 0);
|
|
698
|
-
|
|
699
|
-
if (frames.length > 1) {
|
|
700
|
-
pImg.gifProperties = {
|
|
701
|
-
displayIndex: 0,
|
|
702
|
-
loopLimit,
|
|
703
|
-
loopCount: 0,
|
|
704
|
-
frames,
|
|
705
|
-
numFrames,
|
|
706
|
-
playing: true,
|
|
707
|
-
timeDisplayed: 0,
|
|
708
|
-
lastChangeTime: 0
|
|
709
|
-
};
|
|
710
|
-
}
|
|
711
|
-
|
|
712
|
-
return pImg;
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
/**
|
|
716
|
-
* @private
|
|
717
|
-
* @param {(LEFT|RIGHT|CENTER)} xAlign either LEFT, RIGHT or CENTER
|
|
718
|
-
* @param {(TOP|BOTTOM|CENTER)} yAlign either TOP, BOTTOM or CENTER
|
|
719
|
-
* @param {Number} dx
|
|
720
|
-
* @param {Number} dy
|
|
721
|
-
* @param {Number} dw
|
|
722
|
-
* @param {Number} dh
|
|
723
|
-
* @param {Number} sw
|
|
724
|
-
* @param {Number} sh
|
|
725
|
-
* @returns {Object}
|
|
726
|
-
*/
|
|
727
|
-
|
|
728
|
-
function _imageContain(xAlign, yAlign, dx, dy, dw, dh, sw, sh) {
|
|
729
|
-
const r = Math.max(sw / dw, sh / dh);
|
|
730
|
-
const [adjusted_dw, adjusted_dh] = [sw / r, sh / r];
|
|
731
|
-
let x = dx;
|
|
732
|
-
let y = dy;
|
|
733
|
-
|
|
734
|
-
if (xAlign === constants.CENTER) {
|
|
735
|
-
x += (dw - adjusted_dw) / 2;
|
|
736
|
-
} else if (xAlign === constants.RIGHT) {
|
|
737
|
-
x += dw - adjusted_dw;
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
if (yAlign === constants.CENTER) {
|
|
741
|
-
y += (dh - adjusted_dh) / 2;
|
|
742
|
-
} else if (yAlign === constants.BOTTOM) {
|
|
743
|
-
y += dh - adjusted_dh;
|
|
744
|
-
}
|
|
745
|
-
return { x, y, w: adjusted_dw, h: adjusted_dh };
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
/**
|
|
749
|
-
* @private
|
|
750
|
-
* @param {(LEFT|RIGHT|CENTER)} xAlign either LEFT, RIGHT or CENTER
|
|
751
|
-
* @param {(TOP|BOTTOM|CENTER)} yAlign either TOP, BOTTOM or CENTER
|
|
752
|
-
* @param {Number} dw
|
|
753
|
-
* @param {Number} dh
|
|
754
|
-
* @param {Number} sx
|
|
755
|
-
* @param {Number} sy
|
|
756
|
-
* @param {Number} sw
|
|
757
|
-
* @param {Number} sh
|
|
758
|
-
* @returns {Object}
|
|
759
|
-
*/
|
|
760
|
-
function _imageCover(xAlign, yAlign, dw, dh, sx, sy, sw, sh) {
|
|
761
|
-
const r = Math.max(dw / sw, dh / sh);
|
|
762
|
-
const [adjusted_sw, adjusted_sh] = [dw / r, dh / r];
|
|
763
|
-
|
|
764
|
-
let x = sx;
|
|
765
|
-
let y = sy;
|
|
766
|
-
|
|
767
|
-
if (xAlign === constants.CENTER) {
|
|
768
|
-
x += (sw - adjusted_sw) / 2;
|
|
769
|
-
} else if (xAlign === constants.RIGHT) {
|
|
770
|
-
x += sw - adjusted_sw;
|
|
771
|
-
}
|
|
772
|
-
|
|
773
|
-
if (yAlign === constants.CENTER) {
|
|
774
|
-
y += (sh - adjusted_sh) / 2;
|
|
775
|
-
} else if (yAlign === constants.BOTTOM) {
|
|
776
|
-
y += sh - adjusted_sh;
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
return { x, y, w: adjusted_sw, h: adjusted_sh };
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
/**
|
|
783
|
-
* @private
|
|
784
|
-
* @param {(CONTAIN|COVER)} [fit] either CONTAIN or COVER
|
|
785
|
-
* @param {(LEFT|RIGHT|CENTER)} xAlign either LEFT, RIGHT or CENTER
|
|
786
|
-
* @param {(TOP|BOTTOM|CENTER)} yAlign either TOP, BOTTOM or CENTER
|
|
787
|
-
* @param {Number} dx
|
|
788
|
-
* @param {Number} dy
|
|
789
|
-
* @param {Number} dw
|
|
790
|
-
* @param {Number} dh
|
|
791
|
-
* @param {Number} sx
|
|
792
|
-
* @param {Number} sy
|
|
793
|
-
* @param {Number} sw
|
|
794
|
-
* @param {Number} sh
|
|
795
|
-
* @returns {Object}
|
|
796
|
-
*/
|
|
797
|
-
function _imageFit(fit, xAlign, yAlign, dx, dy, dw, dh, sx, sy, sw, sh) {
|
|
798
|
-
if (fit === constants.COVER) {
|
|
799
|
-
const { x, y, w, h } = _imageCover(xAlign, yAlign, dw, dh, sx, sy, sw, sh);
|
|
800
|
-
sx = x;
|
|
801
|
-
sy = y;
|
|
802
|
-
sw = w;
|
|
803
|
-
sh = h;
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
if (fit === constants.CONTAIN) {
|
|
807
|
-
const { x, y, w, h } = _imageContain(
|
|
808
|
-
xAlign,
|
|
809
|
-
yAlign,
|
|
810
|
-
dx,
|
|
811
|
-
dy,
|
|
812
|
-
dw,
|
|
813
|
-
dh,
|
|
814
|
-
sw,
|
|
815
|
-
sh
|
|
816
|
-
);
|
|
817
|
-
dx = x;
|
|
818
|
-
dy = y;
|
|
819
|
-
dw = w;
|
|
820
|
-
dh = h;
|
|
821
|
-
}
|
|
822
|
-
return { sx, sy, sw, sh, dx, dy, dw, dh };
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
/**
|
|
826
|
-
* Validates clipping params. Per drawImage spec sWidth and sHight cannot be
|
|
827
|
-
* negative or greater than image intrinsic width and height
|
|
828
|
-
* @private
|
|
829
|
-
* @param {Number} sVal
|
|
830
|
-
* @param {Number} iVal
|
|
831
|
-
* @returns {Number}
|
|
832
|
-
* @private
|
|
833
|
-
*/
|
|
834
|
-
function _sAssign(sVal, iVal) {
|
|
835
|
-
if (sVal > 0 && sVal < iVal) {
|
|
836
|
-
return sVal;
|
|
837
|
-
} else {
|
|
838
|
-
return iVal;
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
/**
|
|
843
|
-
* Draws an image to the canvas.
|
|
844
|
-
*
|
|
845
|
-
* The first parameter, `img`, is the source image to be drawn. `img` can be
|
|
846
|
-
* any of the following objects:
|
|
847
|
-
* - <a href="#/p5.Image">p5.Image</a>
|
|
848
|
-
* - <a href="#/p5.Element">p5.Element</a>
|
|
849
|
-
* - <a href="#/p5.Texture">p5.Texture</a>
|
|
850
|
-
* - <a href="#/p5.Framebuffer">p5.Framebuffer</a>
|
|
851
|
-
* - <a href="#/p5.FramebufferTexture">p5.FramebufferTexture</a>
|
|
852
|
-
*
|
|
853
|
-
* The second and third parameters, `dx` and `dy`, set the coordinates of the
|
|
854
|
-
* destination image's top left corner. See
|
|
855
|
-
* <a href="#/p5/imageMode">imageMode()</a> for other ways to position images.
|
|
856
|
-
*
|
|
857
|
-
* ```js example
|
|
858
|
-
* let img;
|
|
859
|
-
*
|
|
860
|
-
* async function setup() {
|
|
861
|
-
* // Load the image.
|
|
862
|
-
* img = await loadImage('assets/laDefense.jpg');
|
|
863
|
-
*
|
|
864
|
-
* createCanvas(100, 100);
|
|
865
|
-
*
|
|
866
|
-
* background(50);
|
|
867
|
-
*
|
|
868
|
-
* // Draw the image.
|
|
869
|
-
* image(img, 0, 0);
|
|
870
|
-
*
|
|
871
|
-
* describe('An image of the underside of a white umbrella with a gridded ceiling above.');
|
|
872
|
-
* }
|
|
873
|
-
* ```
|
|
874
|
-
*
|
|
875
|
-
* Here's a diagram that explains how optional parameters work in `image()`:
|
|
876
|
-
*
|
|
877
|
-
* <img src="assets/drawImage.png"></img>
|
|
878
|
-
*
|
|
879
|
-
* The fourth and fifth parameters, `dw` and `dh`, are optional. They set the
|
|
880
|
-
* the width and height to draw the destination image. By default, `image()`
|
|
881
|
-
* draws the full source image at its original size.
|
|
882
|
-
*
|
|
883
|
-
* The sixth and seventh parameters, `sx` and `sy`, are also optional.
|
|
884
|
-
* These coordinates define the top left corner of a subsection to draw from
|
|
885
|
-
* the source image.
|
|
886
|
-
*
|
|
887
|
-
* The eighth and ninth parameters, `sw` and `sh`, are also optional.
|
|
888
|
-
* They define the width and height of a subsection to draw from the source
|
|
889
|
-
* image. By default, `image()` draws the full subsection that begins at
|
|
890
|
-
* `(sx, sy)` and extends to the edges of the source image.
|
|
891
|
-
*
|
|
892
|
-
* The ninth parameter, `fit`, is also optional. It enables a subsection of
|
|
893
|
-
* the source image to be drawn without affecting its aspect ratio. If
|
|
894
|
-
* `CONTAIN` is passed, the full subsection will appear within the destination
|
|
895
|
-
* rectangle. If `COVER` is passed, the subsection will completely cover the
|
|
896
|
-
* destination rectangle. This may have the effect of zooming into the
|
|
897
|
-
* subsection.
|
|
898
|
-
*
|
|
899
|
-
* The tenth and eleventh paremeters, `xAlign` and `yAlign`, are also
|
|
900
|
-
* optional. They determine how to align the fitted subsection. `xAlign` can
|
|
901
|
-
* be set to either `LEFT`, `RIGHT`, or `CENTER`. `yAlign` can be set to
|
|
902
|
-
* either `TOP`, `BOTTOM`, or `CENTER`. By default, both `xAlign` and `yAlign`
|
|
903
|
-
* are set to `CENTER`.
|
|
904
|
-
*
|
|
905
|
-
* @method image
|
|
906
|
-
* @param {p5.Image|p5.Element|p5.Texture|p5.Framebuffer|p5.FramebufferTexture} img image to display.
|
|
907
|
-
* @param {Number} x x-coordinate of the top-left corner of the image.
|
|
908
|
-
* @param {Number} y y-coordinate of the top-left corner of the image.
|
|
909
|
-
* @param {Number} [width] width to draw the image.
|
|
910
|
-
* @param {Number} [height] height to draw the image.
|
|
911
|
-
*
|
|
912
|
-
* @example
|
|
913
|
-
* <div>
|
|
914
|
-
* <code>
|
|
915
|
-
* let img;
|
|
916
|
-
*
|
|
917
|
-
* async function setup() {
|
|
918
|
-
* // Load the image.
|
|
919
|
-
* img = await loadImage('assets/laDefense.jpg');
|
|
920
|
-
*
|
|
921
|
-
* createCanvas(100, 100);
|
|
922
|
-
*
|
|
923
|
-
* background(50);
|
|
924
|
-
*
|
|
925
|
-
* // Draw the image.
|
|
926
|
-
* image(img, 10, 10);
|
|
927
|
-
*
|
|
928
|
-
* describe('An image of the underside of a white umbrella with a gridded ceiling above. The image has dark gray borders on its left and top.');
|
|
929
|
-
* }
|
|
930
|
-
* </code>
|
|
931
|
-
* </div>
|
|
932
|
-
*
|
|
933
|
-
* <div>
|
|
934
|
-
* <code>
|
|
935
|
-
* let img;
|
|
936
|
-
*
|
|
937
|
-
* async function setup() {
|
|
938
|
-
* // Load the image.
|
|
939
|
-
* img = await loadImage('assets/laDefense.jpg');
|
|
940
|
-
*
|
|
941
|
-
* createCanvas(100, 100);
|
|
942
|
-
*
|
|
943
|
-
* background(50);
|
|
944
|
-
*
|
|
945
|
-
* // Draw the image 50x50.
|
|
946
|
-
* image(img, 0, 0, 50, 50);
|
|
947
|
-
*
|
|
948
|
-
* describe('An image of the underside of a white umbrella with a gridded ceiling above. The image is drawn in the top left corner of a dark gray square.');
|
|
949
|
-
* }
|
|
950
|
-
* </code>
|
|
951
|
-
* </div>
|
|
952
|
-
*
|
|
953
|
-
* <div>
|
|
954
|
-
* <code>
|
|
955
|
-
* let img;
|
|
956
|
-
*
|
|
957
|
-
* async function setup() {
|
|
958
|
-
* // Load the image.
|
|
959
|
-
* img = await loadImage('assets/laDefense.jpg');
|
|
960
|
-
*
|
|
961
|
-
* createCanvas(100, 100);
|
|
962
|
-
*
|
|
963
|
-
* background(50);
|
|
964
|
-
*
|
|
965
|
-
* // Draw the center of the image.
|
|
966
|
-
* image(img, 25, 25, 50, 50, 25, 25, 50, 50);
|
|
967
|
-
*
|
|
968
|
-
* describe('An image of a gridded ceiling drawn in the center of a dark gray square.');
|
|
969
|
-
* }
|
|
970
|
-
* </code>
|
|
971
|
-
* </div>
|
|
972
|
-
*
|
|
973
|
-
* <div>
|
|
974
|
-
* <code>
|
|
975
|
-
* let img;
|
|
976
|
-
*
|
|
977
|
-
* async function setup() {
|
|
978
|
-
* // Load the image.
|
|
979
|
-
* img = await loadImage('assets/moonwalk.jpg');
|
|
980
|
-
* createCanvas(100, 100);
|
|
981
|
-
*
|
|
982
|
-
* background(50);
|
|
983
|
-
*
|
|
984
|
-
* // Draw the image and scale it to fit within the canvas.
|
|
985
|
-
* image(img, 0, 0, width, height, 0, 0, img.width, img.height, CONTAIN);
|
|
986
|
-
*
|
|
987
|
-
* describe('An image of an astronaut on the moon. The top and bottom borders of the image are dark gray.');
|
|
988
|
-
* }
|
|
989
|
-
* </code>
|
|
990
|
-
* </div>
|
|
991
|
-
*
|
|
992
|
-
* <div>
|
|
993
|
-
* <code>
|
|
994
|
-
* let img;
|
|
995
|
-
*
|
|
996
|
-
* async function setup() {
|
|
997
|
-
* // Load the image.
|
|
998
|
-
* img = await loadImage('assets/laDefense50.png');
|
|
999
|
-
*
|
|
1000
|
-
* createCanvas(100, 100);
|
|
1001
|
-
*
|
|
1002
|
-
* background(50);
|
|
1003
|
-
*
|
|
1004
|
-
* // Draw the image and scale it to cover the canvas.
|
|
1005
|
-
* image(img, 0, 0, width, height, 0, 0, img.width, img.height, COVER);
|
|
1006
|
-
*
|
|
1007
|
-
* describe('A pixelated image of the underside of a white umbrella with a gridded ceiling above.');
|
|
1008
|
-
* }
|
|
1009
|
-
* </code>
|
|
1010
|
-
* </div>
|
|
1011
|
-
*/
|
|
1012
|
-
/**
|
|
1013
|
-
* @method image
|
|
1014
|
-
* @param {p5.Image|p5.Element|p5.Texture|p5.Framebuffer|p5.FramebufferTexture} img
|
|
1015
|
-
* @param {Number} dx the x-coordinate of the destination
|
|
1016
|
-
* rectangle in which to draw the source image
|
|
1017
|
-
* @param {Number} dy the y-coordinate of the destination
|
|
1018
|
-
* rectangle in which to draw the source image
|
|
1019
|
-
* @param {Number} dWidth the width of the destination rectangle
|
|
1020
|
-
* @param {Number} dHeight the height of the destination rectangle
|
|
1021
|
-
* @param {Number} sx the x-coordinate of the subsection of the source
|
|
1022
|
-
* image to draw into the destination rectangle
|
|
1023
|
-
* @param {Number} sy the y-coordinate of the subsection of the source
|
|
1024
|
-
* image to draw into the destination rectangle
|
|
1025
|
-
* @param {Number} [sWidth] the width of the subsection of the
|
|
1026
|
-
* source image to draw into the destination
|
|
1027
|
-
* rectangle
|
|
1028
|
-
* @param {Number} [sHeight] the height of the subsection of the
|
|
1029
|
-
* source image to draw into the destination rectangle
|
|
1030
|
-
* @param {(CONTAIN|COVER)} [fit] either CONTAIN or COVER
|
|
1031
|
-
* @param {(LEFT|RIGHT|CENTER)} [xAlign=CENTER] either LEFT, RIGHT or CENTER default is CENTER
|
|
1032
|
-
* @param {(TOP|BOTTOM|CENTER)} [yAlign=CENTER] either TOP, BOTTOM or CENTER default is CENTER
|
|
1033
|
-
*/
|
|
1034
|
-
fn.image = function(
|
|
1035
|
-
img,
|
|
1036
|
-
dx,
|
|
1037
|
-
dy,
|
|
1038
|
-
dWidth,
|
|
1039
|
-
dHeight,
|
|
1040
|
-
sx,
|
|
1041
|
-
sy,
|
|
1042
|
-
sWidth,
|
|
1043
|
-
sHeight,
|
|
1044
|
-
fit,
|
|
1045
|
-
xAlign,
|
|
1046
|
-
yAlign
|
|
1047
|
-
) {
|
|
1048
|
-
// set defaults per spec: https://goo.gl/3ykfOq
|
|
1049
|
-
|
|
1050
|
-
// p5._validateParameters('image', arguments);
|
|
1051
|
-
|
|
1052
|
-
let defW = img.width;
|
|
1053
|
-
let defH = img.height;
|
|
1054
|
-
yAlign = yAlign || constants.CENTER;
|
|
1055
|
-
xAlign = xAlign || constants.CENTER;
|
|
1056
|
-
|
|
1057
|
-
if (img.elt) {
|
|
1058
|
-
defW = defW !== undefined ? defW : img.elt.width;
|
|
1059
|
-
defH = defH !== undefined ? defH : img.elt.height;
|
|
1060
|
-
}
|
|
1061
|
-
if (img.elt && img.elt.videoWidth && !img.canvas) {
|
|
1062
|
-
// video no canvas
|
|
1063
|
-
defW = defW !== undefined ? defW : img.elt.videoWidth;
|
|
1064
|
-
defH = defH !== undefined ? defH : img.elt.videoHeight;
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
let _dx = dx;
|
|
1068
|
-
let _dy = dy;
|
|
1069
|
-
let _dw = dWidth || defW;
|
|
1070
|
-
let _dh = dHeight || defH;
|
|
1071
|
-
let _sx = sx || 0;
|
|
1072
|
-
let _sy = sy || 0;
|
|
1073
|
-
let _sw = sWidth !== undefined ? sWidth : defW;
|
|
1074
|
-
let _sh = sHeight !== undefined ? sHeight : defH;
|
|
1075
|
-
|
|
1076
|
-
_sw = _sAssign(_sw, defW);
|
|
1077
|
-
_sh = _sAssign(_sh, defH);
|
|
1078
|
-
|
|
1079
|
-
// This part needs cleanup and unit tests
|
|
1080
|
-
// see issues https://github.com/processing/p5.js/issues/1741
|
|
1081
|
-
// and https://github.com/processing/p5.js/issues/1673
|
|
1082
|
-
let pd = 1;
|
|
1083
|
-
|
|
1084
|
-
if (img.elt && !img.canvas && img.elt.style.width) {
|
|
1085
|
-
//if img is video and img.elt.size() has been used and
|
|
1086
|
-
//no width passed to image()
|
|
1087
|
-
if (img.elt.videoWidth && !dWidth) {
|
|
1088
|
-
pd = img.elt.videoWidth;
|
|
1089
|
-
} else {
|
|
1090
|
-
//all other cases
|
|
1091
|
-
pd = img.elt.width;
|
|
1092
|
-
}
|
|
1093
|
-
pd /= parseInt(img.elt.style.width, 10);
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
_sx *= pd;
|
|
1097
|
-
_sy *= pd;
|
|
1098
|
-
_sh *= pd;
|
|
1099
|
-
_sw *= pd;
|
|
1100
|
-
|
|
1101
|
-
let vals = canvas.modeAdjust(_dx, _dy, _dw, _dh, this._renderer.states.imageMode);
|
|
1102
|
-
vals = _imageFit(
|
|
1103
|
-
fit,
|
|
1104
|
-
xAlign,
|
|
1105
|
-
yAlign,
|
|
1106
|
-
vals.x,
|
|
1107
|
-
vals.y,
|
|
1108
|
-
vals.w,
|
|
1109
|
-
vals.h,
|
|
1110
|
-
_sx,
|
|
1111
|
-
_sy,
|
|
1112
|
-
_sw,
|
|
1113
|
-
_sh
|
|
1114
|
-
);
|
|
1115
|
-
|
|
1116
|
-
// tint the image if there is a tint
|
|
1117
|
-
this._renderer.image(
|
|
1118
|
-
img,
|
|
1119
|
-
vals.sx,
|
|
1120
|
-
vals.sy,
|
|
1121
|
-
vals.sw,
|
|
1122
|
-
vals.sh,
|
|
1123
|
-
vals.dx,
|
|
1124
|
-
vals.dy,
|
|
1125
|
-
vals.dw,
|
|
1126
|
-
vals.dh
|
|
1127
|
-
);
|
|
1128
|
-
};
|
|
1129
|
-
|
|
1130
|
-
/**
|
|
1131
|
-
* Tints images using a color.
|
|
1132
|
-
*
|
|
1133
|
-
* The version of `tint()` with one parameter interprets it one of four ways.
|
|
1134
|
-
* If the parameter is a number, it's interpreted as a grayscale value. If the
|
|
1135
|
-
* parameter is a string, it's interpreted as a CSS color string. An array of
|
|
1136
|
-
* `[R, G, B, A]` values or a <a href="#/p5.Color">p5.Color</a> object can
|
|
1137
|
-
* also be used to set the tint color.
|
|
1138
|
-
*
|
|
1139
|
-
* The version of `tint()` with two parameters uses the first one as a
|
|
1140
|
-
* grayscale value and the second as an alpha value. For example, calling
|
|
1141
|
-
* `tint(255, 128)` will make an image 50% transparent.
|
|
1142
|
-
*
|
|
1143
|
-
* The version of `tint()` with three parameters interprets them as RGB or
|
|
1144
|
-
* HSB values, depending on the current
|
|
1145
|
-
* <a href="#/p5/colorMode">colorMode()</a>. The optional fourth parameter
|
|
1146
|
-
* sets the alpha value. For example, `tint(255, 0, 0, 100)` will give images
|
|
1147
|
-
* a red tint and make them transparent.
|
|
1148
|
-
*
|
|
1149
|
-
* @method tint
|
|
1150
|
-
* @param {Number} v1 red or hue value.
|
|
1151
|
-
* @param {Number} v2 green or saturation value.
|
|
1152
|
-
* @param {Number} v3 blue or brightness.
|
|
1153
|
-
* @param {Number} [alpha]
|
|
1154
|
-
*
|
|
1155
|
-
* @example
|
|
1156
|
-
* <div>
|
|
1157
|
-
* <code>
|
|
1158
|
-
* let img;
|
|
1159
|
-
*
|
|
1160
|
-
* async function setup() {
|
|
1161
|
-
* // Load the image.
|
|
1162
|
-
* img = await loadImage('assets/laDefense.jpg');
|
|
1163
|
-
*
|
|
1164
|
-
* createCanvas(100, 100);
|
|
1165
|
-
*
|
|
1166
|
-
* // Left image.
|
|
1167
|
-
* image(img, 0, 0);
|
|
1168
|
-
*
|
|
1169
|
-
* // Right image.
|
|
1170
|
-
* // Tint with a CSS color string.
|
|
1171
|
-
* tint('red');
|
|
1172
|
-
* image(img, 50, 0);
|
|
1173
|
-
*
|
|
1174
|
-
* describe('Two images of an umbrella and a ceiling side-by-side. The image on the right has a red tint.');
|
|
1175
|
-
* }
|
|
1176
|
-
* </code>
|
|
1177
|
-
* </div>
|
|
1178
|
-
*
|
|
1179
|
-
* <div>
|
|
1180
|
-
* <code>
|
|
1181
|
-
* let img;
|
|
1182
|
-
*
|
|
1183
|
-
* async function setup() {
|
|
1184
|
-
* // Load the image.
|
|
1185
|
-
* img = await loadImage('assets/laDefense.jpg');
|
|
1186
|
-
*
|
|
1187
|
-
* createCanvas(100, 100);
|
|
1188
|
-
*
|
|
1189
|
-
* // Left image.
|
|
1190
|
-
* image(img, 0, 0);
|
|
1191
|
-
*
|
|
1192
|
-
* // Right image.
|
|
1193
|
-
* // Tint with RGB values.
|
|
1194
|
-
* tint(255, 0, 0);
|
|
1195
|
-
* image(img, 50, 0);
|
|
1196
|
-
*
|
|
1197
|
-
* describe('Two images of an umbrella and a ceiling side-by-side. The image on the right has a red tint.');
|
|
1198
|
-
* }
|
|
1199
|
-
* </code>
|
|
1200
|
-
* </div>
|
|
1201
|
-
*
|
|
1202
|
-
* <div>
|
|
1203
|
-
* <code>
|
|
1204
|
-
* let img;
|
|
1205
|
-
**
|
|
1206
|
-
* async function setup() {
|
|
1207
|
-
* // Load the image.
|
|
1208
|
-
* img = await loadImage('assets/laDefense.jpg');
|
|
1209
|
-
*
|
|
1210
|
-
* createCanvas(100, 100);
|
|
1211
|
-
*
|
|
1212
|
-
* // Left.
|
|
1213
|
-
* image(img, 0, 0);
|
|
1214
|
-
*
|
|
1215
|
-
* // Right.
|
|
1216
|
-
* // Tint with RGBA values.
|
|
1217
|
-
* tint(255, 0, 0, 100);
|
|
1218
|
-
* image(img, 50, 0);
|
|
1219
|
-
*
|
|
1220
|
-
* describe('Two images of an umbrella and a ceiling side-by-side. The image on the right has a transparent red tint.');
|
|
1221
|
-
* }
|
|
1222
|
-
* </code>
|
|
1223
|
-
* </div>
|
|
1224
|
-
*
|
|
1225
|
-
* <div>
|
|
1226
|
-
* <code>
|
|
1227
|
-
* let img;
|
|
1228
|
-
*
|
|
1229
|
-
* async function setup() {
|
|
1230
|
-
* // Load the image.
|
|
1231
|
-
* img = await loadImage('assets/laDefense.jpg');
|
|
1232
|
-
*
|
|
1233
|
-
* createCanvas(100, 100);
|
|
1234
|
-
*
|
|
1235
|
-
* // Left.
|
|
1236
|
-
* image(img, 0, 0);
|
|
1237
|
-
*
|
|
1238
|
-
* // Right.
|
|
1239
|
-
* // Tint with grayscale and alpha values.
|
|
1240
|
-
* tint(255, 180);
|
|
1241
|
-
* image(img, 50, 0);
|
|
1242
|
-
*
|
|
1243
|
-
* describe('Two images of an umbrella and a ceiling side-by-side. The image on the right is transparent.');
|
|
1244
|
-
* }
|
|
1245
|
-
* </code>
|
|
1246
|
-
* </div>
|
|
1247
|
-
*/
|
|
1248
|
-
/**
|
|
1249
|
-
* @method tint
|
|
1250
|
-
* @param {String} value CSS color string.
|
|
1251
|
-
*/
|
|
1252
|
-
|
|
1253
|
-
/**
|
|
1254
|
-
* @method tint
|
|
1255
|
-
* @param {Number} gray grayscale value.
|
|
1256
|
-
* @param {Number} [alpha]
|
|
1257
|
-
*/
|
|
1258
|
-
|
|
1259
|
-
/**
|
|
1260
|
-
* @method tint
|
|
1261
|
-
* @param {Number[]} values array containing the red, green, blue &
|
|
1262
|
-
* alpha components of the color.
|
|
1263
|
-
*/
|
|
1264
|
-
|
|
1265
|
-
/**
|
|
1266
|
-
* @method tint
|
|
1267
|
-
* @param {p5.Color} color the tint color
|
|
1268
|
-
*/
|
|
1269
|
-
fn.tint = function(...args) {
|
|
1270
|
-
// p5._validateParameters('tint', args);
|
|
1271
|
-
const c = this.color(...args);
|
|
1272
|
-
this._renderer.states.setValue('tint', c._getRGBA([255, 255, 255, 255]));
|
|
1273
|
-
};
|
|
1274
|
-
|
|
1275
|
-
/**
|
|
1276
|
-
* Removes the current tint set by <a href="#/p5/tint">tint()</a>.
|
|
1277
|
-
*
|
|
1278
|
-
* `noTint()` restores images to their original colors.
|
|
1279
|
-
*
|
|
1280
|
-
* @method noTint
|
|
1281
|
-
*
|
|
1282
|
-
* @example
|
|
1283
|
-
* <div>
|
|
1284
|
-
* <code>
|
|
1285
|
-
* let img;
|
|
1286
|
-
*
|
|
1287
|
-
* async function setup() {
|
|
1288
|
-
* // Load the image.
|
|
1289
|
-
* img = await loadImage('assets/laDefense.jpg');
|
|
1290
|
-
*
|
|
1291
|
-
* createCanvas(100, 100);
|
|
1292
|
-
*
|
|
1293
|
-
* // Left.
|
|
1294
|
-
* // Tint with a CSS color string.
|
|
1295
|
-
* tint('red');
|
|
1296
|
-
* image(img, 0, 0);
|
|
1297
|
-
*
|
|
1298
|
-
* // Right.
|
|
1299
|
-
* // Remove the tint.
|
|
1300
|
-
* noTint();
|
|
1301
|
-
* image(img, 50, 0);
|
|
1302
|
-
*
|
|
1303
|
-
* describe('Two images of an umbrella and a ceiling side-by-side. The image on the left has a red tint.');
|
|
1304
|
-
* }
|
|
1305
|
-
* </code>
|
|
1306
|
-
* </div>
|
|
1307
|
-
*/
|
|
1308
|
-
fn.noTint = function() {
|
|
1309
|
-
this._renderer.states.setValue('tint', null);
|
|
1310
|
-
};
|
|
1311
|
-
|
|
1312
|
-
/**
|
|
1313
|
-
* Apply the current tint color to the input image, return the resulting
|
|
1314
|
-
* canvas.
|
|
1315
|
-
*
|
|
1316
|
-
* @private
|
|
1317
|
-
* @param {p5.Image} The image to be tinted
|
|
1318
|
-
* @return {canvas} The resulting tinted canvas
|
|
1319
|
-
*/
|
|
1320
|
-
// fn._getTintedImageCanvas =
|
|
1321
|
-
// p5.Renderer2D.prototype._getTintedImageCanvas;
|
|
1322
|
-
|
|
1323
|
-
/**
|
|
1324
|
-
* Changes the location from which images are drawn when
|
|
1325
|
-
* <a href="#/p5/image">image()</a> is called.
|
|
1326
|
-
*
|
|
1327
|
-
* By default, the first
|
|
1328
|
-
* two parameters of <a href="#/p5/image">image()</a> are the x- and
|
|
1329
|
-
* y-coordinates of the image's upper-left corner. The next parameters are
|
|
1330
|
-
* its width and height. This is the same as calling `imageMode(CORNER)`.
|
|
1331
|
-
*
|
|
1332
|
-
* `imageMode(CORNERS)` also uses the first two parameters of
|
|
1333
|
-
* <a href="#/p5/image">image()</a> as the x- and y-coordinates of the image's
|
|
1334
|
-
* top-left corner. The third and fourth parameters are the coordinates of its
|
|
1335
|
-
* bottom-right corner.
|
|
1336
|
-
*
|
|
1337
|
-
* `imageMode(CENTER)` uses the first two parameters of
|
|
1338
|
-
* <a href="#/p5/image">image()</a> as the x- and y-coordinates of the image's
|
|
1339
|
-
* center. The next parameters are its width and height.
|
|
1340
|
-
*
|
|
1341
|
-
* @method imageMode
|
|
1342
|
-
* @param {(CORNER|CORNERS|CENTER)} mode either CORNER, CORNERS, or CENTER.
|
|
1343
|
-
*
|
|
1344
|
-
* @example
|
|
1345
|
-
*
|
|
1346
|
-
* <div>
|
|
1347
|
-
* <code>
|
|
1348
|
-
* let img;
|
|
1349
|
-
*
|
|
1350
|
-
* async function setup() {
|
|
1351
|
-
* // Load the image.
|
|
1352
|
-
* img = await loadImage('assets/bricks.jpg');
|
|
1353
|
-
*
|
|
1354
|
-
* createCanvas(100, 100);
|
|
1355
|
-
*
|
|
1356
|
-
* background(200);
|
|
1357
|
-
*
|
|
1358
|
-
* // Use CORNER mode.
|
|
1359
|
-
* imageMode(CORNER);
|
|
1360
|
-
*
|
|
1361
|
-
* // Display the image.
|
|
1362
|
-
* image(img, 10, 10, 50, 50);
|
|
1363
|
-
*
|
|
1364
|
-
* describe('A square image of a brick wall is drawn at the top left of a gray square.');
|
|
1365
|
-
* }
|
|
1366
|
-
* </code>
|
|
1367
|
-
* </div>
|
|
1368
|
-
*
|
|
1369
|
-
* <div>
|
|
1370
|
-
* <code>
|
|
1371
|
-
* let img;
|
|
1372
|
-
*
|
|
1373
|
-
* async function setup() {
|
|
1374
|
-
* // Load the image.
|
|
1375
|
-
* img = await loadImage('assets/bricks.jpg');
|
|
1376
|
-
*
|
|
1377
|
-
* createCanvas(100, 100);
|
|
1378
|
-
*
|
|
1379
|
-
* background(200);
|
|
1380
|
-
*
|
|
1381
|
-
* // Use CORNERS mode.
|
|
1382
|
-
* imageMode(CORNERS);
|
|
1383
|
-
*
|
|
1384
|
-
* // Display the image.
|
|
1385
|
-
* image(img, 10, 10, 90, 40);
|
|
1386
|
-
*
|
|
1387
|
-
* describe('An image of a brick wall is drawn on a gray square. The image is squeezed into a small rectangular area.');
|
|
1388
|
-
* }
|
|
1389
|
-
* </code>
|
|
1390
|
-
* </div>
|
|
1391
|
-
*
|
|
1392
|
-
* <div>
|
|
1393
|
-
* <code>
|
|
1394
|
-
* let img;
|
|
1395
|
-
*
|
|
1396
|
-
* async function setup() {
|
|
1397
|
-
* // Load the image.
|
|
1398
|
-
* img = await loadImage('assets/bricks.jpg');
|
|
1399
|
-
*
|
|
1400
|
-
* createCanvas(100, 100);
|
|
1401
|
-
*
|
|
1402
|
-
* background(200);
|
|
1403
|
-
*
|
|
1404
|
-
* // Use CENTER mode.
|
|
1405
|
-
* imageMode(CENTER);
|
|
1406
|
-
*
|
|
1407
|
-
* // Display the image.
|
|
1408
|
-
* image(img, 50, 50, 80, 80);
|
|
1409
|
-
*
|
|
1410
|
-
* describe('A square image of a brick wall is drawn on a gray square.');
|
|
1411
|
-
* }
|
|
1412
|
-
* </code>
|
|
1413
|
-
* </div>
|
|
1414
|
-
*/
|
|
1415
|
-
fn.imageMode = function(m) {
|
|
1416
|
-
// p5._validateParameters('imageMode', arguments);
|
|
1417
|
-
if (
|
|
1418
|
-
m === constants.CORNER ||
|
|
1419
|
-
m === constants.CORNERS ||
|
|
1420
|
-
m === constants.CENTER
|
|
1421
|
-
) {
|
|
1422
|
-
this._renderer.states.setValue('imageMode', m);
|
|
1423
|
-
}
|
|
1424
|
-
};
|
|
1425
|
-
}
|
|
1426
|
-
|
|
1427
|
-
export default loadingDisplaying;
|
|
1428
|
-
|
|
1429
|
-
if(typeof p5 !== 'undefined'){
|
|
1430
|
-
loadingDisplaying(p5, p5.prototype);
|
|
1431
|
-
}
|