OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright 2014 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 "SkBitmap.h" |
| 9 #include "SkCanvas.h" |
| 10 #include "SkColorPriv.h" |
| 11 #include "SkDither.h" |
| 12 #include "SkImagePriv.h" |
| 13 #include "SkPixelRef.h" |
| 14 |
| 15 #ifdef SK_SUPPORT_LEGACY_BITMAP_COMPUTESIZE |
| 16 int SkBitmap::ComputeBytesPerPixel(SkBitmap::Config config) { |
| 17 int bpp; |
| 18 switch (config) { |
| 19 case kNo_Config: |
| 20 bpp = 0; // not applicable |
| 21 break; |
| 22 case kA8_Config: |
| 23 case kIndex8_Config: |
| 24 bpp = 1; |
| 25 break; |
| 26 case kRGB_565_Config: |
| 27 case kARGB_4444_Config: |
| 28 bpp = 2; |
| 29 break; |
| 30 case kARGB_8888_Config: |
| 31 bpp = 4; |
| 32 break; |
| 33 default: |
| 34 SkDEBUGFAIL("unknown config"); |
| 35 bpp = 0; // error |
| 36 break; |
| 37 } |
| 38 return bpp; |
| 39 } |
| 40 |
| 41 size_t SkBitmap::ComputeRowBytes(Config c, int width) { |
| 42 return SkColorTypeMinRowBytes(SkBitmapConfigToColorType(c), width); |
| 43 } |
| 44 |
| 45 int64_t SkBitmap::ComputeSize64(Config config, int width, int height) { |
| 46 SkColorType ct = SkBitmapConfigToColorType(config); |
| 47 int64_t rowBytes = sk_64_mul(SkColorTypeBytesPerPixel(ct), width); |
| 48 return rowBytes * height; |
| 49 } |
| 50 |
| 51 size_t SkBitmap::ComputeSize(Config c, int width, int height) { |
| 52 int64_t size = SkBitmap::ComputeSize64(c, width, height); |
| 53 return sk_64_isS32(size) ? sk_64_asS32(size) : 0; |
| 54 } |
| 55 |
| 56 int64_t SkBitmap::ComputeSafeSize64(Config config, |
| 57 uint32_t width, |
| 58 uint32_t height, |
| 59 size_t rowBytes) { |
| 60 SkImageInfo info = SkImageInfo::Make(width, height, |
| 61 SkBitmapConfigToColorType(config), |
| 62 kPremul_SkAlphaType); |
| 63 return info.getSafeSize64(rowBytes); |
| 64 } |
| 65 |
| 66 size_t SkBitmap::ComputeSafeSize(Config config, |
| 67 uint32_t width, |
| 68 uint32_t height, |
| 69 size_t rowBytes) { |
| 70 int64_t safeSize = ComputeSafeSize64(config, width, height, rowBytes); |
| 71 int32_t safeSize32 = (int32_t)safeSize; |
| 72 |
| 73 if (safeSize32 != safeSize) { |
| 74 safeSize32 = 0; |
| 75 } |
| 76 return safeSize32; |
| 77 } |
| 78 #endif |
| 79 |
| 80 #ifdef SK_SUPPORT_LEGACY_BITMAPCONFIG |
| 81 |
| 82 SkBitmap::Config SkBitmap::config() const { |
| 83 return SkColorTypeToBitmapConfig(fInfo.colorType()); |
| 84 } |
| 85 |
| 86 bool SkBitmap::setConfig(Config config, int width, int height, size_t rowBytes, |
| 87 SkAlphaType alphaType) { |
| 88 SkColorType ct = SkBitmapConfigToColorType(config); |
| 89 return this->setConfig(SkImageInfo::Make(width, height, ct, alphaType), |
| 90 rowBytes); |
| 91 } |
| 92 |
| 93 bool SkBitmap::allocConfigPixels(Config config, int width, int height, |
| 94 bool isOpaque) { |
| 95 SkColorType ct = SkBitmapConfigToColorType(config); |
| 96 SkAlphaType at = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType; |
| 97 return this->allocPixels(SkImageInfo::Make(width, height, ct, at)); |
| 98 } |
| 99 |
| 100 /////////////////////////////////////////////////////////////////////////////// |
| 101 |
| 102 bool SkBitmap::canCopyTo(Config dstConfig) const { |
| 103 return this->canCopyTo(SkBitmapConfigToColorType(dstConfig)); |
| 104 } |
| 105 |
| 106 bool SkBitmap::copyTo(SkBitmap* dst, Config dstConfig, Allocator* alloc) const { |
| 107 if (!this->canCopyTo(dstConfig)) { |
| 108 return false; |
| 109 } |
| 110 |
| 111 // if we have a texture, first get those pixels |
| 112 SkBitmap tmpSrc; |
| 113 const SkBitmap* src = this; |
| 114 |
| 115 if (fPixelRef) { |
| 116 SkIRect subset; |
| 117 subset.setXYWH(fPixelRefOrigin.fX, fPixelRefOrigin.fY, |
| 118 fInfo.width(), fInfo.height()); |
| 119 if (fPixelRef->readPixels(&tmpSrc, &subset)) { |
| 120 SkASSERT(tmpSrc.width() == this->width()); |
| 121 SkASSERT(tmpSrc.height() == this->height()); |
| 122 |
| 123 // did we get lucky and we can just return tmpSrc? |
| 124 if (tmpSrc.config() == dstConfig && NULL == alloc) { |
| 125 dst->swap(tmpSrc); |
| 126 // If the result is an exact copy, clone the gen ID. |
| 127 if (dst->pixelRef() && dst->pixelRef()->info() == fPixelRef->inf
o()) { |
| 128 dst->pixelRef()->cloneGenID(*fPixelRef); |
| 129 } |
| 130 return true; |
| 131 } |
| 132 |
| 133 // fall through to the raster case |
| 134 src = &tmpSrc; |
| 135 } |
| 136 } |
| 137 |
| 138 // we lock this now, since we may need its colortable |
| 139 SkAutoLockPixels srclock(*src); |
| 140 if (!src->readyToDraw()) { |
| 141 return false; |
| 142 } |
| 143 |
| 144 // The only way to be readyToDraw is if fPixelRef is non NULL. |
| 145 SkASSERT(fPixelRef != NULL); |
| 146 |
| 147 SkBitmap tmpDst; |
| 148 tmpDst.setConfig(dstConfig, src->width(), src->height(), 0, |
| 149 src->alphaType()); |
| 150 |
| 151 // allocate colortable if srcConfig == kIndex8_Config |
| 152 SkColorTable* ctable = (dstConfig == kIndex8_Config) ? |
| 153 new SkColorTable(*src->getColorTable()) : NULL; |
| 154 SkAutoUnref au(ctable); |
| 155 if (!tmpDst.allocPixels(alloc, ctable)) { |
| 156 return false; |
| 157 } |
| 158 |
| 159 if (!tmpDst.readyToDraw()) { |
| 160 // allocator/lock failed |
| 161 return false; |
| 162 } |
| 163 |
| 164 // pixelRef must be non NULL or tmpDst.readyToDraw() would have |
| 165 // returned false. |
| 166 SkASSERT(tmpDst.pixelRef() != NULL); |
| 167 |
| 168 /* do memcpy for the same configs cases, else use drawing |
| 169 */ |
| 170 if (src->config() == dstConfig) { |
| 171 if (tmpDst.getSize() == src->getSize()) { |
| 172 memcpy(tmpDst.getPixels(), src->getPixels(), src->getSafeSize()); |
| 173 SkPixelRef* pixelRef = tmpDst.pixelRef(); |
| 174 |
| 175 // In order to reach this point, we know that the width, config and |
| 176 // rowbytes of the SkPixelRefs are the same, but it is possible for |
| 177 // the heights to differ, if this SkBitmap's height is a subset of |
| 178 // fPixelRef. Only if the SkPixelRefs' heights match are we |
| 179 // guaranteed that this is an exact copy, meaning we should clone |
| 180 // the genID. |
| 181 if (pixelRef->info().fHeight == fPixelRef->info().fHeight) { |
| 182 // TODO: what to do if the two infos match, BUT |
| 183 // fPixelRef is premul and pixelRef is opaque? |
| 184 // skipping assert for now |
| 185 // https://code.google.com/p/skia/issues/detail?id=2012 |
| 186 // SkASSERT(pixelRef->info() == fPixelRef->info()
); |
| 187 SkASSERT(pixelRef->info().fWidth == fPixelRef->info().fWidth); |
| 188 SkASSERT(pixelRef->info().fColorType == fPixelRef->info().fColor
Type); |
| 189 pixelRef->cloneGenID(*fPixelRef); |
| 190 } |
| 191 } else { |
| 192 const char* srcP = reinterpret_cast<const char*>(src->getPixels()); |
| 193 char* dstP = reinterpret_cast<char*>(tmpDst.getPixels()); |
| 194 // to be sure we don't read too much, only copy our logical pixels |
| 195 size_t bytesToCopy = tmpDst.width() * tmpDst.bytesPerPixel(); |
| 196 for (int y = 0; y < tmpDst.height(); y++) { |
| 197 memcpy(dstP, srcP, bytesToCopy); |
| 198 srcP += src->rowBytes(); |
| 199 dstP += tmpDst.rowBytes(); |
| 200 } |
| 201 } |
| 202 } else if (SkBitmap::kARGB_4444_Config == dstConfig |
| 203 && SkBitmap::kARGB_8888_Config == src->config()) { |
| 204 SkASSERT(src->height() == tmpDst.height()); |
| 205 SkASSERT(src->width() == tmpDst.width()); |
| 206 for (int y = 0; y < src->height(); ++y) { |
| 207 SkPMColor16* SK_RESTRICT dstRow = (SkPMColor16*) tmpDst.getAddr16(0,
y); |
| 208 SkPMColor* SK_RESTRICT srcRow = (SkPMColor*) src->getAddr32(0, y); |
| 209 DITHER_4444_SCAN(y); |
| 210 for (int x = 0; x < src->width(); ++x) { |
| 211 dstRow[x] = SkDitherARGB32To4444(srcRow[x], |
| 212 DITHER_VALUE(x)); |
| 213 } |
| 214 } |
| 215 } else { |
| 216 // Always clear the dest in case one of the blitters accesses it |
| 217 // TODO: switch the allocation of tmpDst to call sk_calloc_throw |
| 218 tmpDst.eraseColor(SK_ColorTRANSPARENT); |
| 219 |
| 220 SkCanvas canvas(tmpDst); |
| 221 SkPaint paint; |
| 222 |
| 223 paint.setDither(true); |
| 224 canvas.drawBitmap(*src, 0, 0, &paint); |
| 225 } |
| 226 |
| 227 dst->swap(tmpDst); |
| 228 return true; |
| 229 } |
| 230 |
| 231 bool SkBitmap::deepCopyTo(SkBitmap* dst, Config dstConfig) const { |
| 232 const SkColorType dstCT = SkBitmapConfigToColorType(dstConfig); |
| 233 |
| 234 if (!this->canCopyTo(dstConfig)) { |
| 235 return false; |
| 236 } |
| 237 |
| 238 // If we have a PixelRef, and it supports deep copy, use it. |
| 239 // Currently supported only by texture-backed bitmaps. |
| 240 if (fPixelRef) { |
| 241 SkPixelRef* pixelRef = fPixelRef->deepCopy(dstConfig); |
| 242 if (pixelRef) { |
| 243 uint32_t rowBytes; |
| 244 if (this->colorType() == dstCT) { |
| 245 // Since there is no subset to pass to deepCopy, and deepCopy |
| 246 // succeeded, the new pixel ref must be identical. |
| 247 SkASSERT(fPixelRef->info() == pixelRef->info()); |
| 248 pixelRef->cloneGenID(*fPixelRef); |
| 249 // Use the same rowBytes as the original. |
| 250 rowBytes = fRowBytes; |
| 251 } else { |
| 252 // With the new config, an appropriate fRowBytes will be compute
d by setConfig. |
| 253 rowBytes = 0; |
| 254 } |
| 255 |
| 256 SkImageInfo info = fInfo; |
| 257 info.fColorType = dstCT; |
| 258 if (!dst->setConfig(info, rowBytes)) { |
| 259 return false; |
| 260 } |
| 261 dst->setPixelRef(pixelRef, fPixelRefOrigin)->unref(); |
| 262 return true; |
| 263 } |
| 264 } |
| 265 |
| 266 if (this->getTexture()) { |
| 267 return false; |
| 268 } else { |
| 269 return this->copyTo(dst, dstConfig, NULL); |
| 270 } |
| 271 } |
| 272 |
| 273 #endif |
OLD | NEW |