1/* 2 * Copyright 2013 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "gm.h" 9 10#include "Resources.h" 11#include "SampleCode.h" 12#include "SkBlurMask.h" 13#include "SkBlurDrawLooper.h" 14#include "SkCanvas.h" 15#include "SkColorPriv.h" 16#include "SkForceLinking.h" 17#include "SkImageDecoder.h" 18#include "SkOSFile.h" 19#include "SkStream.h" 20#include "SkString.h" 21#include "SkSystemEventTypes.h" 22#include "SkTypes.h" 23#include "SkUtils.h" 24#include "SkView.h" 25 26__SK_FORCE_IMAGE_DECODER_LINKING; 27 28// Defined in SampleColorFilter.cpp 29extern SkShader* createChecker(); 30 31/** 32 * Interprets c as an unpremultiplied color, and returns the 33 * premultiplied equivalent. 34 */ 35static SkPMColor premultiply_unpmcolor(SkPMColor c) { 36 U8CPU a = SkGetPackedA32(c); 37 U8CPU r = SkGetPackedR32(c); 38 U8CPU g = SkGetPackedG32(c); 39 U8CPU b = SkGetPackedB32(c); 40 return SkPreMultiplyARGB(a, r, g, b); 41} 42 43class UnpremulView : public SampleView { 44public: 45 UnpremulView(SkString res) 46 : fResPath(res) 47 , fPremul(true) 48 , fDecodeSucceeded(false) { 49 this->nextImage(); 50 } 51 52protected: 53 // overrides from SkEventSink 54 virtual bool onQuery(SkEvent* evt) SK_OVERRIDE { 55 if (SampleCode::TitleQ(*evt)) { 56 SampleCode::TitleR(evt, "unpremul"); 57 return true; 58 } 59 SkUnichar uni; 60 if (SampleCode::CharQ(*evt, &uni)) { 61 char utf8[kMaxBytesInUTF8Sequence]; 62 size_t size = SkUTF8_FromUnichar(uni, utf8); 63 // Only consider events for single char keys 64 if (1 == size) { 65 switch (utf8[0]) { 66 case fNextImageChar: 67 this->nextImage(); 68 return true; 69 case fTogglePremulChar: 70 this->togglePremul(); 71 return true; 72 default: 73 break; 74 } 75 } 76 } 77 return this->INHERITED::onQuery(evt); 78 } 79 80 virtual void onDrawBackground(SkCanvas* canvas) SK_OVERRIDE { 81 SkPaint paint; 82 SkAutoTUnref<SkShader> shader(createChecker()); 83 paint.setShader(shader.get()); 84 canvas->drawPaint(paint); 85 } 86 87 virtual void onDrawContent(SkCanvas* canvas) SK_OVERRIDE { 88 SkPaint paint; 89 paint.setAntiAlias(true); 90 paint.setTextSize(SkIntToScalar(24)); 91 SkAutoTUnref<SkBlurDrawLooper> looper( 92 SkBlurDrawLooper::Create(SK_ColorBLUE, 93 SkBlurMask::ConvertRadiusToSigma(SkIntToScalar(2)), 94 0, 0)); 95 paint.setLooper(looper); 96 SkScalar height = paint.getFontMetrics(NULL); 97 if (!fDecodeSucceeded) { 98 SkString failure; 99 if (fResPath.size() == 0) { 100 failure.printf("resource path is required!"); 101 } else { 102 failure.printf("Failed to decode %s", fCurrFile.c_str()); 103 } 104 canvas->drawText(failure.c_str(), failure.size(), 0, height, paint); 105 return; 106 } 107 108 // Name, size of the file, and whether or not it is premultiplied. 109 SkString header(SkOSPath::SkBasename(fCurrFile.c_str())); 110 header.appendf(" [%dx%d] %s", fBitmap.width(), fBitmap.height(), 111 (fPremul ? "premultiplied" : "unpremultiplied")); 112 canvas->drawText(header.c_str(), header.size(), 0, height, paint); 113 canvas->translate(0, height); 114 115 // Help messages 116 header.printf("Press '%c' to move to the next image.'", fNextImageChar); 117 canvas->drawText(header.c_str(), header.size(), 0, height, paint); 118 canvas->translate(0, height); 119 120 header.printf("Press '%c' to toggle premultiplied decode.", fTogglePremulChar); 121 canvas->drawText(header.c_str(), header.size(), 0, height, paint); 122 123 // Now draw the image itself. 124 canvas->translate(height * 2, height * 2); 125 if (!fPremul) { 126 // A premultiplied bitmap cannot currently be drawn. 127 SkAutoLockPixels alp(fBitmap); 128 // Copy it to a bitmap which can be drawn, converting 129 // to premultiplied: 130 SkBitmap bm; 131 if (!bm.allocN32Pixels(fBitmap.width(), fBitmap.height())) { 132 SkString errMsg("allocPixels failed"); 133 canvas->drawText(errMsg.c_str(), errMsg.size(), 0, height, paint); 134 return; 135 } 136 for (int i = 0; i < fBitmap.width(); ++i) { 137 for (int j = 0; j < fBitmap.height(); ++j) { 138 *bm.getAddr32(i, j) = premultiply_unpmcolor(*fBitmap.getAddr32(i, j)); 139 } 140 } 141 canvas->drawBitmap(bm, 0, 0); 142 } else { 143 canvas->drawBitmap(fBitmap, 0, 0); 144 } 145 } 146 147private: 148 const SkString fResPath; 149 SkString fCurrFile; 150 bool fPremul; 151 bool fDecodeSucceeded; 152 SkBitmap fBitmap; 153 SkOSFile::Iter fFileIter; 154 155 static const char fNextImageChar = 'j'; 156 static const char fTogglePremulChar = 'h'; 157 158 void nextImage() { 159 if (fResPath.size() == 0) { 160 return; 161 } 162 SkString basename; 163 if (!fFileIter.next(&basename)) { 164 fFileIter.reset(fResPath.c_str()); 165 if (!fFileIter.next(&basename)) { 166 // Perhaps this should draw some error message? 167 return; 168 } 169 } 170 fCurrFile = SkOSPath::SkPathJoin(fResPath.c_str(), basename.c_str()); 171 this->decodeCurrFile(); 172 } 173 174 void decodeCurrFile() { 175 if (fCurrFile.size() == 0) { 176 fDecodeSucceeded = false; 177 return; 178 } 179 SkFILEStream stream(fCurrFile.c_str()); 180 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream)); 181 if (NULL == decoder.get()) { 182 fDecodeSucceeded = false; 183 return; 184 } 185 if (!fPremul) { 186 decoder->setRequireUnpremultipliedColors(true); 187 } 188 fDecodeSucceeded = decoder->decode(&stream, &fBitmap, kN32_SkColorType, 189 SkImageDecoder::kDecodePixels_Mode); 190 this->inval(NULL); 191 } 192 193 void togglePremul() { 194 fPremul = !fPremul; 195 this->decodeCurrFile(); 196 } 197 198 typedef SampleView INHERITED; 199}; 200 201////////////////////////////////////////////////////////////////////////////// 202 203static SkView* MyFactory() { 204 return new UnpremulView(GetResourcePath()); 205} 206static SkViewRegister reg(MyFactory); 207