1
2 #include "SkBitmap.h"
3 #include "SkCanvas.h"
4 #include "SkColor.h"
5 #include "SkColorPriv.h"
6 #include "SkDevice.h"
7 #include "SkGraphics.h"
8 #include "SkImageDecoder.h"
9 #include "SkImageEncoder.h"
10 #include "SkOSFile.h"
11 #include "SkPathOpsDebug.h"
12 #include "SkPicture.h"
13 #include "SkRTConf.h"
14 #include "SkTSort.h"
15 #include "SkStream.h"
16 #include "SkString.h"
17 #include "SkTArray.h"
18 #include "SkTDArray.h"
19 #include "SkThreadPool.h"
20 #include "SkTime.h"
21 #include "Test.h"
22
23 #ifdef SK_BUILD_FOR_WIN
24 #define PATH_SLASH "\\"
25 #define IN_DIR "D:\\skp\\slave"
26 #define OUT_DIR "D:\\skpOut\\1\\"
27 #else
28 #define PATH_SLASH "/"
29 #define IN_DIR "/skp/2311328-7fc2228/slave"
30 #define OUT_DIR "/skpOut/4/"
31 #endif
32
33 const struct {
34 int directory;
35 const char* filename;
36 } skipOverSept[] = {
37 { 3, "http___www_americascup_com_.skp"}, // !simple->closed()
38 {18, "http___www_argus_presse_fr_.skp"}, // can't find winding of remaining vertical edge
39 {31, "http___www_narayana_verlag_de_.skp"}, // !simple->closed()
40 {36, "http___www_educationalcraft_com_.skp"}, // cubic / cubic near end / assert in SkIntersections::insert
41 {44, "http___www_cooksnaps_com_.skp"}, // !simple->isClosed()
42 {48, "http___www_narayana_publishers_com_.skp"}, // !simple->isClosed()
43 {51, "http___www_freedominthe50states_org_.skp"}, // corrupt dash data
44 {52, "http___www_aceinfographics_com_.skp"}, // right angle winding assert
45 {53, "http___www_lojaanabotafogo_com_br_.skp"}, // rrect validate assert
46 {57, "http___www_vantageproduction_com_.skp"}, // !isClosed()
47 {64, "http___www_etiqadd_com_.skp"}, // !simple->closed()
48 {84, "http___www_swapspacesystems_com_.skp"}, // !simple->closed()
49 {90, "http___www_tcmevents_org_.skp"}, // !simple->closed()
50 {96, "http___www_paseoitaigara_com_br_.skp"}, // !simple->closed()
51 {98, "http___www_mortgagemarketguide_com_.skp"}, // !simple->closed()
52 {99, "http___www_kitcheninspirations_wordpress_com_.skp"}, // checkSmall / bumpSpan
53 };
54
55 /* stats
56 97 http___www_brandyandvinca_com_.skp pixelError=3
57 95 http___www_into_asia_com_.skp pixelError=12
58 93 http___www_lunarplanner_com_.skp pixelError=14
59 98 http___www_lovelyitalia_com_.skp pixelError=17
60 90 http___www_inter_partner_blogspot_com_.skp pixelError=18
61 99 http___www_maxarea_com_.skp pixelError=26
62 98 http___www_maroonsnet_org_.skp pixelError=33
63 92 http___www_belinaart_ru_.skp pixelError=50
64 100 http___www_chroot_ro_.skp pixelError=62
65 99 http___www_hsbrands_com_.skp pixelError=98
66 95 http___www_tournamentindicator_com_.skp pixelError=122
67 93 http___www_businesses_com_au_.skp pixelError=162
68 90 http___www_regenesys_net_.skp pixelError=182
69 88 http___www_1863544208148625103_c18eac63985503fa85b06358959c1ba27fc36f82_blogspot_com_.skp pixelError=186
70 97 http___www_pregacoesevangelica_com_br_.skp pixelError=240
71 77 http___www_zhenggang_org_.skp pixelError=284
72 96 http___slidesharemailer_com_.skp pixelError=522
73 94 http___www_gensteel_com_.skp pixelError=555
74 68 http___www_jf_eti_br_.skp pixelError=610
75 83 http___www_swishiat_com_.skp pixelError=706
76 96 http___www_matusikmissive_com_au_.skp pixelError=2580
77 95 http___www_momentumnation_com_.skp pixelError=3938
78 92 http___www_rssowl_com_.skp pixelError=5113
79 96 http___www_sexxygirl_tv_.skp pixelError=7605
80 99 http___www_georgevalah_wordpress_com_.skp pixelError=8386
81 78 http___www_furbo_org_.skp pixelError=8656
82 78 http___www_djxhemary_wordpress_com_.skp pixelError=8976
83 100 http___www_mindcontrolblackassassins_com_.skp pixelError=31950
84 98 http___bababillgates_free_fr_.skp pixelError=40237
85 98 http___hepatite_ro_.skp pixelError=44370
86 86 http___www_somethingwagging_com_.skp pixelError=47794
87 84 http___www_beverageuniverse_com_.skp pixelError=65450
88 50 http___www_aveksa_com_.skp pixelError=68194
89 10 http___www_publiker_pl_.skp pixelError=89997
90 61 http___www_dominos_co_id_.skp pixelError=476868
91 87 http___www_du_edu_om_.skp time=46
92 87 http___www_bigload_de_.skp time=46
93 100 http___www_home_forum_com_.skp time=48
94 97 http___www_hotamateurchat_com_.skp time=48
95 97 http___www_myrsky_com_cn_.skp time=48
96 98 http___www_techiegeex_com_.skp time=49
97 82 http___www_fashionoutletsofchicago_com_.skp time=50
98 77 http___www_dynamischbureau_nl_.skp time=50
99 82 http___www_mayihelpu_co_in_.skp time=50
100 84 http___www_vbox7_com_user_history_viewers_.skp time=50
101 85 http___www_ktokogda_com_.skp time=50
102 85 http___www_propertyturkeysale_com_.skp time=50
103 85 http___www_51play_com_.skp time=50
104 86 http___www_bayalarm_com_.skp time=50
105 87 http___www_eaglepictures_com_.skp time=50
106 88 http___www_atlasakvaryum_com_.skp time=50
107 91 http___www_pioneerchryslerjeep_com_.skp time=50
108 94 http___www_thepulsemag_com_.skp time=50
109 95 http___www_dcshoes_com_ph_.skp time=50
110 96 http___www_montrealmassage_ca_.skp time=50
111 96 http___www_jkshahclasses_com_.skp time=50
112 96 http___www_webcamconsult_com_.skp time=51
113 100 http___www_bsoscblog_com_.skp time=52
114 95 http___www_flaktwoods_com_.skp time=53
115 91 http___www_qivivo_com_.skp time=54
116 90 http___www_unitender_com_.skp time=56
117 97 http___www_casinogaming_com_.skp time=56
118 97 http___www_rootdownload_com_.skp time=56
119 94 http___www_aspa_ev_de_.skp time=57
120 98 http___www_tenpieknyswiat_pl_.skp time=57
121 93 http___www_transocean_de_.skp time=58
122 94 http___www_vdo2_blogspot_com_.skp time=58
123 94 http___www_asmaissexy_com_br_.skp time=58
124 100 http___www_prefeiturasjm_com_br_.skp time=60
125 100 http___www_eduinsuranceclick_blogspot_com_.skp time=60
126 96 http___www_bobdunsire_com_.skp time=61
127 96 http___www_omgkettlecorn_com_.skp time=61
128 85 http___www_fbbsessions_com_.skp time=62
129 86 http___www_hector_ru_.skp time=62
130 87 http___www_wereldsupporter_nl_.skp time=62
131 90 http___www_arello_com_.skp time=62
132 93 http___www_bayerplastics_com_.skp time=62
133 93 http___www_superandolamovida_com_ar_.skp time=62
134 96 http___www_med_rbf_ru_.skp time=62
135 81 http___www_carnegiescience_edu_.skp time=65
136 87 http___www_asanewengland_com_.skp time=65
137 92 http___www_turkce_karakter_appspot_com_.skp time=65
138 94 http___www_k3a_org_.skp time=65
139 96 http___www_powermaccenter_com_.skp time=65
140 98 http___www_avto49_ru_.skp time=67
141 100 http___www_hetoldeambaecht_nl_.skp time=68
142 95 http___www_marine_ie_.skp time=69
143 96 http___www_quebecvapeboutique_com_.skp time=69
144 95 http___www_brays_ingles_com_.skp time=70
145 100 http___www_lacondesa_com_.skp time=72
146 95 http___www_timbarrathai_com_au_.skp time=76
147 95 http___www_cuissedegrenouille_com_.skp time=76
148 95 http___www_iwama51_ru_.skp time=76
149 99 http___www_fotoantologia_it_.skp time=76
150 92 http___www_indian_architects_com_.skp time=78
151 92 http___www_totalwomanspa_com_.skp time=78
152 100 http___www_fachverband_spielhallen_de_.skp time=83
153 93 http___www_golshanemehr_ir_.skp time=84
154 95 http___www_maryesses_com_.skp time=84
155 99 http___www_ddcorp_ca_.skp time=89
156 90 http___www_brontops_com_.skp time=89
157 94 http___www_robgolding_com_.skp time=89
158 91 http___www_tecban_com_br_.skp time=91
159 98 http___www_costamesakarate_com_.skp time=100
160 95 http___www_monsexyblog_com_.skp time=103
161 97 http___www_stornowaygazette_co_uk_.skp time=103
162 93 http___www_fitforaframe_com_.skp time=104
163 98 http___www_intentionoftheday_com_.skp time=113
164 100 http___www_tailgateclothing_com_.skp time=117
165 95 http___www_senbros_com_.skp time=118
166 93 http___www_lettoblog_com_.skp time=121
167 94 http___www_maxineschallenge_com_au_.skp time=125
168 95 http___www_savvycard_net_.skp time=127
169 95 http___www_open_ac_mu_.skp time=129
170 96 http___www_avgindia_in_.skp time=135
171 97 http___www_stocktonseaview_com_.skp time=135
172 96 http___www_distroller_com_.skp time=142
173 94 http___www_travoggalop_dk_.skp time=144
174 100 http___www_history_im_.skp time=144
175 94 http___www_playradio_sk_.skp time=145
176 92 http___www_linglongglass_com_.skp time=151
177 97 http___www_bizzna_com_.skp time=151
178 96 http___www_spiros_ws_.skp time=154
179 91 http___www_rosen_meents_co_il_.skp time=156
180 81 http___www_hoteldeluxeportland_com_.skp time=158
181 92 http___www_freetennis_org_.skp time=161
182 93 http___www_aircharternetwork_com_au_.skp time=161
183 94 http___www_austinparks_org_.skp time=165
184 89 http___www_bevvy_co_.skp time=168
185 91 http___www_sosyalhile_net_.skp time=168
186 98 http___www_minvih_gob_ve_.skp time=171
187 89 http___www_streetfoodmtl_com_.skp time=172
188 92 http___www_loveslatinas_tumblr_com_.skp time=178
189 93 http___www_madbites_co_in_.skp time=180
190 94 http___www_rocktarah_ir_.skp time=185
191 97 http___www_penthouselife_com_.skp time=185
192 96 http___www_appymonkey_com_.skp time=196
193 92 http___www_pasargadhotels_com_.skp time=203
194 99 http___www_marina_mil_pe_.skp time=203
195 89 http___www_kays_co_uk_.skp time=205
196 77 http___www_334588_com_.skp time=211
197 83 http___www_trendbad24_de_.skp time=211
198 81 http___www_cdnetworks_co_kr_.skp time=216
199 94 http___www_schellgames_com_.skp time=223
200 95 http___www_juliaweddingnews_cn_.skp time=230
201 92 http___www_xcrafters_pl_.skp time=253
202 93 http___www_pondoo_com_.skp time=253
203 96 http___www_helsinkicapitalpartners_fi_.skp time=255
204 88 http___www_nadtexican_com_.skp time=259
205 85 http___www_canstockphoto_hu_.skp time=266
206 78 http___www_ecovacs_com_cn_.skp time=271
207 93 http___www_brookfieldplaceny_com_.skp time=334
208 93 http___www_fmastrengthtraining_com_.skp time=337
209 94 http___www_turtleonthebeach_com_.skp time=394
210 90 http___www_temptationthemovie_com_.skp time=413
211 95 http___www_patongsawaddi_com_.skp time=491
212 91 http___www_online_radio_appspot_com_.skp time=511
213 68 http___www_richardmiller_co_uk_.skp time=528
214 63 http___www_eschrade_com_.skp time=543
215 55 http___www_interaction_inf_br_.skp time=625
216 38 http___www_huskyliners_com_.skp time=632
217 86 http___granda_net_.skp time=1067
218 24 http___www_cocacolafm_com_br_.skp time=1081
219 */
220
221 size_t skipOverSeptCount = sizeof(skipOverSept) / sizeof(skipOverSept[0]);
222
223 enum TestStep {
224 kCompareBits,
225 kEncodeFiles,
226 };
227
228 enum {
229 kMaxLength = 256,
230 kMaxFiles = 128,
231 kSmallLimit = 1000,
232 };
233
234 struct TestResult {
initTestResult235 void init(int dirNo) {
236 fDirNo = dirNo;
237 sk_bzero(fFilename, sizeof(fFilename));
238 fTestStep = kCompareBits;
239 fScale = 1;
240 }
241
statusTestResult242 SkString status() {
243 SkString outStr;
244 outStr.printf("%s %d %d\n", fFilename, fPixelError, fTime);
245 return outStr;
246 }
247
progressTestResult248 SkString progress() {
249 SkString outStr;
250 outStr.printf("dir=%d %s ", fDirNo, fFilename);
251 if (fPixelError) {
252 outStr.appendf(" err=%d", fPixelError);
253 }
254 if (fTime) {
255 outStr.appendf(" time=%d", fTime);
256 }
257 if (fScale != 1) {
258 outStr.appendf(" scale=%d", fScale);
259 }
260 outStr.appendf("\n");
261 return outStr;
262
263 }
264
TestTestResult265 static void Test(int dirNo, const char* filename, TestStep testStep) {
266 TestResult test;
267 test.init(dirNo);
268 test.fTestStep = testStep;
269 strcpy(test.fFilename, filename);
270 test.testOne();
271 }
272
testTestResult273 void test(int dirNo, const SkString& filename) {
274 init(dirNo);
275 strcpy(fFilename, filename.c_str());
276 testOne();
277 }
278
279 void testOne();
280
281 char fFilename[kMaxLength];
282 TestStep fTestStep;
283 int fDirNo;
284 int fPixelError;
285 int fTime;
286 int fScale;
287 };
288
289 class SortByPixel : public TestResult {
290 public:
operator <(const SortByPixel & rh) const291 bool operator<(const SortByPixel& rh) const {
292 return fPixelError < rh.fPixelError;
293 }
294 };
295
296 class SortByTime : public TestResult {
297 public:
operator <(const SortByTime & rh) const298 bool operator<(const SortByTime& rh) const {
299 return fTime < rh.fTime;
300 }
301 };
302
303 class SortByName : public TestResult {
304 public:
operator <(const SortByName & rh) const305 bool operator<(const SortByName& rh) const {
306 return strcmp(fFilename, rh.fFilename) < 0;
307 }
308 };
309
310 struct TestState {
initTestState311 void init(int dirNo, skiatest::Reporter* reporter) {
312 fReporter = reporter;
313 fResult.init(dirNo);
314 }
315
316 SkTDArray<SortByPixel> fPixelWorst;
317 SkTDArray<SortByTime> fSlowest;
318 skiatest::Reporter* fReporter;
319 TestResult fResult;
320 };
321
322 struct TestRunner {
TestRunnerTestRunner323 TestRunner(skiatest::Reporter* reporter, int threadCount)
324 : fNumThreads(threadCount)
325 , fReporter(reporter) {
326 }
327
328 ~TestRunner();
329 void render();
330 int fNumThreads;
331 SkTDArray<class TestRunnable*> fRunnables;
332 skiatest::Reporter* fReporter;
333 };
334
335 class TestRunnable : public SkRunnable {
336 public:
run()337 virtual void run() SK_OVERRIDE {
338 SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
339 (*fTestFun)(&fState);
340 }
341
342 TestState fState;
343 void (*fTestFun)(TestState*);
344 };
345
346
347 class TestRunnableDir : public TestRunnable {
348 public:
TestRunnableDir(void (* testFun)(TestState *),int dirNo,TestRunner * runner)349 TestRunnableDir(void (*testFun)(TestState*), int dirNo, TestRunner* runner) {
350 fState.init(dirNo, runner->fReporter);
351 fTestFun = testFun;
352 }
353
354 };
355
356 class TestRunnableFile : public TestRunnable {
357 public:
TestRunnableFile(void (* testFun)(TestState *),int dirNo,const char * name,TestRunner * runner)358 TestRunnableFile(void (*testFun)(TestState*), int dirNo, const char* name, TestRunner* runner) {
359 fState.init(dirNo, runner->fReporter);
360 strcpy(fState.fResult.fFilename, name);
361 fTestFun = testFun;
362 }
363 };
364
365 class TestRunnableEncode : public TestRunnableFile {
366 public:
TestRunnableEncode(void (* testFun)(TestState *),int dirNo,const char * name,TestRunner * runner)367 TestRunnableEncode(void (*testFun)(TestState*), int dirNo, const char* name, TestRunner* runner)
368 : TestRunnableFile(testFun, dirNo, name, runner) {
369 fState.fResult.fTestStep = kEncodeFiles;
370 }
371 };
372
~TestRunner()373 TestRunner::~TestRunner() {
374 for (int index = 0; index < fRunnables.count(); index++) {
375 SkDELETE(fRunnables[index]);
376 }
377 }
378
render()379 void TestRunner::render() {
380 SkThreadPool pool(fNumThreads);
381 for (int index = 0; index < fRunnables.count(); ++ index) {
382 pool.add(fRunnables[index]);
383 }
384 }
385
386 ////////////////////////////////////////////////
387
388 static const char outOpDir[] = OUT_DIR "opClip";
389 static const char outOldDir[] = OUT_DIR "oldClip";
390 static const char outSkpDir[] = OUT_DIR "skpTest";
391 static const char outDiffDir[] = OUT_DIR "outTest";
392 static const char outStatusDir[] = OUT_DIR "statusTest";
393
make_filepath(int dirNo,const char * dir,const char * name)394 static SkString make_filepath(int dirNo, const char* dir, const char* name) {
395 SkString path(dir);
396 if (dirNo) {
397 path.appendf("%d", dirNo);
398 }
399 path.append(PATH_SLASH);
400 path.append(name);
401 return path;
402 }
403
make_in_dir_name(int dirNo)404 static SkString make_in_dir_name(int dirNo) {
405 SkString dirName(IN_DIR);
406 dirName.appendf("%d", dirNo);
407 if (!sk_exists(dirName.c_str())) {
408 SkDebugf("could not read dir %s\n", dirName.c_str());
409 return SkString();
410 }
411 return dirName;
412 }
413
make_stat_dir_name(int dirNo)414 static SkString make_stat_dir_name(int dirNo) {
415 SkString dirName(outStatusDir);
416 dirName.appendf("%d", dirNo);
417 if (!sk_exists(dirName.c_str())) {
418 SkDebugf("could not read dir %s\n", dirName.c_str());
419 return SkString();
420 }
421 return dirName;
422 }
423
make_one_out_dir(const char * outDirStr)424 static bool make_one_out_dir(const char* outDirStr) {
425 SkString outDir = make_filepath(0, outDirStr, "");
426 if (!sk_exists(outDir.c_str())) {
427 if (!sk_mkdir(outDir.c_str())) {
428 SkDebugf("could not create dir %s\n", outDir.c_str());
429 return false;
430 }
431 }
432 return true;
433 }
434
make_out_dirs()435 static bool make_out_dirs() {
436 SkString outDir = make_filepath(0, OUT_DIR, "");
437 if (!sk_exists(outDir.c_str())) {
438 if (!sk_mkdir(outDir.c_str())) {
439 SkDebugf("could not create dir %s\n", outDir.c_str());
440 return false;
441 }
442 }
443 return make_one_out_dir(outOldDir)
444 && make_one_out_dir(outOpDir)
445 && make_one_out_dir(outSkpDir)
446 && make_one_out_dir(outDiffDir)
447 && make_one_out_dir(outStatusDir);
448 }
449
make_png_name(const char * filename)450 static SkString make_png_name(const char* filename) {
451 SkString pngName = SkString(filename);
452 pngName.remove(pngName.size() - 3, 3);
453 pngName.append("png");
454 return pngName;
455 }
456
similarBits(const SkBitmap & gr,const SkBitmap & sk)457 static int similarBits(const SkBitmap& gr, const SkBitmap& sk) {
458 const int kRowCount = 3;
459 const int kThreshold = 3;
460 int width = SkTMin(gr.width(), sk.width());
461 if (width < kRowCount) {
462 return true;
463 }
464 int height = SkTMin(gr.height(), sk.height());
465 if (height < kRowCount) {
466 return true;
467 }
468 int errorTotal = 0;
469 SkTArray<int, true> errorRows;
470 errorRows.push_back_n(width * kRowCount);
471 SkAutoLockPixels autoGr(gr);
472 SkAutoLockPixels autoSk(sk);
473 for (int y = 0; y < height; ++y) {
474 SkPMColor* grRow = gr.getAddr32(0, y);
475 SkPMColor* skRow = sk.getAddr32(0, y);
476 int* base = &errorRows[0];
477 int* cOut = &errorRows[y % kRowCount];
478 for (int x = 0; x < width; ++x) {
479 SkPMColor grColor = grRow[x];
480 SkPMColor skColor = skRow[x];
481 int dr = SkGetPackedR32(grColor) - SkGetPackedR32(skColor);
482 int dg = SkGetPackedG32(grColor) - SkGetPackedG32(skColor);
483 int db = SkGetPackedB32(grColor) - SkGetPackedB32(skColor);
484 int error = cOut[x] = SkTMax(SkAbs32(dr), SkTMax(SkAbs32(dg), SkAbs32(db)));
485 if (error < kThreshold || x < 2) {
486 continue;
487 }
488 if (base[x - 2] < kThreshold
489 || base[width + x - 2] < kThreshold
490 || base[width * 2 + x - 2] < kThreshold
491 || base[x - 1] < kThreshold
492 || base[width + x - 1] < kThreshold
493 || base[width * 2 + x - 1] < kThreshold
494 || base[x] < kThreshold
495 || base[width + x] < kThreshold
496 || base[width * 2 + x] < kThreshold) {
497 continue;
498 }
499 errorTotal += error;
500 }
501 }
502 return errorTotal;
503 }
504
addError(TestState * data,const TestResult & testResult)505 static bool addError(TestState* data, const TestResult& testResult) {
506 if (testResult.fPixelError <= 0 && testResult.fTime <= 0) {
507 return false;
508 }
509 int worstCount = data->fPixelWorst.count();
510 int pixelError = testResult.fPixelError;
511 if (pixelError > 0) {
512 for (int index = 0; index < worstCount; ++index) {
513 if (pixelError > data->fPixelWorst[index].fPixelError) {
514 data->fPixelWorst[index] = *(SortByPixel*) &testResult;
515 return true;
516 }
517 }
518 }
519 int slowCount = data->fSlowest.count();
520 int time = testResult.fTime;
521 if (time > 0) {
522 for (int index = 0; index < slowCount; ++index) {
523 if (time > data->fSlowest[index].fTime) {
524 data->fSlowest[index] = *(SortByTime*) &testResult;
525 return true;
526 }
527 }
528 }
529 if (pixelError > 0 && worstCount < kMaxFiles) {
530 *data->fPixelWorst.append() = *(SortByPixel*) &testResult;
531 return true;
532 }
533 if (time > 0 && slowCount < kMaxFiles) {
534 *data->fSlowest.append() = *(SortByTime*) &testResult;
535 return true;
536 }
537 return false;
538 }
539
timePict(SkPicture * pic,SkCanvas * canvas)540 static SkMSec timePict(SkPicture* pic, SkCanvas* canvas) {
541 canvas->save();
542 int pWidth = pic->width();
543 int pHeight = pic->height();
544 const int maxDimension = 1000;
545 const int slices = 3;
546 int xInterval = SkTMax(pWidth - maxDimension, 0) / (slices - 1);
547 int yInterval = SkTMax(pHeight - maxDimension, 0) / (slices - 1);
548 SkRect rect = {0, 0, SkIntToScalar(SkTMin(maxDimension, pWidth)),
549 SkIntToScalar(SkTMin(maxDimension, pHeight))};
550 canvas->clipRect(rect);
551 SkMSec start = SkTime::GetMSecs();
552 for (int x = 0; x < slices; ++x) {
553 for (int y = 0; y < slices; ++y) {
554 pic->draw(canvas);
555 canvas->translate(0, SkIntToScalar(yInterval));
556 }
557 canvas->translate(SkIntToScalar(xInterval), SkIntToScalar(-yInterval * slices));
558 }
559 SkMSec end = SkTime::GetMSecs();
560 canvas->restore();
561 return end - start;
562 }
563
drawPict(SkPicture * pic,SkCanvas * canvas,int scale)564 static void drawPict(SkPicture* pic, SkCanvas* canvas, int scale) {
565 canvas->clear(SK_ColorWHITE);
566 if (scale != 1) {
567 canvas->save();
568 canvas->scale(1.0f / scale, 1.0f / scale);
569 }
570 pic->draw(canvas);
571 if (scale != 1) {
572 canvas->restore();
573 }
574 }
575
writePict(const SkBitmap & bitmap,const char * outDir,const char * pngName)576 static void writePict(const SkBitmap& bitmap, const char* outDir, const char* pngName) {
577 SkString outFile = make_filepath(0, outDir, pngName);
578 if (!SkImageEncoder::EncodeFile(outFile.c_str(), bitmap,
579 SkImageEncoder::kPNG_Type, 100)) {
580 SkDebugf("unable to encode gr %s (width=%d height=%d)\n", pngName,
581 bitmap.width(), bitmap.height());
582 }
583 }
584
testOne()585 void TestResult::testOne() {
586 SkPicture* pic = NULL;
587 {
588 #if DEBUG_SHOW_TEST_NAME
589 if (fTestStep == kCompareBits) {
590 SkString testName(fFilename);
591 const char http[] = "http";
592 if (testName.startsWith(http)) {
593 testName.remove(0, sizeof(http) - 1);
594 }
595 while (testName.startsWith("_")) {
596 testName.remove(0, 1);
597 }
598 const char dotSkp[] = ".skp";
599 if (testName.endsWith(dotSkp)) {
600 size_t len = testName.size();
601 testName.remove(len - (sizeof(dotSkp) - 1), sizeof(dotSkp) - 1);
602 }
603 testName.prepend("skp");
604 testName.append("1");
605 strncpy(DEBUG_FILENAME_STRING, testName.c_str(), DEBUG_FILENAME_STRING_LENGTH);
606 } else if (fTestStep == kEncodeFiles) {
607 strncpy(DEBUG_FILENAME_STRING, "", DEBUG_FILENAME_STRING_LENGTH);
608 }
609 #endif
610 SkString path = make_filepath(fDirNo, IN_DIR, fFilename);
611 SkFILEStream stream(path.c_str());
612 if (!stream.isValid()) {
613 SkDebugf("invalid stream %s\n", path.c_str());
614 goto finish;
615 }
616 pic = SkPicture::CreateFromStream(&stream, &SkImageDecoder::DecodeMemory);
617 if (!pic) {
618 SkDebugf("unable to decode %s\n", fFilename);
619 goto finish;
620 }
621 int width = pic->width();
622 int height = pic->height();
623 SkBitmap oldBitmap, opBitmap;
624 fScale = 1;
625 while (width / fScale > 32767 || height / fScale > 32767) {
626 ++fScale;
627 }
628 do {
629 int dimX = (width + fScale - 1) / fScale;
630 int dimY = (height + fScale - 1) / fScale;
631 if (oldBitmap.allocN32Pixels(dimX, dimY) &&
632 opBitmap.allocN32Pixels(dimX, dimY)) {
633 break;
634 }
635 SkDebugf("-%d-", fScale);
636 } while (++fScale < 256);
637 if (fScale >= 256) {
638 SkDebugf("unable to allocate bitmap for %s (w=%d h=%d)\n", fFilename,
639 width, height);
640 goto finish;
641 }
642 oldBitmap.eraseColor(SK_ColorWHITE);
643 SkCanvas oldCanvas(oldBitmap);
644 oldCanvas.setAllowSimplifyClip(false);
645 opBitmap.eraseColor(SK_ColorWHITE);
646 SkCanvas opCanvas(opBitmap);
647 opCanvas.setAllowSimplifyClip(true);
648 drawPict(pic, &oldCanvas, fScale);
649 drawPict(pic, &opCanvas, fScale);
650 if (fTestStep == kCompareBits) {
651 fPixelError = similarBits(oldBitmap, opBitmap);
652 int oldTime = timePict(pic, &oldCanvas);
653 int opTime = timePict(pic, &opCanvas);
654 fTime = SkTMax(0, oldTime - opTime);
655 } else if (fTestStep == kEncodeFiles) {
656 SkString pngStr = make_png_name(fFilename);
657 const char* pngName = pngStr.c_str();
658 writePict(oldBitmap, outOldDir, pngName);
659 writePict(opBitmap, outOpDir, pngName);
660 }
661 }
662 finish:
663 if (pic) {
664 pic->unref();
665 }
666 }
667
makeStatusString(int dirNo)668 static SkString makeStatusString(int dirNo) {
669 SkString statName;
670 statName.printf("stats%d.txt", dirNo);
671 SkString statusFile = make_filepath(0, outStatusDir, statName.c_str());
672 return statusFile;
673 }
674
675 class PreParser {
676 public:
PreParser(int dirNo,bool threaded)677 PreParser(int dirNo, bool threaded)
678 : fDirNo(dirNo)
679 , fIndex(0)
680 , fThreaded(threaded) {
681 SkString statusPath = makeStatusString(dirNo);
682 if (!sk_exists(statusPath.c_str())) {
683 return;
684 }
685 SkFILEStream reader;
686 reader.setPath(statusPath.c_str());
687 while (fetch(reader, &fResults.push_back()))
688 ;
689 fResults.pop_back();
690 }
691
fetch(SkFILEStream & reader,TestResult * result)692 bool fetch(SkFILEStream& reader, TestResult* result) {
693 char c;
694 int i = 0;
695 result->init(fDirNo);
696 result->fPixelError = 0;
697 result->fTime = 0;
698 do {
699 bool readOne = reader.read(&c, 1) != 0;
700 if (!readOne) {
701 // SkASSERT(i == 0); // the current text may be incomplete -- if so, ignore it
702 return false;
703 }
704 if (c == ' ') {
705 result->fFilename[i++] = '\0';
706 break;
707 }
708 result->fFilename[i++] = c;
709 SkASSERT(i < kMaxLength);
710 } while (true);
711 do {
712 if (!reader.read(&c, 1)) {
713 return false;
714 }
715 if (c == ' ') {
716 break;
717 }
718 SkASSERT(c >= '0' && c <= '9');
719 result->fPixelError = result->fPixelError * 10 + (c - '0');
720 } while (true);
721 bool minus = false;
722 do {
723 if (!reader.read(&c, 1)) {
724 return false;
725 }
726 if (c == '\n') {
727 break;
728 }
729 if (c == '-') {
730 minus = true;
731 continue;
732 }
733 SkASSERT(c >= '0' && c <= '9');
734 result->fTime = result->fTime * 10 + (c - '0');
735 } while (true);
736 if (minus) {
737 result->fTime = -result->fTime;
738 }
739 return true;
740 }
741
match(const SkString & filename,SkFILEWStream * stream,TestResult * result)742 bool match(const SkString& filename, SkFILEWStream* stream, TestResult* result) {
743 if (fThreaded) {
744 for (int index = 0; index < fResults.count(); ++index) {
745 const TestResult& test = fResults[index];
746 if (filename.equals(test.fFilename)) {
747 *result = test;
748 SkString outStr(result->status());
749 stream->write(outStr.c_str(), outStr.size());
750 return true;
751 }
752 }
753 } else if (fIndex < fResults.count()) {
754 *result = fResults[fIndex++];
755 SkASSERT(filename.equals(result->fFilename));
756 SkString outStr(result->status());
757 stream->write(outStr.c_str(), outStr.size());
758 return true;
759 }
760 return false;
761 }
762
763 private:
764 int fDirNo;
765 int fIndex;
766 SkTArray<TestResult, true> fResults;
767 bool fThreaded;
768 };
769
doOneDir(TestState * state,bool threaded)770 static bool doOneDir(TestState* state, bool threaded) {
771 int dirNo = state->fResult.fDirNo;
772 skiatest::Reporter* reporter = state->fReporter;
773 SkString dirName = make_in_dir_name(dirNo);
774 if (!dirName.size()) {
775 return false;
776 }
777 SkOSFile::Iter iter(dirName.c_str(), "skp");
778 SkString filename;
779 int testCount = 0;
780 PreParser preParser(dirNo, threaded);
781 SkFILEWStream statusStream(makeStatusString(dirNo).c_str());
782 while (iter.next(&filename)) {
783 for (size_t index = 0; index < skipOverSeptCount; ++index) {
784 if (skipOverSept[index].directory == dirNo
785 && strcmp(filename.c_str(), skipOverSept[index].filename) == 0) {
786 goto checkEarlyExit;
787 }
788 }
789 if (preParser.match(filename, &statusStream, &state->fResult)) {
790 (void) addError(state, state->fResult);
791 ++testCount;
792 goto checkEarlyExit;
793 }
794 {
795 TestResult& result = state->fResult;
796 result.test(dirNo, filename);
797 SkString outStr(result.status());
798 statusStream.write(outStr.c_str(), outStr.size());
799 statusStream.flush();
800 if (addError(state, result)) {
801 SkDebugf("%s", result.progress().c_str());
802 }
803 }
804 ++testCount;
805 if (reporter->verbose()) {
806 SkDebugf(".");
807 if (++testCount % 100 == 0) {
808 SkDebugf("%d\n", testCount);
809 }
810 }
811 checkEarlyExit:
812 if (0 && testCount >= 1) {
813 return true;
814 }
815 }
816 return true;
817 }
818
initTest()819 static bool initTest() {
820 #if !defined SK_BUILD_FOR_WIN && !defined SK_BUILD_FOR_MAC
821 SK_CONF_SET("images.jpeg.suppressDecoderWarnings", true);
822 SK_CONF_SET("images.png.suppressDecoderWarnings", true);
823 #endif
824 return make_out_dirs();
825 }
826
initUberTest(int firstDirNo,int lastDirNo)827 static bool initUberTest(int firstDirNo, int lastDirNo) {
828 if (!initTest()) {
829 return false;
830 }
831 for (int index = firstDirNo; index <= lastDirNo; ++index) {
832 SkString statusDir(outStatusDir);
833 statusDir.appendf("%d", index);
834 if (!make_one_out_dir(statusDir.c_str())) {
835 return false;
836 }
837 }
838 return true;
839 }
840
841
testSkpClipEncode(TestState * data)842 static void testSkpClipEncode(TestState* data) {
843 data->fResult.testOne();
844 if (data->fReporter->verbose()) {
845 SkDebugf("+");
846 }
847 }
848
encodeFound(skiatest::Reporter * reporter,TestState & state)849 static void encodeFound(skiatest::Reporter* reporter, TestState& state) {
850 if (reporter->verbose()) {
851 if (state.fPixelWorst.count()) {
852 SkTDArray<SortByPixel*> worst;
853 for (int index = 0; index < state.fPixelWorst.count(); ++index) {
854 *worst.append() = &state.fPixelWorst[index];
855 }
856 SkTQSort<SortByPixel>(worst.begin(), worst.end() - 1);
857 for (int index = 0; index < state.fPixelWorst.count(); ++index) {
858 const TestResult& result = *worst[index];
859 SkDebugf("%d %s pixelError=%d\n", result.fDirNo, result.fFilename, result.fPixelError);
860 }
861 }
862 if (state.fSlowest.count()) {
863 SkTDArray<SortByTime*> slowest;
864 for (int index = 0; index < state.fSlowest.count(); ++index) {
865 *slowest.append() = &state.fSlowest[index];
866 }
867 if (slowest.count() > 0) {
868 SkTQSort<SortByTime>(slowest.begin(), slowest.end() - 1);
869 for (int index = 0; index < slowest.count(); ++index) {
870 const TestResult& result = *slowest[index];
871 SkDebugf("%d %s time=%d\n", result.fDirNo, result.fFilename, result.fTime);
872 }
873 }
874 }
875 }
876
877 int threadCount = reporter->allowThreaded() ? SkThreadPool::kThreadPerCore : 1;
878 TestRunner testRunner(reporter, threadCount);
879 for (int index = 0; index < state.fPixelWorst.count(); ++index) {
880 const TestResult& result = state.fPixelWorst[index];
881 SkString filename(result.fFilename);
882 if (!filename.endsWith(".skp")) {
883 filename.append(".skp");
884 }
885 *testRunner.fRunnables.append() = SkNEW_ARGS(TestRunnableEncode,
886 (&testSkpClipEncode, result.fDirNo, filename.c_str(), &testRunner));
887 }
888 testRunner.render();
889 #if 0
890 for (int index = 0; index < state.fPixelWorst.count(); ++index) {
891 const TestResult& result = state.fPixelWorst[index];
892 SkString filename(result.fFilename);
893 if (!filename.endsWith(".skp")) {
894 filename.append(".skp");
895 }
896 TestResult::Test(result.fDirNo, filename.c_str(), kEncodeFiles);
897 if (reporter->verbose()) SkDebugf("+");
898 }
899 #endif
900 }
901
DEF_TEST(PathOpsSkpClip,reporter)902 DEF_TEST(PathOpsSkpClip, reporter) {
903 if (!initTest()) {
904 return;
905 }
906 SkTArray<TestResult, true> errors;
907 TestState state;
908 state.init(0, reporter);
909 for (int dirNo = 1; dirNo <= 100; ++dirNo) {
910 if (reporter->verbose()) {
911 SkDebugf("dirNo=%d\n", dirNo);
912 }
913 state.fResult.fDirNo = dirNo;
914 if (!doOneDir(&state, false)) {
915 break;
916 }
917 }
918 encodeFound(reporter, state);
919 }
920
testSkpClipMain(TestState * data)921 static void testSkpClipMain(TestState* data) {
922 (void) doOneDir(data, true);
923 }
924
DEF_TEST(PathOpsSkpClipThreaded,reporter)925 DEF_TEST(PathOpsSkpClipThreaded, reporter) {
926 if (!initTest()) {
927 return;
928 }
929 int threadCount = reporter->allowThreaded() ? SkThreadPool::kThreadPerCore : 1;
930 TestRunner testRunner(reporter, threadCount);
931 const int firstDirNo = 1;
932 for (int dirNo = firstDirNo; dirNo <= 100; ++dirNo) {
933 *testRunner.fRunnables.append() = SkNEW_ARGS(TestRunnableDir,
934 (&testSkpClipMain, dirNo, &testRunner));
935 }
936 testRunner.render();
937 TestState state;
938 state.init(0, reporter);
939 for (int dirNo = firstDirNo; dirNo <= 100; ++dirNo) {
940 TestState& testState = testRunner.fRunnables[dirNo - 1]->fState;
941 SkASSERT(testState.fResult.fDirNo == dirNo);
942 for (int inner = 0; inner < testState.fPixelWorst.count(); ++inner) {
943 addError(&state, testState.fPixelWorst[inner]);
944 }
945 for (int inner = 0; inner < testState.fSlowest.count(); ++inner) {
946 addError(&state, testState.fSlowest[inner]);
947 }
948 }
949 encodeFound(reporter, state);
950 }
951
testSkpClipUber(TestState * data)952 static void testSkpClipUber(TestState* data) {
953 data->fResult.testOne();
954 SkString dirName = make_stat_dir_name(data->fResult.fDirNo);
955 if (!dirName.size()) {
956 return;
957 }
958 SkString statName(data->fResult.fFilename);
959 SkASSERT(statName.endsWith(".skp"));
960 statName.remove(statName.size() - 4, 4);
961 statName.appendf(".%d.%d.skp", data->fResult.fPixelError, data->fResult.fTime);
962 SkString statusFile = make_filepath(data->fResult.fDirNo, outStatusDir, statName.c_str());
963 SkFILE* file = sk_fopen(statusFile.c_str(), kWrite_SkFILE_Flag);
964 if (!file) {
965 SkDebugf("failed to create %s", statusFile.c_str());
966 return;
967 }
968 sk_fclose(file);
969 if (data->fReporter->verbose()) {
970 if (data->fResult.fPixelError || data->fResult.fTime) {
971 SkDebugf("%s", data->fResult.progress().c_str());
972 } else {
973 SkDebugf(".");
974 }
975 }
976 }
977
buildTests(skiatest::Reporter * reporter,int firstDirNo,int lastDirNo,SkTDArray<TestResult> * tests,SkTDArray<SortByName * > * sorted)978 static bool buildTests(skiatest::Reporter* reporter, int firstDirNo, int lastDirNo, SkTDArray<TestResult>* tests,
979 SkTDArray<SortByName*>* sorted) {
980 for (int dirNo = firstDirNo; dirNo <= lastDirNo; ++dirNo) {
981 SkString dirName = make_stat_dir_name(dirNo);
982 if (!dirName.size()) {
983 return false;
984 }
985 SkOSFile::Iter iter(dirName.c_str(), "skp");
986 SkString filename;
987 while (iter.next(&filename)) {
988 TestResult test;
989 test.init(dirNo);
990 SkString spaceFile(filename);
991 char* spaces = spaceFile.writable_str();
992 int spaceSize = (int) spaceFile.size();
993 for (int index = 0; index < spaceSize; ++index) {
994 if (spaces[index] == '.') {
995 spaces[index] = ' ';
996 }
997 }
998 int success = sscanf(spaces, "%s %d %d skp", test.fFilename,
999 &test.fPixelError, &test.fTime);
1000 if (success < 3) {
1001 SkDebugf("failed to scan %s matched=%d\n", filename.c_str(), success);
1002 return false;
1003 }
1004 *tests[dirNo - firstDirNo].append() = test;
1005 }
1006 if (!sorted) {
1007 continue;
1008 }
1009 SkTDArray<TestResult>& testSet = tests[dirNo - firstDirNo];
1010 int count = testSet.count();
1011 for (int index = 0; index < count; ++index) {
1012 *sorted[dirNo - firstDirNo].append() = (SortByName*) &testSet[index];
1013 }
1014 if (sorted[dirNo - firstDirNo].count()) {
1015 SkTQSort<SortByName>(sorted[dirNo - firstDirNo].begin(),
1016 sorted[dirNo - firstDirNo].end() - 1);
1017 if (reporter->verbose()) {
1018 SkDebugf("+");
1019 }
1020 }
1021 }
1022 return true;
1023 }
1024
1025 bool Less(const SortByName& a, const SortByName& b);
Less(const SortByName & a,const SortByName & b)1026 bool Less(const SortByName& a, const SortByName& b) {
1027 return a < b;
1028 }
1029
DEF_TEST(PathOpsSkpClipUberThreaded,reporter)1030 DEF_TEST(PathOpsSkpClipUberThreaded, reporter) {
1031 const int firstDirNo = 1;
1032 const int lastDirNo = 100;
1033 if (!initUberTest(firstDirNo, lastDirNo)) {
1034 return;
1035 }
1036 const int dirCount = lastDirNo - firstDirNo + 1;
1037 SkTDArray<TestResult> tests[dirCount];
1038 SkTDArray<SortByName*> sorted[dirCount];
1039 if (!buildTests(reporter, firstDirNo, lastDirNo, tests, sorted)) {
1040 return;
1041 }
1042 int threadCount = reporter->allowThreaded() ? SkThreadPool::kThreadPerCore : 1;
1043 TestRunner testRunner(reporter, threadCount);
1044 for (int dirNo = firstDirNo; dirNo <= lastDirNo; ++dirNo) {
1045 SkString dirName = make_in_dir_name(dirNo);
1046 if (!dirName.size()) {
1047 continue;
1048 }
1049 SkOSFile::Iter iter(dirName.c_str(), "skp");
1050 SkString filename;
1051 while (iter.next(&filename)) {
1052 int count;
1053 SortByName name;
1054 for (size_t index = 0; index < skipOverSeptCount; ++index) {
1055 if (skipOverSept[index].directory == dirNo
1056 && strcmp(filename.c_str(), skipOverSept[index].filename) == 0) {
1057 goto checkEarlyExit;
1058 }
1059 }
1060 name.init(dirNo);
1061 strncpy(name.fFilename, filename.c_str(), filename.size() - 4); // drop .skp
1062 count = sorted[dirNo - firstDirNo].count();
1063 if (SkTSearch<SortByName, Less>(sorted[dirNo - firstDirNo].begin(),
1064 count, &name, sizeof(&name)) < 0) {
1065 *testRunner.fRunnables.append() = SkNEW_ARGS(TestRunnableFile,
1066 (&testSkpClipUber, dirNo, filename.c_str(), &testRunner));
1067 }
1068 checkEarlyExit:
1069 ;
1070 }
1071
1072 }
1073 testRunner.render();
1074 SkTDArray<TestResult> results[dirCount];
1075 if (!buildTests(reporter, firstDirNo, lastDirNo, results, NULL)) {
1076 return;
1077 }
1078 SkTDArray<TestResult> allResults;
1079 for (int dirNo = firstDirNo; dirNo <= lastDirNo; ++dirNo) {
1080 SkTDArray<TestResult>& array = results[dirNo - firstDirNo];
1081 allResults.append(array.count(), array.begin());
1082 }
1083 int allCount = allResults.count();
1084 SkTDArray<SortByPixel*> pixels;
1085 SkTDArray<SortByTime*> times;
1086 for (int index = 0; index < allCount; ++index) {
1087 *pixels.append() = (SortByPixel*) &allResults[index];
1088 *times.append() = (SortByTime*) &allResults[index];
1089 }
1090 TestState state;
1091 if (pixels.count()) {
1092 SkTQSort<SortByPixel>(pixels.begin(), pixels.end() - 1);
1093 for (int inner = 0; inner < kMaxFiles; ++inner) {
1094 *state.fPixelWorst.append() = *pixels[allCount - inner - 1];
1095 }
1096 }
1097 if (times.count()) {
1098 SkTQSort<SortByTime>(times.begin(), times.end() - 1);
1099 for (int inner = 0; inner < kMaxFiles; ++inner) {
1100 *state.fSlowest.append() = *times[allCount - inner - 1];
1101 }
1102 }
1103 encodeFound(reporter, state);
1104 }
1105
DEF_TEST(PathOpsSkpClipOneOff,reporter)1106 DEF_TEST(PathOpsSkpClipOneOff, reporter) {
1107 if (!initTest()) {
1108 return;
1109 }
1110 const int testIndex = 43 - 37;
1111 int dirNo = skipOverSept[testIndex].directory;
1112 SkAssertResult(make_in_dir_name(dirNo).size());
1113 SkString filename(skipOverSept[testIndex].filename);
1114 TestResult state;
1115 state.test(dirNo, filename);
1116 if (reporter->verbose()) {
1117 SkDebugf("%s", state.status().c_str());
1118 }
1119 state.fTestStep = kEncodeFiles;
1120 state.testOne();
1121 }
1122