├── GifToAnimationDrawable ├── .gitignore ├── gif2animdraw ├── gif2animdraw.sh ├── index.html └── upload.php ├── LICENSE ├── README.md ├── css └── studio.css ├── index.html ├── js └── asset-studio.pack.js └── lib ├── cssreset-3.4.1.min.css └── jquery.min.js /GifToAnimationDrawable/.gitignore: -------------------------------------------------------------------------------- 1 | uploads 2 | outputs 3 | -------------------------------------------------------------------------------- /GifToAnimationDrawable/gif2animdraw: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | require 'fileutils' 3 | require 'rmagick' 4 | require 'slop' 5 | require 'builder' 6 | 7 | opts = Slop.new do 8 | banner "gif2animdraw [options]\n" 9 | on :i, :input=, 'path to animated GIF (or directory of GIFs)', :required => true 10 | on :o, :outdir=, 'path to root output directory', :required => true 11 | on :d, :density=, 'density name to use for frames', :required=>true 12 | on :oneshot, 'if set, animation does not repeat', :required => false, :default=>false 13 | help 14 | end 15 | 16 | begin 17 | opts.parse 18 | rescue 19 | puts opts.help 20 | exit -1 21 | end 22 | 23 | if !['ldpi', 'mdpi', 'tvdpi', 'hdpi', 'xhdpi', 'xxhdpi', 'xxxhdpi'].include?(opts[:d]) 24 | puts "Invalid density #{opts[:d]}" 25 | exit -1 26 | end 27 | 28 | glob=File.directory?(opts[:i]) ? File.join(opts[:i], '*.gif') : opts[:i] 29 | 30 | Dir[glob].each do |gif| 31 | input=Magick::ImageList.new(gif).coalesce 32 | output=File.join(opts[:o], 'drawable') 33 | output_density=File.join(opts[:o], 'drawable-'+opts[:d]) 34 | basename=File.basename(gif, '.gif').downcase.gsub(/\W/,'_') 35 | 36 | FileUtils.mkdir_p output 37 | FileUtils.mkdir_p output_density 38 | 39 | Dir.glob(File.join(output_density, basename+'*.png')).each {|f| File.delete(f) } 40 | 41 | input.write File.join(output_density, basename+'_%d.png') 42 | 43 | builder = Builder::XmlMarkup.new(:indent=>2) 44 | 45 | builder.tag!('animation-list', 46 | 'xmlns:android'=>'http://schemas.android.com/apk/res/android', 47 | 'android:oneshot'=>opts[:oneshot]) do 48 | i=0 49 | input.each do |frame| 50 | builder.item('android:drawable'=>"@drawable/#{basename}_#{i}", 51 | 'android:duration'=>frame.delay*1000/input.ticks_per_second) 52 | i+=1 53 | end 54 | end 55 | 56 | open(File.join(output, basename+'.xml'), 'w') do |f| 57 | f.puts builder.target! 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /GifToAnimationDrawable/gif2animdraw.sh: -------------------------------------------------------------------------------- 1 | gif=$1; 2 | filename="${gif%.*}"; 3 | echo "Filename is $filename" 4 | density=$2; 5 | targetDensities=$3; 6 | 7 | function getScaleForDensity() { 8 | case "$1" in 9 | ("ldpi") echo 0.75 ;; 10 | ("mdpi") echo 1 ;; 11 | ("hdpi") echo 1.5 ;; 12 | ("xhdpi") echo 2 ;; 13 | ("xxhdpi") echo 3 ;; 14 | ("xxxhdpi") echo 4 ;; 15 | esac 16 | } 17 | 18 | declare -a allowed_densities=("ldpi" "mdpi" "hdpi" "xhdpi" "xxhdpi" "xxxhdpi") 19 | valid_density=false; 20 | for i in "${allowed_densities[@]}"; do 21 | if [ "$density" == "$i" ]; then 22 | valid_density=true; 23 | fi 24 | # or do whatever with individual element of the array 25 | done 26 | 27 | if [ $valid_density == false ]; then 28 | density="hdpi"; 29 | fi 30 | echo "Density is $density" 31 | 32 | mkdir drawable drawable-$density; 33 | 34 | cd drawable-$density; 35 | 36 | pwd 37 | 38 | prevdirsize=-1; 39 | curdirsize=`ls -1| wc -l`; 40 | echo "prev=$prevdirsize current=$curdirsize"; 41 | for (( i=0; prevdirsize' > $filename.xml 57 | 58 | for (( i=0; i' >> $filename.xml; 60 | done 61 | 62 | echo -e '' >> $filename.xml; 63 | 64 | cd .. 65 | 66 | refdens=$(getScaleForDensity $density) 67 | 68 | echo reference density is $refdens 69 | 70 | for i in $(echo $targetDensities | sed "s/,/ /g") 71 | do 72 | if [ $i == $density ]; then 73 | continue; 74 | fi 75 | mkdir drawable-$i; cd drawable-$i; 76 | curdens=$(getScaleForDensity $i) 77 | echo current density is $curdens 78 | rp=`echo $curdens $refdens | awk '{printf "%.2f", $1/$2}'` 79 | rp=`echo $rp 100 | awk '{printf "%d", $1*$2}'` 80 | echo rp is $rp in float n is $n; 81 | for (( j=0; j 2 | 3 | 4 | 5 | 6 | Android Resource Tools 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 18 |
19 |
20 |
21 | 22 |
23 |
24 | 25 | 28 |
29 |
30 | 36 |
37 |
38 |
39 | Convert and Download .zip 40 |
41 |
42 |

NOTE: GIFs with frames > 50 or size > 200 kB may take some time to convert


43 | 46 |
47 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /GifToAnimationDrawable/upload.php: -------------------------------------------------------------------------------- 1 | Home"; 50 | // Check if a file was uploaded 51 | if(!isset($_FILES["$fileNameFromForm"])){ 52 | echo"No file uploaded".$GO_HOME; 53 | return; 54 | } 55 | 56 | // Check if it was an image 57 | $imageFileType=pathinfo($target_file,PATHINFO_EXTENSION); 58 | $check=getimagesize($_FILES["$fileNameFromForm"]["tmp_name"]); 59 | if($check==false){ 60 | echo"File is not an image".$GO_HOME; 61 | return; 62 | } 63 | 64 | // Allow certain file formats 65 | if($imageFileType!="gif"){ 66 | echo"Sorry, only GIF files are allowed".$GO_HOME; 67 | return; 68 | } 69 | 70 | if(move_uploaded_file($_FILES["$fileNameFromForm"]["tmp_name"],$target_file)){ 71 | generate_drawable($target_file,$targetuuid,$drawableDensity,$drawableName,$targetDensities); 72 | }else{ 73 | echo"An error occurred".$GO_HOME; 74 | } 75 | ?> 76 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AndroidResourceTools 2 | 3 | ##GifToAnimationDrawable 4 | 5 | Converts Animated GIFs to AnimationDrawables. 6 | 7 | P.S. Web front-end for this functionality can be found at http://tusharonweb.in/AndroidResourceTools/GifToAnimationDrawable 8 | 9 | ###Pre-Requisites 10 | 11 | - Install imagemagick 12 | - For OS X - Install package from http://cactuslab.com/imagemagick/ 13 | - For ubuntu - From terminal, run "sudo apt-get install imagemagick" 14 | 15 | ###Usage 16 | 17 |
./gif2animdraw.sh   <path_to_gif>   <source_density>   <comma_seperated_target_densities_without_space>
18 | 
19 | Example ./gif2animdraw.sh   giphy.gif   hdpi   ldpi,xhdpi,xxhdpi
20 | -------------------------------------------------------------------------------- /css/studio.css: -------------------------------------------------------------------------------- 1 | .offscreen{position:absolute;left:-10000px;top:0;}html,body{font-family:roboto,sans-serif;font-size:16px;font-weight:400;line-height:20px;background-color:#f0f0f0;color:#333;-webkit-font-smoothing:antialiased;}strong{font-weight:600;}a{color:#09c;}#main-container{background-color:#f8f8f8;width:800px;margin:24px auto;border-radius:2px;}#page-header,#page-links,#inputs,#outputs,#footer{padding:24px;}#page-header{padding-bottom:0;}#page-header a[href="index.html"]{text-transform:uppercase;font-weight:600;font-size:13px;line-height:13px;color:#fff;background-color:#33b5e5;padding:8px 12px 8px 24px;margin-left:-24px;margin-bottom:16px;display:inline-block;text-decoration:none;position:relative;}#page-header a[href="index.html"]:before{content:'';background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OEY0QThENzBCQzA0MTFFMTkzQzNCNUU4MkRENzA1NEEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OEY0QThENzFCQzA0MTFFMTkzQzNCNUU4MkRENzA1NEEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4RjRBOEQ2RUJDMDQxMUUxOTNDM0I1RTgyREQ3MDU0QSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4RjRBOEQ2RkJDMDQxMUUxOTNDM0I1RTgyREQ3MDU0QSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqEMv2QAAACFSURBVHjaYvz//z8DJYCJgUJAUwMEgfgkEH8CYn+cqkBhgAOX/YeA1UDMhksdLs2KQPwGiN8CsREeS3AaMB1qeyM+zfgMeADEf4FYj5ABuAJxPTSAYwlGAw6TZYD4GRB/BGJLcrwAwvnQcNgOxFzkGMAHxHuA+AUQB+BSxzi88wJRACDAAD2/ZsRomMY9AAAAAElFTkSuQmCC);position:absolute;left:8px;top:7px;width:16px;height:16px;}#page-header h1{font-weight:100;font-size:36px;line-height:36px;color:#888;}#page-header h1 a{text-decoration:none;color:#fff;}#page-intro{margin-top:16px;margin-bottom:16px;color:#888;}#page-header p,#page-links p{width:40em;}#page-header ul+h2,#page-links ul+h2{margin-top:24px;}h2{color:#000;font-weight:600;font-size:16px;line-height:24px;margin-bottom:8px;text-transform:uppercase;}h2 span{color:#888;font-weight:400;}#breadcrumb{color:#888;font-weight:400;font-size:16px;line-height:20px;margin-bottom:8px;}#breadcrumb a{text-decoration:none;color:#888;}#breadcrumb em{color:#09c;font-weight:600;}#page-links ul{margin:12px 24px;}#page-links li{font-size:16px;line-height:24px;margin-bottom:4px;font-weight:600;}#page-linsk li.deprecated{color:#ccc;}#page-links li.deprecated a{color:#ccc;}#outputs,#footer{border-top:4px solid #f0f0f0;}#inputs h3,#outputs h3{font-weight:600;font-size:16px;line-height:24px;margin-bottom:8px;}.form-field-outer{margin-bottom:24px;}.form-field-outer>label{font-weight:400;display:inline-block;vertical-align:top;margin-top:6px;width:148px;margin-right:12px;color:#111;}.form-field-outer>label .form-field-help-text{font-weight:400;font-size:13px;line-height:13px;color:#aaa;}.form-field-outer[disabled]{opacity:.2;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;-webkit-filter:grayscale(100%);filter:url("data:image/svg+xml;utf8,#grayscale");filter:gray;}.form-field-container{display:inline-block;vertical-align:top;}.form-text,.ui-autocomplete-input{margin:0;padding:3px 6px;border:0;border-bottom:1px solid #33b5e5;background-color:transparent;}.ui-autocomplete{z-index:3!important;}.form-image-hidden-file-field{position:absolute;left:-10000px;opacity:0;}.form-image-clipart-attribution{font-weight:400;font-size:13px;line-height:13px;color:#888;margin-top:4px;}.form-image-clipart-filter{background-color:#ccc;color:#fff;font-size:13px;width:230px;border:0;padding:4px 10px;}.form-image-clipart-filter:focus{background-color:#09c;outline:0;}.form-image-clipart-filter::-webkit-input-placeholder{color:rgba(255,255,255,0.5);font-style:italic;}.form-image-clipart-list{background-color:#fff;border:1px solid #ccc;margin-top:-1px;width:250px;height:200px;overflow-y:scroll;}.form-image-clipart-list img{border:3px solid #fff;width:48px;height:48px;cursor:pointer;}.form-image-clipart-list img:hover{border:3px solid #ccc;}.form-image-clipart-list img.selected{border:3px solid #33b5e5;}.form-image-preview{background-color:#fff;display:inline-block;max-height:100px;max-width:250px;border:1px solid #ccc;}.form-color{width:36px;height:36px;border-radius:2px;cursor:pointer;}.form-range{margin-top:6px;width:200px;}.form-range{display:inline-block;vertical-align:bottom;-webkit-appearance:none;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MTMyM0U2NjVCQkZGMTFFMTkzQzNCNUU4MkRENzA1NEEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MTMyM0U2NjZCQkZGMTFFMTkzQzNCNUU4MkRENzA1NEEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDoxMzIzRTY2M0JCRkYxMUUxOTNDM0I1RTgyREQ3MDU0QSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDoxMzIzRTY2NEJCRkYxMUUxOTNDM0I1RTgyREQ3MDU0QSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PnAuiP8AAAAOSURBVHjaYjDe+hQgwAAC7AHOIjCyxwAAAABJRU5ErkJggg==);background-size:1px;background-repeat:repeat-x;background-position:0 50%;background-color:transparent;}.form-range::-webkit-slider-thumb{-webkit-appearance:none;background:#33b5e5;width:16px;height:16px;border-radius:8px;}.form-range::-webkit-slider-thumb:active{background:#09c;}.form-range-text{display:inline-block;vertical-align:bottom;padding-left:6px;font-size:13px;line-height:13px;}.form-field-drop-target.drag-hover{outline:2px solid #33b5e5;outline-offset:5px;}.form-field-buttonset input[type=radio],.form-field-checkbutton{display:none;}.form-button,button{margin:0;color:#fff;display:inline-block;vertical-align:top;text-decoration:none;display:inline-block;text-transform:uppercase;font-family:Roboto;font-size:13px;line-height:13px;font-weight:400;padding:8px 16px;background-color:#33b5e5;border:1px solid #33b5e5;cursor:pointer;}.form-button[disabled],button[disabled]{background-color:transparent;border-color:#000;color:#000;opacity:.2;cursor:default;}.form-button:not([disabled]):hover,button:not([disabled]):hover{background-color:#09c;border-color:#09c;}.form-button:not([disabled]):active,button:not([disabled]):active{background-color:#069;border-color:#069;}.form-field-buttonset label,.form-field-checkbutton+label{display:inline-block;text-transform:uppercase;font-size:13px;line-height:13px;font-weight:600;padding:8px 16px;border-top:1px solid #ccc;border-bottom:1px solid #ccc;cursor:pointer;}.form-field-checkbutton+label{border-left:1px solid #ccc;border-right:1px solid #ccc;}.form-field-buttonset label:nth-last-of-type(1){border-right:1px solid #ccc;}.form-field-buttonset label:nth-of-type(1){border-left:1px solid #ccc;}.form-field-buttonset input:checked+label,.form-field-checkbutton:checked+label{background-color:#33b5e5;color:#fff;border-color:#33b5e5;}.form-subform{margin-top:24px;}.form-subform .form-field-outer{margin-bottom:6px;}.form-subform .form-field-outer>label{display:inline-block;padding-right:12px;width:auto;font-weight:400;font-size:13px;color:#888;text-transform:uppercase;}.out-image-group{display:inline-block;background-color:#fff;padding:8px 10px;margin-right:12px;margin-bottom:12px;border-radius:2px;}.out-image-group>.label{font-weight:600;margin-bottom:6px;}.out-image-block{display:inline-block;vertical-align:top;margin-right:6px;margin-bottom:6px;font-size:13px;line-height:13px;text-align:center;}.out-image{border:1px solid #ccc;margin-top:4px;display:inline-block;position:relative;}.out-image-group.dark{background-color:rgba(0,0,0,0.9);}.out-image-group.dark .label{color:#ccc;}.out-image-group.dark .out-image{border:1px solid #555;}.out-image-group.dark.half{background-color:rgba(0,0,0,0.5);}#outputs hr{border:0;margin:4px 0;height:1px;}.browser-unsupported-note{padding:6px 12px;border:0!important;box-shadow:0 3px 15px rgba(0,0,0,0.25);-webkit-box-shadow:0 3px 15px rgba(0,0,0,0.25);-moz-box-shadow:0 3px 15px rgba(0,0,0,0.25);}.colorpicker{z-index:10000;} -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Android Resource Tools 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 17 | 23 | 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /js/asset-studio.pack.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2010 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | var Base=function(){};Base.extend=function(b,e){var f=Base.prototype.extend;Base._prototyping=true;var d=new this;f.call(d,b);delete Base._prototyping;var c=d.constructor;var a=d.constructor=function(){if(!Base._prototyping){if(this._constructing||this.constructor==a){this._constructing=true;c.apply(this,arguments);delete this._constructing}else{if(arguments[0]!=null){return(arguments[0].extend||f).call(arguments[0],d)}}}};a.ancestor=this;a.extend=this.extend;a.forEach=this.forEach;a.implement=this.implement;a.prototype=d;a.toString=this.toString;a.valueOf=function(g){return(g=="object")?a:c.valueOf()};f.call(a,e);if(typeof a.init=="function"){a.init()}return a};Base.prototype={extend:function(b,h){if(arguments.length>1){var e=this[b];if(e&&(typeof h=="function")&&(!e.valueOf||e.valueOf()!=h.valueOf())&&/\bbase\b/.test(h)){var a=h.valueOf();h=function(){var l=this.base||Base.prototype.base;this.base=e;var i=a.apply(this,arguments);this.base=l;return i};h.valueOf=function(i){return(i=="object")?h:a};h.toString=Base.toString}this[b]=h}else{if(b){var g=Base.prototype.extend;if(!Base._prototyping&&typeof this!="function"){g=this.extend||g}var d={toSource:null};var f=["constructor","toString","valueOf"];var c=Base._prototyping?0:1;while(j=f[c++]){if(b[j]!=d[j]){g.call(this,j,b[j])}}for(var j in b){if(!d[j]){g.call(this,j,b[j])}}}}return this},base:function(){}};Base=Base.extend({constructor:function(){this.extend(arguments[0])}},{ancestor:Object,version:"1.1",forEach:function(a,d,c){for(var b in a){if(this.prototype[b]===undefined){d.call(c,a[b],b,a)}}},implement:function(){for(var a=0;a=0;--i){this.y2[i]=this.y2[i]*this.y2[i+1]+this.u[i]}}SplineInterpolator.prototype.interpolate=function(x){var n=this.ya.length;var klo=0;var khi=n-1;while(khi-klo>1){var k=(khi+klo)>>1;if(this.xa[k]>x){khi=k}else{klo=k}}var h=this.xa[khi]-this.xa[klo];var a=(this.xa[khi]-x)/h;var b=(x-this.xa[klo])/h;return a*this.ya[klo]+b*this.ya[khi]+((a*a*a-a)*this.y2[klo]+(b*b*b-b)*this.y2[khi])*(h*h)/6};var Texture=(function(){Texture.fromElement=function(element){var texture=new Texture(0,0,gl.RGBA,gl.UNSIGNED_BYTE);texture.loadContentsOf(element);return texture};function Texture(width,height,format,type){this.gl=gl;this.id=gl.createTexture();this.width=width;this.height=height;this.format=format;this.type=type;gl.bindTexture(gl.TEXTURE_2D,this.id);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.LINEAR);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.CLAMP_TO_EDGE);gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.CLAMP_TO_EDGE);if(width&&height){gl.texImage2D(gl.TEXTURE_2D,0,this.format,width,height,0,this.format,this.type,null)}}Texture.prototype.loadContentsOf=function(element){this.width=element.width||element.videoWidth;this.height=element.height||element.videoHeight;gl.bindTexture(gl.TEXTURE_2D,this.id);gl.texImage2D(gl.TEXTURE_2D,0,this.format,this.format,this.type,element)};Texture.prototype.initFromBytes=function(width,height,data){this.width=width;this.height=height;this.format=gl.RGBA;this.type=gl.UNSIGNED_BYTE;gl.bindTexture(gl.TEXTURE_2D,this.id);gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,width,height,0,gl.RGBA,this.type,new Uint8Array(data))};Texture.prototype.destroy=function(){gl.deleteTexture(this.id);this.id=null};Texture.prototype.use=function(unit){gl.activeTexture(gl.TEXTURE0+(unit||0));gl.bindTexture(gl.TEXTURE_2D,this.id)};Texture.prototype.unuse=function(unit){gl.activeTexture(gl.TEXTURE0+(unit||0));gl.bindTexture(gl.TEXTURE_2D,null)};Texture.prototype.ensureFormat=function(width,height,format,type){if(arguments.length==1){var texture=arguments[0];width=texture.width;height=texture.height;format=texture.format;type=texture.type}if(width!=this.width||height!=this.height||format!=this.format||type!=this.type){this.width=width;this.height=height;this.format=format;this.type=type;gl.bindTexture(gl.TEXTURE_2D,this.id);gl.texImage2D(gl.TEXTURE_2D,0,this.format,width,height,0,this.format,this.type,null)}};Texture.prototype.drawTo=function(callback){gl.framebuffer=gl.framebuffer||gl.createFramebuffer();gl.bindFramebuffer(gl.FRAMEBUFFER,gl.framebuffer);gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,this.id,0);if(gl.checkFramebufferStatus(gl.FRAMEBUFFER)!==gl.FRAMEBUFFER_COMPLETE){throw new Error("incomplete framebuffer")}gl.viewport(0,0,this.width,this.height);callback();gl.bindFramebuffer(gl.FRAMEBUFFER,null)};var canvas=null;function getCanvas(texture){if(canvas==null){canvas=document.createElement("canvas")}canvas.width=texture.width;canvas.height=texture.height;var c=canvas.getContext("2d");c.clearRect(0,0,canvas.width,canvas.height);return c}Texture.prototype.fillUsingCanvas=function(callback){callback(getCanvas(this));this.format=gl.RGBA;this.type=gl.UNSIGNED_BYTE;gl.bindTexture(gl.TEXTURE_2D,this.id);gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl.RGBA,gl.UNSIGNED_BYTE,canvas);return this};Texture.prototype.toImage=function(image){this.use();Shader.getDefaultShader().drawRect();var size=this.width*this.height*4;var pixels=new Uint8Array(size);var c=getCanvas(this);var data=c.createImageData(this.width,this.height);gl.readPixels(0,0,this.width,this.height,gl.RGBA,gl.UNSIGNED_BYTE,pixels);for(var i=0;i 0.0) { color.rgb = (color.rgb - 0.5) / (1.0 - contrast) + 0.5; } else { color.rgb = (color.rgb - 0.5) * (1.0 + contrast) + 0.5; } gl_FragColor = color; } ");simpleShader.call(this,gl.brightnessContrast,{brightness:clamp(-1,brightness,1),contrast:clamp(-1,contrast,1)});return this}function splineInterpolate(points){var interpolator=new SplineInterpolator(points);var array=[];for(var i=0;i<256;i++){array.push(clamp(0,Math.floor(interpolator.interpolate(i/255)*256),255))}return array}function curves(red,green,blue){red=splineInterpolate(red);if(arguments.length==1){green=blue=red}else{green=splineInterpolate(green);blue=splineInterpolate(blue)}var array=[];for(var i=0;i<256;i++){array.splice(array.length,0,red[i],green[i],blue[i],255)}this._.extraTexture.initFromBytes(256,1,array);this._.extraTexture.use(1);gl.curves=gl.curves||new Shader(null," uniform sampler2D texture; uniform sampler2D map; varying vec2 texCoord; void main() { vec4 color = texture2D(texture, texCoord); color.r = texture2D(map, vec2(color.r)).r; color.g = texture2D(map, vec2(color.g)).g; color.b = texture2D(map, vec2(color.b)).b; gl_FragColor = color; } ");gl.curves.textures({map:1});simpleShader.call(this,gl.curves,{});return this}function denoise(exponent){gl.denoise=gl.denoise||new Shader(null," uniform sampler2D texture; uniform float exponent; uniform float strength; uniform vec2 texSize; varying vec2 texCoord; void main() { vec4 center = texture2D(texture, texCoord); vec4 color = vec4(0.0); float total = 0.0; for (float x = -4.0; x <= 4.0; x += 1.0) { for (float y = -4.0; y <= 4.0; y += 1.0) { vec4 sample = texture2D(texture, texCoord + vec2(x, y) / texSize); float weight = 1.0 - abs(dot(sample.rgb - center.rgb, vec3(0.25))); weight = pow(weight, exponent); color += sample * weight; total += weight; } } gl_FragColor = color / total; } ");for(var i=0;i<2;i++){simpleShader.call(this,gl.denoise,{exponent:Math.max(0,exponent),texSize:[this.width,this.height]})}return this}function hueSaturation(hue,saturation){gl.hueSaturation=gl.hueSaturation||new Shader(null," uniform sampler2D texture; uniform float hue; uniform float saturation; varying vec2 texCoord; void main() { vec4 color = texture2D(texture, texCoord); /* hue adjustment, wolfram alpha: RotationTransform[angle, {1, 1, 1}][{x, y, z}] */ float angle = hue * 3.14159265; float s = sin(angle), c = cos(angle); vec3 weights = (vec3(2.0 * c, -sqrt(3.0) * s - c, sqrt(3.0) * s - c) + 1.0) / 3.0; float len = length(color.rgb); color.rgb = vec3( dot(color.rgb, weights.xyz), dot(color.rgb, weights.zxy), dot(color.rgb, weights.yzx) ); /* saturation adjustment */ float average = (color.r + color.g + color.b) / 3.0; if (saturation > 0.0) { color.rgb += (average - color.rgb) * (1.0 - 1.0 / (1.001 - saturation)); } else { color.rgb += (average - color.rgb) * (-saturation); } gl_FragColor = color; } ");simpleShader.call(this,gl.hueSaturation,{hue:clamp(-1,hue,1),saturation:clamp(-1,saturation,1)});return this}function noise(amount){gl.noise=gl.noise||new Shader(null," uniform sampler2D texture; uniform float amount; varying vec2 texCoord; float rand(vec2 co) { return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); } void main() { vec4 color = texture2D(texture, texCoord); float diff = (rand(texCoord) - 0.5) * amount; color.r += diff; color.g += diff; color.b += diff; gl_FragColor = color; } ");simpleShader.call(this,gl.noise,{amount:clamp(0,amount,1)});return this}function sepia(amount){gl.sepia=gl.sepia||new Shader(null," uniform sampler2D texture; uniform float amount; varying vec2 texCoord; void main() { vec4 color = texture2D(texture, texCoord); float r = color.r; float g = color.g; float b = color.b; color.r = min(1.0, (r * (1.0 - (0.607 * amount))) + (g * (0.769 * amount)) + (b * (0.189 * amount))); color.g = min(1.0, (r * 0.349 * amount) + (g * (1.0 - (0.314 * amount))) + (b * 0.168 * amount)); color.b = min(1.0, (r * 0.272 * amount) + (g * 0.534 * amount) + (b * (1.0 - (0.869 * amount)))); gl_FragColor = color; } ");simpleShader.call(this,gl.sepia,{amount:clamp(0,amount,1)});return this}function unsharpMask(radius,strength){gl.unsharpMask=gl.unsharpMask||new Shader(null," uniform sampler2D blurredTexture; uniform sampler2D originalTexture; uniform float strength; uniform float threshold; varying vec2 texCoord; void main() { vec4 blurred = texture2D(blurredTexture, texCoord); vec4 original = texture2D(originalTexture, texCoord); gl_FragColor = mix(blurred, original, 1.0 + strength); } ");this._.extraTexture.ensureFormat(this._.texture);this._.texture.use();this._.extraTexture.drawTo(function(){Shader.getDefaultShader().drawRect()});this._.extraTexture.use(1);this.triangleBlur(radius);gl.unsharpMask.textures({originalTexture:1});simpleShader.call(this,gl.unsharpMask,{strength:strength});this._.extraTexture.unuse(1);return this}function vibrance(amount){gl.vibrance=gl.vibrance||new Shader(null," uniform sampler2D texture; uniform float amount; varying vec2 texCoord; void main() { vec4 color = texture2D(texture, texCoord); float average = (color.r + color.g + color.b) / 3.0; float mx = max(color.r, max(color.g, color.b)); float amt = (mx - average) * (-amount * 3.0); color.rgb = mix(color.rgb, vec3(mx), amt); gl_FragColor = color; } ");simpleShader.call(this,gl.vibrance,{amount:clamp(-1,amount,1)});return this}function vignette(size,amount){gl.vignette=gl.vignette||new Shader(null," uniform sampler2D texture; uniform float size; uniform float amount; varying vec2 texCoord; void main() { vec4 color = texture2D(texture, texCoord); float dist = distance(texCoord, vec2(0.5, 0.5)); color.rgb *= smoothstep(0.8, size * 0.799, dist * (amount + size)); gl_FragColor = color; } ");simpleShader.call(this,gl.vignette,{size:clamp(0,size,1),amount:clamp(0,amount,1)});return this}function lensBlur(radius,brightness,angle){gl.lensBlurPrePass=gl.lensBlurPrePass||new Shader(null," uniform sampler2D texture; uniform float power; varying vec2 texCoord; void main() { vec4 color = texture2D(texture, texCoord); color = pow(color, vec4(power)); gl_FragColor = vec4(color); } ");var common=" uniform sampler2D texture0; uniform sampler2D texture1; uniform vec2 delta0; uniform vec2 delta1; uniform float power; varying vec2 texCoord; "+randomShaderFunc+" vec4 sample(vec2 delta) { /* randomize the lookup values to hide the fixed number of samples */ float offset = random(vec3(delta, 151.7182), 0.0); vec4 color = vec4(0.0); float total = 0.0; for (float t = 0.0; t <= 30.0; t++) { float percent = (t + offset) / 30.0; color += texture2D(texture0, texCoord + delta * percent); total += 1.0; } return color / total; } ";gl.lensBlur0=gl.lensBlur0||new Shader(null,common+" void main() { gl_FragColor = sample(delta0); } ");gl.lensBlur1=gl.lensBlur1||new Shader(null,common+" void main() { gl_FragColor = (sample(delta0) + sample(delta1)) * 0.5; } ");gl.lensBlur2=gl.lensBlur2||new Shader(null,common+" void main() { vec4 color = (sample(delta0) + 2.0 * texture2D(texture1, texCoord)) / 3.0; gl_FragColor = pow(color, vec4(power)); } ").textures({texture1:1});var dir=[];for(var i=0;i<3;i++){var a=angle+i*Math.PI*2/3;dir.push([radius*Math.sin(a)/this.width,radius*Math.cos(a)/this.height])}var power=Math.pow(10,clamp(-1,brightness,1));simpleShader.call(this,gl.lensBlurPrePass,{power:power});this._.extraTexture.ensureFormat(this._.texture);simpleShader.call(this,gl.lensBlur0,{delta0:dir[0]},this._.texture,this._.extraTexture);simpleShader.call(this,gl.lensBlur1,{delta0:dir[1],delta1:dir[2]},this._.extraTexture,this._.extraTexture);simpleShader.call(this,gl.lensBlur0,{delta0:dir[1]});this._.extraTexture.use(1);simpleShader.call(this,gl.lensBlur2,{power:1/power,delta0:dir[2]});return this}function tiltShift(startX,startY,endX,endY,blurRadius,gradientRadius){gl.tiltShift=gl.tiltShift||new Shader(null," uniform sampler2D texture; uniform float blurRadius; uniform float gradientRadius; uniform vec2 start; uniform vec2 end; uniform vec2 delta; uniform vec2 texSize; varying vec2 texCoord; "+randomShaderFunc+" void main() { vec4 color = vec4(0.0); float total = 0.0; /* randomize the lookup values to hide the fixed number of samples */ float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); vec2 normal = normalize(vec2(start.y - end.y, end.x - start.x)); float radius = smoothstep(0.0, 1.0, abs(dot(texCoord * texSize - start, normal)) / gradientRadius) * blurRadius; for (float t = -30.0; t <= 30.0; t++) { float percent = (t + offset - 0.5) / 30.0; float weight = 1.0 - abs(percent); vec4 sample = texture2D(texture, texCoord + delta / texSize * percent * radius); /* switch to pre-multiplied alpha to correctly blur transparent images */ sample.rgb *= sample.a; color += sample * weight; total += weight; } gl_FragColor = color / total; /* switch back from pre-multiplied alpha */ gl_FragColor.rgb /= gl_FragColor.a + 0.00001; } ");var dx=endX-startX;var dy=endY-startY;var d=Math.sqrt(dx*dx+dy*dy);simpleShader.call(this,gl.tiltShift,{blurRadius:blurRadius,gradientRadius:gradientRadius,start:[startX,startY],end:[endX,endY],delta:[dx/d,dy/d],texSize:[this.width,this.height]});simpleShader.call(this,gl.tiltShift,{blurRadius:blurRadius,gradientRadius:gradientRadius,start:[startX,startY],end:[endX,endY],delta:[-dy/d,dx/d],texSize:[this.width,this.height]});return this}function triangleBlur(radius){gl.triangleBlur=gl.triangleBlur||new Shader(null," uniform sampler2D texture; uniform vec2 delta; varying vec2 texCoord; "+randomShaderFunc+" void main() { vec4 color = vec4(0.0); float total = 0.0; /* randomize the lookup values to hide the fixed number of samples */ float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); for (float t = -30.0; t <= 30.0; t++) { float percent = (t + offset - 0.5) / 30.0; float weight = 1.0 - abs(percent); vec4 sample = texture2D(texture, texCoord + delta * percent); /* switch to pre-multiplied alpha to correctly blur transparent images */ sample.rgb *= sample.a; color += sample * weight; total += weight; } gl_FragColor = color / total; /* switch back from pre-multiplied alpha */ gl_FragColor.rgb /= gl_FragColor.a + 0.00001; } ");simpleShader.call(this,gl.triangleBlur,{delta:[radius/this.width,0]});simpleShader.call(this,gl.triangleBlur,{delta:[0,radius/this.height]});return this}function zoomBlur(centerX,centerY,strength){gl.zoomBlur=gl.zoomBlur||new Shader(null," uniform sampler2D texture; uniform vec2 center; uniform float strength; uniform vec2 texSize; varying vec2 texCoord; "+randomShaderFunc+" void main() { vec4 color = vec4(0.0); float total = 0.0; vec2 toCenter = center - texCoord * texSize; /* randomize the lookup values to hide the fixed number of samples */ float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); for (float t = 0.0; t <= 40.0; t++) { float percent = (t + offset) / 40.0; float weight = 4.0 * (percent - percent * percent); vec4 sample = texture2D(texture, texCoord + toCenter * percent * strength / texSize); /* switch to pre-multiplied alpha to correctly blur transparent images */ sample.rgb *= sample.a; color += sample * weight; total += weight; } gl_FragColor = color / total; /* switch back from pre-multiplied alpha */ gl_FragColor.rgb /= gl_FragColor.a + 0.00001; } ");simpleShader.call(this,gl.zoomBlur,{center:[centerX,centerY],strength:strength,texSize:[this.width,this.height]});return this}function colorHalftone(centerX,centerY,angle,size){gl.colorHalftone=gl.colorHalftone||new Shader(null," uniform sampler2D texture; uniform vec2 center; uniform float angle; uniform float scale; uniform vec2 texSize; varying vec2 texCoord; float pattern(float angle) { float s = sin(angle), c = cos(angle); vec2 tex = texCoord * texSize - center; vec2 point = vec2( c * tex.x - s * tex.y, s * tex.x + c * tex.y ) * scale; return (sin(point.x) * sin(point.y)) * 4.0; } void main() { vec4 color = texture2D(texture, texCoord); vec3 cmy = 1.0 - color.rgb; float k = min(cmy.x, min(cmy.y, cmy.z)); cmy = (cmy - k) / (1.0 - k); cmy = clamp(cmy * 10.0 - 3.0 + vec3(pattern(angle + 0.26179), pattern(angle + 1.30899), pattern(angle)), 0.0, 1.0); k = clamp(k * 10.0 - 5.0 + pattern(angle + 0.78539), 0.0, 1.0); gl_FragColor = vec4(1.0 - cmy - k, color.a); } ");simpleShader.call(this,gl.colorHalftone,{center:[centerX,centerY],angle:angle,scale:Math.PI/size,texSize:[this.width,this.height]});return this}function dotScreen(centerX,centerY,angle,size){gl.dotScreen=gl.dotScreen||new Shader(null," uniform sampler2D texture; uniform vec2 center; uniform float angle; uniform float scale; uniform vec2 texSize; varying vec2 texCoord; float pattern() { float s = sin(angle), c = cos(angle); vec2 tex = texCoord * texSize - center; vec2 point = vec2( c * tex.x - s * tex.y, s * tex.x + c * tex.y ) * scale; return (sin(point.x) * sin(point.y)) * 4.0; } void main() { vec4 color = texture2D(texture, texCoord); float average = (color.r + color.g + color.b) / 3.0; gl_FragColor = vec4(vec3(average * 10.0 - 5.0 + pattern()), color.a); } ");simpleShader.call(this,gl.dotScreen,{center:[centerX,centerY],angle:angle,scale:Math.PI/size,texSize:[this.width,this.height]});return this}function edgeWork(radius){gl.edgeWork1=gl.edgeWork1||new Shader(null," uniform sampler2D texture; uniform vec2 delta; varying vec2 texCoord; "+randomShaderFunc+" void main() { vec2 color = vec2(0.0); vec2 total = vec2(0.0); /* randomize the lookup values to hide the fixed number of samples */ float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); for (float t = -30.0; t <= 30.0; t++) { float percent = (t + offset - 0.5) / 30.0; float weight = 1.0 - abs(percent); vec3 sample = texture2D(texture, texCoord + delta * percent).rgb; float average = (sample.r + sample.g + sample.b) / 3.0; color.x += average * weight; total.x += weight; if (abs(t) < 15.0) { weight = weight * 2.0 - 1.0; color.y += average * weight; total.y += weight; } } gl_FragColor = vec4(color / total, 0.0, 1.0); } ");gl.edgeWork2=gl.edgeWork2||new Shader(null," uniform sampler2D texture; uniform vec2 delta; varying vec2 texCoord; "+randomShaderFunc+" void main() { vec2 color = vec2(0.0); vec2 total = vec2(0.0); /* randomize the lookup values to hide the fixed number of samples */ float offset = random(vec3(12.9898, 78.233, 151.7182), 0.0); for (float t = -30.0; t <= 30.0; t++) { float percent = (t + offset - 0.5) / 30.0; float weight = 1.0 - abs(percent); vec2 sample = texture2D(texture, texCoord + delta * percent).xy; color.x += sample.x * weight; total.x += weight; if (abs(t) < 15.0) { weight = weight * 2.0 - 1.0; color.y += sample.y * weight; total.y += weight; } } float c = clamp(10000.0 * (color.y / total.y - color.x / total.x) + 0.5, 0.0, 1.0); gl_FragColor = vec4(c, c, c, 1.0); } ");simpleShader.call(this,gl.edgeWork1,{delta:[radius/this.width,0]});simpleShader.call(this,gl.edgeWork2,{delta:[0,radius/this.height]});return this}function hexagonalPixelate(centerX,centerY,scale){gl.hexagonalPixelate=gl.hexagonalPixelate||new Shader(null," uniform sampler2D texture; uniform vec2 center; uniform float scale; uniform vec2 texSize; varying vec2 texCoord; void main() { vec2 tex = (texCoord * texSize - center) / scale; tex.y /= 0.866025404; tex.x -= tex.y * 0.5; vec2 a; if (tex.x + tex.y - floor(tex.x) - floor(tex.y) < 1.0) a = vec2(floor(tex.x), floor(tex.y)); else a = vec2(ceil(tex.x), ceil(tex.y)); vec2 b = vec2(ceil(tex.x), floor(tex.y)); vec2 c = vec2(floor(tex.x), ceil(tex.y)); vec3 TEX = vec3(tex.x, tex.y, 1.0 - tex.x - tex.y); vec3 A = vec3(a.x, a.y, 1.0 - a.x - a.y); vec3 B = vec3(b.x, b.y, 1.0 - b.x - b.y); vec3 C = vec3(c.x, c.y, 1.0 - c.x - c.y); float alen = length(TEX - A); float blen = length(TEX - B); float clen = length(TEX - C); vec2 choice; if (alen < blen) { if (alen < clen) choice = a; else choice = c; } else { if (blen < clen) choice = b; else choice = c; } choice.x += choice.y * 0.5; choice.y *= 0.866025404; choice *= scale / texSize; gl_FragColor = texture2D(texture, choice + center / texSize); } ");simpleShader.call(this,gl.hexagonalPixelate,{center:[centerX,centerY],scale:scale,texSize:[this.width,this.height]});return this}function ink(strength){gl.ink=gl.ink||new Shader(null," uniform sampler2D texture; uniform float strength; uniform vec2 texSize; varying vec2 texCoord; void main() { vec2 dx = vec2(1.0 / texSize.x, 0.0); vec2 dy = vec2(0.0, 1.0 / texSize.y); vec4 color = texture2D(texture, texCoord); float bigTotal = 0.0; float smallTotal = 0.0; vec3 bigAverage = vec3(0.0); vec3 smallAverage = vec3(0.0); for (float x = -2.0; x <= 2.0; x += 1.0) { for (float y = -2.0; y <= 2.0; y += 1.0) { vec3 sample = texture2D(texture, texCoord + dx * x + dy * y).rgb; bigAverage += sample; bigTotal += 1.0; if (abs(x) + abs(y) < 2.0) { smallAverage += sample; smallTotal += 1.0; } } } vec3 edge = max(vec3(0.0), bigAverage / bigTotal - smallAverage / smallTotal); gl_FragColor = vec4(color.rgb - dot(edge, edge) * strength * 100000.0, color.a); } ");simpleShader.call(this,gl.ink,{strength:strength*strength*strength*strength*strength,texSize:[this.width,this.height]});return this}function bulgePinch(centerX,centerY,radius,strength){gl.bulgePinch=gl.bulgePinch||warpShader(" uniform float radius; uniform float strength; uniform vec2 center; "," coord -= center; float distance = length(coord); if (distance < radius) { float percent = distance / radius; if (strength > 0.0) { coord *= mix(1.0, smoothstep(0.0, radius / distance, percent), strength * 0.75); } else { coord *= mix(1.0, pow(percent, 1.0 + strength * 0.75) * radius / distance, 1.0 - percent); } } coord += center; ");simpleShader.call(this,gl.bulgePinch,{radius:radius,strength:clamp(-1,strength,1),center:[centerX,centerY],texSize:[this.width,this.height]});return this}function matrixWarp(matrix,inverse,useTextureSpace){gl.matrixWarp=gl.matrixWarp||warpShader(" uniform mat3 matrix; uniform bool useTextureSpace; "," if (useTextureSpace) coord = coord / texSize * 2.0 - 1.0; vec3 warp = matrix * vec3(coord, 1.0); coord = warp.xy / warp.z; if (useTextureSpace) coord = (coord * 0.5 + 0.5) * texSize; ");matrix=Array.prototype.concat.apply([],matrix);if(matrix.length==4){matrix=[matrix[0],matrix[1],0,matrix[2],matrix[3],0,0,0,1]}else{if(matrix.length!=9){throw"can only warp with 2x2 or 3x3 matrix"}}simpleShader.call(this,gl.matrixWarp,{matrix:inverse?getInverse(matrix):matrix,texSize:[this.width,this.height],useTextureSpace:useTextureSpace|0});return this}function perspective(before,after){var a=getSquareToQuad.apply(null,after);var b=getSquareToQuad.apply(null,before);var c=multiply(getInverse(a),b);return this.matrixWarp(c)}function swirl(centerX,centerY,radius,angle){gl.swirl=gl.swirl||warpShader(" uniform float radius; uniform float angle; uniform vec2 center; "," coord -= center; float distance = length(coord); if (distance < radius) { float percent = (radius - distance) / radius; float theta = percent * percent * angle; float s = sin(theta); float c = cos(theta); coord = vec2( coord.x * c - coord.y * s, coord.x * s + coord.y * c ); } coord += center; ");simpleShader.call(this,gl.swirl,{radius:radius,center:[centerX,centerY],angle:angle,texSize:[this.width,this.height]});return this}return exports})();imagelib.drawing={};imagelib.drawing.context=function(size){var canvas=document.createElement("canvas");canvas.width=size.w;canvas.height=size.h;canvas.style.setProperty("image-rendering","optimizeQuality",null);return canvas.getContext("2d")};imagelib.drawing.copy=function(dstCtx,src,size){dstCtx.drawImage(src.canvas||src,0,0,size.w,size.h)};imagelib.drawing.clear=function(ctx,size){ctx.clearRect(0,0,size.w,size.h)};imagelib.drawing.drawCenterInside=function(dstCtx,src,dstRect,srcRect){if(srcRect.w/srcRect.h>dstRect.w/dstRect.h){var h=srcRect.h*dstRect.w/srcRect.w;imagelib.drawing.drawImageScaled(dstCtx,src,srcRect.x,srcRect.y,srcRect.w,srcRect.h,dstRect.x,dstRect.y+(dstRect.h-h)/2,dstRect.w,h)}else{var w=srcRect.w*dstRect.h/srcRect.h;imagelib.drawing.drawImageScaled(dstCtx,src,srcRect.x,srcRect.y,srcRect.w,srcRect.h,dstRect.x+(dstRect.w-w)/2,dstRect.y,w,dstRect.h)}};imagelib.drawing.drawCenterCrop=function(dstCtx,src,dstRect,srcRect){if(srcRect.w/srcRect.h>dstRect.w/dstRect.h){var w=srcRect.h*dstRect.w/dstRect.h;imagelib.drawing.drawImageScaled(dstCtx,src,srcRect.x+(srcRect.w-w)/2,srcRect.y,w,srcRect.h,dstRect.x,dstRect.y,dstRect.w,dstRect.h)}else{var h=srcRect.w*dstRect.h/dstRect.w;imagelib.drawing.drawImageScaled(dstCtx,src,srcRect.x,srcRect.y+(srcRect.h-h)/2,srcRect.w,h,dstRect.x,dstRect.y,dstRect.w,dstRect.h)}};imagelib.drawing.drawImageScaled=function(dstCtx,src,sx,sy,sw,sh,dx,dy,dw,dh){if((dw= event.data.minAlpha) { "," l = Math.min(x, l); "," t = Math.min(y, t); "," r = Math.max(x, r); "," b = Math.max(y, b); "," } "," } "," } "," "," if (l > r) { "," // no pixels, couldn't trim "," postMessage({ x: 0, y: 0, w: event.data.size.w, h: event.data.size.h });"," return; "," } "," "," postMessage({ x: l, y: t, w: r - l + 1, h: b - t + 1 }); ","}; ",""].join("\n");imagelib.drawing.getTrimRect=function(ctx,size,minAlpha,callback){callback=callback||function(){};if(!ctx.canvas){var src=ctx;ctx=imagelib.drawing.context(size);imagelib.drawing.copy(ctx,src,size)}if(minAlpha==0){callback({x:0,y:0,w:size.w,h:size.h})}minAlpha=minAlpha||1;var worker=imagelib.util.runWorkerJs(imagelib.drawing.trimRectWorkerJS_,{imageData:ctx.getImageData(0,0,size.w,size.h),size:size,minAlpha:minAlpha},callback);return worker};imagelib.drawing.getCenterOfMass=function(ctx,size,minAlpha,callback){callback=callback||function(){};if(!ctx.canvas){var src=ctx;ctx=imagelib.drawing.context(size);imagelib.drawing.copy(ctx,src,size)}if(minAlpha==0){callback({x:size.w/2,y:size.h/2})}minAlpha=minAlpha||1;var l=size.w,t=size.h,r=0,b=0;var imageData=ctx.getImageData(0,0,size.w,size.h);var sumX=0;var sumY=0;var n=0;var alpha;for(var y=0;y=minAlpha){sumX+=x;sumY+=y;++n}}}if(n<=0){callback({x:size.w/2,h:size.h/2})}callback({x:Math.round(sumX/n),y:Math.round(sumY/n)})};imagelib.drawing.copyAsAlpha=function(dstCtx,src,size,onColor,offColor){onColor=onColor||"#fff";offColor=offColor||"#000";dstCtx.save();dstCtx.clearRect(0,0,size.w,size.h);dstCtx.globalCompositeOperation="source-over";imagelib.drawing.copy(dstCtx,src,size);dstCtx.globalCompositeOperation="source-atop";dstCtx.fillStyle=onColor;dstCtx.fillRect(0,0,size.w,size.h);dstCtx.globalCompositeOperation="destination-atop";dstCtx.fillStyle=offColor;dstCtx.fillRect(0,0,size.w,size.h);dstCtx.restore()};imagelib.drawing.makeAlphaMask=function(ctx,size,fillColor){var src=ctx.getImageData(0,0,size.w,size.h);var dst=ctx.createImageData(size.w,size.h);var srcData=src.data;var dstData=dst.data;var i,g;for(var y=0;yradius2){matrix[index]=0}else{matrix[index]=Math.exp(-distance2/sigma22)/sqrtPiSigma22}total+=matrix[index];index++}}imagelib.drawing.applyFilter(new ConvolutionFilter(matrix,total,0,true),ctx,size)}function glfxblur_(radius,ctx,size){var canvas=fx.canvas();var texture=canvas.texture(ctx.canvas);canvas.draw(texture).triangleBlur(radius).update();ctx.clearRect(0,0,canvas.width,canvas.height);ctx.drawImage(canvas,0,0)}imagelib.drawing.blur=function(radius,ctx,size){try{if(size.w>128||size.h>128){glfxblur_(radius,ctx,size)}else{slowblur_(radius,ctx,size)}}catch(e){slowblur_(radius,ctx,size)}}})();imagelib.drawing.fx=function(effects,dstCtx,src,size){effects=effects||[];var outerEffects=[];var innerEffects=[];var fillEffects=[];for(var i=0;i=6){e=true}}if(!e){$("
").addClass("browser-unsupported-note ui-state-highlight").attr("title","Your browser is not supported.").append($('')).append($("

").html('Currently only Google Chrome is recommended and supported. Your mileage may vary with other browsers.')).prependTo("body")}};b.forms={};b.forms.Form=Base.extend({constructor:function(e,d){this.id_=e;this.params_=d;this.fields_=d.fields;this.pauseNotify_=false;for(var c=0;c").addClass("form-field-outer").append($("

").addClass("form-field-help-text").css("display",this.params_.helpText?"":"none").html(this.params_.helpText))).append($("
").addClass("form-field-container")).appendTo(c);return this.baseEl_},setEnabled:function(c){if(this.baseEl_){if(c){this.baseEl_.removeAttr("disabled")}else{this.baseEl_.attr("disabled","disabled")}}}});b.forms.TextField=b.forms.Field.extend({createUI:function(c){var e=$(".form-field-container",this.base(c));var d=this;this.el_=$("").attr("type","text").addClass("form-text").val(this.getValue()).bind("change",function(){d.setValue($(this).val(),true)}).bind("keydown change",function(){var g=this;var f=d.getValue();window.setTimeout(function(){var h=$(g).val();if(f!=h){d.setValue(h,true)}},0)}).appendTo(e)},getValue:function(){var c=this.value_;if(typeof c!="string"){c=this.params_.defaultValue||""}return c},setValue:function(d,c){this.value_=d;if(!c){$(this.el_).val(d)}this.form_.notifyChanged_(this)},serializeValue:function(){return this.getValue()},deserializeValue:function(c){this.setValue(c)}});b.forms.AutocompleteTextField=b.forms.Field.extend({createUI:function(c){var g=$(".form-field-container",this.base(c));var f=this;var e=this.getHtmlId()+"-datalist";this.el_=$("").attr("type","text").addClass("form-text").attr("list",e).val(this.getValue()).bind("keydown change",function(){var h=this;window.setTimeout(function(){f.setValue($(h).val(),true)},0)}).appendTo(g);this.datalistEl_=$("").attr("id",e).appendTo(g);this.params_.items=this.params_.items||[];for(var d=0;d").attr("value",this.params_.items[d]))}},getValue:function(){var c=this.value_;if(typeof c!="string"){c=this.params_.defaultValue||""}return c},setValue:function(d,c){this.value_=d;if(!c){$(this.el_).val(d)}this.form_.notifyChanged_(this)},serializeValue:function(){return this.getValue()},deserializeValue:function(c){this.setValue(c)}});b.forms.ColorField=b.forms.Field.extend({createUI:function(c){var e=$(".form-field-container",this.base(c));var d=this;this.el_=$("").addClass("form-color").attr("type","text").attr("id",this.getHtmlId()).css("background-color",this.getValue().color).appendTo(e);this.el_.spectrum({color:this.getValue().color,showInput:true,showPalette:true,palette:[["#ffffff","#000000"],["#f44336","#e91e63"],["#9c27b0","#673ab7"],["#3f51b5","#2196f3"],["#03a9f4","#00bcd4"],["#009688","#4caf50"],["#8bc34a","#cddc39"],["#ffeb3b","#ffc107"],["#ff9800","#ff5722"],["#9e9e9e","#607d8b"]],localStorageKey:"recentcolors",showInitial:true,showButtons:false,change:function(f){d.setValue({color:f.toHexString()},true)}});if(this.params_.alpha){this.alphaEl_=$("").attr("type","range").attr("min",0).attr("max",100).val(this.getValue().alpha).addClass("form-range").change(function(){d.setValue({alpha:Number(d.alphaEl_.val())},true)}).appendTo(e);this.alphaTextEl_=$("
").addClass("form-range-text").text(this.getValue().alpha+"%").appendTo(e)}},getValue:function(){var c=this.value_||this.params_.defaultValue||"#000000";if(/^([0-9a-f]{6}|[0-9a-f]{3})$/i.test(c)){c="#"+c}var d=this.alpha_;if(typeof d!="number"){d=this.params_.defaultAlpha;if(typeof d!="number"){d=100}}return{color:c,alpha:d}},setValue:function(e,d){e=e||{};if("color" in e){this.value_=e.color}if("alpha" in e){this.alpha_=e.alpha}var c=this.getValue();this.el_.css("background-color",c.color);if(!d){$(this.el_).spectrum("set",c.color);if(this.alphaEl_){this.alphaEl_.val(c.alpha)}}if(this.alphaTextEl_){this.alphaTextEl_.text(c.alpha+"%")}this.form_.notifyChanged_(this)},serializeValue:function(){var c=this.getValue();return c.color.replace(/^#/,"")+","+c.alpha},deserializeValue:function(d){var e={};var c=d.split(",",2);if(c.length>=1){e.color=c[0]}if(c.length>=2){e.alpha=parseInt(c[1],10)}this.setValue(e)}});b.forms.EnumField=b.forms.Field.extend({createUI:function(c){var g=$(".form-field-container",this.base(c));var f=this;if(this.params_.buttons){this.el_=$("
").attr("id",this.getHtmlId()).addClass("form-field-buttonset").appendTo(g);for(var d=0;d").attr({type:"radio",name:this.getHtmlId(),id:this.getHtmlId()+"-"+e.id,value:e.id}).change(function(){f.setValueInternal_($(this).val(),true)}).appendTo(this.el_);$("