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
33const 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
5697 http___www_brandyandvinca_com_.skp pixelError=3
5795 http___www_into_asia_com_.skp pixelError=12
5893 http___www_lunarplanner_com_.skp pixelError=14
5998 http___www_lovelyitalia_com_.skp pixelError=17
6090 http___www_inter_partner_blogspot_com_.skp pixelError=18
6199 http___www_maxarea_com_.skp pixelError=26
6298 http___www_maroonsnet_org_.skp pixelError=33
6392 http___www_belinaart_ru_.skp pixelError=50
64100 http___www_chroot_ro_.skp pixelError=62
6599 http___www_hsbrands_com_.skp pixelError=98
6695 http___www_tournamentindicator_com_.skp pixelError=122
6793 http___www_businesses_com_au_.skp pixelError=162
6890 http___www_regenesys_net_.skp pixelError=182
6988 http___www_1863544208148625103_c18eac63985503fa85b06358959c1ba27fc36f82_blogspot_com_.skp pixelError=186
7097 http___www_pregacoesevangelica_com_br_.skp pixelError=240
7177 http___www_zhenggang_org_.skp pixelError=284
7296 http___slidesharemailer_com_.skp pixelError=522
7394 http___www_gensteel_com_.skp pixelError=555
7468 http___www_jf_eti_br_.skp pixelError=610
7583 http___www_swishiat_com_.skp pixelError=706
7696 http___www_matusikmissive_com_au_.skp pixelError=2580
7795 http___www_momentumnation_com_.skp pixelError=3938
7892 http___www_rssowl_com_.skp pixelError=5113
7996 http___www_sexxygirl_tv_.skp pixelError=7605
8099 http___www_georgevalah_wordpress_com_.skp pixelError=8386
8178 http___www_furbo_org_.skp pixelError=8656
8278 http___www_djxhemary_wordpress_com_.skp pixelError=8976
83100 http___www_mindcontrolblackassassins_com_.skp pixelError=31950
8498 http___bababillgates_free_fr_.skp pixelError=40237
8598 http___hepatite_ro_.skp pixelError=44370
8686 http___www_somethingwagging_com_.skp pixelError=47794
8784 http___www_beverageuniverse_com_.skp pixelError=65450
8850 http___www_aveksa_com_.skp pixelError=68194
8910 http___www_publiker_pl_.skp pixelError=89997
9061 http___www_dominos_co_id_.skp pixelError=476868
9187 http___www_du_edu_om_.skp time=46
9287 http___www_bigload_de_.skp time=46
93100 http___www_home_forum_com_.skp time=48
9497 http___www_hotamateurchat_com_.skp time=48
9597 http___www_myrsky_com_cn_.skp time=48
9698 http___www_techiegeex_com_.skp time=49
9782 http___www_fashionoutletsofchicago_com_.skp time=50
9877 http___www_dynamischbureau_nl_.skp time=50
9982 http___www_mayihelpu_co_in_.skp time=50
10084 http___www_vbox7_com_user_history_viewers_.skp time=50
10185 http___www_ktokogda_com_.skp time=50
10285 http___www_propertyturkeysale_com_.skp time=50
10385 http___www_51play_com_.skp time=50
10486 http___www_bayalarm_com_.skp time=50
10587 http___www_eaglepictures_com_.skp time=50
10688 http___www_atlasakvaryum_com_.skp time=50
10791 http___www_pioneerchryslerjeep_com_.skp time=50
10894 http___www_thepulsemag_com_.skp time=50
10995 http___www_dcshoes_com_ph_.skp time=50
11096 http___www_montrealmassage_ca_.skp time=50
11196 http___www_jkshahclasses_com_.skp time=50
11296 http___www_webcamconsult_com_.skp time=51
113100 http___www_bsoscblog_com_.skp time=52
11495 http___www_flaktwoods_com_.skp time=53
11591 http___www_qivivo_com_.skp time=54
11690 http___www_unitender_com_.skp time=56
11797 http___www_casinogaming_com_.skp time=56
11897 http___www_rootdownload_com_.skp time=56
11994 http___www_aspa_ev_de_.skp time=57
12098 http___www_tenpieknyswiat_pl_.skp time=57
12193 http___www_transocean_de_.skp time=58
12294 http___www_vdo2_blogspot_com_.skp time=58
12394 http___www_asmaissexy_com_br_.skp time=58
124100 http___www_prefeiturasjm_com_br_.skp time=60
125100 http___www_eduinsuranceclick_blogspot_com_.skp time=60
12696 http___www_bobdunsire_com_.skp time=61
12796 http___www_omgkettlecorn_com_.skp time=61
12885 http___www_fbbsessions_com_.skp time=62
12986 http___www_hector_ru_.skp time=62
13087 http___www_wereldsupporter_nl_.skp time=62
13190 http___www_arello_com_.skp time=62
13293 http___www_bayerplastics_com_.skp time=62
13393 http___www_superandolamovida_com_ar_.skp time=62
13496 http___www_med_rbf_ru_.skp time=62
13581 http___www_carnegiescience_edu_.skp time=65
13687 http___www_asanewengland_com_.skp time=65
13792 http___www_turkce_karakter_appspot_com_.skp time=65
13894 http___www_k3a_org_.skp time=65
13996 http___www_powermaccenter_com_.skp time=65
14098 http___www_avto49_ru_.skp time=67
141100 http___www_hetoldeambaecht_nl_.skp time=68
14295 http___www_marine_ie_.skp time=69
14396 http___www_quebecvapeboutique_com_.skp time=69
14495 http___www_brays_ingles_com_.skp time=70
145100 http___www_lacondesa_com_.skp time=72
14695 http___www_timbarrathai_com_au_.skp time=76
14795 http___www_cuissedegrenouille_com_.skp time=76
14895 http___www_iwama51_ru_.skp time=76
14999 http___www_fotoantologia_it_.skp time=76
15092 http___www_indian_architects_com_.skp time=78
15192 http___www_totalwomanspa_com_.skp time=78
152100 http___www_fachverband_spielhallen_de_.skp time=83
15393 http___www_golshanemehr_ir_.skp time=84
15495 http___www_maryesses_com_.skp time=84
15599 http___www_ddcorp_ca_.skp time=89
15690 http___www_brontops_com_.skp time=89
15794 http___www_robgolding_com_.skp time=89
15891 http___www_tecban_com_br_.skp time=91
15998 http___www_costamesakarate_com_.skp time=100
16095 http___www_monsexyblog_com_.skp time=103
16197 http___www_stornowaygazette_co_uk_.skp time=103
16293 http___www_fitforaframe_com_.skp time=104
16398 http___www_intentionoftheday_com_.skp time=113
164100 http___www_tailgateclothing_com_.skp time=117
16595 http___www_senbros_com_.skp time=118
16693 http___www_lettoblog_com_.skp time=121
16794 http___www_maxineschallenge_com_au_.skp time=125
16895 http___www_savvycard_net_.skp time=127
16995 http___www_open_ac_mu_.skp time=129
17096 http___www_avgindia_in_.skp time=135
17197 http___www_stocktonseaview_com_.skp time=135
17296 http___www_distroller_com_.skp time=142
17394 http___www_travoggalop_dk_.skp time=144
174100 http___www_history_im_.skp time=144
17594 http___www_playradio_sk_.skp time=145
17692 http___www_linglongglass_com_.skp time=151
17797 http___www_bizzna_com_.skp time=151
17896 http___www_spiros_ws_.skp time=154
17991 http___www_rosen_meents_co_il_.skp time=156
18081 http___www_hoteldeluxeportland_com_.skp time=158
18192 http___www_freetennis_org_.skp time=161
18293 http___www_aircharternetwork_com_au_.skp time=161
18394 http___www_austinparks_org_.skp time=165
18489 http___www_bevvy_co_.skp time=168
18591 http___www_sosyalhile_net_.skp time=168
18698 http___www_minvih_gob_ve_.skp time=171
18789 http___www_streetfoodmtl_com_.skp time=172
18892 http___www_loveslatinas_tumblr_com_.skp time=178
18993 http___www_madbites_co_in_.skp time=180
19094 http___www_rocktarah_ir_.skp time=185
19197 http___www_penthouselife_com_.skp time=185
19296 http___www_appymonkey_com_.skp time=196
19392 http___www_pasargadhotels_com_.skp time=203
19499 http___www_marina_mil_pe_.skp time=203
19589 http___www_kays_co_uk_.skp time=205
19677 http___www_334588_com_.skp time=211
19783 http___www_trendbad24_de_.skp time=211
19881 http___www_cdnetworks_co_kr_.skp time=216
19994 http___www_schellgames_com_.skp time=223
20095 http___www_juliaweddingnews_cn_.skp time=230
20192 http___www_xcrafters_pl_.skp time=253
20293 http___www_pondoo_com_.skp time=253
20396 http___www_helsinkicapitalpartners_fi_.skp time=255
20488 http___www_nadtexican_com_.skp time=259
20585 http___www_canstockphoto_hu_.skp time=266
20678 http___www_ecovacs_com_cn_.skp time=271
20793 http___www_brookfieldplaceny_com_.skp time=334
20893 http___www_fmastrengthtraining_com_.skp time=337
20994 http___www_turtleonthebeach_com_.skp time=394
21090 http___www_temptationthemovie_com_.skp time=413
21195 http___www_patongsawaddi_com_.skp time=491
21291 http___www_online_radio_appspot_com_.skp time=511
21368 http___www_richardmiller_co_uk_.skp time=528
21463 http___www_eschrade_com_.skp time=543
21555 http___www_interaction_inf_br_.skp time=625
21638 http___www_huskyliners_com_.skp time=632
21786 http___granda_net_.skp time=1067
21824 http___www_cocacolafm_com_br_.skp time=1081
219*/
220
221size_t skipOverSeptCount = sizeof(skipOverSept) / sizeof(skipOverSept[0]);
222
223enum TestStep {
224    kCompareBits,
225    kEncodeFiles,
226};
227
228enum {
229    kMaxLength = 256,
230    kMaxFiles = 128,
231    kSmallLimit = 1000,
232};
233
234struct TestResult {
235    void init(int dirNo) {
236        fDirNo = dirNo;
237        sk_bzero(fFilename, sizeof(fFilename));
238        fTestStep = kCompareBits;
239        fScale = 1;
240    }
241
242    SkString status() {
243        SkString outStr;
244        outStr.printf("%s %d %d\n", fFilename, fPixelError, fTime);
245        return outStr;
246    }
247
248    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
265    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
273    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
289class SortByPixel : public TestResult {
290public:
291    bool operator<(const SortByPixel& rh) const {
292        return fPixelError < rh.fPixelError;
293    }
294};
295
296class SortByTime : public TestResult {
297public:
298    bool operator<(const SortByTime& rh) const {
299        return fTime < rh.fTime;
300    }
301};
302
303class SortByName : public TestResult {
304public:
305    bool operator<(const SortByName& rh) const {
306        return strcmp(fFilename, rh.fFilename) < 0;
307    }
308};
309
310struct TestState {
311    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
322struct TestRunner {
323    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
335class TestRunnable : public SkRunnable {
336public:
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
347class TestRunnableDir : public TestRunnable {
348public:
349    TestRunnableDir(void (*testFun)(TestState*), int dirNo, TestRunner* runner) {
350        fState.init(dirNo, runner->fReporter);
351        fTestFun = testFun;
352    }
353
354};
355
356class TestRunnableFile : public TestRunnable {
357public:
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
365class TestRunnableEncode : public TestRunnableFile {
366public:
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
373TestRunner::~TestRunner() {
374    for (int index = 0; index < fRunnables.count(); index++) {
375        SkDELETE(fRunnables[index]);
376    }
377}
378
379void 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
388static const char outOpDir[] = OUT_DIR "opClip";
389static const char outOldDir[] = OUT_DIR "oldClip";
390static const char outSkpDir[] = OUT_DIR "skpTest";
391static const char outDiffDir[] = OUT_DIR "outTest";
392static const char outStatusDir[] = OUT_DIR "statusTest";
393
394static 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
404static 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
414static 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
424static 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
435static 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
450static 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
457static 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
505static 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
540static 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
564static 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
576static 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
585void 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    }
662finish:
663    if (pic) {
664        pic->unref();
665    }
666}
667
668static 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
675class PreParser {
676public:
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
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
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
763private:
764    int fDirNo;
765    int fIndex;
766    SkTArray<TestResult, true> fResults;
767    bool fThreaded;
768};
769
770static 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        }
811checkEarlyExit:
812        if (0 && testCount >= 1) {
813            return true;
814        }
815    }
816    return true;
817}
818
819static 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
827static 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
842static void testSkpClipEncode(TestState* data) {
843    data->fResult.testOne();
844    if (data->fReporter->verbose()) {
845       SkDebugf("+");
846    }
847}
848
849static 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
902DEF_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
921static void testSkpClipMain(TestState* data) {
922        (void) doOneDir(data, true);
923}
924
925DEF_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
952static 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
978static 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
1025bool Less(const SortByName& a, const SortByName& b);
1026bool Less(const SortByName& a, const SortByName& b) {
1027    return a < b;
1028}
1029
1030DEF_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
1106DEF_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