OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4SurfaceLoaders.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 
18 /* Extension to C4Surface that handles bitmaps in C4Group files */
19 
20 #include "C4Include.h"
21 #include "graphics/C4Surface.h"
22 
23 #include "c4group/C4GroupSet.h"
24 #include "c4group/C4Group.h"
25 #include "lib/C4Log.h"
26 #include "graphics/StdPNG.h"
27 #include "lib/StdColors.h"
28 
29 bool C4Surface::LoadAny(C4Group &hGroup, const char *szName, bool fOwnPal, bool fNoErrIfNotFound, int iFlags)
30 {
31  // Entry name
32  char szFilename[_MAX_FNAME+1];
33  SCopy(szName,szFilename,_MAX_FNAME);
34  char *szExt = GetExtension(szFilename);
35  if (!*szExt)
36  {
37  // no extension: Default to extension that is found as file in group
38  const char * const extensions[] = { "png", "bmp", "jpeg", "jpg", nullptr };
39  int i = 0; const char *szExt;
40  while ((szExt = extensions[i++]))
41  {
42  EnforceExtension(szFilename, szExt);
43  if (hGroup.FindEntry(szFilename)) break;
44  }
45  }
46  // Load surface
47  return Load(hGroup,szFilename,fOwnPal,fNoErrIfNotFound,iFlags);
48 }
49 
50 
51 bool C4Surface::LoadAny(C4GroupSet &hGroupset, const char *szName, bool fOwnPal, bool fNoErrIfNotFound, int iFlags)
52 {
53  // Entry name
54  char szFilename[_MAX_FNAME+1];
55  SCopy(szName,szFilename,_MAX_FNAME);
56  char *szExt = GetExtension(szFilename);
57  C4Group * pGroup;
58  if (!*szExt)
59  {
60  // no extension: Default to extension that is found as file in group
61  const char * const extensions[] = { "png", "bmp", "jpeg", "jpg", nullptr };
62  int i = 0; const char *szExt;
63  while ((szExt = extensions[i++]))
64  {
65  EnforceExtension(szFilename, szExt);
66  pGroup = hGroupset.FindEntry(szFilename);
67  if (pGroup) break;
68  }
69  }
70  else
71  pGroup = hGroupset.FindEntry(szFilename);
72  if (!pGroup) return false;
73  // Load surface
74  return Load(*pGroup,szFilename,fOwnPal,fNoErrIfNotFound,iFlags);
75 }
76 
77 bool C4Surface::Load(C4Group &hGroup, const char *szFilename, bool, bool fNoErrIfNotFound, int iFlags)
78 {
79  int ScaleToSet = 1;
80  // Image is scaled?
81  StdStrBuf strFilename;
82  char strBasename[_MAX_FNAME + 1]; SCopy(szFilename, strBasename, _MAX_FNAME); RemoveExtension(strBasename);
83  int32_t extpos; int scale;
84  if (((extpos = SCharLastPos('.', strBasename)) > -1) && (sscanf(strBasename+extpos+1, "%d", &scale) == 1))
85  {
86  // Filename with scale information was passed. Just store scale.
87  ScaleToSet = scale;
88  }
89  else
90  {
91  // a filename with out scale information was passed in
92  // Look for scaled images
93  const size_t base_length = std::strlen(strBasename);
94  char strExtension[128 + 1]; SCopy(GetExtension(szFilename), strExtension, 128);
95  if (strExtension[0])
96  {
97  char scaled_name[_MAX_PATH+1];
98  std::string wildcard(strBasename);
99  wildcard += ".*.";
100  wildcard += strExtension;
101  int max_scale = -1;
102  if (hGroup.FindEntry(wildcard.c_str(), scaled_name))
103  {
104  do
105  {
106  int scale = -1;
107  if (sscanf(scaled_name + base_length + 1, "%d", &scale) == 1)
108  if (scale > max_scale)
109  {
110  max_scale = scale;
111  ScaleToSet = max_scale;
112  strFilename.Copy(scaled_name);
113  szFilename = strFilename.getData();
114  }
115  }
116  while (hGroup.FindNextEntry(wildcard.c_str(), scaled_name));
117  }
118  }
119  }
120  // Access entry
121  if (!hGroup.AccessEntry(szFilename))
122  {
123  // file not found
124  if (!fNoErrIfNotFound) LogF("%s: %s%c%s", LoadResStr("IDS_PRC_FILENOTFOUND"), hGroup.GetFullName().getData(), (char) DirectorySeparator, szFilename);
125  return false;
126  }
127  bool fSuccess = Read(hGroup, GetExtension(szFilename), iFlags);
128  // loading error? log!
129  if (!fSuccess)
130  LogF("%s: %s%c%s", LoadResStr("IDS_ERR_NOFILE"), hGroup.GetFullName().getData(), (char) DirectorySeparator, szFilename);
131  // Work around the broken Default()-convention
132  Scale = ScaleToSet;
133  // done, success
134  return fSuccess;
135 }
136 
137 bool C4Surface::Read(CStdStream &hGroup, const char * extension, int iFlags)
138 {
139  // determine file type by file extension and load accordingly
140  if (SEqualNoCase(extension, "png"))
141  return ReadPNG(hGroup, iFlags);
142  else if (SEqualNoCase(extension, "jpeg")
143  || SEqualNoCase(extension, "jpg"))
144  return ReadJPEG(hGroup, iFlags);
145  else if (SEqualNoCase(extension, "bmp"))
146  return ReadBMP(hGroup, iFlags);
147  else
148  return false;
149 }
150 
151 bool C4Surface::ReadPNG(CStdStream &hGroup, int iFlags)
152 {
153  // create mem block
154  int iSize=hGroup.AccessedEntrySize();
155  BYTE *pData=new BYTE[iSize];
156  // load file into mem
157  hGroup.Read((void *) pData, iSize);
158  // load as png file
159  CPNGFile png;
160  bool fSuccess=png.Load(pData, iSize);
161  // free data
162  delete [] pData;
163  // abort if loading wasn't successful
164  if (!fSuccess) return false;
165  // create surface(s) - do not create an 8bit-buffer!
166  if (!Create(png.iWdt, png.iHgt, iFlags)) return false;
167  // lock for writing data
168  if (!Lock()) return false;
169  if (!texture)
170  {
171  Unlock();
172  return false;
173  }
174  // write pixels
175  // Get Texture and lock it
176  if (!texture->Lock()) return false;
177  int maxX = std::min(Wdt, iTexSize);
178  int maxY = std::min(Hgt, iTexSize);
179  for (int iY = 0; iY < maxY; ++iY)
180  {
181 #ifndef __BIG_ENDIAN__
182  if (png.iClrType == PNG_COLOR_TYPE_RGB_ALPHA)
183  {
184  // Optimize the easy case of a png in the same format as the display
185  // 32 bit
186  DWORD *pPix=(DWORD *) (((char *) texture->texLock.pBits) + iY * texture->texLock.Pitch);
187  memcpy (pPix, png.GetRow(iY), maxX * sizeof(*pPix));
188  int iX = maxX;
189  while (iX--) { if (((BYTE *)pPix)[3] == 0x00) *pPix = 0x00000000; ++pPix; }
190  }
191  else
192 #endif
193  {
194  // Loop through every pixel and convert
195  for (int iX = 0; iX < maxX; ++iX)
196  {
197  uint32_t dwCol = png.GetPix(iX, iY);
198  // if color is fully transparent, ensure it's black
199  if (dwCol>>24 == 0x00) dwCol=0x00000000;
200  // set pix in surface
201  DWORD *pPix=(DWORD *) (((char *) texture->texLock.pBits) + iY * texture->texLock.Pitch + iX * 4);
202  *pPix=dwCol;
203  }
204  }
205  }
206 
207  // unlock
208  texture->Unlock();
209  Unlock();
210  // return if successful
211  return fSuccess;
212 }
213 
214 bool C4Surface::SavePNG(C4Group &hGroup, const char *szFilename, bool fSaveAlpha, bool fSaveOverlayOnly)
215 {
216  // Using temporary file at C4Group temp path
217  char szTemp[_MAX_PATH+1];
218  SCopy(C4Group_GetTempPath(),szTemp);
219  SAppend(GetFilename(szFilename),szTemp);
220  MakeTempFilename(szTemp);
221  // Save to temporary file
222  if (!C4Surface::SavePNG(szTemp, fSaveAlpha, fSaveOverlayOnly, false)) return false;
223  // Move temp file to group
224  if (!hGroup.Move(szTemp,GetFilename(szFilename))) return false;
225  // Success
226  return true;
227 }
228 
229 /* JPEG loading */
230 
231 // Some distributions ship jpeglib.h with extern "C", others don't - gah.
232 extern "C"
233 {
234 /* avoid conflict with conflicting FAR typedefs */
235 #undef FAR
236 #include <jpeglib.h>
237 }
238 #include <setjmp.h>
239 
240 // Straight from the libjpeg example
242 {
243  struct jpeg_error_mgr pub; /* "public" fields */
244  jmp_buf setjmp_buffer; /* for return to caller */
245 };
246 
247 typedef struct my_error_mgr * my_error_ptr;
248 
249 static void my_error_exit (j_common_ptr cinfo)
250 {
251  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
252  my_error_ptr myerr = (my_error_ptr) cinfo->err;
253  /* Always display the message. */
254  /* We could postpone this until after returning, if we chose. */
255  (*cinfo->err->output_message) (cinfo);
256  /* Return control to the setjmp point */
257  longjmp(myerr->setjmp_buffer, 1);
258 }
259 static void my_output_message (j_common_ptr cinfo)
260 {
261  char buffer[JMSG_LENGTH_MAX];
262  (*cinfo->err->format_message) (cinfo, buffer);
263  LogF("libjpeg: %s", buffer);
264 }
265 static void jpeg_noop (j_decompress_ptr cinfo) {}
266 static const unsigned char end_of_input = JPEG_EOI;
267 static boolean fill_input_buffer (j_decompress_ptr cinfo)
268 {
269  // The doc says to give fake end-of-inputs if there is no more data
270  cinfo->src->next_input_byte = &end_of_input;
271  cinfo->src->bytes_in_buffer = 1;
272  return (boolean)true;
273 }
274 static void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
275 {
276  cinfo->src->next_input_byte += num_bytes;
277  cinfo->src->bytes_in_buffer -= num_bytes;
278  if (cinfo->src->bytes_in_buffer <= 0)
279  {
280  cinfo->src->next_input_byte = &end_of_input;
281  cinfo->src->bytes_in_buffer = 1;
282  }
283 }
284 
285 bool C4Surface::ReadJPEG(CStdStream &hGroup, int iFlags)
286 {
287  // create mem block
288  size_t size=hGroup.AccessedEntrySize();
289  unsigned char *pData=new unsigned char[size];
290  // load file into mem
291  hGroup.Read(pData, size);
292  // stuff for libjpeg
293  struct jpeg_decompress_struct cinfo;
294  struct my_error_mgr jerr;
295  JSAMPARRAY buffer; /* Output row buffer */
296  int row_stride; /* physical row width in output buffer */
297  /* We set up the normal JPEG error routines, then override error_exit. */
298  cinfo.err = jpeg_std_error(&jerr.pub);
299  jerr.pub.error_exit = my_error_exit;
300  jerr.pub.output_message = my_output_message;
301  // apparantly, this is needed so libjpeg does not exit() the engine away
302  if (setjmp(jerr.setjmp_buffer))
303  {
304  // some fatal error
305  jpeg_destroy_decompress(&cinfo);
306  delete [] pData;
307  return false;
308  }
309  jpeg_create_decompress(&cinfo);
310 
311  // no fancy function calling
312  jpeg_source_mgr blub;
313  cinfo.src = &blub;
314  blub.next_input_byte = pData;
315  blub.bytes_in_buffer = size;
316  blub.init_source = jpeg_noop;
317  blub.fill_input_buffer = fill_input_buffer;
318  blub.skip_input_data = skip_input_data;
319  blub.resync_to_restart = jpeg_resync_to_restart;
320  blub.term_source = jpeg_noop;
321 
322  // a missing image is an error
323  jpeg_read_header(&cinfo, (boolean)true);
324 
325  // Let libjpeg convert for us
326  cinfo.out_color_space = JCS_RGB;
327  jpeg_start_decompress(&cinfo);
328 
329  // create surface(s) - do not create an 8bit-buffer!
330  if (!Create(cinfo.output_width, cinfo.output_height, iFlags)) return false;
331  // JSAMPLEs per row in output buffer
332  row_stride = cinfo.output_width * cinfo.output_components;
333  // Make a one-row-high sample array that will go away at jpeg_destroy_decompress
334  buffer = (*cinfo.mem->alloc_sarray)
335  ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
336  // lock for writing data
337  if (!Lock()) return false;
338  while (cinfo.output_scanline < cinfo.output_height)
339  {
340  // read an 1-row-array of scanlines
341  jpeg_read_scanlines(&cinfo, buffer, 1);
342  // put the data in the image
343  for (unsigned int i = 0; i < cinfo.output_width; ++i)
344  {
345  const unsigned char * const start = buffer[0] + i * cinfo.output_components;
346  const uint32_t c = C4RGB(*start, *(start + 1), *(start + 2));
347  SetPixDw(i, cinfo.output_scanline - 1, c);
348  }
349  }
350  // unlock
351  Unlock();
352  // clean up
353  jpeg_finish_decompress(&cinfo);
354  jpeg_destroy_decompress(&cinfo);
355  // free data
356  delete [] pData;
357  // return if successful
358  return true;
359 }
char * GetFilename(char *szPath)
Definition: StdFile.cpp:55
const char * getData() const
Definition: StdBuf.h:450
bool FindEntry(const char *szWildCard, StdStrBuf *sFileName=nullptr, size_t *iSize=nullptr)
Definition: C4Group.cpp:1774
bool ReadBMP(CStdStream &hGroup, int iFlags)
Definition: C4Surface.cpp:333
DWORD GetPix(int iX, int iY)
Definition: StdPNG.cpp:173
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:129
int Wdt
Definition: C4Surface.h:67
void SAppend(const char *szSource, char *szTarget, int iMaxL)
Definition: Standard.cpp:234
bool AccessEntry(const char *szWildCard, size_t *iSize=nullptr, char *sFileName=nullptr, bool NeedsToBeAGroup=false)
Definition: C4Group.cpp:1695
virtual size_t AccessedEntrySize() const =0
bool Lock()
Definition: C4Surface.cpp:463
bool SavePNG(C4Group &hGroup, const char *szFilename, bool fSaveAlpha=true, bool fSaveOverlayOnly=false)
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:184
bool Unlock()
Definition: C4Surface.cpp:474
uint8_t BYTE
#define _MAX_PATH
int Hgt
Definition: C4Surface.h:67
struct jpeg_error_mgr pub
int Scale
Definition: C4Surface.h:68
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
bool LoadAny(C4Group &hGroup, const char *szFilename, bool fOwnPal, bool fNoErrIfNotFound, int iFlags)
int iClrType
Definition: StdPNG.h:45
unsigned long iHgt
Definition: StdPNG.h:44
int SCharLastPos(char cTarget, const char *szInStr)
Definition: Standard.cpp:224
bool SetPixDw(int iX, int iY, DWORD dwCol)
Definition: C4Surface.cpp:586
StdStrBuf GetFullName() const
Definition: C4Group.cpp:2078
bool Move(const char *szFile, const char *szAddAs)
Definition: C4Group.cpp:1325
C4Group * FindEntry(const char *szWildcard, int32_t *pPriority=nullptr, int32_t *pID=nullptr)
Definition: C4GroupSet.cpp:177
bool ReadPNG(CStdStream &hGroup, int iFlags)
int iTexSize
Definition: C4Surface.h:70
#define C4RGB(r, g, b)
Definition: StdColors.h:28
#define _MAX_FNAME
char * GetExtension(char *szFilename)
Definition: StdFile.cpp:131
void RemoveExtension(char *szFilename)
Definition: StdFile.cpp:316
const char * C4Group_GetTempPath()
Definition: C4Group.cpp:80
bool Load(C4Group &hGroup, const char *szFilename, bool fOwnPal, bool fNoErrIfNotFound, int iFlags)
std::unique_ptr< C4TexRef > texture
Definition: C4Surface.h:80
bool FindNextEntry(const char *szWildCard, StdStrBuf *sFileName=nullptr, size_t *iSize=nullptr, bool fStartAtFilename=false)
Definition: C4Group.cpp:1780
void EnforceExtension(char *szFilename, const char *szExtension)
Definition: StdFile.cpp:299
bool Read(CStdStream &hGroup, const char *extension, int iFlags)
unsigned long iWdt
Definition: StdPNG.h:44
struct my_error_mgr * my_error_ptr
void MakeTempFilename(char *szFilename)
Definition: StdFile.cpp:333
bool Create(int iWdt, int iHgt, int iFlags=0)
Definition: C4Surface.cpp:171
uint32_t DWORD
#define DirectorySeparator
bool Load(BYTE *pFile, int iSize)
Definition: StdPNG.cpp:153
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:253
void Copy()
Definition: StdBuf.h:475
virtual bool Read(void *pBuffer, size_t iSize)=0
bool ReadJPEG(CStdStream &hGroup, int iFlags)
uint32_t * GetRow(int iY)
Definition: StdPNG.h:56
int iSize
Definition: TstC4NetIO.cpp:35