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