1363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger/* 2363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger * Copyright 2012 Google Inc. 3363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger * 4363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger * Use of this source code is governed by a BSD-style license that can be 5363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger * found in the LICENSE file. 6363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger */ 7363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "skdiff.h" 8363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "skdiff_utils.h" 9363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkBitmap.h" 10363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkData.h" 11363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkImageDecoder.h" 12363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkImageEncoder.h" 13363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkStream.h" 14363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkTemplates.h" 15363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkTypes.h" 16363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 17363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerbool are_buffers_equal(SkData* skdata1, SkData* skdata2) { 18363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if ((NULL == skdata1) || (NULL == skdata2)) { 19363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return false; 20363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 21363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (skdata1->size() != skdata2->size()) { 22363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return false; 23363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 24363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return (0 == memcmp(skdata1->data(), skdata2->data(), skdata1->size())); 25363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger} 26363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 27363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek SollenbergerSkData* read_file(const char* file_path) { 28363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkFILEStream fileStream(file_path); 29363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (!fileStream.isValid()) { 30363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf("WARNING: could not open file <%s> for reading\n", file_path); 31363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return NULL; 32363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 33363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger size_t bytesInFile = fileStream.getLength(); 34363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger size_t bytesLeftToRead = bytesInFile; 35363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 36363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger void* bufferStart = sk_malloc_throw(bytesInFile); 37363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger char* bufferPointer = (char*)bufferStart; 38363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger while (bytesLeftToRead > 0) { 39363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger size_t bytesReadThisTime = fileStream.read(bufferPointer, bytesLeftToRead); 40363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (0 == bytesReadThisTime) { 41363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf("WARNING: error reading from <%s>\n", file_path); 42363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger sk_free(bufferStart); 43363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return NULL; 44363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 45363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger bytesLeftToRead -= bytesReadThisTime; 46363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger bufferPointer += bytesReadThisTime; 47363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 48363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return SkData::NewFromMalloc(bufferStart, bytesInFile); 49363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger} 50363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 51363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerbool get_bitmap(SkData* fileBits, DiffResource& resource, SkImageDecoder::Mode mode) { 52363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkMemoryStream stream(fileBits->data(), fileBits->size()); 53363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 54363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkImageDecoder* codec = SkImageDecoder::Factory(&stream); 55363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (NULL == codec) { 56363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf("ERROR: no codec found for <%s>\n", resource.fFullPath.c_str()); 57363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger resource.fStatus = DiffResource::kCouldNotDecode_Status; 58363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return false; 59363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 60363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 61363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // In debug, the DLL will automatically be unloaded when this is deleted, 62363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // but that shouldn't be a problem in release mode. 63363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkAutoTDelete<SkImageDecoder> ad(codec); 64363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 65363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger stream.rewind(); 66363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (!codec->decode(&stream, &resource.fBitmap, SkBitmap::kARGB_8888_Config, mode)) { 67363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf("ERROR: codec failed for basePath <%s>\n", resource.fFullPath.c_str()); 68363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger resource.fStatus = DiffResource::kCouldNotDecode_Status; 69363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return false; 70363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 71363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 72363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger resource.fStatus = DiffResource::kDecoded_Status; 73363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return true; 74363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger} 75363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 76363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger/** Thanks to PNG, we need to force all pixels 100% opaque. */ 77363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic void force_all_opaque(const SkBitmap& bitmap) { 78363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkAutoLockPixels lock(bitmap); 79363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger for (int y = 0; y < bitmap.height(); y++) { 80363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger for (int x = 0; x < bitmap.width(); x++) { 81363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT); 82363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 83363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 84363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger} 85363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 86363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerbool write_bitmap(const SkString& path, const SkBitmap& bitmap) { 87363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkBitmap copy; 88363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger bitmap.copyTo(©, SkBitmap::kARGB_8888_Config); 89363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger force_all_opaque(copy); 90363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return SkImageEncoder::EncodeFile(path.c_str(), copy, 91363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkImageEncoder::kPNG_Type, 100); 92363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger} 93363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 94363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger/// Return a copy of the "input" string, within which we have replaced all instances 95363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger/// of oldSubstring with newSubstring. 96363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger/// 97363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger/// TODO: If we like this, we should move it into the core SkString implementation, 98363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger/// adding more checks and ample test cases, and paying more attention to efficiency. 99363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic SkString replace_all(const SkString &input, 100363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger const char oldSubstring[], const char newSubstring[]) { 101363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkString output; 102363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger const char *input_cstr = input.c_str(); 103363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger const char *first_char = input_cstr; 104363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger const char *match_char; 105363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger int oldSubstringLen = strlen(oldSubstring); 106363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger while (NULL != (match_char = strstr(first_char, oldSubstring))) { 107363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger output.append(first_char, (match_char - first_char)); 108363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger output.append(newSubstring); 109363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger first_char = match_char + oldSubstringLen; 110363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 111363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger output.append(first_char); 112363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return output; 113363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger} 114363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 115363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic SkString filename_to_derived_filename(const SkString& filename, const char *suffix) { 116363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkString diffName (filename); 117363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger const char* cstring = diffName.c_str(); 118363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger int dotOffset = strrchr(cstring, '.') - cstring; 119363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger diffName.remove(dotOffset, diffName.size() - dotOffset); 120363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger diffName.append(suffix); 121363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 122363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // In case we recursed into subdirectories, replace slashes with something else 123363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // so the diffs will all be written into a single flat directory. 124363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger diffName = replace_all(diffName, PATH_DIV_STR, "_"); 125363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return diffName; 126363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger} 127363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 128363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek SollenbergerSkString filename_to_diff_filename(const SkString& filename) { 129363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return filename_to_derived_filename(filename, "-diff.png"); 130363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger} 131363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 132363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek SollenbergerSkString filename_to_white_filename(const SkString& filename) { 133363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return filename_to_derived_filename(filename, "-white.png"); 134363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger} 135363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 136363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergervoid create_and_write_diff_image(DiffRecord* drp, 137363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger DiffMetricProc dmp, 138363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger const int colorThreshold, 139363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger const SkString& outputDir, 140363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger const SkString& filename) { 141363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger const int w = drp->fBase.fBitmap.width(); 142363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger const int h = drp->fBase.fBitmap.height(); 143363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 144363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (w != drp->fComparison.fBitmap.width() || h != drp->fComparison.fBitmap.height()) { 145363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fResult = DiffRecord::kDifferentSizes_Result; 146363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } else { 147363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fDifference.fBitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h); 148363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fDifference.fBitmap.allocPixels(); 149363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 150363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fWhite.fBitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h); 151363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fWhite.fBitmap.allocPixels(); 152363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 153363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkASSERT(DiffRecord::kUnknown_Result == drp->fResult); 154363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger compute_diff(drp, dmp, colorThreshold); 155363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkASSERT(DiffRecord::kUnknown_Result != drp->fResult); 156363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 157363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 158363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (outputDir.isEmpty()) { 159363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fDifference.fStatus = DiffResource::kUnspecified_Status; 160363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fWhite.fStatus = DiffResource::kUnspecified_Status; 161363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 162363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } else { 163363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fDifference.fFilename = filename_to_diff_filename(filename); 164363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fDifference.fFullPath = outputDir; 165363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fDifference.fFullPath.append(drp->fDifference.fFilename); 166363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fDifference.fStatus = DiffResource::kSpecified_Status; 167363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 168363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fWhite.fFilename = filename_to_white_filename(filename); 169363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fWhite.fFullPath = outputDir; 170363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fWhite.fFullPath.append(drp->fWhite.fFilename); 171363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fWhite.fStatus = DiffResource::kSpecified_Status; 172363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 173363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (DiffRecord::kDifferentPixels_Result == drp->fResult) { 174363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (write_bitmap(drp->fDifference.fFullPath, drp->fDifference.fBitmap)) { 175363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fDifference.fStatus = DiffResource::kExists_Status; 176363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } else { 177363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fDifference.fStatus = DiffResource::kDoesNotExist_Status; 178363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 179363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (write_bitmap(drp->fWhite.fFullPath, drp->fWhite.fBitmap)) { 180363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fWhite.fStatus = DiffResource::kExists_Status; 181363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } else { 182363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fWhite.fStatus = DiffResource::kDoesNotExist_Status; 183363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 184363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 185363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 186363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger} 187