bool CanTypeFitValueOrig(const U value) { const intmax_t bot_t = intmax_t(std::numeric_limits<T>::min() ); const intmax_t bot_u = intmax_t(std::numeric_limits<U>::min() ); const uintmax_t top_t = uintmax_t(std::numeric_limits<T>::max() ); const uintmax_t top_u = uintmax_t(std::numeric_limits<U>::max() ); return !( (bot_t > bot_u && value < static_cast<U> (bot_t)) || (top_t < top_u && value > static_cast<U> (top_t)) ); }
bool Timer::add(Event* e, int id, int64_t cycle, void* param) { const intptr_t key((intptr_t)e); auto ib = m_clients.find(key); if ( ib == m_clients.end() ) { auto res = m_clients.insert(client_cont::value_type(key, event_cont())); if ( res.second == false ) { PWLOGLIB("failed to insert timer event: e:%p id:%d cycle:%jd param:%p", e, id, intmax_t(cycle), param); return false; } event_cont& ec(res.first->second); // 이후 코드는 insert 성공 여부와 상관 없이 반복자가 무효하다. invalidateIterator(); if ( false == ec.insert(event_cont::value_type(id, event_type(param, cycle, s_getNow()))).second ) { PWLOGLIB("failed to insert timer event: e:%p id:%d cycle:%jd param:%p", e, id, intmax_t(cycle), param); m_clients.erase(ib); return false; } //PWTRACE("add new timer event: e:%p type:%s id:%d cycle:%jdms", e, typeid(*e).name(), id, cycle); return true; } auto& ec = ib->second; auto ib_event = ec.find(id); if ( ec.end() == ib_event ) { if ( false == ec.insert(event_cont::value_type(id, event_type(param, cycle, s_getNow()))).second ) { PWLOGLIB("failed to insert timer event: e:%p id:%d cycle:%jd param:%p", e, id, intmax_t(cycle), param); return false; } invalidateIterator(); //PWTRACE("add new timer event: e:%p type:%s id:%d cycle:%jdms", e, typeid(*e).name(), id, cycle); return true; } // 반복자를 건들지 않았으므로 invalidateIterator를 호출하지 않는다. auto& et = ib_event->second; et.param = param; et.cycle = cycle; et.start = s_getNow(); //PWTRACE("add new timer event: e:%p type:%s id:%d cycle:%jdms", e, typeid(*e).name(), id, cycle); return true; }
static void TestPrintSignedMax() { PoisonOutput(); sprintf(output, "%" PRIdMAX, intmax_t(-INTMAX_C(432157943248732))); MOZ_RELEASE_ASSERT(!strcmp(output, "-432157943248732")); PoisonOutput(); sprintf(output, "%" PRIiMAX, intmax_t(INTMAX_C(325719232983))); MOZ_RELEASE_ASSERT(!strcmp(output, "325719232983")); }
/* * gjs_profiler_extract_maps: * * This function will write the mapped section information to the * capture file so that the callgraph builder can generate symbols * from the stack addresses provided. * * Returns: %TRUE if successful; otherwise %FALSE and the profile * should abort. */ static bool gjs_profiler_extract_maps(GjsProfiler *self) { using AutoStrv = std::unique_ptr<char *, decltype(&g_strfreev)>; int64_t now = g_get_monotonic_time() * 1000L; g_assert(((void) "Profiler must be set up before extracting maps", self)); GjsAutoChar path = g_strdup_printf("/proc/%jd/maps", intmax_t(self->pid)); char *content_tmp; size_t len; if (!g_file_get_contents(path, &content_tmp, &len, nullptr)) return false; GjsAutoChar content = content_tmp; AutoStrv lines(g_strsplit(content, "\n", 0), g_strfreev); for (size_t ix = 0; lines.get()[ix]; ix++) { char file[256]; unsigned long start; unsigned long end; unsigned long offset; unsigned long inode; file[sizeof file - 1] = '\0'; int r = sscanf(lines.get()[ix], "%lx-%lx %*15s %lx %*x:%*x %lu %255s", &start, &end, &offset, &inode, file); if (r != 5) continue; if (strcmp("[vdso]", file) == 0) { offset = 0; inode = 0; } if (!sp_capture_writer_add_map(self->capture, now, -1, self->pid, start, end, offset, inode, file)) return false; } return true; }
/** * gjs_profiler_start: * @self: A #GjsProfiler * * As expected, this starts the GjsProfiler. * * This will enable the underlying JS profiler and register a POSIX timer to * deliver SIGPROF on the configured sampling frequency. * * To reduce sampling overhead, #GjsProfiler stashes information about the * profile to be calculated once the profiler has been disabled. Calling * gjs_profiler_stop() will result in that delayed work to be completed. * * You should call gjs_profiler_stop() when the profiler is no longer needed. */ void gjs_profiler_start(GjsProfiler *self) { g_return_if_fail(self); if (self->running) return; #ifdef ENABLE_PROFILER g_return_if_fail(!self->capture); struct sigaction sa = { 0 }; struct sigevent sev = { 0 }; struct itimerspec its = { 0 }; struct itimerspec old_its; GjsAutoChar path = g_strdup(self->filename); if (!path) path = g_strdup_printf("gjs-%jd.syscap", intmax_t(self->pid)); self->capture = sp_capture_writer_new(path, 0); if (!self->capture) { g_warning("Failed to open profile capture"); return; } if (!gjs_profiler_extract_maps(self)) { g_warning("Failed to extract proc maps"); g_clear_pointer(&self->capture, sp_capture_writer_unref); return; } self->stack_depth = 0; /* Setup our signal handler for SIGPROF delivery */ sa.sa_flags = SA_RESTART | SA_SIGINFO; sa.sa_sigaction = gjs_profiler_sigprof; sigemptyset(&sa.sa_mask); if (sigaction(SIGPROF, &sa, nullptr) == -1) { g_warning("Failed to register sigaction handler: %s", g_strerror(errno)); g_clear_pointer(&self->capture, sp_capture_writer_unref); return; } /* * Create our SIGPROF timer * * We want to receive a SIGPROF signal on the JS thread using our * configured sampling frequency. Instead of allowing any thread to be * notified, we set the _tid value to ensure that only our thread gets * delivery of the signal. This feature is generally just for * threading implementations, but it works for us as well and ensures * that the thread is blocked while we capture the stack. */ sev.sigev_notify = SIGEV_THREAD_ID; sev.sigev_signo = SIGPROF; sev._sigev_un._tid = syscall(__NR_gettid); if (timer_create(CLOCK_MONOTONIC, &sev, &self->timer) == -1) { g_warning("Failed to create profiler timer: %s", g_strerror(errno)); g_clear_pointer(&self->capture, sp_capture_writer_unref); return; } /* Calculate sampling interval */ its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = NSEC_PER_SEC / SAMPLES_PER_SEC; its.it_value.tv_sec = 0; its.it_value.tv_nsec = NSEC_PER_SEC / SAMPLES_PER_SEC; /* Now start this timer */ if (timer_settime(self->timer, 0, &its, &old_its) != 0) { g_warning("Failed to enable profiler timer: %s", g_strerror(errno)); timer_delete(self->timer); g_clear_pointer(&self->capture, sp_capture_writer_unref); return; } self->running = true; /* Notify the JS runtime of where to put stack info */ js::SetContextProfilingStack(self->cx, self->stack, &self->stack_depth, G_N_ELEMENTS(self->stack)); /* Start recording stack info */ js::EnableContextProfilingStack(self->cx, true); g_message("Profiler started"); #else /* !ENABLE_PROFILER */ self->running = true; g_message("Profiler is disabled. Recompile with --enable-profiler to use."); #endif /* ENABLE_PROFILER */ }
bool EmberRender(EmberOptions& opt) { #ifdef USECL EmberCLns::OpenCLInfo& info(EmberCLns::OpenCLInfo::Instance());#endif std::cout.imbue(std::locale("")); if (opt.DumpArgs()) cout << opt.GetValues(OPT_USE_RENDER) << endl; if (opt.OpenCLInfo()) { cout << "\nOpenCL Info: " << endl; cout << info.DumpInfo(); return true; } Timing t; bool writeSuccess = false; byte* finalImagep; uint padding; size_t i, channels; size_t strips; size_t iterCount; string filename; string inputPath = GetPath(opt.Input()); ostringstream os; pair<size_t, size_t> p; vector<Ember<T>> embers; vector<byte> finalImage; EmberStats stats; EmberReport emberReport; EmberImageComments comments; XmlToEmber<T> parser; EmberToXml<T> emberToXml; vector<QTIsaac<ISAAC_SIZE, ISAAC_INT>> randVec; const vector<pair<size_t, size_t>> devices = Devices(opt.Devices()); unique_ptr<RenderProgress<T>> progress(new RenderProgress<T>()); unique_ptr<Renderer<T, float>> renderer(CreateRenderer<T>(opt.EmberCL() ? OPENCL_RENDERER : CPU_RENDERER, devices, false, 0, emberReport)); vector<string> errorReport = emberReport.ErrorReport(); if (!errorReport.empty()) emberReport.DumpErrorReport(); if (!renderer.get()) { cout << "Renderer creation failed, exiting." << endl; return false; } if (opt.EmberCL() && renderer->RendererType() != OPENCL_RENDERER)//OpenCL init failed, so fall back to CPU. opt.EmberCL(false); if (!InitPaletteList<T>(opt.PalettePath())) return false; if (!ParseEmberFile(parser, opt.Input(), embers)) return false; if (!opt.EmberCL()) { if (opt.ThreadCount() == 0) { cout << "Using " << Timing::ProcessorCount() << " automatically detected threads." << endl; opt.ThreadCount(Timing::ProcessorCount()); } else { cout << "Using " << opt.ThreadCount() << " manually specified threads." << endl; } renderer->ThreadCount(opt.ThreadCount(), opt.IsaacSeed() != "" ? opt.IsaacSeed().c_str() : nullptr); } else { cout << "Using OpenCL to render." << endl; if (opt.Verbose()) { for (auto& device : devices) { cout << "Platform: " << info.PlatformName(device.first) << endl; cout << "Device: " << info.DeviceName(device.first, device.second) << endl; } } if (opt.ThreadCount() > 1) cout << "Cannot specify threads with OpenCL, using 1 thread." << endl; opt.ThreadCount(1); renderer->ThreadCount(opt.ThreadCount(), opt.IsaacSeed() != "" ? opt.IsaacSeed().c_str() : nullptr); if (opt.BitsPerChannel() != 8) { cout << "Bits per channel cannot be anything other than 8 with OpenCL, setting to 8." << endl; opt.BitsPerChannel(8); } } if (opt.Format() != "jpg" && opt.Format() != "png" && opt.Format() != "ppm" && opt.Format() != "bmp") { cout << "Format must be jpg, png, ppm, or bmp not " << opt.Format() << ". Setting to jpg." << endl; } channels = opt.Format() == "png" ? 4 : 3; if (opt.BitsPerChannel() == 16 && opt.Format() != "png") { cout << "Support for 16 bits per channel images is only present for the png format. Setting to 8." << endl; opt.BitsPerChannel(8); } else if (opt.BitsPerChannel() != 8 && opt.BitsPerChannel() != 16) { cout << "Unexpected bits per channel specified " << opt.BitsPerChannel() << ". Setting to 8." << endl; opt.BitsPerChannel(8); } if (opt.InsertPalette() && opt.BitsPerChannel() != 8) { cout << "Inserting palette only supported with 8 bits per channel, insertion will not take place." << endl; opt.InsertPalette(false); } if (opt.AspectRatio() < 0) { cout << "Invalid pixel aspect ratio " << opt.AspectRatio() << endl << ". Must be positive, setting to 1." << endl; opt.AspectRatio(1); } if (!opt.Out().empty() && (embers.size() > 1)) { cout << "Single output file " << opt.Out() << " specified for multiple images. Changing to use prefix of badname-changethis instead. Always specify prefixes when reading a file with multiple embers." << endl; opt.Out(""); opt.Prefix("badname-changethis"); } //Final setup steps before running. os.imbue(std::locale("")); padding = uint(std::log10(double(embers.size()))) + 1; renderer->EarlyClip(opt.EarlyClip()); renderer->YAxisUp(opt.YAxisUp()); renderer->LockAccum(opt.LockAccum()); renderer->InsertPalette(opt.InsertPalette()); renderer->PixelAspectRatio(T(opt.AspectRatio())); renderer->Transparency(opt.Transparency()); renderer->NumChannels(channels); renderer->BytesPerChannel(opt.BitsPerChannel() / 8); renderer->Priority(eThreadPriority(Clamp<intmax_t>(intmax_t(opt.Priority()), intmax_t(eThreadPriority::LOWEST), intmax_t(eThreadPriority::HIGHEST)))); renderer->Callback(opt.DoProgress() ? progress.get() : nullptr); for (i = 0; i < embers.size(); i++) { if (opt.Verbose() && embers.size() > 1) cout << "\nFlame = " << i + 1 << "/" << embers.size() << endl; else if (embers.size() > 1) VerbosePrint(endl); if (opt.Supersample() > 0) embers[i].m_Supersample = opt.Supersample(); if (opt.SubBatchSize() != DEFAULT_SBS) embers[i].m_SubBatchSize = opt.SubBatchSize(); embers[i].m_TemporalSamples = 1;//Force temporal samples to 1 for render. embers[i].m_Quality *= T(opt.QualityScale()); embers[i].m_FinalRasW = size_t(T(embers[i].m_FinalRasW) * opt.SizeScale()); embers[i].m_FinalRasH = size_t(T(embers[i].m_FinalRasH) * opt.SizeScale()); embers[i].m_PixelsPerUnit *= T(opt.SizeScale()); if (embers[i].m_FinalRasW == 0 || embers[i].m_FinalRasH == 0) { cout << "Output image " << i << " has dimension 0: " << embers[i].m_FinalRasW << ", " << embers[i].m_FinalRasH << ". Setting to 1920 x 1080." << endl; embers[i].m_FinalRasW = 1920; embers[i].m_FinalRasH = 1080; } //Cast to double in case the value exceeds 2^32. double imageMem = double(renderer->NumChannels()) * double(embers[i].m_FinalRasW) * double(embers[i].m_FinalRasH) * double(renderer->BytesPerChannel()); double maxMem = pow(2.0, double((sizeof(void*) * 8) - 1)); if (imageMem > maxMem)//Ensure the max amount of memory for a process is not exceeded. { cout << "Image " << i << " size > " << maxMem << ". Setting to 1920 x 1080." << endl; embers[i].m_FinalRasW = 1920; embers[i].m_FinalRasH = 1080; } stats.Clear(); renderer->SetEmber(embers[i]); renderer->PrepFinalAccumVector(finalImage);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run(). if (opt.Strips() > 1) { strips = opt.Strips(); } else { p = renderer->MemoryRequired(1, true, false);//No threaded write for render, only for animate. strips = CalcStrips(double(p.second), double(renderer->MemoryAvailable()), opt.UseMem()); if (strips > 1) VerbosePrint("Setting strips to " << strips << " with specified memory usage of " << opt.UseMem()); } strips = VerifyStrips(embers[i].m_FinalRasH, strips, [&](const string& s) { cout << s << endl; },//Greater than height. [&](const string& s) { cout << s << endl; },//Mod height != 0. [&](const string& s) { cout << s << endl; });//Final strips value to be set. //For testing incremental renderer. //int sb = 1; //bool resume = false, success = false; //do //{ // success = renderer->Run(finalImage, 0, sb, false/*resume == false*/) == RENDER_OK; // sb++; // resume = true; //} //while (success && renderer->ProcessState() != ACCUM_DONE); StripsRender<T>(renderer.get(), embers[i], finalImage, 0, strips, opt.YAxisUp(), [&](size_t strip)//Pre strip. { if (opt.Verbose() && (strips > 1) && strip > 0) cout << endl; if (strips > 1) VerbosePrint("Strip = " << (strip + 1) << "/" << strips); }, [&](size_t strip)//Post strip. { progress->Clear(); stats += renderer->Stats(); }, [&](size_t strip)//Error. { cout << "Error: image rendering failed, skipping to next image." << endl; renderer->DumpErrorReport();//Something went wrong, print errors. }, //Final strip. //Original wrote every strip as a full image which could be very slow with many large images. //Only write once all strips for this image are finished. [&](Ember<T>& finalEmber) { if (!opt.Out().empty()) { filename = opt.Out(); } else if (opt.NameEnable() && !finalEmber.m_Name.empty()) { filename = inputPath + opt.Prefix() + finalEmber.m_Name + opt.Suffix() + "." + opt.Format(); } else { ostringstream fnstream; fnstream << inputPath << opt.Prefix() << setfill('0') << setw(padding) << i << opt.Suffix() << "." << opt.Format(); filename = fnstream.str(); } //TotalIterCount() is actually using ScaledQuality() which does not get reset upon ember assignment, //so it ends up using the correct value for quality * strips. iterCount = renderer->TotalIterCount(1); comments = renderer->ImageComments(stats, opt.PrintEditDepth(), opt.IntPalette(), opt.HexPalette()); os.str(""); os << comments.m_NumIters << " / " << iterCount << " (" << std::fixed << std::setprecision(2) << ((double(stats.m_Iters) / double(iterCount)) * 100) << "%)"; VerbosePrint("\nIters ran/requested: " + os.str()); if (!opt.EmberCL()) VerbosePrint("Bad values: " << stats.m_Badvals); VerbosePrint("Render time: " + t.Format(stats.m_RenderMs)); VerbosePrint("Pure iter time: " + t.Format(stats.m_IterMs)); VerbosePrint("Iters/sec: " << size_t(stats.m_Iters / (stats.m_IterMs / 1000.0)) << endl); VerbosePrint("Writing " + filename); if ((opt.Format() == "jpg" || opt.Format() == "bmp") && renderer->NumChannels() == 4) RgbaToRgb(finalImage, finalImage, renderer->FinalRasW(), renderer->FinalRasH()); finalImagep = finalImage.data(); writeSuccess = false; if (opt.Format() == "png") writeSuccess = WritePng(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, opt.BitsPerChannel() / 8, opt.PngComments(), comments, opt.Id(), opt.Url(), opt.Nick()); else if (opt.Format() == "jpg") writeSuccess = WriteJpeg(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, int(opt.JpegQuality()), opt.JpegComments(), comments, opt.Id(), opt.Url(), opt.Nick()); else if (opt.Format() == "ppm") writeSuccess = WritePpm(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH); else if (opt.Format() == "bmp") writeSuccess = WriteBmp(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH); if (!writeSuccess) cout << "Error writing " << filename << endl; }); if (opt.EmberCL() && opt.DumpKernel()) { if (auto rendererCL = dynamic_cast<RendererCL<T, float>*>(renderer.get())) { cout << "Iteration kernel:\n" << rendererCL->IterKernel() << "\n\n" << "Density filter kernel:\n" << rendererCL->DEKernel() << "\n\n" << "Final accumulation kernel:\n" << rendererCL->FinalAccumKernel() << endl; } } VerbosePrint("Done."); } t.Toc("\nFinished in: ", true); return true; }
void MsgChannel::eventReadData(size_t len) { //PWSHOWMETHOD(); do { switch(m_recv_state) { case RecvState::START: { //PWTRACE("RecvState::START: %p", this); m_recv.clear(); m_recv_bodylen = 0; m_dest_bodylen = 0; m_recv_state = RecvState::HEADER; }// Fall to RecvState::HEADER /* no break */ case RecvState::HEADER: { //PWTRACE("RecvState::HEADER: %p", this); if ( m_rbuf->getReadableSize() < size_t(MsgPacket::limit_type::MIN_HEADER_SIZE) ) { //PWTRACE("not yet: min:%jd", intmax_t(MsgPacket::limit_type::MIN_HEADER_SIZE) ); // 아직 헤더 크기를 받지 못함. return; } IoBuffer::blob_type b; m_rbuf->grabRead(b); const char* eol(PWStr::findLine(b.buf, b.size)); if ( nullptr == eol ) { if ( b.size > size_t(MsgPacket::limit_type::MAX_HEADER_SIZE) ) { PWLOGLIB("too long header: input:%jd", intmax_t(b.size)); m_recv_state = RecvState::ERROR; goto PROC_ERROR; } return; } const size_t cplen(eol - b.buf); if ( not m_recv.setHeader(b.buf, cplen) ) { // invalid packet std::string out; PWEnc::encodeHex(out, b.buf, cplen); PWLOGLIB("invalid packet: ch:%p chtype:%s header:%s", this, typeid(*this).name(), out.c_str()); m_rbuf->moveRead(cplen+2); m_recv_state = RecvState::ERROR; goto PROC_ERROR; } m_rbuf->moveRead(cplen+2); if ( (m_dest_bodylen = m_recv.getBodySize()) > 0 ) { m_recv_state = RecvState::BODY; } else { //m_recv_state = RecvState::DONE; //PWTRACE("RecvState::DONE: %p", this); //eventReadPacket(m_recv, m_recv.m_body.buf, m_recv.m_body.size); hookReadPacket(m_recv, m_recv.m_body.buf, m_recv.m_body.size); m_recv_state = RecvState::START; break; } }// Fall to RecvState::BODY /* no break */ case RecvState::BODY: { //PWTRACE("RecvState::BODY: %p", this); blob_type& body(m_recv.m_body); if ( body.buf == nullptr ) { if ( not body.allocate(m_dest_bodylen) ) { PWLOGLIB("not enough memory"); m_recv_state = RecvState::ERROR; break; } }// if (body.buf) IoBuffer::blob_type b; m_rbuf->grabRead(b); const size_t cplen(std::min(b.size, (m_dest_bodylen - m_recv_bodylen)) ); if ( cplen > 0 ) { char* p(const_cast<char*>(body.buf) + m_recv_bodylen); ::memcpy(p, b.buf, cplen); m_recv_bodylen += cplen; m_rbuf->moveRead(cplen); } // 아직 더 받아야할 패킷이 있으면 반환한다. if ( m_recv_bodylen not_eq m_dest_bodylen ) { return; } m_recv_state = RecvState::DONE; } /* no break */ case RecvState::DONE: { //PWTRACE("RecvState::DONE: %p", this); hookReadPacket(m_recv, m_recv.m_body.buf, m_recv.m_body.size); m_recv_state = RecvState::START; break; }// case RecvState::DONE case RecvState::ERROR: { PROC_ERROR: //PWTRACE("RecvState::ERROR: %p", this); eventError(Error::INVALID_PACKET, 0); m_recv_state = RecvState::START; if ( isInstDeleteOrExpired() ) return; } /* no break */ default: { PWLOGLIB("Invalid state: %d", static_cast<int>(m_recv_state)); m_recv_state = RecvState::START; break; } }// switch(m_recv_state) } while ( true ); }
//------------------------------------------------------------------------------ GDChart & GDChart::createChart() { create(width_,height_); bool isIntergerOnlyValues = true; intptr_t i, j, xCount = 0, x, y, x0 = 0, y0 = 0; // calc min max ldouble minValue = DBL_MAX, maxValue = -DBL_MAX; for( i = data_.count() - 1; i >= 0; i-- ){ j = data_[i].count(); xCount = tmax(xCount,j); const Array<ldouble> & data = data_[i]; for( j = data.count() - 1; j >= 0; j-- ){ volatile intmax_t v = intmax_t(data[j]); volatile ldouble lv = ldouble(v); if( lv != data[j] ) isIntergerOnlyValues = false; minValue = tmin(minValue,data[j]); maxValue = tmax(maxValue,data[j]); } } ldouble yAxis = (maxValue - minValue) / (height_ - topBorder_ - bottomBorder_); intptr_t leftBorderDelta = 0, rightBorderDelta = 0, topBorderDelta = 0, bottomBorderDelta = 0; // clear image fill(0,0,colorAllocate(255,255,255)); // draw lines intptr_t lineColor = colorAllocate(230,230,230); // draw vert grid lines for( j = 0; j < xCount; j++ ){ x = (width_ - leftBorder_ - rightBorder_) * j / (xCount - 1) + leftBorder_; line(x,topBorder_,x,height_ - bottomBorder_,lineColor); } intptr_t yLabelColor = makeColor(ldouble(j),ldouble(j),ldouble(j)); for( y = topBorder_; uintptr_t(y) <= height_ - bottomBorder_; y += fontHeight(font_) * 2 ){ ldouble v = maxValue - (y - topBorder_) * yAxis; // draw horiz grid line line(leftBorder_,y,width_ - rightBorder_,y,lineColor); // draw ylabel utf8::String label; if( isIntergerOnlyValues ){ label = printTraffic(intmax_t(v),true);//utf8::String::print("%"PRIdPTR,intptr_t(v)); } else { label = utf8::String::print("%.2"PRF_LDBL"f",v); } uintptr_t sz = label.size(); x = leftBorder_ - sz * fontWidth(font_); string(GD::font(font_),x,y,label.c_str(),yLabelColor); if( x < 0 && -x > leftBorderDelta ) leftBorderDelta = -x; } // draw data lines for( i = 0; uintptr_t(i) < data_.count(); i++ ){ intptr_t color = makeColor(ldouble(i + 1),ldouble(i + 1),ldouble(i + 1)); const Array<ldouble> & data = data_[i]; for( j = 0; uintptr_t(j) < data.count(); j++ ){ x = (width_ - leftBorder_ - rightBorder_) * j / (xCount - 1) + leftBorder_; y = intptr_t(height_ - topBorder_ - bottomBorder_ - (data[j] - minValue) / yAxis) + topBorder_; if( j > 0 ) line(x0,y0,x,y,color); x0 = x; y0 = y; } } intptr_t xBarSize = 2, yBarSize = 2; intptr_t barColor = colorAllocate(255,0,0); intptr_t xLabelColor = makeColor(ldouble(i + 1),ldouble(i + 1),ldouble(i + 1)); for( i = 0; uintptr_t(i) < data_.count(); i++ ){ const Array<ldouble> & data = data_[i]; for( j = 0; uintptr_t(j) < data.count(); j++ ){ x = (width_ - leftBorder_ - rightBorder_) * j / (xCount - 1) + leftBorder_; y = intptr_t(height_ - topBorder_ - bottomBorder_ - (data[j] - minValue) / yAxis) + topBorder_; // draw bar filledRectangle( tmax(leftBorder_,uintptr_t(x - xBarSize)), tmax(topBorder_,uintptr_t(y - yBarSize)), tmin(width_ - rightBorder_,uintptr_t(x + xBarSize)), tmin(height_ - bottomBorder_,uintptr_t(y + yBarSize)), barColor ); x0 = x; y0 = y; // draw xlabel y = height_ - bottomBorder_ + xBarSize; utf8::String label(utf8::String::print("%"PRIdPTR,intptr_t(j + xlvs_))); string(GD::font(font_),x + xBarSize,y,label.c_str(),xLabelColor); if( y + fontHeight(font_) >= height_ ) bottomBorderDelta = y + fontHeight(font_) - height_ + 1; x = x + xBarSize + fontWidth(font_) * label.size(); if( uintptr_t(x) >= width_ ) rightBorderDelta = x - width_ + 1; } } if( leftBorderDelta != 0 || rightBorderDelta != 0 || topBorderDelta != 0 || bottomBorderDelta != 0 ){ GDChart chart(*this); chart.leftBorder_ += leftBorderDelta; chart.rightBorder_ += rightBorderDelta; chart.topBorder_ += topBorderDelta; chart.bottomBorder_ += bottomBorderDelta; chart.createChart(); xchg(image_,chart.image_); xchg(png_,chart.png_); xchg(pngSize_,chart.pngSize_); } else { gdFree(png_); png_ = pngPtrEx(&pngSize_,9); } return *this; }