RSS

2010/12/14

在網頁中插入音樂

一直以來, 要在網頁上播放音樂都是個令人頭痛的問題. 一個不負責任的 developer 可以很隨便的用一個 <embed> tag 來處理, 迫使讀者要在特定環境加特定的軟件才可以播放.

[後補: 為免誤會, 我在此呼籲... 請各位必須避免在讀者不知情的情況下播放音樂. 例如是當進入頁面時自動播放音樂之類. 當你嚇到使用者的一刻就是他們離開的一刻.]

<embed> 的最大問題, 是它並不是一個 HTML 4.01 的 tag. 不過這個 tag 在 HTML 5 重新出現, 結果就像是把不負責任的行為合理化一樣...

<embed> 基本用途就是 embeded content, 亦即是瀏覽器會交由 plugin 去處理內容, 一般來說無法控制顯示的結果. 例如你 embed 的是一個 mp3 檔案的話, 用 IE 時可能會出現 Windows Media Player plugin, 用 Safari 時就大概會出現 Quicktime plugin. 要是使用者沒有安裝 plugin, 又沒有安裝權限的話, 那就只有說句不好意思了..

比較好的選擇, 是 HTML 4.01 已有的 <object> tag. 利用 <object> tag 配合 <paramgt; tag 的話, 可以控制的東西比較多. 例如是指定使用某一個 plugin, 要某一類的控制項等等.

可惜的是, 這個方法仍然是局限於 plugin 的使用. 而且, 不同的 plugin, 甚至是不同的瀏覽器所需要的設定都不同. 基於方便使用者的考量, 我會傾向選擇一個大部份使用者都有安裝的 plugin 來使用, 例如是, Flash.

利用 Flash-based 的音樂播放器. 好處是當大部份使用者都有安裝 Flash player 的情況下, 幾乎可以肯定大部份讀者都能夠享用你的音樂. 而且, 載入 Flash player 的 HTML 已經比常成熟, 對於 developer 來說也相當方便.

當然, 在 HTML 5 的年代, 我們可以用 <audio> tag 解決以上一切問題.... 是嗎?

使用 <audio> tag 的結果, 是瀏覽器會自行處理及播放音樂檔. Plugin 的問題是解決了, 但是瀏覽器的問題始終存在. 其中最大的問題是, 現時沒有一種音樂格式是全部主流瀏覽器都可以播放的. 例如說 Firefox 3.x 及 Opera 不會播放 mp3, IE 9 及 Safari 5 不會播放 ogg (要加 plugin). 難道要把音樂轉成巨大的 wave 格式嗎?

現時最好的方法, 就是同時提供 ogg 及 mp3 格式. 至於將來, 就看完全開放的 WebM 格式是否能夠普及化了. 不過當然還要顧及未能支援 HTML 5 的瀏覽器, 要提供 Flash player 作後備之用.

好了, 長篇大論之後就是萬眾期待的 code 了...




<audio> tag 本身可以加 src 屬性. 不過因為要顧及其他 browser 的關係, 就改為加入多個 <source> tag 讓瀏覽器決定. 其中的 type 屬性 可有可無. 如果瀏覽器不支援 <audio> tag, 就會按 <audio> tag 內的 Flash player code 顯示(或以任何其他信息取代).

有一點必需注意的, 就是 <audio> tag 的 controls 屬性會令瀏覽器顯示控制項. 而這個控制項的樣式全由瀏覽器決定. 如果要自行設計控制項的話, 就必須自行使用 HTML + CSS 設計, 然後用 JavaScript 來控制. 使用 JavaScript 可以控制播放的位置及音量等, 這亦是相比起用 Flash player, 使用 HTML 5 播放音樂的最大優點.

有時候, 我們要知道究竟現在的瀏覽器是否支援 <audio> tag. 我們可以利用 audio element 的 canPlayType function. 這個 function 本身可以查詢瀏覽器是不支援某一類的媒體, 只要這個 function 存在, 就表示瀏覽器支援 <audio> tag.

舉個例, 假如 HTML 的部份是這樣:






而我們希望, 當瀏覽器不支援 <audio> tag 的時候, 就提整個 <div> 的內容改為一個 Flash player 的話, 可以這樣做:


var audioElem = document.getElementById("audioPlayer");

if(!audioElem.canPlayType) {
loadFlashPlayer("audio");
}


這裡假設了有一個叫 loadFlashPlayer 的 function 會在 element id 叫 audio 的 element 載入一個 Flash player. 實際情況視乎選用的 Flash player 而定.

只不過是一個 <audio> tag, 也有這麼多的考慮. 可想而知當要用 <video> tag 時會有幾頭痛. 不知道將來 WebM 格式如果能夠普及的話, 會不會改善問題呢? 就讓我們拭目以待吧.

2010/12/04

利用 PHP 為圖片加上水印

為保護自己網站的圖片, 很多時都會為圖片加上水印. 不過如果每一張都要手動加水印的話有時會很不方便.

如果使用 PHP 的話, 其實可以利用 PHP 實時為圖片加上水印, 做法亦相當簡單. 而且還可以隨時轉換水印, 相當方便.

使用 GD2 library

現時一般的 PHP 設定都已經配備了 GD2 library. 利用 GD2 library, 就可以為相片加上水印. 方法是先載入要加上水印的圖片, 以及水印本身的圖片. 這裡假設圖片是 JPEG, 而水印則是 PNG.


$image = imageCreateFromJpeg("image.jpg");
$watermark = imageCreateFromPng("watermark.png");


然後使用imageCopyMerge()這個 function, 就可以把水印加到圖片上. 假設水印是加在圖片右下角的話, 大概是這樣 (假設圖片有足夠空間放入水印):


$watermarkWidth = imageSX($watermark);
$watermarkHeight = imageSY($watermark);
$watermarkPosX = imageSX($image) - $watermarkWidth - $padding;
$watermarkPosY = imageSY($image) - $watermarkHeight - $padding;

imageCopyMerge($image, $watermark,
$watermarkPosX, $watermarkPosY,
0, 0,
$watermarkWidth, $watermarkHeight,
100);


imageCopyMerge 的作用是把一幅圖的一個部份融合到另一幅圖之上, 這裡的做法是直接把全個水印加到圖片之上. 修改參數的話可以把水印的一部份加到圖片上, 或者是增加水印的透明度也可以.

最後的步驟, 就是把圖片顯示出來, 以及做一點清潔的工作.


header("Content-Type: image/jpeg");
imageJpeg($image);

imageDestroy($watermark);
imageDestroy($image);


不過, 這個做法會為伺服器帶來相當大的負擔, 而且當圖片非常大的時候, 亦相當費時. 解決方法是把圖片儲存下來...


imageJpeg($image, "imageWithWatermark.jpg");


當然要適當地改名, 以及準備一個可寫入的地方. 當要用圖片時, 只需直接 output 及加 header..


header('Content-Type: image/jpeg');
readFile("imageWithWatermark.jpg");


Alpha channel

使用 imageCopyMerge 的最大問題, 是它未能處理 alpha channel. 不幸地, 很多時候水印的設計都有透明度, 那麼就要考慮其他方法了. 在各種方法之中, 個人最喜歡的就是把水印當成畫筆(Brush) 的方法. 那就是, 把水印定義為一個畫筆, 再在圖片上畫一點.


$image = imageCreateFromJpeg("image.jpg");
$watermark = imageCreateFromPng("watermark.png");
$padding = 10;

$watermarkPosX = imageSX($image) - imageSX($watermark) - $padding;
$watermarkPosY = imageSY($image) - imageSY($watermark) - $padding;

imageSetBrush($image, $watermark);
imageLine($image,
$watermarkPosX, $watermarkPosY,
$watermarkPosX, $watermarkPosY,
IMG_COLOR_BRUSHED);

header('Content-Type: image/jpg');
imageJpeg($image);

imageDestroy($image);
imageDestroy($watermark);


這裡利用了 imageSetBrush 為圖片定義了畫筆, 然後再用 imageLine 畫了一條線, 不過線的起點及終點是同一點. 留意最後的 IMG_COLOR_BRUSHED 參數取代了原本應用的顏色參數.

ImageMagick

除了使用 GD2 之外, 亦可以考慮使用 ImageMagick, PHP 的 Imagick 功能比較多, 效率也較高, 不過需要安裝, 如果是使用 web hosting 的話就未必可以使用了. ImageMagick 基本上是一套 command line 的工具, PHP 提供了 Imagick class 來使用這個工具.


$image = new Imagick("image.jpg");
$watermark = new Imagick("watermark.png");
$padding = 10;

$watermarkPosX = $image->getImageWidth() - $watermark->getImageWidth() - $padding;
$watermarkPosY = $image->getImageHeight() - $watermark->getImageHeight() - $padding;

$image->compositeImage($watermark,
Imagick::COMPOSITE_DISSOLVE,
$watermarkPosX, $watermarkPosY);

header('Content-Type: image/jpg');
echo $image;

$watermark->destroy();
$image->destroy();


要留意的有幾點, 首先是其中的參數 magick::COMPOSITE_DISSOLVE 可以控制效果, 可以參考有關文件找到其他效果. 建議逐一試用.

另外要留意的是 Imagick object 可以直接 output, 所以只需要用 echoprint 就可以顯示圖片. 不過當然, 也可以選擇先儲存起來:

$image->setCompressionQuality(100);
$image->setImageFormat('jpeg');
$image->writeImage("fileWithWatermark.jpg");


要小心的是格式和副檔名必需相容, 是 jpeg 的話就要用 .jpg.

ImageMagick 還有更多用途, 有機會的話再分享.