1 /* 2 * Copyright (C) 2010 Apple, Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 #include "config.h" 26 27 #include "QTDecompressionSession.h" 28 29 #include <ImageCompression.h> 30 #include <algorithm> 31 32 class QTDecompressionSessionClient { 33 public: 34 static void trackingCallback(void *decompressionTrackingRefCon, OSStatus, 35 ICMDecompressionTrackingFlags decompressionTrackingFlags, CVPixelBufferRef pixelBuffer, 36 TimeValue64, TimeValue64, ICMValidTimeFlags, void *, void *) 37 { 38 QTDecompressionSession* session = static_cast<QTDecompressionSession*>(decompressionTrackingRefCon); 39 ASSERT(session); 40 41 if (decompressionTrackingFlags & kICMDecompressionTracking_FrameDecoded) 42 session->m_latestFrame = QTPixelBuffer(pixelBuffer); 43 } 44 }; 45 46 PassOwnPtr<QTDecompressionSession> QTDecompressionSession::create(unsigned long pixelFormat, size_t width, size_t height) 47 { 48 return adoptPtr(new QTDecompressionSession(pixelFormat, width, height)); 49 } 50 51 QTDecompressionSession::QTDecompressionSession(unsigned long pixelFormat, size_t width, size_t height) 52 : m_session(0) 53 , m_pixelFormat(pixelFormat) 54 , m_width(width) 55 , m_height(height) 56 { 57 initializeSession(); 58 } 59 60 QTDecompressionSession::~QTDecompressionSession() 61 { 62 if (m_session) 63 ICMDecompressionSessionRelease(m_session); 64 } 65 66 void QTDecompressionSession::initializeSession() 67 { 68 if (m_session) 69 return; 70 71 ICMPixelFormatInfo pixelFormatInfo = {sizeof(ICMPixelFormatInfo), 0}; 72 if (ICMGetPixelFormatInfo(m_pixelFormat, &pixelFormatInfo) != noErr) { 73 // The ICM does not know anything about the pixelFormat contained in 74 // the pixel buffer, so it won't be able to convert it to RGBA. 75 return; 76 } 77 78 // The depth and cType fields of the ImageDescriptionHandle are filled 79 // out according to the instructions in Technical Q&A QA1183: 80 // http://developer.apple.com/library/mac/#qa/qa2001/qa1183.html 81 bool isIndexed = pixelFormatInfo.formatFlags & kICMPixelFormatIsIndexed; 82 bool isQD = pixelFormatInfo.formatFlags & kICMPixelFormatIsSupportedByQD; 83 bool isMonochrome = pixelFormatInfo.formatFlags & kICMPixelFormatIsMonochrome; 84 bool hasAlpha = pixelFormatInfo.formatFlags & kICMPixelFormatHasAlphaChannel; 85 86 unsigned int depth = 24; // The default depth is 24. 87 if (hasAlpha) 88 depth = 32; // Any pixel format with alpha gets a depth of 32. 89 else if (isMonochrome) { 90 // Grayscale pixel formats get depths 33 through 40, depending 91 // on their bits per pixel. Yes, this means that 16-bit grayscale 92 // and 8-bit grayscale have the same pixel depth. 93 depth = 32 + std::min<unsigned int>(8, pixelFormatInfo.bitsPerPixel[0]); 94 } else if (isIndexed) { 95 // Indexed pixel formats get a depth of 1 through 8, depending on 96 // the their bits per pixel. 97 depth = pixelFormatInfo.bitsPerPixel[0]; 98 } 99 100 // If QuickDraw supports the given pixel format, the cType should be kRawCodecType. 101 // Otherwise, use the pixel format code for the cType. We are assuming the pixel 102 // buffer is uncompressed. 103 unsigned long cType = isQD ? kRawCodecType : m_pixelFormat; 104 105 ImageDescriptionHandle description = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription)); 106 (**description).idSize = sizeof(ImageDescription); 107 (**description).cType = cType; 108 (**description).version = 2; 109 (**description).spatialQuality = codecLosslessQuality; 110 (**description).width = m_width; 111 (**description).height = m_height; 112 (**description).hRes = 72 << 16; // 72 DPI as a fixed-point number 113 (**description).vRes = 72 << 16; // 72 DPI as a fixed-point number 114 (**description).frameCount = 1; 115 (**description).depth = depth; 116 (**description).clutID = -1; 117 118 // Create the mandatory ICMDecompressionSessionOptions, but leave 119 // all the default values. 120 ICMDecompressionSessionOptionsRef options = 0; 121 ICMDecompressionSessionOptionsCreate(kCFAllocatorDefault, &options); 122 123 CFDictionaryRef pixelBufferAttributes = QTPixelBuffer::createPixelBufferAttributesDictionary(QTPixelBuffer::ConfigureForCGImage); 124 125 ICMDecompressionTrackingCallbackRecord callback = { 126 QTDecompressionSessionClient::trackingCallback, 127 this, 128 }; 129 130 ICMDecompressionSessionCreate(kCFAllocatorDefault, 131 description, 132 options, 133 pixelBufferAttributes, 134 &callback, 135 &m_session); 136 137 if (pixelBufferAttributes) 138 CFRelease(pixelBufferAttributes); 139 140 ICMDecompressionSessionOptionsRelease(options); 141 DisposeHandle((Handle)description); 142 } 143 144 bool QTDecompressionSession::canDecompress(QTPixelBuffer inBuffer) 145 { 146 return m_session 147 && inBuffer.pixelFormatType() == m_pixelFormat 148 && inBuffer.width() == m_width 149 && inBuffer.height() == m_height; 150 } 151 152 QTPixelBuffer QTDecompressionSession::decompress(QTPixelBuffer inBuffer) 153 { 154 if (!canDecompress(inBuffer)) 155 return QTPixelBuffer(); 156 157 inBuffer.lockBaseAddress(); 158 ICMDecompressionSessionDecodeFrame(m_session, 159 static_cast<UInt8*>(inBuffer.baseAddress()), 160 inBuffer.dataSize(), 161 0, // frameOptions 162 0, // frameTime 163 0); // sourceFrameRefCon 164 165 // Because we passed in 0 for frameTime, the above function 166 // is synchronous, and the client callback will have been 167 // called before the function returns, and m_latestFrame 168 // will contain the newly decompressed frame. 169 return m_latestFrame; 170 } 171