//----------------------------------------------------------------------------- // Export a view of the model as an image; we just take a screenshot, by // rendering the view in the usual way and then copying the pixels. //----------------------------------------------------------------------------- void SolveSpace::ExportAsPngTo(char *filename) { int w = (int)SS.GW.width, h = (int)SS.GW.height; // No guarantee that the back buffer contains anything valid right now, // so repaint the scene. And hide the toolbar too. int prevShowToolbar = SS.showToolbar; SS.showToolbar = false; SS.GW.Paint(); SS.showToolbar = prevShowToolbar; FILE *f = fopen(filename, "wb"); if(!f) goto err; png_struct *png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(!png_ptr) goto err; png_info *info_ptr = png_create_info_struct(png_ptr); if(!png_ptr) goto err; if(setjmp(png_jmpbuf(png_ptr))) goto err; png_init_io(png_ptr, f); // glReadPixels wants to align things on 4-boundaries, and there's 3 // bytes per pixel. As long as the row width is divisible by 4, all // works out. w &= ~3; h &= ~3; png_set_IHDR(png_ptr, info_ptr, w, h, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT); png_write_info(png_ptr, info_ptr); // Get the pixel data from the framebuffer BYTE *pixels = (BYTE *)AllocTemporary(3*w*h); BYTE **rowptrs = (BYTE **)AllocTemporary(h*sizeof(BYTE *)); glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixels); int y; for(y = 0; y < h; y++) { // gl puts the origin at lower left, but png puts it top left rowptrs[y] = pixels + ((h - 1) - y)*(3*w); } png_write_image(png_ptr, rowptrs); png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); fclose(f); return; err: Error("Error writing PNG file '%s'", filename); if(f) fclose(f); return; }
SBsp3 *SBsp3::Alloc(void) { return (SBsp3 *)AllocTemporary(sizeof(SBsp3)); }
SBsp2 *SBsp2::Alloc(void) { return (SBsp2 *)AllocTemporary(sizeof(SBsp2)); }
SEdgeLl *SEdgeLl::Alloc(void) { return (SEdgeLl *)AllocTemporary(sizeof(SEdgeLl)); }
//----------------------------------------------------------------------------- // Make a kd-tree of edges. This is used for O(log(n)) implementations of stuff // that would naively be O(n). //----------------------------------------------------------------------------- SKdNodeEdges *SKdNodeEdges::Alloc(void) { return (SKdNodeEdges *)AllocTemporary(sizeof(SKdNodeEdges)); }
//----------------------------------------------------------------------------- // Load a glyph from the .ttf file into memory. Assumes that the .ttf file // is already seeked to the correct location, and writes the result to // glyphs[index] //----------------------------------------------------------------------------- void TtfFont::LoadGlyph(int index) { if(index < 0 || index >= glyphs) return; int i; SWORD contours = GetWORD(); SWORD xMin = GetWORD(); SWORD yMin = GetWORD(); SWORD xMax = GetWORD(); SWORD yMax = GetWORD(); if(useGlyph['A'] == index) { scale = (1024*1024) / yMax; } if(contours > 0) { WORD *endPointsOfContours = (WORD *)AllocTemporary(contours*sizeof(WORD)); for(i = 0; i < contours; i++) { endPointsOfContours[i] = GetWORD(); } WORD totalPts = endPointsOfContours[i-1] + 1; WORD instructionLength = GetWORD(); for(i = 0; i < instructionLength; i++) { // We can ignore the instructions, since we're doing vector // output. (void)GetBYTE(); } BYTE *flags = (BYTE *)AllocTemporary(totalPts*sizeof(BYTE)); SWORD *x = (SWORD *)AllocTemporary(totalPts*sizeof(SWORD)); SWORD *y = (SWORD *)AllocTemporary(totalPts*sizeof(SWORD)); // Flags, that indicate format of the coordinates #define FLAG_ON_CURVE (1 << 0) #define FLAG_DX_IS_BYTE (1 << 1) #define FLAG_DY_IS_BYTE (1 << 2) #define FLAG_REPEAT (1 << 3) #define FLAG_X_IS_SAME (1 << 4) #define FLAG_X_IS_POSITIVE (1 << 4) #define FLAG_Y_IS_SAME (1 << 5) #define FLAG_Y_IS_POSITIVE (1 << 5) for(i = 0; i < totalPts; i++) { flags[i] = GetBYTE(); if(flags[i] & FLAG_REPEAT) { int n = GetBYTE(); BYTE f = flags[i]; int j; for(j = 0; j < n; j++) { i++; if(i >= totalPts) { throw "too many points in glyph"; } flags[i] = f; } } } // x coordinates SWORD xa = 0; for(i = 0; i < totalPts; i++) { if(flags[i] & FLAG_DX_IS_BYTE) { BYTE v = GetBYTE(); if(flags[i] & FLAG_X_IS_POSITIVE) { xa += v; } else { xa -= v; } } else { if(flags[i] & FLAG_X_IS_SAME) { // no change } else { SWORD d = GetWORD(); xa += d; } } x[i] = xa; } // y coordinates SWORD ya = 0; for(i = 0; i < totalPts; i++) { if(flags[i] & FLAG_DY_IS_BYTE) { BYTE v = GetBYTE(); if(flags[i] & FLAG_Y_IS_POSITIVE) { ya += v; } else { ya -= v; } } else { if(flags[i] & FLAG_Y_IS_SAME) { // no change } else { SWORD d = GetWORD(); ya += d; } } y[i] = ya; } Glyph *g = &(glyph[index]); g->pt = (FontPoint *)MemAlloc(totalPts*sizeof(FontPoint)); int contour = 0; for(i = 0; i < totalPts; i++) { g->pt[i].x = x[i]; g->pt[i].y = y[i]; g->pt[i].onCurve = (BYTE)(flags[i] & FLAG_ON_CURVE); if(i == endPointsOfContours[contour]) { g->pt[i].lastInContour = true; contour++; } else { g->pt[i].lastInContour = false; } } g->pts = totalPts; g->xMax = xMax; g->xMin = xMin; } else { // This is a composite glyph, TODO. } }
//----------------------------------------------------------------------------- // Load a TrueType font into memory. We care about the curves that define // the letter shapes, and about the mappings that determine which glyph goes // with which character. //----------------------------------------------------------------------------- bool TtfFont::LoadFontFromFile(bool nameOnly) { if(loaded) return true; int i; fh = fopen(fontFile, "rb"); if(!fh) { return false; } try { // First, load the Offset Table DWORD version = GetDWORD(); WORD numTables = GetWORD(); WORD searchRange = GetWORD(); WORD entrySelector = GetWORD(); WORD rangeShift = GetWORD(); // Now load the Table Directory; our goal in doing this will be to // find the addresses of the tables that we will need. DWORD glyfAddr = -1, glyfLen; DWORD cmapAddr = -1, cmapLen; DWORD headAddr = -1, headLen; DWORD locaAddr = -1, locaLen; DWORD maxpAddr = -1, maxpLen; DWORD nameAddr = -1, nameLen; DWORD hmtxAddr = -1, hmtxLen; DWORD hheaAddr = -1, hheaLen; for(i = 0; i < numTables; i++) { char tag[5] = "xxxx"; tag[0] = GetBYTE(); tag[1] = GetBYTE(); tag[2] = GetBYTE(); tag[3] = GetBYTE(); DWORD checksum = GetDWORD(); DWORD offset = GetDWORD(); DWORD length = GetDWORD(); if(strcmp(tag, "glyf")==0) { glyfAddr = offset; glyfLen = length; } else if(strcmp(tag, "cmap")==0) { cmapAddr = offset; cmapLen = length; } else if(strcmp(tag, "head")==0) { headAddr = offset; headLen = length; } else if(strcmp(tag, "loca")==0) { locaAddr = offset; locaLen = length; } else if(strcmp(tag, "maxp")==0) { maxpAddr = offset; maxpLen = length; } else if(strcmp(tag, "name")==0) { nameAddr = offset; nameLen = length; } else if(strcmp(tag, "hhea")==0) { hheaAddr = offset; hheaLen = length; } else if(strcmp(tag, "hmtx")==0) { hmtxAddr = offset; hmtxLen = length; } } if(glyfAddr == -1 || cmapAddr == -1 || headAddr == -1 || locaAddr == -1 || maxpAddr == -1 || hmtxAddr == -1 || nameAddr == -1 || hheaAddr == -1) { throw "missing table addr"; } // Load the name table. This gives us display names for the font, which // we need when we're giving the user a list to choose from. fseek(fh, nameAddr, SEEK_SET); WORD nameFormat = GetWORD(); WORD nameCount = GetWORD(); WORD nameStringOffset = GetWORD(); // And now we're at the name records. Go through those till we find // one that we want. int displayNameOffset, displayNameLength; for(i = 0; i < nameCount; i++) { WORD platformID = GetWORD(); WORD encodingID = GetWORD(); WORD languageID = GetWORD(); WORD nameId = GetWORD(); WORD length = GetWORD(); WORD offset = GetWORD(); if(nameId == 4) { displayNameOffset = offset; displayNameLength = length; break; } } if(nameOnly && i >= nameCount) { throw "no name"; } if(nameOnly) { // Find the display name, and store it in the provided buffer. fseek(fh, nameAddr+nameStringOffset+displayNameOffset, SEEK_SET); int c = 0; for(i = 0; i < displayNameLength; i++) { BYTE b = GetBYTE(); if(b && c < (sizeof(name.str) - 2)) { name.str[c++] = b; } } name.str[c++] = '\0'; fclose(fh); return true; } // Load the head table; we need this to determine the format of the // loca table, 16- or 32-bit entries fseek(fh, headAddr, SEEK_SET); DWORD headVersion = GetDWORD(); DWORD headFontRevision = GetDWORD(); DWORD headCheckSumAdj = GetDWORD(); DWORD headMagicNumber = GetDWORD(); WORD headFlags = GetWORD(); WORD headUnitsPerEm = GetWORD(); (void)GetDWORD(); // created time (void)GetDWORD(); (void)GetDWORD(); // modified time (void)GetDWORD(); WORD headXmin = GetWORD(); WORD headYmin = GetWORD(); WORD headXmax = GetWORD(); WORD headYmax = GetWORD(); WORD headMacStyle = GetWORD(); WORD headLowestRecPPEM = GetWORD(); WORD headFontDirectionHint = GetWORD(); WORD headIndexToLocFormat = GetWORD(); WORD headGlyphDataFormat = GetWORD(); if(headMagicNumber != 0x5F0F3CF5) { throw "bad magic number"; } // Load the hhea table, which contains the number of entries in the // horizontal metrics (hmtx) table. fseek(fh, hheaAddr, SEEK_SET); DWORD hheaVersion = GetDWORD(); WORD hheaAscender = GetWORD(); WORD hheaDescender = GetWORD(); WORD hheaLineGap = GetWORD(); WORD hheaAdvanceWidthMax = GetWORD(); WORD hheaMinLsb = GetWORD(); WORD hheaMinRsb = GetWORD(); WORD hheaXMaxExtent = GetWORD(); WORD hheaCaretSlopeRise = GetWORD(); WORD hheaCaretSlopeRun = GetWORD(); WORD hheaCaretOffset = GetWORD(); (void)GetWORD(); (void)GetWORD(); (void)GetWORD(); (void)GetWORD(); WORD hheaMetricDataFormat = GetWORD(); WORD hheaNumberOfMetrics = GetWORD(); // Load the maxp table, which determines (among other things) the number // of glyphs in the font fseek(fh, maxpAddr, SEEK_SET); DWORD maxpVersion = GetDWORD(); WORD maxpNumGlyphs = GetWORD(); WORD maxpMaxPoints = GetWORD(); WORD maxpMaxContours = GetWORD(); WORD maxpMaxComponentPoints = GetWORD(); WORD maxpMaxComponentContours = GetWORD(); WORD maxpMaxZones = GetWORD(); WORD maxpMaxTwilightPoints = GetWORD(); WORD maxpMaxStorage = GetWORD(); WORD maxpMaxFunctionDefs = GetWORD(); WORD maxpMaxInstructionDefs = GetWORD(); WORD maxpMaxStackElements = GetWORD(); WORD maxpMaxSizeOfInstructions = GetWORD(); WORD maxpMaxComponentElements = GetWORD(); WORD maxpMaxComponentDepth = GetWORD(); glyphs = maxpNumGlyphs; glyph = (Glyph *)MemAlloc(glyphs*sizeof(glyph[0])); // Load the hmtx table, which gives the horizontal metrics (spacing // and advance width) of the font. fseek(fh, hmtxAddr, SEEK_SET); WORD hmtxAdvanceWidth; SWORD hmtxLsb; for(i = 0; i < min(glyphs, hheaNumberOfMetrics); i++) { hmtxAdvanceWidth = GetWORD(); hmtxLsb = (SWORD)GetWORD(); glyph[i].leftSideBearing = hmtxLsb; glyph[i].advanceWidth = hmtxAdvanceWidth; } // The last entry in the table applies to all subsequent glyphs also. for(; i < glyphs; i++) { glyph[i].leftSideBearing = hmtxLsb; glyph[i].advanceWidth = hmtxAdvanceWidth; } // Load the cmap table, which determines the mapping of characters to // glyphs. fseek(fh, cmapAddr, SEEK_SET); DWORD usedTableAddr = -1; WORD cmapVersion = GetWORD(); WORD cmapTableCount = GetWORD(); for(i = 0; i < cmapTableCount; i++) { WORD platformId = GetWORD(); WORD encodingId = GetWORD(); DWORD offset = GetDWORD(); if(platformId == 3 && encodingId == 1) { // The Windows Unicode mapping is our preference usedTableAddr = cmapAddr + offset; } } if(usedTableAddr == -1) { throw "no used table addr"; } // So we can load the desired subtable; in this case, Windows Unicode, // which is us. fseek(fh, usedTableAddr, SEEK_SET); WORD mapFormat = GetWORD(); WORD mapLength = GetWORD(); WORD mapVersion = GetWORD(); WORD mapSegCountX2 = GetWORD(); WORD mapSearchRange = GetWORD(); WORD mapEntrySelector = GetWORD(); WORD mapRangeShift = GetWORD(); if(mapFormat != 4) { // Required to use format 4 per spec throw "not format 4"; } int segCount = mapSegCountX2 / 2; WORD *endChar = (WORD *)AllocTemporary(segCount*sizeof(WORD)); WORD *startChar = (WORD *)AllocTemporary(segCount*sizeof(WORD)); WORD *idDelta = (WORD *)AllocTemporary(segCount*sizeof(WORD)); WORD *idRangeOffset = (WORD *)AllocTemporary(segCount*sizeof(WORD)); DWORD *filePos = (DWORD *)AllocTemporary(segCount*sizeof(DWORD)); for(i = 0; i < segCount; i++) { endChar[i] = GetWORD(); } WORD mapReservedPad = GetWORD(); for(i = 0; i < segCount; i++) { startChar[i] = GetWORD(); } for(i = 0; i < segCount; i++) { idDelta[i] = GetWORD(); } for(i = 0; i < segCount; i++) { filePos[i] = ftell(fh); idRangeOffset[i] = GetWORD(); } // So first, null out the glyph table in our in-memory representation // of the font; any character for which cmap does not provide a glyph // corresponds to -1 for(i = 0; i < arraylen(useGlyph); i++) { useGlyph[i] = 0; } for(i = 0; i < segCount; i++) { WORD v = idDelta[i]; if(idRangeOffset[i] == 0) { int j; for(j = startChar[i]; j <= endChar[i]; j++) { if(j > 0 && j < arraylen(useGlyph)) { // Don't create a reference to a glyph that we won't // store because it's bigger than the table. if((WORD)(j + v) < glyphs) { // Arithmetic is modulo 2^16 useGlyph[j] = (WORD)(j + v); } } } } else { int j; for(j = startChar[i]; j <= endChar[i]; j++) { if(j > 0 && j < arraylen(useGlyph)) { int fp = filePos[i]; fp += (j - startChar[i])*sizeof(WORD); fp += idRangeOffset[i]; fseek(fh, fp, SEEK_SET); useGlyph[j] = GetWORD(); } } } } // Load the loca table. This contains the offsets of each glyph, // relative to the beginning of the glyf table. fseek(fh, locaAddr, SEEK_SET); DWORD *glyphOffsets = (DWORD *)AllocTemporary(glyphs*sizeof(DWORD)); for(i = 0; i < glyphs; i++) { if(headIndexToLocFormat == 1) { // long offsets, 32 bits glyphOffsets[i] = GetDWORD(); } else if(headIndexToLocFormat == 0) { // short offsets, 16 bits but divided by 2 glyphOffsets[i] = GetWORD()*2; } else { throw "bad headIndexToLocFormat"; } } scale = 1024; // Load the glyf table. This contains the actual representations of the // letter forms, as piecewise linear or quadratic outlines. for(i = 0; i < glyphs; i++) { fseek(fh, glyfAddr + glyphOffsets[i], SEEK_SET); LoadGlyph(i); } } catch (char *s) { dbp("failed: '%s'", s); fclose(fh); return false; } fclose(fh); loaded = true; return true; }