Skip to content

Commit

Permalink
allow to use different blur placeholder modes (#49070)
Browse files Browse the repository at this point in the history
### What?

allows to change blur mode for structured images

also improve performance for static metadata images in app dir by avoiding computing blur placeholder

### Why?

we might want to change the blur mode (in dicussion)

### How?

adds an enum to control the mode
  • Loading branch information
sokra committed May 2, 2023
1 parent 90153ff commit 1a2bed2
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 34 deletions.
3 changes: 2 additions & 1 deletion packages/next-swc/crates/next-core/src/app_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ use crate::{
context::{get_edge_compile_time_info, get_edge_resolve_options_context},
transition::NextEdgeTransition,
},
next_image::module::StructuredImageModuleType,
next_image::module::{BlurPlaceholderMode, StructuredImageModuleType},
next_route_matcher::NextParamsMatcherVc,
next_server::context::{
get_server_compile_time_info, get_server_module_options_context,
Expand Down Expand Up @@ -783,6 +783,7 @@ import {}, {{ chunks as {} }} from "COMPONENT_{}";
inner_module_id,
StructuredImageModuleType::create_module(
SourceAssetVc::new(*path).into(),
BlurPlaceholderMode::None,
state.context,
)
.into(),
Expand Down
36 changes: 31 additions & 5 deletions packages/next-swc/crates/next-core/src/next_image/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,43 @@ use turbo_binding::{

use super::source_asset::StructuredImageSourceAsset;

#[turbo_tasks::value(serialization = "auto_for_input")]
#[derive(Clone, Copy, Debug, PartialOrd, Ord, Hash)]
pub enum BlurPlaceholderMode {
/// Do not generate a blur placeholder at all.
None,
/// Generate a blur placeholder as data url and embed it directly into the
/// JavaScript code. This needs to compute the blur placeholder eagerly and
/// has a higher computation overhead.
DataUrl,
/// Avoid generating a blur placeholder eagerly and uses `/_next/image`
/// instead to compute one on demand. This changes the UX slightly (blur
/// placeholder is shown later than it should be) and should
/// only be used for development.
NextImageUrl,
}

/// Module type that analyzes images and offers some meta information like
/// width, height and blur placeholder as export from the module.
#[turbo_tasks::value]
pub struct StructuredImageModuleType {}
pub struct StructuredImageModuleType {
pub blur_placeholder_mode: BlurPlaceholderMode,
}

impl StructuredImageModuleType {
pub(crate) fn create_module(
source: AssetVc,
blur_placeholder_mode: BlurPlaceholderMode,
context: AssetContextVc,
) -> EcmascriptModuleAssetVc {
let static_asset = StaticModuleAssetVc::new(source, context);
EcmascriptModuleAssetVc::new_with_inner_assets(
StructuredImageSourceAsset { image: source }.cell().into(),
StructuredImageSourceAsset {
image: source,
blur_placeholder_mode,
}
.cell()
.into(),
context,
Value::new(EcmascriptModuleAssetType::Ecmascript),
EcmascriptInputTransformsVc::empty(),
Expand All @@ -49,8 +73,10 @@ impl StructuredImageModuleType {
#[turbo_tasks::value_impl]
impl StructuredImageModuleTypeVc {
#[turbo_tasks::function]
pub fn new() -> Self {
StructuredImageModuleTypeVc::cell(StructuredImageModuleType {})
pub fn new(blur_placeholder_mode: Value<BlurPlaceholderMode>) -> Self {
StructuredImageModuleTypeVc::cell(StructuredImageModuleType {
blur_placeholder_mode: blur_placeholder_mode.into_value(),
})
}
}

Expand All @@ -63,6 +89,6 @@ impl CustomModuleType for StructuredImageModuleType {
context: AssetContextVc,
_part: Option<ModulePartVc>,
) -> AssetVc {
StructuredImageModuleType::create_module(source, context).into()
StructuredImageModuleType::create_module(source, self.blur_placeholder_mode, context).into()
}
}
84 changes: 59 additions & 25 deletions packages/next-swc/crates/next-core/src/next_image/source_asset.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::io::Write;

use anyhow::{bail, Result};
use serde::Serialize;
use turbo_binding::{
turbo::{
tasks::primitives::StringVc,
Expand All @@ -17,6 +16,8 @@ use turbo_binding::{
},
};

use super::module::BlurPlaceholderMode;

fn modifier() -> StringVc {
StringVc::cell("structured image object".to_string())
}
Expand All @@ -30,22 +31,12 @@ fn blur_options() -> BlurPlaceholderOptionsVc {
.cell()
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ImageExport<'a> {
width: u32,
height: u32,
#[serde(rename = "blurDataURL")]
blur_data_url: Option<&'a str>,
blur_width: u32,
blur_height: u32,
}

/// An source asset that transforms an image into javascript code which exports
/// an object with meta information like width, height and a blur placeholder.
#[turbo_tasks::value(shared)]
pub struct StructuredImageSourceAsset {
pub image: AssetVc,
pub blur_placeholder_mode: BlurPlaceholderMode,
}

#[turbo_tasks::value_impl]
Expand All @@ -62,20 +53,63 @@ impl Asset for StructuredImageSourceAsset {
bail!("Input source is not a file and can't be transformed into image information");
};
let mut result = RopeBuilder::from("");
let info = get_meta_data(self.image.ident(), content, Some(blur_options())).await?;
let info = ImageExport {
width: info.width,
height: info.height,
blur_width: info.blur_placeholder.as_ref().map_or(0, |p| p.width),
blur_height: info.blur_placeholder.as_ref().map_or(0, |p| p.height),
blur_data_url: info.blur_placeholder.as_ref().map(|p| p.data_url.as_str()),
};
writeln!(result, "import src from \"IMAGE\";",)?;
writeln!(
result,
"export default {{ src, ...{} }}",
StringifyJs(&info)
)?;
let blur_options = blur_options();
match self.blur_placeholder_mode {
BlurPlaceholderMode::NextImageUrl => {
let info = get_meta_data(self.image.ident(), content, None).await?;
let width = info.width;
let height = info.height;
let blur_options = blur_options.await?;
let (blur_width, blur_height) = if width > height {
(
blur_options.size,
(blur_options.size as f32 * height as f32 / width as f32).ceil() as u32,
)
} else {
(
(blur_options.size as f32 * width as f32 / height as f32).ceil() as u32,
blur_options.size,
)
};
writeln!(
result,
"export default {{ src, width: {width}, height: {height}, blurDataURL: \
`/_next/image?w={blur_width}&q={quality}&url=${{encodeURIComponent(src)}}`, \
blurWidth: {blur_width}, blurHeight: {blur_height} }}",
width = StringifyJs(&info.width),
height = StringifyJs(&info.height),
quality = StringifyJs(&blur_options.quality),
blur_width = StringifyJs(&blur_width),
blur_height = StringifyJs(&blur_height),
)?;
}
BlurPlaceholderMode::DataUrl => {
let info = get_meta_data(self.image.ident(), content, Some(blur_options)).await?;
writeln!(
result,
"export default {{ src, width: {width}, height: {height}, blurDataURL: \
{blur_data_url}, blurWidth: {blur_width}, blurHeight: {blur_height} }}",
width = StringifyJs(&info.width),
height = StringifyJs(&info.height),
blur_data_url =
StringifyJs(&info.blur_placeholder.as_ref().map(|p| p.data_url.as_str())),
blur_width =
StringifyJs(&info.blur_placeholder.as_ref().map_or(0, |p| p.width)),
blur_height =
StringifyJs(&info.blur_placeholder.as_ref().map_or(0, |p| p.height),),
)?;
}
BlurPlaceholderMode::None => {
let info = get_meta_data(self.image.ident(), content, None).await?;
writeln!(
result,
"export default {{ src, width: {width}, height: {height} }}",
width = StringifyJs(&info.width),
height = StringifyJs(&info.height),
)?;
}
};
Ok(AssetContent::File(FileContent::Content(result.build().into()).cell()).cell())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ use turbo_binding::{
},
},
};
use turbo_tasks::trace::TraceRawVcs;
use turbo_tasks::{trace::TraceRawVcs, Value};

use crate::next_image::StructuredImageModuleTypeVc;
use crate::next_image::{module::BlurPlaceholderMode, StructuredImageModuleTypeVc};

/// Returns a rule which applies the Next.js page export stripping transform.
pub async fn get_next_pages_transforms_rule(
Expand Down Expand Up @@ -98,7 +98,7 @@ pub fn get_next_image_rule() -> ModuleRule {
ModuleRuleCondition::ResourcePathEndsWith(".svg".to_string()),
]),
vec![ModuleRuleEffect::ModuleType(ModuleType::Custom(
StructuredImageModuleTypeVc::new().into(),
StructuredImageModuleTypeVc::new(Value::new(BlurPlaceholderMode::DataUrl)).into(),
))],
)
}
Expand Down

0 comments on commit 1a2bed2

Please sign in to comment.