├── .gitignore ├── README.md ├── Rakefile ├── pixel.js └── pixel.min.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | docs 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pixel 2 | 3 | A pixel drawing library. 4 | 5 | See a demo app using this library at [http://drawbang.com](http://drawbang.com) and its source code at [http://github.com/potomak/drawbang](http://github.com/potomak/drawbang) 6 | 7 | ## Documentation 8 | 9 | Generate documentation using Docco 10 | 11 | rake doc 12 | 13 | ## Compiling 14 | 15 | Compile library using Google Closure Compiler REST API 16 | 17 | rake compile 18 | 19 | More info about Google Closure Compiler REST API at http://code.google.com/closure/compiler/docs/gettingstarted_api.html -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rake' 3 | require 'net/http' 4 | 5 | desc "Default task is :compile" 6 | task :default => :compile 7 | 8 | desc "Generate documentation" 9 | task :doc do 10 | library_path = 'pixel.js' 11 | 12 | puts "Generating documentation for #{library_path}" 13 | sh "docco #{library_path}" 14 | end 15 | 16 | desc "Compile library, it depends on :doc task" 17 | task :compile => :doc do 18 | library_path = 'pixel.js' 19 | output_path = "#{library_path.gsub(/\.js$/, '')}.min.js" 20 | 21 | puts "Compiling #{library_path}" 22 | uri = URI('http://closure-compiler.appspot.com/compile') 23 | options = { 24 | 'js_code' => File.open(library_path).read, 25 | 'compilation_level' => 'SIMPLE_OPTIMIZATIONS', 26 | 'output_format' => 'text', 27 | 'output_info' => 'compiled_code' 28 | } 29 | res = Net::HTTP.post_form(uri, options) 30 | 31 | puts "Writing compiled code to #{output_path}" 32 | File.open(output_path, 'w') {|f| f.write(res.body)} 33 | end -------------------------------------------------------------------------------- /pixel.js: -------------------------------------------------------------------------------- 1 | // Pixel is a javascript pixel drawing library. 2 | 3 | var PIXEL = function() { 4 | // Global constants. 5 | var TRANSPARENT = "rgba(0, 0, 0, 0)"; 6 | 7 | // Global variables. 8 | var version = '0.1', 9 | debug = false, 10 | matrix = [], 11 | frames = [], 12 | animation = null, 13 | currentFrame = 0, 14 | /* NOTE: deprecated 15 | onionFrame = null, 16 | */ 17 | mainCanvas = null, 18 | previewCanvases = [], 19 | drawing = false, 20 | action = "pixel", 21 | settings = { 22 | previewCanvas: { 23 | pixelSize: 1, 24 | size: 16, 25 | gridColor: "#eeeeee", 26 | showGrid: false 27 | }, 28 | mainCanvas: { 29 | pixelSize: 20, 30 | size: 320, 31 | gridColor: "#eeeeee", 32 | showGrid: false 33 | } 34 | }, 35 | history = { 36 | action: [], 37 | undo: [], 38 | oldUndo: [], 39 | redo: [] 40 | }; 41 | 42 | // ## Canvas(canvas, settings) 43 | // 44 | // A canvas object. 45 | // 46 | // `canvas` a canvas element.
47 | // `settings` an object with settings to draw this canvas. 48 | function Canvas(canvas, settings) { 49 | // A canvas element. 50 | this.canvas = canvas; 51 | 52 | // Context element of `canvas` 53 | this.ctx = canvas.getContext("2d"); 54 | 55 | // An object with canvas settings. 56 | // 57 | // Example settings: 58 | // 59 | // { 60 | // pixelSize: 1, 61 | // size: 16, 62 | // gridColor: "#eeeeee", 63 | // showGrid: false 64 | // } 65 | this.settings = settings; 66 | 67 | // ## clearCanvas() 68 | // 69 | // Clears canvas. 70 | this.clearCanvas = function() { 71 | this.canvas.width = this.canvas.width; 72 | } 73 | 74 | // ## drawGrid() 75 | // 76 | // Draws canvas grid. 77 | this.drawGrid = function() { 78 | var correction = 0.5; 79 | 80 | if(this.settings.showGrid) { 81 | for (var x = correction+this.settings.pixelSize; x < this.settings.size; x += this.settings.pixelSize) { 82 | this.ctx.moveTo(x, 0); 83 | this.ctx.lineTo(x, this.settings.size); 84 | this.ctx.moveTo(0, x); 85 | this.ctx.lineTo(this.settings.size, x); 86 | } 87 | 88 | this.ctx.strokeStyle = this.settings.gridColor; 89 | this.ctx.stroke(); 90 | } 91 | } 92 | 93 | // ## getDataURL() 94 | // 95 | // Returns canvas data url string as `image/png`. 96 | this.getDataURL = function() { 97 | return this.canvas.toDataURL("image/png"); 98 | } 99 | 100 | // ## draw(m) 101 | // 102 | // Draws canvas using `m` as bitmap data. 103 | this.draw = function(m) { 104 | this.clearCanvas(); 105 | 106 | /* NOTE: deprecated 107 | 108 | if(onionFrame != null && typeof frames[onionFrame] != 'undefined' && frames[onionFrame] != null) { 109 | for(var i = 0; i < frames[onionFrame].length; i++) { 110 | for(var j = 0; j < frames[onionFrame][i].length; j++) { 111 | c = frames[onionFrame][i][j]; 112 | if(c != TRANSPARENT) { 113 | components = c.match(/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/); 114 | c = "rgba(" + 115 | new Number("0x" + components[1]) + ", " + 116 | new Number("0x" + components[2]) + ", " + 117 | new Number("0x" + components[3]) + ", 0.5)"; 118 | } 119 | ctx.fillStyle = c; 120 | ctx.fillRect(i*pixelSize, j*pixelSize, pixelSize, pixelSize); 121 | } 122 | } 123 | } 124 | */ 125 | 126 | for(var i = 0; i < m.length; i++) { 127 | for(var j = 0; j < m[i].length; j++) { 128 | this.ctx.fillStyle = m[i][j]; 129 | this.ctx.fillRect(i*this.settings.pixelSize, j*this.settings.pixelSize, this.settings.pixelSize, this.settings.pixelSize); 130 | } 131 | } 132 | 133 | this.drawGrid(); 134 | } 135 | } 136 | 137 | 138 | // ## init(mainCanvas, previewCanvases, debug) 139 | // 140 | // Initializes Pixel library. 141 | // 142 | // `mainCanvas` is a HTML5 canvas elements.
143 | // `previewCanvases` is an array of HTML5 canvas elements.
144 | // `debug` is a flag to override default debug settings. 145 | var init = function(aMainCanvas, aPreviewCanvases, aDebug) { 146 | mainCanvas = new Canvas(aMainCanvas, settings.mainCanvas); 147 | for(var i = 0; i < aPreviewCanvases.length; i++) { 148 | previewCanvases[i] = new Canvas(aPreviewCanvases[i], settings.previewCanvas); 149 | } 150 | typeof aDebug != 'undefined' ? debug = aDebug : null; 151 | 152 | initMatrix(); 153 | initCanvas(); 154 | } 155 | 156 | // ## initMatrix() 157 | // 158 | // Initializes matrix values to transparent. 159 | var initMatrix = function() { 160 | var length = settings.mainCanvas.size/settings.mainCanvas.pixelSize 161 | matrix = []; 162 | 163 | for(var i = 0; i < length; i++) { 164 | matrix.push(new Array(length)); 165 | } 166 | 167 | for(var i = 0; i < matrix.length; i++) { 168 | for(var j = 0; j < matrix[i].length; j++) { 169 | matrix[i][j] = TRANSPARENT; 170 | } 171 | } 172 | } 173 | 174 | // ## initCanvas() 175 | // 176 | // Initializes canvas and history. 177 | var initCanvas = function() { 178 | history = { 179 | action: [], 180 | undo: [], 181 | oldUndo: [], 182 | redo: [] 183 | }; 184 | 185 | draw(); 186 | } 187 | 188 | // ## log(obj) 189 | // 190 | // Logs `obj` to `console` if `debug` flag is `true`. 191 | var log = function(obj) { 192 | debug && console.log([(new Date()).toString(), obj]); 193 | } 194 | 195 | // ## printMatrix(m) 196 | // 197 | // Returns a formatted string representing `m`, where `m` is a matrix composed by 198 | // an *array of arrays*. 199 | var printMatrix = function(m) { 200 | mString = ""; 201 | 202 | for(var i = 0; i < m.length; i++) { 203 | for(var j = 0; j < m[i].length; j++) { 204 | mString += (TRANSPARENT == m[i][j] ? "0" : "X") + ", "; 205 | } 206 | 207 | mString += "\n"; 208 | } 209 | 210 | return mString; 211 | } 212 | 213 | // ## setDraw(flag) 214 | // 215 | // Sets `drawing` attribute. 216 | var setDraw = function(wantToDraw) { 217 | drawing = wantToDraw; 218 | } 219 | 220 | // ## setAction(action) 221 | // 222 | // Sets `action` attribute. 223 | var setAction = function(wantedAction) { 224 | action = wantedAction; 225 | } 226 | 227 | // ## clearCanvasAt(index) 228 | // 229 | // Clears canvas at `index`. 230 | var clearCanvasAt = function(index) { 231 | previewCanvases[index].clearCanvas(); 232 | 233 | if(currentFrame == index) { 234 | mainCanvas.clearCanvas(); 235 | frames[currentFrame] = null; 236 | initMatrix(); 237 | initCanvas(); 238 | } 239 | } 240 | 241 | // ## clearCanvas() 242 | // 243 | // Clears current frame canvas. 244 | var clearCanvas = function() { 245 | clearCanvasAt(currentFrame); 246 | } 247 | 248 | // ## removeFrame(index) 249 | // 250 | // Removes frame object from `frames` at `index`. 251 | var removeFrame = function(index) { 252 | frames.splice(index, 1); 253 | } 254 | 255 | // ## copyMatrix(m) 256 | // 257 | // Returns an object copied from `m` matrix. 258 | var copyMatrix = function(m) { 259 | if(typeof m != 'undefined') { 260 | var copy = m.slice(); 261 | 262 | for(var i = 0; i < m.length; i++) { 263 | copy[i] = m[i].slice(); 264 | } 265 | 266 | return copy; 267 | } 268 | } 269 | 270 | // ## doAction(x, y, color) 271 | // 272 | // Executes `action` at (`x`, `y`) with `color`. 273 | var doAction = function(x, y, color) { 274 | if(drawing) { 275 | var coords = { 276 | x: pixelify(x, settings.mainCanvas.pixelSize), 277 | y: pixelify(y, settings.mainCanvas.pixelSize) 278 | } 279 | 280 | switch(action) { 281 | case "pixel": 282 | var startColor = drawPixel(coords.x, coords.y, color); 283 | 284 | if(startColor != false) { 285 | history.undo.push(function() { 286 | drawPixel(coords.x, coords.y, startColor); 287 | }); 288 | 289 | history.action.push(function() { 290 | drawPixel(coords.x, coords.y, color); 291 | }); 292 | } 293 | 294 | break; 295 | 296 | case "clearPixel": 297 | var startColor = drawPixel(coords.x, coords.y, TRANSPARENT); 298 | 299 | if(startColor != false) { 300 | history.undo.push(function() { 301 | drawPixel(coords.x, coords.y, startColor); 302 | }); 303 | 304 | history.action.push(function() { 305 | drawPixel(coords.x, coords.y, TRANSPARENT); 306 | }); 307 | } 308 | 309 | break; 310 | 311 | case "fill": 312 | var startMatrix = fillPixels(coords.x, coords.y, color); 313 | 314 | if(startColor != false) { 315 | history.undo.push(function() { 316 | draw(startMatrix); 317 | }); 318 | 319 | history.action.push(function() { 320 | fillPixels(coords.x, coords.y, color) 321 | }); 322 | } 323 | 324 | break; 325 | 326 | default: 327 | log("unknown action:" + action); 328 | } 329 | } 330 | } 331 | 332 | // ## pixelify(val) 333 | // 334 | // Returns quantized value of `val` by `pixelSize`. 335 | // 336 | // `val` a number.
337 | // `pixelSize` a number representing *pixel size in pixels* 338 | var pixelify = function(val, pixelSize) { 339 | var i = Math.floor(val/pixelSize); 340 | 341 | i >= matrix.length && (i = matrix.length-1); 342 | i <= 0 && (i = 0); 343 | 344 | return i; 345 | } 346 | 347 | // ## drawPixel(x, y, color) 348 | // 349 | // Draws pixel at (`x`, `y`) of `color`. 350 | var drawPixel = function(x, y, color) { 351 | var startColor = matrix[x][y]; 352 | 353 | if(startColor != color) { 354 | matrix[x][y] = color; 355 | draw(); 356 | 357 | return startColor; 358 | } 359 | 360 | return false; 361 | } 362 | 363 | // ## getColorAt(x, y) 364 | // 365 | // Returns color string at (`x`, `y`). 366 | // 367 | // Color string format: 368 | // 369 | // /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/ 370 | var getColorAt = function(x, y) { 371 | return TRANSPARENT == matrix[x][y] ? "#ffffff" : matrix[x][y]; 372 | } 373 | 374 | // ## fillPixels(x, y, color) 375 | // 376 | // Fills pixels. 377 | var fillPixels = function(x, y, color) { 378 | var startColor = getColorAt(x, y); 379 | 380 | if(startColor != color) { 381 | var startMatrix = copyMatrix(matrix), 382 | start = (new Date()).getTime(); 383 | 384 | fillPixel(x, y, startColor, color); 385 | log("flood fill time: " + ((new Date()).getTime()-start)); 386 | 387 | draw(); 388 | 389 | return startMatrix; 390 | } 391 | 392 | return false; 393 | } 394 | 395 | // ## fillPixel(x, y, startColor, endColor) 396 | // 397 | // Recursive part of `fillPixels` function. 398 | // 399 | // `x`
400 | // `y`
401 | // `startColor` a hex representation of starting color.
402 | // `endColor` a hex representation of target color. 403 | var fillPixel = function(x, y, startColor, endColor) { 404 | if(x >= 0 && x < matrix[0].length && y >= 0 && y < matrix.length) { 405 | if(getColorAt(x, y) == startColor) { 406 | matrix[x][y] = endColor; 407 | 408 | fillPixel(x+1, y, startColor, endColor); 409 | fillPixel(x-1, y, startColor, endColor); 410 | fillPixel(x, y+1, startColor, endColor); 411 | fillPixel(x, y-1, startColor, endColor); 412 | } 413 | } 414 | } 415 | 416 | // ## draw(m) 417 | // 418 | // Draws main canvas and preview canvas at `currentFrame` using `m` as matrix or 419 | // global `matrix` if `m` is `undefined`. 420 | var draw = function(m) { 421 | typeof m == 'undefined' ? m = matrix : matrix = copyMatrix(m); 422 | 423 | mainCanvas.clearCanvas(); 424 | previewCanvases[currentFrame].clearCanvas(); 425 | 426 | /* NOTE: deprecated 427 | 428 | if(onionFrame != null && typeof frames[onionFrame] != 'undefined' && frames[onionFrame] != null) { 429 | for(var i = 0; i < frames[onionFrame].length; i++) { 430 | for(var j = 0; j < frames[onionFrame][i].length; j++) { 431 | c = frames[onionFrame][i][j]; 432 | if(c != TRANSPARENT) { 433 | components = c.match(/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/); 434 | c = "rgba(" + 435 | new Number("0x" + components[1]) + ", " + 436 | new Number("0x" + components[2]) + ", " + 437 | new Number("0x" + components[3]) + ", 0.5)"; 438 | } 439 | ctx.fillStyle = c; 440 | ctx.fillRect(i*pixelSize, j*pixelSize, pixelSize, pixelSize); 441 | } 442 | } 443 | } 444 | */ 445 | 446 | mainCanvas.draw(m); 447 | previewCanvases[currentFrame].draw(m); 448 | } 449 | 450 | // ## getHistory() 451 | // 452 | // Returns history object. 453 | var getHistory = function() { 454 | return history; 455 | } 456 | 457 | // ## undo() 458 | // 459 | // Undoes latest action. 460 | var undo = function() { 461 | if(history.undo.length > 0) { 462 | var todo = history.undo.pop(); 463 | todo.call(); 464 | history.redo.push(history.action.pop()); 465 | history.oldUndo.push(todo); 466 | } 467 | } 468 | 469 | // ## redo() 470 | // 471 | // Viceversa of `undo()`. 472 | // 473 | // **Deprecation warning** 474 | var redo = function() { 475 | if(history.redo.length > 0) { 476 | var todo = history.redo.pop(); 477 | todo.call(); 478 | history.undo.push(history.oldUndo.pop()); 479 | history.action.push(todo); 480 | } 481 | } 482 | 483 | // ## getFrame(frame) 484 | // 485 | // Returns data matrix for frame at index `frame`. 486 | var getFrame = function(frame) { 487 | return currentFrame == frame ? matrix : frames[frame]; 488 | } 489 | 490 | // ## getCurrentFrame() 491 | // 492 | // Returns current frame data matrix. 493 | var getCurrentFrame = function() { 494 | return matrix; 495 | } 496 | 497 | // ## getCurrentFrameId() 498 | // 499 | // Returns current frame id. 500 | var getCurrentFrameId = function() { 501 | return currentFrame; 502 | } 503 | 504 | // ## setCurrentFrame(frame) 505 | // 506 | // Sets current frame and matrix to object at index `frame`. 507 | var setCurrentFrame = function(frame) { 508 | log("setCurrentFrame: " + frame); 509 | 510 | if(frame != currentFrame) { 511 | frames[currentFrame] = copyMatrix(matrix); 512 | matrix = copyMatrix(frames[frame]); 513 | 514 | // a new frame 515 | if(typeof matrix == 'undefined') { 516 | // set current frame placeholder 517 | frames[frame] = null; 518 | // initialize matrix 519 | initMatrix(); 520 | } 521 | 522 | currentFrame = frame; 523 | 524 | initCanvas(); 525 | 526 | log("setCurrentFrame - frames.length: " + frames.length); 527 | } 528 | } 529 | 530 | // ## setOnionFrame(frame) 531 | // 532 | // Sets `frame` as onion frame and draws canvas. 533 | // 534 | // **Deprecation warning** 535 | var setOnionFrame = function(frame) { 536 | onionFrame = frame; 537 | draw(); 538 | } 539 | 540 | // ## getCurrentOnionFrameId() 541 | // 542 | // Returns current onion frame index. 543 | // 544 | // **Deprecation warning** 545 | var getCurrentOnionFrameId = function() { 546 | return onionFrame; 547 | } 548 | 549 | // ## play(fps, callback) 550 | // 551 | // Plays animation at `fps` frames per second. 552 | // 553 | // At every frame redraw `callback` is called. 554 | var play = function(fps, callback) { 555 | if(frames.length > 1) { 556 | animation = setInterval(function() { 557 | activeFrame = (currentFrame+1)%frames.length; 558 | log([ 559 | "play animation", 560 | "activeFrame: " + activeFrame, 561 | "currentFrame: " + currentFrame, 562 | "frames.length: " + frames.length 563 | ]); 564 | setCurrentFrame(activeFrame); 565 | callback(activeFrame); 566 | }, (1/fps)*1000); 567 | } 568 | } 569 | 570 | // ## stop() 571 | // 572 | // Stops animation. 573 | var stop = function() { 574 | clearInterval(animation); 575 | animation = null; 576 | } 577 | 578 | // ## moveTop() 579 | // 580 | // Moves canvas top by one pixel. 581 | var moveTop = function() { 582 | var startMatrix = copyMatrix(matrix), 583 | start = (new Date()).getTime(); 584 | 585 | // For each column of pixels 586 | for(var i = 0; i < matrix.length; i++) { 587 | // push at beginning of column latest array element. 588 | matrix[i].push(matrix[i].shift()); 589 | } 590 | 591 | log("move top time: " + ((new Date()).getTime()-start)); 592 | 593 | draw(); 594 | 595 | history.undo.push(function() { 596 | draw(startMatrix); 597 | }); 598 | } 599 | 600 | // ## moveRight() 601 | // 602 | // Moves canvas right by one pixel. 603 | var moveRight = function() { 604 | var startMatrix = copyMatrix(matrix), 605 | start = (new Date()).getTime(); 606 | 607 | // For each row of pixels: 608 | for(j = 0; j < matrix[0].length; j++) { 609 | // save latest row pixel to `temp` buffer, 610 | var temp = matrix[matrix.length-1][j]; 611 | 612 | // shift elements by row, 613 | for(i = matrix.length - 1; i > 0; i--) { 614 | matrix[i][j] = matrix[i-1][j]; 615 | } 616 | 617 | // set first row element as `temp`. 618 | matrix[0][j] = temp; 619 | } 620 | 621 | log("move right time: " + ((new Date()).getTime()-start)); 622 | 623 | draw(); 624 | 625 | history.undo.push(function() { 626 | draw(startMatrix); 627 | }); 628 | } 629 | 630 | // ## flipVertical() 631 | // 632 | // Flips canvas vertically. 633 | var flipVertical = function() { 634 | var startMatrix = copyMatrix(matrix), 635 | start = (new Date()).getTime(); 636 | 637 | // For each column of pixels, 638 | for(var i = 0; i < matrix.length; i++) { 639 | // for half of each row of pixels, 640 | for(var j = 0; j < matrix[i].length/2; j++) { 641 | var temp = matrix[i][j], 642 | length = matrix[i].length; 643 | 644 | // swap first half column with second half. 645 | matrix[i][j] = matrix[i][length-1-j]; 646 | matrix[i][length-1-j] = temp; 647 | } 648 | } 649 | 650 | log("flip vertical time: " + ((new Date()).getTime()-start)); 651 | 652 | draw(); 653 | 654 | history.undo.push(function() { 655 | draw(startMatrix); 656 | }); 657 | } 658 | 659 | // ## flipHorizontal() 660 | // 661 | // Flips canvas horizontally. 662 | var flipHorizontal = function() { 663 | var startMatrix = copyMatrix(matrix), 664 | start = (new Date()).getTime(); 665 | 666 | // For half of each column of pixels, 667 | for(var i = 0; i < matrix.length/2; i++) { 668 | // for each row of pixels, 669 | for(var j = 0; j < matrix[i].length; j++) { 670 | var temp = matrix[i][j], 671 | length = matrix.length; 672 | 673 | // swap first half row with second half. 674 | matrix[i][j] = matrix[length-1-i][j]; 675 | matrix[length-1-i][j] = temp; 676 | } 677 | } 678 | 679 | log("flip vertical time: " + ((new Date()).getTime()-start)); 680 | 681 | draw(); 682 | 683 | history.undo.push(function() { 684 | draw(startMatrix); 685 | }); 686 | } 687 | 688 | // ## rotate() 689 | // 690 | // Rotates canvas left by 90 degrees. 691 | var rotate = function() { 692 | var startMatrix = copyMatrix(matrix), 693 | start = (new Date()).getTime(); 694 | 695 | // For each column of pixels, 696 | for(var i = 0; i < matrix.length; i++) { 697 | // for each row of pixels, 698 | for(var j = 0; j < matrix[i].length; j++) { 699 | // swap each element to swap row with column. 700 | matrix[i][j] = startMatrix[matrix[i].length-1 - j][i]; 701 | } 702 | } 703 | 704 | log("rotate time: " + ((new Date()).getTime()-start)); 705 | 706 | draw(); 707 | 708 | history.undo.push(function() { 709 | draw(startMatrix); 710 | }); 711 | } 712 | 713 | // ## copyFrameAt(index) 714 | // 715 | // Copies frame at `index` to current frame. 716 | // 717 | // `index` an integer representing an index of `frames` array. 718 | var copyFrameAt = function(index) { 719 | var startMatrix = copyMatrix(matrix), 720 | start = (new Date()).getTime(); 721 | 722 | matrix = copyMatrix(getFrame(index)); 723 | 724 | log("copyFrameAt " + index + ": " + ((new Date()).getTime()-start)); 725 | 726 | draw(); 727 | 728 | history.undo.push(function() { 729 | draw(startMatrix); 730 | }); 731 | } 732 | 733 | return { 734 | init: init, 735 | clearCanvasAt: clearCanvasAt, 736 | clearCanvas: clearCanvas, 737 | removeFrame: removeFrame, 738 | setDraw: setDraw, 739 | setAction: setAction, 740 | doAction: doAction, 741 | getHistory: getHistory, 742 | undo: undo, 743 | /* NOTE: deprecated 744 | redo: redo, 745 | */ 746 | getFrame: getFrame, 747 | setCurrentFrame: setCurrentFrame, 748 | /* NOTE: deprecated 749 | setOnionFrame: setOnionFrame, 750 | getCurrentOnionFrameId: getCurrentOnionFrameId, 751 | */ 752 | getCurrentFrame: getCurrentFrame, 753 | getCurrentFrameId: getCurrentFrameId, 754 | play: play, 755 | stop: stop, 756 | moveRight: moveRight, 757 | moveTop: moveTop, 758 | flipHorizontal: flipHorizontal, 759 | flipVertical: flipVertical, 760 | rotate: rotate, 761 | copyFrameAt: copyFrameAt, 762 | log: log 763 | }; 764 | }(); -------------------------------------------------------------------------------- /pixel.min.js: -------------------------------------------------------------------------------- 1 | var PIXEL=function(){function x(a,b){this.canvas=a;this.ctx=a.getContext("2d");this.settings=b;this.clearCanvas=function(){this.canvas.width=this.canvas.width};this.drawGrid=function(){if(this.settings.showGrid){for(var a=0.5+this.settings.pixelSize;a=b.length&&(c=b.length-1);0>=c&&(c=0);return c}, 4 | o=function(a,d,c){var e=b[a][d];return e!=c?(b[a][d]=c,h(),e):!1},C=function(a,d){return"rgba(0, 0, 0, 0)"==b[a][d]?"#ffffff":b[a][d]},D=function(a,d,c){var e=C(a,d);if(e!=c){var g=l(b),k=(new Date).getTime();q(a,d,e,c);m("flood fill time: "+((new Date).getTime()-k));h();return g}return!1},q=function(a,d,c,e){0<=a&&a