1 /*=========================================================================
4 Module: $RCSfile: gdcmFile.cxx,v $
6 Date: $Date: 2004/11/19 18:49:39 $
7 Version: $Revision: 1.158 $
9 Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10 l'Image). All rights reserved. See Doc/License.txt or
11 http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
13 This software is distributed WITHOUT ANY WARRANTY; without even
14 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 PURPOSE. See the above copyright notices for more information.
17 =========================================================================*/
20 #include "gdcmDebug.h"
22 #include "gdcmBinEntry.h"
27 typedef std::pair<TagDocEntryHT::iterator,TagDocEntryHT::iterator> IterHT;
29 //-------------------------------------------------------------------------
30 // Constructor / Destructor
32 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
33 * file (Header only deals with the ... header)
34 * Opens (in read only and when possible) an existing file and checks
35 * for DICOM compliance. Returns NULL on failure.
36 * It will be up to the user to load the pixels into memory
37 * (see GetImageData, GetImageDataRaw)
38 * \note the in-memory representation of all available tags found in
39 * the DICOM header is post-poned to first header information access.
40 * This avoid a double parsing of public part of the header when
41 * user sets an a posteriori shadow dictionary (efficiency can be
42 * seen as a side effect).
43 * @param header already built Header
45 File::File(Header *header)
47 HeaderInternal = header;
53 * \brief Constructor dedicated to deal with the *pixels* area of a ACR/DICOMV3
54 * file (Header only deals with the ... header)
55 * Opens (in read only and when possible) an existing file and checks
56 * for DICOM compliance. Returns NULL on failure.
57 * It will be up to the user to load the pixels into memory
58 * (see GetImageData, GetImageDataRaw)
59 * \note the in-memory representation of all available tags found in
60 * the DICOM header is post-poned to first header information access.
61 * This avoid a double parsing of public part of the header when
62 * one sets an a posteriori shadow dictionary (efficiency can be
63 * seen as a side effect).
64 * @param filename file to be opened for parsing
66 File::File(std::string const & filename )
68 HeaderInternal = new Header( filename );
74 * \brief Factorization for various forms of constructors.
76 void File::Initialise()
78 WriteMode = WMODE_NATIVE;
79 WriteType = WTYPE_IMPL_VR;
80 PixelConverter = NULL; //just in case
83 if ( HeaderInternal->IsReadable() )
85 ImageDataSizeRaw = ComputeDecompressedPixelDataSizeFromHeader();
86 if ( HeaderInternal->HasLUT() )
88 ImageDataSize = 3 * ImageDataSizeRaw;
92 ImageDataSize = ImageDataSizeRaw;
95 PixelConverter = new PixelConvert;
96 PixelConverter->GrabInformationsFromHeader( HeaderInternal );
98 Archive = new DocEntryArchive( HeaderInternal );
105 * \brief canonical destructor
106 * \note If the Header was created by the File constructor,
107 * it is destroyed by the File
113 delete PixelConverter;
122 delete HeaderInternal;
126 DeleteInitialValues();
130 * \brief Sets some initial values for the Constructor
131 * \warning not end user intended
133 void File::SaveInitialValues()
135 PixelRead = -1; // no ImageData read yet.
136 LastAllocatedPixelDataLength = 0;
141 InitialPlanConfig = "";
142 InitialBitsAllocated = "";
145 InitialRedLUTDescr = 0;
146 InitialGreenLUTDescr = 0;
147 InitialBlueLUTDescr = 0;
148 InitialRedLUTData = 0;
149 InitialGreenLUTData = 0;
150 InitialBlueLUTData = 0;
152 if ( HeaderInternal->IsReadable() )
154 // the following values *may* be modified
155 // by File::GetImageDataIntoVectorRaw
156 // we save their initial value.
157 InitialSpp = HeaderInternal->GetEntryByNumber(0x0028,0x0002);
158 InitialPhotInt = HeaderInternal->GetEntryByNumber(0x0028,0x0004);
159 InitialPlanConfig = HeaderInternal->GetEntryByNumber(0x0028,0x0006);
161 InitialBitsAllocated = HeaderInternal->GetEntryByNumber(0x0028,0x0100);
162 InitialHighBit = HeaderInternal->GetEntryByNumber(0x0028,0x0102);
164 // the following entries *may* be removed from the H table
165 // (NOT deleted ...) by File::GetImageDataIntoVectorRaw
166 // we keep a pointer on them.
167 InitialRedLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1101);
168 InitialGreenLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1102);
169 InitialBlueLUTDescr = HeaderInternal->GetDocEntryByNumber(0x0028,0x1103);
171 InitialRedLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1201);
172 InitialGreenLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1202);
173 InitialBlueLUTData = HeaderInternal->GetDocEntryByNumber(0x0028,0x1203);
178 * \brief restores some initial values
179 * \warning not end user intended
181 void File::RestoreInitialValues()
183 if ( HeaderInternal->IsReadable() )
185 // the following values *may* have been modified
186 // by File::GetImageDataIntoVectorRaw
187 // we restore their initial value.
188 if ( InitialSpp != "")
189 HeaderInternal->SetEntryByNumber(InitialSpp,0x0028,0x0002);
190 if ( InitialPhotInt != "")
191 HeaderInternal->SetEntryByNumber(InitialPhotInt,0x0028,0x0004);
192 if ( InitialPlanConfig != "")
194 HeaderInternal->SetEntryByNumber(InitialPlanConfig,0x0028,0x0006);
195 if ( InitialBitsAllocated != "")
196 HeaderInternal->SetEntryByNumber(InitialBitsAllocated,0x0028,0x0100);
197 if ( InitialHighBit != "")
198 HeaderInternal->SetEntryByNumber(InitialHighBit,0x0028,0x0102);
200 // the following entries *may* be have been removed from the H table
201 // (NOT deleted ...) by File::GetImageDataIntoVectorRaw
204 if (InitialRedLUTDescr)
205 HeaderInternal->AddEntry(InitialRedLUTDescr);
206 if (InitialGreenLUTDescr)
207 HeaderInternal->AddEntry(InitialGreenLUTDescr);
208 if (InitialBlueLUTDescr)
209 HeaderInternal->AddEntry(InitialBlueLUTDescr);
211 if (InitialRedLUTData)
212 HeaderInternal->AddEntry(InitialBlueLUTDescr);
213 if (InitialGreenLUTData)
214 HeaderInternal->AddEntry(InitialGreenLUTData);
215 if (InitialBlueLUTData)
216 HeaderInternal->AddEntry(InitialBlueLUTData);
221 * \brief delete initial values (il they were saved)
222 * of InitialLutDescriptors and InitialLutData
224 void File::DeleteInitialValues()
226 // InitialLutDescriptors and InitialLutData
227 // will have to be deleted if the don't belong any longer
228 // to the Header H table when the header is deleted...
231 // We don't know if the InitialLutData are still in the header or not !
232 /* if ( InitialRedLUTDescr )
233 delete InitialRedLUTDescr;
235 if ( InitialGreenLUTDescr )
236 delete InitialGreenLUTDescr;
238 if ( InitialBlueLUTDescr )
239 delete InitialBlueLUTDescr;
241 if ( InitialRedLUTData )
242 delete InitialRedLUTData;
244 if ( InitialGreenLUTData )
245 delete InitialGreenLUTData;
247 if ( InitialBlueLUTData )
248 delete InitialBlueLUTData;*/
251 //-----------------------------------------------------------------------------
254 //-----------------------------------------------------------------------------
258 * \brief computes the length (in bytes) we must ALLOCATE to receive the
259 * image(s) pixels (multiframes taken into account)
260 * \warning : it is NOT the group 7FE0 length
261 * (no interest for compressed images).
263 int File::ComputeDecompressedPixelDataSizeFromHeader()
265 // see PS 3.3-2003 : C.7.6.3.2.1
275 // YBR_FULL_422 (no LUT, no Palette)
281 // ex : gdcm-US-ALOKA-16.dcm
282 // 0028|1221 [OW] [Segmented Red Palette Color Lookup Table Data]
283 // 0028|1222 [OW] [Segmented Green Palette Color Lookup Table Data]
284 // 0028|1223 [OW] [Segmented Blue Palette Color Lookup Table Data]
286 // ex : OT-PAL-8-face.dcm
287 // 0028|1201 [US] [Red Palette Color Lookup Table Data]
288 // 0028|1202 [US] [Green Palette Color Lookup Table Data]
289 // 0028|1203 [US] [Blue Palette Color Lookup Table Data]
291 int numberBitsAllocated = HeaderInternal->GetBitsAllocated();
292 // Number of "Bits Allocated" is fixed to 16 when:
293 // - it is not defined (i.e. it's value is 0)
294 // - it's 12, since we will expand the image to 16 bits (see
295 // PixelConvert::ConvertDecompress12BitsTo16Bits() )
296 if ( numberBitsAllocated == 0 || numberBitsAllocated == 12 )
298 numberBitsAllocated = 16;
301 int DecompressedSize = HeaderInternal->GetXSize()
302 * HeaderInternal->GetYSize()
303 * HeaderInternal->GetZSize()
304 * ( numberBitsAllocated / 8 )
305 * HeaderInternal->GetSamplesPerPixel();
307 return DecompressedSize;
311 * \brief - Allocates necessary memory,
312 * - Reads the pixels from disk (uncompress if necessary),
313 * - Transforms YBR pixels, if any, into RGB pixels
314 * - Transforms 3 planes R, G, B, if any, into a single RGB Plane
315 * - Transforms single Grey plane + 3 Palettes into a RGB Plane
316 * - Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
317 * @return Pointer to newly allocated pixel data.
318 * NULL if alloc fails
320 uint8_t* File::GetImageData()
322 if ( ! GetDecompressed() )
324 // If the decompression failed nothing can be done.
329 if ( HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage() )
331 pixelData = PixelConverter->GetRGB();
335 // When no LUT or LUT conversion fails, return the decompressed
336 pixelData = PixelConverter->GetDecompressed();
339 // PIXELCONVERT CLEANME
340 // Restore the header in a disk-consistent state
341 // (if user asks twice to get the pixels from disk)
342 if ( PixelRead != -1 ) // File was "read" before
344 RestoreInitialValues();
346 if ( PixelConverter->GetRGB() )
348 // now, it's an RGB image
349 // Lets's write it in the Header
350 std::string spp = "3"; // Samples Per Pixel
351 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
352 std::string rgb = "RGB "; // Photometric Interpretation
353 HeaderInternal->SetEntryByNumber(rgb,0x0028,0x0004);
354 std::string planConfig = "0"; // Planar Configuration
355 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
356 PixelRead = 0; // no PixelRaw
360 if ( HeaderInternal->HasLUT() )
362 // The LUT interpretation failed
363 std::string photometricInterpretation = Util::DicomString("MONOCHROME1");
364 HeaderInternal->SetEntryByNumber( photometricInterpretation,
366 PixelRead = 0; // no PixelRaw
370 if ( PixelConverter->IsDecompressedRGB() )
372 ///////////////////////////////////////////////////
373 // now, it's an RGB image
374 // Lets's write it in the Header
375 // Droping Palette Color out of the Header
376 // has been moved to the Write process.
377 // TODO : move 'values' modification to the write process
378 // : save also (in order to be able to restore)
379 // : 'high bit' -when not equal to 'bits stored' + 1
380 // : 'bits allocated', when it's equal to 12 ?!
381 std::string spp = "3"; // Samples Per Pixel
382 std::string photInt = "RGB "; // Photometric Interpretation
383 std::string planConfig = "0"; // Planar Configuration
384 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
385 HeaderInternal->SetEntryByNumber(photInt,0x0028,0x0004);
386 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
388 PixelRead = 1; // PixelRaw
392 // We say the value *is* loaded.
393 SetPixelData(pixelData);
394 // END PIXELCONVERT CLEANME
401 * Read the pixels from disk (uncompress if necessary),
402 * Transforms YBR pixels, if any, into RGB pixels
403 * Transforms 3 planes R, G, B, if any, into a single RGB Plane
404 * Transforms single Grey plane + 3 Palettes into a RGB Plane
405 * Copies at most MaxSize bytes of pixel data to caller allocated
407 * \warning This function allows people that want to build a volume
408 * from an image stack *not to* have, first to get the image pixels,
409 * and then move them to the volume area.
410 * It's absolutely useless for any VTK user since vtk chooses
411 * to invert the lines of an image, that is the last line comes first
412 * (for some axis related reasons?). Hence he will have
413 * to load the image line by line, starting from the end.
414 * VTK users have to call GetImageData
416 * @param destination Address (in caller's memory space) at which the
417 * pixel data should be copied
418 * @param maxSize Maximum number of bytes to be copied. When MaxSize
419 * is not sufficient to hold the pixel data the copy is not
420 * executed (i.e. no partial copy).
421 * @return On success, the number of bytes actually copied. Zero on
422 * failure e.g. MaxSize is lower than necessary.
424 size_t File::GetImageDataIntoVector (void* destination, size_t maxSize)
426 if ( ! GetDecompressed() )
428 // If the decompression failed nothing can be done.
432 if ( HeaderInternal->HasLUT() && PixelConverter->BuildRGBImage() )
434 if ( PixelConverter->GetRGBSize() > maxSize )
436 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
437 "than caller's expected MaxSize");
441 (void*)PixelConverter->GetRGB(),
442 PixelConverter->GetRGBSize() );
443 return PixelConverter->GetRGBSize();
446 // Either no LUT conversion necessary or LUT conversion failed
447 if ( PixelConverter->GetDecompressedSize() > maxSize )
449 dbg.Verbose(0, "File::GetImageDataIntoVector: pixel data bigger"
450 "than caller's expected MaxSize");
454 (void*)PixelConverter->GetDecompressed(),
455 PixelConverter->GetDecompressedSize() );
456 return PixelConverter->GetDecompressedSize();
460 * \brief Allocates necessary memory,
461 * Transforms YBR pixels (if any) into RGB pixels
462 * Transforms 3 planes R, G, B (if any) into a single RGB Plane
463 * Copies the pixel data (image[s]/volume[s]) to newly allocated zone.
464 * DOES NOT transform Grey plane + 3 Palettes into a RGB Plane
465 * @return Pointer to newly allocated pixel data.
466 * \ NULL if alloc fails
468 uint8_t* File::GetImageDataRaw ()
470 uint8_t* decompressed = GetDecompressed();
471 if ( ! decompressed )
476 // PIXELCONVERT CLEANME
477 // Restore the header in a disk-consistent state
478 // (if user asks twice to get the pixels from disk)
479 if ( PixelRead != -1 ) // File was "read" before
481 RestoreInitialValues();
483 if ( PixelConverter->IsDecompressedRGB() )
485 ///////////////////////////////////////////////////
486 // now, it's an RGB image
487 // Lets's write it in the Header
488 // Droping Palette Color out of the Header
489 // has been moved to the Write process.
490 // TODO : move 'values' modification to the write process
491 // : save also (in order to be able to restore)
492 // : 'high bit' -when not equal to 'bits stored' + 1
493 // : 'bits allocated', when it's equal to 12 ?!
494 std::string spp = "3"; // Samples Per Pixel
495 std::string photInt = "RGB "; // Photometric Interpretation
496 std::string planConfig = "0"; // Planar Configuration
497 HeaderInternal->SetEntryByNumber(spp,0x0028,0x0002);
498 HeaderInternal->SetEntryByNumber(photInt,0x0028,0x0004);
499 HeaderInternal->SetEntryByNumber(planConfig,0x0028,0x0006);
502 // We say the value *is* loaded.
503 SetPixelData(decompressed);
505 PixelRead = 1; // PixelRaw
506 // END PIXELCONVERT CLEANME
511 uint8_t* File::GetDecompressed()
513 uint8_t* decompressed = PixelConverter->GetDecompressed();
514 if ( ! decompressed )
516 // The decompressed image migth not be loaded yet:
517 std::ifstream* fp = HeaderInternal->OpenFile();
518 PixelConverter->ReadAndDecompressPixelData( fp );
519 if(fp) HeaderInternal->CloseFile();
520 decompressed = PixelConverter->GetDecompressed();
521 if ( ! decompressed )
523 dbg.Verbose(0, "File::GetDecompressed: read/decompress of "
524 "pixel data apparently went wrong.");
533 * \brief Points the internal Pixel_Data pointer to the callers inData
534 * image representation, BUT WITHOUT COPYING THE DATA.
535 * 'image' Pixels are presented as C-like 2D arrays : line per line.
536 * 'volume'Pixels are presented as C-like 3D arrays : plane per plane
537 * \warning Since the pixels are not copied, it is the caller's responsability
538 * not to deallocate it's data before gdcm uses them (e.g. with
539 * the Write() method.
540 * @param inData user supplied pixel area
541 * @param expectedSize total image size, in Bytes
545 bool File::SetImageData(uint8_t* inData, size_t expectedSize)
547 HeaderInternal->SetImageDataSize( expectedSize );
548 // FIXME : if already allocated, memory leak !
550 ImageDataSize = ImageDataSizeRaw = expectedSize;
552 // FIXME : 7fe0, 0010 IS NOT set ...
557 * \brief Writes on disk A SINGLE Dicom file
558 * NO test is performed on processor "Endiannity".
559 * It's up to the user to call his Reader properly
560 * @param fileName name of the file to be created
561 * (any already existing file is over written)
562 * @return false if write fails
565 bool File::WriteRawData(std::string const & fileName)
567 std::ofstream fp1(fileName.c_str(), std::ios::out | std::ios::binary );
570 dbg.Verbose(2, "Fail to open (write) file:", fileName.c_str());
573 fp1.write((char*)Pixel_Data, ImageDataSize);
580 * \brief Writes on disk A SINGLE Dicom file,
581 * using the Implicit Value Representation convention
582 * NO test is performed on processor "Endiannity".
583 * @param fileName name of the file to be created
584 * (any already existing file is overwritten)
585 * @return false if write fails
588 bool File::WriteDcmImplVR (std::string const & fileName)
590 SetWriteTypeToDcmImplVR();
591 return Write(fileName);
595 * \brief Writes on disk A SINGLE Dicom file,
596 * using the Explicit Value Representation convention
597 * NO test is performed on processor "Endiannity". * @param fileName name of the file to be created
598 * (any already existing file is overwritten)
599 * @return false if write fails
602 bool File::WriteDcmExplVR (std::string const & fileName)
604 SetWriteTypeToDcmExplVR();
605 return Write(fileName);
609 * \brief Writes on disk A SINGLE Dicom file,
610 * using the ACR-NEMA convention
611 * NO test is performed on processor "Endiannity".
612 * (a l'attention des logiciels cliniques
613 * qui ne prennent en entrée QUE des images ACR ...
614 * \warning if a DICOM_V3 header is supplied,
615 * groups < 0x0008 and shadow groups are ignored
616 * \warning NO TEST is performed on processor "Endiannity".
617 * @param fileName name of the file to be created
618 * (any already existing file is overwritten)
619 * @return false if write fails
622 bool File::WriteAcr (std::string const & fileName)
625 return Write(fileName);
628 bool File::Write(std::string const& fileName)
633 return WriteBase(fileName,ImplicitVR);
635 return WriteBase(fileName,ExplicitVR);
637 return WriteBase(fileName,ACR);
642 //-----------------------------------------------------------------------------
645 * \brief NOT a end user inteded function
646 * (used by WriteDcmExplVR, WriteDcmImplVR, WriteAcr, etc)
647 * @param fileName name of the file to be created
648 * (any already existing file is overwritten)
649 * @param type file type (ExplicitVR, ImplicitVR, ...)
650 * @return false if write fails
652 bool File::WriteBase (std::string const & fileName, FileType type)
654 if ( PixelRead == -1 && type != ExplicitVR)
659 std::ofstream* fp1 = new std::ofstream(fileName.c_str(),
660 std::ios::out | std::ios::binary);
663 dbg.Verbose(2, "Failed to open (write) File: " , fileName.c_str());
667 if ( type == ImplicitVR || type == ExplicitVR )
669 // writing Dicom File Preamble
670 char filePreamble[128];
671 memset(filePreamble, 0, 128);
672 fp1->write(filePreamble, 128);
673 fp1->write("DICM", 4);
676 // --------------------------------------------------------------
677 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
679 // if recognition code tells us we dealt with a LibIDO image
680 // we reproduce on disk the switch between lineNumber and columnNumber
681 // just before writting ...
683 /// \todo the best trick would be *change* the recognition code
684 /// but pb expected if user deals with, e.g. COMPLEX images
686 std::string rows, columns;
687 if ( HeaderInternal->GetFileType() == ACR_LIBIDO)
690 //rows = HeaderInternal->GetEntryByNumber(0x0028, 0x0010);
691 //columns = HeaderInternal->GetEntryByNumber(0x0028, 0x0011);
693 //HeaderInternal->SetEntryByNumber(columns, 0x0028, 0x0010);
694 //HeaderInternal->SetEntryByNumber(rows , 0x0028, 0x0011);
696 // ----------------- End of Special Patch ----------------
698 uint16_t grPixel = HeaderInternal->GetGrPixel();
699 uint16_t numPixel = HeaderInternal->GetNumPixel();;
701 DocEntry* PixelElement =
702 GetHeader()->GetDocEntryByNumber(grPixel, numPixel);
704 if ( PixelRead == 1 )
706 // we read pixel 'as is' (no tranformation LUT -> RGB)
707 PixelElement->SetLength( ImageDataSizeRaw );
709 else if ( PixelRead == 0 )
711 // we tranformed GrayLevel pixels + LUT into RGB Pixel
712 PixelElement->SetLength( ImageDataSize );
715 HeaderInternal->Write(fp1, type);
717 // --------------------------------------------------------------
718 // Special Patch to allow gdcm to re-write ACR-LibIDO formated images
720 // ...and we restore the Header to be Dicom Compliant again
721 // just after writting
723 if ( HeaderInternal->GetFileType() == ACR_LIBIDO )
726 //HeaderInternal->SetEntryByNumber(rows , 0x0028, 0x0010);
727 //HeaderInternal->SetEntryByNumber(columns, 0x0028, 0x0011);
729 // ----------------- End of Special Patch ----------------
737 * \brief Access to the underlying \ref PixelConverter RGBA LUT
739 uint8_t* File::GetLutRGBA()
741 return PixelConverter->GetLutRGBA();
744 //-----------------------------------------------------------------------------
747 * \brief Set the pixel datas in the good entry of the Header
749 void File::SetPixelData(uint8_t* data)
751 GetHeader()->SetEntryByNumber( GDCM_BINLOADED,
752 GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
754 // Will be 7fe0, 0010 in standard case
755 DocEntry* currentEntry = GetHeader()->GetDocEntryByNumber(GetHeader()->GetGrPixel(), GetHeader()->GetNumPixel());
758 if ( BinEntry* binEntry = dynamic_cast<BinEntry *>(currentEntry) )
759 // Flag is to false because datas are kept in the gdcmPixelConvert
760 binEntry->SetBinArea( data, false );
764 void File::SetToRAW()
768 void File::SetToRGB()
776 void File::SetToLibido()
778 ValEntry *oldRow = dynamic_cast<ValEntry *>(HeaderInternal->GetDocEntryByNumber(0x0028, 0x0010));
779 ValEntry *oldCol = dynamic_cast<ValEntry *>(HeaderInternal->GetDocEntryByNumber(0x0028, 0x0011));
782 if( oldRow && oldCol )
784 std::string rows, columns;
786 ValEntry *newRow=new ValEntry(oldRow->GetDictEntry());
787 ValEntry *newCol=new ValEntry(oldCol->GetDictEntry());
789 newRow->Copy(oldCol);
790 newCol->Copy(oldRow);
792 newRow->SetValue(oldCol->GetValue());
793 newCol->SetValue(oldRow->GetValue());
795 Archive->Push(newRow);
796 Archive->Push(newCol);
800 void File::RestoreFromLibido()
802 Archive->Restore(0x0028, 0x0010);
803 Archive->Restore(0x0028, 0x0011);
806 //-----------------------------------------------------------------------------
807 } // end namespace gdcm