Skip to content

Commit

Permalink
Merge pull request #1 from alabulei1/alabulei1-add-poster
Browse files Browse the repository at this point in the history
add poster
  • Loading branch information
alabulei1 authored Mar 12, 2021
2 parents 1838ce1 + 5eb0dbb commit c6b770c
Show file tree
Hide file tree
Showing 20 changed files with 411 additions and 0 deletions.
16 changes: 16 additions & 0 deletions faas/poster/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "watermark"
version = "0.1.0"
authors = ["ubuntu"]
edition = "2018"

[lib]
name = "watermark_lib"
path = "src/lib.rs"
crate-type =["cdylib"]

[dependencies]
image = { version = "0.23.0", default-features = false, features = ["jpeg", "png", "gif"] }
imageproc = "=0.21.0"
rusttype = "=0.9.2"
wasm-bindgen = "=0.2.61"
65 changes: 65 additions & 0 deletions faas/poster/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# The image watermark example

In this example, we demonstrate how to create and run a Rust function in the Second State Rust FaaS.

## Prerequisites

If you have not done so already, follow these simple instructions to install [Rust](https://www.rust-lang.org/tools/install) and [ssvmup](https://www.secondstate.io/articles/ssvmup/).

## Build the WASM bytecode

```
ssvmup build
```

## FaaS

Upload the wasm file in the `pkg` folder to the FaaS. Double check the `.wasm` file name before you upload.

```
curl --location --request POST 'https://rpc.ssvm.secondstate.io:8081/api/executables' \
--header 'Content-Type: application/octet-stream' \
--header 'SSVM-Description: watermark' \
--data-binary '@pkg/watermark_lib_bg.wasm'
```

Returns

```
{"wasm_id":148,"wasm_sha256":"0x0a3227cd8d76c32f4788ca8d020091f89c41f4abc7a3c3b1c10490d439a22b1b","SSVM_Usage_Key":"00000000-0000-0000-0000-000000000000","SSVM_Admin_Key":"aaaa-bbbb-cccc-dddd-0000"}
```

Note: You can update this binary with the `SSVM_Admin_Key`.

```
$ curl --location --request PUT 'https://rpc.ssvm.secondstate.io:8081/api/update_wasm_binary/148' \
--header 'Content-Type: application/octet-stream' \
--header 'SSVM_Admin_Key: 7dxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx0c41' \
--data-binary '@pkg/watermark_lib_bg.wasm'
```

## Setup the watermark text

Add watermark to a local PNG image.

```
$ curl --location --request POST 'https://rpc.ssvm.secondstate.io:8081/api/multipart/run/148/watermark/bytes' \
--header 'Content-Type: multipart/form-data' \
--form 'input_1=Meow Human!' \
--form 'input_2=@test/cat.png' \
--output tmp.png
```

Make a pre-fetched FaaS call to add watermark to an Internet image.

```
$ curl --location --request POST 'https://rpc.ssvm.secondstate.io:8081/api/multipart/run/148/watermark/bytes' \
--header 'Content-Type: multipart/form-data' \
--form 'input_1=Woof Human!' \
--form 'fetch_input_2=https://www.secondstate.io/demo/dog.png' \
--output tmp.png
```

## Serverless web app

Open web page [html/index.html](html/index.html) in any browser. See a [static demo](https://second-state.github.io/wasm-learning/faas/watermark/html/index.html).
Binary file added faas/poster/html/dog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
79 changes: 79 additions & 0 deletions faas/poster/html/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Watermark</title>

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>

<script>
function callService() {
setTimeout(function(){
$('#process').prop('disabled', true);
},0);

var formData = new FormData();
formData.append('input_1', $('#input_1').val());
formData.append('input_2', $('#input_2')[0].files[0]);
// console.log($('#input_2')[0].files[0]);

$.ajax({
url: "https://rpc.ssvm.secondstate.io:8081/api/multipart/run/148/watermark/bytes",
type: "post",
data : formData,
contentType: false,
processData: false,
xhrFields:{
responseType: 'blob'
},
success: function (data) {
const img_url = URL.createObjectURL(data);
$('#wm_img').prop('src', img_url);
$('#process').prop('disabled', false);
},
error: function(){
alert("Cannot get data");
$('#process').prop('disabled', false);
}
});

return false;
}
</script>
</head>

<body>
<div class="container">
<div style="text-align:center;margin:25px">
<a href="https://www.secondstate.io/"><img style="border:0;" src="https://www.secondstate.io/assets/img/logo.png"></a>
<div style="font-size:90%;color:gray;margin:20px"><a href="https://www.secondstate.io/faas/">Fast, safe, portable and serverless Rust functions as services</a></div>
</div>

<h1>Watermark</h1>
<p class="lead">Add a custom watermark to an image. <a href="https://www.secondstate.io/articles/mixing-text-and-binary-data-in-call-arguments/">See code tutorial</a></p>

<form id="draw" enctype="multipart/form-data">
<div class="form-group">
<label for="input_1">The watermark text</label>
<input type="text" class="form-control" id="input_1" name="input_1" value=""/>
</div>

<div class="form-group">
<label for="input_2">Please upload an image file (<a href="dog.png">example</a>)</label>
<input type="file" class="form-control-file" id="input_2" name="input_2">
</div>

<button class="btn btn-primary mb-2" id="process" name="process" value="1" onclick="return callService();">Add Watermark</button>
</form>

<div class="jumbotron">
<img id="wm_img" class="rounded mx-auto d-block"/>
</div>

</div> <!-- /container -->
</body>
</html>
73 changes: 73 additions & 0 deletions faas/poster/html/index2.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Watermark</title>

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>

<script>
function callService() {
setTimeout(function(){
$('#process').prop('disabled', true);
},0);

$.ajax({
url: "https://rpc.ssvm.secondstate.io:8081/api/run/148/watermark/bytes",
type: "post",
beforeSend: function(xhr) {
xhr.setRequestHeader("SSVM_Fetch", $('#img_url').val());
},
xhrFields:{
responseType: 'blob'
},
success: function (data) {
// alert(JSON.stringify(data));
// var binaryData = [];
// binaryData.push(data);
// const img_url = window.URL.createObjectURL(new Blob(binaryData, {type: "image/png"}))
const img_url = URL.createObjectURL(data);
$('#wm_img').prop('src', img_url);
$('#process').prop('disabled', false);
},
error: function(){
alert("Cannot get data");
$('#process').prop('disabled', false);
}
});

return false;
}
</script>
</head>

<body>
<div class="container">
<div style="text-align:center;margin:25px">
<a href="https://www.secondstate.io/"><img style="border:0;" src="https://www.secondstate.io/assets/img/logo.png"></a>
<div style="font-size:90%;color:gray;margin:20px"><a href="https://www.secondstate.io/faas/">Fast, safe, portable and serverless Rust functions as services</a></div>
</div>

<h1>Watermark</h1>
<p class="lead">Add a watermark to an Internet image</p>

<form id="draw">
<div class="form-group">
<label for="img_url">Put an image URL below</label>
<input type="text" class="form-control" id="img_url" name="img_url" value="https://www.secondstate.io/demo/dog.png"/>
</div>

<button class="btn btn-primary mb-2" id="process" name="process" value="1" onclick="return callService();">Add Watermark</button>
</form>

<div class="jumbotron">
<img id="wm_img" class="rounded mx-auto d-block"/>
</div>

</div> <!-- /container -->
</body>
</html>
Binary file added faas/poster/src/HanyiSentyFingerPainting.ttf
Binary file not shown.
Binary file added faas/poster/src/HanyiSentyMarshmallow.ttf
Binary file not shown.
Binary file added faas/poster/src/MaShanZheng-Regular.ttf
Binary file not shown.
Binary file added faas/poster/src/NotoSansSC-Medium.otf
Binary file not shown.
Binary file added faas/poster/src/PingFang Bold.ttf
Binary file not shown.
Binary file added faas/poster/src/crop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
108 changes: 108 additions & 0 deletions faas/poster/src/imagecrop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use image::{
GenericImageView,
DynamicImage,
Rgba,
ImageResult,
};

#[derive(Debug)]
pub struct Point {
pub x: u32,
pub y: u32,
}

pub struct ImageCrop {
pub original: DynamicImage,
}

impl ImageCrop {
pub fn new(img: DynamicImage) -> ImageResult<ImageCrop> {
Ok(ImageCrop {
original: img,
})
}

pub fn calculate_corners(&self) -> (Point, Point) {
(self.top_left_corner(), self.bottom_right_corner())
}

fn is_white(pixel: Rgba<u8>) -> bool {
pixel[0] != 255 &&
pixel[1] != 255 &&
pixel[2] != 255
}

fn top_left_corner(&self) -> Point {
Point {
x: self.top_left_corner_x(),
y: self.top_left_corner_y(),
}
}

fn top_left_corner_x(&self) -> u32 {
for x in 0..(self.original.dimensions().0) {
for y in 0..(self.original.dimensions().1) {
let pixel = self.original.get_pixel(x, y);
if Self::is_white(pixel) {
return x;
}
}
}
unreachable!();
}

fn top_left_corner_y(&self) -> u32 {
for y in 0..(self.original.dimensions().1) {
for x in 0..(self.original.dimensions().0) {
let pixel = self.original.get_pixel(x, y);
if Self::is_white(pixel) {
return y;
}
}
}
unreachable!();
}

fn bottom_right_corner(&self) -> Point {
Point {
x: self.bottom_right_corner_x(),
y: self.bottom_right_corner_y(),
}
}

fn bottom_right_corner_x(&self) -> u32 {
let mut x = self.original.dimensions().0 as i32 - 1;
// Using while loop as currently there is no reliable built-in
// way to use custom negative steps when specifying range
while x >= 0 {
let mut y = self.original.dimensions().1 as i32 - 1;
while y >= 0 {
let pixel = self.original.get_pixel(x as u32, y as u32);
if Self::is_white(pixel) {
return x as u32 + 1;
}
y -= 1;
}
x -= 1;
}
unreachable!();
}

fn bottom_right_corner_y(&self) -> u32 {
let mut y = self.original.dimensions().1 as i32 - 1;
// Using while loop as currently there is no reliable built-in
// way to use custom negative steps when specifying range
while y >= 0 {
let mut x = self.original.dimensions().0 as i32 - 1;
while x >= 0 {
let pixel = self.original.get_pixel(x as u32, y as u32);
if Self::is_white(pixel) {
return y as u32 + 1;
}
x -= 1;
}
y -= 1;
}
unreachable!();
}
}
Loading

0 comments on commit c6b770c

Please sign in to comment.