image.rs 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. // Copyright 2019-2024 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use crate::embedded_assets::{ensure_out_dir, EmbeddedAssetsError, EmbeddedAssetsResult};
  5. use proc_macro2::{Span, TokenStream};
  6. use quote::{quote, ToTokens};
  7. use std::path::Path;
  8. use syn::{punctuated::Punctuated, Ident, PathArguments, PathSegment, Token};
  9. pub fn include_image_codegen(
  10. path: &Path,
  11. out_file_name: &str,
  12. ) -> EmbeddedAssetsResult<TokenStream> {
  13. let out_dir = ensure_out_dir()?;
  14. let mut segments = Punctuated::new();
  15. segments.push(PathSegment {
  16. ident: Ident::new("tauri", Span::call_site()),
  17. arguments: PathArguments::None,
  18. });
  19. let root = syn::Path {
  20. leading_colon: Some(Token![::](Span::call_site())),
  21. segments,
  22. };
  23. image_icon(&root.to_token_stream(), &out_dir, path, out_file_name)
  24. }
  25. pub(crate) fn image_icon(
  26. root: &TokenStream,
  27. out_dir: &Path,
  28. path: &Path,
  29. out_file_name: &str,
  30. ) -> EmbeddedAssetsResult<TokenStream> {
  31. let extension = path.extension().unwrap_or_default();
  32. if extension == "ico" {
  33. ico_icon(root, out_dir, path, out_file_name)
  34. } else if extension == "png" {
  35. png_icon(root, out_dir, path, out_file_name)
  36. } else {
  37. Err(EmbeddedAssetsError::InvalidImageExtension {
  38. extension: extension.into(),
  39. path: path.to_path_buf(),
  40. })
  41. }
  42. }
  43. pub(crate) fn raw_icon(
  44. out_dir: &Path,
  45. path: &Path,
  46. out_file_name: &str,
  47. ) -> EmbeddedAssetsResult<TokenStream> {
  48. let bytes =
  49. std::fs::read(path).unwrap_or_else(|e| panic!("failed to read icon {}: {}", path.display(), e));
  50. let out_path = out_dir.join(out_file_name);
  51. write_if_changed(&out_path, &bytes).map_err(|error| EmbeddedAssetsError::AssetWrite {
  52. path: path.to_owned(),
  53. error,
  54. })?;
  55. let icon = quote!(::std::option::Option::Some(
  56. include_bytes!(concat!(std::env!("OUT_DIR"), "/", #out_file_name)).to_vec()
  57. ));
  58. Ok(icon)
  59. }
  60. pub(crate) fn ico_icon(
  61. root: &TokenStream,
  62. out_dir: &Path,
  63. path: &Path,
  64. out_file_name: &str,
  65. ) -> EmbeddedAssetsResult<TokenStream> {
  66. let file = std::fs::File::open(path)
  67. .unwrap_or_else(|e| panic!("failed to open icon {}: {}", path.display(), e));
  68. let icon_dir = ico::IconDir::read(file)
  69. .unwrap_or_else(|e| panic!("failed to parse icon {}: {}", path.display(), e));
  70. let entry = &icon_dir.entries()[0];
  71. let rgba = entry
  72. .decode()
  73. .unwrap_or_else(|e| panic!("failed to decode icon {}: {}", path.display(), e))
  74. .rgba_data()
  75. .to_vec();
  76. let width = entry.width();
  77. let height = entry.height();
  78. let out_path = out_dir.join(out_file_name);
  79. write_if_changed(&out_path, &rgba).map_err(|error| EmbeddedAssetsError::AssetWrite {
  80. path: path.to_owned(),
  81. error,
  82. })?;
  83. let icon = quote!(#root::image::Image::new(include_bytes!(concat!(std::env!("OUT_DIR"), "/", #out_file_name)), #width, #height));
  84. Ok(icon)
  85. }
  86. pub(crate) fn png_icon(
  87. root: &TokenStream,
  88. out_dir: &Path,
  89. path: &Path,
  90. out_file_name: &str,
  91. ) -> EmbeddedAssetsResult<TokenStream> {
  92. let file = std::fs::File::open(path)
  93. .unwrap_or_else(|e| panic!("failed to open icon {}: {}", path.display(), e));
  94. let decoder = png::Decoder::new(file);
  95. let mut reader = decoder
  96. .read_info()
  97. .unwrap_or_else(|e| panic!("failed to read icon {}: {}", path.display(), e));
  98. let (color_type, _) = reader.output_color_type();
  99. if color_type != png::ColorType::Rgba {
  100. panic!("icon {} is not RGBA", path.display());
  101. }
  102. let mut buffer: Vec<u8> = Vec::new();
  103. while let Ok(Some(row)) = reader.next_row() {
  104. buffer.extend(row.data());
  105. }
  106. let width = reader.info().width;
  107. let height = reader.info().height;
  108. let out_path = out_dir.join(out_file_name);
  109. write_if_changed(&out_path, &buffer).map_err(|error| EmbeddedAssetsError::AssetWrite {
  110. path: path.to_owned(),
  111. error,
  112. })?;
  113. let icon = quote!(#root::image::Image::new(include_bytes!(concat!(std::env!("OUT_DIR"), "/", #out_file_name)), #width, #height));
  114. Ok(icon)
  115. }
  116. fn write_if_changed(out_path: &Path, data: &[u8]) -> std::io::Result<()> {
  117. if let Ok(curr) = std::fs::read(out_path) {
  118. if curr == data {
  119. return Ok(());
  120. }
  121. }
  122. std::fs::write(out_path, data)
  123. }