Skip to content

Commit

Permalink
allow to use different blur placeholder modes
Browse files Browse the repository at this point in the history
improve performance for static metadata images in app dir by avoiding computing blur placeholder
  • Loading branch information
sokra committed May 2, 2023
1 parent e58c63b commit f1d3926
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 need 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.
NextImage,
}

/// 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::NextImage => {
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 f1d3926

Please sign in to comment.