reduce android image size with lossless webp

This commit is contained in:
Samuel Newman
2026-09-03 18:43:44 +03:00
parent 8af501de62
commit d6d4fc761e
37 changed files with 75 additions and 129 deletions
+5 -5
View File
@@ -19,7 +19,7 @@ For the assets Bluesky itself owns, we are not treating anyone's past use as bad
| [`assets/illustrations/`](#1-commissioned-artwork--licensed-to-bluesky-only) | Owen D. Pomery, via Brilliant Artists Ltd | No | Replace |
| [`assets/icons/`](#2-licensed-icon-system--not-ours-to-pass-on) (top level), Central icon glyphs in `bskyembed/assets/` except the Starter Pack mark | Iconists (David & Storm GbR) | No | Source your own |
| [Bluesky marks](#3-bluesky-trademarks-and-brand-assets) — app icons, logos, favicons | Bluesky Social PBC | No | Replace |
| [`assets/kawaii.png`, `assets/kawaii_smol.png`](#4-community-and-contest-artwork--credited-but-not-ours-to-license) | [@sawaratsuki.bsky.social](https://bsky.app/profile/sawaratsuki.bsky.social) | No | Replace or remove |
| [`assets/kawaii.webp`, `assets/kawaii_smol.webp`](#4-community-and-contest-artwork--credited-but-not-ours-to-license) | [@sawaratsuki.bsky.social](https://bsky.app/profile/sawaratsuki.bsky.social) | No | Replace or remove |
| [`assets/icons/custom_logo_japan.svg`](#4-community-and-contest-artwork--credited-but-not-ours-to-license) | A Bluesky Japan logo contest entrant | No | Replace or remove |
| [`assets/icons/apple_logo.svg`](#5-third-party-trademarks) | Apple Inc. | No | Rests on your own basis |
| [`assets/icons/android_logo.svg`](#5-third-party-trademarks) | Google LLC | No | Rests on your own basis |
@@ -40,7 +40,7 @@ Assets are scoped by directory wherever possible, so that adding a file to a car
**`assets/illustrations/`**
The landing-screen illustration, in light and dark variants (`assets/illustrations/illustration-mobile.png` and `assets/illustrations/illustration-mobile-dark.png`), used by `src/view/com/auth/SplashScreen.tsx`.
The landing-screen illustration, in light and dark variants (`assets/illustrations/illustration-mobile.webp` and `assets/illustrations/illustration-mobile-dark.webp`), used by `src/view/com/auth/SplashScreen.tsx`.
**Rights holder: Owen D. Pomery**, represented by Brilliant Artists Ltd. Bluesky Social PBC commissioned the work and holds a usage license. Copyright remains with the artist. Our license is limited to Bluesky's own products and channels, is exclusive to us, and does not permit us to sublicense the artwork or to distribute modified versions of it.
@@ -73,8 +73,8 @@ You may refer to Bluesky by name to describe interoperability or origin — for
- `assets/icon-android-foreground.png`
- `assets/icon-android-monochrome.png`
- `assets/icon-android-notification.png`
- `assets/splash/splash.png`
- `assets/splash/splash-dark.png`
- `assets/splash/splash.webp`
- `assets/splash/splash-dark.webp`
- `assets/splash/android-splash-logo-white.png`
- `assets/icons/logomark.svg`
- `assets/icons/newskie.svg`
@@ -104,7 +104,7 @@ These files stay in this repository because the app needs them to build. **If yo
These are third-party artworks that appear in the app with attribution. We hold no license that lets us pass rights to them on to you.
- `assets/kawaii.png` and `assets/kawaii_smol.png` — **rights holder:
- `assets/kawaii.webp` and `assets/kawaii_smol.webp` — **rights holder:
[@sawaratsuki.bsky.social](https://bsky.app/profile/sawaratsuki.bsky.social)**. Shown as an opt-in variant and credited in `src/view/shell/Drawer.tsx` and `src/view/shell/desktop/RightNav.tsx`.
- `assets/icons/custom_logo_japan.svg`**rights holder: the entrant who won the Bluesky Japan logo contest.**
+2 -2
View File
@@ -344,12 +344,12 @@ module.exports = function (_config) {
ios: {
enableFullScreenImage_legacy: true, // iOS only
backgroundColor: '#006AFF', // primary_500
image: './assets/splash/splash.png',
image: './assets/splash/splash.webp',
resizeMode: 'cover',
dark: {
enableFullScreenImage_legacy: true, // iOS only
backgroundColor: '#002861', // primary_900
image: './assets/splash/splash-dark.png',
image: './assets/splash/splash-dark.webp',
resizeMode: 'cover',
},
},
Binary file not shown.

After

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1012 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 606 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

+2 -2
View File
@@ -21,9 +21,9 @@ import * as SplashScreen from 'expo-splash-screen'
import {Logotype} from '#/view/icons/Logotype'
import {atoms as a} from '#/alf'
// @ts-expect-error
import splashImagePointer from '../assets/splash/splash.png'
import splashImagePointer from '../assets/splash/splash.webp'
// @ts-expect-error
import darkSplashImagePointer from '../assets/splash/splash-dark.png'
import darkSplashImagePointer from '../assets/splash/splash-dark.webp'
const splashImageUri = RNImage.resolveAssetSource(splashImagePointer)!.uri
const darkSplashImageUri = RNImage.resolveAssetSource(
@@ -77,7 +77,7 @@ export function InitialVerificationAnnouncement() {
]}>
<Image
accessibilityIgnoresInvertColors
source={require('../../../../assets/images/initial_verification_announcement_1.png')}
source={require('../../../../assets/images/initial_verification_announcement_1.webp')}
style={[
{
aspectRatio: 353 / 160,
@@ -79,7 +79,7 @@ function Inner({
]}>
<Image
accessibilityIgnoresInvertColors
source={require('../../../assets/images/initial_verification_announcement_1.png')}
source={require('../../../assets/images/initial_verification_announcement_1.webp')}
style={[
{
aspectRatio: 353 / 160,
+2 -2
View File
@@ -448,8 +448,8 @@ export function useOTAUpdates() {
export const splash = (scheme: 'light' | 'dark') => {
const source =
scheme === 'light'
? require('../../../assets/splash/splash.png')
: require('../../../assets/splash/splash-dark.png')
? require('../../../assets/splash/splash.webp')
: require('../../../assets/splash/splash-dark.webp')
return {
image: RNImage.resolveAssetSource(source)!.uri,
@@ -13,10 +13,7 @@ export function AppIconImage({
const t = useTheme()
return (
<Image
source={platform({
ios: icon.iosImage(),
android: icon.androidImage(),
})}
source={icon.image()}
style={[
{width: size, height: size},
platform({
@@ -0,0 +1,22 @@
export const appIconImages = {
default_light: () =>
require('../../../../assets/app-icons/android_icon_legacy_light.webp'),
default_dark: () =>
require('../../../../assets/app-icons/android_icon_legacy_dark.webp'),
core_aurora: () =>
require('../../../../assets/app-icons/android_icon_core_aurora.webp'),
core_sunrise: () =>
require('../../../../assets/app-icons/android_icon_core_sunrise.webp'),
core_sunset: () =>
require('../../../../assets/app-icons/android_icon_core_sunset.webp'),
core_midnight: () =>
require('../../../../assets/app-icons/android_icon_core_midnight.webp'),
core_flat_blue: () =>
require('../../../../assets/app-icons/android_icon_core_flat_blue.webp'),
core_flat_white: () =>
require('../../../../assets/app-icons/android_icon_core_flat_white.webp'),
core_flat_black: () =>
require('../../../../assets/app-icons/android_icon_core_flat_black.webp'),
core_classic: () =>
require('../../../../assets/app-icons/android_icon_core_classic.webp'),
}
@@ -0,0 +1,22 @@
export const appIconImages = {
default_light: () =>
require('../../../../assets/app-icons/ios_icon_legacy_light.png'),
default_dark: () =>
require('../../../../assets/app-icons/ios_icon_legacy_dark.png'),
core_aurora: () =>
require('../../../../assets/app-icons/ios_icon_core_aurora.png'),
core_sunrise: () =>
require('../../../../assets/app-icons/ios_icon_core_sunrise.png'),
core_sunset: () =>
require('../../../../assets/app-icons/ios_icon_core_sunset.png'),
core_midnight: () =>
require('../../../../assets/app-icons/ios_icon_core_midnight.png'),
core_flat_blue: () =>
require('../../../../assets/app-icons/ios_icon_core_flat_blue.png'),
core_flat_white: () =>
require('../../../../assets/app-icons/ios_icon_core_flat_white.png'),
core_flat_black: () =>
require('../../../../assets/app-icons/ios_icon_core_flat_black.png'),
core_classic: () =>
require('../../../../assets/app-icons/ios_icon_core_classic.png'),
}
@@ -4,6 +4,5 @@ import type * as DynamicAppIcon from '@bsky.app/expo-dynamic-app-icon'
export type AppIconSet = {
id: DynamicAppIcon.IconName
name: string
iosImage: () => ImageSource
androidImage: () => ImageSource
image: () => ImageSource
}
@@ -2,6 +2,7 @@ import {useMemo} from 'react'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {appIconImages} from '#/screens/Settings/AppIconSettings/appIconImages'
import {type AppIconSet} from '#/screens/Settings/AppIconSettings/types'
export function useAppIconSets() {
@@ -12,30 +13,12 @@ export function useAppIconSets() {
{
id: 'default_light',
name: _(msg({context: 'Name of app icon variant', message: 'Light'})),
iosImage: () => {
return require(
`../../../../assets/app-icons/ios_icon_legacy_light.png`,
)
},
androidImage: () => {
return require(
`../../../../assets/app-icons/android_icon_legacy_light.png`,
)
},
image: appIconImages.default_light,
},
{
id: 'default_dark',
name: _(msg({context: 'Name of app icon variant', message: 'Dark'})),
iosImage: () => {
return require(
`../../../../assets/app-icons/ios_icon_legacy_dark.png`,
)
},
androidImage: () => {
return require(
`../../../../assets/app-icons/android_icon_legacy_dark.png`,
)
},
image: appIconImages.default_dark,
},
] satisfies AppIconSet[]
@@ -46,118 +29,50 @@ export function useAppIconSets() {
{
id: 'core_aurora',
name: _(msg({context: 'Name of app icon variant', message: 'Aurora'})),
iosImage: () => {
return require(
`../../../../assets/app-icons/ios_icon_core_aurora.png`,
)
},
androidImage: () => {
return require(
`../../../../assets/app-icons/android_icon_core_aurora.png`,
)
},
image: appIconImages.core_aurora,
},
// {
// id: 'core_bonfire',
// name: _(msg({ context: 'Name of app icon variant', message: 'Bonfire' })),
// iosImage: () => {
// return require(`../../../../assets/app-icons/ios_icon_core_bonfire.png`)
// },
// androidImage: () => {
// return require(`../../../../assets/app-icons/android_icon_core_bonfire.png`)
// },
// image: appIconImages.core_bonfire,
// },
{
id: 'core_sunrise',
name: _(msg({context: 'Name of app icon variant', message: 'Sunrise'})),
iosImage: () => {
return require(
`../../../../assets/app-icons/ios_icon_core_sunrise.png`,
)
},
androidImage: () => {
return require(
`../../../../assets/app-icons/android_icon_core_sunrise.png`,
)
},
image: appIconImages.core_sunrise,
},
{
id: 'core_sunset',
name: _(msg({context: 'Name of app icon variant', message: 'Sunset'})),
iosImage: () => {
return require(
`../../../../assets/app-icons/ios_icon_core_sunset.png`,
)
},
androidImage: () => {
return require(
`../../../../assets/app-icons/android_icon_core_sunset.png`,
)
},
image: appIconImages.core_sunset,
},
{
id: 'core_midnight',
name: _(
msg({context: 'Name of app icon variant', message: 'Midnight'}),
),
iosImage: () => {
return require(
`../../../../assets/app-icons/ios_icon_core_midnight.png`,
)
},
androidImage: () => {
return require(
`../../../../assets/app-icons/android_icon_core_midnight.png`,
)
},
image: appIconImages.core_midnight,
},
{
id: 'core_flat_blue',
name: _(
msg({context: 'Name of app icon variant', message: 'Flat Blue'}),
),
iosImage: () => {
return require(
`../../../../assets/app-icons/ios_icon_core_flat_blue.png`,
)
},
androidImage: () => {
return require(
`../../../../assets/app-icons/android_icon_core_flat_blue.png`,
)
},
image: appIconImages.core_flat_blue,
},
{
id: 'core_flat_white',
name: _(
msg({context: 'Name of app icon variant', message: 'Flat White'}),
),
iosImage: () => {
return require(
`../../../../assets/app-icons/ios_icon_core_flat_white.png`,
)
},
androidImage: () => {
return require(
`../../../../assets/app-icons/android_icon_core_flat_white.png`,
)
},
image: appIconImages.core_flat_white,
},
{
id: 'core_flat_black',
name: _(
msg({context: 'Name of app icon variant', message: 'Flat Black'}),
),
iosImage: () => {
return require(
`../../../../assets/app-icons/ios_icon_core_flat_black.png`,
)
},
androidImage: () => {
return require(
`../../../../assets/app-icons/android_icon_core_flat_black.png`,
)
},
image: appIconImages.core_flat_black,
},
{
id: 'core_classic',
@@ -167,16 +82,7 @@ export function useAppIconSets() {
message: 'Bluesky Classic™',
}),
),
iosImage: () => {
return require(
`../../../../assets/app-icons/ios_icon_core_classic.png`,
)
},
androidImage: () => {
return require(
`../../../../assets/app-icons/android_icon_core_classic.png`,
)
},
image: appIconImages.core_classic,
},
] satisfies AppIconSet[]
+2 -2
View File
@@ -12,9 +12,9 @@ import {Logotype} from '#/view/icons/Logotype'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
// @ts-expect-error
import splashImagePointer from '../../../../assets/illustrations/illustration-mobile.png'
import splashImagePointer from '../../../../assets/illustrations/illustration-mobile.webp'
// @ts-expect-error
import darkSplashImagePointer from '../../../../assets/illustrations/illustration-mobile-dark.png'
import darkSplashImagePointer from '../../../../assets/illustrations/illustration-mobile-dark.webp'
const splashImageUri = RNImage.resolveAssetSource(splashImagePointer)!.uri
const darkSplashImageUri = RNImage.resolveAssetSource(
darkSplashImagePointer,
+2 -2
View File
@@ -42,8 +42,8 @@ export const Logo = forwardRef(function LogoImpl(props: Props, ref) {
isJapanLogo
? require('../../../assets/icons/custom_logo_japan.svg')
: size > 100
? require('../../../assets/kawaii.png')
: require('../../../assets/kawaii_smol.png')
? require('../../../assets/kawaii.webp')
: require('../../../assets/kawaii_smol.webp')
}
accessibilityLabel="Bluesky"
accessibilityHint=""