1 //-*****************************************************************************
2 //
3 // Copyright (c) 2013,
4 //  Sony Pictures Imageworks, Inc. and
5 //  Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd.
6 //
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are
11 // met:
12 // *       Redistributions of source code must retain the above copyright
13 // notice, this list of conditions and the following disclaimer.
14 // *       Redistributions in binary form must reproduce the above
15 // copyright notice, this list of conditions and the following disclaimer
16 // in the documentation and/or other materials provided with the
17 // distribution.
18 // *       Neither the name of Sony Pictures Imageworks, nor
19 // Industrial Light & Magic nor the names of their contributors may be used
20 // to endorse or promote products derived from this software without specific
21 // prior written permission.
22 //
23 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 //
35 //-*****************************************************************************
36 
37 #include <Alembic/AbcCoreOgawa/SpwImpl.h>
38 #include <Alembic/AbcCoreOgawa/CpwImpl.h>
39 #include <Alembic/AbcCoreOgawa/WriteUtil.h>
40 
41 namespace Alembic {
42 namespace AbcCoreOgawa {
43 namespace ALEMBIC_VERSION_NS {
44 
45 //-*****************************************************************************
SpwImpl(AbcA::CompoundPropertyWriterPtr iParent,Ogawa::OGroupPtr iGroup,PropertyHeaderPtr iHeader,size_t iIndex)46 SpwImpl::SpwImpl( AbcA::CompoundPropertyWriterPtr iParent,
47                   Ogawa::OGroupPtr iGroup,
48                   PropertyHeaderPtr iHeader,
49                   size_t iIndex ) :
50     m_parent( iParent ), m_header( iHeader ), m_group( iGroup ),
51     m_index( iIndex )
52 {
53     ABCA_ASSERT( m_parent, "Invalid parent" );
54     ABCA_ASSERT( m_header, "Invalid property header" );
55     ABCA_ASSERT( m_group, "Invalid group" );
56 
57     if ( m_header->header.getPropertyType() != AbcA::kScalarProperty )
58     {
59         ABCA_THROW( "Attempted to create a ScalarPropertyWriter from a "
60                     "non-scalar property type" );
61     }
62 }
63 
64 
65 //-*****************************************************************************
~SpwImpl()66 SpwImpl::~SpwImpl()
67 {
68     AbcA::ArchiveWriterPtr archive = m_parent->getObject()->getArchive();
69 
70     index_t maxSamples = archive->getMaxNumSamplesForTimeSamplingIndex(
71             m_header->timeSamplingIndex );
72 
73     Util::uint32_t numSamples = m_header->nextSampleIndex;
74 
75     // a constant property, we wrote the same sample over and over
76     if ( m_header->lastChangedIndex == 0 && numSamples > 0 )
77     {
78         numSamples = 1;
79     }
80 
81     if ( maxSamples < numSamples )
82     {
83         archive->setMaxNumSamplesForTimeSamplingIndex(
84             m_header->timeSamplingIndex, numSamples );
85     }
86 
87     Util::SpookyHash hash;
88     hash.Init(0, 0);
89     HashPropertyHeader( m_header->header, hash );
90 
91     // mix in the accumulated hash if we have samples
92     if ( numSamples != 0 )
93     {
94         hash.Update( m_hash.d, 16 );
95     }
96 
97     Util::uint64_t hash0, hash1;
98     hash.Final( &hash0, &hash1 );
99     Util::shared_ptr< CpwImpl > parent =
100         Alembic::Util::dynamic_pointer_cast< CpwImpl,
101             AbcA::CompoundPropertyWriter > ( m_parent );
102     parent->fillHash( m_index, hash0, hash1 );
103 }
104 
105 //-*****************************************************************************
setFromPreviousSample()106 void SpwImpl::setFromPreviousSample()
107 {
108 
109     // Make sure we aren't writing more samples than we have times for
110     // This applies to acyclic sampling only
111     ABCA_ASSERT(
112         !m_header->header.getTimeSampling()->getTimeSamplingType().isAcyclic()
113         || m_header->header.getTimeSampling()->getNumStoredTimes() >
114         m_header->nextSampleIndex,
115         "Can not set more samples than we have times for when using "
116         "Acyclic sampling." );
117 
118     ABCA_ASSERT( m_header->nextSampleIndex > 0,
119         "Can't set from previous sample before any samples have been written" );
120 
121     Util::Digest digest = m_previousWrittenSampleID->getKey().digest;
122     Util::SpookyHash::ShortEnd(m_hash.words[0], m_hash.words[1],
123                                digest.words[0], digest.words[1]);
124     m_header->nextSampleIndex ++;
125 }
126 
127 //-*****************************************************************************
setSample(const void * iSamp)128 void SpwImpl::setSample( const void *iSamp )
129 {
130     // Make sure we aren't writing more samples than we have times for
131     // This applies to acyclic sampling only
132     ABCA_ASSERT(
133         !m_header->header.getTimeSampling()->getTimeSamplingType().isAcyclic()
134         || m_header->header.getTimeSampling()->getNumStoredTimes() >
135         m_header->nextSampleIndex,
136         "Can not write more samples than we have times for when using "
137         "Acyclic sampling." );
138 
139     AbcA::ArraySample samp( iSamp, m_header->header.getDataType(),
140                             AbcA::Dimensions(1) );
141 
142      // The Key helps us analyze the sample.
143      AbcA::ArraySample::Key key = samp.getKey();
144 
145      // mask out the non-string POD since Ogawa can safely share the same data
146      // even if it originated from a different POD
147      // the non-fixed sizes of our strings (plus added null characters) makes
148      // determing the sie harder so strings are handled seperately
149     if ( key.origPOD != Alembic::Util::kStringPOD &&
150          key.origPOD != Alembic::Util::kWstringPOD )
151     {
152         key.origPOD = Alembic::Util::kInt8POD;
153         key.readPOD = Alembic::Util::kInt8POD;
154     }
155 
156     // We need to write the sample
157     if ( m_header->nextSampleIndex == 0  ||
158         !( m_previousWrittenSampleID &&
159             key == m_previousWrittenSampleID->getKey() ) )
160     {
161 
162         // we only need to repeat samples if this is not the first change
163         if (m_header->firstChangedIndex != 0)
164         {
165             // copy the samples from after the last change to the latest index
166             for ( index_t smpI = m_header->lastChangedIndex + 1;
167                 smpI < m_header->nextSampleIndex; ++smpI )
168             {
169                 assert( smpI > 0 );
170                 CopyWrittenData( m_group, m_previousWrittenSampleID );
171             }
172         }
173 
174         // Write this sample, which will update its internal
175         // cache of what the previously written sample was.
176         AbcA::ArchiveWriterPtr awp = this->getObject()->getArchive();
177 
178         // Write the sample.
179         // This distinguishes between string, wstring, and regular arrays.
180         m_previousWrittenSampleID =
181             WriteData( GetWrittenSampleMap( awp ), m_group, samp, key );
182 
183         if (m_header->firstChangedIndex == 0)
184         {
185             m_header->firstChangedIndex = m_header->nextSampleIndex;
186         }
187         // this index is now the last change
188         m_header->lastChangedIndex = m_header->nextSampleIndex;
189     }
190 
191     if ( m_header->nextSampleIndex == 0 )
192     {
193         m_hash = m_previousWrittenSampleID->getKey().digest;
194     }
195     else
196     {
197         Util::Digest digest = m_previousWrittenSampleID->getKey().digest;
198         Util::SpookyHash::ShortEnd( m_hash.words[0], m_hash.words[1],
199                                     digest.words[0], digest.words[1] );
200     }
201 
202     m_header->nextSampleIndex ++;
203 }
204 
205 //-*****************************************************************************
asScalarPtr()206 AbcA::ScalarPropertyWriterPtr SpwImpl::asScalarPtr()
207 {
208     return shared_from_this();
209 }
210 
211 //-*****************************************************************************
getNumSamples()212 size_t SpwImpl::getNumSamples()
213 {
214     return ( size_t )m_header->nextSampleIndex;
215 }
216 
217 //-*****************************************************************************
setTimeSamplingIndex(Util::uint32_t iIndex)218 void SpwImpl::setTimeSamplingIndex( Util::uint32_t iIndex )
219 {
220     // will assert if TimeSamplingPtr not found
221     AbcA::TimeSamplingPtr ts =
222         m_parent->getObject()->getArchive()->getTimeSampling( iIndex );
223 
224     ABCA_ASSERT( !ts->getTimeSamplingType().isAcyclic() ||
225         ts->getNumStoredTimes() >= m_header->nextSampleIndex,
226         "Already have written more samples than we have times for when using "
227         "Acyclic sampling." );
228 
229     m_header->header.setTimeSampling(ts);
230     m_header->timeSamplingIndex = iIndex;
231 }
232 
233 //-*****************************************************************************
getHeader() const234 const AbcA::PropertyHeader & SpwImpl::getHeader() const
235 {
236     ABCA_ASSERT( m_header, "Invalid header" );
237     return m_header->header;
238 }
239 
240 //-*****************************************************************************
getObject()241 AbcA::ObjectWriterPtr SpwImpl::getObject()
242 {
243     ABCA_ASSERT( m_parent, "Invalid parent" );
244     return m_parent->getObject();
245 }
246 
247 //-*****************************************************************************
getParent()248 AbcA::CompoundPropertyWriterPtr SpwImpl::getParent()
249 {
250     ABCA_ASSERT( m_parent, "Invalid parent" );
251     return m_parent;
252 }
253 
254 } // End namespace ALEMBIC_VERSION_NS
255 } // End namespace AbcCoreOgawa
256 } // End namespace Alembic
257