Skip to content

Commit

Permalink
Patch v1.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
xerdnu committed Jan 31, 2025
1 parent db68d25 commit dd8e4e1
Show file tree
Hide file tree
Showing 15 changed files with 382 additions and 28 deletions.
2 changes: 2 additions & 0 deletions BlastedImage.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@ Pod::Spec.new do |s|

s.dependency "React-Core"
s.dependency "SDWebImage"
s.dependency "SDWebImageSVGCoder"
s.dependency 'SDWebImageAVIFCoder'

end
36 changes: 35 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,37 @@
## [1.0.8] (2025-01-12)
## [1.1.0] (2025-01-31)

#### 🚨 Breaking Changes (Android Only)
- **Upgraded Glide from 4.12.0 to 4.16.0** which may cause duplicate class conflicts if another package in your project also includes Glide.

If you encounter a build error stating `com.bumptech.glide.GeneratedAppGlideModuleImpl is defined multiple times` it means another package in your project is also bundling Glide.

To resolve this check the error message for the conflicting package and remove it if not needed.

Alternatively ensure all dependencies use the same Glide version by adding the following to your android/app/build.gradle file:
```
dependencies {
implementation 'com.github.bumptech.glide:glide:4.16.0'
}
```
#### ✨ New Features

- Added support for returning image dimensions via `onLoad` in combination by setting the `returnSize` param. ([#32](https://github.com/xerdnu/react-native-blasted-image/issues/32))
- Added support for `SVG` images.
- Added support for `AVIF` images. ([#20](https://github.com/xerdnu/react-native-blasted-image/issues/20))
- Added support for changing color with `tintColor` parameter. ([#11](https://github.com/xerdnu/react-native-blasted-image/issues/11))

#### 🔥 Improvements

- Reworked parts of the code and created renderKey that will make sure images that failed in retry operation re-renders. ([#31](https://github.com/xerdnu/react-native-blasted-image/issues/31))
- Reworked other parts of the code for increased performance.

#### 🔄 Changes

- Updated documentation.
- Changed repo for Android buildscript.
- Increased caching limits.

## [1.0.9] (2025-01-12)

#### New Features

Expand Down Expand Up @@ -223,6 +256,7 @@

- Initial release.

[1.1.0]: https://github.com/xerdnu/react-native-blasted-image/compare/v1.0.9...v1.1.0
[1.0.9]: https://github.com/xerdnu/react-native-blasted-image/compare/v1.0.8...v1.0.9
[1.0.8]: https://github.com/xerdnu/react-native-blasted-image/compare/v1.0.7...v1.0.8
[1.0.7]: https://github.com/xerdnu/react-native-blasted-image/compare/v1.0.6...v1.0.7
Expand Down
37 changes: 31 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@

A simple yet powerful image component for React Native, powered by [Glide](https://github.com/bumptech/glide) (Android) and [SDWebImage](https://github.com/SDWebImage/SDWebImage) (iOS).

## Support My Work! 🎉
I truly appreciate your support! If you'd like to help me out, the best way is to check out my latest app — **LogoDuel**.

**LogoDuel** is a fun, turn-based multiplayer trivia game where you challenge friends (or foes!) to guess famous logos. Test your brand knowledge and see who comes out on top!

🚀 **Powered by BlastedImage** for performant and optimized image handling.<br><br>👉 Download now and let the logo battle begin!

[![Get it on Google Play](https://img.shields.io/badge/Google_Play-Download-green?logo=google-play&style=for-the-badge)](https://play.google.com/store/apps/details?id=se.netblast.logoduellen) [![Download on the App Store](https://img.shields.io/badge/App_Store-Download-blue?logo=apple&style=for-the-badge)](https://apps.apple.com/us/app/logoduel/id6470379520)

## Description
Caching remote images has always been a challenge for me with the Image component in React Native. This simplified, yet powerful component, addresses that issue head-on. It offers a robust and performant mechanism for caching remote images, ensuring they're displayed quickly.<br><br>Leveraging the strengths of Glide and SDWebImage, it supports both memory and disk caching for remote images. The newly added Hybrid Assets feature allows you to bundle remote assets in your build, fetching from the network only when necessary. Notably, while it provides these enhanced capabilities for remote images, it seamlessly integrates with the standard React Native Image component when handling local assets using require.

Expand All @@ -16,6 +25,20 @@ Caching remote images has always been a challenge for me with the Image componen
- **Robust Caching**: Benefits from both memory and disk caching for maximum performance.
- **Hybrid Assets**: Bundle remote assets within your build and only fetch from the network if assets are not included.

## Supported Image Types

|&nbsp;&nbsp;Filetype&nbsp;&nbsp;|&nbsp;&nbsp;Android&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;iOS&nbsp;&nbsp;&nbsp;&nbsp;|
| :--------: | :-----: | :-: |
|PNG|||
|APNG|||
|JPEG|||
|SVG|||
|GIF|||
|WebP|||
|AVIF|||
|HEIC|||
|ICO|||

## Installation
### With bare React Native
#### Using npm:
Expand Down Expand Up @@ -59,11 +82,13 @@ import BlastedImage from 'react-native-blasted-image';
| `height` | `Number` | (Optional) Specifies the height of the image. `Overrides height in style` | 100 |
| `resizeMode` | `String` | (Optional) Resize the image with one of the options: `cover`&nbsp;`contain`&nbsp;`center`&nbsp;`stretch` | cover |
| `isBackground` | `Boolean` | (Optional) Makes the image act as a container background similar to the native `ImageBackground` component | false |
| `fallbackSource` | `Object` | (Optional) Object containing a `uri` string for a custom error image. | - |
| `returnSize` | `Boolean` | (Optional) Specifies if `Size` parameters should be returned in `onLoad` callback. | false |
| `retries` | `Number` | (Optional) Specifies the number of retry attempts if the image fails to load. | 3 |
| `onLoad` | `Function` | (Optional) Callback function that gets called when the image has loaded succesfully. | - |
| `tintColor` | `String` | (Optional) Specifies tintColor for the image using hexadecimal/named colors. | - |
| `fallbackSource` | `Object` | (Optional) Object containing a `uri` string for a custom error image. | - |
| `onLoad` | `Function` | (Optional) Callback function that gets called when the image has loaded succesfully.<br>Returns `Size` parameters of the source image if `returnSize` set to `true` | - |
| `onError` | `Function` | (Optional) Callback function that gets called when there was an error loading the image. | - |
| `style` | `Object` | (Optional) Styles to be applied to the image, e.g., `{borderRadius:20}`.<br>See [View Style Props](https://reactnative.dev/docs/view-style-props) for all available styles.
| `style` | `Object` | (Optional) Styles to be applied to the image, e.g., `{borderRadius:20}`.<br>See [View Style Props](https://reactnative.dev/docs/view-style-props) for all available styles.
### Source Parameter
| Parameter | Type | Description | Default |
|--------------|-------------------|-----------------------------------------------------------------------------------------------------|---------|
Expand Down Expand Up @@ -176,11 +201,11 @@ useEffect(() => {
This component was created with inspiration from [react-native-fast-image](https://github.com/DylanVann/react-native-fast-image) that also uses [Glide](https://github.com/bumptech/glide) and [SDWebImage](https://github.com/SDWebImage/SDWebImage). But due to its lack of ongoing maintenance i felt the need to develop this new image component to continue providing robust and performant caching functionality.

## Support My Work! 🎉
I truly appreciate your support! If you'd like to help me out, the best way is to check out my latest app — LogoDuel.
I truly appreciate your support! If you'd like to help me out, the best way is to check out my latest app — **LogoDuel**.

LogoDuel is a fun, fast-paced multiplayer trivia game where you challenge friends (or foes!) to guess famous logos. Test your brand knowledge and see who comes out on top!
**LogoDuel** is a fun, turn-based multiplayer trivia game where you challenge friends (or foes!) to guess famous logos. Test your brand knowledge and see who comes out on top!

👉 Download now and let the logo battle begin!
🚀 **Powered by BlastedImage** for performant and optimized image handling.<br><br>👉 Download now and let the logo battle begin!

[![Get it on Google Play](https://img.shields.io/badge/Google_Play-Download-green?logo=google-play&style=for-the-badge)](https://play.google.com/store/apps/details?id=se.netblast.logoduellen) [![Download on the App Store](https://img.shields.io/badge/App_Store-Download-blue?logo=apple&style=for-the-badge)](https://apps.apple.com/us/app/logoduel/id6470379520)

Expand Down
15 changes: 12 additions & 3 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
buildscript {
repositories {
jcenter()
google()
mavenCentral()
}

dependencies {
Expand Down Expand Up @@ -32,7 +33,15 @@ repositories {
dependencies {
implementation 'com.facebook.react:react-native:+'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'

// Glide v4.16.0
implementation 'com.github.bumptech.glide:glide:4.16.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0'

// AVIF support
implementation 'com.github.bumptech.glide:avif-integration:4.16.0'

// SVG support
api 'com.caverock:androidsvg-aar:1.4'
}

Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public BlastedImageModule(ReactApplicationContext reactContext) {
if (!isGlideInitialized) {
Glide.init(reactContext, new GlideBuilder()
.setDiskCache(new InternalCacheDiskCacheFactory(reactContext, 1024 * 1024 * 1024)) //1gb disk cache
.setMemoryCache(new LruResourceCache(100 * 1024 * 1024)) // 100mb memory cache
.setMemoryCache(new LruResourceCache(256 * 1024 * 1024)) // 256mb memory cache
);
isGlideInitialized = true;
}
Expand Down Expand Up @@ -129,6 +129,7 @@ public Object prepareGlideUrl(String imageUrl, boolean hybridAssets, String clou
public void loadImage(String imageUrl, boolean skipMemoryCache, boolean hybridAssets, String cloudUrl, Promise promise) {

try {
// See BlastedImageModule.m for details regarding NativeEventEmitters (BlastedEventLog etc.)
Object glideUrl = prepareGlideUrl(imageUrl, hybridAssets, cloudUrl, true);

// Is skip skipMemoryCache set for image and should we store it only to disk?
Expand Down
23 changes: 23 additions & 0 deletions android/src/main/java/com/reactlibrary/BlastedViewManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableMap;

import androidx.annotation.Nullable;

import android.widget.ImageView;
import android.view.View;
import android.view.ViewOutlineProvider;
Expand All @@ -16,6 +18,10 @@
import android.graphics.drawable.Drawable;
import android.graphics.Rect;

import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;

import android.util.Log;


Expand Down Expand Up @@ -117,5 +123,22 @@ public void setHeight(ImageView view, int height) {
}
}

@ReactProp(name = "tintColor")
public void setTintColor(ImageView view, @Nullable String color) {
if (color != null && !color.isEmpty()) {
try {
if (!color.startsWith("#") && color.length() == 6 && color.matches("[0-9A-Fa-f]+")) {
color = "#" + color;
}
int parsedColor = Color.parseColor(color);
view.setColorFilter(new PorterDuffColorFilter(parsedColor, PorterDuff.Mode.SRC_IN));
} catch (IllegalArgumentException e) {
Log.e("BlastedViewManager", "Invalid color format: " + color);
}
} else {
view.clearColorFilter();
}
}

// more properties... :)
}
29 changes: 29 additions & 0 deletions android/src/main/java/com/reactlibrary/SvgDecoder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.xerdnu.blastedimage;

import androidx.annotation.NonNull;
import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.ResourceDecoder;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.resource.SimpleResource;
import com.caverock.androidsvg.SVG;
import com.caverock.androidsvg.SVGParseException;
import java.io.IOException;
import java.io.InputStream;

public class SvgDecoder implements ResourceDecoder<InputStream, SVG> {
@Override
public boolean handles(@NonNull InputStream source, @NonNull Options options) {
return true; // Assume all InputStreams are SVG
}

@Override
public Resource<SVG> decode(@NonNull InputStream source, int width, int height, @NonNull Options options)
throws IOException {
try {
SVG svg = SVG.getFromInputStream(source);
return new SimpleResource<>(svg);
} catch (SVGParseException e) {
throw new IOException("Failed to parse SVG", e);
}
}
}
38 changes: 38 additions & 0 deletions android/src/main/java/com/reactlibrary/SvgDrawableResource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.xerdnu.blastedimage;

import android.graphics.drawable.PictureDrawable;
import androidx.annotation.NonNull;
import com.bumptech.glide.load.engine.Resource;

public class SvgDrawableResource implements Resource<PictureDrawable> {
private final PictureDrawable drawable;

public SvgDrawableResource(PictureDrawable drawable) {
if (drawable == null) {
throw new NullPointerException("PictureDrawable must not be null");
}
this.drawable = drawable;
}

@NonNull
@Override
public Class<PictureDrawable> getResourceClass() {
return PictureDrawable.class;
}

@NonNull
@Override
public PictureDrawable get() {
return drawable;
}

@Override
public int getSize() {
return 1;
}

@Override
public void recycle() {
// Do nothing here
}
}
27 changes: 27 additions & 0 deletions android/src/main/java/com/reactlibrary/SvgDrawableTranscoder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.xerdnu.blastedimage;

import android.graphics.Picture;
import android.graphics.drawable.PictureDrawable;


import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
import com.caverock.androidsvg.SVG;

public class SvgDrawableTranscoder implements ResourceTranscoder<SVG, PictureDrawable> {
@Nullable
@Override
public Resource<PictureDrawable> transcode(
@NonNull Resource<SVG> toTranscode,
@NonNull Options options
) {
SVG svg = toTranscode.get();
Picture picture = svg.renderToPicture();
PictureDrawable drawable = new PictureDrawable(picture);
return new SvgDrawableResource(drawable);
}
}
31 changes: 31 additions & 0 deletions android/src/main/java/com/reactlibrary/SvgModule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.xerdnu.blastedimage;

import android.content.Context;
import android.graphics.drawable.PictureDrawable;
import android.util.Log;

import androidx.annotation.NonNull;

import com.bumptech.glide.Glide;
import com.bumptech.glide.Registry;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
import com.caverock.androidsvg.SVG;

import java.io.InputStream;

@GlideModule
public class SvgModule extends AppGlideModule {
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
Log.d("SvgModule", "Registering SVG support in Glide");
registry
.register(SVG.class, PictureDrawable.class, new SvgDrawableTranscoder())
.append(InputStream.class, SVG.class, new SvgDecoder());
}

@Override
public boolean isManifestParsingEnabled() {
return false;
}
}
4 changes: 3 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ declare module 'react-native-blasted-image' {
interface BlastedImageProps {
resizeMode?: 'cover' | 'contain' | 'stretch' | 'repeat' | 'center';
isBackground?: boolean;
returnSize?: boolean;
fallbackSource?: ImageSourcePropType;
source: SourceProp | number;
width?: number;
height?: number;
style?: StyleProp<ViewStyle>;
onLoad?: () => void;
onLoad?: (size?: ImageSize | null) => void;
onError?: (error: Error) => void;
children?: React.ReactNode;
retries?: number;
tintColor?: string;
}

interface BlastedImageStatic {
Expand Down
Loading

0 comments on commit dd8e4e1

Please sign in to comment.