Git Product home page Git Product logo

apng-drawable's Introduction

ApngDrawable

Maven Central

ApngDrawable is fast and light weight Animated Portable Network Graphics(APNG) image decoder library for Android platform. ApngDrawable is written in Kotlin and C++.

How to use

Use Gradle to build the library. Download it from Maven Central and add configurations in the build.gradle file as follows.

// In your top-level project's `build.gradle`
allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

// In your app project's `build.gradle`
dependencies {
  implementation 'com.linecorp:apng:x.y.z'
}

If using Kotlin Gradle DSL, add configurations in the build.gradle.kts file as follows.

// In your top-level project's `build.gradle.kts`
allprojects {
    repositories {
        mavenCentral()
    }
}

// In your app project's `build.gradle.kts`
dependencies {
  implementation("com.linecorp:apng:x.y.z")
}

Getting started

You can decode from a lot of source types, e.g. File, InputStream and Resources.

// Decode from File
val drawable1 = ApngDrawable.decode(File("path/to/file"))

// Decode from InputStream
val drawable2 = File("path/to/file").inputStream().use {
    ApngDrawable.decode(it)
}

// Decode from Resources
val drawable3 = ApngDrawable.decode(context.resources, R.raw.apng_image)

You can find a more advanced way of using the library from the example.

How to build

Note: This operation is necessary when building from code. It's not necessary if you are reading using implementation as shown in "[How to use]".

The patched libpng sources aren't included in the repository. You need to download libpng and apply APNG patch first.

$ cat libpng_version | xargs ./download_libpng_and_apply_apng_patch.sh
$ ./gradlew :sample-app:assembleDebug

How to contribute to ApngDrawable

See CONTRIBUTING.md

If you believe you have discovered a vulnerability or have an issue related to security, please contact the maintainer directly or send us a email to [email protected] before sending a pull request.

License

Copyright 2018 LINE Corporation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

apng-drawable's People

Contributors

ganadist avatar johnbaligod avatar kagemiku avatar r-ralph avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

apng-drawable's Issues

Get Frames as Drawable

Is there any way I can get the individual Frames as individual Drawables or something similar?

Can't compile with NDK r19

* What went wrong:
Execution failed for task ':apng-drawable:externalNativeBuildDebug'.
> Build command failed.
  Error while executing process /Users/user/Library/Android/sdk/cmake/3.6.4111459/bin/cmake with arguments {--build /Users/user/Projects/apng-drawable/apng-drawable/.externalNativeBuild/cmake/debug/x86_64 --target apng-drawable}
  [1/1] Re-running CMake...
  -- Configuring done
  -- Generating done
  -- Build files have been written to: /Users/user/Projects/apng-drawable/apng-drawable/.externalNativeBuild/cmake/debug/x86_64
  [1/24] Building C object CMakeFiles/png.dir/libpng/arm/arm_init.c.o
  [2/24] Building C object CMakeFiles/png.dir/libpng/arm/filter_neon_intrinsics.c.o
  [3/24] Building C object CMakeFiles/png.dir/libpng/png.c.o
  [4/24] Building C object CMakeFiles/png.dir/libpng/pngerror.c.o
  [5/24] Building C object CMakeFiles/png.dir/libpng/pngget.c.o
  [6/24] Building C object CMakeFiles/png.dir/libpng/pngmem.c.o
  [7/24] Building C object CMakeFiles/png.dir/libpng/pngpread.c.o
  [8/24] Building C object CMakeFiles/png.dir/libpng/pngread.c.o
  [9/24] Building C object CMakeFiles/png.dir/libpng/pngrio.c.o
  [10/24] Building C object CMakeFiles/png.dir/libpng/pngrtran.c.o
  [11/24] Building C object CMakeFiles/png.dir/libpng/pngrutil.c.o
  [12/24] Building C object CMakeFiles/png.dir/libpng/pngset.c.o
  [13/24] Building C object CMakeFiles/png.dir/libpng/pngtrans.c.o
  [14/24] Building C object CMakeFiles/png.dir/libpng/pngwio.c.o
  [15/24] Building C object CMakeFiles/png.dir/libpng/pngwrite.c.o
  [16/24] Building C object CMakeFiles/png.dir/libpng/pngwtran.c.o
  [17/24] Building C object CMakeFiles/png.dir/libpng/pngwutil.c.o
  [18/24] Linking C static library libpng.a
  [19/24] Building CXX object CMakeFiles/apng-drawable.dir/apng-drawbale/ApngDecoder.cpp.o
  [20/24] Building CXX object CMakeFiles/apng-drawable.dir/apng-drawbale/ApngDecoderJni.cpp.o
  [21/24] Building CXX object CMakeFiles/apng-drawable.dir/apng-drawbale/ApngFrame.cpp.o
  [22/24] Building CXX object CMakeFiles/apng-drawable.dir/apng-drawbale/ApngImage.cpp.o
  [23/24] Building CXX object CMakeFiles/apng-drawable.dir/apng-drawbale/StreamSource.cpp.o
  [24/24] Linking CXX shared library /Users/user/Projects/apng-drawable/apng-drawable/build/intermediates/cmake/debug/obj/x86_64/libapng-drawable.so
  FAILED: : && /Users/user/Library/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++  --target=x86_64-none-linux-android21 --gcc-toolchain=/Users/user/Library/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64 -fPIC --sysroot /Users/user/Library/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/sysroot -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -Wa,--noexecstack -Wformat -Werror=format-security -stdlib=libc++ -std=c++17 -fno-rtti -fno-exceptions -DBUILD_DEBUG -O0 -fno-limit-debug-info  -Wl,--exclude-libs,libgcc.a -Wl,--exclude-libs,libatomic.a -static-libstdc++ -Wl,--build-id -Wl,--warn-shared-textrel -Wl,--fatal-warnings -Wl,--no-undefined -Qunused-arguments -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -shared -Wl,-soname,libapng-drawable.so -o /Users/user/Projects/apng-drawable/apng-drawable/build/intermediates/cmake/debug/obj/x86_64/libapng-drawable.so CMakeFiles/apng-drawable.dir/apng-drawbale/ApngDecoder.cpp.o CMakeFiles/apng-drawable.dir/apng-drawbale/ApngDecoderJni.cpp.o CMakeFiles/apng-drawable.dir/apng-drawbale/ApngFrame.cpp.o CMakeFiles/apng-drawable.dir/apng-drawbale/ApngImage.cpp.o CMakeFiles/apng-drawable.dir/apng-drawbale/StreamSource.cpp.o  -llog -ljnigraphics libpng.a -lz -latomic -lm "/Users/user/Library/Android/sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/x86_64/libc++_static.a" "/Users/user/Library/Android/sdk/ndk-bundle/sources/cxx-stl/llvm-libc++/libs/x86_64/libc++abi.a" && :
  /Users/user/Library/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include/c++/v1/new:332: error: undefined reference to 'operator delete(void*, std::align_val_t)'
  /Users/user/Library/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include/c++/v1/new:250: error: undefined reference to 'operator new(unsigned long, std::align_val_t)'
  /Users/user/Library/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include/c++/v1/new:250: error: undefined reference to 'operator new(unsigned long, std::align_val_t)'
  /Users/user/Library/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include/c++/v1/new:250: error: undefined reference to 'operator new(unsigned long, std::align_val_t)'
  clang++: error: linker command failed with exit code 1 (use -v to see invocation)
  ninja: build stopped: subcommand failed.

seekTo with frame index

Currently, provides seekTo with duration only and there is no way to seek to specified frame. APNG might have different frame duration on each frame.

Bug: Failed to run sample

I tried simple cloning and opening of the project, but when I tried to build&run, I get this :

C:\Users\User\Desktop\apng-drawable\apng-drawable\src\main\cpp\CMakeLists.txt : C/C++ debug|armeabi-v7a : CMake Error at C:\Users\User\Desktop\apng-drawable\apng-drawable\src\main\cpp\CMakeLists.txt:43 (add_library):
  Cannot find source file:

    C:/Users/User/Desktop/apng-drawable/apng-drawable/src/main/cpp/libpng/arm/arm_init.c

  Tried extensions .c .C .c++ .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp
  .hxx .in .txx

image

How come?

I can't change the size.

How can I change the size?
I tried to set height/width to 'match_parent' but I doesn't shrink and fit to the view.

Introduce a limitter on the max number/size of frame

In the case of an image with an extremely large number of frames specified, the process will be killed due to lack of memory while allocating memory for loading image.
Make it possible to limit the maximum size of an image so that it does not crash when loaded from an untrusted source.

too much memory usage

image
image

i used 2 apng files from asset.
one is 3mb, 100frame, 24fps
one is 1mb, 24frame, 48fps

but there is too much memory usage like screenshots. (1.6gb usage)

is there any memory performance enhancement options?


sorry, i forgot android frame animation is sensitive about pixel.
after resizing image 500x500 to 300x300 it fixed. 😅

download_libpng_and_apply_apng_patch.sh without argument raise error.

I cloned this repository and tried to build, but I found there is no build instruction yet.

And I found there is download_libpng_and_apply_apng_patch.sh , and it seems that it must be run before run gradlew.

So I ran script but I met this error message.

$ ./download_libpng_and_apply_apng_patch.sh 
./download_libpng_and_apply_apng_patch.sh: line 13: $1: unbound variable

My build environment is

OS: ArchLinux (x86_64)
Bash: 4.4.23

Resume feature

Resume: don't reset loop count and don't call callback

bad_alloc/sigabort on malformed apng file

The decoder may lead to a premature termination caused by a very large frameCount being passed to make_unique. In the example below frameCount is 520486912. Maybe this relates to #73 as well.

Output:

libpng warning: sBIT: CRC error
libpng warning: acTL: CRC error
libpng warning: fcTL: CRC error
libpng warning: size in first frame's fcTL must match the size in IHDR
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc

Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50      ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x0000007ff7bb58e8 in __GI_abort () at abort.c:79
#2  0x0000007ff7e7ab28 in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib/aarch64-linux-gnu/libstdc++.so.6
#3  0x0000007ff7e788ac in ?? () from /usr/lib/aarch64-linux-gnu/libstdc++.so.6
#4  0x0000007ff7e788f8 in std::terminate() () from /usr/lib/aarch64-linux-gnu/libstdc++.so.6
#5  0x0000007ff7e78bb0 in __cxa_throw () from /usr/lib/aarch64-linux-gnu/libstdc++.so.6
#6  0x0000007ff7e790e8 in operator new(unsigned long) () from /usr/lib/aarch64-linux-gnu/libstdc++.so.6
#7  0x0000000000409360 in std::make_unique<std::shared_ptr<apng_drawable::ApngFrame> []> (__num=520486912)
    at /usr/lib/gcc/aarch64-linux-gnu/8/../../../../include/c++/8/bits/unique_ptr.h:837
#8  apng_drawable::ApngImage::ApngImage (this=0x846f90, width=4294962472, height=0, frameCount=<optimized out>, loopCount=0) at ApngImage.cpp:30
#9  0x0000000000406ad0 in apng_drawable::ApngDecoder::decode (source=std::unique_ptr<class apng_drawable::StreamSource> = {...}, result=<optimized out>)
    at ApngDecoder.cpp:223
#10 0x0000000000402c14 in main (argc=<optimized out>, argv=<optimized out>) at main.cpp:19

Testcase: crash.apng.zip

Multiple SIGABRT crash reports during decoding phase

We have about 500 crash reports per month, which are difficult to reproduce. Can you please review the attached stacktrace and throw Java Exception instead of crash? Thank you in advance.

SIGABRT Abort program 
    /apex/com.android.runtime/lib/bionic/libc.so:392370 abort
    /apex/com.android.runtime/lib/bionic/libc.so:393739 __assert2
    /data/app/com.photo.app-u2VLzjwd7M7a7x8Ch8Rd4A==/lib/arm/libapng-drawable.so:208563 0xaa38eeb3
    /data/app/com.photo.app-u2VLzjwd7M7a7x8Ch8Rd4A==/lib/arm/libapng-drawable.so:208821 0xaa38efb5
    /data/app/com.photo.app-u2VLzjwd7M7a7x8Ch8Rd4A==/lib/arm/libapng-drawable.so:202099 0xaa38d573
    /data/app/com.photo.app-u2VLzjwd7M7a7x8Ch8Rd4A==/lib/arm/libapng-drawable.so:200227 __cxa_get_exception_ptr
    /data/app/com.photo.app-u2VLzjwd7M7a7x8Ch8Rd4A==/lib/arm/libapng-drawable.so:200171 __cxa_throw
    /data/app/com.photo.app-u2VLzjwd7M7a7x8Ch8Rd4A==/lib/arm/libapng-drawable.so:192599 operator new(unsigned int)
    /data/app/com.photo.app-u2VLzjwd7M7a7x8Ch8Rd4A==/lib/arm/libapng-drawable.so:105311 0xaa375b5f
    /data/app/com.photo.app-u2VLzjwd7M7a7x8Ch8Rd4A==/lib/arm/libapng-drawable.so:105215 apng_drawable::ApngFrame::ApngFrame(unsigned int, unsigned int)
    /data/app/com.photo.app-u2VLzjwd7M7a7x8Ch8Rd4A==/lib/arm/libapng-drawable.so:98233 apng_drawable::ApngDecoder::decode(std::__ndk1::unique_ptr<apng_drawable::StreamSource, std::__ndk1::default_delete<apng_drawable::StreamSource> >, int&)
    /data/app/com.photo.app-u2VLzjwd7M7a7x8Ch8Rd4A==/lib/arm/libapng-drawable.so:100223 Java_com_linecorp_apng_decoder_ApngDecoderJni_decode

Potential secutiry vulnerability in the shared libraries which apng-drawable depends on. Can you help upgrade to patch versions?

Hi, @r-ralph , @johnbaligod , I'd like to report a vulnerability issue in com.linecorp:apng:1.11.0.

Issue Description

com.linecorp:apng:1.11.0 directly or transitively depends on 4 C libraries (.so) cross many platforms(such as x86-64, x86, arm64, armhf). However, I noticed that the shared library is vulnerable, containing the following CVEs:

libapng-drawable.so from C project libpng(version:1.6.35) exposed 1 vulnerabilities:
CVE-2018-14550

Suggested Vulnerability Patch Versions

libpng has fixed the vulnerabilities in versions >=1.6.37

Java build tools cannot report vulnerable C libraries, which may induce potential security issues to many downstream Java projects.
Could you please upgrade the above shared libraries to their patch versions?

Thanks for your help~
Best regards,
Helen Parr

Throw Java exception instead of crash when std:bad_malloc is thrown in native code

We observed this crash in our app:

signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
Abort message: '/usr/local/google/buildbot/src/android/ndk-release-r20/external/libcxx/../../external/libcxxabi/src/abort_message.cpp:73: abort_message: assertion "terminating with uncaught exception of type std::bad_alloc: std::bad_alloc" failed'
    r0  00000000  r1  0000204b  r2  00000006  r3  00000008
    r4  0000204b  r5  0000204b  r6  beaab25c  r7  0000010c
    r8  82000d65  r9  0000013e  r10 b06ead8c  r11 beaab824
    ip  00000041  sp  beaab248  lr  b066704d  pc  b065ed76
backtrace:
    #00 pc 0001cd76  /system/lib/libc.so (abort+58)
    #01 pc 0001cfdf  /system/lib/libc.so (__assert2+22)
    #02 pc 00032f99  /data/app/package-name/lib/arm/libapng-drawable.so
    #03 pc 00033099  /data/app/package-name/lib/arm/libapng-drawable.so
    #04 pc 00031639  /data/app/package-name/lib/arm/libapng-drawable.so
    #05 pc 00030fe7  /data/app/package-name/lib/arm/libapng-drawable.so
    #06 pc 00030faf  /data/app/package-name/lib/arm/libapng-drawable.so (__cxa_throw+74)
    #07 pc 0002f175  /data/app/package-name/lib/arm/libapng-drawable.so (operator new(unsigned int)+56)
    #08 pc 0001984d  /data/app/package-name/lib/arm/libapng-drawable.so
    #09 pc 000197fd  /data/app/package-name/lib/arm/libapng-drawable.so (apng_drawable::ApngFrame::ApngFrame(unsigned int, unsigned int)+32)
    #10 pc 0001802d  /data/app/package-name/lib/arm/libapng-drawable.so (apng_drawable::ApngDecoder::decode(std::__ndk1::unique_ptr<apng_drawable::StreamSource, std::__ndk1::default_delete<apng_drawable::StreamSource>>, int&)+824)
    #11 pc 00018735  /data/app/package-name/lib/arm/libapng-drawable.so (Java_com_linecorp_apng_decoder_ApngDecoderJni_decode+64)
    #12 pc 0008479b  /data/app/package-name/oat/arm/base.odex (offset 0x84000) (com.linecorp.apng.decoder.ApngDecoderJni.decode+130)
    ...

As it is thrown from native code we can't do anything to prevent it from crashing our app. It is nice to throw a Java exception such as OutOfMemoryException to enable the user to handle it (java.lang.OutOfMemoryError is not appropriate as no more memory could be made available by the garbage collector is not necessarily true).

Edit: There is a ErrorCode.ERR_OUT_OF_MEMORY which seems appropriate but is not thrown in this case.

ANR

使用版本:com.linecorp:apng:1.11.0
手机型号:所有
会不定时出现卡顿现象

log:
com.linecorp.apng.decoder.ApngDecoderJni.decode(Native Method)
com.linecorp.apng.decoder.Apng$Companion.decode(Apng.kt:3)
com.linecorp.apng.ApngDrawable$b.try(ApngDrawable.kt:3)
com.linecorp.apng.ApngDrawable$b.new(ApngDrawable.kt:1)
com.linecorp.apng.ApngDrawable$b.this(ApngDrawable.kt:1)
com.xuanyin.utilslibrary.ImageLoader$h.do(ImageLoader.kt:1)
com.xuanyin.utilslibrary.ImageLoader$h.for(ImageLoader.kt:1)
com.bumptech.glide.t.k.extends(SingleRequest.java:12)
com.bumptech.glide.t.k.if(SingleRequest.java:14)
com.bumptech.glide.load.o.l.else(EngineJob.java:1)
com.bumptech.glide.load.o.l$b.run(EngineJob.java:5)
android.os.Handler.handleCallback(Handler.java:900)

invalid sample-app activity name.

When run sample-app on android device, cannot launch app, and print following error messages on logcat.

11-23 18:34:10.162 11374 11374 E AndroidRuntime: Caused by: java.lang.ClassNotFoundException: Didn't find class "com.linecorp.android.apngsample.MainActivity" on path: DexPathList[[zip file "/data/app/com.linecorp.apngsample.debug-sHvmtWzphplGjF0hLXf6eg==/base.apk"],nativeLibraryDirectories=[/data/app/com.linecorp.apngsample.debug-sHvmtWzphplGjF0hLXf6eg==/lib/arm64, /data/app/com.linecorp.apngsample.debug-sHvmtWzphplGjF0hLXf6eg==/base.apk!/lib/arm64-v8a, /system/lib64]]
11-23 18:34:10.162 11374 11374 E AndroidRuntime: 	at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:134)
11-23 18:34:10.162 11374 11374 E AndroidRuntime: 	at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
11-23 18:34:10.162 11374 11374 E AndroidRuntime: 	at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
11-23 18:34:10.162 11374 11374 E AndroidRuntime: 	at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:69)
11-23 18:34:10.162 11374 11374 E AndroidRuntime: 	at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:43)
11-23 18:34:10.162 11374 11374 E AndroidRuntime: 	at android.app.Instrumentation.newActivity(Instrumentation.java:1215)
11-23 18:34:10.162 11374 11374 E AndroidRuntime: 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2832)
11-23 18:34:10.162 11374 11374 E AndroidRuntime: 	... 11 more

On AndroidManifest.xml, it described that have activity named com.linecorp.android.apngsample.MainActivity, but package name of MainActivity is com.linecorp.apngsample

To reduce confusing about class name, it should be use abbreviated class name.

Failure to integrate

I get the following error trying to integrate the library :
implementation("com.linecorp:apng:1.11.0")

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.