Index: Makefile =================================================================== --- Makefile (revision 8160) +++ Makefile (working copy) @@ -265,6 +265,7 @@ SOURCES += gui/components/gui_obj_view_t.cc SOURCES += gui/components/gui_fixedwidth_textarea.cc SOURCES += gui/components/gui_flowtext.cc +SOURCES += gui/components/gui_flowtext_image.cc SOURCES += gui/components/gui_image.cc SOURCES += gui/components/gui_image_list.cc SOURCES += gui/components/gui_komponente.cc @@ -445,6 +446,8 @@ SOURCES += unicode.cc SOURCES += utils/cbuffer_t.cc SOURCES += utils/csv.cc +SOURCES += utils/dr_rdpng.cc +SOURCES += utils/imgloader.cc SOURCES += utils/log.cc SOURCES += utils/searchfolder.cc SOURCES += utils/sha1.cc @@ -629,6 +632,7 @@ include OSX/osx.mk endif +LIBS += -lpng .PHONY: makeobj Index: Simutrans.vcxproj =================================================================== --- Simutrans.vcxproj (revision 8160) +++ Simutrans.vcxproj (working copy) @@ -70,7 +70,7 @@ 4996;4373;%(DisableSpecificWarnings) - kernel32.lib;user32.lib;gdi32.lib;shell32.lib;winmm.lib;zlibstat.lib;advapi32.lib;ws2_32.lib;imm32.lib;libbz2.lib;pthreadVC2.lib;libfreetype2.lib + kernel32.lib;user32.lib;gdi32.lib;shell32.lib;winmm.lib;zlibstat.lib;advapi32.lib;ws2_32.lib;imm32.lib;libbz2.lib;pthreadVC2.lib;libpng.lib;libfreetype2.lib %(AdditionalLibraryDirectories) false libcmt.lib;%(IgnoreSpecificDefaultLibraries) @@ -106,7 +106,7 @@ 4200;4800;4311;4996;4373;%(DisableSpecificWarnings) - kernel32.lib;user32.lib;gdi32.lib;shell32.lib;winmm.lib;zlibstat.lib;advapi32.lib;ws2_32.lib;imm32.lib;libbz2.lib;pthreadVC2.lib + kernel32.lib;user32.lib;gdi32.lib;shell32.lib;winmm.lib;zlibstat.lib;advapi32.lib;ws2_32.lib;imm32.lib;libbz2.lib;pthreadVC2.lib;libpng.lib; %(AdditionalLibraryDirectories) libcmt.lib;%(IgnoreSpecificDefaultLibraries) false @@ -137,6 +137,7 @@ + @@ -283,6 +284,8 @@ + + @@ -450,6 +453,7 @@ + @@ -609,6 +613,8 @@ + + Index: dataobj/height_map_loader.cc =================================================================== --- dataobj/height_map_loader.cc (revision 8160) +++ dataobj/height_map_loader.cc (working copy) @@ -7,6 +7,7 @@ #include "environment.h" #include "height_map_loader.h" #include "../simio.h" +#include "../utils/imgloader.h" height_map_loader_t::height_map_loader_t(bool new_format): @@ -18,225 +19,31 @@ // read height data from bmp or ppm files bool height_map_loader_t::get_height_data_from_file( const char *filename, sint8 groundwater, sint8 *&hfield, sint16 &ww, sint16 &hh, bool update_only_values ) { - if (FILE* const file = fopen(filename, "rb")) { - char id[3]; - // parsing the header of this mixed file format is nottrivial ... - id[0] = fgetc(file); - id[1] = fgetc(file); - id[2] = 0; - if(strcmp(id, "P6")) { - if(strcmp(id, "BM")) { - fclose(file); - dbg->error("height_map_loader_t::load_heightfield()","Heightfield has wrong image type %s instead P6/BM", id); - return false; - } - // bitmap format - fseek( file, 10, SEEK_SET ); - uint32 data_offset; - sint32 w, h, format, table; - sint16 bit_depth; -#ifdef SIM_BIG_ENDIAN - uint32 l; - uint16 s; - fread( &l, 4, 1, file ); - data_offset = endian(l); - fseek( file, 18, SEEK_SET ); - fread( &l, 4, 1, file ); - w = endian(l); - fread( &l, 4, 1, file ); - h = endian(l); - fseek( file, 28, SEEK_SET ); - fread( &s, 2, 1, file ); - bit_depth = endian(s); - fread( &l, 4, 1, file ); - format = endian(l); - fseek( file, 46, SEEK_SET ); - fread( &l, 4, 1, file ); - table = endian(l); -#else - fread( &data_offset, 4, 1, file ); - fseek( file, 18, SEEK_SET ); - fread( &w, 4, 1, file ); - fread( &h, 4, 1, file ); - fseek( file, 28, SEEK_SET ); - fread( &bit_depth, 2, 1, file ); - fread( &format, 4, 1, file ); - fseek( file, 46, SEEK_SET ); - fread( &table, 4, 1, file ); -#endif - if((bit_depth!=8 && bit_depth!=24) || format>1) { - if(!update_only_values) { - dbg->fatal("height_map_loader_t::get_height_data_from_file()","Can only use 8Bit (RLE or normal) or 24 bit bitmaps!"); - } - fclose( file ); - return false; - } + sint32 w, h;//, format, table; + img_loader *l=new img_loader(); + if (l->load_image(filename)!=IMGR_OK) { + delete l; + return false; + } - // skip parsing body - if(update_only_values) { - ww = w; - hh = abs(h); - return true; - } + w=l->get_width(); + h=l->get_height(); + ww=w; + hh=h; + if (update_only_values) { + return true; + } - // now read the data and convert them on the fly - hfield = new sint8[w*h]; - memset( hfield, groundwater, w*h ); - if(bit_depth==8) { - // convert color tables to height levels - if(table==0) { - table = 256; - } - sint8 h_table[256]; - fseek( file, 54, SEEK_SET ); - for( int i=0; i0) - bool mirror = (h<0); - h = abs(h); - for( sint32 y=0; y 0) { - for( sint32 k = 0; k < Count; k++, x++ ) { - hfield[x+(y*w)] = h_table[ColorIndex]; - } - } else if (Count == 0) { - sint32 Flag = ColorIndex; - if (Flag == 0) { - // goto next line - x = 0; - y--; - } - else if (Flag == 1) { - // end of bitmap - break; - } - else if (Flag == 2) { - // skip with cursor - x += (uint8)fgetc(file); - y -= (uint8)fgetc(file); - } - else { - // uncompressed run - Count = Flag; - for( sint32 k = 0; k < Count; k++, x++ ) { - hfield[x+y*w] = h_table[(uint8)fgetc(file)]; - } - if (ftell(file) & 1) { // alway even offset in file - fseek(file, 1, SEEK_CUR); - } - } - } - } - } - } - else { - // uncompressed 24 bits - bool mirror = (h<0); - h = abs(h); - for( sint32 y=0; yget_bits(); + hfield = new sint8[w*h]; + memset( hfield, groundwater, w*h ); + for (int y=0;y='0' && *c<='9') { - c++; - } - } - // now the data - sint32 w = param[0]; - sint32 h = param[1]; - if(param[2]!=255) { - fclose(file); - if(!update_only_values) { - dbg->fatal("height_map_loader_t::load_heightfield()","Heightfield has wrong color depth %d", param[2] ); - } - return false; - } - - // report only values - if(update_only_values) { - fclose(file); - ww = w; - hh = h; - return true; - } - - // ok, now read them in - hfield = new sint8[w*h]; - memset( hfield, groundwater, w*h ); - - for(sint16 y=0; y( strstart(start, "src")); + if (start) { + while (*start=='"'||*start==' '||*start=='='||*start=='\'') start++; + char *end=start; + while (*end && *end!= '"' && *end != '\'') end++; + *end=0; + param=start; + if (param[0]!=0) { + // Load in Image and Store Image ID for displaying + gui_flowtext_image_t img=gui_flowtext_image_t(param); + image_id ret=img.get_image_id(); + images.append(ret); + } + } else { + param=""; + } + } else { + att=ATT_IMG_END; + } + } else if (word[0] == 'i') { att = endtag ? ATT_IT_END : ATT_IT_START; } @@ -239,6 +268,7 @@ const int width = size.w; slist_tpl::iterator link = links.begin(); + slist_tpl::iterator image=images.begin(); int xpos = 0; int ypos = 0; @@ -247,6 +277,8 @@ bool double_it = false; bool link_it = false; // true, if currently underlining for a link int extra_pixel = 0; // extra pixel before next line + int extra_pixels = 0; // extra pixels for image display + int extra_x_pixels=0; // extra pixels in x dimension for image display int last_link_x = 0; // at this position ye need to continue underline drawing int max_width = width; int text_width = width; @@ -276,8 +308,22 @@ } extra_pixel = 1; } - xpos = 0; - last_link_x = 0; + + if ((extra_x_pixels+nxpos)>max_width) { + ypos+=extra_pixels; + extra_pixels=0; + extra_x_pixels=0; + } + if (extra_pixels>0) { + xpos=extra_x_pixels; + nxpos+=extra_x_pixels; + extra_pixels-= LINESPACE+extra_pixel; + } else { + xpos = 0; + extra_pixels = 0; + } + last_link_x = extra_x_pixels; + ypos += LINESPACE+extra_pixel; extra_pixel = 0; } @@ -312,7 +358,9 @@ } extra_pixel = 1; } - ypos += LINESPACE+extra_pixel; + ypos += LINESPACE+extra_pixel+extra_pixels; + extra_x_pixels=0; + extra_pixels=0; last_link_x = 0; extra_pixel = 0; break; @@ -341,18 +389,56 @@ double_it = true; break; - case ATT_H1_END: - double_it = false; - if(doit) { - display_fillbox_wh_clip_rgb(offset.x + 1, offset.y + ypos + LINESPACE, xpos, 1, color, false); - display_fillbox_wh_clip_rgb(offset.x, offset.y + ypos + LINESPACE-1, xpos, 1, double_color, false); - } + case ATT_H1_END: + double_it = false; + if(doit) { + display_fillbox_wh_clip_rgb(offset.x + 1 + extra_x_pixels, offset.y + ypos + LINESPACE, xpos - extra_x_pixels, 1, color, false); + display_fillbox_wh_clip_rgb(offset.x + extra_x_pixels, offset.y + ypos + LINESPACE-1, xpos - extra_x_pixels, 1, double_color, false); + } xpos = 0; extra_pixel = 0; ypos += LINESPACE+2; + if (extra_pixels>0) { + xpos=extra_x_pixels; + extra_pixels-= LINESPACE+extra_pixel; + } + else { + xpos = 0; + extra_pixels = 0; + } color = SYSCOL_TEXT; + break; + + case ATT_IMG_START: + if (image!=images.end()) { + //display image + KOORD_VAL xoff,yoff,xw,yw; + display_get_base_image_offset(image->id,&xoff,&yoff,&xw,&yw); + if ((xpos+xw>max_width)&&(xpos!=0)) { + // New Line if image is too large to fit on line (unless already at new line) + xpos=0; + ypos+=LINESPACE+extra_pixel+extra_pixels; + extra_pixel=0; + extra_pixels=0; + extra_x_pixels=0; + } + if (doit) { + display_base_img(image->id,offset.x+xpos,offset.y+ypos-yoff,0,0,0); + } + xpos+=xw; + //ypos+=yw-LINESPACE; + if (extra_pixels nodes; slist_tpl links; + slist_tpl images; char title[128]; }; Index: gui/components/gui_flowtext_image.cc =================================================================== --- gui/components/gui_flowtext_image.cc (nonexistent) +++ gui/components/gui_flowtext_image.cc (working copy) @@ -0,0 +1,139 @@ +#include "gui_flowtext_image.h" + +#include "../../descriptor/image.h" +#include "../../dataobj/environment.h" +#include "../../simworld.h" +#include "../../dataobj/scenario.h" +#include "../../utils/imgloader.h" +//#include "../../utils/dr_rdpng.h" +#include "../../simdebug.h" + +gui_flowtext_image_t::gui_flowtext_image_t() { + _image_name=""; + _image_id=0; +} + +image_id gui_flowtext_image_t::get_image_id() { + return _image_id; +} + +std::string gui_flowtext_image_t::get_image_name() { + return _image_name; +} + +hashtable_tpl *gui_flowtext_image_t::_images=NULL; + +gui_flowtext_image_t::gui_flowtext_image_t(std::string filePath) { + // Initialize Image list if it has not already been done. + if (_images==NULL) { + _images=new hashtable_tpl(); + } + _image_name=filePath; + + // Check if image is already loaded + _image_id=_images->get(filePath.c_str()); + if (_image_id>0) { + return; + } + + // First Search for image in Scenario Path, then PAK path, the base path + scenario_t *scenario=world()->get_scenario(); + std::string spath=scenario->get_scenario_path().c_str(); + + std::string path= env_t::program_dir+spath+filePath; + FILE *fp=fopen(path.c_str(),"rb"); + + if (fp==NULL) { + path= env_t::program_dir+env_t::objfilename + filePath; + fp=fopen(path.c_str(),"rb"); + if (fp==NULL) { + path= env_t::program_dir+filePath; + fp=fopen(path.c_str(),"rb"); + if (fp==NULL) { + // file not found + _image_id=0; + return; + } + } + } + + fclose(fp); + dbg->message("gui_flow_text_image_t()","Loading File %s",path.c_str()); + img_loader c=img_loader(); + c.load_image(path.c_str()); + dbg->message("gui_flow_text_image_t()","Loaded File %s",path.c_str()); + /*if ((filePath.substr(filePath.length()-3,3)=="png")||(filePath.substr(filePath.length()-3,3)=="PNG")) { + // Load PNG Instead + unsigned char **block=new unsigned char * [1]; + block[0]=(unsigned char *)malloc(1); + uint32 width=0; + uint32 height=0; + uint32 base_img_size=1; + load_block(block,&width,&height,path.c_str(),base_img_size); + c.set_bits(block[0],width,0-height,0x000000FF,0x0000FF00,0x00FF0000); + } else { + // Load BMP + c.load(path.c_str()); + }*/ + uint a = 0x00000000; + uint r = 0x00007C00; + uint g = 0x000003E0; + uint bl = 0x0000001F; + uint size=c.get_width()*c.get_height()+2; + uint16 *buffer=new uint16 [size]; + size=size*2; + c.get_bits((void*)buffer,size,r,g,bl,a,false); + //PIXVAL pixel=0; + //PIXVAL prevPixel=0; + //PIXVAL count=0; + size_t len=0; + // Create a buffer to store all required PIXVAL data + PIXVAL *pixBuffer=new PIXVAL [(c.get_width()+2)*c.get_height()*3]; + bool first=true; + for (uint32 i=0;i<(c.get_width()*c.get_height());i++) { + if (i%c.get_width()==0) { + if (first) { + first=false; + } else { + // Add end of line 0 terminator + pixBuffer[len]=0; + len++; + } + // Add 0 Transparent pixels + pixBuffer[len]=0; + len++; + // Add image width pixels + pixBuffer[len]=c.get_width(); + len++; + } + // Add the pixel data + pixBuffer[len]=buffer[i]; + len++; + } + // Add a 0 at the end of the data + pixBuffer[len]=0; + len++; + + // Not sure how to correctly allocate data for bild_t, other than that in has a set of data + buffer of PIXVAL items + image_t *b=(image_t *)malloc(sizeof(PIXVAL)*len+128); + // Set image data + b->x=0; + b->y=0; + b->w=c.get_width(); + b->h=c.get_height(); + b->zoomable=0; + b->len=len; + for (uint32 i=0;idata[i]=pixBuffer[i]; + } + // delete the temporary pixBuffer + delete pixBuffer; + // register the image + register_image(b); + // Store the image number for later retrieval + _image_id=b->imageid; + char * name=new char [_image_name.length()+1]; + strcpy(name,_image_name.c_str()); + // Store the image details in case it is accessed later + _images->put(name,_image_id); +} Index: gui/components/gui_flowtext_image.h =================================================================== --- gui/components/gui_flowtext_image.h (nonexistent) +++ gui/components/gui_flowtext_image.h (working copy) @@ -0,0 +1,22 @@ +#ifndef GUI_COMPONENTS_FLOWTEXT_IMAGE_H +#define GUI_COMPONENTS_FLOWTEXT_IMAGE_H + +#include +#include "../../display/simimg.h" +#include "../../tpl/hashtable_tpl.h" +#include "../../tpl/stringhashtable_tpl.h" + +class gui_flowtext_image_t { +public: + gui_flowtext_image_t(std::string filePath); + gui_flowtext_image_t(); + + image_id get_image_id(); + std::string get_image_name(); +private: + image_id _image_id; + std::string _image_name; + static hashtable_tpl *_images; +}; + +#endif Index: utils/imgloader.cc =================================================================== --- utils/imgloader.cc (nonexistent) +++ utils/imgloader.cc (working copy) @@ -0,0 +1,916 @@ + +#include "imgloader.h" +#include "dr_rdpng.h" +#include +#include +#include "../simio.h" +#include "../simdebug.h" +#include + +img_loader::img_loader(const char *filename):m_bitmap_data(0),m_bitmap_size(0),m_greyscale(0) { + dispose(); + if (filename!=NULL) { + load_image(filename); + } +} + +img_loader::~img_loader() { + dispose(); +} + +void img_loader::dispose(void) { + if (m_bitmap_data) { + delete[] m_bitmap_data; + m_bitmap_data=NULL; + } + memset(&m_bitmap_file_header,0,sizeof(m_bitmap_file_header)); + memset(&m_bitmap_header,0,sizeof(m_bitmap_header)); +} + +ImgResult img_loader::load_image(const char * filename) { + if (filename==NULL) { + return IMGR_NULL; + } + std::ifstream file(filename, std::ios::binary | std::ios::in); + + if (file.bad()) { + return IMGR_FILE_DOES_NOT_EXIST; + } + + if (!file.is_open()) { + return IMGR_UNABLE_TO_OPEN; + } + + dispose(); + char signature[3]={0,0,0}; + file.read(signature,2); + if (strcmp(signature,"BM")==0) { + file.close(); + return this->load_bmp(filename); + } else if ((signature[0]=='P')&&(signature[1]>='1')&&(signature[1]<='6')) { + file.close(); + return this->load_pxm(filename); + } else if (strcmp(signature,"‰P")==0) { + file.read(signature,2); + if (strcmp(signature,"NG")==0) { + file.close(); + return this->load_png(filename); + } else { + file.close(); + return IMGR_UNSUPPORTED_FORMAT; + } + } + file.close(); + return IMGR_UNSUPPORTED_FORMAT; +} + + +void * img_loader::get_bits() +{ + return (void*)m_bitmap_data; +} + + +ImgResult img_loader::get_bits(void * buffer, uint32 & buffer_size, uint32 red_mask, uint32 green_mask, uint32 blue_mask, uint32 alpha_mask, bool include_padding) +{ + ImgResult result = IMGR_OK; + uint32 BitCountRed = color::bit_count_by_mask(red_mask); + uint32 BitCountGreen = color::bit_count_by_mask(green_mask); + uint32 BitCountBlue = color::bit_count_by_mask(blue_mask); + uint32 BitCountAlpha = color::bit_count_by_mask(alpha_mask); + + unsigned int BitCount = (BitCountRed + BitCountGreen + BitCountBlue + BitCountAlpha + 7) & ~7; + + if (BitCount > 32) { + return IMGR_INVALID_FORMAT; + } + + unsigned int w = get_width(); + unsigned int dataBytesPerLine = (w * BitCount + 7) / 8; + unsigned int LineWidth = (dataBytesPerLine + 3) & ~3; + + if (buffer_size == 0 || buffer == 0) { + buffer_size = (get_width() * get_height() * BitCount) / 8 + sizeof(unsigned int); + return IMGR_OK; + } + + uint8* BufferPtr = (uint8*)buffer; + + uint32 BitPosRed = color::bit_position_by_mask(red_mask); + uint32 BitPosGreen = color::bit_position_by_mask(green_mask); + uint32 BitPosBlue = color::bit_position_by_mask(blue_mask); + uint32 BitPosAlpha = color::bit_position_by_mask(alpha_mask); + + unsigned int j = 0; + + for (uint32 i = 0; i < (uint32)m_bitmap_size; i++) { + *(uint32*)BufferPtr = + (color::convert(m_bitmap_data[i].Blue, 8, BitCountBlue) << BitPosBlue) | + (color::convert(m_bitmap_data[i].Green, 8, BitCountGreen) << BitPosGreen) | + (color::convert(m_bitmap_data[i].Red, 8, BitCountRed) << BitPosRed) | + (color::convert(m_bitmap_data[i].Alpha, 8, BitCountAlpha) << BitPosAlpha); + + if (include_padding) { + j++; + if (j >= w) { + for (uint32 k = 0; k < LineWidth - dataBytesPerLine; k++) { + BufferPtr += (BitCount >> 3); + } + j = 0; + } + + } + + BufferPtr += (BitCount >> 3); + } + + buffer_size -= sizeof(uint32); + + return result; +} + + +ImgResult img_loader::load_bmp(const char *filename) +{ + std::ifstream file(filename, std::ios::binary | std::ios::in); + + if (file.bad()) { + return IMGR_FILE_DOES_NOT_EXIST; + } + + if (file.is_open() == false) { + return IMGR_UNABLE_TO_OPEN; + } + dispose(); + + file.read((char*) &m_bitmap_file_header, sizeof(BITMAP_FILEHEADER_SIZE)); + if (m_bitmap_file_header.Signature!=BITMAP_SIGNATURE) { + return IMGR_INVALID_FORMAT; + } + + file.read((char*) &m_bitmap_header,sizeof(BITMAP_HEADER)); + + // Load Color Table + file.seekg(BITMAP_FILEHEADER_SIZE+m_bitmap_header.HeaderSize,std::ios::beg); + + uint32 color_table_size=0; + if (m_bitmap_header.BitCount==1) { + color_table_size=2; + } else if (m_bitmap_header.BitCount==4) { + color_table_size=16; + } else if (m_bitmap_header.BitCount==8) { + color_table_size=256; + } + + // Always allocate full sized color table (and make sure it is not zero or MSVC will crash!) + BGRA* color_table = new BGRA[color_table_size+1]; + + file.read((char *)color_table,sizeof(BGRA)*m_bitmap_header.ClrUsed); + m_bitmap_size=get_width()*get_height(); + m_bitmap_data=new RGBA[m_bitmap_size]; + + uint32 line_width=((get_width()*get_bit_count()/8)+3)&~3; + uint8* line=new uint8[line_width]; + + file.seekg(m_bitmap_file_header.BitsOffset,std::ios::beg); + + sint32 index=0; + + ImgResult result=IMGR_OK; + + if (m_bitmap_header.Compression==0) { + for (uint32 y=0;y>4)&0x0f].Red; + m_bitmap_data[index].Green=color_table[(Color>>4)&0x0f].Green; + m_bitmap_data[index].Blue=color_table[(Color>>4)&0x0f].Blue; + m_bitmap_data[index].Alpha=color_table[(Color>>4)&0x0f].Alpha; + index++; + m_bitmap_data[index].Red=color_table[Color & 0x0f].Red; + m_bitmap_data[index].Green=color_table[Color & 0x0f].Green; + m_bitmap_data[index].Blue=color_table[Color & 0x0f].Blue; + m_bitmap_data[index].Alpha=color_table[Color & 0x0f].Alpha; + index++; + line_ptr++; + x++; + } else if (m_bitmap_header.BitCount==8) { + uint32 Color=*((uint8*)line_ptr); + m_bitmap_data[index].Red=color_table[Color].Red; + m_bitmap_data[index].Green=color_table[Color].Green; + m_bitmap_data[index].Blue=color_table[Color].Blue; + m_bitmap_data[index].Alpha=color_table[Color].Alpha; + index++; + line_ptr++; + } else if (m_bitmap_header.BitCount==16) { + uint32 Color = *((uint16*) line_ptr); + m_bitmap_data[index].Red=((Color >> 10) & 0x1f) << 3; + m_bitmap_data[index].Green=((Color >> 5) & 0x1f) << 3; + m_bitmap_data[index].Blue=(Color & 0x1f) << 3; + m_bitmap_data[index].Alpha=255; + index++; + line_ptr+=2; + } else if (m_bitmap_header.BitCount==24) { + uint32 Color=*((uint32*)line_ptr); + m_bitmap_data[index].Red=(Color>>16)&0xff; + m_bitmap_data[index].Green=(Color>>8)&0xff; + m_bitmap_data[index].Blue=Color&0xff; + m_bitmap_data[index].Alpha=255; + index++; + line_ptr+=3; + } else if (m_bitmap_header.BitCount==32) { + uint32 Color=*((uint32*)line_ptr); + m_bitmap_data[index].Alpha=(Color>>24)&0xff; + m_bitmap_data[index].Red=(Color>>16)&0xff; + m_bitmap_data[index].Green=(Color>>8)&0xff; + m_bitmap_data[index].Blue=Color&0xff; + index++; + line_ptr+=4; + } + } + } + } else if (m_bitmap_header.Compression==1) { + // RLE Compression + uint8 Count=0; + uint8 color_index=0; + int x=0,y=0; + while (file.eof()==false) { + file.read((char *)&Count,sizeof(uint8)); + file.read((char *)&color_index,sizeof(uint8)); + + if (Count>0) { + index=x+y*get_width(); + for (int k=0;k0){ + // Bitmaps are inverted by default, uninvert the bitmap + for (int yi=0;yi 16) { + return false; + } + + unsigned int w = get_width(); + unsigned int dataBytesPerLine = (w * BitCount + 7) / 8; + unsigned int LineWidth = (dataBytesPerLine + 3) & ~3; + + if (Size == 0 || Buffer == 0) { + Size = (LineWidth * get_height() * BitCount) / 8; + return true; + } + + + if (OptimalPalette) { + PaletteSize = 0; + // Not implemented + } else { + if (BitCount == 1) { + PaletteSize = 2; + // Not implemented: Who need that? + } else if (BitCount == 4) { // 2:2:1 + PaletteSize = 16; + Palette = new BGRA[PaletteSize]; + for (int r = 0; r < 4; r++) { + for (int g = 0; g < 2; g++) { + for (int b = 0; b < 2; b++) { + Palette[r | g << 2 | b << 3].Red = r ? (r << 6) | 0x3f : 0; + Palette[r | g << 2 | b << 3].Green = g ? (g << 7) | 0x7f : 0; + Palette[r | g << 2 | b << 3].Blue = b ? (b << 7) | 0x7f : 0; + Palette[r | g << 2 | b << 3].Alpha = 0xff; + } + } + } + } else if (BitCount == 8) { // 3:3:2 + PaletteSize = 256; + Palette = new BGRA[PaletteSize]; + for (int r = 0; r < 8; r++) { + for (int g = 0; g < 8; g++) { + for (int b = 0; b < 4; b++) { + Palette[r | g << 3 | b << 6].Red = r ? (r << 5) | 0x1f : 0; + Palette[r | g << 3 | b << 6].Green = g ? (g << 5) | 0x1f : 0; + Palette[r | g << 3 | b << 6].Blue = b ? (b << 6) | 0x3f : 0; + Palette[r | g << 3 | b << 6].Alpha = 0xff; + } + } + } + } else if (BitCount == 16) { // 5:5:5 + // Not implemented + } + } + + unsigned int j = 0; + uint8* BufferPtr = (uint8*) Buffer; + + for (uint32 i = 0; i < (uint32)m_bitmap_size; i++) { + if (BitCount == 1) { + // Not implemented: Who needs that? + } else if (BitCount == 4) { + *BufferPtr = ((m_bitmap_data[i].Red >> 6) | (m_bitmap_data[i].Green >> 7) << 2 | (m_bitmap_data[i].Blue >> 7) << 3) << 4; + i++; + *BufferPtr |= (m_bitmap_data[i].Red >> 6) | (m_bitmap_data[i].Green >> 7) << 2 | (m_bitmap_data[i].Blue >> 7) << 3; + } else if (BitCount == 8) { + *BufferPtr = (m_bitmap_data[i].Red >> 5) | (m_bitmap_data[i].Green >> 5) << 3 | (m_bitmap_data[i].Blue >> 5) << 6; + } else if (BitCount == 16) { + // Not implemented + } + + if (IncludePadding) { + j++; + if (j >= w) { + for (unsigned int k = 0; k < (LineWidth - dataBytesPerLine); k++) { + BufferPtr += BitCount / 8; + } + j = 0; + } + } + + BufferPtr++; + } + + Result = true; + + return Result; +} + + +bool img_loader::save_BMP(char * filename,uint32 BitCount) { + bool result=true; + std::ofstream file(filename, std::ios::out | std::ios::binary); + + if (file.is_open()==false) { + return false; + } + BITMAP_FILEHEADER bfh; + BITMAP_HEADER bh; + memset(&bfh,0,sizeof(bfh)); + memset(&bh,0,sizeof(bh)); + + bfh.Signature=BITMAP_SIGNATURE; + bfh.BitsOffset=BITMAP_FILEHEADER_SIZE+sizeof(BITMAP_HEADER); + bfh.Size=(get_width()*get_height()*BitCount)/8+bfh.BitsOffset; + + bh.HeaderSize=sizeof(BITMAP_HEADER); + bh.BitCount=BitCount; + + if (BitCount==32) { + bh.Compression=3; + bh.AlphaMask = 0xff000000; + bh.RedMask = 0x00ff0000; + bh.GreenMask = 0x0000ff00; + bh.BlueMask = 0x000000ff; + } else if (BitCount==16) { + bh.Compression=3; + bh.AlphaMask = 0x00000000; + bh.RedMask = 0x0000001f; + bh.GreenMask = 0x000007E0; + bh.BlueMask = 0x0000F800; + } else { + bh.Compression=0; + } + + unsigned int line_width=(get_width()+3)&~3; + + bh.Planes=1; + bh.Height=get_height(); + bh.Width=get_width(); + bh.SizeImage=(line_width*BitCount*get_height())/8; + bh.PelsPerMeterX=3780; + bh.PelsPerMeterY=3780; + + if (BitCount==32) { + file.write((char*) &bfh, sizeof(BITMAP_FILEHEADER)); + file.write((char*) &bh, sizeof(BITMAP_HEADER)); + file.write((char*) m_bitmap_data, bh.SizeImage); + } else if (BitCount==16) { + uint8* Bitmap = new uint8[bh.SizeImage]; + BGRA *Palette = 0; + uint32 PaletteSize = 0; + + if (get_bits_with_palette(Bitmap, bh.SizeImage, BitCount, Palette, PaletteSize)) { + bfh.BitsOffset += PaletteSize * sizeof(BGRA); + file.write((char*) &bfh, BITMAP_FILEHEADER_SIZE); + file.write((char*) &bh, sizeof(BITMAP_HEADER)); + file.write((char*) Palette, PaletteSize * sizeof(BGRA)); + file.write((char*) Bitmap, bh.SizeImage); + } + delete [] Bitmap; + delete [] Palette; + } else { + uint32 RedMask = 0; + uint32 GreenMask = 0; + uint32 BlueMask = 0; + uint32 AlphaMask = 0; + + if (BitCount == 16) { + RedMask = 0x0000F800; + GreenMask = 0x000007E0; + BlueMask = 0x0000001F; + AlphaMask = 0x00000000; + } else if (BitCount==24) { + RedMask = 0x00FF0000; + GreenMask = 0x0000FF00; + BlueMask = 0x000000FF; + } else { + result=false; + } + if (result) { + if (get_bits(NULL, bh.SizeImage, RedMask, GreenMask, BlueMask, AlphaMask)) { + uint8* Bitmap = new uint8[bh.SizeImage]; + if (get_bits(Bitmap, bh.SizeImage, RedMask, GreenMask, BlueMask, AlphaMask)) { + file.write((char*) &bfh, sizeof(BITMAP_FILEHEADER)); + file.write((char*) &bh, sizeof(BITMAP_HEADER)); + file.write((char*) Bitmap, bh.SizeImage); + } + delete [] Bitmap; + } + } + } + file.close(); + return result; +} + + +bool img_loader::save_PNG(char * filename) +{ + return (bool)write_png(filename, (uint8*)m_bitmap_data, get_width(), get_height(), get_bit_count()); + +} + + +bool img_loader::save_PXM(char * filename, int level) +{ + FILE *f=fopen(filename,"wb"); + if (f==NULL) { + return false; + } + fprintf(f,"P%d\n#Simutrans PxM Writer\n%d\n%d\n",level,get_width(),get_height()); + if (level!=1&&level!=4) { + fprintf(f,"255\n"); + } + if (level<=6) { + for (uint32 y=0;y384) { + fprintf(f,"1 "); + } else { + fprintf(f,"0 "); + } + } else if (level==2) { + int v=(m_bitmap_data[location].Red+m_bitmap_data[location].Green+m_bitmap_data[location].Blue)/3; + if (v>255) { + v=255; + } + fprintf(f,"%d ",v); + } else if (level==3) { + fprintf(f,"%d %d %d\t",m_bitmap_data[location].Red,m_bitmap_data[location].Green,m_bitmap_data[location].Blue); + } else if (level==4) { + uint8 val=0; + for (int x2=0;x2<8;x2++) { + val=val<<1; + int v=m_bitmap_data[location+x2].Red+m_bitmap_data[location+x2].Green+m_bitmap_data[location+x2].Blue; + if (v>384) { + val+=1; + } + + } + fputc(val,f); + x+=7; + } else if (level==5) { + int v=(m_bitmap_data[location].Red+m_bitmap_data[location].Green+m_bitmap_data[location].Blue)/3; + if (v>255) { + v=255; + } + fputc(v,f); + } else if (level==6) { + fputc(m_bitmap_data[location].Red,f); + fputc(m_bitmap_data[location].Green,f); + fputc(m_bitmap_data[location].Blue,f); + } + } + if (level<4) { + fprintf(f,"\n"); + } + } + } + return false; +} + + +bool img_loader::is_greyscale(void) +{ + return m_greyscale; +} + + +ImgResult img_loader::load_png(const char *filename) { + unsigned char **block=new unsigned char * [1]; + block[0]=(unsigned char *)malloc(1); + uint32 width=0; + uint32 height=0; + uint32 base_img_size=1; + if (!load_block(block,&width,&height,filename,base_img_size)) { + return IMGR_UNKNOWN_ERROR; + } + this->set_bits(block[0],width,height,0x0000FF00,0x00FF0000,0xFF000000,0x000000FF); + return IMGR_OK; +} + + +uint32 img_loader::read_next_number_pxm(std::ifstream& file) { + char c=file.get(); + while ((c<'0')||(c>'9')) { + // If there is a # then the line is a comment - read to end of line + if (c=='#') { + while (c!='\n') { + c=file.get(); + if (file.fail()) { + return -1; + } + } + if (file.fail()) { + return -1; + } + } + // get next character + c=file.get(); + } + uint32 ret_val=0; + while ((c>='0')&&(c<='9')) { + ret_val*=10; + ret_val+=(c-'0'); + if (file.fail()) { + return ret_val; + } + c=file.get(); + } + file.unget(); + return ret_val; +} + + +inline sint32 img_loader::standardize_number_pxm(sint32 val, sint32 maxval) { + if (maxval==255) { + return val; + } else if (maxval<255) { + return val*255/maxval; + } else { + int dif=maxval/256; + int ret=val/dif; + if (ret>255) { + return 255; + } + return ret; + } +} + +ImgResult img_loader::load_pxm(const char* filename) { + std::ifstream file(filename, std::ios::binary | std::ios::in); + char signature[3]={0,0,0}; + file.read(signature,2); + // convert second character of signature to level + uint8 level=signature[1]-'0'; + if ((level<1)||(level>6)) { + // should never get here, as the value is checked before entering the function. + return IMGR_INVALID_FORMAT; + } + + //char buffer[256]; + bool binary_data=(level>=4); + + // Read in the width and height of the image + int width=read_next_number_pxm(file); + int height=read_next_number_pxm(file); + + // If width or height are negative an error has occured. + if ((width<0)||(height<0)) { + return IMGR_INVALID_FORMAT; + } + + int maxval=1; + // If image is not Black and White read in the max value (can be up to 65535) + if ((level!=1)&&(level!=4)) { + maxval=read_next_number_pxm(file); + } + // Read in and check that we have a whitespace character + char c=file.get(); + if ((c!=' ')&&(c!='\t')&&(c!='\n')&&(c!='\r')) { + return IMGR_INVALID_FORMAT; + } + m_bitmap_header.Width=width; + m_bitmap_header.Height=height; + m_bitmap_header.BitCount=32; + m_bitmap_header.Compression=3; + m_bitmap_size=width*height; + + // Alocate space for bitmap data + m_bitmap_data=new RGBA[m_bitmap_size]; + + if (binary_data) { + // Read in Binary Data + if (level==4) { + // Black and white image encoded to individual bits, padded to a full byte for each line + int line_len=(width+7)/8; + char *buffer=new char [line_len]; + for (int y=0;y=0;i--) { + if (x1255) { + r*=256; + r+=file.get(); + } + r=standardize_number_pxm(r,maxval); + if (level==5) { + // If greyscale green and blue are the same value as red + g=r; + b=r; + } else { + // If color, read in green and blue values + g=file.get(); + if (maxval>255) { + g*=256; + g+=file.get(); + } + b=file.get(); + if (maxval>255) { + b*=256; + b+=file.get(); + } + g=standardize_number_pxm(g,maxval); + b=standardize_number_pxm(b,maxval); + } + int location=y*width+x; + m_bitmap_data[location].Red=r; + m_bitmap_data[location].Green=g; + m_bitmap_data[location].Blue=b; + m_bitmap_data[location].Alpha=0; + } + } + } + } else { + // Read in ASCII Data + for (int y=0;y 8192) { + m_bitmap_header.Width=8192; + } + return m_bitmap_header.Width<0?-m_bitmap_header.Width:m_bitmap_header.Width; +} + + +uint32 img_loader::get_height(void) { + if (abs(m_bitmap_header.Height) > 8192) { + m_bitmap_header.Height=8192; + } + return m_bitmap_header.Height<0?-m_bitmap_header.Height:m_bitmap_header.Height; +} + + +inline uint32 img_loader::get_bit_count() { + return m_bitmap_header.BitCount; +} + +inline uint32 img_loader::color::bit_count_by_mask(uint32 mask) { + uint32 bit_count=0; + while (mask) { + mask&=mask-1; + bit_count++; + } + return bit_count; +} + +inline uint32 img_loader::color::bit_position_by_mask(uint32 mask) { + return bit_count_by_mask((mask & (~mask + 1)) - 1); +} + +inline uint32 img_loader::color::component_by_mask(uint32 color, uint32 mask) { + uint32 component=color&mask; + return component>>bit_position_by_mask(mask); +} + +inline uint32 img_loader::color::bit_count_to_mask(uint32 bit_count) { + return (bit_count == 32)?0xFFFFFFFF:(1<>= (from_bit_count-to_bit_count); + } else { + color <<= (to_bit_count-from_bit_count); + if (color>0) { + color|=bit_count_to_mask(to_bit_count-from_bit_count); + } + } + return color; +} Index: utils/imgloader.h =================================================================== --- utils/imgloader.h (nonexistent) +++ utils/imgloader.h (working copy) @@ -0,0 +1,213 @@ +#include "../simtypes.h" +#ifndef IMGLOADER_H +#define IMGLOADER_H + +/** + * \enum ImgResult + * ImgResult is for storing return values from the various functions in the img_loader class + */ +enum ImgResult { + IMGR_OK, /// Image Successfully Loaded + IMGR_NULL, /// NULL value passed + IMGR_FILE_DOES_NOT_EXIST, /// File refered to does not exist + IMGR_UNABLE_TO_OPEN, /// File refered to exists but is not able to be opened + IMGR_UNSUPPORTED_FORMAT, /// File refered to exists but is in an unsupported format + IMGR_INVALID_FORMAT, /// File refered to exists and indicates it is a supported format, but is not + IMGR_UNKNOWN_ERROR /// An unknown error occured... +}; + +#ifndef __LITTLE_ENDIAN__ + #ifndef __BIG_ENDIAN__ + #define __LITTLE_ENDIAN__ + #endif +#endif + +#ifdef __LITTLE_ENDIAN__ + #define BITMAP_SIGNATURE 0x4d42 +#else + #define BITMAP_SIGNATURE 0x424d +#endif + +#pragma pack(push, 1) +typedef struct _BITMAP_FILEHEADER { + uint16 Signature; + uint32 Size; + uint32 Reserved; + uint32 BitsOffset; +} BITMAP_FILEHEADER; + +const int BITMAP_FILEHEADER_SIZE=14; + +typedef struct _BITMAP_HEADER { + uint32 HeaderSize; + sint32 Width; + sint32 Height; + uint16 Planes; + uint16 BitCount; + uint32 Compression; + uint32 SizeImage; + sint32 PelsPerMeterX; + sint32 PelsPerMeterY; + uint32 ClrUsed; + uint32 ClrImportant; + uint32 RedMask; + uint32 GreenMask; + uint32 BlueMask; + uint32 AlphaMask; + uint32 CsType; + uint32 Endpoints[9]; // see http://msdn2.microsoft.com/en-us/library/ms536569.aspx + uint32 GammaRed; + uint32 GammaGreen; + uint32 GammaBlue; +} BITMAP_HEADER; + +typedef struct _RGBA { + uint8 Red; + uint8 Green; + uint8 Blue; + uint8 Alpha; +} RGBA; + +typedef struct _BGRA { + uint8 Blue; + uint8 Green; + uint8 Red; + uint8 Alpha; +} BGRA; + +#pragma pack(pop) + +/** + * \class img_loader + * The img_loader class is for the loading of images. It currently supports most BMP & PBM formats, and all PNG formats. + */ +class img_loader { + public: + /** + * img_loader constructor + * @param filename is an optional filename parameter to load an image. + */ + img_loader(const char *filename=NULL); + ~img_loader(); + /** + * load_image + * @param filename is the name/path to the file to load. + * @return Returns an ImgResult enum indicating the result of the attempt to load the file. + */ + ImgResult load_image(const char *filename); + /** + * get_bits + * @return returns a void * array pointing to the ARGB buffer of this image. + */ + void * get_bits(); + /** + * get_bits + * @param buffer is a buffer for the image to be loaded into. + * @param buffer_size is the size of the buffer passed in + * @param red_mask is the mask that should be used for the red bits + * @param green_mask is the mask that should be used for the green bits + * @param blue_mask is the mask that should be used for the blue bits + * @param alpha_mask is an option mask that should be used for the alpha bits (defaults to 0) + * @param include_padding is an option to include padding on the image (defaults to false) + * @return Returns an ImgResult enum indicating the result of the attempt to load the file. + * Get Bits returns the image data in the format requested + */ + ImgResult get_bits(void *buffer, uint32 &buffer_size, uint32 red_mask, uint32 green_mask,uint32 blue_mask,uint32 alpha_mask=0,bool include_padding=false); + + /** + * set_bits + * @param buffer is a buffer to load the bits from + * @param width is the width of the image + * @param height is the height of the image + * @param red_mask is the mask that should be used for the red bits + * @param green_mask is the mask that should be used for the green bits + * @param blue_mask is the mask that should be used for the blue bits + * @param alpha_mask is an option mask that should be used for the alpha bits (defaults to 0) + * @return returns true on success, false of failure + * Set Bits sets the data from the format specified + */ + bool set_bits(void *buffer, uint32 width, uint32 height, uint32 red_mask, uint32 green_mask, uint32 blue_mask,uint32 alpha_mask=0); + + /** + * dispose + * Disposes of current Bitmap Data + */ + void dispose(void); + + /** + * save_BMP + * @param filename is the name to save the file as + * @param BitCount to save as, defaults to 32 + * @return true on success, false on failure + * Saves the image as a BMP file + */ + bool save_BMP(char * filename, uint32 BitCount=32); + + /** + * save_PNG + * @param filename is the name to save the file as + * @return true on success, false on failure + * Saves the image as a PNG file + */ + bool save_PNG(char * filename); + + /** + * save_PBM + * @param filename is the name to save the file as + * @param level is the type of PBM to save as (defaults to P6) + * @return true on success, false on failure + * Saves the image as a PBM file + */ + bool save_PXM(char * filename,int level=6); + + /** + * is_greyscale + * @return Returns true if the image is Greyscale, false otherwise + * Returns true if the image is greyscale + */ + bool is_greyscale(void); + + /** + * get_width + * @return returns the width of the image + * Returns the width of the image - where no image is loaded, it will return 0 + */ + uint32 get_width(void); + + /** + * get_height + * @return returns the height of the image + * Returns the height of the image - where no image is loaded, it will return 0 + */ + uint32 get_height(void); + + /** + * get_bit_count + * @return returns the bit_count of the image + * Returns the bit_count of the image - where no image is loaded, it will return 0 + */ + uint32 get_bit_count(void); + + class color { + public: + static uint32 bit_count_by_mask(uint32 mask); + static uint32 bit_position_by_mask(uint32 mask); + static uint32 component_by_mask(uint32 color, uint32 mask); + static uint32 bit_count_to_mask(uint32 bit_count); + static uint32 convert(uint32 color, uint32 from_bit_count, uint32 to_bit_count); + }; + private: + ImgResult load_bmp(const char *filename); + ImgResult load_png(const char *filename); + ImgResult load_pxm(const char *filename); + uint32 read_next_number_pxm(std::ifstream& file); + sint32 standardize_number_pxm(sint32 val, sint32 maxval); + bool get_bits_with_palette(void* Buffer, uint32 &Size, uint32 BitCount, BGRA* &Palette, uint32 &PaletteSize, bool OptimalPalette=false, bool IncludePadding=true); + BITMAP_FILEHEADER m_bitmap_file_header; + BITMAP_HEADER m_bitmap_header; + RGBA *m_bitmap_data; + sint32 m_bitmap_size; + sint8 m_greyscale; + +}; +#endif