michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.favicons.decoders; michael@0: michael@0: import android.graphics.Bitmap; michael@0: import android.util.Log; michael@0: michael@0: import java.io.ByteArrayOutputStream; michael@0: import java.util.Iterator; michael@0: michael@0: /** michael@0: * Class representing the result of loading a favicon. michael@0: * This operation will produce either a collection of favicons, a single favicon, or no favicon. michael@0: * It is necessary to model single favicons differently to a collection of one favicon (An entity michael@0: * that may not exist with this scheme) since the in-database representation of these things differ. michael@0: * (In particular, collections of favicons are stored in encoded ICO format, whereas single icons are michael@0: * stored as decoded bitmap blobs.) michael@0: */ michael@0: public class LoadFaviconResult { michael@0: private static final String LOGTAG = "LoadFaviconResult"; michael@0: michael@0: byte[] faviconBytes; michael@0: int offset; michael@0: int length; michael@0: michael@0: boolean isICO; michael@0: Iterator bitmapsDecoded; michael@0: michael@0: public Iterator getBitmaps() { michael@0: return bitmapsDecoded; michael@0: } michael@0: michael@0: /** michael@0: * Return a representation of this result suitable for storing in the database. michael@0: * michael@0: * @return A byte array containing the bytes from which this result was decoded, michael@0: * or null if re-encoding failed. michael@0: */ michael@0: public byte[] getBytesForDatabaseStorage() { michael@0: // Begin by normalising the buffer. michael@0: if (offset != 0 || length != faviconBytes.length) { michael@0: final byte[] normalised = new byte[length]; michael@0: System.arraycopy(faviconBytes, offset, normalised, 0, length); michael@0: offset = 0; michael@0: faviconBytes = normalised; michael@0: } michael@0: michael@0: // For results containing multiple images, we store the result verbatim. (But cutting the michael@0: // buffer to size first). michael@0: // We may instead want to consider re-encoding the entire ICO as a collection of efficiently michael@0: // encoded PNGs. This may not be worth the CPU time (Indeed, the encoding of single-image michael@0: // favicons may also not be worth the time/space tradeoff.). michael@0: if (isICO) { michael@0: return faviconBytes; michael@0: } michael@0: michael@0: // For results containing a single image, we re-encode the michael@0: // result as a PNG in an effort to save space. michael@0: final Bitmap favicon = ((FaviconDecoder.SingleBitmapIterator) bitmapsDecoded).peek(); michael@0: final ByteArrayOutputStream stream = new ByteArrayOutputStream(); michael@0: michael@0: try { michael@0: if (favicon.compress(Bitmap.CompressFormat.PNG, 100, stream)) { michael@0: return stream.toByteArray(); michael@0: } michael@0: } catch (OutOfMemoryError e) { michael@0: Log.w(LOGTAG, "Out of memory re-compressing favicon."); michael@0: } michael@0: michael@0: Log.w(LOGTAG, "Favicon re-compression failed."); michael@0: return null; michael@0: } michael@0: michael@0: }