zetbaitsu/Compressor: Android Image Compression with a Constraint Pipeline
An android image compression library.
At a glance
- What is it?
- Compressor is a Kotlin library that shrinks large photos on Android by running a list of constraints against a file. It is aimed at apps that upload user photos, and its API is small enough to read in one sitting.
- Who is it for?
- Adopt Compressor if your Android app needs to shrink camera photos before upload and you want the resize, quality, format and target size decisions expressed as a readable constraint list. Do not adopt it if you need a single pass that reliably lands under a byte budget on every device, or if you are not already using coroutines.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly Kotlin, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Compressor solves for Android apps that handle camera photos
A modern phone camera produces files that are far larger than most backends want to store or transmit. The usual fix is to decode the bitmap, resize it, re-encode it at a lower quality, and write it somewhere. That sequence is not hard, but it is easy to get wrong: you have to pick a sample size that avoids loading a full-resolution bitmap into memory, you have to close streams, and you have to decide what to do when the result is still too big. Compressor packages that sequence behind a builder. The README describes it as a library that turns large photos into smaller ones "with very less or negligible loss in quality of the image." The audience is Android developers writing in Kotlin who already have a File pointing at a photo and want a smaller File back. It is not a general-purpose image processor, and the README gives no indication that it does anything other than compress a single image file at a time.
The constraint pipeline: how a compression request is actually executed
The design that separates Compressor from a one-call resize helper is the Constraint interface. A constraint is an object with two methods, isSatisfied and satisfy. The compressor collects a list of them, checks each one against the current image file, and calls satisfy on the ones that report false. The built-in constraints cover the operations you would expect: resolution, quality, format, and a byte size target. The README shows a full custom configuration that sets resolution(1280, 720), quality(80), format(Bitmap.CompressFormat.WEBP) and size(2_097_152) for a 2 MB ceiling. Because constraints are just objects, you can write your own. The README's example, MyLowerCaseNameConstraint, does not touch pixels at all: it renames the file to lower case and returns the renamed File. That is a useful illustration of the abstraction's real shape. It is not an image pipeline with hooks; it is a loop over arbitrary file transformations, and image operations happen to be the ones shipped in the box. The size constraint is the one worth thinking about, because a byte target is not something a resize can guarantee in one step. The documentation does not describe the iteration strategy the size constraint uses, so how many encode passes a stubborn image costs is not something you can determine from the README.
Installing Compressor and compressing your first file
Compressor is published to Maven Central under the group id zelory, and the README's Gradle section gives a single dependency line. Add it to the module that handles images.
dependencies {
implementation 'id.zelory:compressor:3.0.1'
}After a Gradle sync, the Compressor object is available. The simplest call takes a Context and the source File and returns the compressed File. Since version 3 the library is built on Kotlin coroutines, and the README is explicit that the call should be made from a coroutine scope.
lifecycleScope.launch {
val compressedImageFile = Compressor.compress(context, actualImageFile)
}If you need the result somewhere specific rather than the library's default location, the builder form takes a destination. The default() call applies the library's standard constraint set, and destination(myFile) redirects the output.
val compressedImageFile = Compressor.compress(context, actualImageFile) {
default()
destination(myFile)
}For most apps the first snippet is enough. If you want a smaller output than the defaults produce, the README shows narrowing the default with named arguments, for example default(width = 640, format = Bitmap.CompressFormat.WEBP).
Where Compressor is the wrong tool
The constraint model has a sharp edge. A constraint reports whether it is satisfied and then transforms the file; nothing in the interface expresses ordering, cost, or the relationship between constraints. If you set both a resolution and a byte size ceiling, the README does not say which is applied first, whether the size target is re-checked after the resolution change, or what happens when the two cannot both be met. For a strict upload limit, that ambiguity matters. A pipeline you write yourself with explicit decode, scale and encode steps gives you a predictable number of passes and a result you can assert on. Compressor gives you a declarative list and a result you inspect after the fact. The second limitation is the coroutine requirement. The README states that calling Compressor should be done from a coroutine scope, and the escape hatch for running on the main thread is to pass Dispatchers.Main as the third argument. An app that has not adopted coroutines, or that wraps this in a Java-only module, is fighting the library's grain. Third, the README documents no error contract. There is no listed exception type, no return of null, and no discussion of what happens when the source file is unreadable or the destination is not writable. You will find out by running it, and you should handle the failure yourself rather than assume the library reports it in a particular way.
Compressor compared with writing the BitmapFactory path yourself
The real alternative is not another library. It is doing the work directly with BitmapFactory and Bitmap.compress. That path gives you full control: you choose inSampleSize before decoding, you decide the exact number of encode attempts, and you know precisely when memory is released. The cost is code you have to maintain and test, and it is code that tends to drift between screens. Compressor's advantage is that the resize, quality, format and size decisions live in one place and read as a list. Its disadvantage is that the same decisions are less visible at the call site. If your app compresses in one place with one fixed policy, hand-rolled code is competitive and removes a dependency. If compression policy varies by screen, by upload target or by user setting, the constraint list starts to pay for itself. The extension mechanism is the clearest expression of that: the README shows wrapping a custom constraint in an extension function so a call site reads as a single named operation.
Maintenance status, licence and upgrade cost
The last push to the default branch was on 2026-03-05. The most recent release listed is v3.0.1 from 2021-03-22, with v3.0.0 before it in 2020 and a v2.1.1 line that was published the same day as v3.0.1. That gap between release tags and repository activity is the thing to weigh: the API you depend on has been stable for years, which cuts both ways. Stable means few surprises. It also means that if you hit one of the ambiguities described above, you are more likely to work around it than to see it resolved. The repository is not archived. The licence file is not present at the top level of the repository, and the README's License section carries the Apache License 2.0 text with a 2016 copyright line for Zetra. Apache 2.0 is a permissive licence with an explicit patent grant and a notice requirement, but the absence of a standalone LICENSE file is worth confirming with whoever handles licensing on your team before you ship. Upgrading from the 2.x line is not a drop-in change: v3 moved the library onto coroutines, and the README points readers of the old API to README_v2.md rather than describing the migration.
Editorial conclusion
Adopt Compressor if your Android app needs to shrink camera photos before upload and you want the resize, quality, format and target size decisions expressed as a readable constraint list. Do not adopt it if you need a single pass that reliably lands under a byte budget on every device, or if you are not already using coroutines. Before wiring it in, verify the licence text in the repository, confirm that the last push on 2026-03-05 is recent enough for your release cycle, and check whether the WEBP output format is acceptable to every consumer of the file downstream.
Frequently asked questions
How do I install zetbaitsu/Compressor in an Android project?
Add the dependency id.zelory:compressor:3.0.1 to your module's Gradle dependencies block and sync. The README gives that single line under its Gradle heading.
How do I use zetbaitsu/Compressor to compress an image?
Call Compressor.compress(context, actualImageFile) from a coroutine scope, for example inside lifecycleScope.launch. For custom output, pass a builder block with default(), resolution(), quality(), format() or size().
Can zetbaitsu/Compressor run on the main thread?
Yes. The README shows passing Dispatchers.Main as the third argument to Compressor.compress, which runs the operation on the main thread instead of a background dispatcher.
How do I add a custom constraint to zetbaitsu/Compressor?
Implement the Constraint interface with isSatisfied and satisfy, then add it inside the builder block with constraint(MyConstraint()). The README shows a file-renaming constraint as the example.
What image format does zetbaitsu/Compressor output?
The format is set by the format() constraint, and the README's examples pass Bitmap.CompressFormat.WEBP. The default() constraint set applies the library's own format choice, which the README does not spell out.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/zetbaitsu-compressor)