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