JNIEXPORT void JNICALL Java_org_openpanodroid_panoutils_android_CubicPanoNative_calculateCubeSide(JNIEnv *env, jclass obj, jobject panoBmp, jobject faceBmp, jint face) { LOGI("Entering native calculateCubeSide()"); AndroidBitmapInfo panoBmpInfo; AndroidBitmapInfo faceBmpInfo; void *panoPixels; void *facePixels; CubeCoordinates cubeCoordinates; SphericalCoordinates sphericalCoordinates; if (AndroidBitmap_getInfo(env, panoBmp, &panoBmpInfo) < 0) { LOGE("Could not get info for pano bitmap"); return; } if (AndroidBitmap_getInfo(env, faceBmp, &faceBmpInfo) < 0) { LOGE("Could not get info for face bitmap"); return; } if (panoBmpInfo.width != 2*panoBmpInfo.height) { LOGE("Invalid pano bitmap size (width != 2*height)"); return; } if (faceBmpInfo.width != faceBmpInfo.height) { LOGE("Invalid face bitmap size (width != height)"); return; } if (faceBmpInfo.format != panoBmpInfo.format) { LOGE("Format of pano bitmap and face bitmap not equal"); return; } if (panoBmpInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888) { LOGE("Invalid bitmap format != RGBA_8888"); return; } if (AndroidBitmap_lockPixels(env, panoBmp, &panoPixels) < 0) { LOGE("Could not lock pixels of pano bitmap"); return; } if (AndroidBitmap_lockPixels(env, faceBmp, &facePixels) < 0) { LOGE("Could not lock pixels of face bitmap"); AndroidBitmap_unlockPixels(env, panoBmp); return; } initTextureToCubeCoordinates(faceBmpInfo.width); for (int xTexture = 0; xTexture < faceBmpInfo.width; xTexture++) { LOGI("Texture row: %d", xTexture); for (int yTexture = 0; yTexture < faceBmpInfo.height; yTexture++) { textureToCubeCoordinates(xTexture, yTexture, &cubeCoordinates); cubeToSphericalCoordinates(&cubeCoordinates, face, &sphericalCoordinates); uint32_t pixel = getEquirectangularPixel(panoPixels, panoBmpInfo.width, panoBmpInfo.height, panoBmpInfo.stride, &sphericalCoordinates); setPixelRGBA(facePixels, faceBmpInfo.stride, xTexture, yTexture, pixel); } } AndroidBitmap_unlockPixels(env, panoBmp); AndroidBitmap_unlockPixels(env, faceBmp); LOGI("Leaving native calculateCubeSide()"); }
void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int) { if (m_frameBufferCache.isEmpty()) return; // Initialize the framebuffer if needed. ImageFrame& buffer = m_frameBufferCache[0]; if (buffer.status() == ImageFrame::FrameEmpty) { png_structp png = m_reader->pngPtr(); if (!buffer.setSize(scaledSize().width(), scaledSize().height())) { longjmp(JMPBUF(png), 1); return; } unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3; if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr())) { m_reader->createInterlaceBuffer(colorChannels * size().width() * size().height()); if (!m_reader->interlaceBuffer()) { longjmp(JMPBUF(png), 1); return; } } #if USE(QCMSLIB) if (m_reader->colorTransform()) { m_reader->createRowBuffer(colorChannels * size().width()); if (!m_reader->rowBuffer()) { longjmp(JMPBUF(png), 1); return; } } #endif buffer.setStatus(ImageFrame::FramePartial); buffer.setHasAlpha(false); buffer.setColorProfile(m_colorProfile); // For PNGs, the frame always fills the entire image. buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); } /* libpng comments (here to explain what follows). * * this function is called for every row in the image. If the * image is interlacing, and you turned on the interlace handler, * this function will be called for every row in every pass. * Some of these rows will not be changed from the previous pass. * When the row is not changed, the new_row variable will be NULL. * The rows and passes are called in order, so you don't really * need the row_num and pass, but I'm supplying them because it * may make your life easier. */ // Nothing to do if the row is unchanged, or the row is outside // the image bounds: libpng may send extra rows, ignore them to // make our lives easier. if (!rowBuffer) return; int y = !m_scaled ? rowIndex : scaledY(rowIndex); if (y < 0 || y >= scaledSize().height()) return; /* libpng comments (continued). * * For the non-NULL rows of interlaced images, you must call * png_progressive_combine_row() passing in the row and the * old row. You can call this function for NULL rows (it will * just return) and for non-interlaced images (it just does the * memcpy for you) if it will make the code easier. Thus, you * can just do this for all cases: * * png_progressive_combine_row(png_ptr, old_row, new_row); * * where old_row is what was displayed for previous rows. Note * that the first pass (pass == 0 really) will completely cover * the old row, so the rows do not have to be initialized. After * the first pass (and only for interlaced images), you will have * to pass the current row, and the function will combine the * old row and the new row. */ bool hasAlpha = m_reader->hasAlpha(); unsigned colorChannels = hasAlpha ? 4 : 3; png_bytep row = rowBuffer; if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { row = interlaceBuffer + (rowIndex * colorChannels * size().width()); png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); } #if USE(QCMSLIB) if (qcms_transform* transform = m_reader->colorTransform()) { qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width()); row = m_reader->rowBuffer(); } #endif // Write the decoded row pixels to the frame buffer. ImageFrame::PixelData* address = buffer.getAddr(0, y); int width = scaledSize().width(); unsigned char nonTrivialAlphaMask = 0; #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) if (m_scaled) { for (int x = 0; x < width; ++x) { png_bytep pixel = row + m_scaledColumns[x] * colorChannels; unsigned alpha = hasAlpha ? pixel[3] : 255; buffer.setRGBA(address++, pixel[0], pixel[1], pixel[2], alpha); nonTrivialAlphaMask |= (255 - alpha); } } else #endif { png_bytep pixel = row; if (hasAlpha) { if (buffer.premultiplyAlpha()) { for (int x = 0; x < width; ++x, pixel += 4) setPixelPremultipliedRGBA(address++, pixel, nonTrivialAlphaMask); } else { for (int x = 0; x < width; ++x, pixel += 4) setPixelRGBA(address++, pixel, nonTrivialAlphaMask); } } else { for (int x = 0; x < width; ++x, pixel += 3) setPixelRGB(address++, pixel); } } if (nonTrivialAlphaMask && !buffer.hasAlpha()) buffer.setHasAlpha(true); }