-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
toDataURL() enlarges image three times #855
toDataURL() enlarges image three times #855
Comments
I tried to follow the code in the hope to find any clues. But I found nothing. Does anyone know where to look for this odd behaviour? Since the enlargement is exactly 3 times in both width and height I thought I'd find anything related to RGB. I don't know anything of the PNG format, but I suppose an RGB file is 3 times larger than a B/W file (not taking compression differences into account). Could this factor have been erroneously used when calculating pixel width and height somewhere? @msand can you push me in the right direction? Edit: |
@msageryd I think the scaling might be related to this: Could probably take the density into consideration when creating the bitmap, and add a scaling matrix to the canvas, before rendering: |
Try perhaps something like this: String toDataURL() {
float invScale = 1f / mScale;
Bitmap bitmap = Bitmap.createBitmap(
Math.round(getWidth() * invScale),
Math.round(getHeight() * invScale),
Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bitmap);
c.scale(invScale, invScale);
drawChildren(c);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
bitmap.recycle();
byte[] bitmapBytes = stream.toByteArray();
return Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
} |
The scaling call might actually causing another extra scaling, accounting for it in the bitmap size might be enough. |
@Mand, tried that modified function above and it doesn't seem to work. For testing purposes I updated the function within node modules and then rebuild within xcode - perhaps I did something wrong and that's why I do not see the change (output is still 3x the normal size)? |
These changes are for android not ios, you need to rebuild in android studio instead of xcode. And, i haven't tested these changes |
Thanks @msans. Obviously this approach is a workaround which requires re-rendering of svg, so ideally this issue should be solved natively in react-native-svg instead. |
I think for now at least, unless we want to make a major version jump, if we want to make any changes here we need to keep them backwards compatible. We could perhaps introduce a parameter, to say that you want a downscaled version rather than the scaled bitmap size. |
I think a parameter would be great. I think there are two distinct use cases for toDataUrl().
|
I've implemented support for an optional second argument to the toDataURL method, as an options object parameter having a width and height property. Would this cover your needs? https://github.com/react-native-community/react-native-svg/tree/toDataURL Example: <Svg
ref={ele => {
ele.toDataURL(
res => {
console.log(res);
},
{
width: 100,
height: 100,
},
);
}}
width="100%"
height="100%"
viewBox="0 0 100 100"
> |
@msand strikes again =) |
Would be great to validate the implementation before merging and releasing it at least. I haven't opened the output or drawn it on screen yet, only made sure that it gives output on both ios and android so far. |
Ok, sure. |
@msageryd Can you try with the develop branch? I think i have it working correctly now: import React, { Component } from 'react';
import { View, Image } from 'react-native';
import Svg, { Path } from 'react-native-svg';
class SvgComponent extends Component {
state = {};
onRef = ref => {
if (ref && !this.state.base64) {
ref.toDataURL(
base64 => {
this.setState({ base64 });
},
{ width: 200, height: 200 },
);
}
};
render() {
return (
<View style={{ flex: 1 }}>
<Svg
ref={this.onRef}
viewBox="0 0 400 400"
width="100%"
{...this.props}
height="250"
>
<Path fill="none" stroke="#00f" d="M1 1h398v398H1z" />
<Path
d="M100 100h200L200 300z"
fill="red"
stroke="#00f"
strokeWidth={3}
/>
</Svg>
<View
style={{
width: '100%',
flex: 1,
marginTop: 5,
borderWidth: 1,
alignItems: 'center',
justifyContent: 'center',
}}
>
{this.state.base64 && (
<Image
source={{ uri: `data:image/png;base64,${this.state.base64}` }}
style={{ width: 250, height: 250 }}
/>
)}
</View>
</View>
);
}
}
export default SvgComponent; |
Btw, I've added support for calling toDataUrl already in the ref function, don't have to wait for onLayout in javascript anymore. |
Sorry for the delay on my side, I'm swamped for the moment.. Everything seems to work as usual if I omit the size object in the toDataUrl call. When I apply a size object the following happens:
|
More info..
|
Kind of works..
If I call |
@msand I'm sorry, but this version gets me into more trouble. Somehow my since long trusted rendering loop is now delayed at least one tick. My SVG objects are not rendered until I pan the screen, i.e. forces a re-render. The first render doesn't seem to happen. This part in my app has been stable for a long time, so I suspect something fishy is going on in your develop branch. Do you have any clues, or should I try to isolate the problem (might take a while)? |
This sounds quite strange, relatively little has changed in the code since v9.2.4 |
I'll probably need to port the logic from android to ios, for handling the case where not all your content is rendered yet. Android needed it even for small amounts of content, the view creation commands would be in the queue when toDataUrl gets called, and even putting it in the end of the queue then wasn't enough. So it needs to be queued to happen only when the rendering is actually done. |
In that case I suspect that something was lingering in my cache again. The cache seems to bite me quite often when I jump in and out of different versions libraries. I've just found some new magic that might help. I'll give it a go later today and report back here. The new magic: It seems that the cache is problematic for more than me. I really hope that this can be addressed in RN going forward. https://gist.github.com/jarretmoses/c2e4786fd342b3444f3bc6beff32098d |
Can you try with v9.3.5 ? |
@msageryd There were some issues with text layout I've mostly fixed now, not perfectly spec conformant yet, but probably good enough for most cases. Can you try v9.3.6 ? |
I feel guilty for not putting time into this while you are working so hard. Right now I'm in the middle of a release and I'm working around the clock. If all goes well I've upgraded both my app and the servers by tonight. Right after this release, testing your fixes is on top of my list. |
At last I have had some time for this.
pseudo code:
|
BTW, the base64 output includes linebreaks, but only sometimes. Is the linebreaks inserted when the files are really big? My test file is about 7000x5000px. I think that the linebreaks should be fine in general, but maybe iOS has problems with this? Anyway, I have to strip them out in order to get a working png file.
|
@msand please tell me if you want me to test this in any other way. |
Can you try with this in the latest version? App.js import React, {Component} from 'react';
import {View, Image, StyleSheet} from 'react-native';
import Svg, {Path} from 'react-native-svg';
class SvgComponent extends Component {
state = {};
onRef = ref => {
if (ref && !this.state.base64) {
ref.toDataURL(
base64 => {
this.setState({base64});
},
{width: 200, height: 200},
);
}
};
render() {
return (
<View style={styles.container}>
<Svg
width="100%"
height="250"
ref={this.onRef}
viewBox="0 0 400 400"
{...this.props}>
<Path fill="none" stroke="#00f" d="M1 1h398v398H1z" />
<Path
d="M100 100h200L200 300z"
fill="red"
stroke="#00f"
strokeWidth={3}
/>
</Svg>
<View style={styles.imageView}>
{this.state.base64 && (
<Image
source={{uri: `data:image/png;base64,${this.state.base64}`}}
style={styles.image}
/>
)}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
image: {width: 250, height: 250},
container: {flex: 1},
imageView: {
flex: 1,
marginTop: 5,
width: '100%',
borderWidth: 1,
alignItems: 'center',
justifyContent: 'center',
},
});
export default SvgComponent; |
It is still an issue on iOS. The size still gets tripled. It works fine on android. Tested with v9.13.3. |
@damnhipster @msageryd Can you try with the latest commit from the develop branch? |
🎉 This issue has been resolved in version 9.13.4 🎉 The release is available on: Your semantic-release bot 📦🚀 |
It seems that toDataURL scales up the png image tree times. The following code should give a 100x100px image file, shouldn't it? I get a 300x300px file.
If this is by design, I'll go ahead and compensate for it. But if it's a bug that will be fixed eventually my compensation will lead to downscaled images when it's fixed.
The text was updated successfully, but these errors were encountered: