source: golgotha/src/render/mip.cc

Last change on this file was 80, checked in by Sam Hocevar, 11 years ago
  • Adding the Golgotha source code. Not sure what's going to be interesting in there, but since it's all public domain, there's certainly stuff to pick up.
File size: 19.5 KB
Line 
1/********************************************************************** <BR>
2  This file is part of Crack dot Com's free source code release of
3  Golgotha. <a href="http://www.crack.com/golgotha_release"> <BR> for
4  information about compiling & licensing issues visit this URL</a>
5  <PRE> If that doesn't help, contact Jonathan Clark at
6  golgotha_source@usa.net (Subject should have "GOLG" in it)
7***********************************************************************/
8
9// *.mip file structure
10//32 bytes - null terminated string, texture name (no paths)
11//1st sw32 - number of mip levels
12//followed by that many offsets
13//seek into the file at each offset to find the corresponding mip structure
14//the mip structure is 2 sw32's (width and height) followed by width*height*2
15//bytes of pixel data (16-bit color, 565 RGB)
16
17#include <stdio.h>
18#include "arch.hh"
19#include "mip.hh"
20#include "palette/pal.hh"
21#include "mip_average.hh"
22#include "loaders/jpg_load.hh"
23#include "image/image.hh"
24#include "loaders/jpg_write.hh"
25#include "r1_res.hh"
26#include "file/ram_file.hh"
27#include "image/context.hh"
28
29//no compression, 24 bpp
30unsigned char tga_header[12] = {0,0,2,0,0,0,0,0,0,0,0,0};
31
32#define TGA_HEADER_SIZE (12)
33
34
35
36i4_image_class *adjust_to_power_of_two(i4_image_class *src_texture)
37{
38  sw32 new_width,new_height;
39 
40  sw32 old_width  = src_texture->width();
41  sw32 old_height = src_texture->height();
42
43  I4_ASSERT(src_texture->pal->source.pixel_depth==I4_32BIT, "");
44
45  //find closest power of 2
46  for (new_width  = 1; (new_width  < old_width)  && (new_width < r1_max_texture_size);  new_width  *= 2);
47  for (new_height = 1; (new_height < old_height) && (new_height < r1_max_texture_size); new_height *= 2); 
48
49  //obtain minimum aspect ratio of 8x1
50  if (new_width > new_height)
51  {   
52    for ( ; new_height < (new_width>>3); new_height *= 2);
53  }
54  else
55  if (new_height > new_width)
56  {
57    for ( ; new_width < (new_height>>3); new_width *= 2); 
58  }
59
60  //do we need to allocated an adjusted texture?
61  if (new_width==src_texture->width() && new_height==src_texture->height())
62    return 0;
63
64  //yes 
65  i4_pixel_format fmt;
66  fmt.default_format();
67
68  const i4_pal *pal = i4_pal_man.register_pal(&fmt);
69
70  i4_image_class *new_texture = i4_create_image(new_width,new_height,pal);
71
72  w32 *old_tex = (w32 *)src_texture->data;
73  w32 *new_tex = (w32 *)new_texture->data;
74  w32 *dst     = new_tex;
75 
76  double width_ratio  = (double)old_width  / (double)new_width;
77  double height_ratio = (double)old_height / (double)new_height;
78   
79  //now scale the old to fit the new
80  sw32 i,j;
81
82  for (j=0; j<new_height; j++)
83  for (i=0; i<new_width;  i++, dst++)
84  {   
85    *dst = old_tex[(sw32)((double)j * height_ratio)*old_width + (sw32)((double)i * width_ratio)];
86  }
87
88  return new_texture;
89}
90
91sw32 num_mips(sw32 width, sw32 height)
92{
93  sw32 num_mip_levels = 0;
94 
95  sw32 mipwidth;
96  sw32 mipheight;
97
98  while (1)
99  {
100    mipwidth  = width  / (1<<num_mip_levels);
101    mipheight = height / (1<<num_mip_levels);
102   
103    num_mip_levels++;
104
105    if (mipwidth==0 || mipheight==0 || (mipwidth < 8 && mipheight < 8))
106    {
107      //dont do this last one
108      num_mip_levels--;
109      break; 
110    }       
111
112    if (num_mip_levels==8)
113      break;
114  }
115 
116  return num_mip_levels;
117}
118
119void get_header_info(mipheader_t &header, i4_image_class *src_texture,
120                     char *texture_name, w32 chroma_color)
121{
122  sw32 i,j;
123  sw32 width  = src_texture->width();
124  sw32 height = src_texture->height();
125 
126  i4_bool has_chroma = i4_F;
127  i4_bool has_alpha  = i4_F; 
128
129  w32 rt=0,gt=0,bt=0, t=0;
130 
131  if (src_texture->get_pal()->source.pixel_depth==I4_32BIT)
132  {
133    w32 *base_mip = (w32 *)src_texture->data;
134
135    sw32 im_size = width * height;
136   
137    for (i=0; i<im_size; i++, base_mip++)
138    {
139      w32 c = *base_mip;
140
141      if (c & 0xFF000000)
142        has_alpha = i4_T;
143
144      if (c==chroma_color)
145        has_chroma = i4_T;
146      else
147      {
148        rt += ((c>>16) & 0xFF);
149        gt += ((c>>8)  & 0xFF);
150        bt += ((c>>0)  & 0xFF);
151        t++;
152      }
153    }
154  }
155  else
156  {
157    i4_draw_context_class context(0,0,width-1,height-1);
158
159    for (int y=0; y<height; y++)
160    {   
161      for (int x=0; x<width; x++)
162      {
163        i4_color c=src_texture->get_pixel(x,y, context);
164   
165        if (c & 0xFF000000)
166          has_alpha = i4_T;
167
168        if (c==chroma_color)
169          has_chroma = i4_T;
170        else       
171        {
172          rt += ((c>>16) & 0xFF);
173          gt += ((c>>8)  & 0xFF);
174          bt += ((c>>0)  & 0xFF);
175          t++;
176        }
177      }   
178    }
179  }
180
181  memset(&header,0,sizeof(mipheader_t));
182
183  header.average_color = ((rt/t)<<16) | ((gt/t)<<8) | (bt/t);
184
185  char *tname = r1_remove_paths(texture_name);
186  if (strlen(tname)>31) tname[31]=0;
187
188  strcpy(header.tname, tname); 
189
190  header.base_width  = src_texture->width();
191  header.base_height = src_texture->height();
192 
193  //wtf?
194  //header.num_mip_levels = num_mips(src_texture->width(), src_texture->height());
195  header.num_mip_levels = 1;
196 
197  header.offsets[0] = r1_mip_header_disk_size();
198  for (i=1; i<R1_MAX_MIP_LEVELS; i++)
199    header.offsets[i] = 0xFFFFFFFF;
200
201  header.flags = 0; 
202 
203  if (has_alpha)
204    header.flags = R1_MIP_IS_ALPHATEXTURE;
205  else
206  if (has_chroma)
207    header.flags = R1_MIP_IS_TRANSPARENT;
208  else
209  if ((src_texture->width() >= 16) && (src_texture->height() >= 16))
210    header.flags = R1_MIP_IS_JPG_COMPRESSED; 
211}
212
213i4_bool r1_write_tga_mips(i4_image_class *src_texture, char *dst_file,
214                          char *texture_name, w32 chroma_color)
215{
216  sw32 i,j;
217  mipheader_t header;
218
219  i4_file_class *f = i4_open(i4_const_str(dst_file), I4_WRITE);
220
221  if (!f) return i4_F;
222
223  i4_image_class *adjusted_texture = adjust_to_power_of_two(src_texture);
224  i4_image_class *use_texture = adjusted_texture ? adjusted_texture : src_texture;
225
226  get_header_info(header, use_texture, texture_name, chroma_color);
227  header.write(f);
228
229  if (!(header.flags & R1_MIP_IS_JPG_COMPRESSED))
230  {
231    w8 rgb[4];
232    i4_color c;
233
234    sw32 width  = use_texture->width();
235    sw32 height = use_texture->height();
236
237    f->write_32(width);
238    f->write_32(height);
239   
240    i4_draw_context_class context(0,0, width-1, height-1);
241
242    if (header.flags & R1_MIP_IS_ALPHATEXTURE)
243    {
244      for (j=0; j<height; j++)
245      {
246        for (i=0; i<width; i++)
247        {
248          //assumes that i4_color is a 32-bit ARGB
249          c = use_texture->get_pixel(i,j,context);
250         
251          rgb[0] = (c>>24) & 0xFF;
252          rgb[1] = (c>>16) & 0xFF;
253          rgb[2] = (c>>8)  & 0xFF;
254          rgb[3] = c & 0xFF;
255
256          f->write(rgb,4);
257        }
258      }
259    }
260    else
261    {
262      for (j=0; j<height; j++)
263      {
264        for (i=0; i<width; i++)
265        {
266          c = use_texture->get_pixel(i,j,context);
267         
268          rgb[0] = (c>>16) & 0xFF;
269          rgb[1] = (c>>8)  & 0xFF;
270          rgb[2] = c & 0xFF;
271
272          f->write(rgb,3);
273        }
274      }
275    }
276  }
277  else
278  {
279    i4_str *fmt=r1_gets("encoding_jpg").sprintf(200, dst_file);
280    i4_status_class *status=i4_create_status(*fmt);
281    delete fmt;
282    i4_write_jpeg(use_texture, f, 90, status);
283    if (status)
284      delete status;
285  }
286
287  if (use_texture!=src_texture)
288    delete use_texture;
289
290  delete f;
291  return i4_T;
292}
293
294//#undef LINUX
295
296/*
297DECOMPRESSION ROUTINES
298*/
299
300
301w8  *temp_mip_24=0;
302w16 *temp_mip_16=0;
303
304sw32 last_mip_24_size = 0;
305sw32 last_mip_16_size = 0;
306
307i4_bool decompress_to_square = i4_F;
308
309void r1_setup_decompression(i4_pixel_format *reg_fmt,
310                            i4_pixel_format *chroma_fmt,
311                            i4_pixel_format *alpha_fmt,
312                            w32 chroma_color, i4_bool square_textures)
313{
314  setup_pixel_formats(*reg_fmt,*chroma_fmt,*alpha_fmt,chroma_color);
315  last_mip_24_size = 0;
316  last_mip_16_size = 0;
317  temp_mip_24 = 0;
318  temp_mip_16 = 0;
319  decompress_to_square = square_textures;
320}
321
322void r1_end_decompression()
323{
324  if (temp_mip_24)
325  {   
326    i4_free(temp_mip_24);
327    temp_mip_24 = 0;
328  }
329 
330  if (temp_mip_16)
331  {
332    i4_free(temp_mip_16);
333    temp_mip_16 = 0;
334  }
335 
336  last_mip_24_size = 0;
337  last_mip_16_size = 0; 
338}
339
340void process_intern_raw_decomp(i4_file_class *dst_file,
341                               i4_file_class *dst_lowmip_file,
342                               i4_file_class *src_file,
343                               mipheader_t &new_mipheader,
344                               mipheader_t *&old_mipheader,
345                               sw32 &start_here)
346{
347  sw32 i,j,k,width,height;
348  sw32 num_to_copy = new_mipheader.num_mip_levels;
349  sw32 base_width  = old_mipheader->base_width;
350
351  w8 base_pixel_size = 3;
352 
353  if (old_mipheader->flags & R1_MIP_IS_TRANSPARENT)
354    base_pixel_size = 3;
355
356  if (old_mipheader->flags & R1_MIP_IS_ALPHATEXTURE)
357    base_pixel_size = 4;
358 
359  //this base mip better exist or else we must generate it 
360  if (old_mipheader->offsets[start_here] == 0xFFFFFFFF)
361  {
362    //have to generate the new base mip, starting with mip #0. sucks.
363    src_file->seek(old_mipheader->offsets[0]+8);   
364   
365    src_file->read(temp_mip_24,old_mipheader->base_width*old_mipheader->base_height*base_pixel_size);
366
367    for (k=1; k < start_here; k++)
368    {           
369      width  = old_mipheader->base_width  / (1<<k);
370      height = old_mipheader->base_height / (1<<k);
371
372      make_next_mip(temp_mip_24,0,width,height,base_width,old_mipheader->flags);
373    }
374    //when thats done, temp_mip_24 has the proper starting new mip level
375  }
376
377  for (k=0; k<num_to_copy; k++)
378  {   
379    width  = old_mipheader->base_width  / (1<<(k+start_here));
380    height = old_mipheader->base_height / (1<<(k+start_here));
381
382    dst_file->write_32(width);
383    dst_file->write_32(height);
384
385    if (old_mipheader->offsets[k+start_here] != 0xFFFFFFFF)
386    {       
387      //+8 to skip the width and height
388      src_file->seek(old_mipheader->offsets[k+start_here]+8);     
389
390      //just read copy and convert
391      src_file->read(temp_mip_24,width*height*base_pixel_size);
392     
393      mip_24_to_16(temp_mip_24,temp_mip_16,width,height,base_width,old_mipheader->flags);           
394    }
395    else
396    {
397      //assumes that temp_mip has the last mip level
398      //already in it. stores the next mip level in temp_mip
399     
400      make_next_mip(temp_mip_24,temp_mip_16,width,height,base_width,old_mipheader->flags);
401    }
402   
403    dst_file->write(temp_mip_16,width*height*2);
404   
405    if (dst_lowmip_file && k==num_to_copy-1)
406    {
407      dst_lowmip_file->write_32(width);
408      dst_lowmip_file->write_32(height);
409      dst_lowmip_file->write(temp_mip_16,width*height*2);
410    }
411  }     
412}
413
414void process_intern_jpg_decomp(i4_file_class *dst_file,
415                               i4_file_class *dst_lowmip_file,
416                               i4_file_class *src_file,
417                               mipheader_t &new_mipheader,
418                               mipheader_t *&old_mipheader,
419                               sw32 &start_here)
420{
421  sw32 i,j,k,width,height;
422  sw32 num_to_copy = new_mipheader.num_mip_levels;
423  sw32 base_width  = old_mipheader->base_width;
424  sw32 jpg_width,jpg_height;
425
426  //this base mip better exist or else we must generate it 
427  if (old_mipheader->offsets[start_here] == 0xFFFFFFFF)
428  {
429    //have to generate the new base mip, starting with mip #0. sucks.
430    src_file->seek(old_mipheader->offsets[0]);
431
432    i4_jpg_loader_instance.special_load24(src_file,temp_mip_24,&jpg_width,&jpg_height);
433
434    if (jpg_width != old_mipheader->base_width || jpg_height != old_mipheader->base_height)
435    {
436      i4_error("jpg size matchup error");
437    }
438
439    for (k=1; k < start_here; k++)
440    {           
441      width  = old_mipheader->base_width  / (1<<k);
442      height = old_mipheader->base_height / (1<<k);
443                 
444      make_next_mip(temp_mip_24,0,width,height,base_width,old_mipheader->flags);
445    }
446    //when thats done, temp_mip has the proper starting new mip level
447  } 
448
449  for (k=0; k<num_to_copy; k++)
450  {   
451    width  = old_mipheader->base_width  / (1<<(k+start_here));
452    height = old_mipheader->base_height / (1<<(k+start_here));
453
454    dst_file->write_32(width);
455    dst_file->write_32(height);
456   
457    if (old_mipheader->offsets[k+start_here] != 0xFFFFFFFF)
458    {     
459      src_file->seek(old_mipheader->offsets[k+start_here]);
460
461      i4_jpg_loader_instance.special_load24(src_file,temp_mip_24,&jpg_width,&jpg_height);
462
463      if (jpg_width != width || jpg_height != height)
464      {
465        i4_error("jpg size matchup error");
466      }
467     
468      //just read, convert, and copy
469      mip_24_to_16(temp_mip_24,temp_mip_16,width,height,base_width,old_mipheader->flags);     
470    }
471    else
472    {
473      //assumes that temp_mip has the last mip level
474      //already in it. stores the next mip level in temp_mip
475     
476      make_next_mip(temp_mip_24,temp_mip_16,width,height,base_width,old_mipheader->flags);
477    }
478   
479    dst_file->write(temp_mip_16,width*height*2);
480   
481    if (dst_lowmip_file && k==num_to_copy-1)
482    {
483      dst_lowmip_file->write_32(width);
484      dst_lowmip_file->write_32(height);
485      dst_lowmip_file->write(temp_mip_16,width*height*2);
486    }
487  }
488}
489
490i4_bool r1_decompress_to_local_mip(i4_file_class *orig_src_file,
491                                   i4_file_class *dst_lowmip_file,
492                                   char *network_file,
493                                   char *local_file,
494                                   mipheader_t *old_mipheader,
495                                   sw32 max_mip_dimention)
496{
497  sw32 start_here=-1;
498  sw32 num_to_copy=0;
499  sw32 width,height;
500  sw32 i,j,k;
501  w8   *ram_file_buffer = 0;
502  i4_ram_file_class *rf = 0;
503
504  i4_file_class *src_file = orig_src_file;
505
506  //make sure there is space allocated
507  sw32 need_24_size = old_mipheader->base_width*old_mipheader->base_height*4;
508  sw32 need_16_size = old_mipheader->base_width*old_mipheader->base_height*2;
509
510  if (need_24_size > last_mip_24_size)
511  {
512    if (temp_mip_24)
513      i4_free(temp_mip_24);
514
515    temp_mip_24 = (w8 *)i4_malloc(need_24_size,"temp 24/32 bit decompression buffer");
516    last_mip_24_size = need_24_size;
517  }
518
519  if (need_16_size > last_mip_16_size)
520  {
521    if (temp_mip_16)
522      i4_free(temp_mip_16);
523
524    temp_mip_16 = (w16 *)i4_malloc(need_16_size,"temp 16 bit decompression buffer");
525    last_mip_16_size = need_16_size;
526  }
527
528  if (decompress_to_square && (old_mipheader->base_width != old_mipheader->base_height))
529  {
530    //make it a square, create a new i4_ram_file_class and point src_file to it
531    mipheader_t m = *old_mipheader;
532    m.base_width  = (old_mipheader->base_width < old_mipheader->base_height) ? (old_mipheader->base_width):(old_mipheader->base_height);
533    m.base_height = m.base_width;
534    m.num_mip_levels = 1;
535   
536    //declarations above
537    ram_file_buffer = (w8 *)i4_malloc(need_24_size+r1_mip_header_disk_size(),"texture decompression ram file buffer");
538    rf = new i4_ram_file_class(ram_file_buffer,need_24_size+r1_mip_header_disk_size());
539   
540    m.offsets[0] = r1_mip_header_disk_size();
541    for (i=1; i<R1_MAX_MIP_LEVELS;i++)
542      m.offsets[i] = 0xFFFFFFFF;
543
544    m.write(rf);   
545    rf->write_32(m.base_width);
546    rf->write_32(m.base_height);
547
548    w8 base_pixel_size = 3;
549
550    if (old_mipheader->flags & R1_MIP_IS_TRANSPARENT)
551      base_pixel_size = 3;
552
553    if (old_mipheader->flags & R1_MIP_IS_ALPHATEXTURE)
554      base_pixel_size = 4;
555   
556    if (old_mipheader->flags & R1_MIP_IS_JPG_COMPRESSED)
557    {
558      src_file->seek(old_mipheader->offsets[0]);
559
560      sw32 jpg_width,jpg_height;
561     
562      i4_jpg_loader_instance.special_load24(src_file,temp_mip_24,&jpg_width,&jpg_height);
563      if (jpg_width != old_mipheader->base_width || jpg_height != old_mipheader->base_height)
564      {
565        i4_error("jpg size matchup error");
566      }
567     
568      m.flags &= (~R1_MIP_IS_JPG_COMPRESSED);
569    }
570    else
571    {
572      src_file->seek(old_mipheader->offsets[0]+8);
573      src_file->read(temp_mip_24, old_mipheader->base_width*old_mipheader->base_height*base_pixel_size);
574    }
575   
576    make_square(temp_mip_24, ram_file_buffer+r1_mip_header_disk_size()+8,
577                base_pixel_size,
578                old_mipheader->base_width, old_mipheader->base_height,
579                m.base_width);
580   
581    src_file = rf;
582    *old_mipheader = m;
583  }
584
585  sw32 num_mip = num_mips(old_mipheader->base_width,old_mipheader->base_height);
586  for (i=0; i<num_mip; i++)
587  {
588    width  = old_mipheader->base_width  / (1<<i);
589    height = old_mipheader->base_height / (1<<i);
590   
591    if (width <= max_mip_dimention && height <= max_mip_dimention)
592    {
593      if (start_here==-1) start_here = i;
594      num_to_copy++;     
595    }   
596  }
597
598  //we need to copy at least 1 mip level
599  //AND we need to shrink it before we store the local mip level
600  if (start_here==-1)
601  {   
602    start_here  = old_mipheader->num_mip_levels-1;
603    num_to_copy = 1;
604  }
605
606  mipheader_t new_mipheader;
607  memset(&new_mipheader,0,sizeof(mipheader_t));
608
609  strcpy(new_mipheader.tname,old_mipheader->tname); 
610  new_mipheader.num_mip_levels = num_to_copy;
611  new_mipheader.flags          = old_mipheader->flags;
612  new_mipheader.base_width     = old_mipheader->base_width  / (1<<(start_here));
613  new_mipheader.base_height    = old_mipheader->base_height / (1<<(start_here));
614  new_mipheader.average_color  = old_mipheader->average_color;
615
616  generate_mip_offsets(new_mipheader.base_width,
617                       new_mipheader.base_height,
618                       num_to_copy,
619                       new_mipheader.offsets,
620                       2);
621 
622  i4_file_class *dst_file; 
623   
624  dst_file = i4_open(i4_const_str(local_file), I4_WRITE);
625
626  if (!dst_file)
627    i4_warning("decompress_to_local_mip: dest texture null"); 
628 
629  if (!src_file)
630    i4_warning("decompress_to_local_mip: source texture null");
631
632  new_mipheader.write(dst_file);
633 
634  //which way will the file be decompressed?
635  if (old_mipheader->flags & R1_MIP_IS_JPG_COMPRESSED)
636    process_intern_jpg_decomp(dst_file,dst_lowmip_file,src_file,
637                              new_mipheader,old_mipheader,start_here);
638  else
639    process_intern_raw_decomp(dst_file,dst_lowmip_file,src_file,
640                              new_mipheader,old_mipheader,start_here);
641   
642  delete dst_file;
643   
644  //copy over the old info
645  *old_mipheader = new_mipheader;
646 
647  //free up the temporary ram file
648  if (rf)
649  {
650    delete rf;
651    rf = 0;
652  }
653
654  if (ram_file_buffer)
655  {
656    i4_free(ram_file_buffer);
657    ram_file_buffer=0;
658  }
659
660  return i4_T;
661}
662
663
664//regular shifting info
665w32 mip_r_and; w8 mip_r_shift; //r and g shifts are to the left
666w32 mip_g_and; w8 mip_g_shift;
667w32 mip_b_and; w8 mip_b_shift; //b shift is to the right
668
669//chroma-key shifting info
670w32 mip_c_r_and; w8 mip_c_r_shift; //r and g shifts are to the left
671w32 mip_c_g_and; w8 mip_c_g_shift;
672w32 mip_c_b_and; w8 mip_c_b_shift; //b shift is to the right
673w32 mip_c_a_and; w8 mip_c_a_shift; //a shift is to the left
674
675//alpha texture shifting info
676w32 mip_a_r_and; w8 mip_a_r_shift; //r and g shifts are to the left
677w32 mip_a_g_and; w8 mip_a_g_shift;
678w32 mip_a_b_and; w8 mip_a_b_shift; //b shift is to the right
679w32 mip_a_a_and; w8 mip_a_a_shift; //a shift is to the left
680
681w8 mip_chroma_red,mip_chroma_green,mip_chroma_blue;
682
683i4_profile_class pf_make_next_mip("make_next_mip");
684i4_profile_class pf_mip_24_to_16("mip_24_to_16");
Note: See TracBrowser for help on using the repository browser.