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::Basename(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 bm.allocN32Pixels(fBitmap.width(), fBitmap.height()); 132 for (int i = 0; i < fBitmap.width(); ++i) { 133 for (int j = 0; j < fBitmap.height(); ++j) { 134 *bm.getAddr32(i, j) = premultiply_unpmcolor(*fBitmap.getAddr32(i, j)); 135 } 136 } 137 canvas->drawBitmap(bm, 0, 0); 138 } else { 139 canvas->drawBitmap(fBitmap, 0, 0); 140 } 141 } 142 143private: 144 const SkString fResPath; 145 SkString fCurrFile; 146 bool fPremul; 147 bool fDecodeSucceeded; 148 SkBitmap fBitmap; 149 SkOSFile::Iter fFileIter; 150 151 static const char fNextImageChar = 'j'; 152 static const char fTogglePremulChar = 'h'; 153 154 void nextImage() { 155 if (fResPath.size() == 0) { 156 return; 157 } 158 SkString basename; 159 if (!fFileIter.next(&basename)) { 160 fFileIter.reset(fResPath.c_str()); 161 if (!fFileIter.next(&basename)) { 162 // Perhaps this should draw some error message? 163 return; 164 } 165 } 166 fCurrFile = SkOSPath::Join(fResPath.c_str(), basename.c_str()); 167 this->decodeCurrFile(); 168 } 169 170 void decodeCurrFile() { 171 if (fCurrFile.size() == 0) { 172 fDecodeSucceeded = false; 173 return; 174 } 175 SkFILEStream stream(fCurrFile.c_str()); 176 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream)); 177 if (NULL == decoder.get()) { 178 fDecodeSucceeded = false; 179 return; 180 } 181 if (!fPremul) { 182 decoder->setRequireUnpremultipliedColors(true); 183 } 184 fDecodeSucceeded = decoder->decode(&stream, &fBitmap, kN32_SkColorType, 185 SkImageDecoder::kDecodePixels_Mode); 186 this->inval(NULL); 187 } 188 189 void togglePremul() { 190 fPremul = !fPremul; 191 this->decodeCurrFile(); 192 } 193 194 typedef SampleView INHERITED; 195}; 196 197////////////////////////////////////////////////////////////////////////////// 198 199static SkView* MyFactory() { 200 return new UnpremulView(GetResourcePath()); 201} 202static SkViewRegister reg(MyFactory); 203