pelib  2.0.0
src/TetrisSchedule.cpp
Go to the documentation of this file.
00001 /*
00002  Copyright 2015 Nicolas Melot
00003 
00004  This file is part of Pelib.
00005 
00006  Pelib is free software: you can redistribute it and/or modify
00007  it under the terms of the GNU General Public License as published by
00008  the Free Software Foundation, either version 3 of the License, or
00009  (at your option) any later version.
00010 
00011  Pelib is distributed in the hope that it will be useful,
00012  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014  GNU General Public License for more details.
00015 
00016  You should have received a copy of the GNU General Public License
00017  along with Pelib. If not, see <http://www.gnu.org/licenses/>.
00018 */
00019 
00020 #include <libxml++/libxml++.h>
00021 #include <exception>
00022 #include <vector>
00023 #include <sstream>
00024 #include <cstdlib>
00025 #include <string>
00026 
00027 extern "C"{
00028 #include <igraph.h>
00029 }
00030 #include <cairomm/context.h>
00031 #include <cairomm/surface.h>
00032 #include <sigc++/signal.h>
00033 #include <cmath>
00034 
00035 #include <pelib/Schedule.hpp>
00036 #include <pelib/TetrisSchedule.hpp>
00037 
00038 #include <pelib/Scalar.hpp>
00039 #include <pelib/Vector.hpp>
00040 #include <pelib/Matrix.hpp>
00041 #include <pelib/Set.hpp>
00042 #include <pelib/Task.hpp>
00043 #include <pelib/Taskgraph.hpp>
00044 
00045 #include <pelib/CastException.hpp>
00046 #include <pelib/ParseException.hpp>
00047 
00048 #ifdef debug
00049 #undef debug
00050 #endif
00051 
00052 #ifndef debug
00053 #define debug(expr) cerr << "[" << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << "] " << #expr << " = \"" << (expr) << "\"." << endl;
00054 #endif
00055 
00056 using namespace pelib;
00057 using namespace std;
00058 using namespace Cairo;
00059 
00060 static uint32_t
00061 shiftLeft(uint32_t value, uint32_t shift)
00062 {
00063         return value << shift;
00064 }
00065 
00066 static uint32_t
00067 shiftRight(uint32_t value, uint32_t shift)
00068 {
00069         return value >> shift;
00070 }
00071 
00072 static unsigned char
00073 decode(uint32_t value, uint32_t position)
00074 {
00075         return shiftRight(value & shiftLeft(255, position * 8), position * 8);
00076 }
00077 
00078 static uint32_t
00079 encode(uint32_t base, unsigned char value, uint32_t position)
00080 {
00081         return base - shiftLeft(decode(base, position), position * 8) + shiftLeft(value, position * 8);
00082 }
00083 
00084 static
00085 uint32_t
00086 HSLtoRGB(uint32_t hsl, double *CC)
00087 {
00088         double H = decode(hsl, 3);
00089         double S = decode(hsl, 2);
00090         double L = decode(hsl, 1);
00091         double A = decode(hsl, 0);
00092 
00093         H = H / 255 * 6;
00094         H = fmod(H, 2);
00095         S = S / 255;
00096         L = L / 255;
00097         
00098         double R, G, B, C;
00099         if(CC != NULL)
00100         {
00101                 C = *CC;
00102         }
00103         else
00104         {
00105                 C = (1 - abs(2 * L  - 1)) * S;
00106         }
00107         double X =  C * (1 - abs(H - 1));
00108 
00109         if(0 <= H && H < 1)
00110         {
00111                 R = C;
00112                 G = X;
00113                 B = 0;
00114         }
00115         else if(1 <= H && H < 2)
00116         {
00117                 R = X;
00118                 G = C;
00119                 B = 0;
00120         }
00121         else if(2 <= H && H < 3)
00122         {
00123                 R = 0;
00124                 G = C;
00125                 B = X;
00126         }
00127         else if(3 <= H && H < 4)
00128         {
00129                 R = 0;
00130                 G = X;
00131                 B = C;
00132         }
00133         else if(4 <= H && H < 5)
00134         {
00135                 R = X;
00136                 G = 0;
00137                 B = C;
00138         }
00139         else if(5 <= H && H < 6)
00140         {
00141                 R = C;
00142                 G = 0;
00143                 B = X;
00144         }
00145         else
00146         {
00147                 R = G = B = 0;
00148         }
00149 
00150         //double m = L - C / 2;
00151         double m = L - (0.3 * R + 0.59 * G + 0.11 * B);
00152 
00153         R = (R + m) * 255;
00154         G = (G + m) * 255;
00155         B = (B + m) * 255;
00156 
00157         // Round RGB values to closest integer by truncating value + 0.5
00158         uint32_t rgb = A + encode(0, R + 0.5, 3) + encode(0, G + 0.5, 2) + encode(0, B + 0.5, 1);
00159 
00160         return rgb;
00161 }
00162 
00163 // Give HSLtoRGB the output value C if you want to convert back the color to RGB
00164 static
00165 uint32_t
00166 RGBtoHSL(uint32_t rgb, double &C)
00167 {
00168         double R = decode(rgb, 3);
00169         double G = decode(rgb, 2);
00170         double B = decode(rgb, 1);
00171         double A = decode(rgb, 0);
00172 
00173         R = R / 255;
00174         G = G / 255;
00175         B = B / 255;
00176 
00177         double M = R > G ? R > B ? R : B : G > B ? G : B;
00178         double m = R < G ? R < B ? R : B : G < B ? G : B;
00179         C = M - m;
00180 
00181         double H = 0;
00182         if(M == R)
00183         {
00184                 H = fmod((G - B) / C, 6);
00185         }
00186         else if(M == G)
00187         {
00188                 H = ((B - R) / C) + 2;
00189         }
00190         else if(M == B)
00191         {
00192                 H = ((R - G) / C) + 4;
00193         }
00194         else
00195         {
00196                 // Cannot happen
00197         }
00198         H = 60 * H;
00199 
00200         // HSL
00201         //double L = (M + m) / 2;
00202         //double S = C == 0 ? 0 : C / (1 - abs(2 * L - 1));
00203 
00204         // Rec 601 NTSC
00205         double L = 0.3 * R + 0.59 * G + 0.11 * B;
00206         double S = 1 - m / ((R + G + B) / 3);
00207 
00208         uint32_t hsl = A + encode(0, H / 360 * 255 + 0.5, 3) + encode(0, S * 255 + 0.5, 2) + encode(0, L * 255 + 0.5, 1);
00209 
00210         return hsl;
00211 }
00212 
00213 static
00214 map<float, uint32_t>
00215 makeGradient(vector<uint32_t> colors, set<float> values)
00216 {
00217         map<float, uint32_t> gradient;
00218         float minValue = *values.begin();
00219         float maxValue = *values.rbegin();
00220 
00221         for(set<float>::iterator i = values.begin(); i != values.end(); i++)
00222         {
00223                 double C;
00224                 float value = *i;
00225                 float position = (value - minValue) / (maxValue - minValue) * (colors.size() - 1);
00226                 size_t minIndex = (size_t)floor(position);
00227                 size_t maxIndex = (size_t)ceil(position);
00228                 position = (position - minIndex);
00229                 vector<uint32_t>::iterator minIter = colors.begin();
00230                 std::advance(minIter, minIndex);
00231                 uint32_t minColor = *minIter;
00232                 unsigned char minRed = decode(minColor, 3);
00233                 unsigned char minGreen = decode(minColor, 2);
00234                 unsigned char minBlue = decode(minColor, 1);
00235                 unsigned char minAlpha = decode(minColor, 0);
00236                 unsigned char minLuminosity = decode(RGBtoHSL(minColor, C), 2);
00237 
00238                 vector<uint32_t>::iterator maxIter = colors.begin();
00239                 std::advance(maxIter, maxIndex);
00240                 uint32_t maxColor = *maxIter;
00241                 unsigned char maxRed = decode(maxColor, 3);
00242                 unsigned char maxGreen = decode(maxColor, 2);
00243                 unsigned char maxBlue = decode(maxColor, 1);
00244                 unsigned char maxAlpha = decode(maxColor, 0);
00245                 unsigned char maxLuminosity = decode(RGBtoHSL(maxColor, C), 2);
00246 
00247                 unsigned char valueRed = minRed * (1 - position) + maxRed * position;
00248                 unsigned char valueGreen = minGreen * (1 - position) + maxGreen * position;
00249                 unsigned char valueBlue = minBlue * (1 - position) + maxBlue * position;
00250                 unsigned char valueAlpha = minAlpha * (1 - position) + maxAlpha * position;
00251                 unsigned char valueLuminosity = minLuminosity * (1 - position) + maxLuminosity * position;
00252                 uint32_t valueColor = encode(0, valueRed, 3) + encode(0, valueGreen, 2) + encode(0, valueBlue, 1) + encode(0, valueAlpha, 0);
00253                 valueColor = RGBtoHSL(valueColor, C);
00254                 valueColor = HSLtoRGB(encode(valueColor, valueLuminosity, 2), &C);
00255 
00256                 gradient.insert(pair<float, int>(value, valueColor));
00257         }
00258 
00259         return gradient;
00260 }
00261 
00262 float
00263 TetrisSchedule::defaultRatio()
00264 {
00265         return 4.0 / 3;
00266 }
00267 
00268 float
00269 TetrisSchedule::defaultStrokeSize()
00270 {
00271         return 1;
00272 }
00273 
00274 bool
00275 TetrisSchedule::defaultFrequencyLegend()
00276 {
00277         return true;
00278 }
00279 
00280 vector<uint32_t>
00281 TetrisSchedule::defaultFrequencyColors()
00282 {
00283         double C;
00284         vector<uint32_t> colors;
00285         uint32_t darkRed = encode(0, 84, 3) + encode(0, 0, 2) + encode(0, 0, 1) + 255;
00286         RGBtoHSL(darkRed, C);
00287         uint32_t lightYellow = encode(0, 255, 3) + encode(0, 250, 2) + encode(0, 165, 1) + 255;
00288         RGBtoHSL(lightYellow, C);
00289         colors.push_back(lightYellow);
00290         colors.push_back(darkRed);
00291 
00292         return colors;
00293 }
00294 
00295 bool
00296 TetrisSchedule::defaultTaskLabel()
00297 {
00298         return false;
00299 }
00300 
00301 bool
00302 TetrisSchedule::defaultTaskId()
00303 {
00304         return true;
00305 }
00306 
00307 TetrisSchedule::TetrisSchedule()
00308 {
00309         this->ratio = defaultRatio();
00310         this->showFrequencies = defaultFrequencyLegend();
00311         this->showTaskId = defaultTaskId();
00312         this->useTaskName = defaultTaskLabel();
00313         this->colors = defaultFrequencyColors();
00314         this->strokeSize = defaultStrokeSize();
00315 }
00316 
00317 TetrisSchedule::TetrisSchedule(float ratio, bool showFrequencies, bool showTaskId, bool useTaskName, vector<uint32_t> frequency_colors, float strokeSize)
00318 {
00319         this->ratio = ratio;
00320         this->showFrequencies = showFrequencies;
00321         this->colors = frequency_colors;
00322         this->showTaskId = showTaskId;
00323         this->useTaskName = useTaskName;
00324         this->strokeSize = strokeSize;
00325 }
00326 
00327 TetrisSchedule::~TetrisSchedule()
00328 {
00329         // Do nothing
00330 }
00331 
00332 void
00333 TetrisSchedule::dump(ostream& os, const Schedule &data, const Taskgraph &tg, const Platform &pt) const
00334 {
00335         dump(os, &data, &tg, &pt);
00336 }
00337 
00338 // sigc callback class initialized with an output stream
00339 class Slot: public sigc::mem_functor2<ErrorStatus, Slot, const unsigned char*, uint32_t>
00340 {
00341         public:
00342                 Slot(ostream &out): sigc::mem_functor2<ErrorStatus, Slot, const unsigned char* , uint32_t>(sigc::mem_functor2<ErrorStatus, Slot, const unsigned char*, uint32_t>(&Slot::write))
00343                 {
00344                         this->out = &out;
00345                 }
00346                 
00347                 ErrorStatus
00348                 write(const unsigned char* data, uint32_t length)
00349                 {
00350                         bool success = true;
00351                         for(size_t i = 0; i < length && success; i++)
00352                         {
00353                                 success = success && (*this->out << data[i]).good();
00354                         }
00355 
00356                         return success ? CAIRO_STATUS_SUCCESS: CAIRO_STATUS_WRITE_ERROR;
00357                 };
00358         
00359                 ErrorStatus
00360                 operator()(const unsigned char* data, uint32_t length)
00361                 {
00362                         return write(data, length);
00363                 }
00364 
00365         private:
00366                 ostream *out;
00367 };
00368 
00369 class Canvas
00370 {
00371         public:
00372                 Canvas(double height, double width, double thickness, double magnify, map<float, uint32_t> colors, bool drawDeadline, bool showFrequencies)
00373                 {
00374                         this->height = height;
00375                         this->width = width;
00376                         this->thickness = thickness;
00377                         this->magnify = magnify;
00378                         this->colors = colors;
00379                         this->drawDeadline = drawDeadline;
00380                         this->showFrequencies = showFrequencies;
00381                 }
00382                 
00383                 void
00384                 dump(ostream& os, const Schedule *sched, const Taskgraph *tg, const Platform *pt, bool show_task_id, bool useTaskName, float strokeSize)
00385                 {
00386 #ifdef CAIRO_HAS_SVG_SURFACE
00387                         // Compute canvas size, taking thickness and magnification into account
00388                         this->magnify = height >= magnify ? magnify : magnify / height;
00389                         height = height * this->magnify;
00390                         this->absthick = thickness * height * strokeSize;
00391                         this->height = height + absthick;
00392                         this->width = width * this->magnify + absthick;
00393 
00394                         // Create cairo surface into svg output stream
00395                         Slot slot(os);
00396                         this->surface = SvgSurface::create_for_stream(slot, this->width, this->height);
00397                         this->cr = Cairo::Context::create(surface);
00398 
00399                         // Partition drawing surface
00400                         // Space for frequency legend
00401                         if(this->showFrequencies)
00402                         {
00403                                 size_t f = this->colors.size();
00404                                 this->legend_size = (((this->height - this->absthick) / (f + 2))); // Space for legend
00405                         }
00406                         else
00407                         {
00408                                 this->legend_size = 0;
00409                         }
00410                         // Space for y axis legend
00411                         this->setFontSize(4 * this->absthick);
00412                         cairo_text_extents_t te;
00413                         cairo_font_extents_t fe;
00414                         cr->select_font_face("Sans", FONT_SLANT_NORMAL, FONT_WEIGHT_BOLD);
00415                         cr->get_text_extents("Time", te);
00416                         cr->get_font_extents(fe);
00417                         this->yaxis_size = 4 * absthick - te.y_bearing;
00418 
00419                         // Space between extreme-left and extreme-right tasks and extremities of barriers
00420                         this->margin = 3 * absthick;
00421                         size_t p = pt->getCores().size();
00422                         this->core_size = (this->width - absthick - this->margin * 2 - this->legend_size - this->yaxis_size) / p;
00423 
00424                         // Browse all tasks to determine the biggest font size that fits all tasks to draw
00425                         double startSize = this->getHeight() > this->getWidth() ? this->getHeight() : this->getWidth();
00426                         double fontSize = startSize;
00427                         for(set<Task>::iterator i = sched->getTasks().begin(); i != sched->getTasks().end(); i++)
00428                         {
00429                                 Task task = *i;
00430                                 task.setWorkload(tg->getTasks().find(task)->getWorkload());
00431                                 double runtime = task.runtime(i->getWidth(), i->getFrequency());
00432                                 double width = i->getWidth();
00433                                 string name;
00434                                 if(useTaskName)
00435                                 {
00436                                         name = i->getName();
00437                                 }
00438                                 else
00439                                 {
00440                                         stringstream ss;
00441                                         ss << std::distance(sched->getTasks().begin(), i) + 1;
00442                                         name = ss.str();
00443                                 }
00444                                 double idealSize = this->idealFontSize(runtime, width, name, startSize);
00445                                 if(idealSize <= fontSize)
00446                                 {
00447                                         fontSize = idealSize;
00448                                 }
00449                         }
00450 
00451                         // Check out font size of frequency legend
00452                         float legend_height = 0;
00453                         float legend_font_size = 0;
00454                         map<float, string> freq_labels;
00455                         if(this->showFrequencies)
00456                         {
00457                                 legend_height = this->legend_size;
00458                                 float legend_width = legend_height / this->core_size;
00459                                 legend_font_size = legend_height;
00460                                 for(map<float, uint32_t>::const_iterator i = this->colors.begin(); i != this->colors.end(); i++)
00461                                 {
00462                                         float freq = i->first * (*pt->getCores().begin())->getFrequencyUnit();
00463                                         uint32_t letter_number = ((uint32_t)log10(freq)) / 3;
00464                                         float short_freq = freq;
00465                                         unsigned char letter;
00466                                         switch(letter_number)
00467                                         {
00468                                                 case 0:
00469                                                         letter = '\0';
00470                                                 break;
00471                                                 case 1:
00472                                                         letter = 'K';
00473                                                         short_freq /= 1000;
00474                                                 break;
00475                                                 case 2:
00476                                                         letter = 'M';
00477                                                         short_freq /= 1000;
00478                                                         short_freq /= 1000;
00479                                                 break;
00480                                                 case 3:
00481                                                         letter = 'G';
00482                                                         short_freq /= 1000;
00483                                                         short_freq /= 1000;
00484                                                         short_freq /= 1000;
00485                                                 break;
00486                                                 break;
00487                                                 case 4:
00488                                                         letter = 'T';
00489                                                         short_freq /= 1000;
00490                                                         short_freq /= 1000;
00491                                                         short_freq /= 1000;
00492                                                         short_freq /= 1000;
00493                                                 break;
00494                                                 default:
00495                                                         letter = 'X';
00496                                                 break;
00497                                         }
00498                                         stringstream freq_label;
00499                                         if(letter != '\0')
00500                                         {
00501                                                 freq_label << short_freq << letter;
00502                                         }
00503                                         else
00504                                         {
00505                                                 freq_label << short_freq;
00506                                         }
00507                                         freq_labels.insert(pair<float, string>(freq, freq_label.str()));
00508                                         double ideal_legend_size = this->idealFontSize(legend_height, legend_width, freq_label.str(), (double)legend_height);
00509                                         if(ideal_legend_size < legend_font_size)
00510                                         {
00511                                                 legend_font_size = ideal_legend_size;
00512                                         }       
00513                                 }
00514                         }
00515 
00516                         // Set the pen thickness and style
00517                         cr->set_line_width(this->absthick);
00518                         cr->set_line_cap(LINE_CAP_ROUND);
00519                         cr->set_line_join(LINE_JOIN_ROUND);
00520                         setColor(255); // solid black
00521 
00522                         // Draw y axis legend
00523                         // Vertical line
00524                         cr->move_to((te.height + this->yaxis_size + absthick) / 2, height - this->absthick * 2.5); 
00525                         cr->line_to((te.height + this->yaxis_size + absthick) / 2, this->absthick * 2.5); 
00526                         cr->stroke();
00527                         // Left arrow stroke
00528                         cr->move_to((te.height + this->yaxis_size + absthick) / 2, this->absthick * 2.5); 
00529                         cr->line_to((te.height + this->yaxis_size + absthick) / 2 - this->absthick * 2, this->absthick * 6.5); 
00530                         cr->stroke();
00531                         // Right arrow stroke
00532                         cr->move_to((te.height + this->yaxis_size + absthick) / 2, this->absthick * 2.5); 
00533                         cr->line_to((te.height + this->yaxis_size + absthick) / 2 + this->absthick * 2, this->absthick * 6.5); 
00534                         cr->stroke();
00535                         // Text
00536                         cr->save();
00537                         this->setFontSize(4 * this->absthick);
00538                         cr->move_to(-te.y_bearing, height / 2 + te.width / 2 - te.x_bearing); 
00539                         cr->translate(-te.y_bearing, height / 2 + te.width / 2 - te.x_bearing); 
00540                         cr->rotate_degrees(-90);
00541                         cr->show_text("Time");
00542                         cr->restore();
00543 
00544                         // Draw frequency legend
00545                         if(this->showFrequencies)
00546                         {
00547                                 for(map<float, uint32_t>::iterator i = this->colors.begin(); i != this->colors.end(); i++)
00548                                 {
00549                                         uint32_t color = i->second;
00550                                         size_t index = std::distance(this->colors.begin(), i);
00551 
00552                                         cr->rectangle(this->width - legend_height - absthick / 2, legend_height * (index + 1) + absthick / 2, legend_height, legend_height);
00553                                         setColor(color);
00554                                         cr->fill_preserve();
00555                                         setColor(255);
00556                                         cr->stroke();
00557 
00558                                         // Write Frequency
00559                                         float freq = i->first * (*pt->getCores().begin())->getFrequencyUnit();
00560                                         cr->set_font_size(legend_font_size);
00561                                         cairo_text_extents_t te;
00562                                         cairo_font_extents_t fe;
00563                                         cr->select_font_face("Sans", FONT_SLANT_NORMAL, FONT_WEIGHT_BOLD);
00564                                         cr->get_font_extents(fe);
00565                                         cr->get_text_extents(freq_labels.find(freq)->second, te);
00566                                         cr->move_to(this->width - 0.5 * legend_height - te.width / 2 - absthick / 2 - te.x_bearing, legend_height * (index + 2) - legend_height / 2 - fe.descent + fe.height / 2 + absthick / 2);
00567                                         setColor(255);
00568                                         cr->show_text(freq_labels.find(freq)->second);
00569                                 }
00570                         }
00571 
00572                         // Restore font size computed above
00573                         this->setFontSize(fontSize);
00574 
00575                         // Browse all tasks in schedule to draw them
00576                         map<Task, pair<size_t, size_t> > drawn;
00577                         for(Schedule::table::const_iterator i = sched->getSchedule().begin(); i != sched->getSchedule().end(); i++)
00578                         {
00579                                 for(Schedule::sequence::const_iterator j = i->second.begin(); j != i->second.end(); j++)
00580                                 {
00581                                         // Checks the number of core already covered when drawing this task
00582                                         Task task = *j->second.first;
00583                                         // done is the number of cores already drawn for the task
00584                                         // next is the first core after what has been already drawn for the task
00585                                         size_t done = 0, next = 1;
00586                                         size_t core = i->first;
00587 
00588                                         // Find the task in the list of task already partially drawn
00589                                         if(drawn.find(task) != drawn.end())
00590                                         {
00591                                                 done = drawn.find(task)->second.first;
00592                                                 next = drawn.find(task)->second.second;
00593 
00594                                                 // Remove task from list only if it will run again
00595                                                 if(done < task.getWidth() && core >= next)
00596                                                 {
00597                                                         drawn.erase(drawn.find(task));
00598                                                 }
00599                                         }
00600 
00601                                         // Draw the task only if all its core has not been drawn yet,
00602                                         // and if the current core is at least as high as the next allowed
00603                                         // core computed last time the task was partially drawn
00604                                         if(done < task.getWidth() && core >= next)
00605                                         {
00606                                                 task.setWorkload(tg->getTasks().find(task)->getWorkload());
00607                                                 task.setMaxWidth(tg->getTasks().find(task)->getMaxWidth());
00608                                                 task.setEfficiencyString(tg->getTasks().find(task)->getEfficiencyString());
00609                                                 double runtime = task.runtime(task.getWidth(), task.getFrequency());
00610 
00611                                                 // Looks for the number of cores through the task can be drawn in a continuous manner
00612                                                 // Instead of the task's width, because mapping may not be to contiguous cores
00613                                                 size_t width = 0;
00614                                                 int counter = core - 1;
00615                                                 for(Schedule::table::const_iterator k = i; k != sched->getSchedule().end(); k++, counter++)
00616                                                 {
00617                                                         bool task_found = false;
00618                                                         for(Schedule::sequence::const_iterator l = k->second.begin(); l != k->second.end(); l++)
00619                                                         {
00620                                                                 if(*l->second.first == task && k->first == counter + 1)
00621                                                                 {
00622                                                                         width++;
00623                                                                         task_found = true;;
00624                                                                 }
00625                                                         }
00626 
00627                                                         if(!task_found)
00628                                                         {
00629                                                                 break;
00630                                                         }
00631                                                 }
00632                                                 double start = task.getStartTime();
00633                                                 string name;
00634                                                 if(useTaskName)
00635                                                 {
00636                                                         name = task.getName();
00637                                                 }
00638                                                 else
00639                                                 {
00640                                                         stringstream ss;
00641                                                         ss << (std::distance(sched->getTasks().begin(), sched->getTasks().find(task)) + 1);
00642                                                         name = ss.str();
00643                                                 }
00644                                                 this->drawTask(runtime, width, task.getFrequency(), start, core, name, show_task_id);
00645                                                 drawn.insert(pair<Task, pair<size_t, size_t> >(task, pair<size_t, size_t>(done + width, core + width)));
00646                                         }
00647                                 }
00648                         }
00649 
00650                         // Draw final deadline, if necessary
00651                         this->drawBarrier(0);
00652                         if(drawDeadline)
00653                         {
00654                                 this->drawBarrier(this->height - this->absthick);
00655                         }
00656 #else
00657 #warning You must compile cairo with SVG support for this schedule output to work.
00658 #endif
00659                 }
00660 
00661                 void
00662                 drawTask(double time, double width, double frequency, double start, double firstCore, string label, bool show_task_id)
00663                 {
00664 #ifdef CAIRO_HAS_SVG_SURFACE
00665                         // Task rectangle
00666                         cr->rectangle((firstCore - 1) * this->core_size + absthick / 2 + margin + yaxis_size, height - absthick / 2 - (start + time) * magnify, width * this->core_size, time * magnify);
00667                         // Fill rectangle in solid red
00668                         uint32_t color = this->colors.find(frequency)->second;
00669                         setColor(color);
00670                         cr->fill_preserve();
00671                         // Trace stroke in solid back
00672                         setColor(255);
00673                         cr->stroke();
00674 
00675                         // Add task name
00676                         if(this->fontSize > 0 && show_task_id)
00677                         {
00678                                 cr->set_font_size(this->fontSize);
00679                                 cairo_text_extents_t te;
00680                                 cairo_font_extents_t fe;
00681                                 cr->select_font_face("Sans", FONT_SLANT_NORMAL, FONT_WEIGHT_BOLD);
00682                                 cr->get_font_extents(fe);
00683                                 cr->get_text_extents(label, te);
00684                                 // See http://www.cairographics.org/tutorial/ for more information on cairo text placement
00685                                 // Secont "Understanding text"
00686                                 cr->move_to(
00687                                         // Move along x
00688                                         absthick / 2 + // half the line thickness
00689                                         margin + // Extra length of beginning and end barriers over actual schedule
00690                                         this->yaxis_size + // Y axis legend space
00691                                         (firstCore - 1) * this->core_size + // Find the first core in which the task is being drawn
00692                                         this->core_size * width / 2 - // Middle of the task to be drawn
00693                                         te.width / 2 - te.x_bearing, // Half the the text length
00694                                         // Move along y
00695                                         height - // Whole height of the figure 
00696                                         absthick / 2 - // Half of line thickness
00697                                         start * magnify - // Starting time of the task, time by magnification factor
00698                                         time * magnify / 2 - // Half of the task running time
00699                                         fe.descent + // Space in the text under the baseline where the font is applied (lower space necessary to draw letters p, gand j for example)
00700                                         fe.height / 2 // Height of the rest of the text
00701                                 );
00702                                 setColor(255);
00703                                 cr->show_text(label);
00704                         }
00705 #endif
00706                 }
00707 
00708                 double
00709                 idealFontSize(double time, double width, string label, double startSize)
00710                 {
00711 #ifdef CAIRO_HAS_SVG_SURFACE
00712                         double font_size = startSize;
00713                         double delta = font_size;
00714                         double minError = 0.5 * this->absthick;
00715                         double maxError = 1 * this->absthick;
00716 
00717                         while(true)
00718                         {
00719                                 cairo_text_extents_t te;
00720                                 cairo_font_extents_t fe;
00721                                 cr->set_font_size(font_size);
00722                                 cr->select_font_face("Sans", FONT_SLANT_NORMAL, FONT_WEIGHT_BOLD);
00723                                 cr->get_font_extents(fe);
00724                                 cr->get_text_extents(label, te);
00725                                 double text_width = te.width - te.x_bearing;
00726                                 double text_height = te.height;
00727 
00728                                 if(((text_width >= (width * this->core_size - 2 * absthick - minError)) || (text_height >= (time * this->magnify - 2 * absthick - minError))) && delta > 0)
00729                                 {
00730                                         delta = delta / 2;
00731                                         font_size = font_size - delta;
00732                                 }
00733                                 else if(((text_width <= (width * this->core_size - 2 * absthick - maxError)) && (text_height <= (time * this->magnify - 2 * absthick - maxError))) && delta > 0)
00734                                 {               
00735                                         font_size = font_size + delta;
00736                                 }
00737                                 else
00738                                 {
00739                                         return font_size;
00740                                 }
00741                         }
00742 #endif
00743                 }
00744 
00745                 void
00746                 drawBarrier(double time)
00747                 {
00748 #ifdef CAIRO_HAS_SVG_SURFACE
00749                         //cr->move_to(this->absthick / 2 + this->yaxis_size, height - time * magnify - this->absthick / 2); 
00750                         //cr->line_to(width - this->absthick / 2 - legend_size, height - time * magnify - this->absthick / 2);
00751                         cr->move_to(this->absthick / 2 + this->yaxis_size, height - time - this->absthick / 2); 
00752                         cr->line_to(width - this->absthick / 2 - legend_size, height - time - this->absthick / 2);
00753                         setColor(255); // solid black
00754                         cr->stroke();
00755 #endif
00756                 }
00757 
00758                 double
00759                 getFontSize() const
00760                 {
00761                         return this->fontSize;
00762                 }
00763 
00764                 void
00765                 setFontSize(double size)
00766                 {
00767                         this->fontSize = size;
00768                         this->cr->set_font_size(this->fontSize);
00769                 }
00770 
00771                 double
00772                 getWidth() const
00773                 {
00774                         return this->width;
00775                 }
00776                 
00777                 double
00778                 getHeight() const
00779                 {
00780                         return this->height;
00781                 }
00782 
00783         protected:
00784                 void
00785                 setColor(uint32_t rgba)
00786                 {
00787                         cr->set_source_rgba((float)decode(rgba, 3) / 255, (float)decode(rgba, 2) / 255, (float)decode(rgba, 1) / 255, (float)decode(rgba, 0) / 255);
00788                 }
00789 
00790                 float thickness;
00791                 double magnify;
00792                 double height;
00793                 double width;
00794                 double absthick;
00795                 double core_size;
00796                 double margin;
00797                 double legend_size;
00798                 double fontSize;
00799                 double yaxis_size;
00800                 bool drawDeadline;
00801                 bool showFrequencies;
00802                 map<float, uint32_t> colors;
00803 #ifdef CAIRO_HAS_SVG_SURFACE
00804                 Cairo::RefPtr<Cairo::Context> cr;
00805                 Cairo::RefPtr<Cairo::SvgSurface> surface;
00806 #endif
00807 };
00808 
00809 void
00810 TetrisSchedule::dump(ostream& os, const Schedule *sched, const Taskgraph *tg, const Platform *pt) const
00811 {
00812         set<float> frequencies;
00813         for(set<const Core*>::iterator i = pt->getCores().begin(); i != pt->getCores().end(); i++)
00814         {
00815                 const Core *core = *i;
00816                 for(set<float>::iterator j = core->getFrequencies().begin(); j != core->getFrequencies().end(); j++)
00817                 {
00818                         frequencies.insert(*j);
00819                 }
00820         }
00821 
00822         double thickness = 0.02;
00823         double magnify = 100;
00824 
00825         Schedule::table schedule = sched->getSchedule();
00826 
00827         // Browse all tasks to find the time the last task stops        
00828         double max_stop_time = 0;
00829         for(Schedule::table::const_iterator i = schedule.begin(); i != schedule.end(); i++)
00830         {
00831                 for(Schedule::sequence::const_iterator j = i->second.begin(); j != i->second.end(); j++)
00832                 {
00833                         Task task = *j->second.first;
00834                         task.setWorkload(tg->getTasks().find(task)->getWorkload());
00835                         task.setMaxWidth(tg->getTasks().find(task)->getMaxWidth());
00836                         task.setEfficiencyString(tg->getTasks().find(task)->getEfficiencyString());
00837                         double stop_time = task.getStartTime() + task.runtime(task.getWidth(), task.getFrequency());
00838                         if(stop_time > max_stop_time)
00839                         {
00840                                 max_stop_time = stop_time;
00841                         }
00842                 }
00843         }
00844 
00845         double deadline = tg->getDeadline(pt);
00846         bool drawDeadline = deadline > 0;
00847         deadline = deadline > 0 ? deadline : max_stop_time;
00848         Canvas(deadline, deadline * ratio, thickness, magnify, makeGradient(colors, frequencies), drawDeadline, this->showFrequencies).dump(os, sched, tg, pt, showTaskId, useTaskName, strokeSize);
00849 }
00850 
00851 TetrisSchedule*
00852 TetrisSchedule::clone() const
00853 {
00854         return new TetrisSchedule();
00855 }
00856