Image wip

pull/645/head
Tienson Qin 2020-04-08 10:09:15 +08:00
parent cd57db1465
commit 160510a298
6 changed files with 210 additions and 4 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,29 @@
(ns frontend.blob)
(defn- decode
"Decodes the data portion of a data url from base64"
[[media-type data]]
[media-type (js/atob data)])
(defn- uint8
"Converts a base64 decoded data string to a Uint8Array"
[[media-type data]]
(->> (map #(.charCodeAt %1) data)
js/Uint8Array.
(vector media-type)))
(defn- make-blob
"Creates a JS Blob object from a media type and a Uint8Array"
[[media-type uint8]]
(js/Blob. (array uint8) (js-obj "type" media-type)))
(defn blob
"Converts a data-url into a JS Blob. This is useful for uploading
image data from JavaScript."
[data-url]
{:pre [(string? data-url)]}
(-> (re-find #"^data:([^;]+);base64,(.*)$" data-url)
rest
decode
uint8
make-blob))

View File

@ -8,14 +8,15 @@
[frontend.mixins :as mixins]
[frontend.db :as db]
[frontend.state :as state]
[frontend.format.org-mode :as org]))
[frontend.format.org-mode :as org]
[goog.object :as gobj]
[frontend.image :as image]))
(def edit-content (atom ""))
(rum/defc editor-box <
(mixins/event-mixin
(fn [state]
(let [heading (first (:rum/args state))]
(prn "heading: " heading)
(mixins/hide-when-esc-or-outside
state
nil
@ -33,7 +34,30 @@
:style {:border "none"
:border-radius 0
:background "transparent"
:margin-top 12.5}})])
:margin-top 12.5}})
[:input
{:id "files"
:type "file"
:on-change (fn [e]
(let [files (.-files (.-target e))]
(image/upload
files
(fn [file file-form-data file-name file-type]
;; TODO: set uploading
(.append file-form-data "name" file-name)
(.append file-form-data file-type true)
;; (citrus/dispatch!
;; :image/upload
;; file-form-data
;; (fn [url]
;; (reset! uploading? false)
;; (swap! form assoc name url)
;; (if on-uploaded
;; (on-uploaded form name url))))
))))
;; :hidden true
}]])
(defn split-first [re s]
(clojure.string/split s re 2))

54
web/src/frontend/exif.js Normal file
View File

@ -0,0 +1,54 @@
// copied from https://stackoverflow.com/questions/7584794/accessing-jpeg-exif-rotation-data-in-javascript-on-the-client-side
function objectURLToBlob(url, callback) {
var http = new XMLHttpRequest();
http.open("GET", url, true);
http.responseType = "blob";
http.onload = function(e) {
if (this.status == 200 || this.status === 0) {
callback(this.response);
}
};
http.send();
}
export var getEXIFOrientation = function (img, callback) {
var reader = new FileReader();
reader.onload = e => {
var view = new DataView(e.target.result)
if (view.getUint16(0, false) !== 0xFFD8) {
return callback(-2)
}
var length = view.byteLength
var offset = 2
while (offset < length) {
var marker = view.getUint16(offset, false)
offset += 2
if (marker === 0xFFE1) {
if (view.getUint32(offset += 2, false) !== 0x45786966) {
return callback(-1)
}
var little = view.getUint16(offset += 6, false) === 0x4949
offset += view.getUint32(offset + 4, little)
var tags = view.getUint16(offset, little)
offset += 2
for (var i = 0; i < tags; i++) {
if (view.getUint16(offset + (i * 12), little) === 0x0112) {
var o = view.getUint16(offset + (i * 12) + 8, little);
return callback(o)
}
}
} else if ((marker & 0xFF00) !== 0xFF00) {
break
} else {
offset += view.getUint16(offset, false)
}
}
return callback(-1)
};
objectURLToBlob(img.src, function (blob) {
reader.readAsArrayBuffer(blob.slice(0, 65536));
});
}

View File

@ -0,0 +1,92 @@
(ns frontend.image
(:require [goog.object :as gobj]
[frontend.blob :as blob]
["/frontend/exif" :as exif]
[frontend.util :as util]))
(defn reverse?
[exif-orientation]
(contains? #{5 6 7 8} exif-orientation))
(defn re-scale
[exif-orientation width height max-width max-height]
(let [[width height]
(if (reverse? exif-orientation)
[height width]
[width height])]
(let [ratio (/ width height)
to-width (if (> width max-width) max-width width)
to-height (if (> height max-height) max-height height)
new-ratio (/ to-width to-height)]
(let [[w h] (cond
(> new-ratio ratio)
[(* ratio to-height) to-height]
(< new-ratio ratio)
[to-width (/ to-width ratio)]
:else
[to-width to-height])]
[(int w) (int h)]))))
(defn fix-orientation
"Given image and exif orientation, ensure the photo is displayed
rightside up"
[img exif-orientation cb max-width max-height]
(let [off-canvas (js/document.createElement "canvas")
ctx ^js (.getContext off-canvas "2d")
width (gobj/get img "width")
height (gobj/get img "height")
[to-width to-height] (re-scale exif-orientation width height max-width max-height)]
(gobj/set ctx "imageSmoothingEnabled" false)
(set! (.-width off-canvas) to-width)
(set! (.-height off-canvas) to-height)
;; rotate
(let [[width height] (if (reverse? exif-orientation)
[to-height to-width]
[to-width to-height])]
(case exif-orientation
2 (.transform ctx -1 0 0 1 width 0)
3 (.transform ctx -1 0 0 -1 width height)
4 (.transform ctx 1 0 0 -1 0 height)
5 (.transform ctx 0 1 1 0 0 0)
6 (.transform ctx 0 1 -1 0 height 0)
7 (.transform ctx 0 -1 -1 0 height width)
8 (.transform ctx 0 -1 1 0 0 width)
(.transform ctx 1 0 0 1 0 0))
(.drawImage ctx img 0 0 width height))
(cb off-canvas)))
(defn get-orientation
[img cb max-width max-height]
(exif/getEXIFOrientation
img
(fn [orientation]
(fix-orientation img orientation cb max-width max-height))))
(defn upload
[files file-cb & {:keys [max-width max-height]
:or {max-width 1920
max-height 1080}}]
(doseq [file (array-seq files)]
(let [file-type (gobj/get file "type")
file-name (str (util/year-month-day-concat) "_"
(gobj/get file "name"))]
(if (= 0 (.indexOf type "image/"))
(let [img (js/Image.)]
(set! (.-onload img)
(fn []
(get-orientation img
(fn [^js off-canvas]
(let [file-form-data ^js (js/FormData.)
data-url (.toDataURL off-canvas)
blob (blob/blob data-url)]
(.append file-form-data "file" blob)
(file-cb file file-form-data file-name file-type)))
max-width
max-height)))
(set! (.-src img)
(.createObjectURL (or (.-URL js/window)
(.-webkitURL js/window))
file)))))))

View File

@ -134,3 +134,10 @@
:year "numeric"
:day "numeric"
:weekday "long"})))
(defn year-month-day-concat
[]
(let [{:keys [year month day]} (get-date)
month (if (< month 10) (str "0" month) month)
day (if (< day 10) (str "0" day) day)]
(str year "_" month "_" day)))