@@ -951,6 +951,12 @@ class Framebuffer {
951951 this . end ( ) ;
952952 }
953953
954+ /**
955+ * Call this befpre updating <a href="#/p5.Framebuffer/pixels">pixels</a>
956+ * and calling <a href="#/p5.Framebuffer/updatePixels">updatePixels</a>
957+ * to replace the content of the framebuffer with the data in the pixels
958+ * array.
959+ */
954960 loadPixels ( ) {
955961 const gl = this . gl ;
956962 const prevFramebuffer = gl . getParameter ( gl . FRAMEBUFFER_BINDING ) ;
@@ -971,7 +977,9 @@ class Framebuffer {
971977 }
972978
973979 /**
974- * Get a region of pixels, or a single pixel, from the canvas.
980+ * Get a region of pixels from the canvas in the form of a
981+ * <a href="#/p5.Image">p5.Image</a>, or a single pixel as an array of
982+ * numbers.
975983 *
976984 * Returns an array of [R,G,B,A] values for any pixel or grabs a section of
977985 * an image. If the Framebuffer has been set up to not store alpha values, then
@@ -985,8 +993,8 @@ class Framebuffer {
985993 * @method get
986994 * @param {Number } x x-coordinate of the pixel
987995 * @param {Number } y y-coordinate of the pixel
988- * @param {Number } w width
989- * @param {Number } h height
996+ * @param {Number } w width of the section to be returned
997+ * @param {Number } h height of the section to be returned
990998 * @return {p5.Image } the rectangle <a href="#/p5.Image">p5.Image</a>
991999 */
9921000 /**
@@ -1000,15 +1008,20 @@ class Framebuffer {
10001008 * @return {Number[] } color of pixel at x,y in array format [R, G, B, A]
10011009 */
10021010 get ( x , y , w , h ) {
1011+ p5 . _validateParameters ( 'p5.Framebuffer.get' , arguments ) ;
10031012 const colorFormat = this . _glColorFormat ( ) ;
10041013 if ( x === undefined && y === undefined ) {
10051014 x = 0 ;
10061015 y = 0 ;
10071016 w = this . width ;
10081017 h = this . height ;
10091018 } else if ( w === undefined && h === undefined ) {
1010- if ( x < 0 || y < 0 || w >= this . width || h >= this . height ) {
1011- return [ 0 , 0 , 0 , 0 ] ;
1019+ if ( x < 0 || y < 0 || x >= this . width || y >= this . height ) {
1020+ console . warn (
1021+ 'The x and y values passed to p5.Framebuffer.get are outside of its range and will be clamped.'
1022+ ) ;
1023+ x = this . target . constrain ( x , 0 , this . width - 1 ) ;
1024+ y = this . target . constrain ( y , 0 , this . height - 1 ) ;
10121025 }
10131026
10141027 return readPixelWebGL (
@@ -1021,6 +1034,11 @@ class Framebuffer {
10211034 ) ;
10221035 }
10231036
1037+ x = this . target . constrain ( x , 0 , this . width - 1 ) ;
1038+ y = this . target . constrain ( y , 0 , this . height - 1 ) ;
1039+ w = this . target . constrain ( w , 1 , this . width - x ) ;
1040+ h = this . target . constrain ( h , 1 , this . height - y ) ;
1041+
10241042 const rawData = readPixelsWebGL (
10251043 undefined ,
10261044 this . gl ,
@@ -1039,18 +1057,23 @@ class Framebuffer {
10391057 const fullData = new Uint8ClampedArray (
10401058 w * h * this . density * this . density * 4
10411059 ) ;
1060+
1061+ // Default channels that aren't in the framebuffer (e.g. alpha, if the
1062+ // framebuffer is in RGB mode instead of RGBA) to 255
1063+ fullData . fill ( 255 ) ;
1064+
10421065 const channels = colorFormat . type === this . gl . RGB ? 3 : 4 ;
10431066 for ( let y = 0 ; y < h * this . density ; y ++ ) {
10441067 for ( let x = 0 ; x < w * this . density ; x ++ ) {
10451068 for ( let channel = 0 ; channel < 4 ; channel ++ ) {
10461069 const idx = ( y * w * this . density + x ) * 4 + channel ;
1047- if ( channel >= channels ) {
1048- fullData [ idx ] = 255 ;
1049- } else {
1050- const prevIdx = channels === 4
1070+ if ( channel < channels ) {
1071+ // Find the index of this pixel in `rawData`, which might have a
1072+ // different number of channels
1073+ const rawDataIdx = channels === 4
10511074 ? idx
10521075 : ( y * w * this . density + x ) * channels + channel ;
1053- fullData [ idx ] = rawData [ prevIdx ] ;
1076+ fullData [ idx ] = rawData [ rawDataIdx ] ;
10541077 }
10551078 }
10561079 }
@@ -1138,6 +1161,21 @@ class Framebuffer {
11381161 const gl = this . gl ;
11391162 this . colorP5Texture . bindTexture ( ) ;
11401163 const colorFormat = this . _glColorFormat ( ) ;
1164+
1165+ const channels = colorFormat . format === gl . RGBA ? 4 : 3 ;
1166+ const len =
1167+ this . width * this . height * this . density * this . density * channels ;
1168+ const TypedArrayClass = colorFormat . type === gl . UNSIGNED_BYTE
1169+ ? Uint8Array
1170+ : Float32Array ;
1171+ if (
1172+ ! ( this . pixels instanceof TypedArrayClass ) || this . pixels . length !== len
1173+ ) {
1174+ throw new Error (
1175+ 'The pixels array has not been set correctly. Please call loadPixels() before updatePixels().'
1176+ ) ;
1177+ }
1178+
11411179 gl . texImage2D (
11421180 gl . TEXTURE_2D ,
11431181 0 ,
0 commit comments