• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
trackingCallback(void * decompressionTrackingRefCon,OSStatus,ICMDecompressionTrackingFlags decompressionTrackingFlags,CVPixelBufferRef pixelBuffer,TimeValue64,TimeValue64,ICMValidTimeFlags,void *,void *)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 
create(unsigned long pixelFormat,size_t width,size_t height)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 
QTDecompressionSession(unsigned long pixelFormat,size_t width,size_t height)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 
~QTDecompressionSession()60 QTDecompressionSession::~QTDecompressionSession()
61 {
62     if (m_session)
63         ICMDecompressionSessionRelease(m_session);
64 }
65 
initializeSession()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 
canDecompress(QTPixelBuffer inBuffer)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 
decompress(QTPixelBuffer inBuffer)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