]> git.sesse.net Git - casparcg/blob - server/producers/targa/TargaManager.cpp
Fixed a problem related to character encoding that prevented the PK project from...
[casparcg] / server / producers / targa / TargaManager.cpp
1 /*\r
2 * copyright (c) 2010 Sveriges Television AB <info@casparcg.com>\r
3 *\r
4 *  This file is part of CasparCG.\r
5 *\r
6 *    CasparCG is free software: you can redistribute it and/or modify\r
7 *    it under the terms of the GNU General Public License as published by\r
8 *    the Free Software Foundation, either version 3 of the License, or\r
9 *    (at your option) any later version.\r
10 *\r
11 *    CasparCG is distributed in the hope that it will be useful,\r
12 *    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14 *    GNU General Public License for more details.\r
15 \r
16 *    You should have received a copy of the GNU General Public License\r
17 *    along with CasparCG.  If not, see <http://www.gnu.org/licenses/>.\r
18 *\r
19 */\r
20  \r
21 #include "..\..\stdafx.h"\r
22 \r
23 #include "TargaManager.h"\r
24 #include "..\..\frame\FrameManager.h"\r
25 #include "..\..\frame\FrameMediaController.h"\r
26 #include "..\..\utils\FileInputStream.h"\r
27 #include "..\..\fileinfo.h"\r
28 #include "..\..\frame\buffers\StaticFrameBuffer.h"\r
29 \r
30 namespace caspar {\r
31 \r
32 using namespace caspar::utils;\r
33 \r
34 ///////////////////////////////\r
35 //  TargaProducer declaration\r
36 //\r
37 class TargaProducer : public MediaProducer, public FrameMediaController\r
38 {\r
39 public:\r
40         explicit TargaProducer(PixmapDataPtr pImage);\r
41         virtual ~TargaProducer();\r
42 \r
43         virtual IMediaController* QueryController(const tstring& id);\r
44 \r
45         virtual bool Initialize(FrameManagerPtr pFrameManager);\r
46         virtual FrameBuffer& GetFrameBuffer() {\r
47                 return frameBuffer_;\r
48         }\r
49 \r
50 private:\r
51         StaticFrameBuffer frameBuffer_;\r
52         caspar::utils::PixmapDataPtr pImage_;\r
53 };\r
54 \r
55 //////////////////////////////\r
56 //  TargaManager definition\r
57 //\r
58 TargaManager::TargaManager()\r
59 {\r
60         _extensions.push_back(TEXT("tga"));\r
61 }\r
62 \r
63 TargaManager::~TargaManager()\r
64 {}\r
65 \r
66 MediaProducerPtr TargaManager::CreateProducer(const tstring& filename) {\r
67         MediaProducerPtr result;\r
68         if(filename.length() > 0) {\r
69                 utils::InputStreamPtr pTgaFile(new utils::FileInputStream(filename));\r
70                 if(pTgaFile->Open()) {\r
71                         PixmapDataPtr pImage;\r
72                         if(TargaManager::Load(pTgaFile, pImage)) {\r
73                                 result = MediaProducerPtr(new TargaProducer(pImage));\r
74                         }\r
75                 }\r
76         }\r
77         return result;\r
78 }\r
79 \r
80 bool TargaManager::getFileInfo(FileInfo* pFileInfo)\r
81 {\r
82         if(pFileInfo != 0) {\r
83                 pFileInfo->length = 1;\r
84                 pFileInfo->type = TEXT("still");\r
85                 pFileInfo->encoding = TEXT("TGA");\r
86                 return true;\r
87         }\r
88         return false;\r
89 }\r
90 \r
91 PixmapDataPtr TargaManager::CropPadToFrameFormat(PixmapDataPtr pSource, const FrameFormatDescription& fmtDesc)\r
92 {\r
93         if(pSource->width == fmtDesc.width && pSource->height == fmtDesc.height)\r
94                 return pSource;\r
95 \r
96         unsigned short colsToCopy = pSource->width;\r
97         unsigned short rowsToCopy = pSource->height;\r
98 \r
99         int offsetX = 0;\r
100         if(pSource->width > fmtDesc.width)\r
101         {\r
102                 offsetX = (pSource->width-fmtDesc.width)/2;\r
103                 colsToCopy = fmtDesc.width;\r
104         }\r
105 \r
106         int offsetY = 0;\r
107         if(pSource->height > fmtDesc.height)\r
108         {\r
109                 offsetY = (pSource->height-fmtDesc.height)/2;\r
110                 rowsToCopy = fmtDesc.height;\r
111         }\r
112 \r
113         int bytesPerPixel = pSource->bpp;\r
114 \r
115         PixmapDataPtr pNewImage(new caspar::utils::PixmapData(fmtDesc.width, fmtDesc.height, bytesPerPixel));\r
116         unsigned char* pNewImageData = pNewImage->GetDataPtr();\r
117 \r
118         //initialize new buffer with zeroes\r
119         memset(pNewImageData, 0, fmtDesc.width*fmtDesc.height*bytesPerPixel);\r
120 \r
121         for(int i=0;i<rowsToCopy;++i)\r
122                 memcpy(&(pNewImageData[bytesPerPixel*fmtDesc.width*i]), &(pSource->GetDataPtr()[bytesPerPixel*(pSource->width*(i+offsetY)+offsetX)]), bytesPerPixel*colsToCopy);\r
123 \r
124         return pNewImage;\r
125 }\r
126 \r
127 \r
128 bool TargaManager::Load(utils::InputStreamPtr spTGA, PixmapDataPtr& pResult)\r
129 {\r
130         utils::InputStream* pTGA = spTGA.get();\r
131         PixmapDataPtr pImage(new utils::PixmapData());\r
132 \r
133         bool returnValue = true;\r
134         //correct headers to compare to\r
135         char headerUncompressed[12]     = {0,0, 2,0,0,0,0,0,0,0,0,0};\r
136         char headerCompressed[12]       = {0,0,10,0,0,0,0,0,0,0,0,0};\r
137 \r
138         unsigned char header[12];\r
139         unsigned char tgaInfo[6];\r
140         unsigned int nImageSize = 0, nBytesPerPixel = 0;\r
141 \r
142         unsigned char *pColorBuffer = NULL;\r
143 \r
144         unsigned int i;\r
145         unsigned int nBytesPerRow;\r
146         int nCurrentRow = 0;\r
147 \r
148         char rowDirection = -1;\r
149 \r
150         pTGA->Read(header, 12); //read header to be able to process compressed files in one way and uncompressed files in another\r
151         pTGA->Read(tgaInfo, 6); //read image-info such as height and width\r
152 \r
153         int width = tgaInfo[1] * 256 + tgaInfo[0];      //extract width\r
154         int height = tgaInfo[3] * 256 + tgaInfo[2];     //extract height\r
155         int bits = tgaInfo[4];                                          //extract bits/pixel\r
156 \r
157         pImage->Set(width, height, 4);\r
158         unsigned char* pImageData = pImage->GetDataPtr();\r
159 \r
160         rowDirection = -1;\r
161         if(tgaInfo[5] & 0x20)\r
162                 rowDirection = 1;\r
163 \r
164         //calculate usefull numbers\r
165         nBytesPerPixel = bits / 8;\r
166         nBytesPerRow = pImage->width * nBytesPerPixel;\r
167 \r
168         //internal data always have 4 bytes / pixel\r
169         nImageSize = pImage->width * pImage->height * 4;\r
170 \r
171         if(nBytesPerPixel != 3 && nBytesPerPixel != 4)\r
172         {\r
173                 returnValue = false;\r
174                 goto tgaLoaderExit;\r
175         }\r
176 \r
177         memset(pImageData, 0, nImageSize);\r
178 \r
179         //Workaround for image identification field problem\r
180         {\r
181                 unsigned char iifSize = header[0];\r
182                 unsigned char temp[256];\r
183                 if(iifSize > 0)\r
184                         pTGA->Read(temp, iifSize);\r
185         }\r
186         header[0] = 0;\r
187         header[7] = 0;\r
188         //END workaround\r
189 \r
190         //We've got an uncompressed file on our hands, take care of it\r
191         if(memcmp(headerUncompressed, header, 12) == 0)\r
192         {\r
193                 unsigned char* pRowBuffer = new unsigned char[nBytesPerRow];\r
194 \r
195                 int rowIndex=0;\r
196                 if(rowDirection == -1)\r
197                         nCurrentRow = height-1;\r
198                 else\r
199                         nCurrentRow = 0;\r
200 \r
201                 for(; rowIndex < height; nCurrentRow+=rowDirection, ++rowIndex)\r
202                 {\r
203                         if(pTGA->Read(pRowBuffer, nBytesPerRow) < nBytesPerRow)\r
204                         {\r
205                                 delete[] pRowBuffer;\r
206                                 returnValue = false;\r
207                                 goto tgaLoaderExit;\r
208                         }\r
209  \r
210                         //Swap color-channels\r
211                         for(i=0;i<pImage->width;i++)\r
212                         {\r
213                                 pImageData[4*(nCurrentRow*width+i)+0] = pRowBuffer[i*nBytesPerPixel+0];\r
214                                 pImageData[4*(nCurrentRow*width+i)+1] = pRowBuffer[i*nBytesPerPixel+1];\r
215                                 pImageData[4*(nCurrentRow*width+i)+2] = pRowBuffer[i*nBytesPerPixel+2];\r
216                                 if(nBytesPerPixel == 4)\r
217                                         pImageData[4*(nCurrentRow*width+i)+3] = pRowBuffer[i*nBytesPerPixel+3];\r
218                                 else\r
219                                         pImageData[4*(nCurrentRow*width+i)+3] = 255;\r
220                         }\r
221                 }\r
222                 delete[] pRowBuffer;\r
223         }\r
224 \r
225         //wasn't uncompressed, is it compressed? in that case, take care of it!\r
226         else if(memcmp(headerCompressed, header, 12) == 0)\r
227         {\r
228                 int rowIndex=0;\r
229                 if(rowDirection == -1)\r
230                         nCurrentRow = pImage->height-1;\r
231                 else\r
232                         nCurrentRow = 0;\r
233 \r
234                 nBytesPerRow = width * 4;\r
235 \r
236                 int nPixelCount = height * width;\r
237                 int nCurrentPixel = 0;\r
238                 int nCurrentByte = 0;\r
239 \r
240                 pColorBuffer = new unsigned char[nBytesPerPixel];\r
241 \r
242                 do\r
243                 {\r
244                         unsigned char chunkHeader = 0;\r
245 \r
246                         //read chunkHeader - exit on error\r
247                         if(pTGA->Read(&chunkHeader, 1) < 0)\r
248                         {\r
249                                 returnValue = false;\r
250                                 goto tgaLoaderExit;\r
251                         }\r
252                         \r
253                         if(chunkHeader < 128) //it's a RAW-header\r
254                         {\r
255                                 chunkHeader++;\r
256                                 \r
257                                 for(unsigned short counter=0; counter<chunkHeader; counter++)\r
258                                 {\r
259                                         //read pixeldata - exit on error\r
260                                         if(pTGA->Read(pColorBuffer, nBytesPerPixel) < nBytesPerPixel)\r
261                                         {\r
262                                                 returnValue = false;\r
263                                                 goto tgaLoaderExit;\r
264                                         }\r
265 \r
266                                         unsigned int thisByte = nCurrentRow*nBytesPerRow+nCurrentByte;\r
267 \r
268 \r
269                                         pImageData[thisByte+0] = pColorBuffer[0];\r
270                                         pImageData[thisByte+1] = pColorBuffer[1];\r
271                                         pImageData[thisByte+2] = pColorBuffer[2];\r
272                                         if(nBytesPerPixel == 4)\r
273                                                 pImageData[thisByte+3] = pColorBuffer[3];\r
274                                         else\r
275                                                 pImageData[thisByte+3] = 255;\r
276 \r
277                                         nCurrentByte += 4;\r
278                                         nCurrentPixel++;\r
279                                         if(nCurrentByte >= nBytesPerRow)\r
280                                         {\r
281                                                 nCurrentRow +=  rowDirection;\r
282                                                 ++rowIndex;\r
283                                                 nCurrentByte = 0;\r
284                                         }\r
285                                 }\r
286                         }\r
287                         else //it's a RLE header\r
288                         {\r
289                                 chunkHeader -= 127;\r
290 \r
291                                 //read pixeldata - exit on error\r
292                                 if(pTGA->Read(pColorBuffer, nBytesPerPixel) < nBytesPerPixel)\r
293                                 {\r
294                                         returnValue = false;\r
295                                         goto tgaLoaderExit;\r
296                                 }\r
297 \r
298                                 //repeat this pixel\r
299                                 for(unsigned short counter=0; counter<chunkHeader; counter++)\r
300                                 {\r
301                                         unsigned int thisByte = nCurrentRow*nBytesPerRow+nCurrentByte;\r
302 \r
303 \r
304                                         pImageData[thisByte+0] = pColorBuffer[0];\r
305                                         pImageData[thisByte+1] = pColorBuffer[1];\r
306                                         pImageData[thisByte+2] = pColorBuffer[2];\r
307                                         if(nBytesPerPixel == 4)\r
308                                                 pImageData[thisByte+3] = pColorBuffer[3];\r
309                                         else\r
310                                                 pImageData[thisByte+3] = 255;\r
311 \r
312                                         nCurrentByte += 4;\r
313                                         nCurrentPixel++;\r
314                                         if(nCurrentByte >= nBytesPerRow)\r
315                                         {\r
316                                                 nCurrentRow += rowDirection;\r
317                                                 ++rowIndex;\r
318                                                 nCurrentByte = 0;\r
319                                         }\r
320                                 }\r
321                         }\r
322                 }while(nCurrentPixel<nPixelCount && rowIndex < pImage->height);\r
323         }\r
324         else\r
325                 returnValue = false;\r
326 \r
327 tgaLoaderExit:\r
328         if(pColorBuffer != NULL)\r
329                 delete[] pColorBuffer;\r
330 \r
331         if(returnValue)\r
332                 pResult = pImage;\r
333 \r
334         return returnValue;\r
335 }\r
336 \r
337 \r
338 ///////////////////////////////\r
339 //  TargaProducer definition\r
340 //\r
341 TargaProducer::TargaProducer(PixmapDataPtr pImage) : pImage_(pImage) {\r
342 }\r
343 \r
344 TargaProducer::~TargaProducer() {\r
345 }\r
346 \r
347 IMediaController* TargaProducer::QueryController(const tstring& id) {\r
348         if(id == TEXT("FrameController"))\r
349                 return this;\r
350         \r
351         return 0;\r
352 }\r
353 \r
354 bool TargaProducer::Initialize(FrameManagerPtr pFrameManager) {\r
355         if(pFrameManager != 0) {\r
356                 FramePtr pFrame = pFrameManager->CreateFrame();\r
357                 if(pFrame != 0 && pFrame->GetDataPtr() != 0) {\r
358                         PixmapDataPtr pResult = TargaManager::CropPadToFrameFormat(pImage_, pFrameManager->GetFrameFormatDescription());\r
359 \r
360                         unsigned char* pFrameData = pFrame->GetDataPtr();\r
361                         unsigned char* pImageData = pResult->GetDataPtr();\r
362 \r
363                         memcpy(pFrameData, pImageData, pFrame->GetDataSize());\r
364 \r
365                         frameBuffer_.push_back(pFrame);\r
366                         return true;\r
367                 }\r
368         }\r
369         return false;\r
370 }\r
371 \r
372 }       //namespace caspar