| // Copyright 2016 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <directfb.h> |
| |
| #include "starboard/blitter.h" |
| #include "starboard/log.h" |
| #include "starboard/memory.h" |
| #include "starboard/shared/directfb/blitter_internal.h" |
| |
| namespace { |
| int WordToByteOrder(int bytes_per_pixel, int byte_index) { |
| // Since DirectFB stores color in word-order but we wish to output in |
| // byte-order fomat, we perform that conversion here. |
| #if SB_IS_LITTLE_ENDIAN |
| return bytes_per_pixel - 1 - byte_index; |
| #else |
| return byte_index; |
| #endif |
| } |
| |
| void SwizzlePixels(void* in_data, |
| void* out_data, |
| int width, |
| int height, |
| int in_bytes_per_pixel, |
| int in_pitch_in_bytes, |
| int out_pitch_in_bytes, |
| int channel_1, |
| int channel_2, |
| int channel_3, |
| int channel_4) { |
| uint8_t* in_data_bytes = static_cast<uint8_t*>(in_data); |
| uint8_t* out_data_bytes = static_cast<uint8_t*>(out_data); |
| |
| bool needs_swizzle = true; |
| if (WordToByteOrder(in_bytes_per_pixel, channel_1) == 0 && |
| WordToByteOrder(in_bytes_per_pixel, channel_2) == 1 && |
| WordToByteOrder(in_bytes_per_pixel, channel_3) == 2 && |
| WordToByteOrder(in_bytes_per_pixel, channel_4) == 3) { |
| SB_DCHECK(in_bytes_per_pixel == 4); |
| needs_swizzle = false; |
| } |
| |
| for (int y = 0; y < height; ++y) { |
| if (needs_swizzle) { |
| for (int x = 0; x < width; ++x) { |
| out_data_bytes[0] = |
| in_data_bytes[WordToByteOrder(in_bytes_per_pixel, channel_1)]; |
| out_data_bytes[1] = |
| in_data_bytes[WordToByteOrder(in_bytes_per_pixel, channel_2)]; |
| out_data_bytes[2] = |
| in_data_bytes[WordToByteOrder(in_bytes_per_pixel, channel_3)]; |
| out_data_bytes[3] = |
| in_data_bytes[WordToByteOrder(in_bytes_per_pixel, channel_4)]; |
| |
| out_data_bytes += 4; |
| in_data_bytes += in_bytes_per_pixel; |
| } |
| |
| // Increment through the gab between the input data pitch and its |
| // width * bpp. |
| in_data_bytes += in_pitch_in_bytes - width * in_bytes_per_pixel; |
| out_data_bytes += out_pitch_in_bytes - width * 4; |
| } else { |
| SbMemoryCopy(out_data_bytes, in_data_bytes, width * 4); |
| in_data_bytes += in_pitch_in_bytes; |
| out_data_bytes += out_pitch_in_bytes; |
| } |
| } |
| } |
| } // namespace |
| |
| bool SbBlitterDownloadSurfacePixels(SbBlitterSurface surface, |
| SbBlitterPixelDataFormat pixel_format, |
| int pitch_in_bytes, |
| void* out_pixel_data) { |
| if (!SbBlitterIsSurfaceValid(surface)) { |
| SB_DLOG(ERROR) << __FUNCTION__ << ": Invalid surface."; |
| return false; |
| } |
| if (out_pixel_data == NULL) { |
| SB_DLOG(ERROR) << __FUNCTION__ << ": |out_pixel_data| cannot be NULL."; |
| return false; |
| } |
| if (!SbBlitterIsPixelFormatSupportedByDownloadSurfacePixels(surface, |
| pixel_format)) { |
| SB_DLOG(ERROR) << __FUNCTION__ << ": Unsupported pixel format."; |
| return false; |
| } |
| if (pitch_in_bytes < |
| surface->info.width * SbBlitterBytesPerPixelForFormat(pixel_format)) { |
| SB_DLOG(ERROR) |
| << __FUNCTION__ |
| << ": Output pitch must be at least as large as (width * BPP)."; |
| return false; |
| } |
| |
| { |
| // Wait for all previously flushed draw calls to be executed. |
| starboard::ScopedLock lock(surface->device->mutex); |
| if (surface->device->dfb->WaitIdle(surface->device->dfb) != DFB_OK) { |
| SB_DLOG(ERROR) << __FUNCTION__ << ": WaitIdle() failed."; |
| return false; |
| } |
| } |
| |
| IDirectFBSurface* dfb_surface = surface->surface; |
| |
| DFBSurfacePixelFormat surface_pixel_format; |
| dfb_surface->GetPixelFormat(dfb_surface, &surface_pixel_format); |
| |
| // Lock the surface's pixels and then copy them out into the user's provided |
| // memory. Swizzel the pixel's bytes as we copy them out, if necessary. |
| void* data; |
| int surface_pitch_in_bytes; |
| if (dfb_surface->Lock(dfb_surface, DSLF_READ, &data, |
| &surface_pitch_in_bytes) != DFB_OK) { |
| SB_DLOG(ERROR) << __FUNCTION__ << ": Error calling Lock()."; |
| return false; |
| } |
| |
| switch (DFB_PIXELFORMAT_INDEX(surface_pixel_format)) { |
| case DFB_PIXELFORMAT_INDEX(DSPF_ARGB): { |
| switch (pixel_format) { |
| case kSbBlitterPixelDataFormatARGB8: { |
| SwizzlePixels(data, out_pixel_data, surface->info.width, |
| surface->info.height, 4, surface_pitch_in_bytes, |
| pitch_in_bytes, 0, 1, 2, 3); |
| } break; |
| case kSbBlitterPixelDataFormatBGRA8: { |
| SwizzlePixels(data, out_pixel_data, surface->info.width, |
| surface->info.height, 4, surface_pitch_in_bytes, |
| pitch_in_bytes, 3, 2, 1, 0); |
| } break; |
| case kSbBlitterPixelDataFormatRGBA8: { |
| SwizzlePixels(data, out_pixel_data, surface->info.width, |
| surface->info.height, 4, surface_pitch_in_bytes, |
| pitch_in_bytes, 1, 2, 3, 0); |
| } break; |
| default: { SB_NOTREACHED(); } |
| } |
| } break; |
| |
| case DFB_PIXELFORMAT_INDEX(DSPF_A8): { |
| // Convert A8 to either ARGB, BGRA, or RGBA. |
| SwizzlePixels(data, out_pixel_data, surface->info.width, |
| surface->info.height, 1, surface_pitch_in_bytes, |
| pitch_in_bytes, 0, 0, 0, 0); |
| } break; |
| |
| default: { |
| SB_DLOG(ERROR) << __FUNCTION__ << ": Unsupported pixel format index (" |
| << DFB_PIXELFORMAT_INDEX(surface_pixel_format) << ")."; |
| dfb_surface->Unlock(dfb_surface); |
| return false; |
| } |
| } |
| |
| // Okay the copy is complete, unlock the pixels and carry on. |
| dfb_surface->Unlock(dfb_surface); |
| |
| return true; |
| } |