| /* | |
| * Copyright 2022 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "src/gpu/ganesh/GrAtlasTypes.h" | |
| #include "include/core/SkAlphaType.h" | |
| #include "include/core/SkImageInfo.h" | |
| #include "include/core/SkPixmap.h" | |
| #include "include/private/SkMalloc.h" | |
| #include "src/core/SkSwizzlePriv.h" | |
| GrPlot::GrPlot(int pageIndex, | |
| int plotIndex, | |
| GrAtlasGenerationCounter* generationCounter, | |
| int offX, int offY, | |
| int width, int height, | |
| SkColorType colorType, | |
| size_t bpp) | |
| : fLastUpload(skgpu::Token::InvalidToken()) | |
| , fLastUse(skgpu::Token::InvalidToken()) | |
| , fFlushesSinceLastUse(0) | |
| , fGenerationCounter(generationCounter) | |
| , fGenID(fGenerationCounter->next()) | |
| , fPlotLocator(pageIndex, plotIndex, fGenID) | |
| , fData(nullptr) | |
| , fWidth(width) | |
| , fHeight(height) | |
| , fX(offX) | |
| , fY(offY) | |
| , fRectanizer(width, height) | |
| , fOffset(SkIPoint16::Make(fX * fWidth, fY * fHeight)) | |
| , fColorType(colorType) | |
| , fBytesPerPixel(bpp) | |
| #ifdef SK_DEBUG | |
| , fDirty(false) | |
| , fUploadInFlight(false) | |
| #endif | |
| { | |
| // We expect the allocated dimensions to be a multiple of 4 bytes | |
| SkASSERT(((width*fBytesPerPixel) & 0x3) == 0); | |
| // The padding for faster uploads only works for 1, 2 and 4 byte texels | |
| SkASSERT(fBytesPerPixel != 3 && fBytesPerPixel <= 4); | |
| fDirtyRect.setEmpty(); | |
| } | |
| GrPlot::~GrPlot() { | |
| sk_free(fData); | |
| } | |
| bool GrPlot::addRect(int width, int height, GrAtlasLocator* atlasLocator) { | |
| SkASSERT(width <= fWidth && height <= fHeight); | |
| SkIPoint16 loc; | |
| if (!fRectanizer.addRect(width, height, &loc)) { | |
| return false; | |
| } | |
| auto rect = GrIRect16::MakeXYWH(loc.fX, loc.fY, width, height); | |
| fDirtyRect.join({rect.fLeft, rect.fTop, rect.fRight, rect.fBottom}); | |
| rect.offset(fOffset.fX, fOffset.fY); | |
| atlasLocator->updateRect(rect); | |
| SkDEBUGCODE(fDirty = true;) | |
| return true; | |
| } | |
| void* GrPlot::dataAt(SkIPoint atlasPoint) { | |
| if (!fData) { | |
| // We use calloc here because our contract is that all pixel data is initially zero. | |
| // This is of particular importance when a caller uses padding with prepForRender(). | |
| fData = reinterpret_cast<std::byte*>(sk_calloc_throw(this->rowBytes() * fHeight)); | |
| } | |
| auto localPoint = atlasPoint - SkIPoint{fOffset.fX, fOffset.fY}; | |
| SkASSERT(localPoint.fX >= 0 && localPoint.fX < fWidth); | |
| SkASSERT(localPoint.fY >= 0 && localPoint.fY < fHeight); | |
| size_t offset = fBytesPerPixel * (localPoint.fY * fWidth + localPoint.fX); | |
| return fData + offset; | |
| } | |
| bool GrPlot::addSubImage(int width, int height, const void* image, GrAtlasLocator* atlasLocator) { | |
| if (!this->addRect(width, height, atlasLocator)) { | |
| return false; | |
| } | |
| const unsigned char* imagePtr = (const unsigned char*)image; | |
| unsigned char* dataPtr = (unsigned char*)this->dataAt(atlasLocator->topLeft()); | |
| size_t imageRB = width * fBytesPerPixel; | |
| size_t plotRB = this->rowBytes(); | |
| // copy into the data buffer, swizzling as we go if this is ARGB data | |
| constexpr bool kBGRAIsNative = kN32_SkColorType == kBGRA_8888_SkColorType; | |
| if (4 == fBytesPerPixel && kBGRAIsNative) { | |
| for (int i = 0; i < height; ++i) { | |
| SkOpts::RGBA_to_BGRA((uint32_t*)dataPtr, (const uint32_t*)imagePtr, width); | |
| dataPtr += plotRB; | |
| imagePtr += imageRB; | |
| } | |
| } else { | |
| for (int i = 0; i < height; ++i) { | |
| memcpy(dataPtr, imagePtr, imageRB); | |
| dataPtr += plotRB; | |
| imagePtr += imageRB; | |
| } | |
| } | |
| return true; | |
| } | |
| std::pair<const void*, SkIRect> GrPlot::prepareForUpload() { | |
| if (!fData || fDirtyRect.isEmpty()) { | |
| return {nullptr, {}}; | |
| } | |
| // We should only be issuing uploads if we are dirty | |
| SkASSERT(fDirty); | |
| SkASSERT(!fUploadInFlight); | |
| SkDEBUGCODE(fUploadInFlight = true;) | |
| const std::byte* dataPtr; | |
| SkIRect offsetRect; | |
| // Clamp to 4-byte aligned boundaries | |
| unsigned int clearBits = 0x3 / fBytesPerPixel; | |
| fDirtyRect.fLeft &= ~clearBits; | |
| fDirtyRect.fRight += clearBits; | |
| fDirtyRect.fRight &= ~clearBits; | |
| SkASSERT(fDirtyRect.fRight <= fWidth); | |
| // Set up dataPtr | |
| dataPtr = fData; | |
| dataPtr += this->rowBytes() * fDirtyRect.fTop; | |
| dataPtr += fBytesPerPixel * fDirtyRect.fLeft; | |
| offsetRect = fDirtyRect.makeOffset(fOffset.fX, fOffset.fY); | |
| // We cannot clear the dirty rect here because we do not know the results of the | |
| // associated writePixels call. | |
| return {dataPtr, offsetRect}; | |
| } | |
| void GrPlot::resetRects(bool freeData) { | |
| fRectanizer.reset(); | |
| fGenID = fGenerationCounter->next(); | |
| fPlotLocator = GrPlotLocator(this->pageIndex(), this->plotIndex(), fGenID); | |
| fLastUpload = skgpu::Token::InvalidToken(); | |
| fLastUse = skgpu::Token::InvalidToken(); | |
| if (freeData) { | |
| sk_free(fData); | |
| fData = nullptr; | |
| } else if (fData) { | |
| // zero out the plot | |
| sk_bzero(fData, this->rowBytes() * fHeight); | |
| } | |
| fDirtyRect.setEmpty(); | |
| SkDEBUGCODE(fDirty = false;) | |
| SkDEBUGCODE(fUploadInFlight = false;) | |
| } |