├── LICENSE ├── README.md ├── android_webp_test ├── 0002-add-dec_speed-test-app.patch ├── README.md ├── android_webp_test_004.tar.gz ├── archives │ └── REMOVE.ME ├── bin │ └── REMOVE.ME ├── images │ ├── REMOVE.ME │ └── thomassine.webp ├── logs │ └── REMOVE.ME ├── make_tar.sh └── run_me.sh ├── demos └── battery │ ├── README.md │ ├── css │ └── style.css │ ├── index.html │ └── js │ └── index.js └── memory_study ├── 0001-RAM-study-patch.patch ├── README.md ├── generate_webp.sh ├── memory_vs_size_lossless.png ├── memory_vs_width_lossy.png ├── run_test.sh ├── size_distrib.png └── width_distrib.png /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libwebp-demo 2 | 3 | Demos, code samples and research materials related to libwebp and the [WebP] 4 | image format. 5 | 6 | [WebP]: https://developers.google.com/speed/webp/ 7 | 8 | ## webp.js 9 | 10 | [webp.js](https://webmproject.github.io/libwebp-demo/webp_js/index.html) is a demo page showcasing the JavaScript version of libwebp. 11 | 12 | Sources: https://github.com/webmproject/libwebp-demo/tree/gh-pages/webp_js 13 | 14 | 15 | ## Decoding timing test 16 | 17 | [android_timing_test/](https://github.com/webmproject/libwebp-demo/tree/master/android_webp_test) contains a script to measure the decoding speed on Android devices. 18 | 19 | ## Battery usage test 20 | 21 | [demos/battery/](https://github.com/webmproject/libwebp-demo/tree/master/demos/battery) contains a script to measure the battery consumption when repeatingly decoding an image (WebP, JPEG, ...). 22 | -------------------------------------------------------------------------------- /android_webp_test/0002-add-dec_speed-test-app.patch: -------------------------------------------------------------------------------- 1 | From b9bb89936a8610a10d8e7d7d8b13c48671a949dd Mon Sep 17 00:00:00 2001 2 | From: Pascal Massimino 3 | Date: Sat, 4 Feb 2017 01:48:28 -0800 4 | Subject: [PATCH] add dec_speed test app 5 | 6 | Change-Id: I3f08b18f609406f0e94f3dd1ce20e90a735efe4d 7 | --- 8 | examples/Android.mk | 16 ++++++++ 9 | examples/dec_speed.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++ 10 | 2 files changed, 130 insertions(+) 11 | create mode 100644 examples/dec_speed.c 12 | 13 | diff --git a/examples/Android.mk b/examples/Android.mk 14 | index 00d53baf..2aedec41 100644 15 | --- a/examples/Android.mk 16 | +++ b/examples/Android.mk 17 | @@ -80,3 +80,19 @@ LOCAL_STATIC_LIBRARIES := example_util imageio_util imagedec webpmux webp 18 | LOCAL_MODULE := img2webp_example 19 | 20 | include $(BUILD_EXECUTABLE) 21 | + 22 | +################################################################################ 23 | +# dec_speed 24 | + 25 | +include $(CLEAR_VARS) 26 | + 27 | +LOCAL_SRC_FILES := \ 28 | + dec_speed.c \ 29 | + 30 | +LOCAL_CFLAGS := $(WEBP_CFLAGS) 31 | +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../src 32 | +LOCAL_STATIC_LIBRARIES := webp 33 | + 34 | +LOCAL_MODULE := dec_speed 35 | + 36 | +include $(BUILD_EXECUTABLE) 37 | diff --git a/examples/dec_speed.c b/examples/dec_speed.c 38 | new file mode 100644 39 | index 00000000..76c4e47d 40 | --- /dev/null 41 | +++ b/examples/dec_speed.c 2017-02-10 11:59:32.573220321 +0100 42 | @@ -0,0 +1,119 @@ 43 | +// Copyright 2017 Google Inc. All Rights Reserved. 44 | +// 45 | +// Use of this source code is governed by a BSD-style license 46 | +// that can be found in the COPYING file in the root of the source 47 | +// tree. An additional intellectual property rights grant can be found 48 | +// in the file PATENTS. All contributing project authors may 49 | +// be found in the AUTHORS file in the root of the source tree. 50 | +// ----------------------------------------------------------------------------- 51 | +// 52 | +// Command-line tool for measuring decoding speed. 53 | +// gcc -o dec_speed dec_speed.c -O2 -I../src/ ../src/libwebp.a -lm -lpthread 54 | +// 55 | +// Author: Skal (pascal.massimino@gmail.com) 56 | + 57 | +#include 58 | +#include 59 | +#include 60 | +#include 61 | +#include 62 | + 63 | +#include "webp/decode.h" 64 | + 65 | +#define MAX_FILES 600 66 | +#define MEMORY_SIZE ((size_t)256u * 1024 * 1024) // 256M 67 | + 68 | +static double GetTime() { 69 | + struct timeval t; 70 | + gettimeofday(&t, NULL); 71 | + return (double)t.tv_sec + (double)t.tv_usec / 1000000.; 72 | +} 73 | + 74 | +int main(int argc, const char *argv[]) { 75 | + int verbose = 0; 76 | + int num_loops = 1; 77 | + const uint8_t* files_start[MAX_FILES]; 78 | + size_t files_size[MAX_FILES]; 79 | + int num_files = 0; 80 | + int error = -1; 81 | + int n; 82 | + uint8_t* mem = (uint8_t*)malloc(MEMORY_SIZE * sizeof(*mem)); 83 | + const uint8_t* const mem_end = mem + MEMORY_SIZE; 84 | + uint8_t* ptr = mem; 85 | + 86 | + if (mem == NULL) goto End; 87 | + 88 | + for (n = 1; n < argc; ++n) { 89 | + if (argv[n][0] == '-') { 90 | + num_loops = atoi(&argv[n][1]); 91 | + } else { 92 | + int ok = 0; 93 | + size_t file_size; 94 | + const char* file_name = argv[n]; 95 | + FILE* in = fopen(file_name, "rb"); 96 | + if (in == NULL) { 97 | + printf("File %s not found! skipping.\n", file_name); 98 | + continue; 99 | + } 100 | + fseek(in, 0, SEEK_END); 101 | + file_size = ftell(in); 102 | + fseek(in, 0, SEEK_SET); 103 | + if (verbose) printf("%s (%d bytes)\n", file_name, (int)file_size); 104 | + if (ptr + file_size > mem_end) { 105 | + printf("Memory area exhausted after file %s.\n", file_name); 106 | + goto Finish; 107 | + } 108 | + if (fread(ptr, file_size, 1, in) != 1) { 109 | + printf("Couldn't read file %s.\n", file_name); 110 | + goto Finish; 111 | + } 112 | + ok = 1; 113 | + Finish: 114 | + fclose(in); 115 | + if (ok) { 116 | + files_start[num_files] = ptr; 117 | + files_size[num_files] = file_size; 118 | + ptr += file_size; 119 | + ++num_files; 120 | + if (num_files == MAX_FILES) break; 121 | + } else { 122 | + error = -1; 123 | + break; 124 | + } 125 | + } 126 | + } 127 | + if (verbose) printf("Read %d files\n", num_files); 128 | + error = 0; 129 | + 130 | + { 131 | + const int kMinIterations = 3; // untimed warm-up iterations 132 | + double elapsed = 0.; 133 | + double min_time = -1.; 134 | + int n, k; 135 | + for (n = 0; n < num_files; ++n) { 136 | + for (k = 0; k < num_loops + kMinIterations; ++k) { 137 | + const double now = GetTime(); 138 | + double delta; 139 | + const uint8_t* const out = 140 | + WebPDecodeARGB(files_start[n], files_size[n], NULL, NULL); 141 | + if (out == NULL) { 142 | + printf("Problem decoding file #%d\n", n); 143 | + error = -1; 144 | + break; 145 | + } 146 | + free((void*)out); 147 | + if (k >= kMinIterations) { 148 | + delta = GetTime() - now; 149 | + if (min_time < 0. || delta < min_time) min_time = delta; 150 | + elapsed += delta; 151 | + } 152 | + } 153 | + } 154 | + printf("Elapsed: %.3lf s Minimum: %.3lf s\n", elapsed, min_time); 155 | + } 156 | + if (error != 0) printf("PROBLEM!\n"); 157 | + 158 | + End: 159 | + free(mem); 160 | + return error; 161 | +} 162 | -- 163 | 2.11.0.390.gc69c2f50cf-goog 164 | -------------------------------------------------------------------------------- /android_webp_test/README.md: -------------------------------------------------------------------------------- 1 | # android timing test 2 | 3 | Crude script for measuring WebP decoding speed on Android device. 4 | 5 | Version 003 : https://github.com/webmproject/libwebp-demo/raw/master/android_webp_test/android_webp_test_003.tar.gz 6 | 7 | This script will download the released libwebp archives, compile a binary named 8 | 'dec_speed_*' to measure decoding speed, run it on the device and report the 9 | decoding speed. 10 | 11 | ## Pre-requisites: 12 | * android NDK 13 | * adb 14 | * wget 15 | * a writable /data/local/tmp directory on your device 16 | 17 | ## Installation 18 | 19 | Just download and extract the latest android_webp_test_*.tar.gz archive. 20 | You should have a `run_me.sh` script along with some directories for 21 | storing images, binaries, etc. 22 | The device to test should be plugged and visible when running `adb devices -l`. 23 | 24 | ## How to run: 25 | 26 | `./run_me.sh images/*.webp` should suffice. 27 | 28 | By default, the script with download, extract and compile the official 29 | libwebp release archives. It will also transfer the test images on the 30 | device. 31 | Subsequently, you can use the `-r` option to only re-do the timing test. 32 | To only redo the timing test with new images, use the options `-r +t` (in 33 | this order). 34 | 35 | The pictures you want to use for testing are searched as ./images/*.webp 36 | by default. You can pass the images list as argument to the script. 37 | 38 | Once the timing is finished (for all release versions to test), the 39 | results will be stored as logs/LOG_* (and printed). 40 | 41 | There are other options available, as listed by `run_me.sh -h`: 42 | 43 | ``` 44 | -d ............. don't download archives 45 | -t ............. don't transfer images to phone 46 | +t ............. transfer images to phone 47 | -e ............. don't extract archive (= keep local code modifications) 48 | +e ............. extract archive (= erase local code modifications) 49 | -c ............. don't recompile the timing binary 50 | +c ............. recompile the timing binary 51 | -r ............. only re-run timing (equivalent to -d -t -e -c) 52 | -R .... releases to test (e.g. "0.5.0 0.5.2 0.6.0") 53 | -loops ... number of timing loops to perform 54 | -arch . target ndk architecture (armeabi-v7a, arm64-v8a, ...) 55 | -adb .. the device id to use with 'adb' 56 | ``` 57 | 58 | ## Advices to get a more stable timing 59 | 60 | To reduce the measurement variability: 61 | 62 | * beware of thermal throttling on the device! 63 | * it is advised to have the device be in 'airplane mode' 64 | * try to keep the screen switched on (by regularly swiping, e.g.) 65 | * don't hesitate to use -loops option to raise the number of loops 66 | 67 | But overall, expect at least 2% timing noise! 68 | -------------------------------------------------------------------------------- /android_webp_test/android_webp_test_004.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webmproject/libwebp-demo/02e9afe116dc68d70278e5ed5d8e5716688e2b01/android_webp_test/android_webp_test_004.tar.gz -------------------------------------------------------------------------------- /android_webp_test/archives/REMOVE.ME: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webmproject/libwebp-demo/02e9afe116dc68d70278e5ed5d8e5716688e2b01/android_webp_test/archives/REMOVE.ME -------------------------------------------------------------------------------- /android_webp_test/bin/REMOVE.ME: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webmproject/libwebp-demo/02e9afe116dc68d70278e5ed5d8e5716688e2b01/android_webp_test/bin/REMOVE.ME -------------------------------------------------------------------------------- /android_webp_test/images/REMOVE.ME: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webmproject/libwebp-demo/02e9afe116dc68d70278e5ed5d8e5716688e2b01/android_webp_test/images/REMOVE.ME -------------------------------------------------------------------------------- /android_webp_test/images/thomassine.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webmproject/libwebp-demo/02e9afe116dc68d70278e5ed5d8e5716688e2b01/android_webp_test/images/thomassine.webp -------------------------------------------------------------------------------- /android_webp_test/logs/REMOVE.ME: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webmproject/libwebp-demo/02e9afe116dc68d70278e5ed5d8e5716688e2b01/android_webp_test/logs/REMOVE.ME -------------------------------------------------------------------------------- /android_webp_test/make_tar.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | version="004" 4 | 5 | echo "****" 6 | echo " DID YOU UPAGE THE VERSION NUMBER TO ${version} IN THE run_me.sh HEADER?" 7 | echo "****" 8 | 9 | name="android_webp_test_${version}" 10 | rm -rf ${name} && mkdir ${name} 11 | for d in bin images archives logs; do 12 | mkdir ${name}/${d} 13 | touch ${name}/${d}/REMOVE.ME 14 | done 15 | 16 | cp images/thomassine.webp ${name}/images 17 | cp run_me.sh 0002-add-dec_speed-test-app.patch ${name} 18 | 19 | tar czf ${name}.tar.gz ${name} 20 | 21 | echo "${name}.tar.gz : DONE" 22 | -------------------------------------------------------------------------------- /android_webp_test/run_me.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Crude script for measuring WebP decoding speed on Android device. 4 | # 5 | # Version 004 6 | # 7 | # This script will download the released liwebp archives, patch them, compile 8 | # a binary named 'dec_speed_*' to measure decoding speed, run it on the device 9 | # and report the numbers. 10 | # 11 | # Pre-requisites: 12 | # android NDK 13 | # adb 14 | # wget 15 | # a writable /data/local/tmp directory on your device 16 | # 17 | # How to run: 18 | # By default, the script with download, extract and compile the official 19 | # libwebp release archives. It will also transfer the images to test on the 20 | # phone. 21 | # Subsequently, you can use the '-r' option to only re-do the timing test. 22 | # To only redo the timing test with new images, use the options "-r +t" (in 23 | # this order). 24 | # 25 | # The pictures you want to use for testing are searched as ./images/*.webp 26 | # by default. You can pass the images list as argument to the script. 27 | # 28 | # Once the timing is finished (for all release versions to test), the 29 | # results will be stored as logs/LOG_* (and printed). 30 | # 31 | # There are other options available, as listed by `run_me.sh -h`: 32 | # 33 | # Beware of thermal throttling on the device! It is advised to have the device 34 | # be in 'airplane mode' and with screen on, to avoid measurement variability. 35 | # 36 | # -> Overall, expect at least 2% timing noise! 37 | 38 | set -e 39 | home=`pwd` 40 | 41 | ################################################################################ 42 | # Customizable section: 43 | 44 | # the libwebp releases to test 45 | releases="0.4.1 0.4.2 0.4.3 0.4.4 0.5.0 0.5.1 0.5.2 0.6.0" 46 | 47 | # "yes" or "no" 48 | download_tgz="yes" 49 | transfer_images="yes" 50 | extract="yes" 51 | compile="yes" 52 | 53 | # parameters 54 | adb='adb' 55 | ndk_build='ndk-build' 56 | arch="armeabi-v7a" 57 | num_loops="2" # number of decoding loops to perform for timing 58 | 59 | # command-line arguments and list of pictures to test 60 | usage() { 61 | echo "usage: run_me.sh [options] webp_file [webp_files...]" 62 | echo "options:" 63 | echo " -d ............. don't download archives" 64 | echo " -t ............. don't transfer images to phone" 65 | echo " +t ............. transfer images to phone" 66 | echo " -e ............. don't extract archive (= keep local code modifications)" 67 | echo " +e ............. extract archive (= erase local code modifications)" 68 | echo " -c ............. don't recompile the timing binary" 69 | echo " +c ............. recompile the timing binary" 70 | echo " -r ............. only re-run timing (equivalent to -d -t -e -c)" 71 | echo " -R .... releases to test (e.g. \"0.5.0 0.5.2 0.6.0\")" 72 | echo " -loops ... number of timing loops to perform" 73 | echo " -arch . target ndk architecture (armeabi-v7a, arm64-v8a, ...)" 74 | echo " -adb .. the device id to use with 'adb'" 75 | echo " -h ............. this help" 76 | echo 77 | echo " Example: \'./run_me.sh -r +t images/some_new*.webp\' will transfer some " 78 | echo " new picture files and redo the timing without recompilation." 79 | exit 0 80 | } 81 | 82 | test_images="" 83 | if [ ! -z "$1" ]; then 84 | while [[ $# -gt 0 ]] 85 | do 86 | arg="$1" 87 | case ${arg} in 88 | -t) transfer_images="no";; 89 | +t) transfer_images="yes";; 90 | -d) download_tgz="no";; 91 | -e) extract="no";; 92 | +e) extract="yes";; 93 | -c) compile="no";; 94 | +c) compile="yes";; 95 | -r|--rerun) 96 | transfer_images="no" 97 | download_tgz="no" 98 | extract="no" 99 | compile="no" 100 | ;; 101 | -loops) shift; num_loops=$1;; 102 | -adb) shift; adb="adb -s $1";; 103 | -arch) shift; arch=$1;; 104 | -R) shift; releases=$1;; 105 | -h|--help) usage;; 106 | *) test_images="${test_images} ${arg}";; 107 | esac 108 | shift 109 | done 110 | else 111 | test_images=`ls ./images/*.webp` 112 | fi 113 | 114 | # directories 115 | device_base_dir="/data/local/tmp" 116 | device_dir="${device_base_dir}/test" 117 | images_dir="${device_base_dir}/images" 118 | 119 | ################################################################################ 120 | # download archives 121 | 122 | if [ x"${download_tgz}" = "xyes" ]; then 123 | echo "downloading archives ${releases}..." 124 | if [ ! -e archives ]; then mkdir archives; fi 125 | for v in ${releases}; do 126 | archive=libwebp-${v}.tar.gz 127 | echo " ... ${archive}" 128 | if [ ! -e archives/${archive} ]; then 129 | wget -q https://storage.googleapis.com/downloads.webmproject.org/releases/webp/${archive} 130 | mv ${archive} archives 131 | echo " downloaded OK." 132 | fi 133 | done 134 | fi 135 | 136 | ################################################################################ 137 | # transfer images 138 | 139 | if [ x"${transfer_images}" == "xyes" ]; then 140 | echo "Transfering test images to ${images_dir}" 141 | i=0 142 | ${adb} shell "if [ ! -e ${images_dir} ]; then mkdir ${images_dir}; fi" 143 | for f in ${test_images}; do 144 | [ ! -e ${f} ] && echo "WebP image file $f not found" && exit 145 | ${adb} push $f ${images_dir} &> /dev/null 146 | (( i += 1 )) 147 | done 148 | echo " done. Transferred ${i} files." 149 | fi 150 | 151 | images_list="" 152 | for x in ${test_images}; do 153 | images_list="${images_list} ${images_dir}/${x##*/}" 154 | done 155 | 156 | ################################################################################ 157 | # create testing directory 158 | 159 | echo "creating fresh directory ${device_dir} on phone" 160 | ${adb} shell "rm -rf ${device_dir} && mkdir ${device_dir}" 161 | 162 | # because the list of images to test can be quite long, we need a simple script 163 | # on the device instead of using 'adb shell' ... 164 | cat << EOF > go_timing 165 | #!/bin/sh 166 | ./\$1 -${num_loops} ${images_list} 167 | EOF 168 | ${adb} push go_timing ${device_dir} 169 | 170 | ################################################################################ 171 | # run the test 172 | 173 | echo "Testing ${arch}..." 174 | for version in ${releases}; do 175 | archive=libwebp-${version} 176 | bin_name="dec_speed_${arch}_${version}" 177 | 178 | # extract the archive 179 | if [ x"${extract}" == "xyes" ]; then 180 | # extract 181 | rm -rf ${archive} 182 | tar xzf archives/${archive}.tar.gz 183 | # patch 184 | cd ${home}/${archive} 185 | patch -p1 < ${home}/0002-add-dec_speed-test-app.patch 186 | cd ${home} 187 | fi 188 | 189 | # build test binary and push to device 190 | if [ x"${compile}" == "xyes" ]; then 191 | # clean up past builds 192 | rm -rf obj libs jni 193 | ln -s ${archive} jni 194 | # build 195 | ${ndk_build} -s APP_ABI=${arch} -j APP_CFLAGS=-fPIE APP_LDFLAGS=-pie > /dev/null 196 | mv ./libs/${arch}/dec_speed ./bin/${bin_name} 197 | echo " ... ${arch}_${version} compiled OK." 198 | fi 199 | 200 | ${adb} push ./bin/${bin_name} ${device_dir} &> /dev/null 201 | 202 | # perform timing 203 | log_file="LOG_${arch}_${version}" 204 | ${adb} shell "cd ${device_dir} && sh ./go_timing ${bin_name} > ${log_file}" 205 | 206 | # retrieve logs 207 | ${adb} pull ${device_dir}/${log_file} &> /dev/null 208 | result=`cat ${log_file}` 209 | echo " version ${version} ${result}" 210 | mv ${log_file} logs 211 | done 212 | -------------------------------------------------------------------------------- /demos/battery/README.md: -------------------------------------------------------------------------------- 1 | # WebP Battery test 2 | 3 | A battery test experiment. This demo uses the [Battery Status API](https://w3c.github.io/battery/) and decodes as many JPEG or WebP images as possible within a given (preset) battery drop. Hence, this test focuses on battery performance rather than speed. 4 | 5 | Use the edit icon on the top right to modify the settings, and start button to run the experiment. 6 | 7 | The UI is built with [PaperGUI](http://google.github.io/paper-gui). 8 | -------------------------------------------------------------------------------- /demos/battery/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #1d1f20; 3 | } 4 | 5 | a { 6 | color: #2196f3; 7 | } 8 | 9 | table { 10 | margin: 24px 24px 0 0; 11 | padding: 12px; 12 | line-height: 24px; 13 | min-width: 290px; 14 | color: white; 15 | background-color: rgba(0,0,0,.5); 16 | border-radius: 2px; 17 | } 18 | 19 | td:nth-child(2) { 20 | padding-left: 12px; 21 | min-width: 110px; 22 | text-align: right; 23 | } 24 | 25 | .ui { 26 | position: fixed; 27 | top: 24px; 28 | left: 24px; 29 | } 30 | 31 | #btn { 32 | height: 54px; 33 | padding: 0 24px; 34 | border: none; 35 | background-color: white; 36 | color: #ff8507; 37 | text-transform: uppercase; 38 | border-radius: 2px; 39 | box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2); 40 | } 41 | 42 | .paper-gui-0 #fab.paper-gui { 43 | top: 24px; 44 | right: 24px; 45 | } 46 | 47 | .copyme { 48 | position: absolute; 49 | top: 0; 50 | left: 0; 51 | transform: scale(0); 52 | opacity: 0; 53 | } 54 | -------------------------------------------------------------------------------- /demos/battery/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | WebP Battery test experiment 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 22 |
19 | Hello there, welcome to this battery performance test. Battery API is required for this test and might not be around for very long. 20 |

Press Start to run the test, or use the orange button to fiddle with the parameters. To reduce noise, make sure to test over a large-enough battery drain threshold (default is 5%).

21 |
23 |
24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /demos/battery/js/index.js: -------------------------------------------------------------------------------- 1 | // List of preset test images. CORS must be enabled for these resources. 2 | var images = { 3 | 'WebP 600 x 800 px': 'https://www.gstatic.com/webp/measurement/example1.webp', 4 | 'WebP 800 x 533 px': 'https://www.gstatic.com/webp/measurement/example2.webp', 5 | 'WebP 1200 x 800 px': 'https://www.gstatic.com/webp/measurement/example3.webp', 6 | 'JPEG 600 x 800 px': 'https://www.gstatic.com/webp/measurement/example1.jpg', 7 | 'JPEG 800 x 533 px': 'https://www.gstatic.com/webp/measurement/example2.jpg', 8 | 'JPEG 1200 x 800 px': 'https://www.gstatic.com/webp/measurement/example3.jpg', 9 | }; 10 | 11 | // Experiment configuration parameters. 12 | var params = { 13 | showInfo: true, 14 | presetUrl: images['WebP 600 x 800 px'], 15 | customUrl: '', 16 | batteryDrop: 5 17 | }; 18 | 19 | // Public variables. 20 | var counter = 0; 21 | var running = false; 22 | var timings, fetchPromise, once; 23 | var stats = {}; 24 | var infoEl = document.getElementById('info'); 25 | var imageEl = document.getElementById('image'); 26 | 27 | // Clears the stats object and fetch counter. 28 | function resetStats() { 29 | stats = {}; 30 | counter = 0; 31 | imageEl.src = ''; 32 | updateInfo(); 33 | }; 34 | 35 | // Renders the stats object as an HTML table. 36 | function updateInfo() { 37 | if (!params.showInfo) { 38 | infoEl.style.display = 'none'; 39 | return; 40 | } 41 | var tableData = {}; 42 | 43 | if (stats.currentBatt && stats.initialBatt) { 44 | tableData['Battery level (at start)'] = Math.floor(stats.currentBatt * 100) 45 | + '% (' + Math.floor(stats.initialBatt * 100) + '%)'; 46 | } 47 | if (stats.fileSize && stats.mime) { 48 | tableData['File info'] = stats.mime + ', ' 49 | + Math.round(stats.fileSize / 1000) + ' Kb'; 50 | } 51 | tableData['Fetch count'] = counter; 52 | if (stats.batt && stats.batt.length) { 53 | tableData['Images/batt.% (avg)'] = stats.batt[stats.batt.length - 1] + ' (' 54 | + Math.round(average(stats.batt)) + ')'; 55 | } 56 | if (stats.fetch && stats.fetch.length) { 57 | tableData['Fetch time (avg)'] = stats.fetch[stats.fetch.length - 1] 58 | + ' ms (' + Math.round(average(stats.fetch)) + ' ms)'; 59 | } 60 | if (stats.load && stats.load.length) { 61 | tableData['Load time (avg)'] = stats.load[stats.load.length - 1] + ' ms (' 62 | + Math.round(average(stats.load)) + ' ms)'; 63 | } 64 | 65 | var html = ''; 66 | Object.keys(tableData).forEach(function(key) { 67 | html += '' + key + '' + tableData[key] + ''; 68 | }); 69 | window.requestAnimationFrame(function() { 70 | infoEl.innerHTML = html; 71 | infoEl.style.display = ''; 72 | }); 73 | } 74 | 75 | // Downloads an image by URL via fetch API, storing some stats in the process, 76 | // and renders it to the DOM via createObjectURL. 77 | var doFetch = function(url) { 78 | if (fetchPromise) { 79 | return; 80 | } 81 | timings = { 82 | fetchStart: Date.now() 83 | }; 84 | fetchPromise = fetch(url) 85 | .then(function(response) { 86 | timings.fetchEnd = Date.now(); 87 | return response.blob(); 88 | }, function(e) { 89 | alert('Unable to fetch the image. Either the URL is incorrect' 90 | + ' or the file has no Access-Control-Allow-Origin header.'); 91 | stop(); 92 | fetchPromise = null; 93 | }) 94 | .then(function(imageBlob) { 95 | fetchPromise = null; 96 | if (!imageBlob || imageBlob.type.indexOf('image/') < 0) { 97 | alert('Woops, fetched file is not an image!'); 98 | stop(); 99 | return; 100 | } 101 | stats.mime = imageBlob.type.replace('image/', ''); 102 | stats.fileSize = imageBlob.size; 103 | URL.revokeObjectURL(imageEl.src); 104 | var objectURL = URL.createObjectURL(imageBlob); 105 | timings.loadStart = Date.now(); 106 | imageEl.src = objectURL; 107 | counter++; 108 | }); 109 | 110 | }; 111 | 112 | // Fetches the next image or shows test results if the loop was interrupted. 113 | var fetchNext = function(opt_manual) { 114 | once = !!opt_manual; 115 | if (!running && !opt_manual) { 116 | alert('Test complete!\n\nImages loaded: ' + counter 117 | + (stats.batt && stats.batt.length ? '\nImages/batt.%: ' 118 | + Math.round(average(stats.batt)) : '\nNo battery stats.')); 119 | return; 120 | } 121 | var url = params.customUrl || params.presetUrl; 122 | doFetch(url + '?ts=' + Date.now()); 123 | }; 124 | 125 | // Image element load callback. Tracks timings and triggers the next fetch. 126 | imageEl.onload = function() { 127 | var loadEnd = Date.now(); 128 | if (!stats.fetch) { 129 | stats.fetch = []; 130 | stats.load = []; 131 | } 132 | stats.fetch.push(timings.fetchEnd - timings.fetchStart); 133 | stats.load.push(loadEnd - timings.loadStart); 134 | updateInfo(); 135 | !once && fetchNext(); 136 | once = false; 137 | }; 138 | 139 | // Interrupts the fetch loop. 140 | function stop() { 141 | running = false; 142 | document.getElementById('btn').innerText = 'Start'; 143 | } 144 | 145 | // Callback for the start/stop button. Runs or interrupts the fetch loop. 146 | function startStop() { 147 | if (running) { 148 | stop(); 149 | return; 150 | } 151 | var start = function() { 152 | document.getElementById('btn').innerText = 'Stop'; 153 | running = true; 154 | fetchNext(); 155 | } 156 | if (navigator.getBattery) { 157 | navigator.getBattery().then(function(battery) { 158 | stats.initialBatt = stats.currentBatt = battery.level; 159 | if (battery.charging) { 160 | alert('Device is currently charging, the test will likely run until' 161 | + ' you stop it manually.'); 162 | } 163 | start(); 164 | }); 165 | } else { 166 | alert('Device doesn\'t support battery API, you\'ll have to manually stop' 167 | + ' the test.'); 168 | start(); 169 | } 170 | }; 171 | 172 | // Battery level change handler. Logs the fetched images count per dropped %. 173 | function batteryChangeHandler(level) { 174 | if (running && !!stats.initialBatt) { 175 | if (!stats.batt) { 176 | stats.batt = []; 177 | } 178 | if (stats.batt.length > 0) { 179 | stats.batt.push( 180 | counter - stats.batt.reduce(function(a, b) { 181 | return a + b; 182 | })); 183 | } else if (stats.initialBatt - level < 0.02) { 184 | // First battery level log. We skip the first percent drop because 185 | // it was probably not entirely consumed by the test and would skew 186 | // the stats. 187 | stats.firstPercentDrop = counter; 188 | } else { 189 | // First entry in the batt stats (count of images for the first full 190 | // percent drop). 191 | stats.batt.push(counter - stats.firstPercentDrop); 192 | } 193 | if ((stats.initialBatt - level) >= params.batteryDrop / 100) { 194 | stop(); 195 | } 196 | } 197 | stats.currentBatt = level; 198 | updateInfo(); 199 | } 200 | 201 | // Monitors battery level changes. 202 | if (navigator.getBattery) { 203 | navigator.getBattery().then(function(battery) { 204 | battery.onlevelchange = function() { 205 | batteryChangeHandler(battery.level); 206 | }; 207 | }); 208 | }; 209 | 210 | // Utility function. 211 | function average(arr) { 212 | return arr.reduce((a, b) => a + b) / arr.length; 213 | }; 214 | 215 | // Build the GUI. 216 | var gui = new PaperGUI(); 217 | gui.add(params, 'showInfo').name('Show stats (uses more battery)') 218 | .onChange(updateInfo); 219 | gui.add(params, 'batteryDrop').min(2).name('Stop test after battery drop (%)'); 220 | gui.add(params, 'presetUrl', images).name('Target image').onChange(stop); 221 | gui.add(params, 'customUrl').name('Custom image URL').onChange(stop); 222 | params.test = function() { 223 | !running && fetchNext(true); 224 | }; 225 | gui.add(params, 'test').name('Test image load'); 226 | params.reset = function() { 227 | resetStats(); 228 | }; 229 | gui.add(params, 'reset').name('Reset stats'); 230 | 231 | // Bonus feature: export stats object to clipboard. 232 | if (document.queryCommandSupported('copy')) { 233 | params.copyStats = function() { 234 | window.getSelection().removeAllRanges(); 235 | var textContainerEl = document.querySelector('.copyme'); 236 | textContainerEl.value = JSON.stringify(stats); 237 | var range = document.createRange(); 238 | range.selectNode(textContainerEl); 239 | window.getSelection().addRange(range); 240 | try { 241 | var successful = document.execCommand('copy'); 242 | alert(successful ? 'Stats have been copied to your clipboard as a JSON string.' : 'Copy command failed!'); 243 | } catch(e) { 244 | alert('Woops. Clouldn\'t copy the data to the clipboard!' + e); 245 | } 246 | }; 247 | gui.add(params, 'copyStats').name('Export stats'); 248 | } 249 | -------------------------------------------------------------------------------- /memory_study/0001-RAM-study-patch.patch: -------------------------------------------------------------------------------- 1 | From f1d06a9c46114a3b11597b494e3946c4d680ab7c Mon Sep 17 00:00:00 2001 2 | From: Pascal Massimino 3 | Date: Fri, 19 May 2017 13:05:02 +0200 4 | Subject: [PATCH] RAM-study patch 5 | 6 | Change-Id: I4fc234702a5aecdc1b1447a1629cabb04ac49be8 7 | --- 8 | examples/ram_test.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 9 | makefile.unix | 7 ++- 10 | src/utils/utils.c | 7 ++- 11 | 3 files changed, 150 insertions(+), 3 deletions(-) 12 | create mode 100644 examples/ram_test.c 13 | 14 | diff --git a/examples/ram_test.c b/examples/ram_test.c 15 | new file mode 100644 16 | index 00000000..4ac80a3b 17 | --- /dev/null 18 | +++ b/examples/ram_test.c 19 | @@ -0,0 +1,139 @@ 20 | +// Copyright 2017 Google Inc. All Rights Reserved. 21 | +// 22 | +// Use of this source code is governed by a BSD-style license 23 | +// that can be found in the COPYING file in the root of the source 24 | +// tree. An additional intellectual property rights grant can be found 25 | +// in the file PATENTS. All contributing project authors may 26 | +// be found in the AUTHORS file in the root of the source tree. 27 | +// ----------------------------------------------------------------------------- 28 | +// 29 | +// Simple decoding test for monitoring RAM usage 30 | +// 31 | +// Author: Skal (pascal.massimino@gmail.com) 32 | + 33 | +#include 34 | +#include 35 | +#include 36 | +#include 37 | + 38 | +#ifdef HAVE_CONFIG_H 39 | +#include "webp/config.h" 40 | +#endif 41 | + 42 | +#include "../examples/example_util.h" 43 | +#include "../imageio/image_enc.h" 44 | +#include "../imageio/webpdec.h" 45 | + 46 | +static uint8_t* AllocateExternalBuffer(WebPDecoderConfig* config, 47 | + WebPOutputFileFormat format, 48 | + int use_external_memory) { 49 | + uint8_t* external_buffer = NULL; 50 | + WebPDecBuffer* const output_buffer = &config->output; 51 | + int w = config->input.width; 52 | + int h = config->input.height; 53 | + if (config->options.use_scaling) { 54 | + w = config->options.scaled_width; 55 | + h = config->options.scaled_height; 56 | + } else if (config->options.use_cropping) { 57 | + w = config->options.crop_width; 58 | + h = config->options.crop_height; 59 | + } 60 | + if (format >= RGB && format <= rgbA_4444) { 61 | + const int bpp = (format == RGB || format == BGR) ? 3 62 | + : (format == RGBA_4444 || format == rgbA_4444 || 63 | + format == RGB_565) ? 2 64 | + : 4; 65 | + uint32_t stride = bpp * w + 7; // <- just for exercising 66 | + external_buffer = (uint8_t*)malloc(stride * h); 67 | + if (external_buffer == NULL) return NULL; 68 | + output_buffer->u.RGBA.stride = stride; 69 | + output_buffer->u.RGBA.size = stride * h; 70 | + output_buffer->u.RGBA.rgba = external_buffer; 71 | + } else { // YUV and YUVA 72 | + const int has_alpha = WebPIsAlphaMode(output_buffer->colorspace); 73 | + uint8_t* tmp; 74 | + uint32_t stride = w + 3; 75 | + uint32_t uv_stride = (w + 1) / 2 + 13; 76 | + uint32_t total_size = stride * h * (has_alpha ? 2 : 1) 77 | + + 2 * uv_stride * (h + 1) / 2; 78 | + assert(format >= YUV && format <= YUVA); 79 | + external_buffer = (uint8_t*)malloc(total_size); 80 | + if (external_buffer == NULL) return NULL; 81 | + tmp = external_buffer; 82 | + output_buffer->u.YUVA.y = tmp; 83 | + output_buffer->u.YUVA.y_stride = stride; 84 | + output_buffer->u.YUVA.y_size = stride * h; 85 | + tmp += output_buffer->u.YUVA.y_size; 86 | + if (has_alpha) { 87 | + output_buffer->u.YUVA.a = tmp; 88 | + output_buffer->u.YUVA.a_stride = stride; 89 | + output_buffer->u.YUVA.a_size = stride * h; 90 | + tmp += output_buffer->u.YUVA.a_size; 91 | + } else { 92 | + output_buffer->u.YUVA.a = NULL; 93 | + output_buffer->u.YUVA.a_stride = 0; 94 | + } 95 | + output_buffer->u.YUVA.u = tmp; 96 | + output_buffer->u.YUVA.u_stride = uv_stride; 97 | + output_buffer->u.YUVA.u_size = uv_stride * (h + 1) / 2; 98 | + tmp += output_buffer->u.YUVA.u_size; 99 | + 100 | + output_buffer->u.YUVA.v = tmp; 101 | + output_buffer->u.YUVA.v_stride = uv_stride; 102 | + output_buffer->u.YUVA.v_size = uv_stride * (h + 1) / 2; 103 | + tmp += output_buffer->u.YUVA.v_size; 104 | + assert(tmp <= external_buffer + total_size); 105 | + } 106 | + output_buffer->is_external_memory = use_external_memory; 107 | + return external_buffer; 108 | +} 109 | + 110 | +// declared in src/utils/utils.c 111 | +extern size_t high_water_mark; 112 | + 113 | +int main(int argc, const char *argv[]) { 114 | + int c; 115 | + for (c = 1; c < argc; ++c) { 116 | + const char *in_file = argv[c]; 117 | + 118 | + WebPDecoderConfig config; 119 | + WebPDecBuffer* const output_buffer = &config.output; 120 | + WebPBitstreamFeatures* const bitstream = &config.input; 121 | + WebPOutputFileFormat format = RGBA; // rgbA? 122 | + uint8_t* external_buffer = NULL; 123 | + const uint8_t* data = NULL; 124 | + size_t data_size = 0; 125 | + 126 | + if (!WebPInitDecoderConfig(&config)) { 127 | + fprintf(stderr, "Library version mismatch!\n"); 128 | + return -1; 129 | + } 130 | + output_buffer->colorspace = MODE_RGBA; 131 | + if (!LoadWebP(in_file, &data, &data_size, bitstream)) { 132 | + return -1; 133 | + } 134 | + external_buffer = AllocateExternalBuffer(&config, format, 1); 135 | + if (external_buffer == NULL) return -1; 136 | + 137 | + { 138 | + VP8StatusCode status; 139 | + high_water_mark = 0; 140 | + status = DecodeWebPIncremental(data, data_size, &config); 141 | + if (status != VP8_STATUS_OK) { 142 | + PrintWebPError(in_file, status); 143 | + return -1; 144 | + } 145 | + printf("%s %d x %d %d %lu\n", 146 | + in_file, bitstream->width, bitstream->height, 147 | + bitstream->width * bitstream->height, 148 | + high_water_mark); 149 | + } 150 | + WebPFreeDecBuffer(output_buffer); 151 | + free((void*)external_buffer); 152 | + free((void*)data); 153 | + } 154 | + 155 | + return 0; 156 | +} 157 | + 158 | +//------------------------------------------------------------------------------ 159 | diff --git a/makefile.unix b/makefile.unix 160 | index c8b38e55..45139909 100644 161 | --- a/makefile.unix 162 | +++ b/makefile.unix 163 | @@ -332,7 +332,7 @@ OUT_LIBS += imageio/libimageenc.a 164 | OUT_LIBS += src/libwebpdecoder.a 165 | OUT_LIBS += src/libwebp.a 166 | EXTRA_LIB = extras/libwebpextras.a 167 | -OUT_EXAMPLES = examples/cwebp examples/dwebp 168 | +OUT_EXAMPLES = examples/cwebp examples/dwebp examples/ram_test 169 | EXTRA_EXAMPLES = examples/gif2webp examples/vwebp examples/webpmux \ 170 | examples/anim_diff examples/img2webp examples/webpinfo 171 | OTHER_EXAMPLES = extras/get_disto extras/webp_quality extras/vwebp_sdl 172 | @@ -382,6 +382,7 @@ src/demux/libwebpdemux.a: $(LIBWEBPDEMUX_OBJS) 173 | examples/anim_diff: examples/anim_diff.o $(ANIM_UTIL_OBJS) $(GIFDEC_OBJS) 174 | examples/cwebp: examples/cwebp.o 175 | examples/dwebp: examples/dwebp.o 176 | +examples/ram_test: examples/ram_test.o 177 | examples/gif2webp: examples/gif2webp.o $(GIFDEC_OBJS) 178 | examples/vwebp: examples/vwebp.o 179 | examples/webpmux: examples/webpmux.o 180 | @@ -404,6 +405,10 @@ examples/dwebp: imageio/libimageenc.a 181 | examples/dwebp: imageio/libimageio_util.a 182 | examples/dwebp: src/libwebp.a 183 | examples/dwebp: EXTRA_LIBS += $(DWEBP_LIBS) 184 | +examples/ram_test: imageio/libimagedec.a 185 | +examples/ram_test: imageio/libimageio_util.a 186 | +examples/ram_test: src/libwebp.a 187 | +examples/ram_test: EXTRA_LIBS += $(DWEBP_LIBS) 188 | examples/gif2webp: examples/libexample_util.a imageio/libimageio_util.a 189 | examples/gif2webp: examples/libgifdec.a src/mux/libwebpmux.a src/libwebp.a 190 | examples/gif2webp: EXTRA_LIBS += $(GIF_LIBS) 191 | diff --git a/src/utils/utils.c b/src/utils/utils.c 192 | index dfcd3ab2..1270d095 100644 193 | --- a/src/utils/utils.c 194 | +++ b/src/utils/utils.c 195 | @@ -41,7 +41,7 @@ 196 | // This can be used to emulate environment with very limited memory. 197 | // Example: export MALLOC_LIMIT=64000000 && ./examples/dwebp picture.webp 198 | 199 | -// #define PRINT_MEM_INFO 200 | + #define PRINT_MEM_INFO 201 | // #define PRINT_MEM_TRAFFIC 202 | // #define MALLOC_FAIL_AT 203 | // #define MALLOC_LIMIT 204 | @@ -66,9 +66,12 @@ struct MemBlock { 205 | }; 206 | 207 | static MemBlock* all_blocks = NULL; 208 | + 209 | +extern size_t high_water_mark; 210 | + 211 | static size_t total_mem = 0; 212 | static size_t total_mem_allocated = 0; 213 | -static size_t high_water_mark = 0; 214 | +size_t high_water_mark = 0; 215 | static size_t mem_limit = 0; 216 | 217 | static int exit_registered = 0; 218 | -- 219 | 2.13.0.303.g4ebf302169-goog 220 | 221 | -------------------------------------------------------------------------------- /memory_study/README.md: -------------------------------------------------------------------------------- 1 | # Study of memory usage during WebP decoding 2 | 3 | * Goal: 4 | Evaluate the memory consumption when decoding a WebP file to an external buffer 5 | * Non-goal: encoding memory isn’t measured, nor processing speed. 6 | 7 | ## Key result: 8 | * Lossy: RAM used in bytes = 127. * width 9 | * Lossless: RAM using in bytes = 4.1 * width * height 10 | 11 | ## Setup & methodology: 12 | * libwebp has a built-in mechanism 13 | for tracking memory traffic (cf src/utils/utils.c). 14 | * We patch the libwebp 0.6.0 tree to export the high-water memory mark variable. 15 | * We compile a very simple program (ram_test.c) to incrementally decode some WebP bitstreams to an external RGBA buffer. 16 | 17 | ``` 18 | wget https://github.com/webmproject/libwebp/archive/v0.6.0.tar.gz 19 | tar xzf v0.6.0.tar.gz 20 | cd libwebp-0.6.0/ 21 | patch -p1 < ../0001-RAM-study-patch.patch 22 | make -j -f makefile.unix examples/ram_test 23 | cp examples/ram_test .. 24 | cd .. 25 | ``` 26 | 27 | * Using ./ram_test, we then record the memory used for the decoding process (only!) 28 | and correlate this data to the image dimensions. 29 | 30 | ## Corpus 31 | 32 | We used crawled images from the web (JPEG and PNG), transcoded with default parameters. 33 | These images are converted to WebP (lossy and lossless) using the 'generate_webp.sh' script. 34 | Altogether, this represents 5194 files in each categories. 35 | 36 | ## Results: 37 | 38 | * Some statistics about the width distribution of the input corpus: 39 | 40 | ![width distribution](https://github.com/webmproject/libwebp-demo/blob/master/memory_study/width_distrib.png) 41 | 42 | * Log-scale size distribution: 43 | 44 | ![size distribution](https://github.com/webmproject/libwebp-demo/blob/master/memory_study/size_distrib.png) 45 | 46 | * Memory usage as function of number of pixels for lossless mode: 47 | 48 | ![memory vs size lossless](https://github.com/webmproject/libwebp-demo/blob/master/memory_study/memory_vs_size_lossless.png) 49 | 50 | * Memory usage as function of width for lossy mode: 51 | 52 | ![memory vs width lossy](https://github.com/webmproject/libwebp-demo/blob/master/memory_study/memory_vs_width_lossy.png) 53 | 54 | Note that few lossless images scale with the width only (or, at, least with much less memory than the total size). 55 | This corresponds to a very special case and combination of coding tools. 56 | 57 | 58 | ## Conclusion: 59 | 60 | Lossy decoding scales with the width of the image (by design). Approximately: memory_bytes ~= width * 127 61 | 62 | Lossless decoding scales with the total number of pixels as: memory_bytes ~= 4.1 * width * height 63 | -------------------------------------------------------------------------------- /memory_study/generate_webp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # script to generate the source WebP files to be decoded 4 | # during the memory test. 5 | # 6 | # Output directory is /tmp/RAM-study by default. 7 | # 'cwebp' needs to be already installed. 8 | 9 | out="/tmp/RAM-study" 10 | mkdir ${out} 11 | mkdir ${out}/lossy 12 | mkdir ${out}/lossless 13 | 14 | i=0 15 | for f in $*; do 16 | cwebp $f -noalpha -o ${out}/lossy/${i}.webp -quiet 17 | cwebp $f -z 6 -o ${out}/lossless/${i}.webp -quiet 18 | ((i += 1)) 19 | done 20 | -------------------------------------------------------------------------------- /memory_study/memory_vs_size_lossless.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webmproject/libwebp-demo/02e9afe116dc68d70278e5ed5d8e5716688e2b01/memory_study/memory_vs_size_lossless.png -------------------------------------------------------------------------------- /memory_study/memory_vs_width_lossy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webmproject/libwebp-demo/02e9afe116dc68d70278e5ed5d8e5716688e2b01/memory_study/memory_vs_width_lossy.png -------------------------------------------------------------------------------- /memory_study/run_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # decode all the WebP files generated by the 'generate_webp.sh' script 4 | # and record memory consumption. 5 | # Then, plot the curves with gnuplot. 6 | 7 | out="/tmp/RAM-study" 8 | 9 | ./ram_test ${out}/lossy/*.webp > LOG0 10 | ./ram_test ${out}/lossless/*.webp > LOG1 11 | 12 | sort LOG0 -n -k 2 > LOG0_S # sort by width 13 | sort LOG1 -n -k 5 > LOG1_S # sort by size 14 | 15 | # This gnuplot command will generate the following curves: 16 | # width_distrib.png 17 | # size_distrib.png 18 | # memory_vs_width_lossy.png 19 | # memory_vs_size_lossless.png 20 | gnuplot < graph.plt 21 | -------------------------------------------------------------------------------- /memory_study/size_distrib.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webmproject/libwebp-demo/02e9afe116dc68d70278e5ed5d8e5716688e2b01/memory_study/size_distrib.png -------------------------------------------------------------------------------- /memory_study/width_distrib.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webmproject/libwebp-demo/02e9afe116dc68d70278e5ed5d8e5716688e2b01/memory_study/width_distrib.png --------------------------------------------------------------------------------