├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── assets └── global │ ├── Fighter_attack.gif │ ├── FromBitmap.png │ ├── framechanges-2.gif │ ├── galapagosColor.bmp │ ├── galapagosColor.gif │ ├── galapagosColor.png │ ├── galapagosColor_smaller.png │ ├── galapagosGrey8bpp.png │ ├── pirate_small.png │ ├── transparent.gif │ └── xing_b24.bmp ├── flambe.yaml ├── flambe_AbstractPixels.hxproj ├── flash_build.hxml ├── format_build.hxml ├── icons ├── 114x114.png ├── 128x128.png ├── 144x144.png ├── 57x57.png ├── 72x72.png └── README.md ├── java_AbstractPixels.hxproj ├── java_build.hxml ├── luxe_AbstractPixels.hxproj ├── nme_AbstractPixels.hxproj ├── nme_project.nmml ├── openfl_AbstractPixels.hxproj ├── openfl_project.xml ├── project.flow ├── src ├── AbstractPixelsDemo.hx ├── Bresenham.hx ├── FlambeAbstractPixelsDemo.hx ├── FormatAbstractPixelsDemo.hx ├── ImageLoader.hx ├── JavaAbstractPixelsDemo.hx ├── LuxeAbstractPixelsDemo.hx └── hxPixels │ └── Pixels.hx └── web ├── README.md ├── favicon.ico └── index.html /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [azrafe7] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build and Release Folders 2 | bin/ 3 | bin-debug/ 4 | bin-release/ 5 | build/ 6 | 7 | # Other files and folders 8 | .settings/ 9 | .*.swp 10 | ._* 11 | .DS_Store 12 | RECOVER_* 13 | 14 | # Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties` 15 | # should NOT be excluded as they contain compiler settings and other important 16 | # information for Eclipse / Flash Builder. 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Giuseppe Di Mauro (azrafe7) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | hxPixels 2 | ======== 3 | 4 | An _hxperimental_ (cross-target/cross-lib) Pixels abstract, in Haxe 3.1+ 5 | 6 | 7 | Simple interface to access/manipulate pixel values among different targets and libs, having a way to get/set individual pixels (or bytes), and be able to apply them back to the original source (taking care of framework-specific details under the hood). 8 | 9 | ### API 10 | 11 | For the supported libs the following methods are implemented (**note** that all color values are in ARGB format): 12 | 13 | - `get`/`setPixel32()`: get/set pixel value (with alpha) at `x`,`y` 14 | - `get`/`setPixel()`: get/set pixel value (without alpha) at `x`,`y` 15 | - `get`/`setByte()`: get/set byte value at `i` 16 | - `clone()`: make a duplicate of the `Pixels` instance 17 | - `fillRect()`: fill a rect area with pixel value (with alpha) 18 | - `convertTo()`: convert to specified `PixelFormat` 19 | - `bytes`: access to the _raw_ underlying bytes (in source-specific format) 20 | - `format`: change internal color mapping 21 | 22 | ### Supported classes/libs 23 | 24 | - `BitmapData`: flash, openfl and nme (and flambe flash) 25 | - `Texture`: flambe (`applyToFlambeTexture()` only for html - not flash -, due to limitations imposed by Stage3d) 26 | - `BufferedImage`: java 27 | - `ImageData`: plain js 28 | - `Texture` and `AssetImage`: snow/luxe 29 | - png, bmp and gif: format lib 30 | 31 | ### Implementation 32 | 33 | `Pixels` is an abstract over `haxe.io.Bytes`. It stores the raw bytes in the underlying `bytes` var (in source-specific color format), providing an ARGB API over them. 34 | 35 | ### Usage 36 | See the [src folder](https://github.com/azrafe7/hxPixels/tree/master/src) for examples on how to use it, and please file an issue if you have problems or spot a bug. 37 | 38 | ### Warning 39 | The code abstracting bitmaps from the above-mentioned libs is strictly coupled with their internal implementations, so it can easily break when they update. 40 | It may still be useful to work with pixels on a _generic_ bitmap structure. 41 | 42 | ### License (MIT) 43 | See LICENSE file. -------------------------------------------------------------------------------- /assets/global/Fighter_attack.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azrafe7/hxPixels/d5292060d2a31f34b21d3c5089b9c1502d741b3e/assets/global/Fighter_attack.gif -------------------------------------------------------------------------------- /assets/global/FromBitmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azrafe7/hxPixels/d5292060d2a31f34b21d3c5089b9c1502d741b3e/assets/global/FromBitmap.png -------------------------------------------------------------------------------- /assets/global/framechanges-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azrafe7/hxPixels/d5292060d2a31f34b21d3c5089b9c1502d741b3e/assets/global/framechanges-2.gif -------------------------------------------------------------------------------- /assets/global/galapagosColor.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azrafe7/hxPixels/d5292060d2a31f34b21d3c5089b9c1502d741b3e/assets/global/galapagosColor.bmp -------------------------------------------------------------------------------- /assets/global/galapagosColor.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azrafe7/hxPixels/d5292060d2a31f34b21d3c5089b9c1502d741b3e/assets/global/galapagosColor.gif -------------------------------------------------------------------------------- /assets/global/galapagosColor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azrafe7/hxPixels/d5292060d2a31f34b21d3c5089b9c1502d741b3e/assets/global/galapagosColor.png -------------------------------------------------------------------------------- /assets/global/galapagosColor_smaller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azrafe7/hxPixels/d5292060d2a31f34b21d3c5089b9c1502d741b3e/assets/global/galapagosColor_smaller.png -------------------------------------------------------------------------------- /assets/global/galapagosGrey8bpp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azrafe7/hxPixels/d5292060d2a31f34b21d3c5089b9c1502d741b3e/assets/global/galapagosGrey8bpp.png -------------------------------------------------------------------------------- /assets/global/pirate_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azrafe7/hxPixels/d5292060d2a31f34b21d3c5089b9c1502d741b3e/assets/global/pirate_small.png -------------------------------------------------------------------------------- /assets/global/transparent.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azrafe7/hxPixels/d5292060d2a31f34b21d3c5089b9c1502d741b3e/assets/global/transparent.gif -------------------------------------------------------------------------------- /assets/global/xing_b24.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azrafe7/hxPixels/d5292060d2a31f34b21d3c5089b9c1502d741b3e/assets/global/xing_b24.bmp -------------------------------------------------------------------------------- /flambe.yaml: -------------------------------------------------------------------------------- 1 | # Basic information about the game. 2 | name: FlambeAbstractPixelsDemo 3 | description: A short one-line description. 4 | 5 | # Information about the game developer. 6 | developer: 7 | name: Your Company Name 8 | url: http://yourwebsite.com 9 | 10 | # The game's unique identifier. 11 | id: com.yourdomain.yourgame 12 | 13 | # The game's version string. 14 | version: 1.0.0 15 | 16 | # The main class name. 17 | main: FlambeAbstractPixelsDemo 18 | 19 | # The platform to use when invoking `run` or `build` with no arguments. 20 | default_platform: flash 21 | 22 | # The initial orientation and fullscreen state on mobile devices. 23 | orientation: portrait 24 | fullscreen: true 25 | 26 | # Additional flags to pass to the Haxe compiler. 27 | # haxe_flags: -lib nape -D foobar 28 | 29 | # Additional paths to include in the build. 30 | # extra_paths: 31 | # assets: dir1 dir2 32 | # libs: dir1 dir2 33 | # src: ../dir1 ../dir2 34 | # web: ["dir1 with spaces", "dir2"] 35 | 36 | # Android-specific configuration. 37 | android: 38 | # https://developer.android.com/guide/topics/manifest/manifest-intro.html 39 | AndroidManifest.xml: | 40 | 41 | 42 | 43 | # The signing password for certs/android.p12 44 | password: password 45 | 46 | # iOS-specific configuration. 47 | ios: 48 | # http://developer.apple.com/library/ios/#documentation/general/Reference/InfoPlistKeyReference 49 | Info.plist: | 50 | UIDeviceFamily 51 | 52 | 1 53 | 2 54 | 55 | UIPrerenderedIcon 56 | 57 | # The signing password for certs/ios-development.p12 58 | password: password 59 | 60 | # Firefox App-specific configuration. 61 | firefox: 62 | # https://developer.mozilla.org/en-US/docs/Web/Apps/Manifest 63 | manifest.webapp: 64 | { 65 | default_locale: "en", 66 | # type: "privileged", 67 | # permissions: [...] 68 | } 69 | -------------------------------------------------------------------------------- /flambe_AbstractPixels.hxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | cmd /c flambe build $(TargetBuild) --$(BuildConfig) 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /flash_build.hxml: -------------------------------------------------------------------------------- 1 | -cp src/ 2 | -main AbstractPixelsDemo 3 | -swf-version 11.2 4 | -swf-header 1024:780:60:FFFFFF 5 | -swf bin/AbstractPixelsDemo.swf 6 | -D fdb 7 | -debug 8 | #mac 9 | -cmd open bin/AbstractPixelsDemo.swf 10 | #win 11 | #-cmd start bin/AbstractPixelsDemo.swf -------------------------------------------------------------------------------- /format_build.hxml: -------------------------------------------------------------------------------- 1 | -cp src/ 2 | -main FormatAbstractPixelsDemo 3 | -debug 4 | -lib format 5 | -resource assets/global/galapagosColor.png@galapagosColor.png 6 | -resource assets/global/galapagosGrey8bpp.png@galapagosGrey8bpp.png 7 | -resource assets/global/FromBitmap.png@FromBitmap.png 8 | -resource assets/global/pirate_small.png@pirate_small.png 9 | -resource assets/global/galapagosColor.bmp@galapagosColor.bmp 10 | -resource assets/global/xing_b24.bmp@xing_b24.bmp 11 | -resource assets/global/framechanges-2.gif@framechanges-2.gif 12 | -resource assets/global/transparent.gif@transparent.gif 13 | -resource assets/global/Fighter_attack.gif@Fighter_attack.gif 14 | # uncomment the line below if on haxe >= 4 15 | #--run FormatAbstractPixelsDemo 16 | -x FormatAbstractPixelsDemo 17 | 18 | --next 19 | -cp src/ 20 | -main FormatAbstractPixelsDemo 21 | -debug 22 | -lib format 23 | -resource assets/global/galapagosColor.png@galapagosColor.png 24 | -resource assets/global/galapagosGrey8bpp.png@galapagosGrey8bpp.png 25 | -resource assets/global/FromBitmap.png@FromBitmap.png 26 | -resource assets/global/pirate_small.png@pirate_small.png 27 | -resource assets/global/galapagosColor.bmp@galapagosColor.bmp 28 | -resource assets/global/xing_b24.bmp@xing_b24.bmp 29 | -resource assets/global/framechanges-2.gif@framechanges-2.gif 30 | -resource assets/global/transparent.gif@transparent.gif 31 | -resource assets/global/Fighter_attack.gif@Fighter_attack.gif 32 | #-cpp bin/format/cpp 33 | -------------------------------------------------------------------------------- /icons/114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azrafe7/hxPixels/d5292060d2a31f34b21d3c5089b9c1502d741b3e/icons/114x114.png -------------------------------------------------------------------------------- /icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azrafe7/hxPixels/d5292060d2a31f34b21d3c5089b9c1502d741b3e/icons/128x128.png -------------------------------------------------------------------------------- /icons/144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azrafe7/hxPixels/d5292060d2a31f34b21d3c5089b9c1502d741b3e/icons/144x144.png -------------------------------------------------------------------------------- /icons/57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azrafe7/hxPixels/d5292060d2a31f34b21d3c5089b9c1502d741b3e/icons/57x57.png -------------------------------------------------------------------------------- /icons/72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azrafe7/hxPixels/d5292060d2a31f34b21d3c5089b9c1502d741b3e/icons/72x72.png -------------------------------------------------------------------------------- /icons/README.md: -------------------------------------------------------------------------------- 1 | This folder is needed by flambe. -------------------------------------------------------------------------------- /java_AbstractPixels.hxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /java_build.hxml: -------------------------------------------------------------------------------- 1 | -cp src/ 2 | -main JavaAbstractPixelsDemo 3 | -debug 4 | -java bin/java -------------------------------------------------------------------------------- /luxe_AbstractPixels.hxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | "$(CompilerPath)/haxelib" run flow build $(TargetBuild) --$(BuildConfig) 46 | 47 | 48 | 49 | 50 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /nme_AbstractPixels.hxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /nme_project.nmml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /openfl_AbstractPixels.hxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | "$(CompilerPath)/haxelib" run lime build "$(OutputFile)" $(TargetBuild) -$(BuildConfig) -Dfdb 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /openfl_project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /project.flow: -------------------------------------------------------------------------------- 1 | { 2 | 3 | luxe:{ 4 | window: { 5 | width:1024, 6 | height:780, 7 | title:'pixels', 8 | fullscreen:false, 9 | resizable:true, 10 | borderless:false 11 | } 12 | }, 13 | 14 | project : { 15 | name : 'empty', 16 | version : '1.0.0', 17 | author : 'luxeengine', 18 | 19 | app : { 20 | main: 'LuxeAbstractPixelsDemo', 21 | output: 'bin/luxe', 22 | name : 'luxe_empty', 23 | package : 'com.luxeengine.empty' 24 | }, 25 | 26 | build : { 27 | dependencies : { 28 | luxe : '*', 29 | } 30 | }, 31 | 32 | files : { 33 | assets : 'assets/' 34 | } 35 | 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /src/AbstractPixelsDemo.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import flash.display.Stage; 4 | import flash.events.Event; 5 | import flash.events.MouseEvent; 6 | import flash.display.Bitmap; 7 | import flash.display.BitmapData; 8 | import flash.display.Sprite; 9 | import flash.events.KeyboardEvent; 10 | import flash.Lib; 11 | import haxe.Timer; 12 | import hxPixels.Pixels; 13 | 14 | 15 | #if (use_loader) 16 | 17 | @:bitmap("assets/global/galapagosColor.png") 18 | class Asset1 extends flash.display.BitmapData {} 19 | 20 | @:bitmap("assets/global/FromBitmap.png") 21 | class Asset2 extends flash.display.BitmapData {} 22 | 23 | #end 24 | 25 | 26 | class AbstractPixelsDemo extends Sprite { 27 | 28 | var assetFileNames:Array = [ 29 | "assets/global/galapagosColor.png", 30 | "assets/global/FromBitmap.png" 31 | ]; 32 | var assetClassNames:Array = [ 33 | "Asset1", 34 | "Asset2" 35 | ]; 36 | 37 | 38 | public static function main(): Void { 39 | Lib.current.addChild(new AbstractPixelsDemo()); 40 | } 41 | 42 | 43 | function runTests(bmds:Array, titles:Array) { 44 | for (i in 0...bmds.length) { 45 | test(bmds[i], titles[i]); 46 | } 47 | } 48 | 49 | 50 | public function new() { 51 | super(); 52 | 53 | // programmatically generated test BitmapData 54 | var genBmd = new BitmapData(480, 80, true, 0xA0102030); 55 | //genBmd.image.buffer.premultiplied = false; 56 | 57 | var bmds = []; 58 | var titles = [for (assetName in assetFileNames) assetName]; 59 | titles.push("Generated BMD"); 60 | 61 | #if (use_loader && jsprime) 62 | trace(" -- JSPRIME: SKIPPING ImageLoader --"); 63 | #end 64 | 65 | #if (use_loader && !jsprime) 66 | 67 | var loader = new ImageLoader(); 68 | trace(" -- USING ImageLoader --"); 69 | 70 | loader.load(assetClassNames, function(loader) { 71 | for (assetName in assetClassNames) bmds.push(loader.getBitmapData(assetName)); 72 | bmds.push(genBmd); 73 | 74 | runTests(bmds, titles); 75 | }); 76 | 77 | #else 78 | 79 | var emptyLine = ""; 80 | for (i in 0...assetFileNames.length) { 81 | var assetName = assetFileNames[i]; 82 | if (i == assetFileNames.length - 1) emptyLine = "\n"; 83 | trace(assetName + ": " + (openfl.Assets.exists(assetName) ? "ok" : "not exists") + emptyLine); 84 | var bmd = openfl.Assets.getBitmapData(assetName, false); 85 | bmds.push(bmd); 86 | } 87 | bmds.push(genBmd); 88 | 89 | runTests(bmds, titles); 90 | 91 | #end 92 | 93 | // key presses 94 | Lib.current.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown); 95 | } 96 | 97 | public function test(bitmapData:BitmapData, id:String):Void { 98 | 99 | // show the image bmp 100 | var bitmap:Bitmap = new Bitmap(bitmapData); 101 | addChild(bitmap); 102 | bitmap.x = (Lib.current.stage.stageWidth - bitmap.width) / 2; 103 | bitmap.y = (Lib.current.stage.stageHeight - bitmap.height) / 2; 104 | 105 | trace('[ testing $id ]'); 106 | 107 | // load bitmapData into pixels abstract 108 | var startTime = Timer.stamp(); 109 | var pixels:Pixels = bitmapData; 110 | trace('load ${Timer.stamp() - startTime}'); 111 | 112 | // generate random points 113 | var points = []; 114 | var NUM_POINTS = 10000; 115 | for (i in 0...NUM_POINTS) points.push( { x: Std.int(Math.random() * pixels.width), y: Std.int(Math.random() * pixels.height) } ); 116 | 117 | // read random points 118 | startTime = Timer.stamp(); 119 | for (i in 0...NUM_POINTS) { 120 | var color = pixels.getPixel32(points[i].x, points[i].y); 121 | } 122 | trace('get ${Timer.stamp() - startTime}'); 123 | 124 | // add random red points 125 | startTime = Timer.stamp(); 126 | for (i in 0...NUM_POINTS) { 127 | pixels.setPixel32(points[i].x, points[i].y, 0xFFFF0000); 128 | } 129 | // if this green line doesn't go _exactly_ from top-left to bottom-right, 130 | // then there's something wrong with the Pixels impl. 131 | Bresenham.line(pixels, 0, 0, pixels.width - 1, pixels.height - 1, 0x00FF00); 132 | trace('set ${Timer.stamp() - startTime}'); 133 | 134 | // apply the modified pixels back to bitmapData 135 | startTime = Timer.stamp(); 136 | pixels.applyToBitmapData(bitmapData); 137 | trace('apply ${Timer.stamp() - startTime}'); 138 | 139 | // trace info 140 | trace("pixels " + pixels.width, pixels.height, pixels.count, "0x" + StringTools.hex(pixels.getPixel32(50, 50), 8)); 141 | #if (jsprime || !(html5 && openfl_legacy)) 142 | trace("bitmapData " + bitmapData.width, bitmapData.height, bitmapData.width * bitmapData.height, "0x" + StringTools.hex(bitmapData.getPixel32(50, 50), 8) + "\n"); 143 | #end 144 | } 145 | 146 | function onKeyDown(event:KeyboardEvent):Void 147 | { 148 | if (event.keyCode == 27) { // ESC 149 | #if flash 150 | flash.system.System.exit(1); 151 | #elseif sys 152 | Sys.exit(1); 153 | #end 154 | } 155 | } 156 | } -------------------------------------------------------------------------------- /src/Bresenham.hx: -------------------------------------------------------------------------------- 1 | package; 2 | import hxPixels.Pixels; 3 | 4 | /** 5 | * ... 6 | * @author azrafe7 7 | */ 8 | class Bresenham { 9 | 10 | // see: http://en.wikipedia.org/wiki/Bresenham's_line_algorithm 11 | static public function lineCallback(fromX:Int, fromY:Int, toX:Int, toY:Int, precision:Int = 1, callback:Int->Int->Void = null):Void 12 | { 13 | if (callback == null) callback = function(x, y) { }; 14 | 15 | var steep:Bool = Math.abs(toY - fromY) > Math.abs(toX - fromX); 16 | 17 | if (steep) { // swap x <-> y 18 | var tmp:Int; 19 | 20 | tmp = fromX; 21 | fromX = fromY; 22 | fromY = tmp; 23 | 24 | tmp = toX; 25 | toX = toY; 26 | toY = tmp; 27 | } 28 | 29 | var deltaX:Int = Std.int(Math.abs(toX - fromX)); 30 | var deltaY:Int = Std.int(Math.abs(toY - fromY)); 31 | var error:Int = Std.int(deltaX / 2); 32 | var x:Int = fromX; 33 | var y:Int = fromY; 34 | var count:Int = -1; 35 | 36 | var xStep:Int = fromX < toX ? 1 : -1; 37 | var yStep:Int = fromY < toY ? 1 : -1; 38 | 39 | while (x != toX) { 40 | if (count == precision || count < 0) { 41 | if (steep) callback(y, x) 42 | else callback(x, y); 43 | count = 0; 44 | } 45 | 46 | error -= deltaY; 47 | if (error < 0) { 48 | y += yStep; 49 | error += deltaX; 50 | } 51 | x += xStep; 52 | count++; 53 | } 54 | 55 | // last point 56 | if (steep) callback(y, x) 57 | else callback(x, y); 58 | } 59 | 60 | static public function line(pixels:Pixels, fromX:Int, fromY:Int, toX:Int, toY:Int, color:Int, precision:Int = 1):Void 61 | { 62 | lineCallback(fromX, fromY, toX, toY, precision, function (x, y) { 63 | pixels.setPixel32(x, y, 0xFF000000 | color); 64 | }); 65 | } 66 | } -------------------------------------------------------------------------------- /src/FlambeAbstractPixelsDemo.hx: -------------------------------------------------------------------------------- 1 | package ; 2 | 3 | import flambe.display.Texture; 4 | import flambe.Entity; 5 | import flambe.System; 6 | import flambe.asset.AssetPack; 7 | import flambe.asset.Manifest; 8 | import flambe.display.FillSprite; 9 | import flambe.display.ImageSprite; 10 | import haxe.Timer; 11 | import hxPixels.Pixels; 12 | 13 | #if flash 14 | @:bitmap("assets/global/galapagosColor.png") 15 | class GalapagosColor extends flash.display.BitmapData { } 16 | #end 17 | 18 | class FlambeAbstractPixelsDemo 19 | { 20 | private static function main () 21 | { 22 | // Wind up all platform-specific stuff 23 | System.init(); 24 | 25 | // Catch uncaught exceptions 26 | System.uncaughtError.connect(log); 27 | 28 | // Load up the compiled pack in the assets directory named "global" 29 | var manifest = Manifest.fromAssets("global"); 30 | var loader = System.loadAssetPack(manifest); 31 | loader.get(onSuccess); 32 | } 33 | 34 | private static function onSuccess (pack :AssetPack) 35 | { 36 | var texture:Texture = pack.getTexture("galapagosColor"); 37 | 38 | // show the sprite 39 | var sprite = new ImageSprite(texture).centerAnchor().setXY(System.stage.width/2, System.stage.height/2); 40 | var entity = new Entity().add(sprite); 41 | System.root.addChild(entity); 42 | 43 | // load texture into pixels abstract 44 | var startTime = Timer.stamp(); 45 | #if html 46 | var pixels:Pixels = texture; 47 | #else 48 | var bitmapData = new GalapagosColor(0, 0); 49 | var pixels:Pixels = bitmapData; 50 | #end 51 | log("load " + (Timer.stamp() - startTime) + " " + pixels.width + "x" + pixels.height); 52 | 53 | // add random red points 54 | startTime = Timer.stamp(); 55 | for (i in 0...10000) { 56 | pixels.setPixel32(Std.int(Math.random() * pixels.width), Std.int(Math.random() * pixels.height), 0xFFFF0000); 57 | } 58 | // if this green line doesn't go _exactly_ from top-left to bottom-right, 59 | // then there's something wrong with the Pixels impl. 60 | Bresenham.line(pixels, 0, 0, pixels.width - 1, pixels.height - 1, 0x00FF00); 61 | log("set " + (Timer.stamp() - startTime)); 62 | 63 | // apply the modified pixels back to texture 64 | startTime = Timer.stamp(); 65 | #if html 66 | pixels.applyToFlambeTexture(texture); 67 | #else 68 | pixels.applyToBitmapData(bitmapData); 69 | texture = System.renderer.createTextureFromImage(bitmapData); 70 | entity.remove(sprite); 71 | sprite = new ImageSprite(texture).centerAnchor().setXY(System.stage.width/2, System.stage.height/2); 72 | entity.add(sprite); 73 | #end 74 | log("apply " + (Timer.stamp() - startTime)); 75 | } 76 | 77 | static public function log(x:Dynamic):Void { 78 | #if flash 79 | flash.external.ExternalInterface.call("console.log", x); 80 | #else 81 | js.Browser.console.log(x); 82 | #end 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/FormatAbstractPixelsDemo.hx: -------------------------------------------------------------------------------- 1 | package; 2 | 3 | import haxe.io.Bytes; 4 | import haxe.io.BytesInput; 5 | import haxe.io.Path; 6 | import haxe.Resource; 7 | import haxe.Timer; 8 | import hxPixels.Pixels; 9 | 10 | 11 | enum DataWrapper { 12 | PNG(data:format.png.Data); 13 | BMP(data:format.bmp.Data); 14 | GIF(data:format.gif.Data); 15 | Invalid; 16 | } 17 | 18 | class FormatAbstractPixelsDemo { 19 | 20 | var startTime:Float = 0.0; 21 | 22 | public static function main(): Void { 23 | new FormatAbstractPixelsDemo(); 24 | } 25 | 26 | public function new() { 27 | for (id in Resource.listNames()) { 28 | var res = Resource.getBytes(id); 29 | var bytesInput = new BytesInput(res); 30 | 31 | trace('[ testing $id ]'); 32 | 33 | test(bytesInput, id); 34 | } 35 | } 36 | 37 | public function test(input:BytesInput, id:String):Void { 38 | 39 | var ext = id.substr(-3).toLowerCase(); 40 | 41 | // read input into data using format tools 42 | startTime = Timer.stamp(); 43 | var dataWrapper = switch (ext) { 44 | case "png": 45 | var pngReader = new format.png.Reader(input); 46 | var data = pngReader.read(); 47 | PNG(data); 48 | 49 | case "bmp": 50 | var bmpReader = new format.bmp.Reader(input); 51 | var data = bmpReader.read(); 52 | BMP(data); 53 | 54 | case "gif": 55 | var gifReader = new format.gif.Reader(input); 56 | var data = gifReader.read(); 57 | GIF(data); 58 | 59 | default: 60 | Invalid; 61 | } 62 | trace('read ${Timer.stamp() - startTime}'); 63 | 64 | if (dataWrapper == Invalid) { 65 | trace("ERROR: Invalid data.\n"); 66 | return; 67 | } 68 | 69 | // load bitmapData into pixels abstract 70 | var pixels:Pixels = null; 71 | startTime = Timer.stamp(); 72 | pixels = switch (dataWrapper) { 73 | case PNG(data): data; 74 | case BMP(data): data; 75 | case GIF(data): Pixels.fromGIFData(data, Std.random(format.gif.Tools.framesCount(data)), Std.random(2) == 0 ? true : false); 76 | default: null; 77 | } 78 | trace('load ${Timer.stamp() - startTime}'); 79 | 80 | // generate random points 81 | var points = []; 82 | var NUM_POINTS = 10000; 83 | for (i in 0...NUM_POINTS) points.push( { x: Std.random(pixels.width), y: Std.random(pixels.height) } ); 84 | 85 | // read random points 86 | startTime = Timer.stamp(); 87 | for (i in 0...NUM_POINTS) { 88 | var color = pixels.getPixel32(points[i].x, points[i].y); 89 | } 90 | trace('get ${Timer.stamp() - startTime}'); 91 | 92 | // add random red points 93 | startTime = Timer.stamp(); 94 | for (i in 0...NUM_POINTS) { 95 | pixels.setPixel32(points[i].x, points[i].y, 0xFFFF0000); 96 | } 97 | // if this green line doesn't go _exactly_ from top-left to bottom-right, 98 | // then there's something wrong with the Pixels impl. 99 | Bresenham.line(pixels, 0, 0, pixels.width - 1, pixels.height - 1, 0x00FF00); 100 | trace('set ${Timer.stamp() - startTime}'); 101 | 102 | // trace info 103 | trace("pixels " + pixels.width, pixels.height, pixels.count, StringTools.hex(pixels.getPixel32(0, 0))); 104 | 105 | writeModifiedPNG(pixels, id); 106 | writeModifiedBMP(pixels, id); 107 | } 108 | 109 | public function writeModifiedPNG(pixels:Pixels, fileName:String) { 110 | var dir = Path.directory(Sys.programPath()); 111 | var outputFileName = "out_" + fileName + ".png"; 112 | var file = sys.io.File.write(Path.join([dir, outputFileName]), true); 113 | var pngWriter = new format.png.Writer(file); 114 | startTime = Timer.stamp(); 115 | pixels.convertTo(PixelFormat.ARGB); 116 | trace('convert ${Timer.stamp() - startTime}'); 117 | var pngData = format.png.Tools.build32ARGB(pixels.width, pixels.height, pixels.bytes); 118 | pngWriter.write(pngData); 119 | trace("written to '" + outputFileName + "'\n"); 120 | } 121 | 122 | public function writeModifiedBMP(pixels:Pixels, fileName:String) { 123 | var dir = Path.directory(Sys.programPath()); 124 | var outputFileName = "out_" + fileName + ".bmp"; 125 | var file = sys.io.File.write(Path.join([dir, outputFileName]), true); 126 | var bmpWriter = new format.bmp.Writer(file); 127 | startTime = Timer.stamp(); 128 | pixels.convertTo(PixelFormat.ARGB); 129 | trace('convert ${Timer.stamp() - startTime}'); 130 | var bmpData = format.bmp.Tools.buildFromARGB(pixels.width, pixels.height, pixels.bytes); 131 | bmpWriter.write(bmpData); 132 | trace("written to '" + outputFileName + "'\n"); 133 | } 134 | } -------------------------------------------------------------------------------- /src/ImageLoader.hx: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple class that wraps the loading of BitmapDatas embedded via @:bitmap metatag (for openfl/nme). 3 | * 4 | * Why?: 5 | * In the html5 target, openfl _awesomely_ embeds BitmapData by base64 encoding it as 6 | * a haxe.Resource. The problem is that the process of istantiating it afterwards is 7 | * asynchronous (as Javascript/html5 mandates). This requires the user to pass a callback 8 | * for every new instantiated BitmapData in order to be sure the image has finished loading. 9 | * 10 | * This class attempts to simplify the bulk-load of multiple BitmapDatas by presenting a 11 | * centralized onComplete callback (with optional timeout parameters), across all? targets. 12 | * 13 | * The main purpose of the approach implemented here is to be able to read/write the images' 14 | * raw pixel data in html5, working around common cross-origin issues. 15 | * 16 | * Usage: 17 | * @:bitmap("assets/smallheart.png") class Heart extends BitmapData {} 18 | * @:bitmap("assets/background.png") class BackDrop extends BitmapData {} 19 | * 20 | * ... 21 | * var loader = new ImageLoader(); 22 | * 23 | * loader.load([Heart, BackDrop], onLoaded, 2000, onTimeOut); 24 | * 25 | * // or 26 | * 27 | * loader.load(["Heart", "BackDrop"], onLoaded, 2000, onTimeOut); 28 | * ... 29 | * 30 | * function onLoaded(_) { 31 | * myBitmap.bitmapData = loader.getBitmapData("Heart"); 32 | * sceneBMD = loader.getBitmapData("BackDrop"); 33 | * } 34 | * 35 | * function onTimeOut(_) { 36 | * // handle timeout here 37 | * } 38 | */ 39 | class ImageLoader 40 | { 41 | public var map:Map; 42 | 43 | public function new() { 44 | map = new Map(); 45 | } 46 | 47 | public function load(bmdClassesOrClassNames:Array, onComplete:ImageLoader->Void, timeOutMs:Int = 0, ?onTimeOut:ImageLoader->Void) { 48 | var count = bmdClassesOrClassNames.length; 49 | var timedOut = false; 50 | 51 | var bmdClasses:Array> = []; 52 | 53 | // resolve classNames to classes 54 | if (Std.is(bmdClassesOrClassNames[0], String)) { 55 | var emptyLine = ""; 56 | for (i in 0...bmdClassesOrClassNames.length) { 57 | var clsName = bmdClassesOrClassNames[i]; 58 | var resolvedClass = Type.resolveClass(clsName); 59 | if (i == bmdClassesOrClassNames.length - 1) emptyLine = "\n"; 60 | trace(clsName + ": " + (resolvedClass != null ? "ok" : "null") + emptyLine); 61 | bmdClasses.push(cast resolvedClass); 62 | } 63 | } else { 64 | bmdClasses = cast bmdClassesOrClassNames; 65 | } 66 | 67 | // timeout handler 68 | if (count > 0 && timeOutMs > 0) { 69 | haxe.Timer.delay(function ():Void { 70 | if (count != 0) { 71 | timedOut = true; 72 | if (onTimeOut != null) onTimeOut(this); 73 | else throw 'ImageLoader timed out (${timeOutMs/1000}s elapsed)!'; 74 | } 75 | }, timeOutMs); 76 | } 77 | 78 | for (i in 0...bmdClasses.length) { 79 | var bmdClass = bmdClasses[i]; 80 | var name = Type.getClassName(bmdClass); 81 | 82 | #if html5 // for openfl html5 the loading is async 83 | Type.createInstance(bmdClass, [0, 0, true, 0, function(bmd) { 84 | if (timedOut) return; 85 | map[name] = bmd; 86 | count--; 87 | if (count == 0) onComplete(this); 88 | }]); 89 | #else 90 | map[name] = Type.createInstance(bmdClass, [0, 0]); 91 | count--; 92 | if (count == 0) onComplete(this); 93 | #end 94 | } 95 | } 96 | 97 | public function getBitmapData(className:String) { 98 | var res = map[className]; 99 | return res != null ? res : throw 'No @:bitmap named "${className}" found!'; 100 | } 101 | 102 | public function list():Array { 103 | return [for (k in map.keys()) k]; 104 | } 105 | } -------------------------------------------------------------------------------- /src/JavaAbstractPixelsDemo.hx: -------------------------------------------------------------------------------- 1 | package ; 2 | 3 | import haxe.Timer; 4 | import hxPixels.Pixels; 5 | import java.lang.System; 6 | import java.javax.swing.JFrame; 7 | import java.javax.swing.JLabel; 8 | import java.awt.Color; 9 | import java.awt.Dimension; 10 | import java.awt.Graphics; 11 | import java.awt.Graphics2D; 12 | import java.awt.RenderingHints; 13 | import java.javax.imageio.ImageIO; 14 | import java.awt.image.BufferedImage; 15 | import java.javax.swing.ImageIcon; 16 | import java.awt.event.*; 17 | 18 | class JavaAbstractPixelsDemo extends JFrame implements KeyListener { 19 | 20 | public static function main() 21 | { 22 | new JavaAbstractPixelsDemo(); 23 | } 24 | 25 | public function new() 26 | { 27 | super("JavaAbstractPixelsDemo"); 28 | //System.setProperty("sun.java2d.opengl","True"); 29 | setSize(1024, 780); 30 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 31 | setLocationRelativeTo(null); 32 | 33 | var image:BufferedImage = null; 34 | try { 35 | image = ImageIO.read(new java.io.File("../../assets/global/galapagosColor.png")); 36 | } catch (e:Dynamic) { 37 | throw e; 38 | } 39 | 40 | trace(image.getWidth(), image.getHeight(), image.getType()); // https://docs.oracle.com/javase/7/docs/api/constant-values.html#java.awt.image.BufferedImage.TYPE_4BYTE_ABGR 41 | 42 | add(new JLabel(new ImageIcon(image))); 43 | 44 | // load image into pixels abstract 45 | var startTime = Timer.stamp(); 46 | var pixels:Pixels = image; 47 | trace("load", Timer.stamp() - startTime); 48 | 49 | // add random red points 50 | startTime = Timer.stamp(); 51 | for (i in 0...10000) { 52 | pixels.setPixel32(Std.int(Math.random() * pixels.width), Std.int(Math.random() * pixels.height), 0xFFFF0000); 53 | } 54 | // if this green line doesn't go _exactly_ from top-left to bottom-right, 55 | // then there's something wrong with the Pixels impl. 56 | Bresenham.line(pixels, 0, 0, pixels.width - 1, pixels.height - 1, 0x00FF00); 57 | trace("set", Timer.stamp() - startTime); 58 | 59 | // apply the modified pixels back to image 60 | startTime = haxe.Timer.stamp(); 61 | pixels.applyToBufferedImage(image); 62 | trace("apply", Timer.stamp() - startTime); 63 | 64 | // trace info 65 | trace("pixels", pixels.width, pixels.height, pixels.count, StringTools.hex(pixels.getPixel32(100, 100))); 66 | trace("image ", image.getWidth(), image.getHeight(), image.getWidth() * image.getHeight(), StringTools.hex(image.getRGB(100, 100))); 67 | 68 | 69 | setVisible(true); 70 | 71 | addKeyListener(this); 72 | } 73 | 74 | public function keyPressed(e:KeyEvent) { 75 | if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { 76 | Sys.exit(1); 77 | } 78 | } 79 | 80 | public function keyTyped(e:KeyEvent) { } 81 | 82 | public function keyReleased(e:KeyEvent) { } 83 | } -------------------------------------------------------------------------------- /src/LuxeAbstractPixelsDemo.hx: -------------------------------------------------------------------------------- 1 | 2 | import haxe.Timer; 3 | import hxPixels.Pixels; 4 | import luxe.Input.Key; 5 | import luxe.Input.KeyEvent; 6 | import luxe.Sprite; 7 | import phoenix.Texture; 8 | import snow.api.Promise; 9 | import snow.types.Types.AssetImage; 10 | 11 | class LuxeAbstractPixelsDemo extends luxe.Game { 12 | 13 | var assets:Array = [ 14 | "assets/global/galapagosColor.png", 15 | "assets/global/FromBitmap.png" 16 | ]; 17 | 18 | override function ready() { 19 | 20 | var z = 0; 21 | 22 | for (asset in assets) { 23 | 24 | var promisedTexture = Luxe.resources.load_texture(asset); 25 | var promisedAsset = Luxe.snow.assets.image(asset); 26 | 27 | Promise.all([promisedTexture, promisedAsset]).then(function (fulfilledArray):Void { 28 | test(cast fulfilledArray[0], cast fulfilledArray[1], asset, z--); 29 | }); 30 | } 31 | } //ready 32 | 33 | function test(texture:Texture, assetImage:AssetImage, id:String, z:Int):Void { 34 | 35 | var sprite = new Sprite({ 36 | texture: texture, 37 | pos: Luxe.screen.mid, 38 | depth: z 39 | }); 40 | 41 | trace('[ testing $id ]'); 42 | 43 | // load into pixels abstract 44 | var startTime = Timer.stamp(); 45 | var pixels:Pixels = Pixels.fromLuxeAssetImage(assetImage); 46 | trace('load ${Timer.stamp() - startTime}'); 47 | 48 | // generate random points 49 | var points = []; 50 | var NUM_POINTS = 10000; 51 | for (i in 0...NUM_POINTS) points.push( { x: Std.int(Math.random() * pixels.width), y: Std.int(Math.random() * pixels.height) } ); 52 | 53 | // read random points 54 | startTime = Timer.stamp(); 55 | for (i in 0...NUM_POINTS) { 56 | var color = pixels.getPixel32(points[i].x, points[i].y); 57 | } 58 | trace('get ${Timer.stamp() - startTime}'); 59 | 60 | // add random red points 61 | startTime = Timer.stamp(); 62 | for (i in 0...10000) { 63 | var color = 0xFF0000; 64 | pixels.setPixel32(Std.int(Math.random() * texture.width), Std.int(Math.random() * texture.height), 65 | 0xFF000000 | color); 66 | } 67 | // if this green line doesn't go _exactly_ from top-left to bottom-right, 68 | // then there's something wrong with the Pixels impl. 69 | Bresenham.line(pixels, 0, 0, pixels.width - 1, pixels.height - 1, 0x00FF00); 70 | trace('set ${Timer.stamp() - startTime}'); 71 | 72 | // apply the modified pixels back 73 | startTime = Timer.stamp(); 74 | pixels.applyToLuxeTexture(texture); 75 | trace('apply ${Timer.stamp() - startTime}\n'); 76 | } 77 | 78 | override function onkeyup( e:KeyEvent ) { 79 | 80 | if(e.keycode == Key.escape) { 81 | Luxe.shutdown(); 82 | } 83 | 84 | } //onkeyup 85 | 86 | override function update(dt:Float) { 87 | 88 | } //update 89 | 90 | 91 | } //Main 92 | -------------------------------------------------------------------------------- /src/hxPixels/Pixels.hx: -------------------------------------------------------------------------------- 1 | package hxPixels; 2 | 3 | import haxe.io.Bytes; 4 | import haxe.io.UInt8Array; 5 | import haxe.io.UInt32Array; 6 | 7 | #if (openfl && !nme) 8 | #if (openfl >= "8.0.0") 9 | typedef LimeImageCanvasUtil = lime._internal.graphics.ImageCanvasUtil; 10 | #else 11 | typedef LimeImageCanvasUtil = lime.graphics.utils.ImageCanvasUtil; 12 | #end 13 | #end 14 | 15 | 16 | /** 17 | * Class abstracting pixels for various libs/targets (for easier manipulation). 18 | * 19 | * The exposed get/set methods will transparently work in ARGB format, while 20 | * the underlying bytes' pixel format will be automatically inferenced when one 21 | * of the `from_` methods is called. 22 | * 23 | * You can still override the channel mapping by setting `format` afterwards. 24 | * 25 | * NOTE: One important thing to keep in mind is that, on some targets/libs, a 26 | * pixel with an alpha value of zero doesn't necessarily mean that RGB will be 27 | * zero too (f.e. openfl with neko/cpp targets). This means that instead of 28 | * relying blindly on RGB values you should - in such cases - first test the 29 | * alpha one. 30 | * 31 | * @author azrafe7 32 | */ 33 | @:expose 34 | @:forward 35 | @:native("Pixels") 36 | abstract Pixels(PixelsData) 37 | { 38 | static public inline var CHANNEL_MASK:Int = 3; 39 | 40 | /** 41 | * Constructor. If `alloc` is false no memory will be allocated for `bytes`, 42 | * but the other properties (width, height, count) will still be set. 43 | */ 44 | inline public function new(width:Int, height:Int, alloc:Bool = true) 45 | { 46 | this = new PixelsData(width, height, alloc); 47 | } 48 | 49 | /** Returns the byte value at `i` position, as if the data were in ARGB format. */ 50 | inline public function getByte(i:Int) { 51 | return this.bytes.get((i & ~CHANNEL_MASK) + this.format.channelMap[i & CHANNEL_MASK]); 52 | } 53 | 54 | /** 55 | * Returns the raw pixel value (32-bits) at `i` position. 56 | * 57 | * NOTE: `i` is multiplied by 4, to align to pixel values (e.g. `getRawInt32(3)` returns the 4th pixel). 58 | */ 59 | @:arrayAccess 60 | inline public function getRawInt32(i:Int) { 61 | return this.bytes.getInt32(i << 2); 62 | } 63 | 64 | /** Returns the raw pixel value (with alpha) at `x`,`y`, in the original source format. */ 65 | inline public function getRawPixel32(x:Int, y:Int):Pixel { 66 | var pos = (y * this.width + x) << 2; 67 | return this.bytes.getInt32(pos); 68 | } 69 | 70 | /** Returns the pixel value (without alpha) at `x`,`y`, as if the data were in ARGB format. */ 71 | inline public function getPixel(x:Int, y:Int):Pixel { 72 | var pos = (y * this.width + x) << 2; 73 | 74 | var r = this.bytes.get(pos + this.format.R) << 16; 75 | var g = this.bytes.get(pos + this.format.G) << 8; 76 | var b = this.bytes.get(pos + this.format.B); 77 | 78 | return cast (r | g | b); 79 | } 80 | 81 | /** Returns the pixel value (with alpha) at `x`,`y`, as if the data were in ARGB format. */ 82 | inline public function getPixel32(x:Int, y:Int):Pixel { 83 | var pos = (y * this.width + x) << 2; 84 | 85 | var a = this.bytes.get(pos + this.format.A) << 24; 86 | var r = this.bytes.get(pos + this.format.R) << 16; 87 | var g = this.bytes.get(pos + this.format.G) << 8; 88 | var b = this.bytes.get(pos + this.format.B); 89 | 90 | return cast (a | r | g | b); 91 | } 92 | 93 | /** Sets the byte value at `i` pos, as if the data were in ARGB format. */ 94 | inline public function setByte(i:Int, value:Int) { 95 | this.bytes.set((i & ~CHANNEL_MASK) + this.format.channelMap[i & CHANNEL_MASK], value); 96 | } 97 | 98 | /** 99 | * Sets the raw pixel value (32-bits) at `i` position. 100 | * 101 | * NOTE: `i` is multiplied by 4, to align to pixel values (e.g. `setRawInt32(3, 0xFF010203)` sets the 4th pixel). 102 | */ 103 | @:arrayAccess 104 | inline public function setRawInt32(i:Int, value:Int) { 105 | this.bytes.setInt32(i << 2, value); 106 | } 107 | 108 | /** Sets the raw pixel value (with alpha) at `x`,`y`, in the original source format. */ 109 | inline public function setRawPixel32(x:Int, y:Int, value:Int) { 110 | var pos = (y * this.width + x) << 2; 111 | return this.bytes.setInt32(pos, value); 112 | } 113 | 114 | /** Sets the pixel value (without alpha) at `x`,`y`, with `value` expressed in RGB format. */ 115 | inline public function setPixel(x:Int, y:Int, value:Int) { 116 | var pos = (y * this.width + x) << 2; 117 | 118 | var r = (value >> 16) & 0xFF; 119 | var g = (value >> 8) & 0xFF; 120 | var b = (value) & 0xFF; 121 | 122 | this.bytes.set(pos + this.format.R, r); 123 | this.bytes.set(pos + this.format.G, g); 124 | this.bytes.set(pos + this.format.B, b); 125 | } 126 | 127 | /** Sets the pixel value (with alpha) at `x`,`y`, with `value` expressed in ARGB format. */ 128 | inline public function setPixel32(x:Int, y:Int, value:Int) { 129 | var pos = (y * this.width + x) << 2; 130 | 131 | var a = (value >> 24) & 0xFF; 132 | var r = (value >> 16) & 0xFF; 133 | var g = (value >> 8) & 0xFF; 134 | var b = (value) & 0xFF; 135 | 136 | this.bytes.set((pos + this.format.A), a); 137 | this.bytes.set((pos + this.format.R), r); 138 | this.bytes.set((pos + this.format.G), g); 139 | this.bytes.set((pos + this.format.B), b); 140 | } 141 | 142 | /** Fills the specified rect area, with `value` expressed in ARGB format. Doesn't do any bound checking. */ 143 | public function fillRect(x:Int, y:Int, width:Int, height:Int, value:Int):Void { 144 | var pos = (y * this.width + x) << 2; 145 | 146 | var stridePixels = new Pixels(width, 1, true); 147 | stridePixels.format = this.format; 148 | var stride = width << 2; 149 | 150 | for (x in 0...width) stridePixels.setPixel32(x, 0, value); 151 | for (y in 0...height) { 152 | this.bytes.blit(pos, stridePixels.bytes, 0, stride); 153 | pos += this.width << 2; 154 | } 155 | } 156 | 157 | public function clone():Pixels { 158 | var clone:Pixels = new Pixels(this.width, this.height, true); 159 | clone.bytes.blit(0, this.bytes, 0, this.bytes.length); 160 | clone.format = this.format; 161 | return clone; 162 | } 163 | 164 | static public function fromBytes(bytes:Bytes, width:Int, height:Int, ?format:PixelFormat):Pixels { 165 | var pixels = new Pixels(width, height, false); 166 | pixels.format = (format != null) ? format : PixelFormat.ARGB; 167 | pixels.bytes = bytes; 168 | return pixels; 169 | } 170 | 171 | inline public function convertTo(format:PixelFormat):Pixels { 172 | return convert(cast this, format, true); 173 | } 174 | 175 | static public function convert(pixels:Pixels, toFormat:PixelFormat, inPlace:Bool = false):Pixels { 176 | var res = inPlace ? pixels : pixels.clone(); 177 | 178 | if (toFormat == pixels.format) return res; 179 | 180 | var i = 0; 181 | var pos = 0; 182 | 183 | // fast path in case only B and R have to be swapped 184 | if ((pixels.format == PixelFormat.BGRA && toFormat == PixelFormat.RGBA) || 185 | (pixels.format == PixelFormat.RGBA && toFormat == PixelFormat.BGRA)) 186 | { 187 | while (i < pixels.count) { 188 | 189 | var r = pixels.getByte(pos + 1); 190 | var b = pixels.getByte(pos + 3); 191 | 192 | res.bytes.set(pos + toFormat.R, r); 193 | res.bytes.set(pos + toFormat.B, b); 194 | 195 | i++; 196 | pos += 4; 197 | } 198 | } else { 199 | while (i < pixels.count) { 200 | 201 | var a = pixels.getByte(pos + 0); 202 | var r = pixels.getByte(pos + 1); 203 | var g = pixels.getByte(pos + 2); 204 | var b = pixels.getByte(pos + 3); 205 | 206 | res.bytes.set(pos + toFormat.A, a); 207 | res.bytes.set(pos + toFormat.R, r); 208 | res.bytes.set(pos + toFormat.G, g); 209 | res.bytes.set(pos + toFormat.B, b); 210 | 211 | i++; 212 | pos += 4; 213 | } 214 | } 215 | 216 | res.format = toFormat; 217 | return res; 218 | } 219 | 220 | #if (format) // convert from png, bmp and gif data using `HaxeFoundation/format` lib (underlying bytes in BGRA format) 221 | 222 | @:from static public function fromPNGData(data:format.png.Data) { 223 | var header = format.png.Tools.getHeader(data); 224 | var bytes = format.png.Tools.extract32(data); 225 | var pixels = new Pixels(header.width, header.height, false); 226 | pixels.bytes = bytes; 227 | pixels.format = PixelFormat.BGRA; 228 | 229 | return pixels; 230 | } 231 | 232 | @:from static public function fromBMPData(data:format.bmp.Data) { 233 | var header = data.header; 234 | var bytes = format.bmp.Tools.extractBGRA(data); 235 | var pixels = new Pixels(header.width, header.height, false); 236 | pixels.bytes = bytes; 237 | pixels.format = PixelFormat.BGRA; 238 | 239 | return pixels; 240 | } 241 | 242 | static public function fromGIFData(data:format.gif.Data, frameIndex:Int = 0, full:Bool = true) { 243 | var pixels:Pixels; 244 | 245 | if (full) { 246 | pixels = new Pixels(data.logicalScreenDescriptor.width, data.logicalScreenDescriptor.height, false); 247 | pixels.bytes = format.gif.Tools.extractFullBGRA(data, frameIndex); 248 | } else { 249 | var frame = format.gif.Tools.frame(data, frameIndex); 250 | pixels = new Pixels(frame.width, frame.height, false); 251 | pixels.bytes = format.gif.Tools.extractBGRA(data, frameIndex); 252 | } 253 | pixels.format = PixelFormat.BGRA; 254 | 255 | return pixels; 256 | } 257 | 258 | #end 259 | 260 | #if (flambe) // in flambe texture bytes are in RGBA format 261 | 262 | @:from static public function fromFlambeTexture(texture:flambe.display.Texture) { 263 | var pixels = new Pixels(texture.width, texture.height, true); 264 | pixels.format = PixelFormat.RGBA; 265 | 266 | var data = texture.readPixels(0, 0, texture.width, texture.height); 267 | pixels.bytes.blit(0, data, 0, data.length); 268 | 269 | return pixels; 270 | } 271 | 272 | #if (flambe && html) // not possible in (flambe && flash) due to Stage3D limitations 273 | public function applyToFlambeTexture(texture:flambe.display.Texture) { 274 | texture.writePixels(this.bytes, 0, 0, this.width, this.height); 275 | } 276 | #end 277 | 278 | #end 279 | 280 | #if (snow || luxe) // in snow/luxe texture bytes are in RGBA format (and must account for padded sizes when submitting) 281 | 282 | @:from static public function fromLuxeTexture(texture:phoenix.Texture) { 283 | var pixels = new Pixels(texture.width, texture.height, false); 284 | pixels.format = PixelFormat.RGBA; 285 | 286 | var data = new snow.api.buffers.Uint8Array(texture.width * texture.height * 4); 287 | texture.fetch(data); 288 | pixels.bytes = data.toBytes(); 289 | 290 | return pixels; 291 | } 292 | 293 | @:from static public function fromLuxeAssetImage(assetImage:snow.types.Types.AssetImage) { 294 | var image:snow.types.Types.ImageData = assetImage.image; 295 | var pixels = new Pixels(image.width, image.height, true); 296 | pixels.format = PixelFormat.RGBA; 297 | 298 | var data = image.pixels.toBytes(); 299 | var stride = image.width * 4; 300 | 301 | for (y in 0...image.height) { 302 | pixels.bytes.blit(y * stride, data, y * image.width_actual * 4, stride); 303 | } 304 | 305 | return pixels; 306 | } 307 | 308 | public function applyToLuxeTexture(texture:phoenix.Texture) { 309 | var data = Bytes.alloc(texture.width_actual * texture.height_actual * 4); 310 | 311 | var padded_width = texture.width_actual; 312 | var stride = this.width * 4; 313 | 314 | for (y in 0...this.height) { 315 | data.blit(y * padded_width * 4, this.bytes, y * stride, stride); 316 | } 317 | 318 | texture.submit(snow.api.buffers.Uint8Array.fromBytes(data)); // rebind texture 319 | } 320 | #end 321 | 322 | #if (!macro && (flash || openfl || nme || (flambe && flash))) 323 | 324 | // NOTE: in openfl (and possibly nme) the texture pixels _might_ be in premultipliedAlpha format. 325 | // cfr. `bmd.image.buffer.premultiplied` (openfl) and `bmd.premultipliedAlpha` (nme) 326 | 327 | @:from static public function fromBitmapData(bmd:flash.display.BitmapData) { 328 | #if (js && !jsprime) 329 | 330 | var pixels = new Pixels(bmd.width, bmd.height, false); 331 | pixels.format = PixelFormat.RGBA; 332 | 333 | // force buffer creation 334 | var image = bmd.image; 335 | LimeImageCanvasUtil.convertToCanvas(image); 336 | LimeImageCanvasUtil.createImageData(image); 337 | 338 | var data = image.buffer.data; 339 | pixels.bytes = Bytes.ofData(data.buffer); 340 | 341 | #else 342 | 343 | var pixels = new Pixels(bmd.width, bmd.height, false); 344 | 345 | #if flash 346 | 347 | pixels.format = PixelFormat.ARGB; 348 | 349 | var ba = bmd.getPixels(bmd.rect); 350 | pixels.bytes = Bytes.ofData(ba); 351 | 352 | #elseif (openfl_next || openfl >= "4.0.0") 353 | 354 | //trace("!flash openfl"); 355 | pixels.format = PixelFormat.BGRA; 356 | 357 | var data = @:privateAccess bmd.image.buffer.data.buffer.getData(); 358 | pixels.bytes = Bytes.ofData(data); 359 | 360 | #else 361 | 362 | //trace("!next openfl < 4.0.0"); 363 | pixels.format = PixelFormat.ARGB; 364 | 365 | var ba = bmd.getPixels(bmd.rect); 366 | pixels.bytes = (ba); 367 | 368 | #end 369 | 370 | #end 371 | 372 | return pixels; 373 | } 374 | 375 | public function applyToBitmapData(bmd:flash.display.BitmapData) { 376 | #if (js && !jsprime) 377 | 378 | var image = bmd.image; 379 | 380 | #if (openfl < "4.0.0") 381 | 382 | LimeImageCanvasUtil.convertToData(image); 383 | image.dirty = true; 384 | 385 | #else 386 | 387 | image.buffer.data = lime.utils.UInt8Array.fromBytes(this.bytes); 388 | image.type = lime.graphics.ImageType.DATA; 389 | image.dirty = true; 390 | image.version++; 391 | 392 | #end 393 | 394 | #else 395 | 396 | #if flash 397 | 398 | //trace("flash"); 399 | var ba = this.bytes.getData(); 400 | ba.endian = flash.utils.Endian.BIG_ENDIAN; 401 | ba.position = 0; 402 | bmd.setPixels(bmd.rect, ba); 403 | 404 | #elseif (openfl_next || openfl >= "4.0.0") 405 | 406 | //trace("!flash openfl"); 407 | bmd.image.buffer.data = lime.utils.UInt8Array.fromBytes(this.bytes); 408 | 409 | #if (openfl >= "4.0.0") 410 | 411 | //trace("!flash openfl >= 4"); 412 | bmd.image.dirty = true; 413 | bmd.image.version++; 414 | 415 | #end 416 | 417 | #else 418 | 419 | //trace("!next openfl < 4.0.0"); 420 | var ba = openfl.utils.ByteArray.fromBytes(this.bytes); 421 | ba.position = 0; 422 | bmd.setPixels(bmd.rect, ba); 423 | 424 | #end 425 | 426 | #end 427 | } 428 | 429 | #end 430 | 431 | #if java 432 | 433 | @:from static public function fromBufferedImage(image:java.awt.image.BufferedImage) { 434 | var pixels = new Pixels(image.getWidth(), image.getHeight(), true); 435 | pixels.format = PixelFormat.RGBA; 436 | 437 | var buffer = new java.NativeArray(pixels.bytes.length); 438 | buffer = image.getRaster().getPixels(0, 0, pixels.width, pixels.height, buffer); 439 | 440 | for (i in 0...buffer.length) pixels.bytes.set(i, buffer[i]); 441 | 442 | return pixels; 443 | } 444 | 445 | public function applyToBufferedImage(image:java.awt.image.BufferedImage) { 446 | var buffer = new java.NativeArray(this.bytes.length); 447 | for (i in 0...buffer.length) buffer[i] = this.bytes.get(i); 448 | 449 | image.getRaster().setPixels(0, 0, this.width, this.height, buffer); 450 | } 451 | 452 | #end 453 | 454 | #if js // plain js - conversion from ImageData 455 | 456 | @:from static public function fromImageData(image:js.html.ImageData) { 457 | var pixels = new Pixels(image.width, image.height, false); 458 | pixels.format = PixelFormat.RGBA; 459 | 460 | var u8ClampedArray:js.html.Uint8ClampedArray = image.data; 461 | 462 | var u8Array = haxe.io.UInt8Array.fromData(cast u8ClampedArray); 463 | pixels.bytes = u8Array.view.buffer; 464 | 465 | return pixels; 466 | } 467 | 468 | public function applyToImageData(imageData:js.html.ImageData) { 469 | var u8clampedArray = new js.html.Uint8ClampedArray(this.bytes.getData()); 470 | imageData.data.set(u8clampedArray); 471 | return imageData; 472 | } 473 | 474 | #end 475 | } 476 | 477 | 478 | @:allow(hxPixels.Pixels) 479 | private class PixelsData 480 | { 481 | inline static public var BYTES_PER_PIXEL:Int = 4; 482 | 483 | /** Total number of pixels. */ 484 | public var count(default, null):Int; 485 | 486 | /** Bytes representing the pixels (in the raw format used by the original source). */ 487 | public var bytes(default, set):Bytes; 488 | inline function set_bytes(bytes:Bytes):Bytes { 489 | this.bytes = bytes; 490 | this.uint8Array = UInt8Array.fromBytes(bytes); 491 | this.uint32Array = UInt32Array.fromBytes(bytes); 492 | return this.bytes; 493 | } 494 | 495 | /** Width of the source image. */ 496 | public var width(default, null):Int; 497 | 498 | /** Height of the source image. */ 499 | public var height(default, null):Int; 500 | 501 | /** Internal pixel format. */ 502 | public var format:PixelFormat; 503 | 504 | /** UInt8Array view over bytes. */ 505 | public var uint8Array:UInt8Array = null; 506 | /** UInt32Array view over bytes. */ 507 | public var uint32Array:UInt32Array = null; 508 | 509 | /** 510 | * Constructor. If `alloc` is false no memory will be allocated for `bytes`, 511 | * but the other properties (width, height, count) will still be set. 512 | * 513 | * `format` defaults to ARGB. 514 | */ 515 | public function new(width:Int, height:Int, alloc:Bool = true, format:PixelFormat = null) 516 | { 517 | this.count = width * height; 518 | 519 | if (alloc) bytes = Bytes.alloc(this.count << 2); 520 | 521 | this.width = width; 522 | this.height = height; 523 | this.format = format != null ? format : PixelFormat.ARGB; 524 | } 525 | } 526 | 527 | @:expose 528 | @:allow(hxPixels.Pixels) 529 | @:allow(hxPixels.Pixel) 530 | class PixelFormat { 531 | 532 | static public var ARGB(default, null):PixelFormat; 533 | static public var RGBA(default, null):PixelFormat; 534 | static public var BGRA(default, null):PixelFormat; 535 | 536 | /** Internal. Don't modify any of these. */ 537 | var channelMap:Array; 538 | var ch0:Channel; 539 | var ch1:Channel; 540 | var ch2:Channel; 541 | var ch3:Channel; 542 | 543 | var name:String; 544 | 545 | static function __init__():Void { 546 | ARGB = new PixelFormat(CH_0, CH_1, CH_2, CH_3, "ARGB"); 547 | RGBA = new PixelFormat(CH_3, CH_0, CH_1, CH_2, "RGBA"); 548 | BGRA = new PixelFormat(CH_3, CH_2, CH_1, CH_0, "BGRA"); 549 | } 550 | 551 | /** 552 | * Rearranges the bytes of a pixel/Int in `fromFormat` to a new pixel in `toFormat`. 553 | * 554 | * E.g.: 555 | * 556 | * `var argb:Pixel = 0xAA2266BB; // 0xaarrggbb 557 | * 558 | * // 559 | * var same = PixelFormat.convert(argb, PixelFormat.ARGB, PixelFormat.ARGB); // 0xAA2266BB 560 | * var rgba = PixelFormat.convert(same, PixelFormat.ARGB, PixelFormat.RGBA); // 0x2266BBAA 561 | * var bgra = PixelFormat.convert(rgba, PixelFormat.RGBA, PixelFormat.BGRA); // 0xBB6622AA 562 | * var back = PixelFormat.convert(bgra, PixelFormat.BGRA, PixelFormat.ARGB); // 0xAA2266BB 563 | * var rgba2 = PixelFormat.convert(bgra, PixelFormat.BGRA, PixelFormat.RGBA); // 0x2266BBAA` 564 | * 565 | */ 566 | static public function convert(px:Pixel, fromFormat:PixelFormat, toFormat:PixelFormat):Pixel { 567 | return 568 | (((px >> (8 * (Pixels.CHANNEL_MASK - fromFormat.A))) & 0xFF) << (8 * (Pixels.CHANNEL_MASK - toFormat.A))) | 569 | (((px >> (8 * (Pixels.CHANNEL_MASK - fromFormat.R))) & 0xFF) << (8 * (Pixels.CHANNEL_MASK - toFormat.R))) | 570 | (((px >> (8 * (Pixels.CHANNEL_MASK - fromFormat.G))) & 0xFF) << (8 * (Pixels.CHANNEL_MASK - toFormat.G))) | 571 | (((px >> (8 * (Pixels.CHANNEL_MASK - fromFormat.B))) & 0xFF) << (8 * (Pixels.CHANNEL_MASK - toFormat.B))); 572 | } 573 | 574 | inline static public function getNativeFormatFor(target:TargetType):PixelFormat { 575 | return switch (target) { 576 | case FORMAT: 577 | BGRA; 578 | case FLASH, NME_FLASH, NME_DESKTOP, OPENFL_FLASH, FLAMBE_FLASH: 579 | ARGB; 580 | case OPENFL_DESKTOP: 581 | BGRA; 582 | case JS, OPENFL_JS, LUXE, FLAMBE_WEB: 583 | RGBA; 584 | case JAVA: 585 | RGBA; 586 | default: 587 | throw "Unhandled target!"; 588 | } 589 | } 590 | 591 | inline public function new(a:Channel, r:Channel, g:Channel, b:Channel, name:String = "PixelFormat"):Void { 592 | this.channelMap = [a, r, g, b]; 593 | this.ch0 = a; 594 | this.ch1 = r; 595 | this.ch2 = g; 596 | this.ch3 = b; 597 | this.name = name; 598 | } 599 | 600 | public var A(get, null):Channel; 601 | inline private function get_A():Channel { 602 | return ch0; 603 | } 604 | 605 | public var R(get, null):Channel; 606 | inline private function get_R():Channel { 607 | return ch1; 608 | } 609 | 610 | public var G(get, null):Channel; 611 | inline private function get_G():Channel { 612 | return ch2; 613 | } 614 | 615 | public var B(get, null):Channel; 616 | inline private function get_B():Channel { 617 | return ch3; 618 | } 619 | 620 | public function toString():String { 621 | return name; 622 | } 623 | } 624 | 625 | @:enum abstract Channel(Int) to Int { 626 | 627 | static public var MASK = [0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF]; 628 | 629 | var CH_0 = 0; 630 | var CH_1 = 1; 631 | var CH_2 = 2; 632 | var CH_3 = 3; 633 | 634 | @:op(A + B) static function add(a:Int, b:Channel):Int; 635 | } 636 | 637 | /** 638 | * Abstracts an ARGB pixel over Int. 639 | * 640 | * NOTE: it's just a convenience class to easily access ARGB channels' values (wrapping bit math). 641 | * Since it's built on top of a primitive (value) type, be careful with what you expect when 642 | * assigning to it. In particular consider that: 643 | * 644 | * `pixels.getPixel32(10, 10).R = 0xFF;` 645 | * 646 | * will NOT modify the actual pixel (but just an Int copy/representation of it!) 647 | * 648 | * What you really want is probably: 649 | * 650 | * `var pixel = pixels.getPixel32(10, 10); 651 | * pixel.R = 0xFF; // or pixel.fR = 1.0; 652 | * pixels.setPixel32(10, 10, pixel);` 653 | * 654 | * Also note that, for performance reasons, no clamping is performed when setting values, i.e.: 655 | * 656 | * `pixel.B = 0xFFFF;` 657 | * 658 | * is perfectly valid, but will probably result in unwanted behaviour. 659 | */ 660 | @:expose 661 | @:forward 662 | @:native("Pixel") 663 | abstract Pixel(Int) from Int to Int 664 | { 665 | inline static public function fclamp(value:Float):Float { 666 | if (value <= 0.) return 0.; 667 | else if (value >= 1.) return 1.; 668 | else return value; 669 | } 670 | 671 | inline static public function iclamp(value:Int):Int { 672 | if (value <= 0) return 0; 673 | else if (value >= 255) return 255; 674 | else return value; 675 | } 676 | 677 | inline static public function iround(value:Float):Int { 678 | return Std.int(value + .5); 679 | } 680 | 681 | inline public function multiplyAlpha():Pixel { 682 | var fA = (this:Pixel).fA; 683 | return 684 | (this & 0xFF000000) | 685 | (iclamp(iround(fA * (this:Pixel).R)) << 16) | 686 | (iclamp(iround(fA * (this:Pixel).G)) << 8) | 687 | (iclamp(iround(fA * (this:Pixel).B))); 688 | } 689 | 690 | inline public function unmultiplyAlpha():Pixel { 691 | var inv_fA = 1. / ((this:Pixel).fA + 0.00000001); // inc fA to avoid divide_by_zero special case 692 | return 693 | (this & 0xFF000000) | 694 | (iclamp(iround(inv_fA * (this:Pixel).R)) << 16) | 695 | (iclamp(iround(inv_fA * (this:Pixel).G)) << 8) | 696 | (iclamp(iround(inv_fA * (this:Pixel).B))); 697 | } 698 | 699 | inline static public function create(a:Int, r:Int, g:Int, b:Int):Pixel { 700 | return ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | b; 701 | } 702 | 703 | inline static public function fcreate(a:Float, r:Float, g:Float, b:Float):Pixel { 704 | return create(Std.int(a * 255.), Std.int(r * 255.), Std.int(g * 255.), Std.int(b * 255.)); 705 | } 706 | 707 | inline public function getChannel(ch:Channel):Int { 708 | return (this >> (8 * (Pixels.CHANNEL_MASK - ch))) & 0xFF; 709 | } 710 | 711 | inline public function setChannel(ch:Channel, value:Int):Int { 712 | this = (this & ~Channel.MASK[ch]) | (value << (8 * (Pixels.CHANNEL_MASK - ch))); 713 | return value; 714 | } 715 | 716 | public var A(get, set):Int; 717 | inline private function get_A():Int { 718 | return (this >> 24) & 0xFF; 719 | } 720 | inline private function set_A(a:Int):Int { 721 | this = (this & 0x00FFFFFF) | (a << 24); 722 | return a; 723 | } 724 | 725 | public var R(get, set):Int; 726 | inline private function get_R():Int { 727 | return (this >> 16) & 0xFF; 728 | } 729 | inline private function set_R(r:Int):Int { 730 | this = (this & 0xFF00FFFF) | (r << 16); 731 | return r; 732 | } 733 | 734 | public var G(get, set):Int; 735 | inline private function get_G():Int { 736 | return (this >> 8) & 0xFF; 737 | } 738 | inline private function set_G(g:Int):Int { 739 | this = (this & 0xFFFF00FF) | (g << 8); 740 | return g; 741 | } 742 | 743 | public var B(get, set):Int; 744 | inline private function get_B():Int { 745 | return this & 0xFF; 746 | } 747 | inline private function set_B(b:Int):Int { 748 | this = (this & 0xFFFFFF00) | b; 749 | return b; 750 | } 751 | 752 | // channels as floats (expected range is [0...1]) 753 | public var fA(get, set):Float; 754 | inline private function get_fA():Float { 755 | return (this : Pixel).A / 255.; 756 | } 757 | inline private function set_fA(a:Float):Float { 758 | this = (this & 0x00FFFFFF) | (Std.int(a * 255) << 24); 759 | return a; 760 | } 761 | 762 | public var fR(get, set):Float; 763 | inline private function get_fR():Float { 764 | return (this : Pixel).R / 255.; 765 | } 766 | inline private function set_fR(r:Float):Float { 767 | this = (this & 0xFF00FFFF) | (Std.int(r * 255) << 16); 768 | return r; 769 | } 770 | 771 | public var fG(get, set):Float; 772 | inline private function get_fG():Float { 773 | return (this : Pixel).G / 255.; 774 | } 775 | inline private function set_fG(g:Float):Float { 776 | this = (this & 0xFFFF00FF) | (Std.int(g * 255) << 8); 777 | return g; 778 | } 779 | 780 | public var fB(get, set):Float; 781 | inline private function get_fB():Float { 782 | return (this : Pixel).B / 255.; 783 | } 784 | inline private function set_fB(b:Float):Float { 785 | this = (this & 0xFFFFFF00) | (Std.int(b * 255)); 786 | return b; 787 | } 788 | 789 | // forward operators from Int (same order used in std/Int32) 790 | @:op(-A) function negate():Pixel; 791 | @:op(++A) function preIncrement():Pixel; 792 | @:op(A++) function postIncrement():Pixel; 793 | @:op(--A) function preDecrement():Pixel; 794 | @:op(A--) function postDecrement():Pixel; 795 | 796 | @:op(A + B) static function add(a:Pixel, b:Pixel):Pixel; 797 | @:op(A + B) @:commutative static function addInt(a:Pixel, b:Int):Pixel; 798 | @:op(A + B) @:commutative static function addFloat(a:Pixel, b:Float):Float; 799 | @:op(A - B) static function sub(a:Pixel, b:Pixel):Pixel; 800 | @:op(A - B) static function subInt(a:Pixel, b:Int):Pixel; 801 | @:op(A - B) static function intSub(a:Int, b:Pixel):Pixel; 802 | @:op(A - B) static function subFloat(a:Pixel, b:Float):Float; 803 | @:op(A - B) static function floatSub(a:Float, b:Pixel):Float; 804 | @:op(A * B) static function mul(a:Pixel, b:Pixel):Pixel; 805 | @:op(A * B) @:commutative static function mulInt(a:Pixel, b:Int):Pixel; 806 | @:op(A * B) static function mul(a:Pixel, b:Pixel):Pixel; 807 | @:op(A * B) @:commutative static function mulInt(a:Pixel, b:Int):Pixel; 808 | @:op(A * B) @:commutative static function mulFloat(a:Pixel, b:Float):Float; 809 | @:op(A / B) static function div(a:Pixel, b:Pixel):Float; 810 | @:op(A / B) static function divInt(a:Pixel, b:Int):Float; 811 | @:op(A / B) static function intDiv(a:Int, b:Pixel):Float; 812 | @:op(A / B) static function divFloat(a:Pixel, b:Float):Float; 813 | @:op(A / B) static function floatDiv(a:Float, b:Pixel):Float; 814 | 815 | @:op(A % B) static function mod(a:Pixel, b:Pixel):Pixel; 816 | @:op(A % B) static function modInt(a:Pixel, b:Int):Int; 817 | @:op(A % B) static function intMod(a:Int, b:Pixel):Int; 818 | @:op(A % B) static function modFloat(a:Pixel, b:Float):Float; 819 | @:op(A % B) static function floatMod(a:Float, b:Pixel):Float; 820 | 821 | @:op(A == B) static function eq(a:Pixel, b:Pixel):Bool; 822 | @:op(A == B) @:commutative static function eqInt(a:Pixel, b:Int):Bool; 823 | @:op(A == B) @:commutative static function eqFloat(a:Pixel, b:Float):Bool; 824 | 825 | @:op(A != B) static function neq(a:Pixel, b:Pixel):Bool; 826 | @:op(A != B) @:commutative static function neqInt(a:Pixel, b:Int):Bool; 827 | @:op(A != B) @:commutative static function neqFloat(a:Pixel, b:Float):Bool; 828 | 829 | @:op(A < B) static function lt(a:Pixel, b:Pixel):Bool; 830 | @:op(A < B) static function ltInt(a:Pixel, b:Int):Bool; 831 | @:op(A < B) static function intLt(a:Int, b:Pixel):Bool; 832 | @:op(A < B) static function ltFloat(a:Pixel, b:Float):Bool; 833 | @:op(A < B) static function floatLt(a:Float, b:Pixel):Bool; 834 | 835 | @:op(A <= B) static function lte(a:Pixel, b:Pixel):Bool; 836 | @:op(A <= B) static function lteInt(a:Pixel, b:Int):Bool; 837 | @:op(A <= B) static function intLte(a:Int, b:Pixel):Bool; 838 | @:op(A <= B) static function lteFloat(a:Pixel, b:Float):Bool; 839 | @:op(A <= B) static function floatLte(a:Float, b:Pixel):Bool; 840 | 841 | @:op(A > B) static function gt(a:Pixel, b:Pixel):Bool; 842 | @:op(A > B) static function gtInt(a:Pixel, b:Int):Bool; 843 | @:op(A > B) static function intGt(a:Int, b:Pixel):Bool; 844 | @:op(A > B) static function gtFloat(a:Pixel, b:Float):Bool; 845 | @:op(A > B) static function floatGt(a:Float, b:Pixel):Bool; 846 | 847 | @:op(A >= B) static function gte(a:Pixel, b:Pixel):Bool; 848 | @:op(A >= B) static function gteInt(a:Pixel, b:Int):Bool; 849 | @:op(A >= B) static function intGte(a:Int, b:Pixel):Bool; 850 | @:op(A >= B) static function gteFloat(a:Pixel, b:Float):Bool; 851 | @:op(A >= B) static function floatGte(a:Float, b:Pixel):Bool; 852 | 853 | @:op(~A) function complement():Pixel; 854 | 855 | @:op(A & B) static function and(a:Pixel, b:Pixel):Pixel; 856 | @:op(A & B) @:commutative static function andInt(a:Pixel, b:Int):Pixel; 857 | 858 | @:op(A | B) static function or(a:Pixel, b:Pixel):Pixel; 859 | @:op(A | B) @:commutative static function orInt(a:Pixel, b:Int):Pixel; 860 | 861 | @:op(A ^ B) static function xor(a:Pixel, b:Pixel):Pixel; 862 | @:op(A ^ B) @:commutative static function xorInt(a:Pixel, b:Int):Pixel; 863 | 864 | 865 | @:op(A >> B) static function shr(a:Pixel, b:Pixel):Pixel; 866 | @:op(A >> B) static function shrInt(a:Pixel, b:Int):Pixel; 867 | @:op(A >> B) static function intShr(a:Int, b:Pixel):Pixel; 868 | 869 | @:op(A >>> B) static function ushr(a:Pixel, b:Pixel):Pixel; 870 | @:op(A >>> B) static function ushrInt(a:Pixel, b:Int):Pixel; 871 | @:op(A >>> B) static function intUshr(a:Int, b:Pixel):Pixel; 872 | 873 | @:op(A << B) static function shl(a:Pixel, b:Pixel):Pixel; 874 | @:op(A << B) static function shlInt(a:Pixel, b:Int):Pixel; 875 | @:op(A << B) static function intShl(a:Int, b:Pixel):Pixel; 876 | } 877 | 878 | @:enum abstract TargetType(String) { 879 | var FLASH = "flash"; 880 | var FORMAT = "format"; 881 | var FLAMBE_FLASH = "flambe flash"; 882 | var FLAMBE_WEB = "flambe web"; 883 | var LUXE = "luxe"; 884 | var OPENFL_JS = "openfl js"; 885 | var OPENFL_DESKTOP = "openfl desktop"; 886 | var OPENFL_FLASH = "openfl flash"; 887 | var NME_DESKTOP = "nme desktop"; 888 | var NME_FLASH = "nme flash"; 889 | var JAVA = "java"; 890 | var JS = "js"; 891 | var UNKNOWN = "unknown"; 892 | 893 | static public inline function getCurrent():TargetType { 894 | #if (flash && !(openfl || nme || flambe)) 895 | return FLASH; 896 | #elseif (luxe || snow) 897 | return LUXE; 898 | #elseif flambe 899 | #if (flash) 900 | return FLAMBE_FLASH; 901 | #else 902 | return FLAMBE_WEB; 903 | #end 904 | #elseif (openfl && !nme) 905 | #if (js) 906 | return OPENFL_JS; 907 | #elseif (neko || cpp) 908 | return OPENFL_DESKTOP; 909 | #elseif flash 910 | return OPENFL_FLASH; 911 | #else 912 | return UNKNOWN; 913 | #end 914 | #elseif (nme) 915 | #if (neko || cpp) 916 | return NME_DESKTOP; 917 | #elseif flash 918 | return NME_FLASH; 919 | #else 920 | return UNKNOWN; 921 | #end 922 | #elseif java 923 | return JAVA; 924 | #elseif js 925 | return JS; 926 | #else 927 | return UNKNOWN; 928 | #end 929 | } 930 | } 931 | 932 | @:enum abstract Endianness(Int) to Int { 933 | var BIG = 0; 934 | var LITTLE = 1; 935 | 936 | static public var names = ["BIG_ENDIAN", "LITTLE_ENDIAN"]; 937 | 938 | /** 939 | * Get system endianness. 940 | * 941 | * NOTE: It _might_ differ from actual texture bytes implementation 942 | * (e.g. flash usually uses BIG_ENDIAN even though the host system is LITTLE_ENDIAN). 943 | */ 944 | static public function getCurrent():Endianness { 945 | var a:UInt32Array = UInt32Array.fromArray([0xDDCCBBAA]); 946 | var b:UInt8Array = UInt8Array.fromBytes(a.view.buffer); 947 | if (b[0] == 0xDD) return BIG; 948 | else /*if (b[0] == 0xAA)*/ return LITTLE; 949 | } 950 | 951 | inline static public function getName(endian:Endianness) { 952 | return names[endian]; 953 | } 954 | } -------------------------------------------------------------------------------- /web/README.md: -------------------------------------------------------------------------------- 1 | This folder is needed by flambe. -------------------------------------------------------------------------------- /web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azrafe7/hxPixels/d5292060d2a31f34b21d3c5089b9c1502d741b3e/web/favicon.ico -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Your Game 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 60 | 61 | 62 | 63 |
64 | 65 | 68 | 69 | 77 | 78 | 79 | 80 | --------------------------------------------------------------------------------