// Reads Tiff Images RGBAImage* RGBAImage::ReadTiff(char *filename) { RGBAImage *fimg = 0; TIFF* tif = TIFFOpen(filename, "r"); char emsg[1024]; if (tif) { TIFFRGBAImage img; if (TIFFRGBAImageBegin(&img, tif, 0, emsg)) { size_t npixels; uint32* raster; npixels = img.width * img.height; raster = (uint32*) _TIFFmalloc(npixels * sizeof (uint32)); if (raster != NULL) { if (TIFFRGBAImageGet(&img, raster, img.width, img.height)) { // result is in ABGR fimg = new RGBAImage(img.width, img.height); for (int y = 0; y < img.height; y++) { for (int x = 0; x < img.width; x++) { fimg->Set(x,y, raster[x + y * img.width]); } } } _TIFFfree(raster); } } TIFFRGBAImageEnd(&img); } else { TIFFError(filename, emsg); } return fimg; }
static RGBAImage *ReadFromFile(const string &filename) { cout << "reading from file:" << filename << "\n"; vector<uint8_t> lodepng_image; //the raw pixels unsigned width, height; unsigned error = lodepng::decode(lodepng_image, width, height, filename.c_str()); if (error) { cout << "decoder error " << error << ": " << lodepng_error_text(error) << endl; return NULL; } //the pixels are now in the vector "image", 4 bytes per pixel, //ordered RGBARGBA..., use it as texture, draw it, ... cout << "width " << width << ", height " << height << "\n"; RGBAImage *rgbaimg = new RGBAImage(width,height,filename); for(unsigned y = 0; y < height; y += 1) { for(unsigned x = 0; x < width; x += 1) { uint32_t red = lodepng_image[4 * y * width + 4 * x + 0]; //red uint32_t green = lodepng_image[4 * y * width + 4 * x + 1]; //green uint32_t blue = lodepng_image[4 * y * width + 4 * x + 2]; //blue uint32_t alpha = lodepng_image[4 * y * width + 4 * x + 3]; //alpha rgbaimg->Set( red, green, blue, alpha, y*width+x ); } } return rgbaimg; }
Image* LoadHLWBuff( byte* buffer ){ byte *buf_p; unsigned long mipdatasize; int columns, rows, numPixels; byte *pixbuf; int row, column; byte *palette; LPWAD3_MIP lpMip; unsigned char red, green, blue, alphabyte; lpMip = (LPWAD3_MIP)buffer; //!\todo Make endian-safe. mipdatasize = GET_MIP_DATA_SIZE( lpMip->width,lpMip->height ); palette = buffer + mipdatasize + 2; buf_p = buffer + lpMip->offsets[0]; columns = lpMip->width; rows = lpMip->height; numPixels = columns * rows; RGBAImage* image = new RGBAImage( columns, rows ); for ( row = 0; row < rows; row++ ) { pixbuf = image->getRGBAPixels() + row * columns * 4; for ( column = 0; column < columns; column++ ) { int palIndex; palIndex = *buf_p++; red = *( palette + ( palIndex * 3 ) ); green = *( palette + ( palIndex * 3 ) + 1 ); blue = *( palette + ( palIndex * 3 ) + 2 ); // HalfLife engine makes pixels that are BLUE transparent. // So show them that way in the editor. if ( blue == 0xff && red == 0x00 && green == 0x00 ) { alphabyte = 0x00; blue = 0x00; // don't set the resulting pixel to blue } else { alphabyte = 0xff; } *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = alphabyte; } } return image; }
/** * Simple octree color quantization: Similar to http://rosettacode.org/wiki/Color_quantization#C */ void octreeColorQuantize(const RGBAImage& image, size_t max_colors, std::vector<RGBAPixel>& colors, Octree** octree) { assert(max_colors > 0); // have an octree with the colors as leaves Octree* internal_octree = new Octree(); // and a priority queue of leaves to be processed // the order of leaves is very important, see NodeComparator std::priority_queue<Octree*, std::vector<Octree*>, NodeComparator> queue; // insert the colors into the octree for (int x = 0; x < image.getWidth(); x++) { for (int y = 0; y < image.getHeight(); y++) { RGBAPixel color = image.pixel(x, y); Octree* node = Octree::findOrCreateNode(internal_octree, color); node->setColor(color); // add the leaf only once to the queue if (node->getCount() == 1) queue.push(node); } } // now: reduce the leaves until we have less colors than maximum while (queue.size() > max_colors) { Octree* node = queue.top(); assert(node->isLeaf()); queue.pop(); // add the color value of the leaf to the parent node->reduceToParent(); Octree* parent = node->getParent(); // delete the leaf (leaf is automatically removed from parent in reduceToParent()) delete node; // add parent to queue if it is a leaf now if (parent->isLeaf()) queue.push(parent); } // gather the quantized colors while (queue.size()) { Octree* node = queue.top(); assert(node->isLeaf()); node->setColorID(colors.size()); colors.push_back(node->getColor()); queue.pop(); } if (octree != nullptr) *octree = internal_octree; else delete internal_octree; }
GLuint GLUtils::generateTexture(const RGBAImage &img, GLuint &tex) { int width = img.width(); int height = img.height(); cout<<"Image size: "<<width<<"x"<<height<<endl; cout<<"Generating image texture..."<<endl; GLfloat *m = new GLfloat[width * height * 4]; int i, j; //float c; for (i = 0; i < height; i++) { int y = i; for (j = 0; j < width; j++) { int x = j; float r = img(x, y, 0); float g = img(x, y, 1); float b = img(x, y, 2); float a = img(x, y, 3); int baseIdx = (y * width + x) * 4; //float randVaue = (float)rand()/(float)RAND_MAX; m[baseIdx+ 0] = r; m[baseIdx+ 1] = g; m[baseIdx+ 2] = b; m[baseIdx+ 3] = a; } } glEnable(GL_TEXTURE_2D); glDeleteTextures(1, &tex); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, width, height, 0, GL_RGBA, GL_FLOAT, m); glDisable(GL_TEXTURE_2D); cout<<"Image texture ID: "<<tex<<endl; delete[] m; return tex; }
static Image* LoadImage (ArchiveFile& file, const char *extension) { RGBAImage* image = (RGBAImage *) 0; /* load the buffer from pk3 or filesystem */ ScopedArchiveBuffer buffer(file); GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_type(extension, (GError**) 0); if (loader == (GdkPixbufLoader*)0) { g_warning("could not get a loader for: '%s'\n", extension); return image; } GError *error = (GError *) 0; if (gdk_pixbuf_loader_write(loader, (const guchar *) buffer.buffer, static_cast<gsize> (buffer.length), &error)) { int pos = 0; GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); const int width = gdk_pixbuf_get_width(pixbuf); const int height = gdk_pixbuf_get_height(pixbuf); const gboolean hasAlpha = gdk_pixbuf_get_has_alpha(pixbuf); const int stepWidth = gdk_pixbuf_get_n_channels(pixbuf); const guchar *pixels = gdk_pixbuf_get_pixels(pixbuf); image = new RGBAImage(width, height, false); byte *rgba = image->getRGBAPixels(); const int rowextra = gdk_pixbuf_get_rowstride(pixbuf) - width * stepWidth; for (int y = 0; y < height; ++y, pixels += rowextra) { for (int x = 0; x < width; ++x) { rgba[pos++] = *(pixels++); rgba[pos++] = *(pixels++); rgba[pos++] = *(pixels++); if (hasAlpha && *pixels != 255) image->setHasAlpha(true); rgba[pos++] = hasAlpha ? *(pixels++) : 255; } } g_object_unref(pixbuf); } else { g_warning("image could not get loaded: '%s' %s\n", file.getName().c_str(), (error != (GError *) 0) ? error->message : ""); if (error) g_error_free(error); } gdk_pixbuf_loader_close(loader, (GError**) 0); return image; }
static Image* LoadImageGDK (ArchiveFile& file) { // Allocate a new GdkPixBuf and create an alpha-channel with alpha=1.0 GdkPixbuf* rawPixbuf = gdk_pixbuf_new_from_file(file.getName().c_str(), NULL); // Only create an alpha channel if the other rawPixbuf could be loaded GdkPixbuf* img = (rawPixbuf != NULL) ? gdk_pixbuf_add_alpha(rawPixbuf, TRUE, 255, 0, 255) : NULL; if (img != NULL) { // Allocate a new image RGBAImage* image = new RGBAImage(gdk_pixbuf_get_width(img), gdk_pixbuf_get_height(img), false); // Initialise the source buffer pointers guchar* gdkStart = gdk_pixbuf_get_pixels(img); int rowstride = gdk_pixbuf_get_rowstride(img); int numChannels = gdk_pixbuf_get_n_channels(img); // Set the target buffer pointer to the first RGBAPixel RGBAPixel* targetPixel = image->pixels; // Now do an unelegant cycle over all the pixels and move them into the target for (unsigned int y = 0; y < image->height; y++) { for (unsigned int x = 0; x < image->width; x++) { guchar* gdkPixel = gdkStart + y * rowstride + x * numChannels; // Copy the values from the GdkPixel targetPixel->red = gdkPixel[0]; targetPixel->green = gdkPixel[1]; targetPixel->blue = gdkPixel[2]; targetPixel->alpha = gdkPixel[3]; if (targetPixel->alpha != 255) image->setHasAlpha(true); // Increase the pointer targetPixel++; } } // Free the GdkPixbufs from the memory g_object_unref(G_OBJECT(img)); g_object_unref(G_OBJECT(rawPixbuf)); return image; } else { g_warning("image could not get loaded: '%s'\n", file.getName().c_str()); } // No image could be loaded, return NULL return NULL; }
Image* LoadDDSBuff( const byte* buffer ){ int width, height; ddsPF_t pixelFormat; if ( DDSGetInfo( reinterpret_cast<ddsBuffer_t*>( const_cast<byte*>( buffer ) ), &width, &height, &pixelFormat ) == -1 ) { return 0; } RGBAImage* image = new RGBAImage( width, height ); if ( DDSDecompress( reinterpret_cast<ddsBuffer_t*>( const_cast<byte*>( buffer ) ), image->getRGBAPixels() ) == -1 ) { image->release(); return 0; } return image; }
static RGBAImage *ToRGBAImage(FIBITMAP *image, const char *filename=NULL) { const int w = FreeImage_GetWidth(image); const int h = FreeImage_GetHeight(image); RGBAImage* result = new RGBAImage(w, h, filename); // Copy the image over to our internal format, FreeImage has the scanlines bottom to top though. unsigned int* dest = result->Get_Data(); for( int y=0; y < h; y++, dest += w ) { const unsigned int* scanline = reinterpret_cast<const unsigned int*>(FreeImage_GetScanLine(image, h - y - 1)); memcpy(dest, scanline, sizeof(dest[0]) * w); } return result; }
void testPNG() { RGBAImage img; img.create(100, 100); for (vector<RGBAPixel>::iterator it = img.data.begin(); it != img.data.end(); it++) { *it = ((rand() % 256) << 24) | ((rand() % 256) << 16) | ((rand() % 256) << 8) | (rand() % 256); } img.writePNG("test.png"); RGBAImage img2; img2.readPNG("test.png"); if (img2.data != img.data) cout << "images don't match after trip through PNG!" << endl; else cout << "PNG test successful" << endl; }
Image* LoadMDLImageBuff( byte* buffer ){ if ( !LoadPalette() ) { return 0; } if ( !ident_equal( buffer, MDL_IDENT ) ) { globalErrorStream() << "LoadMDLImage: data has wrong ident\n"; return 0; } PointerInputStream inputStream( buffer ); inputStream.seek( 4 + 4 + 12 + 12 + 4 + 12 ); //int numskins = istream_read_int32_le( inputStream ); int skinwidth = istream_read_int32_le( inputStream ); int skinheight = istream_read_int32_le( inputStream ); inputStream.seek( 4 + 4 + 4 + 4 + 4 + 4 ); switch ( istream_read_int32_le( inputStream ) ) { case MDL_SKIN_SINGLE: break; case MDL_SKIN_GROUP: int numskins = istream_read_int32_le( inputStream ); inputStream.seek( numskins * 4 ); break; } RGBAImage* image = new RGBAImage( skinwidth, skinheight ); unsigned char* pRGBA = image->getRGBAPixels(); for ( int i = 0; i < ( skinheight ); i++ ) { for ( int j = 0; j < ( skinwidth ); j++ ) { byte index = istream_read_byte( inputStream ); *pRGBA++ = mdl_palette[index * 3 + 0]; *pRGBA++ = mdl_palette[index * 3 + 1]; *pRGBA++ = mdl_palette[index * 3 + 2]; *pRGBA++ = 255; } } return image; }
static Image* LoadJPGBuff_( const void *src_buffer, int src_size ){ struct jpeg_decompress_struct cinfo; struct my_jpeg_error_mgr jerr; cinfo.err = jpeg_std_error( &jerr.pub ); jerr.pub.error_exit = my_jpeg_error_exit; if ( setjmp( jerr.setjmp_buffer ) ) { //< TODO: use c++ exceptions instead of setjmp/longjmp to handle errors globalErrorStream() << "WARNING: JPEG library error: " << errormsg << "\n"; jpeg_destroy_decompress( &cinfo ); return 0; } jpeg_create_decompress( &cinfo ); jpeg_buffer_src( &cinfo, const_cast<void*>( src_buffer ), src_size ); jpeg_read_header( &cinfo, TRUE ); jpeg_start_decompress( &cinfo ); int row_stride = cinfo.output_width * cinfo.output_components; RGBAImage* image = new RGBAImage( cinfo.output_width, cinfo.output_height ); JSAMPARRAY buffer = ( *cinfo.mem->alloc_sarray )( ( j_common_ptr ) & cinfo, JPOOL_IMAGE, row_stride, 1 ); while ( cinfo.output_scanline < cinfo.output_height ) { jpeg_read_scanlines( &cinfo, buffer, 1 ); if ( cinfo.out_color_components == 4 ) { j_putRGBAScanline( buffer[0], cinfo.output_width, image->getRGBAPixels(), cinfo.output_scanline - 1 ); } else if ( cinfo.out_color_components == 3 ) { j_putRGBScanline( buffer[0], cinfo.output_width, image->getRGBAPixels(), cinfo.output_scanline - 1 ); } else if ( cinfo.out_color_components == 1 ) { j_putGrayScanlineToRGB( buffer[0], cinfo.output_width, image->getRGBAPixels(), cinfo.output_scanline - 1 ); } } jpeg_finish_decompress( &cinfo ); jpeg_destroy_decompress( &cinfo ); return image; }
void CaveRendermode::draw(RGBAImage& image, const mc::BlockPos& pos, uint16_t id, uint16_t data) { // a nice color gradient to see something // (because the whole map is just full of cave stuff, // one can't differentiate the single caves) double h1 = (double) (64 - pos.y) / 64; if (pos.y > 64) h1 = 0; double h2 = 0; if (pos.y >= 64 && pos.y < 96) h2 = (double) (96 - pos.y) / 32; else if (pos.y > 16 && pos.y < 64) h2 = (double) (pos.y - 16) / 48; double h3 = 0; if (pos.y > 64) h3 = (double) (pos.y - 64) / 64; int R = h1 * 128.0 + 128.0; int G = h2 * 255.0; int B = h3 * 255.0; int Y = (R*3+G*10+B)/14; //get luminance of recolor // We try to do luminance-neutral additive/subtractive color instead of alpha blending, for better contrast // So first subtract luminance from each component. R = (R-Y)/3; // /3 is similar to alpha=85 G = (G-Y)/3; B = (B-Y)/3; int size = image.getWidth(); for (int y = 0; y < size; y++) for (int x = 0; x < size; x++) { uint32_t pixel = image.getPixel(x, y); if (pixel != 0) { image.setPixel(x,y, rgba_add_clamp(pixel, R, G, B)); } } }
void TopdownTileRenderer::renderTile(const TilePos& tile_pos, RGBAImage& tile) { int texture_size = images->getTextureSize(); tile.setSize(getTileSize(), getTileSize()); for (int x = 0; x < tile_width; x++) { for (int z = 0; z < tile_width; z++) { mc::ChunkPos chunkpos(tile_pos.getX() * tile_width + x, tile_pos.getY() * tile_width + z); current_chunk = world->getChunk(chunkpos); if (current_chunk != nullptr) renderChunk(*current_chunk, tile, texture_size*16*x, texture_size*16*z); } } }
void testOctreeWithImage(const RGBAImage& image) { std::set<RGBAPixel> colors; int r = 0, g = 0, b = 0, count = 0; Octree octree; // insert all pixels into an octree for (int x = 0; x < image.getWidth(); x++) { for (int y = 0; y < image.getHeight(); y++) { RGBAPixel color = image.getPixel(x, y); colors.insert(color); r += rgba_red(color); g += rgba_green(color); b += rgba_blue(color); count++; Octree::findOrCreateNode(&octree, color)->setColor(color); } } // make sure that all colors are inserted correctly BOOST_CHECK(octree.isRoot() && !octree.isLeaf()); BOOST_CHECK(!octree.hasColor()); // reduce all colors up to the root of the tree // the color should be the overall average color traverseReduceOctree(&octree); BOOST_CHECK(octree.hasColor()); RGBAPixel average1 = octree.getColor(); RGBAPixel average2 = rgba(r / count, g / count, b / count, 255); BOOST_CHECK_EQUAL(average1, average2); BOOST_TEST_MESSAGE("Overall colors: " << colors.size()); BOOST_TEST_MESSAGE("Pixels per color: " << (double) (image.getWidth() * image.getHeight()) / colors.size()); BOOST_TEST_MESSAGE("Average color: " << (int) rgba_red(average1) << "," << (int) rgba_green(average1) << "," << (int) rgba_blue(average1)); }
static std::shared_ptr<FIBITMAP> to_free_image(const RGBAImage &image) { const auto *data = image.get_data(); std::shared_ptr<FIBITMAP> bitmap( FreeImage_Allocate(image.get_width(), image.get_height(), 32, 0x000000ff, 0x0000ff00, 0x00ff0000), FreeImageDeleter()); assert(bitmap.get()); for (auto y = 0u; y < image.get_height(); y++, data += image.get_width()) { auto scanline = reinterpret_cast<unsigned int *>( FreeImage_GetScanLine(bitmap.get(), image.get_height() - y - 1)); memcpy(scanline, data, sizeof(data[0]) * image.get_width()); } return bitmap; }
void TopdownTileRenderer::renderChunk(const mc::Chunk& chunk, RGBAImage& tile, int dx, int dy) { // TODO implement preblit water render behavior int texture_size = images->getTextureSize(); for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { std::deque<RenderBlock> blocks; // TODO make this water thing a bit nicer bool in_water = false; int water = 0; mc::LocalBlockPos localpos(x, z, 0); //int height = chunk.getHeightAt(localpos); //localpos.y = height; localpos.y = -1; if (localpos.y >= mc::CHUNK_HEIGHT*16 || localpos.y < 0) localpos.y = mc::CHUNK_HEIGHT*16 - 1; uint16_t id = chunk.getBlockID(localpos); while (id == 0 && localpos.y > 0) { localpos.y--; id = chunk.getBlockID(localpos); } if (localpos.y < 0) continue; while (localpos.y >= 0) { mc::BlockPos globalpos = localpos.toGlobalPos(chunk.getPos()); id = chunk.getBlockID(localpos); if (id == 0) { in_water = false; localpos.y--; continue; } uint16_t data = chunk.getBlockData(localpos); bool is_water = (id == 8 || id == 9) && data == 0; if (render_mode->isHidden(globalpos, id, data)) { localpos.y--; continue; } if (is_water && !use_preblit_water) { if (is_water == in_water) { localpos.y--; continue; } in_water = is_water; } else if (use_preblit_water) { if (!is_water) water = 0; else { water++; if (water > images->getMaxWaterPreblit()) { auto it = blocks.begin(); while (it != blocks.end()) { auto current = it++; if (it == blocks.end() || (it->id != 8 && it->id != 9)) { RenderBlock& top = *current; // blocks.erase(current); top.id = 8; top.data = OPAQUE_WATER; top.block = images->getBlock(top.id, top.data); render_mode->draw(top.block, top.pos, top.id, top.data); // blocks.insert(current, top); break; } else { blocks.erase(current); } } break; } } } data = checkNeighbors(globalpos, id, data); RGBAImage block = images->getBlock(id, data); if (Biome::isBiomeBlock(id, data)) { block = images->getBiomeBlock(id, data, getBiomeOfBlock(globalpos, &chunk)); } render_mode->draw(block, globalpos, id, data); RenderBlock render_block; render_block.block = block; render_block.id = id; render_block.data = data; render_block.pos = globalpos; blocks.push_back(render_block); if (!images->isBlockTransparent(id, data)) break; localpos.y--; } while (blocks.size() > 0) { RenderBlock render_block = blocks.back(); tile.alphaBlit(render_block.block, dx + x*texture_size, dy + z*texture_size); blocks.pop_back(); } } } }
bool expandMap(const string& outputpath) { // read old params MapParams mp; if (!mp.readFile(outputpath)) { cerr << "pigmap.params missing or corrupt" << endl; return false; } int32_t tileSize = mp.tileSize(); // to expand a map, the following must be done: // 1. the top-left quadrant of the current zoom level 1 needs to be moved to zoom level 2, where // it will become the bottom-right quadrant of the top-left quadrant of the new zoom level 1, // so the top-level file "0.png" and subdirectory "0" must become "0/3.png" and "0/3", // respectively; and similarly for the other three quadrants // 2. new zoom level 1 tiles must be created: "0.png" is 3/4 empty, but has a shrunk version of // the old "0.png" (which is the new "0/3.png") in its bottom-right, etc. // 3. a new "base.png" must be created from the new zoom level 1 tiles // move everything at zoom 1 or higher one level deeper // ...first the subdirectories renameFile(outputpath + "/0", outputpath + "/old0"); renameFile(outputpath + "/1", outputpath + "/old1"); renameFile(outputpath + "/2", outputpath + "/old2"); renameFile(outputpath + "/3", outputpath + "/old3"); makePath(outputpath + "/0"); makePath(outputpath + "/1"); makePath(outputpath + "/2"); makePath(outputpath + "/3"); renameFile(outputpath + "/old0", outputpath + "/0/3"); renameFile(outputpath + "/old1", outputpath + "/1/2"); renameFile(outputpath + "/old2", outputpath + "/2/1"); renameFile(outputpath + "/old3", outputpath + "/3/0"); // ...now the zoom 1 files const char* formatExtensions[] = {".png", ".jpeg"}; ImageSettings::Format formats[] = {ImageSettings::Format_PNG, ImageSettings::Format_JPEG}; for (int i = 0; i < 2; ++i) { if (ImageSettings::format == formats[i] || ImageSettings::format == ImageSettings::Format_Both) { const char* format = formatExtensions[i]; renameFile(outputpath + "/0" + format, outputpath + "/0/3" + format); renameFile(outputpath + "/1" + format, outputpath + "/1/2" + format); renameFile(outputpath + "/2" + format, outputpath + "/2/1" + format); renameFile(outputpath + "/3" + format, outputpath + "/3/0" + format); } } // build the new zoom 1 tiles RGBAImage old0img; bool used0 = old0img.readPNG(outputpath + "/0/3.png"); RGBAImage new0img; new0img.create(tileSize, tileSize); if (used0) { reduceHalf(new0img, ImageRect(tileSize/2, tileSize/2, tileSize/2, tileSize/2), old0img); new0img.writeImage(outputpath + "/0"); } RGBAImage old1img; bool used1 = old1img.readPNG(outputpath + "/1/2.png"); RGBAImage new1img; new1img.create(tileSize, tileSize); if (used1) { reduceHalf(new1img, ImageRect(0, tileSize/2, tileSize/2, tileSize/2), old1img); new1img.writeImage(outputpath + "/1"); } RGBAImage old2img; bool used2 = old2img.readPNG(outputpath + "/2/1.png"); RGBAImage new2img; new2img.create(tileSize, tileSize); if (used2) { reduceHalf(new2img, ImageRect(tileSize/2, 0, tileSize/2, tileSize/2), old2img); new2img.writeImage(outputpath + "/2"); } RGBAImage old3img; bool used3 = old3img.readPNG(outputpath + "/3/0.png"); RGBAImage new3img; new3img.create(tileSize, tileSize); if (used3) { reduceHalf(new3img, ImageRect(0, 0, tileSize/2, tileSize/2), old3img); new3img.writeImage(outputpath + "/3"); } // build the new base tile RGBAImage newbase; newbase.create(tileSize, tileSize); if (used0) reduceHalf(newbase, ImageRect(0, 0, tileSize/2, tileSize/2), new0img); if (used1) reduceHalf(newbase, ImageRect(tileSize/2, 0, tileSize/2, tileSize/2), new1img); if (used2) reduceHalf(newbase, ImageRect(0, tileSize/2, tileSize/2, tileSize/2), new2img); if (used3) reduceHalf(newbase, ImageRect(tileSize/2, tileSize/2, tileSize/2, tileSize/2), new3img); newbase.writeImage(outputpath + "/base"); // write new params (with incremented baseZoom) mp.baseZoom++; mp.writeFile(outputpath); // touch all tiles, to prevent browser cache mishaps (since many new tiles will have the same // filename as some old tile, but possibly with an earlier timestamp) if (system((string("find ") + outputpath + " -exec touch {} +").c_str()) < 0) cerr << "Error changing mtimes. Ignoring..." << endl; return true; }
Image* LoadIDSPBuff( byte *buffer ){ byte *buf_p; int columns, rows; byte *pixbuf; int row, column; byte *palette; unsigned char red, green, blue, alphabyte; dspriteheader_t *header; dspritev1_t *pinv1; dspritev2_t *pinv2; dspriteframetype_t *pframetype; int version; int numframes; dspriteframe_t *spriteframe; header = (dspriteheader_t *)buffer; if ( header->ident != IDSPRITEHEADER ) { globalWarningStream() << "WARNING: IDSP file has wrong header\n"; return 0; } version = header->version; if ( version != 1 && version != 2 ) { globalWarningStream() << "WARNING: IDSP file has wrong version number " "(" << version << " should be 1 or 2)\n"; return 0; } // initialise variables depending on the sprite version. switch ( version ) { case 1: pinv1 = (dspritev1_t *)( header + 1 ); numframes = pinv1->numframes; columns = pinv1->width; rows = pinv1->height; pframetype = (dspriteframetype_t *)( pinv1 + 1 ); break; case 2: pinv2 = (dspritev2_t *)( header + 1 ); numframes = pinv2->numframes; columns = pinv2->width; rows = pinv2->height; pframetype = (dspriteframetype_t *)( pinv2 + 1 ); break; default: globalWarningStream() << "WARNING: IDSP file has unsupported version\n"; return 0; } if ( numframes > 1 ) { globalWarningStream() << "WARNING: IDSP file has multiple frames, only the first frame will be used.\n"; } // palette = buffer+mipdatasize+2; // buf_p = buffer+lpMip->offsets[0]; RGBAImage* image = new RGBAImage( columns, rows ); #ifdef DEBUG frametype = spriteframetype_t( pframetype->type ); if ( frametype == SPR_SINGLE ) { globalOutputStream() << "Single Frame\n"; } else if ( frametype == SPR_GROUP ) { globalOutputStream() << "Group of Frames\n"; } else { globalWarningStream() << "Bleh!\n"; // <-- we always get this, wtf! } #endif palette = (byte *)( pframetype + 1 ); spriteframe = (dspriteframe_t *)( palette + ( 256 * 3 ) + 4 ); // what are those 4 extra bytes ? what's missing ? buf_p = (byte *)( spriteframe + 1 ); for ( row = 0; row < rows; row++ ) { pixbuf = image->getRGBAPixels() + row * columns * 4; for ( column = 0; column < columns; column++ ) { int palIndex; palIndex = *buf_p++; red = *( palette + ( palIndex * 3 ) ); green = *( palette + ( palIndex * 3 ) + 1 ); blue = *( palette + ( palIndex * 3 ) + 2 ); // HalfLife engine makes pixels that are BLUE transparent. (RGB = 0x0000FF) // So show them that way in the editor. if ( blue == 0xff && red == 0x00 && green == 0x00 ) { alphabyte = 0xff; //FIXME: backwards? (so sprite models to render correctly) blue = 0x00; // don't set the resulting pixel to blue } else { alphabyte = 0x00; //FIXME: backwards? (so sprite models to render correctly) } *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = alphabyte; } } return image; }
void IsometricTileRenderer::renderTile(const TilePos& tile_pos, RGBAImage& tile) { // some vars, set correct image size int block_size = images->getBlockSize(); tile.setSize(getTileSize(), getTileSize()); // get the maximum count of water blocks // blitted about each over, until they are nearly opaque int max_water = images->getMaxWaterPreblit(); // all visible blocks which are rendered in this tile std::set<RenderBlock> blocks; // iterate over the highest blocks in the tile // we use as tile position tile_pos+tile_offset because the offset means that // we treat the tile position as tile_pos, but it's actually tile_pos+tile_offset for (TileTopBlockIterator it(tile_pos, block_size, tile_width); !it.end(); it.next()) { // water render behavior n1: // are we already in a row of water? bool in_water = false; // water render behavior n2: // water counter, how many water blocks are at the moment in this row? int water = 0; // the render block objects in our current block row std::set<RenderBlock> row_nodes; // then iterate over the blocks, which are on the tile at the same position, // beginning from the highest block for (BlockRowIterator block(it.current); !block.end(); block.next()) { // get current chunk position mc::ChunkPos current_chunk_pos(block.current); // check if current chunk is not null // and if the chunk wasn't replaced in the cache (i.e. position changed) if (current_chunk == nullptr || current_chunk->getPos() != current_chunk_pos) // get chunk if not //if (!state.world->hasChunkSection(current_chunk, block.current.y)) // continue; current_chunk = world->getChunk(current_chunk_pos); if (current_chunk == nullptr) { // here is nothing (= air), // so reset state if we are in water in_water = false; continue; } // get local block position mc::LocalBlockPos local(block.current); // now get block id uint16_t id = current_chunk->getBlockID(local); // air is completely transparent so continue if (id == 0) { in_water = false; continue; } // now get the block data uint16_t data = current_chunk->getBlockData(local); // check if the render mode hides this block if (render_mode->isHidden(block.current, id, data)) continue; bool is_water = (id == 8 || id == 9) && data == 0; if (is_water && !use_preblit_water) { // water render behavior n1: // render only the top sides of the water blocks // and darken the ground with the lighting data // used for lighting rendermode // if we are already in water, skip checking this water block if (is_water && in_water) continue; in_water = is_water; } else if (use_preblit_water) { // water render behavior n2: // render the top side of every water block // have also preblit water blocks to skip redundant alphablitting // no lighting is needed because the 'opaque-water-effect' // is created by blitting the top sides of the water blocks // one above the other if (!is_water) { // if not water, reset the counter water = 0; } else { water++; // when we have enough water in a row // we can stop searching more blocks // and replace the already added render blocks with a preblit water block if (water > max_water) { std::set<RenderBlock>::const_iterator it = row_nodes.begin(); // iterate through the render blocks in this row while (it != row_nodes.end()) { std::set<RenderBlock>::const_iterator current = it++; // check if we have reached the top most water block if (it == row_nodes.end() || (it->id != 8 && it->id != 9)) { RenderBlock top = *current; row_nodes.erase(current); // check for neighbors mc::Block south, west; south = getBlock(top.pos + mc::DIR_SOUTH); west = getBlock(top.pos + mc::DIR_WEST); id = 8; data = OPAQUE_WATER; bool neighbor_south = !south.isFullWater(); if (neighbor_south) // data |= DATA_SOUTH; data |= OPAQUE_WATER_SOUTH; bool neighbor_west = !west.isFullWater(); if (neighbor_west) // data |= DATA_WEST; data |= OPAQUE_WATER_WEST; // get image and replace the old render block with this //top.image = images->getOpaqueWater(neighbor_south, // neighbor_west); top.image = images->getBlock(id, data); // don't forget the render mode render_mode->draw(top.image, top.pos, id, data); row_nodes.insert(top); break; } else { // water render block row_nodes.erase(current); } } break; } } } // check for special data (neighbor related) // get block image, check for transparency, create render block... data = checkNeighbors(block.current, id, data); //if (is_water && (data & DATA_WEST) && (data & DATA_SOUTH)) // continue; RGBAImage image; bool transparent = images->isBlockTransparent(id, data); // check for biome data if (Biome::isBiomeBlock(id, data)) image = images->getBiomeBlock(id, data, getBiomeOfBlock(block.current, current_chunk)); else image = images->getBlock(id, data); RenderBlock node; node.x = it.draw_x; node.y = it.draw_y; node.pos = block.current; node.image = image; node.id = id; node.data = data; // let the render mode do their magic with the block image render_mode->draw(node.image, node.pos, id, data); // insert into current row row_nodes.insert(node); // if this block is not transparent, then break if (!transparent) break; } // iterate through the created render blocks for (std::set<RenderBlock>::const_iterator it = row_nodes.begin(); it != row_nodes.end(); ++it) { std::set<RenderBlock>::const_iterator next = it; next++; // insert render block to if (next == row_nodes.end()) { blocks.insert(*it); } else { // skip unnecessary leaves if (it->id == 18 && next->id == 18 && (next->data & 3) == (it->data & 3)) continue; blocks.insert(*it); } } } // now blit all blocks for (std::set<RenderBlock>::const_iterator it = blocks.begin(); it != blocks.end(); ++it) { tile.alphaBlit(it->image, it->x, it->y); } }