pixel access primitives for bitmap and vector graphics

pull/89/head
jmoenig 2019-04-09 10:04:14 +02:00
rodzic ad96635b2e
commit 223e493316
5 zmienionych plików z 104 dodań i 8 usunięć

Wyświetl plik

@ -20,6 +20,7 @@
* new "play sound at sample rate" command
* accept lists and lists of lists as inputs to all sound primitives
* new "play frequency" commands in the Sounds category
* pixel access primitives for bitmap and vector (!) graphics
* added "neg" selector to monadic function reporter in "Operators" category
* added "log2" selector to monadic function reporter in "Operators" category
* added "^" reporter (power of) in the Operators category
@ -61,6 +62,9 @@
* German
* French
### 2019-04-09
* Blocks, Objects, Threads: new "getImageAttribute" reporter primitive
### 2019-04-08
* Blocks, Objects, Threads: new "getSoundAttribute" reporter primitive
* Blocks, Objects, Threads: new "play sound at sample rate" command primitive

Wyświetl plik

@ -6,9 +6,9 @@
<link rel="shortcut icon" href="src/favicon.ico">
<script type="text/javascript" src="src/morphic.js?version=2019-02-07"></script>
<script type="text/javascript" src="src/widgets.js?version=2019-04-05"></script>
<script type="text/javascript" src="src/blocks.js?version=2019-04-08"></script>
<script type="text/javascript" src="src/threads.js?version=2019-04-08"></script>
<script type="text/javascript" src="src/objects.js?version=2019-04-08"></script>
<script type="text/javascript" src="src/blocks.js?version=2019-04-09"></script>
<script type="text/javascript" src="src/threads.js?version=2019-04-09"></script>
<script type="text/javascript" src="src/objects.js?version=2019-04-09"></script>
<script type="text/javascript" src="src/gui.js?version=2019-03-25"></script>
<script type="text/javascript" src="src/paint.js?version=2019-02-22"></script>
<script type="text/javascript" src="src/lists.js?version=2019-02-07"></script>

Wyświetl plik

@ -148,7 +148,7 @@ CustomCommandBlockMorph, SymbolMorph, ToggleButtonMorph, DialMorph*/
// Global stuff ////////////////////////////////////////////////////////
modules.blocks = '2019-April-08';
modules.blocks = '2019-April-09';
var SyntaxElementMorph;
var BlockMorph;
@ -1010,6 +1010,19 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
true // read-only
);
break;
case '%img': // image attributes
part = new InputSlotMorph(
null, // text
false, // numeric?
{
'name' : ['name'],
'width' : ['width'],
'height' : ['height'],
'pixels' : ['pixels']
},
true // read-only
);
break;
case '%rate':
part = new InputSlotMorph(
null,

Wyświetl plik

@ -84,7 +84,7 @@ BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, localize,
TableMorph, TableFrameMorph, normalizeCanvas, BooleanSlotMorph, HandleMorph,
AlignmentMorph, Process, XML_Element, VectorPaintEditorMorph*/
modules.objects = '2019-April-08';
modules.objects = '2019-April-09';
var SpriteMorph;
var StageMorph;
@ -304,6 +304,12 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'looks',
spec: 'costume #'
},
reportGetImageAttribute: {
type: 'reporter',
category: 'looks',
spec: '%img of costume %cst',
defaults: [['width']]
},
doSayFor: {
only: SpriteMorph,
type: 'command',
@ -1994,6 +2000,8 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push(watcherToggle('getCostumeIdx'));
blocks.push(block('getCostumeIdx', this.inheritsAttribute('costume #')));
blocks.push('-');
blocks.push(block('reportGetImageAttribute'));
blocks.push('-');
blocks.push(block('doSayFor'));
blocks.push(block('bubble'));
blocks.push(block('doThinkFor'));
@ -7492,6 +7500,8 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push(watcherToggle('getCostumeIdx'));
blocks.push(block('getCostumeIdx'));
blocks.push('-');
blocks.push(block('reportGetImageAttribute'));
blocks.push('-');
blocks.push(block('changeEffect'));
blocks.push(block('setEffect'));
blocks.push(block('clearEffects'));
@ -8893,6 +8903,32 @@ Costume.prototype.thumbnail = function (extentPoint) {
return trg;
};
// Costume pixel access
Costume.prototype.rasterized = function () {
return this;
};
Costume.prototype.pixels = function () {
var i,
pixels = [],
src = this.contents.getContext('2d').getImageData(
0,
0,
this.contents.width,
this.contents.height
);
for (i = 0; i < src.data.length; i += 4) {
pixels.push(new List([
src.data[i],
src.data[i + 1],
src.data[i + 2],
src.data[i + 3]
]));
}
return new List(pixels);
};
// Costume catching "tainted" canvases
Costume.prototype.isTainted = function () {
@ -9022,6 +9058,22 @@ SVG_Costume.prototype.edit = function (
);
};
// SVG_Costume pixel access
SVG_Costume.prototype.rasterized = function () {
var canvas = newCanvas(this.extent(), true),
ctx = canvas.getContext('2d'),
rasterized;
ctx.drawImage(this.contents, 0, 0);
rasterized = new Costume(
canvas,
this.name,
this.rotationCenter.copy()
);
return rasterized;
};
// CostumeEditorMorph ////////////////////////////////////////////////////////
// CostumeEditorMorph inherits from Morph:

Wyświetl plik

@ -62,7 +62,7 @@ StageMorph, SpriteMorph, StagePrompterMorph, Note, modules, isString, copy,
isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph, Color,
TableFrameMorph, ColorSlotMorph, isSnapObject, Map*/
modules.threads = '2019-April-08';
modules.threads = '2019-April-09';
var ThreadManager;
var Process;
@ -2291,7 +2291,7 @@ Process.prototype.doPlaySoundAtRate = function (name, rate) {
}
source.pause = source.stop;
source.ended = false;
source.onended = function () {this.ended = true; }
source.onended = function () {this.ended = true; };
source.start();
rcvr.parentThatIsA(StageMorph).activeSounds.push(source);
return source;
@ -2420,7 +2420,7 @@ Process.prototype.encodeSound = function (samples, rate) {
}
source = ctx.createBufferSource();
source.buffer = arrayBuffer;
source.audioBuffer = source.buffer; // +++
source.audioBuffer = source.buffer;
return source;
};
@ -4335,6 +4335,33 @@ Process.prototype.doSetInstrument = function (num) {
}
};
// Process image processing primitives
Process.prototype.reportGetImageAttribute = function (choice, name) {
var cst = name instanceof Costume ? name
: (typeof name === 'number' ?
this.blockReceiver().costumes.at(name)
: detect(
this.blockReceiver().costumes.asArray(),
function (c) {return c.name === name.toString(); }
)
),
option = this.inputOption(choice);
switch (option) {
case 'name':
return cst.name;
case 'width':
return cst.width();
case 'height':
return cst.height();
case 'pixels':
return cst.rasterized().pixels();
default:
return 0;
}
};
// Process constant input options
Process.prototype.inputOption = function (dta) {