diff --git a/modules/libpr0n/build/nsImageModule.cpp b/modules/libpr0n/build/nsImageModule.cpp index 0ed1e6b3cc1..d3cbbe38c5e 100644 --- a/modules/libpr0n/build/nsImageModule.cpp +++ b/modules/libpr0n/build/nsImageModule.cpp @@ -58,6 +58,7 @@ #include "imgLoader.h" #include "imgRequest.h" #include "imgRequestProxy.h" +#include "imgTools.h" #ifdef IMG_BUILD_DECODER_gif // gif @@ -102,6 +103,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(imgCache) NS_GENERIC_FACTORY_CONSTRUCTOR(imgContainer) NS_GENERIC_FACTORY_CONSTRUCTOR(imgLoader) NS_GENERIC_FACTORY_CONSTRUCTOR(imgRequestProxy) +NS_GENERIC_FACTORY_CONSTRUCTOR(imgTools) #ifdef IMG_BUILD_DECODER_gif // gif @@ -217,6 +219,10 @@ static const nsModuleComponentInfo components[] = NS_IMGREQUESTPROXY_CID, "@mozilla.org/image/request;1", imgRequestProxyConstructor, }, + { "image tools", + NS_IMGTOOLS_CID, + "@mozilla.org/image/tools;1", + imgToolsConstructor, }, #ifdef IMG_BUILD_DECODER_gif // gif diff --git a/modules/libpr0n/public/ImageErrors.h b/modules/libpr0n/public/ImageErrors.h index 15a26b58112..ffbcab25535 100644 --- a/modules/libpr0n/public/ImageErrors.h +++ b/modules/libpr0n/public/ImageErrors.h @@ -49,4 +49,5 @@ #define NS_IMAGELIB_ERROR_NO_DECODER NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_IMGLIB, 6) #define NS_IMAGELIB_ERROR_NOT_FINISHED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_IMGLIB, 7) #define NS_IMAGELIB_ERROR_LOAD_ABORTED NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_IMGLIB, 8) +#define NS_IMAGELIB_ERROR_NO_ENCODER NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_IMGLIB, 9) diff --git a/modules/libpr0n/public/Makefile.in b/modules/libpr0n/public/Makefile.in index ef1af92dec5..a6be22852e1 100644 --- a/modules/libpr0n/public/Makefile.in +++ b/modules/libpr0n/public/Makefile.in @@ -52,11 +52,12 @@ XPIDLSRCS = \ imgIContainer.idl \ imgIContainerObserver.idl \ imgIDecoder.idl \ - imgIDecoderObserver.idl \ + imgIDecoderObserver.idl \ imgIEncoder.idl \ imgILoad.idl \ imgILoader.idl \ - imgIRequest.idl \ + imgIRequest.idl \ + imgITools.idl \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/modules/libpr0n/public/imgITools.idl b/modules/libpr0n/public/imgITools.idl new file mode 100644 index 00000000000..af91ac05dfe --- /dev/null +++ b/modules/libpr0n/public/imgITools.idl @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Justin Dolske (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.idl" + +interface nsIInputStream; +interface imgIContainer; + +[scriptable, uuid(c395d8f1-c616-4a1b-adfd-747b4b1b2cbe)] +interface imgITools : nsISupports +{ + /** + * decodeImageData + * Caller provides an input stream and mimetype. We read from the stream + * and decompress it (according to the specified mime type) and return + * the resulting imgIContainer. (If the caller already has a container, + * it can be provided as input to be reused). + * + * @param aStream + * An input stream for an encoded image file. + * @param aMimeType + * Type of image in the stream. + * @param aContainer + * An imgIContainer holding the decoded image. Specify |null| when + * calling to have one created, otherwise specify a container to + * be reused. + */ + void decodeImageData(in nsIInputStream aStream, + in ACString aMimeType, + inout imgIContainer aContainer); + + /** + * encodeImage + * Caller provides an image container, and the mime type it should be + * encoded to. We return an input stream for the encoded image data. + * + * @param aContainer + * An image container. + * @param aMimeType + * Type of encoded image desired (eg "image/png"). + */ + nsIInputStream encodeImage(in imgIContainer aContainer, + in ACString aMimeType); + + /** + * encodeScaledImage + * Caller provides an image container, and the mime type it should be + * encoded to. We return an input stream for the encoded image data. + * The encoded image is scaled to the specified dimensions. + * + * @param aContainer + * An image container. + * @param aMimeType + * Type of encoded image desired (eg "image/png"). + * @param aWidth, aHeight + * The size (in pixels) desired for the resulting image. + */ + nsIInputStream encodeScaledImage(in imgIContainer aContainer, + in ACString aMimeType, + in long aWidth, + in long aHeight); +}; diff --git a/modules/libpr0n/src/Makefile.in b/modules/libpr0n/src/Makefile.in index 2f85dbff435..44cc97754db 100644 --- a/modules/libpr0n/src/Makefile.in +++ b/modules/libpr0n/src/Makefile.in @@ -66,7 +66,8 @@ CPPSRCS = \ imgContainer.cpp \ imgLoader.cpp \ imgRequest.cpp \ - imgRequestProxy.cpp + imgRequestProxy.cpp \ + imgTools.cpp include $(topsrcdir)/config/rules.mk diff --git a/modules/libpr0n/src/imgTools.cpp b/modules/libpr0n/src/imgTools.cpp new file mode 100644 index 00000000000..dd822a11d2b --- /dev/null +++ b/modules/libpr0n/src/imgTools.cpp @@ -0,0 +1,356 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Justin Dolske (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "imgTools.h" +#include "nsCOMPtr.h" +#include "nsString.h" +#include "ImageErrors.h" +#include "imgIContainer.h" +#include "imgILoad.h" +#include "imgIDecoder.h" +#include "imgIEncoder.h" +#include "imgIDecoderObserver.h" +#include "imgIContainerObserver.h" +#include "nsIImage.h" +#include "gfxIImageFrame.h" +#include "gfxImageSurface.h" +#include "gfxContext.h" +#include "nsStringStream.h" +#include "nsComponentManagerUtils.h" +#include "nsWeakReference.h" +#include "nsIInterfaceRequestorUtils.h" + + +/* ========== Utility classes ========== */ + + +class HelperLoader : public imgILoad, + public imgIDecoderObserver, + public nsSupportsWeakReference +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_IMGILOAD + NS_DECL_IMGIDECODEROBSERVER + NS_DECL_IMGICONTAINEROBSERVER + HelperLoader(void); + + private: + nsCOMPtr mContainer; +}; + +NS_IMPL_ISUPPORTS4 (HelperLoader, imgILoad, imgIDecoderObserver, imgIContainerObserver, nsISupportsWeakReference) + +HelperLoader::HelperLoader (void) +{ +} + +/* Implement imgILoad::image getter */ +NS_IMETHODIMP +HelperLoader::GetImage(imgIContainer **aImage) +{ + *aImage = mContainer; + NS_IF_ADDREF (*aImage); + return NS_OK; +} + +/* Implement imgILoad::image setter */ +NS_IMETHODIMP +HelperLoader::SetImage(imgIContainer *aImage) +{ + mContainer = aImage; + return NS_OK; +} + +/* Implement imgILoad::isMultiPartChannel getter */ +NS_IMETHODIMP +HelperLoader::GetIsMultiPartChannel(PRBool *aIsMultiPartChannel) +{ + *aIsMultiPartChannel = PR_FALSE; + return NS_OK; +} + +/* Implement imgIDecoderObserver::onStartRequest() */ +NS_IMETHODIMP +HelperLoader::OnStartRequest(imgIRequest *aRequest) +{ + return NS_OK; +} + +/* Implement imgIDecoderObserver::onStartDecode() */ +NS_IMETHODIMP +HelperLoader::OnStartDecode(imgIRequest *aRequest) +{ + return NS_OK; +} + +/* Implement imgIDecoderObserver::onStartContainer() */ +NS_IMETHODIMP +HelperLoader::OnStartContainer(imgIRequest *aRequest, imgIContainer +*aContainer) +{ + return NS_OK; +} + +/* Implement imgIDecoderObserver::onStartFrame() */ +NS_IMETHODIMP +HelperLoader::OnStartFrame(imgIRequest *aRequest, gfxIImageFrame *aFrame) +{ + return NS_OK; +} + +/* Implement imgIDecoderObserver::onDataAvailable() */ +NS_IMETHODIMP +HelperLoader::OnDataAvailable(imgIRequest *aRequest, gfxIImageFrame +*aFrame, const nsIntRect * aRect) +{ + return NS_OK; +} + +/* Implement imgIDecoderObserver::onStopFrame() */ +NS_IMETHODIMP +HelperLoader::OnStopFrame(imgIRequest *aRequest, gfxIImageFrame *aFrame) +{ + return NS_OK; +} + +/* Implement imgIDecoderObserver::onStopContainer() */ +NS_IMETHODIMP +HelperLoader::OnStopContainer(imgIRequest *aRequest, imgIContainer +*aContainer) +{ + return NS_OK; +} + +/* Implement imgIDecoderObserver::onStopDecode() */ +NS_IMETHODIMP +HelperLoader::OnStopDecode(imgIRequest *aRequest, nsresult status, const +PRUnichar *statusArg) +{ + return NS_OK; +} + +/* Implement imgIDecoderObserver::onStopRequest() */ +NS_IMETHODIMP +HelperLoader::OnStopRequest(imgIRequest *aRequest, PRBool aIsLastPart) +{ + return NS_OK; +} + +/* implement imgIContainerObserver::frameChanged() */ +NS_IMETHODIMP +HelperLoader::FrameChanged(imgIContainer *aContainer, + gfxIImageFrame *aFrame, nsIntRect * aDirtyRect) +{ + return NS_OK; +} + + + +/* ========== imgITools implementation ========== */ + + + +NS_IMPL_ISUPPORTS1(imgTools, imgITools) + +imgTools::imgTools() +{ + /* member initializers and constructor code */ +} + +imgTools::~imgTools() +{ + /* destructor code */ +} + + +NS_IMETHODIMP imgTools::DecodeImageData(nsIInputStream* aInStr, + const nsACString& aMimeType, + imgIContainer **aContainer) +{ + nsresult rv; + + // Get an image decoder for our media type + nsCAutoString decoderCID( + NS_LITERAL_CSTRING("@mozilla.org/image/decoder;2?type=") + aMimeType); + + nsCOMPtr decoder = do_CreateInstance(decoderCID.get()); + if (!decoder) + return NS_IMAGELIB_ERROR_NO_DECODER; + + // Init the decoder, we use a small utility class here. + nsCOMPtr loader = new HelperLoader(); + if (!loader) + return NS_ERROR_OUT_OF_MEMORY; + + // If caller provided an existing container, use it. + if (*aContainer) + loader->SetImage(*aContainer); + + rv = decoder->Init(loader); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 length; + rv = aInStr->Available(&length); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 written; + NS_ENSURE_SUCCESS(rv, rv); + rv = decoder->WriteFrom(aInStr, length, &written); + NS_ENSURE_SUCCESS(rv, rv); + NS_ASSERTION(written == length, "decoder didn't eat all of its vegetables"); + rv = decoder->Flush(); + NS_ENSURE_SUCCESS(rv, rv); + rv = decoder->Close(); + NS_ENSURE_SUCCESS(rv, rv); + + // If caller didn't provide an existing container, return the new one. + if (!*aContainer) + loader->GetImage(aContainer); + + return NS_OK; +} + + +NS_IMETHODIMP imgTools::EncodeImage(imgIContainer *aContainer, + const nsACString& aMimeType, + nsIInputStream **aStream) +{ + return EncodeScaledImage(aContainer, aMimeType, 0, 0, aStream); +} + + +NS_IMETHODIMP imgTools::EncodeScaledImage(imgIContainer *aContainer, + const nsACString& aMimeType, + PRInt32 aScaledWidth, + PRInt32 aScaledHeight, + nsIInputStream **aStream) +{ + nsresult rv; + PRBool doScaling = PR_TRUE; + PRUint8 *bitmapData; + PRUint32 bitmapDataLength, strideSize; + + // If no scaled size is specified, we'll just encode the image at its + // original size (no scaling). + if (aScaledWidth == 0 && aScaledHeight == 0) { + doScaling = PR_FALSE; + } else { + NS_ENSURE_ARG(aScaledWidth > 0); + NS_ENSURE_ARG(aScaledHeight > 0); + } + + // Get an image encoder for the media type + nsCAutoString encoderCID( + NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType); + + nsCOMPtr encoder = do_CreateInstance(encoderCID.get()); + if (!encoder) + return NS_IMAGELIB_ERROR_NO_ENCODER; + + // Use frame 0 from the image container. + nsCOMPtr frame; + rv = aContainer->GetFrameAt(0, getter_AddRefs(frame)); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt32 w,h; + frame->GetWidth(&w); + frame->GetHeight(&h); + if (!w || !h) + return NS_ERROR_FAILURE; + + nsCOMPtr img(do_GetInterface(frame)); + nsRefPtr dest; + + if (!doScaling) { + // If we're not scaling the image, use the actual width/height. + aScaledWidth = w; + aScaledHeight = h; + + img->LockImagePixels(PR_FALSE); + bitmapData = img->GetBits(); + if (!bitmapData) { + img->UnlockImagePixels(PR_FALSE); + return NS_ERROR_FAILURE; + } + + frame->GetImageBytesPerRow(&strideSize); + bitmapDataLength = aScaledHeight * strideSize; + + } else { + // Prepare to draw a scaled version of the image to a temporary surface... + + // Get the source image surface + nsRefPtr gfxsurf; + rv = img->GetSurface(getter_AddRefs(gfxsurf)); + NS_ENSURE_SUCCESS(rv, rv); + + // Create a temporary image surface + dest = new gfxImageSurface(gfxIntSize(aScaledWidth, aScaledHeight), + gfxASurface::ImageFormatARGB32); + if (!dest) + return NS_ERROR_OUT_OF_MEMORY; + + gfxContext ctx(dest); + + // Set scaling + gfxFloat sw = (double) aScaledWidth / w; + gfxFloat sh = (double) aScaledHeight / h; + ctx.Scale(sw, sh); + + // Paint a scaled image + ctx.SetOperator(gfxContext::OPERATOR_SOURCE); + ctx.SetSource(gfxsurf); + ctx.Paint(); + + bitmapData = dest->Data(); + strideSize = dest->Stride(); + bitmapDataLength = aScaledHeight * strideSize; + } + + // Encode the bitmap + rv = encoder->InitFromData(bitmapData, bitmapDataLength, + aScaledWidth, aScaledHeight, strideSize, + imgIEncoder::INPUT_FORMAT_HOSTARGB, EmptyString()); + if (!doScaling) + img->UnlockImagePixels(PR_FALSE); + + NS_ENSURE_SUCCESS(rv, rv); + + return CallQueryInterface(encoder, aStream); +} diff --git a/modules/libpr0n/src/imgTools.h b/modules/libpr0n/src/imgTools.h new file mode 100644 index 00000000000..24c7a674cd7 --- /dev/null +++ b/modules/libpr0n/src/imgTools.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Justin Dolske (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "imgITools.h" + +#define NS_IMGTOOLS_CID \ +{ /* fd9a9e8a-a77b-496a-b7bb-263df9715149 */ \ + 0xfd9a9e8a, \ + 0xa77b, \ + 0x496a, \ + {0xb7, 0xbb, 0x26, 0x3d, 0xf9, 0x71, 0x51, 0x49} \ +} + +class imgTools : public imgITools +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_IMGITOOLS + + imgTools(); + virtual ~imgTools(); +}; diff --git a/modules/libpr0n/test/unit/image1.png b/modules/libpr0n/test/unit/image1.png new file mode 100644 index 00000000000..2756cf0cb3c Binary files /dev/null and b/modules/libpr0n/test/unit/image1.png differ diff --git a/modules/libpr0n/test/unit/image1png16x16.jpg b/modules/libpr0n/test/unit/image1png16x16.jpg new file mode 100644 index 00000000000..cfb749ab47a Binary files /dev/null and b/modules/libpr0n/test/unit/image1png16x16.jpg differ diff --git a/modules/libpr0n/test/unit/image1png64x64.jpg b/modules/libpr0n/test/unit/image1png64x64.jpg new file mode 100644 index 00000000000..364d568f615 Binary files /dev/null and b/modules/libpr0n/test/unit/image1png64x64.jpg differ diff --git a/modules/libpr0n/test/unit/image2.jpg b/modules/libpr0n/test/unit/image2.jpg new file mode 100644 index 00000000000..b2131bf0c1c Binary files /dev/null and b/modules/libpr0n/test/unit/image2.jpg differ diff --git a/modules/libpr0n/test/unit/image2jpg16x16.png b/modules/libpr0n/test/unit/image2jpg16x16.png new file mode 100644 index 00000000000..c3a4aee6166 Binary files /dev/null and b/modules/libpr0n/test/unit/image2jpg16x16.png differ diff --git a/modules/libpr0n/test/unit/image2jpg32x32.png b/modules/libpr0n/test/unit/image2jpg32x32.png new file mode 100644 index 00000000000..72300877109 Binary files /dev/null and b/modules/libpr0n/test/unit/image2jpg32x32.png differ diff --git a/modules/libpr0n/test/unit/image3.ico b/modules/libpr0n/test/unit/image3.ico new file mode 100644 index 00000000000..d44438903b7 Binary files /dev/null and b/modules/libpr0n/test/unit/image3.ico differ diff --git a/modules/libpr0n/test/unit/image3ico16x16.png b/modules/libpr0n/test/unit/image3ico16x16.png new file mode 100644 index 00000000000..e9e520cb6cf Binary files /dev/null and b/modules/libpr0n/test/unit/image3ico16x16.png differ diff --git a/modules/libpr0n/test/unit/image3ico32x32.png b/modules/libpr0n/test/unit/image3ico32x32.png new file mode 100644 index 00000000000..421b9ce30d1 Binary files /dev/null and b/modules/libpr0n/test/unit/image3ico32x32.png differ diff --git a/modules/libpr0n/test/unit/test_imgtools.js b/modules/libpr0n/test/unit/test_imgtools.js new file mode 100644 index 00000000000..24a34ffa51c --- /dev/null +++ b/modules/libpr0n/test/unit/test_imgtools.js @@ -0,0 +1,294 @@ +/* + * Tests for imgITools + */ + +const TESTDIR = "modules/libpr0n/test/unit/"; +const Ci = Components.interfaces; +const Cc = Components.classes; + + +/* + * dumpToFile() + * + * For test development, dumps the specified array to a file. + * Call |dumpToFile(outData);| in a test to file to a file. + */ +function dumpToFile(aData) { + const path = "/tmp"; + + var outputFile = Cc["@mozilla.org/file/local;1"]. + createInstance(Ci.nsILocalFile); + outputFile.initWithPath(path); + outputFile.append("testdump.png"); + + var outputStream = Cc["@mozilla.org/network/file-output-stream;1"]. + createInstance(Ci.nsIFileOutputStream); + // WR_ONLY|CREAT|TRUNC + outputStream.init(outputFile, 0x02 | 0x08 | 0x20, 0644, null); + + var bos = Cc["@mozilla.org/binaryoutputstream;1"]. + createInstance(Ci.nsIBinaryOutputStream); + bos.setOutputStream(outputStream); + + bos.writeByteArray(aData, aData.length); + + outputStream.close(); +} + + +/* + * getFileInputStream() + * + * Returns an input stream for the specified file. + */ +function getFileInputStream(aFile) { + var inputStream = Cc["@mozilla.org/network/file-input-stream;1"]. + createInstance(Ci.nsIFileInputStream); + // init the stream as RD_ONLY, -1 == default permissions. + inputStream.init(aFile, 0x01, -1, null); + + // Blah. The image decoders use ReadSegments, which isn't implemented on + // file input streams. Use a buffered stream to make it work. + var bis = Cc["@mozilla.org/network/buffered-input-stream;1"]. + createInstance(Ci.nsIBufferedInputStream); + bis.init(inputStream, 1024); + + return bis; +} + + +/* + * streamToArray() + * + * Consumes an input stream, and returns its bytes as an array. + */ +function streamToArray(aStream) { + var size = aStream.available(); + + // use a binary input stream to grab the bytes. + var bis = Cc["@mozilla.org/binaryinputstream;1"]. + createInstance(Ci.nsIBinaryInputStream); + bis.setInputStream(aStream); + + var bytes = bis.readByteArray(size); + if (size != bytes.length) + throw "Didn't read expected number of bytes"; + + return bytes; +} + + +/* + * compareArrays + * + * Compares two arrays, and throws if there's a difference. + */ +function compareArrays(aArray1, aArray2) { + do_check_eq(aArray1.length, aArray2.length); + + for (var i = 0; i < aArray1.length; i++) + if (aArray1[i] != aArray2[i]) + throw "arrays differ at index " + i; +} + + +function run_test() { +try { + + +/* ========== 0 ========== */ +var testnum = 0; +var testdesc = "imgITools setup"; + +var imgTools = Cc["@mozilla.org/image/tools;1"]. + getService(Ci.imgITools); + +if (!imgTools) + throw "Couldn't get imgITools service" + + +/* ========== 1 ========== */ +testnum++; +testdesc = "test decoding a PNG"; + +// 64x64 png, 10698 bytes. +var imgName = "image1.png"; +var inMimeType = "image/png"; +var imgFile = do_get_file(TESTDIR + imgName); + +var istream = getFileInputStream(imgFile); +do_check_eq(istream.available(), 10698); + +var outParam = { value: null }; +imgTools.decodeImageData(istream, inMimeType, outParam); +var container = outParam.value; + +// It's not easy to look at the pixel values from JS, so just +// check the container's size. +do_check_eq(container.width, 64); +do_check_eq(container.height, 64); + + +/* ========== 2 ========== */ +testnum++; +testdesc = "test encoding a scaled JPEG"; + +// we'll reuse the container from the previous test +istream = imgTools.encodeScaledImage(container, "image/jpeg", 16, 16); + +var encodedBytes = streamToArray(istream); +// Get bytes for exected result +var refName = "image1png16x16.jpg"; +var refFile = do_get_file(TESTDIR + refName); +istream = getFileInputStream(refFile); +do_check_eq(istream.available(), 733); +var referenceBytes = streamToArray(istream); + +// compare the encoder's output to the reference file. +compareArrays(encodedBytes, referenceBytes); + + +/* ========== 3 ========== */ +testnum++; +testdesc = "test encoding an unscaled JPEG"; + +// we'll reuse the container from the previous test +istream = imgTools.encodeImage(container, "image/jpeg"); +encodedBytes = streamToArray(istream); + +// Get bytes for exected result +refName = "image1png64x64.jpg"; +refFile = do_get_file(TESTDIR + refName); +istream = getFileInputStream(refFile); +do_check_eq(istream.available(), 1593); +referenceBytes = streamToArray(istream); + +// compare the encoder's output to the reference file. +compareArrays(encodedBytes, referenceBytes); + + +/* ========== 4 ========== */ +testnum++; +testdesc = "test decoding a JPEG"; + +// 32x32 jpeg, 3494 bytes. +imgName = "image2.jpg"; +inMimeType = "image/jpeg"; +imgFile = do_get_file(TESTDIR + imgName); + +istream = getFileInputStream(imgFile); +do_check_eq(istream.available(), 3494); + +outParam = {}; +imgTools.decodeImageData(istream, inMimeType, outParam); +container = outParam.value; + +// It's not easy to look at the pixel values from JS, so just +// check the container's size. +do_check_eq(container.width, 32); +do_check_eq(container.height, 32); + + +/* ========== 5 ========== */ +testnum++; +testdesc = "test encoding a scaled PNG"; + +// we'll reuse the container from the previous test +istream = imgTools.encodeScaledImage(container, "image/png", 16, 16); + +encodedBytes = streamToArray(istream); +// Get bytes for exected result +refName = "image2jpg16x16.png"; +refFile = do_get_file(TESTDIR + refName); +istream = getFileInputStream(refFile); +do_check_eq(istream.available(), 948); +referenceBytes = streamToArray(istream); + +// compare the encoder's output to the reference file. +compareArrays(encodedBytes, referenceBytes); + + +/* ========== 6 ========== */ +testnum++; +testdesc = "test encoding an unscaled PNG"; + +// we'll reuse the container from the previous test +istream = imgTools.encodeImage(container, "image/png"); +encodedBytes = streamToArray(istream); + +// Get bytes for exected result +refName = "image2jpg32x32.png"; +refFile = do_get_file(TESTDIR + refName); +istream = getFileInputStream(refFile); +do_check_eq(istream.available(), 3105); +referenceBytes = streamToArray(istream); + +// compare the encoder's output to the reference file. +compareArrays(encodedBytes, referenceBytes); + + +/* ========== 7 ========== */ +testnum++; +testdesc = "test decoding a ICO"; + +// 16x16 ico, 1406 bytes. +imgName = "image3.ico"; +inMimeType = "image/x-icon"; +imgFile = do_get_file(TESTDIR + imgName); + +istream = getFileInputStream(imgFile); +do_check_eq(istream.available(), 1406); + +outParam = { value: null }; +imgTools.decodeImageData(istream, inMimeType, outParam); +container = outParam.value; + +// It's not easy to look at the pixel values from JS, so just +// check the container's size. +do_check_eq(container.width, 16); +do_check_eq(container.height, 16); + + +/* ========== 8 ========== */ +testnum++; +testdesc = "test encoding a scaled PNG"; // note that we're scaling UP + +// we'll reuse the container from the previous test +istream = imgTools.encodeScaledImage(container, "image/png", 32, 32); +encodedBytes = streamToArray(istream); + +// Get bytes for exected result +refName = "image3ico32x32.png"; +refFile = do_get_file(TESTDIR + refName); +istream = getFileInputStream(refFile); +do_check_eq(istream.available(), 2281); +referenceBytes = streamToArray(istream); + +// compare the encoder's output to the reference file. +compareArrays(encodedBytes, referenceBytes); + + +/* ========== 9 ========== */ +testnum++; +testdesc = "test encoding an unscaled PNG"; + +// we'll reuse the container from the previous test +istream = imgTools.encodeImage(container, "image/png"); +encodedBytes = streamToArray(istream); + +// Get bytes for exected result +refName = "image3ico16x16.png"; +refFile = do_get_file(TESTDIR + refName); +istream = getFileInputStream(refFile); +do_check_eq(istream.available(), 330); +referenceBytes = streamToArray(istream); + +// compare the encoder's output to the reference file. +compareArrays(encodedBytes, referenceBytes); + +/* ========== end ========== */ + +} catch (e) { + throw "FAILED in test #" + testnum + " -- " + testdesc + ": " + e; +} +}; diff --git a/toolkit/components/places/src/Makefile.in b/toolkit/components/places/src/Makefile.in index 84f07115674..62e11379127 100644 --- a/toolkit/components/places/src/Makefile.in +++ b/toolkit/components/places/src/Makefile.in @@ -59,6 +59,8 @@ REQUIRES = xpcom \ morkreader \ pref \ necko \ + imglib2 \ + gfx \ intl \ layout \ locale \ diff --git a/toolkit/components/places/src/nsFaviconService.cpp b/toolkit/components/places/src/nsFaviconService.cpp index f5edfac38ab..91ee438c4b0 100644 --- a/toolkit/components/places/src/nsFaviconService.cpp +++ b/toolkit/components/places/src/nsFaviconService.cpp @@ -59,14 +59,12 @@ #include "nsNetUtil.h" #include "nsReadableUtils.h" #include "nsStreamUtils.h" +#include "nsStringStream.h" #include "mozStorageHelper.h" -// This is the maximum favicon size that we will bother storing. Most icons -// are about 4K. Some people add 32x32 versions at different bit depths, -// making them much bigger. It would be nice if could extract just the 16x16 -// version that we need. Instead, we'll just store everything below this -// sanity threshold. -#define MAX_FAVICON_SIZE 32768 +// For favicon optimization +#include "imgITools.h" +#include "imgIContainer.h" #define FAVICON_BUFFER_INCREMENT 8192 @@ -553,6 +551,24 @@ nsFaviconService::SetFaviconData(nsIURI* aFavicon, const PRUint8* aData, PRTime aExpiration) { nsresult rv; + PRUint32 dataLen = aDataLen; + const PRUint8* data = aData; + const nsACString* mimeType = &aMimeType; + nsCString newData, newMimeType; + + // If the page provided a large image for the favicon (eg, a highres image + // or a multiresolution .ico file), we don't want to store more data than + // needed. An uncompressed 16x16 RGBA image is 1024 bytes, and almost all + // sensible 16x16 icons are under 1024 bytes. + if (aDataLen > 1024) { + rv = OptimizeFaviconImage(aData, aDataLen, aMimeType, newData, newMimeType); + if (NS_SUCCEEDED(rv) && newData.Length() < aDataLen) { + data = reinterpret_cast(const_cast(newData.get())), + dataLen = newData.Length(); + mimeType = &newMimeType; + } + } + mozIStorageStatement* statement; { // this block forces the scoper to reset our statement: necessary for the @@ -583,9 +599,9 @@ nsFaviconService::SetFaviconData(nsIURI* aFavicon, const PRUint8* aData, mozStorageStatementScoper scoper(statement); // the insert and update statements share all but the 0th parameter - rv = statement->BindBlobParameter(1, aData, aDataLen); + rv = statement->BindBlobParameter(1, data, dataLen); NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindUTF8StringParameter(2, aMimeType); + rv = statement->BindUTF8StringParameter(2, *mimeType); NS_ENSURE_SUCCESS(rv, rv); rv = statement->BindInt64Parameter(3, aExpiration); NS_ENSURE_SUCCESS(rv, rv); @@ -800,6 +816,48 @@ nsFaviconService::GetFaviconSpecForIconString(const nsCString& aSpec, nsACString } +// nsFaviconService::OptimizeFaviconImage +// +// Given a blob of data (a image file already read into a buffer), optimize +// its size by recompressing it as a 16x16 PNG. +nsresult +nsFaviconService::OptimizeFaviconImage(const PRUint8* aData, PRUint32 aDataLen, + const nsACString& aMimeType, + nsACString& aNewData, + nsACString& aNewMimeType) +{ + nsresult rv; + + + nsCOMPtr imgtool = do_CreateInstance("@mozilla.org/image/tools;1"); + + nsCOMPtr stream; + rv = NS_NewByteInputStream(getter_AddRefs(stream), + reinterpret_cast(aData), aDataLen, + NS_ASSIGNMENT_DEPEND); + NS_ENSURE_SUCCESS(rv, rv); + + // decode image + nsCOMPtr container; + rv = imgtool->DecodeImageData(stream, aMimeType, getter_AddRefs(container)); + NS_ENSURE_SUCCESS(rv, rv); + + aNewMimeType.AssignLiteral("image/png"); + + // scale and recompress + nsCOMPtr iconStream; + rv = imgtool->EncodeScaledImage(container, aNewMimeType, 16, 16, + getter_AddRefs(iconStream)); + NS_ENSURE_SUCCESS(rv, rv); + + // Read the stream into a new buffer. + rv = NS_ConsumeStream(iconStream, PR_UINT32_MAX, aNewData); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + + NS_IMPL_ISUPPORTS4(FaviconLoadListener, nsIRequestObserver, nsIStreamListener, @@ -926,9 +984,6 @@ FaviconLoadListener::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext nsIInputStream *aInputStream, PRUint32 aOffset, PRUint32 aCount) { - if (aOffset + aCount > MAX_FAVICON_SIZE) - return NS_ERROR_FAILURE; // too big - nsCString buffer; nsresult rv = NS_ConsumeStream(aInputStream, aCount, buffer); if (rv != NS_BASE_STREAM_WOULD_BLOCK && NS_FAILED(rv)) diff --git a/toolkit/components/places/src/nsFaviconService.h b/toolkit/components/places/src/nsFaviconService.h index 5a15590d5c4..fd8970fb75a 100644 --- a/toolkit/components/places/src/nsFaviconService.h +++ b/toolkit/components/places/src/nsFaviconService.h @@ -87,6 +87,9 @@ public: nsresult GetFaviconLinkForIconString(const nsCString& aIcon, nsIURI** aOutput); void GetFaviconSpecForIconString(const nsCString& aIcon, nsACString& aOutput); + static nsresult OptimizeFaviconImage(const PRUint8* aData, PRUint32 aDataLen, + const nsACString& aMimeType, + nsACString& aNewData, nsACString& aNewMimeType); NS_DECL_ISUPPORTS NS_DECL_NSIFAVICONSERVICE diff --git a/toolkit/components/places/tests/unit/expected-favicon-big16.ico.png b/toolkit/components/places/tests/unit/expected-favicon-big16.ico.png new file mode 100644 index 00000000000..e9e520cb6cf Binary files /dev/null and b/toolkit/components/places/tests/unit/expected-favicon-big16.ico.png differ diff --git a/toolkit/components/places/tests/unit/expected-favicon-big32.jpg.png b/toolkit/components/places/tests/unit/expected-favicon-big32.jpg.png new file mode 100644 index 00000000000..c3a4aee6166 Binary files /dev/null and b/toolkit/components/places/tests/unit/expected-favicon-big32.jpg.png differ diff --git a/toolkit/components/places/tests/unit/expected-favicon-big4.jpg.png b/toolkit/components/places/tests/unit/expected-favicon-big4.jpg.png new file mode 100644 index 00000000000..1175ba32034 Binary files /dev/null and b/toolkit/components/places/tests/unit/expected-favicon-big4.jpg.png differ diff --git a/toolkit/components/places/tests/unit/expected-favicon-big48.ico.png b/toolkit/components/places/tests/unit/expected-favicon-big48.ico.png new file mode 100644 index 00000000000..b23e0569c48 Binary files /dev/null and b/toolkit/components/places/tests/unit/expected-favicon-big48.ico.png differ diff --git a/toolkit/components/places/tests/unit/expected-favicon-big64.png.png b/toolkit/components/places/tests/unit/expected-favicon-big64.png.png new file mode 100644 index 00000000000..afe2c2dffe1 Binary files /dev/null and b/toolkit/components/places/tests/unit/expected-favicon-big64.png.png differ diff --git a/toolkit/components/places/tests/unit/expected-favicon-scale160x3.jpg.png b/toolkit/components/places/tests/unit/expected-favicon-scale160x3.jpg.png new file mode 100644 index 00000000000..1b121e99518 Binary files /dev/null and b/toolkit/components/places/tests/unit/expected-favicon-scale160x3.jpg.png differ diff --git a/toolkit/components/places/tests/unit/expected-favicon-scale3x160.jpg.png b/toolkit/components/places/tests/unit/expected-favicon-scale3x160.jpg.png new file mode 100644 index 00000000000..56021cbd591 Binary files /dev/null and b/toolkit/components/places/tests/unit/expected-favicon-scale3x160.jpg.png differ diff --git a/toolkit/components/places/tests/unit/favicon-big16.ico b/toolkit/components/places/tests/unit/favicon-big16.ico new file mode 100644 index 00000000000..d44438903b7 Binary files /dev/null and b/toolkit/components/places/tests/unit/favicon-big16.ico differ diff --git a/toolkit/components/places/tests/unit/favicon-big32.jpg b/toolkit/components/places/tests/unit/favicon-big32.jpg new file mode 100644 index 00000000000..b2131bf0c1c Binary files /dev/null and b/toolkit/components/places/tests/unit/favicon-big32.jpg differ diff --git a/toolkit/components/places/tests/unit/favicon-big4.jpg b/toolkit/components/places/tests/unit/favicon-big4.jpg new file mode 100644 index 00000000000..b84fcd35a65 Binary files /dev/null and b/toolkit/components/places/tests/unit/favicon-big4.jpg differ diff --git a/toolkit/components/places/tests/unit/favicon-big48.ico b/toolkit/components/places/tests/unit/favicon-big48.ico new file mode 100644 index 00000000000..f22522411d3 Binary files /dev/null and b/toolkit/components/places/tests/unit/favicon-big48.ico differ diff --git a/toolkit/components/places/tests/unit/favicon-big64.png b/toolkit/components/places/tests/unit/favicon-big64.png new file mode 100644 index 00000000000..2756cf0cb3c Binary files /dev/null and b/toolkit/components/places/tests/unit/favicon-big64.png differ diff --git a/toolkit/components/places/tests/unit/favicon-normal16.png b/toolkit/components/places/tests/unit/favicon-normal16.png new file mode 100644 index 00000000000..62b69a3d031 Binary files /dev/null and b/toolkit/components/places/tests/unit/favicon-normal16.png differ diff --git a/toolkit/components/places/tests/unit/favicon-normal32.png b/toolkit/components/places/tests/unit/favicon-normal32.png new file mode 100644 index 00000000000..5535363c94d Binary files /dev/null and b/toolkit/components/places/tests/unit/favicon-normal32.png differ diff --git a/toolkit/components/places/tests/unit/favicon-scale160x3.jpg b/toolkit/components/places/tests/unit/favicon-scale160x3.jpg new file mode 100644 index 00000000000..422ee7ea0b1 Binary files /dev/null and b/toolkit/components/places/tests/unit/favicon-scale160x3.jpg differ diff --git a/toolkit/components/places/tests/unit/favicon-scale3x160.jpg b/toolkit/components/places/tests/unit/favicon-scale3x160.jpg new file mode 100644 index 00000000000..e8514966a02 Binary files /dev/null and b/toolkit/components/places/tests/unit/favicon-scale3x160.jpg differ diff --git a/toolkit/components/places/tests/unit/test_favicons.js b/toolkit/components/places/tests/unit/test_favicons.js new file mode 100644 index 00000000000..45ac5367b6d --- /dev/null +++ b/toolkit/components/places/tests/unit/test_favicons.js @@ -0,0 +1,324 @@ +/* + * Tests for nsIFaviconService + */ + +const TESTDIR = "toolkit/components/places/tests/unit/"; + +/* + * dumpToFile() + * + * For test development, dumps the specified array to a file. + * Call |dumpToFile(outData);| in a test to file to a file. + */ +function dumpToFile(aData) { + const path = "/tmp"; + + var outputFile = Cc["@mozilla.org/file/local;1"]. + createInstance(Ci.nsILocalFile); + outputFile.initWithPath(path); + outputFile.append("testdump.png"); + + var outputStream = Cc["@mozilla.org/network/file-output-stream;1"]. + createInstance(Ci.nsIFileOutputStream); + // WR_ONLY|CREAT|TRUNC + outputStream.init(outputFile, 0x02 | 0x08 | 0x20, 0644, null); + + var bos = Cc["@mozilla.org/binaryoutputstream;1"]. + createInstance(Ci.nsIBinaryOutputStream); + bos.setOutputStream(outputStream); + + bos.writeByteArray(aData, aData.length); + + outputStream.close(); +} + +/* + * readFileData() + * + * Reads the data from the specified nsIFile, and returns an array of bytes. + */ +function readFileData(aFile) { + var inputStream = Cc["@mozilla.org/network/file-input-stream;1"]. + createInstance(Ci.nsIFileInputStream); + // init the stream as RD_ONLY, -1 == default permissions. + inputStream.init(aFile, 0x01, -1, null); + var size = inputStream.available(); + + // use a binary input stream to grab the bytes. + var bis = Cc["@mozilla.org/binaryinputstream;1"]. + createInstance(Ci.nsIBinaryInputStream); + bis.setInputStream(inputStream); + + var bytes = bis.readByteArray(size); + + if (size != bytes.length) + throw "Didn't read expected number of bytes"; + + return bytes; +} + + +/* + * setAndGetFaviconData() + * + * Calls setFaviconData() with the specified image data, + * and then retrieves it with getFaviconData(). Returns + * and array of bytes and mimetype. + */ +function setAndGetFaviconData(aFilename, aData, aMimeType) { + var iconURI = uri("http://places.test/" + aFilename); + + iconsvc.setFaviconData(iconURI, + aData, aData.length, aMimeType, + Number.MAX_VALUE); + + var mimeTypeOutparam = {}; + + var outData = iconsvc.getFaviconData(iconURI, + mimeTypeOutparam, {}); + + return [outData, mimeTypeOutparam.value]; +} + + +/* + * compareArrays + * + * Compares two arrays, and throws if there's a difference. + */ +function compareArrays(aArray1, aArray2) { + do_check_eq(aArray1.length, aArray2.length); + + for (var i = 0; i < aArray1.length; i++) + if (aArray1[i] != aArray2[i]) + throw "arrays differ at index " + i; +} + + +var iconsvc; + +function run_test() { +try { + + +/* ========== 0 ========== */ +var testnum = 0; +var testdesc = "nsIFaviconService setup"; + +iconsvc = Cc["@mozilla.org/browser/favicon-service;1"]. + getService(Ci.nsIFaviconService); + +if (!iconsvc) + throw "Couldn't get nsIFaviconService service" + + +/* ========== 1 ========== */ +testnum++; +testdesc = "test storing a normal 16x16 icon"; + +// 16x16 png, 286 bytes. +var iconName = "favicon-normal16.png"; +var inMimeType = "image/png"; +var iconFile = do_get_file(TESTDIR + iconName); + +var inData = readFileData(iconFile); +do_check_eq(inData.length, 286); + +var [outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType); + +// Ensure input and output are identical +do_check_eq(inMimeType, outMimeType); +compareArrays(inData, outData); + + +/* ========== 2 ========== */ +testnum++; +testdesc = "test storing a normal 32x32 icon"; + +// 32x32 png, 344 bytes. +iconName = "favicon-normal32.png"; +inMimeType = "image/png"; +iconFile = do_get_file(TESTDIR + iconName); + +inData = readFileData(iconFile); +do_check_eq(inData.length, 344); + +[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType); + +// Ensure input and output are identical +do_check_eq(inMimeType, outMimeType); +compareArrays(inData, outData); + + +/* ========== 3 ========== */ +testnum++; +testdesc = "test storing an oversize 16x16 icon "; + +// in: 16x16 ico, 1406 bytes. +// out: 16x16 png +iconName = "favicon-big16.ico"; +inMimeType = "image/x-icon"; +iconFile = do_get_file(TESTDIR + iconName); + +inData = readFileData(iconFile); +do_check_eq(inData.length, 1406); + +[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType); + +// Read in the expected output. +var expectedFile = do_get_file(TESTDIR + "expected-" + iconName + ".png"); +var expectedData = readFileData(expectedFile); + +// Compare thet expected data to the actual data. +do_check_eq("image/png", outMimeType); +compareArrays(expectedData, outData); + +/* ========== 4 ========== */ +testnum++; +testdesc = "test storing an oversize 4x4 icon "; + +// in: 4x4 jpg, 4751 bytes. +// out: 16x16 png +iconName = "favicon-big4.jpg"; +inMimeType = "image/jpeg"; +iconFile = do_get_file(TESTDIR + iconName); + +inData = readFileData(iconFile); +do_check_eq(inData.length, 4751); + +[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType); + +// Read in the expected output. +var expectedFile = do_get_file(TESTDIR + "expected-" + iconName + ".png"); +var expectedData = readFileData(expectedFile); + +// Compare thet expected data to the actual data. +do_check_eq("image/png", outMimeType); +compareArrays(expectedData, outData); + + +/* ========== 5 ========== */ +testnum++; +testdesc = "test storing an oversize 32x32 icon "; + +// in: 32x32 jpg, 3494 bytes. +// out: 16x16 png +iconName = "favicon-big32.jpg"; +inMimeType = "image/jpeg"; +iconFile = do_get_file(TESTDIR + iconName); + +inData = readFileData(iconFile); +do_check_eq(inData.length, 3494); + +[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType); + +// Read in the expected output. +var expectedFile = do_get_file(TESTDIR + "expected-" + iconName + ".png"); +var expectedData = readFileData(expectedFile); + +// Compare thet expected data to the actual data. +do_check_eq("image/png", outMimeType); +compareArrays(expectedData, outData); + +/* ========== 6 ========== */ +testnum++; +testdesc = "test storing an oversize 48x48 icon "; + +// in: 48x48 ico, 56646 bytes. +// (howstuffworks.com icon, contains 13 icons with sizes from 16x16 to +// 48x48 in varying depths) +// out: 16x16 png +iconName = "favicon-big48.ico"; +inMimeType = "image/x-icon"; +iconFile = do_get_file(TESTDIR + iconName); + +inData = readFileData(iconFile); +do_check_eq(inData.length, 56646); + +[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType); + +// Read in the expected output. +var expectedFile = do_get_file(TESTDIR + "expected-" + iconName + ".png"); +var expectedData = readFileData(expectedFile); + +// Compare thet expected data to the actual data. +do_check_eq("image/png", outMimeType); +compareArrays(expectedData, outData); + +/* ========== 7 ========== */ +testnum++; +testdesc = "test storing an oversize 64x64 icon "; + +// in: 64x64 png, 10698 bytes. +// out: 16x16 png +iconName = "favicon-big64.png"; +inMimeType = "image/png"; +iconFile = do_get_file(TESTDIR + iconName); + +inData = readFileData(iconFile); +do_check_eq(inData.length, 10698); + +[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType); + +// Read in the expected output. +var expectedFile = do_get_file(TESTDIR + "expected-" + iconName + ".png"); +var expectedData = readFileData(expectedFile); + +// Compare thet expected data to the actual data. +do_check_eq("image/png", outMimeType); +compareArrays(expectedData, outData); + +/* ========== 8 ========== */ +testnum++; +testdesc = "test scaling an oversize 160x3 icon "; + +// in: 160x3 jpg, 5095 bytes. +// out: 16x16 png +iconName = "favicon-scale160x3.jpg"; +inMimeType = "image/jpeg"; +iconFile = do_get_file(TESTDIR + iconName); + +inData = readFileData(iconFile); +do_check_eq(inData.length, 5095); + +[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType); + +// Read in the expected output. +var expectedFile = do_get_file(TESTDIR + "expected-" + iconName + ".png"); +var expectedData = readFileData(expectedFile); + +// Compare thet expected data to the actual data. +do_check_eq("image/png", outMimeType); +compareArrays(expectedData, outData); + +/* ========== 9 ========== */ +testnum++; +testdesc = "test scaling an oversize 3x160 icon "; + +// in: 3x160 jpg, 5059 bytes. +// out: 16x16 png +iconName = "favicon-scale3x160.jpg"; +inMimeType = "image/jpeg"; +iconFile = do_get_file(TESTDIR + iconName); + +inData = readFileData(iconFile); +do_check_eq(inData.length, 5059); + +[outData, outMimeType] = setAndGetFaviconData(iconName, inData, inMimeType); + +// Read in the expected output. +var expectedFile = do_get_file(TESTDIR + "expected-" + iconName + ".png"); +var expectedData = readFileData(expectedFile); + +// Compare thet expected data to the actual data. +do_check_eq("image/png", outMimeType); +compareArrays(expectedData, outData); + + + +/* ========== end ========== */ + +} catch (e) { + throw "FAILED in test #" + testnum + " -- " + testdesc + ": " + e; +} +};